From 47711d616eeee2ee8917f3f315f3666dd4f03a04 Mon Sep 17 00:00:00 2001 From: mark Date: Thu, 15 Jun 2006 04:14:30 +0000 Subject: [PATCH] --- Data/Assemblies.cfg | 6 + Data/Binary/Bounds.bin | Bin 0 -> 131072 bytes Data/Bulk Orders/Blacksmith/armor.cfg | 29 + Data/Bulk Orders/Blacksmith/largeaxes.cfg | 8 + Data/Bulk Orders/Blacksmith/largechain.cfg | 5 + Data/Bulk Orders/Blacksmith/largefencing.cfg | 7 + Data/Bulk Orders/Blacksmith/largemaces.cfg | 8 + Data/Bulk Orders/Blacksmith/largeplate.cfg | 8 + Data/Bulk Orders/Blacksmith/largepolearms.cfg | 4 + Data/Bulk Orders/Blacksmith/largering.cfg | 6 + Data/Bulk Orders/Blacksmith/largeswords.cfg | 8 + Data/Bulk Orders/Blacksmith/weapons.cfg | 28 + Data/Bulk Orders/Tailoring/boneset.cfg | 7 + Data/Bulk Orders/Tailoring/cloth.cfg | 31 + Data/Bulk Orders/Tailoring/farmer.cfg | 6 + .../Tailoring/femaleleatherset.cfg | 8 + Data/Bulk Orders/Tailoring/fishergirl.cfg | 6 + Data/Bulk Orders/Tailoring/gypsy.cfg | 6 + Data/Bulk Orders/Tailoring/hatset.cfg | 6 + Data/Bulk Orders/Tailoring/jester.cfg | 6 + Data/Bulk Orders/Tailoring/lady.cfg | 6 + Data/Bulk Orders/Tailoring/leather.cfg | 40 + Data/Bulk Orders/Tailoring/maleleatherset.cfg | 8 + Data/Bulk Orders/Tailoring/pirate.cfg | 6 + Data/Bulk Orders/Tailoring/shoeset.cfg | 6 + Data/Bulk Orders/Tailoring/studdedset.cfg | 7 + Data/Bulk Orders/Tailoring/towncrier.cfg | 7 + Data/Bulk Orders/Tailoring/wizard.cfg | 6 + Data/Common.MAP | 864 ++ Data/Components/doors.txt | 28 + Data/Components/floors.txt | 42 + Data/Components/misc.txt | 70 + Data/Components/roof.txt | 32 + Data/Components/stairs.txt | 12 + Data/Components/suppinfo.txt | 1417 ++ Data/Components/teleprts.txt | 3 + Data/Components/walls.txt | 125 + Data/Decoration/Britannia/_covetous.cfg | 927 ++ Data/Decoration/Britannia/_deceit.cfg | 883 ++ Data/Decoration/Britannia/_despise.cfg | 439 + Data/Decoration/Britannia/_destard.cfg | 448 + Data/Decoration/Britannia/_fire.cfg | 203 + Data/Decoration/Britannia/_hedgemaze.cfg | 906 ++ Data/Decoration/Britannia/_hythloth.cfg | 2000 +++ Data/Decoration/Britannia/_ice.cfg | 162 + Data/Decoration/Britannia/_orccave.cfg | 3961 ++++++ Data/Decoration/Britannia/_shame.cfg | 901 ++ Data/Decoration/Britannia/_terathankeep.cfg | 183 + Data/Decoration/Britannia/_wrong.cfg | 370 + Data/Decoration/Britannia/britain.cfg | 5373 ++++++++ Data/Decoration/Britannia/bucs.cfg | 722 + Data/Decoration/Britannia/bucsportal.cfg | 137 + Data/Decoration/Britannia/cove.cfg | 317 + Data/Decoration/Britannia/delucia.cfg | 229 + Data/Decoration/Britannia/despise.cfg | 17 + Data/Decoration/Britannia/destard.cfg | 3 + Data/Decoration/Britannia/hythloth.cfg | 20 + Data/Decoration/Britannia/jhelom.cfg | 2769 ++++ Data/Decoration/Britannia/magincia.cfg | 1481 +++ Data/Decoration/Britannia/minoc.cfg | 1568 +++ Data/Decoration/Britannia/moonglow.cfg | 1990 +++ Data/Decoration/Britannia/nujelm.cfg | 2121 +++ Data/Decoration/Britannia/papua.cfg | 456 + Data/Decoration/Britannia/patches.cfg | 141 + Data/Decoration/Britannia/serpent pillars.cfg | 13 + Data/Decoration/Britannia/serpentshold.cfg | 1251 ++ Data/Decoration/Britannia/shame.cfg | 71 + Data/Decoration/Britannia/shrines.cfg | 120 + Data/Decoration/Britannia/skara.cfg | 2212 ++++ .../Britannia/solen matriarch quest.cfg | 6 + .../Britannia/study of solen quest.cfg | 7 + Data/Decoration/Britannia/trinsic.cfg | 1996 +++ Data/Decoration/Britannia/vesper.cfg | 1905 +++ Data/Decoration/Britannia/wind.cfg | 773 ++ .../Britannia/witch apprentice quest.cfg | 48 + Data/Decoration/Britannia/yew.cfg | 79 + .../Felucca/_champion teleporters.cfg | 100 + Data/Decoration/Felucca/_star room.cfg | 31 + .../Felucca/ambitious solen queen quest.cfg | 3 + Data/Decoration/Felucca/khaldun entrance.cfg | 439 + Data/Decoration/Felucca/khaldun.cfg | 739 ++ Data/Decoration/Felucca/ocllo.cfg | 1318 ++ .../Felucca/solen matriarch quest.cfg | 3 + Data/Decoration/Felucca/yew.cfg | 2248 ++++ Data/Decoration/Ilshenar/_blood.cfg | 58 + Data/Decoration/Ilshenar/_central.cfg | 329 + Data/Decoration/Ilshenar/_exodus.cfg | 1155 ++ Data/Decoration/Ilshenar/_gwenno.cfg | 62 + Data/Decoration/Ilshenar/_ratcave.cfg | 71 + Data/Decoration/Ilshenar/_regvolom.cfg | 21 + Data/Decoration/Ilshenar/_serpentine.cfg | 28 + Data/Decoration/Ilshenar/_sheepfarm.cfg | 31 + Data/Decoration/Ilshenar/_sorcerers.cfg | 344 + Data/Decoration/Ilshenar/_terortskitas.cfg | 75 + Data/Decoration/Ilshenar/_twinoaks.cfg | 99 + Data/Decoration/Ilshenar/camp.cfg | 114 + Data/Decoration/Ilshenar/camp2.cfg | 23 + Data/Decoration/Ilshenar/enslavedgargs.cfg | 87 + Data/Decoration/Ilshenar/gargoylecity.cfg | 192 + Data/Decoration/Ilshenar/golemsandjukas.cfg | 135 + Data/Decoration/Ilshenar/lakeshire.cfg | 477 + Data/Decoration/Ilshenar/meercatacombs.cfg | 309 + Data/Decoration/Ilshenar/misc.cfg | 87 + Data/Decoration/Ilshenar/mistas.cfg | 1311 ++ Data/Decoration/Ilshenar/patches.cfg | 27 + Data/Decoration/Ilshenar/ratfortress.cfg | 17 + Data/Decoration/Ilshenar/savagecamp.cfg | 52 + Data/Decoration/Ilshenar/shrines.cfg | 28 + Data/Decoration/Ilshenar/vinculuminn.cfg | 27 + Data/Decoration/Ilshenar/wispdungeon.cfg | 520 + Data/Decoration/Malas/_luna.cfg | 184 + Data/Decoration/Malas/_umbra.cfg | 96 + Data/Decoration/Malas/dark tides quest.cfg | 121 + Data/Decoration/Malas/doom.cfg | 135 + .../Malas/emino's undertaking quest.cfg | 142 + .../Malas/haochi's trials quest.cfg | 85 + Data/Decoration/Malas/luna.cfg | 6 + Data/Decoration/Malas/markcontainers.cfg | 17 + Data/Decoration/Malas/summoning quest.cfg | 44 + .../Tokuno/terrible hatchlings quest.cfg | 3 + Data/Decoration/Tokuno/tokuno.cfg | 127 + .../Decoration/Tokuno/treasures of tokuno.cfg | 14 + Data/Decoration/Trammel/_haven additions.cfg | 999 ++ Data/Decoration/Trammel/_minoc camp.cfg | 77 + .../Trammel/ambitious solen queen quest.cfg | 3 + Data/Decoration/Trammel/collector quest.cfg | 23 + Data/Decoration/Trammel/haven.cfg | 334 + Data/Decoration/Trammel/magincia.cfg | 9 + .../Trammel/solen matriarch quest.cfg | 3 + .../Trammel/uzeraan turmoil quest.cfg | 108 + Data/Decoration/Trammel/yew.cfg | 2247 ++++ Data/Locations/felucca.xml | 279 + Data/Locations/ilshenar.xml | 111 + Data/Locations/malas.xml | 79 + Data/Locations/tokuno.xml | 42 + Data/Locations/trammel.xml | 258 + Data/Regions.xml | 2149 +++ Data/SpawnDefinitions.xml | 22 + Data/bodyTable.cfg | 966 ++ Data/containers.cfg | 45 + Data/items.cfg | 143 + Data/mobiles.cfg | 58 + Data/names.xml | 645 + Data/objects.xml | 2632 ++++ Data/pageresponse.cfg | 10 + Data/shrink.cfg | 356 + Data/signs.cfg | 509 + Data/treasure.cfg | 200 + Scripts/Accounting/AccessRestrictions.cs | 46 + Scripts/Accounting/Account.cs | 1173 ++ Scripts/Accounting/AccountAttackLimiter.cs | 142 + Scripts/Accounting/AccountComment.cs | 77 + Scripts/Accounting/AccountHandler.cs | 396 + Scripts/Accounting/AccountTag.cs | 61 + Scripts/Accounting/Accounts.cs | 106 + Scripts/Accounting/Firewall.cs | 131 + Scripts/Accounting/IPLimiter.cs | 59 + Scripts/Commands/Add.cs | 606 + Scripts/Commands/Attributes.cs | 40 + Scripts/Commands/Batch.cs | 458 + Scripts/Commands/BoundingBoxPicker.cs | 68 + Scripts/Commands/ConvertPlayers.cs | 90 + Scripts/Commands/Decorate.cs | 1139 ++ Scripts/Commands/Docs.cs | 2404 ++++ Scripts/Commands/Dupe.cs | 145 + Scripts/Commands/ExportWSC.cs | 83 + Scripts/Commands/GenCategorization.cs | 427 + Scripts/Commands/GenTeleporter.cs | 1050 ++ .../Commands/Generic/Commands/BaseCommand.cs | 205 + Scripts/Commands/Generic/Commands/Commands.cs | 1039 ++ .../Commands/Generic/Commands/Interface.cs | 570 + .../Generic/Extensions/BaseExtension.cs | 184 + .../Compilers/ConditionalCompiler.cs | 569 + .../Extensions/Compilers/DistinctCompiler.cs | 249 + .../Extensions/Compilers/SortCompiler.cs | 172 + .../Generic/Extensions/DistinctExtension.cs | 89 + .../Generic/Extensions/LimitExtension.cs | 46 + .../Generic/Extensions/SortExtension.cs | 109 + .../Generic/Extensions/WhereExtension.cs | 55 + .../Implementors/AreaCommandImplementor.cs | 128 + .../Implementors/BaseCommandImplementor.cs | 336 + .../ContainedCommandImplementor.cs | 85 + .../Implementors/GlobalCommandImplementor.cs | 60 + .../Implementors/MultiCommandImplementor.cs | 77 + .../Generic/Implementors/ObjectConditional.cs | 266 + .../Implementors/OnlineCommandImplementor.cs | 67 + .../Implementors/RegionCommandImplementor.cs | 61 + .../Implementors/SelfCommandImplementor.cs | 27 + .../Implementors/SerialCommandImplementor.cs | 66 + .../Implementors/SingleCommandImplementor.cs | 94 + Scripts/Commands/Handlers.cs | 1496 +++ Scripts/Commands/Logging.cs | 144 + Scripts/Commands/Properties.cs | 796 ++ Scripts/Commands/SignParser.cs | 139 + Scripts/Commands/Skills.cs | 152 + Scripts/Commands/SkillsMenu.cs | 40 + Scripts/Commands/Statics.cs | 577 + Scripts/Commands/VisibilityList.cs | 148 + Scripts/Commands/Wipe.cs | 111 + Scripts/Context Menus/AddToSpellbookEntry.cs | 63 + Scripts/Context Menus/EatEntry.cs | 25 + Scripts/Context Menus/OpenBankEntry.cs | 33 + Scripts/Context Menus/TeachEntry.cs | 30 + Scripts/Engines/AI/AI/AnimalAI.cs | 157 + Scripts/Engines/AI/AI/ArcherAI.cs | 131 + Scripts/Engines/AI/AI/BaseAI.cs | 2748 ++++ Scripts/Engines/AI/AI/BerserkAI.cs | 87 + Scripts/Engines/AI/AI/HealerAI.cs | 176 + Scripts/Engines/AI/AI/MageAI.cs | 992 ++ Scripts/Engines/AI/AI/MeleeAI.cs | 183 + Scripts/Engines/AI/AI/PredatorAI.cs | 100 + Scripts/Engines/AI/AI/ThiefAI.cs | 194 + Scripts/Engines/AI/AI/VendorAI.cs | 149 + Scripts/Engines/AI/Creature/BaseCreature.cs | 4792 +++++++ Scripts/Engines/AI/Creature/Dummy.cs | 152 + Scripts/Engines/AI/Creature/DummySpecific.cs | 814 ++ .../Engines/AI/Creature/OppositionGroup.cs | 140 + Scripts/Engines/AI/Creature/Paragon.cs | 182 + Scripts/Engines/AI/Creature/SpeedInfo.cs | 213 + .../AI/Targets/AIControlMobileTarget.cs | 45 + Scripts/Engines/AI/Team/Team.cs | 32 + Scripts/Engines/AI/Team/TeamRegistry.cs | 29 + Scripts/Engines/BulkOrders/Books/BOBFilter.cs | 88 + .../Engines/BulkOrders/Books/BOBFilterGump.cs | 215 + Scripts/Engines/BulkOrders/Books/BOBGump.cs | 611 + .../Engines/BulkOrders/Books/BOBLargeEntry.cs | 110 + .../BulkOrders/Books/BOBLargeSubEntry.cs | 58 + .../Engines/BulkOrders/Books/BOBSmallEntry.cs | 101 + .../Engines/BulkOrders/Books/BODBuyGump.cs | 126 + Scripts/Engines/BulkOrders/Books/BODType.cs | 10 + .../Engines/BulkOrders/Books/BulkOrderBook.cs | 271 + .../Engines/BulkOrders/BulkMaterialType.cs | 45 + Scripts/Engines/BulkOrders/LargeBOD.cs | 256 + .../Engines/BulkOrders/LargeBODAcceptGump.cs | 106 + Scripts/Engines/BulkOrders/LargeBODGump.cs | 100 + Scripts/Engines/BulkOrders/LargeBODTarget.cs | 25 + Scripts/Engines/BulkOrders/LargeBulkEntry.cs | 189 + Scripts/Engines/BulkOrders/LargeSmithBOD.cs | 139 + Scripts/Engines/BulkOrders/LargeTailorBOD.cs | 134 + Scripts/Engines/BulkOrders/Rewards.cs | 739 ++ Scripts/Engines/BulkOrders/SmallBOD.cs | 279 + .../Engines/BulkOrders/SmallBODAcceptGump.cs | 93 + Scripts/Engines/BulkOrders/SmallBODGump.cs | 83 + Scripts/Engines/BulkOrders/SmallBODTarget.cs | 25 + Scripts/Engines/BulkOrders/SmallBulkEntry.cs | 111 + Scripts/Engines/BulkOrders/SmallSmithBOD.cs | 244 + Scripts/Engines/BulkOrders/SmallTailorBOD.cs | 236 + Scripts/Engines/CannedEvil/ChampionAltar.cs | 58 + .../Engines/CannedEvil/ChampionPlatform.cs | 89 + Scripts/Engines/CannedEvil/ChampionSkull.cs | 73 + .../CannedEvil/ChampionSkullBrazier.cs | 173 + .../CannedEvil/ChampionSkullPlatform.cs | 131 + .../Engines/CannedEvil/ChampionSkullType.cs | 15 + Scripts/Engines/CannedEvil/ChampionSpawn.cs | 1076 ++ .../Engines/CannedEvil/ChampionSpawnType.cs | 106 + Scripts/Engines/CannedEvil/HarrowerGate.cs | 59 + Scripts/Engines/CannedEvil/RestartTimer.cs | 23 + Scripts/Engines/CannedEvil/SliceTimer.cs | 23 + Scripts/Engines/CannedEvil/StarRoomGate.cs | 106 + Scripts/Engines/Chat/Channel.cs | 548 + Scripts/Engines/Chat/Chat.cs | 174 + Scripts/Engines/Chat/ChatActionHandler.cs | 24 + Scripts/Engines/Chat/ChatActionHandlers.cs | 359 + Scripts/Engines/Chat/ChatCommand.cs | 44 + Scripts/Engines/Chat/ChatUser.cs | 321 + Scripts/Engines/Chat/Chatold.cs | 20 + Scripts/Engines/Chat/Packets.cs | 30 + Scripts/Engines/Craft/Core/CraftContext.cs | 58 + Scripts/Engines/Craft/Core/CraftGroup.cs | 39 + Scripts/Engines/Craft/Core/CraftGroupCol.cs | 48 + Scripts/Engines/Craft/Core/CraftGump.cs | 582 + Scripts/Engines/Craft/Core/CraftGumpItem.cs | 287 + Scripts/Engines/Craft/Core/CraftItem.cs | 1309 ++ Scripts/Engines/Craft/Core/CraftItemCol.cs | 58 + .../Craft/Core/CraftItemIDAttribute.cs | 18 + Scripts/Engines/Craft/Core/CraftRes.cs | 71 + Scripts/Engines/Craft/Core/CraftResCol.cs | 32 + Scripts/Engines/Craft/Core/CraftSkill.cs | 33 + Scripts/Engines/Craft/Core/CraftSkillCol.cs | 32 + Scripts/Engines/Craft/Core/CraftSubRes.cs | 58 + Scripts/Engines/Craft/Core/CraftSubResCol.cs | 75 + Scripts/Engines/Craft/Core/CraftSystem.cs | 337 + Scripts/Engines/Craft/Core/CustomCraft.cs | 36 + Scripts/Engines/Craft/Core/Enhance.cs | 313 + .../Engines/Craft/Core/QueryMakersMarkGump.cs | 54 + Scripts/Engines/Craft/Core/Recipes.cs | 123 + Scripts/Engines/Craft/Core/Repair.cs | 423 + Scripts/Engines/Craft/Core/Resmelt.cs | 121 + Scripts/Engines/Craft/DefAlchemy.cs | 165 + Scripts/Engines/Craft/DefBlacksmithy.cs | 547 + Scripts/Engines/Craft/DefBowFletching.cs | 133 + Scripts/Engines/Craft/DefCarpentry.cs | 345 + Scripts/Engines/Craft/DefCartography.cs | 88 + Scripts/Engines/Craft/DefCooking.cs | 247 + Scripts/Engines/Craft/DefGlassblowing.cs | 133 + Scripts/Engines/Craft/DefInscription.cs | 308 + Scripts/Engines/Craft/DefMasonry.cs | 150 + Scripts/Engines/Craft/DefTailoring.cs | 324 + Scripts/Engines/Craft/DefTinkering.cs | 493 + Scripts/Engines/Doom/GauntletSpawner.cs | 719 + Scripts/Engines/Ethics/Core/Ethic.cs | 252 + Scripts/Engines/Ethics/Core/Persistance.cs | 66 + Scripts/Engines/Ethics/Core/Player.cs | 171 + Scripts/Engines/Ethics/Core/Power.cs | 34 + .../Ethics/Definitions/EthicDefinition.cs | 39 + .../Ethics/Definitions/PowerDefinition.cs | 30 + Scripts/Engines/Ethics/Evil/Ethic.cs | 37 + .../Ethics/Evil/Mobiles/UnholyFamiliar.cs | 86 + .../Ethics/Evil/Mobiles/UnholySteed.cs | 91 + Scripts/Engines/Ethics/Evil/Powers/Blight.cs | 80 + .../Ethics/Evil/Powers/SummonFamiliar.cs | 47 + .../Engines/Ethics/Evil/Powers/UnholyItem.cs | 71 + .../Engines/Ethics/Evil/Powers/UnholySense.cs | 94 + .../Ethics/Evil/Powers/UnholyShield.cs | 34 + .../Engines/Ethics/Evil/Powers/UnholySteed.cs | 47 + .../Engines/Ethics/Evil/Powers/UnholyWord.cs | 23 + .../Engines/Ethics/Evil/Powers/VileBlade.cs | 23 + Scripts/Engines/Ethics/Hero/Ethic.cs | 40 + .../Ethics/Hero/Mobiles/HolyFamiliar.cs | 86 + .../Engines/Ethics/Hero/Mobiles/HolySteed.cs | 91 + Scripts/Engines/Ethics/Hero/Powers/Bless.cs | 80 + .../Engines/Ethics/Hero/Powers/HolyBlade.cs | 23 + .../Engines/Ethics/Hero/Powers/HolyItem.cs | 71 + .../Engines/Ethics/Hero/Powers/HolySense.cs | 94 + .../Engines/Ethics/Hero/Powers/HolyShield.cs | 34 + .../Engines/Ethics/Hero/Powers/HolySteed.cs | 47 + .../Engines/Ethics/Hero/Powers/HolyWord.cs | 23 + .../Ethics/Hero/Powers/SummonFamiliar.cs | 47 + .../Collections/CandidateCollection.cs | 199 + .../Factions/Collections/FactionCollection.cs | 199 + .../Collections/FactionGuardCollection.cs | 199 + .../Collections/FactionItemCollection.cs | 199 + .../Collections/FactionTrapCollection.cs | 199 + .../Collections/FactionVendorCollection.cs | 199 + .../Collections/GuardListCollection.cs | 199 + .../Collections/MonolithCollection.cs | 199 + .../Collections/PlayerStateCollection.cs | 199 + .../Factions/Collections/SigilCollection.cs | 199 + .../Collections/SilverGivenCollection.cs | 199 + .../Factions/Collections/TownCollection.cs | 199 + .../Collections/VendorListCollection.cs | 200 + Scripts/Engines/Factions/Core/Election.cs | 563 + Scripts/Engines/Factions/Core/Faction.cs | 1390 ++ Scripts/Engines/Factions/Core/FactionItem.cs | 138 + Scripts/Engines/Factions/Core/FactionState.cs | 258 + Scripts/Engines/Factions/Core/Generator.cs | 79 + Scripts/Engines/Factions/Core/GuardList.cs | 26 + Scripts/Engines/Factions/Core/Keywords.cs | 159 + .../Engines/Factions/Core/MerchantTitles.cs | 87 + Scripts/Engines/Factions/Core/Persistance.cs | 93 + Scripts/Engines/Factions/Core/PlayerState.cs | 134 + Scripts/Engines/Factions/Core/Reflector.cs | 123 + .../Engines/Factions/Core/SilverGivenEntry.cs | 23 + .../Engines/Factions/Core/StrongholdRegion.cs | 41 + Scripts/Engines/Factions/Core/Town.cs | 547 + Scripts/Engines/Factions/Core/TownState.cs | 168 + Scripts/Engines/Factions/Core/VendorList.cs | 26 + .../Factions/Definitions/FactionDefinition.cs | 97 + .../Definitions/FactionItemDefinition.cs | 51 + .../Factions/Definitions/GuardDefinition.cs | 42 + .../Factions/Definitions/RankDefinition.cs | 25 + .../Definitions/StrongholdDefintion.cs | 27 + .../Factions/Definitions/TownDefinition.cs | 59 + .../Factions/Definitions/VendorDefinition.cs | 83 + .../Engines/Factions/Gumps/ElectionGump.cs | 128 + .../Factions/Gumps/ElectionManagementGump.cs | 218 + Scripts/Engines/Factions/Gumps/FactionGump.cs | 62 + .../Factions/Gumps/FactionImbueGump.cs | 104 + .../Factions/Gumps/FactionStoneGump.cs | 346 + Scripts/Engines/Factions/Gumps/FinanceGump.cs | 276 + .../Factions/Gumps/HorseBreederGump.cs | 92 + .../Engines/Factions/Gumps/JoinStoneGump.cs | 52 + .../Factions/Gumps/LeaveFactionGump.cs | 92 + Scripts/Engines/Factions/Gumps/SheriffGump.cs | 177 + .../Engines/Factions/Gumps/TownStoneGump.cs | 206 + Scripts/Engines/Factions/Gumps/VoteGump.cs | 68 + .../Instances/Factions/CouncilOfMages.cs | 86 + .../Factions/Instances/Factions/Minax.cs | 84 + .../Instances/Factions/Shadowlords.cs | 84 + .../Instances/Factions/TrueBritannians.cs | 89 + .../Factions/Instances/Towns/Britain.cs | 26 + .../Factions/Instances/Towns/Magincia.cs | 26 + .../Engines/Factions/Instances/Towns/Minoc.cs | 26 + .../Factions/Instances/Towns/Moonglow.cs | 26 + .../Factions/Instances/Towns/SkaraBrae.cs | 26 + .../Factions/Instances/Towns/Trinsic.cs | 26 + .../Factions/Instances/Towns/Vesper.cs | 26 + .../Engines/Factions/Instances/Towns/Yew.cs | 26 + .../Engines/Factions/Items/BaseMonolith.cs | 135 + .../Factions/Items/BaseSystemController.cs | 66 + .../Engines/Factions/Items/FactionStone.cs | 105 + Scripts/Engines/Factions/Items/JoinStone.cs | 81 + Scripts/Engines/Factions/Items/Sigil.cs | 470 + Scripts/Engines/Factions/Items/Silver.cs | 58 + .../Factions/Items/StrongholdMonolith.cs | 40 + .../Engines/Factions/Items/TownMonolith.cs | 40 + Scripts/Engines/Factions/Items/TownStone.cs | 87 + .../Factions/Items/Traps/BaseFactionTrap.cs | 294 + .../Items/Traps/BaseFactionTrapDeed.cs | 112 + .../Items/Traps/FactionExplosionTrap.cs | 85 + .../Factions/Items/Traps/FactionGasTrap.cs | 85 + .../Factions/Items/Traps/FactionSawTrap.cs | 85 + .../Factions/Items/Traps/FactionSpikeTrap.cs | 85 + .../Items/Traps/FactionTrapRemovalKit.cs | 82 + .../Factions/Mobiles/FactionWarHorse.cs | 112 + .../Mobiles/Guards/BaseFactionGuard.cs | 515 + .../Factions/Mobiles/Guards/GuardAI.cs | 779 ++ .../Engines/Factions/Mobiles/Guards/Orders.cs | 146 + .../Mobiles/Guards/Types/FactionBerserker.cs | 74 + .../Guards/Types/FactionDeathKnight.cs | 70 + .../Mobiles/Guards/Types/FactionDragoon.cs | 75 + .../Mobiles/Guards/Types/FactionHenchman.cs | 67 + .../Mobiles/Guards/Types/FactionKnight.cs | 75 + .../Mobiles/Guards/Types/FactionMercenary.cs | 65 + .../Guards/Types/FactionNecromancer.cs | 68 + .../Mobiles/Guards/Types/FactionPaladin.cs | 76 + .../Mobiles/Guards/Types/FactionSorceress.cs | 72 + .../Mobiles/Guards/Types/FactionWizard.cs | 69 + .../Mobiles/Vendors/BaseFactionVendor.cs | 107 + .../Mobiles/Vendors/FactionBoardVendor.cs | 76 + .../Mobiles/Vendors/FactionBottleVendor.cs | 81 + .../Mobiles/Vendors/FactionHorseVendor.cs | 82 + .../Mobiles/Vendors/FactionOreVendor.cs | 77 + .../Mobiles/Vendors/FactionReagentVendor.cs | 95 + Scripts/Engines/Harvest/Core/HarvestBank.cs | 82 + .../Engines/Harvest/Core/HarvestDefinition.cs | 134 + .../Engines/Harvest/Core/HarvestResource.cs | 34 + .../Engines/Harvest/Core/HarvestSoundTimer.cs | 33 + Scripts/Engines/Harvest/Core/HarvestSystem.cs | 473 + Scripts/Engines/Harvest/Core/HarvestTarget.cs | 95 + Scripts/Engines/Harvest/Core/HarvestTimer.cs | 31 + Scripts/Engines/Harvest/Core/HarvestVein.cs | 25 + Scripts/Engines/Harvest/Fishing.cs | 530 + Scripts/Engines/Harvest/Lumberjacking.cs | 158 + Scripts/Engines/Harvest/Mining.cs | 404 + Scripts/Engines/Help/HelpGump.cs | 313 + Scripts/Engines/Help/PagePrompt.cs | 30 + Scripts/Engines/Help/PagePromptGump.cs | 61 + Scripts/Engines/Help/PageQueue.cs | 391 + Scripts/Engines/Help/PageQueueGump.cs | 834 ++ Scripts/Engines/Help/PageResponseGump.cs | 39 + Scripts/Engines/Help/SpeechLog.cs | 148 + Scripts/Engines/Help/SpeechLogGump.cs | 112 + Scripts/Engines/Help/StuckMenu.cs | 290 + .../Engines/Khaldun/Books/GrimmochJournal.cs | 779 ++ .../Engines/Khaldun/Books/LysanderNotebook.cs | 539 + .../Engines/Khaldun/Books/TavarasJournal.cs | 1333 ++ Scripts/Engines/Khaldun/KhaldunGen.cs | 211 + .../Engines/Khaldun/KhaldunPitTeleporter.cs | 108 + .../Khaldun/Mobiles/GrimmochDrummel.cs | 131 + .../Khaldun/Mobiles/LysanderGathenwale.cs | 132 + Scripts/Engines/Khaldun/Mobiles/MorgBergen.cs | 111 + .../Engines/Khaldun/Mobiles/TavaraSewel.cs | 120 + Scripts/Engines/Khaldun/PuzzleChest.cs | 788 ++ Scripts/Engines/Khaldun/RaisableItem.cs | 177 + Scripts/Engines/Khaldun/RaiseSwitch.cs | 223 + Scripts/Engines/MyRunUO/Config.cs | 51 + .../Engines/MyRunUO/DatabaseCommandQueue.cs | 202 + Scripts/Engines/MyRunUO/LayerComparer.cs | 97 + Scripts/Engines/MyRunUO/MyRunUO.cs | 640 + Scripts/Engines/MyRunUO/MyRunUOStatus.cs | 74 + Scripts/Engines/Party/AddPartyTarget.cs | 47 + Scripts/Engines/Party/DeclineTimer.cs | 38 + Scripts/Engines/Party/Packets.cs | 79 + Scripts/Engines/Party/Party.cs | 477 + Scripts/Engines/Party/PartyCommands.cs | 123 + Scripts/Engines/Party/PartyMemberInfo.cs | 20 + Scripts/Engines/Party/RemovePartyTarget.cs | 30 + Scripts/Engines/Pathing/FastAStarAlgorithm.cs | 292 + Scripts/Engines/Pathing/MoveResult.cs | 14 + Scripts/Engines/Pathing/Movement.cs | 516 + Scripts/Engines/Pathing/MovementPath.cs | 165 + Scripts/Engines/Pathing/PathAlgorithm.cs | 35 + Scripts/Engines/Pathing/PathFollower.cs | 204 + Scripts/Engines/Pathing/SlowAStarAlgorithm.cs | 260 + Scripts/Engines/Plants/EmptyTheBowlGump.cs | 117 + Scripts/Engines/Plants/MainPlantGump.cs | 388 + .../Engines/Plants/MiscItems/FertileDirt.cs | 39 + .../Engines/Plants/MiscItems/GreenThorns.cs | 727 + .../Engines/Plants/MiscItems/OrangePetals.cs | 149 + Scripts/Engines/Plants/MiscItems/RedLeaves.cs | 108 + .../Plants/MiscMobiles/GiantIceWorm.cs | 74 + .../Plants/Network/DisplayHelpTopic.cs | 18 + Scripts/Engines/Plants/PlantBowl.cs | 191 + Scripts/Engines/Plants/PlantHue.cs | 184 + Scripts/Engines/Plants/PlantItem.cs | 587 + Scripts/Engines/Plants/PlantPourTarget.cs | 32 + Scripts/Engines/Plants/PlantResources.cs | 52 + Scripts/Engines/Plants/PlantSystem.cs | 709 + Scripts/Engines/Plants/PlantType.cs | 206 + Scripts/Engines/Plants/PollinateTarget.cs | 92 + Scripts/Engines/Plants/ReproductionGump.cs | 266 + Scripts/Engines/Plants/Seed.cs | 190 + Scripts/Engines/Plants/SetToDecorativeGump.cs | 79 + .../AmbitiousQueenQuest.cs | 157 + .../Ambitious Solen Queen/Conversations.cs | 274 + .../Mobiles/AmbitiousSolenQueen.cs | 225 + .../Ambitious Solen Queen/Objectives.cs | 175 + .../Quests/Collector/CollectorQuest.cs | 122 + .../Engines/Quests/Collector/Conversations.cs | 874 ++ .../Quests/Collector/Items/EnchantedPaints.cs | 141 + .../Quests/Collector/Items/ImageType.cs | 107 + .../Quests/Collector/Items/Obsidian.cs | 264 + .../Quests/Collector/Items/PaintedImage.cs | 86 + .../Quests/Collector/Mobiles/AlbertaGiacco.cs | 94 + .../Collector/Mobiles/ElwoodMcCarrin.cs | 243 + .../Quests/Collector/Mobiles/GabrielPiete.cs | 105 + .../Quests/Collector/Mobiles/Impresario.cs | 164 + .../Quests/Collector/Mobiles/TomasONeerlan.cs | 111 + .../Engines/Quests/Collector/Objectives.cs | 533 + Scripts/Engines/Quests/Core/BaseQuester.cs | 128 + .../Quests/Core/Items/DynamicTeleporter.cs | 70 + .../Quests/Core/Items/EnchantedSextant.cs | 192 + .../Quests/Core/Items/HornOfRetreat.cs | 202 + .../Engines/Quests/Core/Items/QuestItem.cs | 121 + .../Engines/Quests/Core/QuestCallbackEntry.cs | 26 + .../Engines/Quests/Core/QuestConversation.cs | 156 + Scripts/Engines/Quests/Core/QuestItemInfo.cs | 72 + Scripts/Engines/Quests/Core/QuestObjective.cs | 278 + .../Engines/Quests/Core/QuestRestartInfo.cs | 42 + .../Engines/Quests/Core/QuestSerializer.cs | 197 + Scripts/Engines/Quests/Core/QuestSystem.cs | 686 + .../Quests/Core/Regions/CancelQuestRegion.cs | 44 + .../Regions/QuestCompleteObjectiveRegion.cs | 43 + .../Quests/Core/Regions/QuestNoEntryRegion.cs | 67 + .../Quests/Core/Regions/QuestOfferRegion.cs | 43 + .../Quests/Dark Tides/Conversations.cs | 607 + .../Quests/Dark Tides/DarkTidesQuest.cs | 153 + .../Dark Tides/Items/CrystalCaveBarrier.cs | 72 + .../Quests/Dark Tides/Items/DarkTidesHorn.cs | 44 + .../Dark Tides/Items/DarkTidesTeleporter.cs | 77 + .../Quests/Dark Tides/Items/KronusScroll.cs | 166 + .../Dark Tides/Items/KronusScrollBox.cs | 77 + .../Quests/Dark Tides/Items/MaabusCoffin.cs | 158 + .../Dark Tides/Items/ScrollOfAbraxus.cs | 146 + .../Dark Tides/Items/VaultOfSecretsBarrier.cs | 53 + .../Quests/Dark Tides/Mobiles/Horus.cs | 198 + .../Quests/Dark Tides/Mobiles/Maabus.cs | 47 + .../Quests/Dark Tides/Mobiles/Mardoth.cs | 230 + .../Dark Tides/Mobiles/SummonedPaladin.cs | 241 + .../Engines/Quests/Dark Tides/Objectives.cs | 642 + .../Emino's Undertaking/Conversations.cs | 610 + .../EminosUndertakingQuest.cs | 159 + .../Items/BlueNinjaQuestTeleporter.cs | 51 + .../Emino's Undertaking/Items/EminosKatana.cs | 48 + .../Items/EminosKatanaChest.cs | 148 + .../Items/GreenNinjaQuestTeleporter.cs | 51 + .../Items/GuardianBarrier.cs | 82 + .../Emino's Undertaking/Items/NoteForZoel.cs | 48 + .../Items/WhiteNinjaQuestTeleporter.cs | 59 + .../Emino's Undertaking/Mobiles/Emino.cs | 227 + .../Mobiles/EnshroudedFigure.cs | 56 + .../Emino's Undertaking/Mobiles/Henchman.cs | 55 + .../Mobiles/HiddenFigure.cs | 96 + .../Mobiles/JedahEntille.cs | 60 + .../Emino's Undertaking/Mobiles/Zoel.cs | 138 + .../Quests/Emino's Undertaking/Objectives.cs | 393 + .../Quests/Haochi's Trials/Conversations.cs | 713 + .../Haochi's Trials/HaochisTrialsQuest.cs | 151 + .../Haochi's Trials/Items/HaochisKatana.cs | 46 + .../Items/HaochisKatanaGenerator.cs | 80 + .../Items/HaochisTreasureChest.cs | 105 + .../Haochi's Trials/Items/HonorCandle.cs | 79 + .../Haochi's Trials/Mobiles/CursedSoul.cs | 70 + .../Haochi's Trials/Mobiles/DeadlyImp.cs | 80 + .../Haochi's Trials/Mobiles/DiseasedCat.cs | 56 + .../Haochi's Trials/Mobiles/FierceDragon.cs | 102 + .../Quests/Haochi's Trials/Mobiles/Haochi.cs | 182 + .../Mobiles/HaochisGuardsman.cs | 85 + .../Haochi's Trials/Mobiles/InjuredWolf.cs | 59 + .../Quests/Haochi's Trials/Mobiles/Relnia.cs | 96 + .../Haochi's Trials/Mobiles/YoungNinja.cs | 62 + .../Haochi's Trials/Mobiles/YoungRonin.cs | 68 + .../Quests/Haochi's Trials/Objectives.cs | 555 + .../Quests/Solen Matriarch/Conversations.cs | 330 + .../Solen Matriarch/Mobiles/SolenMatriarch.cs | 339 + .../Quests/Solen Matriarch/Objectives.cs | 216 + .../Solen Matriarch/SolenMatriarchQuest.cs | 160 + .../Study of the Solen Hive/Conversations.cs | 205 + .../Mobiles/Naturalist.cs | 158 + .../Study of the Solen Hive/NestArea.cs | 91 + .../Study of the Solen Hive/Objectives.cs | 203 + .../StudyOfSolenQuest.cs | 111 + .../Terrible Hatchlings/Conversations.cs | 111 + .../Mobiles/AnsellaGryen.cs | 138 + .../Quests/Terrible Hatchlings/Objectives.cs | 178 + .../TerribleHatchlingsQuest.cs | 70 + .../Quests/The Summoning/Conversations.cs | 93 + .../The Summoning/Items/BellOfTheDead.cs | 135 + .../The Summoning/Items/ChylothShroud.cs | 32 + .../The Summoning/Items/ChylothStaff.cs | 35 + .../Quests/The Summoning/Items/GoldenSkull.cs | 35 + .../The Summoning/Items/GrandGrimoire.cs | 36 + .../The Summoning/Items/SummoningAltar.cs | 64 + .../Quests/The Summoning/Mobiles/Chyloth.cs | 323 + .../Quests/The Summoning/Mobiles/Victoria.cs | 164 + .../Quests/The Summoning/Objectives.cs | 191 + .../Quests/The Summoning/TheSummoningQuest.cs | 166 + .../Quests/Uzeraan Turmoil/Conversations.cs | 970 ++ .../Quests/Uzeraan Turmoil/Items/Cannon.cs | 181 + .../Uzeraan Turmoil/Items/DaemonBloodChest.cs | 72 + .../Uzeraan Turmoil/Items/QuestDaemonBlood.cs | 45 + .../Uzeraan Turmoil/Items/QuestDaemonBone.cs | 45 + .../Uzeraan Turmoil/Items/QuestFertileDirt.cs | 45 + .../Items/SchmendrickApprenticeCorpse.cs | 205 + .../Items/SchmendrickScrollOfPower.cs | 46 + .../Items/UzeraanTurmoilHorn.cs | 43 + .../Items/UzeraanTurmoilTeleporter.cs | 74 + .../Quests/Uzeraan Turmoil/Mobiles/Dryad.cs | 202 + .../Uzeraan Turmoil/Mobiles/MansionGuard.cs | 80 + .../Mobiles/MilitiaCanoneer.cs | 113 + .../Uzeraan Turmoil/Mobiles/MilitiaFighter.cs | 151 + .../Uzeraan Turmoil/Mobiles/Schmendrick.cs | 156 + .../Quests/Uzeraan Turmoil/Mobiles/Uzeraan.cs | 434 + .../Quests/Uzeraan Turmoil/Objectives.cs | 638 + .../Uzeraan Turmoil/UzeraanTurmoilQuest.cs | 232 + .../Quests/Witch Apprentice/Conversations.cs | 732 ++ .../Quests/Witch Apprentice/Ingredient.cs | 112 + .../Quests/Witch Apprentice/Items/Cauldron.cs | 37 + .../Items/HagApprenticeCorpse.cs | 109 + .../Witch Apprentice/Items/HangoverCure.cs | 90 + .../Witch Apprentice/Items/MagicFlute.cs | 77 + .../Witch Apprentice/Items/MoonfireBrew.cs | 34 + .../Witch Apprentice/Mobiles/Blackheart.cs | 120 + .../Witch Apprentice/Mobiles/Grizelda.cs | 220 + .../Witch Apprentice/Mobiles/Zeefzorpul.cs | 49 + .../Quests/Witch Apprentice/Objectives.cs | 528 + .../Witch Apprentice/WitchApprenticeQuest.cs | 141 + Scripts/Engines/RemoteAdmin/Network.cs | 254 + Scripts/Engines/RemoteAdmin/PacketHandlers.cs | 173 + Scripts/Engines/RemoteAdmin/Packets.cs | 145 + .../Reports/Objects/Charts/BarGraph.cs | 311 + .../Engines/Reports/Objects/Charts/Chart.cs | 44 + .../Reports/Objects/Charts/ChartItem.cs | 46 + .../Objects/Charts/ChartItemCollection.cs | 205 + .../Reports/Objects/Charts/PieChart.cs | 47 + .../Reports/Objects/Reports/ItemValue.cs | 53 + .../Objects/Reports/ItemValueCollection.cs | 210 + .../Engines/Reports/Objects/Reports/Report.cs | 74 + .../Reports/Objects/Reports/ReportColumn.cs | 55 + .../Objects/Reports/ReportColumnCollection.cs | 210 + .../Reports/Objects/Reports/ReportItem.cs | 39 + .../Objects/Reports/ReportItemCollection.cs | 215 + .../Reports/Objects/Snapshots/Snapshot.cs | 52 + .../Objects/Snapshots/SnapshotCollection.cs | 200 + .../Objects/Snapshots/SnapshotHistory.cs | 65 + .../Engines/Reports/Objects/Staffing/Info.cs | 103 + .../Reports/Objects/Staffing/PageInfo.cs | 257 + .../Objects/Staffing/PageInfoCollection.cs | 200 + .../Reports/Objects/Staffing/QueueStatus.cs | 50 + .../Objects/Staffing/QueueStatusCollection.cs | 200 + .../Reports/Objects/Staffing/ResponseInfo.cs | 59 + .../Staffing/ResponseInfoCollection.cs | 205 + .../Reports/Objects/Staffing/StaffHistory.cs | 406 + .../Reports/Persistance/PersistableObject.cs | 51 + .../PersistableObjectCollection.cs | 205 + .../Reports/Persistance/PersistableType.cs | 58 + .../Reports/Persistance/PersistanceReader.cs | 122 + .../Reports/Persistance/PersistanceWriter.cs | 129 + .../Reports/Rendering/BarGraphRenderer.cs | 923 ++ .../Reports/Rendering/ChartRenderer.cs | 71 + Scripts/Engines/Reports/Rendering/DataItem.cs | 110 + .../Engines/Reports/Rendering/HtmlRenderer.cs | 541 + .../Reports/Rendering/PieChartRenderer.cs | 252 + Scripts/Engines/Reports/Reports.cs | 420 + Scripts/Engines/Spawner/Spawner.cs | 777 ++ Scripts/Engines/Spawner/SpawnerGump.cs | 138 + Scripts/Engines/Spawner/SpawnerType.cs | 14 + .../Treasures of Tokuno/GreaterArtifacts.cs | 610 + .../Treasures of Tokuno/LesserArtifacts.cs | 785 ++ .../Treasures of Tokuno/TreasuresOfTokuno.cs | 506 + .../Engines/VeteranRewards/RewardCategory.cs | 28 + .../VeteranRewards/RewardChoiceGump.cs | 160 + .../VeteranRewards/RewardConfirmGump.cs | 69 + Scripts/Engines/VeteranRewards/RewardEntry.cs | 57 + Scripts/Engines/VeteranRewards/RewardList.cs | 22 + .../VeteranRewards/RewardNoticeGump.cs | 40 + .../Engines/VeteranRewards/RewardSystem.cs | 361 + Scripts/Engines/Virtues/Compassion.cs | 47 + Scripts/Engines/Virtues/Honor.cs | 412 + Scripts/Engines/Virtues/Justice.cs | 248 + Scripts/Engines/Virtues/Sacrifice.cs | 189 + Scripts/Engines/Virtues/Valor.cs | 138 + Scripts/Engines/Virtues/VirtueGump.cs | 182 + Scripts/Engines/Virtues/VirtueHelper.cs | 115 + Scripts/Engines/Virtues/VirtueInfoGump.cs | 104 + Scripts/Engines/Virtues/VirtueStatusGump.cs | 104 + Scripts/Gumps/AddDoorGump.cs | 125 + Scripts/Gumps/AddGump.cs | 257 + Scripts/Gumps/AdminGump.cs | 2933 +++++ Scripts/Gumps/BanDurationGump.cs | 244 + Scripts/Gumps/BaseGridGump.cs | 197 + Scripts/Gumps/BaseImageTileButtonsGump.cs | 131 + Scripts/Gumps/CategorizedAddGump.cs | 383 + Scripts/Gumps/ClientGump.cs | 302 + Scripts/Gumps/ConfirmReleaseGump.cs | 48 + Scripts/Gumps/Go/ChildNode.cs | 66 + Scripts/Gumps/Go/GoGump.cs | 358 + Scripts/Gumps/Go/LocationTree.cs | 67 + Scripts/Gumps/Go/ParentNode.cs | 79 + Scripts/Gumps/Guilds/DeclareFealtyGump.cs | 54 + Scripts/Gumps/Guilds/GrantGuildTitleGump.cs | 57 + Scripts/Gumps/Guilds/GuildAbbrvPrompt.cs | 55 + Scripts/Gumps/Guilds/GuildAcceptWarGump.cs | 67 + .../Gumps/Guilds/GuildAdminCandidatesGump.cs | 126 + Scripts/Gumps/Guilds/GuildCandidatesGump.cs | 34 + Scripts/Gumps/Guilds/GuildChangeTypeGump.cs | 84 + Scripts/Gumps/Guilds/GuildCharterGump.cs | 73 + Scripts/Gumps/Guilds/GuildCharterPrompt.cs | 48 + Scripts/Gumps/Guilds/GuildDeclarePeaceGump.cs | 64 + Scripts/Gumps/Guilds/GuildDeclareWarGump.cs | 88 + Scripts/Gumps/Guilds/GuildDeclareWarPrompt.cs | 59 + Scripts/Gumps/Guilds/GuildDismissGump.cs | 62 + Scripts/Gumps/Guilds/GuildGump.cs | 225 + Scripts/Gumps/Guilds/GuildListGump.cs | 67 + Scripts/Gumps/Guilds/GuildMobileListGump.cs | 68 + Scripts/Gumps/Guilds/GuildNamePrompt.cs | 55 + Scripts/Gumps/Guilds/GuildRejectWarGump.cs | 64 + .../Guilds/GuildRescindDeclarationGump.cs | 64 + Scripts/Gumps/Guilds/GuildRosterGump.cs | 34 + Scripts/Gumps/Guilds/GuildTitlePrompt.cs | 50 + Scripts/Gumps/Guilds/GuildWarAdminGump.cs | 121 + Scripts/Gumps/Guilds/GuildWarGump.cs | 116 + Scripts/Gumps/Guilds/GuildWebsitePrompt.cs | 45 + Scripts/Gumps/Guilds/GuildmasterGump.cs | 184 + .../Guilds/New Guild System/AdvancedSearch.cs | 75 + .../Guilds/New Guild System/BaseGuildGump.cs | 144 + .../New Guild System/BaseGuildListGump.cs | 209 + .../New Guild System/Create Guild Gump.cs | 101 + .../Guilds/New Guild System/DiplomacyGump.cs | 280 + .../Guilds/New Guild System/GuildInfoGump.cs | 175 + .../GuildInvitationRequest.cs | 63 + .../New Guild System/GuildMemberInfoGump.cs | 231 + .../New Guild System/GuildRosterGump.cs | 266 + .../Guilds/New Guild System/OtherGuildInfo.cs | 602 + .../New Guild System/War Declaration gump.cs | 130 + Scripts/Gumps/Guilds/RecruitTarget.cs | 95 + Scripts/Gumps/HouseDemolishGump.cs | 138 + Scripts/Gumps/HouseGump.cs | 755 ++ Scripts/Gumps/HouseGumpAOS.cs | 1431 ++ Scripts/Gumps/HouseTransferGump.cs | 66 + Scripts/Gumps/NoticeGump.cs | 48 + Scripts/Gumps/PetResurrectGump.cs | 69 + Scripts/Gumps/PlayerVendorGumps.cs | 960 ++ Scripts/Gumps/PolymorphGump.cs | 249 + Scripts/Gumps/Properties/PropsConfig.cs | 42 + Scripts/Gumps/Properties/PropsGump.cs | 758 ++ Scripts/Gumps/Properties/SetBodyGump.cs | 292 + Scripts/Gumps/Properties/SetCustomEnumGump.cs | 50 + Scripts/Gumps/Properties/SetGump.cs | 279 + Scripts/Gumps/Properties/SetListOptionGump.cs | 187 + Scripts/Gumps/Properties/SetObjectGump.cs | 307 + Scripts/Gumps/Properties/SetObjectTarget.cs | 66 + Scripts/Gumps/Properties/SetPoint2DGump.cs | 237 + Scripts/Gumps/Properties/SetPoint3DGump.cs | 243 + Scripts/Gumps/Properties/SetTimeSpanGump.cs | 239 + Scripts/Gumps/ReclaimVendorGump.cs | 79 + Scripts/Gumps/ReportMurderer.cs | 169 + Scripts/Gumps/ResurrectGump.cs | 225 + Scripts/Gumps/RunebookGump.cs | 433 + Scripts/Gumps/SetSecureLevelGump.cs | 93 + Scripts/Gumps/SkillsGump.cs | 546 + Scripts/Gumps/TithingGump.cs | 125 + Scripts/Gumps/VendorInventoryGump.cs | 119 + Scripts/Gumps/VendorRentalGumps.cs | 628 + Scripts/Gumps/ViewHousesGump.cs | 302 + Scripts/Gumps/WarningGump.cs | 54 + Scripts/Gumps/WhoGump.cs | 311 + Scripts/Gumps/YoungGumps.cs | 98 + Scripts/Items/Addons/AbbatoirAddon.cs | 71 + Scripts/Items/Addons/AddonComponent.cs | 285 + .../Items/Addons/AlchemistTableEastAddon.cs | 63 + .../Items/Addons/AlchemistTableSouthAddon.cs | 63 + Scripts/Items/Addons/AnvilEastAddon.cs | 63 + Scripts/Items/Addons/AnvilSouthAddon.cs | 63 + .../Items/Addons/ArcaneBookshelfEastAddon.cs | 64 + .../Items/Addons/ArcaneBookshelfSouthAddon.cs | 64 + Scripts/Items/Addons/ArcaneCircleAddon.cs | 71 + .../Items/Addons/ArcanistStatueEastAddon.cs | 63 + .../Items/Addons/ArcanistStatueSouthAddon.cs | 63 + Scripts/Items/Addons/ArcheryButteAddon.cs | 367 + Scripts/Items/Addons/BallotBox.cs | 391 + Scripts/Items/Addons/BaseAddon.cs | 293 + Scripts/Items/Addons/BaseAddonDeed.cs | 114 + Scripts/Items/Addons/BearRugs.cs | 269 + Scripts/Items/Addons/BloodPentagram.cs | 70 + Scripts/Items/Addons/DartBoard.cs | 218 + Scripts/Items/Addons/ElvenBedEastAddon.cs | 64 + Scripts/Items/Addons/ElvenBedSouthAddon.cs | 64 + Scripts/Items/Addons/ElvenDresserEastAddon.cs | 64 + .../Items/Addons/ElvenDresserSouthAddon.cs | 64 + Scripts/Items/Addons/ElvenForgeAddon.cs | 63 + .../Items/Addons/ElvenLoveseatEastAddon.cs | 64 + .../Items/Addons/ElvenLoveseatSouthAddon.cs | 64 + .../Addons/ElvenSpinningwheelEastAddon.cs | 137 + .../Addons/ElvenSpinningwheelSouthAddon.cs | 139 + Scripts/Items/Addons/ElvenStoveEastAddon.cs | 63 + Scripts/Items/Addons/ElvenStoveSouthAddon.cs | 63 + .../Items/Addons/ElvenWashbasinEastAddon.cs | 64 + .../Items/Addons/ElvenWashbasinSouthAddon.cs | 64 + .../Items/Addons/FancyElvenTableEastAddon.cs | 65 + .../Items/Addons/FancyElvenTableSouthAddon.cs | 65 + Scripts/Items/Addons/FireColumnAddon.cs | 67 + Scripts/Items/Addons/FlourMillEastAddon.cs | 230 + Scripts/Items/Addons/FlourMillSouthAddon.cs | 216 + Scripts/Items/Addons/FlowerTapestries.cs | 241 + Scripts/Items/Addons/GiantWebs.cs | 207 + Scripts/Items/Addons/GozaMats.cs | 474 + .../Addons/GrayBrickFireplaceEastAddon.cs | 64 + .../Addons/GrayBrickFireplaceSouthAddon.cs | 64 + Scripts/Items/Addons/JackOLantern.cs | 88 + Scripts/Items/Addons/LargeBedEastAddon.cs | 66 + Scripts/Items/Addons/LargeBedSouthAddon.cs | 66 + Scripts/Items/Addons/LargeForgeEastAddon.cs | 66 + Scripts/Items/Addons/LargeForgeSouthAddon.cs | 66 + .../Items/Addons/LargeStoneTableEastAddon.cs | 73 + .../Items/Addons/LargeStoneTableSouthAddon.cs | 73 + Scripts/Items/Addons/LoomEastAddon.cs | 84 + Scripts/Items/Addons/LoomSouthAddon.cs | 79 + .../Items/Addons/MediumStoneTableEastAddon.cs | 72 + .../Addons/MediumStoneTableSouthAddon.cs | 72 + .../Items/Addons/OrnateElvenTableEastAddon.cs | 65 + .../Addons/OrnateElvenTableSouthAddon.cs | 65 + Scripts/Items/Addons/ParrotPerchAddon.cs | 63 + Scripts/Items/Addons/PentagramAddon.cs | 71 + Scripts/Items/Addons/PickpocketDips.cs | 274 + Scripts/Items/Addons/PyramidAddon.cs | 63 + Scripts/Items/Addons/RejuvinationAnkhs.cs | 180 + Scripts/Items/Addons/SHTeleporter.cs | 453 + .../Addons/SandstoneFireplaceEastAddon.cs | 64 + .../Addons/SandstoneFireplaceSouthAddon.cs | 64 + .../Items/Addons/SandstoneFountainAddon.cs | 54 + Scripts/Items/Addons/SerpentPillarAddon.cs | 54 + Scripts/Items/Addons/ShrineOfWisdomAddon.cs | 65 + Scripts/Items/Addons/SkullPileAddon.cs | 44 + Scripts/Items/Addons/SmallBedEastAddon.cs | 64 + Scripts/Items/Addons/SmallBedSouthAddon.cs | 64 + Scripts/Items/Addons/SmallForgeAddon.cs | 63 + .../Items/Addons/SpinningwheelEastAddon.cs | 147 + .../Items/Addons/SpinningwheelSouthAddon.cs | 139 + .../Items/Addons/SquirrelStatueEastAddon.cs | 63 + .../Items/Addons/SquirrelStatueSouthAddon.cs | 63 + .../Items/Addons/StoneFireplaceEastAddon.cs | 64 + .../Items/Addons/StoneFireplaceSouthAddon.cs | 64 + Scripts/Items/Addons/StoneFountainAddon.cs | 54 + Scripts/Items/Addons/StoneOvenEastAddon.cs | 64 + Scripts/Items/Addons/StoneOvenSouthAddon.cs | 64 + Scripts/Items/Addons/StretchedHides.cs | 237 + Scripts/Items/Addons/TallElvenBedEastAddon.cs | 66 + .../Items/Addons/TallElvenBedSouthAddon.cs | 66 + Scripts/Items/Addons/Telescope.cs | 106 + Scripts/Items/Addons/TrainingDummies.cs | 284 + .../Items/Addons/WarriorStatueEastAddon.cs | 63 + .../Items/Addons/WarriorStatueSouthAddon.cs | 63 + Scripts/Items/Addons/WaterTroughEastAddon.cs | 70 + Scripts/Items/Addons/WaterTroughSouthAddon.cs | 70 + Scripts/Items/Addons/WaterVat.cs | 87 + Scripts/Items/Armor/ArmorEnums.cs | 64 + .../Items/Armor/Artifacts/ArmorOfFortune.cs | 42 + .../Armor/Artifacts/Craftable/BrambleCoat.cs | 46 + .../Artifacts/Craftable/IronwoodCrown.cs | 46 + .../Artifacts/Craftable/SongWovenMantle.cs | 43 + .../Artifacts/Craftable/SpellWovenBritches.cs | 43 + .../Artifacts/Craftable/StitchersMittens.cs | 43 + .../Armor/Artifacts/GauntletsOfNobility.cs | 53 + .../Items/Armor/Artifacts/HelmOfInsight.cs | 47 + .../Armor/Artifacts/HolyKnightsBreastplate.cs | 45 + .../Armor/Artifacts/InquisitorsResolution.cs | 50 + .../Items/Armor/Artifacts/JackalsCollar.cs | 52 + .../Items/Armor/Artifacts/LeggingsOfBane.cs | 54 + .../Items/Armor/Artifacts/MidnightBracers.cs | 46 + .../Artifacts/OrnateCrownOfTheHarrower.cs | 51 + .../Armor/Artifacts/ShadowDancerLeggings.cs | 55 + Scripts/Items/Armor/Artifacts/TunicOfFire.cs | 56 + .../Armor/Artifacts/VoiceOfTheFallenKing.cs | 53 + Scripts/Items/Armor/BaseArmor.cs | 1606 +++ Scripts/Items/Armor/Bone/BoneArms.cs | 54 + Scripts/Items/Armor/Bone/BoneChest.cs | 54 + Scripts/Items/Armor/Bone/BoneGloves.cs | 54 + Scripts/Items/Armor/Bone/BoneLegs.cs | 51 + Scripts/Items/Armor/Chain/ChainChest.cs | 49 + Scripts/Items/Armor/Chain/ChainHatsuburi.cs | 46 + Scripts/Items/Armor/Chain/ChainLegs.cs | 49 + Scripts/Items/Armor/DaemonBone/DaemonArms.cs | 63 + Scripts/Items/Armor/DaemonBone/DaemonChest.cs | 63 + .../Items/Armor/DaemonBone/DaemonGloves.cs | 63 + Scripts/Items/Armor/DaemonBone/DaemonLegs.cs | 58 + Scripts/Items/Armor/Dragon/DragonArms.cs | 53 + Scripts/Items/Armor/Dragon/DragonChest.cs | 53 + Scripts/Items/Armor/Dragon/DragonGloves.cs | 53 + Scripts/Items/Armor/Dragon/DragonHelm.cs | 53 + Scripts/Items/Armor/Dragon/DragonLegs.cs | 50 + Scripts/Items/Armor/Helmets/Bascinet.cs | 49 + Scripts/Items/Armor/Helmets/BoneHelm.cs | 50 + Scripts/Items/Armor/Helmets/ChainCoif.cs | 47 + Scripts/Items/Armor/Helmets/Circlet.cs | 53 + Scripts/Items/Armor/Helmets/CloseHelm.cs | 49 + Scripts/Items/Armor/Helmets/DaemonHelm.cs | 61 + Scripts/Items/Armor/Helmets/GemmedCirclet.cs | 53 + Scripts/Items/Armor/Helmets/Helmet.cs | 49 + Scripts/Items/Armor/Helmets/LeafTonlet.cs | 53 + Scripts/Items/Armor/Helmets/LeatherCap.cs | 53 + Scripts/Items/Armor/Helmets/NorseHelm.cs | 49 + Scripts/Items/Armor/Helmets/OrcHelm.cs | 49 + Scripts/Items/Armor/Helmets/PlateHelm.cs | 51 + Scripts/Items/Armor/Helmets/RavenHelm.cs | 51 + Scripts/Items/Armor/Helmets/RoyalCirclet.cs | 53 + Scripts/Items/Armor/Helmets/VultureHelm.cs | 51 + Scripts/Items/Armor/Helmets/WingedHelm.cs | 51 + .../Items/Armor/Leather/FemaleLeafChest.cs | 54 + .../Items/Armor/Leather/FemaleLeatherChest.cs | 52 + Scripts/Items/Armor/Leather/LeafArms.cs | 53 + Scripts/Items/Armor/Leather/LeafChest.cs | 53 + Scripts/Items/Armor/Leather/LeafGloves.cs | 140 + Scripts/Items/Armor/Leather/LeafGorget.cs | 52 + Scripts/Items/Armor/Leather/LeafLegs.cs | 54 + Scripts/Items/Armor/Leather/LeatherArms.cs | 53 + .../Items/Armor/Leather/LeatherBustierArms.cs | 52 + Scripts/Items/Armor/Leather/LeatherChest.cs | 53 + Scripts/Items/Armor/Leather/LeatherDo.cs | 49 + Scripts/Items/Armor/Leather/LeatherGloves.cs | 139 + Scripts/Items/Armor/Leather/LeatherGorget.cs | 49 + Scripts/Items/Armor/Leather/LeatherHaidate.cs | 50 + .../Items/Armor/Leather/LeatherHiroSode.cs | 49 + Scripts/Items/Armor/Leather/LeatherJingasa.cs | 49 + Scripts/Items/Armor/Leather/LeatherLegs.cs | 50 + Scripts/Items/Armor/Leather/LeatherMempo.cs | 49 + .../Items/Armor/Leather/LeatherNinjaHood.cs | 49 + .../Items/Armor/Leather/LeatherNinjaJacket.cs | 49 + .../Items/Armor/Leather/LeatherNinjaMitts.cs | 49 + .../Items/Armor/Leather/LeatherNinjaPants.cs | 49 + Scripts/Items/Armor/Leather/LeatherShorts.cs | 52 + Scripts/Items/Armor/Leather/LeatherSkirt.cs | 55 + Scripts/Items/Armor/Leather/LeatherSuneate.cs | 49 + .../Armor/Plate/DecorativePlateKabuto.cs | 46 + Scripts/Items/Armor/Plate/FemalePlateChest.cs | 54 + .../Items/Armor/Plate/FemaleWoodlandChest.cs | 52 + .../Items/Armor/Plate/HeavyPlateJingasa.cs | 46 + .../Items/Armor/Plate/LightPlateJingasa.cs | 46 + Scripts/Items/Armor/Plate/PlateArms.cs | 52 + .../Items/Armor/Plate/PlateBattleKabuto.cs | 46 + Scripts/Items/Armor/Plate/PlateChest.cs | 52 + Scripts/Items/Armor/Plate/PlateDo.cs | 46 + Scripts/Items/Armor/Plate/PlateGloves.cs | 52 + Scripts/Items/Armor/Plate/PlateGorget.cs | 48 + Scripts/Items/Armor/Plate/PlateHaidate.cs | 46 + Scripts/Items/Armor/Plate/PlateHatsuburi.cs | 46 + Scripts/Items/Armor/Plate/PlateHiroSode.cs | 46 + Scripts/Items/Armor/Plate/PlateLegs.cs | 49 + Scripts/Items/Armor/Plate/PlateMempo.cs | 46 + Scripts/Items/Armor/Plate/PlateSuneate.cs | 46 + .../Items/Armor/Plate/SmallPlateJingasa.cs | 46 + .../Items/Armor/Plate/StandardPlateKabuto.cs | 46 + Scripts/Items/Armor/Plate/WoodlandArms.cs | 49 + Scripts/Items/Armor/Plate/WoodlandChest.cs | 49 + Scripts/Items/Armor/Plate/WoodlandGloves.cs | 49 + Scripts/Items/Armor/Plate/WoodlandGorget.cs | 49 + Scripts/Items/Armor/Plate/WoodlandLegs.cs | 49 + Scripts/Items/Armor/Ranger/RangerArms.cs | 54 + Scripts/Items/Armor/Ranger/RangerChest.cs | 54 + Scripts/Items/Armor/Ranger/RangerGloves.cs | 51 + Scripts/Items/Armor/Ranger/RangerGorget.cs | 50 + Scripts/Items/Armor/Ranger/RangerLegs.cs | 54 + Scripts/Items/Armor/Ring/RingmailArms.cs | 52 + Scripts/Items/Armor/Ring/RingmailChest.cs | 52 + Scripts/Items/Armor/Ring/RingmailGloves.cs | 52 + Scripts/Items/Armor/Ring/RingmailLegs.cs | 49 + .../Items/Armor/Studded/FemaleStuddedChest.cs | 55 + Scripts/Items/Armor/Studded/HideChest.cs | 54 + .../Items/Armor/Studded/HideFemaleChest.cs | 56 + Scripts/Items/Armor/Studded/HideGloves.cs | 54 + Scripts/Items/Armor/Studded/HideGorget.cs | 55 + Scripts/Items/Armor/Studded/HidePants.cs | 54 + Scripts/Items/Armor/Studded/HidePauldrons.cs | 54 + Scripts/Items/Armor/Studded/StuddedArms.cs | 53 + .../Items/Armor/Studded/StuddedBustierArms.cs | 52 + Scripts/Items/Armor/Studded/StuddedChest.cs | 53 + Scripts/Items/Armor/Studded/StuddedDo.cs | 47 + Scripts/Items/Armor/Studded/StuddedGloves.cs | 53 + Scripts/Items/Armor/Studded/StuddedGorget.cs | 52 + Scripts/Items/Armor/Studded/StuddedHaidate.cs | 47 + .../Items/Armor/Studded/StuddedHiroSode.cs | 47 + Scripts/Items/Armor/Studded/StuddedLegs.cs | 53 + Scripts/Items/Armor/Studded/StuddedMempo.cs | 47 + Scripts/Items/Armor/Studded/StuddedSuneate.cs | 47 + Scripts/Items/Body Parts/BonePile.cs | 45 + Scripts/Items/Body Parts/Head.cs | 128 + Scripts/Items/Body Parts/LeftArm.cs | 31 + Scripts/Items/Body Parts/LeftLeg.cs | 31 + Scripts/Items/Body Parts/RibCage.cs | 45 + Scripts/Items/Body Parts/RightArm.cs | 31 + Scripts/Items/Body Parts/RightLeg.cs | 31 + Scripts/Items/Body Parts/Torso.cs | 32 + Scripts/Items/Books/BaseBook.cs | 466 + Scripts/Items/Books/BlueBook.cs | 46 + Scripts/Items/Books/BrownBook.cs | 46 + .../Books/Defined/BlackthornWelcomeBook.cs | 297 + Scripts/Items/Books/Defined/BookContent.cs | 61 + Scripts/Items/Books/Defined/DrakovsJournal.cs | 129 + Scripts/Items/Books/Defined/FropozJournal.cs | 182 + Scripts/Items/Books/Defined/KaburJournal.cs | 267 + Scripts/Items/Books/Defined/LibraryBooks.cs | 6211 +++++++++ .../Defined/TranslatedGargoyleJournal.cs | 164 + Scripts/Items/Books/RedBook.cs | 46 + Scripts/Items/Books/TanBook.cs | 46 + .../Clothing/Artifacts/DivineCountenance.cs | 61 + .../Items/Clothing/Artifacts/HatOfTheMagi.cs | 56 + .../Clothing/Artifacts/HuntersHeaddress.cs | 56 + .../Clothing/Artifacts/SpiritOfTheTotem.cs | 54 + Scripts/Items/Clothing/BaseClothing.cs | 998 ++ Scripts/Items/Clothing/Cloaks.cs | 306 + Scripts/Items/Clothing/Hats.cs | 1034 ++ Scripts/Items/Clothing/MiddleTorso.cs | 303 + Scripts/Items/Clothing/OuterLegs.cs | 168 + Scripts/Items/Clothing/OuterTorso.cs | 665 + Scripts/Items/Clothing/Pants.cs | 167 + Scripts/Items/Clothing/Shirts.cs | 204 + Scripts/Items/Clothing/Shoes.cs | 450 + Scripts/Items/Clothing/Waist.cs | 133 + Scripts/Items/Construction/Ankhs.cs | 433 + Scripts/Items/Construction/Chairs/Benchs.cs | 33 + Scripts/Items/Construction/Chairs/Chairs.cs | 246 + Scripts/Items/Construction/Chairs/Stools.cs | 66 + Scripts/Items/Construction/Chairs/Thrones.cs | 68 + .../Decorative/DecorativeShield.cs | 426 + .../Decorative/DecorativeWeapon.cs | 546 + .../Decorative/PaintingPortraits.cs | 200 + .../Items/Construction/Decorative/Tapestry.cs | 1192 ++ Scripts/Items/Construction/Doors/BaseDoor.cs | 590 + Scripts/Items/Construction/Doors/Doors.cs | 359 + .../Items/Construction/Doors/HouseDoors.cs | 253 + .../Items/Construction/Doors/Portcullis.cs | 60 + .../Items/Construction/Doors/SecretDoors.cs | 160 + Scripts/Items/Construction/Floors/Floors.cs | 628 + .../Items/Construction/Misc/BarrelParts.cs | 113 + Scripts/Items/Construction/Misc/Easle.cs | 36 + Scripts/Items/Construction/Misc/MeltedWax.cs | 34 + Scripts/Items/Construction/Misc/MusicStand.cs | 68 + Scripts/Items/Construction/Misc/Obelisk.cs | 33 + Scripts/Items/Construction/Misc/Screens.cs | 65 + Scripts/Items/Construction/Misc/Statues.cs | 350 + Scripts/Items/Construction/Misc/Vase.cs | 85 + Scripts/Items/Construction/Misc/Vines.cs | 42 + .../Construction/Ruined/RuinedItemSingle.cs | 315 + Scripts/Items/Construction/Signs/BaseSign.cs | 31 + .../Items/Construction/Signs/LocalizedSign.cs | 56 + Scripts/Items/Construction/Signs/Sign.cs | 107 + Scripts/Items/Construction/Tables/Tables.cs | 159 + .../Items/Construction/Tables/WritingTable.cs | 36 + Scripts/Items/Construction/Walls/BaseWall.cs | 30 + .../Items/Construction/Walls/DarkWoodWall.cs | 54 + .../Construction/Walls/ThickGrayStoneWall.cs | 64 + .../Items/Construction/Walls/ThinBrickWall.cs | 70 + .../Items/Construction/Walls/ThinStoneWall.cs | 59 + .../Construction/Walls/WhiteStoneWall.cs | 77 + Scripts/Items/Containers/BaseTreasureChest.cs | 249 + Scripts/Items/Containers/Container.cs | 891 ++ .../Items/Containers/FillableContainers.cs | 1502 +++ .../Items/Containers/FurnitureContainer.cs | 428 + Scripts/Items/Containers/LockableContainer.cs | 376 + Scripts/Items/Containers/MarkContainer.cs | 267 + Scripts/Items/Containers/ParagonChest.cs | 207 + Scripts/Items/Containers/Strongbox.cs | 167 + Scripts/Items/Containers/TrapableContainer.cs | 258 + Scripts/Items/Containers/TreasureChest.cs | 89 + Scripts/Items/Containers/TreasureMapChest.cs | 508 + .../BaseDecorationArtifact.cs | 79 + .../DoomDecorationArtifacts.cs | 698 + .../SEDecorationArtifacts.cs | 1627 +++ .../StealableArtifactsSpawner.cs | 420 + Scripts/Items/Deeds/BarkeepContract.cs | 96 + Scripts/Items/Deeds/ClothingBlessDeed.cs | 102 + Scripts/Items/Deeds/CommodityDeed.cs | 199 + Scripts/Items/Deeds/DragonBardingDeed.cs | 149 + Scripts/Items/Deeds/HairRestylingDeed.cs | 147 + Scripts/Items/Deeds/HolidayTreeDeed.cs | 161 + Scripts/Items/Deeds/NameChangeDeed.cs | 46 + Scripts/Items/Deeds/NewPlayerTicket.cs | 198 + Scripts/Items/Deeds/VendorRentalContract.cs | 368 + Scripts/Items/Facial/Beard.cs | 330 + Scripts/Items/Facial/Hair.cs | 491 + Scripts/Items/Farming/FarmableCabbage.cs | 51 + Scripts/Items/Farming/FarmableCarrot.cs | 51 + Scripts/Items/Farming/FarmableCotton.cs | 47 + Scripts/Items/Farming/FarmableCrop.cs | 94 + Scripts/Items/Farming/FarmableFlax.cs | 51 + Scripts/Items/Farming/FarmableLettuce.cs | 51 + Scripts/Items/Farming/FarmableOnion.cs | 51 + Scripts/Items/Farming/FarmablePumpkin.cs | 53 + Scripts/Items/Farming/FarmableTurnip.cs | 51 + Scripts/Items/Farming/FarmableWheat.cs | 47 + Scripts/Items/Food/Asian.cs | 355 + Scripts/Items/Food/Beverage.cs | 1162 ++ Scripts/Items/Food/BeverageEmpty.cs | 59 + Scripts/Items/Food/Bowls.cs | 532 + Scripts/Items/Food/CookableFood.cs | 893 ++ Scripts/Items/Food/Cooking.cs | 664 + Scripts/Items/Food/Food.cs | 1264 ++ Scripts/Items/Food/Fruits.cs | 632 + Scripts/Items/Food/Vegetables.cs | 211 + Scripts/Items/Games/Backgammon.cs | 55 + Scripts/Items/Games/BaseBoard.cs | 169 + Scripts/Items/Games/BasePiece.cs | 105 + Scripts/Items/Games/CheckerBoard.cs | 44 + Scripts/Items/Games/CheckersPieces.cs | 61 + Scripts/Items/Games/ChessPieces.cs | 341 + Scripts/Items/Games/Chessboard.cs | 69 + Scripts/Items/Games/Dices.cs | 39 + .../Games/Mahjong/MahjongDealerIndicator.cs | 73 + Scripts/Items/Games/Mahjong/MahjongDices.cs | 52 + Scripts/Items/Games/Mahjong/MahjongEnums.cs | 58 + Scripts/Items/Games/Mahjong/MahjongGame.cs | 302 + .../Games/Mahjong/MahjongPacketHandlers.cs | 245 + .../Items/Games/Mahjong/MahjongPieceDim.cs | 50 + Scripts/Items/Games/Mahjong/MahjongPlayers.cs | 552 + Scripts/Items/Games/Mahjong/MahjongTile.cs | 97 + .../Games/Mahjong/MahjongTileTypeGenerator.cs | 35 + .../Mahjong/MahjongWallBreakIndicator.cs | 58 + Scripts/Items/Games/Mahjong/Packets.cs | 208 + Scripts/Items/Gems/Amber.cs | 45 + Scripts/Items/Gems/Amethyst.cs | 45 + Scripts/Items/Gems/Citrine.cs | 45 + Scripts/Items/Gems/Diamond.cs | 45 + Scripts/Items/Gems/Emerald.cs | 45 + Scripts/Items/Gems/Ruby.cs | 45 + Scripts/Items/Gems/Sapphire.cs | 45 + Scripts/Items/Gems/StarSapphire.cs | 45 + Scripts/Items/Gems/Tourmaline.cs | 45 + Scripts/Items/Guilds/GuildDeed.cs | 143 + Scripts/Items/Guilds/GuildTeleporter.cs | 105 + Scripts/Items/Guilds/Guildstone.cs | 379 + .../Jewels/Artifacts/BraceletOfHealth.cs | 37 + .../Artifacts/Craftable/EssenceOfBattle.cs | 36 + .../Artifacts/Craftable/PendantOfTheMagi.cs | 38 + .../Artifacts/Craftable/ResillientBracer.cs | 41 + .../Jewels/Artifacts/OrnamentOfTheMagician.cs | 43 + .../Jewels/Artifacts/RingOfTheElements.cs | 40 + .../Items/Jewels/Artifacts/RingOfTheVile.cs | 42 + Scripts/Items/Jewels/BaseJewel.cs | 353 + Scripts/Items/Jewels/Beads.cs | 31 + Scripts/Items/Jewels/Bracelet.cs | 85 + Scripts/Items/Jewels/Earrings.cs | 85 + Scripts/Items/Jewels/Necklace.cs | 167 + Scripts/Items/Jewels/Ring.cs | 85 + Scripts/Items/Lights/BaseEquipableLight.cs | 64 + Scripts/Items/Lights/BaseLight.cs | 233 + Scripts/Items/Lights/Brazier.cs | 36 + Scripts/Items/Lights/BrazierTall.cs | 36 + Scripts/Items/Lights/Candelabra.cs | 36 + Scripts/Items/Lights/CandelabraStand.cs | 36 + Scripts/Items/Lights/Candle.cs | 40 + Scripts/Items/Lights/CandleLarge.cs | 40 + Scripts/Items/Lights/CandleLong.cs | 40 + Scripts/Items/Lights/CandleShort.cs | 40 + Scripts/Items/Lights/CandleSkull.cs | 59 + Scripts/Items/Lights/DarkSource.cs | 30 + Scripts/Items/Lights/HangingLantern.cs | 37 + Scripts/Items/Lights/HeatingStand.cs | 60 + Scripts/Items/Lights/LampPost1.cs | 37 + Scripts/Items/Lights/LampPost2.cs | 37 + Scripts/Items/Lights/LampPost3.cs | 37 + Scripts/Items/Lights/Lantern.cs | 86 + Scripts/Items/Lights/LightSource.cs | 30 + Scripts/Items/Lights/PaperLantern.cs | 38 + Scripts/Items/Lights/RedHangingLantern.cs | 71 + Scripts/Items/Lights/RoundPaperLantern.cs | 38 + Scripts/Items/Lights/ShojiLantern.cs | 38 + Scripts/Items/Lights/Torch.cs | 62 + Scripts/Items/Lights/WallSconce.cs | 74 + Scripts/Items/Lights/WallTorch.cs | 74 + Scripts/Items/Lights/WhiteHangingLantern.cs | 71 + Scripts/Items/Maps/BlankMap.cs | 36 + Scripts/Items/Maps/CityMap.cs | 52 + Scripts/Items/Maps/IndecipherableMap.cs | 42 + Scripts/Items/Maps/LocalMap.cs | 42 + Scripts/Items/Maps/MapItem.cs | 384 + Scripts/Items/Maps/PresetMap.cs | 151 + Scripts/Items/Maps/SeaChart.cs | 52 + Scripts/Items/Maps/TreasureMap.cs | 932 ++ Scripts/Items/Maps/WorldMap.cs | 50 + .../Minor Artifacts/AdmiralsHeartyRum.cs | 34 + .../Items/Minor Artifacts/AlchemistsBauble.cs | 38 + .../Minor Artifacts/ArcticDeathDealer.cs | 50 + Scripts/Items/Minor Artifacts/BlazeOfDeath.cs | 51 + .../Items/Minor Artifacts/BowOfTheJukaKing.cs | 41 + .../Items/Minor Artifacts/BurglarsBandana.cs | 58 + .../Minor Artifacts/CandelabraOfSouls.cs | 33 + .../CaptainQuacklebushsCutlass.cs | 45 + .../Items/Minor Artifacts/CavortingClub.cs | 44 + Scripts/Items/Minor Artifacts/ColdBlood.cs | 50 + .../Items/Minor Artifacts/DreadPirateHat.cs | 61 + .../Minor Artifacts/EnchantedTitanLegBone.cs | 42 + .../Items/Minor Artifacts/GhostShipAnchor.cs | 37 + .../Minor Artifacts/GlovesOfThePugilist.cs | 42 + Scripts/Items/Minor Artifacts/GoldBricks.cs | 33 + Scripts/Items/Minor Artifacts/GwennosHarp.cs | 39 + .../Items/Minor Artifacts/HeartOfTheLion.cs | 47 + Scripts/Items/Minor Artifacts/IolosLute.cs | 40 + Scripts/Items/Minor Artifacts/LunaLance.cs | 42 + .../Items/Minor Artifacts/ML/AegisOfGrace.cs | 56 + .../Items/Minor Artifacts/ML/BladeDance.cs | 44 + .../Items/Minor Artifacts/ML/Bonesmasher.cs | 44 + .../Items/Minor Artifacts/ML/FeyLeggings.cs | 52 + .../Items/Minor Artifacts/ML/FleshRipper.cs | 47 + .../ML/MelisandesCorrodedHatchet.cs | 43 + .../Minor Artifacts/ML/PadsOfTheCuSidhe.cs | 33 + .../Items/Minor Artifacts/ML/RaedsGlory.cs | 45 + .../Minor Artifacts/ML/RighteousAnger.cs | 43 + .../Minor Artifacts/ML/RobeOfTheEclipse.cs | 39 + .../Minor Artifacts/ML/RobeOfTheEquinox.cs | 40 + .../Items/Minor Artifacts/ML/SoulSeeker.cs | 51 + Scripts/Items/Minor Artifacts/ML/TalonBite.cs | 48 + Scripts/Items/Minor Artifacts/NightsKiss.cs | 42 + .../NoxRangersHeavyCrossbow.cs | 50 + Scripts/Items/Minor Artifacts/OrcishVisage.cs | 46 + .../Minor Artifacts/PhillipsWoodenSteed.cs | 34 + Scripts/Items/Minor Artifacts/PixieSwatter.cs | 52 + .../Items/Minor Artifacts/PolarBearMask.cs | 54 + .../Minor Artifacts/SeahorseStatuette.cs | 34 + .../ShieldOfInvulnerability.cs | 53 + .../Minor Artifacts/ShipModelOfTheHMSCape.cs | 34 + Scripts/Items/Minor Artifacts/StaffOfPower.cs | 42 + .../Items/Minor Artifacts/VioletCourage.cs | 47 + .../Items/Minor Artifacts/WrathOfTheDryad.cs | 48 + Scripts/Items/Misc/ArcaneGem.cs | 201 + Scripts/Items/Misc/BankCheck.cs | 161 + Scripts/Items/Misc/Beeswax.cs | 40 + Scripts/Items/Misc/Blocker.cs | 110 + Scripts/Items/Misc/Blood.cs | 57 + Scripts/Items/Misc/Bola.cs | 198 + Scripts/Items/Misc/BolaBall.cs | 44 + Scripts/Items/Misc/BulletinBoards.cs | 614 + Scripts/Items/Misc/ClockworkAssembly.cs | 134 + Scripts/Items/Misc/CommunicationCrystals.cs | 483 + Scripts/Items/Misc/Corpses/Corpse.cs | 965 ++ .../Items/Misc/Corpses/CorpseNameAttribute.cs | 20 + Scripts/Items/Misc/Corpses/DecayedCorpse.cs | 115 + Scripts/Items/Misc/Corpses/Packets.cs | 125 + Scripts/Items/Misc/EffectController.cs | 336 + Scripts/Items/Misc/EffectItem.cs | 94 + Scripts/Items/Misc/ExecutionersCap.cs | 31 + Scripts/Items/Misc/FlipableAttribute.cs | 116 + Scripts/Items/Misc/GlassItems.cs | 1528 +++ Scripts/Items/Misc/Gold.cs | 76 + Scripts/Items/Misc/Guillotine.cs | 115 + Scripts/Items/Misc/HairDye.cs | 193 + Scripts/Items/Misc/IDurability.cs | 21 + Scripts/Items/Misc/InteriorDecorator.cs | 247 + Scripts/Items/Misc/Key.cs | 468 + Scripts/Items/Misc/KeyRing.cs | 206 + Scripts/Items/Misc/LOSBlocker.cs | 119 + Scripts/Items/Misc/Moonstone.cs | 203 + Scripts/Items/Misc/MoonstoneGate.cs | 85 + Scripts/Items/Misc/MorphItem.cs | 146 + Scripts/Items/Misc/OilCloth.cs | 142 + Scripts/Items/Misc/Origami.cs | 247 + Scripts/Items/Misc/PlayerBulletinBoards.cs | 640 + Scripts/Items/Misc/PlayerVendorDeed.cs | 104 + Scripts/Items/Misc/PoolOfAcid.cs | 161 + Scripts/Items/Misc/PowerCrystal.cs | 45 + Scripts/Items/Misc/PowerGenerator.cs | 571 + Scripts/Items/Misc/PromotionalToken.cs | 159 + Scripts/Items/Misc/PublicMoongate.cs | 467 + Scripts/Items/Misc/Rares.cs | 718 + Scripts/Items/Misc/ResGate.cs | 57 + Scripts/Items/Misc/Scales.cs | 93 + Scripts/Items/Misc/SerpentPillar.cs | 126 + Scripts/Items/Misc/SpecialBeardDye.cs | 186 + Scripts/Items/Misc/SpecialHairDye.cs | 191 + Scripts/Items/Misc/Static.cs | 99 + Scripts/Items/Misc/SwarmOfFlies.cs | 37 + Scripts/Items/Misc/Teleporter.cs | 494 + Scripts/Items/Misc/TrashBarrel.cs | 151 + Scripts/Items/Misc/TrashChest.cs | 61 + Scripts/Items/Misc/TribalBerry.cs | 46 + Scripts/Items/Misc/TribalPaint.cs | 83 + Scripts/Items/Misc/UnholyBone.cs | 117 + Scripts/Items/Misc/WarningItem.cs | 241 + Scripts/Items/Misc/Waypoint.cs | 161 + Scripts/Items/Misc/WindChimes.cs | 189 + .../Items/PlantsFlowers/Potted/EmptyPots.cs | 58 + .../PlantsFlowers/Potted/PottedCactus.cs | 166 + .../PlantsFlowers/Potted/PottedPlants.cs | 85 + .../Items/PlantsFlowers/Potted/PottedTrees.cs | 58 + Scripts/Items/Resources/Arrows/Arrow.cs | 52 + Scripts/Items/Resources/Arrows/Bolt.cs | 52 + Scripts/Items/Resources/Arrows/Feather.cs | 53 + Scripts/Items/Resources/Arrows/Shaft.cs | 53 + .../Items/Resources/Blacksmithing/Ingots.cs | 434 + Scripts/Items/Resources/Blacksmithing/Ore.cs | 585 + .../Items/Resources/Blacksmithing/Scales.cs | 275 + Scripts/Items/Resources/Fishing/BigFish.cs | 79 + Scripts/Items/Resources/Fishing/Fish.cs | 47 + Scripts/Items/Resources/Fishing/MagicFish.cs | 201 + Scripts/Items/Resources/Masonry/Granite.cs | 318 + Scripts/Items/Resources/MiscMLResources.cs | 1003 ++ .../Resources/Reagents/BagOfAllReagents.cs | 50 + .../Resources/Reagents/BagOfNecroReagents.cs | 42 + .../Items/Resources/Reagents/BagOfReagents.cs | 45 + .../Items/Resources/Reagents/BaseReagent.cs | 41 + Scripts/Items/Resources/Reagents/BatWing.cs | 47 + .../Items/Resources/Reagents/BlackPearl.cs | 47 + Scripts/Items/Resources/Reagents/Bloodmoss.cs | 47 + .../Items/Resources/Reagents/DaemonBlood.cs | 47 + .../Items/Resources/Reagents/DaemonBone.cs | 45 + Scripts/Items/Resources/Reagents/DeadWood.cs | 47 + Scripts/Items/Resources/Reagents/Garlic.cs | 47 + Scripts/Items/Resources/Reagents/Ginseng.cs | 48 + Scripts/Items/Resources/Reagents/GraveDust.cs | 47 + .../Items/Resources/Reagents/MandrakeRoot.cs | 47 + .../Items/Resources/Reagents/Nightshade.cs | 47 + .../Items/Resources/Reagents/NoxCrystal.cs | 47 + Scripts/Items/Resources/Reagents/PigIron.cs | 47 + .../Items/Resources/Reagents/SpidersSilk.cs | 47 + .../Items/Resources/Reagents/SulfurousAsh.cs | 47 + Scripts/Items/Resources/Tailor/BoltOfCloth.cs | 73 + Scripts/Items/Resources/Tailor/Bone.cs | 49 + Scripts/Items/Resources/Tailor/Cloth.cs | 78 + Scripts/Items/Resources/Tailor/Cotton.cs | 115 + Scripts/Items/Resources/Tailor/Flax.cs | 105 + Scripts/Items/Resources/Tailor/Hides.cs | 282 + Scripts/Items/Resources/Tailor/Leathers.cs | 246 + Scripts/Items/Resources/Tailor/UncutCloth.cs | 78 + Scripts/Items/Resources/Tailor/Wool.cs | 115 + .../Items/Resources/Tailor/YarnsAndThreads.cs | 235 + Scripts/Items/Shields/Artifacts/Aegis.cs | 47 + .../Items/Shields/Artifacts/ArcaneShield.cs | 46 + Scripts/Items/Shields/BaseShield.cs | 192 + Scripts/Items/Shields/BronzeShield.cs | 45 + Scripts/Items/Shields/Buckler.cs | 45 + Scripts/Items/Shields/ChaosShield.cs | 78 + Scripts/Items/Shields/HeaterShield.cs | 45 + Scripts/Items/Shields/MetalKiteShield.cs | 58 + Scripts/Items/Shields/MetalShield.cs | 45 + Scripts/Items/Shields/OrderShield.cs | 81 + Scripts/Items/Shields/WoodenKiteShield.cs | 48 + Scripts/Items/Shields/WoodenShield.cs | 45 + .../Blacksmith Items/Misc/AnvilForge.cs | 61 + .../Blacksmith Items/Misc/LargeForge.cs | 357 + Scripts/Items/Skill Items/Camping/Bedroll.cs | 130 + Scripts/Items/Skill Items/Camping/Campfire.cs | 216 + Scripts/Items/Skill Items/Camping/Kindling.cs | 120 + .../Skill Items/Carpenter Items/Board.cs | 54 + .../Carpenter Items/TaxidermyKit.cs | 462 + .../Items/Skill Items/Fishing/FishingPole.cs | 50 + .../Fishing/Misc/MessageInABottle.cs | 111 + Scripts/Items/Skill Items/Fishing/Misc/SOS.cs | 336 + .../Items/Skill Items/Fishing/Misc/Sextant.cs | 159 + .../Fishing/Misc/ShipwreckedItem.cs | 61 + .../Fishing/Misc/SpecialFishingNet.cs | 344 + .../Harvest Tools/BaseHarvestTool.cs | 244 + .../Harvest Tools/GargoylesPickaxe.cs | 59 + .../Harvest Tools/ProspectorsTool.cs | 173 + .../Items/Skill Items/Harvest Tools/Shovel.cs | 40 + .../Harvest Tools/SturdyPickaxe.cs | 59 + .../Skill Items/Harvest Tools/SturdyShovel.cs | 42 + Scripts/Items/Skill Items/Lumberjack/Log.cs | 50 + .../Skill Items/Magical/BookOfBushido.cs | 45 + .../Skill Items/Magical/BookOfChivalry.cs | 45 + .../Skill Items/Magical/BookOfNinjitsu.cs | 46 + .../Skill Items/Magical/Misc/BlankScroll.cs | 40 + .../Items/Skill Items/Magical/Misc/Bottle.cs | 40 + .../Skill Items/Magical/Misc/Moongate.cs | 457 + .../Skill Items/Magical/Misc/PotionKeg.cs | 326 + .../Skill Items/Magical/Misc/RecallRune.cs | 321 + .../Magical/NecromancerSpellbook.cs | 45 + .../Potions/Agility Potions/AgilityPotion.cs | 34 + .../Agility Potions/BaseAgilityPotion.cs | 57 + .../Agility Potions/GreaterAgilityPotion.cs | 34 + .../Skill Items/Magical/Potions/BasePotion.cs | 199 + .../Potions/Cure Potions/BaseCurePotion.cs | 107 + .../Potions/Cure Potions/CurePotion.cs | 50 + .../Potions/Cure Potions/GreaterCurePotion.cs | 51 + .../Potions/Cure Potions/LesserCurePotion.cs | 47 + .../Explosion Potions/BaseExplosionPotion.cs | 292 + .../Explosion Potions/ExplosionPotion.cs | 34 + .../GreaterExplosionPotion.cs | 34 + .../LesserExplosionPotion.cs | 34 + .../Potions/Heal Potions/BaseHealPotion.cs | 80 + .../Potions/Heal Potions/GreaterHealPotion.cs | 35 + .../Potions/Heal Potions/HealPotion.cs | 35 + .../Potions/Heal Potions/LesserHealPotion.cs | 35 + .../Skill Items/Magical/Potions/NightSight.cs | 51 + .../Poison Potions/BasePoisonPotion.cs | 49 + .../Poison Potions/DeadlyPoisonPotion.cs | 36 + .../Poison Potions/GreaterPoisonPotion.cs | 36 + .../Poison Potions/LesserPoisonPotion.cs | 36 + .../Potions/Poison Potions/PoisonPotion.cs | 36 + .../Refresh Potions/BaseRefreshPotion.cs | 48 + .../Potions/Refresh Potions/RefreshPotion.cs | 33 + .../Refresh Potions/TotalRefreshPotion.cs | 33 + .../Strength Potions/BaseStrengthPotion.cs | 57 + .../Strength Potions/GreaterStrengthPotion.cs | 34 + .../Strength Potions/StrengthPotion.cs | 34 + Scripts/Items/Skill Items/Magical/Runebook.cs | 523 + .../Scrolls/Eighth Circle/EarthquakeScroll.cs | 39 + .../Eighth Circle/EnergyVortexScroll.cs | 39 + .../Eighth Circle/ResurrectionScroll.cs | 39 + .../Eighth Circle/SummonAirElementalScroll.cs | 39 + .../Eighth Circle/SummonDaemonScroll.cs | 39 + .../SummonEarthElementalScroll.cs | 39 + .../SummonFireElementalScroll.cs | 39 + .../SummonWaterElementalScroll.cs | 39 + .../Fifth Circle/BladeSpiritsScroll.cs | 39 + .../Scrolls/Fifth Circle/DispelFieldScroll.cs | 39 + .../Scrolls/Fifth Circle/IncognitoScroll.cs | 39 + .../Fifth Circle/MagicReflectScroll.cs | 39 + .../Scrolls/Fifth Circle/MindBlastScroll.cs | 39 + .../Scrolls/Fifth Circle/ParalyzeScroll.cs | 39 + .../Scrolls/Fifth Circle/PoisonFieldScroll.cs | 39 + .../Fifth Circle/SummonCreatureScroll.cs | 39 + .../Scrolls/First Circle/ClumsyScroll.cs | 39 + .../Scrolls/First Circle/CreateFoodScroll.cs | 39 + .../Scrolls/First Circle/FeeblemindScroll.cs | 39 + .../Scrolls/First Circle/HealScroll.cs | 39 + .../Scrolls/First Circle/MagicArrowScroll.cs | 39 + .../Scrolls/First Circle/NightSightScroll.cs | 39 + .../First Circle/ReactiveArmorScroll.cs | 39 + .../Scrolls/First Circle/WeakenScroll.cs | 39 + .../Fourth Circle/ArchProtectionScroll.cs | 39 + .../Scrolls/Fourth Circle/ArchcureScroll.cs | 39 + .../Scrolls/Fourth Circle/CurseScroll.cs | 39 + .../Scrolls/Fourth Circle/FireFieldScroll.cs | 39 + .../Fourth Circle/GreaterHealScroll.cs | 39 + .../Scrolls/Fourth Circle/LightningScroll.cs | 39 + .../Scrolls/Fourth Circle/ManaDrainScroll.cs | 39 + .../Scrolls/Fourth Circle/RecallScroll.cs | 39 + .../Scrolls/Necromancy/AnimateDeadScroll.cs | 39 + .../Scrolls/Necromancy/BloodOathScroll.cs | 39 + .../Scrolls/Necromancy/CorpseSkinScroll.cs | 39 + .../Scrolls/Necromancy/CurseWeaponScroll.cs | 39 + .../Scrolls/Necromancy/EvilOmenScroll.cs | 39 + .../Scrolls/Necromancy/ExorcismScroll.cs | 37 + .../Scrolls/Necromancy/HorrificBeastScroll.cs | 39 + .../Scrolls/Necromancy/LichFormScroll.cs | 39 + .../Scrolls/Necromancy/MindRotScroll.cs | 39 + .../Scrolls/Necromancy/PainSpikeScroll.cs | 39 + .../Scrolls/Necromancy/PoisonStrikeScroll.cs | 39 + .../Scrolls/Necromancy/StrangleScroll.cs | 39 + .../Necromancy/SummonFamiliarScroll.cs | 39 + .../Necromancy/VampiricEmbraceScroll.cs | 39 + .../Necromancy/VengefulSpiritScroll.cs | 39 + .../Scrolls/Necromancy/WitherScroll.cs | 39 + .../Scrolls/Necromancy/WraithFormScroll.cs | 39 + .../Scrolls/Second Circle/AgilityScroll.cs | 39 + .../Scrolls/Second Circle/CunningScroll.cs | 39 + .../Scrolls/Second Circle/CureScroll.cs | 39 + .../Scrolls/Second Circle/HarmScroll.cs | 39 + .../Scrolls/Second Circle/MagicTrapScroll.cs | 39 + .../Second Circle/MagicUnTrapScroll.cs | 39 + .../Scrolls/Second Circle/ProtectionScroll.cs | 39 + .../Scrolls/Second Circle/StrengthScroll.cs | 39 + .../Seventh Circle/ChainLightningScroll.cs | 39 + .../Seventh Circle/EnergyFieldScroll.cs | 39 + .../Seventh Circle/FlamestrikeScroll.cs | 39 + .../Seventh Circle/GateTravelScroll.cs | 39 + .../Seventh Circle/ManaVampireScroll.cs | 39 + .../Seventh Circle/MassDispelScroll.cs | 39 + .../Seventh Circle/MeteorStormScroll.cs | 39 + .../Scrolls/Seventh Circle/PolymorphScroll.cs | 39 + .../Scrolls/Sixth Circle/DispelScroll.cs | 39 + .../Scrolls/Sixth Circle/EnergyBoltScroll.cs | 39 + .../Scrolls/Sixth Circle/ExplosionScroll.cs | 39 + .../Sixth Circle/InvisibilityScroll.cs | 39 + .../Scrolls/Sixth Circle/MarkScroll.cs | 39 + .../Scrolls/Sixth Circle/MassCurseScroll.cs | 39 + .../Sixth Circle/ParalyzeFieldSpell.cs | 39 + .../Scrolls/Sixth Circle/RevealScroll.cs | 39 + .../Magical/Scrolls/SpellScroll.cs | 93 + .../Scrolls/Third Circle/BlessScroll.cs | 39 + .../Scrolls/Third Circle/FireballScroll.cs | 39 + .../Scrolls/Third Circle/MagicLockScroll.cs | 39 + .../Scrolls/Third Circle/PoisonScroll.cs | 39 + .../Scrolls/Third Circle/TelekinisisScroll.cs | 39 + .../Scrolls/Third Circle/TeleportScroll.cs | 39 + .../Scrolls/Third Circle/UnlockScroll.cs | 39 + .../Scrolls/Third Circle/WallOfStoneScroll.cs | 39 + .../Items/Skill Items/Magical/Spellbook.cs | 817 ++ .../Skill Items/Magical/SpellweavingBook.cs | 44 + Scripts/Items/Skill Items/Misc/Bandage.cs | 481 + Scripts/Items/Skill Items/Misc/FireHorn.cs | 225 + .../Items/Skill Items/Misc/RecipeScroll.cs | 124 + Scripts/Items/Skill Items/Misc/RepairDeed.cs | 236 + .../Musical Instruments/BambooFlute.cs | 34 + .../Musical Instruments/BaseInstrument.cs | 587 + .../Skill Items/Musical Instruments/Drums.cs | 34 + .../Skill Items/Musical Instruments/Harp.cs | 34 + .../Musical Instruments/LapHarp.cs | 34 + .../Skill Items/Musical Instruments/Lute.cs | 34 + .../Musical Instruments/Tambourine.cs | 34 + .../Musical Instruments/TambourineTassel.cs | 34 + Scripts/Items/Skill Items/Ninjitsu/EggBomb.cs | 72 + Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs | 337 + .../Items/Skill Items/Ninjitsu/FukiyaDarts.cs | 104 + .../Skill Items/Ninjitsu/LeatherNinjaBelt.cs | 355 + .../Items/Skill Items/Ninjitsu/Shuriken.cs | 105 + .../Items/Skill Items/Ninjitsu/SmokeBomb.cs | 72 + .../Specialized/GlassblowingBook.cs | 62 + .../Skill Items/Specialized/MasonryBook.cs | 62 + Scripts/Items/Skill Items/Specialized/Sand.cs | 52 + .../Skill Items/Specialized/SandMiningBook.cs | 62 + .../Specialized/StoneMiningBook.cs | 62 + .../Tailor Items/Dyetubs/BlackDyeTub.cs | 32 + .../Tailor Items/Dyetubs/BlazeDyeTub.cs | 33 + .../Tailor Items/Dyetubs/CustomHuePicker.cs | 224 + .../Tailor Items/Dyetubs/DyeTub.cs | 257 + .../Tailor Items/Dyetubs/FurnitureDyeTub.cs | 68 + .../Tailor Items/Dyetubs/LeatherDyeTub.cs | 66 + .../Tailor Items/Dyetubs/RewardBlackDyeTub.cs | 63 + .../Tailor Items/Dyetubs/RunebookDyeTub.cs | 66 + .../Tailor Items/Dyetubs/SpecialDyeTub.cs | 62 + .../Tailor Items/Dyetubs/StatuetteDyeTub.cs | 66 + .../Tailor Items/Misc/Dressform.cs | 32 + .../Skill Items/Tailor Items/Misc/Dyes.cs | 97 + .../Skill Items/Tailor Items/Misc/Scissors.cs | 88 + .../Items/Skill Items/Thief/DisguiseKit.cs | 309 + Scripts/Items/Skill Items/Thief/LockPick.cs | 171 + Scripts/Items/Skill Items/Tinkering/Axle.cs | 39 + .../Items/Skill Items/Tinkering/AxleGears.cs | 39 + .../Items/Skill Items/Tinkering/ClockFrame.cs | 39 + .../Items/Skill Items/Tinkering/ClockParts.cs | 39 + Scripts/Items/Skill Items/Tinkering/Clocks.cs | 220 + Scripts/Items/Skill Items/Tinkering/Gears.cs | 39 + Scripts/Items/Skill Items/Tinkering/Globe.cs | 32 + Scripts/Items/Skill Items/Tinkering/Hinge.cs | 39 + .../Skill Items/Tinkering/SextantParts.cs | 39 + .../Items/Skill Items/Tinkering/Springs.cs | 39 + .../Items/Skill Items/Tinkering/Spyglass.cs | 77 + .../Items/Skill Items/Tinkering/Utensils.cs | 277 + .../Items/Skill Items/Tools/BaseRunicTool.cs | 675 + Scripts/Items/Skill Items/Tools/BaseTool.cs | 197 + Scripts/Items/Skill Items/Tools/Blowpipe.cs | 49 + .../Items/Skill Items/Tools/DovetailSaw.cs | 45 + Scripts/Items/Skill Items/Tools/DrawKnife.cs | 41 + .../Items/Skill Items/Tools/FletcherTools.cs | 45 + .../Items/Skill Items/Tools/FlourSifter.cs | 41 + Scripts/Items/Skill Items/Tools/Froe.cs | 41 + Scripts/Items/Skill Items/Tools/Hammer.cs | 41 + Scripts/Items/Skill Items/Tools/Inshave.cs | 41 + .../Items/Skill Items/Tools/JointingPlane.cs | 45 + .../Skill Items/Tools/MalletAndChisel.cs | 41 + .../Items/Skill Items/Tools/MapmakersPen.cs | 47 + .../Items/Skill Items/Tools/MortarPestle.cs | 41 + .../Items/Skill Items/Tools/MouldingPlane.cs | 42 + Scripts/Items/Skill Items/Tools/Nails.cs | 42 + Scripts/Items/Skill Items/Tools/RollingPin.cs | 41 + .../Items/Skill Items/Tools/RunicHammer.cs | 79 + .../Items/Skill Items/Tools/RunicSewingKit.cs | 80 + Scripts/Items/Skill Items/Tools/Saw.cs | 42 + Scripts/Items/Skill Items/Tools/Scorp.cs | 41 + Scripts/Items/Skill Items/Tools/ScribesPen.cs | 47 + Scripts/Items/Skill Items/Tools/SewingKit.cs | 41 + Scripts/Items/Skill Items/Tools/Skillet.cs | 43 + .../Items/Skill Items/Tools/SledgeHammer.cs | 42 + .../Items/Skill Items/Tools/SmithHammer.cs | 44 + .../Items/Skill Items/Tools/SmoothingPlane.cs | 42 + .../Items/Skill Items/Tools/TinkerTools.cs | 42 + Scripts/Items/Skill Items/Tools/Tongs.cs | 42 + .../Blacksmithy/AncientSmithyHammer.cs | 134 + .../Blacksmithy/ColoredAnvil.cs | 38 + .../Blacksmithy/GlovesOfMining.cs | 256 + .../Blacksmithy/PowderOfTemperament.cs | 285 + .../Items/Special/Gifts/HearthOfHomeFire.cs | 132 + Scripts/Items/Special/Gifts/HolySword.cs | 45 + .../Items/Special/Gifts/LeggingsOfEmbers.cs | 48 + Scripts/Items/Special/Gifts/RoseOfTrinsic.cs | 223 + Scripts/Items/Special/Gifts/SamuraiHelm.cs | 52 + .../Items/Special/Gifts/ShaminoCrossbow.cs | 43 + .../Items/Special/Gifts/TapestryOfSosaria.cs | 83 + .../Special/Holiday/Christmas/HolidayTree.cs | 281 + Scripts/Items/Special/Holiday/GiftBox.cs | 41 + Scripts/Items/Special/Holiday/Poinsettias.cs | 62 + Scripts/Items/Special/Holiday/Snowflakes.cs | 62 + Scripts/Items/Special/Holiday/Snowman.cs | 159 + .../Special/Holiday/WinterGiftPackage2003.cs | 37 + Scripts/Items/Special/Holiday/Wreath.cs | 328 + Scripts/Items/Special/MiniHouses.cs | 226 + Scripts/Items/Special/MonsterStatuette.cs | 266 + Scripts/Items/Special/RewardCake.cs | 49 + .../Items/Special/Solen Items/BagOfSending.cs | 270 + .../Special/Solen Items/BallOfSummoning.cs | 345 + .../Special/Solen Items/BraceletOfBinding.cs | 474 + .../Special/Solen Items/MessageHelper.cs | 18 + .../Solen Items/PowderOfTranslocation.cs | 125 + .../Items/Special/Solen Items/ZoogiFungus.cs | 41 + Scripts/Items/Special/SoulStone.cs | 877 ++ .../Special/Special Scrolls/PowerScroll.cs | 368 + .../Special/Special Scrolls/StatScroll.cs | 204 + Scripts/Items/Stones/AlchemyStone.cs | 46 + Scripts/Items/Stones/GMStone.cs | 56 + Scripts/Items/Stones/GamblingStone.cs | 123 + Scripts/Items/Stones/IngotStone.cs | 46 + Scripts/Items/Stones/RegStone.cs | 46 + Scripts/Items/Stones/ScribeStone.cs | 46 + Scripts/Items/Stones/SmithStone.cs | 46 + Scripts/Items/Stones/TailorStone.cs | 46 + Scripts/Items/Suits/AdminRobe.cs | 31 + Scripts/Items/Suits/BaseSuit.cs | 91 + Scripts/Items/Suits/CounselorRobe.cs | 31 + Scripts/Items/Suits/DeathShroud.cs | 31 + Scripts/Items/Suits/DupreSuit.cs | 31 + Scripts/Items/Suits/GMRobe.cs | 31 + Scripts/Items/Suits/LordBlackthorneSuit.cs | 31 + Scripts/Items/Suits/LordBritishSuit.cs | 31 + Scripts/Items/Suits/SeerRobe.cs | 31 + Scripts/Items/SupplyBags/AlchemyBag.cs | 47 + Scripts/Items/SupplyBags/BagOfIngots.cs | 49 + Scripts/Items/SupplyBags/ScribeBag.cs | 46 + Scripts/Items/SupplyBags/SmithBag.cs | 49 + Scripts/Items/SupplyBags/TailorBag.cs | 52 + Scripts/Items/Traps/BaseTrap.cs | 85 + Scripts/Items/Traps/FireColumnTrap.cs | 47 + Scripts/Items/Traps/FlameSpurtTrap.cs | 181 + Scripts/Items/Traps/GasTrap.cs | 125 + Scripts/Items/Traps/GiantSpikeTrap.cs | 48 + Scripts/Items/Traps/MushroomTrap.cs | 62 + Scripts/Items/Traps/SawTrap.cs | 97 + Scripts/Items/Traps/SpikeTrap.cs | 150 + Scripts/Items/Traps/StoneFaceTrap.cs | 163 + Scripts/Items/Wands/BaseWand.cs | 231 + Scripts/Items/Wands/ClumsyWand.cs | 38 + Scripts/Items/Wands/FeebleWand.cs | 38 + Scripts/Items/Wands/FireballWand.cs | 38 + Scripts/Items/Wands/GreaterHealWand.cs | 38 + Scripts/Items/Wands/HarmWand.cs | 38 + Scripts/Items/Wands/HealWand.cs | 38 + Scripts/Items/Wands/IDWand.cs | 47 + Scripts/Items/Wands/LightningWand.cs | 38 + Scripts/Items/Wands/MagicArrowWand.cs | 38 + Scripts/Items/Wands/ManaDrainWand.cs | 38 + Scripts/Items/Wands/RandomWand.cs | 32 + Scripts/Items/Wands/WandTarget.cs | 26 + Scripts/Items/Wands/WeaknessWand.cs | 38 + .../Items/Weapons/Abilities/ArmorIgnore.cs | 33 + .../Items/Weapons/Abilities/ArmorPierce.cs | 44 + .../Items/Weapons/Abilities/BleedAttack.cs | 133 + Scripts/Items/Weapons/Abilities/Block.cs | 111 + .../Items/Weapons/Abilities/ConcussionBlow.cs | 52 + .../Items/Weapons/Abilities/CrushingBlow.cs | 34 + .../Items/Weapons/Abilities/DefenseMastery.cs | 103 + Scripts/Items/Weapons/Abilities/Disarm.cs | 74 + Scripts/Items/Weapons/Abilities/Dismount.cs | 77 + Scripts/Items/Weapons/Abilities/DoubleShot.cs | 69 + .../Items/Weapons/Abilities/DoubleStrike.cs | 57 + Scripts/Items/Weapons/Abilities/DualWield.cs | 78 + Scripts/Items/Weapons/Abilities/Feint.cs | 79 + .../Weapons/Abilities/FrenziedWhirlwind.cs | 167 + .../Weapons/Abilities/InfectiousStrike.cs | 77 + .../Items/Weapons/Abilities/MortalStrike.cs | 91 + Scripts/Items/Weapons/Abilities/MovingShot.cs | 43 + .../Items/Weapons/Abilities/NerveStrike.cs | 66 + .../Items/Weapons/Abilities/ParalyzingBlow.cs | 126 + .../Items/Weapons/Abilities/RidingSwipe.cs | 72 + .../Items/Weapons/Abilities/ShadowStrike.cs | 54 + .../Items/Weapons/Abilities/TalonStrike.cs | 106 + .../Items/Weapons/Abilities/WeaponAbility.cs | 398 + .../Weapons/Abilities/WhirlwindAttack.cs | 88 + .../Weapons/Artifacts/AxeOfTheHeavens.cs | 42 + .../Weapons/Artifacts/BladeOfInsanity.cs | 45 + .../Weapons/Artifacts/BladeOfTheRighteous.cs | 47 + .../Items/Weapons/Artifacts/BoneCrusher.cs | 48 + .../Weapons/Artifacts/BreathOfTheDead.cs | 42 + .../Items/Weapons/Artifacts/Frostbringer.cs | 47 + .../Weapons/Artifacts/LegacyOfTheDreadLord.cs | 48 + .../Items/Weapons/Artifacts/SerpentsFang.cs | 53 + .../Items/Weapons/Artifacts/StaffOfTheMagi.cs | 54 + .../Weapons/Artifacts/TheBeserkersMaul.cs | 40 + .../Weapons/Artifacts/TheDragonSlayer.cs | 52 + .../Items/Weapons/Artifacts/TheDryadBow.cs | 77 + .../Items/Weapons/Artifacts/TheTaskmaster.cs | 48 + .../Items/Weapons/Artifacts/TitansHammer.cs | 42 + .../Items/Weapons/Artifacts/ZyronicClaw.cs | 50 + Scripts/Items/Weapons/Axes/Axe.cs | 50 + Scripts/Items/Weapons/Axes/BaseAxe.cs | 153 + Scripts/Items/Weapons/Axes/BattleAxe.cs | 51 + Scripts/Items/Weapons/Axes/DoubleAxe.cs | 50 + Scripts/Items/Weapons/Axes/ExecutionersAxe.cs | 50 + Scripts/Items/Weapons/Axes/Hatchet.cs | 50 + Scripts/Items/Weapons/Axes/LargeBattleAxe.cs | 50 + Scripts/Items/Weapons/Axes/Pickaxe.cs | 58 + Scripts/Items/Weapons/Axes/TwoHandedAxe.cs | 50 + Scripts/Items/Weapons/Axes/WarAxe.cs | 60 + Scripts/Items/Weapons/BaseMeleeWeapon.cs | 65 + Scripts/Items/Weapons/BaseWeapon.cs | 3457 +++++ Scripts/Items/Weapons/Fists.cs | 289 + Scripts/Items/Weapons/HitLower.cs | 96 + Scripts/Items/Weapons/Knives/BaseKnife.cs | 59 + Scripts/Items/Weapons/Knives/ButcherKnife.cs | 50 + Scripts/Items/Weapons/Knives/Cleaver.cs | 53 + Scripts/Items/Weapons/Knives/Dagger.cs | 55 + Scripts/Items/Weapons/Knives/SkinningKnife.cs | 50 + .../Items/Weapons/Knives/ThrowingDagger.cs | 125 + .../Artifacts/BlightGrippedLongbow.cs | 40 + .../ML Weapons/Artifacts/ColdForgedBlade.cs | 47 + .../ML Weapons/Artifacts/LuminousRuneBlade.cs | 47 + .../Artifacts/OverseerSunderedBlade.cs | 49 + .../ML Weapons/Artifacts/PhantomStaff.cs | 45 + .../ML Weapons/Artifacts/RuneCarvingKnife.cs | 41 + .../ML Weapons/Artifacts/ShardTrasher.cs | 41 + .../Artifacts/SilvanisFeywoodBow.cs | 46 + .../ML Weapons/Artifacts/TheNightReaper.cs | 41 + .../Items/Weapons/ML Weapons/AssassinSpike.cs | 52 + .../Items/Weapons/ML Weapons/DiamondMace.cs | 50 + .../ML Weapons/ElvenCompositeLongbow.cs | 58 + .../Items/Weapons/ML Weapons/ElvenMachete.cs | 53 + .../Weapons/ML Weapons/ElvenSpellblade.cs | 53 + Scripts/Items/Weapons/ML Weapons/Leafblade.cs | 52 + .../Weapons/ML Weapons/MagicalShortbow.cs | 56 + Scripts/Items/Weapons/ML Weapons/OrnateAxe.cs | 53 + .../Weapons/ML Weapons/RadiantScimitar.cs | 53 + Scripts/Items/Weapons/ML Weapons/RuneBlade.cs | 54 + .../Items/Weapons/ML Weapons/WarCleaver.cs | 53 + Scripts/Items/Weapons/ML Weapons/WildStaff.cs | 50 + Scripts/Items/Weapons/Maces/BaseBashing.cs | 60 + Scripts/Items/Weapons/Maces/Club.cs | 50 + Scripts/Items/Weapons/Maces/FireworksWand.cs | 138 + Scripts/Items/Weapons/Maces/HammerPick.cs | 51 + Scripts/Items/Weapons/Maces/Mace.cs | 50 + Scripts/Items/Weapons/Maces/MagicWand.cs | 46 + Scripts/Items/Weapons/Maces/Maul.cs | 53 + Scripts/Items/Weapons/Maces/Scepter.cs | 50 + Scripts/Items/Weapons/Maces/WarHammer.cs | 53 + Scripts/Items/Weapons/Maces/WarMace.cs | 50 + Scripts/Items/Weapons/PoleArms/Bardiche.cs | 50 + Scripts/Items/Weapons/PoleArms/BasePoleArm.cs | 125 + Scripts/Items/Weapons/PoleArms/Halberd.cs | 50 + Scripts/Items/Weapons/PoleArms/Scythe.cs | 56 + Scripts/Items/Weapons/Ranged/BaseRanged.cs | 141 + Scripts/Items/Weapons/Ranged/Bow.cs | 62 + Scripts/Items/Weapons/Ranged/CompositeBow.cs | 58 + Scripts/Items/Weapons/Ranged/Crossbow.cs | 57 + Scripts/Items/Weapons/Ranged/HeavyCrossbow.cs | 57 + Scripts/Items/Weapons/Ranged/JukaBow.cs | 97 + .../Items/Weapons/Ranged/RepeatingCrossbow.cs | 56 + Scripts/Items/Weapons/SE Weapons/Bokuto.cs | 53 + Scripts/Items/Weapons/SE Weapons/Daisho.cs | 54 + Scripts/Items/Weapons/SE Weapons/Kama.cs | 58 + Scripts/Items/Weapons/SE Weapons/Lajatang.cs | 58 + Scripts/Items/Weapons/SE Weapons/NoDachi.cs | 54 + Scripts/Items/Weapons/SE Weapons/Nunchaku.cs | 53 + Scripts/Items/Weapons/SE Weapons/Sai.cs | 58 + Scripts/Items/Weapons/SE Weapons/Tekagi.cs | 58 + Scripts/Items/Weapons/SE Weapons/Tessen.cs | 56 + Scripts/Items/Weapons/SE Weapons/Tetsubo.cs | 56 + Scripts/Items/Weapons/SE Weapons/Wakizashi.cs | 54 + Scripts/Items/Weapons/SE Weapons/Yumi.cs | 62 + Scripts/Items/Weapons/SlayerEntry.cs | 108 + Scripts/Items/Weapons/SlayerGroup.cs | 207 + Scripts/Items/Weapons/SlayerName.cs | 36 + .../Items/Weapons/SpearsAndForks/BaseSpear.cs | 60 + .../Weapons/SpearsAndForks/BladedStaff.cs | 52 + .../SpearsAndForks/DoubleBladedStaff.cs | 50 + Scripts/Items/Weapons/SpearsAndForks/Pike.cs | 50 + .../Items/Weapons/SpearsAndForks/Pitchfork.cs | 53 + .../Weapons/SpearsAndForks/ShortSpear.cs | 52 + Scripts/Items/Weapons/SpearsAndForks/Spear.cs | 50 + .../Weapons/SpearsAndForks/TribalSpear.cs | 58 + .../Items/Weapons/SpearsAndForks/WarFork.cs | 55 + Scripts/Items/Weapons/Staves/BaseStaff.cs | 45 + Scripts/Items/Weapons/Staves/BlackStaff.cs | 50 + Scripts/Items/Weapons/Staves/GlacialStaff.cs | 41 + Scripts/Items/Weapons/Staves/GnarledStaff.cs | 50 + Scripts/Items/Weapons/Staves/QuarterStaff.cs | 50 + .../Items/Weapons/Staves/ShepherdsCrook.cs | 123 + Scripts/Items/Weapons/Swords/BaseSword.cs | 56 + Scripts/Items/Weapons/Swords/BoneHarvester.cs | 53 + Scripts/Items/Weapons/Swords/Broadsword.cs | 53 + Scripts/Items/Weapons/Swords/CrescentBlade.cs | 53 + Scripts/Items/Weapons/Swords/Cutlass.cs | 53 + Scripts/Items/Weapons/Swords/Katana.cs | 53 + Scripts/Items/Weapons/Swords/Kryss.cs | 60 + Scripts/Items/Weapons/Swords/Lance.cs | 57 + Scripts/Items/Weapons/Swords/Longsword.cs | 53 + Scripts/Items/Weapons/Swords/Scimitar.cs | 53 + Scripts/Items/Weapons/Swords/ThinLongsword.cs | 50 + Scripts/Items/Weapons/Swords/VikingSword.cs | 53 + Scripts/Items/Weapons/WeaponEnums.cs | 66 + Scripts/Misc/AOS.cs | 989 ++ Scripts/Misc/AccountPrompt.cs | 41 + Scripts/Misc/Animations.cs | 30 + Scripts/Misc/AttackMessage.cs | 62 + Scripts/Misc/AutoRestart.cs | 89 + Scripts/Misc/AutoSave.cs | 181 + Scripts/Misc/Broadcasts.cs | 36 + Scripts/Misc/BuffIcons.cs | 301 + Scripts/Misc/CharacterCreation.cs | 1678 +++ Scripts/Misc/Cleanup.cs | 151 + Scripts/Misc/ClientVerification.cs | 20 + Scripts/Misc/CrashGuard.cs | 261 + Scripts/Misc/CurrentExpansion.cs | 31 + Scripts/Misc/DataPath.cs | 105 + Scripts/Misc/DispellableAttribute.cs | 10 + Scripts/Misc/DispellableFieldAttribute.cs | 10 + Scripts/Misc/DoorGenerator.cs | 566 + Scripts/Misc/Email.cs | 65 + Scripts/Misc/Emitter.cs | 726 + Scripts/Misc/Fastwalk.cs | 32 + Scripts/Misc/FoodDecay.cs | 45 + Scripts/Misc/Gifts/GiftGiving.cs | 89 + .../Gifts/Winter2004/DecorativeTopiary.cs | 48 + .../Misc/Gifts/Winter2004/FestiveCactus.cs | 48 + .../Winter2004/LightOfTheWinterSolstice.cs | 103 + Scripts/Misc/Gifts/Winter2004/Mistletoe.cs | 342 + .../Gifts/Winter2004/PileOfGlacialSnow.cs | 145 + Scripts/Misc/Gifts/Winter2004/SnowPile.cs | 131 + Scripts/Misc/Gifts/Winter2004/SnowyTree.cs | 48 + Scripts/Misc/Gifts/Winter2004/Winter2004.cs | 45 + Scripts/Misc/Guild.cs | 1740 +++ Scripts/Misc/HardwareInfo.cs | 183 + Scripts/Misc/InhumanSpeech.cs | 595 + Scripts/Misc/Keywords.cs | 60 + Scripts/Misc/LanguageStatistics.cs | 375 + Scripts/Misc/LightCycle.cs | 141 + Scripts/Misc/LoginStats.cs | 30 + Scripts/Misc/Loot.cs | 671 + Scripts/Misc/LootPack.cs | 961 ++ Scripts/Misc/MapDefinitions.cs | 48 + Scripts/Misc/NameList.cs | 109 + Scripts/Misc/NameVerification.cs | 207 + Scripts/Misc/Notoriety.cs | 391 + Scripts/Misc/OreInfo.cs | 594 + Scripts/Misc/Paperdoll.cs | 37 + Scripts/Misc/Poison.cs | 153 + Scripts/Misc/ProfanityProtection.cs | 124 + Scripts/Misc/Profile.cs | 98 + Scripts/Misc/ProtocolExtensions.cs | 107 + Scripts/Misc/RaceDefinitions.cs | 219 + Scripts/Misc/RegenRates.cs | 201 + Scripts/Misc/RenameRequests.cs | 51 + Scripts/Misc/ServerList.cs | 94 + Scripts/Misc/ShardPoller.cs | 643 + Scripts/Misc/ShrinkTable.cs | 86 + Scripts/Misc/SkillCheck.cs | 364 + Scripts/Misc/SocketOptions.cs | 43 + Scripts/Misc/Test Center/TestCenter.cs | 246 + Scripts/Misc/TextDefinition.cs | 97 + Scripts/Misc/Titles.cs | 390 + Scripts/Misc/TreasureMapProtection.cs | 77 + Scripts/Misc/VendorGenerator.cs | 536 + Scripts/Misc/Weather.cs | 394 + Scripts/Misc/WebStatus.cs | 118 + Scripts/Misc/WeightOverloading.cs | 132 + Scripts/Misc/WelcomeTimer.cs | 53 + Scripts/Misc/uoamVendors.cs | 353 + Scripts/Mobiles/Animals/Bears/BlackBear.cs | 69 + Scripts/Mobiles/Animals/Bears/BrownBear.cs | 68 + Scripts/Mobiles/Animals/Bears/GrizzlyBear.cs | 70 + Scripts/Mobiles/Animals/Bears/PolarBear.cs | 70 + Scripts/Mobiles/Animals/Birds/Chicken.cs | 67 + Scripts/Mobiles/Animals/Birds/Crane.cs | 86 + Scripts/Mobiles/Animals/Birds/Eagle.cs | 71 + Scripts/Mobiles/Animals/Birds/Phoenix.cs | 73 + Scripts/Mobiles/Animals/Canines/DireWolf.cs | 72 + Scripts/Mobiles/Animals/Canines/GreyWolf.cs | 71 + Scripts/Mobiles/Animals/Canines/TimberWolf.cs | 71 + Scripts/Mobiles/Animals/Canines/WhiteWolf.cs | 71 + Scripts/Mobiles/Animals/Cows/Bull.cs | 70 + Scripts/Mobiles/Animals/Cows/Cow.cs | 90 + Scripts/Mobiles/Animals/Felines/Cougar.cs | 69 + Scripts/Mobiles/Animals/Felines/HellCat.cs | 77 + Scripts/Mobiles/Animals/Felines/Panther.cs | 70 + .../Animals/Felines/PredatorHellCat.cs | 75 + .../Mobiles/Animals/Felines/SnowLeopard.cs | 71 + Scripts/Mobiles/Animals/Misc/Boar.cs | 66 + Scripts/Mobiles/Animals/Misc/BullFrog.cs | 72 + Scripts/Mobiles/Animals/Misc/Dolphin.cs | 88 + Scripts/Mobiles/Animals/Misc/Gaman.cs | 90 + Scripts/Mobiles/Animals/Misc/GiantToad.cs | 73 + Scripts/Mobiles/Animals/Misc/Goat.cs | 65 + Scripts/Mobiles/Animals/Misc/Gorilla.cs | 67 + Scripts/Mobiles/Animals/Misc/GreatHart.cs | 81 + Scripts/Mobiles/Animals/Misc/Hind.cs | 80 + Scripts/Mobiles/Animals/Misc/Llama.cs | 65 + Scripts/Mobiles/Animals/Misc/MountainGoat.cs | 69 + Scripts/Mobiles/Animals/Misc/PackHorse.cs | 222 + Scripts/Mobiles/Animals/Misc/PackLlama.cs | 143 + Scripts/Mobiles/Animals/Misc/Pig.cs | 64 + Scripts/Mobiles/Animals/Misc/Sheep.cs | 111 + Scripts/Mobiles/Animals/Misc/Walrus.cs | 69 + Scripts/Mobiles/Animals/Mounts/BaseMount.cs | 458 + Scripts/Mobiles/Animals/Mounts/Beetle.cs | 165 + .../Mobiles/Animals/Mounts/DesertOstard.cs | 67 + Scripts/Mobiles/Animals/Mounts/Ethereals.cs | 677 + Scripts/Mobiles/Animals/Mounts/FireSteed.cs | 80 + .../Mobiles/Animals/Mounts/ForestOstard.cs | 68 + .../Mobiles/Animals/Mounts/FrenziedOstard.cs | 71 + Scripts/Mobiles/Animals/Mounts/HellSteed.cs | 61 + Scripts/Mobiles/Animals/Mounts/Hiryu.cs | 175 + Scripts/Mobiles/Animals/Mounts/Horse.cs | 79 + Scripts/Mobiles/Animals/Mounts/Kirin.cs | 124 + Scripts/Mobiles/Animals/Mounts/LesserHiryu.cs | 183 + Scripts/Mobiles/Animals/Mounts/Nightmare.cs | 123 + .../Mobiles/Animals/Mounts/RidableLlama.cs | 70 + Scripts/Mobiles/Animals/Mounts/Ridgeback.cs | 76 + .../Mobiles/Animals/Mounts/SavageRidgeback.cs | 76 + .../Animals/Mounts/ScaledSwampDragon.cs | 73 + Scripts/Mobiles/Animals/Mounts/SeaHorse.cs | 41 + Scripts/Mobiles/Animals/Mounts/SilverSteed.cs | 45 + .../Mobiles/Animals/Mounts/SkeletalMount.cs | 73 + Scripts/Mobiles/Animals/Mounts/SwampDragon.cs | 210 + Scripts/Mobiles/Animals/Mounts/Unicorn.cs | 132 + .../Animals/Mounts/War Horses/BaseWarHorse.cs | 65 + .../Animals/Mounts/War Horses/CoMWarHorse.cs | 31 + .../Mounts/War Horses/MinaxWarHorse.cs | 31 + .../Animals/Mounts/War Horses/SLWarHorse.cs | 31 + .../Animals/Mounts/War Horses/TBWarHorse.cs | 31 + Scripts/Mobiles/Animals/Reptiles/Alligator.cs | 72 + .../Mobiles/Animals/Reptiles/GiantSerpent.cs | 86 + .../Mobiles/Animals/Reptiles/IceSerpent.cs | 97 + Scripts/Mobiles/Animals/Reptiles/IceSnake.cs | 72 + .../Mobiles/Animals/Reptiles/LavaLizard.cs | 78 + .../Mobiles/Animals/Reptiles/LavaSerpent.cs | 83 + Scripts/Mobiles/Animals/Reptiles/LavaSnake.cs | 75 + .../Mobiles/Animals/Reptiles/SilverSerpent.cs | 82 + Scripts/Mobiles/Animals/Reptiles/Snake.cs | 72 + Scripts/Mobiles/Animals/Rodents/GiantRat.cs | 73 + Scripts/Mobiles/Animals/Rodents/JackRabbit.cs | 81 + Scripts/Mobiles/Animals/Rodents/Rabbit.cs | 82 + Scripts/Mobiles/Animals/Rodents/SewerRat.cs | 71 + Scripts/Mobiles/Animals/Slimes/Jwilson.cs | 71 + .../Town Critters/(UO 3D Only) Parrot.cs | 73 + Scripts/Mobiles/Animals/Town Critters/Bird.cs | 139 + Scripts/Mobiles/Animals/Town Critters/Cat.cs | 67 + Scripts/Mobiles/Animals/Town Critters/Dog.cs | 66 + Scripts/Mobiles/Animals/Town Critters/Rat.cs | 70 + Scripts/Mobiles/Familiars/BaseFamiliar.cs | 148 + Scripts/Mobiles/Familiars/DarkWolf.cs | 82 + Scripts/Mobiles/Familiars/DeathAdder.cs | 58 + Scripts/Mobiles/Familiars/HordeMinion.cs | 199 + Scripts/Mobiles/Familiars/ShadowWisp.cs | 116 + Scripts/Mobiles/Familiars/VampireBat.cs | 61 + Scripts/Mobiles/Guards/ArcherGuard.cs | 397 + Scripts/Mobiles/Guards/BaseGuard.cs | 86 + Scripts/Mobiles/Guards/WarriorGuard.cs | 369 + Scripts/Mobiles/Healers/BaseHealer.cs | 166 + Scripts/Mobiles/Healers/EvilHealer.cs | 64 + .../Mobiles/Healers/EvilWanderingHealer.cs | 65 + Scripts/Mobiles/Healers/FortuneTeller.cs | 80 + Scripts/Mobiles/Healers/Healer.cs | 79 + Scripts/Mobiles/Healers/PricedHealer.cs | 85 + Scripts/Mobiles/Healers/WanderingHealer.cs | 71 + Scripts/Mobiles/Monsters/AOS/AbysmalHorror.cs | 92 + Scripts/Mobiles/Monsters/AOS/BoneDemon.cs | 74 + .../Mobiles/Monsters/AOS/CrystalElemental.cs | 78 + .../Mobiles/Monsters/AOS/DarknightCreeper.cs | 92 + Scripts/Mobiles/Monsters/AOS/DemonKnight.cs | 290 + Scripts/Mobiles/Monsters/AOS/Devourer.cs | 75 + Scripts/Mobiles/Monsters/AOS/FleshGolem.cs | 72 + Scripts/Mobiles/Monsters/AOS/FleshRenderer.cs | 116 + Scripts/Mobiles/Monsters/AOS/Gibberling.cs | 73 + Scripts/Mobiles/Monsters/AOS/GoreFiend.cs | 72 + Scripts/Mobiles/Monsters/AOS/Impaler.cs | 94 + .../Mobiles/Monsters/AOS/MoundOfMaggots.cs | 66 + .../Mobiles/Monsters/AOS/PatchworkSkeleton.cs | 75 + Scripts/Mobiles/Monsters/AOS/Ravager.cs | 69 + Scripts/Mobiles/Monsters/AOS/Revenant.cs | 183 + Scripts/Mobiles/Monsters/AOS/ShadowKnight.cs | 194 + .../Mobiles/Monsters/AOS/SkitteringHopper.cs | 68 + Scripts/Mobiles/Monsters/AOS/Treefellow.cs | 93 + Scripts/Mobiles/Monsters/AOS/VampireBat.cs | 70 + .../Mobiles/Monsters/AOS/WailingBanshee.cs | 73 + .../Mobiles/Monsters/AOS/WandererOfTheVoid.cs | 78 + Scripts/Mobiles/Monsters/Ants/AntLion.cs | 105 + .../Ants/BlackSolenInfiltratorQueen.cs | 111 + .../Ants/BlackSolenInfiltratorWarrior.cs | 112 + .../Mobiles/Monsters/Ants/BlackSolenQueen.cs | 150 + .../Monsters/Ants/BlackSolenWarrior.cs | 151 + .../Mobiles/Monsters/Ants/BlackSolenWorker.cs | 112 + .../Monsters/Ants/RedSolenInfiltratorQueen.cs | 110 + .../Ants/RedSolenInfiltratorWarrior.cs | 112 + .../Mobiles/Monsters/Ants/RedSolenQueen.cs | 150 + .../Mobiles/Monsters/Ants/RedSolenWarrior.cs | 150 + .../Mobiles/Monsters/Ants/RedSolenWorker.cs | 111 + Scripts/Mobiles/Monsters/Ants/SolenHelper.cs | 101 + .../Monsters/Arachnid/Magic/DreadSpider.cs | 77 + .../Arachnid/Magic/TerathanAvenger.cs | 82 + .../Arachnid/Magic/TerathanMatriarch.cs | 77 + .../Monsters/Arachnid/Melee/FrostSpider.cs | 78 + .../Arachnid/Melee/GiantBlackWidow.cs | 75 + .../Monsters/Arachnid/Melee/GiantSpider.cs | 75 + .../Monsters/Arachnid/Melee/TerathanDrone.cs | 80 + .../Arachnid/Melee/TerathanWarrior.cs | 75 + .../Monsters/Elemental/Magic/AirElemental.cs | 82 + .../Elemental/Magic/BloodElemental.cs | 72 + .../Monsters/Elemental/Magic/Efreet.cs | 83 + .../Monsters/Elemental/Magic/FireElemental.cs | 83 + .../Monsters/Elemental/Magic/IceElemental.cs | 72 + .../Elemental/Magic/PoisonElemental.cs | 82 + .../Elemental/Magic/ToxicElemental.cs | 85 + .../Elemental/Magic/WaterElemental.cs | 78 + .../Elemental/Melee/EarthElemental.cs | 78 + .../Monsters/Elemental/Melee/SnowElemental.cs | 71 + .../Monsters/Humanoid/Magic/AncientLich.cs | 108 + .../Monsters/Humanoid/Magic/ArcaneDaemon.cs | 74 + .../Mobiles/Monsters/Humanoid/Magic/Balron.cs | 79 + .../Monsters/Humanoid/Magic/Betrayer.cs | 169 + .../Mobiles/Monsters/Humanoid/Magic/Bogle.cs | 68 + .../Monsters/Humanoid/Magic/BoneMagi.cs | 81 + .../Mobiles/Monsters/Humanoid/Magic/Daemon.cs | 81 + .../Monsters/Humanoid/Magic/ElderGazer.cs | 70 + .../Monsters/Humanoid/Magic/EvilMage.cs | 75 + .../Monsters/Humanoid/Magic/EvilMageLord.cs | 81 + .../Monsters/Humanoid/Magic/FireGargoyle.cs | 73 + .../Monsters/Humanoid/Magic/Gargoyle.cs | 73 + .../Humanoid/Magic/GargoyleDestroyer.cs | 103 + .../Humanoid/Magic/GargoyleEnforcer.cs | 77 + .../Mobiles/Monsters/Humanoid/Magic/Gazer.cs | 72 + .../Humanoid/Magic/GolemController.cs | 95 + .../Monsters/Humanoid/Magic/IceFiend.cs | 69 + .../Mobiles/Monsters/Humanoid/Magic/Imp.cs | 79 + .../Mobiles/Monsters/Humanoid/Magic/Lich.cs | 81 + .../Monsters/Humanoid/Magic/LichLord.cs | 81 + .../Monsters/Humanoid/Magic/OrcishMage.cs | 108 + .../Monsters/Humanoid/Magic/RatmanMage.cs | 87 + .../Monsters/Humanoid/Magic/SavageShaman.cs | 287 + .../Mobiles/Monsters/Humanoid/Magic/Shade.cs | 77 + .../Monsters/Humanoid/Magic/SkeletalMage.cs | 80 + .../Monsters/Humanoid/Magic/Spectre.cs | 77 + .../Monsters/Humanoid/Magic/Succubus.cs | 120 + .../Mobiles/Monsters/Humanoid/Magic/Titan.cs | 72 + .../Mobiles/Monsters/Humanoid/Magic/Wraith.cs | 77 + .../Monsters/Humanoid/Melee/ArcticOgreLord.cs | 72 + .../Monsters/Humanoid/Melee/BoneKnight.cs | 88 + .../Monsters/Humanoid/Melee/Brigand.cs | 93 + .../Monsters/Humanoid/Melee/ChaosDaemon.cs | 72 + .../Mobiles/Monsters/Humanoid/Melee/Cursed.cs | 85 + .../Monsters/Humanoid/Melee/Cyclops.cs | 70 + .../Monsters/Humanoid/Melee/Doppleganger.cs | 64 + .../Humanoid/Melee/EnslavedGargoyle.cs | 68 + .../Mobiles/Monsters/Humanoid/Melee/Ettin.cs | 71 + .../Monsters/Humanoid/Melee/Executioner.cs | 93 + .../Monsters/Humanoid/Melee/FrostTroll.cs | 71 + .../Monsters/Humanoid/Melee/GazerLarva.cs | 64 + .../Mobiles/Monsters/Humanoid/Melee/Ghoul.cs | 75 + .../Monsters/Humanoid/Melee/Guardian.cs | 91 + .../Monsters/Humanoid/Melee/HeadlessOne.cs | 66 + .../Monsters/Humanoid/Melee/HordeMinion.cs | 87 + .../Monsters/Humanoid/Melee/Juggernaut.cs | 139 + .../Humanoid/Melee/KhaldunRevenant.cs | 181 + .../Humanoid/Melee/KhaldunSummoner.cs | 144 + .../Monsters/Humanoid/Melee/KhaldunZealot.cs | 153 + .../Mobiles/Monsters/Humanoid/Melee/Moloch.cs | 68 + .../Monsters/Humanoid/Melee/Mongbat.cs | 69 + .../Mobiles/Monsters/Humanoid/Melee/Mummy.cs | 79 + .../Mobiles/Monsters/Humanoid/Melee/Ogre.cs | 73 + .../Monsters/Humanoid/Melee/OgreLord.cs | 76 + .../Mobiles/Monsters/Humanoid/Melee/Orc.cs | 122 + .../Monsters/Humanoid/Melee/OrcBomber.cs | 161 + .../Monsters/Humanoid/Melee/OrcBrute.cs | 163 + .../Monsters/Humanoid/Melee/OrcCaptain.cs | 114 + .../Monsters/Humanoid/Melee/OrcishLord.cs | 118 + .../Mobiles/Monsters/Humanoid/Melee/Ratman.cs | 73 + .../Monsters/Humanoid/Melee/RatmanArcher.cs | 83 + .../Monsters/Humanoid/Melee/RestlessSoul.cs | 120 + .../Monsters/Humanoid/Melee/RottingCorpse.cs | 80 + .../Mobiles/Monsters/Humanoid/Melee/Savage.cs | 118 + .../Monsters/Humanoid/Melee/SavageRider.cs | 128 + .../Monsters/Humanoid/Melee/ShadowFiend.cs | 144 + .../Monsters/Humanoid/Melee/SkeletalKnight.cs | 87 + .../Monsters/Humanoid/Melee/Skeleton.cs | 82 + .../Humanoid/Melee/SpawnedOrcishLord.cs | 46 + .../Monsters/Humanoid/Melee/SpectralArmour.cs | 97 + .../Monsters/Humanoid/Melee/StoneGargoyle.cs | 74 + .../Monsters/Humanoid/Melee/StrongMongbat.cs | 70 + .../Mobiles/Monsters/Humanoid/Melee/Troll.cs | 69 + .../Mobiles/Monsters/Humanoid/Melee/Zombie.cs | 85 + .../Monsters/LBR/Exodus/ExodusMinion.cs | 195 + .../Monsters/LBR/Exodus/ExodusOverseer.cs | 191 + .../Mobiles/Monsters/LBR/Jukas/JukaLord.cs | 128 + .../Mobiles/Monsters/LBR/Jukas/JukaMage.cs | 206 + .../Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs | 128 + .../Mobiles/Monsters/LBR/Meers/MeerCaptain.cs | 168 + .../Mobiles/Monsters/LBR/Meers/MeerEternal.cs | 217 + .../Mobiles/Monsters/LBR/Meers/MeerMage.cs | 180 + .../Mobiles/Monsters/LBR/Meers/MeerWarrior.cs | 92 + .../Monsters/Mammal/Melee/HellHound.cs | 73 + .../Monsters/Mammal/Melee/VorpalBunny.cs | 145 + .../Mobiles/Monsters/Misc/Magic/DarkWisp.cs | 84 + .../Monsters/Misc/Magic/EtherealWarrior.cs | 123 + Scripts/Mobiles/Monsters/Misc/Magic/Pixie.cs | 82 + .../Mobiles/Monsters/Misc/Magic/ShadowWisp.cs | 82 + Scripts/Mobiles/Monsters/Misc/Magic/Wisp.cs | 84 + .../Monsters/Misc/Melee/BladeSpirits.cs | 123 + .../Mobiles/Monsters/Misc/Melee/Centaur.cs | 83 + .../Monsters/Misc/Melee/EnergyVortex.cs | 130 + .../Mobiles/Monsters/Misc/Melee/FrostOoze.cs | 64 + Scripts/Mobiles/Monsters/Misc/Melee/Golem.cs | 210 + .../Monsters/Misc/Melee/PlagueBeast.cs | 124 + .../Monsters/Misc/Melee/PlagueSpawn.cs | 156 + .../Mobiles/Monsters/Misc/Melee/SandVortex.cs | 111 + Scripts/Mobiles/Monsters/Misc/Melee/Slime.cs | 74 + .../Ore Elementals/AgapiteElemental.cs | 76 + .../Ore Elementals/BronzeElemental.cs | 78 + .../Ore Elementals/CopperElemental.cs | 81 + .../Ore Elementals/DullCopperElemental.cs | 75 + .../Ore Elementals/GoldenElemental.cs | 76 + .../Ore Elementals/ShadowIronElemental.cs | 92 + .../Ore Elementals/ValoriteElemental.cs | 96 + .../Ore Elementals/VeriteElemental.cs | 77 + .../Mobiles/Monsters/Plant/Magic/Reaper.cs | 75 + .../Mobiles/Monsters/Plant/Melee/BogThing.cs | 144 + .../Mobiles/Monsters/Plant/Melee/Bogling.cs | 69 + .../Mobiles/Monsters/Plant/Melee/Corpser.cs | 79 + .../Mobiles/Monsters/Plant/Melee/Quagmire.cs | 76 + .../Monsters/Plant/Melee/SwampTentacle.cs | 69 + .../Monsters/Plant/Melee/WhippingVine.cs | 71 + .../Monsters/Reptile/Magic/AncientWyrm.cs | 91 + .../Monsters/Reptile/Magic/DeepSeaSerpent.cs | 80 + .../Mobiles/Monsters/Reptile/Magic/Dragon.cs | 83 + .../Monsters/Reptile/Magic/Leviathan.cs | 182 + .../Reptile/Magic/OphidianArchmage.cs | 85 + .../Monsters/Reptile/Magic/OphidianMage.cs | 86 + .../Reptile/Magic/OphidianMatriarch.cs | 77 + .../Monsters/Reptile/Magic/SeaSerpent.cs | 86 + .../Reptile/Magic/SerpentineDragon.cs | 123 + .../Monsters/Reptile/Magic/ShadowWyrm.cs | 92 + .../Monsters/Reptile/Magic/SkeletalDragon.cs | 83 + .../Monsters/Reptile/Magic/WhiteWyrm.cs | 86 + .../Mobiles/Monsters/Reptile/Melee/Drake.cs | 82 + .../Mobiles/Monsters/Reptile/Melee/Harpy.cs | 95 + .../Mobiles/Monsters/Reptile/Melee/Kraken.cs | 81 + .../Monsters/Reptile/Melee/Lizardman.cs | 73 + .../Monsters/Reptile/Melee/OphidianKnight.cs | 87 + .../Monsters/Reptile/Melee/OphidianWarrior.cs | 82 + .../Monsters/Reptile/Melee/Scorpion.cs | 80 + .../Monsters/Reptile/Melee/StoneHarpy.cs | 96 + .../Mobiles/Monsters/Reptile/Melee/Wyvern.cs | 104 + Scripts/Mobiles/Monsters/SE/BakeKitsune.cs | 176 + .../Mobiles/Monsters/SE/DeathWatchBeetle.cs | 123 + .../Monsters/SE/DeathWatchBeetleHatchling.cs | 108 + Scripts/Mobiles/Monsters/SE/EliteNinja.cs | 123 + Scripts/Mobiles/Monsters/SE/FanDancer.cs | 183 + Scripts/Mobiles/Monsters/SE/FireBeetle.cs | 106 + Scripts/Mobiles/Monsters/SE/Kappa.cs | 207 + Scripts/Mobiles/Monsters/SE/KazeKemono.cs | 176 + Scripts/Mobiles/Monsters/SE/LadyOfTheSnow.cs | 160 + Scripts/Mobiles/Monsters/SE/Oni.cs | 111 + Scripts/Mobiles/Monsters/SE/RaiJu.cs | 132 + Scripts/Mobiles/Monsters/SE/RevenantLion.cs | 120 + Scripts/Mobiles/Monsters/SE/Ronin.cs | 121 + Scripts/Mobiles/Monsters/SE/RuneBeetle.cs | 206 + Scripts/Mobiles/Monsters/SE/TsukiWolf.cs | 192 + Scripts/Mobiles/Monsters/SE/Yamandon.cs | 182 + Scripts/Mobiles/Monsters/SE/YomotsuElder.cs | 142 + Scripts/Mobiles/Monsters/SE/YomotsuPriest.cs | 142 + Scripts/Mobiles/Monsters/SE/YomotsuWarrior.cs | 139 + .../Monsters/Summons/SummonedAirElemental.cs | 69 + .../Monsters/Summons/SummonedDaemon.cs | 64 + .../Summons/SummonedEarthElemental.cs | 61 + .../Monsters/Summons/SummonedFireElemental.cs | 63 + .../Summons/SummonedWaterElemental.cs | 65 + Scripts/Mobiles/PlayerMobile.cs | 3847 ++++++ Scripts/Mobiles/Special/Barracoon.cs | 232 + Scripts/Mobiles/Special/BaseChampion.cs | 276 + Scripts/Mobiles/Special/BaseShieldGuard.cs | 151 + .../Mobiles/Special/CapturedHordeMinion.cs | 39 + Scripts/Mobiles/Special/ChaosGuard.cs | 41 + Scripts/Mobiles/Special/Harrower.cs | 545 + Scripts/Mobiles/Special/HarrowerTentacles.cs | 221 + Scripts/Mobiles/Special/LordOaks.cs | 221 + Scripts/Mobiles/Special/Mephitis.cs | 81 + Scripts/Mobiles/Special/Neira.cs | 287 + Scripts/Mobiles/Special/OrderGuard.cs | 41 + Scripts/Mobiles/Special/Rikktor.cs | 151 + Scripts/Mobiles/Special/Semidar.cs | 134 + Scripts/Mobiles/Special/Serado.cs | 178 + Scripts/Mobiles/Special/ServantOfSemidar.cs | 49 + Scripts/Mobiles/Special/Silvani.cs | 137 + Scripts/Mobiles/Special/Wanderer.cs | 69 + Scripts/Mobiles/Townfolk/Actor.cs | 69 + Scripts/Mobiles/Townfolk/Artist.cs | 74 + Scripts/Mobiles/Townfolk/Banker.cs | 408 + Scripts/Mobiles/Townfolk/BaseEscortable.cs | 594 + Scripts/Mobiles/Townfolk/BrideGroom.cs | 84 + Scripts/Mobiles/Townfolk/EscortableMage.cs | 74 + Scripts/Mobiles/Townfolk/Gypsy.cs | 82 + Scripts/Mobiles/Townfolk/HarborMaster.cs | 76 + Scripts/Mobiles/Townfolk/Merchant.cs | 85 + Scripts/Mobiles/Townfolk/Messenger.cs | 87 + Scripts/Mobiles/Townfolk/Minter.cs | 38 + Scripts/Mobiles/Townfolk/Ninja.cs | 78 + Scripts/Mobiles/Townfolk/Noble.cs | 84 + Scripts/Mobiles/Townfolk/Peasant.cs | 81 + Scripts/Mobiles/Townfolk/Samurai.cs | 94 + Scripts/Mobiles/Townfolk/Sculptor.cs | 70 + Scripts/Mobiles/Townfolk/SeekerOfAdventure.cs | 90 + Scripts/Mobiles/Townfolk/TownCrier.cs | 568 + Scripts/Mobiles/Vendors/AnimalBuy.cs | 28 + Scripts/Mobiles/Vendors/BaseVendor.cs | 1484 +++ Scripts/Mobiles/Vendors/BeverageBuy.cs | 34 + Scripts/Mobiles/Vendors/GenericBuy.cs | 344 + Scripts/Mobiles/Vendors/GenericSell.cs | 134 + Scripts/Mobiles/Vendors/NPC/Alchemist.cs | 56 + Scripts/Mobiles/Vendors/NPC/AnimalTrainer.cs | 426 + Scripts/Mobiles/Vendors/NPC/Architect.cs | 45 + Scripts/Mobiles/Vendors/NPC/Armorer.cs | 96 + Scripts/Mobiles/Vendors/NPC/Baker.cs | 42 + Scripts/Mobiles/Vendors/NPC/Bard.cs | 48 + Scripts/Mobiles/Vendors/NPC/Barkeeper.cs | 54 + Scripts/Mobiles/Vendors/NPC/Beekeeper.cs | 42 + Scripts/Mobiles/Vendors/NPC/Blacksmith.cs | 150 + Scripts/Mobiles/Vendors/NPC/Bowyer.cs | 65 + Scripts/Mobiles/Vendors/NPC/Butcher.cs | 49 + Scripts/Mobiles/Vendors/NPC/Carpenter.cs | 56 + Scripts/Mobiles/Vendors/NPC/Cobbler.cs | 46 + Scripts/Mobiles/Vendors/NPC/Cook.cs | 57 + .../Mobiles/Vendors/NPC/CustomHairstylist.cs | 587 + Scripts/Mobiles/Vendors/NPC/Farmer.cs | 60 + Scripts/Mobiles/Vendors/NPC/Fisherman.cs | 50 + Scripts/Mobiles/Vendors/NPC/Furtrader.cs | 45 + Scripts/Mobiles/Vendors/NPC/Glassblower.cs | 49 + Scripts/Mobiles/Vendors/NPC/GolemCrafter.cs | 45 + .../NPC/Guildmasters/BardGuildmaster.cs | 40 + .../NPC/Guildmasters/BaseGuildmaster.cs | 167 + .../NPC/Guildmasters/BlacksmithGuildmaster.cs | 70 + .../NPC/Guildmasters/FisherGuildmaster.cs | 35 + .../NPC/Guildmasters/HealerGuildmaster.cs | 51 + .../NPC/Guildmasters/MageGuildmaster.cs | 54 + .../NPC/Guildmasters/MerchantGuildmaster.cs | 36 + .../NPC/Guildmasters/MinerGuildmaster.cs | 36 + .../NPC/Guildmasters/RangerGuildmaster.cs | 45 + .../NPC/Guildmasters/TailorGuildmaster.cs | 35 + .../NPC/Guildmasters/ThiefGuildmaster.cs | 127 + .../NPC/Guildmasters/TinkerGuildmaster.cs | 37 + .../NPC/Guildmasters/WarriorGuildmaster.cs | 41 + .../Mobiles/Vendors/NPC/GypsyAnimalTrainer.cs | 88 + Scripts/Mobiles/Vendors/NPC/GypsyBanker.cs | 89 + Scripts/Mobiles/Vendors/NPC/GypsyMaiden.cs | 80 + Scripts/Mobiles/Vendors/NPC/HairStylist.cs | 43 + Scripts/Mobiles/Vendors/NPC/Herbalist.cs | 50 + Scripts/Mobiles/Vendors/NPC/HolyMage.cs | 82 + Scripts/Mobiles/Vendors/NPC/InnKeeper.cs | 48 + Scripts/Mobiles/Vendors/NPC/IronWorker.cs | 124 + Scripts/Mobiles/Vendors/NPC/Jeweler.cs | 41 + .../Mobiles/Vendors/NPC/KeeperOfChivalry.cs | 85 + Scripts/Mobiles/Vendors/NPC/LeatherWorker.cs | 40 + Scripts/Mobiles/Vendors/NPC/Mage.cs | 60 + Scripts/Mobiles/Vendors/NPC/Mapmaker.cs | 41 + Scripts/Mobiles/Vendors/NPC/Miller.cs | 41 + Scripts/Mobiles/Vendors/NPC/Miner.cs | 51 + Scripts/Mobiles/Vendors/NPC/Provisioner.cs | 45 + Scripts/Mobiles/Vendors/NPC/Rancher.cs | 49 + Scripts/Mobiles/Vendors/NPC/Ranger.cs | 56 + .../Mobiles/Vendors/NPC/RealEstateBroker.cs | 187 + Scripts/Mobiles/Vendors/NPC/Scribe.cs | 56 + Scripts/Mobiles/Vendors/NPC/Shipwright.cs | 49 + Scripts/Mobiles/Vendors/NPC/StoneCrafter.cs | 50 + Scripts/Mobiles/Vendors/NPC/Tailor.cs | 99 + Scripts/Mobiles/Vendors/NPC/Tanner.cs | 41 + Scripts/Mobiles/Vendors/NPC/TavernKeeper.cs | 47 + Scripts/Mobiles/Vendors/NPC/Thief.cs | 56 + Scripts/Mobiles/Vendors/NPC/Tinker.cs | 45 + Scripts/Mobiles/Vendors/NPC/Vagabond.cs | 65 + Scripts/Mobiles/Vendors/NPC/VarietyDealer.cs | 40 + Scripts/Mobiles/Vendors/NPC/Veterinarian.cs | 47 + Scripts/Mobiles/Vendors/NPC/Waiter.cs | 48 + Scripts/Mobiles/Vendors/NPC/Weaponsmith.cs | 117 + Scripts/Mobiles/Vendors/NPC/Weaver.cs | 99 + Scripts/Mobiles/Vendors/PlayerBarkeeper.cs | 1068 ++ Scripts/Mobiles/Vendors/PlayerVendor.cs | 1730 +++ Scripts/Mobiles/Vendors/PresetMapBuy.cs | 23 + Scripts/Mobiles/Vendors/RentedVendor.cs | 431 + .../Vendors/SBInfo/Armors/SBChainmailArmor.cs | 39 + .../Vendors/SBInfo/Armors/SBHelmetArmor.cs | 47 + .../Vendors/SBInfo/Armors/SBLeatherArmor.cs | 57 + .../Vendors/SBInfo/Armors/SBMetalShields.cs | 46 + .../Vendors/SBInfo/Armors/SBPlateArmor.cs | 47 + .../Vendors/SBInfo/Armors/SBRingmailArmor.cs | 42 + .../Vendors/SBInfo/Armors/SBStuddedArmor.cs | 47 + .../Vendors/SBInfo/Armors/SBWoodenShields.cs | 35 + Scripts/Mobiles/Vendors/SBInfo/SBAlchemist.cs | 80 + .../Mobiles/Vendors/SBInfo/SBAnimalTrainer.cs | 51 + Scripts/Mobiles/Vendors/SBInfo/SBArchitect.cs | 40 + Scripts/Mobiles/Vendors/SBInfo/SBBaker.cs | 58 + Scripts/Mobiles/Vendors/SBInfo/SBBanker.cs | 39 + Scripts/Mobiles/Vendors/SBInfo/SBBard.cs | 43 + Scripts/Mobiles/Vendors/SBInfo/SBBarkeeper.cs | 110 + Scripts/Mobiles/Vendors/SBInfo/SBBeekeeper.cs | 37 + .../Mobiles/Vendors/SBInfo/SBBlacksmith.cs | 234 + Scripts/Mobiles/Vendors/SBInfo/SBBowyer.cs | 35 + Scripts/Mobiles/Vendors/SBInfo/SBButcher.cs | 62 + Scripts/Mobiles/Vendors/SBInfo/SBCarpenter.cs | 92 + Scripts/Mobiles/Vendors/SBInfo/SBCobbler.cs | 42 + Scripts/Mobiles/Vendors/SBInfo/SBCook.cs | 89 + Scripts/Mobiles/Vendors/SBInfo/SBFarmer.cs | 76 + Scripts/Mobiles/Vendors/SBInfo/SBFisherman.cs | 45 + .../Mobiles/Vendors/SBInfo/SBFortuneTeller.cs | 35 + Scripts/Mobiles/Vendors/SBInfo/SBFurtrader.cs | 35 + .../Mobiles/Vendors/SBInfo/SBGlassblower.cs | 83 + .../Mobiles/Vendors/SBInfo/SBHairStylist.cs | 39 + Scripts/Mobiles/Vendors/SBInfo/SBHealer.cs | 43 + Scripts/Mobiles/Vendors/SBInfo/SBHerbalist.cs | 48 + Scripts/Mobiles/Vendors/SBInfo/SBHolyMage.cs | 93 + Scripts/Mobiles/Vendors/SBInfo/SBHouseDeed.cs | 72 + Scripts/Mobiles/Vendors/SBInfo/SBInfo.cs | 16 + Scripts/Mobiles/Vendors/SBInfo/SBInnKeeper.cs | 115 + Scripts/Mobiles/Vendors/SBInfo/SBJewel.cs | 78 + .../Vendors/SBInfo/SBKeeperOfChivalry.cs | 34 + .../Mobiles/Vendors/SBInfo/SBLeatherWorker.cs | 37 + Scripts/Mobiles/Vendors/SBInfo/SBMage.cs | 104 + Scripts/Mobiles/Vendors/SBInfo/SBMapmaker.cs | 47 + Scripts/Mobiles/Vendors/SBInfo/SBMiller.cs | 37 + Scripts/Mobiles/Vendors/SBInfo/SBMiner.cs | 47 + .../Vendors/SBInfo/SBPlayerBarkeeper.cs | 49 + .../Mobiles/Vendors/SBInfo/SBProvisioner.cs | 170 + Scripts/Mobiles/Vendors/SBInfo/SBRancher.cs | 35 + Scripts/Mobiles/Vendors/SBInfo/SBRanger.cs | 38 + .../Vendors/SBInfo/SBRealEstateBroker.cs | 37 + Scripts/Mobiles/Vendors/SBInfo/SBSECook.cs | 53 + Scripts/Mobiles/Vendors/SBInfo/SBSEHats.cs | 39 + Scripts/Mobiles/Vendors/SBInfo/SBScribe.cs | 45 + .../Mobiles/Vendors/SBInfo/SBShipwright.cs | 41 + .../Mobiles/Vendors/SBInfo/SBSmithTools.cs | 38 + .../Mobiles/Vendors/SBInfo/SBStoneCrafter.cs | 96 + Scripts/Mobiles/Vendors/SBInfo/SBTailor.cs | 123 + Scripts/Mobiles/Vendors/SBInfo/SBTanner.cs | 93 + .../Mobiles/Vendors/SBInfo/SBTavernKeeper.cs | 113 + Scripts/Mobiles/Vendors/SBInfo/SBThief.cs | 50 + Scripts/Mobiles/Vendors/SBInfo/SBTinker.cs | 132 + Scripts/Mobiles/Vendors/SBInfo/SBVagabond.cs | 81 + .../Mobiles/Vendors/SBInfo/SBVarietyDealer.cs | 136 + .../Mobiles/Vendors/SBInfo/SBVeterinarian.cs | 40 + Scripts/Mobiles/Vendors/SBInfo/SBWaiter.cs | 63 + .../Mobiles/Vendors/SBInfo/SBWeaponSmith.cs | 182 + Scripts/Mobiles/Vendors/SBInfo/SBWeaver.cs | 60 + .../Mobiles/Vendors/SBInfo/SE/SBSEArmor.cs | 50 + .../Mobiles/Vendors/SBInfo/SE/SBSEBowyer.cs | 43 + .../Vendors/SBInfo/SE/SBSECarpenter.cs | 42 + Scripts/Mobiles/Vendors/SBInfo/SE/SBSEFood.cs | 41 + .../Vendors/SBInfo/SE/SBSELeatherArmor.cs | 58 + .../Mobiles/Vendors/SBInfo/SE/SBSEWeapons.cs | 54 + .../Vendors/SBInfo/Weapons/SBAxeWeapon.cs | 50 + .../Vendors/SBInfo/Weapons/SBKnifeWeapon.cs | 41 + .../Vendors/SBInfo/Weapons/SBMaceWeapon.cs | 45 + .../Vendors/SBInfo/Weapons/SBPoleArmWeapon.cs | 37 + .../Vendors/SBInfo/Weapons/SBRangedWeapon.cs | 59 + .../SBInfo/Weapons/SBSpearForkWeapon.cs | 39 + .../Vendors/SBInfo/Weapons/SBStavesWeapon.cs | 41 + .../Vendors/SBInfo/Weapons/SBSwordWeapon.cs | 73 + Scripts/Mobiles/Vendors/VendorInventory.cs | 179 + Scripts/Multis/BaseHouse.cs | 3644 +++++ Scripts/Multis/Boats/BaseBoat.cs | 1671 +++ Scripts/Multis/Boats/BaseBoatDeed.cs | 183 + Scripts/Multis/Boats/BaseDockedBoat.cs | 193 + Scripts/Multis/Boats/ConfirmDryDockGump.cs | 40 + Scripts/Multis/Boats/Hold.cs | 125 + Scripts/Multis/Boats/LargeBoat.cs | 103 + Scripts/Multis/Boats/LargeDragonBoat.cs | 103 + Scripts/Multis/Boats/MediumBoat.cs | 103 + Scripts/Multis/Boats/MediumDragonBoat.cs | 103 + Scripts/Multis/Boats/Plank.cs | 310 + Scripts/Multis/Boats/RenameBoatPrompt.cs | 21 + Scripts/Multis/Boats/SmallBoat.cs | 103 + Scripts/Multis/Boats/SmallDragonBoat.cs | 103 + Scripts/Multis/Boats/Strandedness.cs | 182 + Scripts/Multis/Boats/TillerMan.cs | 119 + Scripts/Multis/Camps/BankerCamp.cs | 49 + Scripts/Multis/Camps/BaseCamp.cs | 152 + Scripts/Multis/Camps/HealerCamp.cs | 49 + Scripts/Multis/Camps/MageCamp.cs | 49 + Scripts/Multis/Camps/OrcCamp.cs | 105 + Scripts/Multis/Camps/RatCamp.cs | 105 + Scripts/Multis/ComponentVerification.cs | 251 + Scripts/Multis/Deeds.cs | 887 ++ Scripts/Multis/HouseFoundation.cs | 2458 ++++ Scripts/Multis/HousePlacement.cs | 342 + Scripts/Multis/HousePlacementTool.cs | 752 ++ Scripts/Multis/HouseSign.cs | 293 + Scripts/Multis/HouseTeleporter.cs | 187 + Scripts/Multis/Houses.cs | 577 + Scripts/Multis/MovingCrate.cs | 330 + Scripts/Multis/PreviewHouse.cs | 144 + Scripts/Regions/BaseRegion.cs | 561 + Scripts/Regions/DungeonRegion.cs | 52 + Scripts/Regions/GreenAcres.cs | 40 + Scripts/Regions/GuardedRegion.cs | 371 + Scripts/Regions/HouseRegion.cs | 393 + Scripts/Regions/Jail.cs | 61 + Scripts/Regions/NoHousingRegion.cs | 26 + Scripts/Regions/Spawning/SpawnDefinition.cs | 405 + Scripts/Regions/Spawning/SpawnEntry.cs | 487 + Scripts/Regions/Spawning/SpawnPersistence.cs | 69 + Scripts/Regions/TownRegion.cs | 13 + Scripts/Skills/Anatomy.cs | 87 + Scripts/Skills/AnimalLore.cs | 356 + Scripts/Skills/AnimalTaming.cs | 363 + Scripts/Skills/ArmsLore.cs | 167 + Scripts/Skills/Begging.cs | 175 + Scripts/Skills/DetectHidden.cs | 116 + Scripts/Skills/Discordance.cs | 239 + Scripts/Skills/EvalInt.cs | 89 + Scripts/Skills/ForensicEval.cs | 92 + Scripts/Skills/Hiding.cs | 108 + Scripts/Skills/Inscribe.cs | 162 + Scripts/Skills/ItemIdentification.cs | 61 + Scripts/Skills/Meditation.cs | 101 + Scripts/Skills/Peacemaking.cs | 199 + Scripts/Skills/Poisoning.cs | 171 + Scripts/Skills/Provocation.cs | 160 + Scripts/Skills/RemoveTrap.cs | 127 + Scripts/Skills/Snooping.cs | 103 + Scripts/Skills/SpiritSpeak.cs | 203 + Scripts/Skills/Stealing.cs | 478 + Scripts/Skills/Stealth.cs | 91 + Scripts/Skills/TasteID.cs | 95 + Scripts/Skills/Tracking.cs | 397 + Scripts/Spells/Base/DisturbType.cs | 15 + Scripts/Spells/Base/SpecialMove.cs | 337 + Scripts/Spells/Base/Spell.cs | 942 ++ Scripts/Spells/Base/SpellCircle.cs | 16 + Scripts/Spells/Base/SpellHelper.cs | 946 ++ Scripts/Spells/Base/SpellInfo.cs | 69 + Scripts/Spells/Base/SpellRegistry.cs | 182 + Scripts/Spells/Base/SpellState.cs | 11 + Scripts/Spells/Bushido/Confidence.cs | 158 + Scripts/Spells/Bushido/CounterAttack.cs | 115 + Scripts/Spells/Bushido/Evasion.cs | 115 + Scripts/Spells/Bushido/HonorableExecution.cs | 197 + Scripts/Spells/Bushido/LightningStrike.cs | 68 + Scripts/Spells/Bushido/MomentumStrike.cs | 75 + Scripts/Spells/Bushido/SamuraiMove.cs | 10 + Scripts/Spells/Bushido/SamuraiSpell.cs | 126 + Scripts/Spells/Chivalry/CleanseByFire.cs | 115 + Scripts/Spells/Chivalry/CloseWounds.cs | 107 + Scripts/Spells/Chivalry/ConsecrateWeapon.cs | 106 + Scripts/Spells/Chivalry/DispelEvil.cs | 116 + Scripts/Spells/Chivalry/DivineFury.cs | 76 + Scripts/Spells/Chivalry/EnemyOfOne.cs | 81 + Scripts/Spells/Chivalry/HolyLight.cs | 68 + Scripts/Spells/Chivalry/NobleSacrifice.cs | 175 + Scripts/Spells/Chivalry/PaladinSpell.cs | 148 + Scripts/Spells/Chivalry/RemoveCurse.cs | 125 + Scripts/Spells/Chivalry/SacredJourney.cs | 189 + Scripts/Spells/Eighth/AirElemental.cs | 54 + Scripts/Spells/Eighth/EarthElemental.cs | 54 + Scripts/Spells/Eighth/Earthquake.cs | 81 + Scripts/Spells/Eighth/EnergyVortex.cs | 100 + Scripts/Spells/Eighth/FireElemental.cs | 55 + Scripts/Spells/Eighth/Resurrection.cs | 101 + Scripts/Spells/Eighth/SummonDaemon.cs | 55 + Scripts/Spells/Eighth/WaterElemental.cs | 54 + Scripts/Spells/Fifth/BladeSpirits.cs | 107 + Scripts/Spells/Fifth/DispelField.cs | 87 + Scripts/Spells/Fifth/Incognito.cs | 194 + Scripts/Spells/Fifth/MagicReflect.cs | 137 + Scripts/Spells/Fifth/MindBlast.cs | 154 + Scripts/Spells/Fifth/Paralyze.cs | 101 + Scripts/Spells/Fifth/PoisonField.cs | 294 + Scripts/Spells/Fifth/SummonCreature.cs | 90 + Scripts/Spells/First/Clumsy.cs | 81 + Scripts/Spells/First/CreateFood.cs | 89 + Scripts/Spells/First/Feeblemind.cs | 81 + Scripts/Spells/First/Heal.cs | 104 + Scripts/Spells/First/MagicArrow.cs | 96 + Scripts/Spells/First/NightSight.cs | 75 + Scripts/Spells/First/ReactiveArmor.cs | 133 + Scripts/Spells/First/Weaken.cs | 81 + Scripts/Spells/Fourth/ArchCure.cs | 144 + Scripts/Spells/Fourth/ArchProtection.cs | 154 + Scripts/Spells/Fourth/Curse.cs | 103 + Scripts/Spells/Fourth/FireField.cs | 284 + Scripts/Spells/Fourth/GreaterHeal.cs | 94 + Scripts/Spells/Fourth/Lightning.cs | 90 + Scripts/Spells/Fourth/ManaDrain.cs | 131 + Scripts/Spells/Fourth/Recall.cs | 196 + Scripts/Spells/Initializer.cs | 153 + Scripts/Spells/Necromancy/AnimateDeadSpell.cs | 419 + Scripts/Spells/Necromancy/BloodOathSpell.cs | 147 + Scripts/Spells/Necromancy/CorpseSkin.cs | 150 + Scripts/Spells/Necromancy/CurseWeapon.cs | 105 + Scripts/Spells/Necromancy/EvilOmen.cs | 116 + Scripts/Spells/Necromancy/Exorcism.cs | 216 + Scripts/Spells/Necromancy/HorrificBeast.cs | 35 + Scripts/Spells/Necromancy/LichForm.cs | 47 + Scripts/Spells/Necromancy/MindRot.cs | 143 + Scripts/Spells/Necromancy/NecromancerSpell.cs | 38 + Scripts/Spells/Necromancy/PainSpike.cs | 138 + Scripts/Spells/Necromancy/PoisonStrike.cs | 106 + Scripts/Spells/Necromancy/Strangle.cs | 195 + Scripts/Spells/Necromancy/SummonFamiliar.cs | 204 + .../Spells/Necromancy/TransformationSpell.cs | 254 + Scripts/Spells/Necromancy/VampiricEmbrace.cs | 53 + Scripts/Spells/Necromancy/VengefulSpirit.cs | 96 + Scripts/Spells/Necromancy/Wither.cs | 78 + Scripts/Spells/Necromancy/WraithForm.cs | 42 + Scripts/Spells/Ninjitsu/AnimalForm.cs | 433 + Scripts/Spells/Ninjitsu/Backstab.cs | 73 + Scripts/Spells/Ninjitsu/DeathStrike.cs | 138 + Scripts/Spells/Ninjitsu/FocusAttack.cs | 75 + Scripts/Spells/Ninjitsu/KiAttack.cs | 128 + Scripts/Spells/Ninjitsu/MirrorImage.cs | 266 + Scripts/Spells/Ninjitsu/NinjaMove.cs | 14 + Scripts/Spells/Ninjitsu/NinjaSpell.cs | 102 + Scripts/Spells/Ninjitsu/ShadowJump.cs | 127 + Scripts/Spells/Ninjitsu/SurpriseAttack.cs | 127 + Scripts/Spells/Reagent.cs | 108 + Scripts/Spells/Second/Agility.cs | 74 + Scripts/Spells/Second/Cunning.cs | 74 + Scripts/Spells/Second/Cure.cs | 90 + Scripts/Spells/Second/Harm.cs | 113 + Scripts/Spells/Second/MagicTrap.cs | 88 + Scripts/Spells/Second/Protection.cs | 162 + Scripts/Spells/Second/RemoveTrap.cs | 83 + Scripts/Spells/Second/Strength.cs | 74 + Scripts/Spells/Seventh/ChainLightning.cs | 138 + Scripts/Spells/Seventh/EnergyField.cs | 190 + Scripts/Spells/Seventh/FlameStrike.cs | 93 + Scripts/Spells/Seventh/GateTravel.cs | 225 + Scripts/Spells/Seventh/ManaVampire.cs | 119 + Scripts/Spells/Seventh/MassDispel.cs | 115 + Scripts/Spells/Seventh/MeteorSwarm.cs | 137 + Scripts/Spells/Seventh/Polymorph.cs | 201 + Scripts/Spells/Sixth/Dispel.cs | 98 + Scripts/Spells/Sixth/EnergyBolt.cs | 96 + Scripts/Spells/Sixth/Explosion.cs | 130 + Scripts/Spells/Sixth/Invisibility.cs | 123 + Scripts/Spells/Sixth/Mark.cs | 93 + Scripts/Spells/Sixth/MassCurse.cs | 105 + Scripts/Spells/Sixth/ParalyzeField.cs | 229 + Scripts/Spells/Sixth/Reveal.cs | 121 + Scripts/Spells/Third/Bless.cs | 71 + Scripts/Spells/Third/Fireball.cs | 92 + Scripts/Spells/Third/MagicLock.cs | 86 + Scripts/Spells/Third/Poison.cs | 123 + Scripts/Spells/Third/Telekinesis.cs | 109 + Scripts/Spells/Third/Teleport.cs | 125 + Scripts/Spells/Third/Unlock.cs | 103 + Scripts/Spells/Third/WallOfStone.cs | 208 + Scripts/Spells/UnsummonTimer.cs | 24 + Scripts/Targets/BladedItemTarget.cs | 124 + Scripts/Targets/MoveTarget.cs | 52 + Scripts/Targets/PickMoveTarget.cs | 27 + Server/AggressorInfo.cs | 212 + Server/AssemblyInfo.cs | 78 + Server/Attributes.cs | 197 + Server/BaseVendor.cs | 213 + Server/Body.cs | 269 + Server/ClientVersion.cs | 347 + Server/Commands.cs | 347 + Server/ContextMenus/ContextMenu.cs | 98 + Server/ContextMenus/ContextMenuEntry.cs | 139 + Server/ContextMenus/OpenBackpackEntry.cs | 40 + Server/ContextMenus/PaperdollEntry.cs | 40 + Server/Effects.cs | 408 + Server/EventLog.cs | 67 + Server/EventSink.cs | 1110 ++ Server/ExpansionInfo.cs | 104 + Server/Geometry.cs | 638 + Server/Guild.cs | 139 + Server/Gumps/Gump.cs | 418 + Server/Gumps/GumpAlphaRegion.cs | 103 + Server/Gumps/GumpBackground.cs | 118 + Server/Gumps/GumpButton.cs | 164 + Server/Gumps/GumpCheck.cs | 135 + Server/Gumps/GumpEntry.cs | 98 + Server/Gumps/GumpGroup.cs | 60 + Server/Gumps/GumpHtml.cs | 147 + Server/Gumps/GumpHtmlLocalized.cs | 287 + Server/Gumps/GumpImage.cs | 117 + Server/Gumps/GumpImageTileButton.cs | 251 + Server/Gumps/GumpImageTiled.cs | 118 + Server/Gumps/GumpItem.cs | 114 + Server/Gumps/GumpLabel.cs | 104 + Server/Gumps/GumpLabelCropped.cs | 133 + Server/Gumps/GumpPage.cs | 60 + Server/Gumps/GumpRadio.cs | 135 + Server/Gumps/GumpTextEntry.cs | 150 + Server/Gumps/GumpTooltip.cs | 60 + Server/Gumps/RelayInfo.cs | 116 + Server/HuePicker.cs | 69 + Server/IAccount.cs | 39 + Server/IEntity.cs | 114 + Server/Insensitive.cs | 81 + Server/Interfaces.cs | 97 + Server/Item.cs | 4474 +++++++ Server/ItemBounds.cs | 69 + Server/Items/BaseMulti.cs | 141 + Server/Items/Container.cs | 1692 +++ Server/Items/Containers.cs | 166 + Server/Items/SecureTradeContainer.cs | 119 + Server/Items/VirtualHair.cs | 177 + Server/KeywordList.cs | 85 + Server/Main.cs | 603 + Server/Map.cs | 2074 +++ Server/Menus/IMenu.cs | 34 + Server/Menus/ItemListMenu.cs | 140 + Server/Menus/QuestionMenu.cs | 96 + Server/Mobile.cs | 10958 ++++++++++++++++ Server/Movement.cs | 75 + Server/MultiData.cs | 584 + Server/Network/BufferPool.cs | 102 + Server/Network/ByteQueue.cs | 153 + Server/Network/Compression.cs | 281 + Server/Network/EncodedPacketHandler.cs | 64 + Server/Network/EncodedReader.cs | 85 + Server/Network/Listener.cs | 193 + Server/Network/MessagePump.cs | 256 + Server/Network/NetState.cs | 907 ++ Server/Network/PacketHandler.cs | 82 + Server/Network/PacketHandlers.cs | 2261 ++++ Server/Network/PacketProfile.cs | 159 + Server/Network/PacketReader.cs | 457 + Server/Network/PacketWriter.cs | 409 + Server/Network/Packets.cs | 3678 ++++++ Server/Network/SendQueue.cs | 223 + Server/Notoriety.cs | 80 + Server/ObjectPropertyList.cs | 198 + Server/Party.cs | 39 + Server/Persistence/BinaryMemoryWriter.cs | 86 + Server/Persistence/DualSaveStrategy.cs | 54 + Server/Persistence/FileOperations.cs | 141 + Server/Persistence/FileQueue.cs | 251 + Server/Persistence/ParallelSaveStrategy.cs | 342 + Server/Persistence/SaveMetrics.cs | 129 + Server/Persistence/SaveStrategy.cs | 50 + Server/Persistence/SequentialFileWriter.cs | 141 + Server/Persistence/StandardSaveStrategy.cs | 181 + Server/Point3DList.cs | 113 + Server/Poison.cs | 146 + Server/Prompt.cs | 55 + Server/QuestArrow.cs | 86 + Server/Race.cs | 211 + Server/Region.cs | 1272 ++ Server/ScriptCompiler.cs | 640 + Server/Sector.cs | 365 + Server/SecureTrade.cs | 282 + Server/Serial.cs | 172 + Server/Serialization.cs | 1706 +++ Server/Skills.cs | 1100 ++ Server/Targeting/LandTarget.cs | 104 + Server/Targeting/MultiTarget.cs | 71 + Server/Targeting/StaticTarget.cs | 100 + Server/Targeting/Target.cs | 376 + Server/Targeting/TargetCancelType.cs | 32 + Server/Targeting/TargetFlags.cs | 31 + Server/TileData.cs | 293 + Server/TileList.cs | 92 + Server/TileMatrix.cs | 616 + Server/TileMatrixPatch.cs | 243 + Server/Timer.cs | 693 + Server/Utility.cs | 1092 ++ Server/VirtueInfo.cs | 147 + Server/World.cs | 789 ++ Server/runuo.ico | Bin 0 -> 13094 bytes 2644 files changed, 479454 insertions(+) create mode 100644 Data/Assemblies.cfg create mode 100644 Data/Binary/Bounds.bin create mode 100644 Data/Bulk Orders/Blacksmith/armor.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largeaxes.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largechain.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largefencing.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largemaces.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largeplate.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largepolearms.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largering.cfg create mode 100644 Data/Bulk Orders/Blacksmith/largeswords.cfg create mode 100644 Data/Bulk Orders/Blacksmith/weapons.cfg create mode 100644 Data/Bulk Orders/Tailoring/boneset.cfg create mode 100644 Data/Bulk Orders/Tailoring/cloth.cfg create mode 100644 Data/Bulk Orders/Tailoring/farmer.cfg create mode 100644 Data/Bulk Orders/Tailoring/femaleleatherset.cfg create mode 100644 Data/Bulk Orders/Tailoring/fishergirl.cfg create mode 100644 Data/Bulk Orders/Tailoring/gypsy.cfg create mode 100644 Data/Bulk Orders/Tailoring/hatset.cfg create mode 100644 Data/Bulk Orders/Tailoring/jester.cfg create mode 100644 Data/Bulk Orders/Tailoring/lady.cfg create mode 100644 Data/Bulk Orders/Tailoring/leather.cfg create mode 100644 Data/Bulk Orders/Tailoring/maleleatherset.cfg create mode 100644 Data/Bulk Orders/Tailoring/pirate.cfg create mode 100644 Data/Bulk Orders/Tailoring/shoeset.cfg create mode 100644 Data/Bulk Orders/Tailoring/studdedset.cfg create mode 100644 Data/Bulk Orders/Tailoring/towncrier.cfg create mode 100644 Data/Bulk Orders/Tailoring/wizard.cfg create mode 100644 Data/Common.MAP create mode 100644 Data/Components/doors.txt create mode 100644 Data/Components/floors.txt create mode 100644 Data/Components/misc.txt create mode 100644 Data/Components/roof.txt create mode 100644 Data/Components/stairs.txt create mode 100644 Data/Components/suppinfo.txt create mode 100644 Data/Components/teleprts.txt create mode 100644 Data/Components/walls.txt create mode 100644 Data/Decoration/Britannia/_covetous.cfg create mode 100644 Data/Decoration/Britannia/_deceit.cfg create mode 100644 Data/Decoration/Britannia/_despise.cfg create mode 100644 Data/Decoration/Britannia/_destard.cfg create mode 100644 Data/Decoration/Britannia/_fire.cfg create mode 100644 Data/Decoration/Britannia/_hedgemaze.cfg create mode 100644 Data/Decoration/Britannia/_hythloth.cfg create mode 100644 Data/Decoration/Britannia/_ice.cfg create mode 100644 Data/Decoration/Britannia/_orccave.cfg create mode 100644 Data/Decoration/Britannia/_shame.cfg create mode 100644 Data/Decoration/Britannia/_terathankeep.cfg create mode 100644 Data/Decoration/Britannia/_wrong.cfg create mode 100644 Data/Decoration/Britannia/britain.cfg create mode 100644 Data/Decoration/Britannia/bucs.cfg create mode 100644 Data/Decoration/Britannia/bucsportal.cfg create mode 100644 Data/Decoration/Britannia/cove.cfg create mode 100644 Data/Decoration/Britannia/delucia.cfg create mode 100644 Data/Decoration/Britannia/despise.cfg create mode 100644 Data/Decoration/Britannia/destard.cfg create mode 100644 Data/Decoration/Britannia/hythloth.cfg create mode 100644 Data/Decoration/Britannia/jhelom.cfg create mode 100644 Data/Decoration/Britannia/magincia.cfg create mode 100644 Data/Decoration/Britannia/minoc.cfg create mode 100644 Data/Decoration/Britannia/moonglow.cfg create mode 100644 Data/Decoration/Britannia/nujelm.cfg create mode 100644 Data/Decoration/Britannia/papua.cfg create mode 100644 Data/Decoration/Britannia/patches.cfg create mode 100644 Data/Decoration/Britannia/serpent pillars.cfg create mode 100644 Data/Decoration/Britannia/serpentshold.cfg create mode 100644 Data/Decoration/Britannia/shame.cfg create mode 100644 Data/Decoration/Britannia/shrines.cfg create mode 100644 Data/Decoration/Britannia/skara.cfg create mode 100644 Data/Decoration/Britannia/solen matriarch quest.cfg create mode 100644 Data/Decoration/Britannia/study of solen quest.cfg create mode 100644 Data/Decoration/Britannia/trinsic.cfg create mode 100644 Data/Decoration/Britannia/vesper.cfg create mode 100644 Data/Decoration/Britannia/wind.cfg create mode 100644 Data/Decoration/Britannia/witch apprentice quest.cfg create mode 100644 Data/Decoration/Britannia/yew.cfg create mode 100644 Data/Decoration/Felucca/_champion teleporters.cfg create mode 100644 Data/Decoration/Felucca/_star room.cfg create mode 100644 Data/Decoration/Felucca/ambitious solen queen quest.cfg create mode 100644 Data/Decoration/Felucca/khaldun entrance.cfg create mode 100644 Data/Decoration/Felucca/khaldun.cfg create mode 100644 Data/Decoration/Felucca/ocllo.cfg create mode 100644 Data/Decoration/Felucca/solen matriarch quest.cfg create mode 100644 Data/Decoration/Felucca/yew.cfg create mode 100644 Data/Decoration/Ilshenar/_blood.cfg create mode 100644 Data/Decoration/Ilshenar/_central.cfg create mode 100644 Data/Decoration/Ilshenar/_exodus.cfg create mode 100644 Data/Decoration/Ilshenar/_gwenno.cfg create mode 100644 Data/Decoration/Ilshenar/_ratcave.cfg create mode 100644 Data/Decoration/Ilshenar/_regvolom.cfg create mode 100644 Data/Decoration/Ilshenar/_serpentine.cfg create mode 100644 Data/Decoration/Ilshenar/_sheepfarm.cfg create mode 100644 Data/Decoration/Ilshenar/_sorcerers.cfg create mode 100644 Data/Decoration/Ilshenar/_terortskitas.cfg create mode 100644 Data/Decoration/Ilshenar/_twinoaks.cfg create mode 100644 Data/Decoration/Ilshenar/camp.cfg create mode 100644 Data/Decoration/Ilshenar/camp2.cfg create mode 100644 Data/Decoration/Ilshenar/enslavedgargs.cfg create mode 100644 Data/Decoration/Ilshenar/gargoylecity.cfg create mode 100644 Data/Decoration/Ilshenar/golemsandjukas.cfg create mode 100644 Data/Decoration/Ilshenar/lakeshire.cfg create mode 100644 Data/Decoration/Ilshenar/meercatacombs.cfg create mode 100644 Data/Decoration/Ilshenar/misc.cfg create mode 100644 Data/Decoration/Ilshenar/mistas.cfg create mode 100644 Data/Decoration/Ilshenar/patches.cfg create mode 100644 Data/Decoration/Ilshenar/ratfortress.cfg create mode 100644 Data/Decoration/Ilshenar/savagecamp.cfg create mode 100644 Data/Decoration/Ilshenar/shrines.cfg create mode 100644 Data/Decoration/Ilshenar/vinculuminn.cfg create mode 100644 Data/Decoration/Ilshenar/wispdungeon.cfg create mode 100644 Data/Decoration/Malas/_luna.cfg create mode 100644 Data/Decoration/Malas/_umbra.cfg create mode 100644 Data/Decoration/Malas/dark tides quest.cfg create mode 100644 Data/Decoration/Malas/doom.cfg create mode 100644 Data/Decoration/Malas/emino's undertaking quest.cfg create mode 100644 Data/Decoration/Malas/haochi's trials quest.cfg create mode 100644 Data/Decoration/Malas/luna.cfg create mode 100644 Data/Decoration/Malas/markcontainers.cfg create mode 100644 Data/Decoration/Malas/summoning quest.cfg create mode 100644 Data/Decoration/Tokuno/terrible hatchlings quest.cfg create mode 100644 Data/Decoration/Tokuno/tokuno.cfg create mode 100644 Data/Decoration/Tokuno/treasures of tokuno.cfg create mode 100644 Data/Decoration/Trammel/_haven additions.cfg create mode 100644 Data/Decoration/Trammel/_minoc camp.cfg create mode 100644 Data/Decoration/Trammel/ambitious solen queen quest.cfg create mode 100644 Data/Decoration/Trammel/collector quest.cfg create mode 100644 Data/Decoration/Trammel/haven.cfg create mode 100644 Data/Decoration/Trammel/magincia.cfg create mode 100644 Data/Decoration/Trammel/solen matriarch quest.cfg create mode 100644 Data/Decoration/Trammel/uzeraan turmoil quest.cfg create mode 100644 Data/Decoration/Trammel/yew.cfg create mode 100644 Data/Locations/felucca.xml create mode 100644 Data/Locations/ilshenar.xml create mode 100644 Data/Locations/malas.xml create mode 100644 Data/Locations/tokuno.xml create mode 100644 Data/Locations/trammel.xml create mode 100644 Data/Regions.xml create mode 100644 Data/SpawnDefinitions.xml create mode 100644 Data/bodyTable.cfg create mode 100644 Data/containers.cfg create mode 100644 Data/items.cfg create mode 100644 Data/mobiles.cfg create mode 100644 Data/names.xml create mode 100644 Data/objects.xml create mode 100644 Data/pageresponse.cfg create mode 100644 Data/shrink.cfg create mode 100644 Data/signs.cfg create mode 100644 Data/treasure.cfg create mode 100644 Scripts/Accounting/AccessRestrictions.cs create mode 100644 Scripts/Accounting/Account.cs create mode 100644 Scripts/Accounting/AccountAttackLimiter.cs create mode 100644 Scripts/Accounting/AccountComment.cs create mode 100644 Scripts/Accounting/AccountHandler.cs create mode 100644 Scripts/Accounting/AccountTag.cs create mode 100644 Scripts/Accounting/Accounts.cs create mode 100644 Scripts/Accounting/Firewall.cs create mode 100644 Scripts/Accounting/IPLimiter.cs create mode 100644 Scripts/Commands/Add.cs create mode 100644 Scripts/Commands/Attributes.cs create mode 100644 Scripts/Commands/Batch.cs create mode 100644 Scripts/Commands/BoundingBoxPicker.cs create mode 100644 Scripts/Commands/ConvertPlayers.cs create mode 100644 Scripts/Commands/Decorate.cs create mode 100644 Scripts/Commands/Docs.cs create mode 100644 Scripts/Commands/Dupe.cs create mode 100644 Scripts/Commands/ExportWSC.cs create mode 100644 Scripts/Commands/GenCategorization.cs create mode 100644 Scripts/Commands/GenTeleporter.cs create mode 100644 Scripts/Commands/Generic/Commands/BaseCommand.cs create mode 100644 Scripts/Commands/Generic/Commands/Commands.cs create mode 100644 Scripts/Commands/Generic/Commands/Interface.cs create mode 100644 Scripts/Commands/Generic/Extensions/BaseExtension.cs create mode 100644 Scripts/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs create mode 100644 Scripts/Commands/Generic/Extensions/Compilers/DistinctCompiler.cs create mode 100644 Scripts/Commands/Generic/Extensions/Compilers/SortCompiler.cs create mode 100644 Scripts/Commands/Generic/Extensions/DistinctExtension.cs create mode 100644 Scripts/Commands/Generic/Extensions/LimitExtension.cs create mode 100644 Scripts/Commands/Generic/Extensions/SortExtension.cs create mode 100644 Scripts/Commands/Generic/Extensions/WhereExtension.cs create mode 100644 Scripts/Commands/Generic/Implementors/AreaCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/ContainedCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/GlobalCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/MultiCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/ObjectConditional.cs create mode 100644 Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/RegionCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/SelfCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/SerialCommandImplementor.cs create mode 100644 Scripts/Commands/Generic/Implementors/SingleCommandImplementor.cs create mode 100644 Scripts/Commands/Handlers.cs create mode 100644 Scripts/Commands/Logging.cs create mode 100644 Scripts/Commands/Properties.cs create mode 100644 Scripts/Commands/SignParser.cs create mode 100644 Scripts/Commands/Skills.cs create mode 100644 Scripts/Commands/SkillsMenu.cs create mode 100644 Scripts/Commands/Statics.cs create mode 100644 Scripts/Commands/VisibilityList.cs create mode 100644 Scripts/Commands/Wipe.cs create mode 100644 Scripts/Context Menus/AddToSpellbookEntry.cs create mode 100644 Scripts/Context Menus/EatEntry.cs create mode 100644 Scripts/Context Menus/OpenBankEntry.cs create mode 100644 Scripts/Context Menus/TeachEntry.cs create mode 100644 Scripts/Engines/AI/AI/AnimalAI.cs create mode 100644 Scripts/Engines/AI/AI/ArcherAI.cs create mode 100644 Scripts/Engines/AI/AI/BaseAI.cs create mode 100644 Scripts/Engines/AI/AI/BerserkAI.cs create mode 100644 Scripts/Engines/AI/AI/HealerAI.cs create mode 100644 Scripts/Engines/AI/AI/MageAI.cs create mode 100644 Scripts/Engines/AI/AI/MeleeAI.cs create mode 100644 Scripts/Engines/AI/AI/PredatorAI.cs create mode 100644 Scripts/Engines/AI/AI/ThiefAI.cs create mode 100644 Scripts/Engines/AI/AI/VendorAI.cs create mode 100644 Scripts/Engines/AI/Creature/BaseCreature.cs create mode 100644 Scripts/Engines/AI/Creature/Dummy.cs create mode 100644 Scripts/Engines/AI/Creature/DummySpecific.cs create mode 100644 Scripts/Engines/AI/Creature/OppositionGroup.cs create mode 100644 Scripts/Engines/AI/Creature/Paragon.cs create mode 100644 Scripts/Engines/AI/Creature/SpeedInfo.cs create mode 100644 Scripts/Engines/AI/Targets/AIControlMobileTarget.cs create mode 100644 Scripts/Engines/AI/Team/Team.cs create mode 100644 Scripts/Engines/AI/Team/TeamRegistry.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BOBFilter.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BOBFilterGump.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BOBGump.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BOBLargeEntry.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BODBuyGump.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BODType.cs create mode 100644 Scripts/Engines/BulkOrders/Books/BulkOrderBook.cs create mode 100644 Scripts/Engines/BulkOrders/BulkMaterialType.cs create mode 100644 Scripts/Engines/BulkOrders/LargeBOD.cs create mode 100644 Scripts/Engines/BulkOrders/LargeBODAcceptGump.cs create mode 100644 Scripts/Engines/BulkOrders/LargeBODGump.cs create mode 100644 Scripts/Engines/BulkOrders/LargeBODTarget.cs create mode 100644 Scripts/Engines/BulkOrders/LargeBulkEntry.cs create mode 100644 Scripts/Engines/BulkOrders/LargeSmithBOD.cs create mode 100644 Scripts/Engines/BulkOrders/LargeTailorBOD.cs create mode 100644 Scripts/Engines/BulkOrders/Rewards.cs create mode 100644 Scripts/Engines/BulkOrders/SmallBOD.cs create mode 100644 Scripts/Engines/BulkOrders/SmallBODAcceptGump.cs create mode 100644 Scripts/Engines/BulkOrders/SmallBODGump.cs create mode 100644 Scripts/Engines/BulkOrders/SmallBODTarget.cs create mode 100644 Scripts/Engines/BulkOrders/SmallBulkEntry.cs create mode 100644 Scripts/Engines/BulkOrders/SmallSmithBOD.cs create mode 100644 Scripts/Engines/BulkOrders/SmallTailorBOD.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionAltar.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionPlatform.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionSkull.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionSkullBrazier.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionSkullPlatform.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionSkullType.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionSpawn.cs create mode 100644 Scripts/Engines/CannedEvil/ChampionSpawnType.cs create mode 100644 Scripts/Engines/CannedEvil/HarrowerGate.cs create mode 100644 Scripts/Engines/CannedEvil/RestartTimer.cs create mode 100644 Scripts/Engines/CannedEvil/SliceTimer.cs create mode 100644 Scripts/Engines/CannedEvil/StarRoomGate.cs create mode 100644 Scripts/Engines/Chat/Channel.cs create mode 100644 Scripts/Engines/Chat/Chat.cs create mode 100644 Scripts/Engines/Chat/ChatActionHandler.cs create mode 100644 Scripts/Engines/Chat/ChatActionHandlers.cs create mode 100644 Scripts/Engines/Chat/ChatCommand.cs create mode 100644 Scripts/Engines/Chat/ChatUser.cs create mode 100644 Scripts/Engines/Chat/Chatold.cs create mode 100644 Scripts/Engines/Chat/Packets.cs create mode 100644 Scripts/Engines/Craft/Core/CraftContext.cs create mode 100644 Scripts/Engines/Craft/Core/CraftGroup.cs create mode 100644 Scripts/Engines/Craft/Core/CraftGroupCol.cs create mode 100644 Scripts/Engines/Craft/Core/CraftGump.cs create mode 100644 Scripts/Engines/Craft/Core/CraftGumpItem.cs create mode 100644 Scripts/Engines/Craft/Core/CraftItem.cs create mode 100644 Scripts/Engines/Craft/Core/CraftItemCol.cs create mode 100644 Scripts/Engines/Craft/Core/CraftItemIDAttribute.cs create mode 100644 Scripts/Engines/Craft/Core/CraftRes.cs create mode 100644 Scripts/Engines/Craft/Core/CraftResCol.cs create mode 100644 Scripts/Engines/Craft/Core/CraftSkill.cs create mode 100644 Scripts/Engines/Craft/Core/CraftSkillCol.cs create mode 100644 Scripts/Engines/Craft/Core/CraftSubRes.cs create mode 100644 Scripts/Engines/Craft/Core/CraftSubResCol.cs create mode 100644 Scripts/Engines/Craft/Core/CraftSystem.cs create mode 100644 Scripts/Engines/Craft/Core/CustomCraft.cs create mode 100644 Scripts/Engines/Craft/Core/Enhance.cs create mode 100644 Scripts/Engines/Craft/Core/QueryMakersMarkGump.cs create mode 100644 Scripts/Engines/Craft/Core/Recipes.cs create mode 100644 Scripts/Engines/Craft/Core/Repair.cs create mode 100644 Scripts/Engines/Craft/Core/Resmelt.cs create mode 100644 Scripts/Engines/Craft/DefAlchemy.cs create mode 100644 Scripts/Engines/Craft/DefBlacksmithy.cs create mode 100644 Scripts/Engines/Craft/DefBowFletching.cs create mode 100644 Scripts/Engines/Craft/DefCarpentry.cs create mode 100644 Scripts/Engines/Craft/DefCartography.cs create mode 100644 Scripts/Engines/Craft/DefCooking.cs create mode 100644 Scripts/Engines/Craft/DefGlassblowing.cs create mode 100644 Scripts/Engines/Craft/DefInscription.cs create mode 100644 Scripts/Engines/Craft/DefMasonry.cs create mode 100644 Scripts/Engines/Craft/DefTailoring.cs create mode 100644 Scripts/Engines/Craft/DefTinkering.cs create mode 100644 Scripts/Engines/Doom/GauntletSpawner.cs create mode 100644 Scripts/Engines/Ethics/Core/Ethic.cs create mode 100644 Scripts/Engines/Ethics/Core/Persistance.cs create mode 100644 Scripts/Engines/Ethics/Core/Player.cs create mode 100644 Scripts/Engines/Ethics/Core/Power.cs create mode 100644 Scripts/Engines/Ethics/Definitions/EthicDefinition.cs create mode 100644 Scripts/Engines/Ethics/Definitions/PowerDefinition.cs create mode 100644 Scripts/Engines/Ethics/Evil/Ethic.cs create mode 100644 Scripts/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs create mode 100644 Scripts/Engines/Ethics/Evil/Mobiles/UnholySteed.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/Blight.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/SummonFamiliar.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/UnholyItem.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/UnholySense.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/UnholyShield.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/UnholySteed.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/UnholyWord.cs create mode 100644 Scripts/Engines/Ethics/Evil/Powers/VileBlade.cs create mode 100644 Scripts/Engines/Ethics/Hero/Ethic.cs create mode 100644 Scripts/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs create mode 100644 Scripts/Engines/Ethics/Hero/Mobiles/HolySteed.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/Bless.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/HolyBlade.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/HolyItem.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/HolySense.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/HolyShield.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/HolySteed.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/HolyWord.cs create mode 100644 Scripts/Engines/Ethics/Hero/Powers/SummonFamiliar.cs create mode 100644 Scripts/Engines/Factions/Collections/CandidateCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/FactionCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/FactionGuardCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/FactionItemCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/FactionTrapCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/FactionVendorCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/GuardListCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/MonolithCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/PlayerStateCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/SigilCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/SilverGivenCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/TownCollection.cs create mode 100644 Scripts/Engines/Factions/Collections/VendorListCollection.cs create mode 100644 Scripts/Engines/Factions/Core/Election.cs create mode 100644 Scripts/Engines/Factions/Core/Faction.cs create mode 100644 Scripts/Engines/Factions/Core/FactionItem.cs create mode 100644 Scripts/Engines/Factions/Core/FactionState.cs create mode 100644 Scripts/Engines/Factions/Core/Generator.cs create mode 100644 Scripts/Engines/Factions/Core/GuardList.cs create mode 100644 Scripts/Engines/Factions/Core/Keywords.cs create mode 100644 Scripts/Engines/Factions/Core/MerchantTitles.cs create mode 100644 Scripts/Engines/Factions/Core/Persistance.cs create mode 100644 Scripts/Engines/Factions/Core/PlayerState.cs create mode 100644 Scripts/Engines/Factions/Core/Reflector.cs create mode 100644 Scripts/Engines/Factions/Core/SilverGivenEntry.cs create mode 100644 Scripts/Engines/Factions/Core/StrongholdRegion.cs create mode 100644 Scripts/Engines/Factions/Core/Town.cs create mode 100644 Scripts/Engines/Factions/Core/TownState.cs create mode 100644 Scripts/Engines/Factions/Core/VendorList.cs create mode 100644 Scripts/Engines/Factions/Definitions/FactionDefinition.cs create mode 100644 Scripts/Engines/Factions/Definitions/FactionItemDefinition.cs create mode 100644 Scripts/Engines/Factions/Definitions/GuardDefinition.cs create mode 100644 Scripts/Engines/Factions/Definitions/RankDefinition.cs create mode 100644 Scripts/Engines/Factions/Definitions/StrongholdDefintion.cs create mode 100644 Scripts/Engines/Factions/Definitions/TownDefinition.cs create mode 100644 Scripts/Engines/Factions/Definitions/VendorDefinition.cs create mode 100644 Scripts/Engines/Factions/Gumps/ElectionGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/ElectionManagementGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/FactionGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/FactionImbueGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/FactionStoneGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/FinanceGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/HorseBreederGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/JoinStoneGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/LeaveFactionGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/SheriffGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/TownStoneGump.cs create mode 100644 Scripts/Engines/Factions/Gumps/VoteGump.cs create mode 100644 Scripts/Engines/Factions/Instances/Factions/CouncilOfMages.cs create mode 100644 Scripts/Engines/Factions/Instances/Factions/Minax.cs create mode 100644 Scripts/Engines/Factions/Instances/Factions/Shadowlords.cs create mode 100644 Scripts/Engines/Factions/Instances/Factions/TrueBritannians.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Britain.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Magincia.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Minoc.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Moonglow.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/SkaraBrae.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Trinsic.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Vesper.cs create mode 100644 Scripts/Engines/Factions/Instances/Towns/Yew.cs create mode 100644 Scripts/Engines/Factions/Items/BaseMonolith.cs create mode 100644 Scripts/Engines/Factions/Items/BaseSystemController.cs create mode 100644 Scripts/Engines/Factions/Items/FactionStone.cs create mode 100644 Scripts/Engines/Factions/Items/JoinStone.cs create mode 100644 Scripts/Engines/Factions/Items/Sigil.cs create mode 100644 Scripts/Engines/Factions/Items/Silver.cs create mode 100644 Scripts/Engines/Factions/Items/StrongholdMonolith.cs create mode 100644 Scripts/Engines/Factions/Items/TownMonolith.cs create mode 100644 Scripts/Engines/Factions/Items/TownStone.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/FactionExplosionTrap.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/FactionGasTrap.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/FactionSawTrap.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/FactionSpikeTrap.cs create mode 100644 Scripts/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs create mode 100644 Scripts/Engines/Factions/Mobiles/FactionWarHorse.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/GuardAI.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Orders.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionBerserker.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDeathKnight.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDragoon.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionHenchman.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionKnight.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionMercenary.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionNecromancer.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionPaladin.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionSorceress.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Guards/Types/FactionWizard.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Vendors/BaseFactionVendor.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Vendors/FactionBoardVendor.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Vendors/FactionBottleVendor.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Vendors/FactionHorseVendor.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Vendors/FactionOreVendor.cs create mode 100644 Scripts/Engines/Factions/Mobiles/Vendors/FactionReagentVendor.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestBank.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestDefinition.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestResource.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestSoundTimer.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestSystem.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestTarget.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestTimer.cs create mode 100644 Scripts/Engines/Harvest/Core/HarvestVein.cs create mode 100644 Scripts/Engines/Harvest/Fishing.cs create mode 100644 Scripts/Engines/Harvest/Lumberjacking.cs create mode 100644 Scripts/Engines/Harvest/Mining.cs create mode 100644 Scripts/Engines/Help/HelpGump.cs create mode 100644 Scripts/Engines/Help/PagePrompt.cs create mode 100644 Scripts/Engines/Help/PagePromptGump.cs create mode 100644 Scripts/Engines/Help/PageQueue.cs create mode 100644 Scripts/Engines/Help/PageQueueGump.cs create mode 100644 Scripts/Engines/Help/PageResponseGump.cs create mode 100644 Scripts/Engines/Help/SpeechLog.cs create mode 100644 Scripts/Engines/Help/SpeechLogGump.cs create mode 100644 Scripts/Engines/Help/StuckMenu.cs create mode 100644 Scripts/Engines/Khaldun/Books/GrimmochJournal.cs create mode 100644 Scripts/Engines/Khaldun/Books/LysanderNotebook.cs create mode 100644 Scripts/Engines/Khaldun/Books/TavarasJournal.cs create mode 100644 Scripts/Engines/Khaldun/KhaldunGen.cs create mode 100644 Scripts/Engines/Khaldun/KhaldunPitTeleporter.cs create mode 100644 Scripts/Engines/Khaldun/Mobiles/GrimmochDrummel.cs create mode 100644 Scripts/Engines/Khaldun/Mobiles/LysanderGathenwale.cs create mode 100644 Scripts/Engines/Khaldun/Mobiles/MorgBergen.cs create mode 100644 Scripts/Engines/Khaldun/Mobiles/TavaraSewel.cs create mode 100644 Scripts/Engines/Khaldun/PuzzleChest.cs create mode 100644 Scripts/Engines/Khaldun/RaisableItem.cs create mode 100644 Scripts/Engines/Khaldun/RaiseSwitch.cs create mode 100644 Scripts/Engines/MyRunUO/Config.cs create mode 100644 Scripts/Engines/MyRunUO/DatabaseCommandQueue.cs create mode 100644 Scripts/Engines/MyRunUO/LayerComparer.cs create mode 100644 Scripts/Engines/MyRunUO/MyRunUO.cs create mode 100644 Scripts/Engines/MyRunUO/MyRunUOStatus.cs create mode 100644 Scripts/Engines/Party/AddPartyTarget.cs create mode 100644 Scripts/Engines/Party/DeclineTimer.cs create mode 100644 Scripts/Engines/Party/Packets.cs create mode 100644 Scripts/Engines/Party/Party.cs create mode 100644 Scripts/Engines/Party/PartyCommands.cs create mode 100644 Scripts/Engines/Party/PartyMemberInfo.cs create mode 100644 Scripts/Engines/Party/RemovePartyTarget.cs create mode 100644 Scripts/Engines/Pathing/FastAStarAlgorithm.cs create mode 100644 Scripts/Engines/Pathing/MoveResult.cs create mode 100644 Scripts/Engines/Pathing/Movement.cs create mode 100644 Scripts/Engines/Pathing/MovementPath.cs create mode 100644 Scripts/Engines/Pathing/PathAlgorithm.cs create mode 100644 Scripts/Engines/Pathing/PathFollower.cs create mode 100644 Scripts/Engines/Pathing/SlowAStarAlgorithm.cs create mode 100644 Scripts/Engines/Plants/EmptyTheBowlGump.cs create mode 100644 Scripts/Engines/Plants/MainPlantGump.cs create mode 100644 Scripts/Engines/Plants/MiscItems/FertileDirt.cs create mode 100644 Scripts/Engines/Plants/MiscItems/GreenThorns.cs create mode 100644 Scripts/Engines/Plants/MiscItems/OrangePetals.cs create mode 100644 Scripts/Engines/Plants/MiscItems/RedLeaves.cs create mode 100644 Scripts/Engines/Plants/MiscMobiles/GiantIceWorm.cs create mode 100644 Scripts/Engines/Plants/Network/DisplayHelpTopic.cs create mode 100644 Scripts/Engines/Plants/PlantBowl.cs create mode 100644 Scripts/Engines/Plants/PlantHue.cs create mode 100644 Scripts/Engines/Plants/PlantItem.cs create mode 100644 Scripts/Engines/Plants/PlantPourTarget.cs create mode 100644 Scripts/Engines/Plants/PlantResources.cs create mode 100644 Scripts/Engines/Plants/PlantSystem.cs create mode 100644 Scripts/Engines/Plants/PlantType.cs create mode 100644 Scripts/Engines/Plants/PollinateTarget.cs create mode 100644 Scripts/Engines/Plants/ReproductionGump.cs create mode 100644 Scripts/Engines/Plants/Seed.cs create mode 100644 Scripts/Engines/Plants/SetToDecorativeGump.cs create mode 100644 Scripts/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs create mode 100644 Scripts/Engines/Quests/Ambitious Solen Queen/Conversations.cs create mode 100644 Scripts/Engines/Quests/Ambitious Solen Queen/Mobiles/AmbitiousSolenQueen.cs create mode 100644 Scripts/Engines/Quests/Ambitious Solen Queen/Objectives.cs create mode 100644 Scripts/Engines/Quests/Collector/CollectorQuest.cs create mode 100644 Scripts/Engines/Quests/Collector/Conversations.cs create mode 100644 Scripts/Engines/Quests/Collector/Items/EnchantedPaints.cs create mode 100644 Scripts/Engines/Quests/Collector/Items/ImageType.cs create mode 100644 Scripts/Engines/Quests/Collector/Items/Obsidian.cs create mode 100644 Scripts/Engines/Quests/Collector/Items/PaintedImage.cs create mode 100644 Scripts/Engines/Quests/Collector/Mobiles/AlbertaGiacco.cs create mode 100644 Scripts/Engines/Quests/Collector/Mobiles/ElwoodMcCarrin.cs create mode 100644 Scripts/Engines/Quests/Collector/Mobiles/GabrielPiete.cs create mode 100644 Scripts/Engines/Quests/Collector/Mobiles/Impresario.cs create mode 100644 Scripts/Engines/Quests/Collector/Mobiles/TomasONeerlan.cs create mode 100644 Scripts/Engines/Quests/Collector/Objectives.cs create mode 100644 Scripts/Engines/Quests/Core/BaseQuester.cs create mode 100644 Scripts/Engines/Quests/Core/Items/DynamicTeleporter.cs create mode 100644 Scripts/Engines/Quests/Core/Items/EnchantedSextant.cs create mode 100644 Scripts/Engines/Quests/Core/Items/HornOfRetreat.cs create mode 100644 Scripts/Engines/Quests/Core/Items/QuestItem.cs create mode 100644 Scripts/Engines/Quests/Core/QuestCallbackEntry.cs create mode 100644 Scripts/Engines/Quests/Core/QuestConversation.cs create mode 100644 Scripts/Engines/Quests/Core/QuestItemInfo.cs create mode 100644 Scripts/Engines/Quests/Core/QuestObjective.cs create mode 100644 Scripts/Engines/Quests/Core/QuestRestartInfo.cs create mode 100644 Scripts/Engines/Quests/Core/QuestSerializer.cs create mode 100644 Scripts/Engines/Quests/Core/QuestSystem.cs create mode 100644 Scripts/Engines/Quests/Core/Regions/CancelQuestRegion.cs create mode 100644 Scripts/Engines/Quests/Core/Regions/QuestCompleteObjectiveRegion.cs create mode 100644 Scripts/Engines/Quests/Core/Regions/QuestNoEntryRegion.cs create mode 100644 Scripts/Engines/Quests/Core/Regions/QuestOfferRegion.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Conversations.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/DarkTidesQuest.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/CrystalCaveBarrier.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/DarkTidesHorn.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/DarkTidesTeleporter.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/KronusScroll.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Items/VaultOfSecretsBarrier.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Mobiles/Horus.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Mobiles/Maabus.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs create mode 100644 Scripts/Engines/Quests/Dark Tides/Objectives.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Conversations.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/EminosUndertakingQuest.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/BlueNinjaQuestTeleporter.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/GreenNinjaQuestTeleporter.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/GuardianBarrier.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Items/WhiteNinjaQuestTeleporter.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Mobiles/EnshroudedFigure.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Mobiles/HiddenFigure.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Mobiles/JedahEntille.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Zoel.cs create mode 100644 Scripts/Engines/Quests/Emino's Undertaking/Objectives.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Conversations.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/HaochisTrialsQuest.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Items/HonorCandle.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/Haochi.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/HaochisGuardsman.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/Relnia.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs create mode 100644 Scripts/Engines/Quests/Haochi's Trials/Objectives.cs create mode 100644 Scripts/Engines/Quests/Solen Matriarch/Conversations.cs create mode 100644 Scripts/Engines/Quests/Solen Matriarch/Mobiles/SolenMatriarch.cs create mode 100644 Scripts/Engines/Quests/Solen Matriarch/Objectives.cs create mode 100644 Scripts/Engines/Quests/Solen Matriarch/SolenMatriarchQuest.cs create mode 100644 Scripts/Engines/Quests/Study of the Solen Hive/Conversations.cs create mode 100644 Scripts/Engines/Quests/Study of the Solen Hive/Mobiles/Naturalist.cs create mode 100644 Scripts/Engines/Quests/Study of the Solen Hive/NestArea.cs create mode 100644 Scripts/Engines/Quests/Study of the Solen Hive/Objectives.cs create mode 100644 Scripts/Engines/Quests/Study of the Solen Hive/StudyOfSolenQuest.cs create mode 100644 Scripts/Engines/Quests/Terrible Hatchlings/Conversations.cs create mode 100644 Scripts/Engines/Quests/Terrible Hatchlings/Mobiles/AnsellaGryen.cs create mode 100644 Scripts/Engines/Quests/Terrible Hatchlings/Objectives.cs create mode 100644 Scripts/Engines/Quests/Terrible Hatchlings/TerribleHatchlingsQuest.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Conversations.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Items/BellOfTheDead.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Items/ChylothShroud.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Items/ChylothStaff.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Items/GoldenSkull.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Items/GrandGrimoire.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Items/SummoningAltar.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Mobiles/Chyloth.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Mobiles/Victoria.cs create mode 100644 Scripts/Engines/Quests/The Summoning/Objectives.cs create mode 100644 Scripts/Engines/Quests/The Summoning/TheSummoningQuest.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Conversations.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/Cannon.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilHorn.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilTeleporter.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MansionGuard.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaCanoneer.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Schmendrick.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Uzeraan.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/Objectives.cs create mode 100644 Scripts/Engines/Quests/Uzeraan Turmoil/UzeraanTurmoilQuest.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Conversations.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Ingredient.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Items/Cauldron.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Items/MagicFlute.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Mobiles/Grizelda.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/Objectives.cs create mode 100644 Scripts/Engines/Quests/Witch Apprentice/WitchApprenticeQuest.cs create mode 100644 Scripts/Engines/RemoteAdmin/Network.cs create mode 100644 Scripts/Engines/RemoteAdmin/PacketHandlers.cs create mode 100644 Scripts/Engines/RemoteAdmin/Packets.cs create mode 100644 Scripts/Engines/Reports/Objects/Charts/BarGraph.cs create mode 100644 Scripts/Engines/Reports/Objects/Charts/Chart.cs create mode 100644 Scripts/Engines/Reports/Objects/Charts/ChartItem.cs create mode 100644 Scripts/Engines/Reports/Objects/Charts/ChartItemCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Charts/PieChart.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/ItemValue.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/ItemValueCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/Report.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/ReportColumn.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/ReportColumnCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/ReportItem.cs create mode 100644 Scripts/Engines/Reports/Objects/Reports/ReportItemCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Snapshots/Snapshot.cs create mode 100644 Scripts/Engines/Reports/Objects/Snapshots/SnapshotCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Snapshots/SnapshotHistory.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/Info.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/PageInfo.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/PageInfoCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/QueueStatus.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/QueueStatusCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/ResponseInfo.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/ResponseInfoCollection.cs create mode 100644 Scripts/Engines/Reports/Objects/Staffing/StaffHistory.cs create mode 100644 Scripts/Engines/Reports/Persistance/PersistableObject.cs create mode 100644 Scripts/Engines/Reports/Persistance/PersistableObjectCollection.cs create mode 100644 Scripts/Engines/Reports/Persistance/PersistableType.cs create mode 100644 Scripts/Engines/Reports/Persistance/PersistanceReader.cs create mode 100644 Scripts/Engines/Reports/Persistance/PersistanceWriter.cs create mode 100644 Scripts/Engines/Reports/Rendering/BarGraphRenderer.cs create mode 100644 Scripts/Engines/Reports/Rendering/ChartRenderer.cs create mode 100644 Scripts/Engines/Reports/Rendering/DataItem.cs create mode 100644 Scripts/Engines/Reports/Rendering/HtmlRenderer.cs create mode 100644 Scripts/Engines/Reports/Rendering/PieChartRenderer.cs create mode 100644 Scripts/Engines/Reports/Reports.cs create mode 100644 Scripts/Engines/Spawner/Spawner.cs create mode 100644 Scripts/Engines/Spawner/SpawnerGump.cs create mode 100644 Scripts/Engines/Spawner/SpawnerType.cs create mode 100644 Scripts/Engines/Treasures of Tokuno/GreaterArtifacts.cs create mode 100644 Scripts/Engines/Treasures of Tokuno/LesserArtifacts.cs create mode 100644 Scripts/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardCategory.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardChoiceGump.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardConfirmGump.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardEntry.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardList.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardNoticeGump.cs create mode 100644 Scripts/Engines/VeteranRewards/RewardSystem.cs create mode 100644 Scripts/Engines/Virtues/Compassion.cs create mode 100644 Scripts/Engines/Virtues/Honor.cs create mode 100644 Scripts/Engines/Virtues/Justice.cs create mode 100644 Scripts/Engines/Virtues/Sacrifice.cs create mode 100644 Scripts/Engines/Virtues/Valor.cs create mode 100644 Scripts/Engines/Virtues/VirtueGump.cs create mode 100644 Scripts/Engines/Virtues/VirtueHelper.cs create mode 100644 Scripts/Engines/Virtues/VirtueInfoGump.cs create mode 100644 Scripts/Engines/Virtues/VirtueStatusGump.cs create mode 100644 Scripts/Gumps/AddDoorGump.cs create mode 100644 Scripts/Gumps/AddGump.cs create mode 100644 Scripts/Gumps/AdminGump.cs create mode 100644 Scripts/Gumps/BanDurationGump.cs create mode 100644 Scripts/Gumps/BaseGridGump.cs create mode 100644 Scripts/Gumps/BaseImageTileButtonsGump.cs create mode 100644 Scripts/Gumps/CategorizedAddGump.cs create mode 100644 Scripts/Gumps/ClientGump.cs create mode 100644 Scripts/Gumps/ConfirmReleaseGump.cs create mode 100644 Scripts/Gumps/Go/ChildNode.cs create mode 100644 Scripts/Gumps/Go/GoGump.cs create mode 100644 Scripts/Gumps/Go/LocationTree.cs create mode 100644 Scripts/Gumps/Go/ParentNode.cs create mode 100644 Scripts/Gumps/Guilds/DeclareFealtyGump.cs create mode 100644 Scripts/Gumps/Guilds/GrantGuildTitleGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildAbbrvPrompt.cs create mode 100644 Scripts/Gumps/Guilds/GuildAcceptWarGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildAdminCandidatesGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildCandidatesGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildChangeTypeGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildCharterGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildCharterPrompt.cs create mode 100644 Scripts/Gumps/Guilds/GuildDeclarePeaceGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildDeclareWarGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildDeclareWarPrompt.cs create mode 100644 Scripts/Gumps/Guilds/GuildDismissGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildListGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildMobileListGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildNamePrompt.cs create mode 100644 Scripts/Gumps/Guilds/GuildRejectWarGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildRescindDeclarationGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildRosterGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildTitlePrompt.cs create mode 100644 Scripts/Gumps/Guilds/GuildWarAdminGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildWarGump.cs create mode 100644 Scripts/Gumps/Guilds/GuildWebsitePrompt.cs create mode 100644 Scripts/Gumps/Guilds/GuildmasterGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/AdvancedSearch.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/BaseGuildGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/BaseGuildListGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/Create Guild Gump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/DiplomacyGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/GuildInfoGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/GuildRosterGump.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/OtherGuildInfo.cs create mode 100644 Scripts/Gumps/Guilds/New Guild System/War Declaration gump.cs create mode 100644 Scripts/Gumps/Guilds/RecruitTarget.cs create mode 100644 Scripts/Gumps/HouseDemolishGump.cs create mode 100644 Scripts/Gumps/HouseGump.cs create mode 100644 Scripts/Gumps/HouseGumpAOS.cs create mode 100644 Scripts/Gumps/HouseTransferGump.cs create mode 100644 Scripts/Gumps/NoticeGump.cs create mode 100644 Scripts/Gumps/PetResurrectGump.cs create mode 100644 Scripts/Gumps/PlayerVendorGumps.cs create mode 100644 Scripts/Gumps/PolymorphGump.cs create mode 100644 Scripts/Gumps/Properties/PropsConfig.cs create mode 100644 Scripts/Gumps/Properties/PropsGump.cs create mode 100644 Scripts/Gumps/Properties/SetBodyGump.cs create mode 100644 Scripts/Gumps/Properties/SetCustomEnumGump.cs create mode 100644 Scripts/Gumps/Properties/SetGump.cs create mode 100644 Scripts/Gumps/Properties/SetListOptionGump.cs create mode 100644 Scripts/Gumps/Properties/SetObjectGump.cs create mode 100644 Scripts/Gumps/Properties/SetObjectTarget.cs create mode 100644 Scripts/Gumps/Properties/SetPoint2DGump.cs create mode 100644 Scripts/Gumps/Properties/SetPoint3DGump.cs create mode 100644 Scripts/Gumps/Properties/SetTimeSpanGump.cs create mode 100644 Scripts/Gumps/ReclaimVendorGump.cs create mode 100644 Scripts/Gumps/ReportMurderer.cs create mode 100644 Scripts/Gumps/ResurrectGump.cs create mode 100644 Scripts/Gumps/RunebookGump.cs create mode 100644 Scripts/Gumps/SetSecureLevelGump.cs create mode 100644 Scripts/Gumps/SkillsGump.cs create mode 100644 Scripts/Gumps/TithingGump.cs create mode 100644 Scripts/Gumps/VendorInventoryGump.cs create mode 100644 Scripts/Gumps/VendorRentalGumps.cs create mode 100644 Scripts/Gumps/ViewHousesGump.cs create mode 100644 Scripts/Gumps/WarningGump.cs create mode 100644 Scripts/Gumps/WhoGump.cs create mode 100644 Scripts/Gumps/YoungGumps.cs create mode 100644 Scripts/Items/Addons/AbbatoirAddon.cs create mode 100644 Scripts/Items/Addons/AddonComponent.cs create mode 100644 Scripts/Items/Addons/AlchemistTableEastAddon.cs create mode 100644 Scripts/Items/Addons/AlchemistTableSouthAddon.cs create mode 100644 Scripts/Items/Addons/AnvilEastAddon.cs create mode 100644 Scripts/Items/Addons/AnvilSouthAddon.cs create mode 100644 Scripts/Items/Addons/ArcaneBookshelfEastAddon.cs create mode 100644 Scripts/Items/Addons/ArcaneBookshelfSouthAddon.cs create mode 100644 Scripts/Items/Addons/ArcaneCircleAddon.cs create mode 100644 Scripts/Items/Addons/ArcanistStatueEastAddon.cs create mode 100644 Scripts/Items/Addons/ArcanistStatueSouthAddon.cs create mode 100644 Scripts/Items/Addons/ArcheryButteAddon.cs create mode 100644 Scripts/Items/Addons/BallotBox.cs create mode 100644 Scripts/Items/Addons/BaseAddon.cs create mode 100644 Scripts/Items/Addons/BaseAddonDeed.cs create mode 100644 Scripts/Items/Addons/BearRugs.cs create mode 100644 Scripts/Items/Addons/BloodPentagram.cs create mode 100644 Scripts/Items/Addons/DartBoard.cs create mode 100644 Scripts/Items/Addons/ElvenBedEastAddon.cs create mode 100644 Scripts/Items/Addons/ElvenBedSouthAddon.cs create mode 100644 Scripts/Items/Addons/ElvenDresserEastAddon.cs create mode 100644 Scripts/Items/Addons/ElvenDresserSouthAddon.cs create mode 100644 Scripts/Items/Addons/ElvenForgeAddon.cs create mode 100644 Scripts/Items/Addons/ElvenLoveseatEastAddon.cs create mode 100644 Scripts/Items/Addons/ElvenLoveseatSouthAddon.cs create mode 100644 Scripts/Items/Addons/ElvenSpinningwheelEastAddon.cs create mode 100644 Scripts/Items/Addons/ElvenSpinningwheelSouthAddon.cs create mode 100644 Scripts/Items/Addons/ElvenStoveEastAddon.cs create mode 100644 Scripts/Items/Addons/ElvenStoveSouthAddon.cs create mode 100644 Scripts/Items/Addons/ElvenWashbasinEastAddon.cs create mode 100644 Scripts/Items/Addons/ElvenWashbasinSouthAddon.cs create mode 100644 Scripts/Items/Addons/FancyElvenTableEastAddon.cs create mode 100644 Scripts/Items/Addons/FancyElvenTableSouthAddon.cs create mode 100644 Scripts/Items/Addons/FireColumnAddon.cs create mode 100644 Scripts/Items/Addons/FlourMillEastAddon.cs create mode 100644 Scripts/Items/Addons/FlourMillSouthAddon.cs create mode 100644 Scripts/Items/Addons/FlowerTapestries.cs create mode 100644 Scripts/Items/Addons/GiantWebs.cs create mode 100644 Scripts/Items/Addons/GozaMats.cs create mode 100644 Scripts/Items/Addons/GrayBrickFireplaceEastAddon.cs create mode 100644 Scripts/Items/Addons/GrayBrickFireplaceSouthAddon.cs create mode 100644 Scripts/Items/Addons/JackOLantern.cs create mode 100644 Scripts/Items/Addons/LargeBedEastAddon.cs create mode 100644 Scripts/Items/Addons/LargeBedSouthAddon.cs create mode 100644 Scripts/Items/Addons/LargeForgeEastAddon.cs create mode 100644 Scripts/Items/Addons/LargeForgeSouthAddon.cs create mode 100644 Scripts/Items/Addons/LargeStoneTableEastAddon.cs create mode 100644 Scripts/Items/Addons/LargeStoneTableSouthAddon.cs create mode 100644 Scripts/Items/Addons/LoomEastAddon.cs create mode 100644 Scripts/Items/Addons/LoomSouthAddon.cs create mode 100644 Scripts/Items/Addons/MediumStoneTableEastAddon.cs create mode 100644 Scripts/Items/Addons/MediumStoneTableSouthAddon.cs create mode 100644 Scripts/Items/Addons/OrnateElvenTableEastAddon.cs create mode 100644 Scripts/Items/Addons/OrnateElvenTableSouthAddon.cs create mode 100644 Scripts/Items/Addons/ParrotPerchAddon.cs create mode 100644 Scripts/Items/Addons/PentagramAddon.cs create mode 100644 Scripts/Items/Addons/PickpocketDips.cs create mode 100644 Scripts/Items/Addons/PyramidAddon.cs create mode 100644 Scripts/Items/Addons/RejuvinationAnkhs.cs create mode 100644 Scripts/Items/Addons/SHTeleporter.cs create mode 100644 Scripts/Items/Addons/SandstoneFireplaceEastAddon.cs create mode 100644 Scripts/Items/Addons/SandstoneFireplaceSouthAddon.cs create mode 100644 Scripts/Items/Addons/SandstoneFountainAddon.cs create mode 100644 Scripts/Items/Addons/SerpentPillarAddon.cs create mode 100644 Scripts/Items/Addons/ShrineOfWisdomAddon.cs create mode 100644 Scripts/Items/Addons/SkullPileAddon.cs create mode 100644 Scripts/Items/Addons/SmallBedEastAddon.cs create mode 100644 Scripts/Items/Addons/SmallBedSouthAddon.cs create mode 100644 Scripts/Items/Addons/SmallForgeAddon.cs create mode 100644 Scripts/Items/Addons/SpinningwheelEastAddon.cs create mode 100644 Scripts/Items/Addons/SpinningwheelSouthAddon.cs create mode 100644 Scripts/Items/Addons/SquirrelStatueEastAddon.cs create mode 100644 Scripts/Items/Addons/SquirrelStatueSouthAddon.cs create mode 100644 Scripts/Items/Addons/StoneFireplaceEastAddon.cs create mode 100644 Scripts/Items/Addons/StoneFireplaceSouthAddon.cs create mode 100644 Scripts/Items/Addons/StoneFountainAddon.cs create mode 100644 Scripts/Items/Addons/StoneOvenEastAddon.cs create mode 100644 Scripts/Items/Addons/StoneOvenSouthAddon.cs create mode 100644 Scripts/Items/Addons/StretchedHides.cs create mode 100644 Scripts/Items/Addons/TallElvenBedEastAddon.cs create mode 100644 Scripts/Items/Addons/TallElvenBedSouthAddon.cs create mode 100644 Scripts/Items/Addons/Telescope.cs create mode 100644 Scripts/Items/Addons/TrainingDummies.cs create mode 100644 Scripts/Items/Addons/WarriorStatueEastAddon.cs create mode 100644 Scripts/Items/Addons/WarriorStatueSouthAddon.cs create mode 100644 Scripts/Items/Addons/WaterTroughEastAddon.cs create mode 100644 Scripts/Items/Addons/WaterTroughSouthAddon.cs create mode 100644 Scripts/Items/Addons/WaterVat.cs create mode 100644 Scripts/Items/Armor/ArmorEnums.cs create mode 100644 Scripts/Items/Armor/Artifacts/ArmorOfFortune.cs create mode 100644 Scripts/Items/Armor/Artifacts/Craftable/BrambleCoat.cs create mode 100644 Scripts/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs create mode 100644 Scripts/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs create mode 100644 Scripts/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs create mode 100644 Scripts/Items/Armor/Artifacts/Craftable/StitchersMittens.cs create mode 100644 Scripts/Items/Armor/Artifacts/GauntletsOfNobility.cs create mode 100644 Scripts/Items/Armor/Artifacts/HelmOfInsight.cs create mode 100644 Scripts/Items/Armor/Artifacts/HolyKnightsBreastplate.cs create mode 100644 Scripts/Items/Armor/Artifacts/InquisitorsResolution.cs create mode 100644 Scripts/Items/Armor/Artifacts/JackalsCollar.cs create mode 100644 Scripts/Items/Armor/Artifacts/LeggingsOfBane.cs create mode 100644 Scripts/Items/Armor/Artifacts/MidnightBracers.cs create mode 100644 Scripts/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs create mode 100644 Scripts/Items/Armor/Artifacts/ShadowDancerLeggings.cs create mode 100644 Scripts/Items/Armor/Artifacts/TunicOfFire.cs create mode 100644 Scripts/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs create mode 100644 Scripts/Items/Armor/BaseArmor.cs create mode 100644 Scripts/Items/Armor/Bone/BoneArms.cs create mode 100644 Scripts/Items/Armor/Bone/BoneChest.cs create mode 100644 Scripts/Items/Armor/Bone/BoneGloves.cs create mode 100644 Scripts/Items/Armor/Bone/BoneLegs.cs create mode 100644 Scripts/Items/Armor/Chain/ChainChest.cs create mode 100644 Scripts/Items/Armor/Chain/ChainHatsuburi.cs create mode 100644 Scripts/Items/Armor/Chain/ChainLegs.cs create mode 100644 Scripts/Items/Armor/DaemonBone/DaemonArms.cs create mode 100644 Scripts/Items/Armor/DaemonBone/DaemonChest.cs create mode 100644 Scripts/Items/Armor/DaemonBone/DaemonGloves.cs create mode 100644 Scripts/Items/Armor/DaemonBone/DaemonLegs.cs create mode 100644 Scripts/Items/Armor/Dragon/DragonArms.cs create mode 100644 Scripts/Items/Armor/Dragon/DragonChest.cs create mode 100644 Scripts/Items/Armor/Dragon/DragonGloves.cs create mode 100644 Scripts/Items/Armor/Dragon/DragonHelm.cs create mode 100644 Scripts/Items/Armor/Dragon/DragonLegs.cs create mode 100644 Scripts/Items/Armor/Helmets/Bascinet.cs create mode 100644 Scripts/Items/Armor/Helmets/BoneHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/ChainCoif.cs create mode 100644 Scripts/Items/Armor/Helmets/Circlet.cs create mode 100644 Scripts/Items/Armor/Helmets/CloseHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/DaemonHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/GemmedCirclet.cs create mode 100644 Scripts/Items/Armor/Helmets/Helmet.cs create mode 100644 Scripts/Items/Armor/Helmets/LeafTonlet.cs create mode 100644 Scripts/Items/Armor/Helmets/LeatherCap.cs create mode 100644 Scripts/Items/Armor/Helmets/NorseHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/OrcHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/PlateHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/RavenHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/RoyalCirclet.cs create mode 100644 Scripts/Items/Armor/Helmets/VultureHelm.cs create mode 100644 Scripts/Items/Armor/Helmets/WingedHelm.cs create mode 100644 Scripts/Items/Armor/Leather/FemaleLeafChest.cs create mode 100644 Scripts/Items/Armor/Leather/FemaleLeatherChest.cs create mode 100644 Scripts/Items/Armor/Leather/LeafArms.cs create mode 100644 Scripts/Items/Armor/Leather/LeafChest.cs create mode 100644 Scripts/Items/Armor/Leather/LeafGloves.cs create mode 100644 Scripts/Items/Armor/Leather/LeafGorget.cs create mode 100644 Scripts/Items/Armor/Leather/LeafLegs.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherArms.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherBustierArms.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherChest.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherDo.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherGloves.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherGorget.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherHaidate.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherHiroSode.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherJingasa.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherLegs.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherMempo.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherNinjaHood.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherNinjaJacket.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherNinjaMitts.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherNinjaPants.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherShorts.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherSkirt.cs create mode 100644 Scripts/Items/Armor/Leather/LeatherSuneate.cs create mode 100644 Scripts/Items/Armor/Plate/DecorativePlateKabuto.cs create mode 100644 Scripts/Items/Armor/Plate/FemalePlateChest.cs create mode 100644 Scripts/Items/Armor/Plate/FemaleWoodlandChest.cs create mode 100644 Scripts/Items/Armor/Plate/HeavyPlateJingasa.cs create mode 100644 Scripts/Items/Armor/Plate/LightPlateJingasa.cs create mode 100644 Scripts/Items/Armor/Plate/PlateArms.cs create mode 100644 Scripts/Items/Armor/Plate/PlateBattleKabuto.cs create mode 100644 Scripts/Items/Armor/Plate/PlateChest.cs create mode 100644 Scripts/Items/Armor/Plate/PlateDo.cs create mode 100644 Scripts/Items/Armor/Plate/PlateGloves.cs create mode 100644 Scripts/Items/Armor/Plate/PlateGorget.cs create mode 100644 Scripts/Items/Armor/Plate/PlateHaidate.cs create mode 100644 Scripts/Items/Armor/Plate/PlateHatsuburi.cs create mode 100644 Scripts/Items/Armor/Plate/PlateHiroSode.cs create mode 100644 Scripts/Items/Armor/Plate/PlateLegs.cs create mode 100644 Scripts/Items/Armor/Plate/PlateMempo.cs create mode 100644 Scripts/Items/Armor/Plate/PlateSuneate.cs create mode 100644 Scripts/Items/Armor/Plate/SmallPlateJingasa.cs create mode 100644 Scripts/Items/Armor/Plate/StandardPlateKabuto.cs create mode 100644 Scripts/Items/Armor/Plate/WoodlandArms.cs create mode 100644 Scripts/Items/Armor/Plate/WoodlandChest.cs create mode 100644 Scripts/Items/Armor/Plate/WoodlandGloves.cs create mode 100644 Scripts/Items/Armor/Plate/WoodlandGorget.cs create mode 100644 Scripts/Items/Armor/Plate/WoodlandLegs.cs create mode 100644 Scripts/Items/Armor/Ranger/RangerArms.cs create mode 100644 Scripts/Items/Armor/Ranger/RangerChest.cs create mode 100644 Scripts/Items/Armor/Ranger/RangerGloves.cs create mode 100644 Scripts/Items/Armor/Ranger/RangerGorget.cs create mode 100644 Scripts/Items/Armor/Ranger/RangerLegs.cs create mode 100644 Scripts/Items/Armor/Ring/RingmailArms.cs create mode 100644 Scripts/Items/Armor/Ring/RingmailChest.cs create mode 100644 Scripts/Items/Armor/Ring/RingmailGloves.cs create mode 100644 Scripts/Items/Armor/Ring/RingmailLegs.cs create mode 100644 Scripts/Items/Armor/Studded/FemaleStuddedChest.cs create mode 100644 Scripts/Items/Armor/Studded/HideChest.cs create mode 100644 Scripts/Items/Armor/Studded/HideFemaleChest.cs create mode 100644 Scripts/Items/Armor/Studded/HideGloves.cs create mode 100644 Scripts/Items/Armor/Studded/HideGorget.cs create mode 100644 Scripts/Items/Armor/Studded/HidePants.cs create mode 100644 Scripts/Items/Armor/Studded/HidePauldrons.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedArms.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedBustierArms.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedChest.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedDo.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedGloves.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedGorget.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedHaidate.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedHiroSode.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedLegs.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedMempo.cs create mode 100644 Scripts/Items/Armor/Studded/StuddedSuneate.cs create mode 100644 Scripts/Items/Body Parts/BonePile.cs create mode 100644 Scripts/Items/Body Parts/Head.cs create mode 100644 Scripts/Items/Body Parts/LeftArm.cs create mode 100644 Scripts/Items/Body Parts/LeftLeg.cs create mode 100644 Scripts/Items/Body Parts/RibCage.cs create mode 100644 Scripts/Items/Body Parts/RightArm.cs create mode 100644 Scripts/Items/Body Parts/RightLeg.cs create mode 100644 Scripts/Items/Body Parts/Torso.cs create mode 100644 Scripts/Items/Books/BaseBook.cs create mode 100644 Scripts/Items/Books/BlueBook.cs create mode 100644 Scripts/Items/Books/BrownBook.cs create mode 100644 Scripts/Items/Books/Defined/BlackthornWelcomeBook.cs create mode 100644 Scripts/Items/Books/Defined/BookContent.cs create mode 100644 Scripts/Items/Books/Defined/DrakovsJournal.cs create mode 100644 Scripts/Items/Books/Defined/FropozJournal.cs create mode 100644 Scripts/Items/Books/Defined/KaburJournal.cs create mode 100644 Scripts/Items/Books/Defined/LibraryBooks.cs create mode 100644 Scripts/Items/Books/Defined/TranslatedGargoyleJournal.cs create mode 100644 Scripts/Items/Books/RedBook.cs create mode 100644 Scripts/Items/Books/TanBook.cs create mode 100644 Scripts/Items/Clothing/Artifacts/DivineCountenance.cs create mode 100644 Scripts/Items/Clothing/Artifacts/HatOfTheMagi.cs create mode 100644 Scripts/Items/Clothing/Artifacts/HuntersHeaddress.cs create mode 100644 Scripts/Items/Clothing/Artifacts/SpiritOfTheTotem.cs create mode 100644 Scripts/Items/Clothing/BaseClothing.cs create mode 100644 Scripts/Items/Clothing/Cloaks.cs create mode 100644 Scripts/Items/Clothing/Hats.cs create mode 100644 Scripts/Items/Clothing/MiddleTorso.cs create mode 100644 Scripts/Items/Clothing/OuterLegs.cs create mode 100644 Scripts/Items/Clothing/OuterTorso.cs create mode 100644 Scripts/Items/Clothing/Pants.cs create mode 100644 Scripts/Items/Clothing/Shirts.cs create mode 100644 Scripts/Items/Clothing/Shoes.cs create mode 100644 Scripts/Items/Clothing/Waist.cs create mode 100644 Scripts/Items/Construction/Ankhs.cs create mode 100644 Scripts/Items/Construction/Chairs/Benchs.cs create mode 100644 Scripts/Items/Construction/Chairs/Chairs.cs create mode 100644 Scripts/Items/Construction/Chairs/Stools.cs create mode 100644 Scripts/Items/Construction/Chairs/Thrones.cs create mode 100644 Scripts/Items/Construction/Decorative/DecorativeShield.cs create mode 100644 Scripts/Items/Construction/Decorative/DecorativeWeapon.cs create mode 100644 Scripts/Items/Construction/Decorative/PaintingPortraits.cs create mode 100644 Scripts/Items/Construction/Decorative/Tapestry.cs create mode 100644 Scripts/Items/Construction/Doors/BaseDoor.cs create mode 100644 Scripts/Items/Construction/Doors/Doors.cs create mode 100644 Scripts/Items/Construction/Doors/HouseDoors.cs create mode 100644 Scripts/Items/Construction/Doors/Portcullis.cs create mode 100644 Scripts/Items/Construction/Doors/SecretDoors.cs create mode 100644 Scripts/Items/Construction/Floors/Floors.cs create mode 100644 Scripts/Items/Construction/Misc/BarrelParts.cs create mode 100644 Scripts/Items/Construction/Misc/Easle.cs create mode 100644 Scripts/Items/Construction/Misc/MeltedWax.cs create mode 100644 Scripts/Items/Construction/Misc/MusicStand.cs create mode 100644 Scripts/Items/Construction/Misc/Obelisk.cs create mode 100644 Scripts/Items/Construction/Misc/Screens.cs create mode 100644 Scripts/Items/Construction/Misc/Statues.cs create mode 100644 Scripts/Items/Construction/Misc/Vase.cs create mode 100644 Scripts/Items/Construction/Misc/Vines.cs create mode 100644 Scripts/Items/Construction/Ruined/RuinedItemSingle.cs create mode 100644 Scripts/Items/Construction/Signs/BaseSign.cs create mode 100644 Scripts/Items/Construction/Signs/LocalizedSign.cs create mode 100644 Scripts/Items/Construction/Signs/Sign.cs create mode 100644 Scripts/Items/Construction/Tables/Tables.cs create mode 100644 Scripts/Items/Construction/Tables/WritingTable.cs create mode 100644 Scripts/Items/Construction/Walls/BaseWall.cs create mode 100644 Scripts/Items/Construction/Walls/DarkWoodWall.cs create mode 100644 Scripts/Items/Construction/Walls/ThickGrayStoneWall.cs create mode 100644 Scripts/Items/Construction/Walls/ThinBrickWall.cs create mode 100644 Scripts/Items/Construction/Walls/ThinStoneWall.cs create mode 100644 Scripts/Items/Construction/Walls/WhiteStoneWall.cs create mode 100644 Scripts/Items/Containers/BaseTreasureChest.cs create mode 100644 Scripts/Items/Containers/Container.cs create mode 100644 Scripts/Items/Containers/FillableContainers.cs create mode 100644 Scripts/Items/Containers/FurnitureContainer.cs create mode 100644 Scripts/Items/Containers/LockableContainer.cs create mode 100644 Scripts/Items/Containers/MarkContainer.cs create mode 100644 Scripts/Items/Containers/ParagonChest.cs create mode 100644 Scripts/Items/Containers/Strongbox.cs create mode 100644 Scripts/Items/Containers/TrapableContainer.cs create mode 100644 Scripts/Items/Containers/TreasureChest.cs create mode 100644 Scripts/Items/Containers/TreasureMapChest.cs create mode 100644 Scripts/Items/Decoration Artifacts/BaseDecorationArtifact.cs create mode 100644 Scripts/Items/Decoration Artifacts/DoomDecorationArtifacts.cs create mode 100644 Scripts/Items/Decoration Artifacts/SEDecorationArtifacts.cs create mode 100644 Scripts/Items/Decoration Artifacts/StealableArtifactsSpawner.cs create mode 100644 Scripts/Items/Deeds/BarkeepContract.cs create mode 100644 Scripts/Items/Deeds/ClothingBlessDeed.cs create mode 100644 Scripts/Items/Deeds/CommodityDeed.cs create mode 100644 Scripts/Items/Deeds/DragonBardingDeed.cs create mode 100644 Scripts/Items/Deeds/HairRestylingDeed.cs create mode 100644 Scripts/Items/Deeds/HolidayTreeDeed.cs create mode 100644 Scripts/Items/Deeds/NameChangeDeed.cs create mode 100644 Scripts/Items/Deeds/NewPlayerTicket.cs create mode 100644 Scripts/Items/Deeds/VendorRentalContract.cs create mode 100644 Scripts/Items/Facial/Beard.cs create mode 100644 Scripts/Items/Facial/Hair.cs create mode 100644 Scripts/Items/Farming/FarmableCabbage.cs create mode 100644 Scripts/Items/Farming/FarmableCarrot.cs create mode 100644 Scripts/Items/Farming/FarmableCotton.cs create mode 100644 Scripts/Items/Farming/FarmableCrop.cs create mode 100644 Scripts/Items/Farming/FarmableFlax.cs create mode 100644 Scripts/Items/Farming/FarmableLettuce.cs create mode 100644 Scripts/Items/Farming/FarmableOnion.cs create mode 100644 Scripts/Items/Farming/FarmablePumpkin.cs create mode 100644 Scripts/Items/Farming/FarmableTurnip.cs create mode 100644 Scripts/Items/Farming/FarmableWheat.cs create mode 100644 Scripts/Items/Food/Asian.cs create mode 100644 Scripts/Items/Food/Beverage.cs create mode 100644 Scripts/Items/Food/BeverageEmpty.cs create mode 100644 Scripts/Items/Food/Bowls.cs create mode 100644 Scripts/Items/Food/CookableFood.cs create mode 100644 Scripts/Items/Food/Cooking.cs create mode 100644 Scripts/Items/Food/Food.cs create mode 100644 Scripts/Items/Food/Fruits.cs create mode 100644 Scripts/Items/Food/Vegetables.cs create mode 100644 Scripts/Items/Games/Backgammon.cs create mode 100644 Scripts/Items/Games/BaseBoard.cs create mode 100644 Scripts/Items/Games/BasePiece.cs create mode 100644 Scripts/Items/Games/CheckerBoard.cs create mode 100644 Scripts/Items/Games/CheckersPieces.cs create mode 100644 Scripts/Items/Games/ChessPieces.cs create mode 100644 Scripts/Items/Games/Chessboard.cs create mode 100644 Scripts/Items/Games/Dices.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongDealerIndicator.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongDices.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongEnums.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongGame.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongPacketHandlers.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongPieceDim.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongPlayers.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongTile.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongTileTypeGenerator.cs create mode 100644 Scripts/Items/Games/Mahjong/MahjongWallBreakIndicator.cs create mode 100644 Scripts/Items/Games/Mahjong/Packets.cs create mode 100644 Scripts/Items/Gems/Amber.cs create mode 100644 Scripts/Items/Gems/Amethyst.cs create mode 100644 Scripts/Items/Gems/Citrine.cs create mode 100644 Scripts/Items/Gems/Diamond.cs create mode 100644 Scripts/Items/Gems/Emerald.cs create mode 100644 Scripts/Items/Gems/Ruby.cs create mode 100644 Scripts/Items/Gems/Sapphire.cs create mode 100644 Scripts/Items/Gems/StarSapphire.cs create mode 100644 Scripts/Items/Gems/Tourmaline.cs create mode 100644 Scripts/Items/Guilds/GuildDeed.cs create mode 100644 Scripts/Items/Guilds/GuildTeleporter.cs create mode 100644 Scripts/Items/Guilds/Guildstone.cs create mode 100644 Scripts/Items/Jewels/Artifacts/BraceletOfHealth.cs create mode 100644 Scripts/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs create mode 100644 Scripts/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs create mode 100644 Scripts/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs create mode 100644 Scripts/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs create mode 100644 Scripts/Items/Jewels/Artifacts/RingOfTheElements.cs create mode 100644 Scripts/Items/Jewels/Artifacts/RingOfTheVile.cs create mode 100644 Scripts/Items/Jewels/BaseJewel.cs create mode 100644 Scripts/Items/Jewels/Beads.cs create mode 100644 Scripts/Items/Jewels/Bracelet.cs create mode 100644 Scripts/Items/Jewels/Earrings.cs create mode 100644 Scripts/Items/Jewels/Necklace.cs create mode 100644 Scripts/Items/Jewels/Ring.cs create mode 100644 Scripts/Items/Lights/BaseEquipableLight.cs create mode 100644 Scripts/Items/Lights/BaseLight.cs create mode 100644 Scripts/Items/Lights/Brazier.cs create mode 100644 Scripts/Items/Lights/BrazierTall.cs create mode 100644 Scripts/Items/Lights/Candelabra.cs create mode 100644 Scripts/Items/Lights/CandelabraStand.cs create mode 100644 Scripts/Items/Lights/Candle.cs create mode 100644 Scripts/Items/Lights/CandleLarge.cs create mode 100644 Scripts/Items/Lights/CandleLong.cs create mode 100644 Scripts/Items/Lights/CandleShort.cs create mode 100644 Scripts/Items/Lights/CandleSkull.cs create mode 100644 Scripts/Items/Lights/DarkSource.cs create mode 100644 Scripts/Items/Lights/HangingLantern.cs create mode 100644 Scripts/Items/Lights/HeatingStand.cs create mode 100644 Scripts/Items/Lights/LampPost1.cs create mode 100644 Scripts/Items/Lights/LampPost2.cs create mode 100644 Scripts/Items/Lights/LampPost3.cs create mode 100644 Scripts/Items/Lights/Lantern.cs create mode 100644 Scripts/Items/Lights/LightSource.cs create mode 100644 Scripts/Items/Lights/PaperLantern.cs create mode 100644 Scripts/Items/Lights/RedHangingLantern.cs create mode 100644 Scripts/Items/Lights/RoundPaperLantern.cs create mode 100644 Scripts/Items/Lights/ShojiLantern.cs create mode 100644 Scripts/Items/Lights/Torch.cs create mode 100644 Scripts/Items/Lights/WallSconce.cs create mode 100644 Scripts/Items/Lights/WallTorch.cs create mode 100644 Scripts/Items/Lights/WhiteHangingLantern.cs create mode 100644 Scripts/Items/Maps/BlankMap.cs create mode 100644 Scripts/Items/Maps/CityMap.cs create mode 100644 Scripts/Items/Maps/IndecipherableMap.cs create mode 100644 Scripts/Items/Maps/LocalMap.cs create mode 100644 Scripts/Items/Maps/MapItem.cs create mode 100644 Scripts/Items/Maps/PresetMap.cs create mode 100644 Scripts/Items/Maps/SeaChart.cs create mode 100644 Scripts/Items/Maps/TreasureMap.cs create mode 100644 Scripts/Items/Maps/WorldMap.cs create mode 100644 Scripts/Items/Minor Artifacts/AdmiralsHeartyRum.cs create mode 100644 Scripts/Items/Minor Artifacts/AlchemistsBauble.cs create mode 100644 Scripts/Items/Minor Artifacts/ArcticDeathDealer.cs create mode 100644 Scripts/Items/Minor Artifacts/BlazeOfDeath.cs create mode 100644 Scripts/Items/Minor Artifacts/BowOfTheJukaKing.cs create mode 100644 Scripts/Items/Minor Artifacts/BurglarsBandana.cs create mode 100644 Scripts/Items/Minor Artifacts/CandelabraOfSouls.cs create mode 100644 Scripts/Items/Minor Artifacts/CaptainQuacklebushsCutlass.cs create mode 100644 Scripts/Items/Minor Artifacts/CavortingClub.cs create mode 100644 Scripts/Items/Minor Artifacts/ColdBlood.cs create mode 100644 Scripts/Items/Minor Artifacts/DreadPirateHat.cs create mode 100644 Scripts/Items/Minor Artifacts/EnchantedTitanLegBone.cs create mode 100644 Scripts/Items/Minor Artifacts/GhostShipAnchor.cs create mode 100644 Scripts/Items/Minor Artifacts/GlovesOfThePugilist.cs create mode 100644 Scripts/Items/Minor Artifacts/GoldBricks.cs create mode 100644 Scripts/Items/Minor Artifacts/GwennosHarp.cs create mode 100644 Scripts/Items/Minor Artifacts/HeartOfTheLion.cs create mode 100644 Scripts/Items/Minor Artifacts/IolosLute.cs create mode 100644 Scripts/Items/Minor Artifacts/LunaLance.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/AegisOfGrace.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/BladeDance.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/Bonesmasher.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/FeyLeggings.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/FleshRipper.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/MelisandesCorrodedHatchet.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/PadsOfTheCuSidhe.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/RaedsGlory.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/RighteousAnger.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/SoulSeeker.cs create mode 100644 Scripts/Items/Minor Artifacts/ML/TalonBite.cs create mode 100644 Scripts/Items/Minor Artifacts/NightsKiss.cs create mode 100644 Scripts/Items/Minor Artifacts/NoxRangersHeavyCrossbow.cs create mode 100644 Scripts/Items/Minor Artifacts/OrcishVisage.cs create mode 100644 Scripts/Items/Minor Artifacts/PhillipsWoodenSteed.cs create mode 100644 Scripts/Items/Minor Artifacts/PixieSwatter.cs create mode 100644 Scripts/Items/Minor Artifacts/PolarBearMask.cs create mode 100644 Scripts/Items/Minor Artifacts/SeahorseStatuette.cs create mode 100644 Scripts/Items/Minor Artifacts/ShieldOfInvulnerability.cs create mode 100644 Scripts/Items/Minor Artifacts/ShipModelOfTheHMSCape.cs create mode 100644 Scripts/Items/Minor Artifacts/StaffOfPower.cs create mode 100644 Scripts/Items/Minor Artifacts/VioletCourage.cs create mode 100644 Scripts/Items/Minor Artifacts/WrathOfTheDryad.cs create mode 100644 Scripts/Items/Misc/ArcaneGem.cs create mode 100644 Scripts/Items/Misc/BankCheck.cs create mode 100644 Scripts/Items/Misc/Beeswax.cs create mode 100644 Scripts/Items/Misc/Blocker.cs create mode 100644 Scripts/Items/Misc/Blood.cs create mode 100644 Scripts/Items/Misc/Bola.cs create mode 100644 Scripts/Items/Misc/BolaBall.cs create mode 100644 Scripts/Items/Misc/BulletinBoards.cs create mode 100644 Scripts/Items/Misc/ClockworkAssembly.cs create mode 100644 Scripts/Items/Misc/CommunicationCrystals.cs create mode 100644 Scripts/Items/Misc/Corpses/Corpse.cs create mode 100644 Scripts/Items/Misc/Corpses/CorpseNameAttribute.cs create mode 100644 Scripts/Items/Misc/Corpses/DecayedCorpse.cs create mode 100644 Scripts/Items/Misc/Corpses/Packets.cs create mode 100644 Scripts/Items/Misc/EffectController.cs create mode 100644 Scripts/Items/Misc/EffectItem.cs create mode 100644 Scripts/Items/Misc/ExecutionersCap.cs create mode 100644 Scripts/Items/Misc/FlipableAttribute.cs create mode 100644 Scripts/Items/Misc/GlassItems.cs create mode 100644 Scripts/Items/Misc/Gold.cs create mode 100644 Scripts/Items/Misc/Guillotine.cs create mode 100644 Scripts/Items/Misc/HairDye.cs create mode 100644 Scripts/Items/Misc/IDurability.cs create mode 100644 Scripts/Items/Misc/InteriorDecorator.cs create mode 100644 Scripts/Items/Misc/Key.cs create mode 100644 Scripts/Items/Misc/KeyRing.cs create mode 100644 Scripts/Items/Misc/LOSBlocker.cs create mode 100644 Scripts/Items/Misc/Moonstone.cs create mode 100644 Scripts/Items/Misc/MoonstoneGate.cs create mode 100644 Scripts/Items/Misc/MorphItem.cs create mode 100644 Scripts/Items/Misc/OilCloth.cs create mode 100644 Scripts/Items/Misc/Origami.cs create mode 100644 Scripts/Items/Misc/PlayerBulletinBoards.cs create mode 100644 Scripts/Items/Misc/PlayerVendorDeed.cs create mode 100644 Scripts/Items/Misc/PoolOfAcid.cs create mode 100644 Scripts/Items/Misc/PowerCrystal.cs create mode 100644 Scripts/Items/Misc/PowerGenerator.cs create mode 100644 Scripts/Items/Misc/PromotionalToken.cs create mode 100644 Scripts/Items/Misc/PublicMoongate.cs create mode 100644 Scripts/Items/Misc/Rares.cs create mode 100644 Scripts/Items/Misc/ResGate.cs create mode 100644 Scripts/Items/Misc/Scales.cs create mode 100644 Scripts/Items/Misc/SerpentPillar.cs create mode 100644 Scripts/Items/Misc/SpecialBeardDye.cs create mode 100644 Scripts/Items/Misc/SpecialHairDye.cs create mode 100644 Scripts/Items/Misc/Static.cs create mode 100644 Scripts/Items/Misc/SwarmOfFlies.cs create mode 100644 Scripts/Items/Misc/Teleporter.cs create mode 100644 Scripts/Items/Misc/TrashBarrel.cs create mode 100644 Scripts/Items/Misc/TrashChest.cs create mode 100644 Scripts/Items/Misc/TribalBerry.cs create mode 100644 Scripts/Items/Misc/TribalPaint.cs create mode 100644 Scripts/Items/Misc/UnholyBone.cs create mode 100644 Scripts/Items/Misc/WarningItem.cs create mode 100644 Scripts/Items/Misc/Waypoint.cs create mode 100644 Scripts/Items/Misc/WindChimes.cs create mode 100644 Scripts/Items/PlantsFlowers/Potted/EmptyPots.cs create mode 100644 Scripts/Items/PlantsFlowers/Potted/PottedCactus.cs create mode 100644 Scripts/Items/PlantsFlowers/Potted/PottedPlants.cs create mode 100644 Scripts/Items/PlantsFlowers/Potted/PottedTrees.cs create mode 100644 Scripts/Items/Resources/Arrows/Arrow.cs create mode 100644 Scripts/Items/Resources/Arrows/Bolt.cs create mode 100644 Scripts/Items/Resources/Arrows/Feather.cs create mode 100644 Scripts/Items/Resources/Arrows/Shaft.cs create mode 100644 Scripts/Items/Resources/Blacksmithing/Ingots.cs create mode 100644 Scripts/Items/Resources/Blacksmithing/Ore.cs create mode 100644 Scripts/Items/Resources/Blacksmithing/Scales.cs create mode 100644 Scripts/Items/Resources/Fishing/BigFish.cs create mode 100644 Scripts/Items/Resources/Fishing/Fish.cs create mode 100644 Scripts/Items/Resources/Fishing/MagicFish.cs create mode 100644 Scripts/Items/Resources/Masonry/Granite.cs create mode 100644 Scripts/Items/Resources/MiscMLResources.cs create mode 100644 Scripts/Items/Resources/Reagents/BagOfAllReagents.cs create mode 100644 Scripts/Items/Resources/Reagents/BagOfNecroReagents.cs create mode 100644 Scripts/Items/Resources/Reagents/BagOfReagents.cs create mode 100644 Scripts/Items/Resources/Reagents/BaseReagent.cs create mode 100644 Scripts/Items/Resources/Reagents/BatWing.cs create mode 100644 Scripts/Items/Resources/Reagents/BlackPearl.cs create mode 100644 Scripts/Items/Resources/Reagents/Bloodmoss.cs create mode 100644 Scripts/Items/Resources/Reagents/DaemonBlood.cs create mode 100644 Scripts/Items/Resources/Reagents/DaemonBone.cs create mode 100644 Scripts/Items/Resources/Reagents/DeadWood.cs create mode 100644 Scripts/Items/Resources/Reagents/Garlic.cs create mode 100644 Scripts/Items/Resources/Reagents/Ginseng.cs create mode 100644 Scripts/Items/Resources/Reagents/GraveDust.cs create mode 100644 Scripts/Items/Resources/Reagents/MandrakeRoot.cs create mode 100644 Scripts/Items/Resources/Reagents/Nightshade.cs create mode 100644 Scripts/Items/Resources/Reagents/NoxCrystal.cs create mode 100644 Scripts/Items/Resources/Reagents/PigIron.cs create mode 100644 Scripts/Items/Resources/Reagents/SpidersSilk.cs create mode 100644 Scripts/Items/Resources/Reagents/SulfurousAsh.cs create mode 100644 Scripts/Items/Resources/Tailor/BoltOfCloth.cs create mode 100644 Scripts/Items/Resources/Tailor/Bone.cs create mode 100644 Scripts/Items/Resources/Tailor/Cloth.cs create mode 100644 Scripts/Items/Resources/Tailor/Cotton.cs create mode 100644 Scripts/Items/Resources/Tailor/Flax.cs create mode 100644 Scripts/Items/Resources/Tailor/Hides.cs create mode 100644 Scripts/Items/Resources/Tailor/Leathers.cs create mode 100644 Scripts/Items/Resources/Tailor/UncutCloth.cs create mode 100644 Scripts/Items/Resources/Tailor/Wool.cs create mode 100644 Scripts/Items/Resources/Tailor/YarnsAndThreads.cs create mode 100644 Scripts/Items/Shields/Artifacts/Aegis.cs create mode 100644 Scripts/Items/Shields/Artifacts/ArcaneShield.cs create mode 100644 Scripts/Items/Shields/BaseShield.cs create mode 100644 Scripts/Items/Shields/BronzeShield.cs create mode 100644 Scripts/Items/Shields/Buckler.cs create mode 100644 Scripts/Items/Shields/ChaosShield.cs create mode 100644 Scripts/Items/Shields/HeaterShield.cs create mode 100644 Scripts/Items/Shields/MetalKiteShield.cs create mode 100644 Scripts/Items/Shields/MetalShield.cs create mode 100644 Scripts/Items/Shields/OrderShield.cs create mode 100644 Scripts/Items/Shields/WoodenKiteShield.cs create mode 100644 Scripts/Items/Shields/WoodenShield.cs create mode 100644 Scripts/Items/Skill Items/Blacksmith Items/Misc/AnvilForge.cs create mode 100644 Scripts/Items/Skill Items/Blacksmith Items/Misc/LargeForge.cs create mode 100644 Scripts/Items/Skill Items/Camping/Bedroll.cs create mode 100644 Scripts/Items/Skill Items/Camping/Campfire.cs create mode 100644 Scripts/Items/Skill Items/Camping/Kindling.cs create mode 100644 Scripts/Items/Skill Items/Carpenter Items/Board.cs create mode 100644 Scripts/Items/Skill Items/Carpenter Items/TaxidermyKit.cs create mode 100644 Scripts/Items/Skill Items/Fishing/FishingPole.cs create mode 100644 Scripts/Items/Skill Items/Fishing/Misc/MessageInABottle.cs create mode 100644 Scripts/Items/Skill Items/Fishing/Misc/SOS.cs create mode 100644 Scripts/Items/Skill Items/Fishing/Misc/Sextant.cs create mode 100644 Scripts/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs create mode 100644 Scripts/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs create mode 100644 Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs create mode 100644 Scripts/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs create mode 100644 Scripts/Items/Skill Items/Harvest Tools/ProspectorsTool.cs create mode 100644 Scripts/Items/Skill Items/Harvest Tools/Shovel.cs create mode 100644 Scripts/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs create mode 100644 Scripts/Items/Skill Items/Harvest Tools/SturdyShovel.cs create mode 100644 Scripts/Items/Skill Items/Lumberjack/Log.cs create mode 100644 Scripts/Items/Skill Items/Magical/BookOfBushido.cs create mode 100644 Scripts/Items/Skill Items/Magical/BookOfChivalry.cs create mode 100644 Scripts/Items/Skill Items/Magical/BookOfNinjitsu.cs create mode 100644 Scripts/Items/Skill Items/Magical/Misc/BlankScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Misc/Bottle.cs create mode 100644 Scripts/Items/Skill Items/Magical/Misc/Moongate.cs create mode 100644 Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs create mode 100644 Scripts/Items/Skill Items/Magical/Misc/RecallRune.cs create mode 100644 Scripts/Items/Skill Items/Magical/NecromancerSpellbook.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Agility Potions/AgilityPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Agility Potions/GreaterAgilityPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Cure Potions/CurePotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Cure Potions/GreaterCurePotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Cure Potions/LesserCurePotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/ExplosionPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/GreaterExplosionPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/LesserExplosionPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Heal Potions/GreaterHealPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Heal Potions/HealPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Heal Potions/LesserHealPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/NightSight.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Poison Potions/DeadlyPoisonPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Poison Potions/GreaterPoisonPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Poison Potions/LesserPoisonPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Poison Potions/PoisonPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/RefreshPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/TotalRefreshPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Strength Potions/GreaterStrengthPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Potions/Strength Potions/StrengthPotion.cs create mode 100644 Scripts/Items/Skill Items/Magical/Runebook.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EarthquakeScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EnergyVortexScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/ResurrectionScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonAirElementalScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonDaemonScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonEarthElementalScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonFireElementalScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonWaterElementalScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/BladeSpiritsScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/DispelFieldScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/IncognitoScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MagicReflectScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MindBlastScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/ParalyzeScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/PoisonFieldScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/SummonCreatureScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ClumsyScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/CreateFoodScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/FeeblemindScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/HealScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/MagicArrowScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/NightSightScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ReactiveArmorScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/First Circle/WeakenScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchProtectionScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchcureScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/CurseScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/FireFieldScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/GreaterHealScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/LightningScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ManaDrainScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/RecallScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/AnimateDeadScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/BloodOathScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CorpseSkinScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CurseWeaponScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/EvilOmenScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/ExorcismScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/HorrificBeastScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/LichFormScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/MindRotScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PainSpikeScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PoisonStrikeScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/StrangleScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/SummonFamiliarScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VampiricEmbraceScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VengefulSpiritScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WitherScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WraithFormScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/AgilityScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CunningScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CureScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/HarmScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicTrapScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicUnTrapScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/ProtectionScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/StrengthScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ChainLightningScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/EnergyFieldScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/FlamestrikeScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/GateTravelScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ManaVampireScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MassDispelScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MeteorStormScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/PolymorphScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/DispelScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/EnergyBoltScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ExplosionScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/InvisibilityScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MarkScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MassCurseScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ParalyzeFieldSpell.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/RevealScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/SpellScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/BlessScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/FireballScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/MagicLockScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/PoisonScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TelekinisisScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TeleportScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/UnlockScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/WallOfStoneScroll.cs create mode 100644 Scripts/Items/Skill Items/Magical/Spellbook.cs create mode 100644 Scripts/Items/Skill Items/Magical/SpellweavingBook.cs create mode 100644 Scripts/Items/Skill Items/Misc/Bandage.cs create mode 100644 Scripts/Items/Skill Items/Misc/FireHorn.cs create mode 100644 Scripts/Items/Skill Items/Misc/RecipeScroll.cs create mode 100644 Scripts/Items/Skill Items/Misc/RepairDeed.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/BambooFlute.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/BaseInstrument.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/Drums.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/Harp.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/LapHarp.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/Lute.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/Tambourine.cs create mode 100644 Scripts/Items/Skill Items/Musical Instruments/TambourineTassel.cs create mode 100644 Scripts/Items/Skill Items/Ninjitsu/EggBomb.cs create mode 100644 Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs create mode 100644 Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs create mode 100644 Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs create mode 100644 Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs create mode 100644 Scripts/Items/Skill Items/Ninjitsu/SmokeBomb.cs create mode 100644 Scripts/Items/Skill Items/Specialized/GlassblowingBook.cs create mode 100644 Scripts/Items/Skill Items/Specialized/MasonryBook.cs create mode 100644 Scripts/Items/Skill Items/Specialized/Sand.cs create mode 100644 Scripts/Items/Skill Items/Specialized/SandMiningBook.cs create mode 100644 Scripts/Items/Skill Items/Specialized/StoneMiningBook.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/CustomHuePicker.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Misc/Dressform.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Misc/Dyes.cs create mode 100644 Scripts/Items/Skill Items/Tailor Items/Misc/Scissors.cs create mode 100644 Scripts/Items/Skill Items/Thief/DisguiseKit.cs create mode 100644 Scripts/Items/Skill Items/Thief/LockPick.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Axle.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/AxleGears.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/ClockFrame.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/ClockParts.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Clocks.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Gears.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Globe.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Hinge.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/SextantParts.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Springs.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Spyglass.cs create mode 100644 Scripts/Items/Skill Items/Tinkering/Utensils.cs create mode 100644 Scripts/Items/Skill Items/Tools/BaseRunicTool.cs create mode 100644 Scripts/Items/Skill Items/Tools/BaseTool.cs create mode 100644 Scripts/Items/Skill Items/Tools/Blowpipe.cs create mode 100644 Scripts/Items/Skill Items/Tools/DovetailSaw.cs create mode 100644 Scripts/Items/Skill Items/Tools/DrawKnife.cs create mode 100644 Scripts/Items/Skill Items/Tools/FletcherTools.cs create mode 100644 Scripts/Items/Skill Items/Tools/FlourSifter.cs create mode 100644 Scripts/Items/Skill Items/Tools/Froe.cs create mode 100644 Scripts/Items/Skill Items/Tools/Hammer.cs create mode 100644 Scripts/Items/Skill Items/Tools/Inshave.cs create mode 100644 Scripts/Items/Skill Items/Tools/JointingPlane.cs create mode 100644 Scripts/Items/Skill Items/Tools/MalletAndChisel.cs create mode 100644 Scripts/Items/Skill Items/Tools/MapmakersPen.cs create mode 100644 Scripts/Items/Skill Items/Tools/MortarPestle.cs create mode 100644 Scripts/Items/Skill Items/Tools/MouldingPlane.cs create mode 100644 Scripts/Items/Skill Items/Tools/Nails.cs create mode 100644 Scripts/Items/Skill Items/Tools/RollingPin.cs create mode 100644 Scripts/Items/Skill Items/Tools/RunicHammer.cs create mode 100644 Scripts/Items/Skill Items/Tools/RunicSewingKit.cs create mode 100644 Scripts/Items/Skill Items/Tools/Saw.cs create mode 100644 Scripts/Items/Skill Items/Tools/Scorp.cs create mode 100644 Scripts/Items/Skill Items/Tools/ScribesPen.cs create mode 100644 Scripts/Items/Skill Items/Tools/SewingKit.cs create mode 100644 Scripts/Items/Skill Items/Tools/Skillet.cs create mode 100644 Scripts/Items/Skill Items/Tools/SledgeHammer.cs create mode 100644 Scripts/Items/Skill Items/Tools/SmithHammer.cs create mode 100644 Scripts/Items/Skill Items/Tools/SmoothingPlane.cs create mode 100644 Scripts/Items/Skill Items/Tools/TinkerTools.cs create mode 100644 Scripts/Items/Skill Items/Tools/Tongs.cs create mode 100644 Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs create mode 100644 Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs create mode 100644 Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs create mode 100644 Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs create mode 100644 Scripts/Items/Special/Gifts/HearthOfHomeFire.cs create mode 100644 Scripts/Items/Special/Gifts/HolySword.cs create mode 100644 Scripts/Items/Special/Gifts/LeggingsOfEmbers.cs create mode 100644 Scripts/Items/Special/Gifts/RoseOfTrinsic.cs create mode 100644 Scripts/Items/Special/Gifts/SamuraiHelm.cs create mode 100644 Scripts/Items/Special/Gifts/ShaminoCrossbow.cs create mode 100644 Scripts/Items/Special/Gifts/TapestryOfSosaria.cs create mode 100644 Scripts/Items/Special/Holiday/Christmas/HolidayTree.cs create mode 100644 Scripts/Items/Special/Holiday/GiftBox.cs create mode 100644 Scripts/Items/Special/Holiday/Poinsettias.cs create mode 100644 Scripts/Items/Special/Holiday/Snowflakes.cs create mode 100644 Scripts/Items/Special/Holiday/Snowman.cs create mode 100644 Scripts/Items/Special/Holiday/WinterGiftPackage2003.cs create mode 100644 Scripts/Items/Special/Holiday/Wreath.cs create mode 100644 Scripts/Items/Special/MiniHouses.cs create mode 100644 Scripts/Items/Special/MonsterStatuette.cs create mode 100644 Scripts/Items/Special/RewardCake.cs create mode 100644 Scripts/Items/Special/Solen Items/BagOfSending.cs create mode 100644 Scripts/Items/Special/Solen Items/BallOfSummoning.cs create mode 100644 Scripts/Items/Special/Solen Items/BraceletOfBinding.cs create mode 100644 Scripts/Items/Special/Solen Items/MessageHelper.cs create mode 100644 Scripts/Items/Special/Solen Items/PowderOfTranslocation.cs create mode 100644 Scripts/Items/Special/Solen Items/ZoogiFungus.cs create mode 100644 Scripts/Items/Special/SoulStone.cs create mode 100644 Scripts/Items/Special/Special Scrolls/PowerScroll.cs create mode 100644 Scripts/Items/Special/Special Scrolls/StatScroll.cs create mode 100644 Scripts/Items/Stones/AlchemyStone.cs create mode 100644 Scripts/Items/Stones/GMStone.cs create mode 100644 Scripts/Items/Stones/GamblingStone.cs create mode 100644 Scripts/Items/Stones/IngotStone.cs create mode 100644 Scripts/Items/Stones/RegStone.cs create mode 100644 Scripts/Items/Stones/ScribeStone.cs create mode 100644 Scripts/Items/Stones/SmithStone.cs create mode 100644 Scripts/Items/Stones/TailorStone.cs create mode 100644 Scripts/Items/Suits/AdminRobe.cs create mode 100644 Scripts/Items/Suits/BaseSuit.cs create mode 100644 Scripts/Items/Suits/CounselorRobe.cs create mode 100644 Scripts/Items/Suits/DeathShroud.cs create mode 100644 Scripts/Items/Suits/DupreSuit.cs create mode 100644 Scripts/Items/Suits/GMRobe.cs create mode 100644 Scripts/Items/Suits/LordBlackthorneSuit.cs create mode 100644 Scripts/Items/Suits/LordBritishSuit.cs create mode 100644 Scripts/Items/Suits/SeerRobe.cs create mode 100644 Scripts/Items/SupplyBags/AlchemyBag.cs create mode 100644 Scripts/Items/SupplyBags/BagOfIngots.cs create mode 100644 Scripts/Items/SupplyBags/ScribeBag.cs create mode 100644 Scripts/Items/SupplyBags/SmithBag.cs create mode 100644 Scripts/Items/SupplyBags/TailorBag.cs create mode 100644 Scripts/Items/Traps/BaseTrap.cs create mode 100644 Scripts/Items/Traps/FireColumnTrap.cs create mode 100644 Scripts/Items/Traps/FlameSpurtTrap.cs create mode 100644 Scripts/Items/Traps/GasTrap.cs create mode 100644 Scripts/Items/Traps/GiantSpikeTrap.cs create mode 100644 Scripts/Items/Traps/MushroomTrap.cs create mode 100644 Scripts/Items/Traps/SawTrap.cs create mode 100644 Scripts/Items/Traps/SpikeTrap.cs create mode 100644 Scripts/Items/Traps/StoneFaceTrap.cs create mode 100644 Scripts/Items/Wands/BaseWand.cs create mode 100644 Scripts/Items/Wands/ClumsyWand.cs create mode 100644 Scripts/Items/Wands/FeebleWand.cs create mode 100644 Scripts/Items/Wands/FireballWand.cs create mode 100644 Scripts/Items/Wands/GreaterHealWand.cs create mode 100644 Scripts/Items/Wands/HarmWand.cs create mode 100644 Scripts/Items/Wands/HealWand.cs create mode 100644 Scripts/Items/Wands/IDWand.cs create mode 100644 Scripts/Items/Wands/LightningWand.cs create mode 100644 Scripts/Items/Wands/MagicArrowWand.cs create mode 100644 Scripts/Items/Wands/ManaDrainWand.cs create mode 100644 Scripts/Items/Wands/RandomWand.cs create mode 100644 Scripts/Items/Wands/WandTarget.cs create mode 100644 Scripts/Items/Wands/WeaknessWand.cs create mode 100644 Scripts/Items/Weapons/Abilities/ArmorIgnore.cs create mode 100644 Scripts/Items/Weapons/Abilities/ArmorPierce.cs create mode 100644 Scripts/Items/Weapons/Abilities/BleedAttack.cs create mode 100644 Scripts/Items/Weapons/Abilities/Block.cs create mode 100644 Scripts/Items/Weapons/Abilities/ConcussionBlow.cs create mode 100644 Scripts/Items/Weapons/Abilities/CrushingBlow.cs create mode 100644 Scripts/Items/Weapons/Abilities/DefenseMastery.cs create mode 100644 Scripts/Items/Weapons/Abilities/Disarm.cs create mode 100644 Scripts/Items/Weapons/Abilities/Dismount.cs create mode 100644 Scripts/Items/Weapons/Abilities/DoubleShot.cs create mode 100644 Scripts/Items/Weapons/Abilities/DoubleStrike.cs create mode 100644 Scripts/Items/Weapons/Abilities/DualWield.cs create mode 100644 Scripts/Items/Weapons/Abilities/Feint.cs create mode 100644 Scripts/Items/Weapons/Abilities/FrenziedWhirlwind.cs create mode 100644 Scripts/Items/Weapons/Abilities/InfectiousStrike.cs create mode 100644 Scripts/Items/Weapons/Abilities/MortalStrike.cs create mode 100644 Scripts/Items/Weapons/Abilities/MovingShot.cs create mode 100644 Scripts/Items/Weapons/Abilities/NerveStrike.cs create mode 100644 Scripts/Items/Weapons/Abilities/ParalyzingBlow.cs create mode 100644 Scripts/Items/Weapons/Abilities/RidingSwipe.cs create mode 100644 Scripts/Items/Weapons/Abilities/ShadowStrike.cs create mode 100644 Scripts/Items/Weapons/Abilities/TalonStrike.cs create mode 100644 Scripts/Items/Weapons/Abilities/WeaponAbility.cs create mode 100644 Scripts/Items/Weapons/Abilities/WhirlwindAttack.cs create mode 100644 Scripts/Items/Weapons/Artifacts/AxeOfTheHeavens.cs create mode 100644 Scripts/Items/Weapons/Artifacts/BladeOfInsanity.cs create mode 100644 Scripts/Items/Weapons/Artifacts/BladeOfTheRighteous.cs create mode 100644 Scripts/Items/Weapons/Artifacts/BoneCrusher.cs create mode 100644 Scripts/Items/Weapons/Artifacts/BreathOfTheDead.cs create mode 100644 Scripts/Items/Weapons/Artifacts/Frostbringer.cs create mode 100644 Scripts/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs create mode 100644 Scripts/Items/Weapons/Artifacts/SerpentsFang.cs create mode 100644 Scripts/Items/Weapons/Artifacts/StaffOfTheMagi.cs create mode 100644 Scripts/Items/Weapons/Artifacts/TheBeserkersMaul.cs create mode 100644 Scripts/Items/Weapons/Artifacts/TheDragonSlayer.cs create mode 100644 Scripts/Items/Weapons/Artifacts/TheDryadBow.cs create mode 100644 Scripts/Items/Weapons/Artifacts/TheTaskmaster.cs create mode 100644 Scripts/Items/Weapons/Artifacts/TitansHammer.cs create mode 100644 Scripts/Items/Weapons/Artifacts/ZyronicClaw.cs create mode 100644 Scripts/Items/Weapons/Axes/Axe.cs create mode 100644 Scripts/Items/Weapons/Axes/BaseAxe.cs create mode 100644 Scripts/Items/Weapons/Axes/BattleAxe.cs create mode 100644 Scripts/Items/Weapons/Axes/DoubleAxe.cs create mode 100644 Scripts/Items/Weapons/Axes/ExecutionersAxe.cs create mode 100644 Scripts/Items/Weapons/Axes/Hatchet.cs create mode 100644 Scripts/Items/Weapons/Axes/LargeBattleAxe.cs create mode 100644 Scripts/Items/Weapons/Axes/Pickaxe.cs create mode 100644 Scripts/Items/Weapons/Axes/TwoHandedAxe.cs create mode 100644 Scripts/Items/Weapons/Axes/WarAxe.cs create mode 100644 Scripts/Items/Weapons/BaseMeleeWeapon.cs create mode 100644 Scripts/Items/Weapons/BaseWeapon.cs create mode 100644 Scripts/Items/Weapons/Fists.cs create mode 100644 Scripts/Items/Weapons/HitLower.cs create mode 100644 Scripts/Items/Weapons/Knives/BaseKnife.cs create mode 100644 Scripts/Items/Weapons/Knives/ButcherKnife.cs create mode 100644 Scripts/Items/Weapons/Knives/Cleaver.cs create mode 100644 Scripts/Items/Weapons/Knives/Dagger.cs create mode 100644 Scripts/Items/Weapons/Knives/SkinningKnife.cs create mode 100644 Scripts/Items/Weapons/Knives/ThrowingDagger.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/AssassinSpike.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/DiamondMace.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/ElvenMachete.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/ElvenSpellblade.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/Leafblade.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/MagicalShortbow.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/OrnateAxe.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/RadiantScimitar.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/RuneBlade.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/WarCleaver.cs create mode 100644 Scripts/Items/Weapons/ML Weapons/WildStaff.cs create mode 100644 Scripts/Items/Weapons/Maces/BaseBashing.cs create mode 100644 Scripts/Items/Weapons/Maces/Club.cs create mode 100644 Scripts/Items/Weapons/Maces/FireworksWand.cs create mode 100644 Scripts/Items/Weapons/Maces/HammerPick.cs create mode 100644 Scripts/Items/Weapons/Maces/Mace.cs create mode 100644 Scripts/Items/Weapons/Maces/MagicWand.cs create mode 100644 Scripts/Items/Weapons/Maces/Maul.cs create mode 100644 Scripts/Items/Weapons/Maces/Scepter.cs create mode 100644 Scripts/Items/Weapons/Maces/WarHammer.cs create mode 100644 Scripts/Items/Weapons/Maces/WarMace.cs create mode 100644 Scripts/Items/Weapons/PoleArms/Bardiche.cs create mode 100644 Scripts/Items/Weapons/PoleArms/BasePoleArm.cs create mode 100644 Scripts/Items/Weapons/PoleArms/Halberd.cs create mode 100644 Scripts/Items/Weapons/PoleArms/Scythe.cs create mode 100644 Scripts/Items/Weapons/Ranged/BaseRanged.cs create mode 100644 Scripts/Items/Weapons/Ranged/Bow.cs create mode 100644 Scripts/Items/Weapons/Ranged/CompositeBow.cs create mode 100644 Scripts/Items/Weapons/Ranged/Crossbow.cs create mode 100644 Scripts/Items/Weapons/Ranged/HeavyCrossbow.cs create mode 100644 Scripts/Items/Weapons/Ranged/JukaBow.cs create mode 100644 Scripts/Items/Weapons/Ranged/RepeatingCrossbow.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Bokuto.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Daisho.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Kama.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Lajatang.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/NoDachi.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Nunchaku.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Sai.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Tekagi.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Tessen.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Tetsubo.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Wakizashi.cs create mode 100644 Scripts/Items/Weapons/SE Weapons/Yumi.cs create mode 100644 Scripts/Items/Weapons/SlayerEntry.cs create mode 100644 Scripts/Items/Weapons/SlayerGroup.cs create mode 100644 Scripts/Items/Weapons/SlayerName.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/BladedStaff.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/Pike.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/Pitchfork.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/ShortSpear.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/Spear.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/TribalSpear.cs create mode 100644 Scripts/Items/Weapons/SpearsAndForks/WarFork.cs create mode 100644 Scripts/Items/Weapons/Staves/BaseStaff.cs create mode 100644 Scripts/Items/Weapons/Staves/BlackStaff.cs create mode 100644 Scripts/Items/Weapons/Staves/GlacialStaff.cs create mode 100644 Scripts/Items/Weapons/Staves/GnarledStaff.cs create mode 100644 Scripts/Items/Weapons/Staves/QuarterStaff.cs create mode 100644 Scripts/Items/Weapons/Staves/ShepherdsCrook.cs create mode 100644 Scripts/Items/Weapons/Swords/BaseSword.cs create mode 100644 Scripts/Items/Weapons/Swords/BoneHarvester.cs create mode 100644 Scripts/Items/Weapons/Swords/Broadsword.cs create mode 100644 Scripts/Items/Weapons/Swords/CrescentBlade.cs create mode 100644 Scripts/Items/Weapons/Swords/Cutlass.cs create mode 100644 Scripts/Items/Weapons/Swords/Katana.cs create mode 100644 Scripts/Items/Weapons/Swords/Kryss.cs create mode 100644 Scripts/Items/Weapons/Swords/Lance.cs create mode 100644 Scripts/Items/Weapons/Swords/Longsword.cs create mode 100644 Scripts/Items/Weapons/Swords/Scimitar.cs create mode 100644 Scripts/Items/Weapons/Swords/ThinLongsword.cs create mode 100644 Scripts/Items/Weapons/Swords/VikingSword.cs create mode 100644 Scripts/Items/Weapons/WeaponEnums.cs create mode 100644 Scripts/Misc/AOS.cs create mode 100644 Scripts/Misc/AccountPrompt.cs create mode 100644 Scripts/Misc/Animations.cs create mode 100644 Scripts/Misc/AttackMessage.cs create mode 100644 Scripts/Misc/AutoRestart.cs create mode 100644 Scripts/Misc/AutoSave.cs create mode 100644 Scripts/Misc/Broadcasts.cs create mode 100644 Scripts/Misc/BuffIcons.cs create mode 100644 Scripts/Misc/CharacterCreation.cs create mode 100644 Scripts/Misc/Cleanup.cs create mode 100644 Scripts/Misc/ClientVerification.cs create mode 100644 Scripts/Misc/CrashGuard.cs create mode 100644 Scripts/Misc/CurrentExpansion.cs create mode 100644 Scripts/Misc/DataPath.cs create mode 100644 Scripts/Misc/DispellableAttribute.cs create mode 100644 Scripts/Misc/DispellableFieldAttribute.cs create mode 100644 Scripts/Misc/DoorGenerator.cs create mode 100644 Scripts/Misc/Email.cs create mode 100644 Scripts/Misc/Emitter.cs create mode 100644 Scripts/Misc/Fastwalk.cs create mode 100644 Scripts/Misc/FoodDecay.cs create mode 100644 Scripts/Misc/Gifts/GiftGiving.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/DecorativeTopiary.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/FestiveCactus.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/Mistletoe.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/SnowPile.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/SnowyTree.cs create mode 100644 Scripts/Misc/Gifts/Winter2004/Winter2004.cs create mode 100644 Scripts/Misc/Guild.cs create mode 100644 Scripts/Misc/HardwareInfo.cs create mode 100644 Scripts/Misc/InhumanSpeech.cs create mode 100644 Scripts/Misc/Keywords.cs create mode 100644 Scripts/Misc/LanguageStatistics.cs create mode 100644 Scripts/Misc/LightCycle.cs create mode 100644 Scripts/Misc/LoginStats.cs create mode 100644 Scripts/Misc/Loot.cs create mode 100644 Scripts/Misc/LootPack.cs create mode 100644 Scripts/Misc/MapDefinitions.cs create mode 100644 Scripts/Misc/NameList.cs create mode 100644 Scripts/Misc/NameVerification.cs create mode 100644 Scripts/Misc/Notoriety.cs create mode 100644 Scripts/Misc/OreInfo.cs create mode 100644 Scripts/Misc/Paperdoll.cs create mode 100644 Scripts/Misc/Poison.cs create mode 100644 Scripts/Misc/ProfanityProtection.cs create mode 100644 Scripts/Misc/Profile.cs create mode 100644 Scripts/Misc/ProtocolExtensions.cs create mode 100644 Scripts/Misc/RaceDefinitions.cs create mode 100644 Scripts/Misc/RegenRates.cs create mode 100644 Scripts/Misc/RenameRequests.cs create mode 100644 Scripts/Misc/ServerList.cs create mode 100644 Scripts/Misc/ShardPoller.cs create mode 100644 Scripts/Misc/ShrinkTable.cs create mode 100644 Scripts/Misc/SkillCheck.cs create mode 100644 Scripts/Misc/SocketOptions.cs create mode 100644 Scripts/Misc/Test Center/TestCenter.cs create mode 100644 Scripts/Misc/TextDefinition.cs create mode 100644 Scripts/Misc/Titles.cs create mode 100644 Scripts/Misc/TreasureMapProtection.cs create mode 100644 Scripts/Misc/VendorGenerator.cs create mode 100644 Scripts/Misc/Weather.cs create mode 100644 Scripts/Misc/WebStatus.cs create mode 100644 Scripts/Misc/WeightOverloading.cs create mode 100644 Scripts/Misc/WelcomeTimer.cs create mode 100644 Scripts/Misc/uoamVendors.cs create mode 100644 Scripts/Mobiles/Animals/Bears/BlackBear.cs create mode 100644 Scripts/Mobiles/Animals/Bears/BrownBear.cs create mode 100644 Scripts/Mobiles/Animals/Bears/GrizzlyBear.cs create mode 100644 Scripts/Mobiles/Animals/Bears/PolarBear.cs create mode 100644 Scripts/Mobiles/Animals/Birds/Chicken.cs create mode 100644 Scripts/Mobiles/Animals/Birds/Crane.cs create mode 100644 Scripts/Mobiles/Animals/Birds/Eagle.cs create mode 100644 Scripts/Mobiles/Animals/Birds/Phoenix.cs create mode 100644 Scripts/Mobiles/Animals/Canines/DireWolf.cs create mode 100644 Scripts/Mobiles/Animals/Canines/GreyWolf.cs create mode 100644 Scripts/Mobiles/Animals/Canines/TimberWolf.cs create mode 100644 Scripts/Mobiles/Animals/Canines/WhiteWolf.cs create mode 100644 Scripts/Mobiles/Animals/Cows/Bull.cs create mode 100644 Scripts/Mobiles/Animals/Cows/Cow.cs create mode 100644 Scripts/Mobiles/Animals/Felines/Cougar.cs create mode 100644 Scripts/Mobiles/Animals/Felines/HellCat.cs create mode 100644 Scripts/Mobiles/Animals/Felines/Panther.cs create mode 100644 Scripts/Mobiles/Animals/Felines/PredatorHellCat.cs create mode 100644 Scripts/Mobiles/Animals/Felines/SnowLeopard.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Boar.cs create mode 100644 Scripts/Mobiles/Animals/Misc/BullFrog.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Dolphin.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Gaman.cs create mode 100644 Scripts/Mobiles/Animals/Misc/GiantToad.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Goat.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Gorilla.cs create mode 100644 Scripts/Mobiles/Animals/Misc/GreatHart.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Hind.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Llama.cs create mode 100644 Scripts/Mobiles/Animals/Misc/MountainGoat.cs create mode 100644 Scripts/Mobiles/Animals/Misc/PackHorse.cs create mode 100644 Scripts/Mobiles/Animals/Misc/PackLlama.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Pig.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Sheep.cs create mode 100644 Scripts/Mobiles/Animals/Misc/Walrus.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/BaseMount.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Beetle.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/DesertOstard.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Ethereals.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/FireSteed.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/ForestOstard.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/FrenziedOstard.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/HellSteed.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Hiryu.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Horse.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Kirin.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/LesserHiryu.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Nightmare.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/RidableLlama.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Ridgeback.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/SavageRidgeback.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/ScaledSwampDragon.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/SeaHorse.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/SilverSteed.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/SkeletalMount.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/SwampDragon.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/Unicorn.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs create mode 100644 Scripts/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/Alligator.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/GiantSerpent.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/IceSerpent.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/IceSnake.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/LavaLizard.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/LavaSerpent.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/LavaSnake.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/SilverSerpent.cs create mode 100644 Scripts/Mobiles/Animals/Reptiles/Snake.cs create mode 100644 Scripts/Mobiles/Animals/Rodents/GiantRat.cs create mode 100644 Scripts/Mobiles/Animals/Rodents/JackRabbit.cs create mode 100644 Scripts/Mobiles/Animals/Rodents/Rabbit.cs create mode 100644 Scripts/Mobiles/Animals/Rodents/SewerRat.cs create mode 100644 Scripts/Mobiles/Animals/Slimes/Jwilson.cs create mode 100644 Scripts/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs create mode 100644 Scripts/Mobiles/Animals/Town Critters/Bird.cs create mode 100644 Scripts/Mobiles/Animals/Town Critters/Cat.cs create mode 100644 Scripts/Mobiles/Animals/Town Critters/Dog.cs create mode 100644 Scripts/Mobiles/Animals/Town Critters/Rat.cs create mode 100644 Scripts/Mobiles/Familiars/BaseFamiliar.cs create mode 100644 Scripts/Mobiles/Familiars/DarkWolf.cs create mode 100644 Scripts/Mobiles/Familiars/DeathAdder.cs create mode 100644 Scripts/Mobiles/Familiars/HordeMinion.cs create mode 100644 Scripts/Mobiles/Familiars/ShadowWisp.cs create mode 100644 Scripts/Mobiles/Familiars/VampireBat.cs create mode 100644 Scripts/Mobiles/Guards/ArcherGuard.cs create mode 100644 Scripts/Mobiles/Guards/BaseGuard.cs create mode 100644 Scripts/Mobiles/Guards/WarriorGuard.cs create mode 100644 Scripts/Mobiles/Healers/BaseHealer.cs create mode 100644 Scripts/Mobiles/Healers/EvilHealer.cs create mode 100644 Scripts/Mobiles/Healers/EvilWanderingHealer.cs create mode 100644 Scripts/Mobiles/Healers/FortuneTeller.cs create mode 100644 Scripts/Mobiles/Healers/Healer.cs create mode 100644 Scripts/Mobiles/Healers/PricedHealer.cs create mode 100644 Scripts/Mobiles/Healers/WanderingHealer.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/AbysmalHorror.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/BoneDemon.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/CrystalElemental.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/DarknightCreeper.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/DemonKnight.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/Devourer.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/FleshGolem.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/FleshRenderer.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/Gibberling.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/GoreFiend.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/Impaler.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/MoundOfMaggots.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/PatchworkSkeleton.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/Ravager.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/Revenant.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/ShadowKnight.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/SkitteringHopper.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/Treefellow.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/VampireBat.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/WailingBanshee.cs create mode 100644 Scripts/Mobiles/Monsters/AOS/WandererOfTheVoid.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/AntLion.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/BlackSolenQueen.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/BlackSolenWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/BlackSolenWorker.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/RedSolenQueen.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/RedSolenWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/RedSolenWorker.cs create mode 100644 Scripts/Mobiles/Monsters/Ants/SolenHelper.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs create mode 100644 Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/AirElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/Efreet.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/FireElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/IceElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/ToxicElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Balron.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Bogle.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Daemon.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Gazer.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/GolemController.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Imp.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Lich.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/LichLord.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Shade.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Spectre.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Succubus.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Titan.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Magic/Wraith.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Brigand.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Cursed.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Ettin.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Executioner.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Guardian.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Moloch.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Mummy.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Ogre.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Orc.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Ratman.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Savage.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/SpawnedOrcishLord.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Troll.cs create mode 100644 Scripts/Mobiles/Monsters/Humanoid/Melee/Zombie.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Jukas/JukaLord.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Jukas/JukaMage.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Meers/MeerEternal.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Meers/MeerMage.cs create mode 100644 Scripts/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Mammal/Melee/HellHound.cs create mode 100644 Scripts/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Magic/DarkWisp.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Magic/Pixie.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Magic/Wisp.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/Centaur.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/FrostOoze.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/Golem.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/SandVortex.cs create mode 100644 Scripts/Mobiles/Monsters/Misc/Melee/Slime.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/CopperElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Magic/Reaper.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Melee/BogThing.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Melee/Bogling.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Melee/Corpser.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Melee/Quagmire.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs create mode 100644 Scripts/Mobiles/Monsters/Plant/Melee/WhippingVine.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/Dragon.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/Leviathan.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/Drake.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/Harpy.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/Lizardman.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/Scorpion.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs create mode 100644 Scripts/Mobiles/Monsters/Reptile/Melee/Wyvern.cs create mode 100644 Scripts/Mobiles/Monsters/SE/BakeKitsune.cs create mode 100644 Scripts/Mobiles/Monsters/SE/DeathWatchBeetle.cs create mode 100644 Scripts/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs create mode 100644 Scripts/Mobiles/Monsters/SE/EliteNinja.cs create mode 100644 Scripts/Mobiles/Monsters/SE/FanDancer.cs create mode 100644 Scripts/Mobiles/Monsters/SE/FireBeetle.cs create mode 100644 Scripts/Mobiles/Monsters/SE/Kappa.cs create mode 100644 Scripts/Mobiles/Monsters/SE/KazeKemono.cs create mode 100644 Scripts/Mobiles/Monsters/SE/LadyOfTheSnow.cs create mode 100644 Scripts/Mobiles/Monsters/SE/Oni.cs create mode 100644 Scripts/Mobiles/Monsters/SE/RaiJu.cs create mode 100644 Scripts/Mobiles/Monsters/SE/RevenantLion.cs create mode 100644 Scripts/Mobiles/Monsters/SE/Ronin.cs create mode 100644 Scripts/Mobiles/Monsters/SE/RuneBeetle.cs create mode 100644 Scripts/Mobiles/Monsters/SE/TsukiWolf.cs create mode 100644 Scripts/Mobiles/Monsters/SE/Yamandon.cs create mode 100644 Scripts/Mobiles/Monsters/SE/YomotsuElder.cs create mode 100644 Scripts/Mobiles/Monsters/SE/YomotsuPriest.cs create mode 100644 Scripts/Mobiles/Monsters/SE/YomotsuWarrior.cs create mode 100644 Scripts/Mobiles/Monsters/Summons/SummonedAirElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs create mode 100644 Scripts/Mobiles/Monsters/Summons/SummonedEarthElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Summons/SummonedFireElemental.cs create mode 100644 Scripts/Mobiles/Monsters/Summons/SummonedWaterElemental.cs create mode 100644 Scripts/Mobiles/PlayerMobile.cs create mode 100644 Scripts/Mobiles/Special/Barracoon.cs create mode 100644 Scripts/Mobiles/Special/BaseChampion.cs create mode 100644 Scripts/Mobiles/Special/BaseShieldGuard.cs create mode 100644 Scripts/Mobiles/Special/CapturedHordeMinion.cs create mode 100644 Scripts/Mobiles/Special/ChaosGuard.cs create mode 100644 Scripts/Mobiles/Special/Harrower.cs create mode 100644 Scripts/Mobiles/Special/HarrowerTentacles.cs create mode 100644 Scripts/Mobiles/Special/LordOaks.cs create mode 100644 Scripts/Mobiles/Special/Mephitis.cs create mode 100644 Scripts/Mobiles/Special/Neira.cs create mode 100644 Scripts/Mobiles/Special/OrderGuard.cs create mode 100644 Scripts/Mobiles/Special/Rikktor.cs create mode 100644 Scripts/Mobiles/Special/Semidar.cs create mode 100644 Scripts/Mobiles/Special/Serado.cs create mode 100644 Scripts/Mobiles/Special/ServantOfSemidar.cs create mode 100644 Scripts/Mobiles/Special/Silvani.cs create mode 100644 Scripts/Mobiles/Special/Wanderer.cs create mode 100644 Scripts/Mobiles/Townfolk/Actor.cs create mode 100644 Scripts/Mobiles/Townfolk/Artist.cs create mode 100644 Scripts/Mobiles/Townfolk/Banker.cs create mode 100644 Scripts/Mobiles/Townfolk/BaseEscortable.cs create mode 100644 Scripts/Mobiles/Townfolk/BrideGroom.cs create mode 100644 Scripts/Mobiles/Townfolk/EscortableMage.cs create mode 100644 Scripts/Mobiles/Townfolk/Gypsy.cs create mode 100644 Scripts/Mobiles/Townfolk/HarborMaster.cs create mode 100644 Scripts/Mobiles/Townfolk/Merchant.cs create mode 100644 Scripts/Mobiles/Townfolk/Messenger.cs create mode 100644 Scripts/Mobiles/Townfolk/Minter.cs create mode 100644 Scripts/Mobiles/Townfolk/Ninja.cs create mode 100644 Scripts/Mobiles/Townfolk/Noble.cs create mode 100644 Scripts/Mobiles/Townfolk/Peasant.cs create mode 100644 Scripts/Mobiles/Townfolk/Samurai.cs create mode 100644 Scripts/Mobiles/Townfolk/Sculptor.cs create mode 100644 Scripts/Mobiles/Townfolk/SeekerOfAdventure.cs create mode 100644 Scripts/Mobiles/Townfolk/TownCrier.cs create mode 100644 Scripts/Mobiles/Vendors/AnimalBuy.cs create mode 100644 Scripts/Mobiles/Vendors/BaseVendor.cs create mode 100644 Scripts/Mobiles/Vendors/BeverageBuy.cs create mode 100644 Scripts/Mobiles/Vendors/GenericBuy.cs create mode 100644 Scripts/Mobiles/Vendors/GenericSell.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Alchemist.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/AnimalTrainer.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Architect.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Armorer.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Baker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Bard.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Barkeeper.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Beekeeper.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Blacksmith.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Bowyer.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Butcher.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Carpenter.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Cobbler.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Cook.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/CustomHairstylist.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Farmer.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Fisherman.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Furtrader.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Glassblower.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/GolemCrafter.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/BardGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/FisherGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/HealerGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/MageGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/MerchantGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/MinerGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/RangerGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/TailorGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/ThiefGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Guildmasters/WarriorGuildmaster.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/GypsyAnimalTrainer.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/GypsyBanker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/GypsyMaiden.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/HairStylist.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Herbalist.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/HolyMage.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/InnKeeper.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/IronWorker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Jeweler.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/KeeperOfChivalry.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/LeatherWorker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Mage.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Mapmaker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Miller.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Miner.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Provisioner.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Rancher.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Ranger.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/RealEstateBroker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Scribe.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Shipwright.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/StoneCrafter.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Tailor.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Tanner.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/TavernKeeper.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Thief.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Tinker.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Vagabond.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/VarietyDealer.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Veterinarian.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Waiter.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Weaponsmith.cs create mode 100644 Scripts/Mobiles/Vendors/NPC/Weaver.cs create mode 100644 Scripts/Mobiles/Vendors/PlayerBarkeeper.cs create mode 100644 Scripts/Mobiles/Vendors/PlayerVendor.cs create mode 100644 Scripts/Mobiles/Vendors/PresetMapBuy.cs create mode 100644 Scripts/Mobiles/Vendors/RentedVendor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBChainmailArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBHelmetArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBLeatherArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBMetalShields.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBPlateArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBRingmailArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBStuddedArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Armors/SBWoodenShields.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBAlchemist.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBAnimalTrainer.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBArchitect.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBaker.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBanker.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBard.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBarkeeper.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBeekeeper.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBlacksmith.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBBowyer.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBButcher.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBCarpenter.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBCobbler.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBCook.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBFarmer.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBFisherman.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBFortuneTeller.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBFurtrader.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBGlassblower.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBHairStylist.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBHealer.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBHerbalist.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBHolyMage.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBHouseDeed.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBInfo.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBInnKeeper.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBJewel.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBKeeperOfChivalry.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBLeatherWorker.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBMage.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBMapmaker.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBMiller.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBMiner.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBPlayerBarkeeper.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBProvisioner.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBRancher.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBRanger.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBRealEstateBroker.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBSECook.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBSEHats.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBScribe.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBShipwright.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBSmithTools.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBStoneCrafter.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBTailor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBTanner.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBTavernKeeper.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBThief.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBTinker.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBVagabond.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBVarietyDealer.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBVeterinarian.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBWaiter.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBWeaponSmith.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SBWeaver.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SE/SBSEArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SE/SBSEBowyer.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SE/SBSECarpenter.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SE/SBSEFood.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SE/SBSELeatherArmor.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/SE/SBSEWeapons.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBAxeWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBKnifeWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBMaceWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBPoleArmWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBRangedWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSpearForkWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBStavesWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSwordWeapon.cs create mode 100644 Scripts/Mobiles/Vendors/VendorInventory.cs create mode 100644 Scripts/Multis/BaseHouse.cs create mode 100644 Scripts/Multis/Boats/BaseBoat.cs create mode 100644 Scripts/Multis/Boats/BaseBoatDeed.cs create mode 100644 Scripts/Multis/Boats/BaseDockedBoat.cs create mode 100644 Scripts/Multis/Boats/ConfirmDryDockGump.cs create mode 100644 Scripts/Multis/Boats/Hold.cs create mode 100644 Scripts/Multis/Boats/LargeBoat.cs create mode 100644 Scripts/Multis/Boats/LargeDragonBoat.cs create mode 100644 Scripts/Multis/Boats/MediumBoat.cs create mode 100644 Scripts/Multis/Boats/MediumDragonBoat.cs create mode 100644 Scripts/Multis/Boats/Plank.cs create mode 100644 Scripts/Multis/Boats/RenameBoatPrompt.cs create mode 100644 Scripts/Multis/Boats/SmallBoat.cs create mode 100644 Scripts/Multis/Boats/SmallDragonBoat.cs create mode 100644 Scripts/Multis/Boats/Strandedness.cs create mode 100644 Scripts/Multis/Boats/TillerMan.cs create mode 100644 Scripts/Multis/Camps/BankerCamp.cs create mode 100644 Scripts/Multis/Camps/BaseCamp.cs create mode 100644 Scripts/Multis/Camps/HealerCamp.cs create mode 100644 Scripts/Multis/Camps/MageCamp.cs create mode 100644 Scripts/Multis/Camps/OrcCamp.cs create mode 100644 Scripts/Multis/Camps/RatCamp.cs create mode 100644 Scripts/Multis/ComponentVerification.cs create mode 100644 Scripts/Multis/Deeds.cs create mode 100644 Scripts/Multis/HouseFoundation.cs create mode 100644 Scripts/Multis/HousePlacement.cs create mode 100644 Scripts/Multis/HousePlacementTool.cs create mode 100644 Scripts/Multis/HouseSign.cs create mode 100644 Scripts/Multis/HouseTeleporter.cs create mode 100644 Scripts/Multis/Houses.cs create mode 100644 Scripts/Multis/MovingCrate.cs create mode 100644 Scripts/Multis/PreviewHouse.cs create mode 100644 Scripts/Regions/BaseRegion.cs create mode 100644 Scripts/Regions/DungeonRegion.cs create mode 100644 Scripts/Regions/GreenAcres.cs create mode 100644 Scripts/Regions/GuardedRegion.cs create mode 100644 Scripts/Regions/HouseRegion.cs create mode 100644 Scripts/Regions/Jail.cs create mode 100644 Scripts/Regions/NoHousingRegion.cs create mode 100644 Scripts/Regions/Spawning/SpawnDefinition.cs create mode 100644 Scripts/Regions/Spawning/SpawnEntry.cs create mode 100644 Scripts/Regions/Spawning/SpawnPersistence.cs create mode 100644 Scripts/Regions/TownRegion.cs create mode 100644 Scripts/Skills/Anatomy.cs create mode 100644 Scripts/Skills/AnimalLore.cs create mode 100644 Scripts/Skills/AnimalTaming.cs create mode 100644 Scripts/Skills/ArmsLore.cs create mode 100644 Scripts/Skills/Begging.cs create mode 100644 Scripts/Skills/DetectHidden.cs create mode 100644 Scripts/Skills/Discordance.cs create mode 100644 Scripts/Skills/EvalInt.cs create mode 100644 Scripts/Skills/ForensicEval.cs create mode 100644 Scripts/Skills/Hiding.cs create mode 100644 Scripts/Skills/Inscribe.cs create mode 100644 Scripts/Skills/ItemIdentification.cs create mode 100644 Scripts/Skills/Meditation.cs create mode 100644 Scripts/Skills/Peacemaking.cs create mode 100644 Scripts/Skills/Poisoning.cs create mode 100644 Scripts/Skills/Provocation.cs create mode 100644 Scripts/Skills/RemoveTrap.cs create mode 100644 Scripts/Skills/Snooping.cs create mode 100644 Scripts/Skills/SpiritSpeak.cs create mode 100644 Scripts/Skills/Stealing.cs create mode 100644 Scripts/Skills/Stealth.cs create mode 100644 Scripts/Skills/TasteID.cs create mode 100644 Scripts/Skills/Tracking.cs create mode 100644 Scripts/Spells/Base/DisturbType.cs create mode 100644 Scripts/Spells/Base/SpecialMove.cs create mode 100644 Scripts/Spells/Base/Spell.cs create mode 100644 Scripts/Spells/Base/SpellCircle.cs create mode 100644 Scripts/Spells/Base/SpellHelper.cs create mode 100644 Scripts/Spells/Base/SpellInfo.cs create mode 100644 Scripts/Spells/Base/SpellRegistry.cs create mode 100644 Scripts/Spells/Base/SpellState.cs create mode 100644 Scripts/Spells/Bushido/Confidence.cs create mode 100644 Scripts/Spells/Bushido/CounterAttack.cs create mode 100644 Scripts/Spells/Bushido/Evasion.cs create mode 100644 Scripts/Spells/Bushido/HonorableExecution.cs create mode 100644 Scripts/Spells/Bushido/LightningStrike.cs create mode 100644 Scripts/Spells/Bushido/MomentumStrike.cs create mode 100644 Scripts/Spells/Bushido/SamuraiMove.cs create mode 100644 Scripts/Spells/Bushido/SamuraiSpell.cs create mode 100644 Scripts/Spells/Chivalry/CleanseByFire.cs create mode 100644 Scripts/Spells/Chivalry/CloseWounds.cs create mode 100644 Scripts/Spells/Chivalry/ConsecrateWeapon.cs create mode 100644 Scripts/Spells/Chivalry/DispelEvil.cs create mode 100644 Scripts/Spells/Chivalry/DivineFury.cs create mode 100644 Scripts/Spells/Chivalry/EnemyOfOne.cs create mode 100644 Scripts/Spells/Chivalry/HolyLight.cs create mode 100644 Scripts/Spells/Chivalry/NobleSacrifice.cs create mode 100644 Scripts/Spells/Chivalry/PaladinSpell.cs create mode 100644 Scripts/Spells/Chivalry/RemoveCurse.cs create mode 100644 Scripts/Spells/Chivalry/SacredJourney.cs create mode 100644 Scripts/Spells/Eighth/AirElemental.cs create mode 100644 Scripts/Spells/Eighth/EarthElemental.cs create mode 100644 Scripts/Spells/Eighth/Earthquake.cs create mode 100644 Scripts/Spells/Eighth/EnergyVortex.cs create mode 100644 Scripts/Spells/Eighth/FireElemental.cs create mode 100644 Scripts/Spells/Eighth/Resurrection.cs create mode 100644 Scripts/Spells/Eighth/SummonDaemon.cs create mode 100644 Scripts/Spells/Eighth/WaterElemental.cs create mode 100644 Scripts/Spells/Fifth/BladeSpirits.cs create mode 100644 Scripts/Spells/Fifth/DispelField.cs create mode 100644 Scripts/Spells/Fifth/Incognito.cs create mode 100644 Scripts/Spells/Fifth/MagicReflect.cs create mode 100644 Scripts/Spells/Fifth/MindBlast.cs create mode 100644 Scripts/Spells/Fifth/Paralyze.cs create mode 100644 Scripts/Spells/Fifth/PoisonField.cs create mode 100644 Scripts/Spells/Fifth/SummonCreature.cs create mode 100644 Scripts/Spells/First/Clumsy.cs create mode 100644 Scripts/Spells/First/CreateFood.cs create mode 100644 Scripts/Spells/First/Feeblemind.cs create mode 100644 Scripts/Spells/First/Heal.cs create mode 100644 Scripts/Spells/First/MagicArrow.cs create mode 100644 Scripts/Spells/First/NightSight.cs create mode 100644 Scripts/Spells/First/ReactiveArmor.cs create mode 100644 Scripts/Spells/First/Weaken.cs create mode 100644 Scripts/Spells/Fourth/ArchCure.cs create mode 100644 Scripts/Spells/Fourth/ArchProtection.cs create mode 100644 Scripts/Spells/Fourth/Curse.cs create mode 100644 Scripts/Spells/Fourth/FireField.cs create mode 100644 Scripts/Spells/Fourth/GreaterHeal.cs create mode 100644 Scripts/Spells/Fourth/Lightning.cs create mode 100644 Scripts/Spells/Fourth/ManaDrain.cs create mode 100644 Scripts/Spells/Fourth/Recall.cs create mode 100644 Scripts/Spells/Initializer.cs create mode 100644 Scripts/Spells/Necromancy/AnimateDeadSpell.cs create mode 100644 Scripts/Spells/Necromancy/BloodOathSpell.cs create mode 100644 Scripts/Spells/Necromancy/CorpseSkin.cs create mode 100644 Scripts/Spells/Necromancy/CurseWeapon.cs create mode 100644 Scripts/Spells/Necromancy/EvilOmen.cs create mode 100644 Scripts/Spells/Necromancy/Exorcism.cs create mode 100644 Scripts/Spells/Necromancy/HorrificBeast.cs create mode 100644 Scripts/Spells/Necromancy/LichForm.cs create mode 100644 Scripts/Spells/Necromancy/MindRot.cs create mode 100644 Scripts/Spells/Necromancy/NecromancerSpell.cs create mode 100644 Scripts/Spells/Necromancy/PainSpike.cs create mode 100644 Scripts/Spells/Necromancy/PoisonStrike.cs create mode 100644 Scripts/Spells/Necromancy/Strangle.cs create mode 100644 Scripts/Spells/Necromancy/SummonFamiliar.cs create mode 100644 Scripts/Spells/Necromancy/TransformationSpell.cs create mode 100644 Scripts/Spells/Necromancy/VampiricEmbrace.cs create mode 100644 Scripts/Spells/Necromancy/VengefulSpirit.cs create mode 100644 Scripts/Spells/Necromancy/Wither.cs create mode 100644 Scripts/Spells/Necromancy/WraithForm.cs create mode 100644 Scripts/Spells/Ninjitsu/AnimalForm.cs create mode 100644 Scripts/Spells/Ninjitsu/Backstab.cs create mode 100644 Scripts/Spells/Ninjitsu/DeathStrike.cs create mode 100644 Scripts/Spells/Ninjitsu/FocusAttack.cs create mode 100644 Scripts/Spells/Ninjitsu/KiAttack.cs create mode 100644 Scripts/Spells/Ninjitsu/MirrorImage.cs create mode 100644 Scripts/Spells/Ninjitsu/NinjaMove.cs create mode 100644 Scripts/Spells/Ninjitsu/NinjaSpell.cs create mode 100644 Scripts/Spells/Ninjitsu/ShadowJump.cs create mode 100644 Scripts/Spells/Ninjitsu/SurpriseAttack.cs create mode 100644 Scripts/Spells/Reagent.cs create mode 100644 Scripts/Spells/Second/Agility.cs create mode 100644 Scripts/Spells/Second/Cunning.cs create mode 100644 Scripts/Spells/Second/Cure.cs create mode 100644 Scripts/Spells/Second/Harm.cs create mode 100644 Scripts/Spells/Second/MagicTrap.cs create mode 100644 Scripts/Spells/Second/Protection.cs create mode 100644 Scripts/Spells/Second/RemoveTrap.cs create mode 100644 Scripts/Spells/Second/Strength.cs create mode 100644 Scripts/Spells/Seventh/ChainLightning.cs create mode 100644 Scripts/Spells/Seventh/EnergyField.cs create mode 100644 Scripts/Spells/Seventh/FlameStrike.cs create mode 100644 Scripts/Spells/Seventh/GateTravel.cs create mode 100644 Scripts/Spells/Seventh/ManaVampire.cs create mode 100644 Scripts/Spells/Seventh/MassDispel.cs create mode 100644 Scripts/Spells/Seventh/MeteorSwarm.cs create mode 100644 Scripts/Spells/Seventh/Polymorph.cs create mode 100644 Scripts/Spells/Sixth/Dispel.cs create mode 100644 Scripts/Spells/Sixth/EnergyBolt.cs create mode 100644 Scripts/Spells/Sixth/Explosion.cs create mode 100644 Scripts/Spells/Sixth/Invisibility.cs create mode 100644 Scripts/Spells/Sixth/Mark.cs create mode 100644 Scripts/Spells/Sixth/MassCurse.cs create mode 100644 Scripts/Spells/Sixth/ParalyzeField.cs create mode 100644 Scripts/Spells/Sixth/Reveal.cs create mode 100644 Scripts/Spells/Third/Bless.cs create mode 100644 Scripts/Spells/Third/Fireball.cs create mode 100644 Scripts/Spells/Third/MagicLock.cs create mode 100644 Scripts/Spells/Third/Poison.cs create mode 100644 Scripts/Spells/Third/Telekinesis.cs create mode 100644 Scripts/Spells/Third/Teleport.cs create mode 100644 Scripts/Spells/Third/Unlock.cs create mode 100644 Scripts/Spells/Third/WallOfStone.cs create mode 100644 Scripts/Spells/UnsummonTimer.cs create mode 100644 Scripts/Targets/BladedItemTarget.cs create mode 100644 Scripts/Targets/MoveTarget.cs create mode 100644 Scripts/Targets/PickMoveTarget.cs create mode 100644 Server/AggressorInfo.cs create mode 100644 Server/AssemblyInfo.cs create mode 100644 Server/Attributes.cs create mode 100644 Server/BaseVendor.cs create mode 100644 Server/Body.cs create mode 100644 Server/ClientVersion.cs create mode 100644 Server/Commands.cs create mode 100644 Server/ContextMenus/ContextMenu.cs create mode 100644 Server/ContextMenus/ContextMenuEntry.cs create mode 100644 Server/ContextMenus/OpenBackpackEntry.cs create mode 100644 Server/ContextMenus/PaperdollEntry.cs create mode 100644 Server/Effects.cs create mode 100644 Server/EventLog.cs create mode 100644 Server/EventSink.cs create mode 100644 Server/ExpansionInfo.cs create mode 100644 Server/Geometry.cs create mode 100644 Server/Guild.cs create mode 100644 Server/Gumps/Gump.cs create mode 100644 Server/Gumps/GumpAlphaRegion.cs create mode 100644 Server/Gumps/GumpBackground.cs create mode 100644 Server/Gumps/GumpButton.cs create mode 100644 Server/Gumps/GumpCheck.cs create mode 100644 Server/Gumps/GumpEntry.cs create mode 100644 Server/Gumps/GumpGroup.cs create mode 100644 Server/Gumps/GumpHtml.cs create mode 100644 Server/Gumps/GumpHtmlLocalized.cs create mode 100644 Server/Gumps/GumpImage.cs create mode 100644 Server/Gumps/GumpImageTileButton.cs create mode 100644 Server/Gumps/GumpImageTiled.cs create mode 100644 Server/Gumps/GumpItem.cs create mode 100644 Server/Gumps/GumpLabel.cs create mode 100644 Server/Gumps/GumpLabelCropped.cs create mode 100644 Server/Gumps/GumpPage.cs create mode 100644 Server/Gumps/GumpRadio.cs create mode 100644 Server/Gumps/GumpTextEntry.cs create mode 100644 Server/Gumps/GumpTooltip.cs create mode 100644 Server/Gumps/RelayInfo.cs create mode 100644 Server/HuePicker.cs create mode 100644 Server/IAccount.cs create mode 100644 Server/IEntity.cs create mode 100644 Server/Insensitive.cs create mode 100644 Server/Interfaces.cs create mode 100644 Server/Item.cs create mode 100644 Server/ItemBounds.cs create mode 100644 Server/Items/BaseMulti.cs create mode 100644 Server/Items/Container.cs create mode 100644 Server/Items/Containers.cs create mode 100644 Server/Items/SecureTradeContainer.cs create mode 100644 Server/Items/VirtualHair.cs create mode 100644 Server/KeywordList.cs create mode 100644 Server/Main.cs create mode 100644 Server/Map.cs create mode 100644 Server/Menus/IMenu.cs create mode 100644 Server/Menus/ItemListMenu.cs create mode 100644 Server/Menus/QuestionMenu.cs create mode 100644 Server/Mobile.cs create mode 100644 Server/Movement.cs create mode 100644 Server/MultiData.cs create mode 100644 Server/Network/BufferPool.cs create mode 100644 Server/Network/ByteQueue.cs create mode 100644 Server/Network/Compression.cs create mode 100644 Server/Network/EncodedPacketHandler.cs create mode 100644 Server/Network/EncodedReader.cs create mode 100644 Server/Network/Listener.cs create mode 100644 Server/Network/MessagePump.cs create mode 100644 Server/Network/NetState.cs create mode 100644 Server/Network/PacketHandler.cs create mode 100644 Server/Network/PacketHandlers.cs create mode 100644 Server/Network/PacketProfile.cs create mode 100644 Server/Network/PacketReader.cs create mode 100644 Server/Network/PacketWriter.cs create mode 100644 Server/Network/Packets.cs create mode 100644 Server/Network/SendQueue.cs create mode 100644 Server/Notoriety.cs create mode 100644 Server/ObjectPropertyList.cs create mode 100644 Server/Party.cs create mode 100644 Server/Persistence/BinaryMemoryWriter.cs create mode 100644 Server/Persistence/DualSaveStrategy.cs create mode 100644 Server/Persistence/FileOperations.cs create mode 100644 Server/Persistence/FileQueue.cs create mode 100644 Server/Persistence/ParallelSaveStrategy.cs create mode 100644 Server/Persistence/SaveMetrics.cs create mode 100644 Server/Persistence/SaveStrategy.cs create mode 100644 Server/Persistence/SequentialFileWriter.cs create mode 100644 Server/Persistence/StandardSaveStrategy.cs create mode 100644 Server/Point3DList.cs create mode 100644 Server/Poison.cs create mode 100644 Server/Prompt.cs create mode 100644 Server/QuestArrow.cs create mode 100644 Server/Race.cs create mode 100644 Server/Region.cs create mode 100644 Server/ScriptCompiler.cs create mode 100644 Server/Sector.cs create mode 100644 Server/SecureTrade.cs create mode 100644 Server/Serial.cs create mode 100644 Server/Serialization.cs create mode 100644 Server/Skills.cs create mode 100644 Server/Targeting/LandTarget.cs create mode 100644 Server/Targeting/MultiTarget.cs create mode 100644 Server/Targeting/StaticTarget.cs create mode 100644 Server/Targeting/Target.cs create mode 100644 Server/Targeting/TargetCancelType.cs create mode 100644 Server/Targeting/TargetFlags.cs create mode 100644 Server/TileData.cs create mode 100644 Server/TileList.cs create mode 100644 Server/TileMatrix.cs create mode 100644 Server/TileMatrixPatch.cs create mode 100644 Server/Timer.cs create mode 100644 Server/Utility.cs create mode 100644 Server/VirtueInfo.cs create mode 100644 Server/World.cs create mode 100644 Server/runuo.ico diff --git a/Data/Assemblies.cfg b/Data/Assemblies.cfg new file mode 100644 index 000000000..357aefb97 --- /dev/null +++ b/Data/Assemblies.cfg @@ -0,0 +1,6 @@ +System.dll +System.Web.dll +System.Xml.dll +System.Data.dll +System.Drawing.dll +System.Windows.Forms.dll \ No newline at end of file diff --git a/Data/Binary/Bounds.bin b/Data/Binary/Bounds.bin new file mode 100644 index 0000000000000000000000000000000000000000..dbe5830d0e5601f36a0879915093dc592d07ba2c GIT binary patch literal 131072 zcmeFaS&XIEk>~fNmgdePt12t^y!Ymwcgd`s#adVki~FXHn^uw%MN*ZgFKXJeB zetB;$tSpkE@)FFbIBT3ZapJ^@vwTHS6l=wj{uV_?{~ayH^!L%?nBaQxVDVAGalt}x zLgBzp@kWA^#p}h9guhnYD>zepwjX{tg?}~S(@IZ#N9oC5&FAtS%;j89@e{?>gijP3 z3GWn}nXb(o-_7w;8E@zKPEP-|;_g)6*AhNke62Vne75*pAHOHXzme0qJ+rx8b4nla zrtebD_dpK6l;CX9%k?>bwVkg2ffPSiT+ZoMGaptGK2sQ<6=$huP8@f>^_<@HPm3P& z^NrN*>EeYHUkxY!PIxEx(o=){!-tHAnyXK4IIeo~j@%h}Ij_4eu<9yB+ z;rHiyULFBInZpBG4(5w)E*Cl&$H*J-N`|Lu@SThU8JEK#`F~M~2akMLa{j9s?o|FS zbGj%m@Tm72#T|)`x0P*zKvDFJHsk!A9{?&KKd)&w*QSG_w9UqV$ugp2+9) z;~M|WOC7<%`rgjC+ttv#L3|_YyH3VC8Yj@-5OjPa;ZxGXx2N#Q6y8$p)_m|nGuI#Odb7Ay<*jCUB)%d1fr{M1_fv|GcF?XfL&}MGfw;To0tB2a^7~h@p9f>$mtEIg;CB?>Ej8(j-c^N8Ml38x?`SZz8K9X zwV=sfYQ$&=^DC829-8%9d~fNFV9Ioe#b6l`Y|>^HHTD{?R;M4I9P96y7Nw z%i)jZbjBM6d>KU_;n)?_e_yT_eFSd3)RrCN@sIqG{)e*14Z+uhqZiu6cZJa}-zPYw zD{;cVoa0|k>DQzeT@Ldj`L9J?`f+oXv9oLe76yso-DOW0w1=!t?C3#sTAJT6)&ycpc>NQHahrL^t$# z+@4mx8Kr+zuqXJk;FRFQxjgJj&nvd$s_k<p>8OX(&Pml|d%*3O7H)b2 zua`F%zkPn%xsux%^YXK)zFGA$?OPC}{#j9Cd@}b-*bm=O3iv!|FI4Ra_oMCkuoKF8 z_gpRiwOqc}1=s`Rk8xhw1CFoS6}AJ8rSwy37j(6x<-+c;UGk+Af288+2B31{6tRnT9BWMxJ1)=S`%dA=lFBX&a5;xBXZ%dUskcr?6`z;9TR*+2^kx4I*B|>Gk6gXK>zMG5P4)d_gX=)?FokX}9(8jjVSpKkF)%^0%(5O!s!{o4xiYbbJ1caa{U`-br)}hDUnF zJ@XX!lUWZx2|J{pyL#1&+W5) zW&3fEuIp;Yld^8l3tpGjyejyZaN2i75PNA(;f(8J!gp>Le6hVdD`-7zIGgoEr#Pu% zu)|K}_>&3FN+a!D{viGDU3%#KPNvuE9Pr;!I~D}pp6@36?4;%&?DaW8^w@fTKKh6m8u!TGH^-$g$2?Rx{|mv(09-(j4uW%)1tJ01_XPlHjGVmy;icczC6%T!c|B(9IxqR@C z37@Ut)PJ&qW2a6F_qyXX*<@p~!ngWOBOZDvH*orceImkJTIYJ)|FDl^FRm(_dKP6b z&k0_t;7If}+0~H``~AKlV+$6=kv*5P^i8};>QvF3wc`?gn#4eDX(8x_XhpY750DWZ}B?w{q@oho&?#D&PoJL?k^z(}FfXL-iv7E;5k|fCZs&d2c<>Y5s)kyjsg)FIu z@WX)f8?>`X?{(~ipvOh*i+0r0_>3y|^;+z&V!x3-_F2Q88y#;ucvg0)>oJ_|`|Yyfz?fPN2>DE=t z@O1>?p`XJ(7`;EUANAGSI~+$JuV`Oy6g1tx7GDc#+n>Wmwd2qAm-!K#cKzctPc9a} z90CiK|EDQ@Hu1%r>|3%>3aZf!U1n-FhGeQ``7{5s;gVc^5z;J7Hu`5@|_-L&WZpnELw z8+!zMA3oRN0bk#betEy-#r9=KQo#5u$6eqhL@sQ{wgpQ$Lf<;Pn*Y6P&mi4f>%GzV z-H(S4qJH|A7_ur$2-wlT%%9bayT4a+_-eoZSF>N)e*ZbGWUy;iWv2jHk9a;^tCT}L z;nZinLOR<~b1B`bw7=H@F88R)<@f%`&AY4Dd|(!Pclm!4@@=N|A^Rm>Z)|E^%WpHE zn=Hd4-f%-6D&%4#?H@(@Cj#zZw5DAJ$aUlUlb_o z+st&BpJn()rpI#6c=A3^FSje`?&NgQ-freg#GBr-+&KT%RlVr#u1bdfuF7}2n~#pi-Z=XUk7f1q77Q@V}J=aRqXYqTSv`RwwkzbAUlKln*I?@9JM5PU`2fFI{c z_@O_S75=>BY*Fw_S`n=ZeolI%srb(oFUj9R`sY*li#pdrIVZK=X1#YU`$0~q!8WRTBQb(`mn>Zr8K^^LZ4{n`J+5WW868qf<$*y8q#y z<#AKviSjqn{5qE9kbbrQ2KkD3+PkTK!q16ZyT4nhy_@Y!1#=O@Owk^3i+1O`W5|uBKf_b5C7MiBmn;y{xi}o>Oc1N%koW6oj&7?(+@8F zYLI%rw3qgdTOO+Y#CR<68!f*skM9n*Jaw{Mbu;Yr`*~FSsPM1{kO#)caJ(sY5+ATk zS5Hqm+Z~1_|B$n$VuA0C-0wx+p?k2uxB9<1Uq-s=biR-CHm_?1&AyYvBYsBvKJ4H0 z6wdjpQ%Y}tRpj?MS>j#Z>-~88XL;A?r_y;uL+ZaTpO+yX{&0TA`**JYl+wGsu5VI!#6u7H zs`0L$c6?)iZ&TtU^i5~~A@Q!?e$0rU$@`Jqhw(mLl;iVvliH_r{XSneCEWFYBb6U= zG+lfv%f+`+`)7nh?{q#dMLhC%Quzs=sX)u$+iDx~_tro;{GHA1!{2B+ zEvMkbTmIqO8%bZB$?{sp$NuRXsr{#hlv6Laj=P<4zSI52{-(n%58&)W-k0S89QtPZ z3@&E$Uj&v{T=c2?`-i(st5VDTrwW_s;|)}=TbRg7c$R6f6P<$_37Sl%DIxu zt3%uW<$gxskuLCntGS$;v9GZmok33>23>U+>FVJ@|G9yEnT37Xm#NYp^#&bv81Z!& z>FVLz?FUissQ!rd*2^1JPo$4|H`3K%y}UYo_4u+}pAU8X{_Ka*e(c>UtkWCu;oqpo z*YP_2fk(Q)14jGq8<4C0Yu}X-}{lTE49>0D0?LHmPCVEObL!JUg`9SRdGx|$A$OrzNj9(c6 z>vU0Wp;_Sm-0#tjsBcuf*Y|M`tCjpZ)Q`X8-NW6?H~0rRUqQR)WXW;vf_-l0$#%XM z?f%+nesn(b?@B6fLit&dU)KFC)(KO^x3e7tUkj~HU(ECG3&OBJpUt@YZ9@JA&(HQd zOz0e*@kes{%Nf6%ao?NiWPj6yEC;8zzh^@IYux=jq4~qO`*A|+Xyab*Plyl3EpHR* z590x$n{zBL%8tS>|84!nuC(0He)cIl{dPvXqMeVW^beeY~S9uk)q?TU6HPw+YDk9I{neXqms1nkt`w9D)9VRDAucE9X&{1yS1`sdzX zm1l@`!>fs&@;rQrW7nS967Tc=p zl<$4bN}k3=(Fs9*;B{#JeYO%#xu5Oli~IpeI5VW&h&TT}oAkoE>|XF0LF{U9;7s

eAYX&8edJNzbHt5Hs!a&&w5o7$oiH0 zR^FGnnBivP=X@TQ$CZQgu**tsy32dN5H@VQ?RPl$BrX-7O5@>DmgmdD;pYY6@aaMM zhdB@TfFSA5=llBSbrPF-9;1wqbhp2_&6D8pOoa2Z$c$o4|5U&efN;)WV~KaaxpKwA^d*fKF_?QRLDyS$#+&bew#^^VtMxY z!;V5MAA$cmwX;+FEcM5@_)UB_`xV9&4j$)p2&ep6rKA74`8-rt^H_vCy~}M6l*|5w z=R3~PKsV*XmrmAGZS5P7{{i(c{%P**fUjwOVjQds@;fEDC;yt{1i8PU^Fz0!c>CLz z<@qKb?}9MSJ?{+KD|?L(JBNJOJ-f+2Cpw+acG-I|4&l$Rdf@X_*}2Hm6+!UJ$?kPH z?OBy18BV2h*R3S2F>k}iuuq3U*UR{BNV7KuzbAP`55FaQ1$q6B>@o1SHJ(}tUeVYi z-4*2+(`oN(DgH--$kSWW6Ugt^<#(an@5_$^{+i^D`RZ-S4gLFF%|poJPZW-x`APA4 zwFA02Plg7Dm3^LdqRBF1T+nCutuAk&_k4 zFMjS-LB{j4q>XzC3xdd9OWKFek9{{TP0u*#NKaa>uGZkSd{1sXwQpH9;g^{a7=G7wlVOcrp7w!izU#w`Sf_AJ4C!YG~=Xtd%AmxCQZb8%l z=fqjlcW27i==Y!bFt6Fk@^W`h@BH(^2%l9h^vR}rWv^-@3WoK(3{{zp~Mx=Q-EU`Oy{f~(?;@s-3! zs6t-nR3H5@mE@UsBJN7_>OzHX?E9u_3I6Q_%~#7k<$)vjw2RkG(CaZSs_~>lerQiq z909_gI*$A-??pT3CAo$(Ney9a1lP0J&@m20oVh?UQNTFBhLwEOyW{segcsn~uVPYl62W z{Fww-()~I23;8Zflks~%|IzMcaTmTWsPzGfZ)@emZ!OvH#?i-}oPSh!+-0+!*d z^C{nuaP%DS?gb>CaOAivc|pHz2-1#e$tNqI6G@s`mpqs9ZRB})Rebn};ybcG`4P{L zbv<(1RXo4-e*9Ho*ar?T(|;wU+lmjC!+RF|B3;0Gc;IUlIwK!)N<8|#TcZ<0(d!|OPDw556i5|11)K4YG2Y19$UI}FG(;ryt77!3d7 zZ!%TvuI+<`q!(9} z-uV_3OaD0B=aiB6)szo=f_#2&Y=6qxN(bdaKmPN@T%YS-snB7&#^F`|kPm!ox$tH8 z?L%IpKepo|^$p4?`Rq61{UhcXAbjn}6J~fJhu@#^a~awXzcqd21JXXyS0Vem;Ahi3 z8|(Mw{yP3b8h_lY^*BWGpl3_?G9+V7es%IU+ST~tY~I5L)9U6Ra0_O zhhqvqqIG>h^w?3IEUiM`?*Rv*pIXwhb*SJPzl}4FSO)||o?GG*bT9N_C&_tBEoUCT zU-J~_c$n|74_3v$fYh_7dDu8|Py7nO3XgO6<~#gweDqWFW55TM3w|4(kbFSriR9-3 zhP<3k?Om5<$F4zdVsG(oH1pendJ6k8{ERKpi+nY-0>PeW3-W^=?-M+c+IL>(VzGG6 zX`bdj^Tq6Mo0UDoeBBV_HzPVeDmX7lKj6o&!tl$p|3!PIvOEK!V>-+ElwjCpjMJs0 zH?Rk}w*Zc%&Ah&r_1}UZ_+qk8R+OIkfqM?vD{*fE|1I{&syN~CO?voxHbKsZlkcQL z$k!8pv8zsr9^>m7o=fRZieKbEognEQzae`9`8lO>sE6~`*a;ir9P)HpDS>AMk@Nc$ zkHztn*6rATkH~+3oUe#3>_5^`9+2|dDuM6|D$)3QhW7uT7d`YlFyiSK>>To)6J6k( z0|r7D>F-PQ1T+>egc8!9 zgc~jk(hq^JO0ubUMKJ0K=<&XgS_| zd2Y`zdWO|k&0pn1N&jGbx1+aC&oF#z`}Rg}HGf@R>bTgG<^#gdN`La>J{WqTqjev8 zoAsgX!_TDnmiAk$-`Z)Od_en9_~9=L!v81-{muM&Ugt49-#o6q@chDjbD!oJ^w(jf z8(iP|UPZZHzM%d`4_!&~O;ZrRQM$mBKm4Lj0!mF#5lzaP(DAA=cCPr|>hfhoEakdXRAZq|C!{4`@R2O}=}i zQE4ysDgJ|aKOac>hcu4Z4_Oi)`P@e#d_g4xS5!0f;Rp2mj)#r%jwb#rE0lR`QqmU?z7bA(<`42?C*W^g z$@Ne#_Qs4#qr4@>(ogt@po8?p$2+OZ;x7IZtSaVHyvE2kvkvL#4fwLIn(6nZVwu-x z#2@H7rgXFuFEshMrAhdVAoVxJZ|l!al02+V=sqHdymI@7`h5$B_G5QJ=b4lazQOl( zNhEx`COL%f*n#H%*?xMbJD$_~d{SGME%aYedA2*R)u6`_;~%=H4|zMMaY1=MDE?>F zTWB}!KN2t4@9(AX*ODCsJ=atHE!oY`&38Z*RwSaQ3 zDSk!iz9hR7{$9=HoXF`;iW2(eyuvAuamcv&SF{s|94sgXJ@fm;zb_uKPiTKkOQH5_ zEhhfcf5_)t4(Hqi{nA#e*eA5tp8da;1o`d?oowIZuZi zcO+Syka=A582sYi2at1jz(zi2($#)1{92d%o8N0m&ir)-^p4M0AUE*o+r@vVzlEs& zaUaq@-zxq|lC$p?KkGx%f3x^^^_TJVP9Gxg-xg<>7r$NnK_5mx)4r}&2DBfm1HDu3 zTi%_ZAqko^&`5?ycMWsV- zKdp4ELqDx_*b~nw9rE@`rGpR4d3`jeRPd4Y1LKSR6X-giR9&-AJViWLB5?m{OKE`~~&G&q|k`v;)#eb6F z|B&JTnBjk!;lGvOVb$RB{w%}45=77auLbb5Sc71PT_&+7O z%ljm+XL^4(!-o@GReq1Vms7ruG_~pYCmEW~PVvub;qOTDX#el!aJREl{PT?eiwysd z3>kmO2mQggKnmf{_XP2W{|m_`cHEaTJujwsc>@xi@&jbLk0;nFkSOZ6J=#&fo9=%= zaSH#(8UND^sb@^Gcs9}9RZqKqx3^pTRmT784F7`!m!yZ`Z+Rc+zt8bL_w+lGCgux$ zZ7b6k@c&A37~g^D6n{(kpey^z$zy!4rz5@Mb~wIl*AG&EcGK)feSeVLkj#CMLk>viz#lM;O{tNX#@=%4#!rzoWN00na^Hm)( zo}N?vjIZZ4!_*<;{84FA=y+N)NgZBL_+uKWj|je${3_`6jl@^{+x)ifm&AS#^Fz5W z6Z&aXJC{o)_dtI^^Q8<_+sjL(0P+ zidPi7huvfP4?i32!dpzak4x^suM0ANt*NJWf=ZV8XGpnteFov-j32GR2>1B`ya~<+ zm4(W2zYg}(V13ZLl&k~%c_)=)Ota9*G@r~VoOz<9-XYzraN5cF5ZB}LH27V?mnG#< zp4S71lD=Uz4IbxBm>poe|% zLFn@j?3*)Bu}e<;lpyuasT|rhBgk(Uf0&0R)L!^Y326%>8>O{vuYpm zbVT9E6Z0?fvZ4^^N1hmuZE*;CkZ<^fb-{cH4&ONcg1k2bNyj(=XZ=fkd<}|vINd`2 zV@idbjtjC5;p-QqV|SkT!-CK|uKKBm`2d`APop66KdE`R4jH%DP4JOZ5xd1r|G3gK z9`Qf3PVR|AjE~hmq#eA^0l(O%7>2VO3tzCyi05>Nq0edFNW91yDhH#&u|sU&-x2nJB zKfd|^owJe@`k(L20Qs&A{WL3_aXl+f2lM-Uh7CdFgmC1fsa`@JW(19QGTu?DLcF>) zl{=?g@DFbv?QKZvp$G2+?Y&p+L0{Z02tFms12V7RPr$!{U-6>}Hr0>FS5tq{NBF-A z<(^lGg>rix8}|d2B=M9VdTUX3Ao{Bc(YNgXBbU4a6!S&QALz+tt*{u6EkWdHMJv02 zQ9k{{yBt8?2_ZjzXyj%=RvY7eQIK+$G;@-FSuo@$(j#BYAJ8!&>J9J6^)Z8O7k+Ds zN57apdH}kQq#5zJm({!>!={*vg__~cdujxzWJPJ)I%(MV*X^_L<*qykffaQ*wrKd zxFF^78dN~&=hP_cb9PzD&#Q6JhZh05?#cMLAa=lv!f6kyX!0*B9RA)e80n!K?*w)Y zQtI~F-;13Q!d_P(oHCq_8iXbIGpnnPI=5C&`Unbxl@pOj|d{a(<&c6 z;q~Tsd+Ify?MV2|xVk^tYuLA*@6ZGM7A4ojcVq$6e-~x%pAmdn5WX^B!@s6#W4n$Z&nZvFy94)Kg?F}c}h}YXuS>Yc%K`^3N=3iKb+`=X7nNc zD9VLu(ygWXp!0}IVxO89(+jPG<}`ERk369s8WVTXr?mH|V$o->_96O=^;kgk+;ZCI zF@$gP%D2-xB;w)Qiu`^7so&=!4dL@rK8LxK&TF_o;R~nFX)Atz`ejn`Y5jFq;xnrW z+Kn9xg!{-n`W!m(Rv|B}x{&81N=JFD8=!+ztJK4MK>V!!QVyqR$&Wn<#2?7`W&L7$ z{5_K;<)eH~jX)RYOreu`1^$v7Ib}7+uPUd3TVBtmanHF5!J+)AopRnk$zD);VnVt`4y$$S8YG>j1TH#{bxS-+#>vf&)fs4 z=c6Aue-0h2n}DnnY1h0U<!Jei)j2pryGn`jAa$ClKI>#H%DZZuni?R>Me@XT@_CZe`;xVP%kpGvT`^Eil zJ$P85=skA)pa*(^*3W#;-T0#TguX@_VIM6fdeB?&k$KbojE1?zu#Nf#W@=U)aTmK2~e!JN!DN<7>yH!{3AE4ttM$#FHNVgjd`1y4T0-ItETU z)*r}aBlrK6tjEp@Qg3|UVp5!fZ>+MAH&)AxCstpy6E6nwXd|b)O*2_nB@}5K@x3V9 zTP2Km`e8Nizt>@WN23ZEe~btC82Sxs^dpI0_Mhn|I1L}IH`tx?xVk$*_El*=`jGwu zr#wyv1tgsNBeXa8MyZqoME|hg%XnlLAG(gI9PA8Exu6%0iLa#N>tpZ>I{^7)|A6?* z$<8Oe?=`S~z=B}?W9WM?oQlB`m`MCBQfr{UrB*PGvHt>EUz=`5HS{->b4>rOsC@dn zC5{J#?saKA=tG}T4&xWP**7HrLf4_(8wJUHImEGQ{7J_`|Lb_EJkQ z_#TjYu}k1bOOSezWBLJWn)a|uL%6+_oX2}PrSHiTO8iE~&k7<>W&A5C{yzElDL>-5 zuSP#CizB4R9;?Dtd4?%>F|Pypu2~(24`+3c6nzN{961N4edqMDDe!EDfnO6&edTwS zeLfMr2>*c|FOMd?DLSaXBRYt$;_#jG&Dcq6Y7zAz7yQsW)&7`LZ`$tg{jh2E4s_%1 zVxF8+DfFvPPtkwKBXB9fMfD;&Fl0Wv;%!hp=Dj1TU;@PnQz41IfZ zAbh!|>gsSS9d)|E883Wq7(19-AHWHXRQT6cIB;Fz$YopM)piXlZ)-f`@rQ}NZt?$S z`2S@1->5|PQ{GPFs=G}(+IPFs;kW3D9)4X>W_UHhW7#jWs+N$xD~b)TX>5`HGikha zv=487{l^*pI|)t{|5}Dy;~A$N#rFhXkSD1v_^ke;|1ZgoWBz|w5c#?&IYkff3NUi@ zv@q}ss-54gQj8_&eJpUs>6tV?4DUC_1?PGxZ%M6TK0=Q&4#uUI2}h4xf5dlPHgv{z*@ip%3tXLLd9krn9M3&|5+*prfieAoI&n zX(}N1gMrf-HnoxkU&;M5F1qNafRjmDId{2|!rP*Yc;06P#(k>eqKk2}nJf-N#@Sin z0cq!?=mJiu9N?T%U`YYdY(V_ubD|4A%!w}aCHC|nY$z7L+R{+i%Hf%OE1)|4K7z&k$<-|WLkPo81*jtcjD=#z2yPZ`I2zm0J+uYNG@@4d~7Q%=vx z6zrT;&1lefD(Rtl$s_%R|DOD_g2Xp8?to1}@&(?K{|9}vtdZ*adPy&~grm2(-$49& z8pq7P{=Vm?%3-`x9&)v$bj-K&f^{5vnxcd9W>gY(BlldYup_D9*D3z*4FC5G|F;BJ za$mq#%6mkTF$$8w^C}QMJ&3O;|D&Q9d3r>D4cW;9Kb_kDu;@kaKCDpWk#zh9zcT^u zHN-IJ`OfG6cn{w=-;qM^Y=xA6OnQUggyf4K`i);R^)+q97pS`>XOtqxoJ_IOkTrB2Sy~M^!3vaXF12?x%WQW?o^w_589IyNP*aS}pgy z(3CvWZ)bD)FC?7%vV_m8SNI{n{E&-v<$qo?Hsj+l$p_=Qr*oIAL$63vpuaa2kK8<} zc=W|{if7(?Nb!{0)%gCn@RNBwa8HK*;yVQ7V?T=VctPRF`w9Jv`YwwO=Do{lpOyK< z`xe}HV*Wd+aO!6r4_znpAAD*H2X1DZeF@TWF4TPNC3svoeu?;Q1m9nD{0}8Fl=J0e zM;uH31nPM^&EHRJ#)h9)B{#_PGinF+#ud>AzppADctw!$e_8RQzb2e=pAlqb1|?rTqpE%PM&htg~QjL`VBsw)qi7xw1@O()DQ6U0qIxf#q(*N z#II_7SH1W2G36T;uHe-F=$BX*aL)$*OeDL9{_yu=@Uzh$tZQfwkp3>um)O68JVEc8 za6TPhJf&ADvgb4tf=J-?IAeP(`(TKDn0OZ318 z=!SlOA3NqP%70U#gOGao?sh=<4UWF=rE{k6`BTDN4t8VQ@9*Vz`4-jR&hLHOhGuep zrn7|YM7PUf+&wN091uNpzxZo-J;TS-e6lDCOtH1@Wvr z;g9FruJ{T6*K&D_$`7BGl*0XDId7_V*N1 zo6cKN?`QkvA%B$1FXU}dui}63x@ju+JM#i)}hrQ{co@}2Gr2`=UFiys^Q zO^agWbwTCxo72AtpHTTg&W-b<9FK4GEO;62bYDpNiu-tsBmC{G1HI0{uJ`xgxpjg3 z&Sbtdgn4}WoK2i3^F9ybFV=C)3h?dUNaMO8O^JRSm!0eIKS*#rpEo=%NRFoDiggb1 ziN0LZi~#+sX`Z2f;Wl>a?J4~BWK9*4S!M$Esce{mlSesr@wTg~v^tk1gYM}9L2(m&57 zec9DHS(pEEf`l_~ccgK1doz3`^~-AUmy$=u{a>c=h1_pT>HIC_em{k`L?`3yyyEdA z0HN@_zFW-9f4+Drjg(h3gYv^(W*)sD%}EdPejs|Pm*Il)fge#mAn)mpif3MUESKwX z>Ty6lqj2~8==f3jb{oFe@_*aX@0O1T>1>bfmJi4g^K4i4n#Z&E>y|Y_nD2YCZ27S+ z;MWz7e(t9A3icG|-Uz=h$3K!F@6@8Gy0S$0VgF&bb+aAlbMf}`Fi&s`jvxMI|JDqtSxjUqJAO0`*=lwrR^E!GSeSK1p^#tc)kU!Q@ zl)s``f&Kuazwpu_&nt!Rlj0vmUbw}9y@Xv#IrvcnLVrU&OnDta?1*_$j$OvS0CLCa zW9CureYrlLt6o(7l*_IG62UGT>DWggUsDjfi2WOOOxluX{GOceB_DPh^kGk7=lWGP z{E_x|98)^zTvj@_%j*`-+d~KS+g`FCoc$u~BKCQym-BO^JEf9=e9sQ~h98li_8(S0 z=Itrvqdz8;k8r*#Ncp@QNWMkoV;r<9^)fGWj(MX;zyUKU00YeN4DdZ8aaQ!n?$f}UuX z`4sOO`92x#P78iX5S;zYfD6JuBThrdDg8%1yf22{W1j|k*c}017PQ=O z63B849kZesIam>-A2|Pl<#|F9N_jI%K|J;s?czKLa*H1ux#QhJaLzLr_kK91g{fyo zf632o7j$rbne?2Wfgj7tNq@7xXZ?fRyPnv0W*l*@%I~Uh-n-ef z^grdWYGIsldV4sG_b$z!sm!Mm9#RXfXX+6D_PlC^Z?V4NegOT)D~a%Anz$LSY(hxHWi*dPy0)$8%* z^Vut@2MC91=JPoB?DMFcUWcFDkL3q_^fUXMw4)>b(_g&4&5!vD`2n(S8#bR|-!o2G zAG035Q}B7M2%rh-1)Noh9uJfYpL+VsJ@uaoFUya5_6lFfeyg4G zy^!xYzmV5`#_3V!_Nr}$}!_(nB5xT0qgP- z;Q)F4{eCBdq%kcFCx04Un zAMD#PvrG%3|CbfQywu8YQRC0^o6qxengzYeIX(33lH$>0Yl5E7%JhV@YmXjl38E)i zUr-P8DfS}Y#bI3Y>If}ZmluQjIN!;9i`7XvoCkJ!aj(ty)X=Z!f93_sV_rp1wgsW5 zsc`f<=RL6lINw#rm0anb1gQ3x^!LX4y{r6BY3Bz&)lNsbem4*nv2tdH>9Llun>laJyvBYvcFYC& z2^R&g>b|wd<8J%GfI>C%tonzvBy%fMNI-6-xXUb9^2DLXIDme{}qf^56V; zA=SSodv>ol_5$Z``OOLL6=z;-r4<6_q#(hT+{p<5H=;J51;OO63jW_Zg)qL*s7c&0TE^*cerguZwn8|dffE_nNP;0hbix9o_CH)4@393^jBRTn4ea~dD{hp_7Uee_*Ef#bvCcx zW@JzDt3&J&>RVUL;983_3dl1j?G5o5w8RwkyFy`2C^SOy6qqm@khm#w37ftl71w9({s-W=U1Lz+sL^2=l9l#k8s-$Gs?+$ zz;3hsX1_w+-s2rp^whZ0x!>(q8W$h2BX}R1{Kq6I{CEeI-(~T0H;{Mz_-zldSG>O3 z2`-7B?%&an^~H7ZE#SEDhs4iwf{$f>*7*RA=2#LZJfGvqz^=#If)Cn}7UbQ2M>c%*r(_rZt{D4Fh8)bgWUo|&+rNW{j~M_=E$AHJ>E_U0}psQytfnY6Yy#@?Pqs^^#allj&(vl)*(Rld7+p6HZ%;YT=>iW0rc`tA?pw98^_1} zk6TiH{Mz&bx0@&roOFE8ihkgJG3{f&qs}kZshs=b{uTE?40-v5{V}vA^C+_=5PvW0 zm{x+zSwGb0W^m>U{O8OggYni2^k-N6<~JDs!N#LK0j*~&ujo5|)p3s8^IbROg?$6$ ziuYv+pVNQz@0xJRn-hd@ypBxzRfQm*_?@ix(9UDRN}P1;t^koc$^+Kv7==^sa?)aq zFTblYDLFyjd6)G@5{}8!lUmCw6kRR`Q8MYNmxc3#q@5;V4zbfBv2Hr{Y z+V0yE?QW*=QmEHjB8&c8=)*hoFZLfxNc+(ohNQF`40H;VUqTapldl!IRN zJ9v>Fdk*`AaNhf+oI{c<=XZIm23gmkE%u*}!H3Z>=-+w&%89rf08}45{E`Ptq z(f-l3Kkclxi}|+tojv>W{a!^iyq<2aIQ?`p`2amwIlw86-%*hB@N0?<$b90~^6tT* zG{11~o$xBeo>)a1Fyq+y1?t{0w2~M=-|D8Dnz@14};8yyq_=% zvY*zHHf6rydpwjw{~HEghoj@sN4(RFMr{h-DagC~_$zpanO{{7cfzq3IOh?to*ui8 zuQ9w^2)#@4!?FTv3L3wj@J9CAKriEieZ*0)PS;kPb8V{%wmpMB=DQiJ7f%Xezi=zS z^Agr4^O5c0mLT4EbHp=h^qd=b#sRXD#Qie~)l8`@u$i z?{HIo1?;-`PU5io;V`WQ*OjMZCun*uq<-g}^-;J8*pu9T(9*+K#yNa!)%eQ2k>D%*gP;3F zzwLSNhw_dD_DtC6eDwsnnhL?*X8+WB%I}S_AIiAktI+6EzVqw!-seP$LN5fwf4w1j zVV%HvzF&!eq~qOqh8(@;)D?A zY3Ai7JTOW zAB_KX*^B6mF@@1ee)s_HBDC-?#3 z=ZYxVDV}uLy-sg?m-i_{j|FtSulCFTxcZfamoiD2BFXUhQndC9N(9Sa@YIy4lCF7nUCkwJkeD>bsWAgYX1+&JGMaH8FPQy zf3T?jC443KJLiZa9px?OeB2jtxc#Sm|2%NylJC$m4{$!2`Qn5$5b|_h|DwM5&ekyb zfq&bDANV8bZ~GB<1JMtp3&=RIA2Q(f{H8yUCqv{C`SE^YygO5kw>`P3aZkP_rAFVd zo5noUOE~nPuQ<2EZ@d16?wPy~Kb;`w8lP5s@cTWjzs$eh#|EOO4ol-8XGa7b|9l^g z>0jIBSM;{@yqox|X@9+t^$#7xq5W@HH1g2Lzqye1mHW)zH*4qpGs4ji$91xB6eRzN zY?qFL+xbgaCvlE@CrCZx;`4_A-Uz?Z&u7H%Q4s!Y$8YwluomnO@w>g;(m@Y!UyyOm zyuo}B>vY!f%m>UPhMWWjAMK~NUTKLM^x4*ZJ;EpQc;`Ha)3LuhIG(Z8Sy$AAf zIBR76V|yDtfINGji*q+fD*cYX3i)i&EaA3`!oS#3h|kM))B^m`zJI}fqW2MfUWczp zz(@8uZKtqf03VR@F>wR^M1APrLeyK|l>3ey#S-uRTKuw{G>iFVT=X+S`06k9@a_!f z9MBV`{;!T#w~JSZgP*RSaNpwtqA%eK_h795ea?+>iJY*H&X4}5{oE?0oMQ=cpUv%{ z9Mi$~+3|~eUjctDztAJRcS(M}pT+M~8h^awwUO`@wQEfOiu8W3)O6xsrGCoi-64MH zMdWi;Qe}P5Ny@6;bUS#J%IVQBvZ1ga8(~{D`cmC%` z{~*`gcZSam^|R%wsaSCPf4HB+_=$IV!%sb{7Qtt}S75nDj%?Qlq^5T9rwT>yJ=urU zGbah7{rLaTtH=ZXI{5DXh;t`V?}f@cRp7`)K=cXpL2uN%nCONtes>W&2fekVet;j?OYn#H8t8w{sWPHyANm=& z^f>Uj4sg;l-q9DFf91E8{;p!JKWTSM6gVB@kbZSLxp$A=JeeB&eue4-w{*9G)>3&uJ3TG5}r|9nUgI+m0I{=~VAs{AWh{IFkoM(yNx zB#%$(4f(OY!VW3VnNFxC>yy!TB2Gn+k_-_??jh-gO{fX+NWZnRjfD za83rCd-Wf*^voCBf`&hF&ye|=ANSY!(Lcm<%7=M$O%Q(Z{icAFgWS_j=2!GYOY@5D zfOrSvNYeMKk|6lcI~3-p_e-kzsh4|(qagKh8kKR2{)YeT3J*ft)6nnzR&e$yeU35C zGj4@=OVG>Se>$4R;h>x{FSR9cK;D<5J)6l+!GG>??DHPT)4F0A-)jjnKGBCgLGF$9 zBvFiSk5}KbX57xIoXDqWrS^Mh$Q|{a5{Kb4dWm*3Kh@Jy&YJSko^sy^z2JJ_3-r>D zyz`9SIjtOwlT(7wc}A2`-w8=D51uEw%I|p?e!`!v;g0t{3F)1V7a!1rd~plteHtM3a88nN&M$!T zj$J_L_WK?1zb(xA6+5t-`Gj2v{m=`X*1wR?8+N7+=rawUv_;92~MhJ z)9v>MkWaU}q~CU*-+gIGGOPL9;xqVimfLy#i~K$(=W#%ML;Oc>xnByzeng&`?_Hkl z&P}xhd5iYc%b6cij>nVxFV3eipGP}fzrR=E@6|Ei`d&2mIXo^s-mCQ?XDb>J^!t5E zA9ORWf^P0l^V`sWA5GAF^AGo9()WbXN%){s*mt%S1eh9xHm8S}w^%n|?{WSeKJ#un zkn`T~i}Op+*Adszn@y#lKe>fZ{n-21$MHTGc1PqVJ@@d?tGruFIQ|K6W=qBg?T7!I z2Pd4=;Qkhl*8-+zM_&G zK9l5bQ7OQWNK&Y0Q6*81-$O&+`Fo7KGvIvwz7cY1+~KT4Xdmx-BDd?R3Aqk?da|G1 z;j9;!fB0@C<#28m`(sZ3qCN3l8QM#E!{$}Sdv!jgoz;1ae9S|HGcUzFML71aVaUJd z!@8bFKgakChj{!ZO^ZuCal->3#{eeB>erosojd4}&FZj)T82uS=zxy@jzrFTv*lY0V=J#_c z5AZ+ind&>d&@pTtQt&`O)6ST0=x_R+_eAKwru4M+r~PvHh0vqCvqre@A<-{>KdUXs z3Z<(aW!=j9j9*Xwf9tt(Y5cV$$;^ix?f)~rdB=rcdEe6hAnt=Y{PIx9EmZiE~V$hp6_65r2ee!;J>u9=4RqMuliqnD9OZgmjP z>ll>Byg)k6Zy`T(s*mx_tPh{?Loi=#D4cVac#+^U->V@%U$sPzS?^Ik>t*D-r+3=n z<5^K)xiVij6@G`pFDjgR&WPVmAMd}H>6srk(s>f6r@YfjPy6w+F#hnr5RN~}^OoO* zUsE{vs={}hKFU!x@we0`HwQ2E+RZ7yPXGH?AM&*-iAJyEpT`d5{eQ+O_7Cy+OXyGD z&tqN&C!TYI#N$W6e&T&L{6CzQ4Y{_x{o*IVtAJetM9yumO(=xltUR^YQPZNs zcF4~Xjy*y+-}hvk_&cAhE4lZKTwsqwhxZ}yD>H8K_ha8TWLXb1ZTw1e3Y zh~EvqEU6UiF#H^}+wXCb-hP0lD8WAEy(R1({6f^jt{ME~Jx?sIb)7FnuTBe6|7!7) zFviJCspZ(U#2*%990rbm#(rYQPY9>I%X&8(KJb1vmKT00G*q11L?1v0`*-}pKgstB zsFzz>z|v0jz5w>V`EP%U{j>N-ybr;9Li{Mt{cJy4SLMMs)@8PPd6$%aI;3#Yvx7wZ zv8>mc3a!$$C=5H;ew&ucaX*xF(GS?e{J0lNdcHyow7piPEBw@crwF>xkK79<{eY z{rfdPBG>GT($0p;p!abo}Sib}A4@cHM4_>H^+8Q+{^ zqdZ=Bpx-%f0M7YoaQug~ldp_ZF6WM!&lVL+Jo6s&81G5pw^;89fHh|9nDzu&pm=jc1!aYe4G$OqFSPm z_RT8=^s(w>Ty!+v$hVZ?IlaJ&yj&L~J+JLje|#5a7=-@&WCx6b$SJE`xOLyf3qw-|<;4UQXfb`VtZRi2aU12>;PTyMyRC_H*ix{P!w+7)*is z!+C0coZIGyy~Z!@6YK<2V*R<1a*#m&M?bKR^}NMCl5v654u^Xmi~Zv|zEe5%^rLX( z)?dqFABUG7cvslpQM!<-4G-!(#CFy6O==qKzG^hrxp41=8iA-oDz zjd1h_^(`m3lIWt}TM1`=L?3#6kNkW50V5o}hJ8Rg`Thwz3=Opm{lYn4ew2ef!8^V1 zjdyy<7w5G2+RQ=x0e`CU5%R@&Ixam1rIPb)~g_jk*%DR_%lT&PtzvBku2g_&uKUn@?{MPzM$A^8-e2G4(uz0 zoYhEV9Bv9i=UH(IyeG(SLNfUxg+q^G@k(ZT}$^M3t4~mZ#*tN&hJxN(ery&lEE+D zS^jJ~zwL9o)$oXaFqij)O62!Udi{#>K9%s#s6_0g7i6g+KQGFDWnOt+DVgUuufcrr zg5Fm{&#{U^Kl5rR^80Si%Q0WBsfL}PlI?2$;{Brwd-1h24|TGgG%1KZHhHQn%nL3W^sQFza{N$>Obmj2{KRdm230M=ho}=)X#++5vTZt9%Y}OALn=Y z9aYKvnrbOOR)_q!_sY-bd9d&7ZwiRL#*goV@LSP3kl%tdEI+=}!mm!xZt?eA<@WGBBYvDi;kT00vtH%LzBj+% zgU{O(x^-^*ZYIa`jsWfSdGxZJGr8P*Go6PsJel*w_cjmbdOw}vlqli%sSM-%a@+&E zl*^%B`1nNepC|j_#p1t6^U%C36!iP5{P)bS(`n@o^g6nc>gT)M*Hb-v)kk|t?|H@V z-Q&OXeD8OO@Tw3mCcl*8;rcv~&SFUY+F<}dcWkYDcy z4$i~K755z|_mudHT!1rwG!I~BVQeE=64@TI5@wS;TNc0?HP=(mv;-nz@2b|xH zlv~Zep)cp~>j{ntZdP!@_gYW9!^?g(^0SfO|Ldjuy!5;I6%hWpUc!-|a|!3&81P=U zi#Ad^@cUD_o2k5Nd<$|#{{uA`{j*dHk91``_8sqmGG5|c(3Uul+_$7BSXXz7*ZUCq zCW^OIF7V9+I~ng3Ur%^5*<;wR`02@iGQ$%&-^mKRdGm1eBjd#FINpcmJ9Z=VvCam5 zr4OC{)tvs-6n-qdE5iQ8SCT%N%I}>_X&(xHHROQ^Xnvl~eDyuddByVsI=+O22fcL& z{pRB%eF(jcw9YrAytYP4K-2L^rYG{fo%6q~+Q?Tz=ks^R`7XHgzukxUUpSu;ko*&p z%t3f9IHh|)laPB z%JIi|#P4N!9@o5Nf60p}96CsUvAB}*-3X*W%EOMhD8JKk2jloRY2RtL{dbUV zZ}PrZet8~W?T<(D-=^`x{Bf5?D8IuRiTuzD9}(QpI){Bw&f)Nz6a2U!?|Obru=Jz( z+!y;*{5U^DeBd!3#(AbHsZ zl8^H&PLI8Z9|JoHdVQXCKI2{4kLba!JT0X2J66zpJ*DqxMxmZqcR~;PH}2<;i*Njn z=kv2=I==@R>5k@f>|WF3#|5F2cR;!4#_3k<7tS{!8NKY^m{rIf!kHIHzo7Ork8KFv zn$j%@gFfGHsh-Pnd#))KJ^xIiyPLn86mqZ^J+FfHG%xHnJ@Rup?Z2_!znuDGT0O`< zE9b3Pf3lAU-~Db1{vPukh}?3nk@-L5b3faK-tXwUbnvlL{8fhkX@&@!D8vGOSJ=|(Iz*_3;Sb z2}Zu%LdqLY-@SFcJC#2yKJxDdBJcAWCD?Ji8;ZQo%f72a^y7lU>kz)Of6O?-FNJ=( zBny)9wjeu@a`-MDzx|Y({VFf)ILg~Ef75=p^G4{awkPb!{o>orD+jsvd{ds!183gG zPsF?yehtqz?S!M(`ECq$`a(+Y^(%U-nebTOMtbx#c0K1S__;p(O`yd`? zC0&I39A^oGZtH*A!G2vCALl*?+d;kWS3BIEz0pg%-b;FKTyJ2{Vb^dj8$HiCKIZ#& zk|*ZDvzq(B{T}WSrQ{p~_Xp|c!-8nWc0Q-g_x=JW{)lkfEsv#gSG2=q{PB!Gp72%8 z#H<4sWuYKx}(|%)?vh^nL!v2= zuD?a`lj3jEoZ`pDA7}VC2Jr6`f0D}kR`K_e9rBIh?<$`0*2(g|k;;kvM&8AwUcQq~ z`7`oB@oS4RJcpcLVq8wEJ=pPgt2Ktb44);ry#Ie8+m`cTf01zBL5B~ax2H4@pf7kY zpC9k1x%|t*I2Uy_*~NEjW`Yjtbv*uG&eKsp?|wWdjCIoaG~UrW#-GmkGYOv+zu+(X z4Cn>cV@|i6_+Rcnd_DQ0+G)KpRx|~f-y=Qi82W4H^roYk=wbf+r>VYP@t~9tf}{ zh`wAee9j+;{$7<_1{@Z@lH-9c-|xiDtLLDjA^FDt#J&jn|3OJC;e7Yl{QF#@uaoRp z(tjz%chWm~r2A~n|9pbnL!msN^*Ar_px6EVF7!L?E%m(LO`FYpz@Nzc5bxGiA@uV; z0Q#SMNlq8ccu(oUr#0?Jm2W!A@&n(m@^6%W=$_4d0n#2|lp8R@gFom`+FysHn-V`F zJn%Yy0>@s9bEqBlC+U}qR}&0;LE{#?qLKGy{oQi%eLjUR7vD|eq@#R{%eDM|>QeFj zRL(JlGY(dSJO1a%PF&OXj?w!w3a4MF4|@*W_5D2Mo6*ch`~^{fKjMl?V%~XLl%n@J z-$6MKX8Zw39DKX35r@8hTqBZpavzlX<2@{PJ;1qlMLOQi06r$ofSg}d2<684k*4y~ zj!O!Mo=Ym9{MVF%aYFsz*VO{|*KDqzm#d(I6VPLtMSPF#sjP>|O?}O*cOF+c@Zm|( z!8qmCDKOqGTgY^C3zhPokjAEb+KV3Ky${kqqMm>r+5_~xO#0V)ldsQ_j$41oLsS2a ziSjr{bzb>t$Nie|u$wqf1l$xP9k2|ifBi1ggQCawJpNGpEDH%fu9`?6_h)z?*K+7{ zSolY6PdNXnRIlHGApB^m7di308{P{gd^17p6PI@^L-v)RGt#$mIO{{`2)jJ!IG5Y| zOqQ<)#3}N-9NMv(@CoJQXMV6QF&(p+E`Nm<|7NtyeBk^J^e!kJzbNO3%A=m~oc~Bp zcdP~{6Ma7SMS9MS0j~+t9-r@G-_Z0d=JLjKId|vw+>`0&+#30>^y={P5TQqBX)2c7rkdU=P7`uSb~e3?r0`TQ8^XDc}M{9#Ua zxB7t}a(W|hFaD!JJ0spd^?cvZe9!o53EnOUj{fp@#?XiM`?8NgIO7C;1Z13Zj|4p8 z1LyrS>g79A{I;W){7p$5_6p~2Zc+Lp8X5QrkBP(h38xj#Z@Y3Lp7a-$4}E(k!xQ=! z_8;d0&^rq%kN%s=exl3jSH}CI{F3zF1#tiv`A$kQoqsc>dq_CxA69=-AMchKUP$?_ zss!jipX?*Xll=)7ayjP}gFIbGa)4f7e}Vfh_D@|(>wo8mk2B)9?KST|fTRDAGw29E z6#Fi;i}x(xW4uGaxTQVp?|fY03;Gv0_kL*CyyO|aw1nT0apZyZ6?E}6CCUXy4mdxE z-1FWE`R4U6({Zd{KJosZ+V1*I1?;MSN2POnd`^IOoRF&pNf2_yyg_^79f+1VNBXu} z2A$lW2j`wX>DL8GkNq2vbbOVH_VS*oanFmqO9bB1f99L#U%pPxe9pTJ=F@oUH||w} z^Zo+!G~Oih)#r=XR0DJ@iO(FeT0i=4)L0KBt7IPXvlOFu5YU>B|klCFwF?}8|U4{L(N zhu#6g7vA|GJl>^URZYk<=V;+qqz}DR)l+qPSeNq-IOAhVkaqB{SwQS0-l6ci=pM}l z;Jyz#El9uLEvk6WfZnf{Pe1T3e?a7ddo=;;>FYT8;(KpE^g4RS01*n&| zTO2v%eNOm%PI>@6$9-z_1mgwy;C?1J?((Q0wYj8NPvV`4& zUBrGI>HTu}W%-rqAAgq){f?c-d1Ie1jPHlEL@z(&n-%S4@#9Xx6_t0FAn%nd3Vu%I zQR5|zx2E7{RNj){^D1v#@UmKZn;@}p(62GDPlz2|$i8gpFMR%*@Eczbvi{+>p_zwY zd9D?z@Xv9sjd&pSq5sQty?(g&KYDq7i+449nNPjEU%r{=r_Jnd;$1p^TkA(-jcGOi zFQxJNrQ&D8#)=;$f8Cf?Ie(nS-H(fZkivgb{8@&7kjmRs3VzJr^w{(A^aT#w%vS%^tiPujnV;{^ zQl9JS<^C9Ee;~KK^UL_))fj#rhn_#Fk9ScFUr!R<%j?l@^4lY)Z>IUD*Z0@=l#coH zYSIs!Kjt@;`wPD!<>A-hR~`2U_`Br?{I*>_9PSi9STEzFl!Gh6z<2I{<~97uh8r0^ zm)6OA7mM|B>EC`$-Oc*L`;j3R_`jGx`3?m0XG`OWd69RlfoocM@oOi2fP79R`CCi( zt|{N|%E$X}J}*SL$B*B8q8yjEsu2Yp>!O=_xhF(>dEdwLgzs zc2V`c9`f-{Y#ow6zO!0|%=gRE(zK8EV&Cviit`WF!~Im|$0`gu7{}O$0pSy~Lg372 z+!GDRd>7w2I3>!!Sx-R^vrjVePHQ-={xe(d3Z!LPufgIDQ*yaq@4yz7R1aE^&|$Q5$OeeQr! z4*l9vKGN~s2CUh^^I^oi_WJ}65*R4A#cYMH>>APZ4cU7eSgD|pVB|g zc)*_Nilg`${7x6|*3z%&7x=Rx$)ue=CyM`&RS@@4u`*c)V)qk{mF;-=#a9$OUgop@ znNQ`l)Po+keE)^=&nC$GaK`;k)T-=U=w&nk8NZa*kp1X!>wAx@>Iv#wS2%LYxj-P_ zUGaGFcY#>9T8{nx=*1phfk*b;67(mzTXk|kgg>8#;&lv>3r;ym*uce z&3hcw|5g13Zf7Um{P?Zs6n1|uf_46L#otQpxGre@)XnfphOejo;CG-tRB@6@0#;RPY`9ocTV^<-C#0?_^lk=k#0iqnG#&tKpRlUr+g`)K3n7Z6v+4 z4}Kxfd=uT!?PdLhe4bH!z`dsT{N(S3`#pc;(sCI0$>JVatk;k?+Kc}XxOF{eKJa}6 z;^Vvp?-UznT~@B=y4h}Jf5h~ApQ60C>-A*$&avOa=e;7*dp`(xDV5K;E`F|`^`PtZ zcT@=H9;x{W}7Gf59Ik489fQQ@X9*2U%z?R7YQ2Iw}uB|NTB!p%p2cZq&Dqx9wY_WV=r z@91~8Gx`mAaKBh@44!X94)$_h5P9)E#2Y`S`CibAoW6T{J&)Aql@h1_%JKHWZx8eF zvLw^%o&{m0JaON{^2Pk*aP%zU#+jFxk1R)%IX&~5_48JJP5N@a+FHKLEz74q-g!1; zy$${>#SE=C=&YABDm~%D=owWn_Vt%lKkos2QxN;}6~U(9cQajOdFIOj{@(Iov-mx& zrSNP1UY3`t{9p&*zp>qLqjG}$RO@5>a}PQo_9*wE0}|fI?-LAz*s~qw9|g%jm(TZX z2U*{69(gB7dDFSP;gJ2_(q6*8q1;gre%uUsuj(c4V}oDK^LQPP`fYy~vbwiVAL)Re z7f0J?Zr^KKsnAaAzv+CRVOqGuPmxUVtpuk99d78a5LNT@k_Xk@Wn2C+lr+(ogArTcoGFN$o@h zzMAqstI+N3_#2JK&N!RwT;yTU9*p}mVSi)K@bi8M{Q0^t;8Pje-rgF1f3i?Ed7+zK&q4Vg9{mvZK#bGf;-k_>9zC+3^3OcW`>J)wda|K#)_HuNH*oet{T>YK#nT7#pE$6e z{c$i(gP*KJSU&?$!r>?M z#Xg_U$p#L@_lOCPeN(DX6u2S?j!9Kf3xX}^6PX&c))u6D15K!qny3wdoR-SE&{LJ@ea+5UJ2oy zkcM94F!Xol`3?&>uQ+nQl=lFEd~eE-S5L}u7VX_HxK}>xSG}XkiSM2TY>5+`Z*Sy# z7tMUGKH^7}^WKO1ec8R%>-OKZq|(k>4-KbkaR-?yK=V2g(ET-Y4Y)KbZHpFQs?=c;}z@{kFoNrtfKQm5b0T zA9i>-zoRw^e%RZ8(7q4HI%w4Rey_t1$U+e7<{iIi&}3&%LAj*WAPIr0*dOLWgrd zdobUP;32Ou59~L5x8=Xvd^^!kxjVhn74LKLZUNu_*zTRKo$8DDy|!ns>7$+x67;)g z{?21l>*T=ekoE71UVOu!uv#?JdYJDG622^)aN_xWklMH3^x(g1iBCYj_gBRUN59M~ z2D#*2dgJ~cz34>HkNB!y zCS*KaP5voh9glRTBfdufWZtVo-c=&p?bZD*w0xt*rl>83M3XEGdxM|k=D zg?c*!zp?g?lFJA$`7+$^8R_0FtlAy$sgA$fa_jlRue?)x>ha;AO>Wzuw<}KbH3uCUUs_>wI^d{e?og2J`V48raf0S&PS=pbPvaopH_GGeG$MpK-W_l0zir#(XJ1f^^(Q}UIVSRtz@Otv6T-Wz5 z@uytY{(L~}=Zktb6u;Oxy;B+Wa4zJy>ft@zQ>q7eRqsdg<1RfUl6@f4__v0i<-f9&18C3WS@_T+HTH&Cbi=M zeL=-^AD7L}dzNGQy-XlG6Uoap2Xo9oCH$i~o#R`2uhsbs z7sOBSg>-V|nd0Z-NujUszN3By{=PT}{AGskq;(lM>$7KyU)12QlHY>-tQ?5v{7ead zmGOYT)E60ue<#CV$-ictl^ppDf12Zes+A+*Kh|0g`27sOCwT^cGpGAn#$S~L6aK{v zpUmw$r!c~czPxjC=NWyscs#*RXXx*iJ)`d#{r~Nq-ELjQ700(}@1T{c<|H`A&hcmL zvv;CW8WlSNAw-&(nm9mJLmI`kNgGiW)oN*kB_hNP5V&@F%>@@oya5+TeSk_xeTTjQ z-1h%lA7`I)im^;0Bsa4y>#sE*Yt5{^X6@Pg%$o83T$O!O?pomQqkWurOZq5o_4D~| zb6WQeeVo?$f3)|s{%BuI&s&>lm&@6W{-V4Wwytxcxmx^l&@D0!xbKPBHRThrZ|KD2 zo9Oy{!&ZP%F73kky%T)ij{0wf{9A!qVHTty? zcD@nm{@w5)^G21@Cgi)SYDXtcPa3{perrc9V*G&E_m0|+zCi4t#TQI(J34=i^QCum z?ik}3#1Db!3*-W`9DZpq-}$!nu&cp(SwHz^{p2^R0_*CB>wPWse%bnkeqHZZtiS6T zaWhtab) z%KhSmk@-C8 zbaNZ625H`_2h+2><6WC4lAq<6 z|23MaCXHU<=XiGnGd*!MKWS#l-VX5peSCH_ujhKuaP+>`)qAFgZ$iF%HPr`UuXpTy zr>XAP`%F`PXzxMLZigQQ-KO)sr>WkL^bhR46n@V4{lGi+-b^{(GaI#MKcu1Ks?Rkk zxm;6$UA2d6BIw7ekAfafpO73c=X@Rwe&dlo9`#QIjlOc6-7iG`V;ax*6G!iBq093X z`zm%&?4Z~|83)A=6hBb>K=A{`4-`L8{6O&o#SaueQ2apg1H}&%KT!NY@dL#V6hBb> zK=A{`58S&SAg<(Pt#~|WJh2Y+lp;YW-&I^O>m_Hks*`n&AnVG1s@0vWf2?hlXR*&> zpNF?k;ukJyT`qA|FKJaX>vnsxFz{)uqE2LAmrGjJ&idU8TGh^eE)(|a{n59(+{dBV z&*AOoAU{^}kN)^LisJ&mwV-H?#I(a)r{n97{&(HocUfO|6!nw(*bh@r_Y0BpeXeWb zc~w_mSj0qK{Zkb5mx2GLIf^>|*lnB03D~hv9BwlI5XyS`~{#GmAglk%H zNnAa}vk-?dqc~qjqL9WEwdd(SvwanbtG#Y^c}{W7#1$_qu99-K;$T`i?DMlaMn5{$ zr^<)^I&f9X;3)qe(ZqFpW>4Z;@i5E_A68Tl_6>bf^f^iMcFZVeOtZ=3{d`aFMSf2+ zN%(e+KB;(Tu3u>79oOSR^w_CBH@dFA3e5KWOEmI7G3;nwL7a5EpVEzy@)x4~am{Ec z=k4rBf3(}{A@7x;cFkFI7yBeVVev+_?YF}|bleZ>Et#A}i{?TPL~nt5h{%AE=O zc*^G4wc>w>u&EWf&Ac1h`T4$C3llVz zKHtrZzQkQ6zbTVX{(0rHA0h1{pME5t zRv+*o9@OLXPYCr1-krp7?lq-h*n{g`d zIB&p_=Tz>WI{Z8t)7sc)tXxNNb(HVOZ-B?5{(f?L8mDf@pILeL7lZBj+R}USi}34J z_(k3~zc5@9`o5RPhQ@>2zu(99zP9=rZvSw)kFP%(|GK(qIA8r?h@Md-ANG98&Ywds z*NmR8-V8rL+&X^j4XrQce%-SA$HfPFLA2+;YvXgV`m5Q&gwnmeH)RRDzuXEr?^?Zy z>g{O9HQ{i(3w`wTF>U^j=QDnAKg50*&)<$`Eb_N9ds@=_@r0Fs#KuP>OU2JL8bLg- z8~uSZn(?rI$dZurW@hDS5*Y{Z8N^0Ww9sJkPjvn%C$ZO;iyY)8(jVj}65rhsS1!tN z;jOr)^~c4y<-1cOeI11CchE?02Vv@s@w}k^Qr=JTp?yTpa5 zBR`Vf5|d9N^%ul7G5IGt|CE#H@>1@k=>vH#zmZ(p0iF=&Md>Y3q|#k~AkQP_ONp+x zR-eq5&S)NV5Yo=Zp3euN-!FPx%W|nVXvB#}CCn5qa~X6I0$GPT8&r#A*1sMUzA5dgi+) z=Y%1rkt8mEzCinP{$TX^+qdaA?;HA?)1Qx{j>WT}=SP$dre4{PXI}c?Ide??^!%>v zpU^0WcF);U)A=P|>~daON_6|26?f-H{Q3z=N?a7pY%CG|BQNnG)kI$6kBy#^WYVXi fA1x|b>5YBIL#xO=F{Ibj@6km3#Pc>^^7j81+P Shrine of Isamu +LocalizedStatic 0x14B7 (LabelNumber=1070790) +1050 517 15 + +# Valor -> Shrine of Isamu +LocalizedStatic 0x14B8 (LabelNumber=1070790) +1050 518 15 + +# Valor -> Shrine of Isamu +LocalizedStatic 0x14B9 (LabelNumber=1070790) +1051 518 15 + +# Valor -> Shrine of Isamu +LocalizedStatic 0x14BA (LabelNumber=1070790) +1051 517 15 + +# bellows +Static 0x197A +767 1257 25 +767 1262 25 + +# bellows +Static 0x197D +767 1257 25 + +# forge +Static 0x197E (Light=Circle300) +768 1257 25 +768 1262 25 + +# forge +Static 0x1982 (Light=Circle300) +769 1257 25 +769 1262 25 + +# bellows +Static 0x1986 +638 1204 52 +638 1208 32 + +# forge +Static 0x198A (Light=Circle300) +638 1205 52 +638 1209 32 + +# forge +Static 0x198E (Light=Circle300) +764 1258 25 + +# forge +Static 0x1991 (Light=Circle300) +764 1258 25 + +# bellows +Static 0x1992 +638 1207 52 +638 1211 32 + +# forge +Static 0x1996 (Light=Circle300) +638 1206 52 +638 1210 32 + +# forge +Static 0x199D (Light=Circle300) +764 1257 25 + +# tower sculpture +# May the town of Zento prosper. Memorial tree planted by Dept and maha. +LocalizedStatic 0x241A (LabelNumber=1063344) +748 1231 25 \ No newline at end of file diff --git a/Data/Decoration/Tokuno/treasures of tokuno.cfg b/Data/Decoration/Tokuno/treasures of tokuno.cfg new file mode 100644 index 000000000..724aaea83 --- /dev/null +++ b/Data/Decoration/Tokuno/treasures of tokuno.cfg @@ -0,0 +1,14 @@ +# Ihara Soko +Spawner 0x1F13 (Spawn=IharaSoko; Count=1; HomeRange=0) +678 1277 47 + +#blockers around him +Blocker 0x21A4 +677 1277 47 +679 1277 47 +678 1276 47 +678 1278 47 +677 1276 47 +677 1278 47 +679 1276 47 +679 1278 47 \ No newline at end of file diff --git a/Data/Decoration/Trammel/_haven additions.cfg b/Data/Decoration/Trammel/_haven additions.cfg new file mode 100644 index 000000000..8f47a7f8b --- /dev/null +++ b/Data/Decoration/Trammel/_haven additions.cfg @@ -0,0 +1,999 @@ +# archery butte +ArcheryButte 0x100B +3744 2576 40 +3747 2576 40 + +# armoire +Armoire 0xA4F +3641 2464 0 +3624 2632 0 +3664 2497 0 + +# chair +BambooChair 0xB5A +3713 2649 20 +3665 2530 0 +3665 2532 0 +3673 2540 0 +3673 2538 0 +3673 2536 0 +3673 2534 0 + +# chair +BambooChair 0xB5C +3732 2633 40 +3666 2621 0 + +# chair +BambooChair 0xB5D +3737 2564 40 +3676 2534 0 +3676 2536 0 +3676 2538 0 +3676 2540 0 +3705 2588 20 + +# barred metal door +BarredMetalDoor 0x68D (Facing=SouthCCW) +3626 2621 0 + +# barred metal door +BarredMetalDoor 0x68F (Facing=NorthCW) +3626 2620 0 + +# barrel +FillableBarrel 0xE77 +3665 2648 5 +3664 2648 0 +3664 2648 5 +3664 2649 5 +3669 2663 3 +3669 2662 -2 +3668 2662 -2 +3668 2662 3 +3668 2663 -2 +3668 2663 3 +3648 2680 -2 +3648 2680 3 +3659 2666 -2 +3659 2665 -2 +3658 2665 -2 +3661 2673 -2 +3661 2673 3 +3647 2662 -2 +3647 2662 3 +3646 2662 -2 +3646 2662 3 +3646 2663 -2 +3646 2663 3 +3647 2679 -2 +3647 2679 3 +3647 2680 -2 +3647 2680 3 +3665 2648 0 +3664 2649 0 +3669 2663 -2 +3669 2662 3 +3644 2649 2 +3645 2649 -2 +3652 2654 5 +3651 2654 5 +3652 2654 0 +3652 2655 0 +3645 2650 2 +3644 2649 -3 +3645 2650 -3 +3651 2654 0 +3652 2655 5 +3651 2655 5 +3651 2655 0 +3645 2649 3 + +# bottle of wine +BeverageBottle 0x9C7 (Content=Wine) +3666 2651 6 +3672 2653 6 +3676 2648 6 +3668 2651 6 + +# board%s +Board 0x1BD7 +3640 2507 0 + +# bulletin board +BulletinBoard 0x1E5E +3673 2624 0 + +# candelabra +CandelabraStand 0xB26 +3638 2550 0 +3670 2528 0 +3656 2528 0 +3677 2542 0 +3670 2608 0 +3672 2622 0 + +# candle +CandleLarge 0xB1A +3669 2651 6 +3704 2653 26 +3714 2648 26 +3733 2632 46 +3603 2504 6 +3608 2499 6 +3629 2632 6 +3652 2475 6 +3654 2464 6 +3669 2496 6 +3704 2587 26 +3736 2563 46 + +# skull with candle +CandleSkull 0x1854 +3628 2545 6 +3634 2531 6 + +# game board +CheckerBoard 0xFA6 +3672 2649 4 + +# cleaver +Cleaver 0xEC2 +3704 2649 26 + +# clock +Clock 0x104C +3659 2504 3 + +# clock frame%s% +ClockFrame 0x104D +3662 2507 2 + +# wooden door +DarkWoodDoor 0x6A9 (Facing=WestCCW) +3676 2591 0 + +# wooden door +DarkWoodDoor 0x6AF (Facing=NorthCCW) +3607 2507 0 +3639 2499 0 + +# chest of drawers +Drawer 0xA2C +3626 2632 0 +3666 2496 0 +3666 2613 0 +3666 2608 0 +3674 2613 0 +3675 2618 0 +3675 2608 0 + +# chest of drawers +Drawer 0xA34 +3640 2466 0 + +# drum +Drums 0xE9C +3674 2534 6 +3675 2540 6 + +# armoire +FancyArmoire 0xA4D +3608 2496 0 + +# bookcase +LibraryBookcase 0xA97 +3586 2568 20 +3622 2472 0 +3600 2568 20 +3601 2568 20 +3634 2632 0 +3637 2632 0 +3616 2472 0 +3621 2475 0 +3619 2475 0 +3620 2475 0 +3618 2475 0 + +# bookcase +LibraryBookcase 0xA98 +3590 2568 20 +3633 2632 0 +3636 2632 0 + +# bookcase +LibraryBookcase 0xA99 +3584 2569 20 +3584 2573 20 +3648 2471 0 +3648 2472 0 +3648 2475 0 +3648 2476 0 +3632 2549 0 + +# gears +Gears 0x1053 +3665 2504 6 +3658 2504 6 + +# standing harp +Harp 0xEB1 +3663 2528 0 +3664 2528 0 +3665 2528 0 +3673 2528 0 +3676 2528 0 + +# iron gate +IronGate 0x82C (Facing=SouthCW) +3423 2651 47 + +# iron gate +IronGate 0x82E (Facing=NorthCCW) +3423 2650 47 + +# lamp post +LampPost1 0xB20 (Light=Circle300) +3613 2582 0 +3614 2624 0 + +# crate +FillableLargeCrate 0xE3C +3674 2587 3 +3673 2587 9 +3673 2587 6 +3673 2587 3 +3671 2595 6 +3672 2595 6 +3671 2595 3 +3624 2537 6 +3624 2537 3 +3624 2538 3 +3671 2596 3 +3672 2595 3 +3672 2596 3 + +# lute +Lute 0xEB3 +3674 2538 6 +3675 2534 6 + +# crate +MediumCrate 0xE3E +3673 2588 3 +3673 2588 0 + +# crate +MediumCrate 0xE3F +3624 2536 3 +3675 2587 6 +3675 2587 3 + +# metal door +MetalDoor 0x675 (Facing=WestCW) +3599 2575 20 +3592 2590 20 + +# metal door +MetalDoor 0x677 (Facing=EastCCW) +3593 2590 20 + +# metal door +MetalDoor 0x67D (Facing=SouthCW) +3586 2588 0 +3594 2581 20 +3597 2594 20 +3612 2588 0 + +# metal door +MetalDoor 0x67F (Facing=NorthCCW) +3586 2587 0 +3594 2580 20 +3597 2593 20 +3612 2587 0 + +# metal chest +FillableMetalGoldenChest 0xE40 +3688 2506 0 +3688 2505 0 + +# pickpocket dip +PickpocketDipSouthAddon 0x1EC0 +3698 2507 0 +3695 2507 0 +3691 2509 0 +3689 2509 0 + +# pitcher of liquor +Pitcher 0x1F99 (Content=Liquor) +3664 2643 6 + +# flowerpot +PottedPlant1 0x11CB +3667 2620 6 + +# potted tree +PottedTree 0x11C8 +3600 2504 0 +3665 2496 0 + +# potted tree +PottedTree1 0x11C9 +3640 2465 0 + +# small fish +PrizedFish 0xDD6 +3669 2659 -2 + +# recall rune +RecallRune 0x1F14 (Hue=50;Description=a haunted graveyard west of here;Marked=true;Target=(3422, 2653, 48);TargetMap=Trammel) +3594 2581 7 + +# rope +Rope 0x14F8 +3660 2677 -2 +3632 2632 0 +3647 2669 -2 + +# rope +Rope 0x14FA +3669 2660 -2 + +# scissors +Scissors 0xF9E +3692 2478 6 + +# scissors +Scissors 0xF9F +3688 2484 6 + +# stone wall +SecretStoneDoor2 0x324 (Facing=WestCW) +3522 2993 9 + +# stone wall +SecretStoneDoor2 0x326 (Facing=EastCCW) +3523 2993 9 + +# stone wall +SecretStoneDoor2 0x328 (Facing=WestCCW) +3430 2990 5 + +# stone wall +SecretStoneDoor2 0x32A (Facing=EastCW) +3431 2990 5 + +# stone wall +SecretStoneDoor2 0x332 (Facing=NorthCW) +3371 2861 7 + +# sewing kit +SewingKit 0xF9D +3691 2478 6 + +# music stand +ShortMusicStand 0xEB6 +3660 2528 0 +3659 2528 0 +3661 2528 0 +3662 2528 0 +3666 2528 0 +3667 2528 0 +3668 2528 0 +3669 2528 0 + +# skinning knife +SkinningKnife 0xEC4 +3736 2682 46 + +# pile%s% of wool +Static 0x101F +3691 2466 0 +3693 2469 0 + +# wood curls +Static 0x1038 +3668 2509 0 +3668 2506 0 +3659 2506 0 +3645 2498 0 +3642 2506 0 +3643 2503 0 + +# sack of flour +Static 0x1039 +3666 2643 0 + +# loom bench +Static 0x104A +3692 2482 0 + +# magical sparkles +Static 0x1153 +3597 2582 0 + +# potted tree +Static 0x11C9 +3700 2504 0 +3697 2504 0 +3694 2504 0 + +# a bulk order deed completed by Hanse +LocalizedStatic 0x14EF (LabelNumber=1048169;Hue=805) +3588 2600 6 + +# plough +Static 0x1500 +3740 2645 40 + +# harrow +Static 0x1505 +3740 2643 40 + +# water barrel +Static 0x154D +3417 2494 10 +3418 2494 5 +3417 2493 10 + +# pewter bowl +Static 0x15FD +3654 2465 6 + +# large pewter bowl +Static 0x1603 +3654 2467 6 + +# folded cloth +Static 0x175F (Hue=438) +3688 2464 6 + +# folded cloth +Static 0x1761 (Hue=53) +3691 2464 6 + +# folded cloth +Static 0x1762 (Hue=438) +3688 2468 6 + +# folded cloth +Static 0x1762 (Hue=53) +3690 2464 6 + +# folded cloth +Static 0x1763 (Hue=438) +3688 2467 6 + +# folded cloth +Static 0x1763 (Hue=53) +3692 2464 6 + +# folded cloth +Static 0x1764 (Hue=438) +3688 2465 6 + +# folded cloth +Static 0x1764 (Hue=53) +3693 2464 6 + +# heating stand +Static 0x184E (Light=Circle150) +3612 2471 6 +3610 2477 6 + +# scales +Static 0x1851 +3614 2471 6 + +# skull with candle +CandleSkull 0x1858 +3611 2474 6 + +# empty vials +Static 0x185B +3613 2471 6 + +# full vials +Static 0x185E +3611 2476 6 + +# dishing stump +Static 0x1865 +3745 2689 40 + +# woodworker's bench +Static 0x19F1 +3642 2498 0 + +# woodworker's bench +Static 0x19F2 +3642 2497 0 + +# woodworker's bench +Static 0x19F3 +3642 2496 0 + +# stone wall +Static 0x1A +3692 2508 0 + +# stone wall +Static 0x1B +3692 2507 0 +3692 2504 0 + +# boards +Static 0x1BD9 +3644 2496 0 +3643 2496 0 + +# logs +Static 0x1BDE +3648 2498 0 +3640 2508 0 + +# logs +Static 0x1BDF +3648 2499 0 +3648 2500 0 +3648 2501 0 +3648 2503 0 +3648 2504 0 +3640 2509 0 + +# stone wall +Static 0x1C +3689 2508 0 +3688 2508 0 + +# stone wall +Static 0x1E +3690 2508 0 + +# raw fish +Static 0x1E18 +3736 2683 46 + +# books +Static 0x1E21 +3611 2475 6 + +# books +Static 0x1E24 +3611 2477 6 + +# books +Static 0x1E25 +3610 2474 6 + +# dartboard +DartBoard 0x1E2E +3698 2504 0 +3695 2504 0 + +# dartboard +DartBoard 0x1E2F +3688 2512 0 +3688 2515 0 + +# trophy +Static 0x1E60 +3738 2680 40 + +# trophy +Static 0x1E65 +3739 2680 40 + +# trophy +Static 0x1E66 +3742 2680 40 + +# trophy +Static 0x1E68 +3736 2692 40 + +# trophy +Static 0x1E69 +3736 2689 40 + +# trophy +Static 0x1E6A +3736 2691 40 + +# trophy +Static 0x1E6B +3736 2686 40 + +# tool kit +Static 0x1EBA +3656 2504 6 + +# tinker's tools +Static 0x1EBC +3664 2504 6 + +# stone wall +Static 0x21 +3691 2508 0 + +# wooden ramp +Static 0x87A +3695 2761 35 +3695 2760 35 +3695 2759 35 + +# cauldron +Static 0x974 +3735 2645 40 + +# slab%s% of bacon +Static 0x977 +3704 2650 26 + +# tray +Static 0x991 +3651 2469 6 + +# tray +Static 0x992 +3664 2644 6 + +# ceramic mug +CeramicMug 0x995 +3634 2496 6 +3667 2651 6 +3665 2651 6 +3672 2652 6 +3676 2653 6 +3736 2565 46 +3731 2632 46 +3627 2632 6 +3704 2589 26 +3676 2649 6 +3608 2467 6 + +# mug +CeramicMug 0x998 +3650 2474 6 + +# mug +CeramicMug 0x999 +3650 2475 6 + +# knife +Knife 0x9A5 +3736 2681 46 + +# metal box +FillableMetalBox 0x9A8 +3688 2472 6 + +# silverware +Static 0x9BE +3651 2474 6 + +# bottles of wine +Static 0x9C4 +3669 2644 0 +3664 2642 6 + +# silverware +Static 0x9D4 +3732 2632 46 +3628 2632 6 +3651 2475 6 + +# silverware +Static 0x9D5 +3704 2588 27 +3736 2564 46 + +# plate +Plate 0x9D7 +3635 2496 6 +3736 2564 46 +3732 2632 46 +3628 2632 6 +3651 2475 6 +3651 2474 6 +3704 2588 26 +3608 2466 6 + +# pot +Static 0x9E0 +3653 2469 6 + +# pot +Static 0x9E1 +3652 2469 6 + +# pot +Static 0x9E3 +3669 2642 0 +3664 2641 6 + +# kettle +Static 0x9ED +3584 2577 0 +3667 2641 0 +3651 2465 0 + +# fork +Fork 0x9F4 +3635 2496 6 + +# knife +Knife 0x9F6 +3635 2496 7 + +# knife +Knife 0x9F7 +3608 2466 7 + +# spoon +Spoon 0x9F8 +3635 2496 7 + +# spoon +Spoon 0x9F9 +3608 2466 7 + +# candle +Static 0xA26 (Light=Circle150) +3688 2476 6 +3636 2496 6 + +# candle +Static 0xA26 (Light=Circle150) +3608 2465 6 + +# candle +Static 0xB1A (Light=Circle150) +3640 2467 6 + +# table +Static 0xB34 (Hue=1154) +3594 2581 0 + +# table +Static 0xB70 +3668 2504 0 +3664 2504 0 + +# table +Static 0xB71 +3669 2504 0 +3665 2504 0 + +# wooden signpost +Static 0xB9A +3666 2512 0 + +# debris +Static 0xC2D +3669 2506 0 + +# raw cotton +Static 0xDEF +3688 2469 0 + +# knitting +Static 0xDF6 +3693 2478 6 + +# chessmen +Static 0xE14 +3672 2648 5 + +# bloody water +Static 0xE23 +3736 2684 46 + +# stump +Static 0xE56 +3651 2504 0 + +# strong box +FillableMetalBox 0xE80 +3668 2597 6 + +# sheet music +Static 0xEBF +3674 2528 0 +3675 2528 0 + +# dress form +Static 0xEC6 +3690 2468 0 +3688 2480 0 + +# dress form +Static 0xEC7 +3693 2467 0 + +# bolt%s% of cloth +Static 0xF96 (Hue=438) +3693 2479 0 +3692 2479 0 + +# bolt%s% of cloth +Static 0xF97 (Hue=53) +3692 2465 0 + +# bolt%s% of cloth +Static 0xF98 (Hue=438) +3689 2467 0 +3689 2482 0 + +# bolt%s% of cloth +Static 0xF9A (Hue=438) +3689 2466 0 +3689 2483 0 + +# bolt%s% of cloth +Static 0xF9C (Hue=38) +3693 2465 0 + +# playing cards +Static 0xFA3 +3676 2652 6 + +# fire pit +Static 0xFAC +3735 2645 40 + +# backgammon game +Backgammon 0xFAD +3676 2650 4 + +# barrel +Static 0xFAE +3661 2672 -2 +3671 2665 -2 +3645 2674 -2 + +# Hanse's ancient smithy hammer +LocalizedStatic 0xFB4 (LabelNumber=1048173;Hue=1154) +3459 2777 25 + +# book +Static 0xFBD +3688 2474 6 +3714 2649 26 +3640 2468 6 +3608 2500 6 +3666 2531 6 +3675 2532 6 + +# book +Static 0xFBE +3604 2504 6 +3668 2496 6 +3666 2620 6 + +# pen and ink +Static 0xFBF +3688 2474 6 +3714 2649 26 +3640 2468 6 +3608 2500 6 +3666 2531 6 +3675 2532 6 + +# fireplace +StoneFireplaceEastAddon 0x959 +3584 2581 20 + +# fireplace +StoneFireplaceSouthAddon 0x961 +3741 2680 40 + +# oven +StoneOvenEastAddon 0x92C +3584 2575 0 +3584 2578 0 + +# stool +Stool 0xA2A +3664 2652 0 +3665 2652 0 +3666 2652 0 +3667 2652 0 +3668 2652 0 + +# wooden door +StrongWoodDoor 0x6E5 (Facing=WestCW) +3606 2573 0 +3595 2567 0 +3601 2573 0 +3596 2573 0 +3592 2580 0 + +# wooden door +StrongWoodDoor 0x6ED (Facing=SouthCW) +3692 2506 0 + +# wooden door +StrongWoodDoor 0x6EF (Facing=NorthCCW) +3692 2505 0 + +# music stand +TallMusicStand 0xEBB +3674 2528 0 +3675 2528 0 + +# tambourine +Tambourine 0xE9D +3675 2536 6 +3674 2540 6 + +# tambourine +TambourineTassel 0xE9E +3674 2536 6 +3675 2538 6 + +# tool kit +TinkerTools 0x1EB8 +3668 2504 6 + +# training dummy +TrainingDummy 0x1070 +3730 2576 40 +3733 2576 40 + +# training dummy +TrainingDummy 0x1074 +3728 2586 40 +3728 2581 40 + +# chair +WoodenChair 0xB56 +3673 2532 0 + +# chair +WoodenChair 0xB57 +3627 2529 0 +3651 2473 0 +3666 2619 0 + +# chair +WoodenChair 0xB58 +3689 2474 0 +3641 2468 0 +3667 2531 0 +3676 2532 0 + +# chair +WoodenChair 0xB59 +3635 2497 0 +3651 2476 0 +3668 2497 0 + +# chair +WoodenChairCushion 0xB52 +3608 2509 0 + +# chair +WoodenChairCushion 0xB53 +3613 2504 0 +3739 2687 40 + +# chair +WoodenChairCushion 0xB54 +3604 2505 0 +3739 2690 40 + +# chair +WoodenChairCushion 0xB55 +3609 2500 0 + +# wooden chest +FillableWoodenChest 0xE42 +3674 2611 0 +3674 2622 0 + +# wooden chest +FillableWoodenChest 0xE43 +3665 2613 0 +3678 2613 0 +3664 2608 0 diff --git a/Data/Decoration/Trammel/_minoc camp.cfg b/Data/Decoration/Trammel/_minoc camp.cfg new file mode 100644 index 000000000..f6667b51e --- /dev/null +++ b/Data/Decoration/Trammel/_minoc camp.cfg @@ -0,0 +1,77 @@ +# gruesome standard +Static 0x041F +2635 701 0 + +# garbage +Static 0x10EE +2629 701 0 +2622 703 0 +2626 700 0 +2625 706 0 +2625 708 0 +2629 703 0 + +# tent wall +Static 0x02EB (Hue=0x33) +2620 702 0 +2622 707 0 +2627 698 0 +2627 708 0 +2620 702 1 + +# tent wall +Static 0x02EC (Hue=0x33) +2621 701 0 +2623 706 0 +2628 697 0 +2628 707 0 +2621 701 1 + +# skinned goat +Static 0x1E88 +2624 705 0 + +# fire pit +Static 0x0FAC +2625 703 0 + +# campfire +Static 0x0DE3 +2625 703 0 + +# cauldron +Static 0x0974 +2625 703 0 + +# stump +Static 0x0E59 +2624 709 0 + +# executioner's axe +Static 0x0F46 +2625 708 0 + +# gruesome standard +Static 0x0428 +2626 711 0 + +# gruesome standard +Static 0x0420 +2627 694 0 + +# skinned goat +Static 0x1E89 +2631 696 0 + +# skinned deer +Static 0x1E90 +2627 701 0 +2627 701 1 + +# stump +Static 0x0E58 +2627 703 0 + +# skinned deer +Static 0x1E91 +2627 705 0 diff --git a/Data/Decoration/Trammel/ambitious solen queen quest.cfg b/Data/Decoration/Trammel/ambitious solen queen quest.cfg new file mode 100644 index 000000000..5da3fb865 --- /dev/null +++ b/Data/Decoration/Trammel/ambitious solen queen quest.cfg @@ -0,0 +1,3 @@ +# Red Ambitious Solen Queen +Spawner 0x1F13 (Spawn=RedAmbitiousSolenQueen; Count=1; HomeRange=14) +5790 1983 2 \ No newline at end of file diff --git a/Data/Decoration/Trammel/collector quest.cfg b/Data/Decoration/Trammel/collector quest.cfg new file mode 100644 index 000000000..ce1cbe33f --- /dev/null +++ b/Data/Decoration/Trammel/collector quest.cfg @@ -0,0 +1,23 @@ +# Elwood McCarrin +Spawner 0x1F13 (Spawn=ElwoodMcCarrin; Count=1; HomeRange=0) +3666 2652 0 + +# Alberta Giacco +Spawner 0x1F13 (Spawn=AlbertaGiacco; Count=1; HomeRange=0) +2899 709 0 + +# Gabriel Piete +Spawner 0x1F13 (Spawn=GabrielPiete; Count=1; HomeRange=0) +1443 1557 51 + +# Impresario +Spawner 0x1F13 (Spawn=Impresario; Count=1; HomeRange=0) +1454 1593 20 +3738 1230 0 +3736 1220 2 +1429 4011 0 +1424 4008 0 + +# Tomas O'Neerlan +Spawner 0x1F13 (Spawn=TomasONeerlan; Count=1; HomeRange=0) +1839 2674 0 \ No newline at end of file diff --git a/Data/Decoration/Trammel/haven.cfg b/Data/Decoration/Trammel/haven.cfg new file mode 100644 index 000000000..a7186906b --- /dev/null +++ b/Data/Decoration/Trammel/haven.cfg @@ -0,0 +1,334 @@ +# ankh +AnkhEast 0x0004 +3606 2582 10 + +# stone wall +Static 0x001C +3738 2679 40 + +# plaster wall +Static 0x014F +3673 2583 0 + +# wall +Static 0x0450 (Hue=0x482) +3606 2582 0 + +# wall +Static 0x0451 (Hue=0x482) +3606 2581 0 + +# wall +Static 0x0452 (Hue=0x482) +3605 2582 0 + +# wall +Static 0x0453 (Hue=0x482) +3605 2581 0 + +# marble floor +Static 0x0496 (Hue=0x482) +3606 2582 5 + +# slate roof +Static 0x0596 +3680 2600 20 +3681 2600 17 + +# oven +StoneOvenEastAddon 0x092C +3656 2508 0 +3624 2548 0 +3624 2546 0 + +# oven +StoneOvenSouthAddon 0x0930 +3666 2639 0 +3653 2464 0 + +# fireplace +Static 0x095F +3741 2680 40 + +# fireplace +Static 0x0960 +3740 2680 40 + +# fireplace +StoneFireplaceSouthAddon 0x0961 +3650 2464 0 +3669 2640 0 + +# cauldron +Static 0x0974 +3736 2572 40 + +# candelabra +CandelabraStand 0x0A29 (Unlit) +3638 2550 0 + +# chest of drawers +Drawer 0x0A2C +3697 2472 0 +3738 2561 40 +3745 2680 40 +3605 2568 20 +3604 2568 20 +3610 2464 0 +3705 2584 20 + +# chest of drawers +Drawer 0x0A34 +3728 2634 40 +3632 2501 0 + +# chest of drawers +FancyDrawer 0x0A38 +3600 2505 0 +3608 2498 0 +3632 2544 0 + +# armoire +FancyArmoire 0x0A4D +3602 2504 0 +3609 2496 0 + +# armoire +Armoire 0x0A53 +3632 2497 0 +3696 2478 0 + +# bookcase +LibraryBookcase 0x0A97 +3629 2566 20 +3602 2568 20 +3619 2472 0 +3617 2472 0 +3609 2504 0 +3620 2472 0 +3599 2568 20 +3622 2475 0 + +# bookcase +LibraryBookcase 0x0A98 +3619 2475 0 +3621 2475 0 +3616 2472 0 +3630 2566 20 +3600 2568 20 +3634 2566 20 + +# bookcase +LibraryBookcase 0x0A99 +3608 2505 0 +3648 2473 0 +3648 2470 0 +3623 2567 20 +3626 2566 20 +3696 2473 0 +3632 2550 0 + +# bookcase +LibraryBookcase 0x0A9A +3626 2568 20 +3623 2565 20 +3632 2549 0 +3648 2476 0 +3648 2471 0 + +# bookcase +LibraryBookcase 0x0A9B +3622 2472 0 +3618 2475 0 +3620 2475 0 +3635 2566 20 +3601 2568 20 +3590 2568 20 + +# bookcase +LibraryBookcase 0x0A9C +3648 2475 0 +3648 2472 0 +3623 2568 20 +3623 2569 20 +3626 2567 20 +3623 2566 20 + +# lamp post +LampPost1 0x0B20 +3735 2640 40 +3720 2655 20 +3657 2521 0 +3657 2622 0 +3657 2654 0 +3657 2633 0 +3657 2478 0 +3646 2607 0 +3646 2625 0 +3651 2510 0 +3658 2568 0 +3658 2563 0 +3657 2545 0 +3678 2521 0 +3678 2489 0 +3614 2593 0 +3672 2633 0 +3669 2478 0 +3669 2545 0 +3711 2592 20 +3614 2480 0 +3743 2568 40 +3672 2510 0 +3645 2478 0 +3646 2534 0 +3646 2521 0 +3625 2510 0 +3694 2521 0 +3625 2489 0 +3616 2504 0 +3632 2536 0 +3646 2545 0 +3646 2582 0 +3615 2520 0 + +# candelabra +CandelabraStand 0x0B26 +3672 2542 0 +3614 2510 0 + +# counter +Static 0x0B3E +3590 2594 0 +3589 2594 0 + +# counter +Static 0x0B40 +3588 2594 0 +3591 2594 0 + +# wooden signpost +Static 0x0B98 +3666 2512 0 + +# red moongate +Static 0x0DDA (Hue=0x482; Light=Circle300) +3632 2566 0 +3632 2566 20 + +# crate +FillableLargeCrate 0x0E3C +3679 2593 0 +3624 2538 0 +3624 2537 0 +3671 2595 0 +3673 2590 0 +3678 2592 0 +3674 2588 0 +3679 2592 0 +3672 2596 0 +3674 2587 0 +3672 2595 0 +3673 2587 0 +3673 2589 0 +3671 2596 0 + +# crate +MediumCrate 0x0E3F +3673 2588 0 +3624 2536 0 +3675 2587 0 + +# metal chest +MetalGoldenChest 0x0E40 +3627 2618 4 +3595 2584 20 +3595 2583 20 + +# metal chest +FillableMetalGoldenChest 0x0E41 +3677 2587 -2 +3678 2587 -2 + +# wooden chest +WoodenChest 0x0E43 +3737 2561 40 +3730 2632 40 +3709 2584 20 + +# fire pit +Static 0x0FAC (Light=Circle225) +3704 2596 20 +3736 2572 40 + +# anvil +AnvilEastAddon 0x0FAF +3607 2599 0 + +# anvil +AnvilSouthAddon 0x0FB0 +3605 2598 0 +3602 2599 0 +3644 2618 0 +3644 2615 0 +3597 2603 0 +3593 2596 0 +3603 2603 0 + +# forge +SmallForgeAddon 0x0FB1 +3608 2599 0 +3592 2596 0 +3603 2604 0 +3644 2614 0 +3605 2597 0 +3602 2598 0 + +# spinning wheel +SpinningWheelEastAddon 0x1019 +3691 2485 0 + +# upright loom +LoomSouthAddon 0x1061 +3692 2482 0 + +# stone face +StoneFaceTrapNoDamage 0x10FD +3607 2581 0 +3604 2581 0 + +# magical sparkles +Static 0x1153 +3606 2582 0 +3607 2582 0 +3607 2583 0 +3607 2583 7 +3608 2582 15 + +# potted tree +Static 0x11C8 +3637 2496 0 + +# potted tree +Static 0x11C9 +3704 2584 20 +3608 2504 0 +3680 2477 0 +3633 2496 0 + +# flowerpot +Static 0x11CA +3728 2632 40 +3600 2504 0 + +# flowerpot +Static 0x11CB +3608 2501 7 + +# flour mill +FlourMillSouthAddon 0x192C +3629 2536 0 + +# bellows +LargeForgeEastAddon 0x1986 +3644 2619 0 +3596 2601 0 \ No newline at end of file diff --git a/Data/Decoration/Trammel/magincia.cfg b/Data/Decoration/Trammel/magincia.cfg new file mode 100644 index 000000000..1d0f2b105 --- /dev/null +++ b/Data/Decoration/Trammel/magincia.cfg @@ -0,0 +1,9 @@ +# metal signpost +Static 0x0BA0 +3778 2256 20 + +# lamp post +LampPost3 0xB24 +3758 2256 30 +3773 2233 30 +3793 2232 20 \ No newline at end of file diff --git a/Data/Decoration/Trammel/solen matriarch quest.cfg b/Data/Decoration/Trammel/solen matriarch quest.cfg new file mode 100644 index 000000000..ea10963da --- /dev/null +++ b/Data/Decoration/Trammel/solen matriarch quest.cfg @@ -0,0 +1,3 @@ +# Red Solen Matriarch +Spawner 0x1F13 (Spawn=RedSolenMatriarch; Count=1; HomeRange=14) +5776 1898 22 \ No newline at end of file diff --git a/Data/Decoration/Trammel/uzeraan turmoil quest.cfg b/Data/Decoration/Trammel/uzeraan turmoil quest.cfg new file mode 100644 index 000000000..cd9461f98 --- /dev/null +++ b/Data/Decoration/Trammel/uzeraan turmoil quest.cfg @@ -0,0 +1,108 @@ +# a magical teleporter +UzeraanTurmoilTeleporter 0x1822 +3597 2582 0 + +# metal chest +DaemonBloodChest 0x9AB +3456 2558 50 + +# Mansion guard +Spawner 0x1F13 (Spawn=MansionGuard; Count=1; HomeRange=0) +3585 2588 0 +3585 2586 0 +3603 2583 0 +3613 2586 0 +3613 2589 0 + +# Uzeraan +Spawner 0x1F13 (Spawn=Uzeraan; Count=1; HomeRange=0) +3595 2587 0 + +# Schmendrick +Spawner 0x1F13 (Spawn=Schmendrick; Count=1; HomeRange=0) +5211 1818 0 + +# Schmendrick apprentice corpse +Spawner 0x1F13 (Spawn=SchmendrickApprenticeCorpse; Count=1; HomeRange=15; MinDelay=0:10:0; MaxDelay=0:10:0) +5261 1831 0 + +# Schmendrick cave teleporter +Static 0x1822 +5260 1841 0 + +Teleporter 0x1BC3 (PointDest=(3613, 2592, 0)) +5260 1841 0 + +# Dryad +Spawner 0x1F13 (Spawn=Dryad; Count=1; HomeRange=2) +3555 2698 5 + +# fire +Static 0x19AB +5210 1817 0 +5211 1814 0 +5205 1818 0 + +# sparkle +Static 0x374A +5208 1817 0 + +# sparkle +Static 0x377A +5209 1815 0 + +# sparkle +Static 0x376A +5208 1815 0 + +# magical sparkles +Static 0x1153 +5207 1816 0 +5260 1842 0 +5261 1841 0 + +# death vortex +Static 0x3789 +5208 1815 0 + +# magical sparkles +Static 0x1156 +5261 1841 0 +5261 1842 0 + +# cannon south +Cannon 0x0 (CannonDirection=South) +3678 2768 33 +3675 2767 31 +3593 2810 50 + +# cannon west +Cannon 0x0 (CannonDirection=West) +3681 2776 39 +3683 2779 44 +3659 2790 47 +3705 2832 29 + +# cannon east +Cannon 0x0 (CannonDirection=East) +3662 2795 68 +3596 2807 50 + +# cannon north +Cannon 0x0 (CannonDirection=North) +3593 2804 50 +3702 2837 33 + +# cannon balls +Static 0xE74 +3706 2833 24 +3595 2808 49 +3594 2808 50 +3663 2793 67 +3661 2791 53 +3662 2793 67 +3676 2768 32 +3676 2767 31 +3682 2777 41 +3683 2777 42 +3681 2777 40 \ No newline at end of file diff --git a/Data/Decoration/Trammel/yew.cfg b/Data/Decoration/Trammel/yew.cfg new file mode 100644 index 000000000..9a396047e --- /dev/null +++ b/Data/Decoration/Trammel/yew.cfg @@ -0,0 +1,2247 @@ +######### +# Doors # +######### + +# metal door +MetalDoor 0x0675 (Facing=WestCW) +319 791 20 +299 764 20 +312 783 20 +313 791 20 +356 839 20 +369 903 0 +369 867 0 + +# metal door +MetalDoor 0x0677 (Facing=EastCCW) +313 783 20 +357 839 20 +370 903 0 +370 867 0 + +# metal door +MetalDoor 0x067D (Facing=SouthCW) +289 771 20 +367 878 0 +367 878 20 +289 771 0 +327 793 20 +343 884 20 +343 885 0 +351 835 20 +371 871 0 +367 873 20 +367 873 0 +343 873 20 +343 873 0 + +# metal door +MetalDoor 0x067F (Facing=NorthCCW) +327 792 20 +351 834 20 + + +# barred metal door +BarredMetalDoor 0x0685 (Facing=WestCW) +262 769 -2 +262 769 20 +268 769 -2 +268 769 20 +274 769 -2 +274 769 20 +280 769 -2 +280 769 20 +286 769 -2 +286 769 20 + +# barred metal door +BarredMetalDoor 0x0689 (Facing=WestCCW) +262 773 -2 +262 773 20 +268 773 -2 +268 773 20 +274 773 -2 +274 773 20 +280 773 -2 +280 773 20 +286 773 -2 +286 773 20 + + +# wooden gate +DarkWoodGate 0x0866 (Facing=WestCW) +355 891 0 +666 951 0 +666 1191 0 +355 890 20 +562 1111 0 + +# wooden gate +DarkWoodGate 0x0868 (Facing=EastCCW) +356 891 0 +667 951 0 +667 1191 0 +356 890 20 +563 1111 0 + + +# stone wall +SecretStoneDoor1 0x00E8 (Facing=WestCW) +308 791 0 + +# stone wall +SecretStoneDoor1 0x00EA (Facing=EastCCW) +261 763 0 +262 779 0 +262 779 20 +267 763 0 +268 779 0 +268 779 20 +273 763 0 +274 779 0 +274 779 20 +279 763 0 +280 779 0 +280 779 20 +285 763 0 +286 779 0 +286 779 20 +291 777 20 + +# stone wall +SecretStoneDoor1 0x00EC (Facing=WestCCW) +261 763 20 +267 763 20 +273 763 20 +279 763 20 +285 763 20 + +# stone wall +SecretStoneDoor1 0x00F0 (Facing=SouthCW) +296 761 20 +301 762 0 + +# stone wall +SecretStoneDoor1 0x00F2 (Facing=NorthCCW) +301 761 0 + + +############ +# Standard # +############ + +# stone wall +Static 0x00C8 +309 791 0 + +# stone wall +Static 0x00C9 +564 1307 0 + +# patch +Static 0x02F1 +597 837 0 + +# patch +Static 0x02F2 +597 837 0 + +# cobblestones +Static 0x0515 +383 917 0 + +# cobblestones +Static 0x0516 +309 786 0 +309 787 0 + +# oven +StoneOvenEastAddon 0x092C +552 1217 0 + +# oven +StoneOvenSouthAddon 0x0930 +394 1216 0 +553 984 -2 +665 1000 0 +682 1200 0 + +# slab of bacon +Static 0x0976 +457 1141 6 +603 1187 6 + +# slab of bacon +Static 0x0977 +392 1218 6 +602 1184 10 +603 1184 6 + +# wheel of cheese +CheeseWheel 0x097E +452 1110 6 + +# basket +Basket 0x0990 +284 986 4 +416 1035 4 +656 1005 6 + +# tray +Static 0x0991 +392 1218 6 +539 1009 6 +603 1184 6 +642 844 4 +643 834 4 +654 938 6 + +# tray +Static 0x0992 +416 1037 4 +450 1117 6 +459 1141 6 +555 1221 6 +574 1123 6 +679 1202 6 + +# fruit basket +FruitBasket 0x0993 +421 1035 6 +642 833 4 +656 1004 6 + +# ceramic mug +CeramicMug 0x0995 +642 834 4 +642 837 4 +642 838 4 +642 840 4 +642 842 4 + +# ceramic mug +CeramicMug 0x0997 +400 1219 6 +452 1112 6 +555 1210 6 +599 1187 6 +624 1149 6 +643 836 4 +643 838 4 +643 840 4 +643 842 4 +643 844 4 +648 938 6 +648 1084 6 +677 1202 6 + +# bottle of liquor +BeverageBottle 0x099B (Content=Liquor) +261 774 0 +261 774 20 +262 766 0 +262 766 20 +267 774 0 +267 774 20 +268 766 0 +268 766 20 +273 774 0 +274 766 0 +274 766 20 +274 774 20 +279 774 0 +279 774 20 +280 766 0 +280 766 20 +285 774 0 +285 774 20 +286 766 0 +286 766 20 +324 1240 6 +398 1220 6 +400 1219 6 +420 1034 6 +450 963 6 +451 1112 6 +452 1112 6 +458 1135 6 +459 1133 6 +554 1212 6 +555 1210 6 +597 1188 6 +599 1187 6 +624 1147 6 +634 1040 6 +646 939 6 +648 938 6 +648 1084 6 +660 1003 6 +662 1002 6 +677 1202 6 +677 1203 6 +734 883 6 +736 882 6 + +# bottles of liquor +Static 0x099E +608 873 5 +608 876 0 +609 872 10 +611 872 0 +611 875 0 +613 875 5 +613 877 0 +615 872 0 +615 873 5 +615 875 0 +617 873 0 +617 874 0 +617 877 0 +619 876 0 +620 874 10 +621 874 0 + +# fork +Fork 0x09A3 +451 1111 6 +458 1134 6 +554 1211 6 +642 835 4 +642 837 4 +642 839 4 +642 841 4 +642 843 4 + +# fork +Fork 0x09A4 +399 1219 6 +598 1187 6 +647 938 6 +678 1202 6 + +# knife +Knife 0x09A5 +451 1111 6 +458 1134 6 +554 1211 6 +642 835 4 +642 837 4 +642 839 4 +642 841 4 +642 843 4 + +# knife +Knife 0x09A6 +399 1219 6 +535 1011 6 +598 1187 6 +647 938 6 +678 1202 6 + +# metal box +FillableMetalBox 0x09A8 +555 988 6 +562 1008 6 +565 960 6 + +# wooden box +WoodenBox 0x09AA +731 880 0 +739 880 0 + +# metal chest +FillableMetalChest 0x09AB (ContentType=Inn) +603 808 0 +603 816 0 +604 826 0 +607 826 0 +617 824 0 +619 808 0 +649 808 0 +653 824 0 +668 808 0 +668 816 0 + +# bushel +Basket 0x09AC +656 1006 6 + +# cooked bird +CookedBird 0x09B7 +642 844 4 +643 834 4 + +# cooked bird +CookedBird 0x09B8 +459 1141 6 +555 1221 6 +679 1202 6 + +# roast pig +RoastPig 0x09BB +281 984 4 + +# roast pig +RoastPig 0x09BC +392 1220 9 + +# silverware +Static 0x09BD +283 986 4 + +# silverware +Static 0x09BE +421 1034 6 +481 850 4 +483 850 4 +661 1002 6 +735 882 6 + +# sausage +Sausage 0x09C0 +603 1188 6 + +# sausage +Sausage 0x09C1 +392 1221 6 + +# ham +Ham 0x09C9 +604 1184 11 + +# ceramic mug +CeramicMug 0x09CA +324 1240 6 +398 1220 6 +451 1112 6 +554 1212 6 +597 1188 6 +634 1040 6 +646 939 6 +677 1203 6 + +# grape bunch +Grapes 0x09D1 +594 846 0 +594 858 0 +597 853 0 +600 841 0 +600 846 0 +600 857 0 +603 848 0 +603 860 0 +606 845 0 +606 858 0 +609 854 0 +610 883 6 +610 884 6 +610 885 6 +610 886 6 +610 887 6 +610 892 6 +611 892 6 +612 846 0 +612 856 0 +612 892 6 +613 892 6 +614 883 6 +614 884 6 +614 885 6 +614 886 6 +614 887 6 +615 842 0 +615 850 0 +615 858 0 +615 892 6 +616 892 6 +617 892 6 +618 883 6 +618 884 6 +618 885 6 +618 886 6 +618 887 6 +618 892 6 + +# ham +Ham 0x09D3 +603 1189 6 + +# silverware +Static 0x09D4 +284 987 4 +286 987 4 +420 1035 6 +451 963 6 +480 851 4 +482 851 4 +661 1003 6 +735 883 6 + +# silverware +Static 0x09D5 +287 986 4 +328 1061 4 + +# pitcher +Static 0x09D6 +480 851 4 +481 850 4 +482 851 4 +483 850 4 + +# plate +Plate 0x09D7 +283 986 4 +284 987 4 +286 987 4 +287 986 4 +323 1240 6 +328 1061 4 +399 1219 6 +399 1220 6 +420 1035 6 +421 1034 6 +451 963 6 +451 1111 6 +452 1111 6 +458 1134 6 +459 1134 6 +480 851 4 +481 850 4 +482 851 4 +483 850 4 +554 1211 6 +555 1211 6 +598 1187 6 +598 1188 6 +624 1148 6 +633 1040 6 +642 835 4 +642 837 4 +642 839 4 +642 841 4 +642 843 4 +643 835 4 +643 837 4 +643 839 4 +643 841 4 +643 843 4 +647 938 6 +647 939 6 +648 1085 6 +661 1002 6 +661 1003 6 +678 1202 6 +678 1203 6 +735 882 6 +735 883 6 + +# cake +Cake 0x09E9 +451 1117 6 +555 1212 6 + +# kettle +Static 0x09ED +397 1216 0 +449 1117 6 +458 1141 6 +553 1221 6 +555 986 6 +654 936 6 +685 1201 6 + +# milk +Pitcher 0x09F0 (Content=Milk) +642 836 4 +643 842 4 + +# cut of raw ribs +RawRibs 0x09F1 +460 1141 6 +528 1011 3 + +# fork +Fork 0x09F4 +323 1240 6 +399 1220 6 +573 1123 6 +598 1188 6 +633 1040 6 +647 939 6 +678 1203 6 + +# fork +Fork 0x09F5 +452 1111 6 +459 1134 6 +555 1211 6 +624 1148 6 +643 835 4 +643 837 4 +643 839 4 +643 841 4 +643 843 4 +648 1085 6 + +# knife +Knife 0x09F6 +323 1240 6 +399 1220 6 +598 1188 6 +633 1040 6 +647 939 6 +678 1203 6 + +# knife +Knife 0x09F7 +452 1111 6 +459 1134 6 +555 1211 6 +603 1188 6 +624 1148 6 +643 835 4 +643 837 4 +643 839 4 +643 841 4 +643 843 4 +648 1085 6 + +# spoon +Spoon 0x09F8 +323 1240 6 +399 1220 6 +598 1188 6 +633 1040 6 +647 939 6 +678 1203 6 + +# spoon +Spoon 0x09F9 +452 1111 6 +459 1134 6 +555 1211 6 +624 1148 6 +643 835 4 +643 837 4 +643 839 4 +643 841 4 +643 843 4 +648 1085 6 + +# candle +CandleLarge 0x0A26 +419 1034 6 +452 963 6 + +# candelabra +CandelabraStand 0x0A29 +282 989 0 + +# chest of drawers +Drawer 0x0A2C +331 1056 0 +333 1056 0 +392 1224 0 +426 1040 0 +449 1128 0 +457 960 0 +457 1112 0 +486 840 0 +595 1184 0 +625 1144 0 +637 1040 0 +648 944 0 +649 1080 0 +666 1008 0 +682 1208 0 +732 880 0 +738 880 0 + +# chest of drawers +FancyDrawer 0x0A30 +613 824 0 +665 808 0 +665 816 0 +669 824 0 + +# chest of drawers +Drawer 0x0A34 +320 1245 0 + +# chest of drawers +FancyDrawer 0x0A38 +336 883 20 +336 884 0 +480 841 0 +600 812 0 +600 820 0 +600 828 0 +616 811 0 +616 828 0 +648 811 0 +648 826 0 +656 828 0 + +# armoire +FancyArmoire 0x0A4D +609 824 0 +670 824 0 + +# armoire +Armoire 0x0A4F +280 976 0 +285 976 0 +394 1224 0 +453 1128 0 +461 960 0 +597 1184 0 +629 1144 0 +650 944 0 +653 1080 0 +670 1008 0 + +# armoire +FancyArmoire 0x0A51 +336 882 0 +336 882 20 +480 842 0 +600 811 0 +600 819 0 +600 827 0 +616 810 0 +616 829 0 +648 810 0 +648 827 0 +656 829 0 +664 813 0 +664 821 0 + +# armoire +Armoire 0x0A53 +320 1241 0 +328 1057 0 +456 1117 0 +560 1221 0 +632 1045 0 +680 1209 0 + +# folded sheet +Static 0x0A92 +529 960 2 +532 960 2 +535 960 2 +538 960 2 +541 960 2 + +# folded sheet +Static 0x0A93 +424 1043 2 +424 1045 2 + +# bookcase +LibraryBookcase 0x0A97 +291 976 0 +330 1240 0 +339 872 0 +339 872 20 +401 1216 0 +406 1216 0 +450 1104 0 +461 1128 0 +473 840 0 +556 1208 0 +568 960 0 +598 1184 0 +601 1184 0 +626 842 20 +628 1032 0 +630 816 20 +638 816 20 +641 936 0 +642 816 20 +642 1080 0 +644 1080 0 + +# bookcase +LibraryBookcase 0x0A98 +290 976 0 +292 976 0 +329 1240 0 +333 1240 0 +338 872 0 +338 872 20 +340 872 0 +340 872 20 +400 1216 0 +449 1104 0 +457 1128 0 +476 840 0 +477 840 0 +553 1208 0 +557 1208 0 +565 808 0 +569 960 0 +599 1184 0 +625 842 20 +625 1032 0 +631 816 20 +639 816 20 +641 848 0 +641 1080 0 +642 848 0 +642 936 0 +643 848 0 +643 1080 0 +644 848 0 +644 936 0 +645 848 0 + +# bookcase +LibraryBookcase 0x0A99 +320 1061 0 +448 1109 0 +456 1134 0 +528 968 0 +528 973 0 +560 968 0 +616 1146 0 +616 1148 0 +624 825 20 +624 838 0 +624 839 0 +624 840 0 +624 849 0 +624 850 0 +624 851 0 +624 851 20 +624 852 0 +624 852 20 +624 853 0 +624 853 20 +624 1033 0 +640 937 0 +640 941 0 +642 843 20 +642 844 20 +642 845 20 +642 846 20 +672 1201 0 +728 881 0 +728 882 0 +728 884 0 +728 885 0 + +# bookcase +LibraryBookcase 0x0A9A +320 1058 0 +320 1060 0 +336 884 20 +448 1110 0 +456 1129 0 +528 969 0 +528 972 0 +616 1147 0 +624 826 20 +624 843 0 +624 844 0 +624 845 0 +672 1202 0 +672 1204 0 + +# bookcase +LibraryBookcase 0x0A9B +289 976 0 +293 976 0 +332 1240 0 +337 872 0 +337 872 20 +341 872 0 +341 872 20 +405 1216 0 +452 1104 0 +453 1104 0 +474 840 0 +554 1208 0 +561 808 0 +563 808 0 +600 1184 0 +629 1032 0 +632 816 20 +640 816 20 +640 1080 0 +641 816 20 +643 842 20 +644 842 20 +645 842 20 +645 936 0 +645 1080 0 + +# bookcase +LibraryBookcase 0x0A9C +320 1057 0 +336 874 20 +448 1111 0 +456 1133 0 +560 969 0 +568 1121 0 +624 827 20 +624 833 0 +624 834 0 +624 835 0 +672 1205 0 + +# candle +CandleLarge 0x0B1A +280 981 4 +320 787 26 +322 1240 6 +328 1060 4 +328 1247 6 +356 834 26 +398 1219 6 +424 1040 6 +448 1106 6 +448 1128 6 +456 1112 6 +458 1133 6 +474 844 6 +480 840 5 +515 988 6 +539 1008 6 +554 1210 6 +555 984 6 +560 1216 6 +562 1012 6 +565 966 6 +597 1187 6 +606 814 6 +606 816 6 +614 824 6 +616 814 6 +616 824 6 +621 1146 6 +627 845 6 +632 1040 6 +634 1032 6 +640 1087 6 +646 938 6 +648 808 6 +648 824 6 +648 1086 6 +656 824 6 +660 1002 6 +664 808 6 +664 816 6 +664 1008 6 +679 1203 6 +728 880 6 +742 880 6 +460 965 2 +632 816 6 +635 816 26 +638 816 6 + +# candelabra +Candelabra 0x0B1D +292 980 4 + +# candelabra +CandelabraStand 0x0B26 +320 1048 0 +332 816 20 +336 872 0 +336 872 20 +344 816 20 +344 902 0 +344 902 20 +366 902 0 +366 902 20 +486 848 0 +542 960 0 +542 974 0 +624 842 20 +624 1032 0 +640 846 0 +642 842 20 +528 960 0 +528 967 0 +344 876 0 +344 876 20 +365 876 0 +365 876 20 + +# wooden bench +WoodenBench 0x0B2C +312 794 20 +312 795 20 +312 796 20 +312 797 20 + +# wooden bench +WoodenBench 0x0B2D +315 792 20 +316 792 20 +317 792 20 +348 880 0 +348 881 20 +348 882 0 +348 883 20 +348 884 0 +348 885 20 +348 886 0 +348 887 20 +348 888 0 +348 889 20 +348 890 0 +349 880 0 +349 881 20 +349 882 0 +349 883 20 +349 884 0 +349 885 20 +349 886 0 +349 887 20 +349 888 0 +349 889 20 +349 890 0 +350 880 0 +350 881 20 +350 882 0 +350 883 20 +350 884 0 +350 885 20 +350 886 0 +350 887 20 +350 888 0 +350 889 20 +350 890 0 +351 880 0 +351 881 20 +351 882 0 +351 883 20 +351 884 0 +351 885 20 +351 886 0 +351 887 20 +351 888 0 +351 889 20 +351 890 0 +352 880 0 +352 881 20 +352 882 0 +352 883 20 +352 884 0 +352 885 20 +352 886 0 +352 887 20 +352 888 0 +352 889 20 +352 890 0 +353 880 0 +353 881 20 +353 882 0 +353 883 20 +353 884 0 +353 885 20 +353 887 20 +353 888 0 +353 889 20 +353 890 0 +358 880 0 +358 881 20 +358 882 0 +358 883 20 +358 884 0 +358 885 20 +358 886 0 +358 887 20 +358 888 0 +358 889 20 +358 890 0 +359 880 0 +359 881 20 +359 882 0 +359 883 20 +359 884 0 +359 885 20 +359 886 0 +359 887 20 +359 888 0 +359 889 20 +359 890 0 +360 880 0 +360 881 20 +360 882 0 +360 883 20 +360 884 0 +360 885 20 +360 886 0 +360 887 20 +360 888 0 +360 889 20 +360 890 0 +361 880 0 +361 881 20 +361 882 0 +361 883 20 +361 884 0 +361 885 20 +361 886 0 +361 887 20 +361 888 0 +361 889 20 +361 890 0 +362 880 0 +362 881 20 +362 882 0 +362 883 20 +362 884 0 +362 885 20 +362 886 0 +362 887 20 +362 888 0 +362 889 20 +362 890 0 +363 880 0 +363 881 20 +363 882 0 +363 883 20 +363 884 0 +363 885 20 +363 886 0 +363 887 20 +363 888 0 +363 889 20 +363 890 0 + +# chair +FancyWoodenChairCushion 0x0B4E +282 986 0 + +# chair +FancyWoodenChairCushion 0x0B4F +475 849 0 +481 849 0 +483 849 0 + +# chair +FancyWoodenChairCushion 0x0B50 +284 988 0 +286 988 0 +480 852 0 +482 852 0 + +# chair +FancyWoodenChairCushion 0x0B51 +288 986 0 +644 851 0 + +# chair +WoodenChairCushion 0x0B52 +473 843 0 +529 970 0 +625 831 20 +625 836 20 +626 834 0 +626 839 0 +626 844 0 +626 851 0 +641 835 0 +641 837 0 +641 839 0 +641 841 0 +641 843 0 + +# chair +WoodenChairCushion 0x0B53 +291 978 0 +319 786 20 +355 833 20 +631 818 20 +640 818 20 + +# chair +WoodenChairCushion 0x0B54 +355 900 20 +355 901 0 +475 853 0 +636 817 20 + +# chair +WoodenChairCushion 0x0B55 +337 879 0 +337 879 20 +635 1034 0 +645 831 20 +645 835 0 +645 836 20 +645 837 0 +645 839 0 +645 841 0 +645 843 0 + +# chair +WoodenChair 0x0B56 +450 1111 0 +457 1134 0 +514 990 0 +553 1211 0 +561 1010 0 +563 963 0 + +# chair +WoodenChair 0x0B57 +329 1246 0 +350 892 20 +350 893 0 +352 892 20 +352 893 0 +359 892 20 +359 893 0 +361 892 20 +361 893 0 +399 1218 0 +421 1033 0 +534 972 0 +536 972 0 +537 1010 0 +563 810 0 +568 1011 6 +598 1186 0 +620 1145 0 +641 1086 0 +647 937 0 +661 1001 0 +678 1201 0 +735 881 0 + +# chair +WoodenChair 0x0B58 +449 1107 0 +453 1111 0 +460 1134 0 +556 1211 0 +571 964 0 +625 1148 0 +641 939 0 +641 1083 0 +649 1085 0 + +# chair +WoodenChair 0x0B59 +321 1056 0 +323 1241 0 +350 900 0 +350 900 20 +399 1221 0 +403 1217 0 +420 1036 0 +459 1129 0 +562 812 0 +564 812 0 +598 1189 0 +633 1041 0 +647 940 0 +661 1004 0 +678 1204 0 +735 884 0 + +# chair +BambooChair 0x0B5A +571 1011 6 +664 1003 0 + +# chair +BambooChair 0x0B5B +459 964 0 +567 1008 6 + +# chair +BambooChair 0x0B5C +451 964 0 +534 968 0 +536 968 0 + +# chair +BambooChair 0x0B5D +281 980 0 +329 1061 0 +541 962 0 +541 963 0 +669 1003 0 + +# foot stool +FootStool 0x0B5E +458 960 0 +531 960 0 +534 960 0 +537 960 0 +540 960 0 + +# bench +Static 0x0B5F +425 1035 0 +428 1035 0 + +# bench +Static 0x0B60 +425 1034 0 +428 1034 0 + +# bench +Static 0x0B65 +427 1036 0 +667 1004 0 + +# bench +Static 0x0B66 +426 1036 0 +666 1004 0 + +# wooden signpost +Static 0x0B98 +618 1152 0 +626 1040 0 + +# broken chair +Static 0x0C10 +562 1010 6 + +# barber scissors +Static 0x0DFC +322 1048 6 + +# bloody bandage +Static 0x0E20 +539 960 0 + +# clean bandage +Static 0x0E21 +330 1247 6 + +# bloody bandage +Static 0x0E22 +329 1251 0 +531 960 0 +641 1089 0 + +# bloody water +Static 0x0E23 +320 1051 0 +330 1250 0 +533 960 0 +539 961 0 + +# crystal ball +Static 0x0E2E (Light=Circle150) +627 834 6 + +# crate +LargeCrate 0x0E3C +308 784 0 +308 784 3 +308 785 0 +308 786 0 +309 784 0 +309 784 3 +309 788 1 +309 788 4 +310 784 0 +310 787 0 +310 787 3 +310 787 6 +310 788 0 +310 788 3 +310 789 0 +310 789 3 +310 789 6 +311 789 0 +311 789 3 +317 784 0 +317 784 3 +317 784 6 +317 784 9 +317 785 0 +317 785 3 +317 794 0 +317 794 3 +317 794 6 +317 795 0 +317 795 3 +317 795 6 +318 784 0 +318 784 3 +318 785 0 +318 794 0 +318 794 3 +318 794 6 +318 795 0 +318 795 3 +318 795 6 +656 1002 0 +656 1003 0 + +# crate +LargeCrate 0x0E3D +311 788 -23 +312 790 -23 + +# mill crate +FillableLargeCrate 0x0E3D (ContentType=Mill) +546 810 0 +546 810 3 +546 811 0 +547 810 0 +547 810 3 +547 811 0 +547 811 3 + +# crate +MediumCrate 0x0E3E +323 795 0 +323 795 3 +323 796 0 +324 795 0 +324 796 0 +324 796 3 +416 1033 0 +416 1033 3 +416 1034 0 +448 961 0 +448 961 3 +448 962 0 +544 812 0 +544 812 3 +544 813 0 +544 813 3 +544 813 6 + +# crate +MediumCrate 0x0E3F +417 1032 0 +417 1032 3 +449 960 0 +449 960 3 + +# wooden chest +WoodenChest 0x0E42 +424 1042 0 +461 966 0 +664 1014 0 + +# wooden chest +WoodenChest 0x0E43 +311 784 0 +324 1061 0 +482 840 0 + +# empty jars +Static 0x0E46 +324 1048 6 + +# full jars +Static 0x0E4B +536 973 6 + +# stump +Static 0x0E59 +637 1155 0 + +# barrel +Barrel 0x0E77 +312 792 0 +312 792 5 +312 792 10 +312 793 0 +312 793 5 +312 794 0 +313 792 0 +313 792 5 +313 793 0 +314 785 6 +314 792 0 +323 790 0 +323 790 5 +323 790 10 +323 791 0 +324 790 0 +324 790 5 +324 790 10 +324 791 0 +325 790 0 +325 790 5 +325 790 10 +325 791 0 +416 1032 0 +448 960 0 +448 960 5 +544 808 0 +544 808 5 +544 808 10 +544 809 0 +544 809 5 +545 808 0 +545 808 5 +549 808 0 +549 809 0 +549 812 0 +549 812 5 +549 813 0 +549 813 5 +550 808 0 +550 808 5 +550 809 0 +550 809 5 +550 812 0 +550 812 5 +550 813 0 +550 813 5 +608 872 0 +608 872 5 +608 872 10 +608 873 0 +609 872 0 +609 872 5 +609 873 0 +609 873 5 +609 892 0 +610 882 0 +610 888 0 +612 874 0 +612 874 5 +612 874 10 +613 873 0 +613 873 5 +613 873 10 +613 874 0 +613 874 5 +613 875 0 +614 873 0 +614 873 5 +614 874 0 +614 874 5 +614 882 0 +614 888 0 +614 892 0 +615 873 0 +618 888 0 +619 873 0 +619 892 0 +620 873 0 +620 873 5 +620 873 10 +620 874 0 +620 874 5 +621 873 0 +661 1008 0 +730 888 0 + +# metal chest +MetalChest 0x0E7C +260 766 0 +260 766 20 +260 774 20 +260 775 0 +266 766 0 +266 766 20 +266 774 20 +266 775 0 +272 766 0 +272 766 20 +272 774 20 +272 775 0 +278 766 0 +278 766 20 +278 774 20 +278 775 0 +283 774 40 +284 766 0 +284 766 20 +284 774 20 +284 775 0 + +# inn metal chest +FillableMetalChest 0x0E7C (ContentType=Inn) +656 826 0 +656 827 0 + +# crate +SmallCrate 0x0E7E +448 962 3 + +# strong box +FillableMetalBox 0x0E80 +512 988 6 + +# drum +Static 0x0E9C +627 821 20 + +# tambourine +Static 0x0E9D +627 823 20 + +# standing harp +Static 0x0EB1 +625 818 20 + +# lap harp +Static 0x0EB2 +627 818 20 + +# lute +Static 0x0EB4 +625 821 20 +625 823 20 + +# music stand +Static 0x0EB9 +625 819 20 +627 819 20 + +# music stand +Static 0x0EBC +624 821 20 +624 823 20 + +# sheet music +Static 0x0EC0 +624 821 20 +624 823 20 + +# cleaver +Static 0x0EC3 +281 984 4 +604 1184 11 + +# skinning knife +Static 0x0EC4 +602 1184 10 + +# clean bandage +Static 0x0EE9 +322 1048 6 +323 1048 6 +533 967 6 +535 967 6 +535 973 6 +536 967 6 +640 1084 6 +642 1087 6 +642 1092 0 + +# bottle +Static 0x0EFE +642 851 26 + +# bottle +Static 0x0EFF +642 852 26 + +# bottle +Static 0x0F00 +644 830 26 + +# bottle +Static 0x0F01 +642 850 26 + +# bottle +Static 0x0F03 +644 830 26 + +# Black Potion +Static 0x0F06 +644 835 26 +645 850 26 + +# Blue Potion +Static 0x0F08 +644 844 26 + +# Green Potion +Static 0x0F0A +642 853 26 + +# Red Potion +Static 0x0F0B +329 1254 6 +332 1254 6 +533 973 10 +534 973 9 +537 973 10 +640 1083 6 +640 1094 6 +644 846 26 + +# Yellow Potion +Static 0x0F0C +330 1254 6 +331 1254 6 +640 1082 6 +641 1094 6 +644 845 26 + +# Purple Potion +Static 0x0F0D +645 851 26 + +# empty bottle +Static 0x0F0E +644 837 26 +645 852 26 + +# arrows +Static 0x0F41 +616 1145 6 + +# crossbow +Static 0x0F4F +620 1146 6 + +# crossbow +Static 0x0F50 +616 1149 6 + +# easel with canvas +Static 0x0F68 +643 822 20 +643 825 20 +643 828 20 + +# Batwing +Static 0x0F78 +626 853 26 + +# Daemon Bone +Static 0x0F80 +624 845 26 + +# Pig Iron +Static 0x0F8A +624 846 26 +626 835 26 + +# Sulfurous Ash +# Nox Crystal +Static 0x0F8E +626 830 26 +626 852 26 + +# scissors +Scissors 0x0F9E +534 967 6 +535 973 6 + +# barrel +Static 0x0FAE +313 784 0 +313 784 7 +313 785 0 +313 785 7 +314 784 0 +314 784 7 +314 785 0 +314 786 0 +314 787 0 + +# book +Static 0x0FBD +280 980 4 +336 879 6 +336 879 26 +448 1107 6 +515 990 6 +530 970 6 +626 831 26 +626 836 26 +627 844 6 +627 851 6 +640 939 6 +643 851 6 +644 831 26 +644 836 26 + +# book +Static 0x0FBE +291 979 4 +319 787 26 +321 1055 6 +329 1247 6 +352 893 24 +352 894 4 +355 834 26 +355 898 26 +355 899 6 +359 893 24 +359 894 4 +403 1216 6 +459 1128 6 +474 842 6 +537 1011 6 +563 811 4 +636 816 26 +641 1087 6 + +# pen and ink +Static 0x0FBF +280 979 4 +290 979 4 +336 879 6 +336 879 26 +351 893 24 +351 894 4 +358 893 24 +358 894 4 +448 1107 6 +474 843 6 +515 990 6 +530 970 6 +536 1011 6 +564 810 4 +565 963 6 +626 831 26 +626 836 26 +627 844 6 +627 851 6 +640 939 6 +643 851 6 +644 831 26 +644 836 26 + +# pen and ink +Static 0x0FC0 +318 787 26 +320 1055 6 +329 1247 6 +354 834 26 +354 898 26 +355 899 6 +403 1216 6 +458 965 2 +459 1128 6 +636 816 26 +641 1087 6 + +# paints and brush +Static 0x0FC1 +643 826 20 +644 822 20 +644 827 20 + +# glass pitcher +Pitcher 0x0FF6 +643 842 4 + +# pewter mug +PewterMug 0x0FFF +419 1035 6 +450 963 6 +458 1135 6 + +# pewter mug +PewterMug 0x1000 +336 878 26 +336 880 6 +420 1034 6 +459 1133 6 +660 1003 6 +662 1002 6 +734 882 6 +736 883 6 + +# wash basin +Static 0x1008 +534 967 6 + +# archery butte +ArcheryButte 0x100A +560 972 0 + +# archery butte +ArcheryButte 0x100B +573 960 0 + +# hammer +Static 0x102A +563 966 6 + +# hammer +Static 0x102B +562 1009 6 +619 1146 6 + +# nails +Static 0x102E +562 1011 6 + +# nails +Static 0x102F +563 966 6 + +# saw +Static 0x1034 +622 1153 0 + +# saw +Static 0x1035 +616 1144 6 + +# sack of flour +Static 0x1039 +556 984 0 +557 984 0 + +# open sack of flour +Static 0x103A +544 810 0 +544 811 0 +544 817 0 +544 818 6 +544 819 6 +544 820 6 +544 823 0 +544 824 0 +544 826 6 +544 827 6 +545 821 0 +545 824 0 +545 828 0 +545 829 0 +546 808 0 +547 808 0 +548 808 0 +549 824 0 +550 816 0 +551 816 0 +552 814 0 +552 815 0 +554 814 6 +554 827 6 +554 828 0 +555 814 6 +555 827 6 +556 824 0 +556 825 0 +557 814 6 +557 815 0 +557 828 0 +558 814 0 +558 815 0 +560 816 0 +560 822 6 +562 821 0 +562 822 6 +563 821 0 +563 822 6 +565 816 0 + +# bread loaf +Static 0x103C +261 766 0 +261 766 20 +262 775 0 +262 775 20 +267 766 0 +267 766 20 +268 774 20 +268 775 0 +273 766 0 +273 766 20 +274 774 0 +274 775 20 +279 766 0 +279 766 20 +280 774 20 +280 775 0 +285 766 0 +285 766 20 +286 774 0 +286 774 20 + +# dough +Static 0x103D +554 988 6 +554 1221 6 +685 1202 6 + +# cookie mix +Static 0x103F +555 987 6 +556 1221 6 +685 1204 6 + +# rolling pin +Static 0x1043 +448 1117 6 +552 1221 6 +553 988 6 +685 1203 6 + +# clock +Clock 0x104C +631 1032 6 +632 1032 6 + +# clock frame +ClockFrame 0x104E +569 1008 6 +636 1032 6 + +# clock parts +ClockParts 0x104F +628 1035 8 + +# axle with gears +AxleGears 0x1051 +627 1035 6 + +# axle with gears +AxleGears 0x1052 +636 1032 8 + +# gears +Gears 0x1053 +626 1035 6 +637 1032 6 + +# sextant +Static 0x1057 +634 1034 7 + +# sextant +Static 0x1058 +629 1035 6 + +# sextant parts +SextantParts 0x1059 +625 1035 7 + +# sextant parts +SextantParts 0x105A +634 1033 6 + +# springs +Springs 0x105D +627 1035 8 +635 1032 8 + +# potted tree +PottedTree 0x11C8 +532 967 0 +532 973 0 + +# flowerpot +PottedPlant 0x11CA +328 1240 0 +448 1104 0 +534 1008 0 +538 967 0 +538 973 0 +552 1208 0 +568 1120 0 +640 936 0 +648 1080 0 +665 1008 0 +672 1200 0 +733 880 0 +737 880 0 + +# flowerpot +PottedPlant1 0x11CB +451 1110 6 +459 1135 9 +514 992 10 +530 972 6 +539 1011 9 +648 939 10 + +# flowerpot +PottedPlant2 0x11CC +562 966 9 +565 965 9 + +# bed +Static 0x11D2 +485 840 0 + +# bed +Static 0x11D3 +485 842 0 + +# bed +Static 0x11D4 +483 842 0 + +# bed +Static 0x11D5 +483 840 0 + +# altar +AbbatoirAddon 0x1216 +311 786 0 + +# teleporter +Teleporter 0x1BC3 (PointDest=(5750, 350, 5)) +311 786 0 + +# water barrel +Static 0x154D +618 882 0 + +#REMOVED WITH ML -> yew changed back to normal +# a glowing pod +#Static 0x1728 (Hue=0x5F; Name=a glowing pod) +#383 1009 28 +#383 1009 41 +#384 1008 32 +#384 1008 47 +#386 1005 28 +#386 1005 43 +#386 1006 28 +#565 969 28 +#565 969 41 +#566 968 32 +#566 968 47 +#568 965 28 +#568 965 43 +#568 966 28 +#648 913 28 +#648 913 41 +#649 912 32 +#649 912 47 +#651 909 28 +#651 909 43 +#651 910 28 + +# water +Static 0x1797 +463 1280 -5 +464 1280 -5 +465 1280 -5 +466 1280 -5 +467 1280 -5 +468 1280 -5 +469 1280 15 + +# water +Static 0x179A +469 1281 -5 +470 1281 -5 + +# alchemical symbol +Static 0x1822 +311 786 -24 + +# teleporter +Teleporter 0x1BC3 (PointDest=(314, 784, 0)) +311 786 -24 + +# flour mill +FlourMillSouthAddon 0x192C +550 820 -2 +552 820 -2 +554 820 -2 + +# shaft +Static 0x1BD4 +564 966 6 + +# boards +Static 0x1BD8 +623 1155 0 + +# logs +Static 0x1BE2 +616 1152 0 +617 1152 0 + +# Quarantine Area: No pets permitted beyond this point! +LocalizedSign 0x1F29 (LabelNumber=1016226) +315 786 -18 + +# Reactive Armor +Static 0x1F2D +624 841 0 +640 848 0 + +# Create Food +Static 0x1F2F +627 839 6 + +# Heal +Static 0x1F31 +624 837 0 +627 843 6 + +# Magic Untrap +Static 0x1F3A +624 832 0 + +# Protection +Static 0x1F3B +645 849 0 + +# Strength +Static 0x1F3C +625 848 0 + +# Arch Cure +Static 0x1F45 +627 852 6 + +# Curse +Static 0x1F47 +624 848 0 +643 853 0 + +# Dispel Field +Static 0x1F4E +627 850 6 + +# Mark +Static 0x1F59 +627 838 6 + +# Mass Dispel +Static 0x1F62 +627 835 6 + +# rock +Static 0x3426 +469 1280 -4 +470 1281 -4 + +# rock +Static 0x3427 +470 1280 -5 + +# rock +Static 0x3429 +469 1281 -4 + +#REMOVED WITH ML -> yew changed back to normal +# a glowing pod +#Static 0x3EE8 (Hue=0x5F; Name=a glowing pod) +#381 1008 57 +#381 1010 47 +#382 1007 65 +#383 1009 59 +#386 1004 39 +#386 1006 76 +#387 1004 53 +#563 968 57 +#563 970 47 +#564 967 65 +#565 969 59 +#568 964 39 +#568 966 76 +#569 964 53 +#646 912 57 +#646 914 47 +#647 911 65 +#648 913 59 +#651 908 39 +#651 910 76 +#652 908 53 \ No newline at end of file diff --git a/Data/Locations/felucca.xml b/Data/Locations/felucca.xml new file mode 100644 index 000000000..890b786e7 --- /dev/null +++ b/Data/Locations/felucca.xml @@ -0,0 +1,279 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/Locations/ilshenar.xml b/Data/Locations/ilshenar.xml new file mode 100644 index 000000000..ae6b0744a --- /dev/null +++ b/Data/Locations/ilshenar.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/Locations/malas.xml b/Data/Locations/malas.xml new file mode 100644 index 000000000..feb73db9d --- /dev/null +++ b/Data/Locations/malas.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/Locations/tokuno.xml b/Data/Locations/tokuno.xml new file mode 100644 index 000000000..ad28477bb --- /dev/null +++ b/Data/Locations/tokuno.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/Locations/trammel.xml b/Data/Locations/trammel.xml new file mode 100644 index 000000000..91815262d --- /dev/null +++ b/Data/Locations/trammel.xml @@ -0,0 +1,258 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/Regions.xml b/Data/Regions.xml new file mode 100644 index 000000000..691764d79 --- /dev/null +++ b/Data/Regions.xml @@ -0,0 +1,2149 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/SpawnDefinitions.xml b/Data/SpawnDefinitions.xml new file mode 100644 index 000000000..ca160c52a --- /dev/null +++ b/Data/SpawnDefinitions.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/bodyTable.cfg b/Data/bodyTable.cfg new file mode 100644 index 000000000..f46eb0a15 --- /dev/null +++ b/Data/bodyTable.cfg @@ -0,0 +1,966 @@ +########################################## +# +# Body type table +# +# Format: +# BodyID BodyType +# +# Types: +# Empty (default) +# Monster +# Sea +# Animal +# Human +# Equipment +# +########################################## + +1 Monster +2 Monster +3 Monster +4 Monster +5 Animal +6 Animal +7 Monster +8 Monster +9 Monster +10 Monster +11 Monster +12 Monster +13 Monster +14 Monster +15 Monster +16 Monster +17 Monster +18 Monster +19 Monster +20 Monster +21 Monster +22 Monster +23 Animal +24 Monster +25 Animal +26 Monster +27 Animal +28 Monster +29 Animal +30 Monster +31 Monster +33 Monster +34 Animal +35 Monster +36 Monster +37 Animal +38 Monster +39 Monster +40 Monster +41 Monster +42 Monster +43 Monster +44 Monster +45 Monster +46 Monster +47 Monster +48 Monster +49 Monster +50 Monster +51 Monster +52 Animal +53 Monster +54 Monster +55 Monster +56 Monster +57 Monster +58 Monster +59 Monster +60 Monster +61 Monster +62 Monster +63 Animal +64 Animal +65 Animal +66 Monster +67 Monster +68 Monster +69 Monster +70 Monster +71 Monster +72 Monster +73 Monster +74 Monster +75 Monster +76 Monster +77 Monster +78 Monster +79 Monster +80 Monster +81 Animal +82 Monster +83 Monster +84 Monster +85 Monster +86 Monster +87 Monster +88 Animal +89 Monster +90 Monster +91 Monster +92 Monster +93 Monster +94 Monster +96 Monster +97 Animal +98 Animal +99 Animal +100 Animal +101 Monster +102 Monster +103 Monster +104 Monster +106 Monster +107 Monster +108 Monster +109 Monster +110 Monster +111 Monster +112 Monster +113 Monster +114 Animal +115 Animal +116 Animal +117 Animal +118 Animal +119 Animal +120 Animal +121 Animal +122 Animal +123 Monster +124 Monster +125 Monster +126 Monster +127 Animal +128 Monster +129 Monster +130 Monster +131 Monster +132 Animal +133 Animal +134 Animal +135 Monster +136 Monster +137 Monster +138 Monster +139 Monster +140 Monster +141 Monster +142 Monster +143 Monster +144 Sea +145 Sea +146 Monster +147 Monster +148 Monster +149 Monster +150 Sea +151 Sea +152 Monster +153 Monster +154 Monster +155 Monster +157 Monster +158 Monster +159 Monster +160 Monster +161 Monster +162 Monster +163 Monster +164 Monster +165 Monster +166 Monster +167 Animal +168 Monster +170 Animal +171 Animal +172 Monster +173 Monster +174 Monster +175 Monster +176 Monster +177 Animal +178 Animal +179 Animal +180 Monster +181 Monster +182 Monster +183 Human +184 Human +185 Human +186 Human +187 Animal +188 Animal +189 Monster +190 Animal +191 Animal +192 Animal +193 Animal +194 Animal +195 Animal +200 Animal +201 Animal +202 Animal +203 Animal +204 Animal +205 Animal +206 Monster +207 Animal +208 Animal +209 Animal +210 Animal +211 Animal +212 Animal +213 Animal +214 Animal +215 Monster +216 Animal +217 Animal +218 Animal +219 Animal +220 Animal +221 Animal +223 Animal +225 Animal +226 Animal +228 Animal +231 Animal +232 Animal +233 Animal +234 Animal +237 Animal +238 Animal +268 Equipment +281 Equipment +283 Equipment +290 Animal +291 Animal +292 Animal +300 Monster +301 Monster +302 Monster +303 Monster +304 Monster +305 Monster +306 Monster +307 Monster +308 Monster +309 Monster +310 Monster +311 Monster +312 Monster +313 Monster +314 Monster +315 Monster +316 Monster +317 Monster +318 Monster +319 Monster +#325 Equipment +349 Equipment +400 Human +401 Human +402 Human +403 Human +404 Equipment +405 Equipment +406 Equipment +407 Equipment +408 Equipment +409 Equipment +410 Equipment +411 Equipment +412 Equipment +413 Equipment +414 Equipment +415 Equipment +416 Equipment +417 Equipment +418 Equipment +419 Equipment +430 Equipment +431 Equipment +434 Equipment +435 Equipment +447 Equipment +448 Equipment +449 Equipment +455 Equipment +465 Equipment +466 Equipment +468 Equipment +469 Equipment +476 Equipment +477 Equipment +479 Equipment +480 Equipment +481 Equipment +482 Equipment +483 Equipment +484 Equipment +485 Equipment +486 Equipment +490 Equipment +492 Equipment +500 Equipment +501 Equipment +502 Equipment +503 Equipment +504 Equipment +505 Equipment +521 Equipment +523 Equipment +525 Equipment +527 Equipment +528 Equipment +529 Equipment +530 Equipment +531 Equipment +538 Equipment +540 Equipment +541 Equipment +542 Equipment +543 Equipment +544 Equipment +545 Equipment +546 Equipment +548 Equipment +549 Equipment +550 Equipment +554 Equipment +555 Equipment +556 Equipment +557 Equipment +560 Equipment +561 Equipment +562 Equipment +563 Equipment +564 Equipment +565 Equipment +566 Equipment +574 Monster +576 Equipment +577 Equipment +578 Equipment +579 Equipment +580 Equipment +581 Equipment +582 Equipment +584 Equipment +585 Equipment +586 Equipment +587 Equipment +588 Equipment +589 Equipment +590 Equipment +591 Equipment +592 Equipment +593 Equipment +594 Equipment +595 Equipment +596 Equipment +597 Equipment +598 Equipment +599 Equipment +600 Equipment +601 Equipment +602 Equipment +603 Equipment +604 Equipment +611 Equipment +612 Equipment +613 Equipment +614 Equipment +615 Equipment +616 Equipment +617 Equipment +618 Equipment +619 Equipment +620 Equipment +621 Equipment +622 Equipment +623 Equipment +624 Equipment +625 Equipment +626 Equipment +627 Equipment +628 Equipment +629 Equipment +630 Equipment +631 Equipment +633 Equipment +635 Equipment +636 Equipment +637 Equipment +638 Equipment +639 Equipment +643 Equipment +644 Equipment +645 Equipment +646 Equipment +648 Equipment +649 Equipment +651 Equipment +653 Equipment +654 Equipment +655 Equipment +656 Equipment +657 Equipment +658 Equipment +659 Equipment +660 Equipment +661 Equipment +662 Equipment +663 Equipment +664 Equipment +665 Equipment +666 Equipment +667 Equipment +668 Equipment +669 Equipment +670 Equipment +671 Equipment +672 Equipment +674 Equipment +675 Equipment +676 Equipment +677 Equipment +678 Equipment +679 Equipment +680 Equipment +681 Equipment +682 Equipment +683 Equipment +687 Equipment +688 Equipment +690 Equipment +691 Equipment +692 Equipment +693 Equipment +694 Equipment +695 Equipment +696 Equipment +697 Equipment +698 Equipment +699 Equipment +700 Equipment +701 Equipment +702 Equipment +703 Equipment +710 Equipment +712 Equipment +713 Equipment +715 Equipment +716 Equipment +720 Equipment +721 Equipment +722 Equipment +723 Equipment +724 Equipment +725 Equipment +726 Equipment +727 Equipment +728 Equipment +729 Equipment +730 Equipment +731 Equipment +732 Equipment +733 Equipment +734 Equipment +735 Equipment +736 Equipment +740 Equipment +742 Equipment +743 Equipment +744 Human +745 Human +746 Monster +747 Monster +748 Monster +749 Monster +750 Human +751 Human +752 Monster +753 Monster +754 Monster +755 Monster +756 Monster +757 Monster +758 Monster +759 Equipment +760 Equipment +761 Equipment +762 Equipment +763 Monster +764 Monster +765 Monster +766 Monster +767 Monster +768 Monster +769 Monster +770 Monster +771 Monster +772 Monster +773 Monster +774 Monster +775 Monster +776 Monster +777 Monster +778 Monster +779 Monster +780 Monster +781 Monster +782 Monster +783 Monster +784 Monster +785 Monster +786 Monster +787 Monster +788 Monster +789 Monster +790 Monster +791 Animal +792 Monster +793 Animal +794 Animal +795 Monster +796 Monster +797 Monster +798 Monster +799 Animal +800 Equipment +801 Equipment +802 Equipment +803 Equipment +804 Monster +805 Monster +806 Monster +807 Monster +808 Monster +820 Equipment +824 Equipment +825 Equipment +826 Equipment +827 Equipment +828 Equipment +846 Equipment +848 Equipment +900 Equipment +901 Equipment +902 Equipment +903 Equipment +904 Equipment +905 Equipment +906 Equipment +909 Equipment +910 Equipment +911 Equipment +912 Equipment +913 Equipment +921 Equipment +922 Equipment +923 Equipment +924 Equipment +925 Equipment +926 Equipment +927 Equipment +928 Equipment +929 Equipment +930 Equipment +931 Equipment +932 Equipment +933 Equipment +934 Equipment +935 Equipment +936 Equipment +937 Equipment +938 Equipment +939 Equipment +971 Equipment +972 Equipment +973 Equipment +980 Equipment +981 Equipment +982 Equipment +983 Equipment +984 Equipment +985 Equipment +986 Equipment +987 Human +988 Human +990 Human +991 Human +992 Equipment +993 Equipment +994 Human +998 Monster +999 Monster +1268 Equipment +1281 Equipment +1283 Equipment +1325 Equipment +1349 Equipment +1404 Equipment +1405 Equipment +1406 Equipment +1407 Equipment +1408 Equipment +1409 Equipment +1410 Equipment +1411 Equipment +1412 Equipment +1413 Equipment +1414 Equipment +1415 Equipment +1416 Equipment +1417 Equipment +1418 Equipment +1419 Equipment +1430 Equipment +1431 Equipment +1434 Equipment +1435 Equipment +1447 Equipment +1448 Equipment +1449 Equipment +1455 Equipment +1465 Equipment +1466 Equipment +1468 Equipment +1469 Equipment +1476 Equipment +1477 Equipment +1479 Equipment +1480 Equipment +1481 Equipment +1482 Equipment +1483 Equipment +1484 Equipment +1485 Equipment +1486 Equipment +1490 Equipment +1492 Equipment +1521 Equipment +1523 Equipment +1525 Equipment +1527 Equipment +1528 Equipment +1529 Equipment +1530 Equipment +1531 Equipment +1538 Equipment +1540 Equipment +1542 Equipment +1543 Equipment +1544 Equipment +1545 Equipment +1546 Equipment +1548 Equipment +1549 Equipment +1550 Equipment +1554 Equipment +1555 Equipment +1556 Equipment +1557 Equipment +1560 Equipment +1561 Equipment +1562 Equipment +1563 Equipment +1564 Equipment +1565 Equipment +1566 Equipment +1672 Equipment +1674 Equipment +1675 Equipment +1676 Equipment +1677 Equipment +1678 Equipment +1679 Equipment +1680 Equipment +1681 Equipment +1682 Equipment +1683 Equipment +1684 Equipment +1685 Equipment +1686 Equipment +1687 Equipment +1688 Equipment +1690 Equipment +1691 Equipment +1692 Equipment +1693 Equipment +1694 Equipment +1695 Equipment +1696 Equipment +1697 Equipment +1698 Equipment +1699 Equipment +1700 Equipment +1701 Equipment +1702 Equipment +1703 Equipment +1710 Equipment +1712 Equipment +1713 Equipment +1715 Equipment +1716 Equipment +1720 Equipment +1721 Equipment +1722 Equipment +1723 Equipment +1724 Equipment +1725 Equipment +1726 Equipment +1727 Equipment +1728 Equipment +1729 Equipment +1730 Equipment +1731 Equipment +1732 Equipment +1733 Equipment +1734 Equipment +1735 Equipment +1736 Equipment +1740 Equipment +1742 Equipment +1743 Equipment +1759 Equipment +1761 Equipment +1762 Equipment +1900 Equipment +1901 Equipment +1902 Equipment +1903 Equipment +1909 Equipment +1910 Equipment +1911 Equipment +1912 Equipment +1913 Equipment +1921 Equipment +1922 Equipment +1923 Equipment +1924 Equipment +1925 Equipment +1926 Equipment +1927 Equipment +1928 Equipment +1929 Equipment +1971 Equipment +1973 Equipment + +# SE Creatures +242 Monster +247 Monster +240 Monster +252 Monster +241 Monster +244 Monster +255 Monster +253 Monster +245 Monster +250 Monster +251 Monster +249 Monster +254 Animal +243 Animal +246 Animal +248 Animal +196 Monster +199 Monster +169 Animal + +# SE Equipment +425 Equipment +426 Equipment +423 Equipment +424 Equipment +442 Equipment +443 Equipment +444 Equipment +445 Equipment +446 Equipment +450 Equipment +451 Equipment +456 Equipment +457 Equipment +458 Equipment +459 Equipment +460 Equipment +461 Equipment +462 Equipment +463 Equipment +464 Equipment +467 Equipment +470 Equipment +471 Equipment +472 Equipment +473 Equipment +474 Equipment +478 Equipment +487 Equipment +488 Equipment +489 Equipment +491 Equipment +506 Equipment +512 Equipment +513 Equipment +514 Equipment +516 Equipment +517 Equipment +518 Equipment +519 Equipment +520 Equipment +526 Equipment +532 Equipment +533 Equipment +534 Equipment +535 Equipment +536 Equipment +537 Equipment +539 Equipment +547 Equipment +551 Equipment +552 Equipment +553 Equipment +558 Equipment +559 Equipment +567 Equipment +568 Equipment +569 Equipment +570 Equipment +571 Equipment +572 Equipment +575 Equipment +583 Equipment +809 Equipment +810 Equipment +811 Equipment +812 Equipment +813 Equipment +814 Equipment +815 Equipment +816 Equipment +817 Equipment +818 Equipment +819 Equipment +821 Equipment +822 Equipment +823 Equipment +833 Equipment +834 Equipment +835 Equipment +836 Equipment +837 Equipment +838 Equipment +839 Equipment +840 Equipment +841 Equipment +842 Equipment + + +# ep1 Equipment +942 Equipment +943 Equipment +944 Equipment +945 Equipment +946 Equipment +947 Equipment +948 Equipment +950 Equipment +951 Equipment +952 Equipment +953 Equipment +954 Equipment +955 Equipment +956 Equipment +957 Equipment +958 Equipment +959 Equipment +960 Equipment +961 Equipment +962 Equipment +963 Equipment +964 Equipment +965 Equipment +966 Equipment +967 Equipment +968 Equipment +969 Equipment +974 Equipment +975 Equipment +976 Equipment +977 Equipment +#Added by JP +920 Equipment +907 Equipment +908 Equipment +914 Equipment +915 Equipment +941 Equipment +896 Equipment +897 Equipment +898 Equipment +899 Equipment +886 Equipment +916 Equipment +917 Equipment +918 Equipment +919 Equipment +978 Equipment +979 Equipment +995 Equipment +996 Equipment +989 Equipment +997 Equipment +890 Equipment #More Hair +891 Equipment #More Hair +892 Equipment #More Hair +893 Equipment #More Hair +894 Equipment #More Hair +895 Equipment #More Hair +889 Equipment #Elf Male Hair 11 + + + +#ep1 races +605 Human # elf male +606 Human # elf female +607 Human # elf male ghost +608 Human # elf female ghost + +#ep1 creatures +256 Monster # Chief Paroxysmus +257 Monster # Dread Horn +258 Monster # Lady M +259 Monster # Monstrous Interred Grizzle +260 Monster # Travesty +261 Monster # Shimmering Effusion +262 Monster # Tormented Minotaur +263 Monster # Minotaur +264 Monster # Changeling +265 Monster # Hydra +266 Monster # Dryad +267 Monster # Troglodyte +269 Monster # Thorn bat +270 Monster # Bulbous Putrification +271 Monster # Satyr +272 Monster # Interred Grizzle +273 Monster # Fetid Essence + +276 Animal # Chimera +277 Animal # Cu Sidhe +278 Animal # Squirrel +279 Animal # Ferret +280 Monster # Plate Armored Minotaur +281 Monster # Leather Armored Minotaur +282 Animal # Parrot +283 Animal # Crow +284 Animal # Mondains Steed +285 Monster # Reaper Form diff --git a/Data/containers.cfg b/Data/containers.cfg new file mode 100644 index 000000000..ad0aa557e --- /dev/null +++ b/Data/containers.cfg @@ -0,0 +1,45 @@ +# All lines are trimmed. Empty lines and lines starting with '#' are ignored +# Container data table +# Format: +# GumpIDRectX RectY RectWidth RectHeightDropSound[ItemID1,ItemID2,...,ItemIDN] + +# Default: +0x3C 44 65 142 94 0x48 + +# Containers +0x9 20 85 104 111 0x42 0x2006,0xECA,0xECB,0xECC,0xECD,0xECE,0xECF,0xED0,0xED1,0xED2 +0x3D 29 34 108 94 0x48 0xE76,0x2256,0x2257 +0x3E 33 36 109 112 0x42 0xE77,0xE7F +0x3F 19 47 163 76 0x4F 0xE7A,0x24D5,0x24D6,0x24D9,0x24DA +0x41 35 38 110 78 0x4F 0x990,0x9AC,0x9B1,0x24D7,0x24D8,0x24DD +0x42 18 105 144 73 0x42 0xE40,0xE41 +0x43 16 51 168 73 0x42 0xE7D,0x9AA +0x44 20 10 150 90 0x42 0xE7E,0x9A9,0xE3C,0xE3D,0xE3E,0xE3F +0x48 76 12 64 56 0x42 0xA30,0xA38 +0x49 18 105 144 73 0x42 0xE42,0xE43 +0x4A 18 105 144 73 0x42 0xE7C,0x9AB +0x4B 16 51 168 73 0x42 0xE80,0x9A8 +0x4C 46 74 150 110 0x42 0x3E65,0x3E93,0x3EAE,0x3EB9 +0x4D 76 12 64 56 0x42 0xA97,0xA98,0xA99,0xA9A,0xA9B,0xA9C,0xA9D,0xA9E +0x4E 24 96 74 56 0x42 0xA4C,0xA4D,0xA50,0xA51 +0x4F 24 96 74 56 0x42 0xA4E,0xA4F,0xA52,0xA53 +0x51 16 10 138 84 0x42 0xA2C,0xA34 +0x52 0 0 110 62 0x42 0x1E5E + +# Game Boards +0x91A 0 0 282 210 -1 0xFA6 +0x92E 0 0 282 230 -1 0xE1C,0xFAD + +# Gift Box +0x102 35 10 155 85 0x42 0x232A,0x232B + +# SE Containers +0x105 10 10 150 95 0x42 0x2857,0x2858 +0x106 10 10 150 95 0x42 0x285B,0x285C +0x107 10 10 150 95 0x42 0x285D,0x285E,0x2859,0x285A +0x108 10 10 116 71 0x4F 0x24DB,0x24DC +0x109 10 10 150 95 0x42 0x280B,0x280C +0x10A 10 10 150 95 0x42 0x280F,0x2810 +0x10B 10 10 150 95 0x42 0x280D,0x280E +0x10C 10 10 150 95 0x42 0x2811,0x2812,0x2815,0x2816,0x2817,0x2818 +0x10D 10 10 150 95 0x42 0x2813,0x2814 \ No newline at end of file diff --git a/Data/items.cfg b/Data/items.cfg new file mode 100644 index 000000000..96def1bc4 --- /dev/null +++ b/Data/items.cfg @@ -0,0 +1,143 @@ +Items (Item) + + Bods & Rewards (SmallBOD; LargeBOD; BulkOrderBook; SturdyPickaxe; SturdyShovel; BaseGlovesOfMining; GargoylesPickaxe; ProspectorsTool; PowderOfTemperament; ColoredAnvil; RunicHammer; AncientSmithyHammer; SmallStretchedHideEastDeed; SmallStretchedHideEastAddon; SmallStretchedHideSouthDeed; SmallStretchedHideSouthAddon; MediumStretchedHideEastDeed; MediumStretchedHideEastAddon; MediumStretchedHideSouthDeed; MediumStretchedHideSouthAddon; LightFlowerTapestryEastDeed; LightFlowerTapestryEastAddon; LightFlowerTapestrySouthDeed; LightFlowerTapestrySouthAddon; DarkFlowerTapestryEastDeed; DarkFlowerTapestryEastAddon; DarkFlowerTapestrySouthDeed; DarkFlowerTapestrySouthAddon; BrownBearRugEastDeed; BrownBearRugEastAddon; BrownBearRugSouthDeed; BrownBearRugSouthAddon; PolarBearRugEastDeed; PolarBearRugEastAddon; PolarBearRugSouthDeed; PolarBearRugSouthAddon; ClothingBlessDeed; RunicSewingKit) + + Smithing (SmallSmithBOD; LargeSmithBOD; SturdyPickaxe; SturdyShovel; BaseGlovesOfMining; GargoylesPickaxe; ProspectorsTool; PowderOfTemperament; ColoredAnvil; RunicHammer; AncientSmithyHammer) + + Tailoring (SmallTailorBOD; LargeTailorBOD; SmallStretchedHideEastDeed; SmallStretchedHideEastAddon; SmallStretchedHideSouthDeed; SmallStretchedHideSouthAddon; MediumStretchedHideEastDeed; MediumStretchedHideEastAddon; MediumStretchedHideSouthDeed; MediumStretchedHideSouthAddon; LightFlowerTapestryEastDeed; LightFlowerTapestryEastAddon; LightFlowerTapestrySouthDeed; LightFlowerTapestrySouthAddon; DarkFlowerTapestryEastDeed; DarkFlowerTapestryEastAddon; DarkFlowerTapestrySouthDeed; DarkFlowerTapestrySouthAddon; BrownBearRugEastDeed; BrownBearRugEastAddon; BrownBearRugSouthDeed; BrownBearRugSouthAddon; PolarBearRugEastDeed; PolarBearRugEastAddon; PolarBearRugSouthDeed; PolarBearRugSouthAddon; ClothingBlessDeed; RunicSewingKit) + + Addons (BaseAddon; BaseAddonDeed) + + Constructed (BaseAddon) + + Deeds (BaseAddonDeed) + + Wearables (BaseArmor; BaseClothing; BaseSuit; BaseJewel; BaseWeapon) + + Armor (BaseArmor) + + Artifacts (ArmorOfFortune; GauntletsOfNobility; HelmOfInsight; HolyKnightsBreastplate; InquisitorsResolution; JackalsCollar; LeggingsOfBane; MidnightBracers; OrnateCrownOfTheHarrower; ShadowDancerLeggings; TunicOfFire; VoiceOfTheFallenKing) + + Bone (BoneArms; BoneChest; BoneGloves; BoneLegs; BoneHelm) + + Chain (ChainChest; ChainLegs; ChainCoif) + + Daemon Bone (DaemonArms; DaemonChest; DaemonGloves; DaemonLegs; DaemonHelm) + + Dragon (DragonArms; DragonChest; DragonGloves; DragonLegs; DragonHelm) + + Helmets (Bascinet; CloseHelm; Helmet; NorseHelm; OrcHelm) + + Leather (FemaleLeatherChest; LeatherArms; LeatherBustierArms; LeatherChest; LeatherGloves; LeatherGorget; LeatherLegs; LeatherShorts; LeatherSkirt; LeatherCap) + + Plate (FemalePlateChest; PlateArms; PlateChest; PlateGloves; PlateGorget; PlateLegs; PlateHelm) + + Ranger (RangerArms; RangerChest; RangerGloves; RangerGorget; RangerLegs) + + Ring (RingmailArms; RingmailChest; RingmailGloves; RingmailLegs) + + Studded Leather (FemaleStuddedChest; StuddedArms; StuddedBustierArms; StuddedChest; StuddedGloves; StuddedGorget; StuddedLegs) + + Shields (BaseShield) + + Artifacts (Aegis; ArcaneShield) + + Weapons (BaseWeapon) + + Artifacts (AxeOfTheHeavens; BladeOfInsanity; BladeOfTheRighteous; BoneCrusher; BreathOfTheDead; Frostbringer; LegacyOfTheDreadLord; SerpentsFang; StaffOfTheMagi; TheBeserkersMaul; TheDragonSlayer; TheTaskmaster; TitansHammer; ZyronicClaw) + + Wands (BaseWand) + + Axes (BaseAxe) + + Knives (BaseKnife) + + Bashing (BaseBashing) + + Pole Arms (BasePoleArm) + + Ranged (BaseRanged) + + Spears (BaseSpear) + + Staves (BaseStaff) + + Swords (BaseSword) + + Clothing (BaseClothing) + + Cloaks (BaseCloak) + + Hats (BaseHat) + + Robes (BaseOuterTorso) + + Shirts (BaseShirt) + + Pants (BasePants; BaseOuterLegs) + + Shoes (BaseShoes) + + Misc (BaseWaist; BaseMiddleTorso) + + Jewelry (BaseJewel) + + Artifacts (BraceletOfHealth; OrnamentOfTheMagician; RingOfTheElements; RingOfTheVile) + + Suits (BaseSuit) + + Addons (BaseAddonDeed; BaseAddon) + + Constructed (BaseAddon) + + Smithing (LargeForgeEastAddon; LargeForgeSouthAddon; SmallForgeAddon; AnvilEastAddon; AnvilSouthAddon) + + Tailoring (LoomEastAddon; LoomSouthAddon; SpinningWheelEastAddon; SpinningWheelSouthAddon) + + Cooking (FlourMillEastAddon; FlourMillSouthAddon; StoneOvenEastAddon; StoneOvenSouthAddon) + + Bedding (LargeBedEastAddon; LargeBedSouthAddon; SmallBedSouthAddon; SmallBedEastAddon) + + Training (TrainingDummyEastAddon; TrainingDummySouthAddon; PickpocketDipEastAddon; PickpocketDipSouthAddon; ArcheryButteAddon) + + Spiritual (PentagramAddon; BloodPentagram; AbbatoirAddon) + + Furniture (LargeStoneTableEastAddon; LargeStoneTableSouthAddon; MediumStoneTableEastAddon; MediumStoneTableSouthAddon) + + Misc (BaseAddon) + + Deeds (BaseAddonDeed) + + Smithing (LargeForgeEastDeed; LargeForgeSouthDeed; SmallForgeDeed; AnvilEastDeed; AnvilSouthDeed) + + Tailoring (LoomEastDeed; LoomSouthDeed; SpinningWheelEastDeed; SpinningWheelSouthDeed) + + Cooking (FlourMillEastDeed; FlourMillSouthDeed; StoneOvenEastDeed; StoneOvenSouthDeed) + + Bedding (LargeBedEastDeed; LargeBedSouthDeed; SmallBedEastDeed; SmallBedSouthDeed) + + Training (TrainingDummyEastDeed; TrainingDummySouthDeed; PickpocketDipEastDeed; PickpocketDipSouthDeed; ArcheryButteDeed) + + Spiritual (PentagramDeed; AbbatoirDeed) + + Furniture (LargeStoneTableEastDeed; LargeStoneTableSouthDeed; MediumStoneTableEastDeed; MediumStoneTableSouthDeed) + + Misc (BaseAddonDeed) + + Books (BaseBook) + + Resources (BaseIngot; BaseOre; BaseGranite; BaseScales; BaseReagent; BaseHides; BaseLeather; Log; Board; BaseClothMaterial; Bone; UncutCloth; BoltOfCloth; Cloth; Wool; Flax; Cotton; BagOfAllReagents; BagOfNecroReagents; BagOfReagents; AlchemyBag; BagOfIngots; ScribeBag; SmithBag; TailorBag; Fish; BigFish; BaseMagicFish; Arrow; Bolt; Feather; Shaft; AlchemyStone; IngotStone; RegStone; ScribeStone; SmithStone; TailorStone; Sand) + + Ingots (BaseIngot) + + Ore (BaseOre) + + Granite (BaseGranite) + + Scales (BaseScales) + + Reagents (BaseReagent) + + Standard (BlackPearl; Bloodmoss; Garlic; Ginseng; MandrakeRoot; Nightshade; SpidersSilk; SulfurousAsh) + + Necromancy (BatWing; DaemonBlood; GraveDust; NoxCrystal; PigIron) + + Wood (Log; Board) + + Fish (Fish; BigFish; BaseMagicFish) + + Tailoring (BaseHides; BaseLeather; BaseClothMaterial; Bone; BoltOfCloth; Cloth; UncutCloth; Wool; Flax; Cotton) + + Hides (BaseHides) + + Leather (BaseLeather) + + Cloth (BoltOfCloth; Cloth; UncutCloth) + + Materials (BaseClothMaterial; Wool; Flax; Cotton) + + Fletching (Arrow; Bolt; Feather; Shaft) + + Bags (BagOfAllReagents; BagOfNecroReagents; BagOfReagents; AlchemyBag; BagOfIngots; ScribeBag; SmithBag; TailorBag) + + Stones (AlchemyStone; IngotStone; RegStone; ScribeStone; SmithStone; TailorStone) + + Entertainment (BaseInstrument; BaseBoard; Dices) + + Instruments (BaseInstrument) + + Games (BaseBoard; Dices) + + Construction (BaseDoor; BaseFloor; Container; DecorativeShield1; DecorativeShield2; DecorativeShield3; DecorativeShield4; DecorativeShield5; DecorativeShield6; DecorativeShield7; DecorativeShield8; DecorativeShield9; DecorativeShield10; DecorativeShield11; DecorativeShieldSword1North; DecorativeShieldSword1West; DecorativeShieldSword2North; DecorativeShieldSword2West; DecorativeBowWest; DecorativeBowNorth; DecorativeAxeNorth; DecorativeAxeWest; DecorativeSwordNorth; DecorativeSwordWest; DecorativeDAxeNorth; DecorativeDAxeWest; LargePainting; WomanPortrait1; WomanPortrait2; ManPortrait1; ManPortrait2; LadyPortrait1; LadyPortrait2; Tapestry1N; Tapestry2N; Tapestry2W; Tapestry3N; Tapestry3W; Tapestry4N; Tapestry4W; Tapestry5N; Tapestry5W; Tapestry6N; Tapestry6W; RuinedFallenChairA; RuinedArmoire; RuinedBookcase; RuinedBooks; CoveredChair; RuinedFallenChairB; RuinedChair; RuinedClock; RuinedDrawers; RuinedPainting; WoodDebris; Rope; IronWire; SilverWire; GoldWire; CopperWire; Whip; PaintsAndBrush; WoodenBench; FancyWoodenChairCushion; WoodenChairCushion; WoodenChair; BambooChair; Stool; FootStool; Throne; WoodenThrone; LargeTable; Nightstand; YewWoodTable; WritingTable; Easle; TallMusicStand; ShortMusicStand) + + Decoration (DecorativeShield1; DecorativeShield2; DecorativeShield3; DecorativeShield4; DecorativeShield5; DecorativeShield6; DecorativeShield7; DecorativeShield8; DecorativeShield9; DecorativeShield10; DecorativeShield11; DecorativeShieldSword1North; DecorativeShieldSword1West; DecorativeShieldSword2North; DecorativeShieldSword2West; DecorativeBowWest; DecorativeBowNorth; DecorativeAxeNorth; DecorativeAxeWest; DecorativeSwordNorth; DecorativeSwordWest; DecorativeDAxeNorth; DecorativeDAxeWest; LargePainting; WomanPortrait1; WomanPortrait2; ManPortrait1; ManPortrait2; LadyPortrait1; LadyPortrait2; Tapestry1N; Tapestry2N; Tapestry2W; Tapestry3N; Tapestry3W; Tapestry4N; Tapestry4W; Tapestry5N; Tapestry5W; Tapestry6N; Tapestry6W) + + Shields (DecorativeShield1; DecorativeShield2; DecorativeShield3; DecorativeShield4; DecorativeShield5; DecorativeShield6; DecorativeShield7; DecorativeShield8; DecorativeShield9; DecorativeShield10; DecorativeShield11; DecorativeShieldSword1North; DecorativeShieldSword1West; DecorativeShieldSword2North; DecorativeShieldSword2West) + + Weapons (DecorativeBowWest; DecorativeBowNorth; DecorativeAxeNorth; DecorativeAxeWest; DecorativeSwordNorth; DecorativeSwordWest; DecorativeDAxeNorth; DecorativeDAxeWest) + + Paintings & Portraits (LargePainting; WomanPortrait1; WomanPortrait2; ManPortrait1; ManPortrait2; LadyPortrait1; LadyPortrait2) + + Tapestries (Tapestry1N; Tapestry2N; Tapestry2W; Tapestry3N; Tapestry3W; Tapestry4N; Tapestry4W; Tapestry5N; Tapestry5W; Tapestry6N; Tapestry6W) + + Broken (RuinedFallenChairA; RuinedArmoire; RuinedBookcase; RuinedBooks; CoveredChair; RuinedFallenChairB; RuinedChair; RuinedClock; RuinedDrawers; RuinedPainting; WoodDebris) + + Rares (Rope; IronWire; SilverWire; GoldWire; CopperWire; Whip; PaintsAndBrush) + + Doors (BaseDoor) + + Floors (BaseFloor) + + Containers (Container) + + Furniture (WoodenBench; FancyWoodenChairCushion; WoodenChairCushion; WoodenChair; BambooChair; Stool; FootStool; Throne; WoodenThrone; LargeTable; Nightstand; YewWoodTable; WritingTable; Easle; TallMusicStand; ShortMusicStand) + + Chairs (WoodenBench; FancyWoodenChairCushion; WoodenChairCushion; WoodenChair; BambooChair; Stool; FootStool; Throne; WoodenThrone) + + Tables (LargeTable; Nightstand; YewWoodTable; WritingTable) + + Other (Easle; TallMusicStand; ShortMusicStand) + + Lights (BaseLight) + + Food & Drink (Food; CookableFood; BaseBeverage; Glass) + + Ready to eat (Food) + + Needs cooking (CookableFood) + + Beverages (BaseBeverage; Glass) + + Cartography (MapItem) + + Hair (Hair; Beard) + + Stonecraft (Vase; LargeVase; StatueSouth; StatueNorth; StatueEast; StatuePegasus; StoneChair) + + Glassblowing (SmallFlask; MediumFlask; LargeFlask; CurvedFlask; LongFlask; SpinningHourglass; GreenBottle; RedBottle; SmallBrownBottle; SmallVioletBottle; TinyYellowBottle; SmallBlueFlask; SmallYellowFlask; SmallRedFlask; SmallEmptyFlask; YellowBeaker; RedBeaker; BlueBeaker; GreenBeaker; EmptyCurvedFlaskW; RedCurvedFlask; LtBlueCurvedFlask; EmptyCurvedFlaskE; BlueCurvedFlask; GreenCurvedFlask; RedRibbedFlask; VioletRibbedFlask; EmptyRibbedFlask; LargeYellowFlask; LargeVioletFlask; LargeEmptyFlask; AniRedRibbedFlask; AniLargeVioletFlask; AniSmallBlueFlask; SmallBlueBottle; SmallGreenBottle; EmptyVIalsWRack; FullVialsWRack; EmptyJar; FullJar; HalfEmptyJar; VioletStemmedBottle; EmptyJars3; EmptyJars4; FullJars3; FullJars4; EmptyJars2; FullJars2; HalfEmptyJars2; EmptyVial; HourglassAni; Hourglass; TinyRedBottle; SmallGreenBottle2; Glass) + + Pots & Plants (SmallEmptyPot; LargeEmptyPot; PottedCactus; PottedCactus1; PottedCactus2; PottedCactus3; PottedCactus4; PottedCactus5; PottedPlant; PottedPlant1; PottedPlant2; PottedTree; PottedTree1) + + Components (BarrelLid; BarrelStaves; BarrelHoops; BarrelTap; Gears; Springs; Hinge; Axle; AxleGears; SextantParts; ClockParts; ClockFrame; Clock; ClockRight; ClockLeft) + + Tools (BaseTool; BaseHarvestTool) + + Crafting (BaseTool) + + Harvesting (BaseHarvestTool) + + Traps (BaseTrap) + + Gems (Amber; Amethyst; Citrine; Diamond; Emerald; Ruby; Sapphire; StarSapphire; Tourmaline) + + Boats (BaseBoat; BaseBoatDeed) + + Constructed (BaseBoat) + + Deeds (BaseBoatDeed) + + House Deeds (HouseDeed; HousePlacementTool) + + Camps (BaseCamp) + + Magical (Spellbook; SpellScroll; BlankScroll; BasePotion; EtherealMount) + + Scrolls (SpellScroll; BlankScroll) + + Necromancy (AnimateDeadScroll; BloodOathScroll; CorpseSkinScroll; CurseWeaponScroll; EvilOmenScroll; HorrificBeastScroll; LichFormScroll; MindRotScroll; PainSpikeScroll; PoisonStrikeScroll; StrangleScroll; SummonFamiliarScroll; VampiricEmbraceScroll; VengefulSpiritScroll; WitherScroll; WraithFormScroll) + + Standard (SpellScroll) + + Circle #1 (ClumsyScroll; CreateFoodScroll; FeeblemindScroll; HealScroll; MagicArrowScroll; NightSightScroll; ReactiveArmorScroll; WeakenScroll) + + Circle #2 (AgilityScroll; CunningScroll; CureScroll; HarmScroll; MagicTrapScroll; MagicUnTrapScroll; ProtectionScroll; StrengthScroll) + + Circle #3 (BlessScroll; FireballScroll; MagicLockScroll; PoisonScroll; TelekinisisScroll; TeleportScroll; UnlockScroll; WallOfStoneScroll) + + Circle #4 (ArchCureScroll; ArchProtectionScroll; CurseScroll; FireFieldScroll; GreaterHealScroll; LightningScroll; ManaDrainScroll; RecallScroll) + + Circle #5 (BladeSpiritsScroll; DispelFieldScroll; IncognitoScroll; MagicReflectScroll; MindBlastScroll; ParalyzeScroll; PoisonFieldScroll; SummonCreatureScroll) + + Circle #6 (DispelScroll; EnergyBoltScroll; ExplosionScroll; InvisibilityScroll; MarkScroll; MassCurseScroll; ParalyzeFieldScroll; RevealScroll) + + Circle #7 (ChainLightningScroll; EnergyFieldScroll; FlamestrikeScroll; GateTravelScroll; ManaVampireScroll; MassDispelScroll; MeteorSwarmScroll; PolymorphScroll) + + Circle #8 (EarthquakeScroll; EnergyVortexScroll; ResurrectionScroll; SummonAirElementalScroll; SummonDaemonScroll; SummonEarthElementalScroll; SummonFireElementalScroll; SummonWaterElementalScroll) + + Potions (BasePotion) + + Agility (BaseAgilityPotion) + + Cure (BaseCurePotion) + + Explosion (BaseExplosionPotion) + + Heal (BaseHealPotion) + + Poison (BasePoisonPotion) + + Refresh (BaseRefreshPotion) + + Strength (BaseStrengthPotion) + + Ethereal Mounts (EtherealMount) + + Uncategorized (Item) \ No newline at end of file diff --git a/Data/mobiles.cfg b/Data/mobiles.cfg new file mode 100644 index 000000000..06ec32cde --- /dev/null +++ b/Data/mobiles.cfg @@ -0,0 +1,58 @@ +Mobiles (Mobile) + + Healers (BaseHealer) + + Champions (BaseChampion; Harrower; Silvani; HarrowerTentacles) + + Guards (BaseGuard; BaseShieldGuard) + + Escortables (BaseEscortable) + + Vendors (BaseVendor) + + Guildmasters (BaseGuildmaster) + + Dummies (DummyMace; DummyFence; DummySword; DummyNox; DummyStun; DummySuper; DummyHealer; DummyAssassin; DummyTheif) + + Creatures (BaseCreature) + + Animals (Gorilla; Walrus; Alligator; Llama; PackLlama; PackHorse; BullFrog; GiantToad; Hind; GreatHart; DireWolf; GreyWolf; TimberWolf; WhiteWolf; HellHound; Dog; GrizzlyBear; PolarBear; BlackBear; BrownBear; Bull; Cow; Bird; Eagle; Chicken; Boar; Pig; Rabbit; JackRabbit; Rat; SewerRat; GiantRat; Goat; MountainGoat; Sheep; Panther; PredatorHellCat; HellCat; SnowLeopard; Cougar; Cat; BaseMount) + + Mounts (BaseMount) + + War Horses (BaseWarHorse) + + Birds & Fowl (Bird; Eagle; Chicken) + + Bovines (Bull; Cow) + + Bruins (GrizzlyBear; PolarBear; BlackBear; BrownBear) + + Canines (DireWolf; GreyWolf; TimberWolf; WhiteWolf; HellHound; Dog) + + Deer (Hind; GreatHart) + + Felines (Panther; PredatorHellCat; HellCat; SnowLeopard; Cougar; Cat) + + Frogs & Toads (BullFrog; GiantToad) + + Lizards (Alligator) + + Llamas & Pack Animals (Llama; PackLlama; PackHorse) + + Rodents (Rabbit; JackRabbit; Rat; SewerRat; GiantRat) + + Porcines (Boar; Pig) + + Ruminants (Goat; MountainGoat; Sheep) + + Uncategorized (Gorilla; Walrus) + + Ants (RedSolenWorker; RedSolenWarrior; RedSolenQueen; RedSolenInfiltratorQueen; RedSolenInfiltratorWarrior; BlackSolenWorker; BlackSolenWarrior; BlackSolenQueen; BlackSolenInfiltratorQueen; BlackSolenInfiltratorWarrior) + + Red (RedSolenWorker; RedSolenWarrior; RedSolenQueen; RedSolenInfiltratorQueen; RedSolenInfiltratorWarrior) + + Black (BlackSolenWorker; BlackSolenWarrior; BlackSolenQueen; BlackSolenInfiltratorQueen; BlackSolenInfiltratorWarrior) + + Elementals (DullCopperElemental; ShadowIronElemental; CopperElemental; BronzeElemental; GoldenElemental; AgapiteElemental; VeriteElemental; ValoriteElemental; AirElemental; BloodElemental; FireElemental; IceElemental; WaterElemental; PoisonElemental; ToxicElemental; EarthElemental; SnowElemental) + + Ore Elementals (DullCopperElemental; ShadowIronElemental; CopperElemental; BronzeElemental; GoldenElemental; AgapiteElemental; VeriteElemental; ValoriteElemental) + + Marine (Dolphin; SeaSerpent; DeepSeaSerpent) + + Good (Pixie; Wisp; EtherealWarrior; Centaur) + + Evil (BaseCreature) + + Age of Shadows (FleshGolem; Gibberling; GoreFiend; Ravager; SkitteringHopper; Treefellow; VampireBat; WailingBanshee; AbysmalHorror; BoneDemon; CrystalElemental; DarknightCreeper; DemonKnight; Devourer; FleshRenderer; Impaler; MoundOfMaggots; PatchworkSkeleton; ShadowKnight; WandererOfTheVoid; ChaosDaemon; HordeMinion) + + Daemons (IceFiend; Balron; Daemon) + + Dragons & Wyrms (Dragon; WhiteWyrm; SerpentineDragon; AncientWyrm; ShadowWyrm; SkeletalDragon; Drake; Wyvern) + + Gargoyles (Gargoyle; FireGargoyle; StoneGargoyle) + + Harpies (Harpy; StoneHarpy) + + Humans (EvilMage; EvilMageLord; Betrayer; Brigand; Executioner; Guardian; KhaldunSummoner; KhaldunZealot) + + Lizards (LavaLizard; Lizardman) + + Mongbats, Imps, & Headless Ones (Mongbat; StrongMongbat; Imp; HeadlessOne) + + Ogres, Ettins, Trolls, Cyclopses, & Titans (Titan; Cyclops; Ettin; FrostTroll; Ogre; Troll; ArcticOgreLord; OgreLord) + + Ophidians (OphidianMage; OphidianArchmage; OphidianMatriarch; OphidianWarrior; OphidianKnight) + + Orcs (OrcishMage; Orc; OrcishLord; OrcBomber; OrcCaptain; OrcishLord; OrcBrute) + + Plague Beasts, Bog Things, & Slimes (Jwilson; FrostOoze; Slime; PlagueSpawn; PlagueBeast; BogThing; Bogling) + + Plants (Corpser; Quagmire; WhippingVine; SwampTentacle; Reaper) + + Ratmen (Ratman; RatmanArcher; RatmanMage) + + Serpents (IceSerpent; IceSnake; LavaSerpent; LavaSnake; GiantSerpent; SilverSerpent; Snake) + + Spiders (DreadSpider; FrostSpider; GiantSpider) + + Terathans (TerathanMatriarch; TerathanAvenger; TerathanDrone; TerathanWarrior) + + Undead (AncientLich; Bogle; BoneMagi; Lich; LichLord; Shade; SkeletalMage; Spectre; Wraith; BoneKnight; SkeletalKnight; Ghoul; RottingCorpse; Skeleton; Zombie; Mummy) + + Ghosts (Bogle; Shade; Spectre; Wraith; Ghoul) + + Skeletons (BoneMagi; SkeletalMage; BoneKnight; SkeletalKnight; Skeleton) + + Walking Dead (RottingCorpse; Zombie; Mummy) + + Liches (AncientLich; Lich; LichLord) + + Vortexes & Blade Spirits (EnergyVortex; SandVortex; BladeSpirits) + + Uncategorized (BaseCreature) + + Uncategorized (Mobile) \ No newline at end of file diff --git a/Data/names.xml b/Data/names.xml new file mode 100644 index 000000000..6b51f14d6 --- /dev/null +++ b/Data/names.xml @@ -0,0 +1,645 @@ + + + + Dosyaku, Warimoto, Ogino, Soutatsu, Wajida, Batsu, Hikoza, Adai, Ukita, Sanesue, + Chihu, Akiosa, Riku, Kongou, Numata, Kirzahn, Gakudai, Nakai, Ukita, Ashimaru, + Fuha, Gakudai, Numata, Waricika, Chihu, Sanesue, Yose, Shinsirou, Yasuke, Akisuke, + Naozumi, Numatta, Kichizo, Nasuno, Akaba, Arikoto, Kengi, Magosuke + + + Ahara, Yume, Ayane, Yuki, Wakae, Akala, Ikuha, Wayu, Haru, Zannia, Shino, Agate, + Akashi, Niji, Yochika, Toyoka, Wayo, Iroha, Nakai, Ayatsumi, Wayu, Yumiha, Enji, + Aasin, Akela, Chiwa, Suni, Wakumi, Ruri, Tama + + + a wren, a swallow, a warbler, a nuthatch, a chickadee, a thrush, + a nightingale, a starling, a skylark, a finch, a crossbill, a sparrow, a towhee, + a woodpecker, a kingfisher, a tern, a plover, a lapwing, a hawk, a cuckoo, a swift + + + Kaltivel, Anshu, Maliel, Baratoz, Almonjin + + + the Lord of the Abyss, the Collector of Souls, the Slayer + + + Pariah, Ydoc Llessue, Zhaan, Lorbna, Gragok, Thranger, Krygar, + Grothelfiend, Centibis, Farthak, Laitesach, Crenabil, Krullus, Legron, + Noirkrach, Blassarrabb, Gragragron, Vendodroth, Flaggroth, Vilithrar + + + Po-Kor, Manglar, Verolyn, Gathfe, Skred, Flandrith, Stavinfeks, + Steelbane, Crarigor, Empalk, Perfus, Cassiel, Magor, Xtul, Vladeer, Scrill, + Slix, Ix, Selminus, Victux + + + Hrallath, Heksen, Peinsluth, Keelus, Kra'an, Ankou, Turi'el, Azazel, + Armarus, Grigorus, Ga'ahp, Therion, Peirazo, Ponerus, Arhaios Ophis, + Vairocan, Arsat, Karnax, Taet Nu'uhn + + + Oghmus, Arametheus, Terxor, Erdok, Archatrix, Jonar, Marth'Fador, Helzigar, + Tyrnak, Krakus, Marcus Fel, Lord Kaos, Doomor, Uhn, Malashim, Samael, Nelokhiel, + Montobulus, Usuhl, Zul + + + a fiery gargoyle, a burning gargoyle, a smoldering gargoyle, a blistering gargoyle, + a sweltering gargoyle, a flaming gargoyle, a scorching gargoyle, + a blazing gargoyle, a searing gargoyle + + + Ansiraal, Aurlem, Betra, Draxsom, Fodus, Forbrak, ForLem, Horffe, Inforlem, Invis, + Laplem, Naxator, Quaeven, Sarpling, Silamo, Tereg, Volesh, Vraal, WisSur + + + Sophanon, Caryax, Daemeox, Phlegon, Aerophus, Euforus, Pallax, + Nikaon, Licouax, Lindsaon, Bastax, Magdanon, Thayax, Aethon, Ceridus, Galenon, + Rhysus, Auramax, Aldrax, Anaxus, Luceus, Quarax, Ariax, Balarax, Vincenus, + Loxias, Birhamus, Lekax, Nyctinus, Myrsinus + + + Galdrion, Briellis, Charpris, Jesurian, Agrast, Beldrion, Polis, + Arafel, Melestoref, Lanculis, Judaselo, Pietrov + + + Klian, Klistra, Laeri, Ciline, Shiale, Ourie, Piepe, Liera, Sili, + Sefi, Cynthe, Nedra, Hali, Jiki, Piku, Rael, Zanne, Zut, Sini, Os, Wienne, Xian, + Ybri, Calee, Shendri, Shri + + + Kahl, Ghrom, Ogger, Vek, Sai'ge, Groov'h, Khaonem, Malen, Atar, + Aicee, Vaype, Halex, Yar, Hanz, Evocah, Bishor, Jelak, Adrok, Praphut, Usile, + Sann, Sabba, Prie, Atuk, Chaca, ShoJo, Baccu, Bonkie, Alli, Jexa, La'Loh, Arda, + Cordee, Lana, Lar, Araka, Rhunda, Squee, Bhora, Niha, Olaufee, Sinthe, Karawyn, + Gruwulf, Masena, Nasha, Sargaza + + + Kahl, Ghrom, Ogger, Vek, Sai'ge, Groov'h, Khaonem, Malen, Atar, + Aicee, Vaype, Halex, Yar, Hanz, Evocah, Bishor, Jelak, Adrok, Praphut, Usile, + Sann, Sabba, Prie, Atuk, Chaca, ShoJo, Baccu, Bonkie, Alli + + + Jexa, La'Loh, Arda, Cordee, Lana, Lar, Araka, Rhunda, Squee, + Bhora, Niha, Olaufee, Sinthe, Karawyn, Gruwulf, Masena, Nasha, Sargaza + + + Zelik, Kronos, Zakron, Velis, Chujil, Hygraph, Dyntrall, Zarus, + Phoseph, Malkavik, Zevras, Vakel, Daklon, Zamog, Tavurk, Drakov, Zazik, Yyntrix, + Zazik, Fropoz, Noxtrag, Makzok, Galzan, Drakan, Drakzik, Vazmog + + + Aamon, Agalierept, Agares, Aglasis, Aiwaz, Astaroth, Ayperos, + Azatoth, Azmodaeus, Azrael, Ba'al, Baal, Barbatos, Bathim, Bathsin, Be'elzebub, + Be'elzebubba, Bechard, Beelzebuth, Botis, Brulefer, Bucon, Buer, Clauneck, + Clitheret, Cthulhu, Druzil, El Chupacabra, Eleogap, Eliezer, Eligor, Eracove, + Faraii, Fleurety, Frimost, Frucissiere, Fruitimiere, Glassyalabolas, Guland, + Gusoyn, Hael, Haristum, Heramael, Hiepacth, Huictiigara, Humots, Khil, Maleki, + Marbas, Mephistopheles, Mersilde, Minoson, Moloch, Molech, Morail, Musisin, + Naberrs, Nebiros, Nebirots, Nyarlathotep, Pentagnony, Proculo, Pruslas, Pursan, + Rofocale, Sargatans, Satanchia, Satanciae, Segal, Sergulath, Sergutthy, + Sidragrosam, Sirchade, Surgat, Sustugriel, Tarchimache, Tarihimal, Trimasel, + Vaelfar, Wormius, Yog-Sothoth, Y'reif Eci, Zoray + + + Xarot the Black Archmage, Kwan Li the Lord of the Mists, + Bazerion the Wizard-Lord, Ylthallynon the Insane + + + Ronlyn, Merkul, Zasfus, Zain, Doraghir, Danaghir, Staylin, + Kraylin, Limnoch, Kranor, Kraenor, Kranostil, Kranostir, Lilithack, Terus, + Thaelin, Thulack, Jiltharis, Garigor, Banothil, Bainothil, Quain, Ilzinias, + Mardis the Avenger, Phalil the Unexpected, Lord Adnoc, Leje the Invincible, + Master Akris, Sartan, Zejron the Wild, Pitt the Elder, Puzilan the Puzzler, + Vantrom, Singhe-Dul, Tyrin Kuhl, Xarot the Black, Kwan Li, Lord of the Mists, + Viktor Blackoak, Odilion, Raith, Lord of Rats, Bazerion, the Wizard-Lord, + Tybevriat Varn, Keldor the Modoc, Magelord Varsan, Izlay the Ebonheart, Slirith + Iceblood, Alcor, Vitar, Feenark, Sang Qui, Lyticant, Aegnor, Aelfric, Ainvar, + Arazion, Ardarion, Arkanis, Athrax, Barghest, Baros, Begarin, Beynlore, Burat, + Cairne, Carthon, Chamdar, Ciric, Cruzado, Cyphrak, D'Harun, Daelon, Daktar, + Darvain, Dracus, Dradar, Draelin, Draenor, Durodund, Eklor, Elrak, Fangorn, + Farynthane, Galtor, Gemma, Gragus, Irian, Israfel, Jaden, Jefahn, K'shar, + Kalimus, Kallomane, Kanax, Kelnos, Kharn, Khir, Kragon, Kylnath, Larac, Lathis, + Lenroc, Lonthorynthoryl, Lorreck, Mattrick, Mazrim, Mazrim, Modrei, Morturus, + Muktar, Murdon, Murron, Myndon, Mythran, Mytor, Nabius, Nalynkal, Nazgul, + Paorin, Quillan, Rendar, Scythyn, Shilor, Sobran, Soltak, Sorz, Taban, Telzar, + Teron, Trethovian, Tyrnar, Ulath, Vandor, Vermithrax, Vlade, Volan, Wydstrin, + X'calak, Xaelin, Xandor, Xarthos, Xaxtox, Xenix, Xiltanth, Xylor, Xystil, Yazad, + Yllthane, Ylthallynon, Ythoryn, Zalifar, Zathrix, Zunrek + + + Ccketakiki, Chachak, Chachaktak, Chackuk, Chak, Chaki, Chaki, + Chakreki, Chaktuki, Chakukki, Charitiki, Chatuki, Chectik, Chectik, Chek, + Chekeckaki, Cheki, Chekkakii, Cherek, Chetak, Chetickuki, Chiackukk, Chichachak, + Chichak, Chichak, Chichoki, Chichoki, Chickek, Chickek, Chickeki, Chickeki, + Chickekiaki, Chikavi, Chikchickeki, Chikckak, Chikek, Chiketckuki, Chiki, + Chikitchaki, Chiktaki, Chiritchek, Chitaviok, Chokchak, Chokirek, Chotechiki, + Ckak, Ckek, Ckekckuki, Ckeki, Ckekickuk, Ckikhiki, Ckikicheki, Ckukchik, + Ckukichek, Ctiktik, Dachek, Dackatuki, Dactuk, Dafactik, Deckarreki, Deektuk, + Defetav, Dekckuk, Detckiki, Detckuki, Detik, Dicchok, Dickiki, Dikfachok, + Ditecckek, Diwarek, Eachik, Eactiki, Ecckkekek, Eckaki, Eckechakiki, Ecketak, + Ecterek, Ectuk, Eekckuk, Eektuk, Eicchiki, Eickuki, Ekiuki, Etakheki, Etavchiki, + Etckuki, Etik, Fachchekiok, Fachok, Fackak, Fackek, Fackik, Factavi, Factik, + Fecckik, Fechaki, Feractav, Fetav, Fetckiki, Firchik, Firecheki, Fireki, + Fitactaki, Fitaki, Fitcheki, Frecckeki, Hekckeki, Hiki, Hikitchaki, Hokchek, + Ikchaki, Iki, Kackak, Kactavi, Kadicchok, Kakhoki, Kaki, Kakiki, Karrekichoki, + Katukickek, Kecckik, Kechaki, Kekachek, Kekachik, Kekkik, Kicchiki, Kichak, + Kickak, Kidikiki, Kietik, Kik, Kikechokii, Kikiaki, Kiktaki, Kirchik, Kireki, + Kireki, Kitak, Kitaki, Kitavi, Kitcheki, Ktukchok, Kukeckaki, Kuki, Kukiecckak, + Rackek, Ractav, Ratitchaki, Recckeki, Recheki, Rekchectik, Reki, Rektav, + Rektavi, Retchectik, Reteckaki, Retituki, Rikchickek, Rikecckak, Ritchek, + Ritchekckiki, Ritiki, Ritikituk, Tackik, Tactaki, Tactikiv, Tadectuk, Tak, Tak, + Taki, Taki, Taktav, Tavchichoki, Taviactak, Taviectuk, Tecckek, Techiki, + Techikickik, Teckak, Tedetik, Tekactiki, Tekchichak, Tickukitaki, Tictak, + Tidetckuki, Tikckek, Tikickeki, Titchaki, Tituki, Tukckaki, Tukitiki, Tukituki, + Vachichak, Vackuk, Vactak, Vaveckaki, Vechoki, Vectaki, Vevactak, Vevitavi, + Vitavi, Vitchak, Vitituki, Vivackuk, Vivitchak, Vovechoki, Warek, Warreki, + Wecckak, Weckaki, Wedachek, Wikchichoki, Wochickeki + + + Aissssaiss, Aisyths, Alasthsiyss, Alessitsl, Alsiths, Alssi, + Alsyth, Asahlysy, Asathsaisss, Asaysth, Asiashy, Asistsais, Asitssis, Asiyss, + Astha, Asthcieth, Astysah, Athcesth, Athys, Athysthes, Atslah, Aysthyss, + Cesthaysth, Cetys, Ciethlish, Citsy, Cythshi, Cytsi, Ekthsisthh, Essith, Estha, + Etys, Halasth, Haless, Halsasah, Hasits, Hekths, Hiiths, Hiitsl, Hissis, Hlyiss, + Hlylylyshi, Hlyshilyly, Hlysylyiss, Hlythilsyth, Hissalsthy, Hysslssi, Hsysylh, + Hthessthil, Hthissy, Htisthh, Htsiathys, Hyisass, Hysil, Hysyiss, Iasia, + Icythhlysy, Iithsissh, Iitsliss, Ilshy, Ilsyth, Isalraaat, Isassthys, Isathys, + Isiss, Isissil, Isshi, Isshlshy, Isstha, Issthas, Issthyl, Issysh, Issyt, + Isthhtis, Istis, Istththh, Isyts, Ithstsesh, Ithsy, Iththis, Itsesh, Itshas, + Itsltlish, Itsthil, Itsylh, Iyssysil, Kthystshas, Kthythes, Lasath, Lastha, + Lasthtsthih, Lesstslah, Lestha, Lilhals, Lilsyth, Lishaless, Lishath, Lissysh, + Lissyt, Lithekths, Litsylh, Llithlish, Lshyshas, Lssissi, Lsthyssy, Lsyth, + Lsythstha, Lyissisis, Lylsthy, Lylyisass, Lyshiisal, Lyssthih, Lyssthy, + Lysyiitsl, Lythiiiths, Lytsthih, Saisalasths, Saishtsi, Saisssmyss, Salsesh, + Salssi, Sasisthasth, Sasss, Sasth, Sath, Sathsthih, Sathys, Satslah, Saysth, + Scesthhals, Seshsthyl, Sessith, Shasits, Shassthy, Shisis, Shitha, Shysil, + Shythis, Siasia, Siaththh, Sicyth, Silshy, Silthyl, Sisal, Sisshi, Sisshlythi, + Sistlilh, Sithtththh, Sitsesh, Sitshas, Sitstsi, Siyss, Slahyisass, Slasath, + Slishless, Ssaisss, Ssasist, Sshiscesth, Ssisal, Ssithsiss, Ssiyth, Sssaisss, + Ssssshi, Ssthasssthas, Ssthihssthy, Ssthssss, Ssthyl, Ssthyssths, Ssyraaath, + Ssysah, Ssyshstha, Ssyssith, Ssytsth, Stasah, Sthasth, Sthasthas, Sthasthih, + Sthihaisss, Sthihasits, Sthilsthy, Sthlyly, Sthlyshi, Sthlysy, Sthlythi, + Sthmissa, Sthmyss, Sthsasist, Sthyss, Sthyasia, Sthycyth, Sthyltasah, Stissh, + Stththh, Stysah, Sycieth, Sylhsths, Syllith, Syraaat, Syshsyts, Syslish, + Syththil, Sythysth, Sytlilh, Sytlish, Sytsyth, Sytsyts, Syys, Tasahasath, + Thalasths, Thaless, Thals, Thatsylh, Thays, Thekths, Theshthis, Thiiths, + Thiitsl, Thilhlyshi, Thishthes, Thlyiss, Thlyly, Thlyshi, Thlysy, Thlythi, + Thmissa, Thmyss, Thsais, Thslish, Ththes, Ththhllith, Ththis, Thtisthh, + Thyisass, Thylmissa, Thys, Thysslah, Tisiss, Tissyth, Tisthhhsy, Tlilhlasth, + Tlishkths, Tseshsath, Tshassasisth, Tsicyth, Tsitys, Tslahsits, Tsthihssthih, + Tsthil, Tsycieth, Tsylhssyt, Tsytysah, Tththhsyt, Tysahyraaath, Tyscesth, + Tysycieth, Yciethhlyly, Yisasshmyss, Yisstisthh, Yllith, Ylsthy, Yraaathlythi, + Ysahlith, Ysasth, Ysath, Yscesth, Yshmissa, Yshtsi, Ysilsia, Yslish, Yssasisth, + Ysssal, Yssthih, Yssths, Yssthy, Ystasah, Ysthsith, Ystsy, Ythals, Ythssysh, + Ytlilh, Ytlish, Ytsais, Ytssysh, Ytsthih + + + Abghat, Adgulg, Aghed, Agugh, Aguk, Almthu, Alog, Ambilge, + Apaugh, Argha, Argigoth, Argug, Arpigig, Auhgan, Azhug, Bagdud, Baghig, + Bahgigoth, Bahgigoth, Bandagh, Barfu, Bargulg, Baugh, Bidgug, Bildud, Bilge, + Bog, Boghat, Bogugh, Borgan, Borug, Braugh, Brougha, Brugagh, Bruigig, Buadagh, + Buggug, Builge, Buimghig, Bulgan, Bumhug, Buomaugh, Buordud, Burghed, Buugug, + Cabugbu, Cagan, Carguk, Carthurg, Clog, Corgak, Crothu, Cubub, Cukgilug, Curbag, + Dabub, Dakgorim, Dakgu, Dalthu, Darfu, Deakgu, Dergu, Derthag, Digdug, Diggu, + Dilug, Ditgurat, Dorgarag, Dregu, Dretkag, Drigka, Drikdarok, Drutha, Dudagog, + Dugarod, Dugorim, Duiltag, Durbag, Eagungad, Eggha, Eggugat, Egharod, Eghuglat, + Eichelberbog, Ekganit, Epkagut, Ergoth, Ertguth, Ewkbanok, Fagdud, Faghig, + Fandagh, Farfu, Farghed, Fargigoth, Farod, Faugh, Feldgulg, Fidgug, Filge, + Fodagog, Fogugh, Fozhug, Frikug, Frug, Frukag, Fubdagog, Fudhagh, Fupgugh, + Furbog, Futgarek, Gaakt, Garekk, Gelub, Gholug, Gilaktug, Ginug, Gnabadug, + Gnadug, Gnalurg, Gnarg, Gnarlug, Gnorl, Gnorth, Gnoth, Gnurl, Golag, Golag, + Golub, Gomatug, Gomoku, Gorgu, Gorlag, Grikug, Grug, Grug, Grukag, Grukk, Grung, + Gruul, Guag, Gubdagog, Gudhagh, Gug, Gug, Gujarek, Gujek, Gujjab, Gulm, Gulrn, + Gunaakt, Gunag, Gunug, Gurukk, Guthakug, Guthug, Gutjja, Hagob, Hagu, Hagub, + Haguk, Hebub, Hegug, Hibub, Hig, Hogug, Hoknath, Hoknuk, Hokulk, Holkurg, + Horknuth, Hrolkug, Hugagug, Hugmug, Hugolm, Ig, Igmut, Ignatz, Ignorg, Igubat, + Igug, Igurg, Ikgnath, Ikkath, Inkathu, Inkathurg, Isagubat, Jogug, Jokgagu, + Jolagh, Jorgagu, Jregh, Jreghug, Jugag, Jughog, Jughragh, Jukha, Jukkhag, + Julakgh, Kabugbu, Kagan, Kaghed, Kahigig, Karfu, Karguk, Karrghed, Karrhig, + Karthurg, Kebub, Kegigoth, Kegth, Kerghug, Kertug, Kilug, Klapdud, Klapdud, + Klog, Klughig, Knagh, Knaraugh, Knodagh, Knorgh, Knuguk, Knurigig, Kodagog, Kog, + Kogan, Komarod, Korgak, Korgulg, Koughat, Kraugug, Krilge, Krothu, Krouthu, + Krugbu, Krugorim, Kubub, Kugbu, Kukgilug, Kulgha, Kupgugh, Kurbag, Kurmbag, + Laghed, Lamgugh, Mabub, Magdud, Malthu, Marfu, Margulg, Mazhug, Meakgu, + Mergigoth, Milug, Mudagog, Mugarod, Mughragh, Mugorim, Murbag, Naghat, Naghig, + Naguk, Nahgigoth, Nakgu, Narfu, Nargulg, Narhbub, Narod, Neghed, Nehrakgu, + Nildud, Nodagog, Nofhug, Nogugh, Nomgulg, Noogugh, Nugbu, Nughilug, Nulgha, + Numhug, Nurbag, Nurghed, Oagungad, Oakgu, Obghat, Oggha, Oggugat, Ogharod, + Oghuglat, Oguk, Ohomdud, Ohulhug, Oilug, Okganit, Olaghig, Olaugh, Olmthu, + Olodagh, Olog, Omaghed, Ombilge, Omegugh, Omogulg, Omugug, Onog, Onubub, Onugug, + Oodagh, Oogorim, Oogugbu, Oomigig, Opathu, Opaugh, Opeghat, Opilge, Opkagut, + Opoguk, Oquagan, Orgha, Orgoth, Orgug, Orpigig, Ortguth, Otugbu, Ougha, + Ougigoth, Ouhgan, Owkbanok, Paghorim, Pahgigoth, Pahgorim, Pakgu, Parfu, Pargu, + Parhbub, Parod, Peghed, Pehrakgu, Pergu, Perthag, Pigdug, Piggu, Pitgurat, + Podagog, Pofhug, Pomgulg, Poogugh, Porgarag, Pregu, Pretkag, Prigka, Prikdarok, + Prutha, Pughilug, Puiltag, Purbag, Qog, Quadagh, Quilge, Quimghig, Quomaugh, + Quordud, Quugug, Raghat, Raguk, Rakgu, Rarfu, Rebub, Rilug, Rodagog, Rogan, + Romarod, Routhu, Rugbu, Rugorim, Rurbag, Rurigig, Sabub, Saghig, Sahgigoth, + Sahgorim, Sakgu, Salthu, Saraugug, Sarfu, Sargulg, Sarhbub, Sarod, Sbghat, + Seakgu, Sguk, Shomdud, Shulhug, Sildud, Silge, Silug, Sinsbog, Slaghig, Slapdud, + Slaugh, Slodagh, Slog, Slughig, Smaghed, Smegugh, Smogulg, Snog, Snubub, Snugug, + Sodagh, Sog, Sogorim, Sogugbu, Sogugh, Sombilge, Somigig, Sonagh, Sorgulg, + Sornaraugh, Soughat, Spathu, Speghat, Spilge, Spoguk, Squagan, Stugbu, Sudagog, + Sugarod, Sugbu, Sugha, Sugigoth, Sugorim, Suhgan, Sulgha, Sulmthu, Sumhug, + Sunodagh, Sunuguk, Supaugh, Supgugh, Surbag, Surgha, Surghed, Surgug, Surpigig, + Tagdud, Taghig, Tandagh, Tandagh, Tarfu, Targhed, Targigoth, Tarod, Taugh, + Taugh, Teldgulg, Tidgug, Tidgug, Tilge, Todagog, Tog, Toghat, Togugh, Torgan, + Torug, Tozhug, Traugh, Trilug, Trougha, Trugagh, Truigig, Tuggug, Tulgan, + Turbag, Turge, Ug, Ugghra, Uggug, Ughat, Ulgan, Ulmragha, Ulmrougha, Umhra, + Umragig, Umruigig, Ungagh, Unrugagh, Urag, Uraugh, Urg, Urgan, Urghat, Urgran, + Urlgan, Urmug, Urug, Urulg, Vabugbu, Vagan, Vagrungad, Vagungad, Vakgar, Vakgu, + Vakmu, Valthurg, Vambag, Vamugbu, Varbu, Varbuk, Varfu, Vargan, Varguk, + Varkgorim, Varthurg, Vegum, Vergu, Verlgu, Verthag, Verthurg, Vetorkag, Vidarok, + Vigdolg, Vigdug, Viggu, Viggulm, Viguka, Vitgurat, Vitgut, Vlog, Vlorg, Vorgak, + Vorgarag, Vothug, Vregu, Vretkag, Vrigka, Vrikdarok, Vrogak, Vrograg, Vrothu, + Vruhag, Vrutha, Vubub, Vugub, Vuiltag, Vukgilug, Vultog, Vulug, Vurbag, Wakgut, + Wanug, Wapkagut, Waruk, Wauktug, Wegub, Welub, Wholug, Wilaktug, Wingloug, + Winug, Woabadug, Woggha, Woggugat, Woggugat, Wogharod, Wogharod, Woghuglat, + Woglug, Wokganit, Womkug, Womrikug, Wonabadug, Worthag, Wraog, Wrug, Wrukag, + Wrukaog, Wubdagog, Wudgh, Wudhagh, Wudugog, Wuglat, Wumanok, Wumkbanok, Wurgoth, + Wurmha, Wurtguth, Wurthu, Wutgarek, Xaakt, Xago, Xagok, Xagu, Xaguk, Xarlug, + Xarpug, Xegug, Xepug, Xig, Xnath, Xnaurl, Xnurl, Xoknath, Xokuk, Xolag, Xolkug, + Xomath, Xomkug, Xomoku, Xonoth, Xorag, Xorakk, Xoroku, Xoruk, Xothkug, Xruul, + Xuag, Xug, Xugaa, Xugag, Xugagug, Xugar, Xugarf, Xugha, Xugor, Xugug, Xujarek, + Xuk, Xulgag, Xunaakt, Xunag, Xunug, Xurek, Xurl, Xurug, Xurukk, Xutag, Xuthakug, + Xutjja, Yaghed, Yagnar, Yagnatz, Yahg, Yahigig, Yakgnath, Yakha, Yalakgh, + Yargug, Yegigoth, Yegoth, Yerghug, Yerug, Ymafubag, Yokgagu, Yokgu, Yolmar, + Yonkathu, Yregh, Yroh, Ysagubar, Yughragh, Yugug, Yugug, Yukgnath, Yukha, + Yulakgh, Yunkathu, Zabghat, Zabub, Zaghig, Zahgigoth, Zahgorim, Zalthu, + Zaraugug, Zarfu, Zargulg, Zarhbub, Zarod, Zeakgu, Zguk, Zildud, Zilge, Zilug, + Zinsbog, Zlapdud, Zlog, Zlughig, Zodagh, Zog, Zogugbu, Zogugh, Zombilge, Zonagh, + Zorfu, Zorgulg, Zorhgigoth, Zornaraugh, Zoughat, Zudagog, Zugarod, Zugbu, + Zugorim, Zuhgan, Zulgha, Zulmthu, Zumhug, Zunodagh, Zunuguk, Zupaugh, Zupgugh, + Zurbag, Zurgha, Zurghed, Zurgug, Zurpigig + + + Aba, Abby, Abella, Abey, Abigail, Abina, Abiona, Abira, Abra, + Abrah, Absinthe, Acacia, Acanit, Acantha, Accalia, Acelin, Achen, Ada, Adalia, + Adara, Addi, Adelaide, Adele, Adelia, Adeline, Adelle, Adena, Aderes, Adesina, + Adie, Adimina, Adiva, Adoncia, Adonia, Adora, Adrienne, Aelina, Afina, Afra, + Afrika, Afton, Agate, Agatha, Agnes, Ahara, Ahave, Ahimsa, Aida, Aiella, Aiko, + Aila, Aileen, Ailsa, Aimee, Ain, Aina, Ainhoa, Ainsley, Aintzane, Airlia, + Aisling, Aislinn, Aithne, Aiyana, Ajara, Ajay, Ajinora, Akako, Akala, Akanke, + Akasma, Akela, Akilah, Akili, Akilina, Akina, Alaina, Alake, Alala, Alamanada, + Alana, Alani, Alanna, Alaqua, Alavda, Alazne, Alberta, Albinka, Alcina, Aldea, + Aldercy, Aleka, Alenne, Alesia, Alessa, Alethea, Alexa, Alexandra, Alexandria, + Alexandrina, Alexis, Ali, Alia, Alice, Alicia, Alida, Alike, Alima, Alina, + Alison, Alita, Alix, Aliz, Aliza, Allele, Alligra, Allinora, Allison, Allyn, + Alma, Alodie, Aloysia, Althea, Alula, Alumit, Alvina, Alvita, Alysa, Alyssa, + Alyssand, Alzena, Ama, Amabel, Amadi, Amadika, Amadis, Amaia, Amala, Amalia, + Amanda, Amandine, Amara, Amarande, Amarante, Amaris, Amata, Ambar, Amber, + Ambika, Ambis, Ameerah, Amelia, Amelina, Amethyst, Amie, Amiella, Amina, Aminta, + Amissa, Amita, Amity, Amoke, Amy, Ananda, Anastasia, Ancelin, Andi, Andra, + Andraianna, Andras, Andrea, Andromeda, Aneida, Anella, Anemone, Anezka, Angela, + Angelica, Angeline, Angelique, Angeni, Ani, Anica, Anieli, Anisa, Anita, Anke, + Ann, Anna, Annabel, Annabelle, Annamarie, Anne, Annette, Annikka, Annora, + Anorah, Anoush, Ansreana, Anteia, Anthea, Antje, Antoinette, Antonia, Aolani, + Apara, Apirka, Apolline, Apolloina, Aponi, April, Aprille, Aprille, Aqua, + Aquene, Ara, Arabella, Arabelle, Araceli, Araminta, Araxie, Arcadia, Ardath, + Ardelia, Arden, Ardis, Ardith, Areiela, Arella, Aretha, Aretina, Ariadne, + Ariana, Aricia, Ariel, Ariene, Arista, Arlene, Arlinda, Armina, Arminda, + Artemisia, Aruna, Arziki, Asaria, Asenka, Ash, Asha, Ashlan, Ashleigh, Ashley, + Asia, Asisa, Aslinda, Aspasia, Asta, Aster, Astera, Astra, Astrea, Astrid, + Atalanta, Atara, Atenne, Ateri, Athalia, Athena, Athla, Atifa, Atta, Aubrey, + Auda, Audny, Audrey, Audrianna, Audun, Augustina, Aura, Aure, Aurelia, Aurilia, + Aurina, Aurkene, Aurora, Autumn, Ava, Avana, Avasa, Avella, Avena, Avie, Avis, + Aviva, Axella, Aya, Ayaluna, Ayame, Ayana, Ayasha, Aydee, Ayela, Ayiana, Ayila, + Ayisha, Ayita, Ayla, Aynora, Ayuna, Azaleah, Azalia, Azarael, Azera, Azha, + Azilea, Azina, Azize, Azora, Azura, Babette, Bacia, Bacia, Baka, Baka, Bakarne, + Balayna, Balea, Balia, Bambi, Banan, Banella, Bara, Barbara, Barika, Basha, + Basha, Basia, Basimah, Batakah, Bathsheba, Batya, Bay, Bayana, Bayo, Bayta, Bea, + Beatrice, Beatrix, Beauina, Becca, Becky, Bedelia, Bel, Belana, Belina, Belinda, + Belita, Bellanca, Belle, Belora, Bente, Beradine, Berilla, Berit, Bernadette, + Bernice, Beryl, Bess, Bessine, Beta, Beth, Bethana, Bethany, Betony, Betty, + Beulah, Beverly, Bevin, Bian, Bianca, Billie, Bina, Bindy, Binti, Birdie, + Birkita, Bixenta, Blanche, Blanda, Blenda, Bliss, Bly, Blythe, Bo, Bohdana, + Bonamy, Bonita, Bonnie, Bonny, Borgny, Braina, Brandi, Brandy, Bren, Brenda, + Brenna, Bretta, Bridget, Bridget, Brie, Brier, Brietta, Brigit, Brigitte, Brina, + Brina, Briona, Briony, Brites, Britta, Brittany, Bronwyn, Brooke, Brynn, Bucia, + Cadence, Caimile, Caitlin, Caitrin, Cala, Calandia, Calandra, Calendonia, Caley, + Calida, Calista, Calla, Callan, Callia, Callidora, Callie, Caltha, Calypso, Cam, + Camelia, Camilia, Camille, Canace, Candace, Candida, Candide, Candra, Cantara, + Caoimhe, Capri, Caprice, Cara, Caradoc, Caresse, Cari, Carina, Carine, Carissa, + Carita, Carla, Carleen, Carlen, Carling, Carlota, Carly, Carma, Carmel, + Carmelina, Carmen, Carna, Carnelian, Carol, Carolina, Caroline, Carolyn, Caron, + Carrie, Caryn, Casey, Casilda, Cassandra, Cassia, Casta, Castalia, Catalina, + Catava, Caterina, Catherine, Cathleen, Cathy, Catriona, Cayla, Ceara, Cecania, + Cecilia, Celandine, Celeste, Celia, Celina, Celina, Cellia, Cerelia, Chaitra, + Chanah, Chanda, Chandi, Chandra, Chane, Chanel, Channa, Chantal, Charis, + Charissa, Charity, Charlotte, Charmaine, Chastity, Chava, Chaviva, Chay, Chaya, + Chelsea, Chenoa, Cherica, Cherice, Cherie, Cheryl, Chesna, Chiara, Chika, + Chilali, Chimlis, Chipo, Chloe, Chloris, Cho, Christa, Christable, Christina, + Christine, Christy, Chyou, Cia, Ciannait, Ciar, Cicely, Cindy, Claire, Clara, + Clarinda, Clarissa, Claudette, Claudia, Claudine, Clementina, Clementine, + Cliantha, Clorinda, Clorinda, Clover, Cochiti, Coleene, Colette, Connie, + Constance, Constanza, Consuela, Cora, Coralie, Corazon, Corbey, Cordeali, + Coretta, Cori, Corinna, Coris, Corliss, Corrine, Cortney, Crescent, Cressida, + Crystal, Cybele, Cybil, Cynthia, Cyprien, Cyrene, Cyrilla, Cytheria, Dabria, + Dacey, Dacia, Dacie, Dacio, Dae, Dagmar, Dagna, Dai, Daily, Daisel, Daisy, + Dakota, Dale, Dalila, Dalilia, Damara, Damitri, Dana, Danett, Dania, Daniella, + Danyelle, Daphene, Daphne, Daphnie, Dar, Dara, Daralis, Darby, Daria, Darla, + Darlene, Dasha, Dasha, Davene, Davine, Davita, Dawn, Daya, Dayna, Deana, Deana, + Deandra, Deb, Debra, Dede, Dee, Deedee, Deianira, Deiene, Deirdre, Delana, + Delaney, Delbin, Delia, Delicia, Delilia, Della, Delphina, Dembe, Demi, + Demitria, Dena, Denby, Denice, Deva, Devaki, Deval, Devi, Devin, Devnet, Devon, + Diamanta, Diane, Dianthe, Diedre, Diella, Dillian, Dilly, Dilys, Dinah, Dionne, + Disa, Dita, diti, Dixie, Dodie, Dolores, Dominique, Dona, Donata, Donielle, + Donner, Dooriya, Dophina, Dora, Doreen, Dorinda, Doris, Dorithy, Dory, Drew, + Drina, Drucilla, Dulcie, Dulcinea, Dusty, Dyan, Dyani, Dymphna, Dyna, Eartha, + Easter, Ebony, Echo, Edana, Edie, Edith, Edlyn, Edna, Edolie, Edria, Edwina, + Efia, Eileen, Eirene, Elaine, Elana, Eleora, Elianor, Elina, Elina, Elisa, + Elise, Eliska, Elissa, Elita, Eliza, Elizabeeth, Elke, Ella, Elle, Ellen, Elly, + Elodie, Eloise, Elsa, Elsie, Elynor, Elyse, Elysia, Ema, Emajane, Emalia, Ember, + Emelie, Emelyne, Emily, Emma, Endora, Engracia, Enid, Enola, Enye, Erasma, + Erianthe, Erica, Erin, Erlina, Erwand, Eskarne, Esmerelda, Esperanza, Esta, + Estelle, Esther, Estu, Etain, Etaina, Etaina, Etanthe, Etta, Eudocia, Eugenia, + Eulalia, Eustacia, Eva, Evacsa, Evadine, Evadne, Evangeline, Evanthe, Eve, + Evelyn, Evita, Evonne, Eyota, Fabienne, Faifuza, Fainche, Faith, Faizah, Fallon, + Fantine, Farha, Farima, Farrah, Fatin, Fawne, Fay, Faye, Fayina, Fayme, Felcia, + Felicite, Felicity, Femi, Feridwyn, Fern, Feronia, Filinda, Fina, Finola, Fiona, + Fiorenza, Flavia, Fleta, Flora, Florence, Frances, Francesca, Francine, + Francisca, Freda, Frederica, Freya, Frida, Frieda, Fuscienne, Gabriella, + Gabrielle, Gaia, Gail, Galatea, Gali, Galina, Galya, Gana, Ganesa, Gauri, Gaye, + Gayle, Gelasia, Gemma, Genevieve, Geogia, Georgeanne, Georgetta, Georgette, + Georgiana, Geradline, Geraldine, Gerda, Gerri, Gertrude, Geva, Ghislaine, + Giacinta, Gianina, Gigi, Gilana, Gilda, Gilen, Gillian, Gin, Gina, Ginger, + Giselle, Gitana, Githa, Gizane, Gleda, Glenna, Glennys, Golda, Goldie, Gotzone, + Grace, Gracie, Grainne, Grazia, Grear, Greta, Gretchen, Grette, Gwen, Gwendolyn, + Gweneth, Gwynne, Gytha, Hadara, Hadassa, Hadiya, Haidee, Hailey, Haimi, Haldis, + Hale, Haley, Hali, Hali, Halima, Halle, Hallie, Hana, Hanan, Hannah, Hanne, + Harmoni, Harriet, Hasna, Hava, Haya, Haylee, Hazel, Hea, Heather, Hei, Heidi, + Heldegarde, Helen, Helena, Helene, Helki, Henka, Henrietta, Hesper, Hester, + Hilary, Hilda, Hinda, Hisa, Holly, Hope, Hoshi, Hyacinth, Hye, Hypatia, Ianthe, + Ida, Idola, Idonia, Ilene, Ilona, Iman, Imogene, India, Indira, Indra, Ines, + Inez, Inga, Ingrid, Iolana, Iolanthe, Iona, Iratze, Irena, Irene, Iris, Irma, + Isabeau, Isabel, Isabella, Isadora, Isaura, Isis, Isleta, Isobel, Isoke, Istas, + Ivana, Ivory, Ivy, Jacelyn, Jacinda, Jacinthe, Jada, Jael, Jaen, Jaimie, Jaione, + Jakinda, Jala, Jamie, Jamila, Jamilah, Jan, Jana, Jane, Janelle, Janet, Janice, + Janis, Janna, Jannelle, Jardena, Jarvia, Jarvinia, Jasmine, Jaya, Jayne, Jean, + Jean, Jeanette, Jeanine, Jelena, Jena, Jenay, Jendayi, Jendyose, Jenica, + Jennettia, Jennifer, Jensine, Jerrilyn, Jessica, Jewel, Jezebel, Jihan, Jillian, + Jin, Jina, Jinny, Jinx, Joakima, Joan, Joanne, Jobey, Jobihna, Jocasa, Jocelyn, + Jodi, Jody, Joelle, Joelliane, Johanna, Joia, Jolan, Jolanta, Jolene, Jolie, + Joline, Jonina, Jora, Jordane, Josephine, Josie, Jotha, Joy, Joyce, Joye, + Juanita, Judith, Juditha, Julia, Juliana, Juliane, Julie, Julietta, Julinka, + Jumoke, Jun, June, Justine, Kaatje, Kachine, Kaclyn, Kaede, Kaethe, Kai, Kaia, + Kaie, Kaili, Kaimi, Kairos, Kaiya, Kakra, Kala, Kalama, Kalanit, Kalare, Kalea, + Kali, Kalika, Kalila, Kalinda, Kalle, Kalli, Kalonice, Kalyca, Kama, Kamala, + Kamali, Kamaria, Kambo, Kameko, Kamilah, Kamilia, Kanda, Kane, Kanene, Kanika, + Kantha, Kanya, Kapera, Kara, Karan, Karayan, Karel, Karen, Karida, Karimah, + Karisa, Karka, Karla, Karlenne, Karli, Karlyn, Karmina, Karol, Karylin, Karyn, + Kasa, Kasen, Kasia, Kasinda, Kassia, Kate, Katherine, Kathleen, Katja, Katoka, + Katrien, Katrina, Kaula, Kaveri, Kavindra, Kay, Kaya, Kaye, Kayla, Kaysa, Kazia, + Keara, Keelin, Keely, Kefira, Kehinde, Kei, Keiko, Keisha, Kelda, Kelia, Kelley, + Kelli, Kellie, Kelly, Kelsey, Kendra, Kennis, Kenyangi, Kepa, Kerani, Kerensa, + Kerstan, Kesare, Kesi, Kesia, Kessie, Keturah, Ketzia, Khalida, Kichi, Kiele, + Kim, Kimberly, Kimmie, Kimmy, Kineta, Kiona, Kira, Kiran, Kirby, Kirima, + Kirsten, Kirti, Kisa, Kiska, Kismet, Kissa, Kita, Kohana, Kolina, Koren, + Koressa, Kristen, Kyly, Kyna, Kynthia, Kyoko, Lacey, Lacie, Laila, Lailie, + Lakeisha, Lala, Lalasa, Lan, Lana, Landra, Lane, Lani, Lara, Laraine, Laralee, + Lari, Larissa, Lark, Latika, Latonia, Laura, Laurana, Laurel, Laurie, Laurinda, + Lauryn, Laveda, Lavern, Laverne, Lavinia, Lea, Leah, Leah, Leala, Leandra, Leba, + Ledah, Lee, Leigh, Leiko, Leila, Leilana, Lena, Lene, Lenor, Lenora, Lenore, + Leona, Leora, Leslie, Letha, Letitia, Levana, Lexine, Lia, Liadan, Lian, Liana, + Liane, Libby, Lien, Lila, Lilith, Lillian, Lillie, Lily, Limber, Lina, Linda, + Lindsay, Lindsey, Linette, Linnae, Linnea, Lisa, Lisette, Litsa, Liv, Liza, + Lois, Lokelani, Lola, Loni, Lora, Lore, Lorelei, Lorelle, Loretta, Lori, + Lorraine, Lotus, Louise, Lucille, Lucine, Lucretia, Lucy, Ludia, Luela, Luisa, + Lukene, Lukina, Lulu, Luna, Lydia, Lynda, Lynelle, Lynn, Lynnda, Lynnette, + Lyris, Lysel, Lysnadra, Mabel, Macaria, Machi, Maddy, Madelaine, Madelina, + Madeline, Madelon, Madelyn, Mady, Mae, Magan, Magara, Magdalen, Magdalena, + Magdaline, Magena, Magenta, Maggie, Mahala, Mahalia, Mai, Maia, Maida, Maisie, + Maitane, Maizah, Maj, Malaya, Malila, Malina, Malinda, Malka, Mallory, Malu, + Mamie, Manda, Mandara, Mandisa, Mandy, Mangena, Manon, Mansi, Manya, Mara, + Marcella, Marcia, Marcy, Maren, Margaret, Margaret, Margarita, Margo, Margot, + Marguirte, Maria, Mariah, Mariam, Mariama, Marian, Mariana, Marianna, Marianne, + Maribel, Marie, Mariel, Marietta, Marily, Marilyn, Marina, Maris, Marisa, + Marisha, Marissa, Marjani, Marjeta, Marjorie, Marlene, Marlo, Marmara, Marnie, + Marnina, Marsha, Marta, Martha, Marti, Martina, Mary, Maryann, Marybeth, + Marylou, Marzia, Matana, Mathea, Matilda, Matrika, Maud, Maura, Maureen, + Maurita, Mavis, Maxine, May, Maya, Meara, Meara, Meda, Medea, Meg, Megan, + Megara, Meghan, Mei, Meira, Mela, Melanie, Melantha, Melba, Melia, Melian, + Melina, Melinda, Melisenda, Melissa, Mellinio, Melodie, Melody, Melosa, Melva, + Mercedes, Meredith, Merele, Mesha, Meta, Mia, Miakoda, Michaela, Michele, + Michelle, Midori, Migina, Mignon, Mika, Millicent, Millie, Min, Mina, Minda, + Mindel, Mindy, Minerva, Minka, Minna, Minnie, Mira, Miranda, Mirem, Miremba, + Mireya, Miriam, Mirielle, Missy, Misty, Mitena, Mitexi, Mitzi, Moira, Mollie, + Molly, Mona, Monique, Moon, Morena, Morgan, Morgana, Morgance, Moria, Moriah, + Muriel, Myra, Nada, Nadia, Nadine, Nadya, Naia, Nailah, Naimah, Nalini, Namazzi, + Nami, Nan, Nana, Nancy, Nanette, Nantale, Naomi, Napea, Nara, Narda, Narmada, + Nasiche, Nastassia, Natalie, Natane, Natasha, Natesa, Naysa, Nazirqah, Neala, + Neci, Nediva, Neely, Nekane, Nell, Neola, Neoma, Neona, Neria, Nerine, Nerissa, + Netti, Neva, Nevada, Neysa, Nicia, Nicola, Nicole, Nicolette, Nika, Nikki, + Nimah, Nina, Niobe, Niola, Nira, Nirvelli, Nissa, Nita, Nitara, Nixie, Noel, + Noelani, Noella, Nolita, Nona, Nona, Nora, Norah, Noreen, Nori, Noriko, Norma, + Nydia, Nyrna, Nyssa, Obelia, Octavia, Odelia, Odelia, Odera, Odessa, Odetta, + Odette, Odile, Ohanna, Okelani, Olathe, Olayinka, Olesia, Olga, Oliana, Olinda, + Olivette, Olivia, Ona, Onida, Opal, Ophelia, Oralie, Orane, Orenda, Oriana, + Orianna, Oriel, Oriole, Orlantha, Ornidaa, Paige, Pakuna, Palmiera, Paloma, + Pamela, Pandita, Pandora, Panthea, Pantzike, Panya, Panyin, Pascale, Patia, + Patience, Patricia, Patsy, Paula, Paulette, Pauline, Pavla, Pazia, Pearl, Peg, + Peggy, Pelagia, Pemba, Penda, Penelope, Peninna, Penny, Penthea, Peony, Perdita, + Perouze, Persis, Petra, Phaedra, Phedra, Philomena, Phoebe, Phylis, Phyllis, + Pia, Pier, Pila, Piper, Polly, Poloma, Porche, Portia, Priscilla, Prudence, + Prudy, Pyrena, Pythia, Qamra, Queena, Quella, Quenby, Quintina, Quiterie, + Rachel, Radella, Radinka, Rae, Rai, Raizel, Ramla, Ramona, Ramya, Randie, Rane, + Ranee, Rani, Raquel, Rashida, Rasine, Ratri, Raven, Rawnie, Rayna, Raynell, + Raziya, Reba, Rebecca, Regan, Regina, Reidun, Remy, Rena, Renata, Rene, Renee, + Rhea, Rhiamon, Rhianne, Rhiannon, Rhoda, Rhodanthe, Rhonda, Rhonna, Rhyssa, Ria, + Riane, Rica, Rihana, Rikki, Rio, Risa, Rita, Riva, Roanna, Roberta, Robin, + Robyn, Rochelle, Rohanna, Rona, Rorie, Rosa, Rosalind, Rosalinda, Rosalinde, + Rosaline, Rosanne, Rose, Roseanne, Rosemarie, Rosemary, Rowena, Roxana, Roxanne, + Ruby, Rumer, Ruth, Ruthann, Ryann, Ryanne, Ryba, Ryssa, Saba, Sabina, Sabiny, + Sabirah, Sabra, Sabrina, Sacha, Sachi, Sade, Sadira, Saffi, Safiya, Sagara, + Saidah, Sakari, Sakinah, Sakti, Sakura, Salihah, Salimah, Salina, Sally, Salome, + Samantha, Samara, Samirah, Sancia, Sandia, Sandra, Sandrine, Sandya, Sara, + Sarah, Sarai, Saraid, Saree, Sarena, Sari, Sarisha, Sasha, Sashenka, Satinka, + Savanna, Saxon, Scotia, Searlait, Season, Sebasten, Seema, Sela, Selena, Selina, + Selma, Semele, Senta, Serafina, Serilda, Sesha, Shaine, Shakira, Shako, + Shammara, Shana, Shanata, Shandra, Shandy, Shani, Shanley, Shanna, Shannon, + Shantay, Shantha, Sharman, Sharon, Sharri, Shashi, Shawn, Shayndel, Sheba, + Sheena, Sheila, Shela, Shelby, Shelley, Shelly, Sherri, Shika, Shin, Shina, + Shira, Shirley, Shobi, Shoshana, Sibley, Sibongile, Sibyl, Sidonia, Sidra, + Sierra, Sigourney, Siham, Sileas, Silva, Silvia, Simba, Simone, Sine, Sinead, + Siobhan, Siran, Sirena, Siroun, Sitara, Sitembile, Siv, Sive, Skyler, Sofi, + Solana, Solange, Soledad, Solita, Sondra, Sonia, Sonja, Sonya, Sophia, Sophie, + Sophronia, Spica, Stacey, Stacia, Stacy, Stefania, Stella, Stephani, Stephanie, + Ster, Stesha, Stockard, Storm, Sukatai, Suki, Sumi, Summer, Sun, Susan, Susanna, + Suzanne, Svetlana, Sybil, Sydelle, Sydney, Syeira, Sylvia, Syna, Synia, Tabitha, + Taci, Tacita, Tadi, Taffy, Tahirah, Tai, Taima, Tainn, Taipa, Taite, Taka, + Takara, Takiyah, Takoda, Talasi, Tale, Talia, Talia, Talitha, Tallulah, Tam, + Tama, Tamara, Tamary, Tamma, Tammy, Tanaka, Tani, Tansy, Tanya, Tao, Tara, Tate, + Tatyana, Tawnie, Tawny, Tayce, Taylor, Teague, Tehya, Tekla, Temina, Terentia, + Terese, Terrilyn, Tertia, Teryn, Tesia, Tess, Tessa, Thadea, Thais, Thalassa, + Thalia, Than, Thana, Thara, Thea, Thekla, Thelma, Theodosia, Theone, Thera, + Thirza, Thora, Thyra, Tia, Tiara, Tienette, Tierney, Tierra, Tiffany, Tilda, + Timandra, Tina, Tiponya, Tirza, Tivona, Tobey, Tola, Tora, Tori, Tory, Tosia, + Tove, Tracey, Tracy, Treasa, Tresa, Treva, Trianon, Tricia, Trilby, Trina, + Trind, Trish, Trisha, Trudy, Tryne, Tryphena, Tyne, Ula, Ulani, Ultima, Uma, + Una, Undine, Undine, Urania, Uriana, Ursula, Uta, Vala, Valentina, Valeria, + Valerie, Valeska, Valonia, Valora, Vanda, Vanessa, Vanora, Vanya, Vashti, Veda, + Velika, Velma, Venesssa, Vera, Verena, Verity, Veronica, Vesta, Vevila, + Victoria, Vidonia, Violet, Violet, Violetta, Virginia, Viridis, Viveka, Vivian, + Voleta, Vrinda, Wakanda, Wanda, Waneta, Wendy, Whilhelmina, Whitney, Wijdan, + Willow, Wilma, Wilona, Winda, Winema, Winifred, Winna, Winona, Wynee, Wynn, + Wynona, Xanthe, Xaveria, Xaviera, Xena, Xenia, Ximena, Xylia, Xylona, Yachne, + Yanice, Yarmilla, Yasmeen, Yasmin, Yelinda, Yenene, Yesmina, Yetta, Yeva, + Yokiko, Yolanda, Yolie, Yonina, Yovela, Yvella, Yvette, Yvonne, Zada, Zahara, + Zahirah, Zahra, Zakia, Zalea, Zalika, Zaltana, Zandra, Zara, Zarah, Zaza, + Zehava, Zelda, Zelenka, Zelia, Zella, Zena, Zenaide, Zenia, Zerlinda, Zeva, + Zevida, Zia, Ziazan, Zigana, Zila, Zina, Zinnia, Zita, Zoe, Zola, Zona, Zora, + Zorah, Zorda, Zosia, Zuleika, Zulema, Zuza, Zuzanny + + + Aaron, Aasin, Abbott, Abdel, Abdiel, Abel, Abijah, Abner, + Abraham, Abran, Ace, Achilles, Ackerley, Adair, Adam, Addison, Adeben, Adem, + Adiran, Adlai, Adler, Adley, Admon, Adolph, Adon, Adonis, Adrian, Adriel, + Aeneas, Agustin, Ahearn, Ahmik, Ahren, Aidan, Aiken, Aimery, Aitan, Ajayi, + Akando, Akbaar, Akello, Akil, Akshay, Alan, Aland, Alano, Alaric, Alastair, + Alben, Albert, Alcander, Alcott, Alden, Alder, Aldrick, Alec, Alek, Aleksy, + Aleron, Aleser, Alex, Alexander, Alfred, Alger, Alim, Alistair, Allaard, Allan, + Allard, Allen, Alonzo, Alphonse, Alphonso, Alston, Altair, Alton, Alvin, Amadeo, + Amadi, Amado, Ambrose, Amiel, Ammon, Amos, Amsden, Anders, Andre, Andreus, + Andrew, Andrey, Andries, Angelo, Angus, Anker, Anoki, Ansel, Anselme, Ansley, + Anson, Anthony, Antonio, Anwar, Archer, Archibald, Ardon, Aren, Ares, Argus, + Ari, Aricin, Arion, Aristo, Aristotle, Arkin, Arlen, Arley, Arlin, Arlo, Arman, + Armen, Armon, Armstrong, Arne, Arnold, Arnon, Aron, Arpiar, Arsen, Arsenio, + Arthur, Ashby, Asher, Ashford, Ashlin, Ashon, Ashur, Athan, Atheron, Atman, + Audric, Audun, Augustin, Augustus, Aurek, Austin, Averill, Avery, Axel, Bae, + Bailey, Baingana, Bakari, Balbo, Balder, Baldwin, Bale, Balendin, Bali, Balin, + Balint, Bancroft, Bandele, Bane, Banning, Baran, Barclay, Barden, Bardo, Bardon, + Barnabas, Barnaby, Barnett, Barney, Baron, Barrett, Barry, Barse, Bart, Barth, + Bartholomew, Barton, Basil, Bastiaan, Baul, Bavol, Baxter, Bay, Bayani, Bayard, + Baylor, Bazyli, Beacan, Beagan, Beaman, Beau, Beaumont, Beauregard, Beck, + Beldon, Belen, Bem, Beman, Ben, Benedict, Benen, Benjamin, Bennett, Benson, + Bent, Bentley, Benton, Berenger, Bergren, Berk, Berkeley, Bernard, Bersh, Bert, + Berthold, Berton, Bertram, Beval, Bevan, Bialy, Bilal, Bishop, Bitalo, Bjorn, + Blade, Blaine, Blair, Blaise, Blake, Blaz, Blorn, Bo, Boden, Bogart, Bohdan, + Bolton, Bond, Booker, Boone, Borden, Boris, Botan, Bowie, Bowman, Boyce, Boyd, + Boyden, Brad, Braden, Bradford, Bradney, Brady, Bram, Bran, Brand, Brandeis, + Brandon, Brant, Braxton, Bray, Braz, Brazil, Bren, Brencis, Brendan, Brendon, + Brennan, Brent, Brentan, Bret, Brett, Brewster, Briac, Brian, Briand, Brice, + Brieg, Brinley, Brishen, Brock, Broderick, Brodny, Brody, Bronson, Bront, Bruce, + Bruno, Brutus, Bryan, Bryant, Bryce, Bryson, Buck, Bud, Budo, Burgess, Burhan, + Burian, Burke, Burl, Burr, Burton, Byran, Byron, Cadeo, Cador, Caedmon, Cailan, + Cain, Caine, Calder, Caldwell, Caleb, Calvin, Cam, Camden, Cameron, Candan, + Canton, Canute, Carden, Carey, Carl, Carlin, Carlo, Carlos, Carlton, Carr, + Carrick, Carrocio, Carroll, Carson, Carson, Carter, Carver, Casey, Casper, + Cassidy, Cassius, Castel, Cato, Caton, Cavan, Ceasar, Cecil, Cedric, Cemal, + Chad, Chadwick, Chaim, Chal, Chale, Chalmers, Chander, Chandler, Chane, Chaney, + Channing, Chapin, Chapman, Charles, Charlton, Chase, Chatha, Chauncy, Chayton, + Chen, Cheney, Chester, Chet, Chevalier, Chike, Chin, Christian, Christoph, + Christopher, Christos, Chuck, Ciceron, Ciro, Clarence, Clark, Claude, Clay, + Clayton, Clement, Cleveland, Clifford, Clifton, Clint, Clinton, Clive, Clyde, + Cody, Colby, Cole, Coleman, Colin, Collin, Colon, Colton, Coman, Condon, Connor, + Conrad, Conway, Corbett, Corbin, Corcoran, Cordell, Corey, Cornelius, Cort, + Coty, Courtland, Craig, Crandall, Creighton, Crispin, Crosby, Cullen, Cullin, + Culver, Curran, Curtis, Cynric, Cyrano, Cyril, Cyrus, Dag, Dagan, Dakarai, + Dakota, Dale, Dallin, Dalton, Daly, Damek, Damen, Damian, Damien, Damion, Damon, + Dana, Dane, Daniel, Danior, Dannik, Dante, Daren, Darien, Dario, Darnell, + Darrel, Darrell, Darren, Daryl, David, Davin, Davis, Deacon, Dean, Decker, + Delaney, Delano, Delbert, Dellan, Delmore, Delsin, Deman, Dempsey, Dempster, + Denby, Dennis, Dennys, Denton, Denver, Der, Derek, Derrick, Derry, Deverell, + Devin, Devlin, Dewey, Diederik, Diego, Dieter, Dillon, Dimitri, Dirk, Dobry, + Dominic, Dominick, Donald, Donatien, Donato, Donnelley, Donnelly, Donovan, + Doron, Dougie, Douglas, Douglass, Dov, Doyle, Drake, Drew, Duane, Dugan, Duglas, + Duncan, Dunstan, Durand, Durriken, Dusan, Dustin, Dutch, Dwayne, Dwight, Dyami, + Dyastro, Dylan, Dymas, Eamon, Earl, Earle, Eaton, Edan, Edgan, Edgar, Edison, + Edmund, Edrin, Edward, Edwin, Edwin, Egan, Einar, Elad, Elden, Eldroth, Elek, + Eli, Elias, Elijah, Elkan, Ellery, Elliot, Elliott, Ellis, Ellsworth, Elmer, + Elmo, Elston, Elton, Elwood, Emanuel, Emil, Emilio, Emmett, Emo, Enoch, Enrico, + Enrique, Ephraim, Erek, Eric, Erik, Ernest, Erol, Errol, Erskine, Erwin, Eryx, + Essien, Esteban, Ethan, Eugene, Evan, Evander, Everett, Evzen, Ezekial, Ezra, + Fabio, Fairfax, Farley, Farrell, Faxon, Felix, Felix, Fenn, Fenton, Fergus, + Ferran, Ferris, Fielding, Filbert, Filmore, Finlay, Finley, Finn, Finnigan, + Fisk, Fitzgerald, Fletcher, Flindo, Flint, Floyd, Flynn, Forbes, Forrest, + Forsythe, Foster, Foster, Francis, Franek, Frank, Franklin, Frasier, Frazer, + Frazier, Fred, Frederick, Fremont, Fritz, Fuller, Fulton, Gabe, Gabriel, Gage, + Galen, Galeno, Galvin, Gamble, Gannon, Gareth, Garfield, Gargan, Garner, + Garrett, Garrick, Garridan, Garrison, Garritt, Garth, Garvin, Gary, Gaspar, + Gaston, Gavin, Gavrie, Gaylord, Gaynor, Geoff, Geoffrey, Geoffry, George, + Gerard, Gerik, Germain, Gerry, Gideon, Gilberto, Giles, Ginton, Givon, Glen, + Glenn, Glenno, Godfrey, Gordon, Gordy, Gorman, Grady, Graham, Gram, Granger, + Grant, Granville, Grayson, Greg, Greger, Gregor, Gregory, Gresham, Griffen, + Griffith, Guilhem, Gunnar, Gunther, Gus, Gustave, Guthrie, Guy, Hackett, Hadden, + Hadi, Hadley, Hadrian, Hagan, Hal, Halden, Hale, Halian, Halsey, Hamilton, + Hamlin, Hank, Hans, Harden, Hardy, Harith, Harlan, Harman, Harold, Harper, + Harrison, Harry, Hart, Hartley, Harvey, Hassan, Hastin, Hastings, Hayden, Hayes, + Haynes, Heath, Hector, Helaku, Henning, Henry, Herbert, Herman, Herschel, + Hilliard, Hilton, Hiroshi, Hobart, Hogan, Holden, Holt, Homes, Horace, Horton, + Houston, Howard, Howrence, Hoyt, Hugh, Hugo, Humphrey, Hunter, Huntley, Hyman, + Iain, Ian, Ilias, Ingmar, Ingram, Ira, Irvin, Irving, Irwin, Isaac, Isaiah, + Israel, Itzak, Ivan, Ivar, Jabari, Jabir, Jack, Jacob, Jacobe, Jacques, Jacson, + Jacy, Jafar, Jagger, Jake, Jal, Jaleel, Jamal, James, Jamison, Jared, Jarek, + Jarman, Jaron, Jarrod, Jarvis, Jason, Jasper, Javan, Javier, Jay, Jebidiah, Jed, + Jedidiah, Jedrek, Jeff, Jeffrey, Jelani, Jeremiah, Jeremy, Jerolin, Jerome, + Jeromy, Jerzy, Jesse, Jessee, Jethro, Jibril, Jin, Jiro, Jivin, Joel, Johann, + John, Jolon, Jonah, Jonathan, Jonathon, Jordan, Jordon, Jorgen, Jorvin, Joseph, + Joshua, Judd, Jude, Julian, Julius, Juma, Jung, Justin, Kadin, Kai, Kaikara, + Kaladin, Kalb, Kale, Kalil, Kalkin, Kalman, Kamal, Kane, Kaniel, Kardal, Karl, + Karsten, Kasch, Kasen, Kaspar, Kateb, Kayin, Keane, Kearney, Kedar, Keefe, + Keelan, Keenan, Kegan, Keir, Keir, Keith, Kelby, Keleman, Kell, Kellen, Kelvin, + Ken, Kenan, Kendall, Kendrick, Kenelm, Kenley, Kennard, Kennedy, Kenneth, Kent, + Kenton, Kenyon, Keona, Ker, Kerby, Kern, Kerry, Kers, Kersen, Kerwin, Kester, + Kevin, Khalil, Khoury, Kiefer, Kieran, Kiernan, Killian, Kin, Kinnel, Kinsey, + Kintan, Kip, Kirby, Kirk, Kiyoshi, Kliftin, Klog, Komor, Kontar, Krischan, + Krister, Kurt, Kyle, Kyler, Laethan, Laird, Lamar, Lamont, Lance, Lander, + Landon, Lane, Lang, Larry, Lars, Lawler, Lawrence, Lazarus, Lear, Lee, Leif, + Leighton, Leland, Len, Lennon, Lennor, Lennox, Lensar, Leo, Leon, Leonard, + Leron, Leroy, Lester, Lev, Levi, Lewis, Lewis, Li, Liam, Like, Lincoln, Lindsey, + Lionel, Llewellyn, Lloyd, Logan, Loren, Lorenzo, Lorne, Louis, Lowell, Lucas, + Lucian, Luis, Luke, Lukyan, Lunt, Luther, Lyle, Lyndon, Lysander, Mac, Macer, + Mack, Mackenzie, Magnus, Malcolm, Malik, Manco, Mandek, Mander, Manfred, + Manning, Mansur, Manuel, Marc, Marcos, Marcus, Marden, Marek, Mario, Mark, + Markham, Markos, Marlin, Marlon, Marlon, Marshal, Marshall, Marsten, Martin, + Martingo, Marvin, Mason, Matai, Mateo, Mather, Matthew, Matthias, Maurice, Max, + Maxwell, Maynard, Mayon, Mead, Meka, Mercer, Merill, Merle, Merrick, Merrik, + Meyer, Micael, Michael, Migon, Miguel, Mike, Mikkel, Mikos, Miles, Miles, Milo, + Milton, Miner, Mitchell, Monroe, Monte, Morgan, Morley, Morris, Mortimer, + Morton, Morven, Morz, Motega, Mukasa, Murdoch, Murdock, Murphy, Myles, Myron, + Naeem, Nalren, Nantan, Nathan, Nathaniel, Neal, Neale, Neil, Nelek, Nelson, + Neron, Nestor, Nevan, Neville, Nevin, Nevin, Nicanor, Nicholas, Nigel, Nikolos, + Nils, Noah, Nodin, Noe, Nolan, Norbert, Norman, Norris, Norton, Nuri, Nyle, + Oakes, Oakley, Ochen, Octavius, Odell, Odin, Odion, Odon, Ogden, Olaf, Olin, + Oliver, Omar, Ordano, Oren, Orion, Orman, Ormand, Orrin, Orson, Orville, Oscar, + Osgood, Osmond, Otis, Otto, Owen, Paco, Palmer, Paolo, Paris, Parker, Parnell, + Pascal, Patamon, Patrick, Patterson, Patton, Paul, Paulin, Pavel, Paxton, + Payton, Pearce, Peder, Pembroke, Penn, Percival, Perry, Peter, Peyton, Phearcy, + Philip, Phillip, Phillippe, Phoenix, Pierce, Pierre, Pierson, Pilan, Platon, + Porter, Prentice, Prescot, Prescott, Preston, Quentin, Quenton, Quillan, Quincy, + Quinlan, Quinn, Rad, Radcliffe, Radman, Rafael, Rafferty, Ragnar, Raidon, + Raleigh, Ralph, Ramiro, Ramon, Ramsay, Ramsey, Ranard, Rance, Randall, Randolph, + Ranen, Ranger, Rankin, Raoul, Raphael, Raul, Ravi, Ravi, Ravid, Ray, Raymond, + Raynor, Reade, Redford, Redmond, Reed, Reese, Reeve, Regan, Reginald, Regis, + Remington, Renaldo, Rendor, Renfry, Renny, Reuben, Rex, Reyhan, Rhett, Rhett, + Rhys, Ricardo, Richard, Richter, Rico, Rider, Ridgley, Rigby, Riley, Rimon, + Ringo, Ringo, Riodan, Riordan, Roarke, Robert, Roberto, Robi, Rockwell, Rod, + Roderick, Rodman, Rodney, Rodrigo, Roger, Roi, Roland, Roldan, Rolf, Ronald, + Ronan, Rooney, Rory, Roscoe, Ross, Roth, Rowan, Rowland, Roy, Royce, Ruben, + Rudd, Rudi, Rudyard, Rufus, Runako, Ruskin, Russ, Russell, Rusty, Rutherford, + Rutledge, Ryan, Ryder, Rylan, Sahale, Sahen, Salim, Saloman, Sam, Samien, + Sammon, Samson, Samuel, Sanders, Sandon, Sandor, Sanford, Sargent, Sarngin, + Sarojin, Saul, Saunders, Sawyer, Saxon, Schuyler, Scott, Sean, Sebastian, + Sebastien, Seif, Selby, Senon, Sergio, Seth, Seung, Severin, Sevilin, Seward, + Seymour, Shane, Shawn, Shea, Sheffield, Sheldon, Shen, Sheridan, Sherman, + Sherwin, Sherwood, Shing, Shunnar, Sidney, Siegfried, Silas, Simon, Sivan, Skip, + Skyler, Slade, Slevin, Smith, Solomon, Sorgan, Soterios, Spalding, Spencer, + Spenser, Standford, Stanley, Stanton, Stasio, Stefan, Stephan, Stephen, + Sterling, Stevan, Steve, Steven, Stewart, Stoke, Stoyan, Strom, Stuart, Subrey, + Sulaiman, Sullican, Sumner, Sutherland, Sutton, Sven, Sylvester, Tab, Tabari, + Tad, Tadi, Tai, Tajo, Talbart, Talbot, Talman, Talos, Tanek, Tanner, Tano, Taro, + Tate, Taurin, Taylor, Tem, Terence, Terrence, Terrill, Terry, Thaddeus, Thai, + Thaman, Thane, Thanos, Theobald, Theodore, Theron, Thierry, Thomas, Thorpe, + Thurston, Thurston, Tibalt, Tiernan, Timothy, Titus, Tobias, Toby, Tod, Todd, + Tomas, Tong, Tor, Torin, Torrance, Townsend, Travers, Travis, Tremain, Tremaine, + Trent, Trevor, Trey, Tristan, Troy, Tryon, Tucker, Tully, Tyee, Tyler, Tymon, + Tyrone, Upton, Uriah, Urian, Van, Vance, Vaughn, Vern, Vernon, Victor, Vincent, + Vinson, Virgil, Vito, Vlad, Vladimir, Vokes, Volf, Wade, Wagner, Walden, Waldo, + Walker, Wallace, Wally, Walter, Ward, Warner, Warren, Watson, Waylan, Wayland, + Waylon, Wayne, Webb, Webster, Wendell, Wesley, Weston, Weylin, Whitaker, Wilfen, + Will, Willard, Willem, William, Wilson, Winston, Winthrop, Wlby, Woody, Wyatt, + Xavier, Xenos, Xerxes, Ximen, Yakecan, Yale, Yancey, Yardley, Yarin, Yerik, + Yero, Yervant, York, Yusuf, Yves, Zachariah, Zachary, Zackery, Zaid, Zaide, + Zane, Zaniel, Zann, Zared, Zarek, Zeke, Zenon, Zion, Ziven, Zorn + + \ No newline at end of file diff --git a/Data/objects.xml b/Data/objects.xml new file mode 100644 index 000000000..1e577ba5b --- /dev/null +++ b/Data/objects.xml @@ -0,0 +1,2632 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/pageresponse.cfg b/Data/pageresponse.cfg new file mode 100644 index 000000000..a32fdd444 --- /dev/null +++ b/Data/pageresponse.cfg @@ -0,0 +1,10 @@ +Not Enough Info Please provide a more informative description of your problem so that we may better help you. +Bad Stuck We apologize, but we do not move players unless they are physically stuck. Take care! +Cannot Teleport We apologize, but staff members do not move players across the map. +Item Request We apologize, but we do not give items to players. +Murder System Query Hello! Information regarding the murder system is available here. +Pet Bonding Hail! The pet bonding system works just as it does on Origin's official UO shards. You can find all the information you need about it here! Take care! +Thief Guild In order to join the thief guild, your character must be at least one week old, must have been logged into the game for a total of 48 hours, and must have 60.0 or more stealing skill. We are not be able to tell you how long until your character will meet these requirements. Take care! +Password Change In order to change your password, please type:
[password newpassword newpassword

Be sure to replace newpassword with the password you would like to use. Take care! +Cannot Help Sorry, we cannot help you with that. +Online Request We do not reveal the online status of players. diff --git a/Data/shrink.cfg b/Data/shrink.cfg new file mode 100644 index 000000000..46ae209b1 --- /dev/null +++ b/Data/shrink.cfg @@ -0,0 +1,356 @@ +# All lines are trimmed. Empty lines and lines starting with '#' are ignored +# Table for converting body values to item numbers +# Format: +# BodyItem + +1 0x20DF +2 0x20D8 +3 0x25B8 +4 0x20D9 +5 0x211D +6 0x211A +7 0x25AF +8 0x2582 +9 0x2584 +10 0x2584 +11 0x25C4 +12 0x20D6 +13 0x25D7 +14 0x20D7 +15 0x25D9 +16 0x25DD +17 0x25AE +18 0x20D8 +19 0x25C4 +20 0x25C5 +21 0x25BF +22 0x258F +23 0x25D0 +24 0x25A4 +25 0x25D1 +26 0x25C7 +27 0x25D1 +28 0x25C6 +29 0x2592 +30 0x20DC +31 0x2595 +33 0x20DE +34 0x25D2 +35 0x20DE +36 0x20DE +37 0x25D2 +38 0x2585 +39 0x25A6 +40 0x2586 +41 0x25AE +42 0x25B7 +43 0x2587 +44 0x25B7 +45 0x25B7 +46 0x20D6 +47 0x20FA +48 0x25B9 +49 0x20D6 +50 0x25BC +51 0x20E8 +52 0x25C2 +53 0x20E9 +54 0x20E9 +55 0x20E9 +56 0x25BC +57 0x25BC +58 0x2100 +59 0x20D6 +60 0x20D6 +61 0x20D6 +62 0x20D6 +63 0x2583 +64 0x25A3 +65 0x25A3 +66 0x25C8 +67 0x258E +68 0x258F +69 0x258F +70 0x25CC +71 0x25C9 +72 0x25CB +73 0x2594 +74 0x259F +75 0x212D +76 0x25CD +77 0x25A2 +78 0x25A5 +79 0x25A5 +80 0x258C +81 0x2130 +82 0x25A5 +83 0x25A8 +84 0x25A8 +85 0x25AB +86 0x25AD +87 0x25AC +88 0x2108 +89 0x25C0 +90 0x25C1 +91 0x25C2 +92 0x25C2 +93 0x25C2 +94 0x20E8 +96 0x20E8 +97 0x2596 +98 0x2596 +99 0x25D1 +100 0x25D2 +101 0x2581 +102 0x2584 +103 0x20D6 +104 0x20D6 +105 0x20D6 +106 0x20D6 +107 0x20D7 +108 0x20D7 +109 0x20D7 +110 0x20D7 +111 0x20D7 +112 0x20D7 +113 0x20D7 +114 0x259B +115 0x259B +116 0x259C +117 0x259D +118 0x259E +119 0x259E +120 0x259E +121 0x259E +122 0x25CE +123 0x2589 +124 0x258A +125 0x258B +126 0x258B +127 0x2597 +128 0x25B6 +129 0x25C8 +130 0x258D +131 0x2590 +132 0x25A0 +133 0x2131 +134 0x25A1 +135 0x25A8 +136 0x25A9 +137 0x25AA +138 0x25AF +139 0x25AF +140 0x25B1 +141 0x2106 +142 0x25B7 +143 0x25B7 +144 0x25BA +145 0x20FB +146 0x25BB +147 0x25BD +148 0x25BE +150 0x20FB +151 0x20F1 +152 0x25CA +153 0x2591 +154 0x25A7 +155 0x20EC +157 0x25C3 +158 0x25D6 +159 0x25D8 +160 0x25D8 +161 0x25DA +162 0x25DB +163 0x25DC +164 0x20ED +165 0x2100 +166 0x20D7 +167 0x2118 +168 0x259F +170 0x20F6 +171 0x25B2 +172 0x20D6 +173 0x25C3 +175 0x2589 +176 0x25B6 +177 0x259C +178 0x259C +179 0x259C +180 0x20D6 +181 0x25AE +182 0x25AE +183 0x2106 +184 0x2107 +185 0x2106 +186 0x2107 +187 0x2615 +188 0x2615 +189 0x25B0 +190 0x21F1 +191 0x25A0 +192 0x25CE +193 0x2615 +194 0x2619 +195 0x260F +200 0x2598 +201 0x211B +202 0x2131 +203 0x2101 +204 0x259A +205 0x2125 +206 0x2131 +207 0x20EB +208 0x20D1 +209 0x2580 +210 0x25B2 +211 0x2118 +212 0x211E +213 0x20E1 +214 0x25B5 +215 0x20D0 +216 0x2103 +217 0x2588 +218 0x25B3 +219 0x25B4 +220 0x20F6 +221 0x20FF +223 0x20EB +225 0x25D3 +226 0x2599 +228 0x259E +231 0x20EF +232 0x20EF +233 0x20F0 +234 0x20D4 +237 0x20D4 +238 0x2123 +290 0x2101 +291 0x2126 +292 0x2127 +300 0x2620 +301 0x2621 +302 0x2622 +303 0x2623 +304 0x2624 +305 0x2625 +306 0x2626 +307 0x2627 +308 0x2628 +309 0x2629 +310 0x262A +311 0x262B +312 0x262C +313 0x262D +314 0x262E +315 0x262F +316 0x2630 +317 0x2631 +318 0x2632 +319 0x2633 +400 0x2106 +401 0x2107 +402 0x2106 +403 0x2107 +744 0x2106 +745 0x2107 +746 0x260E +747 0x262A +748 0x25C7 +749 0x25A4 +750 0x2106 +751 0x2107 +752 0x2610 +753 0x258D +754 0x258D +755 0x258D +756 0x260C +757 0x260B +758 0x258D +763 0x260B +764 0x261F +765 0x261E +766 0x25FC +767 0x25F9 +768 0x2612 +769 0x25F9 +770 0x261C +771 0x261D +772 0x25F8 +773 0x25FA +774 0x25FB +775 0x2613 +776 0x2611 +777 0x260D +778 0x261A +779 0x2607 +780 0x2608 +781 0x2604 +782 0x2603 +783 0x2602 +784 0x2605 +785 0x260E +786 0x260A +787 0x260F +788 0x2618 +789 0x2614 +790 0x2616 +791 0x260F +792 0x2609 +793 0x2617 +794 0x2619 +795 0x2611 +796 0x2611 +797 0x20D6 +798 0x20D6 +799 0x2619 +804 0x2602 +805 0x2604 +806 0x2603 +807 0x2602 +808 0x2602 +820 0x259A +825 0x25B2 +826 0x25B3 +827 0x25B4 +828 0x20F6 +831 0x211A +832 0x211A +833 0x211A +834 0x2597 +835 0x2125 +837 0x25C0 +838 0x25C1 +839 0x2615 +840 0x2615 +841 0x2615 +842 0x2615 +843 0x2123 +846 0x2599 +848 0x259E +970 0x2106 +990 0x2106 +991 0x2106 +994 0x2106 +998 0x25A0 + +#SE Mobiles + +246 0x2763 +254 0x2764 +242 0x2765 +199 0x2766 +247 0x2767 +248 0x2768 +196 0x2769 +243 0x276A +240 0x276B +252 0x276C +241 0x276D +251 0x276E +244 0x276F +250 0x2770 +249 0x2771 +255 0x2772 +253 0x2773 +245 0x281B +169 0x281C diff --git a/Data/signs.cfg b/Data/signs.cfg new file mode 100644 index 000000000..1d6e5250d --- /dev/null +++ b/Data/signs.cfg @@ -0,0 +1,509 @@ +0 3032 373 904 -1 #1016093 +0 2996 2769 976 0 #1016315 +0 3016 2634 2088 10 #1016095 +0 2991 2632 2102 10 #1016417 +0 3026 2666 2096 5 #1016082 +0 3025 2664 2192 4 #1016216 +0 2988 2706 2152 0 #1016138 +0 2996 2714 2104 0 #1016345 +0 3011 2688 2232 -4 #1016346 +0 3023 2712 2178 0 #1016083 +0 3019 2744 2248 -4 #1016217 +0 3084 2732 2192 0 #1023083 +0 3019 2840 873 0 #1016274 +0 2981 2840 878 0 #1016361 +0 3007 2840 801 0 #1016421 +0 3053 2864 731 0 #1016342 +0 3016 2857 856 0 #1016316 +0 3021 2860 801 0 #1016347 +0 3025 2848 929 0 #1016091 +0 3075 2856 910 0 #1016293 +0 3083 2896 673 0 #1016333 +0 2990 2894 664 0 #1016286 +0 3009 2888 721 0 #1016356 +0 3018 2905 720 0 #1016296 +0 3035 2904 737 0 #1016335 +0 2983 2904 794 0 #1016308 +0 3050 2919 680 0 #1016294 +0 2990 2912 680 0 #1016325 +0 2992 2913 808 0 #1016312 +0 2987 2928 852 0 #1016142 +0 3011 2916 905 0 #1016328 +0 2982 2969 624 0 #1016361 +0 3140 2958 712 0 #1016288 +0 2993 2952 929 0 #1016414 +0 3020 2977 640 0 #1016275 +0 2995 2976 875 0 #1016416 +0 3057 2976 817 0 #1016305 +0 3013 3000 833 0 #1016326 +0 2994 2993 824 0 #1016327 +0 2980 2995 768 0 #1016373 +0 2985 3000 777 0 #1016289 +0 3025 3024 777 0 #1016120 +0 3084 2890 3479 15 #1023083 +0 2981 2888 3502 10 #1016256 +0 2986 2909 3496 10 #1016244 +0 2999 2912 3517 10 #1016249 +0 2984 2931 3512 10 #1016390 +0 2995 2976 3406 15 #1016285 +0 2988 2994 3440 15 #1016246 +0 2990 3005 3360 15 #1016247 +0 2980 2977 3360 15 #1016220 +0 3007 3024 3430 15 #1016248 +0 3019 3016 3386 15 #1016268 +0 3014 3009 3360 15 #1016077 +0 3053 3054 3408 15 #1016245 +0 3006 3053 3376 15 #1016255 +0 3025 3561 1174 9 #1016199 +0 2985 3554 1175 0 #1016204 +0 3006 3551 1194 0 #1016203 +0 3026 3551 1178 0 #1016205 +0 3016 3551 1186 0 #1016202 +0 3015 3554 1199 0 #1016224 +0 2997 3704 1213 0 #1016239 +0 3004 3743 1207 0 #1016057 +0 2993 3720 1224 0 #1022993 +0 2995 3740 1314 0 #1016232 +0 2987 3720 1380 0 #1016099 +0 3025 3732 1394 0 #1016198 +0 3085 3753 1240 14 #1016201 +0 3012 3748 1256 0 #1016357 +0 2981 3784 1264 0 #1016271 +0 3011 3784 1231 0 #1016192 +0 3026 3776 1196 0 #1016091 +0 3010 3778 1176 0 #1016153 +2 2979 3632 2537 0 The Shakin' Bakery +2 2982 3680 2480 0 A Stitch in Time Tailor Shop +2 2985 3720 2650 20 The Prime Cut Butcher Shop +2 2988 3658 2585 0 The Healers of Haven +2 2989 3632 2577 19 The Little Shop of Magic +2 2992 3646 2512 0 Carpenters of Haven +2 2996 3670 2624 0 The Bountiful Harvest Inn +2 2998 3634 2640 0 Mapmakers of Haven +2 3012 3665 2657 4 The Albatross Bar and Grill +2 3015 3645 2609 0 The Haven Blacksmith +2 3020 3666 2600 0 #1016162 +2 3025 3613 2585 0 Uzeraan's mansion +2 3026 3666 2512 0 Haven Clockworks and Tinker Shop +2 3026 3690 2520 0 The Haven Thieves' Guild +2 3036 3658 2536 0 #1016057 +2 3083 3624 2610 2 The Second Bank of Haven +2 2966 3617 2480 0 Haven Public Library +1 3025 3608 2609 0 #1016061 +1 2980 3602 2584 0 #1016197 +1 3049 3632 2537 0 #1023049 +1 3018 3646 2512 0 #1016211 +1 2966 3617 2480 0 #1016207 +1 2994 3634 2648 0 #1016042 +1 2988 3625 2616 0 #1016140 +1 3020 3634 2584 0 #1016162 +0 3053 3673 2138 24 #1016171 +0 3009 3673 2184 30 #1016300 +0 2981 3672 2232 20 #1016263 +0 2998 3669 2261 20 #1016307 +1 3036 3658 2536 0 #1016057 +1 3004 3675 2480 0 #1016115 +1 3012 3665 2657 4 #1016276 +1 2996 3670 2624 0 #1016283 +1 3016 3647 2600 0 #1016132 +1 2982 3656 2592 0 #1016022 +0 2995 3704 2167 20 #1016362 +0 2980 3681 2176 20 #1016284 +0 2988 3685 2231 20 #1016139 +0 3060 3682 2261 20 #1016121 +1 3084 3690 2520 0 #1016054 +0 3084 3728 2160 20 #1016051 +0 3026 3720 2184 20 #1016091 +0 2989 3712 2215 20 #1016173 +0 3011 3736 2230 20 #1016128 +0 3081 3712 2242 20 #1016178 +1 2985 3720 2650 20 #1016270 +0 2980 3753 2232 20 #1016049 +0 3048 4548 873 37 #1016107 +0 2992 4414 1096 0 #1016502 +0 2980 4390 1072 0 #1016165 +0 2988 4396 1088 0 #1016185 +0 3007 4400 1115 0 #1016331 +0 2985 4400 1132 0 #1016497 +0 2996 4407 1168 0 #1016354 +0 3014 4419 1136 0 #1016146 +0 3013 4424 1108 0 #1016186 +0 3019 4424 1062 0 #1016506 +0 3008 4436 1168 0 #1016241 +0 3026 4477 1130 0 #1016091 +0 3026 4478 1092 0 #1016111 +0 2982 4459 1072 0 #1016238 +0 2990 4451 1096 0 #1016188 +0 3084 4469 1176 0 #1016116 +0 2996 4483 1072 0 #1016187 +0 3017 4528 1069 0 #1016184 +0 3025 4537 1376 23 #1016076 +0 3026 4518 1402 21 #1016076 +0 3026 4308 1012 0 #1016324 +0 3051 4632 1200 0 #1016149 +0 3025 520 986 0 #1016367 +0 3026 547 832 0 #1016434 +0 2987 544 970 0 #1016143 +0 2985 544 1008 0 #1016436 +0 2979 557 1000 0 #1016317 +0 3022 563 976 0 #1016364 +0 2991 576 1009 0 #1016067 +0 3026 636 856 5 #1016106 +0 3025 624 886 2 #1016432 +0 3026 616 984 0 #1016091 +0 3011 568 2177 0 #1016355 +0 3084 595 2152 0 #1016055 +0 3055 576 2145 0 #1016229 +0 3007 592 2170 0 #1016189 +0 3024 578 2128 0 #1016060 +0 2986 582 2192 0 #1016069 +0 3022 593 2216 0 #1016066 +0 3019 592 2225 0 #1016266 +0 3021 600 2209 0 #1016066 +0 2985 592 2185 0 #1016069 +0 3023 576 2209 0 #1016258 +0 2992 633 2168 0 #1016084 +0 3026 617 2144 0 #1016091 +0 3023 616 2154 0 #1016111 +0 3024 610 2168 0 #1016111 +0 2997 608 2273 0 #1016267 +0 2995 616 2232 0 #1016299 +0 2988 621 2224 0 #1016141 +0 3015 632 2185 0 #1016208 +0 2990 609 2184 0 #1016168 +0 3008 648 2168 0 #1016127 +0 2990 656 2144 0 #1016191 +0 2982 648 2192 0 #1016251 +0 2989 5305 94 19 #1016277 +0 2995 5204 4060 37 #1016278 +0 3020 5217 4019 48 #1016365 +0 3016 5228 4008 37 #1016439 +0 3084 5270 3988 36 #1016430 +0 2989 5291 3982 37 #1016148 +0 3000 5304 3997 37 #1016262 +0 3084 5674 3139 10 #1016431 +0 3009 5671 3148 21 #1016264 +0 2999 5678 3286 10 #1016261 +0 2991 5703 3210 10 #1016290 +0 3013 5720 3205 19 #1016348 +0 2986 5698 3289 24 #1016360 +0 2980 5744 3209 16 #1016193 +0 2987 5740 3216 6 #1016313 +0 2990 5730 3204 19 #1016403 +0 2984 5729 3250 26 #1016391 +0 3019 5740 3267 13 #1016036 +0 2996 5774 3175 20 #1016318 +0 2994 5804 3282 16 #1016215 +0 3007 5802 3287 13 #1016349 +0 2999 1306 1761 25 #1016260 +0 3076 1342 1744 20 #1023075 +0 3008 1370 1584 30 #1016322 +0 3053 1360 1776 15 #1016072 +0 3000 1386 1664 30 #1016262 +0 3073 1432 1656 10 #1016369 +0 2992 1433 1600 20 #1016353 +0 3025 1420 1595 30 #1016071 +0 3054 1425 1584 30 #1016332 +0 7977 1421 1638 50 #1016221 +0 7977 1470 1643 34 #1016503 +0 7977 1480 1643 34 #1016503 +0 7977 1570 1526 46 #1016499 +0 7977 1539 1527 45 #1016505 +0 4762 1459 1622 35 #1016419 +0 4764 1428 1622 32 #1016419 +0 4762 1489 1627 34 #1016419 +0 4765 1428 1622 37 #1016504 +0 3015 1428 1546 30 #1016311 +0 3084 1436 1693 0 #1016302 +0 2997 1424 1747 10 #1016340 +0 3011 1432 1730 20 #1016291 +0 3008 1446 1654 10 #1016145 +0 2979 1460 1610 20 #1016126 +0 3086 1454 1600 20 #1016320 +0 3086 1441 1584 20 #1016320 +0 3023 1440 1611 20 #1016280 +0 3004 1454 1561 30 #1016163 +0 3009 1458 1683 0 #1016222 +0 2982 1470 1696 0 #1016323 +0 3020 1468 1676 0 #1016073 +0 2986 1449 1728 1 #1016295 +0 3070 1444 1670 10 #1016046 +0 2996 1474 1520 20 #1016338 +0 2987 1480 1610 20 #1016137 +0 2996 1501 1627 25 #1016269 +0 3007 1489 1587 32 #1016265 +0 2990 1488 1572 30 #1016359 +0 3022 1472 1584 20 #1016225 +0 3082 1477 1600 20 #1016178 +0 3011 1502 1689 20 #1016281 +0 2966 1493 1724 5 #1016304 +0 2993 1480 1746 0 #1022993 +0 3013 1508 1662 20 #1016110 +0 3026 1505 1576 20 #1016074 +0 3000 1516 1553 36 #1016287 +0 3083 1328 3771 0 #1016155 +0 2995 1368 3829 0 #1016334 +0 2981 1360 3786 0 #1016038 +0 3007 1368 3752 0 #1016098 +0 3008 1362 3768 0 #1016098 +0 3033 1368 3713 0 #1016080 +0 2979 1368 3733 0 #1016049 +0 3026 1391 3832 0 #1016111 +0 3025 1400 3825 0 #1016111 +0 2995 1376 3814 0 #1016334 +0 2965 1400 3769 0 #1016158 +0 3016 1385 3712 0 #1016117 +0 3026 1425 3704 0 #1016091 +0 2992 1425 3824 0 #1016122 +0 2984 1411 3808 0 #1016123 +0 3020 1434 3800 0 #1016194 +0 3012 1433 3776 0 #1016314 +0 2988 1409 3792 0 #1016157 +0 3015 1424 3859 0 #1016420 +0 2990 1427 3993 10 #1016159 +0 2998 1449 3752 0 #1016237 +0 3007 1448 3721 0 #1016242 +0 3059 1440 3746 0 #1016206 +0 3007 1448 3721 0 #1016242 +0 3029 1440 3864 0 #1016133 +0 3008 1449 3864 0 #1016154 +0 3007 1464 3849 0 #1016154 +0 3010 1443 3993 10 #1016343 +0 2985 1464 4024 0 #1016114 +0 2982 1458 4008 0 #1016038 +0 3085 1441 4010 10 #1016214 +0 3008 1472 3872 0 #1016085 +0 2982 1555 1665 37 #1016350 +0 3012 1549 1776 10 #1016374 +0 3077 1592 1562 20 #1016089 +0 2990 1590 1665 22 #1016151 +0 3024 1575 1721 46 #1016236 +0 3007 1640 1688 32 #1016045 +0 3026 1505 1528 40 #1016091 +0 3026 1376 3752 0 #1016156 +0 3025 624 2112 0 #1016091 +0 2992 561 1016 0 #1016067 +0 3012 2970 3432 15 #1016298 +0 3026 3019 768 0 #1016111 +0 3025 2863 989 0 #1016272 +0 3050 4686 1423 0 #1023050 +0 2999 3043 3463 25 #1016249 +0 3012 3011 3464 15 #1016119 +0 3025 2968 3367 15 #1016091 +0 2984 3712 2136 20 #1016368 +0 7977 1614 1636 40 #1016104 +0 7976 1400 1622 50 #1016088 +0 7976 1400 1630 50 #1016047 +0 7976 1495 1643 35 #1016250 +0 7976 1495 1640 35 #1016226 +0 4764 1584 1527 56 #1016500 +0 4765 1584 1527 52 #1016233 +0 4765 1546 1624 23 #1016233 +0 4764 1546 1624 27 #1016233 +0 4759 1565 1584 43 #1016233 +0 4766 1532 1705 35 #1016233 +0 4761 1537 1668 35 #1016233 +0 4762 1561 1670 36 #1016233 +0 4765 1566 1678 35 #1016233 +0 4759 1559 1695 45 #1016233 +0 4766 1599 1571 36 #1016233 +0 4761 1599 1571 33 #1016233 +0 4761 1572 1600 29 #1016233 +0 4765 1595 1678 22 #1016059 +0 4759 1595 1678 25 #1016059 +0 4762 1617 1679 35 #1016059 +0 4764 1562 1720 50 #1016059 +0 4765 1664 1592 15 #1016059 +0 4765 1676 1625 10 #1016059 +0 4759 1640 1680 33 #1016059 +0 4765 1667 1559 38 #1016059 +0 4765 1664 1574 20 #1016059 +0 4766 1558 1753 30 #1016059 +0 4762 1552 1728 37 #1016310 +0 4765 1552 1728 34 #1016310 +0 4765 1532 1705 33 #1016310 +0 4765 1485 1709 15 #1016310 +0 4761 1488 1731 15 #1016310 +0 4764 1468 1732 15 #1016310 +0 4764 1441 1732 19 #1016310 +0 4764 1415 1736 25 #1016310 +0 4761 1415 1736 31 #1016504 +0 4765 1424 1701 18 #1016504 +0 4765 1422 1672 25 #1016504 +0 4766 1439 1585 35 #1016504 +0 4765 1435 1570 45 #1016504 +0 4765 1432 1538 47 #1016504 +0 4764 1432 1538 42 #1016500 +0 4765 1498 1536 46 #1016500 +0 4764 1519 1525 55 #1016500 +0 4762 1362 1756 30 #1016396 +0 4764 1362 1756 23 #1016393 +0 4761 1375 1811 18 #1016393 +0 4759 1355 1835 16 #1016393 +0 4761 1392 1892 9 #1016393 +0 4764 1314 1749 26 #1016393 +0 4764 1262 1742 9 #1016393 +0 7976 1580 2484 4 Only a mind at peace can approach the shrine +0 7976 1604 2484 7 A calm mind may travel where others may not tread +0 4762 1262 1742 15 #1016401 +0 4766 1262 1742 12 #1016398 +0 4764 1392 1892 13 #1016402 +0 4765 1392 1892 18 #1016398 +0 4765 1355 1835 16 #1016398 +0 4766 1375 1811 14 #1016398 +0 4765 1362 1756 27 #1016398 +0 4764 1643 1521 50 #1016394 +0 3025 1557 1613 21 #1016292 +0 2995 1600 1589 20 #1016377 +0 3012 1610 1592 0 #1016352 +0 3024 1619 1769 70 #1016376 +0 3020 1602 1720 20 #1016223 +0 3010 1660 1648 0 #1016018 +0 3011 1632 1586 0 #1016352 +0 3026 1634 1673 26 #1016091 +0 3083 1819 2825 0 #1016050 +0 3024 1850 2777 0 #1016152 +0 2996 1841 2744 0 #1016371 +0 2999 1828 2743 0 #1016408 +0 2989 1852 2709 10 #1016107 +0 3074 1855 2688 0 #1016392 +0 3074 1844 2688 0 #1016392 +0 3020 1851 2800 -8 #1016078 +0 2980 1881 2814 6 #1016048 +0 3008 1899 2664 0 #1016252 +0 3083 1904 2686 10 #1016407 +0 3025 1912 2713 20 #1016406 +0 2988 1914 2813 0 #1016405 +0 3010 1901 2814 0 #1016344 +0 3026 1896 2842 20 #1016091 +0 3016 1933 2776 10 #1016147 +0 3075 1942 2736 10 #1016079 +0 3023 1939 2701 30 #1016179 +0 3024 1938 2694 20 #1016058 +0 3024 1947 2691 30 #1016058 +0 3011 1942 2791 0 #1016319 +0 3024 1959 2695 20 #1016058 +0 2966 2002 2728 30 #1016212 +0 3000 2005 2813 -1 #1016118 +0 2982 1986 2846 15 #1016037 +0 3023 1997 2864 10 #1016404 +0 2985 2000 2888 5 #1016372 +0 2996 2030 2813 9 #1016351 +0 3059 2037 2843 0 #1016259 +0 3023 2221 1193 4 #1016112 +0 3007 2224 1165 0 #1016375 +0 3083 2232 1199 0 Bank of Cove +0 2987 2248 1227 0 #1016313 +0 3024 2428 536 0 #1016181 +0 3053 2459 492 15 #1016329 +0 3020 2459 432 15 #1016341 +0 2986 2442 416 15 #1016358 +0 3003 2432 555 0 #1016336 +0 2984 2472 458 15 #1016124 +0 3076 2482 440 15 #1016337 +0 3012 2474 405 15 #1016279 +0 3008 2525 578 0 #1016422 +0 3000 2521 379 23 #1016262 +0 2992 2510 482 15 #1016339 +0 3028 2500 440 15 #1016309 +0 3026 2523 536 0 #1016363 +0 2988 2576 603 0 #1016313 +0 3084 2505 560 0 #1016052 +0 3020 2534 560 0 #1016366 +0 3016 2470 569 5 #1016306 +0 3076 2016 2753 30 #1016409 +0 3022 618 1152 0 #1016129 +0 2998 626 1040 0 #1016130 +0 3083 3768 1313 0 #1016053 +0 3024 5351 64 15 #1016164 +0 2987 5267 131 20 #1016425 +0 2965 5250 184 22 #1016240 +0 2966 5236 155 15 #1016321 +0 2995 5222 183 8 #1016427 +0 3014 5217 124 0 #1016170 +0 2982 5200 98 5 #1016426 +0 2995 5168 35 22 #1016243 +0 3019 5158 107 5 #1016169 +0 2990 5152 77 28 #1016424 +0 3056 745 2160 0 #1016229 +4 3083 996 517 -50 #1061816 +4 3015 981 511 -50 #1061813 +4 2981 981 526 -50 #1061814 +4 2990 1000 523 -50 #1061818 +4 2992 1001 518 -50 #1061817 +4 3020 994 509 -50 #1061815 +4 2995 1005 522 -30 #1061819 +4 3000 1021 505 -70 #1061820 +4 3015 1997 1369 -87 #1061800 +4 3000 1994 1326 -91 #1061804 +4 3020 2014 1338 -76 #1061809 +4 2979 2023 1347 -90 #1061810 +4 3011 2030 1348 -89 #1061807 +4 2989 2038 1385 -88 #1061799 +4 3009 2055 1390 -90 #1061802 +4 3083 2056 1338 -85 #1061803 +4 2995 2047 1312 -90 #1061808 +4 2992 2069 1298 -81 #1061805 +4 2982 2075 1339 -82 #1061801 +4 2988 2085 1379 -90 #1061806 +4 2995 1067 1431 -88 #1061616 +4 3011 962 641 -90 #1061835 +4 2979 962 633 -90 #1061836 +3 2965 860 654 -39 Librum +3 2965 877 654 -39 Librum +3 2965 861 632 -34 Librum +3 2965 865 632 -34 Librum +3 2965 873 632 -34 Librum +3 2965 877 632 -34 Librum +3 2981 882 616 -34 Skis-In-Lem +3 2981 886 616 -34 Skis-In-Lem +3 2984 780 638 1 Atri-Ben-In-Ailem +3 2984 780 646 1 Atri-Ben-In-Ailem +3 2987 858 592 -33 In-Mani-Lem +3 2987 862 592 -33 In-Mani-Lem +3 2990 837 582 1 Ter-Ort-Lem +3 2995 836 703 1 Ter-Zu +3 2995 845 703 1 Ter-Zu +3 3007 817 694 -38 Aglo-In-Lem +3 3007 822 694 -38 Aglo-In-Lem +3 3007 819 592 -34 Agra-Char-In-Lem +3 3007 823 592 -34 Agra-Char-In-Lem +3 3008 814 596 -34 Agra-Char-In-Lem +3 3008 814 600 -34 Agra-Char-In-Lem +3 3008 814 685 -34 Aglo-In-Lem +3 3008 814 689 -34 Aglo-In-Lem +3 3009 858 694 -39 Klar-Lap-In-Lem +3 3009 863 694 -39 Klar-Lap-In-Lem +3 3015 788 655 1 Zhel-In-Lem +3 3015 796 655 1 Zhel-In-Lem +3 3019 830 678 -39 Ben-In-Lem +3 3019 835 678 -39 Ben-In-Lem +3 3019 831 608 -34 Ben-In-Lem +3 3019 835 608 -34 Ben-In-Lem +3 3025 788 630 1 Lap-In-Lem +3 3025 796 630 1 Lap-In-Lem +3 3025 881 670 -39 Ter-An-Eks-Por +3 3025 886 670 -39 Ter-An-Eks-Por +3 3083 845 678 -39 Atri-Aur +3 3083 850 678 -39 Atri-Aur +3 3083 846 608 -34 Atri-Aur +3 3083 850 608 -34 Atri-Aur +3 3140 868 684 -39 Lap-In-Lem +3 3140 868 689 -39 Lap-In-Lem +3 2996 787 1137 -13 #1016183 +5 2996 677 1220 25 #1063398 +5 3012 694 1229 25 #1063399 +5 2966 675 1280 32 #1063400 +5 2966 680 1280 32 #1063400 +5 2989 707 1300 25 #1063401 +5 3010 718 1301 25 #1063402 +5 5019 736 1281 25 #1063403 +5 3025 768 1298 25 #1063404 +5 3021 789 1302 25 #1063405 +5 3000 790 1284 25 #1063406 +5 3016 764 1255 25 #1063407 +5 2988 780 1227 25 #1063408 +5 3024 729 1229 25 #1063409 +5 3025 710 1251 25 #1063410 +5 3025 710 1257 25 #1063410 +5 3025 710 1270 25 #1063410 \ No newline at end of file diff --git a/Data/treasure.cfg b/Data/treasure.cfg new file mode 100644 index 000000000..34b50ac7e --- /dev/null +++ b/Data/treasure.cfg @@ -0,0 +1,200 @@ +2643 851 +2672 392 +3404 238 +3369 637 +2740 435 +2770 345 +2781 289 +2836 233 +3014 250 +3082 202 +3246 245 +3375 458 +1314 317 +1470 229 +1504 366 +2340 645 +2350 688 +2395 723 +2433 767 +1319 889 +1414 771 +1529 753 +1555 806 +1511 967 +1510 1071 +1562 1058 +961 506 +1162 189 +2337 1159 +2392 1154 +2517 1066 +2458 1042 +1658 2030 +2062 1962 +2089 1987 +2097 1975 +2066 1979 +2058 1992 +2071 2007 +2093 2006 +2187 1991 +1689 1993 +1709 1964 +1726 1998 +1731 2016 +1743 2028 +1754 2020 +2033 1942 +2054 1962 +581 1455 +358 1337 +208 1444 +199 1461 +348 1565 +620 1706 +1027 1180 +1037 1975 +1042 1960 +1042 1903 +1034 1877 +1018 1859 +980 1849 +962 1858 +977 1879 +968 1884 +969 1893 +974 1992 +989 1992 +1024 1990 +2648 2167 +2956 2200 +2968 2170 +2957 2150 +2951 2177 +2629 2221 +2642 2289 +2682 2290 +2727 2309 +2782 2293 +2804 2255 +2850 2252 +2932 2240 +3568 2402 +3702 2825 +3511 2421 +3533 2471 +3417 2675 +3425 2723 +3476 2761 +3541 2784 +3558 2819 +3594 2825 +4419 3117 +4471 3188 +4507 3227 +4496 3241 +4642 3369 +4694 3485 +4448 3130 +4453 3148 +4500 3108 +4513 3103 +4424 3152 +4466 3209 +4477 3230 +4477 3282 +2797 3452 +2803 3488 +2793 3519 +2831 3511 +2989 3606 +3035 3603 +1427 2405 +1466 2181 +1519 2214 +1523 2150 +1541 2115 +1534 2189 +1546 2221 +1595 2193 +1619 2236 +1654 2268 +1655 2304 +1433 2381 +1724 2288 +1703 2318 +1772 2321 +1758 2333 +1765 2430 +1647 2641 +1562 2705 +1670 2808 +1471 2340 +1562 2312 +1450 2301 +1437 2294 +1478 2273 +1464 2245 +1439 2217 +1383 2840 +1388 2984 +1415 3059 +1602 3012 +1664 3062 +2062 2144 +2178 2151 +2104 2123 +2098 2101 +2123 2120 +2130 2108 +2129 2132 +2153 2120 +2162 2148 +2186 2144 +492 2027 +477 2044 +451 2053 +468 2087 +465 2100 +958 2504 +1025 2702 +1290 2735 +2013 3269 +2149 3362 +2097 3384 +2039 3427 +2516 3999 +2453 3942 +2528 3982 +2513 3962 +2512 3918 +2513 3901 +2480 3908 +2421 3902 +2415 3920 +2422 3928 +2370 3428 +2341 3482 +2360 3507 +2387 3505 +2152 3950 +2157 3924 +2140 3940 +2143 3986 +2154 3983 +2162 3988 +2467 3581 +2527 3585 +2482 3624 +2535 3608 +1090 3110 +1094 3131 +1073 3133 +1076 3156 +1068 3182 +1096 3178 +1129 3403 +1135 3445 +1162 3469 +1128 3500 \ No newline at end of file diff --git a/Scripts/Accounting/AccessRestrictions.cs b/Scripts/Accounting/AccessRestrictions.cs new file mode 100644 index 000000000..71d2efb74 --- /dev/null +++ b/Scripts/Accounting/AccessRestrictions.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Misc; + +namespace Server +{ + public class AccessRestrictions + { + public static void Initialize() + { + EventSink.SocketConnect += new SocketConnectEventHandler( EventSink_SocketConnect ); + } + + private static void EventSink_SocketConnect( SocketConnectEventArgs e ) + { + try + { + IPAddress ip = ((IPEndPoint)e.Socket.RemoteEndPoint).Address; + + if ( Firewall.IsBlocked( ip ) ) + { + Console.WriteLine( "Client: {0}: Firewall blocked connection attempt.", ip ); + e.AllowConnection = false; + return; + } + else if ( IPLimiter.SocketBlock && !IPLimiter.Verify( ip ) ) + { + Console.WriteLine( "Client: {0}: Past IP limit threshold", ip ); + + using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) ) + op.WriteLine( "{0}\tPast IP limit threshold\t{1}", ip, DateTime.Now ); + + e.AllowConnection = false; + return; + } + } + catch + { + e.AllowConnection = false; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/Account.cs b/Scripts/Accounting/Account.cs new file mode 100644 index 000000000..f8598036c --- /dev/null +++ b/Scripts/Accounting/Account.cs @@ -0,0 +1,1173 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Security; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using Server; +using Server.Misc; +using Server.Mobiles; +using Server.Network; + +namespace Server.Accounting +{ + public class Account : IAccount, IComparable, IComparable + { + public static readonly TimeSpan YoungDuration = TimeSpan.FromHours( 40.0 ); + + private string m_Username, m_PlainPassword, m_CryptPassword, m_NewCryptPassword; + private AccessLevel m_AccessLevel; + private int m_Flags; + private DateTime m_Created, m_LastLogin; + private TimeSpan m_TotalGameTime; + private List m_Comments; + private List m_Tags; + private Mobile[] m_Mobiles; + private string[] m_IPRestrictions; + private IPAddress[] m_LoginIPs; + private HardwareInfo m_HardwareInfo; + + /// + /// Deletes the account, all characters of the account, and all houses of those characters + /// + public void Delete() + { + for ( int i = 0; i < this.Length; ++i ) + { + Mobile m = this[i]; + + if ( m == null ) + continue; + + ArrayList list = Multis.BaseHouse.GetHouses( m ); + + for ( int j = 0; j < list.Count; ++j ) + ( (Item) list[j] ).Delete(); + + m.Delete(); + + m.Account = null; + m_Mobiles[i] = null; + } + + Accounts.Remove( m_Username ); + } + + /// + /// Object detailing information about the hardware of the last person to log into this account + /// + public HardwareInfo HardwareInfo + { + get { return m_HardwareInfo; } + set { m_HardwareInfo = value; } + } + + /// + /// List of IP addresses for restricted access. '*' wildcard supported. If the array contains zero entries, all IP addresses are allowed. + /// + public string[] IPRestrictions + { + get { return m_IPRestrictions; } + set { m_IPRestrictions = value; } + } + + /// + /// List of IP addresses which have successfully logged into this account. + /// + public IPAddress[] LoginIPs + { + get { return m_LoginIPs; } + set { m_LoginIPs = value; } + } + + /// + /// List of account comments. Type of contained objects is AccountComment. + /// + public List Comments + { + get { if ( m_Comments == null ) m_Comments = new List(); return m_Comments; } + } + + /// + /// List of account tags. Type of contained objects is AccountTag. + /// + public List Tags + { + get { if ( m_Tags == null ) m_Tags = new List(); return m_Tags; } + } + + /// + /// Account username. Case insensitive validation. + /// + public string Username + { + get { return m_Username; } + set { m_Username = value; } + } + + /// + /// Account password. Plain text. Case sensitive validation. May be null. + /// + public string PlainPassword + { + get { return m_PlainPassword; } + set { m_PlainPassword = value; } + } + + /// + /// Account password. Hashed with MD5. May be null. + /// + public string CryptPassword + { + get { return m_CryptPassword; } + set { m_CryptPassword = value; } + } + + /// + /// Account username and password hashed with MD5. May be null. + /// + public string NewCryptPassword + { + get { return m_CryptPassword; } + set { m_CryptPassword = value; } + } + + /// + /// Initial AccessLevel for new characters created on this account. + /// + public AccessLevel AccessLevel + { + get { return m_AccessLevel; } + set { m_AccessLevel = value; } + } + + /// + /// Internal bitfield of account flags. Consider using direct access properties (Banned, Young), or GetFlag/SetFlag methods + /// + public int Flags + { + get { return m_Flags; } + set { m_Flags = value; } + } + + /// + /// Gets or sets a flag indiciating if this account is banned. + /// + public bool Banned + { + get + { + bool isBanned = GetFlag( 0 ); + + if ( !isBanned ) + return false; + + DateTime banTime; + TimeSpan banDuration; + + if ( GetBanTags( out banTime, out banDuration ) ) + { + if ( banDuration != TimeSpan.MaxValue && DateTime.Now >= ( banTime + banDuration ) ) + { + SetUnspecifiedBan( null ); // clear + Banned = false; + return false; + } + } + + return true; + } + set { SetFlag( 0, value ); } + } + + /// + /// Gets or sets a flag indicating if the characters created on this account will have the young status. + /// + public bool Young + { + get { return !GetFlag( 1 ); } + set + { + SetFlag( 1, !value ); + + if ( m_YoungTimer != null ) + { + m_YoungTimer.Stop(); + m_YoungTimer = null; + } + } + } + + /// + /// The date and time of when this account was created. + /// + public DateTime Created + { + get { return m_Created; } + } + + /// + /// Gets or sets the date and time when this account was last accessed. + /// + public DateTime LastLogin + { + get { return m_LastLogin; } + set { m_LastLogin = value; } + } + + /// + /// Gets the total game time of this account, also considering the game time of characters + /// that have been deleted. + /// + public TimeSpan TotalGameTime + { + get + { + for ( int i = 0; i < m_Mobiles.Length; i++ ) + { + PlayerMobile m = m_Mobiles[i] as PlayerMobile; + + if ( m != null && m.NetState != null ) + return m_TotalGameTime + ( DateTime.Now - m.SessionStart ); + } + + return m_TotalGameTime; + } + } + + /// + /// Gets the value of a specific flag in the Flags bitfield. + /// + /// The zero-based flag index. + public bool GetFlag( int index ) + { + return ( m_Flags & ( 1 << index ) ) != 0; + } + + /// + /// Sets the value of a specific flag in the Flags bitfield. + /// + /// The zero-based flag index. + /// The value to set. + public void SetFlag( int index, bool value ) + { + if ( value ) + m_Flags |= ( 1 << index ); + else + m_Flags &= ~( 1 << index ); + } + + /// + /// Adds a new tag to this account. This method does not check for duplicate names. + /// + /// New tag name. + /// New tag value. + public void AddTag( string name, string value ) + { + Tags.Add( new AccountTag( name, value ) ); + } + + /// + /// Removes all tags with the specified name from this account. + /// + /// Tag name to remove. + public void RemoveTag( string name ) + { + for ( int i = Tags.Count - 1; i >= 0; --i ) + { + if ( i >= Tags.Count ) + continue; + + AccountTag tag = Tags[i]; + + if ( tag.Name == name ) + Tags.RemoveAt( i ); + } + } + + /// + /// Modifies an existing tag or adds a new tag if no tag exists. + /// + /// Tag name. + /// Tag value. + public void SetTag( string name, string value ) + { + for ( int i = 0; i < Tags.Count; ++i ) + { + AccountTag tag = Tags[i]; + + if ( tag.Name == name ) + { + tag.Value = value; + return; + } + } + + AddTag( name, value ); + } + + /// + /// Gets the value of a tag -or- null if there are no tags with the specified name. + /// + /// Name of the desired tag value. + public string GetTag( string name ) + { + for ( int i = 0; i < Tags.Count; ++i ) + { + AccountTag tag = Tags[i]; + + if ( tag.Name == name ) + return tag.Value; + } + + return null; + } + + public void SetUnspecifiedBan( Mobile from ) + { + SetBanTags( from, DateTime.MinValue, TimeSpan.Zero ); + } + + public void SetBanTags( Mobile from, DateTime banTime, TimeSpan banDuration ) + { + if ( from == null ) + RemoveTag( "BanDealer" ); + else + SetTag( "BanDealer", from.ToString() ); + + if ( banTime == DateTime.MinValue ) + RemoveTag( "BanTime" ); + else + SetTag( "BanTime", XmlConvert.ToString( banTime, XmlDateTimeSerializationMode.Local ) ); + + if ( banDuration == TimeSpan.Zero ) + RemoveTag( "BanDuration" ); + else + SetTag( "BanDuration", banDuration.ToString() ); + } + + public bool GetBanTags( out DateTime banTime, out TimeSpan banDuration ) + { + string tagTime = GetTag( "BanTime" ); + string tagDuration = GetTag( "BanDuration" ); + + if ( tagTime != null ) + banTime = Utility.GetDateTime( tagTime, DateTime.MinValue ); + else + banTime = DateTime.MinValue; + + if ( tagDuration == "Infinite" ) + { + banDuration = TimeSpan.MaxValue; + } + else if ( tagDuration != null ) + { + banDuration = Utility.ToTimeSpan( tagDuration ); + } + else + { + banDuration = TimeSpan.Zero; + } + + return ( banTime != DateTime.MinValue && banDuration != TimeSpan.Zero ); + } + + private static MD5CryptoServiceProvider m_MD5HashProvider; + private static SHA1CryptoServiceProvider m_SHA1HashProvider; + private static byte[] m_HashBuffer; + + public static string HashMD5( string phrase ) + { + if ( m_MD5HashProvider == null ) + m_MD5HashProvider = new MD5CryptoServiceProvider(); + + if ( m_HashBuffer == null ) + m_HashBuffer = new byte[256]; + + int length = Encoding.ASCII.GetBytes( phrase, 0, phrase.Length > 256 ? 256 : phrase.Length, m_HashBuffer, 0 ); + byte[] hashed = m_MD5HashProvider.ComputeHash( m_HashBuffer, 0, length ); + + return BitConverter.ToString( hashed ); + } + + public static string HashSHA1( string phrase ) + { + if ( m_SHA1HashProvider == null ) + m_SHA1HashProvider = new SHA1CryptoServiceProvider(); + + if ( m_HashBuffer == null ) + m_HashBuffer = new byte[256]; + + int length = Encoding.ASCII.GetBytes( phrase, 0, phrase.Length > 256 ? 256 : phrase.Length, m_HashBuffer, 0 ); + byte[] hashed = m_SHA1HashProvider.ComputeHash( m_HashBuffer, 0, length ); + + return BitConverter.ToString( hashed ); + } + + public void SetPassword( string plainPassword ) + { + switch ( AccountHandler.ProtectPasswords ) + { + case PasswordProtection.None: + { + m_PlainPassword = plainPassword; + m_CryptPassword = null; + m_NewCryptPassword = null; + + break; + } + case PasswordProtection.Crypt: + { + m_PlainPassword = null; + m_CryptPassword = HashMD5( plainPassword ); + m_NewCryptPassword = null; + + break; + } + default: // PasswordProtection.NewCrypt + { + m_PlainPassword = null; + m_CryptPassword = null; + m_NewCryptPassword = HashSHA1( m_Username + plainPassword ); + + break; + } + } + } + + public bool CheckPassword( string plainPassword ) + { + bool ok; + PasswordProtection curProt; + + if ( m_PlainPassword != null ) + { + ok = ( m_PlainPassword == plainPassword ); + curProt = PasswordProtection.None; + } + else if ( m_CryptPassword != null ) + { + ok = ( m_CryptPassword == HashMD5( plainPassword ) ); + curProt = PasswordProtection.Crypt; + } + else + { + ok = ( m_NewCryptPassword == HashSHA1( m_Username + plainPassword ) ); + curProt = PasswordProtection.NewCrypt; + } + + if ( ok && curProt != AccountHandler.ProtectPasswords ) + SetPassword( plainPassword ); + + return ok; + } + + private Timer m_YoungTimer; + + public static void Initialize() + { + EventSink.Connected += new ConnectedEventHandler( EventSink_Connected ); + EventSink.Disconnected += new DisconnectedEventHandler( EventSink_Disconnected ); + EventSink.Login += new LoginEventHandler( EventSing_Login ); + } + + private static void EventSink_Connected( ConnectedEventArgs e ) + { + Account acc = e.Mobile.Account as Account; + + if ( acc == null ) + return; + + if ( acc.Young && acc.m_YoungTimer == null ) + { + acc.m_YoungTimer = new YoungTimer( acc ); + acc.m_YoungTimer.Start(); + } + } + + private static void EventSink_Disconnected( DisconnectedEventArgs e ) + { + Account acc = e.Mobile.Account as Account; + + if ( acc == null ) + return; + + if ( acc.m_YoungTimer != null ) + { + acc.m_YoungTimer.Stop(); + acc.m_YoungTimer = null; + } + + PlayerMobile m = e.Mobile as PlayerMobile; + if ( m == null ) + return; + + acc.m_TotalGameTime += DateTime.Now - m.SessionStart; + } + + private static void EventSing_Login( LoginEventArgs e ) + { + PlayerMobile m = e.Mobile as PlayerMobile; + + if ( m == null ) + return; + + Account acc = m.Account as Account; + + if ( acc == null ) + return; + + if ( m.Young && acc.Young ) + { + TimeSpan ts = YoungDuration - acc.TotalGameTime; + int hours = Math.Max( (int) ts.TotalHours, 0 ); + + m.SendAsciiMessage( "You will enjoy the benefits and relatively safe status of a young player for {0} more hour{1}.", hours, hours != 1 ? "s" : "" ); + } + } + + public void RemoveYoungStatus( int message ) + { + this.Young = false; + + for ( int i = 0; i < m_Mobiles.Length; i++ ) + { + PlayerMobile m = m_Mobiles[i] as PlayerMobile; + + if ( m != null && m.Young ) + { + m.Young = false; + + if ( m.NetState != null ) + { + if ( message > 0 ) + m.SendLocalizedMessage( message ); + + m.SendLocalizedMessage( 1019039 ); // You are no longer considered a young player of Ultima Online, and are no longer subject to the limitations and benefits of being in that caste. + } + } + } + } + + public void CheckYoung() + { + if ( TotalGameTime >= YoungDuration ) + RemoveYoungStatus( 1019038 ); // You are old enough to be considered an adult, and have outgrown your status as a young player! + } + + private class YoungTimer : Timer + { + private Account m_Account; + + public YoungTimer( Account account ) + : base( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.0 ) ) + { + m_Account = account; + + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Account.CheckYoung(); + } + } + + public Account( string username, string password ) + { + m_Username = username; + + SetPassword( password ); + + m_AccessLevel = AccessLevel.Player; + + m_Created = m_LastLogin = DateTime.Now; + m_TotalGameTime = TimeSpan.Zero; + + m_Mobiles = new Mobile[6]; + + m_IPRestrictions = new string[0]; + m_LoginIPs = new IPAddress[0]; + + Accounts.Add( this ); + } + + public Account( XmlElement node ) + { + m_Username = Utility.GetText( node["username"], "empty" ); + + string plainPassword = Utility.GetText( node["password"], null ); + string cryptPassword = Utility.GetText( node["cryptPassword"], null ); + string newCryptPassword = Utility.GetText( node["newCryptPassword"], null ); + + switch ( AccountHandler.ProtectPasswords ) + { + case PasswordProtection.None: + { + if ( plainPassword != null ) + SetPassword( plainPassword ); + else if ( newCryptPassword != null ) + m_NewCryptPassword = newCryptPassword; + else if ( cryptPassword != null ) + m_CryptPassword = cryptPassword; + else + SetPassword( "empty" ); + + break; + } + case PasswordProtection.Crypt: + { + if ( cryptPassword != null ) + m_CryptPassword = cryptPassword; + else if ( plainPassword != null ) + SetPassword( plainPassword ); + else if ( newCryptPassword != null ) + m_NewCryptPassword = newCryptPassword; + else + SetPassword( "empty" ); + + break; + } + default: // PasswordProtection.NewCrypt + { + if ( newCryptPassword != null ) + m_NewCryptPassword = newCryptPassword; + else if ( plainPassword != null ) + SetPassword( plainPassword ); + else if ( cryptPassword != null ) + m_CryptPassword = cryptPassword; + else + SetPassword( "empty" ); + + break; + } + } + + m_AccessLevel = (AccessLevel)Enum.Parse( typeof( AccessLevel ), Utility.GetText( node["accessLevel"], "Player" ), true ); + m_Flags = Utility.GetInt32( Utility.GetText( node["flags"], "0" ), 0 ); + m_Created = Utility.GetDateTime( Utility.GetText( node["created"], null ), DateTime.Now ); + m_LastLogin = Utility.GetDateTime( Utility.GetText( node["lastLogin"], null ), DateTime.Now ); + + m_Mobiles = LoadMobiles( node ); + m_Comments = LoadComments( node ); + m_Tags = LoadTags( node ); + m_LoginIPs = LoadAddressList( node ); + m_IPRestrictions = LoadAccessCheck( node ); + + for ( int i = 0; i < m_Mobiles.Length; ++i ) + { + if ( m_Mobiles[i] != null ) + m_Mobiles[i].Account = this; + } + + TimeSpan totalGameTime = Utility.GetTimeSpan( Utility.GetText( node["totalGameTime"], null ), TimeSpan.Zero ); + if ( totalGameTime == TimeSpan.Zero ) + { + for ( int i = 0; i < m_Mobiles.Length; i++ ) + { + PlayerMobile m = m_Mobiles[i] as PlayerMobile; + + if ( m != null ) + totalGameTime += m.GameTime; + } + } + m_TotalGameTime = totalGameTime; + + if ( this.Young ) + CheckYoung(); + + Accounts.Add( this ); + } + + /// + /// Deserializes a list of string values from an xml element. Null values are not added to the list. + /// + /// The XmlElement from which to deserialize. + /// String list. Value will never be null. + public static string[] LoadAccessCheck( XmlElement node ) + { + string[] stringList; + XmlElement accessCheck = node["accessCheck"]; + + if ( accessCheck != null ) + { + List list = new List(); + + foreach ( XmlElement ip in accessCheck.GetElementsByTagName( "ip" ) ) + { + string text = Utility.GetText( ip, null ); + + if ( text != null ) + list.Add( text ); + } + + stringList = list.ToArray(); + } + else + { + stringList = new string[0]; + } + + return stringList; + } + + /// + /// Deserializes a list of IPAddress values from an xml element. + /// + /// The XmlElement from which to deserialize. + /// Address list. Value will never be null. + public static IPAddress[] LoadAddressList( XmlElement node ) + { + IPAddress[] list; + XmlElement addressList = node["addressList"]; + + if ( addressList != null ) + { + int count = Utility.GetInt32( Utility.GetAttribute( addressList, "count", "0" ), 0 ); + + list = new IPAddress[count]; + + count = 0; + + foreach ( XmlElement ip in addressList.GetElementsByTagName( "ip" ) ) + { + if ( count < list.Length ) + { + IPAddress address; + + if( IPAddress.TryParse( Utility.GetText( ip, null ), out address ) ) + { + list[count] = address; + count++; + } + } + } + + if ( count != list.Length ) + { + IPAddress[] old = list; + list = new IPAddress[count]; + + for ( int i = 0; i < count && i < old.Length; ++i ) + list[i] = old[i]; + } + } + else + { + list = new IPAddress[0]; + } + + return list; + } + + /// + /// Deserializes a list of Mobile instances from an xml element. + /// + /// The XmlElement instance from which to deserialize. + /// Mobile list. Value will never be null. + public static Mobile[] LoadMobiles( XmlElement node ) + { + Mobile[] list = new Mobile[6]; + XmlElement chars = node["chars"]; + + //int length = Accounts.GetInt32( Accounts.GetAttribute( chars, "length", "6" ), 6 ); + //list = new Mobile[length]; + //Above is legacy, no longer used + + if ( chars != null ) + { + foreach ( XmlElement ele in chars.GetElementsByTagName( "char" ) ) + { + try + { + int index = Utility.GetInt32( Utility.GetAttribute( ele, "index", "0" ), 0 ); + int serial = Utility.GetInt32( Utility.GetText( ele, "0" ), 0 ); + + if ( index >= 0 && index < list.Length ) + list[index] = World.FindMobile( serial ); + } + catch + { + } + } + } + + return list; + } + + /// + /// Deserializes a list of AccountComment instances from an xml element. + /// + /// The XmlElement from which to deserialize. + /// Comment list. Value will never be null. + public static List LoadComments( XmlElement node ) + { + List list = null; + XmlElement comments = node["comments"]; + + if ( comments != null ) + { + list = new List(); + + foreach ( XmlElement comment in comments.GetElementsByTagName( "comment" ) ) + { + try { list.Add( new AccountComment( comment ) ); } + catch { } + } + } + + return list; + } + + /// + /// Deserializes a list of AccountTag instances from an xml element. + /// + /// The XmlElement from which to deserialize. + /// Tag list. Value will never be null. + public static List LoadTags( XmlElement node ) + { + List list = null; + XmlElement tags = node["tags"]; + + if ( tags != null ) + { + list = new List(); + + foreach ( XmlElement tag in tags.GetElementsByTagName( "tag" ) ) + { + try { list.Add( new AccountTag( tag ) ); } + catch { } + } + } + + return list; + } + + /// + /// Checks if a specific NetState is allowed access to this account. + /// + /// NetState instance to check. + /// True if allowed, false if not. + public bool HasAccess( NetState ns ) + { + if ( ns == null ) + return false; + + AccessLevel level = Misc.AccountHandler.LockdownLevel; + + if ( level > AccessLevel.Player ) + { + bool hasAccess = false; + + if ( m_AccessLevel >= level ) + { + hasAccess = true; + } + else + { + for ( int i = 0; !hasAccess && i < this.Length; ++i ) + { + Mobile m = this[i]; + + if ( m != null && m.AccessLevel >= level ) + hasAccess = true; + } + } + + if ( !hasAccess ) + return false; + } + + IPAddress ipAddress; + + try { ipAddress = ( (IPEndPoint) ns.Socket.RemoteEndPoint ).Address; } + catch { return false; } + + bool accessAllowed = ( m_IPRestrictions.Length == 0 || IPLimiter.IsExempt( ipAddress ) ); + + for ( int i = 0; !accessAllowed && i < m_IPRestrictions.Length; ++i ) + accessAllowed = Utility.IPMatch( m_IPRestrictions[i], ipAddress ); + + return accessAllowed; + } + + /// + /// Records the IP address of 'ns' in its 'LoginIPs' list. + /// + /// NetState instance to record. + public void LogAccess( NetState ns ) + { + if ( ns == null ) + return; + + IPAddress ipAddress = ns.Address; + + if ( IPLimiter.IsExempt( ipAddress ) ) + return; + + if ( m_LoginIPs.Length == 0 ) + { + if ( AccountHandler.IPTables[ipAddress] == null ) + AccountHandler.IPTables[ipAddress] = 1; + else + AccountHandler.IPTables[ipAddress] = (int) AccountHandler.IPTables[ipAddress] + 1; + } + + bool contains = false; + + for ( int i = 0; !contains && i < m_LoginIPs.Length; ++i ) + contains = m_LoginIPs[i].Equals( ipAddress ); + + if ( contains ) + return; + + IPAddress[] old = m_LoginIPs; + m_LoginIPs = new IPAddress[old.Length + 1]; + + for ( int i = 0; i < old.Length; ++i ) + m_LoginIPs[i] = old[i]; + + m_LoginIPs[old.Length] = ipAddress; + } + + /// + /// Checks if a specific NetState is allowed access to this account. If true, the NetState IPAddress is added to the address list. + /// + /// NetState instance to check. + /// True if allowed, false if not. + public bool CheckAccess( NetState ns ) + { + if ( !HasAccess( ns ) ) + return false; + + LogAccess( ns ); + return true; + } + + /// + /// Serializes this Account instance to an XmlTextWriter. + /// + /// The XmlTextWriter instance from which to serialize. + public void Save( XmlTextWriter xml ) + { + xml.WriteStartElement( "account" ); + + xml.WriteStartElement( "username" ); + xml.WriteString( m_Username ); + xml.WriteEndElement(); + + if ( m_PlainPassword != null ) + { + xml.WriteStartElement( "password" ); + xml.WriteString( m_PlainPassword ); + xml.WriteEndElement(); + } + + if ( m_CryptPassword != null ) + { + xml.WriteStartElement( "cryptPassword" ); + xml.WriteString( m_CryptPassword ); + xml.WriteEndElement(); + } + + if ( m_NewCryptPassword != null ) + { + xml.WriteStartElement( "newCryptPassword" ); + xml.WriteString( m_NewCryptPassword ); + xml.WriteEndElement(); + } + + if ( m_AccessLevel != AccessLevel.Player ) + { + xml.WriteStartElement( "accessLevel" ); + xml.WriteString( m_AccessLevel.ToString() ); + xml.WriteEndElement(); + } + + if ( m_Flags != 0 ) + { + xml.WriteStartElement( "flags" ); + xml.WriteString( XmlConvert.ToString( m_Flags ) ); + xml.WriteEndElement(); + } + + xml.WriteStartElement( "created" ); + xml.WriteString( XmlConvert.ToString( m_Created, XmlDateTimeSerializationMode.Local ) ); + xml.WriteEndElement(); + + xml.WriteStartElement( "lastLogin" ); + xml.WriteString( XmlConvert.ToString( m_LastLogin, XmlDateTimeSerializationMode.Local ) ); + xml.WriteEndElement(); + + xml.WriteStartElement( "totalGameTime" ); + xml.WriteString( XmlConvert.ToString( TotalGameTime ) ); + xml.WriteEndElement(); + + xml.WriteStartElement( "chars" ); + + //xml.WriteAttributeString( "length", m_Mobiles.Length.ToString() ); //Legacy, Not used anymore + + for ( int i = 0; i < m_Mobiles.Length; ++i ) + { + Mobile m = m_Mobiles[i]; + + if ( m != null && !m.Deleted ) + { + xml.WriteStartElement( "char" ); + xml.WriteAttributeString( "index", i.ToString() ); + xml.WriteString( m.Serial.Value.ToString() ); + xml.WriteEndElement(); + } + } + + xml.WriteEndElement(); + + if ( m_Comments != null && m_Comments.Count > 0 ) + { + xml.WriteStartElement( "comments" ); + + for ( int i = 0; i < m_Comments.Count; ++i ) + m_Comments[i].Save( xml ); + + xml.WriteEndElement(); + } + + if ( m_Tags != null && m_Tags.Count > 0 ) + { + xml.WriteStartElement( "tags" ); + + for ( int i = 0; i < m_Tags.Count; ++i ) + m_Tags[i].Save( xml ); + + xml.WriteEndElement(); + } + + if ( m_LoginIPs.Length > 0 ) + { + xml.WriteStartElement( "addressList" ); + + xml.WriteAttributeString( "count", m_LoginIPs.Length.ToString() ); + + for ( int i = 0; i < m_LoginIPs.Length; ++i ) + { + xml.WriteStartElement( "ip" ); + xml.WriteString( m_LoginIPs[i].ToString() ); + xml.WriteEndElement(); + } + + xml.WriteEndElement(); + } + + if ( m_IPRestrictions.Length > 0 ) + { + xml.WriteStartElement( "accessCheck" ); + + for ( int i = 0; i < m_IPRestrictions.Length; ++i ) + { + xml.WriteStartElement( "ip" ); + xml.WriteString( m_IPRestrictions[i] ); + xml.WriteEndElement(); + } + + xml.WriteEndElement(); + } + + xml.WriteEndElement(); + } + + /// + /// Gets the current number of characters on this account. + /// + public int Count + { + get + { + int count = 0; + + for ( int i = 0; i < this.Length; ++i ) + { + if ( this[i] != null ) + ++count; + } + + return count; + } + } + + /// + /// Gets the maximum amount of characters allowed to be created on this account. Values other than 1, 5, or 6 are not supported by the client. + /// + public int Limit + { + get { return ( Core.AOS ? 6 : 5 ); } + } + + /// + /// Gets the maxmimum amount of characters that this account can hold. + /// + public int Length + { + get { return m_Mobiles.Length; } + } + + /// + /// Gets or sets the character at a specified index for this account. Out of bound index values are handled; null returned for get, ignored for set. + /// + public Mobile this[int index] + { + get + { + if ( index >= 0 && index < m_Mobiles.Length ) + { + Mobile m = m_Mobiles[index]; + + if ( m != null && m.Deleted ) + { + m.Account = null; + m_Mobiles[index] = m = null; + } + + return m; + } + + return null; + } + set + { + if ( index >= 0 && index < m_Mobiles.Length ) + { + if ( m_Mobiles[index] != null ) + m_Mobiles[index].Account = null; + + m_Mobiles[index] = value; + + if ( m_Mobiles[index] != null ) + m_Mobiles[index].Account = this; + } + } + } + + public override string ToString() + { + return m_Username; + } + + public int CompareTo( Account other ) + { + if ( other == null ) + return -1; + + return m_Username.CompareTo( other.m_Username ); + } + + public int CompareTo( object obj ) + { + if ( obj is Account ) + return this.CompareTo( (Account) obj ); + + throw new ArgumentException(); + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/AccountAttackLimiter.cs b/Scripts/Accounting/AccountAttackLimiter.cs new file mode 100644 index 000000000..acb646c15 --- /dev/null +++ b/Scripts/Accounting/AccountAttackLimiter.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Network; + +namespace Server.Accounting +{ + public class AccountAttackLimiter + { + public static bool Enabled = true; + + public static void Initialize() + { + if ( !Enabled ) + return; + + RegisterThrottler( 0x80 ); + RegisterThrottler( 0x91 ); + RegisterThrottler( 0xCF ); + } + + public static void RegisterThrottler( int packetID ) + { + PacketHandler ph = PacketHandlers.GetHandler( packetID ); + + if ( ph == null ) + return; + + ph.ThrottleCallback = new ThrottlePacketCallback( Throttle_Callback ); + } + + public static bool Throttle_Callback( NetState ns ) + { + InvalidAccountAccessLog accessLog = FindAccessLog( ns ); + + if ( accessLog == null ) + return true; + + return ( DateTime.Now >= (accessLog.LastAccessTime + ComputeThrottle( accessLog.Counts )) ); + } + + private static List m_List = new List(); + + public static InvalidAccountAccessLog FindAccessLog( NetState ns ) + { + if ( ns == null ) + return null; + + IPAddress ipAddress = ns.Address; + + for ( int i = 0; i < m_List.Count; ++i ) + { + InvalidAccountAccessLog accessLog = m_List[i]; + + if ( accessLog.HasExpired ) + m_List.RemoveAt( i-- ); + else if ( accessLog.Address.Equals( ipAddress ) ) + return accessLog; + } + + return null; + } + + public static void RegisterInvalidAccess( NetState ns ) + { + if ( ns == null || !Enabled ) + return; + + InvalidAccountAccessLog accessLog = FindAccessLog( ns ); + + if ( accessLog == null ) + m_List.Add( accessLog = new InvalidAccountAccessLog( ns.Address ) ); + + accessLog.Counts += 1; + accessLog.RefreshAccessTime(); + } + + public static TimeSpan ComputeThrottle( int counts ) + { + if ( counts >= 15 ) + return TimeSpan.FromMinutes( 5.0 ); + + if ( counts >= 10 ) + return TimeSpan.FromMinutes( 1.0 ); + + if ( counts >= 5 ) + return TimeSpan.FromSeconds( 20.0 ); + + if ( counts >= 3 ) + return TimeSpan.FromSeconds( 10.0 ); + + if ( counts >= 1 ) + return TimeSpan.FromSeconds( 2.0 ); + + return TimeSpan.Zero; + } + } + + public class InvalidAccountAccessLog + { + private IPAddress m_Address; + private DateTime m_LastAccessTime; + private int m_Counts; + + public IPAddress Address + { + get{ return m_Address; } + set{ m_Address = value; } + } + + public DateTime LastAccessTime + { + get{ return m_LastAccessTime; } + set{ m_LastAccessTime = value; } + } + + public bool HasExpired + { + get{ return ( DateTime.Now >= ( m_LastAccessTime + TimeSpan.FromHours( 1.0 ) ) ); } + } + + public int Counts + { + get{ return m_Counts; } + set{ m_Counts = value; } + } + + public void RefreshAccessTime() + { + m_LastAccessTime = DateTime.Now; + } + + public InvalidAccountAccessLog( IPAddress address ) + { + m_Address = address; + RefreshAccessTime(); + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/AccountComment.cs b/Scripts/Accounting/AccountComment.cs new file mode 100644 index 000000000..709e70baf --- /dev/null +++ b/Scripts/Accounting/AccountComment.cs @@ -0,0 +1,77 @@ +using System; +using System.Xml; + +namespace Server.Accounting +{ + public class AccountComment + { + private string m_AddedBy; + private string m_Content; + private DateTime m_LastModified; + + /// + /// A string representing who added this comment. + /// + public string AddedBy + { + get{ return m_AddedBy; } + } + + /// + /// Gets or sets the body of this comment. Setting this value will reset LastModified. + /// + public string Content + { + get{ return m_Content; } + set{ m_Content = value; m_LastModified = DateTime.Now; } + } + + /// + /// The date and time when this account was last modified -or- the comment creation time, if never modified. + /// + public DateTime LastModified + { + get{ return m_LastModified; } + } + + /// + /// Constructs a new AccountComment instance. + /// + /// Initial AddedBy value. + /// Initial Content value. + public AccountComment( string addedBy, string content ) + { + m_AddedBy = addedBy; + m_Content = content; + m_LastModified = DateTime.Now; + } + + /// + /// Deserializes an AccountComment instance from an xml element. + /// + /// The XmlElement instance from which to deserialize. + public AccountComment( XmlElement node ) + { + m_AddedBy = Utility.GetAttribute( node, "addedBy", "empty" ); + m_LastModified = Utility.GetDateTime( Utility.GetAttribute( node, "lastModified" ), DateTime.Now ); + m_Content = Utility.GetText( node, "" ); + } + + /// + /// Serializes this AccountComment instance to an XmlTextWriter. + /// + /// The XmlTextWriter instance from which to serialize. + public void Save( XmlTextWriter xml ) + { + xml.WriteStartElement( "comment" ); + + xml.WriteAttributeString( "addedBy", m_AddedBy ); + + xml.WriteAttributeString( "lastModified", XmlConvert.ToString( m_LastModified, XmlDateTimeSerializationMode.Local ) ); + + xml.WriteString( m_Content ); + + xml.WriteEndElement(); + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/AccountHandler.cs b/Scripts/Accounting/AccountHandler.cs new file mode 100644 index 000000000..049e20efc --- /dev/null +++ b/Scripts/Accounting/AccountHandler.cs @@ -0,0 +1,396 @@ +using System; +using System.IO; +using System.Net; +using Server; +using Server.Network; +using Server.Accounting; +using Server.Engines.Help; +using System.Collections; +using Server.Commands; + +namespace Server.Misc +{ + public enum PasswordProtection + { + None, + Crypt, + NewCrypt + } + + public class AccountHandler + { + private static int MaxAccountsPerIP = 1; + private static bool AutoAccountCreation = true; + private static bool RestrictDeletion = !TestCenter.Enabled; + private static TimeSpan DeleteDelay = TimeSpan.FromDays( 7.0 ); + + public static PasswordProtection ProtectPasswords = PasswordProtection.NewCrypt; + + private static AccessLevel m_LockdownLevel; + + public static AccessLevel LockdownLevel + { + get{ return m_LockdownLevel; } + set{ m_LockdownLevel = value; } + } + + private static CityInfo[] StartingCities = new CityInfo[] + { + new CityInfo( "Yew", "The Empath Abbey", 633, 858, 0 ), + new CityInfo( "Minoc", "The Barnacle", 2476, 413, 15 ), + new CityInfo( "Britain", "Sweet Dreams Inn", 1496, 1628, 10 ), + new CityInfo( "Moonglow", "The Scholars Inn", 4408, 1168, 0 ), + new CityInfo( "Trinsic", "The Traveler's Inn", 1845, 2745, 0 ), + new CityInfo( "Magincia", "The Great Horns Tavern", 3734, 2222, 20 ), + new CityInfo( "Jhelom", "The Mercenary Inn", 1374, 3826, 0 ), + new CityInfo( "Skara Brae", "The Falconer's Inn", 618, 2234, 0 ), + new CityInfo( "Vesper", "The Ironwood Inn", 2771, 976, 0 ), + new CityInfo( "Haven", "Buckler's Hideaway", 3667, 2625, 0 ) + }; + + private static bool PasswordCommandEnabled = false; + + public static void Initialize() + { + EventSink.DeleteRequest += new DeleteRequestEventHandler( EventSink_DeleteRequest ); + EventSink.AccountLogin += new AccountLoginEventHandler( EventSink_AccountLogin ); + EventSink.GameLogin += new GameLoginEventHandler( EventSink_GameLogin ); + + if ( PasswordCommandEnabled ) + CommandSystem.Register( "Password", AccessLevel.Player, new CommandEventHandler( Password_OnCommand ) ); + + if ( Core.AOS ) + { + CityInfo haven = new CityInfo( "Haven", "Uzeraan's Mansion", 3618, 2591, 0 ); + StartingCities[StartingCities.Length - 1] = haven; + } + } + + [Usage( "Password " )] + [Description( "Changes the password of the commanding players account. Requires the same C-class IP address as the account's creator." )] + public static void Password_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + Account acct = from.Account as Account; + + if ( acct == null ) + return; + + IPAddress[] accessList = acct.LoginIPs; + + if ( accessList.Length == 0 ) + return; + + NetState ns = from.NetState; + + if ( ns == null ) + return; + + if ( e.Length == 0 ) + { + from.SendMessage( "You must specify the new password." ); + return; + } + else if ( e.Length == 1 ) + { + from.SendMessage( "To prevent potential typing mistakes, you must type the password twice. Use the format:" ); + from.SendMessage( "Password \"(newPassword)\" \"(repeated)\"" ); + return; + } + + string pass = e.GetString( 0 ); + string pass2 = e.GetString( 1 ); + + if ( pass != pass2 ) + { + from.SendMessage( "The passwords do not match." ); + return; + } + + bool isSafe = true; + + for ( int i = 0; isSafe && i < pass.Length; ++i ) + isSafe = ( pass[i] >= 0x20 && pass[i] < 0x80 ); + + if ( !isSafe ) + { + from.SendMessage( "That is not a valid password." ); + return; + } + + try + { + IPAddress ipAddress = ((IPEndPoint)ns.Socket.RemoteEndPoint).Address; + + if ( Utility.IPMatchClassC( accessList[0], ipAddress ) ) + { + acct.SetPassword( pass ); + from.SendMessage( "The password to your account has changed." ); + } + else + { + PageEntry entry = PageQueue.GetEntry( from ); + + if ( entry != null ) + { + if ( entry.Message.StartsWith( "[Automated: Change Password]" ) ) + from.SendMessage( "You already have a password change request in the help system queue." ); + else + from.SendMessage( "Your IP address does not match that which created this account." ); + } + else if ( PageQueue.CheckAllowedToPage( from ) ) + { + from.SendMessage( "Your IP address does not match that which created this account. A page has been entered into the help system on your behalf." ); + + from.SendLocalizedMessage( 501234, "", 0x35 ); /* The next available Counselor/Game Master will respond as soon as possible. + * Please check your Journal for messages every few minutes. + */ + + PageQueue.Enqueue( new PageEntry( from, String.Format( "[Automated: Change Password]
Desired password: {0}
Current IP address: {1}
Account IP address: {2}", pass, ipAddress, accessList[0] ), PageType.Account ) ); + } + + } + } + catch + { + } + } + + private static void EventSink_DeleteRequest( DeleteRequestEventArgs e ) + { + NetState state = e.State; + int index = e.Index; + + Account acct = state.Account as Account; + + if ( acct == null ) + { + state.Dispose(); + } + else if ( index < 0 || index >= acct.Length ) + { + state.Send( new DeleteResult( DeleteResultType.BadRequest ) ); + state.Send( new CharacterListUpdate( acct ) ); + } + else + { + Mobile m = acct[index]; + + if ( m == null ) + { + state.Send( new DeleteResult( DeleteResultType.CharNotExist ) ); + state.Send( new CharacterListUpdate( acct ) ); + } + else if ( m.NetState != null ) + { + state.Send( new DeleteResult( DeleteResultType.CharBeingPlayed ) ); + state.Send( new CharacterListUpdate( acct ) ); + } + else if ( RestrictDeletion && DateTime.Now < (m.CreationTime + DeleteDelay) ) + { + state.Send( new DeleteResult( DeleteResultType.CharTooYoung ) ); + state.Send( new CharacterListUpdate( acct ) ); + } + else + { + Console.WriteLine( "Client: {0}: Deleting character {1} (0x{2:X})", state, index, m.Serial.Value ); + + acct.Comments.Add( new AccountComment( "System", String.Format( "Character #{0} {1} deleted by {2}", index+1, m, state ) ) ); + + m.Delete(); + state.Send( new CharacterListUpdate( acct ) ); + } + } + } + + public static bool CanCreate( IPAddress ip ) + { + if ( IPTables[ip] == null ) //Sanity + return true; + + + return ((int)IPTables[ip] < MaxAccountsPerIP); + + /* + if ( (int)IPTables[ip] >= MaxAccountsPerIP ) + return false; + else + return true; + */ + } + + private static Hashtable m_IPTables; + + public static Hashtable IPTables + { + get + { + if ( m_IPTables == null ) + { + m_IPTables = new Hashtable(); + + foreach ( Account a in Accounts.GetAccounts() ) + { + if ( a.LoginIPs.Length > 0 ) + { + IPAddress ip = a.LoginIPs[0]; + + if ( m_IPTables[ip] == null ) + m_IPTables[ip] = 1; + else + m_IPTables[ip] = (int)m_IPTables[ip] + 1; + } + } + } + + return m_IPTables; + } + } + + private static Account CreateAccount( NetState state, string un, string pw ) + { + if ( un.Length == 0 || pw.Length == 0 ) + return null; + + bool isSafe = true; + + for ( int i = 0; isSafe && i < un.Length; ++i ) + isSafe = ( un[i] >= 0x20 && un[i] < 0x80 ); + + for ( int i = 0; isSafe && i < pw.Length; ++i ) + isSafe = ( pw[i] >= 0x20 && pw[i] < 0x80 ); + + if ( !isSafe ) + return null; + + if ( !CanCreate( state.Address ) ) + { + Console.WriteLine( "Login: {0}: Account '{1}' not created, ip already has {2} account{3}.", state, un, MaxAccountsPerIP, MaxAccountsPerIP == 1 ? "" : "s" ); + return null; + } + + Console.WriteLine( "Login: {0}: Creating new account '{1}'", state, un ); + + Account a = new Account( un, pw ); + + return a; + } + + public static void EventSink_AccountLogin( AccountLoginEventArgs e ) + { + if ( !IPLimiter.SocketBlock && !IPLimiter.Verify( e.State.Address ) ) + { + e.Accepted = false; + e.RejectReason = ALRReason.InUse; + + Console.WriteLine( "Login: {0}: Past IP limit threshold", e.State ); + + using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) ) + op.WriteLine( "{0}\tPast IP limit threshold\t{1}", e.State, DateTime.Now ); + + return; + } + + string un = e.Username; + string pw = e.Password; + + e.Accepted = false; + Account acct = Accounts.GetAccount( un ) as Account; + + if ( acct == null ) + { + if ( AutoAccountCreation && un.Trim().Length > 0 ) //To prevent someone from mkaing an account of just '' or a bunch of meaningless spaces + { + e.State.Account = acct = CreateAccount( e.State, un, pw ); + e.Accepted = acct == null ? false : acct.CheckAccess( e.State ); + + if ( !e.Accepted ) + e.RejectReason = ALRReason.BadComm; + } + else + { + Console.WriteLine( "Login: {0}: Invalid username '{1}'", e.State, un ); + e.RejectReason = ALRReason.Invalid; + } + } + else if ( !acct.HasAccess( e.State ) ) + { + Console.WriteLine( "Login: {0}: Access denied for '{1}'", e.State, un ); + e.RejectReason = ( m_LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass ); + } + else if ( !acct.CheckPassword( pw ) ) + { + Console.WriteLine( "Login: {0}: Invalid password for '{1}'", e.State, un ); + e.RejectReason = ALRReason.BadPass; + } + else if ( acct.Banned ) + { + Console.WriteLine( "Login: {0}: Banned account '{1}'", e.State, un ); + e.RejectReason = ALRReason.Blocked; + } + else + { + Console.WriteLine( "Login: {0}: Valid credentials for '{1}'", e.State, un ); + e.State.Account = acct; + e.Accepted = true; + + acct.LogAccess( e.State ); + } + + if ( !e.Accepted ) + AccountAttackLimiter.RegisterInvalidAccess( e.State ); + } + + public static void EventSink_GameLogin( GameLoginEventArgs e ) + { + if ( !IPLimiter.SocketBlock && !IPLimiter.Verify( e.State.Address ) ) + { + e.Accepted = false; + + Console.WriteLine( "Login: {0}: Past IP limit threshold", e.State ); + + using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) ) + op.WriteLine( "{0}\tPast IP limit threshold\t{1}", e.State, DateTime.Now ); + + return; + } + + string un = e.Username; + string pw = e.Password; + + Account acct = Accounts.GetAccount( un ) as Account; + + if ( acct == null ) + { + e.Accepted = false; + } + else if ( !acct.HasAccess( e.State ) ) + { + Console.WriteLine( "Login: {0}: Access denied for '{1}'", e.State, un ); + e.Accepted = false; + } + else if ( !acct.CheckPassword( pw ) ) + { + Console.WriteLine( "Login: {0}: Invalid password for '{1}'", e.State, un ); + e.Accepted = false; + } + else if ( acct.Banned ) + { + Console.WriteLine( "Login: {0}: Banned account '{1}'", e.State, un ); + e.Accepted = false; + } + else + { + acct.LogAccess( e.State ); + + Console.WriteLine( "Login: {0}: Account '{1}' at character list", e.State, un ); + e.State.Account = acct; + e.Accepted = true; + e.CityInfo = StartingCities; + } + + if ( !e.Accepted ) + AccountAttackLimiter.RegisterInvalidAccess( e.State ); + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/AccountTag.cs b/Scripts/Accounting/AccountTag.cs new file mode 100644 index 000000000..5f977cd39 --- /dev/null +++ b/Scripts/Accounting/AccountTag.cs @@ -0,0 +1,61 @@ +using System; +using System.Xml; + +namespace Server.Accounting +{ + public class AccountTag + { + private string m_Name, m_Value; + + /// + /// Gets or sets the name of this tag. + /// + public string Name + { + get{ return m_Name; } + set{ m_Name = value; } + } + + /// + /// Gets or sets the value of this tag. + /// + public string Value + { + get{ return m_Value; } + set{ m_Value = value; } + } + + /// + /// Constructs a new AccountTag instance with a specific name and value. + /// + /// Initial name. + /// Initial value. + public AccountTag( string name, string value ) + { + m_Name = name; + m_Value = value; + } + + /// + /// Deserializes an AccountTag instance from an xml element. + /// + /// The XmlElement instance from which to deserialize. + public AccountTag( XmlElement node ) + { + m_Name = Utility.GetAttribute( node, "name", "empty" ); + m_Value = Utility.GetText( node, "" ); + } + + /// + /// Serializes this AccountTag instance to an XmlTextWriter. + /// + /// The XmlTextWriter instance from which to serialize. + public void Save( XmlTextWriter xml ) + { + xml.WriteStartElement( "tag" ); + xml.WriteAttributeString( "name", m_Name ); + xml.WriteString( m_Value ); + xml.WriteEndElement(); + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/Accounts.cs b/Scripts/Accounting/Accounts.cs new file mode 100644 index 000000000..511fe5df1 --- /dev/null +++ b/Scripts/Accounting/Accounts.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Xml; + +namespace Server.Accounting +{ + public class Accounts + { + private static Dictionary m_Accounts = new Dictionary(); + + public static void Configure() + { + EventSink.WorldLoad += new WorldLoadEventHandler( Load ); + EventSink.WorldSave += new WorldSaveEventHandler( Save ); + } + + static Accounts() + { + } + + public static int Count { get { return m_Accounts.Count; } } + + public static ICollection GetAccounts() + { + return m_Accounts.Values; + } + + public static IAccount GetAccount( string username ) + { + IAccount a; + + m_Accounts.TryGetValue( username, out a ); + + return a; + } + + public static void Add( IAccount a ) + { + m_Accounts[a.Username] = a; + } + + public static void Remove( string username ) + { + m_Accounts.Remove( username ); + } + + public static void Load() + { + m_Accounts = new Dictionary( 32, StringComparer.OrdinalIgnoreCase ); + + string filePath = Path.Combine( "Saves/Accounts", "accounts.xml" ); + + if ( !File.Exists( filePath ) ) + return; + + XmlDocument doc = new XmlDocument(); + doc.Load( filePath ); + + XmlElement root = doc["accounts"]; + + foreach ( XmlElement account in root.GetElementsByTagName( "account" ) ) + { + try + { + Account acct = new Account( account ); + } + catch + { + Console.WriteLine( "Warning: Account instance load failed" ); + } + } + } + + public static void Save( WorldSaveEventArgs e ) + { + if ( !Directory.Exists( "Saves/Accounts" ) ) + Directory.CreateDirectory( "Saves/Accounts" ); + + string filePath = Path.Combine( "Saves/Accounts", "accounts.xml" ); + + using ( StreamWriter op = new StreamWriter( filePath ) ) + { + XmlTextWriter xml = new XmlTextWriter( op ); + + xml.Formatting = Formatting.Indented; + xml.IndentChar = '\t'; + xml.Indentation = 1; + + xml.WriteStartDocument( true ); + + xml.WriteStartElement( "accounts" ); + + xml.WriteAttributeString( "count", m_Accounts.Count.ToString() ); + + foreach ( Account a in GetAccounts() ) + a.Save( xml ); + + xml.WriteEndElement(); + + xml.Close(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/Firewall.cs b/Scripts/Accounting/Firewall.cs new file mode 100644 index 000000000..b982f248a --- /dev/null +++ b/Scripts/Accounting/Firewall.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using System.IO; +using System.Net; + +namespace Server +{ + public class Firewall + { + private static ArrayList m_Blocked; + + static Firewall() + { + m_Blocked = new ArrayList(); + + string path = "firewall.cfg"; + + if ( File.Exists( path ) ) + { + using ( StreamReader ip = new StreamReader( path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + line = line.Trim(); + + if ( line.Length == 0 ) + continue; + + object toAdd; + + IPAddress addr; + if( IPAddress.TryParse( line, out addr ) ) + toAdd = addr; + else + toAdd = line; + + m_Blocked.Add( toAdd.ToString() ); + } + } + } + } + + public static ArrayList List + { + get + { + return m_Blocked; + } + } + + public static void RemoveAt( int index ) + { + m_Blocked.RemoveAt( index ); + Save(); + } + + public static void Remove( string pattern ) + { + m_Blocked.Remove( pattern ); + Save(); + } + + public static void Remove( IPAddress ip ) + { + m_Blocked.Remove( ip ); + Save(); + } + + public static void Add( object obj ) + { + if ( !(obj is IPAddress) && !(obj is String) ) + return; + + if ( !m_Blocked.Contains( obj ) ) + m_Blocked.Add( obj ); + + Save(); + } + + public static void Add( string pattern ) + { + if ( !m_Blocked.Contains( pattern ) ) + m_Blocked.Add( pattern ); + + Save(); + } + + public static void Add( IPAddress ip ) + { + if ( !m_Blocked.Contains( ip ) ) + m_Blocked.Add( ip ); + + Save(); + } + + public static void Save() + { + string path = "firewall.cfg"; + + using ( StreamWriter op = new StreamWriter( path ) ) + { + for ( int i = 0; i < m_Blocked.Count; ++i ) + op.WriteLine( m_Blocked[i] ); + } + } + + public static bool IsBlocked( IPAddress ip ) + { + bool contains = false; + + for ( int i = 0; !contains && i < m_Blocked.Count; ++i ) + { + if ( m_Blocked[i] is IPAddress ) + contains = ip.Equals( m_Blocked[i] ); + else if ( m_Blocked[i] is String ) + { + string s = (string)m_Blocked[i]; + + contains = Utility.IPMatchCIDR( s, ip ); + + if( !contains ) + contains = Utility.IPMatch( s, ip ); + } + } + + return contains; + } + } +} \ No newline at end of file diff --git a/Scripts/Accounting/IPLimiter.cs b/Scripts/Accounting/IPLimiter.cs new file mode 100644 index 000000000..f13b247f4 --- /dev/null +++ b/Scripts/Accounting/IPLimiter.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Network; + +namespace Server.Misc +{ + public class IPLimiter + { + public static bool Enabled = true; + public static bool SocketBlock = true; // true to block at connection, false to block at login request + + public const int MaxAddresses = 10; + + public static IPAddress[] Exemptions = new IPAddress[] //For hosting services where there are cases where IPs can be proxied + { + //IPAddress.Parse( "127.0.0.1" ), + }; + + public static bool IsExempt( IPAddress ip ) + { + for ( int i = 0; i < Exemptions.Length; i++ ) + { + if ( ip.Equals( Exemptions[i] ) ) + return true; + } + + return false; + } + + public static bool Verify( IPAddress ourAddress ) + { + if ( !Enabled || IsExempt( ourAddress ) ) + return true; + + List netStates = NetState.Instances; + + int count = 0; + + for ( int i = 0; i < netStates.Count; ++i ) + { + NetState compState = (NetState)netStates[i]; + + if ( ourAddress.Equals( compState.Address ) ) + { + ++count; + + if ( count > MaxAddresses ) + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Add.cs b/Scripts/Commands/Add.cs new file mode 100644 index 000000000..67fbb6622 --- /dev/null +++ b/Scripts/Commands/Add.cs @@ -0,0 +1,606 @@ +using System; +using System.Text; +using System.Reflection; +using System.Collections; +using Server; +using Server.Items; +using Server.Network; +using Server.Targeting; +using CPA = Server.CommandPropertyAttribute; + +namespace Server.Commands +{ + public class Add + { + public static void Initialize() + { + CommandSystem.Register( "Tile", AccessLevel.GameMaster, new CommandEventHandler( Tile_OnCommand ) ); + CommandSystem.Register( "TileRXYZ", AccessLevel.GameMaster, new CommandEventHandler( TileRXYZ_OnCommand ) ); + CommandSystem.Register( "TileXYZ", AccessLevel.GameMaster, new CommandEventHandler( TileXYZ_OnCommand ) ); + CommandSystem.Register( "TileZ", AccessLevel.GameMaster, new CommandEventHandler( TileZ_OnCommand ) ); + } + + public static void Invoke( Mobile from, Point3D start, Point3D end, string[] args ) + { + Invoke( from, start, end, args, null ); + } + + public static void Invoke( Mobile from, Point3D start, Point3D end, string[] args, ArrayList packs ) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat( "{0} {1} building ", from.AccessLevel, CommandLogging.Format( from ) ); + + if ( start == end ) + sb.AppendFormat( "at {0} in {1}", start, from.Map ); + else + sb.AppendFormat( "from {0} to {1} in {2}", start, end, from.Map ); + + sb.Append( ":" ); + + for ( int i = 0; i < args.Length; ++i ) + sb.AppendFormat( " \"{0}\"", args[i] ); + + CommandLogging.WriteLine( from, sb.ToString() ); + + string name = args[0]; + + FixArgs( ref args ); + + string[,] props = null; + + for ( int i = 0; i < args.Length; ++i ) + { + if ( Insensitive.Equals( args[i], "set" ) ) + { + int remains = args.Length - i - 1; + + if ( remains >= 2 ) + { + props = new string[remains / 2, 2]; + + remains /= 2; + + for ( int j = 0; j < remains; ++j ) + { + props[j, 0] = args[i + (j * 2) + 1]; + props[j, 1] = args[i + (j * 2) + 2]; + } + + FixSetString( ref args, i ); + } + + break; + } + } + + Type type = ScriptCompiler.FindTypeByName( name ); + + if ( type == null ) + { + from.SendMessage( "No type with that name was found." ); + return; + } + + DateTime time = DateTime.Now; + + int built = BuildObjects( from, type, start, end, args, props, packs ); + + if ( built > 0 ) + from.SendMessage( "{0} object{1} generated in {2:F1} seconds.", built, built != 1 ? "s" : "", (DateTime.Now - time).TotalSeconds ); + else + SendUsage( type, from ); + } + + public static void FixSetString( ref string[] args, int index ) + { + string[] old = args; + args = new string[index]; + + Array.Copy( old, 0, args, 0, index ); + } + + public static void FixArgs( ref string[] args ) + { + string[] old = args; + args = new string[args.Length - 1]; + + Array.Copy( old, 1, args, 0, args.Length ); + } + + public static int BuildObjects( Mobile from, Type type, Point3D start, Point3D end, string[] args, string[,] props, ArrayList packs ) + { + Utility.FixPoints( ref start, ref end ); + + PropertyInfo[] realProps = null; + + if ( props != null ) + { + realProps = new PropertyInfo[props.GetLength( 0 )]; + + PropertyInfo[] allProps = type.GetProperties( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public ); + + for ( int i = 0; i < realProps.Length; ++i ) + { + PropertyInfo thisProp = null; + + string propName = props[i, 0]; + + for ( int j = 0; thisProp == null && j < allProps.Length; ++j ) + { + if ( Insensitive.Equals( propName, allProps[j].Name ) ) + thisProp = allProps[j]; + } + + if ( thisProp == null ) + { + from.SendMessage( "Property not found: {0}", propName ); + } + else + { + CPA attr = Properties.GetCPA( thisProp ); + + if ( attr == null ) + from.SendMessage( "Property ({0}) not found.", propName ); + else if ( from.AccessLevel < attr.WriteLevel ) + from.SendMessage( "Setting this property ({0}) requires at least {1} access level.", propName, Mobile.GetAccessLevelName( attr.WriteLevel ) ); + else if ( !thisProp.CanWrite ) + from.SendMessage( "Property ({0}) is read only.", propName ); + else + realProps[i] = thisProp; + } + } + } + + ConstructorInfo[] ctors = type.GetConstructors(); + + for ( int i = 0; i < ctors.Length; ++i ) + { + ConstructorInfo ctor = ctors[i]; + + if ( !IsConstructable( ctor ) ) + continue; + + ParameterInfo[] paramList = ctor.GetParameters(); + + if ( args.Length == paramList.Length ) + { + object[] paramValues = ParseValues( paramList, args ); + + if ( paramValues == null ) + continue; + + int built = Build( from, start, end, ctor, paramValues, props, realProps, packs ); + + if ( built > 0 ) + return built; + } + } + + return 0; + } + + public static object[] ParseValues( ParameterInfo[] paramList, string[] args ) + { + object[] values = new object[args.Length]; + + for ( int i = 0; i < args.Length; ++i ) + { + object value = ParseValue( paramList[i].ParameterType, args[i] ); + + if ( value != null ) + values[i] = value; + else + return null; + } + + return values; + } + + public static object ParseValue( Type type, string value ) + { + try + { + if ( IsEnum( type ) ) + { + return Enum.Parse( type, value, true ); + } + else if ( IsType( type ) ) + { + return ScriptCompiler.FindTypeByName( value ); + } + else if ( IsParsable( type ) ) + { + return ParseParsable( type, value ); + } + else + { + object obj = value; + + if ( value != null && value.StartsWith( "0x" ) ) + { + if ( IsSignedNumeric( type ) ) + obj = Convert.ToInt64( value.Substring( 2 ), 16 ); + else if ( IsUnsignedNumeric( type ) ) + obj = Convert.ToUInt64( value.Substring( 2 ), 16 ); + + obj = Convert.ToInt32( value.Substring( 2 ), 16 ); + } + + if ( obj == null && !type.IsValueType ) + return null; + else + return Convert.ChangeType( obj, type ); + } + } + catch + { + return null; + } + } + + public static object Build( Mobile from, ConstructorInfo ctor, object[] values, string[,] props, PropertyInfo[] realProps, ref bool sendError ) + { + object built = ctor.Invoke( values ); + + if ( built != null && realProps != null ) + { + bool hadError = false; + + for ( int i = 0; i < realProps.Length; ++i ) + { + if ( realProps[i] == null ) + continue; + + string result = Properties.InternalSetValue( from, built, built, realProps[i], props[i, 1], props[i, 1], false ); + + if ( result != "Property has been set." ) + { + if ( sendError ) + from.SendMessage( result ); + + hadError = true; + } + } + + if ( hadError ) + sendError = false; + } + + return built; + } + + public static int Build( Mobile from, Point3D start, Point3D end, ConstructorInfo ctor, object[] values, string[,] props, PropertyInfo[] realProps, ArrayList packs ) + { + try + { + Map map = from.Map; + + int objectCount = ( packs == null ? (((end.X - start.X) + 1) * ((end.Y - start.Y) + 1)) : packs.Count ); + + if ( objectCount >= 20 ) + from.SendMessage( "Constructing {0} objects, please wait.", objectCount ); + + bool sendError = true; + + StringBuilder sb = new StringBuilder(); + sb.Append( "Serials: " ); + + if ( packs != null ) + { + for ( int i = 0; i < packs.Count; ++i ) + { + object built = Build( from, ctor, values, props, realProps, ref sendError ); + + if( built is IEntity ) + sb.AppendFormat( "0x{0:X}; ", ((IEntity)built).Serial.Value ); + else + continue; + + if ( built is Item ) + { + Container pack = (Container)packs[i]; + + pack.DropItem( (Item)built ); + } + else if ( built is Mobile ) + { + Mobile m = (Mobile)built; + + m.MoveToWorld( new Point3D( start.X, start.Y, start.Z ), map ); + } + } + } + else + { + for ( int x = start.X; x <= end.X; ++x ) + { + for ( int y = start.Y; y <= end.Y; ++y ) + { + object built = Build( from, ctor, values, props, realProps, ref sendError ); + + if( built is IEntity ) + sb.AppendFormat( "0x{0:X}; ", ((IEntity)built).Serial.Value ); + else + continue; + + if ( built is Item ) + { + Item item = (Item)built; + + item.MoveToWorld( new Point3D( x, y, start.Z ), map ); + } + else if ( built is Mobile ) + { + Mobile m = (Mobile)built; + + m.MoveToWorld( new Point3D( x, y, start.Z ), map ); + } + } + } + } + + CommandLogging.WriteLine( from, sb.ToString() ); + + return objectCount; + } + catch ( Exception ex ) + { + Console.WriteLine(ex); + return 0; + } + } + + public static void SendUsage( Type type, Mobile from ) + { + ConstructorInfo[] ctors = type.GetConstructors(); + bool foundCtor = false; + + for ( int i = 0; i < ctors.Length; ++i ) + { + ConstructorInfo ctor = ctors[i]; + + if ( !IsConstructable( ctor ) ) + continue; + + if ( !foundCtor ) + { + foundCtor = true; + from.SendMessage( "Usage:" ); + } + + SendCtor( type, ctor, from ); + } + + if ( !foundCtor ) + from.SendMessage( "That type is not marked constructable." ); + } + + public static void SendCtor( Type type, ConstructorInfo ctor, Mobile from ) + { + ParameterInfo[] paramList = ctor.GetParameters(); + + StringBuilder sb = new StringBuilder(); + + sb.Append( type.Name ); + + for ( int i = 0; i < paramList.Length; ++i ) + { + if ( i != 0 ) + sb.Append( ',' ); + + sb.Append( ' ' ); + + sb.Append( paramList[i].ParameterType.Name ); + sb.Append( ' ' ); + sb.Append( paramList[i].Name ); + } + + from.SendMessage( sb.ToString() ); + } + + public class AddTarget : Target + { + private string[] m_Args; + + public AddTarget( string[] args ) : base( -1, true, TargetFlags.None ) + { + m_Args = args; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + { + if ( p is Item ) + p = ((Item)p).GetWorldTop(); + else if ( p is Mobile ) + p = ((Mobile)p).Location; + + Add.Invoke( from, new Point3D( p ), new Point3D( p ), m_Args ); + } + } + } + + private class TileState + { + public bool m_UseFixedZ; + public int m_FixedZ; + public string[] m_Args; + + public TileState( string[] args ) : this( false, 0, args ) + { + } + + public TileState( int fixedZ, string[] args ) : this( true, fixedZ, args ) + { + } + + public TileState( bool useFixedZ, int fixedZ, string[] args ) + { + m_UseFixedZ = useFixedZ; + m_FixedZ = fixedZ; + m_Args = args; + } + } + + private static void TileBox_Callback( Mobile from, Map map, Point3D start, Point3D end, object state ) + { + TileState ts = (TileState)state; + + if ( ts.m_UseFixedZ ) + start.Z = end.Z = ts.m_FixedZ; + + Invoke( from, start, end, ts.m_Args ); + } + + [Usage( "Tile [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a targeted bounding box. Optional constructor parameters. Optional set property list." )] + public static void Tile_OnCommand( CommandEventArgs e ) + { + if ( e.Length >= 1 ) + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( e.Arguments ) ); + else + e.Mobile.SendMessage( "Format: Add [params] [set { ...}]" ); + } + + [Usage( "TileRXYZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a given bounding box, (x, y) parameters are relative to your characters position. Optional constructor parameters. Optional set property list." )] + public static void TileRXYZ_OnCommand( CommandEventArgs e ) + { + if ( e.Length >= 6 ) + { + Point3D p = new Point3D( e.Mobile.X + e.GetInt32( 0 ), e.Mobile.Y + e.GetInt32( 1 ), e.Mobile.Z + e.GetInt32( 4 ) ); + Point3D p2 = new Point3D( p.X + e.GetInt32( 2 ) - 1, p.Y + e.GetInt32( 3 ) - 1, p.Z ); + + string[] subArgs = new string[e.Length - 5]; + + for ( int i = 0; i < subArgs.Length; ++i ) + subArgs[i] = e.Arguments[i + 5]; + + Add.Invoke( e.Mobile, p, p2, subArgs ); + } + else + { + e.Mobile.SendMessage( "Format: TileRXYZ [params] [set { ...}]" ); + } + } + + [Usage( "TileXYZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a given bounding box. Optional constructor parameters. Optional set property list." )] + public static void TileXYZ_OnCommand( CommandEventArgs e ) + { + if ( e.Length >= 6 ) + { + Point3D p = new Point3D( e.GetInt32( 0 ), e.GetInt32( 1 ), e.GetInt32( 4 ) ); + Point3D p2 = new Point3D( p.X + e.GetInt32( 2 ) - 1, p.Y + e.GetInt32( 3 ) - 1, e.GetInt32( 4 ) ); + + string[] subArgs = new string[e.Length - 5]; + + for ( int i = 0; i < subArgs.Length; ++i ) + subArgs[i] = e.Arguments[i + 5]; + + Add.Invoke( e.Mobile, p, p2, subArgs ); + } + else + { + e.Mobile.SendMessage( "Format: TileXYZ [params] [set { ...}]" ); + } + } + + [Usage( "TileZ [params] [set { ...}]" )] + [Description( "Tiles an item or npc by name into a targeted bounding box at a fixed Z location. Optional constructor parameters. Optional set property list." )] + public static void TileZ_OnCommand( CommandEventArgs e ) + { + if ( e.Length >= 2 ) + { + string[] subArgs = new string[e.Length - 1]; + + for ( int i = 0; i < subArgs.Length; ++i ) + subArgs[i] = e.Arguments[i + 1]; + + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( TileBox_Callback ), new TileState( e.GetInt32( 0 ), subArgs ) ); + } + else + { + e.Mobile.SendMessage( "Format: TileZ [params] [set { ...}]" ); + } + } + + private static Type m_ConstructableType = typeof( ConstructableAttribute ); + + public static bool IsConstructable( ConstructorInfo ctor ) + { + return ctor.IsDefined( m_ConstructableType, false ); + } + + private static Type m_EnumType = typeof( Enum ); + + public static bool IsEnum( Type type ) + { + return type.IsSubclassOf( m_EnumType ); + } + + private static Type m_TypeType = typeof( Type ); + + public static bool IsType( Type type ) + { + return ( type == m_TypeType || type.IsSubclassOf( m_TypeType ) ); + } + + private static Type m_ParsableType = typeof( ParsableAttribute ); + + public static bool IsParsable( Type type ) + { + return type.IsDefined( m_ParsableType, false ); + } + + private static Type[] m_ParseTypes = new Type[]{ typeof( string ) }; + private static object[] m_ParseArgs = new object[1]; + + public static object ParseParsable( Type type, string value ) + { + MethodInfo method = type.GetMethod( "Parse", m_ParseTypes ); + + m_ParseArgs[0] = value; + + return method.Invoke( null, m_ParseArgs ); + } + + private static Type[] m_SignedNumerics = new Type[] + { + typeof( Int64 ), + typeof( Int32 ), + typeof( Int16 ), + typeof( SByte ) + }; + + public static bool IsSignedNumeric( Type type ) + { + for ( int i = 0; i < m_SignedNumerics.Length; ++i ) + if ( type == m_SignedNumerics[i] ) + return true; + + return false; + } + + private static Type[] m_UnsignedNumerics = new Type[] + { + typeof( UInt64 ), + typeof( UInt32 ), + typeof( UInt16 ), + typeof( Byte ) + }; + + public static bool IsUnsignedNumeric( Type type ) + { + for ( int i = 0; i < m_UnsignedNumerics.Length; ++i ) + if ( type == m_UnsignedNumerics[i] ) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Attributes.cs b/Scripts/Commands/Attributes.cs new file mode 100644 index 000000000..7891dde1f --- /dev/null +++ b/Scripts/Commands/Attributes.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server +{ + public class UsageAttribute : Attribute + { + private string m_Usage; + + public string Usage{ get{ return m_Usage; } } + + public UsageAttribute( string usage ) + { + m_Usage = usage; + } + } + + public class DescriptionAttribute : Attribute + { + private string m_Description; + + public string Description{ get{ return m_Description; } } + + public DescriptionAttribute( string description ) + { + m_Description = description; + } + } + + public class AliasesAttribute : Attribute + { + private string[] m_Aliases; + + public string[] Aliases{ get{ return m_Aliases; } } + + public AliasesAttribute( params string[] aliases ) + { + m_Aliases = aliases; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Batch.cs b/Scripts/Commands/Batch.cs new file mode 100644 index 000000000..0255130f4 --- /dev/null +++ b/Scripts/Commands/Batch.cs @@ -0,0 +1,458 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Commands +{ + public class Batch : BaseCommand + { + private BaseCommandImplementor m_Scope; + private string m_Condition; + private ArrayList m_BatchCommands; + + public BaseCommandImplementor Scope + { + get{ return m_Scope; } + set{ m_Scope = value; } + } + + public string Condition + { + get{ return m_Condition; } + set{ m_Condition = value; } + } + + public ArrayList BatchCommands + { + get{ return m_BatchCommands; } + } + + public Batch() + { + Commands = new string[]{ "Batch" }; + ListOptimized = true; + + m_BatchCommands = new ArrayList(); + m_Condition = ""; + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + if ( list.Count == 0 ) + { + LogFailure( "Nothing was found to use this command on." ); + return; + } + + try + { + BaseCommand[] commands = new BaseCommand[m_BatchCommands.Count]; + CommandEventArgs[] eventArgs = new CommandEventArgs[m_BatchCommands.Count]; + + for ( int i = 0; i < m_BatchCommands.Count; ++i ) + { + BatchCommand bc = (BatchCommand)m_BatchCommands[i]; + + string commandString, argString; + string[] args; + + bc.GetDetails( out commandString, out argString, out args ); + + BaseCommand command = (BaseCommand)m_Scope.Commands[commandString]; + + commands[i] = command; + eventArgs[i] = new CommandEventArgs( e.Mobile, commandString, argString, args ); + + if ( command == null ) + { + e.Mobile.SendMessage( "That is either an invalid command name or one that does not support this modifier: {0}.", commandString ); + return; + } + else if ( e.Mobile.AccessLevel < command.AccessLevel ) + { + e.Mobile.SendMessage( "You do not have access to that command: {0}.", commandString ); + return; + } + else if ( !command.ValidateArgs( m_Scope, eventArgs[i] ) ) + { + return; + } + } + + for ( int i = 0; i < commands.Length; ++i ) + { + BaseCommand command = commands[i]; + BatchCommand bc = (BatchCommand)m_BatchCommands[i]; + + if ( list.Count > 20 ) + CommandLogging.Enabled = false; + + ArrayList usedList; + + if ( Utility.InsensitiveCompare( bc.Object, "Current" ) == 0 ) + { + usedList = list; + } + else + { + Hashtable propertyChains = new Hashtable(); + + usedList = new ArrayList( list.Count ); + + for ( int j = 0; j < list.Count; ++j ) + { + object obj = list[j]; + + if ( obj == null ) + continue; + + Type type = obj.GetType(); + + PropertyInfo[] chain = (PropertyInfo[])propertyChains[type]; + + string failReason = ""; + + if ( chain == null && !propertyChains.Contains( type ) ) + propertyChains[type] = chain = Properties.GetPropertyInfoChain( e.Mobile, type, bc.Object, PropertyAccess.Read, ref failReason ); + + if ( chain == null ) + continue; + + PropertyInfo endProp = Properties.GetPropertyInfo( ref obj, chain, ref failReason ); + + if ( endProp == null ) + continue; + + try + { + obj = endProp.GetValue( obj, null ); + + if ( obj != null ) + usedList.Add( obj ); + } + catch + { + } + } + } + + command.ExecuteList( eventArgs[i], usedList ); + + if ( list.Count > 20 ) + CommandLogging.Enabled = true; + + command.Flush( e.Mobile, list.Count > 20 ); + } + } + catch ( Exception ex ) + { + e.Mobile.SendMessage( ex.Message ); + } + } + + public bool Run( Mobile from ) + { + if ( m_Scope == null ) + { + from.SendMessage( "You must select the batch command scope." ); + return false; + } + else if ( m_Condition.Length > 0 && !m_Scope.SupportsConditionals ) + { + from.SendMessage( "This command scope does not support conditionals." ); + return false; + } + else if ( m_Condition.Length > 0 && !Utility.InsensitiveStartsWith( m_Condition, "where" ) ) + { + from.SendMessage( "The condition field must start with \"where\"." ); + return false; + } + + string[] args = CommandSystem.Split( m_Condition ); + + m_Scope.Process( from, this, args ); + + return true; + } + + public static void Initialize() + { + CommandSystem.Register( "Batch", AccessLevel.Counselor, new CommandEventHandler( Batch_OnCommand ) ); + } + + [Usage( "Batch" )] + [Description( "Allows multiple commands to be run at the same time." )] + public static void Batch_OnCommand( CommandEventArgs e ) + { + Batch batch = new Batch(); + + e.Mobile.SendGump( new BatchGump( e.Mobile, batch ) ); + } + } + + public class BatchCommand + { + private string m_Command; + private string m_Object; + + public string Command + { + get{ return m_Command; } + set{ m_Command = value; } + } + + public string Object + { + get{ return m_Object; } + set{ m_Object = value; } + } + + public void GetDetails( out string command, out string argString, out string[] args ) + { + int indexOf = m_Command.IndexOf( ' ' ); + + if ( indexOf >= 0 ) + { + argString = m_Command.Substring( indexOf + 1 ); + + command = m_Command.Substring( 0, indexOf ); + args = CommandSystem.Split( argString ); + } + else + { + argString = ""; + command = m_Command.ToLower(); + args = new string[0]; + } + } + + public BatchCommand( string command, string obj ) + { + m_Command = command; + m_Object = obj; + } + } + + public class BatchGump : BaseGridGump + { + private Mobile m_From; + private Batch m_Batch; + + public BatchGump( Mobile from, Batch batch ) : base( 30, 30 ) + { + m_From = from; + m_Batch = batch; + + Render(); + } + + public void Render() + { + AddNewPage(); + + /* Header */ + AddEntryHeader( 20 ); + AddEntryHtml( 180, Center( "Batch Commands" ) ); + AddEntryHeader( 20 ); + AddNewLine(); + + AddEntryHeader( 9 ); + AddEntryLabel( 191, "Run Batch" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, GetButtonID( 1, 0, 0 ), ArrowRightWidth, ArrowRightHeight ); + AddNewLine(); + + AddBlankLine(); + + /* Scope */ + AddEntryHeader( 20 ); + AddEntryHtml( 180, Center( "Scope" ) ); + AddEntryHeader( 20 ); + AddNewLine(); + + AddEntryHeader( 9 ); + AddEntryLabel( 191, m_Batch.Scope == null ? "Select Scope" : m_Batch.Scope.Accessors[0] ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, GetButtonID( 1, 0, 1 ), ArrowRightWidth, ArrowRightHeight ); + AddNewLine(); + + AddBlankLine(); + + /* Condition */ + AddEntryHeader( 20 ); + AddEntryHtml( 180, Center( "Condition" ) ); + AddEntryHeader( 20 ); + AddNewLine(); + + AddEntryHeader( 9 ); + AddEntryText( 202, 0, m_Batch.Condition ); + AddEntryHeader( 9 ); + AddNewLine(); + + AddBlankLine(); + + /* Commands */ + AddEntryHeader( 20 ); + AddEntryHtml( 180, Center( "Commands" ) ); + AddEntryHeader( 20 ); + + for ( int i = 0; i < m_Batch.BatchCommands.Count; ++i ) + { + BatchCommand bc = (BatchCommand)m_Batch.BatchCommands[i]; + + AddNewLine(); + + AddImageTiled( CurrentX, CurrentY, 9, 2, 0x24A8 ); + AddImageTiled( CurrentX, CurrentY + 2, 2, EntryHeight + OffsetSize + EntryHeight - 4, 0x24A8 ); + AddImageTiled( CurrentX, CurrentY + EntryHeight + OffsetSize + EntryHeight - 2, 9, 2, 0x24A8 ); + AddImageTiled( CurrentX + 3, CurrentY + 3, 6, EntryHeight + EntryHeight - 4 - OffsetSize, HeaderGumpID ); + + IncreaseX( 9 ); + AddEntryText( 202, 1+(i*2), bc.Command ); + AddEntryHeader( 9, 2 ); + + AddNewLine(); + + IncreaseX( 9 ); + AddEntryText( 202, 2+(i*2), bc.Object ); + } + + AddNewLine(); + + AddEntryHeader( 9 ); + AddEntryLabel( 191, "Add New Command" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, GetButtonID( 1, 0, 2 ), ArrowRightWidth, ArrowRightHeight ); + + FinishPage(); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int type, index; + + if ( !SplitButtonID( info.ButtonID, 1, out type, out index ) ) + return; + + TextRelay entry = info.GetTextEntry( 0 ); + + if ( entry != null ) + m_Batch.Condition = entry.Text; + + for ( int i = m_Batch.BatchCommands.Count - 1; i >= 0; --i ) + { + BatchCommand sc = (BatchCommand)m_Batch.BatchCommands[i]; + + entry = info.GetTextEntry( 1 + (i * 2) ); + + if ( entry != null ) + sc.Command = entry.Text; + + entry = info.GetTextEntry( 2 + (i * 2) ); + + if ( entry != null ) + sc.Object = entry.Text; + + if ( sc.Command == "" && sc.Object == "" ) + m_Batch.BatchCommands.RemoveAt( i ); + } + + switch ( type ) + { + case 0: // main + { + switch ( index ) + { + case 0: // run + { + m_Batch.Run( m_From ); + break; + } + case 1: // set scope + { + m_From.SendGump( new BatchScopeGump( m_From, m_Batch ) ); + return; + } + case 2: // add command + { + m_Batch.BatchCommands.Add( new BatchCommand( "", "" ) ); + break; + } + } + + break; + } + } + + m_From.SendGump( new BatchGump( m_From, m_Batch ) ); + } + } + + public class BatchScopeGump : BaseGridGump + { + private Mobile m_From; + private Batch m_Batch; + + public BatchScopeGump( Mobile from, Batch batch ) : base( 30, 30 ) + { + m_From = from; + m_Batch = batch; + + Render(); + } + + public void Render() + { + AddNewPage(); + + /* Header */ + AddEntryHeader( 20 ); + AddEntryHtml( 140, Center( "Change Scope" ) ); + AddEntryHeader( 20 ); + + /* Options */ + for ( int i = 0; i < BaseCommandImplementor.Implementors.Count; ++i ) + { + BaseCommandImplementor impl = (BaseCommandImplementor)BaseCommandImplementor.Implementors[i]; + + if ( m_From.AccessLevel < impl.AccessLevel ) + continue; + + AddNewLine(); + + AddEntryLabel( 20 + OffsetSize + 140, impl.Accessors[0] ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, GetButtonID( 1, 0, i ), ArrowRightWidth, ArrowRightHeight ); + } + + FinishPage(); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int type, index; + + if ( SplitButtonID( info.ButtonID, 1, out type, out index ) ) + { + switch ( type ) + { + case 0: + { + if ( index < BaseCommandImplementor.Implementors.Count ) + { + BaseCommandImplementor impl = (BaseCommandImplementor)BaseCommandImplementor.Implementors[index]; + + if ( m_From.AccessLevel >= impl.AccessLevel ) + m_Batch.Scope = impl; + } + + break; + } + } + } + + m_From.SendGump( new BatchGump( m_From, m_Batch ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/BoundingBoxPicker.cs b/Scripts/Commands/BoundingBoxPicker.cs new file mode 100644 index 000000000..d0a5e8412 --- /dev/null +++ b/Scripts/Commands/BoundingBoxPicker.cs @@ -0,0 +1,68 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server +{ + public delegate void BoundingBoxCallback( Mobile from, Map map, Point3D start, Point3D end, object state ); + + public class BoundingBoxPicker + { + public static void Begin( Mobile from, BoundingBoxCallback callback, object state ) + { + from.SendMessage( "Target the first location of the bounding box." ); + from.Target = new PickTarget( callback, state ); + } + + private class PickTarget : Target + { + private Point3D m_Store; + private bool m_First; + private Map m_Map; + private BoundingBoxCallback m_Callback; + private object m_State; + + public PickTarget( BoundingBoxCallback callback, object state ) : this( Point3D.Zero, true, null, callback, state ) + { + } + + public PickTarget( Point3D store, bool first, Map map, BoundingBoxCallback callback, object state ) : base( -1, true, TargetFlags.None ) + { + m_Store = store; + m_First = first; + m_Map = map; + m_Callback = callback; + m_State = state; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + IPoint3D p = targeted as IPoint3D; + + if ( p == null ) + return; + else if ( p is Item ) + p = ((Item)p).GetWorldTop(); + + if ( m_First ) + { + from.SendMessage( "Target another location to complete the bounding box." ); + from.Target = new PickTarget( new Point3D( p ), false, from.Map, m_Callback, m_State ); + } + else if ( from.Map != m_Map ) + { + from.SendMessage( "Both locations must reside on the same map." ); + } + else if ( m_Map != null && m_Map != Map.Internal && m_Callback != null ) + { + Point3D start = m_Store; + Point3D end = new Point3D( p ); + + Utility.FixPoints( ref start, ref end ); + + m_Callback( from, m_Map, start, end, m_State ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/ConvertPlayers.cs b/Scripts/Commands/ConvertPlayers.cs new file mode 100644 index 000000000..b4af37e76 --- /dev/null +++ b/Scripts/Commands/ConvertPlayers.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Commands +{ + public class ConvertPlayers + { + public static void Initialize() + { + CommandSystem.Register( "ConvertPlayers", AccessLevel.Administrator, new CommandEventHandler( Convert_OnCommand ) ); + } + + public static void Convert_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendMessage( "Converting all players to PlayerMobile. You will be disconnected. Please Restart the server after the world has finished saving." ); + ArrayList mobs = new ArrayList( World.Mobiles.Values ); + int count = 0; + + foreach ( Mobile m in mobs ) + { + if ( m.Player && !(m is PlayerMobile ) ) + { + count++; + if ( m.NetState != null ) + m.NetState.Dispose(); + + PlayerMobile pm = new PlayerMobile( m.Serial ); + pm.DefaultMobileInit(); + + List copy = new List( m.Items ); + for (int i=0;i 0 ) + { + NetState.ProcessDisposedQueue(); + World.Save(); + + Console.WriteLine( "{0} players have been converted to PlayerMobile. Please restart the server.", count ); + while ( true ) + Console.ReadLine(); + } + else + { + e.Mobile.SendMessage( "Couldn't find any Players to convert." ); + } + } + + private static void CopyProps( Mobile to, Mobile from ) + { + Type type = typeof( Mobile ); + + PropertyInfo[] props = type.GetProperties( BindingFlags.Public | BindingFlags.Instance ); + + for (int p=0;p= 0 ) + { + labelNumber = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + break; + } + } + } + + item = new LocalizedSign( m_ItemID, labelNumber ); + } + else if ( m_Type == typeofAnkhWest || m_Type == typeofAnkhEast ) + { + bool bloodied = false; + + for ( int i = 0; !bloodied && i < m_Params.Length; ++i ) + bloodied = ( m_Params[i] == "Bloodied" ); + + if ( m_Type == typeofAnkhWest ) + item = new AnkhWest( bloodied ); + else + item = new AnkhEast( bloodied ); + } + else if ( m_Type == typeofMarkContainer ) + { + bool bone = false; + bool locked = false; + Map map = Map.Malas; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i] == "Bone" ) + { + bone = true; + } + else if ( m_Params[i] == "Locked" ) + { + locked = true; + } + else if ( m_Params[i].StartsWith( "TargetMap" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + map = Map.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + MarkContainer mc = new MarkContainer( bone, locked ); + + mc.TargetMap = map; + mc.Description = "strange location"; + + item = mc; + } + else if ( m_Type == typeofHintItem ) + { + int range = 0; + int messageNumber = 0; + string messageString = null; + int hintNumber = 0; + string hintString = null; + TimeSpan resetDelay = TimeSpan.Zero; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Range" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + range = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "WarningString" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + messageString = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "WarningNumber" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + messageNumber = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "HintString" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + hintString = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "HintNumber" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + hintNumber = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "ResetDelay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + resetDelay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + HintItem hi = new HintItem( m_ItemID, range, messageNumber, hintNumber ); + + hi.WarningString = messageString; + hi.HintString = hintString; + hi.ResetDelay = resetDelay; + + item = hi; + } + else if ( m_Type == typeofWarningItem ) + { + int range = 0; + int messageNumber = 0; + string messageString = null; + TimeSpan resetDelay = TimeSpan.Zero; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Range" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + range = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "WarningString" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + messageString = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "WarningNumber" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + messageNumber = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "ResetDelay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + resetDelay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + WarningItem wi = new WarningItem( m_ItemID, range, messageNumber ); + + wi.WarningString = messageString; + wi.ResetDelay = resetDelay; + + item = wi; + } + else if ( m_Type == typeofCannon ) + { + CannonDirection direction = CannonDirection.North; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "CannonDirection" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + direction = (CannonDirection)Enum.Parse( typeof( CannonDirection ), m_Params[i].Substring( ++indexOf ), true ); + } + } + + item = new Cannon( direction ); + } + else if ( m_Type == typeofSerpentPillar ) + { + string word = null; + Rectangle2D destination = new Rectangle2D(); + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Word" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + word = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "DestStart" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + destination.Start = Point2D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "DestEnd" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + destination.End = Point2D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + item = new SerpentPillar( word, destination ); + } + else if ( m_Type.IsSubclassOf( typeofBeverage ) ) + { + BeverageType content = BeverageType.Liquor; + bool fill = false; + + for ( int i = 0; !fill && i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Content" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + { + content = (BeverageType)Enum.Parse( typeof( BeverageType ), m_Params[i].Substring( ++indexOf ), true ); + fill = true; + } + } + } + + if ( fill ) + item = (Item)Activator.CreateInstance( m_Type, new object[]{ content } ); + else + item = (Item)Activator.CreateInstance( m_Type ); + } + else if ( m_Type.IsSubclassOf( typeofBaseDoor ) ) + { + DoorFacing facing = DoorFacing.WestCW; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Facing" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + { + facing = (DoorFacing)Enum.Parse( typeof( DoorFacing ), m_Params[i].Substring( ++indexOf ), true ); + break; + } + } + } + + item = (Item)Activator.CreateInstance( m_Type, new object[]{ facing } ); + } + else + { + item = (Item)Activator.CreateInstance( m_Type ); + } + } + catch ( Exception e ) + { + throw new Exception( String.Format( "Bad type: {0}", m_Type ), e ); + } + + if ( item is BaseAddon ) + { + if ( item is MaabusCoffin ) + { + MaabusCoffin coffin = (MaabusCoffin)item; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "SpawnLocation" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + coffin.SpawnLocation = Point3D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + } + else if ( m_ItemID > 0 ) + { + List comps = ((BaseAddon)item).Components; + + for ( int i = 0; i < comps.Count; ++i ) + { + AddonComponent comp = (AddonComponent)comps[i]; + + if ( comp.Offset == Point3D.Zero ) + comp.ItemID = m_ItemID; + } + } + } + else if ( item is BaseLight ) + { + bool unlit = false; + + for ( int i = 0; !unlit && i < m_Params.Length; ++i ) + unlit = ( m_Params[i] == "Unlit" ); + + if ( !unlit ) + ((BaseLight)item).Ignite(); + + ((BaseLight)item).Protected = true; + + if ( m_ItemID > 0 ) + item.ItemID = m_ItemID; + } + else if ( item is Server.Mobiles.Spawner ) + { + Server.Mobiles.Spawner sp = (Server.Mobiles.Spawner)item; + + sp.NextSpawn = TimeSpan.Zero; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Spawn" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.CreaturesName.Add( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "MinDelay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.MinDelay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "MaxDelay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.MaxDelay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "NextSpawn" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.NextSpawn = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Count" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.Count = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Team" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.Team = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "HomeRange" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.HomeRange = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Running" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.Running = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Group" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + sp.Group = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + } + } + else if ( item is RecallRune ) + { + RecallRune rune = (RecallRune)item; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Description" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + rune.Description = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "Marked" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + rune.Marked = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "TargetMap" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + rune.TargetMap = Map.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Target" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + rune.Target = Point3D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + } + else if ( item is SkillTeleporter ) + { + SkillTeleporter tp = (SkillTeleporter)item; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Skill" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Skill = (SkillName)Enum.Parse( typeof( SkillName ), m_Params[i].Substring( ++indexOf ), true ); + } + else if ( m_Params[i].StartsWith( "RequiredFixedPoint" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Required = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ) * 0.01; + } + else if ( m_Params[i].StartsWith( "Required" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Required = Utility.ToDouble( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "MessageString" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.MessageString = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "MessageNumber" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.MessageNumber = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "PointDest" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.PointDest = Point3D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "MapDest" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.MapDest = Map.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Creatures" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Creatures = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "SourceEffect" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.SourceEffect = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "DestEffect" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.DestEffect = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "SoundID" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.SoundID = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Delay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Delay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + if ( m_ItemID > 0 ) + item.ItemID = m_ItemID; + } + else if ( item is KeywordTeleporter ) + { + KeywordTeleporter tp = (KeywordTeleporter)item; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Substring" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Substring = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "Keyword" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Keyword = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Range" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Range = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "PointDest" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.PointDest = Point3D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "MapDest" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.MapDest = Map.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Creatures" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Creatures = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "SourceEffect" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.SourceEffect = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "DestEffect" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.DestEffect = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "SoundID" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.SoundID = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Delay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Delay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + if ( m_ItemID > 0 ) + item.ItemID = m_ItemID; + } + else if ( item is Teleporter ) + { + Teleporter tp = (Teleporter)item; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "PointDest" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.PointDest = Point3D.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "MapDest" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.MapDest = Map.Parse( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Creatures" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Creatures = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "SourceEffect" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.SourceEffect = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "DestEffect" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.DestEffect = Utility.ToBoolean( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "SoundID" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.SoundID = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + } + else if ( m_Params[i].StartsWith( "Delay" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + tp.Delay = TimeSpan.Parse( m_Params[i].Substring( ++indexOf ) ); + } + } + + if ( m_ItemID > 0 ) + item.ItemID = m_ItemID; + } + else if ( item is FillableContainer ) + { + FillableContainer cont = (FillableContainer) item; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "ContentType" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + cont.ContentType = (FillableContentType)Enum.Parse( typeof( FillableContentType ), m_Params[i].Substring( ++indexOf ), true ); + } + } + + if ( m_ItemID > 0 ) + item.ItemID = m_ItemID; + } + else if ( m_ItemID > 0 ) + { + item.ItemID = m_ItemID; + } + + item.Movable = false; + + for ( int i = 0; i < m_Params.Length; ++i ) + { + if ( m_Params[i].StartsWith( "Light" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + item.Light = (LightType)Enum.Parse( typeof( LightType ), m_Params[i].Substring( ++indexOf ), true ); + } + else if ( m_Params[i].StartsWith( "Hue" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + { + int hue = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + + if ( item is DyeTub ) + ((DyeTub)item).DyedHue = hue; + else + item.Hue = hue; + } + } + else if ( m_Params[i].StartsWith( "Name" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + item.Name = m_Params[i].Substring( ++indexOf ); + } + else if ( m_Params[i].StartsWith( "Amount" ) ) + { + int indexOf = m_Params[i].IndexOf( '=' ); + + if ( indexOf >= 0 ) + { + // Must supress stackable warnings + + bool wasStackable = item.Stackable; + + item.Stackable = true; + item.Amount = Utility.ToInt32( m_Params[i].Substring( ++indexOf ) ); + item.Stackable = wasStackable; + } + } + } + + return item; + } + + private static Queue m_DeleteQueue = new Queue(); + + private static bool FindItem( int x, int y, int z, Map map, Item srcItem ) + { + int itemID = srcItem.ItemID; + + bool res = false; + + IPooledEnumerable eable; + + if ( srcItem is BaseDoor ) + { + eable = map.GetItemsInRange( new Point3D( x, y, z ), 1 ); + + foreach ( Item item in eable ) + { + if ( !(item is BaseDoor) ) + continue; + + BaseDoor bd = (BaseDoor)item; + Point3D p; + int bdItemID; + + if ( bd.Open ) + { + p = new Point3D( bd.X - bd.Offset.X, bd.Y - bd.Offset.Y, bd.Z - bd.Offset.Z ); + bdItemID = bd.ClosedID; + } + else + { + p = bd.Location; + bdItemID = bd.ItemID; + } + + if ( p.X != x || p.Y != y ) + continue; + + if ( item.Z == z && bdItemID == itemID ) + res = true; + else if ( Math.Abs( item.Z - z ) < 8 ) + m_DeleteQueue.Enqueue( item ); + } + } + else if ( (TileData.ItemTable[itemID & 0x3FFF].Flags & TileFlag.LightSource) != 0 ) + { + eable = map.GetItemsInRange( new Point3D( x, y, z ), 0 ); + + LightType lt = srcItem.Light; + string srcName = srcItem.ItemData.Name; + + foreach ( Item item in eable ) + { + if ( item.Z == z ) + { + if ( item.ItemID == itemID ) + { + if ( item.Light != lt ) + m_DeleteQueue.Enqueue( item ); + else + res = true; + } + else if ( (item.ItemData.Flags & TileFlag.LightSource) != 0 && item.ItemData.Name == srcName ) + { + m_DeleteQueue.Enqueue( item ); + } + } + } + } + else if ( srcItem is Teleporter || srcItem is FillableContainer || srcItem is BaseBook ) + { + eable = map.GetItemsInRange( new Point3D( x, y, z ), 0 ); + + Type type = srcItem.GetType(); + + foreach ( Item item in eable ) + { + if ( item.Z == z && item.ItemID == itemID ) + { + if ( item.GetType() != type ) + m_DeleteQueue.Enqueue( item ); + else + res = true; + } + } + } + else + { + eable = map.GetItemsInRange( new Point3D( x, y, z ), 0 ); + + foreach ( Item item in eable ) + { + if ( item.Z == z && item.ItemID == itemID ) + { + eable.Free(); + return true; + } + } + } + + eable.Free(); + + while ( m_DeleteQueue.Count > 0 ) + ((Item)m_DeleteQueue.Dequeue()).Delete(); + + return res; + } + + public int Generate( Map[] maps ) + { + int count = 0; + + Item item = null; + + for ( int i = 0; i < m_Entries.Count; ++i ) + { + DecorationEntry entry = (DecorationEntry)m_Entries[i]; + Point3D loc = entry.Location; + string extra = entry.Extra; + + for ( int j = 0; j < maps.Length; ++j ) + { + if ( item == null ) + item = Construct(); + + if ( item == null ) + continue; + + if ( FindItem( loc.X, loc.Y, loc.Z, maps[j], item ) ) + { + } + else + { + item.MoveToWorld( loc, maps[j] ); + ++count; + + if ( item is BaseDoor ) + { + IPooledEnumerable eable = maps[j].GetItemsInRange( loc, 1 ); + + Type itemType = item.GetType(); + + foreach ( Item link in eable ) + { + if ( link != item && link.Z == item.Z && link.GetType() == itemType ) + { + ((BaseDoor)item).Link = (BaseDoor)link; + ((BaseDoor)link).Link = (BaseDoor)item; + break; + } + } + + eable.Free(); + } + else if ( item is MarkContainer ) + { + try{ ((MarkContainer)item).Target = Point3D.Parse( extra ); } + catch{} + } + + item = null; + } + } + } + + if ( item != null ) + item.Delete(); + + return count; + } + + public static ArrayList ReadAll( string path ) + { + using ( StreamReader ip = new StreamReader( path ) ) + { + ArrayList list = new ArrayList(); + + for ( DecorationList v = Read( ip ); v != null; v = Read( ip ) ) + list.Add( v ); + + return list; + } + } + + private static string[] m_EmptyParams = new string[0]; + + public static DecorationList Read( StreamReader ip ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + line = line.Trim(); + + if ( line.Length > 0 && !line.StartsWith( "#" ) ) + break; + } + + if ( line == null || line.Length == 0 ) + return null; + + DecorationList list = new DecorationList(); + + int indexOf = line.IndexOf( ' ' ); + + list.m_Type = ScriptCompiler.FindTypeByName( line.Substring( 0, indexOf++ ), true ); + + if ( list.m_Type == null ) + throw new ArgumentException( String.Format( "Type not found for header: '{0}'", line ) ); + + line = line.Substring( indexOf ); + indexOf = line.IndexOf( '(' ); + if ( indexOf >= 0 ) + { + list.m_ItemID = Utility.ToInt32( line.Substring( 0, indexOf - 1 ) ); + + string parms = line.Substring( ++indexOf ); + + if ( line.EndsWith( ")" ) ) + parms = parms.Substring( 0, parms.Length - 1 ); + + list.m_Params = parms.Split( ';' ); + + for ( int i = 0; i < list.m_Params.Length; ++i ) + list.m_Params[i] = list.m_Params[i].Trim(); + } + else + { + list.m_ItemID = Utility.ToInt32( line ); + list.m_Params = m_EmptyParams; + } + + list.m_Entries = new ArrayList(); + + while ( (line = ip.ReadLine()) != null ) + { + line = line.Trim(); + + if ( line.Length == 0 ) + break; + + if ( line.StartsWith( "#" ) ) + continue; + + list.m_Entries.Add( new DecorationEntry( line ) ); + } + + return list; + } + } + + public class DecorationEntry + { + private Point3D m_Location; + private string m_Extra; + + public Point3D Location{ get{ return m_Location; } } + public string Extra{ get{ return m_Extra; } } + + public DecorationEntry( string line ) + { + string x, y, z; + + Pop( out x, ref line ); + Pop( out y, ref line ); + Pop( out z, ref line ); + + m_Location = new Point3D( Utility.ToInt32( x ), Utility.ToInt32( y ), Utility.ToInt32( z ) ); + m_Extra = line; + } + + public void Pop( out string v, ref string line ) + { + int space = line.IndexOf( ' ' ); + + if ( space >= 0 ) + { + v = line.Substring( 0, space++ ); + line = line.Substring( space ); + } + else + { + v = line; + line = ""; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Docs.cs b/Scripts/Commands/Docs.cs new file mode 100644 index 000000000..df1a9cf86 --- /dev/null +++ b/Scripts/Commands/Docs.cs @@ -0,0 +1,2404 @@ +using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.BulkOrders; +using Server.Commands.Generic; + +namespace Server.Commands +{ + public class Docs + { + public static void Initialize() + { + CommandSystem.Register( "DocGen", AccessLevel.Administrator, new CommandEventHandler( DocGen_OnCommand ) ); + } + + [Usage( "DocGen" )] + [Description( "Generates RunUO documentation." )] + private static void DocGen_OnCommand( CommandEventArgs e ) + { + World.Broadcast( 0x35, true, "Documentation is being generated, please wait." ); + + Network.NetState.FlushAll(); + Network.NetState.Pause(); + + DateTime startTime = DateTime.Now; + + Document(); + + DateTime endTime = DateTime.Now; + + Network.NetState.Resume(); + + World.Broadcast( 0x35, true, "Documentation has been completed. The entire process took {0:F1} seconds.", (endTime - startTime).TotalSeconds ); + } + + private class MemberComparer : IComparer + { + public int Compare( object x, object y ) + { + if ( x == y ) + return 0; + + ConstructorInfo aCtor = x as ConstructorInfo; + ConstructorInfo bCtor = y as ConstructorInfo; + + PropertyInfo aProp = x as PropertyInfo; + PropertyInfo bProp = y as PropertyInfo; + + MethodInfo aMethod = x as MethodInfo; + MethodInfo bMethod = y as MethodInfo; + + bool aStatic = GetStaticFor( aCtor, aProp, aMethod ); + bool bStatic = GetStaticFor( bCtor, bProp, bMethod ); + + if ( aStatic && !bStatic ) + return -1; + else if ( !aStatic && bStatic ) + return 1; + + int v = 0; + + if ( aCtor != null ) + { + if ( bCtor == null ) + v = -1; + } + else if ( bCtor != null ) + { + if ( aCtor == null ) + v = 1; + } + else if ( aProp != null ) + { + if ( bProp == null ) + v = -1; + } + else if ( bProp != null ) + { + if ( aProp == null ) + v = 1; + } + + if ( v == 0 ) + { + v = GetNameFrom( aCtor, aProp, aMethod ).CompareTo( GetNameFrom( bCtor, bProp, bMethod ) ); + } + + if ( v == 0 && aCtor != null && bCtor != null ) + { + v = aCtor.GetParameters().Length.CompareTo( bCtor.GetParameters().Length ); + } + else if ( v == 0 && aMethod != null && bMethod != null ) + { + v = aMethod.GetParameters().Length.CompareTo( bMethod.GetParameters().Length ); + } + + return v; + } + + private bool GetStaticFor( ConstructorInfo ctor, PropertyInfo prop, MethodInfo method ) + { + if ( ctor != null ) + return ctor.IsStatic; + else if ( method != null ) + return method.IsStatic; + + if ( prop != null ) + { + MethodInfo getMethod = prop.GetGetMethod(); + MethodInfo setMethod = prop.GetGetMethod(); + + return (getMethod != null && getMethod.IsStatic) || (setMethod != null && setMethod.IsStatic); + } + + return false; + } + + private string GetNameFrom( ConstructorInfo ctor, PropertyInfo prop, MethodInfo method ) + { + if ( ctor != null ) + return ctor.DeclaringType.Name; + else if ( prop != null ) + return prop.Name; + else if ( method != null ) + return method.Name; + else + return ""; + } + } + + private class TypeComparer : IComparer + { + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + TypeInfo a = x as TypeInfo; + TypeInfo b = y as TypeInfo; + + if ( a == null || b == null ) + throw new ArgumentException(); + + return a.m_TypeName.CompareTo( b.m_TypeName ); + } + } + + private class NamespaceComparer : IComparer + { + public int Compare( object x, object y ) + { + DictionaryEntry a = (DictionaryEntry)x; + DictionaryEntry b = (DictionaryEntry)y; + + return ((string)a.Key).CompareTo( (string)b.Key ); + } + } + + private class TypeInfo + { + public Type m_Type, m_BaseType, m_Declaring; + public string m_FileName, m_TypeName; + public ArrayList m_Derived, m_Nested; + public Type[] m_Interfaces; + public StreamWriter m_Writer; + + public TypeInfo( Type type ) + { + m_Type = type; + + m_BaseType = type.BaseType; + m_Declaring = type.DeclaringType; + + m_Interfaces = type.GetInterfaces(); + + m_TypeName = type.Name.Replace( '<', '(' ); + m_TypeName = m_TypeName.Replace( '>', ')' ); + m_FileName = Docs.GetFileName( "docs/types/", m_TypeName, ".html" ); + + m_Writer = Docs.GetWriter( "docs/types/", m_FileName ); + } + } + + private static readonly char[] ReplaceChars = "<>".ToCharArray(); + + public static string GetFileName( string root, string name, string ext ) + { + if ( name.IndexOfAny( ReplaceChars ) >= 0 ) { + StringBuilder sb = new StringBuilder( name ); + + for ( int i = 0; i < ReplaceChars.Length; ++i ) { + sb.Replace( ReplaceChars[i], '-' ); + } + + name = sb.ToString(); + } + + int index = 0; + string file = String.Concat( name, ext ); + + while ( File.Exists( Path.Combine( root, file ) ) ) { + file = String.Concat( name, ++index, ext ); + } + + return file; + } + + private static string[,] m_Aliases = new string[,] + { + { "System.Object", "object" }, + { "System.String", "string" }, + { "System.Boolean", "bool" }, + { "System.Byte", "byte" }, + { "System.SByte", "sbyte" }, + { "System.Int16", "short" }, + { "System.UInt16", "ushort" }, + { "System.Int32", "int" }, + { "System.UInt32", "uint" }, + { "System.Int64", "long" }, + { "System.UInt64", "ulong" }, + { "System.Single", "float" }, + { "System.Double", "double" }, + { "System.Decimal", "decimal" }, + { "System.Char", "char" }, + { "System.Void", "void" } + }; + + private static string m_RootDirectory = Path.GetDirectoryName( Environment.GetCommandLineArgs()[0] ); + + private const string RefString = "ref "; + + private static int m_AliasLength = m_Aliases.GetLength( 0 ); + + public static string GetPair( Type varType, string name, bool ignoreRef ) + { + string prepend = ""; + StringBuilder append = new StringBuilder(); + + Type realType = varType; + + if ( varType.IsByRef ) + { + if ( !ignoreRef ) + prepend = RefString; + + realType = varType.GetElementType(); + } + + if ( realType.IsPointer ) + { + if ( realType.IsArray ) + { + append.Append( '*' ); + + do + { + append.Append( '[' ); + + for ( int i = 1; i < realType.GetArrayRank(); ++i ) + append.Append( ',' ); + + append.Append( ']' ); + + realType = realType.GetElementType(); + } while ( realType.IsArray ); + + append.Append( ' ' ); + } + else + { + realType = realType.GetElementType(); + append.Append( " *" ); + } + } + else if ( realType.IsArray ) + { + do + { + append.Append( '[' ); + + for ( int i = 1; i < realType.GetArrayRank(); ++i ) + append.Append( ',' ); + + append.Append( ']' ); + + realType = realType.GetElementType(); + } while ( realType.IsArray ); + + append.Append( ' ' ); + } + else + { + append.Append( ' ' ); + } + + string fullName = realType.FullName; + string aliased = null;// = realType.Name; + + TypeInfo info = (TypeInfo)m_Types[realType]; + + if ( info != null ) + { + aliased = String.Format( "{1}", info.m_FileName, info.m_TypeName ); + } + else + { + for ( int i = 0; i < m_AliasLength; ++i ) + { + if ( m_Aliases[i, 0] == fullName ) + { + aliased = m_Aliases[i, 1]; + break; + } + } + + if ( aliased == null ) + aliased = realType.Name; + } + + return String.Concat( prepend, aliased, append, name ); + } + + private static Hashtable m_Types; + private static Hashtable m_Namespaces; + + private static void EnsureDirectory( string path ) + { + path = Path.Combine( m_RootDirectory, path ); + + if ( !Directory.Exists( path ) ) + Directory.CreateDirectory( path ); + } + + private static void DeleteDirectory( string path ) + { + path = Path.Combine( m_RootDirectory, path ); + + if ( Directory.Exists( path ) ) + Directory.Delete( path, true ); + } + + private static void Document() + { + DeleteDirectory( "docs/" ); + EnsureDirectory( "docs/" ); + EnsureDirectory( "docs/namespaces/" ); + EnsureDirectory( "docs/types/" ); + EnsureDirectory( "docs/bods/" ); + + GenerateStyles(); + GenerateIndex(); + + DocumentCommands(); + DocumentKeywords(); + DocumentBodies(); + + DocumentBulkOrders(); + + m_Types = new Hashtable(); + m_Namespaces = new Hashtable(); + + ArrayList assemblies = new ArrayList(); + + assemblies.Add( Core.Assembly ); + + foreach ( Assembly asm in ScriptCompiler.Assemblies ) + assemblies.Add( asm ); + + Assembly[] asms = (Assembly[])assemblies.ToArray( typeof( Assembly ) ); + + for ( int i = 0; i < asms.Length; ++i ) + LoadTypes( asms[i], asms ); + + DocumentLoadedTypes(); + DocumentConstructableObjects(); + } + + private static void AddIndexLink( StreamWriter html, string filePath, string label, string desc ) + { + html.WriteLine( "

{2}

", filePath, desc, label ); + } + + private static void GenerateStyles() + { + using ( StreamWriter css = GetWriter( "docs/", "styles.css" ) ) + { + css.WriteLine( "body { background-color: #FFFFFF; font-family: verdana, arial; font-size: 11px; }" ); + css.WriteLine( "a { color: #28435E; }" ); + css.WriteLine( "a:hover { color: #4878A9; }" ); + css.WriteLine( "td.header { background-color: #9696AA; font-weight: bold; font-size: 12px; }" ); + css.WriteLine( "td.lentry { background-color: #D7D7EB; width: 10%; }" ); + css.WriteLine( "td.rentry { background-color: #FFFFFF; width: 90%; }" ); + css.WriteLine( "td.entry { background-color: #FFFFFF; }" ); + css.WriteLine( "td { font-size: 11px; }" ); + css.WriteLine( ".tbl-border { background-color: #46465A; }" ); + + css.WriteLine( "td.ir {{ background-color: #{0:X6}; }}", Iron ); + css.WriteLine( "td.du {{ background-color: #{0:X6}; }}", DullCopper ); + css.WriteLine( "td.sh {{ background-color: #{0:X6}; }}", ShadowIron ); + css.WriteLine( "td.co {{ background-color: #{0:X6}; }}", Copper ); + css.WriteLine( "td.br {{ background-color: #{0:X6}; }}", Bronze ); + css.WriteLine( "td.go {{ background-color: #{0:X6}; }}", Gold ); + css.WriteLine( "td.ag {{ background-color: #{0:X6}; }}", Agapite ); + css.WriteLine( "td.ve {{ background-color: #{0:X6}; }}", Verite ); + css.WriteLine( "td.va {{ background-color: #{0:X6}; }}", Valorite ); + + css.WriteLine( "td.cl {{ background-color: #{0:X6}; }}", Cloth ); + css.WriteLine( "td.pl {{ background-color: #{0:X6}; }}", Plain ); + css.WriteLine( "td.sp {{ background-color: #{0:X6}; }}", Core.AOS ? SpinedAOS : SpinedLBR ); + css.WriteLine( "td.ho {{ background-color: #{0:X6}; }}", Core.AOS ? HornedAOS : HornedLBR ); + css.WriteLine( "td.ba {{ background-color: #{0:X6}; }}", Core.AOS ? BarbedAOS : BarbedLBR ); + } + } + + private static void GenerateIndex() + { + using ( StreamWriter html = GetWriter( "docs/", "index.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Index" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + + AddIndexLink( html, "commands.html", "Commands", "Every available command. This contains command name, usage, aliases, and description." ); + AddIndexLink( html, "objects.html", "Constructable Objects", "Every constructable item or npc. This contains object name and usage. Hover mouse over parameters to see type description." ); + AddIndexLink( html, "keywords.html", "Speech Keywords", "Lists speech keyword numbers and associated match patterns. These are used in some scripts for multi-language matching of client speech." ); + AddIndexLink( html, "bodies.html", "Body List", "Every usable body number and name. Table is generated from a UO:3D client datafile. If you do not have UO:3D installed, this may be blank." ); + AddIndexLink( html, "overview.html", "Class Overview", "Scripting reference. Contains every class type and contained methods in the core and scripts." ); + AddIndexLink( html, "bods/bod_smith_rewards.html", "Bulk Order Rewards: Smithing", "Reference table for large and small smithing bulk order deed rewards." ); + AddIndexLink( html, "bods/bod_tailor_rewards.html", "Bulk Order Rewards: Tailoring", "Reference table for large and small tailoring bulk order deed rewards." ); + + html.WriteLine( " " ); + html.WriteLine( "" ); + } + } + + private const int Iron = 0xCCCCDD; + private const int DullCopper = 0xAAAAAA; + private const int ShadowIron = 0x777799; + private const int Copper = 0xDDCC99; + private const int Bronze = 0xAA8866; + private const int Gold = 0xDDCC55; + private const int Agapite = 0xDDAAAA; + private const int Verite = 0x99CC77; + private const int Valorite = 0x88AABB; + + private const int Cloth = 0xDDDDDD; + private const int Plain = 0xCCAA88; + private const int SpinedAOS = 0x99BBBB; + private const int HornedAOS = 0xCC8888; + private const int BarbedAOS = 0xAABBAA; + private const int SpinedLBR = 0xAA8833; + private const int HornedLBR = 0xBBBBAA; + private const int BarbedLBR = 0xCCAA88; + + private static void DocumentBulkOrders() + { + using ( StreamWriter html = GetWriter( "docs/bods/", "bod_smith_rewards.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Bulk Orders - Smith Rewards" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + + SmallBOD sbod = new SmallSmithBOD(); + + sbod.Type = typeof( Katana ); + sbod.Material = BulkMaterialType.None; + sbod.AmountMax = 10; + + WriteSmithBODHeader( html, "(Small) Weapons" ); + sbod.RequireExceptional = false; + DocumentSmithBOD( html, sbod.ComputeRewards( true ), "10, 15, 20: Normal", sbod.Material ); + sbod.RequireExceptional = true; + DocumentSmithBOD( html, sbod.ComputeRewards( true ), "10, 15, 20: Exceptional", sbod.Material ); + WriteSmithBODFooter( html ); + + html.WriteLine( "

" ); + html.WriteLine( "

" ); + + + sbod.Type = typeof( PlateArms ); + + WriteSmithBODHeader( html, "(Small) Armor: Normal" ); + + sbod.RequireExceptional = false; + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat ) + { + sbod.Material = mat; + sbod.AmountMax = 10; + DocumentSmithBOD( html, sbod.ComputeRewards( true ), "10, 15, 20", sbod.Material ); + } + + WriteSmithBODFooter( html ); + + html.WriteLine( "

" ); + + WriteSmithBODHeader( html, "(Small) Armor: Exceptional" ); + + sbod.RequireExceptional = true; + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat ) + { + sbod.Material = mat; + + for ( int amt = 15; amt <= 20; amt += 5 ) + { + sbod.AmountMax = amt; + DocumentSmithBOD( html, sbod.ComputeRewards( true ), amt == 20 ? "20" : "10, 15", sbod.Material ); + } + } + + WriteSmithBODFooter( html ); + + html.WriteLine( "

" ); + html.WriteLine( "

" ); + + sbod.Delete(); + + WriteSmithLBOD( html, "Ringmail", LargeBulkEntry.LargeRing ); + WriteSmithLBOD( html, "Chainmail", LargeBulkEntry.LargeChain ); + WriteSmithLBOD( html, "Platemail", LargeBulkEntry.LargePlate ); + + html.WriteLine( " " ); + html.WriteLine( "" ); + } + + using ( StreamWriter html = GetWriter( "docs/bods/", "bod_tailor_rewards.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Bulk Orders - Tailor Rewards" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + + SmallBOD sbod = new SmallTailorBOD(); + + WriteTailorBODHeader( html, "Small Bulk Order" ); + + html.WriteLine( " " ); + html.WriteLine( " Regular: 10, 15" ); + html.WriteLine( " " ); + + sbod.AmountMax = 10; + sbod.RequireExceptional = false; + + sbod.Type = typeof( SkullCap ); + sbod.Material = BulkMaterialType.None; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "10, 15", sbod.Material, sbod.Type ); + + sbod.Type = typeof( LeatherCap ); + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat ) + { + if ( mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite ) + continue; + + sbod.Material = mat; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "10, 15", sbod.Material, sbod.Type ); + } + + html.WriteLine( " " ); + html.WriteLine( " Regular: 20" ); + html.WriteLine( " " ); + + sbod.AmountMax = 20; + sbod.RequireExceptional = false; + + sbod.Type = typeof( SkullCap ); + sbod.Material = BulkMaterialType.None; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "20", sbod.Material, sbod.Type ); + + sbod.Type = typeof( LeatherCap ); + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat ) + { + if ( mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite ) + continue; + + sbod.Material = mat; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "20", sbod.Material, sbod.Type ); + } + + html.WriteLine( " " ); + html.WriteLine( " Exceptional: 10, 15" ); + html.WriteLine( " " ); + + sbod.AmountMax = 10; + sbod.RequireExceptional = true; + + sbod.Type = typeof( SkullCap ); + sbod.Material = BulkMaterialType.None; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "10, 15", sbod.Material, sbod.Type ); + + sbod.Type = typeof( LeatherCap ); + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat ) + { + if ( mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite ) + continue; + + sbod.Material = mat; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "10, 15", sbod.Material, sbod.Type ); + } + + html.WriteLine( " " ); + html.WriteLine( " Exceptional: 20" ); + html.WriteLine( " " ); + + sbod.AmountMax = 20; + sbod.RequireExceptional = true; + + sbod.Type = typeof( SkullCap ); + sbod.Material = BulkMaterialType.None; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "20", sbod.Material, sbod.Type ); + + sbod.Type = typeof( LeatherCap ); + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Barbed; ++mat ) + { + if ( mat >= BulkMaterialType.DullCopper && mat <= BulkMaterialType.Valorite ) + continue; + + sbod.Material = mat; + DocumentTailorBOD( html, sbod.ComputeRewards( true ), "20", sbod.Material, sbod.Type ); + } + + WriteTailorBODFooter( html ); + + html.WriteLine( "

" ); + html.WriteLine( "

" ); + + sbod.Delete(); + + WriteTailorLBOD( html, "Large Bulk Order: 4-part", LargeBulkEntry.Gypsy, true, true ); + WriteTailorLBOD( html, "Large Bulk Order: 5-part", LargeBulkEntry.TownCrier, true, true ); + WriteTailorLBOD( html, "Large Bulk Order: 6-part", LargeBulkEntry.MaleLeatherSet, false, true ); + + html.WriteLine( " " ); + html.WriteLine( "" ); + } + } + + #region Tailor Bods + private static void WriteTailorLBOD( StreamWriter html, string name, SmallBulkEntry[] entries, bool expandCloth, bool expandPlain ) + { + WriteTailorBODHeader( html, name ); + + LargeBOD lbod = new LargeTailorBOD(); + + lbod.Entries = LargeBulkEntry.ConvertEntries( lbod, entries ); + + Type type = entries[0].Type; + + bool showCloth = !( type.IsSubclassOf( typeof( BaseArmor ) ) || type.IsSubclassOf( typeof( BaseShoes ) ) ); + + html.WriteLine( " " ); + html.WriteLine( " Regular" ); + html.WriteLine( " " ); + + lbod.RequireExceptional = false; + lbod.AmountMax = 10; + + if ( showCloth ) + { + lbod.Material = BulkMaterialType.None; + + if ( expandCloth ) + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15", lbod.Material, type ); + lbod.AmountMax = 20; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "20", lbod.Material, type ); + } + else + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material, type ); + } + } + + lbod.Material = BulkMaterialType.None; + + if ( expandPlain ) + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material, typeof( LeatherCap ) ); + lbod.AmountMax = 20; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "20", lbod.Material, typeof( LeatherCap ) ); + } + else + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material, typeof( LeatherCap ) ); + } + + for ( BulkMaterialType mat = BulkMaterialType.Spined; mat <= BulkMaterialType.Barbed; ++mat ) + { + lbod.Material = mat; + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15", lbod.Material, type ); + lbod.AmountMax = 20; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "20", lbod.Material, type ); + } + + html.WriteLine( " " ); + html.WriteLine( " Exceptional" ); + html.WriteLine( " " ); + + lbod.RequireExceptional = true; + lbod.AmountMax = 10; + + if ( showCloth ) + { + lbod.Material = BulkMaterialType.None; + + if ( expandCloth ) + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15", lbod.Material, type ); + lbod.AmountMax = 20; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "20", lbod.Material, type ); + } + else + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material, type ); + } + } + + lbod.Material = BulkMaterialType.None; + + if ( expandPlain ) + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material, typeof( LeatherCap ) ); + lbod.AmountMax = 20; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "20", lbod.Material, typeof( LeatherCap ) ); + } + else + { + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material, typeof( LeatherCap ) ); + } + + for ( BulkMaterialType mat = BulkMaterialType.Spined; mat <= BulkMaterialType.Barbed; ++mat ) + { + lbod.Material = mat; + lbod.AmountMax = 10; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "10, 15", lbod.Material, type ); + lbod.AmountMax = 20; + DocumentTailorBOD( html, lbod.ComputeRewards( true ), "20", lbod.Material, type ); + } + + WriteTailorBODFooter( html ); + + html.WriteLine( "

" ); + html.WriteLine( "

" ); + } + + private static void WriteTailorBODHeader( StreamWriter html, string title ) + { + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " ", title ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + } + + private static void WriteTailorBODFooter( StreamWriter html ) + { + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "
{0}
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
Power Scrolls
\"Small
\"Medium
\"Light
\"Dark
\"Brown
\"Polar
\"Clothing
Runic Kits
+5
+10
+15
+20
\"Runic
\"Runic
\"Runic
 
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
\"Colored
+5
+10
+15
+20
\"Small
\"Medium
\"Light
\"Dark
\"Brown
\"Polar
\"Clothing
\"Runic
\"Runic
\"Runic
Power Scrolls
Runic Kits
" ); + } + + private static void DocumentTailorBOD( StreamWriter html, ArrayList items, string amt, BulkMaterialType material, Type type ) + { + bool[] rewards = new bool[20]; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = (Item)items[i]; + + if ( item is Sandals ) + rewards[5] = true; + else if ( item is SmallStretchedHideEastDeed || item is SmallStretchedHideSouthDeed ) + rewards[10] = rewards[11] = true; + else if ( item is MediumStretchedHideEastDeed || item is MediumStretchedHideSouthDeed ) + rewards[10] = rewards[11] = true; + else if ( item is LightFlowerTapestryEastDeed || item is LightFlowerTapestrySouthDeed ) + rewards[12] = rewards[13] = true; + else if ( item is DarkFlowerTapestryEastDeed || item is DarkFlowerTapestrySouthDeed ) + rewards[12] = rewards[13] = true; + else if ( item is BrownBearRugEastDeed || item is BrownBearRugSouthDeed ) + rewards[14] = rewards[15] = true; + else if ( item is PolarBearRugEastDeed || item is PolarBearRugSouthDeed ) + rewards[14] = rewards[15] = true; + else if ( item is ClothingBlessDeed ) + rewards[16] = true; + else if ( item is PowerScroll ) + { + PowerScroll ps = (PowerScroll)item; + + if ( ps.Value == 105.0 ) + rewards[6] = true; + else if ( ps.Value == 110.0 ) + rewards[7] = true; + else if ( ps.Value == 115.0 ) + rewards[8] = true; + else if ( ps.Value == 120.0 ) + rewards[9] = true; + } + else if ( item is UncutCloth ) + { + if ( item.Hue == 0x483 || item.Hue == 0x48C || item.Hue == 0x488 || item.Hue == 0x48A ) + rewards[0] = true; + else if ( item.Hue == 0x495 || item.Hue == 0x48B || item.Hue == 0x486 || item.Hue == 0x485 ) + rewards[1] = true; + else if ( item.Hue == 0x48D || item.Hue == 0x490 || item.Hue == 0x48E || item.Hue == 0x491 ) + rewards[2] = true; + else if ( item.Hue == 0x48F || item.Hue == 0x494 || item.Hue == 0x484 || item.Hue == 0x497 ) + rewards[3] = true; + else + rewards[4] = true; + } + else if ( item is RunicSewingKit ) + { + RunicSewingKit rkit = (RunicSewingKit)item; + + rewards[16 + CraftResources.GetIndex( rkit.Resource )] = true; + } + + item.Delete(); + } + + string style = null; + string name = null; + + switch ( material ) + { + case BulkMaterialType.None: + { + if ( type.IsSubclassOf( typeof( BaseArmor ) ) || type.IsSubclassOf( typeof( BaseShoes ) ) ) + { + style = "pl"; + name = "Plain"; + } + else + { + style = "cl"; + name = "Cloth"; + } + + break; + } + case BulkMaterialType.Spined: style = "sp"; name = "Spined"; break; + case BulkMaterialType.Horned: style = "ho"; name = "Horned"; break; + case BulkMaterialType.Barbed: style = "ba"; name = "Barbed"; break; + } + + html.WriteLine( " " ); + html.WriteLine( "  - {0} {1}", name, amt ); + + int index = 0; + + while ( index < 20 ) + { + if ( rewards[index] ) + { + html.WriteLine( "
X
", style ); + ++index; + } + else + { + int count = 0; + + while ( index < 20 && !rewards[index] ) + { + ++count; + ++index; + + if ( index == 5 || index == 6 || index == 10 || index == 17 ) + break; + } + + html.WriteLine( "  ", count*25, count==1?"":String.Format( " colspan=\"{0}\"", count ) ); + } + } + + html.WriteLine( " " ); + } + + #endregion + + #region Smith Bods + private static void WriteSmithLBOD( StreamWriter html, string name, SmallBulkEntry[] entries ) + { + LargeBOD lbod = new LargeSmithBOD(); + + lbod.Entries = LargeBulkEntry.ConvertEntries( lbod, entries ); + + WriteSmithBODHeader( html, String.Format( "(Large) {0}: Normal", name ) ); + + lbod.RequireExceptional = false; + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat ) + { + lbod.Material = mat; + lbod.AmountMax = 10; + DocumentSmithBOD( html, lbod.ComputeRewards( true ), "10, 15, 20", lbod.Material ); + } + + WriteSmithBODFooter( html ); + + html.WriteLine( "

" ); + + WriteSmithBODHeader( html, String.Format( "(Large) {0}: Exceptional", name ) ); + + lbod.RequireExceptional = true; + for ( BulkMaterialType mat = BulkMaterialType.None; mat <= BulkMaterialType.Valorite; ++mat ) + { + lbod.Material = mat; + + for ( int amt = 15; amt <= 20; amt += 5 ) + { + lbod.AmountMax = amt; + DocumentSmithBOD( html, lbod.ComputeRewards( true ), amt == 20 ? "20" : "10, 15", lbod.Material ); + } + } + + WriteSmithBODFooter( html ); + + html.WriteLine( "

" ); + html.WriteLine( "

" ); + } + + private static void WriteSmithBODHeader( StreamWriter html, string title ) + { + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " ", title ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + } + + private static void WriteSmithBODFooter( StreamWriter html ) + { + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "
{0}
\"Sturdy
Gloves
\"Gargoyles
\"Prospectors
\"Powder
\"Colored
Power Scrolls
Runic Hammers
Ancient Hammers
+1
+3
+5
+5
+10
+15
+20
Du
Sh
Co
Br
Go
Ag
Ve
Va
+10
+15
+30
+60
 
\"Sturdy
+1
 
+3
 
+5
 
\"Gargoyles
\"Prospectors
\"Powder
\"Colored
+5
+10
+15
+20
Du
Sh
Co
Br
Go
Ag
Ve
Va
+10
+15
+30
+60
Gloves
Power Scrolls
Runic Hammers
Ancient Hammers
" ); + } + + private static void DocumentSmithBOD( StreamWriter html, ArrayList items, string amt, BulkMaterialType material ) + { + bool[] rewards = new bool[24]; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = (Item)items[i]; + + if ( item is SturdyPickaxe || item is SturdyShovel ) + rewards[0] = true; + else if ( item is LeatherGlovesOfMining ) + rewards[1] = true; + else if ( item is StuddedGlovesOfMining ) + rewards[2] = true; + else if ( item is RingmailGlovesOfMining ) + rewards[3] = true; + else if ( item is GargoylesPickaxe ) + rewards[4] = true; + else if ( item is ProspectorsTool ) + rewards[5] = true; + else if ( item is PowderOfTemperament ) + rewards[6] = true; + else if ( item is ColoredAnvil ) + rewards[7] = true; + else if ( item is PowerScroll ) + { + PowerScroll ps = (PowerScroll)item; + + if ( ps.Value == 105.0 ) + rewards[8] = true; + else if ( ps.Value == 110.0 ) + rewards[9] = true; + else if ( ps.Value == 115.0 ) + rewards[10] = true; + else if ( ps.Value == 120.0 ) + rewards[11] = true; + } + else if ( item is RunicHammer ) + { + RunicHammer rh = (RunicHammer)item; + + rewards[11 + CraftResources.GetIndex( rh.Resource )] = true; + } + else if ( item is AncientSmithyHammer ) + { + AncientSmithyHammer ash = (AncientSmithyHammer)item; + + if ( ash.Bonus == 10 ) + rewards[20] = true; + else if ( ash.Bonus == 15 ) + rewards[21] = true; + else if ( ash.Bonus == 30 ) + rewards[22] = true; + else if ( ash.Bonus == 60 ) + rewards[23] = true; + } + + item.Delete(); + } + + string style = null; + string name = null; + + switch ( material ) + { + case BulkMaterialType.None: style = "ir"; name = "Iron"; break; + case BulkMaterialType.DullCopper: style = "du"; name = "Dull Copper"; break; + case BulkMaterialType.ShadowIron: style = "sh"; name = "Shadow Iron"; break; + case BulkMaterialType.Copper: style = "co"; name = "Copper"; break; + case BulkMaterialType.Bronze: style = "br"; name = "Bronze"; break; + case BulkMaterialType.Gold: style = "go"; name = "Gold"; break; + case BulkMaterialType.Agapite: style = "ag"; name = "Agapite"; break; + case BulkMaterialType.Verite: style = "ve"; name = "Verite"; break; + case BulkMaterialType.Valorite: style = "va"; name = "Valorite"; break; + } + + html.WriteLine( " " ); + html.WriteLine( " {0} {1}", name, amt ); + + int index = 0; + + while ( index < 24 ) + { + if ( rewards[index] ) + { + html.WriteLine( "
X
", style ); + ++index; + } + else + { + int count = 0; + + while ( index < 24 && !rewards[index] ) + { + ++count; + ++index; + + if ( index == 4 || index == 8 || index == 12 || index == 20 ) + break; + } + + html.WriteLine( "  ", count*25, count==1?"":String.Format( " colspan=\"{0}\"", count ) ); + } + } + + html.WriteLine( " " ); + } + + #endregion + + public static ArrayList LoadBodies() + { + ArrayList list = new ArrayList(); + + string path = Core.FindDataFile( "models/models.txt" ); + + if ( File.Exists( path ) ) + { + using ( StreamReader ip = new StreamReader( path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + line = line.Trim(); + + if ( line.Length == 0 || line.StartsWith( "#" ) ) + continue; + + string[] split = line.Split( '\t' ); + + if ( split.Length >= 9 ) + { + Body body = Utility.ToInt32( split[0] ); + ModelBodyType type = (ModelBodyType)Utility.ToInt32( split[1] ); + string name = split[8]; + + BodyEntry entry = new BodyEntry( body, type, name ); + + if ( !list.Contains( entry ) ) + list.Add( entry ); + } + } + } + } + + return list; + } + + private static void DocumentBodies() + { + ArrayList list = LoadBodies(); + + using ( StreamWriter html = GetWriter( "docs/", "bodies.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Body List" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "

Back to the index

" ); + + if ( list.Count > 0 ) + { + html.WriteLine( "

Body List

" ); + + list.Sort( new BodyEntrySorter() ); + + ModelBodyType lastType = ModelBodyType.Invalid; + + for ( int i = 0; i < list.Count; ++i ) + { + BodyEntry entry = (BodyEntry)list[i]; + ModelBodyType type = entry.BodyType; + + if ( type != lastType ) + { + if ( lastType != ModelBodyType.Invalid ) + html.WriteLine( "
" ); + + lastType = type; + + html.WriteLine( " ", type ); + + switch ( type ) + { + case ModelBodyType.Monsters: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; + case ModelBodyType.Sea: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; + case ModelBodyType.Animals: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; + case ModelBodyType.Human: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; + case ModelBodyType.Equipment: html.WriteLine( " Monsters | Sea | Animals | Human | Equipment

" ); break; + } + + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " ", type ); + } + + html.WriteLine( " ", entry.Body.BodyID, entry.Name ); + } + + html.WriteLine( "
{0}
{0}{1}
" ); + } + else + { + html.WriteLine( " This feature requires a UO:3D installation." ); + } + + html.WriteLine( " " ); + html.WriteLine( "" ); + } + } + + private static void DocumentKeywords() + { + ArrayList tables = LoadSpeechFile(); + + using ( StreamWriter html = GetWriter( "docs/", "keywords.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Speech Keywords" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "

Back to the index

" ); + html.WriteLine( "

Speech Keywords

" ); + + for ( int p = 0; p < 1 && p < tables.Count; ++p ) + { + Hashtable table = (Hashtable)tables[p]; + + if ( p > 0 ) + html.WriteLine( "
" ); + + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + + ArrayList list = new ArrayList( table.Values ); + list.Sort( new SpeechEntrySorter() ); + + for ( int i = 0; i < list.Count; ++i ) + { + SpeechEntry entry = (SpeechEntry)list[i]; + + html.Write( " " ); + } + + html.WriteLine( "
NumberText
0x{0:X4}", entry.Index ); + + entry.Strings.Sort();//( new EnglishPrioStringSorter() ); + + for ( int j = 0; j < entry.Strings.Count; ++j ) + { + if ( j > 0 ) + html.Write( "
" ); + + string v = (string)entry.Strings[j]; + + for ( int k = 0; k < v.Length; ++k ) + { + char c = v[k]; + + if ( c == '<' ) + html.Write( "<" ); + else if ( c == '>' ) + html.Write( ">" ); + else if ( c == '&' ) + html.Write( "&" ); + else if ( c == '"' ) + html.Write( """ ); + else if ( c == '\'' ) + html.Write( "'" ); + else if ( c >= 0x20 && c < 0x80 ) + html.Write( c ); + else + html.Write( "&#{0};", (int)c ); + } + } + + html.WriteLine( "
" ); + } + + html.WriteLine( " " ); + html.WriteLine( "" ); + } + } + + private class SpeechEntry + { + private int m_Index; + private ArrayList m_Strings; + + public int Index{ get{ return m_Index; } } + public ArrayList Strings{ get{ return m_Strings; } } + + public SpeechEntry( int index ) + { + m_Index = index; + m_Strings = new ArrayList(); + } + } + + private class SpeechEntrySorter : IComparer + { + public int Compare( object x, object y ) + { + SpeechEntry a = (SpeechEntry)x; + SpeechEntry b = (SpeechEntry)y; + + return a.Index.CompareTo( b.Index ); + } + } + + private static ArrayList LoadSpeechFile() + { + ArrayList tables = new ArrayList(); + int lastIndex = -1; + + Hashtable table = null; + + string path = Core.FindDataFile( "speech.mul" ); + + if ( File.Exists( path ) ) + { + using ( FileStream ip = new FileStream( path, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + BinaryReader bin = new BinaryReader( ip ); + + while ( bin.PeekChar() >= 0 ) + { + int index = (bin.ReadByte() << 8) | bin.ReadByte(); + int length = (bin.ReadByte() << 8) | bin.ReadByte(); + string text = Encoding.UTF8.GetString( bin.ReadBytes( length ) ).Trim(); + + if ( text.Length == 0 ) + continue; + + if ( table == null || lastIndex > index ) + { + if ( index == 0 && text == "*withdraw*" ) + tables.Insert( 0, table = new Hashtable() ); + else + tables.Add( table = new Hashtable() ); + } + + lastIndex = index; + + SpeechEntry entry = (SpeechEntry)table[index]; + + if ( entry == null ) + table[index] = entry = new SpeechEntry( index ); + + entry.Strings.Add( text ); + } + } + } + + return tables; + } + + private class DocCommandEntry + { + private AccessLevel m_AccessLevel; + private string m_Name; + private string[] m_Aliases; + private string m_Usage; + private string m_Description; + + public AccessLevel AccessLevel{ get{ return m_AccessLevel; } } + public string Name{ get{ return m_Name; } } + public string[] Aliases{ get{ return m_Aliases; } } + public string Usage{ get{ return m_Usage; } } + public string Description{ get{ return m_Description; } } + + public DocCommandEntry( AccessLevel accessLevel, string name, string[] aliases, string usage, string description ) + { + m_AccessLevel = accessLevel; + m_Name = name; + m_Aliases = aliases; + m_Usage = usage; + m_Description = description; + } + } + + private class CommandEntrySorter : IComparer + { + public int Compare( object x, object y ) + { + DocCommandEntry a = (DocCommandEntry)x; + DocCommandEntry b = (DocCommandEntry)y; + + int v = b.AccessLevel.CompareTo( a.AccessLevel ); + + if ( v == 0 ) + v = a.Name.CompareTo( b.Name ); + + return v; + } + } + + private static void DocumentCommands() + { + using ( StreamWriter html = GetWriter( "docs/", "commands.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Commands" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "

Back to the index

" ); + html.WriteLine( "

Commands

" ); + + ArrayList commands = new ArrayList( CommandSystem.Entries.Values ); + ArrayList list = new ArrayList(); + + commands.Sort(); + commands.Reverse(); + Clean( commands ); + + for ( int i = 0; i < commands.Count; ++i ) + { + CommandEntry e = (CommandEntry)commands[i]; + + MethodInfo mi = e.Handler.Method; + + object[] attrs = mi.GetCustomAttributes( typeof( UsageAttribute ), false ); + + if ( attrs.Length == 0 ) + continue; + + UsageAttribute usage = attrs[0] as UsageAttribute; + + attrs = mi.GetCustomAttributes( typeof( DescriptionAttribute ), false ); + + if ( attrs.Length == 0 ) + continue; + + DescriptionAttribute desc = attrs[0] as DescriptionAttribute; + + if ( usage == null || desc == null ) + continue; + + attrs = mi.GetCustomAttributes( typeof( AliasesAttribute ), false ); + + AliasesAttribute aliases = ( attrs.Length == 0 ? null : attrs[0] as AliasesAttribute ); + + string descString = desc.Description.Replace( "<", "<" ).Replace( ">", ">" ); + + if ( aliases == null ) + list.Add( new DocCommandEntry( e.AccessLevel, e.Command, null, usage.Usage, descString ) ); + else + list.Add( new DocCommandEntry( e.AccessLevel, e.Command, aliases.Aliases, usage.Usage, descString ) ); + } + + for ( int i = 0; i < TargetCommands.AllCommands.Count; ++i ) + { + BaseCommand command = TargetCommands.AllCommands[i]; + + string usage = command.Usage; + string desc = command.Description; + + if ( usage == null || desc == null ) + continue; + + string[] cmds = command.Commands; + string cmd = cmds[0]; + string[] aliases = new string[cmds.Length - 1]; + + for ( int j = 0; j < aliases.Length; ++j ) + aliases[j] = cmds[j + 1]; + + desc = desc.Replace( "<", "<" ).Replace( ">", ">" ); + + if ( command.Supports != CommandSupport.Single ) + { + StringBuilder sb = new StringBuilder( 50 + desc.Length ); + + sb.Append( "Modifiers: " ); + + if ( (command.Supports & CommandSupport.Global) != 0 ) + sb.Append( "Global, " ); + + if ( (command.Supports & CommandSupport.Online) != 0 ) + sb.Append( "Online, " ); + + if ( (command.Supports & CommandSupport.Region) != 0 ) + sb.Append( "Region, " ); + + if ( (command.Supports & CommandSupport.Contained) != 0 ) + sb.Append( "Contained, " ); + + if ( (command.Supports & CommandSupport.Multi) != 0 ) + sb.Append( "Multi, " ); + + if ( (command.Supports & CommandSupport.Area) != 0 ) + sb.Append( "Area, " ); + + if ( (command.Supports & CommandSupport.Self) != 0 ) + sb.Append( "Self, " ); + + sb.Remove( sb.Length - 2, 2 ); + sb.Append( "
" ); + sb.Append( desc ); + + desc = sb.ToString(); + } + + list.Add( new DocCommandEntry( command.AccessLevel, cmd, aliases, usage, desc ) ); + } + + commands = BaseCommandImplementor.Implementors; + + for ( int i = 0; i < commands.Count; ++i ) + { + BaseCommandImplementor command = (BaseCommandImplementor)commands[i]; + + string usage = command.Usage; + string desc = command.Description; + + if ( usage == null || desc == null ) + continue; + + string[] cmds = command.Accessors; + string cmd = cmds[0]; + string[] aliases = new string[cmds.Length - 1]; + + for ( int j = 0; j < aliases.Length; ++j ) + aliases[j] = cmds[j + 1]; + + desc = desc.Replace( "<", "<" ).Replace( ">", ">" ); + + list.Add( new DocCommandEntry( command.AccessLevel, cmd, aliases, usage, desc ) ); + } + + list.Sort( new CommandEntrySorter() ); + + AccessLevel last = AccessLevel.Player; + + foreach ( DocCommandEntry e in list ) + { + if ( e.AccessLevel != last ) + { + if ( last != AccessLevel.Player ) + html.WriteLine( "

" ); + + last = e.AccessLevel; + + html.WriteLine( " ", last ); + + switch ( last ) + { + case AccessLevel.Administrator: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; + case AccessLevel.GameMaster: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; + case AccessLevel.Seer: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; + case AccessLevel.Counselor: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; + case AccessLevel.Player: html.WriteLine( " Administrator | Game Master | Counselor | Player

" ); break; + } + + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " ", last == AccessLevel.GameMaster ? "Game Master" : last.ToString() ); + } + + DocumentCommand( html, e ); + } + + html.WriteLine( "
{0}
" ); + html.WriteLine( " " ); + html.WriteLine( "" ); + } + } + + private static void Clean( ArrayList list ) + { + for ( int i = 0; i < list.Count; ++i ) + { + CommandEntry e = (CommandEntry)list[i]; + + for ( int j = i + 1; j < list.Count; ++j ) + { + CommandEntry c = (CommandEntry)list[j]; + + if ( e.Handler.Method == c.Handler.Method ) + { + list.RemoveAt( j ); + --j; + } + } + } + } + + private static void DocumentCommand( StreamWriter html, DocCommandEntry e ) + { + string usage = e.Usage; + string desc = e.Description; + string[] aliases = e.Aliases; + + html.Write( " {0}", e.Name ); + + if ( aliases == null || aliases.Length == 0 ) + { + html.Write( "Usage: {0}
{1}", usage.Replace( "<", "<" ).Replace( ">", ">" ), desc ); + } + else + { + html.Write( "Usage: {0}
Alias{1}: ", usage.Replace( "<", "<" ).Replace( ">", ">" ), aliases.Length == 1 ? "" : "es" ); + + for ( int i = 0; i < aliases.Length; ++i ) + { + if ( i != 0 ) + html.Write( ", " ); + + html.Write( aliases[i] ); + } + + html.Write( "
{0}", desc ); + } + + html.WriteLine( "" ); + } + + private static void LoadTypes( Assembly a, Assembly[] asms ) + { + Type[] types = a.GetTypes(); + + for ( int i = 0; i < types.Length; ++i ) + { + Type type = types[i]; + + string nspace = type.Namespace; + + if ( nspace == null || type.IsSpecialName ) + continue; + + TypeInfo info = new TypeInfo( type ); + m_Types[type] = info; + + ArrayList nspaces = (ArrayList)m_Namespaces[nspace]; + + if ( nspaces == null ) + m_Namespaces[nspace] = nspaces = new ArrayList(); + + nspaces.Add( info ); + + Type baseType = info.m_BaseType; + + if ( baseType != null && InAssemblies( baseType, asms ) ) + { + TypeInfo baseInfo = (TypeInfo)m_Types[baseType]; + + if ( baseInfo == null ) + m_Types[baseType] = baseInfo = new TypeInfo( baseType ); + + if ( baseInfo.m_Derived == null ) + baseInfo.m_Derived = new ArrayList(); + + baseInfo.m_Derived.Add( info ); + } + + Type decType = info.m_Declaring; + + if ( decType != null ) + { + TypeInfo decInfo = (TypeInfo)m_Types[decType]; + + if ( decInfo == null ) + m_Types[decType] = decInfo = new TypeInfo( decType ); + + if ( decInfo.m_Nested == null ) + decInfo.m_Nested = new ArrayList(); + + decInfo.m_Nested.Add( info ); + } + + for ( int j = 0; j < info.m_Interfaces.Length; ++j ) + { + Type iface = info.m_Interfaces[j]; + + if ( !InAssemblies( iface, asms ) ) + continue; + + TypeInfo ifaceInfo = (TypeInfo)m_Types[iface]; + + if ( ifaceInfo == null ) + m_Types[iface] = ifaceInfo = new TypeInfo( iface ); + + if ( ifaceInfo.m_Derived == null ) + ifaceInfo.m_Derived = new ArrayList(); + + ifaceInfo.m_Derived.Add( info ); + } + } + } + + private static bool InAssemblies( Type t, Assembly[] asms ) + { + Assembly a = t.Assembly; + + for ( int i = 0; i < asms.Length; ++i ) + if ( a == asms[i] ) + return true; + + return false; + } + + private static StreamWriter GetWriter( string root, string name ) + { + return new StreamWriter( Path.Combine( Path.Combine( m_RootDirectory, root ), name ) ); + } + + private static StreamWriter GetWriter( string path ) + { + return new StreamWriter( Path.Combine( m_RootDirectory, path ) ); + } + + private static Type typeofItem = typeof( Item ), typeofMobile = typeof( Mobile ), typeofMap = typeof( Map ); + private static Type typeofCustomEnum = typeof( CustomEnumAttribute ); + + private static bool IsConstructable( Type t, out bool isItem ) + { + if ( isItem = typeofItem.IsAssignableFrom( t ) ) + return true; + + return typeofMobile.IsAssignableFrom( t ); + } + + private static bool IsConstructable( ConstructorInfo ctor ) + { + return ctor.IsDefined( typeof( ConstructableAttribute ), false ); + } + + private static void DocumentConstructableObjects() + { + ArrayList types = new ArrayList( m_Types.Values ); + types.Sort( new TypeComparer() ); + + ArrayList items = new ArrayList(), mobiles = new ArrayList(); + + for ( int i = 0; i < types.Count; ++i ) + { + Type t = ((TypeInfo)types[i]).m_Type; + bool isItem; + + if ( t.IsAbstract || !IsConstructable( t, out isItem ) ) + continue; + + ConstructorInfo[] ctors = t.GetConstructors(); + bool anyConstructable = false; + + for ( int j = 0; !anyConstructable && j < ctors.Length; ++j ) + anyConstructable = IsConstructable( ctors[j] ); + + if ( anyConstructable ) + { + (isItem ? items : mobiles).Add( t ); + (isItem ? items : mobiles).Add( ctors ); + } + } + + using ( StreamWriter html = GetWriter( "docs/", "objects.html" ) ) + { + html.WriteLine( "" ); + html.WriteLine( " " ); + html.WriteLine( " RunUO Documentation - Constructable Objects" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "

Back to the index

" ); + html.WriteLine( "

Constructable Items and Mobiles

" ); + + html.WriteLine( " " ); + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + + for ( int i = 0; i < items.Count; i += 2 ) + DocumentConstructableObject( html, (Type)items[i], (ConstructorInfo[])items[i + 1] ); + + html.WriteLine( "
Item NameUsage


" ); + + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( "
" ); + html.WriteLine( " " ); + html.WriteLine( " " ); + + for ( int i = 0; i < mobiles.Count; i += 2 ) + DocumentConstructableObject( html, (Type)mobiles[i], (ConstructorInfo[])mobiles[i + 1] ); + + html.WriteLine( "
Mobile NameUsage
" ); + + html.WriteLine( " " ); + html.WriteLine( "" ); + } + } + + private static void DocumentConstructableObject( StreamWriter html, Type t, ConstructorInfo[] ctors ) + { + html.Write( " {0}", t.Name ); + + bool first = true; + + for ( int i = 0; i < ctors.Length; ++i ) + { + ConstructorInfo ctor = ctors[i]; + + if ( !IsConstructable( ctor ) ) + continue; + + if ( !first ) + html.Write( "
" ); + + first = false; + + html.Write( "{0}Add {1}", CommandSystem.Prefix, t.Name ); + + ParameterInfo[] parms = ctor.GetParameters(); + + for ( int j = 0; j < parms.Length; ++j ) + { + html.Write( "
{1}", GetTooltipFor( parms[j] ), parms[j].Name ); + } + } + + html.WriteLine( "" ); + } + + private const string HtmlNewLine = " "; + + private static object[,] m_Tooltips = new object[,] + { + { typeof( Byte ), "Numeric value in the range from 0 to 255, inclusive." }, + { typeof( SByte ), "Numeric value in the range from negative 128 to positive 127, inclusive." }, + { typeof( UInt16 ), "Numeric value in the range from 0 to 65,535, inclusive." }, + { typeof( Int16 ), "Numeric value in the range from negative 32,768 to positive 32,767, inclusive." }, + { typeof( UInt32 ), "Numeric value in the range from 0 to 4,294,967,295, inclusive." }, + { typeof( Int32 ), "Numeric value in the range from negative 2,147,483,648 to positive 2,147,483,647, inclusive." }, + { typeof( UInt64 ), "Numeric value in the range from 0 through about 10^20." }, + { typeof( Int64 ), "Numeric value in the approximate range from negative 10^19 through 10^19." }, + { typeof( String ), "Text value. To specify a value containing spaces, encapsulate the value in quote characters:{0}{0}"Spaced text example"" }, + { typeof( Boolean ), "Boolean value which can be either True or False." }, + { typeof( Map ), "Map or facet name. Possible values include:{0}{0}- Felucca{0}- Trammel{0}- Ilshenar{0}- Malas" }, + { typeof( Poison ), "Poison name or level. Possible values include:{0}{0}- Lesser{0}- Regular{0}- Greater{0}- Deadly{0}- Lethal" }, + { typeof( Point3D ), "Three-dimensional coordinate value. Format as follows:{0}{0}"(, , )"" } + }; + + private static string GetTooltipFor( ParameterInfo param ) + { + Type paramType = param.ParameterType; + + for ( int i = 0; i < m_Tooltips.GetLength( 0 ); ++i ) + { + Type checkType = (Type)m_Tooltips[i, 0]; + + if ( paramType == checkType ) + return String.Format( (string)m_Tooltips[i, 1], HtmlNewLine ); + } + + if ( paramType.IsEnum ) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat( "Enumeration value or name. Possible named values include:{0}", HtmlNewLine ); + + string[] names = Enum.GetNames( paramType ); + + for ( int i = 0; i < names.Length; ++i ) + sb.AppendFormat( "{0}- {1}", HtmlNewLine, names[i] ); + + return sb.ToString(); + } + else if ( paramType.IsDefined( typeofCustomEnum, false ) ) + { + object[] attributes = paramType.GetCustomAttributes( typeofCustomEnum, false ); + + if ( attributes != null && attributes.Length > 0 ) + { + CustomEnumAttribute attr = attributes[0] as CustomEnumAttribute; + + if ( attr != null ) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat( "Enumeration value or name. Possible named values include:{0}", HtmlNewLine ); + + string[] names = attr.Names; + + for ( int i = 0; i < names.Length; ++i ) + sb.AppendFormat( "{0}- {1}", HtmlNewLine, names[i] ); + + return sb.ToString(); + } + } + } + else if ( paramType == typeofMap ) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat( "Enumeration value or name. Possible named values include:{0}", HtmlNewLine ); + + string[] names = Map.GetMapNames(); + + for ( int i = 0; i < names.Length; ++i ) + sb.AppendFormat( "{0}- {1}", HtmlNewLine, names[i] ); + + return sb.ToString(); + } + + return ""; + } + + private const string GetString = " get;"; + private const string SetString = " set;"; + + private const string InString = "in "; + private const string OutString = "out "; + + private const string VirtString = "virtual "; + private const string CtorString ="(ctor) "; + private const string StaticString = "(static) "; + + private static void DocumentLoadedTypes() + { + using ( StreamWriter indexHtml = GetWriter( "docs/", "overview.html" ) ) + { + indexHtml.WriteLine( "" ); + indexHtml.WriteLine( " " ); + indexHtml.WriteLine( " RunUO Documentation - Class Overview" ); + indexHtml.WriteLine( " " ); + indexHtml.WriteLine( " " ); + indexHtml.WriteLine( "

Back to the index

" ); + indexHtml.WriteLine( "

Namespaces

" ); + + ArrayList nspaces = new ArrayList( m_Namespaces ); + + nspaces.Sort( new NamespaceComparer() ); + + for ( int i = 0; i < nspaces.Count; ++i ) + { + DictionaryEntry de = (DictionaryEntry)nspaces[i]; + string name = (string)de.Key; + ArrayList types = (ArrayList)de.Value; + + types.Sort( new TypeComparer() ); + + SaveNamespace( name, types, indexHtml ); + } + + indexHtml.WriteLine( " " ); + indexHtml.WriteLine( "" ); + } + } + + private static void SaveNamespace( string name, ArrayList types, StreamWriter indexHtml ) + { + string fileName = GetFileName( "docs/namespaces/", name, ".html" ); + + indexHtml.WriteLine( " {1}
", fileName, name ); + + using ( StreamWriter nsHtml = GetWriter( "docs/namespaces/", fileName ) ) + { + nsHtml.WriteLine( "" ); + nsHtml.WriteLine( " " ); + nsHtml.WriteLine( " RunUO Documentation - Class Overview - {0}", name ); + nsHtml.WriteLine( " " ); + nsHtml.WriteLine( " " ); + nsHtml.WriteLine( "

Back to the namespace index

" ); + nsHtml.WriteLine( "

{0}

", name ); + + for ( int i = 0; i < types.Count; ++i ) + SaveType( (TypeInfo)types[i], nsHtml, fileName, name ); + + nsHtml.WriteLine( " " ); + nsHtml.WriteLine( "" ); + } + } + + private static void SaveType( TypeInfo info, StreamWriter nsHtml, string nsFileName, string nsName ) + { + if ( info.m_Declaring == null ) + nsHtml.WriteLine( " {1}
", info.m_FileName, info.m_TypeName ); + + using ( StreamWriter typeHtml = info.m_Writer ) + { + typeHtml.WriteLine( "" ); + typeHtml.WriteLine( " " ); + typeHtml.WriteLine( " RunUO Documentation - Class Overview - {0}", info.m_TypeName ); + typeHtml.WriteLine( " " ); + typeHtml.WriteLine( " " ); + typeHtml.WriteLine( "

Back to {1}

", nsFileName, nsName ); + + if ( info.m_Type.IsEnum ) + WriteEnum( info, typeHtml ); + else + WriteType( info, typeHtml ); + + typeHtml.WriteLine( " " ); + typeHtml.WriteLine( "" ); + } + } + + private static void WriteEnum( TypeInfo info, StreamWriter typeHtml ) + { + Type type = info.m_Type; + + typeHtml.WriteLine( "

{0} (Enum)

", info.m_TypeName ); + + string[] names = Enum.GetNames( type ); + + bool flags = type.IsDefined( typeof( FlagsAttribute ), false ); + string format; + + if ( flags ) + format = " {0:G} = 0x{1:X}{2}
"; + else + format = " {0:G} = {1:D}{2}
"; + + for ( int i = 0; i < names.Length; ++i ) + { + object value = Enum.Parse( type, names[i] ); + + typeHtml.WriteLine( format, names[i], value, i < (names.Length - 1) ? "," : "" ); + } + } + + private static void WriteType( TypeInfo info, StreamWriter typeHtml ) + { + Type type = info.m_Type; + + typeHtml.Write( "

" ); + + Type decType = info.m_Declaring; + + if ( decType != null ) + { + // We are a nested type + + typeHtml.Write( '(' ); + + TypeInfo decInfo = (TypeInfo)m_Types[decType]; + + if ( decInfo == null ) + typeHtml.Write( decType.Name ); + else + typeHtml.Write( "{1}", decInfo.m_FileName, decInfo.m_TypeName ); + + typeHtml.Write( ") - " ); + } + + typeHtml.Write( info.m_TypeName ); + + Type[] ifaces = info.m_Interfaces; + Type baseType = info.m_BaseType; + + int extendCount = 0; + + if ( baseType != null && baseType != typeof( object ) && baseType != typeof( ValueType ) && !baseType.IsPrimitive ) + { + typeHtml.Write( " : " ); + + TypeInfo baseInfo = (TypeInfo)m_Types[baseType]; + + if ( baseInfo == null ) + typeHtml.Write( baseType.Name ); + else + typeHtml.Write( "{1}", baseInfo.m_FileName, baseInfo.m_TypeName ); + + ++extendCount; + } + + if ( ifaces.Length > 0 ) + { + if ( extendCount == 0 ) + typeHtml.Write( " : " ); + + for ( int i = 0; i < ifaces.Length; ++i ) + { + Type iface = ifaces[i]; + TypeInfo ifaceInfo = (TypeInfo)m_Types[iface]; + + if ( extendCount != 0 ) + typeHtml.Write( ", " ); + + ++extendCount; + + if ( ifaceInfo == null ) + typeHtml.Write( iface.Name ); + else + typeHtml.Write( "{1}", ifaceInfo.m_FileName, ifaceInfo.m_TypeName ); + } + } + + typeHtml.WriteLine( "

" ); + + ArrayList derived = info.m_Derived; + + if ( derived != null ) + { + typeHtml.Write( "

Derived Types: " ); + + derived.Sort( new TypeComparer() ); + + for ( int i = 0; i < derived.Count; ++i ) + { + TypeInfo derivedInfo = (TypeInfo)derived[i]; + + if ( i != 0 ) + typeHtml.Write( ", " ); + + typeHtml.Write( "{1}", derivedInfo.m_FileName, derivedInfo.m_TypeName ); + } + + typeHtml.WriteLine( "

" ); + } + + ArrayList nested = info.m_Nested; + + if ( nested != null ) + { + typeHtml.Write( "

Nested Types: " ); + + nested.Sort( new TypeComparer() ); + + for ( int i = 0; i < nested.Count; ++i ) + { + TypeInfo nestedInfo = (TypeInfo)nested[i]; + + if ( i != 0 ) + typeHtml.Write( ", " ); + + typeHtml.Write( "{1}", nestedInfo.m_FileName, nestedInfo.m_TypeName ); + } + + typeHtml.WriteLine( "

" ); + } + + MemberInfo[] membs = type.GetMembers( BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly ); + + Array.Sort( membs, new MemberComparer() ); + + for ( int i = 0; i < membs.Length; ++i ) + { + MemberInfo mi = membs[i]; + + if ( mi is PropertyInfo ) + WriteProperty( (PropertyInfo)mi, typeHtml ); + else if ( mi is ConstructorInfo ) + WriteCtor( info.m_TypeName, (ConstructorInfo)mi, typeHtml ); + else if ( mi is MethodInfo ) + WriteMethod( (MethodInfo)mi, typeHtml ); + } + } + + private static void WriteProperty( PropertyInfo pi, StreamWriter html ) + { + html.Write( " " ); + + MethodInfo getMethod = pi.GetGetMethod(); + MethodInfo setMethod = pi.GetSetMethod(); + + if ( (getMethod != null && getMethod.IsStatic) || (setMethod != null && setMethod.IsStatic) ) + html.Write( StaticString ); + + html.Write( GetPair( pi.PropertyType, pi.Name, false ) ); + html.Write( '(' ); + + if ( pi.CanRead ) + html.Write( GetString ); + + if ( pi.CanWrite ) + html.Write( SetString ); + + html.WriteLine( " )
" ); + } + + private static void WriteCtor( string name, ConstructorInfo ctor, StreamWriter html ) + { + if ( ctor.IsStatic ) + return; + + html.Write( " " ); + html.Write( CtorString ); + html.Write( name ); + html.Write( '(' ); + + ParameterInfo[] parms = ctor.GetParameters(); + + if ( parms.Length > 0 ) + { + html.Write( ' ' ); + + for ( int i = 0; i < parms.Length; ++i ) + { + ParameterInfo pi = parms[i]; + + if ( i != 0 ) + html.Write( ", " ); + + if ( pi.IsIn ) + html.Write( InString ); + else if ( pi.IsOut ) + html.Write( OutString ); + + html.Write( GetPair( pi.ParameterType, pi.Name, pi.IsOut ) ); + } + + html.Write( ' ' ); + } + + html.WriteLine( ")
" ); + } + + private static void WriteMethod( MethodInfo mi, StreamWriter html ) + { + if ( mi.IsSpecialName ) + return; + + html.Write( " " ); + + if ( mi.IsStatic ) + html.Write( StaticString ); + + if ( mi.IsVirtual ) + html.Write( VirtString ); + + html.Write( GetPair( mi.ReturnType, mi.Name, false ) ); + html.Write( '(' ); + + ParameterInfo[] parms = mi.GetParameters(); + + if ( parms.Length > 0 ) + { + html.Write( ' ' ); + + for ( int i = 0; i < parms.Length; ++i ) + { + ParameterInfo pi = parms[i]; + + if ( i != 0 ) + html.Write( ", " ); + + if ( pi.IsIn ) + html.Write( InString ); + else if ( pi.IsOut ) + html.Write( OutString ); + + html.Write( GetPair( pi.ParameterType, pi.Name, pi.IsOut ) ); + } + + html.Write( ' ' ); + } + + html.WriteLine( ")
" ); + } + } + + public enum ModelBodyType + { + Invalid = -1, + Monsters, + Sea, + Animals, + Human, + Equipment + } + + public class BodyEntry + { + private Body m_Body; + private ModelBodyType m_BodyType; + private string m_Name; + + public Body Body{ get{ return m_Body; } } + public ModelBodyType BodyType{ get{ return m_BodyType; } } + public string Name{ get{ return m_Name; } } + + public BodyEntry( Body body, ModelBodyType bodyType, string name ) + { + m_Body = body; + m_BodyType = bodyType; + m_Name = name; + } + + public override bool Equals( object obj ) + { + BodyEntry e = (BodyEntry)obj; + + return ( m_Body == e.m_Body && m_BodyType == e.m_BodyType && m_Name == e.m_Name ); + } + + public override int GetHashCode() + { + return m_Body.BodyID ^ (int)m_BodyType ^ m_Name.GetHashCode(); + } + } + + public class BodyEntrySorter : IComparer + { + public int Compare( object x, object y ) + { + BodyEntry a = (BodyEntry)x; + BodyEntry b = (BodyEntry)y; + + int v = a.BodyType.CompareTo( b.BodyType ); + + if ( v == 0 ) + v = a.Body.BodyID.CompareTo( b.Body.BodyID ); + + if ( v == 0 ) + v = a.Name.CompareTo( b.Name ); + + return v; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Dupe.cs b/Scripts/Commands/Dupe.cs new file mode 100644 index 000000000..b2d6d2551 --- /dev/null +++ b/Scripts/Commands/Dupe.cs @@ -0,0 +1,145 @@ +using System; +using System.Reflection; +using Server.Items; +using Server.Targeting; + +namespace Server.Commands +{ + public class Dupe + { + public static void Initialize() + { + CommandSystem.Register( "Dupe", AccessLevel.GameMaster, new CommandEventHandler( Dupe_OnCommand ) ); + CommandSystem.Register( "DupeInBag", AccessLevel.GameMaster, new CommandEventHandler( DupeInBag_OnCommand ) ); + } + + [Usage( "Dupe [amount]" )] + [Description( "Dupes a targeted item." )] + private static void Dupe_OnCommand( CommandEventArgs e ) + { + int amount = 1; + if ( e.Length >= 1 ) + amount = e.GetInt32( 0 ); + e.Mobile.Target = new DupeTarget( false, amount > 0 ? amount : 1 ); + e.Mobile.SendMessage( "What do you wish to dupe?" ); + } + + [Usage( "DupeInBag " )] + [Description( "Dupes an item at it's current location (count) number of times." )] + private static void DupeInBag_OnCommand( CommandEventArgs e ) + { + int amount = 1; + if ( e.Length >= 1 ) + amount = e.GetInt32( 0 ); + + e.Mobile.Target = new DupeTarget( true, amount > 0 ? amount : 1 ); + e.Mobile.SendMessage( "What do you wish to dupe?" ); + } + + private class DupeTarget : Target + { + private bool m_InBag; + private int m_Amount; + + public DupeTarget( bool inbag, int amount ) + : base( 15, false, TargetFlags.None ) + { + m_InBag = inbag; + m_Amount = amount; + } + + protected override void OnTarget( Mobile from, object targ ) + { + bool done = false; + if ( !( targ is Item ) ) + { + from.SendMessage( "You can only dupe items." ); + return; + } + + CommandLogging.WriteLine( from, "{0} {1} duping {2} (inBag={3}; amount={4})", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targ ), m_InBag, m_Amount ); + + Item copy = (Item)targ; + Container pack; + + if ( m_InBag ) + { + if ( copy.Parent is Container ) + pack = (Container)copy.Parent; + else if ( copy.Parent is Mobile ) + pack = ( (Mobile)copy.Parent ).Backpack; + else + pack = null; + } + else + pack = from.Backpack; + + Type t = copy.GetType(); + + //ConstructorInfo[] info = t.GetConstructors(); + + ConstructorInfo c = t.GetConstructor( Type.EmptyTypes ); + + if ( c != null ) + { + try + { + from.SendMessage( "Duping {0}...", m_Amount ); + for ( int i = 0; i < m_Amount; i++ ) + { + object o = c.Invoke( null ); + + if ( o != null && o is Item ) + { + Item newItem = (Item)o; + CopyProperties( newItem, copy );//copy.Dupe( item, copy.Amount ); + copy.OnAfterDuped( newItem ); + newItem.Parent = null; + + if ( pack != null ) + pack.DropItem( newItem ); + else + newItem.MoveToWorld( from.Location, from.Map ); + + CommandLogging.WriteLine( from, "{0} {1} duped {2} creating {3}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targ ), CommandLogging.Format( newItem ) ); + } + } + from.SendMessage( "Done" ); + done = true; + } + catch + { + from.SendMessage( "Error!" ); + return; + } + } + + if ( !done ) + { + from.SendMessage( "Unable to dupe. Item must have a 0 parameter constructor." ); + } + } + } + + private static void CopyProperties( Item dest, Item src ) + { + PropertyInfo[] props = src.GetType().GetProperties(); + + for ( int i = 0; i < props.Length; i++ ) + { + try + { + if ( props[i].CanRead && props[i].CanWrite ) + { + //Console.WriteLine( "Setting {0} = {1}", props[i].Name, props[i].GetValue( src, null ) ); + props[i].SetValue( dest, props[i].GetValue( src, null ), null ); + } + } + catch + { + //Console.WriteLine( "Denied" ); + } + } + } + } +} diff --git a/Scripts/Commands/ExportWSC.cs b/Scripts/Commands/ExportWSC.cs new file mode 100644 index 000000000..3a91607a5 --- /dev/null +++ b/Scripts/Commands/ExportWSC.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.IO; +using Server; +using Server.Items; + +namespace Server.Commands +{ + public class ExportCommand + { + private const string ExportFile = @"C:\Uo\WorldForge\items.wsc"; + + public static void Initialize() + { + CommandSystem.Register( "ExportWSC", AccessLevel.Administrator, new CommandEventHandler( Export_OnCommand ) ); + } + + public static void Export_OnCommand( CommandEventArgs e ) + { + StreamWriter w = new StreamWriter( ExportFile ); + ArrayList remove = new ArrayList(); + int count = 0; + + e.Mobile.SendMessage( "Exporting all static items to \"{0}\"...", ExportFile ); + e.Mobile.SendMessage( "This will delete all static items in the world. Please make a backup." ); + + foreach ( Item item in World.Items.Values ) + { + if ( ( item is Static || item is BaseFloor || item is BaseWall ) + && item.RootParent == null ) + { + w.WriteLine( "SECTION WORLDITEM {0}", count ); + w.WriteLine( "{" ); + w.WriteLine( "SERIAL {0}", item.Serial ); + w.WriteLine( "NAME #" ); + w.WriteLine( "NAME2 #" ); + w.WriteLine( "ID {0}", item.ItemID ); + w.WriteLine( "X {0}", item.X ); + w.WriteLine( "Y {0}", item.Y ); + w.WriteLine( "Z {0}", item.Z ); + w.WriteLine( "COLOR {0}", item.Hue ); + w.WriteLine( "CONT -1" ); + w.WriteLine( "TYPE 0" ); + w.WriteLine( "AMOUNT 1" ); + w.WriteLine( "WEIGHT 255" ); + w.WriteLine( "OWNER -1" ); + w.WriteLine( "SPAWN -1" ); + w.WriteLine( "VALUE 1" ); + w.WriteLine( "}" ); + w.WriteLine( "" ); + + count++; + remove.Add( item ); + w.Flush(); + } + } + + w.Close(); + + foreach( Item item in remove ) + item.Delete(); + + e.Mobile.SendMessage( "Export complete. Exported {0} statics.", count ); + } + } +} +/*SECTION WORLDITEM 1 +{ +SERIAL 1073741830 +NAME # +NAME2 # +ID 1709 +X 1439 +Y 1613 +Z 20 +CONT -1 +TYPE 12 +AMOUNT 1 +WEIGHT 25500 +OWNER -1 +SPAWN -1 +VALUE 1 +}*/ diff --git a/Scripts/Commands/GenCategorization.cs b/Scripts/Commands/GenCategorization.cs new file mode 100644 index 000000000..7e6da5084 --- /dev/null +++ b/Scripts/Commands/GenCategorization.cs @@ -0,0 +1,427 @@ +using System; +using System.IO; +using System.Xml; +using System.Collections; +using System.Reflection; +using Server; +using Server.Items; +using Server.Commands; + +namespace Server.Commands +{ + public class Categorization + { + private static CategoryEntry m_RootItems, m_RootMobiles; + + public static CategoryEntry Items + { + get + { + if ( m_RootItems == null ) + Load(); + + return m_RootItems; + } + } + + public static CategoryEntry Mobiles + { + get + { + if ( m_RootMobiles == null ) + Load(); + + return m_RootMobiles; + } + } + + public static void Initialize() + { + CommandSystem.Register( "RebuildCategorization", AccessLevel.Administrator, new CommandEventHandler( RebuildCategorization_OnCommand ) ); + } + + [Usage( "RebuildCategorization" )] + [Description( "Rebuilds the categorization data file used by the Add command." )] + public static void RebuildCategorization_OnCommand( CommandEventArgs e ) + { + CategoryEntry root = new CategoryEntry( null, "Add Menu", new CategoryEntry[]{ Items, Mobiles } ); + + Export( root, "Data/objects.xml", "Objects" ); + + e.Mobile.SendMessage( "Categorization menu rebuilt." ); + } + + public static void RecurseFindCategories( CategoryEntry ce, ArrayList list ) + { + list.Add( ce ); + + for ( int i = 0; i < ce.SubCategories.Length; ++i ) + RecurseFindCategories( ce.SubCategories[i], list ); + } + + public static void Export( CategoryEntry ce, string fileName, string title ) + { + XmlTextWriter xml = new XmlTextWriter( fileName, System.Text.Encoding.UTF8 ); + + xml.Indentation = 1; + xml.IndentChar = '\t'; + xml.Formatting = Formatting.Indented; + + xml.WriteStartDocument( true ); + + RecurseExport( xml, ce ); + + xml.Flush(); + xml.Close(); + } + + public static void RecurseExport( XmlTextWriter xml, CategoryEntry ce ) + { + xml.WriteStartElement( "category" ); + + xml.WriteAttributeString( "title", ce.Title ); + + ArrayList subCats = new ArrayList( ce.SubCategories ); + + subCats.Sort( new CategorySorter() ); + + for ( int i = 0; i < subCats.Count; ++i ) + RecurseExport( xml, (CategoryEntry)subCats[i] ); + + ce.Matched.Sort( new CategorySorter() ); + + for ( int i = 0; i < ce.Matched.Count; ++i ) + { + CategoryTypeEntry cte = (CategoryTypeEntry)ce.Matched[i]; + + xml.WriteStartElement( "object" ); + + xml.WriteAttributeString( "type", cte.Type.ToString() ); + + object obj = cte.Object; + + if ( obj is Item ) + { + Item item = (Item)obj; + + int itemID = item.ItemID; + + if ( item is BaseAddon && ((BaseAddon)item).Components.Count == 1 ) + itemID = ((AddonComponent)(((BaseAddon)item).Components[0])).ItemID; + + if ( itemID >= 0x4000 ) + itemID = 1; + + xml.WriteAttributeString( "gfx", XmlConvert.ToString( itemID ) ); + + int hue = item.Hue & 0x7FFF; + + if ( (hue & 0x4000) != 0 ) + hue = 0; + + if ( hue != 0 ) + xml.WriteAttributeString( "hue", XmlConvert.ToString( hue ) ); + + item.Delete(); + } + else if ( obj is Mobile ) + { + Mobile mob = (Mobile)obj; + + int itemID = ShrinkTable.Lookup( mob, 1 ); + + xml.WriteAttributeString( "gfx", XmlConvert.ToString( itemID ) ); + + int hue = mob.Hue & 0x7FFF; + + if ( (hue & 0x4000) != 0 ) + hue = 0; + + if ( hue != 0 ) + xml.WriteAttributeString( "hue", XmlConvert.ToString( hue ) ); + + mob.Delete(); + } + + xml.WriteEndElement(); + } + + xml.WriteEndElement(); + } + + public static void Load() + { + ArrayList types = new ArrayList(); + + AddTypes( Core.Assembly, types ); + + for ( int i = 0; i < ScriptCompiler.Assemblies.Length; ++i ) + AddTypes( ScriptCompiler.Assemblies[i], types ); + + m_RootItems = Load( types, "Data/items.cfg" ); + m_RootMobiles = Load( types, "Data/mobiles.cfg" ); + } + + private static CategoryEntry Load( ArrayList types, string config ) + { + CategoryLine[] lines = CategoryLine.Load( config ); + + if ( lines.Length > 0 ) + { + int index = 0; + CategoryEntry root = new CategoryEntry( null, lines, ref index ); + + Fill( root, types ); + + return root; + } + + return new CategoryEntry(); + } + + private static Type typeofItem = typeof( Item ); + private static Type typeofMobile = typeof( Mobile ); + private static Type typeofConstructable = typeof( ConstructableAttribute ); + + private static bool IsConstructable( Type type ) + { + if ( !type.IsSubclassOf( typeofItem ) && !type.IsSubclassOf( typeofMobile ) ) + return false; + + ConstructorInfo ctor = type.GetConstructor( Type.EmptyTypes ); + + return ( ctor != null && ctor.IsDefined( typeofConstructable, false ) ); + } + + private static void AddTypes( Assembly asm, ArrayList types ) + { + Type[] allTypes = asm.GetTypes(); + + for ( int i = 0; i < allTypes.Length; ++i ) + { + Type type = allTypes[i]; + + if ( type.IsAbstract ) + continue; + + if ( IsConstructable( type ) ) + types.Add( type ); + } + } + + private static void Fill( CategoryEntry root, ArrayList list ) + { + for ( int i = 0; i < list.Count; ++i ) + { + Type type = (Type)list[i]; + CategoryEntry match = GetDeepestMatch( root, type ); + + if ( match == null ) + continue; + + try + { + match.Matched.Add( new CategoryTypeEntry( type ) ); + } + catch + { + } + } + } + + private static CategoryEntry GetDeepestMatch( CategoryEntry root, Type type ) + { + if ( !root.IsMatch( type ) ) + return null; + + for ( int i = 0; i < root.SubCategories.Length; ++i ) + { + CategoryEntry check = GetDeepestMatch( root.SubCategories[i], type ); + + if ( check != null ) + return check; + } + + return root; + } + } + + public class CategorySorter : IComparer + { + public int Compare( object x, object y ) + { + string a = null, b = null; + + if ( x is CategoryEntry ) + a = ((CategoryEntry)x).Title; + else if ( x is CategoryTypeEntry ) + a = ((CategoryTypeEntry)x).Type.Name; + + if ( y is CategoryEntry ) + b = ((CategoryEntry)y).Title; + else if ( y is CategoryTypeEntry ) + b = ((CategoryTypeEntry)y).Type.Name; + + if ( a == null && b == null ) + return 0; + + if ( a == null ) + return 1; + + if ( b == null ) + return -1; + + return a.CompareTo( b ); + } + } + + public class CategoryTypeEntry + { + private Type m_Type; + private object m_Object; + + public Type Type{ get{ return m_Type; } } + public object Object{ get{ return m_Object; } } + + public CategoryTypeEntry( Type type ) + { + m_Type = type; + m_Object = Activator.CreateInstance( type ); + } + } + + public class CategoryEntry + { + private string m_Title; + private Type[] m_Matches; + private CategoryEntry[] m_SubCategories; + private CategoryEntry m_Parent; + private ArrayList m_Matched; + + public string Title{ get{ return m_Title; } } + public Type[] Matches{ get{ return m_Matches; } } + public CategoryEntry Parent{ get{ return m_Parent; } } + public CategoryEntry[] SubCategories{ get{ return m_SubCategories; } } + public ArrayList Matched{ get{ return m_Matched; } } + + public CategoryEntry() + { + m_Title = "(empty)"; + m_Matches = new Type[0]; + m_SubCategories = new CategoryEntry[0]; + m_Matched = new ArrayList(); + } + + public CategoryEntry( CategoryEntry parent, string title, CategoryEntry[] subCats ) + { + m_Parent = parent; + m_Title = title; + m_SubCategories = subCats; + m_Matches = new Type[0]; + m_Matched = new ArrayList(); + } + + public bool IsMatch( Type type ) + { + bool isMatch = false; + + for ( int i = 0; !isMatch && i < m_Matches.Length; ++i ) + isMatch = ( type == m_Matches[i] || type.IsSubclassOf( m_Matches[i] ) ); + + return isMatch; + } + + public CategoryEntry( CategoryEntry parent, CategoryLine[] lines, ref int index ) + { + m_Parent = parent; + + string text = lines[index].Text; + + int start = text.IndexOf( '(' ); + + if ( start < 0 ) + throw new FormatException( String.Format( "Input string not correctly formatted ('{0}')", text ) ); + + m_Title = text.Substring( 0, start ).Trim(); + + int end = text.IndexOf( ')', ++start ); + + if ( end < start ) + throw new FormatException( String.Format( "Input string not correctly formatted ('{0}')", text ) ); + + text = text.Substring( start, end-start ); + string[] split = text.Split( ';' ); + + ArrayList list = new ArrayList(); + + for ( int i = 0; i < split.Length; ++i ) + { + Type type = ScriptCompiler.FindTypeByName( split[i].Trim() ); + + if ( type == null ) + Console.WriteLine( "Match type not found ('{0}')", split[i].Trim() ); + else + list.Add( type ); + } + + m_Matches = (Type[])list.ToArray( typeof( Type ) ); + list.Clear(); + + int ourIndentation = lines[index].Indentation; + + ++index; + + while ( index < lines.Length && lines[index].Indentation > ourIndentation ) + list.Add( new CategoryEntry( this, lines, ref index ) ); + + m_SubCategories = (CategoryEntry[])list.ToArray( typeof( CategoryEntry ) ); + list.Clear(); + + m_Matched = list; + } + } + + public class CategoryLine + { + private int m_Indentation; + private string m_Text; + + public int Indentation{ get{ return m_Indentation; } } + public string Text{ get{ return m_Text; } } + + public CategoryLine( string input ) + { + int index; + + for ( index = 0; index < input.Length; ++index ) + { + if ( Char.IsLetter( input, index ) ) + break; + } + + if ( index >= input.Length ) + throw new FormatException( String.Format( "Input string not correctly formatted ('{0}')", input ) ); + + m_Indentation = index; + m_Text = input.Substring( index ); + } + + public static CategoryLine[] Load( string path ) + { + ArrayList list = new ArrayList(); + + if ( File.Exists( path ) ) + { + using ( StreamReader ip = new StreamReader( path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + list.Add( new CategoryLine( line ) ); + } + } + + return (CategoryLine[])list.ToArray( typeof( CategoryLine ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/GenTeleporter.cs b/Scripts/Commands/GenTeleporter.cs new file mode 100644 index 000000000..ef7a47a32 --- /dev/null +++ b/Scripts/Commands/GenTeleporter.cs @@ -0,0 +1,1050 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Commands +{ + public class GenTeleporter + { + public GenTeleporter() + { + } + + public static void Initialize() + { + CommandSystem.Register( "TelGen", AccessLevel.Administrator, new CommandEventHandler( GenTeleporter_OnCommand ) ); + } + + [Usage( "TelGen" )] + [Description( "Generates world/dungeon teleporters for all facets." )] + public static void GenTeleporter_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendMessage( "Generating teleporters, please wait." ); + + int count = new TeleportersCreator().CreateTeleporters(); + + count += new SHTeleporter.SHTeleporterCreator().CreateSHTeleporters(); + + e.Mobile.SendMessage( "Teleporter generating complete. {0} teleporters were generated.", count ); + } + + public class TeleportersCreator + { + private int m_Count; + + public TeleportersCreator() + { + } + + private static Queue m_Queue = new Queue(); + + public static bool FindTeleporter( Map map, Point3D p ) + { + IPooledEnumerable eable = map.GetItemsInRange( p, 0 ); + + foreach ( Item item in eable ) + { + if ( item is Teleporter && !(item is KeywordTeleporter) && !(item is SkillTeleporter) ) + { + int delta = item.Z - p.Z; + + if ( delta >= -12 && delta <= 12 ) + m_Queue.Enqueue( item ); + } + } + + eable.Free(); + + while ( m_Queue.Count > 0 ) + ((Item)m_Queue.Dequeue()).Delete(); + + return false; + } + + public void CreateTeleporter( Point3D pointLocation, Point3D pointDestination, Map mapLocation, Map mapDestination, bool back ) + { + if ( !FindTeleporter( mapLocation, pointLocation ) ) + { + m_Count++; + + Teleporter tel = new Teleporter( pointDestination, mapDestination ); + + tel.MoveToWorld( pointLocation, mapLocation ); + } + + if ( back && !FindTeleporter( mapDestination, pointDestination ) ) + { + m_Count++; + + Teleporter telBack = new Teleporter( pointLocation, mapLocation ); + + telBack.MoveToWorld( pointDestination, mapDestination ); + } + } + + public void CreateTeleporter( int xLoc, int yLoc, int zLoc, int xDest, int yDest, int zDest, Map map, bool back ) + { + CreateTeleporter( new Point3D( xLoc, yLoc, zLoc ), new Point3D( xDest, yDest, zDest ), map, map, back); + } + + public void CreateTeleporter( int xLoc, int yLoc, int zLoc, int xDest, int yDest, int zDest, Map mapLocation, Map mapDestination, bool back ) + { + CreateTeleporter( new Point3D( xLoc, yLoc, zLoc ), new Point3D( xDest, yDest, zDest ), mapLocation, mapDestination, back); + } + + public void DestroyTeleporter( int x, int y, int z, Map map ) + { + Point3D p = new Point3D( x, y, z ); + IPooledEnumerable eable = map.GetItemsInRange( p, 0 ); + + foreach ( Item item in eable ) + { + if ( item is Teleporter && !(item is KeywordTeleporter) && !(item is SkillTeleporter) && item.Z == p.Z ) + m_Queue.Enqueue( item ); + } + + eable.Free(); + + while ( m_Queue.Count > 0 ) + ((Item)m_Queue.Dequeue()).Delete(); + } + + public void CreateTeleportersMap( Map map ) + { + // Shame + CreateTeleporter( 512, 1559, 0, 5394, 127, 0, map, true ); + CreateTeleporter( 513, 1559, 0, 5395, 127, 0, map, true ); + CreateTeleporter( 514, 1559, 0, 5396, 127, 0, map, true ); + CreateTeleporter( 5490, 19, -25, 5514, 10, 5, map, true ); + CreateTeleporter( 5875, 19, -5, 5507, 162, 5, map, true ); + CreateTeleporter( 5604, 102, 5, 5514, 147, 25, map, true ); + + CreateTeleporter( 5513, 176, 5, 5540, 187, 0, map, false ); + CreateTeleporter( 5538, 170, 5, 5517, 176, 0, map, false ); + + // Hythloth + CreateTeleporter( 4721, 3813, 0, 5904, 16, 64, map, true ); + CreateTeleporter( 4722, 3813, 0, 5905, 16, 64, map, true ); + CreateTeleporter( 4723, 3813, 0, 5906, 16, 64, map, true ); + CreateTeleporter( 6040, 192, 12, 6059, 88, 24, map, true ); + CreateTeleporter( 6040, 193, 12, 6059, 89, 24, map, true ); + CreateTeleporter( 6040, 194, 12, 6059, 90, 24, map, true ); + + DestroyTeleporter( 5920, 168, 16, map ); + DestroyTeleporter( 5920, 169, 17, map ); + DestroyTeleporter( 5920, 170, 16, map ); + + CreateTeleporter( 5920, 168, 16, 6083, 144, -20, map, false ); + CreateTeleporter( 5920, 169, 16, 6083, 145, -20, map, false ); + CreateTeleporter( 5920, 170, 16, 6083, 146, -20, map, false ); + + CreateTeleporter( 6083, 144, -20, 5920, 168, 22, map, false ); + CreateTeleporter( 6083, 145, -20, 5920, 169, 22, map, false ); + CreateTeleporter( 6083, 146, -20, 5920, 170, 22, map, false ); + + DestroyTeleporter( 5906, 96, 0, map ); + + CreateTeleporter( 5972, 168, 0, 5905, 100, 0, map, false ); + CreateTeleporter( 5906, 96, 0, 5977, 169, 0, map, false ); + + // Covetous + CreateTeleporter( 2498, 916, 0, 5455, 1864, 0, map, true ); + CreateTeleporter( 2499, 916, 0, 5456, 1864, 0, map, true ); + CreateTeleporter( 2500, 916, 0, 5457, 1864, 0, map, true ); + CreateTeleporter( 2384, 836, 0, 5615, 1996, 0, map, true ); + CreateTeleporter( 2384, 837, 0, 5615, 1997, 0, map, true ); + CreateTeleporter( 2384, 838, 0, 5615, 1998, 0, map, true ); + CreateTeleporter( 2420, 883, 0, 5392, 1959, 0, map, true ); + CreateTeleporter( 2421, 883, 0, 5393, 1959, 0, map, true ); + CreateTeleporter( 2422, 883, 0, 5394, 1959, 0, map, true ); + CreateTeleporter( 2455, 858, 0, 5388, 2027, 0, map, true ); + CreateTeleporter( 2456, 858, 0, 5389, 2027, 0, map, true ); + CreateTeleporter( 2457, 858, 0, 5390, 2027, 0, map, true ); + CreateTeleporter( 2544, 850, 0, 5578, 1927, 0, map, true ); + CreateTeleporter( 2545, 850, 0, 5579, 1927, 0, map, true ); + CreateTeleporter( 2546, 850, 0, 5580, 1927, 0, map, true ); + + CreateTeleporter( 5551, 1805, 12, 5556, 1825, -3, map, false ); + CreateTeleporter( 5552, 1805, 12, 5557, 1825, -3, map, false ); + CreateTeleporter( 5553, 1805, 12, 5557, 1825, -3, map, false ); + + DestroyTeleporter( 5551, 1807, 0, map ); + DestroyTeleporter( 5552, 1807, 0, map ); + DestroyTeleporter( 5553, 1807, 0, map ); + + CreateTeleporter( 5556, 1826, -10, 5551, 1806, 7, map, false ); + CreateTeleporter( 5557, 1826, -10, 5552, 1806, 7, map, false ); + + DestroyTeleporter( 5556, 1825, -7, map ); + DestroyTeleporter( 5556, 1827, -13, map ); + DestroyTeleporter( 5557, 1825, -7, map ); + DestroyTeleporter( 5557, 1827, -13, map ); + DestroyTeleporter( 5558, 1825, -7, map ); + + DestroyTeleporter( 5468, 1804, 0, map ); + DestroyTeleporter( 5468, 1805, 0, map ); + DestroyTeleporter( 5468, 1806, 0, map ); + + CreateTeleporter( 5466, 1804, 12, 5593, 1840, -3, map, false ); + CreateTeleporter( 5466, 1805, 12, 5593, 1841, -3, map, false ); + CreateTeleporter( 5466, 1806, 12, 5593, 1842, -3, map, false ); + + DestroyTeleporter( 5595, 1840, -14, map ); + DestroyTeleporter( 5595, 1840, -14, map ); + + CreateTeleporter( 5594, 1840, -9, 5467, 1804, 7, map, false ); + CreateTeleporter( 5594, 1841, -9, 5467, 1805, 7, map, false ); + + // Wrong + CreateTeleporter( 5824, 631, 0, 2041, 215, 14, map, true ); + CreateTeleporter( 5825, 631, 0, 2042, 215, 14, map, true ); + CreateTeleporter( 5826, 631, 0, 2043, 215, 14, map, true ); + CreateTeleporter( 5698, 662, 0, 5793, 527, 10, map, false ); + + DestroyTeleporter( 5863, 525, 15, map ); + DestroyTeleporter( 5863, 526, 15, map ); + DestroyTeleporter( 5863, 527, 15, map ); + DestroyTeleporter( 5868, 537, 15, map ); + DestroyTeleporter( 5868, 538, 15, map ); + DestroyTeleporter( 5869, 538, 15, map ); + DestroyTeleporter( 5733, 554, 20, map ); + DestroyTeleporter( 5862, 527, 15, map ); + + // Deceit + CreateTeleporter( 4110, 430, 5, 5187, 639, 0, map, false ); + CreateTeleporter( 4111, 430, 5, 5188, 639, 0, map, false ); + CreateTeleporter( 4112, 430, 5, 5189, 639, 0, map, false ); + CreateTeleporter( 5187, 639, 0, 4110, 430, 5, map, false ); + CreateTeleporter( 5188, 639, 0, 4111, 430, 5, map, false ); + CreateTeleporter( 5189, 639, 0, 4112, 430, 5, map, false ); + CreateTeleporter( 5216, 586, -13, 5304, 533, 2, map, false ); + CreateTeleporter( 5217, 586, -13, 5305, 533, 2, map, false ); + CreateTeleporter( 5218, 586, -13, 5306, 533, 2, map, false ); + CreateTeleporter( 5304, 532, 7, 5216, 585, -8, map, false ); + CreateTeleporter( 5305, 532, 7, 5217, 585, -8, map, false ); + CreateTeleporter( 5306, 532, 7, 5218, 585, -8, map, false ); + CreateTeleporter( 5218, 761, -28, 5305, 651, 7, map, false ); + CreateTeleporter( 5219, 761, -28, 5306, 651, 7, map, false ); + CreateTeleporter( 5305, 650, 12, 5218, 760, -23, map, false ); + CreateTeleporter( 5306, 650, 12, 5219, 760, -23, map, false ); + CreateTeleporter( 5346, 578, 5, 5137, 649, 5, map, true ); + + CreateTeleporter( 5186, 639, 0, 4110, 430, 5, map, false ); + + // Despise + CreateTeleporter( 5504, 569, 46, 5574, 628, 37, map, false ); + CreateTeleporter( 5504, 570, 46, 5574, 629, 37, map, false ); + CreateTeleporter( 5504, 571, 46, 5574, 630, 37, map, false ); + CreateTeleporter( 5572, 632, 17, 5521, 672, 27, map, false ); + CreateTeleporter( 5572, 633, 17, 5521, 673, 27, map, false ); + CreateTeleporter( 5572, 634, 17, 5521, 674, 27, map, false ); + CreateTeleporter( 5573, 628, 42, 5503, 569, 51, map, false ); + CreateTeleporter( 5573, 629, 42, 5503, 570, 51, map, false ); + CreateTeleporter( 5573, 630, 42, 5503, 571, 51, map, false ); + CreateTeleporter( 5588, 632, 30, 1296, 1082, 0, map, true ); + CreateTeleporter( 5588, 630, 30, 1296, 1080, 0, map, true ); + CreateTeleporter( 5588, 631, 30, 1296, 1081, 0, map, true ); + CreateTeleporter( 5522, 672, 32, 5573, 632, 22, map, false ); + CreateTeleporter( 5522, 673, 32, 5573, 633, 22, map, false ); + CreateTeleporter( 5522, 674, 32, 5573, 634, 22, map, false ); + CreateTeleporter( 5386, 756, -8, 5408, 859, 47, map, false ); + CreateTeleporter( 5386, 757, -8, 5408, 860, 47, map, false ); + CreateTeleporter( 5386, 755, -8, 5408, 858, 47, map, false ); + CreateTeleporter( 5409, 860, 52, 5387, 757, -3, map, false ); + CreateTeleporter( 5409, 858, 52, 5387, 755, -3, map, false ); + CreateTeleporter( 5409, 859, 52, 5387, 756, -3, map, false ); + + // Destard + CreateTeleporter( 5242, 1007, 0, 1175, 2635, 0, map, true ); + CreateTeleporter( 5243, 1007, 0, 1176, 2635, 0, map, true ); + CreateTeleporter( 5244, 1007, 0, 1177, 2635, 0, map, true ); + CreateTeleporter( 5142, 797, 22, 5129, 908, -23, map, false ); + CreateTeleporter( 5143, 797, 22, 5130, 908, -23, map, false ); + CreateTeleporter( 5144, 797, 22, 5131, 908, -23, map, false ); + CreateTeleporter( 5145, 797, 22, 5132, 908, -23, map, false ); + CreateTeleporter( 5153, 808, -25, 5134, 984, 17, map, false ); + CreateTeleporter( 5153, 809, -25, 5134, 985, 17, map, false ); + CreateTeleporter( 5153, 810, -25, 5134, 986, 17, map, false ); + CreateTeleporter( 5153, 811, -25, 5134, 987, 17, map, false ); + CreateTeleporter( 5129, 909, -28, 5142, 798, 17, map, false ); + CreateTeleporter( 5130, 909, -28, 5143, 798, 17, map, false ); + CreateTeleporter( 5131, 909, -28, 5144, 798, 17, map, false ); + CreateTeleporter( 5132, 909, -28, 5145, 798, 17, map, false ); + CreateTeleporter( 5133, 984, 22, 5152, 808, -19, map, false ); + CreateTeleporter( 5133, 985, 22, 5152, 809, -19, map, false ); + CreateTeleporter( 5133, 986, 22, 5152, 810, -19, map, false ); + CreateTeleporter( 5133, 987, 22, 5152, 811, -19, map, false ); + + // Buccaneer's Den underground tunnels + + DestroyTeleporter( 2666, 2073, 5, map ); + DestroyTeleporter( 2669, 2072, -20, map ); + DestroyTeleporter( 2669, 2073, -20, map ); + DestroyTeleporter( 2649, 2194, 4, map ); + DestroyTeleporter( 2649, 2195, -14, map ); + + CreateTeleporter( 2603, 2121, -20, 2605, 2130, 8, map, false ); + CreateTeleporter( 2603, 2120, -20, 2605, 2130, 8, map, false ); + CreateTeleporter( 2669, 2071, -20, 2666, 2099, 3, map, false ); + CreateTeleporter( 2669, 2072, -20, 2666, 2099, 3, map, false ); + CreateTeleporter( 2669, 2073, -20, 2666, 2099, 3, map, false ); + CreateTeleporter( 2676, 2241, -18, 2691, 2234, 2, map, false ); + CreateTeleporter( 2676, 2242, -18, 2691, 2234, 2, map, false ); + CreateTeleporter( 2758, 2092, -20, 2756, 2097, 38, map, false ); + CreateTeleporter( 2759, 2092, -20, 2756, 2097, 38, map, false ); + CreateTeleporter( 2685, 2063, 39, 2685, 2063, -20, map, false ); // that should not be a teleporter: on OSI you simply fall under the ground + + // Misc + CreateTeleporter( 5217, 18, 15, 5204, 74, 17, map, false ); + CreateTeleporter( 5200, 71, 17, 5211, 22, 15, map, false ); + CreateTeleporter( 1997, 81, 7, 5881, 242, 0, map, false ); + CreateTeleporter( 5704, 146, -45, 5705, 305, 7, map, false ); + CreateTeleporter( 5704, 147, -45, 5705, 306, 7, map, false ); + CreateTeleporter( 5874, 146, 27, 5208, 2323, 31, map, false ); + CreateTeleporter( 5875, 146, 27, 5208, 2322, 32, map, false ); + CreateTeleporter( 5876, 146, 27, 5208, 2322, 32, map, false ); + CreateTeleporter( 5877, 146, 27, 5208, 2322, 32, map, false ); + CreateTeleporter( 5923, 169, 1, 5925, 171, 22, map, false ); + CreateTeleporter( 2399, 198, 0, 5753, 436, 79, map, false ); + CreateTeleporter( 2400, 198, 0, 5754, 436, 80, map, false ); + DestroyTeleporter( 5166, 245, 15, map ); + DestroyTeleporter( 1361, 883, 0, map ); + CreateTeleporter( 5191, 152, 0, 1367, 891, 0, map, false ); + CreateTeleporter( 5849, 239, -25, 5831, 324, 27, map, false ); + CreateTeleporter( 5850, 239, -25, 5832, 324, 26, map, false ); + CreateTeleporter( 5851, 239, -25, 5833, 324, 28, map, false ); + CreateTeleporter( 5852, 239, -25, 5834, 324, 27, map, false ); + CreateTeleporter( 5853, 239, -23, 5835, 324, 27, map, false ); + CreateTeleporter( 5882, 241, 0, 1998, 81, 5, map, false ); + CreateTeleporter( 5882, 242, 0, 1998, 81, 5, map, false ); + CreateTeleporter( 5882, 243, 0, 1998, 81, 5, map, false ); + CreateTeleporter( 5706, 305, 12, 5705, 146, -45, map, false ); + CreateTeleporter( 5706, 306, 12, 5705, 147, -45, map, false ); + CreateTeleporter( 5748, 362, 2, 313, 786, -24, map, false ); + CreateTeleporter( 5749, 362, 0, 313, 786, -24, map, false ); + CreateTeleporter( 5750, 362, 3, 314, 786, -24, map, false ); + CreateTeleporter( 5753, 324, 21, 5670, 2391, 40, map, false ); + CreateTeleporter( 5831, 323, 34, 5849, 238, -25, map, false ); + CreateTeleporter( 5832, 323, 34, 5850, 238, -25, map, false ); + CreateTeleporter( 5833, 323, 33, 5851, 238, -25, map, false ); + CreateTeleporter( 5834, 323, 33, 5852, 238, -25, map, false ); + CreateTeleporter( 5835, 323, 33, 5853, 238, -23, map, false ); + CreateTeleporter( 5658, 423, 8, 5697, 3659, 2, map, false ); + CreateTeleporter( 5686, 385, 2, 2777, 894, -23, map, false ); + CreateTeleporter( 5686, 386, 2, 2777, 894, -23, map, false ); + CreateTeleporter( 5686, 387, 2, 2777, 895, -23, map, false ); + CreateTeleporter( 5731, 445, -18, 6087, 3676, 18, map, false ); + CreateTeleporter( 5753, 437, 78, 2400, 199, 0, map, false ); + CreateTeleporter( 5850, 432, 0, 5127, 3143, 97, map, false ); + CreateTeleporter( 5850, 433, -2, 5127, 3143, 97, map, false ); + CreateTeleporter( 5850, 434, -1, 5127, 3143, 97, map, false ); + CreateTeleporter( 5850, 431, 2, 5127, 3143, 97, map, false ); + CreateTeleporter( 5826, 465, -1, 1987, 2063, -40, map, false ); + CreateTeleporter( 5827, 465, -1, 1988, 2063, -40, map, false ); + CreateTeleporter( 5828, 465, 0, 1989, 2063, -40, map, false ); + CreateTeleporter( 313, 786, -24, 5748, 361, 2, map, false ); + CreateTeleporter( 314, 786, -24, 5749, 361, 2, map, false ); + CreateTeleporter( 2776, 895, -23, 5685, 387, 2, map, false ); + //CreateTeleporter( 4545, 851, 30, 5736, 3196, 8, map, false ); + DestroyTeleporter( 4545, 851, 30, map ); + CreateTeleporter( 4540, 898, 32, 4442, 1122, 5, map, false ); + CreateTeleporter( 4300, 968, 5, 4442, 1122, 5, map, false ); + CreateTeleporter( 4436, 1107, 5, 4300, 992, 5, map, false ); + CreateTeleporter( 4443, 1137, 5, 4487, 1475, 5, map, false ); + CreateTeleporter( 4449, 1107, 5, 4539, 890, 28, map, false ); + CreateTeleporter( 4449, 1115, 5, 4671, 1135, 10, map, false ); + CreateTeleporter( 4663, 1134, 13, 4442, 1122, 5, map, false ); + CreateTeleporter( 5701, 1320, 16, 5786, 1336, -8, map, false ); + CreateTeleporter( 5702, 1320, 16, 5787, 1336, -8, map, false ); + CreateTeleporter( 5703, 1320, 16, 5788, 1336, -8, map, false ); + CreateTeleporter( 5786, 1335, -13, 5701, 1319, 13, map, false ); + CreateTeleporter( 5787, 1335, -13, 5702, 1319, 13, map, false ); + CreateTeleporter( 5788, 1335, -13, 5703, 1319, 13, map, false ); + CreateTeleporter( 6005, 1380, 1, 5151, 4063, 37, map, false ); + CreateTeleporter( 6005, 1378, 0, 5151, 4062, 37, map, false ); + CreateTeleporter( 6005, 1379, 2, 5151, 4062, 37, map, false ); + CreateTeleporter( 6025, 1344, -26, 5137, 3664, 27, map, false ); + CreateTeleporter( 6025, 1345, -26, 5137, 3664, 27, map, false ); + CreateTeleporter( 6025, 1346, -26, 5137, 3665, 31, map, false ); + CreateTeleporter( 5687, 1424, 38, 2923, 3406, 8, map, false ); + CreateTeleporter( 5792, 1416, 41, 5758, 2908, 15, map, false ); + CreateTeleporter( 5792, 1417, 41, 5758, 2909, 15, map, false ); + CreateTeleporter( 5792, 1415, 41, 5758, 2907, 15, map, false ); + CreateTeleporter( 5899, 1411, 43, 1630, 3320, 0, map, false ); + CreateTeleporter( 5900, 1411, 42, 1630, 3320, 0, map, false ); + CreateTeleporter( 5918, 1412, -29, 5961, 1409, 59, map, false ); + CreateTeleporter( 5918, 1410, -29, 5961, 1408, 59, map, false ); + CreateTeleporter( 5918, 1411, -29, 5961, 1408, 59, map, false ); + CreateTeleporter( 5961, 1408, 59, 5918, 1411, -29, map, false ); + CreateTeleporter( 5961, 1409, 59, 5918, 1412, -29, map, false ); + CreateTeleporter( 6125, 1411, 15, 6075, 3332, 4, map, false ); + CreateTeleporter( 6126, 1411, 15, 6075, 3332, 4, map, false ); + CreateTeleporter( 6127, 1411, 15, 6075, 3332, 4, map, false ); + CreateTeleporter( 6137, 1409, 2, 6140, 1432, 4, map, false ); + CreateTeleporter( 6138, 1409, 2, 6140, 1432, 4, map, false ); + CreateTeleporter( 6140, 1431, 4, 6137, 1408, 2, map, false ); + CreateTeleporter( 4496, 1475, 15, 4442, 1122, 5, map, false ); + CreateTeleporter( 6031, 1501, 42, 1491, 1642, 24, map, false ); + CreateTeleporter( 6031, 1499, 42, 1491, 1640, 24, map, false ); + CreateTeleporter( 1491, 1640, 24, 6032, 1499, 31, map, false ); + CreateTeleporter( 1491, 1642, 24, 6032, 1501, 31, map, false ); + DestroyTeleporter( 5341, 1602, 0, map ); + CreateTeleporter( 5340, 1599, 40, 5426, 3122, -74, map, false ); + CreateTeleporter( 5341, 1599, 40, 5426, 3122, -74, map, false ); + CreateTeleporter( 1987, 2062, -40, 5826, 464, 0, map, false ); + CreateTeleporter( 1988, 2062, -40, 5827, 464, -1, map, false ); + CreateTeleporter( 1989, 2062, -40, 5828, 464, -1, map, false ); + CreateTeleporter( 5203, 2327, 27, 5876, 147, 25, map, false ); + CreateTeleporter( 5207, 2322, 27, 5877, 147, 25, map, false ); + CreateTeleporter( 5207, 2323, 26, 5876, 147, 25, map, false ); + CreateTeleporter( 5670, 2391, 40, 5753, 325, 10, map, false ); + CreateTeleporter( 5974, 2697, 35, 2985, 2890, 35, map, false ); + CreateTeleporter( 5267, 2757, 35, 424, 3283, 35, map, false ); + CreateTeleporter( 5757, 2908, 14, 5791, 1416, 38, map, false ); + CreateTeleporter( 5757, 2909, 15, 5791, 1417, 40, map, false ); + CreateTeleporter( 5757, 2907, 15, 5791, 1415, 38, map, false ); + CreateTeleporter( 1653, 2963, 0, 1677, 2987, 0, map, false ); + CreateTeleporter( 1677, 2987, 0, 1675, 2987, 20, map, false ); + CreateTeleporter( 5426, 3123, -80, 5341, 1602, 0, map, false ); + CreateTeleporter( 5126, 3143, 99, 5849, 432, 1, map, false ); + //CreateTeleporter( 5736, 3196, 8, 4545, 851, 30, map, false ); + DestroyTeleporter( 5736, 3196, 8, map ); + CreateTeleporter( 424, 3283, 35, 5267, 2757, 35, map, false ); + CreateTeleporter( 1629, 3320, 0, 5899, 1411, 43, map, false ); + CreateTeleporter( 6075, 3332, 4, 6126, 1410, 15, map, false ); + CreateTeleporter( 2923, 3405, 6, 5687, 1423, 39, map, false ); + CreateTeleporter( 1142, 3621, 5, 1414, 3828, 5, map, false ); + CreateTeleporter( 5137, 3664, 27, 6025, 1344, -26, map, false ); + CreateTeleporter( 5137, 3665, 31, 6025, 1345, -26, map, false ); + CreateTeleporter( 5697, 3660, -5, 5658, 424, 0, map, false ); + CreateTeleporter( 6086, 3676, 16, 5731, 446, -16, map, false ); + CreateTeleporter( 1409, 3824, 5, 1124, 3623, 5, map, false ); + CreateTeleporter( 1419, 3832, 5, 1466, 4015, 5, map, false ); + CreateTeleporter( 1406, 3996, 5, 1414, 3828, 5, map, false ); + CreateTeleporter( 5150, 4062, 38, 6005, 1378, 0, map, false ); + CreateTeleporter( 5150, 4063, 38, 6005, 1380, 1, map, false ); + CreateTeleporter( 5906, 4069, 26, 2494, 3576, 5, map, true ); + CreateTeleporter( 2985, 2890, 35, 5974, 2697, 35, map, false ); + + // Mondain's Legacy dungeons + + // Sanctuary + CreateTeleporter( 6172, 21, 0, 765, 1645, 0, map, false ); // Entrance gate + CreateTeleporter( 6172, 22, 0, 765, 1646, 0, map, false ); + CreateTeleporter( 6172, 23, 0, 765, 1647, 0, map, false ); + CreateTeleporter( 766, 1645, 0, 6174, 21, 0, map, false ); + CreateTeleporter( 766, 1646, 0, 6174, 22, 0, map, false ); + CreateTeleporter( 766, 1647, 0, 6174, 23, 0, map, false ); + + CreateTeleporter( 6173, 176, -10, 6233, 15, -10, map, false ); // Zone change + CreateTeleporter( 6174, 176, -10, 6234, 15, -10, map, false ); + CreateTeleporter( 6175, 176, -10, 6235, 15, -10, map, false ); + CreateTeleporter( 6176, 176, -10, 6236, 15, -10, map, false ); + CreateTeleporter( 6177, 176, -10, 6237, 15, -10, map, false ); + CreateTeleporter( 6178, 176, -10, 6238, 15, -10, map, false ); + CreateTeleporter( 6179, 176, -10, 6239, 15, -10, map, false ); + CreateTeleporter( 6233, 14, -10, 6172, 174, -10, map, false ); + CreateTeleporter( 6234, 14, -10, 6173, 174, -10, map, false ); + CreateTeleporter( 6235, 14, -10, 6174, 174, -10, map, false ); + CreateTeleporter( 6236, 14, -10, 6175, 174, -10, map, false ); + CreateTeleporter( 6237, 14, -10, 6176, 174, -10, map, false ); + CreateTeleporter( 6238, 14, -10, 6177, 174, -10, map, false ); + CreateTeleporter( 6239, 14, -10, 6177, 174, -10, map, false ); + CreateTeleporter( 6240, 14, -10, 6178, 174, -10, map, false ); + + CreateTeleporter( 6256, 97, -4, 6257, 95, -10, map, false ); // Ladders + CreateTeleporter( 6260, 97, -4, 6260, 95, -10, map, false ); + CreateTeleporter( 6263, 97, -4, 6262, 95, -10, map, false ); + CreateTeleporter( 6269, 97, -4, 6269, 95, -10, map, false ); + CreateTeleporter( 6273, 97, -4, 6272, 95, -10, map, false ); + CreateTeleporter( 6262, 95, -10, 6262, 99, -10, map, false ); + + CreateTeleporter( 6159, 130, 0, 6317, 63, -20, map, false ); // Holes + CreateTeleporter( 6164, 73, 0, 6320, 22, -20, map, false ); + CreateTeleporter( 6161, 163, -10, 6321, 106, -20, map, false ); + CreateTeleporter( 6276, 40, -10, 6374, 124, -20, map, false ); + CreateTeleporter( 6211, 106, 0, 6355, 34, -20, map, false ); + + CreateTeleporter( 6316, 62, -5, 6160, 131, 0, map, false ); // Cave ladders + CreateTeleporter( 6319, 19, -20, 6165, 74, 0, map, false ); // This actually goes to (6165, 75) inside the wall... + CreateTeleporter( 6356, 32, -9, 6210, 105, 0, map, false ); + CreateTeleporter( 6374, 125, -20, 6277, 41, -10, map, false ); + + CreateTeleporter( 6373, 49, -20, 801, 1682, 0, map, false ); + CreateTeleporter( 6374, 49, -20, 801, 1682, 0, map, false ); + CreateTeleporter( 6375, 49, -20, 801, 1682, 0, map, false ); + CreateTeleporter( 6376, 49, -20, 801, 1682, 0, map, false ); + CreateTeleporter( 6377, 49, -20, 801, 1682, 0, map, false ); + + // Painted Caves + CreateTeleporter( 1714, 2996, 0, 6308, 892, 0, map, false ); // Entrance + CreateTeleporter( 1714, 2997, 0, 6308, 892, 0, map, false ); + CreateTeleporter( 6310, 890, 0, 1716, 2997, 0, map, false ); + CreateTeleporter( 6310, 891, 0, 1716, 2997, 0, map, false ); + CreateTeleporter( 6310, 892, 0, 1716, 2997, 0, map, false ); + CreateTeleporter( 6310, 893, 0, 1716, 2997, 0, map, false ); + } + + public void CreateTeleportersMap2( Map map ) + { + // Dungeon of rock + CreateTeleporter( 2186, 294, -27, 2186, 33, -27, map, true ); + CreateTeleporter( 2187, 294, -27, 2187, 33, -27, map, true ); + CreateTeleporter( 2188, 294, -27, 2188, 33, -27, map, true ); + CreateTeleporter( 2189, 294, -27, 2189, 33, -27, map, true ); + CreateTeleporter( 2189, 320, -7, 1788, 569, 74, map, true ); + CreateTeleporter( 2188, 320, -7, 1787, 569, 74, map, true ); + CreateTeleporter( 2187, 320, -7, 1787, 569, 74, map, false ); + + // Spider Cave + DestroyTeleporter( 1783, 993, -28, map ); + DestroyTeleporter( 1784, 993, -28, map ); + DestroyTeleporter( 1785, 993, -28, map ); + DestroyTeleporter( 1786, 993, -28, map ); + DestroyTeleporter( 1787, 993, -28, map ); + DestroyTeleporter( 1788, 993, -28, map ); + + DestroyTeleporter( 1419, 910, -10, map ); + DestroyTeleporter( 1420, 910, -10, map ); + DestroyTeleporter( 1421, 910, -10, map ); + DestroyTeleporter( 1422, 910, -10, map ); + + CreateTeleporter( 1419, 909, -10, 1784, 994, -28, map, true ); + CreateTeleporter( 1420, 909, -10, 1785, 994, -28, map, true ); + CreateTeleporter( 1421, 909, -10, 1786, 994, -28, map, true ); + CreateTeleporter( 1787, 994, -28, 1421, 909, -10, map, false ); + CreateTeleporter( 1788, 994, -28, 1421, 909, -10, map, false ); + CreateTeleporter( 1783, 994, -28, 1419, 909, -10, map, false ); + + CreateTeleporter( 1861, 980, -28, 1490, 877, 10, map, true ); + CreateTeleporter( 1861, 981, -28, 1490, 878, 10, map, true ); + CreateTeleporter( 1861, 982, -28, 1490, 879, 10, map, true ); + CreateTeleporter( 1861, 983, -28, 1490, 880, 10, map, true ); + CreateTeleporter( 1861, 984, -28, 1490, 880, 10, map, false ); + CreateTeleporter( 1516, 879, 10, 1363, 1105, -26, map, true ); + + // Spectre Dungeon + CreateTeleporter( 1362, 1031, -13, 1981, 1107, -16, map, true ); + CreateTeleporter( 1363, 1031, -13, 1982, 1107, -16, map, true ); + CreateTeleporter( 1364, 1031, -13, 1983, 1107, -16, map, true ); + CreateTeleporter( 1980, 1107, -16, 1362, 1031, -13, map, false ); + CreateTeleporter( 1984, 1107, -16, 1364, 1031, -13, map, false ); + + // BLOOD DUNGEON + CreateTeleporter( 1745, 1236, -30, 2112, 829, -11, map, true ); + CreateTeleporter( 1746, 1236, -30, 2113, 829, -11, map, true ); + CreateTeleporter( 1747, 1236, -30, 2114, 829, -11, map, true ); + CreateTeleporter( 1748, 1236, -30, 2115, 829, -11, map, true ); + CreateTeleporter( 2116, 829, -11, 1748, 1236, -30, map, false ); + + // Mushroom Cave + CreateTeleporter( 1456, 1328, -27, 1479, 1494, -28, map, true ); + CreateTeleporter( 1456, 1329, -27, 1479, 1495, -28, map, true ); + CreateTeleporter( 1456, 1330, -27, 1479, 1496, -28, map, true ); + CreateTeleporter( 1479, 1493, -28, 1456, 1328, -27, map, false ); + CreateTeleporter( 1479, 1497, -28, 1456, 1330, -27, map, false ); + + // RATMAN CAVE + CreateTeleporter( 1029, 1155, -24, 1349, 1512, -3, map, true ); + CreateTeleporter( 1029, 1154, -24, 1349, 1511, -3, map, true ); + CreateTeleporter( 1029, 1153, -24, 1349, 1510, -3, map, true ); + CreateTeleporter( 1349, 1509, -3, 1030, 1153, -24, map, false ); + CreateTeleporter( 1268, 1508, -28, 1250, 1508, -28, map, true ); + CreateTeleporter( 1268, 1509, -28, 1250, 1509, -28, map, true ); + CreateTeleporter( 1268, 1510, -28, 1250, 1510, -28, map, true ); + CreateTeleporter( 1250, 1511, -28, 1268, 1510, -28, map, false ); + CreateTeleporter( 1250, 1512, -28, 1268, 1510, -28, map, false ); + + // Serpentine Passage + DestroyTeleporter( 532, 1532, -7, map ); + DestroyTeleporter( 533, 1532, -7, map ); + DestroyTeleporter( 534, 1532, -7, map ); + + CreateTeleporter( 810, 874, -39, 532, 1532, -7, map, false ); + CreateTeleporter( 811, 874, -39, 533, 1532, -7, map, false ); + CreateTeleporter( 812, 874, -39, 534, 1532, -7, map, false ); + CreateTeleporter( 531, 1533, -4, 810, 875, -40, map, false ); + CreateTeleporter( 532, 1533, -4, 811, 875, -39, map, false ); + CreateTeleporter( 533, 1533, -4, 812, 875, -39, map, false ); + CreateTeleporter( 534, 1533, -4, 813, 875, -40, map, false ); + + CreateTeleporter( 393, 1587, -13, 78, 1366, -36, map, false ); + CreateTeleporter( 394, 1587, -13, 79, 1366, -36, map, false ); + CreateTeleporter( 395, 1587, -13, 80, 1366, -36, map, false ); + CreateTeleporter( 396, 1587, -13, 81, 1366, -36, map, false ); + CreateTeleporter( 78, 1365, -41, 393, 1586, -16, map, false ); + CreateTeleporter( 79, 1365, -41, 394, 1586, -16, map, false ); + CreateTeleporter( 80, 1365, -41, 395, 1586, -16, map, false ); + CreateTeleporter( 81, 1365, -41, 396, 1586, -16, map, false ); + DestroyTeleporter( 82, 1365, -38, map ); + + // ANKH DUNGEON + DestroyTeleporter( 4, 1267, -11, map ); + DestroyTeleporter( 4, 1268, -11, map ); + DestroyTeleporter( 4, 1269, -11, map ); + CreateTeleporter( 668, 928, -84, 3, 1267, -8, map, true ); + CreateTeleporter( 668, 929, -84, 3, 1268, -8, map, true ); + CreateTeleporter( 668, 930, -82, 3, 1269, -8, map, true ); + + DestroyTeleporter( 154, 1473, -8, map ); + DestroyTeleporter( 155, 1473, -8, map ); + DestroyTeleporter( 156, 1473, -8, map ); + CreateTeleporter( 575, 1156, -121, 154, 1473, -8, map, false ); + CreateTeleporter( 576, 1156, -121, 155, 1473, -8, map, false ); + CreateTeleporter( 577, 1156, -121, 156, 1473, -8, map, false ); + CreateTeleporter( 154, 1472, -8, 575, 1155, -121, map, false ); + CreateTeleporter( 155, 1472, -8, 576, 1155, -120, map, false ); + CreateTeleporter( 156, 1472, -8, 577, 1155, -120, map, false ); + + DestroyTeleporter( 10, 1518, -28, map ); + DestroyTeleporter( 10, 1518, -27, map ); + DestroyTeleporter( 10, 1518, -27, map ); + CreateTeleporter( 10, 872, -28, 10, 1518, -27, map, false ); + CreateTeleporter( 11, 872, -28, 11, 1518, -28, map, false ); + CreateTeleporter( 12, 872, -27, 12, 1518, -28, map, false ); + CreateTeleporter( 10, 1519, -28, 10, 873, -28, map, false ); + CreateTeleporter( 11, 1519, -27, 11, 873, -28, map, false ); + CreateTeleporter( 12, 1519, -27, 12, 873, -27, map, false ); + + // Ratman Lair + CreateTeleporter( 636, 813, -62, 164, 743, -28, map, false ); + CreateTeleporter( 164, 746, -16, 636, 815, -52, map, false ); + + // WISP DUNGEON + CreateTeleporter( 348, 1427, 15, 18, 1198, -5, map, true ); + CreateTeleporter( 349, 1427, 15, 19, 1198, -5, map, true ); + CreateTeleporter( 350, 1427, 15, 20, 1198, -5, map, true ); + CreateTeleporter( 351, 1427, 15, 21, 1198, -5, map, true ); + CreateTeleporter( 712, 1490, -3, 686, 1490, -28, map, false ); + CreateTeleporter( 712, 1491, -3, 686, 1491, -28, map, false ); + CreateTeleporter( 712, 1492, -3, 686, 1492, -28, map, false ); + CreateTeleporter( 712, 1493, -3, 686, 1493, -28, map, false ); + CreateTeleporter( 694, 1490, -53, 719, 1490, -28, map, false ); + CreateTeleporter( 694, 1491, -53, 719, 1491, -28, map, false ); + CreateTeleporter( 694, 1492, -53, 719, 1492, -28, map, false ); + CreateTeleporter( 694, 1493, -53, 719, 1493, -28, map, false ); + CreateTeleporter( 775, 1467, -28, 658, 1498, -28, map, false ); + DestroyTeleporter( 658, 1498, -28, map ); + CreateTeleporter( 838, 1550, -6, 728, 1505, -28, map, false ); + CreateTeleporter( 838, 1551, -6, 728, 1506, -28, map, false ); + CreateTeleporter( 838, 1552, -6, 728, 1507, -28, map, false ); + CreateTeleporter( 838, 1553, -6, 728, 1508, -28, map, false ); + CreateTeleporter( 722, 1505, -53, 833, 1550, -28, map, false ); + CreateTeleporter( 722, 1506, -53, 833, 1551, -28, map, false ); + CreateTeleporter( 722, 1507, -53, 833, 1552, -28, map, false ); + CreateTeleporter( 722, 1508, -53, 833, 1553, -28, map, false ); + CreateTeleporter( 954, 1425, -53, 874, 1490, 2, map, false ); + CreateTeleporter( 954, 1426, -53, 874, 1491, 2, map, false ); + CreateTeleporter( 954, 1427, -53, 874, 1492, 2, map, false ); + CreateTeleporter( 954, 1428, -53, 874, 1493, 2, map, false ); + CreateTeleporter( 954, 1429, -53, 874, 1493, 2, map, false ); + CreateTeleporter( 879, 1490, 24, 960, 1425, -28, map, false ); + CreateTeleporter( 879, 1491, 24, 960, 1426, -28, map, false ); + CreateTeleporter( 879, 1492, 24, 960, 1427, -28, map, false ); + CreateTeleporter( 879, 1493, 24, 960, 1428, -28, map, false ); + CreateTeleporter( 948, 1464, -56, 951, 1442, -6, map, true ); + CreateTeleporter( 948, 1465, -56, 951, 1443, -6, map, true ); + CreateTeleporter( 948, 1466, -56, 951, 1444, -6, map, true ); + CreateTeleporter( 948, 1467, -56, 951, 1445, -6, map, true ); + CreateTeleporter( 871, 1433, -6, 897, 1449, -28, map, false ); + CreateTeleporter( 871, 1434, -6, 897, 1450, -28, map, false ); + CreateTeleporter( 871, 1435, -6, 897, 1451, -28, map, false ); + CreateTeleporter( 871, 1436, -6, 897, 1452, -28, map, false ); + CreateTeleporter( 871, 1437, -6, 897, 1453, -28, map, false ); + CreateTeleporter( 892, 1449, -51, 866, 1433, -28, map, false ); + CreateTeleporter( 892, 1450, -51, 866, 1434, -28, map, false ); + CreateTeleporter( 892, 1451, -51, 866, 1435, -28, map, false ); + CreateTeleporter( 892, 1452, -51, 866, 1436, -28, map, false ); + CreateTeleporter( 892, 1453, -51, 866, 1437, -28, map, false ); + CreateTeleporter( 812, 1546, -6, 848, 1434, -28, map, false ); + CreateTeleporter( 812, 1547, -6, 848, 1435, -28, map, false ); + CreateTeleporter( 812, 1548, -6, 848, 1436, -28, map, false ); + CreateTeleporter( 812, 1549, -6, 848, 1437, -28, map, false ); + CreateTeleporter( 843, 1434, -51, 807, 1546, -28, map, false ); + CreateTeleporter( 843, 1435, -51, 807, 1547, -28, map, false ); + CreateTeleporter( 843, 1436, -51, 807, 1548, -28, map, false ); + CreateTeleporter( 843, 1437, -51, 807, 1549, -28, map, false ); + CreateTeleporter( 751, 1473, -28, 763, 1479, -28, map, false ); + DestroyTeleporter( 763, 1479, -28, map ); + CreateTeleporter( 751, 1479, -28, 763, 1555, -28, map, false ); + DestroyTeleporter( 763, 1555, -28, map ); + CreateTeleporter( 752, 1549, -28, 751, 1484, -28, map, false ); + DestroyTeleporter( 751, 1484, -28, map ); + CreateTeleporter( 775, 1492, -28, 827, 1515, -28, map, false ); + DestroyTeleporter( 827, 1515, -28, map ); + DestroyTeleporter( 1013, 1506, 0, map ); + DestroyTeleporter( 1013, 1507, 0, map ); + DestroyTeleporter( 1013, 1508, 0, map ); + DestroyTeleporter( 1013, 1509, 0, map ); + CreateTeleporter( 904, 1360, -21, 1014, 1506, 0, map, true ); + CreateTeleporter( 904, 1361, -21, 1014, 1507, 0, map, true ); + CreateTeleporter( 904, 1362, -21, 1014, 1508, 0, map, true ); + CreateTeleporter( 904, 1363, -21, 1014, 1509, 0, map, true ); + + /*CreateTeleporter( 650, 1297, -58, 626, 1526, -28, map, true ); + CreateTeleporter( 651, 1297, -58, 627, 1526, -28, map, true ); + CreateTeleporter( 652, 1297, -58, 628, 1526, -28, map, true ); + CreateTeleporter( 653, 1297, -58, 629, 1526, -28, map, true );*/ + + // Update: remove old teleporters + DestroyTeleporter( 650, 1297, -58, map ); + DestroyTeleporter( 651, 1297, -58, map ); + DestroyTeleporter( 652, 1297, -58, map ); + DestroyTeleporter( 653, 1297, -58, map ); + DestroyTeleporter( 626, 1526, -28, map ); + DestroyTeleporter( 627, 1526, -28, map ); + DestroyTeleporter( 628, 1526, -28, map ); + DestroyTeleporter( 629, 1526, -28, map ); + + // Update: add new ones + for ( int i = 0; i < 4; ++i ) + { + CreateTeleporter( 650 + i, 1297, -58, 626 + i, 1526, -28, map, false ); + CreateTeleporter( 626 + i, 1527, -28, 650 + i, 1298, i == 1 ? -59 : -58, map, false ); + } + + // WISP DUNGEON MAZE + CreateTeleporter( 747, 1539, -28, 785, 1514, -28, map, false ); + CreateTeleporter( 785, 1524, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 747, 1555, -28, 785, 1570, -28, map, false ); + CreateTeleporter( 784, 1580, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 791, 1548, -28, 781, 1547, -28, map, false ); + CreateTeleporter( 782, 1550, -28, 783, 1538, -28, map, false ); + CreateTeleporter( 783, 1539, -28, 787, 1542, -28, map, false ); + CreateTeleporter( 787, 1543, -28, 781, 1554, -28, map, false ); + CreateTeleporter( 781, 1555, -28, 789, 1556, -28, map, false ); + CreateTeleporter( 789, 1557, -28, 785, 1550, -28, map, false ); + CreateTeleporter( 784, 1550, -28, 777, 1554, -28, map, false ); + CreateTeleporter( 778, 1554, -28, 787, 1538, -28, map, false ); + CreateTeleporter( 787, 1537, -28, 781, 1546, -28, map, false ); + CreateTeleporter( 781, 1545, -28, 789, 1552, -28, map, false ); + CreateTeleporter( 789, 1553, -28, 789, 1546, -28, map, false ); + CreateTeleporter( 789, 1545, -28, 779, 1541, -28, map, false ); + CreateTeleporter( 780, 1541, -28, 785, 1554, -28, map, false ); + CreateTeleporter( 785, 1555, -28, 783, 1542, -28, map, false ); + CreateTeleporter( 782, 1542, -28, 785, 1546, -28, map, false ); + CreateTeleporter( 784, 1546, -28, 776, 1548, -28, map, false ); + CreateTeleporter( 777, 1555, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 777, 1553, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 776, 1549, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 777, 1548, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 777, 1547, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 776, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 778, 1541, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 779, 1540, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 779, 1542, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 780, 1554, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 781, 1553, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 782, 1554, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 780, 1550, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 781, 1549, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 781, 1551, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 780, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 781, 1547, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 782, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 783, 1543, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 784, 1542, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 783, 1541, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 782, 1538, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 783, 1537, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 784, 1538, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 786, 1538, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 787, 1539, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 788, 1538, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 788, 1542, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 787, 1541, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 786, 1542, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 785, 1545, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 786, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 785, 1547, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 785, 1549, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 785, 1551, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 786, 1550, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 784, 1554, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 785, 1553, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 786, 1554, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 788, 1556, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 789, 1555, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 790, 1556, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 788, 1552, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 789, 1551, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 790, 1552, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 790, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 788, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 789, 1547, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 791, 1545, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 791, 1546, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 791, 1547, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 791, 1549, -28, 798, 1547, -28, map, false ); + CreateTeleporter( 791, 1550, -28, 798, 1547, -28, map, false ); + + // Sorcerer`s Dungeon + CreateTeleporter( 546, 455, -40, 426, 113, -28, map, true ); + CreateTeleporter( 547, 455, -40, 427, 113, -28, map, true ); + CreateTeleporter( 548, 455, -40, 428, 113, -28, map, true ); + CreateTeleporter( 429, 113, -28, 548, 455, -40, map, false ); + + CreateTeleporter( 242, 27, -16, 372, 31, -31, map, false ); // stairs - 0x754 + CreateTeleporter( 242, 26, -16, 372, 30, -31, map, false ); // stairs - 0x754 + CreateTeleporter( 242, 25, -16, 372, 29, -31, map, false ); // stairs - 0x754 + CreateTeleporter( 371, 31, -36, 241, 27, -18, map, false ); // stairs - 0x754 + CreateTeleporter( 371, 30, -36, 241, 26, -18, map, false ); // stairs - 0x754 + + DestroyTeleporter( 371, 29, -36, map ); //To remove old erroneous teleporter + + CreateTeleporter( 371, 29, -36, 241, 25, -18, map, false ); // stairs - 0x754 + + CreateTeleporter( 272, 141, -16, 555, 427, -1, map, false ); // stairs - 1st 0x753 + CreateTeleporter( 273, 141, -16, 556, 427, -1, map, false ); // stairs - 1st 0x753 + CreateTeleporter( 274, 141, -16, 557, 427, -1, map, false ); // stairs - 1st 0x753 + CreateTeleporter( 555, 426, -6, 272, 140, -21, map, false ); // stairs - 1st 0x753 + CreateTeleporter( 556, 426, -6, 273, 140, -21, map, false ); // stairs - 1st 0x753 + CreateTeleporter( 557, 426, -6, 274, 140, -21, map, false ); // stairs - 1st 0x753 + + CreateTeleporter( 265, 130, -31, 284, 72, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 266, 130, -31, 285, 72, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 267, 130, -31, 286, 72, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 268, 130, -31, 287, 72, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 284, 73, -16, 265, 131, -28, map, false ); // stairs - 0x753 + CreateTeleporter( 285, 73, -16, 266, 131, -28, map, false ); // stairs - 0x753 + CreateTeleporter( 286, 73, -16, 267, 131, -28, map, false ); // stairs - 0x753 + CreateTeleporter( 287, 73, -16, 268, 131, -28, map, false ); // stairs - 0x753 + + CreateTeleporter( 284, 67, -30, 131, 128, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 285, 67, -30, 132, 128, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 286, 67, -30, 133, 128, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 287, 67, -30, 134, 128, -21, map, false ); // stairs - 0x753 + CreateTeleporter( 131, 129, -16, 284, 68, -28, map, false ); // stairs - 0x753 + CreateTeleporter( 132, 129, -16, 285, 68, -28, map, false ); // stairs - 0x753 + CreateTeleporter( 133, 129, -16, 286, 68, -28, map, false ); // stairs - 0x753 + CreateTeleporter( 134, 129, -16, 287, 68, -28, map, false ); // stairs - 0x753 + + CreateTeleporter( 358, 40, -36, 156, 88, -18, map, false ); // stairs - 0x73A + CreateTeleporter( 358, 41, -36, 156, 89, -18, map, false ); // stairs - 0x73A + CreateTeleporter( 358, 42, -36, 156, 90, -18, map, false ); // stairs - 0x73A + CreateTeleporter( 155, 88, -16, 357, 40, -31, map, false ); // stairs - 0x73A + CreateTeleporter( 155, 89, -16, 357, 41, -31, map, false ); // stairs - 0x73A + CreateTeleporter( 155, 90, -16, 357, 42, -31, map, false ); // stairs - 0x73A + + CreateTeleporter( 259, 90, -28, 236, 113, -28, map, true ); + + // Ancient Lair + + DestroyTeleporter( 83, 747, -28, map ); + DestroyTeleporter( 84, 747, -28, map ); + DestroyTeleporter( 85, 747, -28, map ); + DestroyTeleporter( 86, 747, -28, map ); + CreateTeleporter( 938, 494, -40, 83, 749, -23, map, true ); + CreateTeleporter( 939, 494, -40, 84, 749, -23, map, true ); + CreateTeleporter( 940, 494, -40, 85, 749, -23, map, true ); + CreateTeleporter( 941, 494, -40, 86, 749, -23, map, true ); + + // Lizard Passage + + DestroyTeleporter( 313, 1330, -39, map ); + DestroyTeleporter( 314, 1330, -37, map ); + DestroyTeleporter( 315, 1330, -35, map ); + CreateTeleporter( 313, 1329, -40, 327, 1593, -13, map, true ); + CreateTeleporter( 314, 1329, -38, 328, 1593, -13, map, true ); + CreateTeleporter( 315, 1329, -36, 329, 1593, -13, map, true ); + CreateTeleporter( 330, 1593, -13, 315, 1329, -35, map, false ); + + DestroyTeleporter( 225, 1335, -20, map ); + DestroyTeleporter( 226, 1335, -20, map ); + DestroyTeleporter( 227, 1335, -19, map ); + CreateTeleporter( 265, 1587, -28, 225, 1334, -20, map, true ); + CreateTeleporter( 266, 1587, -28, 226, 1334, -20, map, true ); + CreateTeleporter( 267, 1587, -28, 227, 1334, -20, map, true ); + + // Central Ilshenar + /*CreateTeleporter( 1139, 593, -80, 1237, 582, -19, map, true ); + CreateTeleporter( 1140, 593, -80, 1237, 583, -19, map, true ); + CreateTeleporter( 1141, 593, -80, 1237, 584, -19, map, true ); + CreateTeleporter( 1142, 593, -80, 1237, 585, -19, map, true ); + CreateTeleporter( 912, 451, -80, 708, 667, -39, map, true ); + CreateTeleporter( 912, 452, -80, 709, 667, -39, map, true ); + CreateTeleporter( 912, 453, -80, 710, 667, -39, map, true ); + CreateTeleporter( 711, 667, -39, 912, 453, -80, map, false );*/ + + // Update: remove old teleporters.. + DestroyTeleporter( 1139, 593, -80, map ); + DestroyTeleporter( 1140, 593, -80, map ); + DestroyTeleporter( 1141, 593, -80, map ); + DestroyTeleporter( 1142, 593, -80, map ); + DestroyTeleporter( 1237, 582, -19, map ); + DestroyTeleporter( 1237, 583, -19, map ); + DestroyTeleporter( 1237, 584, -19, map ); + DestroyTeleporter( 1237, 585, -19, map ); + + DestroyTeleporter( 912, 451, -80, map ); + DestroyTeleporter( 912, 452, -80, map ); + DestroyTeleporter( 912, 453, -80, map ); + DestroyTeleporter( 708, 667, -39, map ); + DestroyTeleporter( 709, 667, -39, map ); + DestroyTeleporter( 710, 667, -39, map ); + DestroyTeleporter( 711, 667, -39, map ); + + // Update: add new ones... + CreateTeleporter( 1139, 592, -80, 1238, 583, -19, map, false ); + CreateTeleporter( 1140, 592, -80, 1238, 584, -19, map, false ); + CreateTeleporter( 1141, 592, -80, 1238, 585, -19, map, false ); + CreateTeleporter( 1142, 592, -80, 1238, 585, -19, map, false ); + CreateTeleporter( 1237, 583, -19, 1139, 593, -80, map, false ); + CreateTeleporter( 1237, 584, -19, 1140, 593, -80, map, false ); + CreateTeleporter( 1237, 585, -19, 1141, 593, -80, map, false ); + + CreateTeleporter( 709, 667, -39, 912, 451, -80, map, false ); + CreateTeleporter( 710, 667, -39, 912, 452, -80, map, false ); + CreateTeleporter( 711, 667, -39, 912, 453, -80, map, false ); + + CreateTeleporter( 911, 451, -80, 709, 668, -39, map, false ); + CreateTeleporter( 911, 452, -80, 710, 668, -39, map, false ); + CreateTeleporter( 911, 453, -80, 711, 668, -38, map, false ); + + // Exodus Dungeon + CreateTeleporter( 827, 777, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 827, 778, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 827, 779, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 828, 777, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 828, 778, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 828, 779, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 829, 777, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 829, 778, -80, 1975, 114, -28, map, false ); + CreateTeleporter( 829, 779, -80, 1975, 114, -28, map, false ); + + CreateTeleporter( 1978, 114, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1978, 115, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1978, 116, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1978, 117, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1979, 114, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1979, 115, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1979, 116, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1979, 117, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1980, 114, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1980, 115, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1980, 116, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1980, 117, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1981, 114, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1981, 115, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1981, 116, -28, 835, 778, -80, map, false ); + CreateTeleporter( 1981, 117, -28, 835, 778, -80, map, false ); + } + + public void CreateTeleportersMap3( Map map ) + { + // CreateTeleporter( 408, 254, 2, 428, 319, 2, map, false ); // for doom quest; use blockers to avoid players teleporting into the ship! + // CreateTeleporter( 428, 321, 2, 422, 328, -1, map, false ); // for doom quest; use blockers to avoid players teleporting into the ship! + + // Doom Dungeon + CreateTeleporter( 2317, 1269, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2317, 1268, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2317, 1267, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2317, 1266, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2316, 1269, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2315, 1269, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2315, 1268, -110, 381, 132, 33, map, false ); + CreateTeleporter( 2315, 1267, -109, 381, 132, 33, map, false ); + CreateTeleporter( 2316, 1267, -110, 381, 132, 33, map, false ); + CreateTeleporter( 496, 49, 6, 2350, 1270, -85, map, false ); + DestroyTeleporter( 433, 326, 4, map ); + DestroyTeleporter( 365, 15, -1, map ); + + //Yomotsu Mines Exit + CreateTeleporter( 3, 128, -1, 259, 785, 64, map, Map.Tokuno, false ); + CreateTeleporter( 4, 128, -1, 259, 785, 64, map, Map.Tokuno, false ); + CreateTeleporter( 5, 128, -1, 259, 785, 64, map, Map.Tokuno, false ); + CreateTeleporter( 6, 128, -1, 259, 785, 64, map, Map.Tokuno, false ); + CreateTeleporter( 7, 128, -1, 259, 785, 64, map, Map.Tokuno, false ); + CreateTeleporter( 8, 128, -1, 259, 785, 64, map, Map.Tokuno, false ); + + //Fan Dancer Exit + CreateTeleporter( 64, 336, 11, 983, 195, 24, map, Map.Tokuno, false ); + CreateTeleporter( 64, 337, 11, 983, 195, 24, map, Map.Tokuno, false ); + CreateTeleporter( 64, 338, 11, 983, 195, 24, map, Map.Tokuno, false ); + CreateTeleporter( 64, 339, 11, 983, 195, 24, map, Map.Tokuno, false ); + + //Fan Dancer Levels + CreateTeleporter( 66, 351, -7, 63, 524, -1, map, false ); + CreateTeleporter( 66, 352, -7, 63, 524, -1, map, false ); + CreateTeleporter( 66, 353, -7, 63, 524, -1, map, false ); + CreateTeleporter( 66, 354, -7, 63, 524, -1, map, false ); + + CreateTeleporter( 61, 523, 6, 63, 352, -3, map, false ); + CreateTeleporter( 61, 524, 6, 63, 352, -3, map, false ); + CreateTeleporter( 61, 525, 6, 63, 352, -3, map, false ); + CreateTeleporter( 61, 526, 6, 63, 352, -3, map, false ); + + CreateTeleporter( 103, 555, -6, 76, 691, -1, map, false ); + CreateTeleporter( 103, 556, -6, 76, 691, -1, map, false ); + CreateTeleporter( 103, 557, -6, 76, 691, -1, map, false ); + CreateTeleporter( 103, 558, -6, 76, 691, -1, map, false ); + + CreateTeleporter( 73, 688, 6, 100, 556, -2, map, false ); + CreateTeleporter( 73, 689, 6, 100, 556, -2, map, false ); + CreateTeleporter( 73, 690, 6, 100, 556, -2, map, false ); + CreateTeleporter( 73, 691, 6, 100, 556, -2, map, false ); + CreateTeleporter( 73, 692, 6, 100, 556, -2, map, false ); + CreateTeleporter( 73, 693, 6, 100, 556, -2, map, false ); + CreateTeleporter( 73, 694, 6, 100, 556, -2, map, false ); + + //Ninja cave + CreateTeleporter( 384, 810, -1, 403, 1167, 0, map, false ); + + CreateTeleporter( 403, 1169, 0, 385, 811, -1, map, false ); + CreateTeleporter( 404, 1169, 0, 385, 808, -1, map, false ); + CreateTeleporter( 405, 1169, 0, 385, 808, -1, map, false ); + + // Dungeon Labyrinth + CreateTeleporter( 328, 1972, 5, 1731, 978, -80, map, false ); // Door exit + CreateTeleporter( 328, 1973, 5, 1731, 978, -80, map, false ); + CreateTeleporter( 328, 1974, 5, 1731, 978, -80, map, false ); + CreateTeleporter( 328, 1975, 5, 1731, 978, -80, map, false ); + + // Dungeon Bedlam + CreateTeleporter( 84, 1673, -2, 156, 1613, 0, map, false ); + CreateTeleporter( 156, 1609, 17, 87, 1673, 0, map, false ); + CreateTeleporter( 157, 1609, 17, 87, 1673, 0, map, false ); + } + + public void CreateTeleportersMap4( Map map ) + { + //Yomotso Mines Entrance + CreateTeleporter( 257, 783, 63, 5, 128, -1, map, Map.Malas, false ); + CreateTeleporter( 258, 783, 63, 5, 128, -1, map, Map.Malas, false ); + CreateTeleporter( 259, 783, 63, 5, 128, -1, map, Map.Malas, false ); + CreateTeleporter( 260, 783, 63, 5, 128, -1, map, Map.Malas, false ); + + //Fan dancer Entrance + CreateTeleporter( 988, 194, 15, 67, 337, -1, map, Map.Malas, false ); + CreateTeleporter( 988, 195, 15, 67, 337, -1, map, Map.Malas, false ); + CreateTeleporter( 987, 196, 15, 67, 337, -1, map, Map.Malas, false ); + CreateTeleporter( 988, 197, 18, 67, 337, -1, map, Map.Malas, false ); + + } + public void CreateTeleportersTrammel( Map map ) + { + // Haven + CreateTeleporter( 3632, 2566, 0, 3632, 2566, 20, map, true ); + } + + public void CreateTeleportersFelucca( Map map ) + { + // Star room + CreateTeleporter( 5140, 1773, 0, 5171, 1586, 0, map, false ); + } + + public int CreateTeleporters() + { + CreateTeleportersMap( Map.Felucca ); + CreateTeleportersMap( Map.Trammel ); + CreateTeleportersTrammel( Map.Trammel ); + CreateTeleportersFelucca( Map.Felucca ); + CreateTeleportersMap2( Map.Ilshenar ); + CreateTeleportersMap3( Map.Malas ); + CreateTeleportersMap4( Map.Tokuno ); + return m_Count; + } + } + } +} diff --git a/Scripts/Commands/Generic/Commands/BaseCommand.cs b/Scripts/Commands/Generic/Commands/BaseCommand.cs new file mode 100644 index 000000000..07d2022c5 --- /dev/null +++ b/Scripts/Commands/Generic/Commands/BaseCommand.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; + +namespace Server.Commands.Generic +{ + public enum ObjectTypes + { + Both, + Items, + Mobiles, + All + } + + public abstract class BaseCommand + { + private string[] m_Commands; + private AccessLevel m_AccessLevel; + private CommandSupport m_Implementors; + private ObjectTypes m_ObjectTypes; + private bool m_ListOptimized; + private string m_Usage; + private string m_Description; + + public bool ListOptimized + { + get{ return m_ListOptimized; } + set{ m_ListOptimized = value; } + } + + public string[] Commands + { + get{ return m_Commands; } + set{ m_Commands = value; } + } + + public string Usage + { + get{ return m_Usage; } + set{ m_Usage = value; } + } + + public string Description + { + get{ return m_Description; } + set{ m_Description = value; } + } + + public AccessLevel AccessLevel + { + get{ return m_AccessLevel; } + set{ m_AccessLevel = value; } + } + + public ObjectTypes ObjectTypes + { + get{ return m_ObjectTypes; } + set{ m_ObjectTypes = value; } + } + + public CommandSupport Supports + { + get{ return m_Implementors; } + set{ m_Implementors = value; } + } + + public BaseCommand() + { + m_Responses = new ArrayList(); + m_Failures = new ArrayList(); + } + + public static bool IsAccessible( Mobile from, object obj ) + { + if ( from.AccessLevel >= AccessLevel.Administrator || obj == null ) + return true; + + Mobile mob; + + if ( obj is Mobile ) + mob = (Mobile)obj; + else if ( obj is Item ) + mob = ((Item)obj).RootParent as Mobile; + else + mob = null; + + if ( mob == null || mob == from || from.AccessLevel > mob.AccessLevel ) + return true; + + return false; + } + + public virtual void ExecuteList( CommandEventArgs e, ArrayList list ) + { + for ( int i = 0; i < list.Count; ++i ) + Execute( e, list[i] ); + } + + public virtual void Execute( CommandEventArgs e, object obj ) + { + } + + public virtual bool ValidateArgs( BaseCommandImplementor impl, CommandEventArgs e ) + { + return true; + } + + private ArrayList m_Responses, m_Failures; + + private class MessageEntry + { + public string m_Message; + public int m_Count; + + public MessageEntry( string message ) + { + m_Message = message; + m_Count = 1; + } + + public override string ToString() + { + if ( m_Count > 1 ) + return String.Format( "{0} ({1})", m_Message, m_Count ); + + return m_Message; + } + } + + public void AddResponse( string message ) + { + for ( int i = 0; i < m_Responses.Count; ++i ) + { + MessageEntry entry = (MessageEntry)m_Responses[i]; + + if ( entry.m_Message == message ) + { + ++entry.m_Count; + return; + } + } + + if ( m_Responses.Count == 10 ) + return; + + m_Responses.Add( new MessageEntry( message ) ); + } + + public void AddResponse( Gump gump ) + { + m_Responses.Add( gump ); + } + + public void LogFailure( string message ) + { + for ( int i = 0; i < m_Failures.Count; ++i ) + { + MessageEntry entry = (MessageEntry)m_Failures[i]; + + if ( entry.m_Message == message ) + { + ++entry.m_Count; + return; + } + } + + if ( m_Failures.Count == 10 ) + return; + + m_Failures.Add( new MessageEntry( message ) ); + } + + public void Flush( Mobile from, bool flushToLog ) + { + if ( m_Responses.Count > 0 ) + { + for ( int i = 0; i < m_Responses.Count; ++i ) + { + object obj = m_Responses[i]; + + if ( obj is MessageEntry ) + { + from.SendMessage( ((MessageEntry)obj).ToString() ); + + if ( flushToLog ) + CommandLogging.WriteLine( from, ((MessageEntry)obj).ToString() ); + } + else if ( obj is Gump ) + { + from.SendGump( (Gump) obj ); + } + } + } + else + { + for ( int i = 0; i < m_Failures.Count; ++i ) + from.SendMessage( ((MessageEntry)m_Failures[i]).ToString() ); + } + + m_Responses.Clear(); + m_Failures.Clear(); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Commands/Commands.cs b/Scripts/Commands/Generic/Commands/Commands.cs new file mode 100644 index 000000000..1466b450e --- /dev/null +++ b/Scripts/Commands/Generic/Commands/Commands.cs @@ -0,0 +1,1039 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Accounting; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Multis; +using Server.Network; +using Server.Spells; + +namespace Server.Commands.Generic +{ + public class TargetCommands + { + public static void Initialize() + { + Register( new KillCommand( true ) ); + Register( new KillCommand( false ) ); + Register( new HideCommand( true ) ); + Register( new HideCommand( false ) ); + Register( new KickCommand( true ) ); + Register( new KickCommand( false ) ); + Register( new FirewallCommand() ); + Register( new TeleCommand() ); + Register( new SetCommand() ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "Immortal", "blessed", "true", ObjectTypes.Mobiles ) ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "Invul", "blessed", "true", ObjectTypes.Mobiles ) ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "Mortal", "blessed", "false", ObjectTypes.Mobiles ) ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "NoInvul", "blessed", "false", ObjectTypes.Mobiles ) ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "Squelch", "squelched", "true", ObjectTypes.Mobiles ) ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "Unsquelch", "squelched", "false", ObjectTypes.Mobiles ) ); + + Register( new AliasedSetCommand( AccessLevel.GameMaster, "ShaveHair", "HairItemID", "0", ObjectTypes.Mobiles ) ); + Register( new AliasedSetCommand( AccessLevel.GameMaster, "ShaveBeard", "FacialHairItemID", "0", ObjectTypes.Mobiles ) ); + + Register( new GetCommand() ); + Register( new GetTypeCommand() ); + Register( new DeleteCommand() ); + Register( new RestockCommand() ); + Register( new DismountCommand() ); + Register( new AddCommand() ); + Register( new AddToPackCommand() ); + Register( new TellCommand() ); + Register( new PrivSoundCommand() ); + Register( new IncreaseCommand() ); + Register( new OpenBrowserCommand() ); + Register( new CountCommand() ); + Register( new InterfaceCommand() ); + Register( new RefreshHouseCommand() ); + Register( new ConditionCommand() ); + Register( new Factions.FactionKickCommand( Factions.FactionKickType.Kick ) ); + Register( new Factions.FactionKickCommand( Factions.FactionKickType.Ban ) ); + Register( new Factions.FactionKickCommand( Factions.FactionKickType.Unban ) ); + Register( new BringToPackCommand() ); + } + + private static List m_AllCommands = new List(); + + public static List AllCommands{ get{ return m_AllCommands; } } + + public static void Register( BaseCommand command ) + { + m_AllCommands.Add( command ); + + ArrayList impls = BaseCommandImplementor.Implementors; + + for ( int i = 0; i < impls.Count; ++i ) + { + BaseCommandImplementor impl = (BaseCommandImplementor)impls[i]; + + if ( (command.Supports & impl.SupportRequirement) != 0 ) + impl.Register( command ); + } + } + } + + public class ConditionCommand : BaseCommand + { + public ConditionCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.Simple | CommandSupport.Complex | CommandSupport.Self; + Commands = new string[]{ "Condition" }; + ObjectTypes = ObjectTypes.All; + Usage = "Condition "; + Description = "Checks that the given condition matches a targeted object."; + ListOptimized = true; + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + try + { + string[] args = e.Arguments; + ObjectConditional condition = ObjectConditional.Parse( e.Mobile, ref args ); + + for ( int i = 0; i < list.Count; ++i ) + { + if ( condition.CheckCondition( list[i] ) ) + AddResponse( "True - that object matches the condition." ); + else + AddResponse( "False - that object does not match the condition." ); + } + } + catch ( Exception ex ) + { + e.Mobile.SendMessage( ex.Message ); + } + } + } + + public class BringToPackCommand : BaseCommand + { + public BringToPackCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllItems; + Commands = new string[]{ "BringToPack" }; + ObjectTypes = ObjectTypes.Items; + Usage = "BringToPack"; + Description = "Brings a targeted item to your backpack."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Item item = obj as Item; + + if ( item != null ) + { + if ( e.Mobile.PlaceInBackpack( item ) ) + AddResponse( "The item has been placed in your backpack." ); + else + AddResponse( "Your backpack could not hold the item." ); + } + } + } + + public class RefreshHouseCommand : BaseCommand + { + public RefreshHouseCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.Simple; + Commands = new string[]{ "RefreshHouse" }; + ObjectTypes = ObjectTypes.Items; + Usage = "RefreshHouse"; + Description = "Refreshes a targeted house sign."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( obj is HouseSign ) + { + BaseHouse house = ((HouseSign)obj).Owner; + + if ( house == null ) + { + LogFailure( "That sign has no house attached." ); + } + else + { + house.RefreshDecay(); + AddResponse( "The house has been refreshed." ); + } + } + else + { + LogFailure( "That is not a house sign." ); + } + } + } + + public class CountCommand : BaseCommand + { + public CountCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.Complex; + Commands = new string[]{ "Count" }; + ObjectTypes = ObjectTypes.All; + Usage = "Count"; + Description = "Counts the number of objects that a command modifier would use. Generally used with condition arguments."; + ListOptimized = true; + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + if ( list.Count == 1 ) + AddResponse( "There is one matching object." ); + else + AddResponse( String.Format( "There are {0} matching objects.", list.Count ) ); + } + } + + public class OpenBrowserCommand : BaseCommand + { + public OpenBrowserCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ "OpenBrowser", "OB" }; + ObjectTypes = ObjectTypes.Mobiles; + Usage = "OpenBrowser "; + Description = "Opens the web browser of a targeted player to a specified url."; + } + + public static void OpenBrowser_Callback( Mobile from, bool okay, object state ) + { + object[] states = (object[])state; + Mobile gm = (Mobile)states[0]; + string url = (string)states[1]; + + if ( okay ) + { + gm.SendMessage( "{0} : has opened their web browser to : {1}", from.Name, url ); + from.LaunchBrowser( url ); + } + else + { + from.SendMessage( "You have chosen not to open your web browser." ); + gm.SendMessage( "{0} : has chosen not to open their web browser to : {1}", from.Name, url ); + } + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( e.Length == 1 ) + { + Mobile mob = (Mobile)obj; + Mobile from = e.Mobile; + + if ( mob.Player ) + { + NetState ns = mob.NetState; + + if ( ns == null ) + { + LogFailure( "That player is not online." ); + } + else + { + string url = e.GetString( 0 ); + + CommandLogging.WriteLine( from, "{0} {1} requesting to open web browser of {2} to {3}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ), url ); + AddResponse( "Awaiting user confirmation..." ); + mob.SendGump( new WarningGump( 1060637, 30720, String.Format( "A game master is requesting to open your web browser to the following URL:
{0}", url ), 0xFFC000, 320, 240, new WarningGumpCallback( OpenBrowser_Callback ), new object[]{ from, url } ) ); + } + } + else + { + LogFailure( "That is not a player." ); + } + } + else + { + LogFailure( "Format: OpenBrowser " ); + } + } + } + + public class IncreaseCommand : BaseCommand + { + public IncreaseCommand() + { + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.All; + Commands = new string[]{ "Increase", "Inc" }; + ObjectTypes = ObjectTypes.Both; + Usage = "Increase { ...}"; + Description = "Increases the value of a specified property by the specified offset."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( obj is BaseMulti ) + { + LogFailure( "This command does not work on multis." ); + } + else if ( e.Length >= 2 ) + { + string result = Properties.IncreaseValue( e.Mobile, obj, e.Arguments ); + + if ( result == "The property has been increased." || result == "The properties have been increased." || result == "The property has been decreased." || result == "The properties have been decreased." || result == "The properties have been changed." ) + AddResponse( result ); + else + LogFailure( result ); + } + else + { + LogFailure( "Format: Increase { ...}" ); + } + } + } + + public class PrivSoundCommand : BaseCommand + { + public PrivSoundCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ "PrivSound" }; + ObjectTypes = ObjectTypes.Mobiles; + Usage = "PrivSound "; + Description = "Plays a sound to a given target."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile from = e.Mobile; + + if ( e.Length == 1 ) + { + int index = e.GetInt32( 0 ); + Mobile mob = (Mobile)obj; + + CommandLogging.WriteLine( from, "{0} {1} playing sound {2} for {3}", from.AccessLevel, CommandLogging.Format( from ), index, CommandLogging.Format( mob ) ); + mob.Send( new PlaySound( index, mob.Location ) ); + } + else + { + from.SendMessage( "Format: PrivSound " ); + } + } + } + + public class TellCommand : BaseCommand + { + public TellCommand() + { + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ "Tell" }; + ObjectTypes = ObjectTypes.Mobiles; + Usage = "Tell \"text\""; + Description = "Sends a system message to a targeted player."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile mob = (Mobile)obj; + Mobile from = e.Mobile; + + CommandLogging.WriteLine( from, "{0} {1} telling {2} \"{3}\"", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ), e.ArgString ); + + mob.SendMessage( e.ArgString ); + } + } + + public class AddToPackCommand : BaseCommand + { + public AddToPackCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.All; + Commands = new string[]{ "AddToPack", "AddToCont" }; + ObjectTypes = ObjectTypes.Both; + ListOptimized = true; + Usage = "AddToPack [params] [set { ...}]"; + Description = "Adds an item by name to the backpack of a targeted player or npc, or a targeted container. Optional constructor parameters. Optional set property list."; + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + ArrayList packs = new ArrayList( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + { + object obj = list[i]; + Container cont = null; + + if ( obj is Mobile ) + cont = ((Mobile)obj).Backpack; + else if ( obj is Container ) + cont = (Container)obj; + + if ( cont != null ) + packs.Add( cont ); + else + LogFailure( "That is not a container." ); + } + + Add.Invoke( e.Mobile, e.Mobile.Location, e.Mobile.Location, e.Arguments, packs ); + } + } + + public class AddCommand : BaseCommand + { + public AddCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.Simple | CommandSupport.Self; + Commands = new string[]{ "Add" }; + ObjectTypes = ObjectTypes.All; + Usage = "Add [ [params] [set { ...}]]"; + Description = "Adds an item or npc by name to a targeted location. Optional constructor parameters. Optional set property list. If no arguments are specified, this brings up a categorized add menu."; + } + + public override bool ValidateArgs( BaseCommandImplementor impl, CommandEventArgs e ) + { + if ( e.Length >= 1 ) + { + Type t = ScriptCompiler.FindTypeByName( e.GetString( 0 ) ); + + if ( t == null ) + { + e.Mobile.SendMessage( "No type with that name was found." ); + + string match = e.GetString( 0 ).Trim(); + + if ( match.Length < 3 ) + { + e.Mobile.SendMessage( "Invalid search string." ); + e.Mobile.SendGump( new AddGump( e.Mobile, match, 0, Type.EmptyTypes, false ) ); + } + else + { + e.Mobile.SendGump( new AddGump( e.Mobile, match, 0, AddGump.Match( match ).ToArray(), true ) ); + } + } + else + { + return true; + } + } + else + { + e.Mobile.SendGump( new CategorizedAddGump( e.Mobile ) ); + } + + return false; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + IPoint3D p = obj as IPoint3D; + + if ( p == null ) + return; + + if ( p is Item ) + p = ((Item)p).GetWorldTop(); + else if ( p is Mobile ) + p = ((Mobile)p).Location; + + Add.Invoke( e.Mobile, new Point3D( p ), new Point3D( p ), e.Arguments ); + } + } + + public class TeleCommand : BaseCommand + { + public TeleCommand() + { + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.Simple; + Commands = new string[]{ "Teleport", "Tele" }; + ObjectTypes = ObjectTypes.All; + Usage = "Teleport"; + Description = "Teleports your character to a targeted location."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + IPoint3D p = obj as IPoint3D; + + if ( p == null ) + return; + + Mobile from = e.Mobile; + + SpellHelper.GetSurfaceTop( ref p ); + + //CommandLogging.WriteLine( from, "{0} {1} teleporting to {2}", from.AccessLevel, CommandLogging.Format( from ), new Point3D( p ) ); + + Point3D fromLoc = from.Location; + Point3D toLoc = new Point3D( p ); + + from.Location = toLoc; + from.ProcessDelta(); + + if ( !from.Hidden ) + { + Effects.SendLocationParticles( EffectItem.Create( fromLoc, from.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( toLoc, from.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + + from.PlaySound( 0x1FE ); + } + } + } + + public class DismountCommand : BaseCommand + { + public DismountCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ "Dismount" }; + ObjectTypes = ObjectTypes.Mobiles; + Usage = "Dismount"; + Description = "Forcefully dismounts a given target."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile from = e.Mobile; + Mobile mob = (Mobile)obj; + + CommandLogging.WriteLine( from, "{0} {1} dismounting {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ) ); + + bool takenAction = false; + + for ( int i = 0; i < mob.Items.Count; ++i ) + { + Item item = mob.Items[i]; + + if ( item is IMountItem ) + { + IMount mount = ((IMountItem)item).Mount; + + if ( mount != null ) + { + mount.Rider = null; + takenAction = true; + } + + if ( mob.Items.IndexOf( item ) == -1 ) + --i; + } + } + + for ( int i = 0; i < mob.Items.Count; ++i ) + { + Item item = mob.Items[i]; + + if ( item.Layer == Layer.Mount ) + { + takenAction = true; + item.Delete(); + --i; + } + } + + if ( takenAction ) + AddResponse( "They have been dismounted." ); + else + LogFailure( "They were not mounted." ); + } + } + + public class RestockCommand : BaseCommand + { + public RestockCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllNPCs; + Commands = new string[]{ "Restock" }; + ObjectTypes = ObjectTypes.Mobiles; + Usage = "Restock"; + Description = "Manually restocks a targeted vendor, refreshing the quantity of every item the vendor sells to the maximum. This also invokes the maximum quantity adjustment algorithms."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( obj is BaseVendor ) + { + CommandLogging.WriteLine( e.Mobile, "{0} {1} restocking {2}", e.Mobile.AccessLevel, CommandLogging.Format( e.Mobile ), CommandLogging.Format( obj ) ); + + ((BaseVendor)obj).Restock(); + AddResponse( "The vendor has been restocked." ); + } + else + { + AddResponse( "That is not a vendor." ); + } + } + } + + public class GetTypeCommand : BaseCommand + { + public GetTypeCommand() + { + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.All; + Commands = new string[]{ "GetType" }; + ObjectTypes = ObjectTypes.All; + Usage = "GetType"; + Description = "Gets the type name of a targeted object."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( obj == null ) + { + AddResponse( "The object is null." ); + } + else + { + Type type = obj.GetType(); + + if ( type.DeclaringType == null ) + AddResponse( String.Format( "The type of that object is {0}.", type.Name ) ); + else + AddResponse( String.Format( "The type of that object is {0}.", type.FullName ) ); + } + } + } + + public class GetCommand : BaseCommand + { + public GetCommand() + { + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.All; + Commands = new string[]{ "Get" }; + ObjectTypes = ObjectTypes.All; + Usage = "Get "; + Description = "Gets one or more property values by name of a targeted object."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( e.Length >= 1 ) + { + for ( int i = 0; i < e.Length; ++i ) + { + string result = Properties.GetValue( e.Mobile, obj, e.GetString( i ) ); + + if ( result == "Property not found." || result == "Property is write only." || result.StartsWith( "Getting this property" ) ) + LogFailure( result ); + else + AddResponse( result ); + } + } + else + { + LogFailure( "Format: Get " ); + } + } + } + + public class AliasedSetCommand : BaseCommand + { + private string m_Name; + private string m_Value; + + public AliasedSetCommand( AccessLevel level, string command, string name, string value, ObjectTypes objects ) + { + m_Name = name; + m_Value = value; + + AccessLevel = level; + + if ( objects == ObjectTypes.Items ) + Supports = CommandSupport.AllItems; + else if ( objects == ObjectTypes.Mobiles ) + Supports = CommandSupport.AllMobiles; + else + Supports = CommandSupport.All; + + Commands = new string[]{ command }; + ObjectTypes = objects; + Usage = command; + Description = String.Format( "Sets the {0} property to {1}.", name, value ); + } + + public override void Execute( CommandEventArgs e, object obj ) + { + string result = Properties.SetValue( e.Mobile, obj, m_Name, m_Value ); + + if ( result == "Property has been set." ) + AddResponse( result ); + else + LogFailure( result ); + } + } + + public class SetCommand : BaseCommand + { + public SetCommand() + { + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.All; + Commands = new string[]{ "Set" }; + ObjectTypes = ObjectTypes.Both; + Usage = "Set [...]"; + Description = "Sets one or more property values by name of a targeted object."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( e.Length >= 2 ) + { + for ( int i = 0; (i+1) < e.Length; i += 2 ) + { + string result = Properties.SetValue( e.Mobile, obj, e.GetString( i ), e.GetString( i+1 ) ); + + if ( result == "Property has been set." ) + AddResponse( result ); + else + LogFailure( result ); + } + } + else + { + LogFailure( "Format: Set " ); + } + } + } + + public class DeleteCommand : BaseCommand + { + public DeleteCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllNPCs | CommandSupport.AllItems; + Commands = new string[]{ "Delete", "Remove" }; + ObjectTypes = ObjectTypes.Both; + Usage = "Delete"; + Description = "Deletes a targeted item or mobile. Does not delete players."; + } + + private void OnConfirmCallback( Mobile from, bool okay, object state ) + { + object[] states = (object[])state; + CommandEventArgs e = (CommandEventArgs)states[0]; + ArrayList list = (ArrayList)states[1]; + + bool flushToLog = false; + + if ( okay ) + { + AddResponse( "Delete command confirmed." ); + + if ( list.Count > 20 ) + { + CommandLogging.Enabled = false; + NetState.Pause(); + } + + base.ExecuteList( e, list ); + + if ( list.Count > 20 ) + { + NetState.Resume(); + flushToLog = true; + CommandLogging.Enabled = true; + } + } + else + { + AddResponse( "Delete command aborted." ); + } + + Flush( from, flushToLog ); + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + if ( list.Count > 1 ) + { + e.Mobile.SendGump( new WarningGump( 1060637, 30720, String.Format( "You are about to delete {0} objects. This cannot be undone without a full server revert.

Continue?", list.Count ), 0xFFC000, 420, 280, new WarningGumpCallback( OnConfirmCallback ), new object[]{ e, list } ) ); + AddResponse( "Awaiting confirmation..." ); + } + else + { + base.ExecuteList( e, list ); + } + } + + public override void Execute( CommandEventArgs e, object obj ) + { + if ( obj is Item ) + { + CommandLogging.WriteLine( e.Mobile, "{0} {1} deleting {2}", e.Mobile.AccessLevel, CommandLogging.Format( e.Mobile ), CommandLogging.Format( obj ) ); + ((Item)obj).Delete(); + AddResponse( "The item has been deleted." ); + } + else if ( obj is Mobile && !((Mobile)obj).Player ) + { + CommandLogging.WriteLine( e.Mobile, "{0} {1} deleting {2}", e.Mobile.AccessLevel, CommandLogging.Format( e.Mobile ), CommandLogging.Format( obj ) ); + ((Mobile)obj).Delete(); + AddResponse( "The mobile has been deleted." ); + } + else + { + LogFailure( "That cannot be deleted." ); + } + } + } + + public class KillCommand : BaseCommand + { + private bool m_Value; + + public KillCommand( bool value ) + { + m_Value = value; + + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllMobiles; + Commands = value ? new string[]{ "Kill" } : new string[]{ "Resurrect", "Res" }; + ObjectTypes = ObjectTypes.Mobiles; + + if ( value ) + { + Usage = "Kill"; + Description = "Kills a targeted player or npc."; + } + else + { + Usage = "Resurrect"; + Description = "Resurrects a targeted ghost."; + } + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile mob = (Mobile)obj; + Mobile from = e.Mobile; + + if ( m_Value ) + { + if ( !mob.Alive ) + { + LogFailure( "They are already dead." ); + } + else if ( !mob.CanBeDamaged() ) + { + LogFailure( "They cannot be harmed." ); + } + else + { + CommandLogging.WriteLine( from, "{0} {1} killing {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ) ); + mob.Kill(); + + AddResponse( "They have been killed." ); + } + } + else + { + if ( mob.IsDeadBondedPet ) + { + BaseCreature bc = mob as BaseCreature; + + if ( bc != null ) + { + CommandLogging.WriteLine( from, "{0} {1} resurrecting {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ) ); + + bc.PlaySound( 0x214 ); + bc.FixedEffect( 0x376A, 10, 16 ); + + bc.ResurrectPet(); + + AddResponse( "It has been resurrected." ); + } + } + else if ( !mob.Alive ) + { + CommandLogging.WriteLine( from, "{0} {1} resurrecting {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( mob ) ); + + mob.PlaySound( 0x214 ); + mob.FixedEffect( 0x376A, 10, 16 ); + + mob.Resurrect(); + + AddResponse( "They have been resurrected." ); + } + else + { + LogFailure( "They are not dead." ); + } + } + } + } + + public class HideCommand : BaseCommand + { + private bool m_Value; + + public HideCommand( bool value ) + { + m_Value = value; + + AccessLevel = AccessLevel.Counselor; + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ value ? "Hide" : "Unhide" }; + ObjectTypes = ObjectTypes.Mobiles; + + if ( value ) + { + Usage = "Hide"; + Description = "Makes a targeted mobile disappear in a puff of smoke."; + } + else + { + Usage = "Unhide"; + Description = "Makes a targeted mobile appear in a puff of smoke."; + } + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile m = (Mobile)obj; + + CommandLogging.WriteLine( e.Mobile, "{0} {1} {2} {3}", e.Mobile.AccessLevel, CommandLogging.Format( e.Mobile ), m_Value ? "hiding" : "unhiding", CommandLogging.Format( m ) ); + + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y, m.Z + 4 ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y, m.Z ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y, m.Z - 4 ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X, m.Y + 1, m.Z + 4 ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X, m.Y + 1, m.Z ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X, m.Y + 1, m.Z - 4 ), m.Map, 0x3728, 13 ); + + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y + 1, m.Z + 11 ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y + 1, m.Z + 7 ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y + 1, m.Z + 3 ), m.Map, 0x3728, 13 ); + Effects.SendLocationEffect( new Point3D( m.X + 1, m.Y + 1, m.Z - 1 ), m.Map, 0x3728, 13 ); + + m.PlaySound( 0x228 ); + m.Hidden = m_Value; + + if ( m_Value ) + AddResponse( "They have been hidden." ); + else + AddResponse( "They have been revealed." ); + } + } + + public class FirewallCommand : BaseCommand + { + public FirewallCommand() + { + AccessLevel = AccessLevel.Administrator; + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ "Firewall" }; + ObjectTypes = ObjectTypes.Mobiles; + Usage = "Firewall"; + Description = "Adds a targeted player to the firewall (list of blocked IP addresses). This command does not ban or kick."; + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile from = e.Mobile; + Mobile targ = (Mobile)obj; + NetState state = targ.NetState; + + if ( state != null ) + { + CommandLogging.WriteLine( from, "{0} {1} firewalling {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targ ) ); + + try + { + Firewall.Add( ((IPEndPoint)state.Socket.RemoteEndPoint).Address ); + AddResponse( "They have been firewalled." ); + } + catch ( Exception ex ) + { + LogFailure( ex.Message ); + } + } + else + { + LogFailure( "They are not online." ); + } + } + } + + public class KickCommand : BaseCommand + { + private bool m_Ban; + + public KickCommand( bool ban ) + { + m_Ban = ban; + + AccessLevel = ( ban ? AccessLevel.Administrator : AccessLevel.GameMaster ); + Supports = CommandSupport.AllMobiles; + Commands = new string[]{ ban ? "Ban" : "Kick" }; + ObjectTypes = ObjectTypes.Mobiles; + + if ( ban ) + { + Usage = "Ban"; + Description = "Bans the account of a targeted player."; + } + else + { + Usage = "Kick"; + Description = "Disconnects a targeted player."; + } + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile from = e.Mobile; + Mobile targ = (Mobile)obj; + + if ( from.AccessLevel > targ.AccessLevel ) + { + NetState fromState = from.NetState, targState = targ.NetState; + + if ( fromState != null && targState != null ) + { + Account fromAccount = fromState.Account as Account; + Account targAccount = targState.Account as Account; + + if ( fromAccount != null && targAccount != null ) + { + CommandLogging.WriteLine( from, "{0} {1} {2} {3}", from.AccessLevel, CommandLogging.Format( from ), m_Ban ? "banning" : "kicking", CommandLogging.Format( targ ) ); + + targ.Say( "I've been {0}!", m_Ban ? "banned" : "kicked" ); + + AddResponse( String.Format( "They have been {0}.", m_Ban ? "banned" : "kicked" ) ); + + targState.Dispose(); + + if ( m_Ban ) + { + targAccount.Banned = true; + targAccount.SetUnspecifiedBan( from ); + from.SendGump( new BanDurationGump( targAccount ) ); + } + } + } + else if ( targState == null ) + { + LogFailure( "They are not online." ); + } + } + else + { + LogFailure( "You do not have the required access level to do this." ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Commands/Interface.cs b/Scripts/Commands/Generic/Commands/Interface.cs new file mode 100644 index 000000000..d41964e77 --- /dev/null +++ b/Scripts/Commands/Generic/Commands/Interface.cs @@ -0,0 +1,570 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Targeting; +using Server.Targets; + +namespace Server.Commands.Generic +{ + public class InterfaceCommand : BaseCommand + { + public InterfaceCommand() + { + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.Complex | CommandSupport.Simple; + Commands = new string[]{ "Interface" }; + ObjectTypes = ObjectTypes.Both; + Usage = "Interface [view ]"; + Description = "Opens an interface to interact with matched objects. Generally used with condition arguments."; + ListOptimized = true; + } + + public override void ExecuteList( CommandEventArgs e, ArrayList list ) + { + if ( list.Count > 0 ) + { + List columns = new List(); + + columns.Add( "Object" ); + + if ( e.Length > 0 ) + { + int offset = 0; + + if ( Insensitive.Equals( e.GetString( 0 ), "view" ) ) + ++offset; + + while ( offset < e.Length ) + columns.Add( e.GetString( offset++ ) ); + } + + e.Mobile.SendGump( new InterfaceGump( e.Mobile, columns.ToArray(), list, 0, null ) ); + } + else + { + AddResponse( "No matching objects found." ); + } + } + } + + public class InterfaceGump : BaseGridGump + { + private Mobile m_From; + + private string[] m_Columns; + + private ArrayList m_List; + private int m_Page; + + private object m_Select; + + private const int EntriesPerPage = 15; + + public InterfaceGump( Mobile from, string[] columns, ArrayList list, int page, object select ) : base( 30, 30 ) + { + m_From = from; + + m_Columns = columns; + + m_List = list; + m_Page = page; + + m_Select = select; + + Render(); + } + + public void Render() + { + AddNewPage(); + + if ( m_Page > 0 ) + AddEntryButton( 20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight ); + else + AddEntryHeader( 20 ); + + AddEntryHtml( 40 + ( m_Columns.Length * 130 ) - 20 + ( ( m_Columns.Length - 2 ) * OffsetSize ), Center( String.Format( "Page {0} of {1}", m_Page+1, (m_List.Count + EntriesPerPage - 1) / EntriesPerPage ) ) ); + + if ( (m_Page + 1) * EntriesPerPage < m_List.Count ) + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight ); + else + AddEntryHeader( 20 ); + + if ( m_Columns.Length > 1 ) + { + AddNewLine(); + + for ( int i = 0; i < m_Columns.Length; ++i ) + { + if ( i > 0 && m_List.Count > 0 ) + { + object obj = m_List[0]; + + if ( obj != null ) + { + string failReason = null; + PropertyInfo[] chain = Properties.GetPropertyInfoChain( m_From, obj.GetType(), m_Columns[i], PropertyAccess.Read, ref failReason ); + + if ( chain != null && chain.Length > 0 ) + { + m_Columns[i] = ""; + + for ( int j = 0; j < chain.Length; ++j ) + { + if ( j > 0 ) + m_Columns[i] += '.'; + + m_Columns[i] += chain[j].Name; + } + } + } + } + + AddEntryHtml( 130 + ( i == 0 ? 40 : 0 ), m_Columns[i] ); + } + + AddEntryHeader( 20 ); + } + + for ( int i = m_Page * EntriesPerPage, line = 0; line < EntriesPerPage && i < m_List.Count; ++i, ++line ) + { + AddNewLine(); + + object obj = m_List[i]; + bool isDeleted = false; + + if ( obj is Item ) + { + Item item = (Item)obj; + + if ( !(isDeleted = item.Deleted) ) + AddEntryHtml( 40 + 130, item.GetType().Name ); + } + else if ( obj is Mobile ) + { + Mobile mob = (Mobile)obj; + + if ( !(isDeleted = mob.Deleted) ) + AddEntryHtml( 40 + 130, mob.Name ); + } + + if ( isDeleted ) + { + AddEntryHtml( 40 + 130, "(deleted)" ); + + for ( int j = 1; j < m_Columns.Length; ++j ) + AddEntryHtml( 130, "---" ); + + AddEntryHeader( 20 ); + } + else + { + for ( int j = 1; j < m_Columns.Length; ++j ) + { + object src = obj; + + string value; + string failReason = ""; + + PropertyInfo[] chain = Properties.GetPropertyInfoChain( m_From, src.GetType(), m_Columns[j], PropertyAccess.Read, ref failReason ); + + if ( chain == null || chain.Length == 0 ) + { + value = "---"; + } + else + { + PropertyInfo p = Properties.GetPropertyInfo( ref src, chain, ref failReason ); + + if ( p == null ) + value = "---"; + else + value = PropertiesGump.ValueToString( src, p ); + } + + AddEntryHtml( 130, value ); + } + + bool isSelected = ( m_Select != null && obj == m_Select ); + + AddEntryButton( 20, ( isSelected ? 9762 : ArrowRightID1 ), ( isSelected ? 9763 : ArrowRightID2 ), 3 + i, ArrowRightWidth, ArrowRightHeight ); + } + } + + FinishPage(); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 1: + { + if ( m_Page > 0 ) + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page - 1, m_Select ) ); + + break; + } + case 2: + { + if ( (m_Page + 1) * EntriesPerPage < m_List.Count ) + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page + 1, m_Select ) ); + + break; + } + default: + { + int v = info.ButtonID - 3; + + if ( v >= 0 && v < m_List.Count ) + { + object obj = m_List[v]; + + if ( !BaseCommand.IsAccessible( m_From, obj ) ) + { + m_From.SendMessage( "That is not accessible." ); + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Select ) ); + break; + } + + if ( obj is Item && !((Item)obj).Deleted ) + m_From.SendGump( new InterfaceItemGump( m_From, m_Columns, m_List, m_Page, (Item) obj ) ); + else if ( obj is Mobile && !((Mobile)obj).Deleted ) + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, (Mobile) obj ) ); + else + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Select ) ); + } + + break; + } + } + } + } + + public class InterfaceItemGump : BaseGridGump + { + private Mobile m_From; + + private string[] m_Columns; + + private ArrayList m_List; + private int m_Page; + + private Item m_Item; + + public InterfaceItemGump( Mobile from, string[] columns, ArrayList list, int page, Item item ) : base( 30, 30 ) + { + m_From = from; + + m_Columns = columns; + + m_List = list; + m_Page = page; + + m_Item = item; + + Render(); + } + + public void Render() + { + AddNewPage(); + + AddEntryButton( 20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight ); + AddEntryHtml( 160, m_Item.GetType().Name ); + AddEntryHeader( 20 ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Properties" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Delete" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 3, ArrowRightWidth, ArrowRightHeight ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Go there" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 4, ArrowRightWidth, ArrowRightHeight ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Move to target" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 5, ArrowRightWidth, ArrowRightHeight ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Bring to pack" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 6, ArrowRightWidth, ArrowRightHeight ); + + FinishPage(); + } + + private void InvokeCommand( string ip ) + { + CommandSystem.Handle( m_From, String.Format( "{0}{1}", CommandSystem.Prefix, ip ) ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Item.Deleted ) + { + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + return; + } + else if ( !BaseCommand.IsAccessible( m_From, m_Item ) ) + { + m_From.SendMessage( "That is no longer accessible." ); + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + return; + } + + switch ( info.ButtonID ) + { + case 0: + case 1: + { + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + break; + } + case 2: // Properties + { + m_From.SendGump( new InterfaceItemGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + m_From.SendGump( new PropertiesGump( m_From, m_Item ) ); + break; + } + case 3: // Delete + { + CommandLogging.WriteLine( m_From, "{0} {1} deleting {2}", m_From.AccessLevel, CommandLogging.Format( m_From ), CommandLogging.Format( m_Item ) ); + m_Item.Delete(); + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + break; + } + case 4: // Go there + { + m_From.SendGump( new InterfaceItemGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + InvokeCommand( String.Format( "Go {0}", m_Item.Serial.Value ) ); + break; + } + case 5: // Move to target + { + m_From.SendGump( new InterfaceItemGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + m_From.Target = new MoveTarget( m_Item ); + break; + } + case 6: // Bring to pack + { + Mobile owner = m_Item.RootParent as Mobile; + + if ( owner != null && (owner.Map != null && owner.Map != Map.Internal) && !BaseCommand.IsAccessible( m_From, owner ) /* !m_From.CanSee( owner )*/ ) + { + m_From.SendMessage( "You can not get what you can not see." ); + } + else if ( owner != null && (owner.Map == null || owner.Map == Map.Internal) && owner.Hidden && owner.AccessLevel >= m_From.AccessLevel ) + { + m_From.SendMessage( "You can not get what you can not see." ); + } + else + { + m_From.SendGump( new InterfaceItemGump( m_From, m_Columns, m_List, m_Page, m_Item ) ); + m_From.AddToBackpack( m_Item ); + } + + break; + } + } + } + } + + public class InterfaceMobileGump : BaseGridGump + { + private Mobile m_From; + + private string[] m_Columns; + + private ArrayList m_List; + private int m_Page; + + private Mobile m_Mobile; + + public InterfaceMobileGump( Mobile from, string[] columns, ArrayList list, int page, Mobile mob ) + : base( 30, 30 ) + { + m_From = from; + + m_Columns = columns; + + m_List = list; + m_Page = page; + + m_Mobile = mob; + + Render(); + } + + public void Render() + { + AddNewPage(); + + AddEntryButton( 20, ArrowLeftID1, ArrowLeftID2, 1, ArrowLeftWidth, ArrowLeftHeight ); + AddEntryHtml( 160, m_Mobile.Name ); + AddEntryHeader( 20 ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Properties" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 2, ArrowRightWidth, ArrowRightHeight ); + + if ( !m_Mobile.Player ) + { + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Delete" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 3, ArrowRightWidth, ArrowRightHeight ); + } + + if ( m_Mobile != m_From ) + { + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Go to there" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 4, ArrowRightWidth, ArrowRightHeight ); + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Bring them here" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 5, ArrowRightWidth, ArrowRightHeight ); + } + + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Move to target" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 6, ArrowRightWidth, ArrowRightHeight ); + + if ( m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel ) + { + AddNewLine(); + if ( m_Mobile.Alive ) + { + AddEntryHtml( 20 + OffsetSize + 160, "Kill" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 7, ArrowRightWidth, ArrowRightHeight ); + } + else + { + AddEntryHtml( 20 + OffsetSize + 160, "Resurrect" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 8, ArrowRightWidth, ArrowRightHeight ); + } + } + + if ( m_Mobile.NetState != null ) + { + AddNewLine(); + AddEntryHtml( 20 + OffsetSize + 160, "Client" ); + AddEntryButton( 20, ArrowRightID1, ArrowRightID2, 9, ArrowRightWidth, ArrowRightHeight ); + } + + FinishPage(); + } + + private void InvokeCommand( string ip ) + { + CommandSystem.Handle( m_From, String.Format( "{0}{1}", CommandSystem.Prefix, ip ) ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Mobile.Deleted ) + { + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + return; + } + else if ( !BaseCommand.IsAccessible( m_From, m_Mobile ) ) + { + m_From.SendMessage( "That is no longer accessible." ); + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + return; + } + + switch ( info.ButtonID ) + { + case 0: + case 1: + { + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + break; + } + case 2: // Properties + { + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + m_From.SendGump( new PropertiesGump( m_From, m_Mobile ) ); + break; + } + case 3: // Delete + { + if ( !m_Mobile.Player ) + { + CommandLogging.WriteLine( m_From, "{0} {1} deleting {2}", m_From.AccessLevel, CommandLogging.Format( m_From ), CommandLogging.Format( m_Mobile ) ); + m_Mobile.Delete(); + m_From.SendGump( new InterfaceGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + } + + break; + } + case 4: // Go there + { + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + InvokeCommand( String.Format( "Go {0}", m_Mobile.Serial.Value ) ); + break; + } + case 5: // Bring them here + { + if ( m_From.Map == null || m_From.Map == Map.Internal ) + { + m_From.SendMessage( "You cannot bring that person here." ); + } + else + { + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + m_Mobile.MoveToWorld( m_From.Location, m_From.Map ); + } + + break; + } + case 6: // Move to target + { + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + m_From.Target = new MoveTarget( m_Mobile ); + break; + } + case 7: // Kill + { + if ( m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel ) + m_Mobile.Kill(); + + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + + break; + } + case 8: // Res + { + if ( m_From == m_Mobile || m_From.AccessLevel > m_Mobile.AccessLevel ) + { + m_Mobile.PlaySound( 0x214 ); + m_Mobile.FixedEffect( 0x376A, 10, 16 ); + + m_Mobile.Resurrect(); + } + + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + + break; + } + case 9: // Client + { + m_From.SendGump( new InterfaceMobileGump( m_From, m_Columns, m_List, m_Page, m_Mobile ) ); + + if ( m_Mobile.NetState != null ) + m_From.SendGump( new ClientGump( m_From, m_Mobile.NetState ) ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Extensions/BaseExtension.cs b/Scripts/Commands/Generic/Extensions/BaseExtension.cs new file mode 100644 index 000000000..62bdd8728 --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/BaseExtension.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Server.Commands.Generic +{ + public delegate BaseExtension ExtensionConstructor(); + + public sealed class ExtensionInfo + { + private static Dictionary m_Table = new Dictionary( StringComparer.InvariantCultureIgnoreCase ); + + public static Dictionary Table + { + get { return m_Table; } + } + + public static void Register( ExtensionInfo ext ) + { + m_Table[ext.m_Name] = ext; + } + + private int m_Order; + + private string m_Name; + private int m_Size; + + private ExtensionConstructor m_Constructor; + + public int Order + { + get { return m_Order; } + } + + public string Name + { + get { return m_Name; } + } + + public int Size + { + get { return m_Size; } + } + + public bool IsFixedSize + { + get { return ( m_Size >= 0 ); } + } + + public ExtensionConstructor Constructor + { + get { return m_Constructor; } + } + + public ExtensionInfo( int order, string name, int size, ExtensionConstructor constructor ) + { + m_Name = name; + m_Size = size; + + m_Order = order; + + m_Constructor = constructor; + } + } + + public sealed class Extensions : List + { + public Extensions() + { + } + + public bool IsValid( object obj ) + { + for ( int i = 0; i < this.Count; ++i ) + { + if ( !this[i].IsValid( obj ) ) + return false; + } + + return true; + } + + public void Filter( ArrayList list ) + { + for ( int i = 0; i < this.Count; ++i ) + this[i].Filter( list ); + } + + public static Extensions Parse( Mobile from, ref string[] args ) + { + Extensions parsed = new Extensions(); + + int size = args.Length; + + Type baseType = null; + + for ( int i = args.Length - 1; i >= 0; --i ) + { + ExtensionInfo extInfo = null; + + if ( !ExtensionInfo.Table.TryGetValue( args[i], out extInfo ) ) + continue; + + if ( extInfo.IsFixedSize && i != ( size - extInfo.Size - 1 ) ) + throw new Exception( "Invalid extended argument count." ); + + BaseExtension ext = extInfo.Constructor(); + + ext.Parse( from, args, i + 1, size - i - 1 ); + + if ( ext is WhereExtension ) + baseType = ( ext as WhereExtension ).Conditional.Type; + + parsed.Add( ext ); + + size = i; + } + + parsed.Sort( delegate( BaseExtension a, BaseExtension b ) + { + return ( a.Order - b.Order ); + } ); + + AssemblyEmitter emitter = null; + + foreach ( BaseExtension update in parsed ) + update.Optimize( from, baseType, ref emitter ); + + if ( size != args.Length ) + { + string[] old = args; + args = new string[size]; + + for ( int i = 0; i < args.Length; ++i ) + args[i] = old[i]; + } + + return parsed; + } + } + + public abstract class BaseExtension + { + public abstract ExtensionInfo Info { get; } + + public string Name + { + get { return Info.Name; } + } + + public int Size + { + get { return Info.Size; } + } + + public bool IsFixedSize + { + get { return Info.IsFixedSize; } + } + + public int Order + { + get { return Info.Order; } + } + + public virtual void Optimize( Mobile from, Type baseType, ref AssemblyEmitter assembly ) + { + } + + public virtual void Parse( Mobile from, string[] arguments, int offset, int size ) + { + } + + public virtual bool IsValid( object obj ) + { + return true; + } + + public virtual void Filter( ArrayList list ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs b/Scripts/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs new file mode 100644 index 000000000..71f94d694 --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/Compilers/ConditionalCompiler.cs @@ -0,0 +1,569 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Globalization; +using System.Text; +using Server; + +namespace Server.Commands.Generic +{ + public interface IConditional + { + bool Verify( object obj ); + } + + public interface ICondition + { + // Invoked during the constructor + void Construct( TypeBuilder typeBuilder, ILGenerator il, int index ); + + // Target object will be loaded on the stack + void Compile( MethodEmitter emitter ); + } + + public sealed class TypeCondition : ICondition + { + public static TypeCondition Default = new TypeCondition(); + + void ICondition.Construct( TypeBuilder typeBuilder, ILGenerator il, int index ) + { + } + + void ICondition.Compile( MethodEmitter emitter ) + { + // The object was safely cast to be the conditionals type + // If it's null, then the type cast didn't work... + + emitter.LoadNull(); + emitter.Compare( OpCodes.Ceq ); + emitter.LogicalNot(); + } + } + + public sealed class PropertyValue + { + private Type m_Type; + private object m_Value; + private FieldInfo m_Field; + + public Type Type + { + get { return m_Type; } + } + + public object Value + { + get { return m_Value; } + } + + public FieldInfo Field + { + get { return m_Field; } + } + + public bool HasField + { + get { return ( m_Field != null ); } + } + + public PropertyValue( Type type, object value ) + { + m_Type = type; + m_Value = value; + } + + public void Load( MethodEmitter method ) + { + if ( m_Field != null ) + { + method.LoadArgument( 0 ); + method.LoadField( m_Field ); + } + else if ( m_Value == null ) + { + method.LoadNull( m_Type ); + } + else + { + if ( m_Value is int ) + method.Load( (int) m_Value ); + else if ( m_Value is long ) + method.Load( (long) m_Value ); + else if ( m_Value is float ) + method.Load( (float) m_Value ); + else if ( m_Value is double ) + method.Load( (double) m_Value ); + else if ( m_Value is char ) + method.Load( (char) m_Value ); + else if ( m_Value is bool ) + method.Load( (bool) m_Value ); + else if ( m_Value is string ) + method.Load( (string) m_Value ); + else if ( m_Value is Enum ) + method.Load( (Enum) m_Value ); + else + throw new InvalidOperationException( "Unrecognized comparison value." ); + } + } + + public void Acquire( TypeBuilder typeBuilder, ILGenerator il, string fieldName ) + { + if ( m_Value is string ) + { + string toParse = (string) m_Value; + + if ( !m_Type.IsValueType && toParse == "null" ) + { + m_Value = null; + } + else if ( m_Type == typeof( string ) ) + { + if ( toParse == @"@""null""" ) + toParse = "null"; + + m_Value = toParse; + } + else if ( m_Type.IsEnum ) + { + m_Value = Enum.Parse( m_Type, toParse, true ); + } + else + { + MethodInfo parseMethod = null; + object[] parseArgs = null; + + MethodInfo parseNumber = m_Type.GetMethod( + "Parse", + BindingFlags.Public | BindingFlags.Static, + null, + new Type[] { typeof( string ), typeof( NumberStyles ) }, + null + ); + + if ( parseNumber != null ) + { + NumberStyles style = NumberStyles.Integer; + + if ( Insensitive.StartsWith( toParse, "0x" ) ) + { + style = NumberStyles.HexNumber; + toParse = toParse.Substring( 2 ); + } + + parseMethod = parseNumber; + parseArgs = new object[] { toParse, style }; + } + else + { + MethodInfo parseGeneral = m_Type.GetMethod( + "Parse", + BindingFlags.Public | BindingFlags.Static, + null, + new Type[] { typeof( string ) }, + null + ); + + parseMethod = parseGeneral; + parseArgs = new object[] { toParse }; + } + + if ( parseMethod != null ) + { + m_Value = parseMethod.Invoke( null, parseArgs ); + + if ( !m_Type.IsPrimitive ) + { + m_Field = typeBuilder.DefineField( + fieldName, + m_Type, + FieldAttributes.Private | FieldAttributes.InitOnly + ); + + il.Emit( OpCodes.Ldarg_0 ); + + il.Emit( OpCodes.Ldstr, toParse ); + + if ( parseArgs.Length == 2 ) // dirty evil hack :-( + il.Emit( OpCodes.Ldc_I4, (int) parseArgs[1] ); + + il.Emit( OpCodes.Call, parseMethod ); + il.Emit( OpCodes.Stfld, m_Field ); + } + } + else + { + throw new InvalidOperationException( + String.Format( + "Unable to convert string \"{0}\" into type '{1}'.", + m_Value, + m_Type + ) + ); + } + } + } + } + } + + public abstract class PropertyCondition : ICondition + { + protected Property m_Property; + protected bool m_Not; + + public PropertyCondition( Property property, bool not ) + { + m_Property = property; + m_Not = not; + } + + public abstract void Construct( TypeBuilder typeBuilder, ILGenerator il, int index ); + + public abstract void Compile( MethodEmitter emitter ); + } + + public enum StringOperator + { + Equal, + NotEqual, + + Contains, + + StartsWith, + EndsWith + } + + public sealed class StringCondition : PropertyCondition + { + private StringOperator m_Operator; + private PropertyValue m_Value; + + private bool m_IgnoreCase; + + public StringCondition( Property property, bool not, StringOperator op, object value, bool ignoreCase ) + : base( property, not ) + { + m_Operator = op; + m_Value = new PropertyValue( property.Type, value ); + + m_IgnoreCase = ignoreCase; + } + + public override void Construct( TypeBuilder typeBuilder, ILGenerator il, int index ) + { + m_Value.Acquire( typeBuilder, il, "v" + index ); + } + + public override void Compile( MethodEmitter emitter ) + { + bool inverse = false; + + string methodName; + + switch ( m_Operator ) + { + case StringOperator.Equal: + methodName = "Equals"; + break; + + case StringOperator.NotEqual: + methodName = "Equals"; + inverse = true; + break; + + case StringOperator.Contains: + methodName = "Contains"; + break; + + case StringOperator.StartsWith: + methodName = "StartsWith"; + break; + + case StringOperator.EndsWith: + methodName = "EndsWith"; + break; + + default: + throw new InvalidOperationException( "Invalid string comparison operator." ); + } + + if ( m_IgnoreCase || methodName == "Equals" ) + { + Type type = ( m_IgnoreCase ? typeof( Insensitive ) : typeof( String ) ); + + emitter.BeginCall( + type.GetMethod( + methodName, + BindingFlags.Public | BindingFlags.Static, + null, + new Type[] + { + typeof( string ), + typeof( string ) + }, + null + ) + ); + + emitter.Chain( m_Property ); + m_Value.Load( emitter ); + + emitter.FinishCall(); + } + else + { + Label notNull = emitter.CreateLabel(); + Label moveOn = emitter.CreateLabel(); + + LocalBuilder temp = emitter.AcquireTemp( m_Property.Type ); + + emitter.Chain( m_Property ); + + emitter.StoreLocal( temp ); + emitter.LoadLocal( temp ); + + emitter.BranchIfTrue( notNull ); + + emitter.Load( false ); + emitter.Pop(); + emitter.Branch( moveOn ); + + emitter.MarkLabel( notNull ); + emitter.LoadLocal( temp ); + + emitter.BeginCall( + typeof( string ).GetMethod( + methodName, + BindingFlags.Public | BindingFlags.Instance, + null, + new Type[] + { + typeof( string ) + }, + null + ) + ); + + m_Value.Load( emitter ); + + emitter.FinishCall(); + + emitter.MarkLabel( moveOn ); + } + + if ( m_Not != inverse ) + emitter.LogicalNot(); + } + } + + public enum ComparisonOperator + { + Equal, + NotEqual, + Greater, + GreaterEqual, + Lesser, + LesserEqual + } + + public sealed class ComparisonCondition : PropertyCondition + { + private ComparisonOperator m_Operator; + private PropertyValue m_Value; + + public ComparisonCondition( Property property, bool not, ComparisonOperator op, object value ) + : base( property, not ) + { + m_Operator = op; + m_Value = new PropertyValue( property.Type, value ); + } + + public override void Construct( TypeBuilder typeBuilder, ILGenerator il, int index ) + { + m_Value.Acquire( typeBuilder, il, "v" + index ); + } + + public override void Compile( MethodEmitter emitter ) + { + emitter.Chain( m_Property ); + + bool inverse = false; + + bool couldCompare = + emitter.CompareTo( 1, delegate() + { + m_Value.Load( emitter ); + } ); + + if ( couldCompare ) + { + emitter.Load( 0 ); + + switch ( m_Operator ) + { + case ComparisonOperator.Equal: + emitter.Compare( OpCodes.Ceq ); + break; + + case ComparisonOperator.NotEqual: + emitter.Compare( OpCodes.Ceq ); + inverse = true; + break; + + case ComparisonOperator.Greater: + emitter.Compare( OpCodes.Cgt ); + break; + + case ComparisonOperator.GreaterEqual: + emitter.Compare( OpCodes.Clt ); + inverse = true; + break; + + case ComparisonOperator.Lesser: + emitter.Compare( OpCodes.Clt ); + break; + + case ComparisonOperator.LesserEqual: + emitter.Compare( OpCodes.Cgt ); + inverse = true; + break; + + default: + throw new InvalidOperationException( "Invalid comparison operator." ); + } + } + else + { + // This type is -not- comparable + // We can only support == and != operations + + m_Value.Load( emitter ); + + switch ( m_Operator ) + { + case ComparisonOperator.Equal: + emitter.Compare( OpCodes.Ceq ); + break; + + case ComparisonOperator.NotEqual: + emitter.Compare( OpCodes.Ceq ); + inverse = true; + break; + + case ComparisonOperator.Greater: + case ComparisonOperator.GreaterEqual: + case ComparisonOperator.Lesser: + case ComparisonOperator.LesserEqual: + throw new InvalidOperationException( "Property does not support relational comparisons." ); + + default: + throw new InvalidOperationException( "Invalid operator." ); + } + } + + if ( m_Not != inverse ) + emitter.LogicalNot(); + } + } + + public static class ConditionalCompiler + { + public static IConditional Compile( AssemblyEmitter assembly, Type objectType, ICondition[] conditions, int index ) + { + TypeBuilder typeBuilder = assembly.DefineType( + "__conditional" + index, + TypeAttributes.Public, + typeof( object ) + ); + + #region Constructor + { + ConstructorBuilder ctor = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + Type.EmptyTypes + ); + + ILGenerator il = ctor.GetILGenerator(); + + // : base() + il.Emit( OpCodes.Ldarg_0 ); + il.Emit( OpCodes.Call, typeof( object ).GetConstructor( Type.EmptyTypes ) ); + + for ( int i = 0; i < conditions.Length; ++i ) + conditions[i].Construct( typeBuilder, il, i ); + + // return; + il.Emit( OpCodes.Ret ); + } + #endregion + + #region IComparer + typeBuilder.AddInterfaceImplementation( typeof( IConditional ) ); + + MethodBuilder compareMethod; + + #region Compare + { + MethodEmitter emitter = new MethodEmitter( typeBuilder ); + + emitter.Define( + /* name */ "Verify", + /* attr */ MethodAttributes.Public | MethodAttributes.Virtual, + /* return */ typeof( bool ), + /* params */ new Type[] { typeof( object ) } ); + + LocalBuilder obj = emitter.CreateLocal( objectType ); + LocalBuilder eq = emitter.CreateLocal( typeof( bool ) ); + + emitter.LoadArgument( 1 ); + emitter.CastAs( objectType ); + emitter.StoreLocal( obj ); + + Label done = emitter.CreateLabel(); + + for ( int i = 0; i < conditions.Length; ++i ) + { + if ( i > 0 ) + { + emitter.LoadLocal( eq ); + + emitter.BranchIfFalse( done ); + } + + emitter.LoadLocal( obj ); + + conditions[i].Compile( emitter ); + + emitter.StoreLocal( eq ); + } + + emitter.MarkLabel( done ); + + emitter.LoadLocal( eq ); + + emitter.Return(); + + typeBuilder.DefineMethodOverride( + emitter.Method, + typeof( IConditional ).GetMethod( + "Verify", + new Type[] + { + typeof( object ) + } + ) + ); + + compareMethod = emitter.Method; + } + #endregion + #endregion + + Type conditionalType = typeBuilder.CreateType(); + + return (IConditional) Activator.CreateInstance( conditionalType ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Extensions/Compilers/DistinctCompiler.cs b/Scripts/Commands/Generic/Extensions/Compilers/DistinctCompiler.cs new file mode 100644 index 000000000..e1717284c --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/Compilers/DistinctCompiler.cs @@ -0,0 +1,249 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using Server; + +namespace Server.Commands.Generic +{ + public static class DistinctCompiler + { + public static IComparer Compile( AssemblyEmitter assembly, Type objectType, Property[] props ) + { + TypeBuilder typeBuilder = assembly.DefineType( + "__distinct", + TypeAttributes.Public, + typeof( object ) + ); + + #region Constructor + { + ConstructorBuilder ctor = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + Type.EmptyTypes + ); + + ILGenerator il = ctor.GetILGenerator(); + + // : base() + il.Emit( OpCodes.Ldarg_0 ); + il.Emit( OpCodes.Call, typeof( object ).GetConstructor( Type.EmptyTypes ) ); + + // return; + il.Emit( OpCodes.Ret ); + } + #endregion + + #region IComparer + typeBuilder.AddInterfaceImplementation( typeof( IComparer ) ); + + MethodBuilder compareMethod; + + #region Compare + { + MethodEmitter emitter = new MethodEmitter( typeBuilder ); + + emitter.Define( + /* name */ "Compare", + /* attr */ MethodAttributes.Public | MethodAttributes.Virtual, + /* return */ typeof( int ), + /* params */ new Type[] { typeof( object ), typeof( object ) } ); + + LocalBuilder a = emitter.CreateLocal( objectType ); + LocalBuilder b = emitter.CreateLocal( objectType ); + + LocalBuilder v = emitter.CreateLocal( typeof( int ) ); + + emitter.LoadArgument( 1 ); + emitter.CastAs( objectType ); + emitter.StoreLocal( a ); + + emitter.LoadArgument( 2 ); + emitter.CastAs( objectType ); + emitter.StoreLocal( b ); + + emitter.Load( 0 ); + emitter.StoreLocal( v ); + + Label end = emitter.CreateLabel(); + + for ( int i = 0; i < props.Length; ++i ) + { + if ( i > 0 ) + { + emitter.LoadLocal( v ); + emitter.BranchIfTrue( end ); // if ( v != 0 ) return v; + } + + Property prop = props[i]; + + emitter.LoadLocal( a ); + emitter.Chain( prop ); + + bool couldCompare = + emitter.CompareTo( 1, delegate() + { + emitter.LoadLocal( b ); + emitter.Chain( prop ); + } ); + + if ( !couldCompare ) + throw new InvalidOperationException( "Property is not comparable." ); + + emitter.StoreLocal( v ); + } + + emitter.MarkLabel( end ); + + emitter.LoadLocal( v ); + emitter.Return(); + + typeBuilder.DefineMethodOverride( + emitter.Method, + typeof( IComparer ).GetMethod( + "Compare", + new Type[] + { + typeof( object ), + typeof( object ) + } + ) + ); + + compareMethod = emitter.Method; + } + #endregion + #endregion + + #region IEqualityComparer + typeBuilder.AddInterfaceImplementation( typeof( IEqualityComparer ) ); + + #region Equals + { + MethodEmitter emitter = new MethodEmitter( typeBuilder ); + + emitter.Define( + /* name */ "Equals", + /* attr */ MethodAttributes.Public | MethodAttributes.Virtual, + /* return */ typeof( bool ), + /* params */ new Type[] { typeof( object ), typeof( object ) } ); + + emitter.Generator.Emit( OpCodes.Ldarg_0 ); + emitter.Generator.Emit( OpCodes.Ldarg_1 ); + emitter.Generator.Emit( OpCodes.Ldarg_2 ); + + emitter.Generator.Emit( OpCodes.Call, compareMethod ); + + emitter.Generator.Emit( OpCodes.Ldc_I4_0 ); + + emitter.Generator.Emit( OpCodes.Ceq ); + + emitter.Generator.Emit( OpCodes.Ret ); + + typeBuilder.DefineMethodOverride( + emitter.Method, + typeof( IEqualityComparer ).GetMethod( + "Equals", + new Type[] + { + typeof( object ), + typeof( object ) + } + ) + ); + } + #endregion + + #region GetHashCode + { + MethodEmitter emitter = new MethodEmitter( typeBuilder ); + + emitter.Define( + /* name */ "GetHashCode", + /* attr */ MethodAttributes.Public | MethodAttributes.Virtual, + /* return */ typeof( int ), + /* params */ new Type[] { typeof( object ) } ); + + LocalBuilder obj = emitter.CreateLocal( objectType ); + + emitter.LoadArgument( 1 ); + emitter.CastAs( objectType ); + emitter.StoreLocal( obj ); + + for ( int i = 0; i < props.Length; ++i ) + { + Property prop = props[i]; + + emitter.LoadLocal( obj ); + emitter.Chain( prop ); + + Type active = emitter.Active; + + MethodInfo getHashCode = active.GetMethod( "GetHashCode", Type.EmptyTypes ); + + if ( getHashCode == null ) + getHashCode = typeof( object ).GetMethod( "GetHashCode", Type.EmptyTypes ); + + if ( active != typeof( int ) ) + { + if ( !active.IsValueType ) + { + LocalBuilder value = emitter.AcquireTemp( active ); + + Label valueNotNull = emitter.CreateLabel(); + Label done = emitter.CreateLabel(); + + emitter.StoreLocal( value ); + emitter.LoadLocal( value ); + + emitter.BranchIfTrue( valueNotNull ); + + emitter.Load( 0 ); + emitter.Pop( typeof( int ) ); + + emitter.Branch( done ); + + emitter.MarkLabel( valueNotNull ); + + emitter.LoadLocal( value ); + emitter.Call( getHashCode ); + + emitter.ReleaseTemp( value ); + + emitter.MarkLabel( done ); + } + else + { + emitter.Call( getHashCode ); + } + } + + if ( i > 0 ) + emitter.Xor(); + } + + emitter.Return(); + + typeBuilder.DefineMethodOverride( + emitter.Method, + typeof( IEqualityComparer ).GetMethod( + "GetHashCode", + new Type[] + { + typeof( object ) + } + ) + ); + } + #endregion + #endregion + + Type comparerType = typeBuilder.CreateType(); + + return (IComparer) Activator.CreateInstance( comparerType ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Extensions/Compilers/SortCompiler.cs b/Scripts/Commands/Generic/Extensions/Compilers/SortCompiler.cs new file mode 100644 index 000000000..8c4f5be1f --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/Compilers/SortCompiler.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using Server; + +namespace Server.Commands.Generic +{ + public sealed class OrderInfo + { + private Property m_Property; + private int m_Order; + + public Property Property + { + get { return m_Property; } + set { m_Property = value; } + } + + public bool IsAscending + { + get { return ( m_Order > 0 ); } + set { m_Order = ( value ? +1 : -1 ); } + } + + public bool IsDescending + { + get { return ( m_Order < 0 ); } + set { m_Order = ( value ? -1 : +1 ); } + } + + public int Sign + { + get { return Math.Sign( m_Order ); } + set + { + m_Order = Math.Sign( value ); + + if ( m_Order == 0 ) + throw new InvalidOperationException( "Sign cannot be zero." ); + } + } + + public OrderInfo( Property property, bool isAscending ) + { + m_Property = property; + + this.IsAscending = isAscending; + } + } + + public static class SortCompiler + { + public static IComparer Compile( AssemblyEmitter assembly, Type objectType, OrderInfo[] orders ) + { + TypeBuilder typeBuilder = assembly.DefineType( + "__sort", + TypeAttributes.Public, + typeof( object ) + ); + + #region Constructor + { + ConstructorBuilder ctor = typeBuilder.DefineConstructor( + MethodAttributes.Public, + CallingConventions.Standard, + Type.EmptyTypes + ); + + ILGenerator il = ctor.GetILGenerator(); + + // : base() + il.Emit( OpCodes.Ldarg_0 ); + il.Emit( OpCodes.Call, typeof( object ).GetConstructor( Type.EmptyTypes ) ); + + // return; + il.Emit( OpCodes.Ret ); + } + #endregion + + #region IComparer + typeBuilder.AddInterfaceImplementation( typeof( IComparer ) ); + + MethodBuilder compareMethod; + + #region Compare + { + MethodEmitter emitter = new MethodEmitter( typeBuilder ); + + emitter.Define( + /* name */ "Compare", + /* attr */ MethodAttributes.Public | MethodAttributes.Virtual, + /* return */ typeof( int ), + /* params */ new Type[] { typeof( object ), typeof( object ) } ); + + LocalBuilder a = emitter.CreateLocal( objectType ); + LocalBuilder b = emitter.CreateLocal( objectType ); + + LocalBuilder v = emitter.CreateLocal( typeof( int ) ); + + emitter.LoadArgument( 1 ); + emitter.CastAs( objectType ); + emitter.StoreLocal( a ); + + emitter.LoadArgument( 2 ); + emitter.CastAs( objectType ); + emitter.StoreLocal( b ); + + emitter.Load( 0 ); + emitter.StoreLocal( v ); + + Label end = emitter.CreateLabel(); + + for ( int i = 0; i < orders.Length; ++i ) + { + if ( i > 0 ) + { + emitter.LoadLocal( v ); + emitter.BranchIfTrue( end ); // if ( v != 0 ) return v; + } + + OrderInfo orderInfo = orders[i]; + + Property prop = orderInfo.Property; + int sign = orderInfo.Sign; + + emitter.LoadLocal( a ); + emitter.Chain( prop ); + + bool couldCompare = + emitter.CompareTo( sign, delegate() + { + emitter.LoadLocal( b ); + emitter.Chain( prop ); + } ); + + if ( !couldCompare ) + throw new InvalidOperationException( "Property is not comparable." ); + + emitter.StoreLocal( v ); + } + + emitter.MarkLabel( end ); + + emitter.LoadLocal( v ); + emitter.Return(); + + typeBuilder.DefineMethodOverride( + emitter.Method, + typeof( IComparer ).GetMethod( + "Compare", + new Type[] + { + typeof( object ), + typeof( object ) + } + ) + ); + + compareMethod = emitter.Method; + } + #endregion + #endregion + + Type comparerType = typeBuilder.CreateType(); + + return (IComparer) Activator.CreateInstance( comparerType ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Extensions/DistinctExtension.cs b/Scripts/Commands/Generic/Extensions/DistinctExtension.cs new file mode 100644 index 000000000..396cf7262 --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/DistinctExtension.cs @@ -0,0 +1,89 @@ +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Server.Commands.Generic +{ + public sealed class DistinctExtension : BaseExtension + { + public static ExtensionInfo ExtInfo = new ExtensionInfo( 30, "Distinct", -1, delegate() { return new DistinctExtension(); } ); + + public static void Initialize() + { + ExtensionInfo.Register( ExtInfo ); + } + + public override ExtensionInfo Info + { + get { return ExtInfo; } + } + + private List m_Properties; + + private IComparer m_Comparer; + + public DistinctExtension() + { + m_Properties = new List(); + } + + public override void Optimize( Mobile from, Type baseType, ref AssemblyEmitter assembly ) + { + if ( baseType == null ) + throw new Exception( "Distinct extension may only be used in combination with an object conditional." ); + + foreach ( Property prop in m_Properties ) + { + prop.BindTo( baseType, PropertyAccess.Read ); + prop.CheckAccess( from ); + } + + if ( assembly == null ) + assembly = new AssemblyEmitter( "__dynamic", false ); + + m_Comparer = DistinctCompiler.Compile( assembly, baseType, m_Properties.ToArray() ); + } + + public override void Parse( Mobile from, string[] arguments, int offset, int size ) + { + if ( size < 1 ) + throw new Exception( "Invalid distinction syntax." ); + + int end = offset + size; + + while ( offset < end ) + { + string binding = arguments[offset++]; + + m_Properties.Add( new Property( binding ) ); + } + } + + public override void Filter( ArrayList list ) + { + if ( m_Comparer == null ) + throw new InvalidOperationException( "The extension must first be optimized." ); + + ArrayList copy = new ArrayList( list ); + + copy.Sort( m_Comparer ); + + list.Clear(); + + object last = null; + + for ( int i = 0; i < copy.Count; ++i ) + { + object obj = copy[i]; + + if ( last == null || m_Comparer.Compare( obj, last ) != 0 ) + { + list.Add( obj ); + last = obj; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Extensions/LimitExtension.cs b/Scripts/Commands/Generic/Extensions/LimitExtension.cs new file mode 100644 index 000000000..9836445b9 --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/LimitExtension.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections; +using System.Text; + +namespace Server.Commands.Generic +{ + public sealed class LimitExtension : BaseExtension + { + public static ExtensionInfo ExtInfo = new ExtensionInfo( 80, "Limit", 1, delegate() { return new LimitExtension(); } ); + + public static void Initialize() + { + ExtensionInfo.Register( ExtInfo ); + } + + public override ExtensionInfo Info + { + get { return ExtInfo; } + } + + private int m_Limit; + + public int Limit + { + get { return m_Limit; } + } + + public LimitExtension() + { + } + + public override void Parse( Mobile from, string[] arguments, int offset, int size ) + { + m_Limit = Utility.ToInt32( arguments[offset] ); + + if ( m_Limit < 0 ) + throw new Exception( "Limit cannot be less than zero." ); + } + + public override void Filter( ArrayList list ) + { + if ( list.Count > m_Limit ) + list.RemoveRange( m_Limit, list.Count - m_Limit ); + } + } +} diff --git a/Scripts/Commands/Generic/Extensions/SortExtension.cs b/Scripts/Commands/Generic/Extensions/SortExtension.cs new file mode 100644 index 000000000..713afc37e --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/SortExtension.cs @@ -0,0 +1,109 @@ +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace Server.Commands.Generic +{ + public sealed class SortExtension : BaseExtension + { + public static ExtensionInfo ExtInfo = new ExtensionInfo( 40, "Order", -1, delegate() { return new SortExtension(); } ); + + public static void Initialize() + { + ExtensionInfo.Register( ExtInfo ); + } + + public override ExtensionInfo Info + { + get { return ExtInfo; } + } + + private List m_Orders; + + private IComparer m_Comparer; + + public SortExtension() + { + m_Orders = new List(); + } + + public override void Optimize( Mobile from, Type baseType, ref AssemblyEmitter assembly ) + { + if ( baseType == null ) + throw new Exception( "The ordering extension may only be used in combination with an object conditional." ); + + foreach ( OrderInfo order in m_Orders ) + { + order.Property.BindTo( baseType, PropertyAccess.Read ); + order.Property.CheckAccess( from ); + } + + if ( assembly == null ) + assembly = new AssemblyEmitter( "__dynamic", false ); + + m_Comparer = SortCompiler.Compile( assembly, baseType, m_Orders.ToArray() ); + } + + public override void Parse( Mobile from, string[] arguments, int offset, int size ) + { + if ( size < 1 ) + throw new Exception( "Invalid ordering syntax." ); + + if ( Insensitive.Equals( arguments[offset], "by" ) ) + { + ++offset; + --size; + + if ( size < 1 ) + throw new Exception( "Invalid ordering syntax." ); + } + + int end = offset + size; + + while ( offset < end ) + { + string binding = arguments[offset++]; + + bool isAscending = true; + + if ( offset < end ) + { + string next = arguments[offset]; + + switch ( next.ToLower() ) + { + case "+": + case "up": + case "asc": + case "ascending": + isAscending = true; + ++offset; + break; + + case "-": + case "down": + case "desc": + case "descending": + isAscending = false; + ++offset; + break; + } + } + + Property property = new Property( binding ); + + m_Orders.Add( new OrderInfo( property, isAscending ) ); + } + } + + public override void Filter( ArrayList list ) + { + if ( m_Comparer == null ) + throw new InvalidOperationException( "The extension must first be optimized." ); + + list.Sort( m_Comparer ); + } + } +} diff --git a/Scripts/Commands/Generic/Extensions/WhereExtension.cs b/Scripts/Commands/Generic/Extensions/WhereExtension.cs new file mode 100644 index 000000000..3d9be7d81 --- /dev/null +++ b/Scripts/Commands/Generic/Extensions/WhereExtension.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Server.Commands; + +namespace Server.Commands.Generic +{ + public sealed class WhereExtension : BaseExtension + { + public static ExtensionInfo ExtInfo = new ExtensionInfo( 20, "Where", -1, delegate() { return new WhereExtension(); } ); + + public static void Initialize() + { + ExtensionInfo.Register( ExtInfo ); + } + + public override ExtensionInfo Info + { + get { return ExtInfo; } + } + + private ObjectConditional m_Conditional; + + public ObjectConditional Conditional + { + get { return m_Conditional; } + } + + public WhereExtension() + { + } + + public override void Optimize( Mobile from, Type baseType, ref AssemblyEmitter assembly ) + { + if ( baseType == null ) + throw new InvalidOperationException( "Insanity." ); + + m_Conditional.Compile( ref assembly ); + } + + public override void Parse( Mobile from, string[] arguments, int offset, int size ) + { + if ( size < 1 ) + throw new Exception( "Invalid condition syntax." ); + + m_Conditional = ObjectConditional.ParseDirect( from, arguments, offset, size ); + } + + public override bool IsValid( object obj ) + { + return m_Conditional.CheckCondition( obj ); + } + } +} diff --git a/Scripts/Commands/Generic/Implementors/AreaCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/AreaCommandImplementor.cs new file mode 100644 index 000000000..47a48a371 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/AreaCommandImplementor.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Commands.Generic +{ + public class AreaCommandImplementor : BaseCommandImplementor + { + public AreaCommandImplementor() + { + Accessors = new string[]{ "Area", "Group" }; + SupportRequirement = CommandSupport.Area; + SupportsConditionals = true; + AccessLevel = AccessLevel.GameMaster; + Usage = "Area [condition]"; + Description = "Invokes the command on all appropriate objects in a targeted area. Optional condition arguments can further restrict the set of objects."; + } + + public override void Process( Mobile from, BaseCommand command, string[] args ) + { + BoundingBoxPicker.Begin( from, new BoundingBoxCallback( OnTarget ), new object[]{ command, args } ); + } + + public void OnTarget( Mobile from, Map map, Point3D start, Point3D end, object state ) + { + try + { + object[] states = (object[])state; + BaseCommand command = (BaseCommand)states[0]; + string[] args = (string[])states[1]; + + Rectangle2D rect = new Rectangle2D( start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1 ); + + Extensions ext = Extensions.Parse( from, ref args ); + + bool items, mobiles; + + if ( !CheckObjectTypes( command, ext, out items, out mobiles ) ) + return; + + IPooledEnumerable eable; + + if ( items && mobiles ) + eable = map.GetObjectsInBounds( rect ); + else if ( items ) + eable = map.GetItemsInBounds( rect ); + else if ( mobiles ) + eable = map.GetMobilesInBounds( rect ); + else + return; + + ArrayList objs = new ArrayList(); + + foreach ( object obj in eable ) + { + if ( mobiles && obj is Mobile && !BaseCommand.IsAccessible( from, obj ) ) + continue; + + if ( ext.IsValid( obj ) ) + objs.Add( obj ); + } + + eable.Free(); + + ext.Filter( objs ); + + RunCommand( from, objs, command, args ); + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + + public void OnTarget( Mobile from, object targeted, object state ) + { + try + { + object[] states = (object[])state; + BaseCommand command = (BaseCommand)states[0]; + string[] args = (string[])states[1]; + + switch ( command.ObjectTypes ) + { + case ObjectTypes.Both: + { + if ( !(targeted is Item) && !(targeted is Mobile) ) + { + from.SendMessage( "This command does not work on that." ); + return; + } + + break; + } + case ObjectTypes.Items: + { + if ( !(targeted is Item) ) + { + from.SendMessage( "This command only works on items." ); + return; + } + + break; + } + case ObjectTypes.Mobiles: + { + if ( !(targeted is Mobile) ) + { + from.SendMessage( "This command only works on mobiles." ); + return; + } + + break; + } + } + + RunCommand( from, targeted, command, args ); + + from.BeginTarget( -1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None, new TargetStateCallback( OnTarget ), new object[]{ command, args } ); + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs new file mode 100644 index 000000000..12ffe3385 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/BaseCommandImplementor.cs @@ -0,0 +1,336 @@ +using System; +using System.Text; +using System.Collections; +using Server; + +namespace Server.Commands.Generic +{ + [Flags] + public enum CommandSupport + { + Single = 0x0001, + Global = 0x0002, + Online = 0x0004, + Multi = 0x0008, + Area = 0x0010, + Self = 0x0020, + Region = 0x0040, + Contained = 0x0080, + + All = Single | Global | Online | Multi | Area | Self | Region | Contained, + AllMobiles = All & ~Contained, + AllNPCs = All & ~(Online | Self | Contained), + AllItems = All & ~(Online | Self | Region), + + Simple = Single | Multi, + Complex = Global | Online | Area | Region | Contained + } + + public abstract class BaseCommandImplementor + { + public static void RegisterImplementors() + { + Register( new RegionCommandImplementor() ); + Register( new GlobalCommandImplementor() ); + Register( new OnlineCommandImplementor() ); + Register( new SingleCommandImplementor() ); + Register( new SerialCommandImplementor() ); + Register( new MultiCommandImplementor() ); + Register( new AreaCommandImplementor() ); + Register( new SelfCommandImplementor() ); + Register( new ContainedCommandImplementor() ); + } + + private string[] m_Accessors; + private AccessLevel m_AccessLevel; + private CommandSupport m_SupportRequirement; + private Hashtable m_Commands; + private string m_Usage; + private string m_Description; + private bool m_SupportsConditionals; + + public bool SupportsConditionals + { + get{ return m_SupportsConditionals; } + set{ m_SupportsConditionals = value; } + } + + public string[] Accessors + { + get{ return m_Accessors; } + set{ m_Accessors = value; } + } + + public string Usage + { + get{ return m_Usage; } + set{ m_Usage = value; } + } + + public string Description + { + get{ return m_Description; } + set{ m_Description = value; } + } + + public AccessLevel AccessLevel + { + get{ return m_AccessLevel; } + set{ m_AccessLevel = value; } + } + + public CommandSupport SupportRequirement + { + get{ return m_SupportRequirement; } + set{ m_SupportRequirement = value; } + } + + public Hashtable Commands + { + get{ return m_Commands; } + } + + public BaseCommandImplementor() + { + m_Commands = new Hashtable( StringComparer.OrdinalIgnoreCase ); + } + + public virtual void Compile( Mobile from, BaseCommand command, ref string[] args, ref object obj ) + { + obj = null; + } + + public virtual void Register( BaseCommand command ) + { + for ( int i = 0; i < command.Commands.Length; ++i ) + m_Commands[command.Commands[i]] = command; + } + + public bool CheckObjectTypes( BaseCommand command, Extensions ext, out bool items, out bool mobiles ) + { + items = mobiles = false; + + ObjectConditional cond = ObjectConditional.Empty; + + foreach ( BaseExtension check in ext ) + { + if ( check is WhereExtension ) + { + cond = ( check as WhereExtension ).Conditional; + + break; + } + } + + bool condIsItem = cond.IsItem; + bool condIsMobile = cond.IsMobile; + + switch ( command.ObjectTypes ) + { + case ObjectTypes.All: + case ObjectTypes.Both: + { + if ( condIsItem ) + items = true; + + if ( condIsMobile ) + mobiles = true; + + break; + } + case ObjectTypes.Items: + { + if ( condIsItem ) + { + items = true; + } + else if ( condIsMobile ) + { + command.LogFailure( "You may not use a mobile type condition for this command." ); + return false; + } + + break; + } + case ObjectTypes.Mobiles: + { + if ( condIsMobile ) + { + mobiles = true; + } + else if ( condIsItem ) + { + command.LogFailure( "You may not use an item type condition for this command." ); + return false; + } + + break; + } + } + + return true; + } + + public void RunCommand( Mobile from, BaseCommand command, string[] args ) + { + try + { + object obj = null; + + Compile( from, command, ref args, ref obj ); + + RunCommand( from, obj, command, args ); + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + + public string GenerateArgString( string[] args ) + { + if ( args.Length == 0 ) + return ""; + + // NOTE: this does not preserve the case where quotation marks are used on a single word + + StringBuilder sb = new StringBuilder(); + + for ( int i = 0; i < args.Length; ++i ) + { + if ( i > 0 ) + sb.Append( ' ' ); + + if ( args[i].IndexOf( ' ' ) >= 0 ) + { + sb.Append( '"' ); + sb.Append( args[i] ); + sb.Append( '"' ); + } + else + { + sb.Append( args[i] ); + } + } + + return sb.ToString(); + } + + public void RunCommand( Mobile from, object obj, BaseCommand command, string[] args ) + { + // try + // { + CommandEventArgs e = new CommandEventArgs( from, command.Commands[0], GenerateArgString( args ), args ); + + if ( !command.ValidateArgs( this, e ) ) + return; + + bool flushToLog = false; + + if ( obj is ArrayList ) + { + ArrayList list = (ArrayList)obj; + + if ( list.Count > 20 ) + CommandLogging.Enabled = false; + else if ( list.Count == 0 ) + command.LogFailure( "Nothing was found to use this command on." ); + + command.ExecuteList( e, list ); + + if ( list.Count > 20 ) + { + flushToLog = true; + CommandLogging.Enabled = true; + } + } + else if ( obj != null ) + { + if ( command.ListOptimized ) + { + ArrayList list = new ArrayList(); + list.Add( obj ); + command.ExecuteList( e, list ); + } + else + { + command.Execute( e, obj ); + } + } + + command.Flush( from, flushToLog ); + // } + // catch ( Exception ex ) + // { + // from.SendMessage( ex.Message ); + // } + } + + public virtual void Process( Mobile from, BaseCommand command, string[] args ) + { + RunCommand( from, command, args ); + } + + public virtual void Execute( CommandEventArgs e ) + { + if ( e.Length >= 1 ) + { + BaseCommand command = (BaseCommand)m_Commands[e.GetString( 0 )]; + + if ( command == null ) + { + e.Mobile.SendMessage( "That is either an invalid command name or one that does not support this modifier." ); + } + else if ( e.Mobile.AccessLevel < command.AccessLevel ) + { + e.Mobile.SendMessage( "You do not have access to that command." ); + } + else + { + string[] oldArgs = e.Arguments; + string[] args = new string[oldArgs.Length - 1]; + + for ( int i = 0; i < args.Length; ++i ) + args[i] = oldArgs[i + 1]; + + Process( e.Mobile, command, args ); + } + } + else + { + e.Mobile.SendMessage( "You must supply a command name." ); + } + } + + public void Register() + { + if ( m_Accessors == null ) + return; + + for ( int i = 0; i < m_Accessors.Length; ++i ) + CommandSystem.Register( m_Accessors[i], m_AccessLevel, new CommandEventHandler( Execute ) ); + } + + public static void Register( BaseCommandImplementor impl ) + { + m_Implementors.Add( impl ); + impl.Register(); + } + + private static ArrayList m_Implementors; + + public static ArrayList Implementors + { + get + { + if ( m_Implementors == null ) + { + m_Implementors = new ArrayList(); + RegisterImplementors(); + } + + return m_Implementors; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/ContainedCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/ContainedCommandImplementor.cs new file mode 100644 index 000000000..3e979f8c9 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/ContainedCommandImplementor.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Targeting; + +namespace Server.Commands.Generic +{ + public class ContainedCommandImplementor : BaseCommandImplementor + { + public ContainedCommandImplementor() + { + Accessors = new string[]{ "Contained" }; + SupportRequirement = CommandSupport.Contained; + AccessLevel = AccessLevel.GameMaster; + Usage = "Contained [condition]"; + Description = "Invokes the command on all child items in a targeted container. Optional condition arguments can further restrict the set of objects."; + } + + public override void Process( Mobile from, BaseCommand command, string[] args ) + { + if ( command.ValidateArgs( this, new CommandEventArgs( from, command.Commands[0], GenerateArgString( args ), args ) ) ) + from.BeginTarget( -1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None, new TargetStateCallback( OnTarget ), new object[]{ command, args } ); + } + + public void OnTarget( Mobile from, object targeted, object state ) + { + if ( !BaseCommand.IsAccessible( from, targeted ) ) + { + from.SendMessage( "That is not accessible." ); + return; + } + + object[] states = (object[])state; + BaseCommand command = (BaseCommand)states[0]; + string[] args = (string[])states[1]; + + if ( command.ObjectTypes == ObjectTypes.Mobiles ) + return; // sanity check + + if ( !(targeted is Container) ) + { + from.SendMessage( "That is not a container." ); + } + else + { + try + { + Extensions ext = Extensions.Parse( from, ref args ); + + bool items, mobiles; + + if ( !CheckObjectTypes( command, ext, out items, out mobiles ) ) + return; + + if ( !items ) + { + from.SendMessage( "This command only works on items." ); + return; + } + + Container cont = (Container)targeted; + + Item[] found = cont.FindItemsByType( typeof( Item ), true ); + + ArrayList list = new ArrayList(); + + for ( int i = 0; i < found.Length; ++i ) + { + if ( ext.IsValid( found[i] ) ) + list.Add( found[i] ); + } + + ext.Filter( list ); + + RunCommand( from, list, command, args ); + } + catch ( Exception e ) + { + from.SendMessage( e.Message ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/GlobalCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/GlobalCommandImplementor.cs new file mode 100644 index 000000000..b91ef5f47 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/GlobalCommandImplementor.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Commands.Generic +{ + public class GlobalCommandImplementor : BaseCommandImplementor + { + public GlobalCommandImplementor() + { + Accessors = new string[]{ "Global" }; + SupportRequirement = CommandSupport.Global; + SupportsConditionals = true; + AccessLevel = AccessLevel.Administrator; + Usage = "Global [condition]"; + Description = "Invokes the command on all appropriate objects in the world. Optional condition arguments can further restrict the set of objects."; + } + + public override void Compile( Mobile from, BaseCommand command, ref string[] args, ref object obj ) + { + try + { + Extensions ext = Extensions.Parse( from, ref args ); + + bool items, mobiles; + + if ( !CheckObjectTypes( command, ext, out items, out mobiles ) ) + return; + + ArrayList list = new ArrayList(); + + if ( items ) + { + foreach ( Item item in World.Items.Values ) + { + if ( ext.IsValid( item ) ) + list.Add( item ); + } + } + + if ( mobiles ) + { + foreach ( Mobile mob in World.Mobiles.Values ) + { + if ( ext.IsValid( mob ) ) + list.Add( mob ); + } + } + + ext.Filter( list ); + + obj = list; + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/MultiCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/MultiCommandImplementor.cs new file mode 100644 index 000000000..4a44c2f21 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/MultiCommandImplementor.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Commands.Generic +{ + public class MultiCommandImplementor : BaseCommandImplementor + { + public MultiCommandImplementor() + { + Accessors = new string[]{ "Multi", "m" }; + SupportRequirement = CommandSupport.Multi; + AccessLevel = AccessLevel.Counselor; + Usage = "Multi "; + Description = "Invokes the command on multiple targeted objects."; + } + + public override void Process( Mobile from, BaseCommand command, string[] args ) + { + if ( command.ValidateArgs( this, new CommandEventArgs( from, command.Commands[0], GenerateArgString( args ), args ) ) ) + from.BeginTarget( -1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None, new TargetStateCallback( OnTarget ), new object[]{ command, args } ); + } + + public void OnTarget( Mobile from, object targeted, object state ) + { + object[] states = (object[])state; + BaseCommand command = (BaseCommand)states[0]; + string[] args = (string[])states[1]; + + if ( !BaseCommand.IsAccessible( from, targeted ) ) + { + from.SendMessage( "That is not accessible." ); + from.BeginTarget( -1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None, new TargetStateCallback( OnTarget ), new object[]{ command, args } ); + return; + } + + switch ( command.ObjectTypes ) + { + case ObjectTypes.Both: + { + if ( !(targeted is Item) && !(targeted is Mobile) ) + { + from.SendMessage( "This command does not work on that." ); + return; + } + + break; + } + case ObjectTypes.Items: + { + if ( !(targeted is Item) ) + { + from.SendMessage( "This command only works on items." ); + return; + } + + break; + } + case ObjectTypes.Mobiles: + { + if ( !(targeted is Mobile) ) + { + from.SendMessage( "This command only works on mobiles." ); + return; + } + + break; + } + } + + RunCommand( from, targeted, command, args ); + + from.BeginTarget( -1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None, new TargetStateCallback( OnTarget ), new object[]{ command, args } ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/ObjectConditional.cs b/Scripts/Commands/Generic/Implementors/ObjectConditional.cs new file mode 100644 index 000000000..6a0959e1f --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/ObjectConditional.cs @@ -0,0 +1,266 @@ +using System; +using System.Reflection; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Targeting; +using CPA = Server.CommandPropertyAttribute; + +namespace Server.Commands.Generic +{ + public sealed class ObjectConditional + { + private static readonly Type typeofItem = typeof( Item ); + private static readonly Type typeofMobile = typeof( Mobile ); + + private Type m_ObjectType; + + private ICondition[][] m_Conditions; + + private IConditional[] m_Conditionals; + + public Type Type + { + get { return m_ObjectType; } + } + + public bool IsItem + { + get { return ( m_ObjectType == null || m_ObjectType == typeofItem || m_ObjectType.IsSubclassOf( typeofItem ) ); } + } + + public bool IsMobile + { + get { return ( m_ObjectType == null || m_ObjectType == typeofMobile || m_ObjectType.IsSubclassOf( typeofMobile ) ); } + } + + public static readonly ObjectConditional Empty = new ObjectConditional( null, null ); + + public bool HasCompiled + { + get { return ( m_Conditionals != null ); } + } + + public void Compile( ref AssemblyEmitter emitter ) + { + if ( emitter == null ) + emitter = new AssemblyEmitter( "__dynamic", false ); + + m_Conditionals = new IConditional[m_Conditions.Length]; + + for ( int i = 0; i < m_Conditionals.Length; ++i ) + m_Conditionals[i] = ConditionalCompiler.Compile( emitter, m_ObjectType, m_Conditions[i], i ); + } + + public bool CheckCondition( object obj ) + { + if ( m_ObjectType == null ) + return true; // null type means no condition + + if ( !HasCompiled ) + { + AssemblyEmitter emitter = null; + + Compile( ref emitter ); + } + + for ( int i = 0; i < m_Conditionals.Length; ++i ) + { + if ( m_Conditionals[i].Verify( obj ) ) + return true; + } + + return false; // all conditions false + } + + public static ObjectConditional Parse( Mobile from, ref string[] args ) + { + string[] conditionArgs = null; + + for ( int i = 0; i < args.Length; ++i ) + { + if ( Insensitive.Equals( args[i], "where" ) ) + { + string[] origArgs = args; + + args = new string[i]; + + for ( int j = 0; j < args.Length; ++j ) + args[j] = origArgs[j]; + + conditionArgs = new string[origArgs.Length - i - 1]; + + for ( int j = 0; j < conditionArgs.Length; ++j ) + conditionArgs[j] = origArgs[i + j + 1]; + + break; + } + } + + return ParseDirect( from, conditionArgs, 0, conditionArgs.Length ); + } + + public static ObjectConditional ParseDirect( Mobile from, string[] args, int offset, int size ) + { + if ( args == null || size == 0 ) + return ObjectConditional.Empty; + + int index = 0; + + Type objectType = ScriptCompiler.FindTypeByName( args[offset + index], true ); + + if ( objectType == null ) + throw new Exception( String.Format( "No type with that name ({0}) was found.", args[offset + index] ) ); + + ++index; + + List conditions = new List(); + List current = new List(); + + current.Add( TypeCondition.Default ); + + while ( index < size ) + { + string cur = args[offset + index]; + + bool inverse = false; + + if ( Insensitive.Equals( cur, "not" ) || cur == "!" ) + { + inverse = true; + ++index; + + if ( index >= size ) + throw new Exception( "Improperly formatted object conditional." ); + } + else if ( Insensitive.Equals( cur, "or" ) || cur == "||" ) + { + if ( conditions.Count > 1 ) + { + conditions.Add( current.ToArray() ); + + current.Clear(); + current.Add( TypeCondition.Default ); + } + + ++index; + + continue; + } + + string binding = args[offset + index]; + index++; + + if ( index >= size ) + throw new Exception( "Improperly formatted object conditional." ); + + string oper = args[offset + index]; + index++; + + if ( index >= size ) + throw new Exception( "Improperly formatted object conditional." ); + + string val = args[offset + index]; + index++; + + Property prop = new Property( binding ); + + prop.BindTo( objectType, PropertyAccess.Read ); + prop.CheckAccess( from ); + + ICondition condition = null; + + switch ( oper ) + { + #region Equality + case "=": + case "==": + case "is": + condition = new ComparisonCondition( prop, inverse, ComparisonOperator.Equal, val ); + break; + + case "!=": + condition = new ComparisonCondition( prop, inverse, ComparisonOperator.NotEqual, val ); + break; + #endregion + + #region Relational + case ">": + condition = new ComparisonCondition( prop, inverse, ComparisonOperator.Greater, val ); + break; + + case "<": + condition = new ComparisonCondition( prop, inverse, ComparisonOperator.Lesser, val ); + break; + + case ">=": + condition = new ComparisonCondition( prop, inverse, ComparisonOperator.GreaterEqual, val ); + break; + + case "<=": + condition = new ComparisonCondition( prop, inverse, ComparisonOperator.LesserEqual, val ); + break; + #endregion + + #region Strings + case "==~": + case "~==": + case "=~": + case "~=": + case "is~": + case "~is": + condition = new StringCondition( prop, inverse, StringOperator.Equal, val, true ); + break; + + case "!=~": + case "~!=": + condition = new StringCondition( prop, inverse, StringOperator.NotEqual, val, true ); + break; + + case "starts": + condition = new StringCondition( prop, inverse, StringOperator.StartsWith, val, false ); + break; + + case "starts~": + case "~starts": + condition = new StringCondition( prop, inverse, StringOperator.StartsWith, val, true ); + break; + + case "ends": + condition = new StringCondition( prop, inverse, StringOperator.EndsWith, val, false ); + break; + + case "ends~": + case "~ends": + condition = new StringCondition( prop, inverse, StringOperator.EndsWith, val, true ); + break; + + case "contains": + condition = new StringCondition( prop, inverse, StringOperator.Contains, val, false ); + break; + + case "contains~": + case "~contains": + condition = new StringCondition( prop, inverse, StringOperator.Contains, val, true ); + break; + #endregion + } + + if ( condition == null ) + throw new InvalidOperationException( String.Format( "Unrecognized operator (\"{0}\").", oper ) ); + + current.Add( condition ); + } + + conditions.Add( current.ToArray() ); + + return new ObjectConditional( objectType, conditions.ToArray() ); + } + + public ObjectConditional( Type objectType, ICondition[][] conditions ) + { + m_ObjectType = objectType; + m_Conditions = conditions; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs new file mode 100644 index 000000000..92429b544 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/OnlineCommandImplementor.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; + +namespace Server.Commands.Generic +{ + public class OnlineCommandImplementor : BaseCommandImplementor + { + public OnlineCommandImplementor() + { + Accessors = new string[]{ "Online" }; + SupportRequirement = CommandSupport.Online; + SupportsConditionals = true; + AccessLevel = AccessLevel.GameMaster; + Usage = "Online [condition]"; + Description = "Invokes the command on all mobiles that are currently logged in. Optional condition arguments can further restrict the set of objects."; + } + + public override void Compile( Mobile from, BaseCommand command, ref string[] args, ref object obj ) + { + try + { + Extensions ext = Extensions.Parse( from, ref args ); + + bool items, mobiles; + + if ( !CheckObjectTypes( command, ext, out items, out mobiles ) ) + return; + + if ( !mobiles ) // sanity check + { + command.LogFailure( "This command does not support mobiles." ); + return; + } + + ArrayList list = new ArrayList(); + + List states = NetState.Instances; + + for ( int i = 0; i < states.Count; ++i ) + { + NetState ns = states[i]; + Mobile mob = ns.Mobile; + + if ( mob == null ) + continue; + + if( !BaseCommand.IsAccessible( from, mob ) ) + continue; + + if ( ext.IsValid( mob ) ) + list.Add( mob ); + } + + ext.Filter( list ); + + obj = list; + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/RegionCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/RegionCommandImplementor.cs new file mode 100644 index 000000000..4fae4f0d5 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/RegionCommandImplementor.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Commands.Generic +{ + public class RegionCommandImplementor : BaseCommandImplementor + { + public RegionCommandImplementor() + { + Accessors = new string[]{ "Region" }; + SupportRequirement = CommandSupport.Region; + SupportsConditionals = true; + AccessLevel = AccessLevel.GameMaster; + Usage = "Region [condition]"; + Description = "Invokes the command on all appropriate mobiles in your current region. Optional condition arguments can further restrict the set of objects."; + } + + public override void Compile( Mobile from, BaseCommand command, ref string[] args, ref object obj ) + { + try + { + Extensions ext = Extensions.Parse( from, ref args ); + + bool items, mobiles; + + if ( !CheckObjectTypes( command, ext, out items, out mobiles ) ) + return; + + Region reg = from.Region; + + ArrayList list = new ArrayList(); + + if ( mobiles ) + { + foreach ( Mobile mob in reg.GetMobiles() ) + { + if( !BaseCommand.IsAccessible( from, mob ) ) + continue; + + if ( ext.IsValid( mob ) ) + list.Add( mob ); + } + } + else + { + command.LogFailure( "This command does not support items." ); + return; + } + + ext.Filter( list ); + + obj = list; + } + catch ( Exception ex ) + { + from.SendMessage( ex.Message ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/SelfCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/SelfCommandImplementor.cs new file mode 100644 index 000000000..d052f8b2a --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/SelfCommandImplementor.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Commands.Generic +{ + public class SelfCommandImplementor : BaseCommandImplementor + { + public SelfCommandImplementor() + { + Accessors = new string[]{ "Self" }; + SupportRequirement = CommandSupport.Self; + AccessLevel = AccessLevel.Counselor; + Usage = "Self "; + Description = "Invokes the command on the commanding player."; + } + + public override void Compile( Mobile from, BaseCommand command, ref string[] args, ref object obj ) + { + if ( command.ObjectTypes == ObjectTypes.Items ) + return; // sanity check + + obj = from; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/SerialCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/SerialCommandImplementor.cs new file mode 100644 index 000000000..fc1c06a9f --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/SerialCommandImplementor.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Commands.Generic +{ + public class SerialCommandImplementor : BaseCommandImplementor + { + public SerialCommandImplementor() + { + Accessors = new string[]{ "Serial" }; + SupportRequirement = CommandSupport.Single; + AccessLevel = AccessLevel.Counselor; + Usage = "Serial "; + Description = "Invokes the command on a single object by serial."; + } + + public override void Execute( CommandEventArgs e ) + { + if ( e.Length >= 2 ) + { + Serial serial = e.GetInt32( 0 ); + + object obj = null; + + if ( serial.IsItem ) + obj = World.FindItem( serial ); + else if ( serial.IsMobile ) + obj = World.FindMobile( serial ); + + if ( obj == null ) + { + e.Mobile.SendMessage( "That is not a valid serial." ); + } + else + { + BaseCommand command = (BaseCommand) this.Commands[e.GetString( 1 )]; + + if ( command == null ) + { + e.Mobile.SendMessage( "That is either an invalid command name or one that does not support this modifier." ); + } + else if ( e.Mobile.AccessLevel < command.AccessLevel ) + { + e.Mobile.SendMessage( "You do not have access to that command." ); + } + else + { + string[] oldArgs = e.Arguments; + string[] args = new string[oldArgs.Length - 2]; + + for ( int i = 0; i < args.Length; ++i ) + args[i] = oldArgs[i + 2]; + + RunCommand( e.Mobile, obj, command, args ); + } + } + } + else + { + e.Mobile.SendMessage( "You must supply an object serial and a command name." ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Generic/Implementors/SingleCommandImplementor.cs b/Scripts/Commands/Generic/Implementors/SingleCommandImplementor.cs new file mode 100644 index 000000000..7a9a0b5e9 --- /dev/null +++ b/Scripts/Commands/Generic/Implementors/SingleCommandImplementor.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Commands.Generic +{ + public class SingleCommandImplementor : BaseCommandImplementor + { + public SingleCommandImplementor() + { + Accessors = new string[]{ "Single" }; + SupportRequirement = CommandSupport.Single; + AccessLevel = AccessLevel.Counselor; + Usage = "Single "; + Description = "Invokes the command on a single targeted object. This is the same as just invoking the command directly."; + } + + public override void Register( BaseCommand command ) + { + base.Register( command ); + + for ( int i = 0; i < command.Commands.Length; ++i ) + CommandSystem.Register( command.Commands[i], command.AccessLevel, new CommandEventHandler( Redirect ) ); + } + + public void Redirect( CommandEventArgs e ) + { + BaseCommand command = (BaseCommand)Commands[e.Command]; + + if ( command == null ) + e.Mobile.SendMessage( "That is either an invalid command name or one that does not support this modifier." ); + else if ( e.Mobile.AccessLevel < command.AccessLevel ) + e.Mobile.SendMessage( "You do not have access to that command." ); + else if ( command.ValidateArgs( this, e ) ) + Process( e.Mobile, command, e.Arguments ); + } + + public override void Process( Mobile from, BaseCommand command, string[] args ) + { + if ( command.ValidateArgs( this, new CommandEventArgs( from, command.Commands[0], GenerateArgString( args ), args ) ) ) + from.BeginTarget( -1, command.ObjectTypes == ObjectTypes.All, TargetFlags.None, new TargetStateCallback( OnTarget ), new object[]{ command, args } ); + } + + public void OnTarget( Mobile from, object targeted, object state ) + { + if ( !BaseCommand.IsAccessible( from, targeted ) ) + { + from.SendMessage( "That is not accessible." ); + return; + } + + object[] states = (object[])state; + BaseCommand command = (BaseCommand)states[0]; + string[] args = (string[])states[1]; + + switch ( command.ObjectTypes ) + { + case ObjectTypes.Both: + { + if ( !(targeted is Item) && !(targeted is Mobile) ) + { + from.SendMessage( "This command does not work on that." ); + return; + } + + break; + } + case ObjectTypes.Items: + { + if ( !(targeted is Item) ) + { + from.SendMessage( "This command only works on items." ); + return; + } + + break; + } + case ObjectTypes.Mobiles: + { + if ( !(targeted is Mobile) ) + { + from.SendMessage( "This command only works on mobiles." ); + return; + } + + break; + } + } + + RunCommand( from, targeted, command, args ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Handlers.cs b/Scripts/Commands/Handlers.cs new file mode 100644 index 000000000..dfb200624 --- /dev/null +++ b/Scripts/Commands/Handlers.cs @@ -0,0 +1,1496 @@ +using System; +using System.IO; +using Server; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using Server.Accounting; +using Server.Mobiles; +using Server.Items; +using Server.Menus; +using Server.Menus.Questions; +using Server.Menus.ItemLists; +using Server.Network; +using Server.Spells; +using Server.Targeting; +using Server.Targets; +using Server.Gumps; +using Server.Commands.Generic; + +namespace Server.Commands +{ + public class CommandHandlers + { + public static void Initialize() + { + CommandSystem.Prefix = "["; + + Properties.Register(); + + Register( "Go", AccessLevel.Counselor, new CommandEventHandler( Go_OnCommand ) ); + + Register( "DropHolding", AccessLevel.Counselor, new CommandEventHandler( DropHolding_OnCommand ) ); + + Register( "GetFollowers", AccessLevel.GameMaster, new CommandEventHandler( GetFollowers_OnCommand ) ); + + Register( "ClearFacet", AccessLevel.Administrator, new CommandEventHandler( ClearFacet_OnCommand ) ); + + Register( "Where", AccessLevel.Counselor, new CommandEventHandler( Where_OnCommand ) ); + + Register( "AutoPageNotify", AccessLevel.Counselor, new CommandEventHandler( APN_OnCommand ) ); + Register( "APN", AccessLevel.Counselor, new CommandEventHandler( APN_OnCommand ) ); + + Register( "Animate", AccessLevel.GameMaster, new CommandEventHandler( Animate_OnCommand ) ); + + Register( "Cast", AccessLevel.Counselor, new CommandEventHandler( Cast_OnCommand ) ); + + Register( "Stuck", AccessLevel.Counselor, new CommandEventHandler( Stuck_OnCommand ) ); + + Register( "Help", AccessLevel.Player, new CommandEventHandler( Help_OnCommand ) ); + + Register( "Save", AccessLevel.Administrator, new CommandEventHandler( Save_OnCommand ) ); + + Register( "Move", AccessLevel.GameMaster, new CommandEventHandler( Move_OnCommand ) ); + Register( "Client", AccessLevel.Counselor, new CommandEventHandler( Client_OnCommand ) ); + + Register( "SMsg", AccessLevel.Counselor, new CommandEventHandler( StaffMessage_OnCommand ) ); + Register( "SM", AccessLevel.Counselor, new CommandEventHandler( StaffMessage_OnCommand ) ); + Register( "S", AccessLevel.Counselor, new CommandEventHandler( StaffMessage_OnCommand ) ); + + Register( "BCast", AccessLevel.GameMaster, new CommandEventHandler( BroadcastMessage_OnCommand ) ); + Register( "BC", AccessLevel.GameMaster, new CommandEventHandler( BroadcastMessage_OnCommand ) ); + Register( "B", AccessLevel.GameMaster, new CommandEventHandler( BroadcastMessage_OnCommand ) ); + + Register( "Bank", AccessLevel.GameMaster, new CommandEventHandler( Bank_OnCommand ) ); + + Register( "Echo", AccessLevel.Counselor, new CommandEventHandler( Echo_OnCommand ) ); + + Register( "Sound", AccessLevel.GameMaster, new CommandEventHandler( Sound_OnCommand ) ); + + Register( "ViewEquip", AccessLevel.GameMaster, new CommandEventHandler( ViewEquip_OnCommand ) ); + + Register( "DumpTimers", AccessLevel.Administrator, new CommandEventHandler( DumpTimers_OnCommand ) ); + Register( "CountObjects", AccessLevel.Administrator, new CommandEventHandler( CountObjects_OnCommand ) ); + Register( "ProfileWorld", AccessLevel.Administrator, new CommandEventHandler( ProfileWorld_OnCommand ) ); + Register( "TraceInternal", AccessLevel.Administrator, new CommandEventHandler( TraceInternal_OnCommand ) ); + Register( "TraceExpanded", AccessLevel.Administrator, new CommandEventHandler( TraceExpanded_OnCommand ) ); + Register( "PacketProfiles", AccessLevel.Administrator, new CommandEventHandler( PacketProfiles_OnCommand ) ); + Register( "TimerProfiles", AccessLevel.Administrator, new CommandEventHandler( TimerProfiles_OnCommand ) ); + Register( "SetProfiles", AccessLevel.Administrator, new CommandEventHandler( SetProfiles_OnCommand ) ); + + Register( "Light", AccessLevel.Counselor, new CommandEventHandler( Light_OnCommand ) ); + Register( "Stats", AccessLevel.Counselor, new CommandEventHandler( Stats_OnCommand ) ); + + Register( "ReplaceBankers", AccessLevel.Administrator, new CommandEventHandler( ReplaceBankers_OnCommand ) ); + + Register( "SpeedBoost", AccessLevel.Counselor, new CommandEventHandler( SpeedBoost_OnCommand ) ); + } + + public static void Register( string command, AccessLevel access, CommandEventHandler handler ) + { + CommandSystem.Register( command, access, handler ); + } + + [Usage( "SpeedBoost [true|false]" )] + [Description( "Enables a speed boost for the invoker. Disable with paramaters." )] + private static void SpeedBoost_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + + if ( e.Length <= 1 ) + { + if ( e.Length == 1 && !e.GetBoolean( 0 ) ) + { + from.Send( SpeedBoost.Disabled ); + from.SendMessage( "Speed boost has been disabled." ); + } + else + { + from.Send( SpeedBoost.Enabled ); + from.SendMessage( "Speed boost has been enabled." ); + } + } + else + { + from.SendMessage( "Format: SpeedBoost [true|false]" ); + } + } + + [Usage( "Where" )] + [Description( "Tells the commanding player his coordinates, region, and facet." )] + public static void Where_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + Map map = from.Map; + + from.SendMessage( "You are at {0} {1} {2} in {3}.", from.X, from.Y, from.Z, map ); + + if ( map != null ) + { + Region reg = from.Region; + + if ( !reg.IsDefault ) + { + StringBuilder builder = new StringBuilder(); + + builder.Append( reg.ToString() ); + reg = reg.Parent; + + while ( reg != null ) + { + builder.Append( " <- " + reg.ToString() ); + reg = reg.Parent; + } + + from.SendMessage( "Your region is {0}.", builder.ToString() ); + } + } + } + + [Usage( "DropHolding" )] + [Description( "Drops the item, if any, that a targeted player is holding. The item is placed into their backpack, or if that's full, at their feet." )] + public static void DropHolding_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( DropHolding_OnTarget ) ); + e.Mobile.SendMessage( "Target the player to drop what they are holding." ); + } + + public static void DropHolding_OnTarget( Mobile from, object obj ) + { + if ( obj is Mobile && ((Mobile)obj).Player ) + { + Mobile targ = (Mobile)obj; + Item held = targ.Holding; + + if ( held == null ) + { + from.SendMessage( "They are not holding anything." ); + } + else + { + if ( from.AccessLevel == AccessLevel.Counselor ) + { + Engines.Help.PageEntry pe = Engines.Help.PageQueue.GetEntry( targ ); + + if ( pe == null || pe.Handler != from ) + { + if ( pe == null ) + from.SendMessage( "You may only use this command on someone who has paged you." ); + else + from.SendMessage( "You may only use this command if you are handling their help page." ); + + return; + } + } + + if ( targ.AddToBackpack( held ) ) + from.SendMessage( "The item they were holding has been placed into their backpack." ); + else + from.SendMessage( "The item they were holding has been placed at their feet." ); + + held.ClearBounce(); + + targ.Holding = null; + } + } + else + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( DropHolding_OnTarget ) ); + from.SendMessage( "That is not a player. Try again." ); + } + } + + public static void DeleteList_Callback( Mobile from, bool okay, object state ) + { + if ( okay ) + { + ArrayList list = (ArrayList)state; + + CommandLogging.WriteLine( from, "{0} {1} deleting {2} objects", from.AccessLevel, CommandLogging.Format( from ), list.Count ); + + NetState.Pause(); + + for ( int i = 0; i < list.Count; ++i ) + { + object obj = list[i]; + + if ( obj is Item ) + ((Item)obj).Delete(); + else if ( obj is Mobile ) + ((Mobile)obj).Delete(); + } + + NetState.Resume(); + + from.SendMessage( "You have deleted {0} object{1}.", list.Count, list.Count == 1 ? "" : "s" ); + } + else + { + from.SendMessage( "You have chosen not to delete those objects." ); + } + } + + [Usage( "ClearFacet" )] + [Description( "Deletes all items and mobiles in your facet. Players and their inventory will not be deleted." )] + public static void ClearFacet_OnCommand( CommandEventArgs e ) + { + Map map = e.Mobile.Map; + + if ( map == null || map == Map.Internal ) + { + e.Mobile.SendMessage( "You may not run that command here." ); + return; + } + + ArrayList list = new ArrayList(); + + foreach ( Item item in World.Items.Values ) + { + if ( item.Map == map && item.Parent == null ) + list.Add( item ); + } + + foreach ( Mobile m in World.Mobiles.Values ) + { + if ( m.Map == map && !m.Player ) + list.Add( m ); + } + + if ( list.Count > 0 ) + { + CommandLogging.WriteLine( e.Mobile, "{0} {1} starting facet clear of {2} ({3} objects)", e.Mobile.AccessLevel, CommandLogging.Format( e.Mobile ), map, list.Count ); + + e.Mobile.SendGump( + new WarningGump( 1060635, 30720, + String.Format( "You are about to delete {0} object{1} from this facet. Do you really wish to continue?", + list.Count, list.Count == 1 ? "" : "s" ), + 0xFFC000, 360, 260, new WarningGumpCallback( DeleteList_Callback ), list ) ); + } + else + { + e.Mobile.SendMessage( "There were no objects found to delete." ); + } + } + + [Usage( "GetFollowers" )] + [Description( "Teleports all pets of a targeted player to your location." )] + public static void GetFollowers_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( GetFollowers_OnTarget ) ); + e.Mobile.SendMessage( "Target a player to get their pets." ); + } + + public static void GetFollowers_OnTarget( Mobile from, object obj ) + { + if ( obj is Mobile && ((Mobile)obj).Player ) + { + Mobile master = (Mobile)obj; + ArrayList pets = new ArrayList(); + + foreach ( Mobile m in World.Mobiles.Values ) + { + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + if ( (bc.Controlled && bc.ControlMaster == master) || (bc.Summoned && bc.SummonMaster == master) ) + pets.Add( bc ); + } + } + + if ( pets.Count > 0 ) + { + CommandLogging.WriteLine( from, "{0} {1} getting all followers of {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( master ) ); + + from.SendMessage( "That player has {0} pet{1}.", pets.Count, pets.Count != 1 ? "s" : "" ); + + for ( int i = 0; i < pets.Count; ++i ) + { + Mobile pet = (Mobile)pets[i]; + + if ( pet is IMount ) + ((IMount)pet).Rider = null; // make sure it's dismounted + + pet.MoveToWorld( from.Location, from.Map ); + } + } + else + { + from.SendMessage( "There were no pets found for that player." ); + } + } + else + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( GetFollowers_OnTarget ) ); + from.SendMessage( "That is not a player. Try again." ); + } + } + + public static void ReplaceBankers_OnCommand( CommandEventArgs e ) + { + ArrayList list = new ArrayList(); + + foreach ( Mobile m in World.Mobiles.Values ) + { + if ( (m is Banker) && !(m is BaseCreature) ) + list.Add( m ); + } + + foreach ( Mobile m in list ) + { + Map map = m.Map; + + if ( map != null ) + { + bool hasBankerSpawner = false; + + foreach ( Item item in m.GetItemsInRange( 0 ) ) + { + if ( item is Spawner ) + { + Spawner spawner = (Spawner)item; + + for ( int i = 0; !hasBankerSpawner && i < spawner.CreaturesName.Count; ++i ) + hasBankerSpawner = Insensitive.Equals( (string)spawner.CreaturesName[i], "banker" ); + + if ( hasBankerSpawner ) + break; + } + } + + if ( !hasBankerSpawner ) + { + Spawner spawner = new Spawner( 1, 1, 5, 0, 4, "banker" ); + + spawner.MoveToWorld( m.Location, map ); + } + } + } + } + + private class ViewEqTarget : Target + { + public ViewEqTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !BaseCommand.IsAccessible( from, targeted ) ) + { + from.SendMessage( "That is not accessible." ); + return; + } + + if ( targeted is Mobile ) + from.SendMenu( new EquipMenu( from, (Mobile)targeted, GetEquip( (Mobile)targeted ) ) ); + } + + private static ItemListEntry[] GetEquip( Mobile m ) + { + ItemListEntry[] entries = new ItemListEntry[m.Items.Count]; + + for ( int i = 0; i < m.Items.Count; ++i ) + { + Item item = m.Items[i]; + + entries[i] = new ItemListEntry( String.Format( "{0}: {1}", item.Layer, item.GetType().Name ), item.ItemID, item.Hue ); + } + + return entries; + } + + private class EquipMenu : ItemListMenu + { + private Mobile m_Mobile; + + public EquipMenu( Mobile from, Mobile m, ItemListEntry[] entries ) : base( "Equipment", entries ) + { + m_Mobile = m; + + CommandLogging.WriteLine( from, "{0} {1} getting equip for {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( m ) ); + } + + public override void OnResponse( NetState state, int index ) + { + if ( index >= 0 && index < m_Mobile.Items.Count ) + { + Item item = m_Mobile.Items[index]; + + state.Mobile.SendMenu( new EquipDetailsMenu( m_Mobile, item ) ); + } + } + + private class EquipDetailsMenu : QuestionMenu + { + private Mobile m_Mobile; + private Item m_Item; + + public EquipDetailsMenu( Mobile m, Item item ) : base( String.Format( "{0}: {1}", item.Layer, item.GetType().Name ), new string[]{"Move","Delete","Props"}) + { + m_Mobile = m; + m_Item = item; + } + + public override void OnCancel( NetState state ) + { + state.Mobile.SendMenu( new EquipMenu( state.Mobile, m_Mobile, ViewEqTarget.GetEquip( m_Mobile ) ) ); + } + + public override void OnResponse( NetState state, int index ) + { + if ( index == 0 ) + { + CommandLogging.WriteLine( state.Mobile, "{0} {1} moving equip item {2} of {3}", state.Mobile.AccessLevel, CommandLogging.Format( state.Mobile ), CommandLogging.Format( m_Item ), CommandLogging.Format( m_Mobile ) ); + state.Mobile.Target = new MoveTarget( m_Item ); + } + else if ( index == 1 ) + { + CommandLogging.WriteLine( state.Mobile, "{0} {1} deleting equip item {2} of {3}", state.Mobile.AccessLevel, CommandLogging.Format( state.Mobile ), CommandLogging.Format( m_Item ), CommandLogging.Format( m_Mobile ) ); + m_Item.Delete(); + } + else if ( index == 2 ) + { + CommandLogging.WriteLine( state.Mobile, "{0} {1} opening props for equip item {2} of {3}", state.Mobile.AccessLevel, CommandLogging.Format( state.Mobile ), CommandLogging.Format( m_Item ), CommandLogging.Format( m_Mobile ) ); + state.Mobile.SendGump( new PropertiesGump( state.Mobile, m_Item ) ); + } + } + } + } + } + + [Usage( "PacketProfiles" )] + [Description( "Generates a log file containing performance information pertaining to networking data packets." )] + public static void PacketProfiles_OnCommand( CommandEventArgs e ) + { + try + { + using ( StreamWriter sw = new StreamWriter( "packetprofiles.log", true ) ) + { + sw.WriteLine( "# Dump on {0:f}", DateTime.Now ); + sw.WriteLine( "# Core profiling for " + Core.ProfileTime ); + + PacketProfile[] profiles = PacketProfile.OutgoingProfiles; + + int totalSeconds = (int) Core.ProfileTime.TotalSeconds; + + if ( totalSeconds < 1 ) + totalSeconds = 1; + + sw.WriteLine(); + sw.WriteLine( "# Outgoing:" ); + + for ( int i = 0; i < profiles.Length; ++i ) + { + PacketProfile prof = profiles[i]; + + if ( prof == null ) + continue; + + sw.WriteLine( "0x{0,-10:X2} {6,10} {1,-10} {2,10} {3,-10:F2} {4,10:F5} {5,-10:F5} {7,10} {8,-10} {9,10} {10,10:F5} {11:F5}", i, prof.Count, prof.TotalByteLength, prof.AverageByteLength, prof.TotalProcTime.TotalSeconds, prof.AverageProcTime.TotalSeconds, prof.Constructed, prof.Constructed / totalSeconds, prof.Count / totalSeconds, prof.TotalByteLength / totalSeconds, prof.TotalProcTime.TotalSeconds / totalSeconds, prof.PeakProcTime.TotalSeconds ); + } + + profiles = PacketProfile.IncomingProfiles; + + sw.WriteLine(); + sw.WriteLine( "# Incoming:" ); + + for ( int i = 0; i < profiles.Length; ++i ) + { + PacketProfile prof = profiles[i]; + + if ( prof == null ) + continue; + + sw.WriteLine( "0x{0,-10:X2} {1,-10} {2,10} {3,-10:F2} {4,10:F5} {5:F5} {6:F5}", i, prof.Count, prof.TotalByteLength, prof.AverageByteLength, prof.TotalProcTime.TotalSeconds, prof.AverageProcTime.TotalSeconds, prof.PeakProcTime.TotalSeconds ); + } + + sw.WriteLine(); + sw.WriteLine(); + } + } + catch + { + } + } + + [Usage( "TimerProfiles" )] + [Description( "Generates a log file containing performance information pertaining to timers." )] + public static void TimerProfiles_OnCommand( CommandEventArgs e ) + { + try + { + using ( StreamWriter sw = new StreamWriter( "timerprofiles.log", true ) ) + { + Dictionary profiles = Timer.Profiles; + + sw.WriteLine( "# Dump on {0:f}", DateTime.Now ); + sw.WriteLine( "# Core profiling for " + Core.ProfileTime ); + sw.WriteLine(); + + foreach ( KeyValuePair kvp in profiles ) + { + string name = kvp.Key; + TimerProfile prof = kvp.Value; + + sw.WriteLine( "{6,-100}{0,-12}{1,12} {2,-12}{3,12} {4,-12:F5}{5:F5} {7:F5}", prof.Created, prof.Started, prof.Stopped, prof.Ticked, prof.TotalProcTime.TotalSeconds, prof.AverageProcTime.TotalSeconds, name, prof.PeakProcTime.TotalSeconds ); + } + + sw.WriteLine(); + sw.WriteLine(); + } + } + catch + { + } + } + + [Usage( "SetProfiles [true | false]" )] + [Description( "Enables, disables, or toggles the state of core packet and timer profiling." )] + public static void SetProfiles_OnCommand( CommandEventArgs e ) + { + if ( e.Length == 1 ) + Core.Profiling = e.GetBoolean( 0 ); + else + Core.Profiling = !Core.Profiling; + + e.Mobile.SendMessage( "Profiling has been {0}.", Core.Profiling ? "enabled" : "disabled" ); + } + + [Usage( "DumpTimers" )] + [Description( "Generates a log file of all currently executing timers. Used for tracing timer leaks." )] + public static void DumpTimers_OnCommand( CommandEventArgs e ) + { + try + { + using ( StreamWriter sw = new StreamWriter( "timerdump.log", true ) ) + Timer.DumpInfo( sw ); + } + catch + { + } + } + + private class CountSorter : IComparer + { + public int Compare( object x, object y ) + { + DictionaryEntry a = (DictionaryEntry)x; + DictionaryEntry b = (DictionaryEntry)y; + + int aCount = GetCount( a.Value ); + int bCount = GetCount( b.Value ); + + int v = -aCount.CompareTo( bCount ); + + if ( v == 0 ) + { + Type aType = (Type)a.Key; + Type bType = (Type)b.Key; + + v = aType.FullName.CompareTo( bType.FullName ); + } + + return v; + } + + private int GetCount( object obj ) + { + if ( obj is int ) + return (int) obj; + + if ( obj is int[] ) + { + int[] list = (int[]) obj; + + int total = 0; + + for ( int i = 0; i < list.Length; ++i ) + total += list[i]; + + return total; + } + + return 0; + } + } + + [Usage( "CountObjects" )] + [Description( "Generates a log file detailing all item and mobile types in the world." )] + public static void CountObjects_OnCommand( CommandEventArgs e ) + { + using ( StreamWriter op = new StreamWriter( "objects.log" ) ) + { + Hashtable table = new Hashtable(); + + foreach ( Item item in World.Items.Values ) + { + Type type = item.GetType(); + + object o = (object)table[type]; + + if ( o == null ) + table[type] = 1; + else + table[type] = 1 + (int)o; + } + + ArrayList items = new ArrayList( table ); + + table.Clear(); + + foreach ( Mobile m in World.Mobiles.Values ) + { + Type type = m.GetType(); + + object o = (object)table[type]; + + if ( o == null ) + table[type] = 1; + else + table[type] = 1 + (int)o; + } + + ArrayList mobiles = new ArrayList( table ); + + items.Sort( new CountSorter() ); + mobiles.Sort( new CountSorter() ); + + op.WriteLine( "# Object count table generated on {0}", DateTime.Now ); + op.WriteLine(); + op.WriteLine(); + + op.WriteLine( "# Items:" ); + + foreach ( DictionaryEntry de in items ) + op.WriteLine( "{0}\t{1:F2}%\t{2}", de.Value, (100 * (int)de.Value) / (double)World.Items.Count, de.Key ); + + op.WriteLine(); + op.WriteLine(); + + op.WriteLine( "#Mobiles:" ); + + foreach ( DictionaryEntry de in mobiles ) + op.WriteLine( "{0}\t{1:F2}%\t{2}", de.Value, (100 * (int)de.Value) / (double)World.Mobiles.Count, de.Key ); + } + + e.Mobile.SendMessage( "Object table has been generated. See the file : /objects.log" ); + } + + [Usage( "TraceExpanded" )] + [Description( "Generates a log file describing all items using expanded memory." )] + public static void TraceExpanded_OnCommand( CommandEventArgs e ) + { + Hashtable typeTable = new Hashtable(); + + foreach ( Item item in World.Items.Values ) + { + ExpandFlag flags = item.GetExpandFlags(); + + if ( ( flags & ~(ExpandFlag.TempFlag | ExpandFlag.SaveFlag) ) == 0 ) + continue; + + Type itemType = item.GetType(); + + do + { + int[] countTable = typeTable[itemType] as int[]; + + if ( countTable == null ) + typeTable[itemType] = countTable = new int[8]; + + if ( ( flags & ExpandFlag.Name ) != 0 ) + ++countTable[0]; + + if ( ( flags & ExpandFlag.Items ) != 0 ) + ++countTable[1]; + + if ( ( flags & ExpandFlag.Bounce ) != 0 ) + ++countTable[2]; + + if ( ( flags & ExpandFlag.Holder ) != 0 ) + ++countTable[3]; + + if ( ( flags & ExpandFlag.Blessed ) != 0 ) + ++countTable[4]; + + /*if ( ( flags & ExpandFlag.TempFlag ) != 0 ) + ++countTable[5]; + + if ( ( flags & ExpandFlag.SaveFlag ) != 0 ) + ++countTable[6];*/ + + if ( ( flags & ExpandFlag.Weight ) != 0 ) + ++countTable[7]; + + itemType = itemType.BaseType; + } while ( itemType != typeof( object ) ); + } + + try + { + using ( StreamWriter op = new StreamWriter( "expandedItems.log", true ) ) + { + string[] names = new string[] + { + "Name", + "Items", + "Bounce", + "Holder", + "Blessed", + "TempFlag", + "SaveFlag", + "Weight" + }; + + ArrayList list = new ArrayList( typeTable ); + + list.Sort( new CountSorter() ); + + foreach ( DictionaryEntry de in list ) + { + Type itemType = de.Key as Type; + int[] countTable = de.Value as int[]; + + op.WriteLine( "# {0}", itemType.FullName ); + + for ( int i = 0; i < countTable.Length; ++i ) + { + if ( countTable[i] > 0 ) + op.WriteLine( "{0}\t{1:N0}", names[i], countTable[i] ); + } + + op.WriteLine(); + } + } + } + catch + { + } + } + + [Usage( "TraceInternal" )] + [Description( "Generates a log file describing all items in the 'internal' map." )] + public static void TraceInternal_OnCommand( CommandEventArgs e ) + { + int totalCount = 0; + Hashtable table = new Hashtable(); + + foreach ( Item item in World.Items.Values ) + { + if ( item.Parent != null || item.Map != Map.Internal ) + continue; + + ++totalCount; + + Type type = item.GetType(); + int[] parms = (int[])table[type]; + + if ( parms == null ) + table[type] = parms = new int[]{ 0, 0 }; + + parms[0]++; + parms[1] += item.Amount; + } + + using ( StreamWriter op = new StreamWriter( "internal.log" ) ) + { + op.WriteLine( "# {0} items found", totalCount ); + op.WriteLine( "# {0} different types", table.Count ); + op.WriteLine(); + op.WriteLine(); + op.WriteLine( "Type\t\tCount\t\tAmount\t\tAvg. Amount" ); + + foreach ( DictionaryEntry de in table ) + { + Type type = (Type)de.Key; + int[] parms = (int[])de.Value; + + op.WriteLine( "{0}\t\t{1}\t\t{2}\t\t{3:F2}", type.Name, parms[0], parms[1], (double)parms[1] / parms[0] ); + } + } + } + + [Usage( "ProfileWorld" )] + [Description( "Prints the amount of data serialized for every object type in your world file." )] + public static void ProfileWorld_OnCommand( CommandEventArgs e ) + { + ProfileWorld( "items", "worldprofile_items.log" ); + ProfileWorld( "mobiles", "worldprofile_mobiles.log" ); + } + + public static void ProfileWorld( string type, string opFile ) + { + try + { + ArrayList types = new ArrayList(); + + using ( BinaryReader bin = new BinaryReader( new FileStream( String.Format( "Saves/{0}/{0}.tdb", type ), FileMode.Open, FileAccess.Read, FileShare.Read ) ) ) + { + int count = bin.ReadInt32(); + + for ( int i = 0; i < count; ++i ) + types.Add( ScriptCompiler.FindTypeByFullName( bin.ReadString() ) ); + } + + long total = 0; + + Hashtable table = new Hashtable(); + + using ( BinaryReader bin = new BinaryReader( new FileStream( String.Format( "Saves/{0}/{0}.idx", type ), FileMode.Open, FileAccess.Read, FileShare.Read ) ) ) + { + int count = bin.ReadInt32(); + + for ( int i = 0; i < count; ++i ) + { + int typeID = bin.ReadInt32(); + int serial = bin.ReadInt32(); + long pos = bin.ReadInt64(); + int length = bin.ReadInt32(); + Type objType = (Type)types[typeID]; + + while ( objType != null && objType != typeof( object ) ) + { + object obj = table[objType]; + + if ( obj == null ) + table[objType] = length; + else + table[objType] = length + (int)obj; + + objType = objType.BaseType; + total += length; + } + } + } + + ArrayList list = new ArrayList( table ); + + list.Sort( new CountSorter() ); + + using ( StreamWriter op = new StreamWriter( opFile ) ) + { + op.WriteLine( "# Profile of world {0}", type ); + op.WriteLine( "# Generated on {0}", DateTime.Now ); + op.WriteLine(); + op.WriteLine(); + + foreach ( DictionaryEntry de in list ) + op.WriteLine( "{0}\t{1:F2}%\t{2}", de.Value, (100 * (int)de.Value) / (double)total, de.Key ); + } + } + catch + { + } + } + + [Usage( "ViewEquip" )] + [Description( "Lists equipment of a targeted mobile. From the list you can move, delete, or open props." )] + public static void ViewEquip_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new ViewEqTarget(); + } + + [Usage( "Sound [toAll=true]" )] + [Description( "Plays a sound to players within 12 tiles of you. The (toAll) argument specifies to everyone, or just those who can see you." )] + public static void Sound_OnCommand( CommandEventArgs e ) + { + if ( e.Length == 1 ) + PlaySound( e.Mobile, e.GetInt32( 0 ), true ); + else if ( e.Length == 2 ) + PlaySound( e.Mobile, e.GetInt32( 0 ), e.GetBoolean( 1 ) ); + else + e.Mobile.SendMessage( "Format: Sound [toAll]" ); + } + + private static void PlaySound( Mobile m, int index, bool toAll ) + { + Map map = m.Map; + + if ( map == null ) + return; + + CommandLogging.WriteLine( m, "{0} {1} playing sound {2} (toAll={3})", m.AccessLevel, CommandLogging.Format( m ), index, toAll ); + + Packet p = new PlaySound( index, m.Location ); + + p.Acquire(); + + foreach ( NetState state in m.GetClientsInRange( 12 ) ) + { + if ( toAll || state.Mobile.CanSee( m ) ) + state.Send( p ); + } + + p.Release(); + } + + private class BankTarget : Target + { + public BankTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + Mobile m = (Mobile)targeted; + + BankBox box = ( m.Player ? m.BankBox : m.FindBankNoCreate() ); + + if ( box != null ) + { + CommandLogging.WriteLine( from, "{0} {1} opening bank box of {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targeted ) ); + + if ( from == targeted ) + box.Open(); + else + box.DisplayTo( from ); + } + else + { + from.SendMessage( "They have no bank box." ); + } + } + } + } + + [Usage( "Echo " )] + [Description( "Relays (text) as a system message." )] + public static void Echo_OnCommand( CommandEventArgs e ) + { + string toEcho = e.ArgString.Trim(); + + if ( toEcho.Length > 0 ) + e.Mobile.SendMessage( toEcho ); + else + e.Mobile.SendMessage( "Format: Echo \"\"" ); + } + + [Usage( "Bank" )] + [Description( "Opens the bank box of a given target." )] + public static void Bank_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new BankTarget(); + } + + private class DismountTarget : Target + { + public DismountTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + CommandLogging.WriteLine( from, "{0} {1} dismounting {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targeted ) ); + + Mobile targ = (Mobile)targeted; + + for ( int i = 0; i < targ.Items.Count; ++i ) + { + Item item = targ.Items[i]; + + if ( item is IMountItem ) + { + IMount mount = ((IMountItem)item).Mount; + + if ( mount != null ) + mount.Rider = null; + + if ( targ.Items.IndexOf( item ) == -1 ) + --i; + } + } + + for ( int i = 0; i < targ.Items.Count; ++i ) + { + Item item = targ.Items[i]; + + if ( item.Layer == Layer.Mount ) + { + item.Delete(); + --i; + } + } + } + } + } + + private class ClientTarget : Target + { + public ClientTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + + if ( targ.NetState != null ) + { + CommandLogging.WriteLine( from, "{0} {1} opening client menu of {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targeted ) ); + from.SendGump( new ClientGump( from, targ.NetState ) ); + } + } + } + } + + [Usage( "Client" )] + [Description( "Opens the client gump menu for a given player." )] + private static void Client_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new ClientTarget(); + } + + [Usage( "Move" )] + [Description( "Repositions a targeted item or mobile." )] + private static void Move_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new PickMoveTarget(); + } + + private class FirewallTarget : Target + { + public FirewallTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + + NetState state = targ.NetState; + + if ( state != null ) + { + CommandLogging.WriteLine( from, "{0} {1} firewalling {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targeted ) ); + + try + { + Firewall.Add( ((IPEndPoint)state.Socket.RemoteEndPoint).Address ); + } + catch + { + } + } + } + } + } + + [Usage( "Save" )] + [Description( "Saves the world." )] + private static void Save_OnCommand( CommandEventArgs e ) + { + Misc.AutoSave.Save(); + } + + private static bool FixMap( ref Map map, ref Point3D loc, Item item ) + { + if ( map == null || map == Map.Internal ) + { + Mobile m = item.RootParent as Mobile; + + return ( m != null && FixMap( ref map, ref loc, m ) ); + } + + return true; + } + + private static bool FixMap( ref Map map, ref Point3D loc, Mobile m ) + { + if ( map == null || map == Map.Internal ) + { + map = m.LogoutMap; + loc = m.LogoutLocation; + } + + return ( map != null && map != Map.Internal ); + } + + [Usage( "Go [name | serial | (x y [z]) | (deg min (N | S) deg min (E | W))]" )] + [Description( "With no arguments, this command brings up the go menu. With one argument, (name), you are moved to that regions \"go location.\" Or, if a numerical value is specified for one argument, (serial), you are moved to that object. Two or three arguments, (x y [z]), will move your character to that location. When six arguments are specified, (deg min (N | S) deg min (E | W)), your character will go to an approximate of those sextant coordinates." )] + private static void Go_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + + if ( e.Length == 0 ) + { + GoGump.DisplayTo( from ); + return; + } + + if ( GoGump.MoveToLocation( from, e.ArgString ) ) + return; + + + if ( e.Length == 1 ) + { + try + { + int ser = e.GetInt32( 0 ); + + IEntity ent = World.FindEntity( ser ); + + if ( ent is Item ) + { + Item item = (Item)ent; + + Map map = item.Map; + Point3D loc = item.GetWorldLocation(); + + Mobile owner = item.RootParent as Mobile; + + if( owner != null && (owner.Map != null && owner.Map != Map.Internal) && !BaseCommand.IsAccessible( from, owner ) /* !from.CanSee( owner )*/ ) + { + from.SendMessage( "You can not go to what you can not see." ); + return; + } + else if ( owner != null && (owner.Map == null || owner.Map == Map.Internal) && owner.Hidden && owner.AccessLevel >= from.AccessLevel ) + { + from.SendMessage( "You can not go to what you can not see." ); + return; + } + else if ( !FixMap( ref map, ref loc, item ) ) + { + from.SendMessage( "That is an internal item and you cannot go to it." ); + return; + } + + from.MoveToWorld( loc, map ); + + return; + } + else if ( ent is Mobile ) + { + Mobile m = (Mobile)ent; + + Map map = m.Map; + Point3D loc = m.Location; + + Mobile owner = m; + + if ( owner != null && (owner.Map != null && owner.Map != Map.Internal) && !BaseCommand.IsAccessible( from, owner ) /* !from.CanSee( owner )*/ ) + { + from.SendMessage( "You can not go to what you can not see." ); + return; + } + else if ( owner != null && (owner.Map == null || owner.Map == Map.Internal) && owner.Hidden && owner.AccessLevel >= from.AccessLevel ) + { + from.SendMessage( "You can not go to what you can not see." ); + return; + } + else if ( !FixMap( ref map, ref loc, m ) ) + { + from.SendMessage( "That is an internal mobile and you cannot go to it." ); + return; + } + + from.MoveToWorld( loc, map ); + + return; + } + else + { + string name = e.GetString( 0 ); + Map map; + + for ( int i = 0; i < Map.AllMaps.Count; ++i ) + { + map = Map.AllMaps[i]; + + if ( map.MapIndex == 0x7F || map.MapIndex == 0xFF ) + continue; + + if ( Insensitive.Equals( name, map.Name ) ) + { + from.Map = map; + return; + } + } + + Dictionary list = from.Map.Regions; + + foreach( KeyValuePair kvp in list ) + { + Region r = kvp.Value; + + if ( Insensitive.Equals( r.Name, name ) ) + { + from.Location = new Point3D( r.GoLocation ); + return; + } + } + + for( int i = 0; i < Map.AllMaps.Count; ++i ) + { + Map m = Map.AllMaps[i]; + + if( m.MapIndex == 0x7F || m.MapIndex == 0xFF || from.Map == m ) + continue; + + foreach( Region r in m.Regions.Values ) + { + if( Insensitive.Equals( r.Name, name ) ) + { + from.MoveToWorld( r.GoLocation, m ); + return; + } + } + } + + + + if ( ser != 0 ) + from.SendMessage( "No object with that serial was found." ); + else + from.SendMessage( "No region with that name was found." ); + + return; + } + } + catch + { + } + + from.SendMessage( "Region name not found" ); + } + else if ( e.Length == 2 || e.Length == 3 ) + { + Map map = from.Map; + + if ( map != null ) + { + try + { + /* + * This to avoid being teleported to (0,0) if trying to teleport + * to a region with spaces in its name. + */ + int x = int.Parse( e.GetString( 0 ) ); + int y = int.Parse( e.GetString( 1 ) ); + int z = (e.Length == 3 ) ? int.Parse( e.GetString( 2 ) ) : map.GetAverageZ( x, y ); + + from.Location = new Point3D( x, y, z ); + } + catch + { + from.SendMessage( "Region name not found." ); + } + } + } + else if ( e.Length == 6 ) + { + Map map = from.Map; + + if ( map != null ) + { + Point3D p = Sextant.ReverseLookup( map, e.GetInt32( 3 ), e.GetInt32( 0 ), e.GetInt32( 4 ), e.GetInt32( 1 ), Insensitive.Equals( e.GetString( 5 ), "E" ), Insensitive.Equals( e.GetString( 2 ), "S" ) ); + + if ( p != Point3D.Zero ) + from.Location = p; + else + from.SendMessage( "Sextant reverse lookup failed." ); + } + } + else + { + from.SendMessage( "Format: Go [name | serial | (x y [z]) | (deg min (N | S) deg min (E | W)]" ); + } + } + + [Usage( "Help" )] + [Description( "Lists all available commands." )] + public static void Help_OnCommand( CommandEventArgs e ) + { + Mobile m = e.Mobile; + + ArrayList list = new ArrayList(); + + foreach ( CommandEntry entry in CommandSystem.Entries.Values ) + { + if ( m.AccessLevel >= entry.AccessLevel ) + list.Add( entry ); + } + + list.Sort(); + + StringBuilder sb = new StringBuilder(); + + if ( list.Count > 0 ) + sb.Append( ((CommandEntry)list[0]).Command ); + + for ( int i = 1; i < list.Count; ++i ) + { + string v = ((CommandEntry)list[i]).Command; + + if ( (sb.Length + 1 + v.Length) >= 256 ) + { + m.SendAsciiMessage( 0x482, sb.ToString() ); + sb = new StringBuilder(); + sb.Append( v ); + } + else + { + sb.Append( ' ' ); + sb.Append( v ); + } + } + + if ( sb.Length > 0 ) + m.SendAsciiMessage( 0x482, sb.ToString() ); + } + + [Usage( "SMsg " )] + [Aliases( "S", "SM" )] + [Description( "Broadcasts a message to all online staff." )] + public static void StaffMessage_OnCommand( CommandEventArgs e ) + { + BroadcastMessage( AccessLevel.Counselor, e.Mobile.SpeechHue, String.Format( "[{0}] {1}", e.Mobile.Name, e.ArgString ) ); + } + + [Usage( "BCast " )] + [Aliases( "B", "BC" )] + [Description( "Broadcasts a message to everyone online." )] + public static void BroadcastMessage_OnCommand( CommandEventArgs e ) + { + BroadcastMessage( AccessLevel.Player, 0x482, String.Format( "Staff message from {0}:", e.Mobile.Name ) ); + BroadcastMessage( AccessLevel.Player, 0x482, e.ArgString ); + } + + public static void BroadcastMessage ( AccessLevel ac, int hue, string message ) + { + foreach ( NetState state in NetState.Instances ) + { + Mobile m = state.Mobile; + + if ( m != null && m.AccessLevel >= ac ) + m.SendMessage( hue, message ); + } + } + + private class DeleteItemByLayerTarget : Target + { + private Layer m_Layer; + + public DeleteItemByLayerTarget( Layer layer ) : base( -1, false, TargetFlags.None ) + { + m_Layer = layer; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + Item item = ((Mobile)targeted).FindItemOnLayer( m_Layer ); + + if ( item != null ) + { + CommandLogging.WriteLine( from, "{0} {1} deleting item on layer {2} of {3}", from.AccessLevel, CommandLogging.Format( from ), m_Layer, CommandLogging.Format( targeted ) ); + item.Delete(); + } + } + else + { + from.SendMessage( "Target a mobile." ); + } + } + } + + [Usage( "AutoPageNotify" )] + [Aliases( "APN" )] + [Description( "Toggles your auto-page-notify status." )] + public static void APN_OnCommand( CommandEventArgs e ) + { + Mobile m = e.Mobile; + + m.AutoPageNotify = !m.AutoPageNotify; + + m.SendMessage( "Your auto-page-notify has been turned {0}.", m.AutoPageNotify ? "on" : "off" ); + } + + [Usage( "Animate " )] + [Description( "Makes your character do a specified animation." )] + public static void Animate_OnCommand( CommandEventArgs e ) + { + if ( e.Length == 6 ) + { + e.Mobile.Animate( e.GetInt32( 0 ), e.GetInt32( 1 ), e.GetInt32( 2 ), e.GetBoolean( 3 ), e.GetBoolean( 4 ), e.GetInt32( 5 ) ); + } + else + { + e.Mobile.SendMessage( "Format: Animate " ); + } + } + + [Usage( "Cast " )] + [Description( "Casts a spell by name." )] + public static void Cast_OnCommand( CommandEventArgs e ) + { + if ( e.Length == 1 ) + { + if ( !Multis.DesignContext.Check( e.Mobile ) ) + return; // They are customizing + + Spell spell = SpellRegistry.NewSpell( e.GetString( 0 ), e.Mobile, null ); + + if ( spell != null ) + spell.Cast(); + else + e.Mobile.SendMessage( "That spell was not found." ); + } + else + { + e.Mobile.SendMessage( "Format: Cast " ); + } + } + + private class StuckMenuTarget : Target + { + public StuckMenuTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + if( ((Mobile)targeted).AccessLevel >= from.AccessLevel && targeted != from ) + from.SendMessage( "You can't do that to someone with higher Accesslevel than you!" ); + else + from.SendGump( new StuckMenu( from, (Mobile) targeted, false ) ); + } + } + } + + [Usage( "Stuck" )] + [Description( "Opens a menu of towns, used for teleporting stuck mobiles." )] + public static void Stuck_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new StuckMenuTarget(); + } + + [Usage( "Light ")] + [Description( "Set your local lightlevel." )] + public static void Light_OnCommand( CommandEventArgs e ) + { + e.Mobile.LightLevel = e.GetInt32( 0 ); + } + + [Usage( "Stats")] + [Description( "View some stats about the server." )] + public static void Stats_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendMessage( "Open Connections: {0}", Network.NetState.Instances.Count ); + e.Mobile.SendMessage( "Mobiles: {0}", World.Mobiles.Count ); + e.Mobile.SendMessage( "Items: {0}", World.Items.Count ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Logging.cs b/Scripts/Commands/Logging.cs new file mode 100644 index 000000000..8d4f448d0 --- /dev/null +++ b/Scripts/Commands/Logging.cs @@ -0,0 +1,144 @@ +using System; +using System.IO; +using Server; +using Server.Accounting; + +namespace Server.Commands +{ + public class CommandLogging + { + private static StreamWriter m_Output; + private static bool m_Enabled = true; + + public static bool Enabled{ get{ return m_Enabled; } set{ m_Enabled = value; } } + + public static StreamWriter Output{ get{ return m_Output; } } + + public static void Initialize() + { + EventSink.Command += new CommandEventHandler( EventSink_Command ); + + if ( !Directory.Exists( "Logs" ) ) + Directory.CreateDirectory( "Logs" ); + + string directory = "Logs/Commands"; + + if ( !Directory.Exists( directory ) ) + Directory.CreateDirectory( directory ); + + try + { + m_Output = new StreamWriter( Path.Combine( directory, String.Format( "{0}.log", DateTime.Now.ToLongDateString() ) ), true ); + + m_Output.AutoFlush = true; + + m_Output.WriteLine( "##############################" ); + m_Output.WriteLine( "Log started on {0}", DateTime.Now ); + m_Output.WriteLine(); + } + catch + { + } + } + + public static object Format( object o ) + { + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + if ( m.Account == null ) + return String.Format( "{0} (no account)", m ); + else + return String.Format( "{0} ('{1}')", m, m.Account.Username ); + } + else if ( o is Item ) + { + Item item = (Item)o; + + return String.Format( "0x{0:X} ({1})", item.Serial.Value, item.GetType().Name ); + } + + return o; + } + + public static void WriteLine( Mobile from, string format, params object[] args ) + { + WriteLine( from, String.Format( format, args ) ); + } + + public static void WriteLine( Mobile from, string text ) + { + if ( !m_Enabled ) + return; + + try + { + m_Output.WriteLine( "{0}: {1}: {2}", DateTime.Now, from.NetState, text ); + + string path = Core.BaseDirectory; + + Account acct = from.Account as Account; + + string name = ( acct == null ? from.Name : acct.Username ); + + AppendPath( ref path, "Logs" ); + AppendPath( ref path, "Commands" ); + AppendPath( ref path, from.AccessLevel.ToString() ); + path = Path.Combine( path, String.Format( "{0}.log", name ) ); + + using ( StreamWriter sw = new StreamWriter( path, true ) ) + sw.WriteLine( "{0}: {1}: {2}", DateTime.Now, from.NetState, text ); + } + catch + { + } + } + + private static char[] m_NotSafe = new char[]{ '\\', '/', ':', '*', '?', '"', '<', '>', '|' }; + + public static void AppendPath( ref string path, string toAppend ) + { + path = Path.Combine( path, toAppend ); + + if ( !Directory.Exists( path ) ) + Directory.CreateDirectory( path ); + } + + public static string Safe( string ip ) + { + if ( ip == null ) + return "null"; + + ip = ip.Trim(); + + if ( ip.Length == 0 ) + return "empty"; + + bool isSafe = true; + + for ( int i = 0; isSafe && i < m_NotSafe.Length; ++i ) + isSafe = ( ip.IndexOf( m_NotSafe[i] ) == -1 ); + + if ( isSafe ) + return ip; + + System.Text.StringBuilder sb = new System.Text.StringBuilder( ip ); + + for ( int i = 0; i < m_NotSafe.Length; ++i ) + sb.Replace( m_NotSafe[i], '_' ); + + return sb.ToString(); + } + + public static void EventSink_Command( CommandEventArgs e ) + { + WriteLine( e.Mobile, "{0} {1} used command '{2} {3}'", e.Mobile.AccessLevel, Format( e.Mobile ), e.Command, e.ArgString ); + } + + public static void LogChangeProperty( Mobile from, object o, string name, string value ) + { + WriteLine( from, "{0} {1} set property '{2}' of {3} to '{4}'", from.AccessLevel, Format( from ), name, Format( o ), value ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Properties.cs b/Scripts/Commands/Properties.cs new file mode 100644 index 000000000..73059a6a9 --- /dev/null +++ b/Scripts/Commands/Properties.cs @@ -0,0 +1,796 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Items; +using Server.Gumps; +using CPA = Server.CommandPropertyAttribute; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Commands +{ + public enum PropertyAccess + { + Read = 0x01, + Write = 0x02, + ReadWrite = Read | Write + } + + public class Properties + { + public static void Register() + { + CommandSystem.Register( "Props", AccessLevel.Counselor, new CommandEventHandler( Props_OnCommand ) ); + } + + private class PropsTarget : Target + { + public PropsTarget() : base( -1, true, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( !BaseCommand.IsAccessible( from, o ) ) + from.SendMessage( "That is not accessible." ); + else + from.SendGump( new PropertiesGump( from, o ) ); + } + } + + [Usage( "Props [serial]" )] + [Description( "Opens a menu where you can view and edit all properties of a targeted (or specified) object." )] + private static void Props_OnCommand( CommandEventArgs e ) + { + if ( e.Length == 1 ) + { + IEntity ent = World.FindEntity( e.GetInt32( 0 ) ); + + if ( ent == null ) + e.Mobile.SendMessage( "No object with that serial was found." ); + else if ( !BaseCommand.IsAccessible( e.Mobile, ent ) ) + e.Mobile.SendMessage( "That is not accessible." ); + else + e.Mobile.SendGump( new PropertiesGump( e.Mobile, ent ) ); + } + else + { + e.Mobile.Target = new PropsTarget(); + } + } + + private static bool CIEqual( string l, string r ) + { + return Insensitive.Equals( l, r ); + } + + private static Type typeofCPA = typeof( CPA ); + + public static CPA GetCPA( PropertyInfo p ) + { + object[] attrs = p.GetCustomAttributes( typeofCPA, false ); + + if ( attrs.Length == 0 ) + return null; + + return attrs[0] as CPA; + } + + public static PropertyInfo[] GetPropertyInfoChain( Mobile from, Type type, string propertyString, PropertyAccess endAccess, ref string failReason ) + { + string[] split = propertyString.Split( '.' ); + + if ( split.Length == 0 ) + return null; + + PropertyInfo[] info = new PropertyInfo[split.Length]; + + for ( int i = 0; i < info.Length; ++i ) + { + string propertyName = split[i]; + + if ( CIEqual( propertyName, "current" ) ) + continue; + + PropertyInfo[] props = type.GetProperties( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public ); + + bool isFinal = ( i == (info.Length - 1) ); + + PropertyAccess access = endAccess; + + if ( !isFinal ) + access |= PropertyAccess.Read; + + for ( int j = 0; j < props.Length; ++j ) + { + PropertyInfo p = props[j]; + + if ( CIEqual( p.Name, propertyName ) ) + { + CPA attr = GetCPA( p ); + + if ( attr == null ) + { + failReason = String.Format( "Property '{0}' not found.", propertyName ); + return null; + } + else if ( (access & PropertyAccess.Read) != 0 && from.AccessLevel < attr.ReadLevel ) + { + failReason = String.Format( "You must be at least {0} to get the property '{1}'.", + Mobile.GetAccessLevelName( attr.ReadLevel ), propertyName ); + + return null; + } + else if ( (access & PropertyAccess.Write) != 0 && from.AccessLevel < attr.WriteLevel ) + { + failReason = String.Format( "You must be at least {0} to set the property '{1}'.", + Mobile.GetAccessLevelName( attr.WriteLevel ), propertyName ); + + return null; + } + else if ( (access & PropertyAccess.Read) != 0 && !p.CanRead ) + { + failReason = String.Format( "Property '{0}' is write only.", propertyName ); + return null; + } + else if ( (access & PropertyAccess.Write) != 0 && !p.CanWrite && isFinal ) + { + failReason = String.Format( "Property '{0}' is read only.", propertyName ); + return null; + } + + info[i] = p; + type = p.PropertyType; + break; + } + } + + if ( info[i] == null ) + { + failReason = String.Format( "Property '{0}' not found.", propertyName ); + return null; + } + } + + return info; + } + + public static PropertyInfo GetPropertyInfo( Mobile from, ref object obj, string propertyName, PropertyAccess access, ref string failReason ) + { + PropertyInfo[] chain = GetPropertyInfoChain( from, obj.GetType(), propertyName, access, ref failReason ); + + if ( chain == null ) + return null; + + return GetPropertyInfo( ref obj, chain, ref failReason ); + } + + public static PropertyInfo GetPropertyInfo( ref object obj, PropertyInfo[] chain, ref string failReason ) + { + if ( chain == null || chain.Length == 0 ) + { + failReason = "Property chain is empty."; + return null; + } + + for ( int i = 0; i < chain.Length - 1; ++i ) + { + if ( chain[i] == null ) + continue; + + obj = chain[i].GetValue( obj, null ); + + if ( obj == null ) + { + failReason = String.Format( "Property '{0}' is null.", chain[i] ); + return null; + } + } + + return chain[chain.Length-1]; + } + + public static string GetValue( Mobile from, object o, string name ) + { + string failReason = ""; + + PropertyInfo[] chain = GetPropertyInfoChain( from, o.GetType(), name, PropertyAccess.Read, ref failReason ); + + if ( chain == null || chain.Length == 0 ) + return failReason; + + PropertyInfo p = GetPropertyInfo( ref o, chain, ref failReason ); + + if ( p == null ) + return failReason; + + return InternalGetValue( o, p, chain ); + } + + public static string IncreaseValue( Mobile from, object o, string[] args ) + { + Type type = o.GetType(); + + object[] realObjs = new object[args.Length/2]; + PropertyInfo[] realProps = new PropertyInfo[args.Length/2]; + int[] realValues = new int[args.Length/2]; + + bool positive = false, negative = false; + + for ( int i = 0; i < realProps.Length; ++i ) + { + string name = args[i*2]; + + try + { + string valueString = args[1 + (i*2)]; + + if ( valueString.StartsWith( "0x" ) ) + { + realValues[i] = Convert.ToInt32( valueString.Substring( 2 ), 16 ); + } + else + { + realValues[i] = Convert.ToInt32( valueString ); + } + } + catch + { + return "Offset value could not be parsed."; + } + + if ( realValues[i] > 0 ) + positive = true; + else if ( realValues[i] < 0 ) + negative = true; + else + return "Zero is not a valid value to offset."; + + string failReason = null; + realObjs[i] = o; + realProps[i] = GetPropertyInfo( from, ref realObjs[i], name, PropertyAccess.ReadWrite, ref failReason ); + + if ( failReason != null ) + return failReason; + + if ( realProps[i] == null ) + return "Property not found."; + } + + for ( int i = 0; i < realProps.Length; ++i ) + { + object obj = realProps[i].GetValue( realObjs[i], null ); + long v = (long)Convert.ChangeType( obj, TypeCode.Int64 ); + v += realValues[i]; + + realProps[i].SetValue( realObjs[i], Convert.ChangeType( v, realProps[i].PropertyType ), null ); + } + + if ( realProps.Length == 1 ) + { + if ( positive ) + return "The property has been increased."; + + return "The property has been decreased."; + } + + if ( positive && negative ) + return "The properties have been changed."; + + if ( positive ) + return "The properties have been increased."; + + return "The properties have been decreased."; + } + + private static string InternalGetValue( object o, PropertyInfo p ) + { + return InternalGetValue( o, p, null ); + } + + private static string InternalGetValue( object o, PropertyInfo p, PropertyInfo[] chain ) + { + Type type = p.PropertyType; + + object value = p.GetValue( o, null ); + string toString; + + if ( value == null ) + toString = "null"; + else if ( IsNumeric( type ) ) + toString = String.Format( "{0} (0x{0:X})", value ); + else if ( IsChar( type ) ) + toString = String.Format( "'{0}' ({1} [0x{1:X}])", value, (int) value ); + else if ( IsString( type ) ) + toString = ( (string) value == "null" ? @"@""null""" : String.Format( "\"{0}\"", value ) ); + else + toString = value.ToString(); + + if ( chain == null ) + return String.Format( "{0} = {1}", p.Name, toString ); + + string[] concat = new string[chain.Length*2+1]; + + for ( int i = 0; i < chain.Length; ++i ) + { + concat[(i*2)+0] = chain[i].Name; + concat[(i*2)+1] = ( i < (chain.Length - 1) ) ? "." : " = "; + } + + concat[concat.Length-1] = toString; + + return String.Concat( concat ); + } + + public static string SetValue( Mobile from, object o, string name, string value ) + { + object logObject = o; + + string failReason = ""; + PropertyInfo p = GetPropertyInfo( from, ref o, name, PropertyAccess.Write, ref failReason ); + + if ( p == null ) + return failReason; + + return InternalSetValue( from, logObject, o, p, name, value, true ); + } + + private static Type typeofSerial = typeof( Serial ); + + private static bool IsSerial( Type t ) + { + return ( t == typeofSerial ); + } + + private static Type typeofType = typeof( Type ); + + private static bool IsType( Type t ) + { + return ( t == typeofType ); + } + + private static Type typeofChar = typeof( Char ); + + private static bool IsChar( Type t ) + { + return ( t == typeofChar ); + } + + private static Type typeofString = typeof( String ); + + private static bool IsString( Type t ) + { + return ( t == typeofString ); + } + + private static bool IsEnum( Type t ) + { + return t.IsEnum; + } + + private static Type typeofTimeSpan = typeof( TimeSpan ); + private static Type typeofParsable = typeof( ParsableAttribute ); + + private static bool IsParsable( Type t ) + { + return ( t == typeofTimeSpan || t.IsDefined( typeofParsable, false ) ); + } + + private static Type[] m_ParseTypes = new Type[]{ typeof( string ) }; + private static object[] m_ParseParams = new object[1]; + + private static object Parse( object o, Type t, string value ) + { + MethodInfo method = t.GetMethod( "Parse", m_ParseTypes ); + + m_ParseParams[0] = value; + + return method.Invoke( o, m_ParseParams ); + } + + private static Type[] m_NumericTypes = new Type[] + { + typeof( Byte ), typeof( SByte ), + typeof( Int16 ), typeof( UInt16 ), + typeof( Int32 ), typeof( UInt32 ), + typeof( Int64 ), typeof( UInt64 ) + }; + + private static bool IsNumeric( Type t ) + { + return ( Array.IndexOf( m_NumericTypes, t ) >= 0 ); + } + + public static string ConstructFromString( Type type, object obj, string value, ref object constructed ) + { + object toSet; + bool isSerial = IsSerial( type ); + + if ( isSerial ) // mutate into int32 + type = m_NumericTypes[4]; + + if ( value == "(-null-)" && !type.IsValueType ) + value = null; + + if ( IsEnum( type ) ) + { + try + { + toSet = Enum.Parse( type, value, true ); + } + catch + { + return "That is not a valid enumeration member."; + } + } + else if ( IsType( type ) ) + { + try + { + toSet = ScriptCompiler.FindTypeByName( value ); + + if ( toSet == null ) + return "No type with that name was found."; + } + catch + { + return "No type with that name was found."; + } + } + else if ( IsParsable( type ) ) + { + try + { + toSet = Parse( obj, type, value ); + } + catch + { + return "That is not properly formatted."; + } + } + else if ( value == null ) + { + toSet = null; + } + else if ( value.StartsWith( "0x" ) && IsNumeric( type ) ) + { + try + { + toSet = Convert.ChangeType( Convert.ToUInt64( value.Substring( 2 ), 16 ), type ); + } + catch + { + return "That is not properly formatted."; + } + } + else + { + try + { + toSet = Convert.ChangeType( value, type ); + } + catch + { + return "That is not properly formatted."; + } + } + + if ( isSerial ) // mutate back + toSet = (Serial)((Int32)toSet); + + constructed = toSet; + return null; + } + + public static string SetDirect( Mobile from, object logObject, object obj, PropertyInfo prop, string givenName, object toSet, bool shouldLog ) + { + try + { + if ( toSet is AccessLevel ) + { + AccessLevel newLevel = (AccessLevel) toSet; + AccessLevel reqLevel = AccessLevel.Administrator; + + if ( newLevel == AccessLevel.Administrator ) + reqLevel = AccessLevel.Developer; + else if ( newLevel >= AccessLevel.Developer ) + reqLevel = AccessLevel.Owner; + + if ( from.AccessLevel < reqLevel ) + return "You do not have access to that level."; + } + + if ( shouldLog ) + CommandLogging.LogChangeProperty( from, logObject, givenName, toSet == null ? "(-null-)" : toSet.ToString() ); + + prop.SetValue( obj, toSet, null ); + return "Property has been set."; + } + catch + { + return "An exception was caught, the property may not be set."; + } + } + + public static string InternalSetValue( Mobile from, object logobj, object o, PropertyInfo p, string pname, string value, bool shouldLog ) + { + object toSet = null; + string result = ConstructFromString( p.PropertyType, o, value, ref toSet ); + + if ( result != null ) + return result; + + return SetDirect( from, logobj, o, p, pname, toSet, shouldLog ); + } + } +} + +namespace Server +{ + public abstract class PropertyException : ApplicationException + { + protected Property m_Property; + + public Property Property + { + get { return m_Property; } + } + + public PropertyException( Property property, string message ) + : base( message ) + { + m_Property = property; + } + } + + public abstract class BindingException : PropertyException + { + public BindingException( Property property, string message ) + : base( property, message ) + { + } + } + + public sealed class NotYetBoundException : BindingException + { + public NotYetBoundException( Property property ) + : base( property, String.Format( "Property has not yet been bound." ) ) + { + } + } + + public sealed class AlreadyBoundException : BindingException + { + public AlreadyBoundException( Property property ) + : base( property, String.Format( "Property has already been bound." ) ) + { + } + } + + public sealed class UnknownPropertyException : BindingException + { + public UnknownPropertyException( Property property, string current ) + : base( property, String.Format( "Property '{0}' not found.", current ) ) + { + } + } + + public sealed class ReadOnlyException : BindingException + { + public ReadOnlyException( Property property ) + : base( property, "Property is read-only." ) + { + } + } + + public sealed class WriteOnlyException : BindingException + { + public WriteOnlyException( Property property ) + : base( property, "Property is write-only." ) + { + } + } + + public abstract class AccessException : PropertyException + { + public AccessException( Property property, string message ) + : base( property, message ) + { + } + } + + public sealed class InternalAccessException : AccessException + { + public InternalAccessException( Property property ) + : base( property, "Property is internal." ) + { + } + } + + public abstract class ClearanceException : AccessException + { + protected AccessLevel m_PlayerAccess; + protected AccessLevel m_NeededAccess; + + public AccessLevel PlayerAccess + { + get { return m_PlayerAccess; } + } + + public AccessLevel NeededAccess + { + get { return m_NeededAccess; } + } + + public ClearanceException( Property property, AccessLevel playerAccess, AccessLevel neededAccess, string accessType ) + : base( property, string.Format( + "You must be at least {0} to {1} this property.", + Mobile.GetAccessLevelName( neededAccess ), + accessType + ) ) + { + } + } + + public sealed class ReadAccessException : ClearanceException + { + public ReadAccessException( Property property, AccessLevel playerAccess, AccessLevel neededAccess ) + : base( property, playerAccess, neededAccess, "read" ) + { + } + } + + public sealed class WriteAccessException : ClearanceException + { + public WriteAccessException( Property property, AccessLevel playerAccess, AccessLevel neededAccess ) + : base( property, playerAccess, neededAccess, "write" ) + { + } + } + + public sealed class Property + { + private string m_Binding; + + private PropertyInfo[] m_Chain; + private PropertyAccess m_Access; + + public string Binding + { + get { return m_Binding; } + } + + public bool IsBound + { + get { return ( m_Chain != null ); } + } + + public PropertyAccess Access + { + get { return m_Access; } + } + + public PropertyInfo[] Chain + { + get + { + if ( !IsBound ) + throw new NotYetBoundException( this ); + + return m_Chain; + } + } + + public Type Type + { + get + { + if ( !IsBound ) + throw new NotYetBoundException( this ); + + return m_Chain[m_Chain.Length - 1].PropertyType; + } + } + + public bool CheckAccess( Mobile from ) + { + if ( !IsBound ) + throw new NotYetBoundException( this ); + + for ( int i = 0; i < m_Chain.Length; ++i ) + { + PropertyInfo prop = m_Chain[i]; + + bool isFinal = ( i == ( m_Chain.Length - 1 ) ); + + PropertyAccess access = m_Access; + + if ( !isFinal ) + access |= PropertyAccess.Read; + + CPA security = Properties.GetCPA( prop ); + + if ( security == null ) + throw new InternalAccessException( this ); + + if ( ( access & PropertyAccess.Read ) != 0 && from.AccessLevel < security.ReadLevel ) + throw new ReadAccessException( this, from.AccessLevel, security.ReadLevel ); + + if ( ( access & PropertyAccess.Write ) != 0 && from.AccessLevel < security.WriteLevel ) + throw new WriteAccessException( this, from.AccessLevel, security.ReadLevel ); + } + + return true; + } + + public void BindTo( Type objectType, PropertyAccess desiredAccess ) + { + if ( IsBound ) + throw new AlreadyBoundException( this ); + + string[] split = m_Binding.Split( '.' ); + + PropertyInfo[] chain = new PropertyInfo[split.Length]; + + for ( int i = 0; i < split.Length; ++i ) + { + bool isFinal = ( i == ( chain.Length - 1 ) ); + + chain[i] = objectType.GetProperty( split[i], BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase ); + + if ( chain[i] == null ) + throw new UnknownPropertyException( this, split[i] ); + + objectType = chain[i].PropertyType; + + PropertyAccess access = desiredAccess; + + if ( !isFinal ) + access |= PropertyAccess.Read; + + if ( ( access & PropertyAccess.Read ) != 0 && !chain[i].CanRead ) + throw new WriteOnlyException( this ); + + if ( ( access & PropertyAccess.Write ) != 0 && !chain[i].CanWrite ) + throw new ReadOnlyException( this ); + } + + m_Access = desiredAccess; + m_Chain = chain; + } + + public Property( string binding ) + { + m_Binding = binding; + } + + public Property( PropertyInfo[] chain ) + { + m_Chain = chain; + } + + public override string ToString() + { + if ( !IsBound ) + return m_Binding; + + string[] toJoin = new string[m_Chain.Length]; + + for ( int i = 0; i < toJoin.Length; ++i ) + toJoin[i] = m_Chain[i].Name; + + return string.Join( ".", toJoin ); + } + + public static Property Parse( Type type, string binding, PropertyAccess access ) + { + Property prop = new Property( binding ); + + prop.BindTo( type, access ); + + return prop; + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/SignParser.cs b/Scripts/Commands/SignParser.cs new file mode 100644 index 000000000..efd8b9c65 --- /dev/null +++ b/Scripts/Commands/SignParser.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using System.IO; +using Server; +using Server.Items; + +namespace Server.Commands +{ + public class SignParser + { + private class SignEntry + { + public string m_Text; + public Point3D m_Location; + public int m_ItemID; + public int m_Map; + + public SignEntry( string text, Point3D pt, int itemID, int mapLoc ) + { + m_Text = text; + m_Location = pt; + m_ItemID = itemID; + m_Map = mapLoc; + } + } + + public static void Initialize() + { + CommandSystem.Register( "SignGen", AccessLevel.Administrator, new CommandEventHandler( SignGen_OnCommand ) ); + } + + [Usage( "SignGen" )] + [Description( "Generates world/shop signs on all facets." )] + public static void SignGen_OnCommand( CommandEventArgs c ) + { + Parse( c.Mobile ); + } + + public static void Parse( Mobile from ) + { + string cfg = Path.Combine( Core.BaseDirectory, "Data/signs.cfg" ); + + if ( File.Exists( cfg ) ) + { + ArrayList list = new ArrayList(); + from.SendMessage( "Generating signs, please wait." ); + + using ( StreamReader ip = new StreamReader( cfg ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + string[] split = line.Split( ' ' ); + + SignEntry e = new SignEntry( + line.Substring( split[0].Length + 1 + split[1].Length + 1 + split[2].Length + 1 + split[3].Length + 1 + split[4].Length + 1 ), + new Point3D( Utility.ToInt32( split[2] ), Utility.ToInt32( split[3] ), Utility.ToInt32( split[4] ) ), + Utility.ToInt32( split[1] ), Utility.ToInt32( split[0] ) ); + + list.Add( e ); + } + } + + Map[] brit = new Map[]{ Map.Felucca, Map.Trammel }; + Map[] fel = new Map[]{ Map.Felucca }; + Map[] tram = new Map[]{ Map.Trammel }; + Map[] ilsh = new Map[]{ Map.Ilshenar }; + Map[] malas = new Map[]{ Map.Malas }; + Map[] tokuno = new Map[]{ Map.Tokuno }; + + for ( int i = 0; i < list.Count; ++i ) + { + SignEntry e = (SignEntry)list[i]; + Map[] maps = null; + + switch ( e.m_Map ) + { + case 0: maps = brit; break; // Trammel and Felucca + case 1: maps = fel; break; // Felucca + case 2: maps = tram; break; // Trammel + case 3: maps = ilsh; break; // Ilshenar + case 4: maps = malas; break; // Malas + case 5: maps = tokuno; break; // Tokuno Islands + } + + for ( int j = 0; maps != null && j < maps.Length; ++j ) + Add_Static( e.m_ItemID, e.m_Location, maps[j], e.m_Text ); + } + + from.SendMessage( "Sign generating complete." ); + } + else + { + from.SendMessage( "{0} not found!", cfg ); + } + } + + private static Queue m_ToDelete = new Queue(); + + public static void Add_Static( int itemID, Point3D location, Map map, string name ) + { + IPooledEnumerable eable = map.GetItemsInRange( location, 0 ); + + foreach ( Item item in eable ) + { + if ( item is Sign && item.Z == location.Z && item.ItemID == itemID ) + m_ToDelete.Enqueue( item ); + } + + eable.Free(); + + while ( m_ToDelete.Count > 0 ) + ((Item)m_ToDelete.Dequeue()).Delete(); + + Item sign; + + if ( name.StartsWith( "#" ) ) + { + sign = new LocalizedSign( itemID, Utility.ToInt32( name.Substring( 1 ) ) ); + } + else + { + sign = new Sign( itemID ); + sign.Name = name; + } + + if ( map == Map.Malas ) + { + if ( location.X >= 965 && location.Y >= 502 && location.X <= 1012 && location.Y <= 537 ) + sign.Hue = 0x47E; + else if ( location.X >= 1960 && location.Y >= 1278 && location.X < 2106 && location.Y < 1413 ) + sign.Hue = 0x44E; + } + + sign.MoveToWorld( location, map ); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Skills.cs b/Scripts/Commands/Skills.cs new file mode 100644 index 000000000..67fc93990 --- /dev/null +++ b/Scripts/Commands/Skills.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Commands +{ + public class SkillsCommand + { + public static void Initialize() + { + CommandSystem.Register( "SetSkill", AccessLevel.GameMaster, new CommandEventHandler( SetSkill_OnCommand ) ); + CommandSystem.Register( "GetSkill", AccessLevel.GameMaster, new CommandEventHandler( GetSkill_OnCommand ) ); + CommandSystem.Register( "SetAllSkills", AccessLevel.GameMaster, new CommandEventHandler( SetAllSkills_OnCommand ) ); + } + + [Usage( "SetSkill " )] + [Description( "Sets a skill value by name of a targeted mobile." )] + public static void SetSkill_OnCommand( CommandEventArgs arg ) + { + if ( arg.Length != 2 ) + { + arg.Mobile.SendMessage( "SetSkill " ); + } + else + { + SkillName skill; + try + { + skill = (SkillName)Enum.Parse( typeof( SkillName ), arg.GetString( 0 ), true ); + } + catch + { + arg.Mobile.SendLocalizedMessage( 1005631 ); // You have specified an invalid skill to set. + return; + } + arg.Mobile.Target = new SkillTarget( skill, arg.GetDouble( 1 ) ); + } + } + + [Usage( "SetAllSkills " )] + [Description( "Sets all skill values of a targeted mobile." )] + public static void SetAllSkills_OnCommand( CommandEventArgs arg ) + { + if ( arg.Length != 1 ) + { + arg.Mobile.SendMessage( "SetAllSkills " ); + } + else + { + arg.Mobile.Target = new AllSkillsTarget( arg.GetDouble( 0 ) ); + } + } + + [Usage( "GetSkill " )] + [Description( "Gets a skill value by name of a targeted mobile." )] + public static void GetSkill_OnCommand( CommandEventArgs arg ) + { + if ( arg.Length != 1 ) + { + arg.Mobile.SendMessage( "GetSkill " ); + } + else + { + SkillName skill; + try + { + skill = (SkillName)Enum.Parse( typeof( SkillName ), arg.GetString( 0 ), true ); + } + catch + { + arg.Mobile.SendLocalizedMessage( 1005631 ); // You have specified an invalid skill to set. + return; + } + + arg.Mobile.Target = new SkillTarget( skill ); + } + } + + public class AllSkillsTarget : Target + { + private double m_Value; + + public AllSkillsTarget( double value ) : base( -1, false, TargetFlags.None ) + { + m_Value = value; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + Server.Skills skills = targ.Skills; + + for ( int i = 0; i < skills.Length; ++i ) + skills[i].Base = m_Value; + + CommandLogging.LogChangeProperty( from, targ, "EverySkill.Base", m_Value.ToString() ); + } + else + { + from.SendMessage( "That does not have skills!" ); + } + } + } + + public class SkillTarget : Target + { + private bool m_Set; + private SkillName m_Skill; + private double m_Value; + + public SkillTarget( SkillName skill, double value ) : base( -1, false, TargetFlags.None ) + { + m_Set = true; + m_Skill = skill; + m_Value = value; + } + + public SkillTarget( SkillName skill ) : base( -1, false, TargetFlags.None ) + { + m_Set = false; + m_Skill = skill; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + Skill skill = targ.Skills[m_Skill]; + + if ( skill == null ) + return; + + if ( m_Set ) + { + skill.Base = m_Value; + CommandLogging.LogChangeProperty( from, targ, String.Format( "{0}.Base", m_Skill ), m_Value.ToString() ); + } + + from.SendMessage( "{0} : {1} (Base: {2})", m_Skill, skill.Value, skill.Base ); + } + else + { + from.SendMessage( "That does not have skills!" ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/SkillsMenu.cs b/Scripts/Commands/SkillsMenu.cs new file mode 100644 index 000000000..fbd8e3e6f --- /dev/null +++ b/Scripts/Commands/SkillsMenu.cs @@ -0,0 +1,40 @@ +using System; +using Server; +using Server.Targeting; +using Server.Gumps; + +namespace Server.Commands +{ + public class Skills + { + public static void Initialize() + { + Register(); + } + + public static void Register() + { + CommandSystem.Register( "Skills", AccessLevel.Counselor, new CommandEventHandler( Skills_OnCommand ) ); + } + + private class SkillsTarget : Target + { + public SkillsTarget( ) : base( -1, true, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + from.SendGump( new SkillsGump( from, (Mobile)o ) ); + } + } + + [Usage( "Skills" )] + [Description( "Opens a menu where you can view or edit skills of a targeted mobile." )] + private static void Skills_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new SkillsTarget(); + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/Statics.cs b/Scripts/Commands/Statics.cs new file mode 100644 index 000000000..ffd7469e1 --- /dev/null +++ b/Scripts/Commands/Statics.cs @@ -0,0 +1,577 @@ +using System; +using System.IO; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Commands; +using Server.Targeting; + +namespace Server +{ + public class Statics + { + public static void Initialize() + { + CommandSystem.Register( "Freeze", AccessLevel.Administrator, new CommandEventHandler( Freeze_OnCommand ) ); + CommandSystem.Register( "FreezeMap", AccessLevel.Administrator, new CommandEventHandler( FreezeMap_OnCommand ) ); + CommandSystem.Register( "FreezeWorld", AccessLevel.Administrator, new CommandEventHandler( FreezeWorld_OnCommand ) ); + + CommandSystem.Register( "Unfreeze", AccessLevel.Administrator, new CommandEventHandler( Unfreeze_OnCommand ) ); + CommandSystem.Register( "UnfreezeMap", AccessLevel.Administrator, new CommandEventHandler( UnfreezeMap_OnCommand ) ); + CommandSystem.Register( "UnfreezeWorld", AccessLevel.Administrator, new CommandEventHandler( UnfreezeWorld_OnCommand ) ); + } + + private static Point3D NullP3D = new Point3D( int.MinValue, int.MinValue, int.MinValue ); + + [Usage( "Freeze" )] + [Description( "Makes a targeted area of dynamic items static." )] + public static void Freeze_OnCommand( CommandEventArgs e ) + { + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( FreezeBox_Callback ), null ); + } + + [Usage( "FreezeMap" )] + [Description( "Makes every dynamic item in your map static." )] + public static void FreezeMap_OnCommand( CommandEventArgs e ) + { + Map map = e.Mobile.Map; + + if ( map != null && map != Map.Internal ) + SendWarning( e.Mobile, "You are about to freeze all items in {0}.", BaseFreezeWarning, map, NullP3D, NullP3D, new WarningGumpCallback( FreezeWarning_Callback ) ); + } + + [Usage( "FreezeWorld" )] + [Description( "Makes every dynamic item on all maps static." )] + public static void FreezeWorld_OnCommand( CommandEventArgs e ) + { + SendWarning( e.Mobile, "You are about to freeze every item on every map.", BaseFreezeWarning, null, NullP3D, NullP3D, new WarningGumpCallback( FreezeWarning_Callback ) ); + } + + public static void SendWarning( Mobile m, string header, string baseWarning, Map map, Point3D start, Point3D end, WarningGumpCallback callback ) + { + m.SendGump( new WarningGump( 1060635, 30720, String.Format( baseWarning, String.Format( header, map ) ), 0xFFC000, 420, 400, callback, new StateInfo( map, start, end ) ) ); + } + + private const string BaseFreezeWarning = "{0} " + + "Those items will be removed from the world and placed into the server data files. " + + "Other players will not see the changes unless you distribute your data files to them.

" + + "This operation may not complete unless the server and client are using different data files. " + + "If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files. " + + "Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.

" + + "The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible. " + + "It is strongly recommended that you make backup of the data files mentioned above. " + + "Do you wish to proceed?"; + + private static void FreezeBox_Callback( Mobile from, Map map, Point3D start, Point3D end, object state ) + { + SendWarning( from, "You are about to freeze a section of items.", BaseFreezeWarning, map, start, end, new WarningGumpCallback( FreezeWarning_Callback ) ); + } + + private static void FreezeWarning_Callback( Mobile from, bool okay, object state ) + { + if ( !okay ) + return; + + StateInfo si = (StateInfo)state; + + Freeze( from, si.m_Map, si.m_Start, si.m_End ); + } + + public static void Freeze( Mobile from, Map targetMap, Point3D start3d, Point3D end3d ) + { + Hashtable mapTable = new Hashtable(); + + if ( start3d == NullP3D && end3d == NullP3D ) + { + if ( targetMap == null ) + CommandLogging.WriteLine( from, "{0} {1} invoking freeze for every item in every map", from.AccessLevel, CommandLogging.Format( from ) ); + else + CommandLogging.WriteLine( from, "{0} {1} invoking freeze for every item in {0}", from.AccessLevel, CommandLogging.Format( from ), targetMap ); + + foreach ( Item item in World.Items.Values ) + { + if ( targetMap != null && item.Map != targetMap ) + continue; + + if ( item.Parent != null ) + continue; + + if ( item is Static || item is BaseFloor || item is BaseWall ) + { + Map itemMap = item.Map; + + if ( itemMap == null || itemMap == Map.Internal ) + continue; + + Hashtable table = (Hashtable)mapTable[itemMap]; + + if ( table == null ) + mapTable[itemMap] = table = new Hashtable(); + + Point2D p = new Point2D( item.X >> 3, item.Y >> 3 ); + + DeltaState state = (DeltaState)table[p]; + + if ( state == null ) + table[p] = state = new DeltaState( p ); + + state.m_List.Add( item ); + } + } + } + else if ( targetMap != null ) + { + Point2D start = targetMap.Bound( new Point2D( start3d ) ), end = targetMap.Bound( new Point2D( end3d ) ); + + CommandLogging.WriteLine( from, "{0} {1} invoking freeze from {2} to {3} in {4}", from.AccessLevel, CommandLogging.Format( from ), start, end, targetMap ); + + IPooledEnumerable eable = targetMap.GetItemsInBounds( new Rectangle2D( start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1 ) ); + + foreach ( Item item in eable ) + { + if ( item is Static || item is BaseFloor || item is BaseWall ) + { + Map itemMap = item.Map; + + if ( itemMap == null || itemMap == Map.Internal ) + continue; + + Hashtable table = (Hashtable)mapTable[itemMap]; + + if ( table == null ) + mapTable[itemMap] = table = new Hashtable(); + + Point2D p = new Point2D( item.X >> 3, item.Y >> 3 ); + + DeltaState state = (DeltaState)table[p]; + + if ( state == null ) + table[p] = state = new DeltaState( p ); + + state.m_List.Add( item ); + } + } + + eable.Free(); + } + + if ( mapTable.Count == 0 ) + { + from.SendGump( new NoticeGump( 1060637, 30720, "No freezable items were found. Only the following item types are frozen:
- Static
- BaseFloor
- BaseWall", 0xFFC000, 320, 240, null, null ) ); + return; + } + + bool badDataFile = false; + + int totalFrozen = 0; + + foreach ( DictionaryEntry de in mapTable ) + { + Map map = (Map)de.Key; + Hashtable table = (Hashtable)de.Value; + + TileMatrix matrix = map.Tiles; + + using ( FileStream idxStream = OpenWrite( matrix.IndexStream ) ) + { + using ( FileStream mulStream = OpenWrite( matrix.DataStream ) ) + { + if ( idxStream == null || mulStream == null ) + { + badDataFile = true; + continue; + } + + BinaryReader idxReader = new BinaryReader( idxStream ); + + BinaryWriter idxWriter = new BinaryWriter( idxStream ); + BinaryWriter mulWriter = new BinaryWriter( mulStream ); + + foreach ( DeltaState state in table.Values ) + { + int oldTileCount; + StaticTile[] oldTiles = ReadStaticBlock( idxReader, mulStream, state.m_X, state.m_Y, matrix.BlockWidth, matrix.BlockHeight, out oldTileCount ); + + if ( oldTileCount < 0 ) + continue; + + int newTileCount = 0; + StaticTile[] newTiles = new StaticTile[state.m_List.Count]; + + for ( int i = 0; i < state.m_List.Count; ++i ) + { + Item item = (Item)state.m_List[i]; + + int xOffset = item.X - (state.m_X * 8); + int yOffset = item.Y - (state.m_Y * 8); + + if ( xOffset < 0 || xOffset >= 8 || yOffset < 0 || yOffset >= 8 ) + continue; + + StaticTile newTile = new StaticTile(); + + newTile.m_ID = (short)(item.ItemID & 0x3FFF); + newTile.m_X = (byte)xOffset; + newTile.m_Y = (byte)yOffset; + newTile.m_Z = (sbyte)item.Z; + newTile.m_Hue = (short)item.Hue; + + newTiles[newTileCount++] = newTile; + + item.Delete(); + + ++totalFrozen; + } + + int mulPos = -1; + int length = -1; + int extra = 0; + + if ( (oldTileCount + newTileCount) > 0 ) + { + mulWriter.Seek( 0, SeekOrigin.End ); + + mulPos = (int)mulWriter.BaseStream.Position; + length = (oldTileCount + newTileCount) * 7; + extra = 1; + + for ( int i = 0; i < oldTileCount; ++i ) + { + StaticTile toWrite = oldTiles[i]; + + mulWriter.Write( (short) toWrite.m_ID ); + mulWriter.Write( (byte) toWrite.m_X ); + mulWriter.Write( (byte) toWrite.m_Y ); + mulWriter.Write( (sbyte) toWrite.m_Z ); + mulWriter.Write( (short) toWrite.m_Hue ); + } + + for ( int i = 0; i < newTileCount; ++i ) + { + StaticTile toWrite = newTiles[i]; + + mulWriter.Write( (short) toWrite.m_ID ); + mulWriter.Write( (byte) toWrite.m_X ); + mulWriter.Write( (byte) toWrite.m_Y ); + mulWriter.Write( (sbyte) toWrite.m_Z ); + mulWriter.Write( (short) toWrite.m_Hue ); + } + + mulWriter.Flush(); + } + + int idxPos = ((state.m_X * matrix.BlockHeight) + state.m_Y) * 12; + + idxWriter.Seek( idxPos, SeekOrigin.Begin ); + idxWriter.Write( mulPos ); + idxWriter.Write( length ); + idxWriter.Write( extra ); + + idxWriter.Flush(); + + matrix.SetStaticBlock( state.m_X, state.m_Y, null ); + } + } + } + } + + if ( totalFrozen == 0 && badDataFile ) + from.SendGump( new NoticeGump( 1060637, 30720, "Output data files could not be opened and the freeze operation has been aborted.

This probably means your server and client are using the same data files. Instructions on how to resolve this can be found in the first warning window.", 0xFFC000, 320, 240, null, null ) ); + else + from.SendGump( new NoticeGump( 1060637, 30720, String.Format( "Freeze operation completed successfully.

{0} item{1} frozen.

You must restart your client and update it's data files to see the changes.", totalFrozen, totalFrozen != 1 ? "s were" : " was" ), 0xFFC000, 320, 240, null, null ) ); + } + + private const string BaseUnfreezeWarning = "{0} " + + "Those items will be removed from the static files and exchanged with unmovable dynamic items. " + + "Other players will not see the changes unless you distribute your data files to them.

" + + "This operation may not complete unless the server and client are using different data files. " + + "If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files. " + + "Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.

" + + "The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible. " + + "It is strongly recommended that you make backup of the data files mentioned above. " + + "Do you wish to proceed?"; + + [Usage( "Unfreeze" )] + [Description( "Makes a targeted area of static items dynamic." )] + public static void Unfreeze_OnCommand( CommandEventArgs e ) + { + BoundingBoxPicker.Begin( e.Mobile, new BoundingBoxCallback( UnfreezeBox_Callback ), null ); + } + + [Usage( "UnfreezeMap" )] + [Description( "Makes every static item in your map dynamic." )] + public static void UnfreezeMap_OnCommand( CommandEventArgs e ) + { + Map map = e.Mobile.Map; + + if ( map != null && map != Map.Internal ) + SendWarning( e.Mobile, "You are about to unfreeze all items in {0}.", BaseUnfreezeWarning, map, NullP3D, NullP3D, new WarningGumpCallback( UnfreezeWarning_Callback ) ); + } + + [Usage( "UnfreezeWorld" )] + [Description( "Makes every static item on all maps dynamic." )] + public static void UnfreezeWorld_OnCommand( CommandEventArgs e ) + { + SendWarning( e.Mobile, "You are about to unfreeze every item on every map.", BaseUnfreezeWarning, null, NullP3D, NullP3D, new WarningGumpCallback( UnfreezeWarning_Callback ) ); + } + + private static void UnfreezeBox_Callback( Mobile from, Map map, Point3D start, Point3D end, object state ) + { + SendWarning( from, "You are about to unfreeze a section of items.", BaseUnfreezeWarning, map, start, end, new WarningGumpCallback( UnfreezeWarning_Callback ) ); + } + + private static void UnfreezeWarning_Callback( Mobile from, bool okay, object state ) + { + if ( !okay ) + return; + + StateInfo si = (StateInfo)state; + + Unfreeze( from, si.m_Map, si.m_Start, si.m_End ); + } + + private static void DoUnfreeze( Map map, Point2D start, Point2D end, ref bool badDataFile, ref int totalUnfrozen ) + { + start = map.Bound( start ); + end = map.Bound( end ); + + int xStartBlock = start.X >> 3; + int yStartBlock = start.Y >> 3; + int xEndBlock = end.X >> 3; + int yEndBlock = end.Y >> 3; + + int xTileStart = start.X, yTileStart = start.Y; + int xTileWidth = end.X - start.X + 1, yTileHeight = end.Y - start.Y + 1; + + TileMatrix matrix = map.Tiles; + + using ( FileStream idxStream = OpenWrite( matrix.IndexStream ) ) + { + using ( FileStream mulStream = OpenWrite( matrix.DataStream ) ) + { + if ( idxStream == null || mulStream == null ) + { + badDataFile = true; + return; + } + + BinaryReader idxReader = new BinaryReader( idxStream ); + + BinaryWriter idxWriter = new BinaryWriter( idxStream ); + BinaryWriter mulWriter = new BinaryWriter( mulStream ); + + for ( int x = xStartBlock; x <= xEndBlock; ++x ) + { + for ( int y = yStartBlock; y <= yEndBlock; ++y ) + { + int oldTileCount; + StaticTile[] oldTiles = ReadStaticBlock( idxReader, mulStream, x, y, matrix.BlockWidth, matrix.BlockHeight, out oldTileCount ); + + if ( oldTileCount < 0 ) + continue; + + int newTileCount = 0; + StaticTile[] newTiles = new StaticTile[oldTileCount]; + + int baseX = (x << 3) - xTileStart, baseY = (y << 3) - yTileStart; + + for ( int i = 0; i < oldTileCount; ++i ) + { + StaticTile oldTile = oldTiles[i]; + + int px = baseX + oldTile.m_X; + int py = baseY + oldTile.m_Y; + + if ( px < 0 || px >= xTileWidth || py < 0 || py >= yTileHeight ) + { + newTiles[newTileCount++] = oldTile; + } + else + { + ++totalUnfrozen; + + Item item = new Static( oldTile.m_ID & 0x3FFF ); + + item.Hue = oldTile.m_Hue; + + item.MoveToWorld( new Point3D( px + xTileStart, py + yTileStart, oldTile.m_Z ), map ); + } + } + + int mulPos = -1; + int length = -1; + int extra = 0; + + if ( newTileCount > 0 ) + { + mulWriter.Seek( 0, SeekOrigin.End ); + + mulPos = (int)mulWriter.BaseStream.Position; + length = newTileCount * 7; + extra = 1; + + for ( int i = 0; i < newTileCount; ++i ) + { + StaticTile toWrite = newTiles[i]; + + mulWriter.Write( (short) toWrite.m_ID ); + mulWriter.Write( (byte) toWrite.m_X ); + mulWriter.Write( (byte) toWrite.m_Y ); + mulWriter.Write( (sbyte) toWrite.m_Z ); + mulWriter.Write( (short) toWrite.m_Hue ); + } + + mulWriter.Flush(); + } + + int idxPos = ((x * matrix.BlockHeight) + y) * 12; + + idxWriter.Seek( idxPos, SeekOrigin.Begin ); + idxWriter.Write( mulPos ); + idxWriter.Write( length ); + idxWriter.Write( extra ); + + idxWriter.Flush(); + + matrix.SetStaticBlock( x, y, null ); + } + } + } + } + } + + public static void DoUnfreeze( Map map, ref bool badDataFile, ref int totalUnfrozen ) + { + DoUnfreeze( map, Point2D.Zero, new Point2D( map.Width - 1, map.Height - 1 ), ref badDataFile, ref totalUnfrozen ); + } + + public static void Unfreeze( Mobile from, Map map, Point3D start, Point3D end ) + { + int totalUnfrozen = 0; + bool badDataFile = false; + + if ( map == null ) + { + CommandLogging.WriteLine( from, "{0} {1} invoking unfreeze for every item in every map", from.AccessLevel, CommandLogging.Format( from ) ); + + DoUnfreeze( Map.Felucca, ref badDataFile, ref totalUnfrozen ); + DoUnfreeze( Map.Trammel, ref badDataFile, ref totalUnfrozen ); + DoUnfreeze( Map.Ilshenar, ref badDataFile, ref totalUnfrozen ); + DoUnfreeze( Map.Malas, ref badDataFile, ref totalUnfrozen ); + DoUnfreeze( Map.Tokuno, ref badDataFile, ref totalUnfrozen ); + } + else if ( start == NullP3D && end == NullP3D ) + { + CommandLogging.WriteLine( from, "{0} {1} invoking unfreeze for every item in {2}", from.AccessLevel, CommandLogging.Format( from ), map ); + + DoUnfreeze( map, ref badDataFile, ref totalUnfrozen ); + } + else + { + CommandLogging.WriteLine( from, "{0} {1} invoking unfreeze from {2} to {3} in {4}", from.AccessLevel, CommandLogging.Format( from ), new Point2D( start ), new Point2D( end ), map ); + + DoUnfreeze( map, new Point2D( start ), new Point2D( end ), ref badDataFile, ref totalUnfrozen ); + } + + if ( totalUnfrozen == 0 && badDataFile ) + from.SendGump( new NoticeGump( 1060637, 30720, "Output data files could not be opened and the unfreeze operation has been aborted.

This probably means your server and client are using the same data files. Instructions on how to resolve this can be found in the first warning window.", 0xFFC000, 320, 240, null, null ) ); + else + from.SendGump( new NoticeGump( 1060637, 30720, String.Format( "Unfreeze operation completed successfully.

{0} item{1} unfrozen.

You must restart your client and update it's data files to see the changes.", totalUnfrozen, totalUnfrozen != 1 ? "s were" : " was" ), 0xFFC000, 320, 240, null, null ) ); + } + + private static FileStream OpenWrite( FileStream orig ) + { + if ( orig == null ) + return null; + + try{ return new FileStream( orig.Name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite ); } + catch{ return null; } + } + + private static byte[] m_Buffer; + + private static StaticTile[] m_TileBuffer = new StaticTile[128]; + + private static StaticTile[] ReadStaticBlock( BinaryReader idxReader, FileStream mulStream, int x, int y, int width, int height, out int count ) + { + try + { + if ( x < 0 || x >= width || y < 0 || y >= height ) + { + count = -1; + return m_TileBuffer; + } + + idxReader.BaseStream.Seek( ((x * height) + y) * 12, SeekOrigin.Begin ); + + int lookup = idxReader.ReadInt32(); + int length = idxReader.ReadInt32(); + + if ( lookup < 0 || length <= 0 ) + { + count = 0; + } + else + { + count = length / 7; + + mulStream.Seek( lookup, SeekOrigin.Begin ); + + if ( m_TileBuffer.Length < count ) + m_TileBuffer = new StaticTile[count]; + + StaticTile[] staTiles = m_TileBuffer; + + if ( m_Buffer == null || length > m_Buffer.Length ) + m_Buffer = new byte[length]; + + mulStream.Read( m_Buffer, 0, length ); + + int index = 0; + + for ( int i = 0; i < count; ++i ) + { + staTiles[i].m_ID = (short)(m_Buffer[index++] | (m_Buffer[index++] << 8)); + staTiles[i].m_X = m_Buffer[index++]; + staTiles[i].m_Y = m_Buffer[index++]; + staTiles[i].m_Z = (sbyte)m_Buffer[index++]; + staTiles[i].m_Hue = (short)(m_Buffer[index++] | (m_Buffer[index++] << 8)); + } + } + } + catch + { + count = -1; + } + + return m_TileBuffer; + } + + private class DeltaState + { + public int m_X, m_Y; + public ArrayList m_List; + + public DeltaState( Point2D p ) + { + m_X = p.X; + m_Y = p.Y; + m_List = new ArrayList(); + } + } + + private class StateInfo + { + public Map m_Map; + public Point3D m_Start, m_End; + + public StateInfo( Map map, Point3D start, Point3D end ) + { + m_Map = map; + m_Start = start; + m_End = end; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Commands/VisibilityList.cs b/Scripts/Commands/VisibilityList.cs new file mode 100644 index 000000000..4f757a5ed --- /dev/null +++ b/Scripts/Commands/VisibilityList.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Network; +using System.Collections.Generic; + +namespace Server.Commands +{ + public class VisibilityList + { + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( OnLogin ); + + CommandSystem.Register( "Vis", AccessLevel.Counselor, new CommandEventHandler( Vis_OnCommand ) ); + CommandSystem.Register( "VisList", AccessLevel.Counselor, new CommandEventHandler( VisList_OnCommand ) ); + CommandSystem.Register( "VisClear", AccessLevel.Counselor, new CommandEventHandler( VisClear_OnCommand ) ); + } + + public static void OnLogin( LoginEventArgs e ) + { + if ( e.Mobile is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)e.Mobile; + + pm.VisibilityList.Clear(); + } + } + + [Usage( "Vis" )] + [Description( "Adds or removes a targeted player from your visibility list. Anyone on your visibility list will be able to see you at all times, even when you're hidden." )] + public static void Vis_OnCommand( CommandEventArgs e ) + { + if ( e.Mobile is PlayerMobile ) + { + e.Mobile.Target = new VisTarget(); + e.Mobile.SendMessage( "Select person to add or remove from your visibility list." ); + } + } + + [Usage( "VisList" )] + [Description( "Shows the names of everyone in your visibility list." )] + public static void VisList_OnCommand( CommandEventArgs e ) + { + if ( e.Mobile is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)e.Mobile; + List list = pm.VisibilityList; + + if ( list.Count > 0 ) + { + pm.SendMessage( "You are visible to {0} mobile{1}:", list.Count, list.Count == 1 ? "" : "s" ); + + for ( int i = 0; i < list.Count; ++i ) + pm.SendMessage( "#{0}: {1}", i+1, list[i].Name ); + } + else + { + pm.SendMessage( "Your visibility list is empty." ); + } + } + } + + [Usage( "VisClear" )] + [Description( "Removes everyone from your visibility list." )] + public static void VisClear_OnCommand( CommandEventArgs e ) + { + if ( e.Mobile is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)e.Mobile; + List list = new List( pm.VisibilityList ); + + pm.VisibilityList.Clear(); + pm.SendMessage( "Your visibility list has been cleared." ); + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = list[i]; + + if ( !m.CanSee( pm ) && Utility.InUpdateRange( m, pm ) ) + m.Send( pm.RemovePacket ); + } + } + } + + private class VisTarget : Target + { + public VisTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( from is PlayerMobile && targeted is Mobile ) + { + PlayerMobile pm = (PlayerMobile)from; + Mobile targ = (Mobile)targeted; + + if ( targ.AccessLevel < from.AccessLevel ) + { + List list = pm.VisibilityList; + + if ( list.Contains( targ ) ) + { + list.Remove( targ ); + from.SendMessage( "{0} has been removed from your visibility list.", targ.Name ); + } + else + { + list.Add( targ ); + from.SendMessage( "{0} has been added to your visibility list.", targ.Name ); + } + + if ( Utility.InUpdateRange( targ, from ) ) + { + if ( targ.CanSee( from ) ) + { + targ.Send( new Network.MobileIncoming( targ, from ) ); + + if ( ObjectPropertyList.Enabled ) + { + targ.Send( from.OPLPacket ); + + foreach ( Item item in from.Items ) + targ.Send( item.OPLPacket ); + } + } + else + { + targ.Send( from.RemovePacket ); + } + } + } + else + { + from.SendMessage( "They can already see you!" ); + } + } + else + { + from.SendMessage( "Add only mobiles to your visibility list." ); + } + } + } + } +} diff --git a/Scripts/Commands/Wipe.cs b/Scripts/Commands/Wipe.cs new file mode 100644 index 000000000..6d8bfc2a4 --- /dev/null +++ b/Scripts/Commands/Wipe.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Items; +using Server.Multis; + +namespace Server.Commands +{ + public class Wipe + { + [Flags] + public enum WipeType + { + Items = 0x01, + Mobiles = 0x02, + Multis = 0x04, + All = Items | Mobiles | Multis + } + + public static void Initialize() + { + CommandSystem.Register( "Wipe", AccessLevel.GameMaster, new CommandEventHandler( WipeAll_OnCommand ) ); + CommandSystem.Register( "WipeItems", AccessLevel.GameMaster, new CommandEventHandler( WipeItems_OnCommand ) ); + CommandSystem.Register( "WipeNPCs", AccessLevel.GameMaster, new CommandEventHandler( WipeNPCs_OnCommand ) ); + CommandSystem.Register( "WipeMultis", AccessLevel.GameMaster, new CommandEventHandler( WipeMultis_OnCommand ) ); + } + + [Usage( "Wipe" )] + [Description( "Wipes all items and npcs in a targeted bounding box." )] + private static void WipeAll_OnCommand( CommandEventArgs e ) + { + BeginWipe( e.Mobile, WipeType.Items | WipeType.Mobiles ); + } + + [Usage( "WipeItems" )] + [Description( "Wipes all items in a targeted bounding box." )] + private static void WipeItems_OnCommand( CommandEventArgs e ) + { + BeginWipe( e.Mobile, WipeType.Items ); + } + + [Usage( "WipeNPCs" )] + [Description( "Wipes all npcs in a targeted bounding box." )] + private static void WipeNPCs_OnCommand( CommandEventArgs e ) + { + BeginWipe( e.Mobile, WipeType.Mobiles ); + } + + [Usage( "WipeMultis" )] + [Description( "Wipes all multis in a targeted bounding box." )] + private static void WipeMultis_OnCommand( CommandEventArgs e ) + { + BeginWipe( e.Mobile, WipeType.Multis ); + } + + public static void BeginWipe( Mobile from, WipeType type ) + { + BoundingBoxPicker.Begin( from, new BoundingBoxCallback( WipeBox_Callback ), type ); + } + + private static void WipeBox_Callback( Mobile from, Map map, Point3D start, Point3D end, object state ) + { + DoWipe( from, map, start, end, (WipeType)state ); + } + + public static void DoWipe( Mobile from, Map map, Point3D start, Point3D end, WipeType type ) + { + CommandLogging.WriteLine( from, "{0} {1} wiping from {2} to {3} in {5} ({4})", from.AccessLevel, CommandLogging.Format( from ), start, end, type, map ); + + bool mobiles = ( (type & WipeType.Mobiles) != 0 ); + bool multis = ( (type & WipeType.Multis) != 0 ); + bool items = ( (type & WipeType.Items) != 0 ); + + ArrayList toDelete = new ArrayList(); + + Rectangle2D rect = new Rectangle2D( start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1 ); + + IPooledEnumerable eable; + + if ( (items || multis) && mobiles ) + eable = map.GetObjectsInBounds( rect ); + else if ( items || multis ) + eable = map.GetItemsInBounds( rect ); + else if ( mobiles ) + eable = map.GetMobilesInBounds( rect ); + else + return; + + foreach ( object obj in eable ) + { + if ( items && (obj is Item) && !((obj is BaseMulti) || (obj is HouseSign)) ) + toDelete.Add( obj ); + else if ( multis && (obj is BaseMulti) ) + toDelete.Add( obj ); + else if ( mobiles && (obj is Mobile) && !((Mobile)obj).Player ) + toDelete.Add( obj ); + } + + eable.Free(); + + for ( int i = 0; i < toDelete.Count; ++i ) + { + if ( toDelete[i] is Item ) + ((Item)toDelete[i]).Delete(); + else if ( toDelete[i] is Mobile ) + ((Mobile)toDelete[i]).Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Context Menus/AddToSpellbookEntry.cs b/Scripts/Context Menus/AddToSpellbookEntry.cs new file mode 100644 index 000000000..575a9c4b4 --- /dev/null +++ b/Scripts/Context Menus/AddToSpellbookEntry.cs @@ -0,0 +1,63 @@ +using System; +using Server.Items; +using Server.Targeting; + +namespace Server.ContextMenus +{ + public class AddToSpellbookEntry : ContextMenuEntry + { + public AddToSpellbookEntry() : base( 6144, 3 ) + { + } + + public override void OnClick() + { + if ( Owner.From.CheckAlive() && Owner.Target is SpellScroll ) + Owner.From.Target = new InternalTarget( (SpellScroll)Owner.Target ); + } + + private class InternalTarget : Target + { + private SpellScroll m_Scroll; + + public InternalTarget( SpellScroll scroll ) : base( 3, false, TargetFlags.None ) + { + m_Scroll = scroll; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Spellbook ) + { + if ( from.CheckAlive() && !m_Scroll.Deleted && m_Scroll.Movable && m_Scroll.Amount >= 1 ) + { + Spellbook book = (Spellbook)targeted; + + SpellbookType type = Spellbook.GetTypeForSpell( m_Scroll.SpellID ); + + if ( type != book.SpellbookType ) + { + } + else if ( book.HasSpell( m_Scroll.SpellID ) ) + { + from.SendLocalizedMessage( 500179 ); // That spell is already present in that spellbook. + } + else + { + int val = m_Scroll.SpellID - book.BookOffset; + + if ( val >= 0 && val < book.BookCount ) + { + book.Content |= (ulong)1 << val; + + m_Scroll.Consume(); + + from.Send( new Network.PlaySound( 0x249, book.GetWorldLocation() ) ); + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Context Menus/EatEntry.cs b/Scripts/Context Menus/EatEntry.cs new file mode 100644 index 000000000..e3e3b53f0 --- /dev/null +++ b/Scripts/Context Menus/EatEntry.cs @@ -0,0 +1,25 @@ +using System; +using Server.Items; + +namespace Server.ContextMenus +{ + public class EatEntry : ContextMenuEntry + { + private Mobile m_From; + private Food m_Food; + + public EatEntry( Mobile from, Food food ) : base( 6135, 1 ) + { + m_From = from; + m_Food = food; + } + + public override void OnClick() + { + if ( m_Food.Deleted || !m_Food.Movable || !m_From.CheckAlive() ) + return; + + m_Food.Eat( m_From ); + } + } +} \ No newline at end of file diff --git a/Scripts/Context Menus/OpenBankEntry.cs b/Scripts/Context Menus/OpenBankEntry.cs new file mode 100644 index 000000000..f2b4eff00 --- /dev/null +++ b/Scripts/Context Menus/OpenBankEntry.cs @@ -0,0 +1,33 @@ +using System; +using Server.Items; + +namespace Server.ContextMenus +{ + public class OpenBankEntry : ContextMenuEntry + { + private Mobile m_Banker; + + public OpenBankEntry( Mobile from, Mobile banker ) : base( 6105, 12 ) + { + m_Banker = banker; + } + + public override void OnClick() + { + if ( !Owner.From.CheckAlive() ) + return; + + if ( Owner.From.Criminal ) + { + m_Banker.Say( 500378 ); // Thou art a criminal and cannot access thy bank box. + } + else + { + BankBox box = this.Owner.From.BankBox; + + if ( box != null ) + box.Open(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Context Menus/TeachEntry.cs b/Scripts/Context Menus/TeachEntry.cs new file mode 100644 index 000000000..d5138aff4 --- /dev/null +++ b/Scripts/Context Menus/TeachEntry.cs @@ -0,0 +1,30 @@ +using System; +using Server.Mobiles; + +namespace Server.ContextMenus +{ + public class TeachEntry : ContextMenuEntry + { + private SkillName m_Skill; + private BaseCreature m_Mobile; + private Mobile m_From; + + public TeachEntry( SkillName skill, BaseCreature m, Mobile from, bool enabled ) : base( 6000 + (int)skill, 4 ) + { + m_Skill = skill; + m_Mobile = m; + m_From = from; + + if ( !enabled ) + Flags |= Network.CMEFlags.Disabled; + } + + public override void OnClick() + { + if ( !m_From.CheckAlive() ) + return; + + m_Mobile.Teach( m_Skill, m_From, 0, false ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/AnimalAI.cs b/Scripts/Engines/AI/AI/AnimalAI.cs new file mode 100644 index 000000000..87ec9b5b9 --- /dev/null +++ b/Scripts/Engines/AI/AI/AnimalAI.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +// Ideas +// When you run on animals the panic +// When if ( distance < 8 && Utility.RandomDouble() * Math.Sqrt( (8 - distance) / 6 ) >= incoming.Skills[SkillName.AnimalTaming].Value ) +// More your close, the more it can panic +/* + * AnimalHunterAI, AnimalHidingAI, AnimalDomesticAI... + * + */ + +namespace Server.Mobiles +{ + public class AnimalAI : BaseAI + { + public AnimalAI(BaseCreature m) : base (m) + { + } + + public override bool DoActionWander() + { + // Old: +#if false + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true)) + { + m_Mobile.DebugSay( "There is something near, I go away" ); + Action = ActionType.Backoff; + } + else if ( m_Mobile.IsHurt() || m_Mobile.Combatant != null ) + { + m_Mobile.DebugSay( "I am hurt or being attacked, I flee" ); + Action = ActionType.Flee; + } + else + { + base.DoActionWander(); + } + + return true; +#endif + + // New, only flee @ 10% + + double hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if ( !m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 ) // Less than 10% health + { + m_Mobile.DebugSay( "I am low on health!" ); + Action = ActionType.Flee; + } + else if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + Mobile combatant = m_Mobile.Combatant; + + if ( combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map ) + { + m_Mobile.DebugSay( "My combatant is gone.." ); + + Action = ActionType.Wander; + + return true; + } + + if ( WalkMobileRange( combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight ) ) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo( combatant ); + } + else + { + if ( m_Mobile.GetDistanceToSqrt( combatant ) > m_Mobile.RangePerception + 1 ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I cannot find {0}", combatant.Name ); + + Action = ActionType.Wander; + + return true; + } + else + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I should be closer to {0}", combatant.Name ); + } + } + + if ( !m_Mobile.Controlled && !m_Mobile.Summoned ) + { + double hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if ( hitPercent < 0.1 ) + { + m_Mobile.DebugSay( "I am low on health!" ); + Action = ActionType.Flee; + } + } + + return true; + } + + public override bool DoActionBackoff() + { + double hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + + if ( !m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 ) // Less than 10% health + { + Action = ActionType.Flee; + } + else + { + if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false , true)) + { + if ( WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2) ) + { + m_Mobile.DebugSay( "Well, here I am safe" ); + Action = ActionType.Wander; + } + } + else + { + m_Mobile.DebugSay( "I have lost my focus, lets relax" ); + Action = ActionType.Wander; + } + } + + return true; + } + + public override bool DoActionFlee() + { + AcquireFocusMob(m_Mobile.RangePerception * 2, m_Mobile.FightMode, true, false, true); + + if ( m_Mobile.FocusMob == null ) + m_Mobile.FocusMob = m_Mobile.Combatant; + + return base.DoActionFlee(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/ArcherAI.cs b/Scripts/Engines/AI/AI/ArcherAI.cs new file mode 100644 index 000000000..fbf770418 --- /dev/null +++ b/Scripts/Engines/AI/AI/ArcherAI.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; +using Server.Items; + +namespace Server.Mobiles +{ + public class ArcherAI : BaseAI + { + public ArcherAI(BaseCreature m) : base (m) + { + } + + public override bool DoActionWander() + { + m_Mobile.DebugSay( "I have no combatant" ); + + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected {0} and I will attack", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + return base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + if ( m_Mobile.Combatant == null || m_Mobile.Combatant.Deleted || !m_Mobile.Combatant.Alive || m_Mobile.Combatant.IsDeadBondedPet ) + { + m_Mobile.DebugSay("My combatant is deleted"); + Action = ActionType.Guard; + return true; + } + + if ( (m_Mobile.LastMoveTime + TimeSpan.FromSeconds( 1.0 )) < DateTime.Now ) + { + if (WalkMobileRange(m_Mobile.Combatant, 1, true, m_Mobile.RangeFight, m_Mobile.Weapon.MaxRange)) + { + // Be sure to face the combatant + m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant.Location); + } + else + { + if ( m_Mobile.Combatant != null ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I am still not in range of {0}", m_Mobile.Combatant.Name); + + if ( (int) m_Mobile.GetDistanceToSqrt( m_Mobile.Combatant ) > m_Mobile.RangePerception + 1 ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have lost {0}", m_Mobile.Combatant.Name); + + m_Mobile.Combatant = null; + Action = ActionType.Guard; + return true; + } + } + } + } + + // When we have no ammo, we flee + Container pack = m_Mobile.Backpack; + + if ( pack == null || pack.FindItemByType( typeof( Arrow ) ) == null ) + { + Action = ActionType.Flee; + return true; + } + + + // At 20% we should check if we must leave + if ( m_Mobile.Hits < m_Mobile.HitsMax*20/100 ) + { + bool bFlee = false; + // if my current hits are more than my opponent, i don't care + if ( m_Mobile.Combatant != null && m_Mobile.Hits < m_Mobile.Combatant.Hits) + { + int iDiff = m_Mobile.Combatant.Hits - m_Mobile.Hits; + + if ( Utility.Random(0, 100) > 10 + iDiff) // 10% to flee + the diff of hits + { + bFlee = true; + } + } + else if ( m_Mobile.Combatant != null && m_Mobile.Hits >= m_Mobile.Combatant.Hits) + { + if ( Utility.Random(0, 100) > 10 ) // 10% to flee + { + bFlee = true; + } + } + + if (bFlee) + { + Action = ActionType.Flee; + } + } + + return true; + } + + public override bool DoActionGuard() + { + if ( AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/BaseAI.cs b/Scripts/Engines/AI/AI/BaseAI.cs new file mode 100644 index 000000000..1af317c5a --- /dev/null +++ b/Scripts/Engines/AI/AI/BaseAI.cs @@ -0,0 +1,2748 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Targeting; +using Server.Targets; +using Server.Network; +using Server.Regions; +using Server.ContextMenus; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; +using MoveImpl=Server.Movement.MovementImpl; + +namespace Server.Mobiles +{ + public enum AIType + { + AI_Use_Default, + AI_Melee, + AI_Animal, + AI_Archer, + AI_Healer, + AI_Vendor, + AI_Mage, + AI_Berserk, + AI_Predator, + AI_Thief + } + + public enum ActionType + { + Wander, + Combat, + Guard, + Flee, + Backoff, + Interact + } + + public abstract class BaseAI + { + public Timer m_Timer; + protected ActionType m_Action; + private DateTime m_NextStopGuard; + + public BaseCreature m_Mobile; + + public BaseAI( BaseCreature m ) + { + m_Mobile = m; + + m_Timer = new AITimer( this ); + + bool activate; + + if( !m.PlayerRangeSensitive ) + activate = true; + else if( World.Loading ) + activate = false; + else if( m.Map == null || m.Map == Map.Internal || !m.Map.GetSector( m ).Active ) + activate = false; + else + activate = true; + + if( activate ) + m_Timer.Start(); + + Action = ActionType.Wander; + } + + public ActionType Action + { + get + { + return m_Action; + } + set + { + m_Action = value; + OnActionChanged(); + } + } + + public virtual bool WasNamed( string speech ) + { + string name = m_Mobile.Name; + + return (name != null && Insensitive.StartsWith( speech, name )); + } + + private class InternalEntry : ContextMenuEntry + { + private Mobile m_From; + private BaseCreature m_Mobile; + private BaseAI m_AI; + private OrderType m_Order; + + public InternalEntry( Mobile from, int number, int range, BaseCreature mobile, BaseAI ai, OrderType order ) + : base( number, range ) + { + m_From = from; + m_Mobile = mobile; + m_AI = ai; + m_Order = order; + + if( mobile.IsDeadPet && (order == OrderType.Guard || order == OrderType.Attack || order == OrderType.Transfer || order == OrderType.Drop) ) + Enabled = false; + } + + public override void OnClick() + { + if( !m_Mobile.Deleted && m_Mobile.Controlled && m_From.CheckAlive() ) + { + if( m_Mobile.IsDeadPet && (m_Order == OrderType.Guard || m_Order == OrderType.Attack || m_Order == OrderType.Transfer || m_Order == OrderType.Drop) ) + return; + + bool isOwner = (m_From == m_Mobile.ControlMaster); + bool isFriend = (!isOwner && m_Mobile.IsPetFriend( m_From )); + + if( !isOwner && !isFriend ) + return; + else if( isFriend && m_Order != OrderType.Follow && m_Order != OrderType.Stay && m_Order != OrderType.Stop ) + return; + + switch( m_Order ) + { + case OrderType.Follow: + case OrderType.Attack: + case OrderType.Transfer: + case OrderType.Friend: + case OrderType.Unfriend: + { + if( m_Order == OrderType.Transfer && m_From.HasTrade ) + m_From.SendLocalizedMessage( 1010507 ); // You cannot transfer a pet with a trade pending + else if( m_Order == OrderType.Friend && m_From.HasTrade ) + m_From.SendLocalizedMessage( 1070947 ); // You cannot friend a pet with a trade pending + else + m_AI.BeginPickTarget( m_From, m_Order ); + + break; + } + case OrderType.Release: + { + if( m_Mobile.Summoned ) + goto default; + else + m_From.SendGump( new Gumps.ConfirmReleaseGump( m_From, m_Mobile ) ); + + break; + } + default: + { + if( m_Mobile.CheckControlChance( m_From ) ) + m_Mobile.ControlOrder = m_Order; + + break; + } + } + } + } + } + + public virtual void GetContextMenuEntries( Mobile from, List list ) + { + if( from.Alive && m_Mobile.Controlled && from.InRange( m_Mobile, 14 ) ) + { + if( from == m_Mobile.ControlMaster ) + { + list.Add( new InternalEntry( from, 6107, 14, m_Mobile, this, OrderType.Guard ) ); // Command: Guard + list.Add( new InternalEntry( from, 6108, 14, m_Mobile, this, OrderType.Follow ) ); // Command: Follow + + if( m_Mobile.CanDrop ) + list.Add( new InternalEntry( from, 6109, 14, m_Mobile, this, OrderType.Drop ) ); // Command: Drop + + list.Add( new InternalEntry( from, 6111, 14, m_Mobile, this, OrderType.Attack ) ); // Command: Kill + + list.Add( new InternalEntry( from, 6112, 14, m_Mobile, this, OrderType.Stop ) ); // Command: Stop + list.Add( new InternalEntry( from, 6114, 14, m_Mobile, this, OrderType.Stay ) ); // Command: Stay + + if( !m_Mobile.Summoned ) + { + list.Add( new InternalEntry( from, 6110, 14, m_Mobile, this, OrderType.Friend ) ); // Add Friend + list.Add( new InternalEntry( from, 6099, 14, m_Mobile, this, OrderType.Unfriend ) ); // Remove Friend + list.Add( new InternalEntry( from, 6113, 14, m_Mobile, this, OrderType.Transfer ) ); // Transfer + } + + list.Add( new InternalEntry( from, 6118, 14, m_Mobile, this, OrderType.Release ) ); // Release + } + else if( m_Mobile.IsPetFriend( from ) ) + { + list.Add( new InternalEntry( from, 6108, 14, m_Mobile, this, OrderType.Follow ) ); // Command: Follow + list.Add( new InternalEntry( from, 6112, 14, m_Mobile, this, OrderType.Stop ) ); // Command: Stop + list.Add( new InternalEntry( from, 6114, 14, m_Mobile, this, OrderType.Stay ) ); // Command: Stay + } + } + } + + public virtual void BeginPickTarget( Mobile from, OrderType order ) + { + if( m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange( m_Mobile, 14 ) || from.Map != m_Mobile.Map ) + return; + + bool isOwner = (from == m_Mobile.ControlMaster); + bool isFriend = (!isOwner && m_Mobile.IsPetFriend( from )); + + if( !isOwner && !isFriend ) + return; + else if( isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop ) + return; + + if( from.Target == null ) + { + if( order == OrderType.Transfer ) + from.SendLocalizedMessage( 502038 ); // Click on the person to transfer ownership to. + else if( order == OrderType.Friend ) + from.SendLocalizedMessage( 502020 ); // Click on the player whom you wish to make a co-owner. + else if( order == OrderType.Unfriend ) + from.SendLocalizedMessage( 1070948 ); // Click on the player whom you wish to remove as a co-owner. + + from.Target = new AIControlMobileTarget( this, order ); + } + else if( from.Target is AIControlMobileTarget ) + { + AIControlMobileTarget t = (AIControlMobileTarget)from.Target; + + if( t.Order == order ) + t.AddAI( this ); + } + } + + public virtual void OnAggressiveAction( Mobile aggressor ) + { + Mobile currentCombat = m_Mobile.Combatant; + + if( currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && m_Mobile.GetDistanceToSqrt( currentCombat ) > m_Mobile.GetDistanceToSqrt( aggressor ) ) + m_Mobile.Combatant = aggressor; + } + + public virtual void EndPickTarget( Mobile from, Mobile target, OrderType order ) + { + if( m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange( m_Mobile, 14 ) || from.Map != m_Mobile.Map || !from.CheckAlive() ) + return; + + bool isOwner = (from == m_Mobile.ControlMaster); + bool isFriend = (!isOwner && m_Mobile.IsPetFriend( from )); + + if( !isOwner && !isFriend ) + return; + else if( isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop ) + return; + + if( order == OrderType.Attack ) + { + if( target is BaseCreature && ((BaseCreature)target).IsScaryToPets && m_Mobile.IsScaredOfScaryThings ) + { + m_Mobile.SayTo( from, "Your pet refuses to attack this creature!" ); + return; + } + + if( (SolenHelper.CheckRedFriendship( from ) && + (target is RedSolenInfiltratorQueen + || target is RedSolenInfiltratorWarrior + || target is RedSolenQueen + || target is RedSolenWarrior + || target is RedSolenWorker)) + || (SolenHelper.CheckBlackFriendship( from ) && + (target is BlackSolenInfiltratorQueen + || target is BlackSolenInfiltratorWarrior + || target is BlackSolenQueen + || target is BlackSolenWarrior + || target is BlackSolenWorker)) ) + { + from.SendAsciiMessage( "You can not force your pet to attack a creature you are protected from." ); + return; + } + + if( target is Factions.BaseFactionGuard ) + { + m_Mobile.SayTo( from, "Your pet refuses to attack the guard." ); + return; + } + } + + if( m_Mobile.CheckControlChance( from ) ) + { + m_Mobile.ControlTarget = target; + m_Mobile.ControlOrder = order; + } + } + + public virtual bool HandlesOnSpeech( Mobile from ) + { + if( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if( from.Alive && m_Mobile.Controlled && m_Mobile.Commandable && (from == m_Mobile.ControlMaster || m_Mobile.IsPetFriend( from )) ) + return true; + + return (from.Alive && from.InRange( m_Mobile.Location, 3 ) && m_Mobile.IsHumanInTown()); + } + + private static SkillName[] m_KeywordTable = new SkillName[] + { + SkillName.Parry, + SkillName.Healing, + SkillName.Hiding, + SkillName.Stealing, + SkillName.Alchemy, + SkillName.AnimalLore, + SkillName.ItemID, + SkillName.ArmsLore, + SkillName.Begging, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Peacemaking, + SkillName.Camping, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.DetectHidden, + SkillName.Discordance,//?? + SkillName.EvalInt, + SkillName.Fishing, + SkillName.Provocation, + SkillName.Lockpicking, + SkillName.Magery, + SkillName.MagicResist, + SkillName.Tactics, + SkillName.Snooping, + SkillName.RemoveTrap, + SkillName.Musicianship, + SkillName.Poisoning, + SkillName.Archery, + SkillName.SpiritSpeak, + SkillName.Tailoring, + SkillName.AnimalTaming, + SkillName.TasteID, + SkillName.Tinkering, + SkillName.Veterinary, + SkillName.Forensics, + SkillName.Herding, + SkillName.Tracking, + SkillName.Stealth, + SkillName.Inscribe, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling, + SkillName.Lumberjacking, + SkillName.Mining, + SkillName.Meditation + }; + + public virtual void OnSpeech( SpeechEventArgs e ) + { + if( e.Mobile.Alive && e.Mobile.InRange( m_Mobile.Location, 3 ) && m_Mobile.IsHumanInTown() ) + { + if( e.HasKeyword( 0x9D ) && WasNamed( e.Speech ) ) // *move* + { + if( m_Mobile.Combatant != null ) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 501482 ); + } + else + { + // Excuse me? + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 501516 ); + WalkRandomInHome( 2, 2, 1 ); + } + } + else if( e.HasKeyword( 0x9E ) && WasNamed( e.Speech ) ) // *time* + { + if( m_Mobile.Combatant != null ) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 501482 ); + } + else + { + int generalNumber; + string exactTime; + + Clock.GetTime( m_Mobile, out generalNumber, out exactTime ); + + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, generalNumber ); + } + } + else if( e.HasKeyword( 0x6C ) && WasNamed( e.Speech ) ) // *train + { + if( m_Mobile.Combatant != null ) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 501482 ); + } + else + { + bool foundSomething = false; + + Skills ourSkills = m_Mobile.Skills; + Skills theirSkills = e.Mobile.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 && m_Mobile.CheckTeach( skill.SkillName, e.Mobile ) ) + { + double toTeach = skill.Base / 3.0; + + if( toTeach > 42.0 ) + toTeach = 42.0; + + if( toTeach > theirSkill.Base ) + { + int number = 1043059 + i; + + if( number > 1043107 ) + continue; + + if( !foundSomething ) + m_Mobile.Say( 1043058 ); // I can train the following: + + m_Mobile.Say( number ); + + foundSomething = true; + } + } + } + + if( !foundSomething ) + m_Mobile.Say( 501505 ); // Alas, I cannot teach thee anything. + } + } + else + { + SkillName toTrain = (SkillName)(-1); + + for( int i = 0; toTrain == (SkillName)(-1) && i < e.Keywords.Length; ++i ) + { + int keyword = e.Keywords[i]; + + if( keyword == 0x154 ) + { + toTrain = SkillName.Anatomy; + } + else if( keyword >= 0x6D && keyword <= 0x9C ) + { + int index = keyword - 0x6D; + + if( index >= 0 && index < m_KeywordTable.Length ) + toTrain = m_KeywordTable[index]; + } + } + + if( toTrain != (SkillName)(-1) && WasNamed( e.Speech ) ) + { + if( m_Mobile.Combatant != null ) + { + // I am too busy fighting to deal with thee! + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 501482 ); + } + else + { + Skills skills = m_Mobile.Skills; + Skill skill = skills[toTrain]; + + if( skill == null || skill.Base < 60.0 || !m_Mobile.CheckTeach( toTrain, e.Mobile ) ) + { + m_Mobile.Say( 501507 ); // 'Tis not something I can teach thee of. + } + else + { + m_Mobile.Teach( toTrain, e.Mobile, 0, false ); + } + } + } + } + } + + if( m_Mobile.Controlled && m_Mobile.Commandable ) + { + m_Mobile.DebugSay( "Listening..." ); + + bool isOwner = (e.Mobile == m_Mobile.ControlMaster); + bool isFriend = (!isOwner && m_Mobile.IsPetFriend( e.Mobile )); + + if( e.Mobile.Alive && (isOwner || isFriend) ) + { + m_Mobile.DebugSay( "It's from my master" ); + + int[] keywords = e.Keywords; + string speech = e.Speech; + + // First, check the all* + for( int i = 0; i < keywords.Length; ++i ) + { + int keyword = keywords[i]; + + switch( keyword ) + { + case 0x164: // all come + { + if( !isOwner ) + break; + + if( m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Come; + } + + return; + } + case 0x165: // all follow + { + BeginPickTarget( e.Mobile, OrderType.Follow ); + return; + } + case 0x166: // all guard + { + if( !isOwner ) + break; + + if( m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Guard; + } + return; + } + case 0x167: // all stop + { + if( m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stop; + } + return; + } + case 0x168: // all kill + case 0x169: // all attack + { + if( !isOwner ) + break; + + BeginPickTarget( e.Mobile, OrderType.Attack ); + return; + } + case 0x16B: // all guard me + { + if( !isOwner ) + break; + + if( m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = e.Mobile; + m_Mobile.ControlOrder = OrderType.Guard; + } + return; + } + case 0x16C: // all follow me + { + if( m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = e.Mobile; + m_Mobile.ControlOrder = OrderType.Follow; + } + return; + } + case 0x170: // all stay + { + if( m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + } + return; + } + } + } + + // No all*, so check *command + for( int i = 0; i < keywords.Length; ++i ) + { + int keyword = keywords[i]; + + switch( keyword ) + { + case 0x155: // *come + { + if( !isOwner ) + break; + + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Come; + } + + return; + } + case 0x156: // *drop + { + if( !isOwner ) + break; + + if( !m_Mobile.IsDeadPet && !m_Mobile.Summoned && WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Drop; + } + + return; + } + case 0x15A: // *follow + { + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + BeginPickTarget( e.Mobile, OrderType.Follow ); + + return; + } + case 0x15B: // *friend + { + if( !isOwner ) + break; + + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + if( m_Mobile.Summoned ) + e.Mobile.SendLocalizedMessage( 1005481 ); // Summoned creatures are loyal only to their summoners. + else if( e.Mobile.HasTrade ) + e.Mobile.SendLocalizedMessage( 1070947 ); // You cannot friend a pet with a trade pending + else + BeginPickTarget( e.Mobile, OrderType.Friend ); + } + + return; + } + case 0x15C: // *guard + { + if( !isOwner ) + break; + + if( !m_Mobile.IsDeadPet && WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Guard; + } + + return; + } + case 0x15D: // *kill + case 0x15E: // *attack + { + if( !isOwner ) + break; + + if( !m_Mobile.IsDeadPet && WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + BeginPickTarget( e.Mobile, OrderType.Attack ); + + return; + } + case 0x15F: // *patrol + { + if( !isOwner ) + break; + + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Patrol; + } + + return; + } + case 0x161: // *stop + { + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stop; + } + + return; + } + case 0x163: // *follow me + { + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = e.Mobile; + m_Mobile.ControlOrder = OrderType.Follow; + } + + return; + } + case 0x16D: // *release + { + if( !isOwner ) + break; + + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + if( !m_Mobile.Summoned ) + { + e.Mobile.SendGump( new Gumps.ConfirmReleaseGump( e.Mobile, m_Mobile ) ); + } + else + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Release; + } + } + + return; + } + case 0x16E: // *transfer + { + if( !isOwner ) + break; + + if( !m_Mobile.IsDeadPet && WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + if( m_Mobile.Summoned ) + e.Mobile.SendLocalizedMessage( 1005487 ); // You cannot transfer ownership of a summoned creature. + else if( e.Mobile.HasTrade ) + e.Mobile.SendLocalizedMessage( 1010507 ); // You cannot transfer a pet with a trade pending + else + BeginPickTarget( e.Mobile, OrderType.Transfer ); + } + + return; + } + case 0x16F: // *stay + { + if( WasNamed( speech ) && m_Mobile.CheckControlChance( e.Mobile ) ) + { + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + } + + return; + } + } + } + } + } + else + { + if( e.Mobile.AccessLevel >= AccessLevel.GameMaster ) + { + m_Mobile.DebugSay( "It's from a GM" ); + + if( m_Mobile.FindMyName( e.Speech, true ) ) + { + string[] str = e.Speech.Split( ' ' ); + int i; + + for( i=0; i < str.Length; i++ ) + { + string word = str[i]; + + if( Insensitive.Equals( word, "obey" ) ) + { + m_Mobile.SetControlMaster( e.Mobile ); + + if( m_Mobile.Summoned ) + m_Mobile.SummonMaster = e.Mobile; + + return; + } + } + } + } + } + } + + public virtual bool Think() + { + if( m_Mobile.Deleted ) + return false; + + if( CheckFlee() ) + return true; + + switch( Action ) + { + case ActionType.Wander: + m_Mobile.OnActionWander(); + return DoActionWander(); + + case ActionType.Combat: + m_Mobile.OnActionCombat(); + return DoActionCombat(); + + case ActionType.Guard: + m_Mobile.OnActionGuard(); + return DoActionGuard(); + + case ActionType.Flee: + m_Mobile.OnActionFlee(); + return DoActionFlee(); + + case ActionType.Interact: + m_Mobile.OnActionInteract(); + return DoActionInteract(); + + case ActionType.Backoff: + m_Mobile.OnActionBackoff(); + return DoActionBackoff(); + + default: + return false; + } + } + + public virtual void OnActionChanged() + { + switch( Action ) + { + case ActionType.Wander: + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + m_Mobile.FocusMob = null; + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + break; + + case ActionType.Combat: + m_Mobile.Warmode = true; + m_Mobile.FocusMob = null; + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + break; + + case ActionType.Guard: + m_Mobile.Warmode = true; + m_Mobile.FocusMob = null; + m_Mobile.Combatant = null; + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + m_NextStopGuard = DateTime.Now + TimeSpan.FromSeconds( 10 ); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + break; + + case ActionType.Flee: + m_Mobile.Warmode = true; + m_Mobile.FocusMob = null; + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + break; + + case ActionType.Interact: + m_Mobile.Warmode = false; + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + break; + + case ActionType.Backoff: + m_Mobile.Warmode = false; + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + break; + } + } + + public virtual bool OnAtWayPoint() + { + return true; + } + + public virtual bool DoActionWander() + { + if( CheckHerding() ) + { + m_Mobile.DebugSay( "Praise the shepherd!" ); + } + else if( m_Mobile.CurrentWayPoint != null ) + { + WayPoint point = m_Mobile.CurrentWayPoint; + if( (point.X != m_Mobile.Location.X || point.Y != m_Mobile.Location.Y) && point.Map == m_Mobile.Map && point.Parent == null && !point.Deleted ) + { + m_Mobile.DebugSay( "I will move towards my waypoint." ); + DoMove( m_Mobile.GetDirectionTo( m_Mobile.CurrentWayPoint ) ); + } + else if( OnAtWayPoint() ) + { + m_Mobile.DebugSay( "I will go to the next waypoint" ); + m_Mobile.CurrentWayPoint = point.NextPoint; + if( point.NextPoint != null && point.NextPoint.Deleted ) + m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; + } + } + else if( m_Mobile.IsAnimatedDead ) + { + // animated dead follow their master + Mobile master = m_Mobile.SummonMaster; + + if( master != null && master.Map == m_Mobile.Map && master.InRange( m_Mobile, m_Mobile.RangePerception ) ) + MoveTo( master, false, 1 ); + else + WalkRandomInHome( 2, 2, 1 ); + } + else if( CheckMove() ) + { + if( !m_Mobile.CheckIdle() ) + WalkRandomInHome( 2, 2, 1 ); + } + + if( m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && !m_Mobile.Combatant.IsDeadBondedPet ) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.Combatant ); + } + + return true; + } + + public virtual bool DoActionCombat() + { + Mobile c = m_Mobile.Combatant; + + if( c == null || c.Deleted || c.Map != m_Mobile.Map || !c.Alive || c.IsDeadBondedPet ) + Action = ActionType.Wander; + else + m_Mobile.Direction = m_Mobile.GetDirectionTo( c ); + + return true; + } + + public virtual bool DoActionGuard() + { + if( DateTime.Now < m_NextStopGuard ) + { + m_Mobile.DebugSay( "I am on guard" ); + //m_Mobile.Turn( Utility.Random(0, 2) - 1 ); + } + else + { + m_Mobile.DebugSay( "I stop being on guard" ); + Action = ActionType.Wander; + } + + return true; + } + + public virtual bool DoActionFlee() + { + Mobile from = m_Mobile.FocusMob; + + if( from == null || from.Deleted || from.Map != m_Mobile.Map ) + { + m_Mobile.DebugSay( "I have lost him" ); + Action = ActionType.Guard; + return true; + } + + if( WalkMobileRange( from, 1, true, m_Mobile.RangePerception*2, m_Mobile.RangePerception*3 ) ) + { + m_Mobile.DebugSay( "I have fled" ); + Action = ActionType.Guard; + return true; + } + else + { + m_Mobile.DebugSay( "I am fleeing!" ); + } + + return true; + } + + public virtual bool DoActionInteract() + { + return true; + } + + public virtual bool DoActionBackoff() + { + return true; + } + + public virtual bool Obey() + { + if( m_Mobile.Deleted ) + return false; + + switch( m_Mobile.ControlOrder ) + { + case OrderType.None: + return DoOrderNone(); + + case OrderType.Come: + return DoOrderCome(); + + case OrderType.Drop: + return DoOrderDrop(); + + case OrderType.Friend: + return DoOrderFriend(); + + case OrderType.Unfriend: + return DoOrderUnfriend(); + + case OrderType.Guard: + return DoOrderGuard(); + + case OrderType.Attack: + return DoOrderAttack(); + + case OrderType.Patrol: + return DoOrderPatrol(); + + case OrderType.Release: + return DoOrderRelease(); + + case OrderType.Stay: + return DoOrderStay(); + + case OrderType.Stop: + return DoOrderStop(); + + case OrderType.Follow: + return DoOrderFollow(); + + case OrderType.Transfer: + return DoOrderTransfer(); + + default: + return false; + } + } + + public virtual void OnCurrentOrderChanged() + { + if( m_Mobile.Deleted || m_Mobile.ControlMaster == null || m_Mobile.ControlMaster.Deleted ) + return; + + switch( m_Mobile.ControlOrder ) + { + case OrderType.None: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.Home = m_Mobile.Location; + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Come: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Drop: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = true; + m_Mobile.Combatant = null; + break; + + case OrderType.Friend: + case OrderType.Unfriend: + m_Mobile.ControlMaster.RevealingAction(); + + break; + case OrderType.Guard: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = true; + m_Mobile.Combatant = null; + break; + + case OrderType.Attack: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + + m_Mobile.Warmode = true; + m_Mobile.Combatant = null; + break; + + case OrderType.Patrol: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Release: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Stay: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Stop: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Follow: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + + case OrderType.Transfer: + m_Mobile.ControlMaster.RevealingAction(); + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + m_Mobile.PlaySound( m_Mobile.GetIdleSound() ); + + m_Mobile.Warmode = false; + m_Mobile.Combatant = null; + break; + } + } + + public virtual bool DoOrderNone() + { + m_Mobile.DebugSay( "I have no order" ); + + WalkRandomInHome( 3, 2, 1 ); + + if( m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && !m_Mobile.Combatant.IsDeadBondedPet ) + { + m_Mobile.Warmode = true; + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.Combatant ); + } + else + { + m_Mobile.Warmode = false; + } + + return true; + } + + public virtual bool DoOrderCome() + { + if( m_Mobile.ControlMaster != null && !m_Mobile.ControlMaster.Deleted ) + { + int iCurrDist = (int)m_Mobile.GetDistanceToSqrt( m_Mobile.ControlMaster ); + + if( iCurrDist > m_Mobile.RangePerception ) + { + m_Mobile.DebugSay( "I have lost my master. I stay here" ); + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + } + else + { + m_Mobile.DebugSay( "My master told me come" ); + + // Not exactly OSI style, but better than nothing. + bool bRun = (iCurrDist > 5); + + if( WalkMobileRange( m_Mobile.ControlMaster, 1, bRun, 0, 1 ) ) + { + if( m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && !m_Mobile.Combatant.IsDeadBondedPet ) + { + m_Mobile.Warmode = true; + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.Combatant ); + } + else + { + m_Mobile.Warmode = false; + } + } + } + } + + return true; + } + + public virtual bool DoOrderDrop() + { + if( m_Mobile.IsDeadPet || !m_Mobile.CanDrop ) + return true; + + m_Mobile.DebugSay( "I drop my stuff for my master" ); + + Container pack = m_Mobile.Backpack; + + if( pack != null ) + { + List list = pack.Items; + + for( int i = list.Count - 1; i >= 0; --i ) + if( i < list.Count ) + list[i].MoveToWorld( m_Mobile.Location, m_Mobile.Map ); + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + + return true; + } + + public virtual bool CheckHerding() + { + Point2D target = m_Mobile.TargetLocation; + + if( target == Point2D.Zero ) + return false; // Creature is not being herded + + double distance = m_Mobile.GetDistanceToSqrt( target ); + + if( distance < 1 || distance > 20 ) + { + if( distance < 1 && target.X == 1076 && target.Y == 450 && (m_Mobile is HordeMinionFamiliar) ) + { + PlayerMobile pm = m_Mobile.ControlMaster as PlayerMobile; + + if( pm != null ) + { + QuestSystem qs = pm.Quest; + + if( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FetchAbraxusScrollObjective ) ); + + if( obj != null && !obj.Completed ) + { + m_Mobile.AddToBackpack( new ScrollOfAbraxus() ); + obj.Complete(); + } + } + } + } + + m_Mobile.TargetLocation = Point2D.Zero; + return false; // At the target or too far away + } + + DoMove( m_Mobile.GetDirectionTo( target ) ); + + return true; + } + + public virtual bool DoOrderFollow() + { + if( CheckHerding() ) + { + m_Mobile.DebugSay( "Praise the shepherd!" ); + } + else if( m_Mobile.ControlTarget != null && !m_Mobile.ControlTarget.Deleted && m_Mobile.ControlTarget != m_Mobile ) + { + int iCurrDist = (int)m_Mobile.GetDistanceToSqrt( m_Mobile.ControlTarget ); + + if( iCurrDist > m_Mobile.RangePerception ) + { + m_Mobile.DebugSay( "I have lost the one to follow. I stay here" ); + + if( m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && !m_Mobile.Combatant.IsDeadBondedPet ) + { + m_Mobile.Warmode = true; + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.Combatant ); + } + else + { + m_Mobile.Warmode = false; + } + } + else + { + m_Mobile.DebugSay( "My master told me to follow: {0}", m_Mobile.ControlTarget.Name ); + + // Not exactly OSI style, but better than nothing. + bool bRun = (iCurrDist > 5); + + if( WalkMobileRange( m_Mobile.ControlTarget, 1, bRun, 0, 1 ) ) + { + if( m_Mobile.Combatant != null && !m_Mobile.Combatant.Deleted && m_Mobile.Combatant.Alive && !m_Mobile.Combatant.IsDeadBondedPet ) + { + m_Mobile.Warmode = true; + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.Combatant ); + } + else + { + m_Mobile.Warmode = false; + } + } + } + } + else + { + m_Mobile.DebugSay( "I have nobody to follow" ); + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + } + + return true; + } + + public virtual bool DoOrderFriend() + { + Mobile from = m_Mobile.ControlMaster; + Mobile to = m_Mobile.ControlTarget; + + if( from == null || to == null || from == to || from.Deleted || to.Deleted || !to.Player ) + { + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 502039 ); // *looks confused* + } + else + { + bool youngFrom = from is PlayerMobile ? ((PlayerMobile)from).Young : false; + bool youngTo = to is PlayerMobile ? ((PlayerMobile)to).Young : false; + + if( youngFrom && !youngTo ) + { + from.SendLocalizedMessage( 502040 ); // As a young player, you may not friend pets to older players. + } + else if( !youngFrom && youngTo ) + { + from.SendLocalizedMessage( 502041 ); // As an older player, you may not friend pets to young players. + } + else if( from.CanBeBeneficial( to, true ) ) + { + NetState fromState = from.NetState, toState = to.NetState; + + if( fromState != null && toState != null ) + { + if( from.HasTrade ) + { + from.SendLocalizedMessage( 1070947 ); // You cannot friend a pet with a trade pending + } + else if( to.HasTrade ) + { + to.SendLocalizedMessage( 1070947 ); // You cannot friend a pet with a trade pending + } + else if( m_Mobile.IsPetFriend( to ) ) + { + from.SendLocalizedMessage( 1049691 ); // That person is already a friend. + } + else if( !m_Mobile.AllowNewPetFriend ) + { + from.SendLocalizedMessage( 1005482 ); // Your pet does not seem to be interested in making new friends right now. + } + else + { + // ~1_NAME~ will now accept movement commands from ~2_NAME~. + from.SendLocalizedMessage( 1049676, String.Format( "{0}\t{1}", m_Mobile.Name, to.Name ) ); + + /* ~1_NAME~ has granted you the ability to give orders to their pet ~2_PET_NAME~. + * This creature will now consider you as a friend. + */ + to.SendLocalizedMessage( 1043246, String.Format( "{0}\t{1}", from.Name, m_Mobile.Name ) ); + + m_Mobile.AddPetFriend( to ); + + m_Mobile.ControlTarget = to; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + } + } + } + + m_Mobile.ControlTarget = from; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + + public virtual bool DoOrderUnfriend() + { + Mobile from = m_Mobile.ControlMaster; + Mobile to = m_Mobile.ControlTarget; + + if( from == null || to == null || from == to || from.Deleted || to.Deleted || !to.Player ) + { + m_Mobile.PublicOverheadMessage( MessageType.Regular, 0x3B2, 502039 ); // *looks confused* + } + else if( !m_Mobile.IsPetFriend( to ) ) + { + from.SendLocalizedMessage( 1070953 ); // That person is not a friend. + } + else + { + // ~1_NAME~ will no longer accept movement commands from ~2_NAME~. + from.SendLocalizedMessage( 1070951, String.Format( "{0}\t{1}", m_Mobile.Name, to.Name ) ); + + /* ~1_NAME~ has no longer granted you the ability to give orders to their pet ~2_PET_NAME~. + * This creature will no longer consider you as a friend. + */ + to.SendLocalizedMessage( 1070952, String.Format( "{0}\t{1}", from.Name, m_Mobile.Name ) ); + + m_Mobile.RemovePetFriend( to ); + } + + m_Mobile.ControlTarget = from; + m_Mobile.ControlOrder = OrderType.Follow; + + return true; + } + + public virtual bool DoOrderGuard() + { + if( m_Mobile.IsDeadPet ) + return true; + + Mobile controlMaster = m_Mobile.ControlMaster; + + if( controlMaster == null || controlMaster.Deleted ) + return true; + + Mobile combatant = m_Mobile.Combatant; + + List aggressors = controlMaster.Aggressors; + + if( aggressors.Count > 0 ) + { + for( int i = 0; i < aggressors.Count; ++i ) + { + AggressorInfo info = aggressors[i]; + Mobile attacker = info.Attacker; + + if( attacker != null && !attacker.Deleted && attacker.GetDistanceToSqrt( m_Mobile ) <= m_Mobile.RangePerception ) + { + if( combatant == null || attacker.GetDistanceToSqrt( controlMaster ) < combatant.GetDistanceToSqrt( controlMaster ) ) + combatant = attacker; + } + } + + if( combatant != null ) + m_Mobile.DebugSay( "Crap, my master has been attacked! I will atack one of those bastards!" ); + } + + if( combatant != null && combatant != m_Mobile && combatant != m_Mobile.ControlMaster && !combatant.Deleted && combatant.Alive && !combatant.IsDeadBondedPet && m_Mobile.CanSee( combatant ) && m_Mobile.CanBeHarmful( combatant, false ) && combatant.Map == m_Mobile.Map ) + { + m_Mobile.DebugSay( "Guarding from target..." ); + + m_Mobile.Combatant = combatant; + m_Mobile.FocusMob = combatant; + Action = ActionType.Combat; + + /* + * We need to call Think() here or spell casting monsters will not use + * spells when guarding because their target is never processed. + */ + Think(); + } + else + { + m_Mobile.DebugSay( "Nothing to guard from" ); + + m_Mobile.Warmode = false; + + WalkMobileRange( controlMaster, 1, false, 0, 1 ); + } + + return true; + } + + public virtual bool DoOrderAttack() + { + if( m_Mobile.IsDeadPet ) + return true; + + if( m_Mobile.ControlTarget == null || m_Mobile.ControlTarget.Deleted || m_Mobile.ControlTarget.Map != m_Mobile.Map || !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet ) + { + m_Mobile.DebugSay( "I think he might be dead. He's not anywhere around here at least. That's cool. I'm glad he's dead." ); + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + + if( m_Mobile.FightMode == FightMode.Closest || m_Mobile.FightMode == FightMode.Aggressor ) + { + Mobile newCombatant = null; + double newScore = 0.0; + + List list = m_Mobile.Aggressors; + + for( int i = 0; i < list.Count; ++i ) + { + Mobile aggr = list[i].Attacker; + + if( aggr.Map != m_Mobile.Map || !aggr.InRange( m_Mobile.Location, m_Mobile.RangePerception ) || !m_Mobile.CanSee( aggr ) ) + continue; + + if( aggr.IsDeadBondedPet || !aggr.Alive ) + continue; + + double aggrScore = m_Mobile.GetValueFrom( aggr, FightMode.Closest, false ); + + if( (newCombatant == null || aggrScore > newScore) && m_Mobile.InLOS( aggr ) ) + { + newCombatant = aggr; + newScore = aggrScore; + } + } + + if( newCombatant != null ) + { + m_Mobile.ControlTarget = newCombatant; + m_Mobile.ControlOrder = OrderType.Attack; + m_Mobile.Combatant = newCombatant; + m_Mobile.DebugSay( "But -that- is not dead. Here we go again..." ); + Think(); + } + } + } + else + { + m_Mobile.DebugSay( "Attacking target..." ); + Think(); + } + + return true; + } + + public virtual bool DoOrderPatrol() + { + m_Mobile.DebugSay( "This order is not yet coded" ); + return true; + } + + public virtual bool DoOrderRelease() + { + m_Mobile.DebugSay( "I have been released" ); + + m_Mobile.PlaySound( m_Mobile.GetAngerSound() ); + + m_Mobile.SetControlMaster( null ); + m_Mobile.SummonMaster = null; + + m_Mobile.BondingBegin = DateTime.MinValue; + m_Mobile.OwnerAbandonTime = DateTime.MinValue; + m_Mobile.IsBonded = false; + + SpawnEntry se = m_Mobile.Spawner as SpawnEntry; + if( se != null && se.Home != Point3D.Zero ) + { + m_Mobile.Home = se.Home; + m_Mobile.RangeHome = se.Range; + } + + if( m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet ) + m_Mobile.Delete(); + + return true; + } + + public virtual bool DoOrderStay() + { + if( CheckHerding() ) + m_Mobile.DebugSay( "Praise the shepherd!" ); + else + m_Mobile.DebugSay( "My master told me to stay" ); + + //m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.ControlMaster ); + + return true; + } + + public virtual bool DoOrderStop() + { + if( m_Mobile.ControlMaster == null || m_Mobile.ControlMaster.Deleted ) + return true; + + m_Mobile.DebugSay( "My master told me to stop." ); + + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.ControlMaster ); + m_Mobile.Home = m_Mobile.Location; + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.None; + + return true; + } + + private class TransferItem : Item + { + public static bool IsInCombat( BaseCreature creature ) + { + return (creature != null && (creature.Aggressors.Count > 0 || creature.Aggressed.Count > 0)); + } + + private BaseCreature m_Creature; + + public TransferItem( BaseCreature creature ) + : base( ShrinkTable.Lookup( creature ) ) + { + m_Creature = creature; + + Movable = false; + + if( !Core.AOS ) + { + Name = creature.Name; + } + else if( this.ItemID == ShrinkTable.DefaultItemID || creature.GetType().IsDefined( typeof( FriendlyNameAttribute ), false ) ) + Name = FriendlyNameAttribute.GetFriendlyNameFor( creature.GetType() ).ToString(); + + //(As Per OSI)No name. Normally, set by the ItemID of the Shrink Item unless we either explicitly set it with an Attribute, or, no lookup found + } + + public TransferItem( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1041603 ); // This item represents a pet currently in consideration for trade + list.Add( 1041601, m_Creature.Name ); // Pet Name: ~1_val~ + + if ( m_Creature.ControlMaster != null ) + list.Add( 1041602, m_Creature.ControlMaster.Name ); // Owner: ~1_val~ + } + + public override bool AllowSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) + { + if( !base.AllowSecureTrade( from, to, newOwner, accepted ) ) + return false; + + if( Deleted || m_Creature == null || m_Creature.Deleted || m_Creature.ControlMaster != from || !from.CheckAlive() || !to.CheckAlive() ) + return false; + + if( from.Map != m_Creature.Map || !from.InRange( m_Creature, 14 ) ) + return false; + + bool youngFrom = from is PlayerMobile ? ((PlayerMobile)from).Young : false; + bool youngTo = to is PlayerMobile ? ((PlayerMobile)to).Young : false; + + if( accepted && youngFrom && !youngTo ) + { + from.SendLocalizedMessage( 502051 ); // As a young player, you may not transfer pets to older players. + } + else if( accepted && !youngFrom && youngTo ) + { + from.SendLocalizedMessage( 502052 ); // As an older player, you may not transfer pets to young players. + } + else if( accepted && !m_Creature.CanBeControlledBy( to ) ) + { + string args = String.Format( "{0}\t{1}\t ", to.Name, from.Name ); + + from.SendLocalizedMessage( 1043248, args ); // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + to.SendLocalizedMessage( 1043249, args ); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ + + return false; + } + else if( accepted && !m_Creature.CanBeControlledBy( from ) ) + { + string args = String.Format( "{0}\t{1}\t ", to.Name, from.Name ); + + from.SendLocalizedMessage( 1043250, args ); // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + to.SendLocalizedMessage( 1043251, args ); // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + } + else if( accepted && (to.Followers + m_Creature.ControlSlots) > to.FollowersMax ) + { + to.SendLocalizedMessage( 1049607 ); // You have too many followers to control that creature. + + return false; + } + else if( accepted && IsInCombat( m_Creature ) ) + { + from.SendMessage( "You may not transfer a pet that has recently been in combat." ); + to.SendMessage( "The pet may not be transfered to you because it has recently been in combat." ); + + return false; + } + + return true; + } + + public override void OnSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) + { + if( Deleted ) + return; + + Delete(); + + if( m_Creature == null || m_Creature.Deleted || m_Creature.ControlMaster != from || !from.CheckAlive() || !to.CheckAlive() ) + return; + + if( from.Map != m_Creature.Map || !from.InRange( m_Creature, 14 ) ) + return; + + if( accepted ) + { + if( m_Creature.SetControlMaster( to ) ) + { + if( m_Creature.Summoned ) + m_Creature.SummonMaster = to; + + m_Creature.ControlTarget = to; + m_Creature.ControlOrder = OrderType.Follow; + + m_Creature.BondingBegin = DateTime.MinValue; + m_Creature.OwnerAbandonTime = DateTime.MinValue; + m_Creature.IsBonded = false; + + m_Creature.PlaySound( m_Creature.GetIdleSound() ); + + string args = String.Format( "{0}\t{1}\t{2}", from.Name, m_Creature.Name, to.Name ); + + from.SendLocalizedMessage( 1043253, args ); // You have transferred your pet to ~3_GETTER~. + to.SendLocalizedMessage( 1043252, args ); // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. + } + } + } + } + + public virtual bool DoOrderTransfer() + { + if( m_Mobile.IsDeadPet ) + return true; + + Mobile from = m_Mobile.ControlMaster; + Mobile to = m_Mobile.ControlTarget; + + if( from != to && from != null && !from.Deleted && to != null && !to.Deleted && to.Player ) + { + m_Mobile.DebugSay( "Begin transfer with {0}", to.Name ); + + bool youngFrom = from is PlayerMobile ? ((PlayerMobile)from).Young : false; + bool youngTo = to is PlayerMobile ? ((PlayerMobile)to).Young : false; + + if( youngFrom && !youngTo ) + { + from.SendLocalizedMessage( 502051 ); // As a young player, you may not transfer pets to older players. + } + else if( !youngFrom && youngTo ) + { + from.SendLocalizedMessage( 502052 ); // As an older player, you may not transfer pets to young players. + } + else if( !m_Mobile.CanBeControlledBy( to ) ) + { + string args = String.Format( "{0}\t{1}\t ", to.Name, from.Name ); + + from.SendLocalizedMessage( 1043248, args ); // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + to.SendLocalizedMessage( 1043249, args ); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ + } + else if( !m_Mobile.CanBeControlledBy( from ) ) + { + string args = String.Format( "{0}\t{1}\t ", to.Name, from.Name ); + + from.SendLocalizedMessage( 1043250, args ); // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + to.SendLocalizedMessage( 1043251, args ); // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + } + else if( TransferItem.IsInCombat( m_Mobile ) ) + { + from.SendMessage( "You may not transfer a pet that has recently been in combat." ); + to.SendMessage( "The pet may not be transfered to you because it has recently been in combat." ); + } + else + { + NetState fromState = from.NetState, toState = to.NetState; + + if( fromState != null && toState != null ) + { + if( from.HasTrade ) + { + from.SendLocalizedMessage( 1010507 ); // You cannot transfer a pet with a trade pending + } + else if( to.HasTrade ) + { + to.SendLocalizedMessage( 1010507 ); // You cannot transfer a pet with a trade pending + } + else + { + Container c = fromState.AddTrade( toState ); + c.DropItem( new TransferItem( m_Mobile ) ); + } + } + } + } + + m_Mobile.ControlTarget = null; + m_Mobile.ControlOrder = OrderType.Stay; + + return true; + } + + public virtual bool DoBardPacified() + { + if( DateTime.Now < m_Mobile.BardEndTime ) + { + m_Mobile.DebugSay( "I am pacified, I wait" ); + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + + } + else + { + m_Mobile.DebugSay( "I'm not pacified any longer" ); + m_Mobile.BardPacified = false; + } + + return true; + } + + public virtual bool DoBardProvoked() + { + if( DateTime.Now >= m_Mobile.BardEndTime && (m_Mobile.BardMaster == null || m_Mobile.BardMaster.Deleted || m_Mobile.BardMaster.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt( m_Mobile.BardMaster ) > m_Mobile.RangePerception) ) + { + m_Mobile.DebugSay( "I have lost my provoker" ); + m_Mobile.BardProvoked = false; + m_Mobile.BardMaster = null; + m_Mobile.BardTarget = null; + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + } + else + { + if( m_Mobile.BardTarget == null || m_Mobile.BardTarget.Deleted || m_Mobile.BardTarget.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt( m_Mobile.BardTarget ) > m_Mobile.RangePerception ) + { + m_Mobile.DebugSay( "I have lost my provoke target" ); + m_Mobile.BardProvoked = false; + m_Mobile.BardMaster = null; + m_Mobile.BardTarget = null; + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + } + else + { + m_Mobile.Combatant = m_Mobile.BardTarget; + m_Action = ActionType.Combat; + + m_Mobile.OnThink(); + Think(); + } + } + + return true; + } + + public virtual void WalkRandom( int iChanceToNotMove, int iChanceToDir, int iSteps ) + { + if( m_Mobile.Deleted || m_Mobile.DisallowAllMoves ) + return; + + for( int i=0; i 1.0 ) + offset = 1.0; + + offset = 1.0 - offset; + + delay += (offset * 0.8); + + if( delay < 0.0 ) + delay = 0.0; + + return delay; + } + + private DateTime m_NextMove; + + public DateTime NextMove + { + get { return m_NextMove; } + set { m_NextMove = value; } + } + + public virtual bool CheckMove() + { + return (DateTime.Now >= m_NextMove); + } + + public virtual bool DoMove( Direction d ) + { + return DoMove( d, false ); + } + + public virtual bool DoMove( Direction d, bool badStateOk ) + { + MoveResult res = DoMoveImpl( d ); + + return (res == MoveResult.Success || res == MoveResult.SuccessAutoTurn || (badStateOk && res == MoveResult.BadState)); + } + + private static Queue m_Obstacles = new Queue(); + + public virtual MoveResult DoMoveImpl( Direction d ) + { + if( m_Mobile.Deleted || m_Mobile.Frozen || m_Mobile.Paralyzed || (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting) || m_Mobile.DisallowAllMoves ) + return MoveResult.BadState; + else if( !CheckMove() ) + return MoveResult.BadState; + + // This makes them always move one step, never any direction changes + m_Mobile.Direction = d; + + TimeSpan delay = TimeSpan.FromSeconds( TransformMoveDelay( m_Mobile.CurrentSpeed ) ); + + m_NextMove += delay; + + if( m_NextMove < DateTime.Now ) + m_NextMove = DateTime.Now; + + m_Mobile.Pushing = false; + + MoveImpl.IgnoreMovableImpassables = (m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles); + + if( (m_Mobile.Direction & Direction.Mask) != (d & Direction.Mask) ) + { + bool v = m_Mobile.Move( d ); + + MoveImpl.IgnoreMovableImpassables = false; + return (v ? MoveResult.Success : MoveResult.Blocked); + } + else if( !m_Mobile.Move( d ) ) + { + bool wasPushing = m_Mobile.Pushing; + + bool blocked = true; + + bool canOpenDoors = m_Mobile.CanOpenDoors; + bool canDestroyObstacles = m_Mobile.CanDestroyObstacles; + + if( canOpenDoors || canDestroyObstacles ) + { + m_Mobile.DebugSay( "My movement was blocked, I will try to clear some obstacles." ); + + Map map = m_Mobile.Map; + + if( map != null ) + { + int x = m_Mobile.X, y = m_Mobile.Y; + Movement.Movement.Offset( d, ref x, ref y ); + + int destroyables = 0; + + IPooledEnumerable eable = map.GetItemsInRange( new Point3D( x, y, m_Mobile.Location.Z ), 1 ); + + foreach( Item item in eable ) + { + if( canOpenDoors && item is BaseDoor && (item.Z + item.ItemData.Height) > m_Mobile.Z && (m_Mobile.Z + 16) > item.Z ) + { + if( item.X != x || item.Y != y ) + continue; + + BaseDoor door = (BaseDoor)item; + + if( !door.Locked || !door.UseLocks() ) + m_Obstacles.Enqueue( door ); + + if( !canDestroyObstacles ) + break; + } + else if( canDestroyObstacles && item.Movable && item.ItemData.Impassable && (item.Z + item.ItemData.Height) > m_Mobile.Z && (m_Mobile.Z + 16) > item.Z ) + { + if( !m_Mobile.InRange( item.GetWorldLocation(), 1 ) ) + continue; + + m_Obstacles.Enqueue( item ); + ++destroyables; + } + } + + eable.Free(); + + if( destroyables > 0 ) + Effects.PlaySound( new Point3D( x, y, m_Mobile.Z ), m_Mobile.Map, 0x3B3 ); + + if( m_Obstacles.Count > 0 ) + blocked = false; // retry movement + + while( m_Obstacles.Count > 0 ) + { + Item item = (Item)m_Obstacles.Dequeue(); + + if( item is BaseDoor ) + { + m_Mobile.DebugSay( "Little do they expect, I've learned how to open doors. Didn't they read the script??" ); + m_Mobile.DebugSay( "*twist*" ); + + ((BaseDoor)item).Use( m_Mobile ); + } + else + { + m_Mobile.DebugSay( "Ugabooga. I'm so big and tough I can destroy it: {0}", item.GetType().Name ); + + if( item is Container ) + { + Container cont = (Container)item; + + for( int i = 0; i < cont.Items.Count; ++i ) + { + Item check = cont.Items[i]; + + if( check.Movable && check.ItemData.Impassable && (item.Z + check.ItemData.Height) > m_Mobile.Z ) + m_Obstacles.Enqueue( check ); + } + + cont.Destroy(); + } + else + { + item.Delete(); + } + } + } + + if( !blocked ) + blocked = !m_Mobile.Move( d ); + } + } + + if( blocked ) + { + int offset = (Utility.RandomDouble() >= 0.6 ? 1 : -1); + + for( int i = 0; i < 2; ++i ) + { + m_Mobile.TurnInternal( offset ); + + if( m_Mobile.Move( m_Mobile.Direction ) ) + { + MoveImpl.IgnoreMovableImpassables = false; + return MoveResult.SuccessAutoTurn; + } + } + + MoveImpl.IgnoreMovableImpassables = false; + return (wasPushing ? MoveResult.BadState : MoveResult.Blocked); + } + else + { + MoveImpl.IgnoreMovableImpassables = false; + return MoveResult.Success; + } + } + + MoveImpl.IgnoreMovableImpassables = false; + return MoveResult.Success; + } + + public virtual void WalkRandomInHome( int iChanceToNotMove, int iChanceToDir, int iSteps ) + { + if( m_Mobile.Deleted || m_Mobile.DisallowAllMoves ) + return; + + if( m_Mobile.Home == Point3D.Zero ) + { + if( m_Mobile.Spawner is SpawnEntry ) + { + Region region = ((SpawnEntry)m_Mobile.Spawner).Region; + + if( m_Mobile.Region.AcceptsSpawnsFrom( region ) ) + { + m_Mobile.WalkRegion = region; + WalkRandom( iChanceToNotMove, iChanceToDir, iSteps ); + m_Mobile.WalkRegion = null; + } + else + { + if( region.GoLocation != Point3D.Zero && Utility.Random( 10 ) > 5 ) + { + DoMove( m_Mobile.GetDirectionTo( region.GoLocation ) ); + } + else + { + WalkRandom( iChanceToNotMove, iChanceToDir, 1 ); + } + } + } + else + { + WalkRandom( iChanceToNotMove, iChanceToDir, iSteps ); + } + } + else + { + for( int i=0; i m_Mobile.RangeHome ) + { + DoMove( m_Mobile.GetDirectionTo( m_Mobile.Home ) ); + } + else + { + if( Utility.Random( 10 ) > 5 ) + { + DoMove( m_Mobile.GetDirectionTo( m_Mobile.Home ) ); + } + else + { + WalkRandom( iChanceToNotMove, iChanceToDir, 1 ); + } + } + } + else + { + if( m_Mobile.Location != m_Mobile.Home ) + { + DoMove( m_Mobile.GetDirectionTo( m_Mobile.Home ) ); + } + } + } + } + } + + public virtual bool CheckFlee() + { + if( m_Mobile.CheckFlee() ) + { + Mobile combatant = m_Mobile.Combatant; + + if( combatant == null ) + { + WalkRandom( 1, 2, 1 ); + } + else + { + Direction d = combatant.GetDirectionTo( m_Mobile ); + + d = (Direction)((int)d + Utility.RandomMinMax( -1, +1 )); + + m_Mobile.Direction = d; + m_Mobile.Move( d ); + } + + return true; + } + + return false; + } + + protected PathFollower m_Path; + + public virtual void OnTeleported() + { + if( m_Path != null ) + { + m_Mobile.DebugSay( "Teleported; repathing" ); + m_Path.ForceRepath(); + } + } + + public virtual bool MoveTo( Mobile m, bool run, int range ) + { + if( m_Mobile.Deleted || m_Mobile.DisallowAllMoves || m == null || m.Deleted ) + return false; + + if( m_Mobile.InRange( m, range ) ) + { + m_Path = null; + return true; + } + + if( m_Path != null && m_Path.Goal == m ) + { + if( m_Path.Follow( run, 1 ) ) + { + m_Path = null; + return true; + } + } + else if( !DoMove( m_Mobile.GetDirectionTo( m ), true ) ) + { + m_Path = new PathFollower( m_Mobile, m ); + m_Path.Mover = new MoveMethod( DoMoveImpl ); + + if( m_Path.Follow( run, 1 ) ) + { + m_Path = null; + return true; + } + } + else + { + m_Path = null; + return true; + } + + return false; + } + + /* + * Walk at range distance from mobile + * + * iSteps : Number of steps + * bRun : Do we run + * iWantDistMin : The minimum distance we want to be + * iWantDistMax : The maximum distance we want to be + * + */ + public virtual bool WalkMobileRange( Mobile m, int iSteps, bool bRun, int iWantDistMin, int iWantDistMax ) + { + if( m_Mobile.Deleted || m_Mobile.DisallowAllMoves ) + return false; + + if( m != null ) + { + for( int i=0; i iWantDistMax ) + { + bool needCloser = (iCurrDist > iWantDistMax); + bool needFurther = !needCloser; + + if( needCloser && m_Path != null && m_Path.Goal == m ) + { + if( m_Path.Follow( bRun, 1 ) ) + m_Path = null; + } + else + { + Direction dirTo; + + if( iCurrDist > iWantDistMax ) + dirTo = m_Mobile.GetDirectionTo( m ); + else + dirTo = m.GetDirectionTo( m_Mobile ); + + // Add the run flag + if( bRun ) + dirTo = dirTo | Direction.Running; + + if( !DoMove( dirTo, true ) && needCloser ) + { + m_Path = new PathFollower( m_Mobile, m ); + m_Path.Mover = new MoveMethod( DoMoveImpl ); + + if( m_Path.Follow( bRun, 1 ) ) + m_Path = null; + } + else + { + m_Path = null; + } + } + } + else + { + return true; + } + } + + // Get the curent distance + int iNewDist = (int)m_Mobile.GetDistanceToSqrt( m ); + + if( iNewDist >= iWantDistMin && iNewDist <= iWantDistMax ) + return true; + else + return false; + } + + return false; + } + + /* + * Here we check to acquire a target from our surronding + * + * iRange : The range + * acqType : A type of acquire we want (closest, strongest, etc) + * bPlayerOnly : Don't bother with other creatures or NPCs, want a player + * bFacFriend : Check people in my faction + * bFacFoe : Check people in other factions + * + */ + public virtual bool AcquireFocusMob( int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe ) + { + if( m_Mobile.Deleted ) + return false; + + if( m_Mobile.BardProvoked ) + { + if( m_Mobile.BardTarget == null || m_Mobile.BardTarget.Deleted ) + { + m_Mobile.FocusMob = null; + return false; + } + else + { + m_Mobile.FocusMob = m_Mobile.BardTarget; + return (m_Mobile.FocusMob != null); + } + } + else if( m_Mobile.Controlled ) + { + if( m_Mobile.ControlTarget == null || m_Mobile.ControlTarget.Deleted || !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet || !m_Mobile.InRange( m_Mobile.ControlTarget, m_Mobile.RangePerception * 2 ) ) + { + m_Mobile.FocusMob = null; + return false; + } + else + { + m_Mobile.FocusMob = m_Mobile.ControlTarget; + return (m_Mobile.FocusMob != null); + } + } + + if( m_Mobile.ConstantFocus != null ) + { + m_Mobile.DebugSay( "Acquired my constant focus" ); + m_Mobile.FocusMob = m_Mobile.ConstantFocus; + return true; + } + + if( acqType == FightMode.None ) + { + m_Mobile.FocusMob = null; + return false; + } + + if ( acqType == FightMode.Aggressor && m_Mobile.Aggressors.Count == 0 && m_Mobile.Aggressed.Count == 0 && m_Mobile.FactionAllegiance == null && m_Mobile.EthicAllegiance == null ) + { + m_Mobile.FocusMob = null; + return false; + } + + if( m_Mobile.NextReacquireTime > DateTime.Now ) + { + m_Mobile.FocusMob = null; + return false; + } + + m_Mobile.NextReacquireTime = DateTime.Now + m_Mobile.ReacquireDelay; + + m_Mobile.DebugSay( "Acquiring..." ); + + Map map = m_Mobile.Map; + + if( map != null ) + { + Mobile newFocusMob = null; + double val = double.MinValue; + double theirVal; + + IPooledEnumerable eable = map.GetMobilesInRange( m_Mobile.Location, iRange ); + + foreach( Mobile m in eable ) + { + if ( m.Deleted || m.Blessed ) + continue; + + // Let's not target ourselves... + if ( m == m_Mobile ) + continue; + + // Dead targets are invalid. + if ( !m.Alive || m.IsDeadBondedPet ) + continue; + + // Staff members cannot be targeted. + if ( m.AccessLevel > AccessLevel.Player ) + continue; + + // Does it have to be a player? + if ( bPlayerOnly && !m.Player ) + continue; + + // Can't acquire a target we can't see. + if ( !m_Mobile.CanSee( m ) ) + continue; + + if ( m_Mobile.Summoned && m_Mobile.SummonMaster != null ) + { + // If this is a summon, it can't target its controller. + if ( m == m_Mobile.SummonMaster ) + continue; + + // It also must abide by harmful spell rules. + if ( !Server.Spells.SpellHelper.ValidIndirectTarget( m_Mobile.SummonMaster, m ) ) + continue; + + // Animated creatures cannot attack players directly. + if ( m is PlayerMobile && m_Mobile.IsAnimatedDead ) + continue; + } + + // If we only want faction friends, make sure it's one. + if ( bFacFriend && !m_Mobile.IsFriend( m ) ) + continue; + + // Same goes for faction enemies. + if ( bFacFoe && !m_Mobile.IsEnemy( m ) ) + continue; + + if( acqType == FightMode.Aggressor || acqType == FightMode.Evil ) + { + // Only acquire this mobile if it attacked us, or if it's evil. + bool bValid = false; + + for ( int a = 0; !bValid && a < m_Mobile.Aggressors.Count; ++a ) + bValid = ( m_Mobile.Aggressors[a].Attacker == m ); + + for ( int a = 0; !bValid && a < m_Mobile.Aggressed.Count; ++a ) + bValid = ( m_Mobile.Aggressed[a].Defender == m ); + + #region Ethics & Faction checks + if ( !bValid ) + bValid = ( m_Mobile.GetFactionAllegiance( m ) == BaseCreature.Allegiance.Enemy || m_Mobile.GetEthicAllegiance( m ) == BaseCreature.Allegiance.Enemy ); + #endregion + + if ( acqType == FightMode.Evil && !bValid ) + { + if( m is BaseCreature && ((BaseCreature)m).Controlled && ((BaseCreature)m).ControlMaster != null ) + bValid = ( ((BaseCreature)m).ControlMaster.Karma < 0 ); + else + bValid = ( m.Karma < 0 ); + } + + if ( !bValid ) + continue; + } + + // If it's an enemy factioned mobile, make sure we can be harmful to it. + if ( bFacFoe && !bFacFriend && !m_Mobile.CanBeHarmful( m, false ) ) + continue; + + theirVal = m_Mobile.GetValueFrom( m, acqType, bPlayerOnly ); + + if( theirVal > val && m_Mobile.InLOS( m ) ) + { + newFocusMob = m; + val = theirVal; + } + } + + eable.Free(); + + m_Mobile.FocusMob = newFocusMob; + } + + return (m_Mobile.FocusMob != null); + } + + public virtual void DetectHidden() + { + if( m_Mobile.Deleted || m_Mobile.Map == null ) + return; + + m_Mobile.DebugSay( "Checking for hidden players" ); + + double srcSkill = m_Mobile.Skills[SkillName.DetectHidden].Value; + + if( srcSkill <= 0 ) + return; + + foreach( Mobile trg in m_Mobile.GetMobilesInRange( m_Mobile.RangePerception ) ) + { + if( trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && m_Mobile.InLOS( trg ) ) + { + m_Mobile.DebugSay( "Trying to detect {0}", trg.Name ); + + double trgHiding = trg.Skills[SkillName.Hiding].Value / 2.9; + double trgStealth = trg.Skills[SkillName.Stealth].Value / 1.8; + + double chance = srcSkill / 1.2 - Math.Min( trgHiding, trgStealth ); + + if( chance < srcSkill / 10 ) + chance = srcSkill / 10; + + chance /= 100; + + if( chance > Utility.RandomDouble() ) + { + trg.RevealingAction(); + trg.SendLocalizedMessage( 500814 ); // You have been revealed! + } + } + } + } + + public virtual void Deactivate() + { + if( m_Mobile.PlayerRangeSensitive ) + { + m_Timer.Stop(); + + SpawnEntry se = m_Mobile.Spawner as SpawnEntry; + + if( se != null && se.ReturnOnDeactivate && !m_Mobile.Controlled ) + { + if( se.Home == Point3D.Zero ) + { + if( !m_Mobile.Region.AcceptsSpawnsFrom( se.Region ) ) + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ReturnToHome ) ); + } + } + else if( !m_Mobile.InRange( se.Home, se.Range ) ) + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ReturnToHome ) ); + } + } + } + } + + private void ReturnToHome() + { + SpawnEntry se = m_Mobile.Spawner as SpawnEntry; + + if( se != null ) + { + Point3D loc = se.RandomSpawnLocation( 16, !m_Mobile.CantWalk, m_Mobile.CanSwim ); + + if( loc != Point3D.Zero ) + { + m_Mobile.MoveToWorld( loc, se.Region.Map ); + return; + } + } + } + + public virtual void Activate() + { + if( !m_Timer.Running ) + { + m_Timer.Delay = TimeSpan.Zero; + m_Timer.Start(); + } + } + + /* + * The mobile changed it speed, we must ajust the timer + */ + public virtual void OnCurrentSpeedChanged() + { + m_Timer.Stop(); + m_Timer.Delay = TimeSpan.FromSeconds( Utility.RandomDouble() ); + m_Timer.Interval = TimeSpan.FromSeconds( Math.Max( 0.0, m_Mobile.CurrentSpeed ) ); + m_Timer.Start(); + } + + /* + * The Timer object + */ + private class AITimer : Timer + { + private BaseAI m_Owner; + private DateTime m_NextDetectHidden; + private bool m_bDetectHidden; + + public AITimer( BaseAI owner ) + : base( TimeSpan.FromSeconds( Utility.RandomDouble() ), TimeSpan.FromSeconds( Math.Max( 0.0, owner.m_Mobile.CurrentSpeed ) ) ) + { + m_Owner = owner; + + m_bDetectHidden = false; + m_NextDetectHidden = DateTime.Now; + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if( m_Owner.m_Mobile.Deleted ) + { + Stop(); + return; + } + else if( m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal ) + { + return; + } + else if( m_Owner.m_Mobile.PlayerRangeSensitive )//have to check this in the timer.... + { + Sector sect = m_Owner.m_Mobile.Map.GetSector( m_Owner.m_Mobile ); + if( !sect.Active ) + { + m_Owner.Deactivate(); + return; + } + } + + m_Owner.m_Mobile.OnThink(); + + if( m_Owner.m_Mobile.Deleted ) + { + Stop(); + return; + } + else if( m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal ) + { + return; + } + + if( m_Owner.m_Mobile.BardPacified ) + { + m_Owner.DoBardPacified(); + } + else if( m_Owner.m_Mobile.BardProvoked ) + { + m_Owner.DoBardProvoked(); + } + else + { + if( !m_Owner.m_Mobile.Controlled ) + { + if( !m_Owner.Think() ) + { + Stop(); + return; + } + } + else + { + if( !m_Owner.Obey() ) + { + Stop(); + return; + } + } + } + + if( m_bDetectHidden && DateTime.Now > m_NextDetectHidden ) + { + m_Owner.DetectHidden(); + + // Not exactly OSI style, approximation. + int delay = (15000 / m_Owner.m_Mobile.Int); + + if( delay > 60 ) + delay = 60; + + int min = delay * (9 / 10); // 13s at 1000 int, 33s at 400 int, 54s at <250 int + int max = delay * (10 / 9); // 16s at 1000 int, 41s at 400 int, 66s at <250 int + + m_NextDetectHidden = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( min, max ) ); + } + else + { + m_bDetectHidden = m_Owner.m_Mobile.Skills[SkillName.DetectHidden].Value > 0; + } + } + } + } +} diff --git a/Scripts/Engines/AI/AI/BerserkAI.cs b/Scripts/Engines/AI/AI/BerserkAI.cs new file mode 100644 index 000000000..d5ddbb21b --- /dev/null +++ b/Scripts/Engines/AI/AI/BerserkAI.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +namespace Server.Mobiles +{ + public class BerserkAI : BaseAI + { + public BerserkAI(BaseCreature m) : base (m) + { + } + + public override bool DoActionWander() + { + m_Mobile.DebugSay( "I have No Combatant" ); + + if( AcquireFocusMob( m_Mobile.RangePerception, FightMode.Closest, false, true, true) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected " + m_Mobile.FocusMob.Name + " and I will attack" ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + if( m_Mobile.Combatant == null || m_Mobile.Combatant.Deleted ) + { + m_Mobile.DebugSay("My combatant is deleted"); + Action = ActionType.Guard; + return true; + } + + if( WalkMobileRange( m_Mobile.Combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight ) ) + { + // Be sure to face the combatant + m_Mobile.Direction = m_Mobile.GetDirectionTo( m_Mobile.Combatant.Location ); + } + else + { + if( m_Mobile.Combatant != null ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay("I am still not in range of " + m_Mobile.Combatant.Name); + + if( (int) m_Mobile.GetDistanceToSqrt( m_Mobile.Combatant ) > m_Mobile.RangePerception + 1 ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have lost " + m_Mobile.Combatant.Name ); + + Action = ActionType.Guard; + return true; + } + } + } + + return true; + } + + public override bool DoActionGuard() + { + if ( AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, true, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); + } + + return true; + } + } +} diff --git a/Scripts/Engines/AI/AI/HealerAI.cs b/Scripts/Engines/AI/AI/HealerAI.cs new file mode 100644 index 000000000..51f367307 --- /dev/null +++ b/Scripts/Engines/AI/AI/HealerAI.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +using Server.Spells; +using Server.Spells.First; +using Server.Spells.Second; +using Server.Spells.Fourth; + +namespace Server.Mobiles +{ + public class HealerAI : BaseAI + { + private static NeedDelegate m_Cure = new NeedDelegate( NeedCure ); + private static NeedDelegate m_GHeal = new NeedDelegate( NeedGHeal ); + private static NeedDelegate m_LHeal = new NeedDelegate( NeedLHeal ); + private static NeedDelegate[] m_ACure = new NeedDelegate[] { m_Cure }; + private static NeedDelegate[] m_AGHeal = new NeedDelegate[] { m_GHeal }; + private static NeedDelegate[] m_ALHeal = new NeedDelegate[] { m_LHeal }; + private static NeedDelegate[] m_All = new NeedDelegate[] { m_Cure, m_GHeal, m_LHeal }; + + public HealerAI( BaseCreature m ) : base( m ) + { + } + + public override bool Think() + { + if ( m_Mobile.Deleted ) + return false; + + Target targ = m_Mobile.Target; + + if ( targ != null ) + { + if ( targ is CureSpell.InternalTarget ) + { + ProcessTarget( targ, m_ACure ); + } + else if ( targ is GreaterHealSpell.InternalTarget ) + { + ProcessTarget( targ, m_AGHeal ); + } + else if ( targ is HealSpell.InternalTarget ) + { + ProcessTarget( targ, m_ALHeal ); + } + else + { + targ.Cancel( m_Mobile, TargetCancelType.Canceled ); + } + } + else + { + Mobile toHelp = Find( m_All ); + + if ( toHelp != null ) + { + if ( NeedCure( toHelp ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} needs a cure", toHelp.Name ); + + if ( !(new CureSpell( m_Mobile, null )).Cast() ) + new CureSpell( m_Mobile, null ).Cast(); + } + else if ( NeedGHeal( toHelp ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} needs a greater heal", toHelp.Name ); + + if ( !(new GreaterHealSpell( m_Mobile, null )).Cast() ) + new HealSpell( m_Mobile, null ).Cast(); + } + else if ( NeedLHeal( toHelp ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} needs a lesser heal", toHelp.Name ); + + new HealSpell( m_Mobile, null ).Cast(); + } + } + else + { + if ( AcquireFocusMob( m_Mobile.RangePerception, FightMode.Weakest, false, true, false ) ) + { + WalkMobileRange( m_Mobile.FocusMob, 1, false, 4, 7 ); + } + else + { + WalkRandomInHome( 3, 2, 1 ); + } + } + } + + return true; + } + + private delegate bool NeedDelegate( Mobile m ); + + private void ProcessTarget( Target targ, NeedDelegate[] func ) + { + Mobile toHelp = Find( func ); + + if ( toHelp != null ) + { + if ( targ.Range != -1 && !m_Mobile.InRange( toHelp, targ.Range ) ) + { + DoMove( m_Mobile.GetDirectionTo( toHelp ) | Direction.Running ); + } + else + { + targ.Invoke( m_Mobile, toHelp ); + } + } + else + { + targ.Cancel( m_Mobile, TargetCancelType.Canceled ); + } + } + + private Mobile Find( params NeedDelegate[] funcs ) + { + if ( m_Mobile.Deleted ) + return null; + + Map map = m_Mobile.Map; + + if ( map != null ) + { + double prio = 0.0; + Mobile found = null; + + foreach ( Mobile m in m_Mobile.GetMobilesInRange( m_Mobile.RangePerception ) ) + { + if ( !m_Mobile.CanSee( m ) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team ) + continue; + + for ( int i = 0; i < funcs.Length; ++i ) + { + if ( funcs[i]( m ) ) + { + double val = -m_Mobile.GetDistanceToSqrt( m ); + + if ( found == null || val > prio ) + { + prio = val; + found = m; + } + + break; + } + } + } + + return found; + } + + return null; + } + + private static bool NeedCure( Mobile m ) + { + return m.Poisoned; + } + + private static bool NeedGHeal( Mobile m ) + { + return m.Hits < m.HitsMax - 40; + } + + private static bool NeedLHeal( Mobile m ) + { + return m.Hits < m.HitsMax - 10; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/MageAI.cs b/Scripts/Engines/AI/AI/MageAI.cs new file mode 100644 index 000000000..c1cd92359 --- /dev/null +++ b/Scripts/Engines/AI/AI/MageAI.cs @@ -0,0 +1,992 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; +using Server.Items; +using Server.Spells; +using Server.Spells.First; +using Server.Spells.Second; +using Server.Spells.Third; +using Server.Spells.Fourth; +using Server.Spells.Fifth; +using Server.Spells.Sixth; +using Server.Spells.Seventh; +using Server.Misc; +using Server.Regions; +using Server.SkillHandlers; + +namespace Server.Mobiles +{ + public class MageAI : BaseAI + { + private DateTime m_NextCastTime; + private DateTime m_NextHealTime; + + public MageAI( BaseCreature m ) : base( m ) + { + } + + public override bool Think() + { + if ( m_Mobile.Deleted ) + return false; + + if ( ProcessTarget() ) + return true; + else + return base.Think(); + } + + public virtual bool SmartAI + { + get{ return ( m_Mobile is BaseVendor || m_Mobile is BaseEscortable ); } + } + + private const double HealChance = 0.10; // 10% chance to heal at gm magery + private const double TeleportChance = 0.05; // 5% chance to teleport at gm magery + private const double DispelChance = 0.75; // 75% chance to dispel at gm magery + + public virtual double ScaleByMagery( double v ) + { + return m_Mobile.Skills[SkillName.Magery].Value * v * 0.01; + } + + public override bool DoActionWander() + { + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I am going to attack {0}", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + m_NextCastTime = DateTime.Now; + } + else if ( SmartAI && m_Mobile.Mana < m_Mobile.ManaMax ) + { + m_Mobile.DebugSay( "I am going to meditate" ); + + m_Mobile.UseSkill( SkillName.Meditation ); + } + else + { + m_Mobile.DebugSay( "I am wandering" ); + + m_Mobile.Warmode = false; + + base.DoActionWander(); + + if ( !m_Mobile.Controlled ) + { + Spell spell = CheckCastHealingSpell(); + + if ( spell != null ) + spell.Cast(); + } + } + + return true; + } + + private Spell CheckCastHealingSpell() + { + // If I'm poisoned, always attempt to cure. + if ( m_Mobile.Poisoned ) + return new CureSpell( m_Mobile, null ); + + // Summoned creatures never heal themselves. + if ( m_Mobile.Summoned ) + return null; + + if ( m_Mobile.Controlled ) + { + if ( DateTime.Now < m_NextHealTime ) + return null; + } + + if ( !SmartAI ) + { + if ( ScaleByMagery( HealChance ) < Utility.RandomDouble() ) + return null; + } + else + { + if ( Utility.Random( 0, 4 + (m_Mobile.Hits == 0 ? m_Mobile.HitsMax : (m_Mobile.HitsMax / m_Mobile.Hits)) ) < 3 ) + return null; + } + + Spell spell = null; + + if ( m_Mobile.Hits < (m_Mobile.HitsMax - 50) ) + { + spell = new GreaterHealSpell( m_Mobile, null ); + + if ( spell == null ) + spell = new HealSpell( m_Mobile, null ); + } + else if ( m_Mobile.Hits < (m_Mobile.HitsMax - 10) ) + spell = new HealSpell( m_Mobile, null ); + + double delay; + + if ( m_Mobile.Int >= 500 ) + delay = Utility.RandomMinMax( 7, 10 ); + else + delay = Math.Sqrt( 600 - m_Mobile.Int ); + + m_NextHealTime = DateTime.Now + TimeSpan.FromSeconds( delay ); + + return spell; + } + + public void RunTo( Mobile m ) + { + if ( !SmartAI ) + { + if ( !MoveTo( m, true, m_Mobile.RangeFight ) ) + OnFailedMove(); + + return; + } + + if ( m.Paralyzed || m.Frozen ) + { + if ( m_Mobile.InRange( m, 1 ) ) + RunFrom( m ); + else if ( !m_Mobile.InRange( m, m_Mobile.RangeFight > 2 ? m_Mobile.RangeFight : 2 ) && !MoveTo( m, true, 1 ) ) + OnFailedMove(); + } + else + { + if ( !m_Mobile.InRange( m, m_Mobile.RangeFight ) ) + { + if ( !MoveTo( m, true, 1 ) ) + OnFailedMove(); + } + else if ( m_Mobile.InRange( m, m_Mobile.RangeFight - 1 ) ) + { + RunFrom( m ); + } + } + } + + public void RunFrom( Mobile m ) + { + Run( (m_Mobile.GetDirectionTo( m ) - 4) & Direction.Mask ); + } + + public void OnFailedMove() + { + if ( !m_Mobile.DisallowAllMoves && (SmartAI ? Utility.Random( 4 ) == 0 : ScaleByMagery( TeleportChance ) > Utility.RandomDouble()) ) + { + if ( m_Mobile.Target != null ) + m_Mobile.Target.Cancel( m_Mobile, TargetCancelType.Canceled ); + + new TeleportSpell( m_Mobile, null ).Cast(); + + m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" ); + } + else if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + m_Mobile.DebugSay( "I am stuck" ); + } + } + + public void Run( Direction d ) + { + if ( (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting) || m_Mobile.Paralyzed || m_Mobile.Frozen || m_Mobile.DisallowAllMoves ) + return; + + m_Mobile.Direction = d | Direction.Running; + + if ( !DoMove( m_Mobile.Direction, true ) ) + OnFailedMove(); + } + + public virtual Spell GetRandomDamageSpell() + { + int maxCircle = (int)((m_Mobile.Skills[SkillName.Magery].Value + 20.0) / (100.0 / 7.0)); + + if ( maxCircle < 1 ) + maxCircle = 1; + + switch ( Utility.Random( maxCircle*2 ) ) + { + case 0: case 1: return new MagicArrowSpell( m_Mobile, null ); + case 2: case 3: return new HarmSpell( m_Mobile, null ); + case 4: case 5: return new FireballSpell( m_Mobile, null ); + case 6: case 7: return new LightningSpell( m_Mobile, null ); + case 8: case 9: return new MindBlastSpell( m_Mobile, null ); + case 10: return new EnergyBoltSpell( m_Mobile, null ); + case 11: return new ExplosionSpell( m_Mobile, null ); + default: return new FlameStrikeSpell( m_Mobile, null ); + } + } + + public virtual Spell GetRandomCurseSpell() + { + if ( Utility.RandomBool() ) + { + if ( m_Mobile.Skills[SkillName.Magery].Value >= 40.0 ) + return new CurseSpell( m_Mobile, null ); + } + + switch ( Utility.Random( 3 ) ) + { + default: + case 0: return new WeakenSpell( m_Mobile, null ); + case 1: return new ClumsySpell( m_Mobile, null ); + case 2: return new FeeblemindSpell( m_Mobile, null ); + } + } + + public virtual Spell GetRandomManaDrainSpell() + { + if ( Utility.RandomBool() ) + { + if ( m_Mobile.Skills[SkillName.Magery].Value >= 80.0 ) + return new ManaVampireSpell( m_Mobile, null ); + } + + return new ManaDrainSpell( m_Mobile, null ); + } + + public virtual Spell DoDispel( Mobile toDispel ) + { + if ( !SmartAI ) + { + if ( ScaleByMagery( DispelChance ) > Utility.RandomDouble() ) + return new DispelSpell( m_Mobile, null ); + + return ChooseSpell( toDispel ); + } + + Spell spell = CheckCastHealingSpell(); + + if ( spell == null ) + { + if ( !m_Mobile.DisallowAllMoves && Utility.Random( (int)m_Mobile.GetDistanceToSqrt( toDispel ) ) == 0 ) + spell = new TeleportSpell( m_Mobile, null ); + else if ( Utility.Random( 3 ) == 0 && !m_Mobile.InRange( toDispel, 3 ) && !toDispel.Paralyzed && !toDispel.Frozen ) + spell = new ParalyzeSpell( m_Mobile, null ); + else + spell = new DispelSpell( m_Mobile, null ); + } + + return spell; + } + + public virtual Spell ChooseSpell( Mobile c ) + { + Spell spell = null; + + if ( !SmartAI ) + { + spell = CheckCastHealingSpell(); + + if ( spell != null ) + return spell; + + switch ( Utility.Random( 16 ) ) + { + case 0: + case 1: + case 2: // Poison them + { + m_Mobile.DebugSay( "Attempting to poison" ); + + if ( !c.Poisoned ) + spell = new PoisonSpell( m_Mobile, null ); + + break; + } + case 3: // Bless ourselves. + { + m_Mobile.DebugSay( "Blessing myself" ); + + spell = new BlessSpell( m_Mobile, null ); + break; + } + case 4: + case 5: + case 6: // Curse them. + { + m_Mobile.DebugSay( "Attempting to curse" ); + + spell = GetRandomCurseSpell(); + break; + } + case 7: // Paralyze them. + { + m_Mobile.DebugSay( "Attempting to paralyze" ); + + if ( m_Mobile.Skills[SkillName.Magery].Value > 50.0 ) + spell = new ParalyzeSpell( m_Mobile, null ); + + break; + } + case 8: // Drain mana + { + m_Mobile.DebugSay( "Attempting to drain mana" ); + + spell = GetRandomManaDrainSpell(); + break; + } + + default: // Damage them. + { + m_Mobile.DebugSay( "Just doing damage" ); + + spell = GetRandomDamageSpell(); + break; + } + } + + return spell; + } + + spell = CheckCastHealingSpell(); + + if ( spell != null ) + return spell; + + switch ( Utility.Random( 3 ) ) + { + default: + case 0: // Poison them + { + if ( !c.Poisoned ) + spell = new PoisonSpell( m_Mobile, null ); + + break; + } + case 1: // Deal some damage + { + spell = GetRandomDamageSpell(); + + break; + } + case 2: // Set up a combo + { + if ( m_Mobile.Mana < 40 && m_Mobile.Mana > 15 ) + { + if ( c.Paralyzed && !c.Poisoned ) + { + m_Mobile.DebugSay( "I am going to meditate" ); + + m_Mobile.UseSkill( SkillName.Meditation ); + } + else if ( !c.Poisoned ) + { + spell = new ParalyzeSpell( m_Mobile, null ); + } + } + else if ( m_Mobile.Mana > 60 ) + { + if ( Utility.Random( 2 ) == 0 && !c.Paralyzed && !c.Frozen && !c.Poisoned ) + { + m_Combo = 0; + spell = new ParalyzeSpell( m_Mobile, null ); + } + else + { + m_Combo = 1; + spell = new ExplosionSpell( m_Mobile, null ); + } + } + + break; + } + } + + return spell; + } + + protected int m_Combo = -1; + + public virtual Spell DoCombo( Mobile c ) + { + Spell spell = null; + + if ( m_Combo == 0 ) + { + spell = new ExplosionSpell( m_Mobile, null ); + ++m_Combo; // Move to next spell + } + else if ( m_Combo == 1 ) + { + spell = new WeakenSpell( m_Mobile, null ); + ++m_Combo; // Move to next spell + } + else if ( m_Combo == 2 ) + { + if ( !c.Poisoned ) + spell = new PoisonSpell( m_Mobile, null ); + + ++m_Combo; // Move to next spell + } + + if ( m_Combo == 3 && spell == null ) + { + switch ( Utility.Random( 3 ) ) + { + default: + case 0: + { + if ( c.Int < c.Dex ) + spell = new FeeblemindSpell( m_Mobile, null ); + else + spell = new ClumsySpell( m_Mobile, null ); + + ++m_Combo; // Move to next spell + + break; + } + case 1: + { + spell = new EnergyBoltSpell( m_Mobile, null ); + m_Combo = -1; // Reset combo state + break; + } + case 2: + { + spell = new FlameStrikeSpell( m_Mobile, null ); + m_Combo = -1; // Reset combo state + break; + } + } + } + else if ( m_Combo == 4 && spell == null ) + { + spell = new MindBlastSpell( m_Mobile, null ); + m_Combo = -1; + } + + return spell; + } + + private TimeSpan GetDelay() + { + double del = ScaleByMagery( 3.0 ); + double min = 6.0 - (del * 0.75); + double max = 6.0 - (del * 1.25); + + return TimeSpan.FromSeconds( min + ((max - min) * Utility.RandomDouble()) ); + } + + public override bool DoActionCombat() + { + Mobile c = m_Mobile.Combatant; + m_Mobile.Warmode = true; + + if ( c == null || c.Deleted || !c.Alive || c.IsDeadBondedPet || !m_Mobile.CanSee( c ) || !m_Mobile.CanBeHarmful( c, false ) || c.Map != m_Mobile.Map ) + { + // Our combatant is deleted, dead, hidden, or we cannot hurt them + // Try to find another combatant + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "Something happened to my combatant, so I am going to fight {0}", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = c = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else + { + m_Mobile.DebugSay( "Something happened to my combatant, and nothing is around. I am on guard." ); + Action = ActionType.Guard; + return true; + } + } + + if ( !m_Mobile.InLOS( c ) ) + { + m_Mobile.DebugSay( "I can't see my target" ); + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.DebugSay( "Nobody else is around" ); + m_Mobile.Combatant = c = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + } + + if ( SmartAI && !m_Mobile.StunReady && m_Mobile.Skills[SkillName.Wrestling].Value >= 80.0 && m_Mobile.Skills[SkillName.Anatomy].Value >= 80.0 ) + EventSink.InvokeStunRequest( new StunRequestEventArgs( m_Mobile ) ); + + if ( !m_Mobile.InRange( c, m_Mobile.RangePerception ) ) + { + // They are somewhat far away, can we find something else? + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.Combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else if ( !m_Mobile.InRange( c, m_Mobile.RangePerception * 3 ) ) + { + m_Mobile.Combatant = null; + } + + c = m_Mobile.Combatant; + + if ( c == null ) + { + m_Mobile.DebugSay( "My combatant has fled, so I am on guard" ); + Action = ActionType.Guard; + + return true; + } + } + + if ( !m_Mobile.Controlled && !m_Mobile.Summoned && !m_Mobile.IsParagon ) + { + if ( m_Mobile.Hits < m_Mobile.HitsMax * 20/100 ) + { + // We are low on health, should we flee? + + bool flee = false; + + if ( m_Mobile.Hits < c.Hits ) + { + // We are more hurt than them + + int diff = c.Hits - m_Mobile.Hits; + + flee = ( Utility.Random( 0, 100 ) > (10 + diff) ); // (10 + diff)% chance to flee + } + else + { + flee = Utility.Random( 0, 100 ) > 10; // 10% chance to flee + } + + if ( flee ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I am going to flee from {0}", c.Name ); + + Action = ActionType.Flee; + return true; + } + } + } + + if ( m_Mobile.Spell == null && DateTime.Now > m_NextCastTime && m_Mobile.InRange( c, 12 ) ) + { + // We are ready to cast a spell + + Spell spell = null; + Mobile toDispel = FindDispelTarget( true ); + + if ( m_Mobile.Poisoned ) // Top cast priority is cure + { + m_Mobile.DebugSay( "I am going to cure myself" ); + + spell = new CureSpell( m_Mobile, null ); + } + else if ( toDispel != null ) // Something dispellable is attacking us + { + m_Mobile.DebugSay( "I am going to dispel {0}", toDispel ); + + spell = DoDispel( toDispel ); + } + else if ( SmartAI && m_Combo != -1 ) // We are doing a spell combo + { + spell = DoCombo( c ); + } + else if ( SmartAI && (c.Spell is HealSpell || c.Spell is GreaterHealSpell) && !c.Poisoned ) // They have a heal spell out + { + spell = new PoisonSpell( m_Mobile, null ); + } + else + { + spell = ChooseSpell( c ); + } + + // Now we have a spell picked + // Move first before casting + + if ( SmartAI && toDispel != null ) + { + if ( m_Mobile.InRange( toDispel, 10 ) ) + RunFrom( toDispel ); + else if ( !m_Mobile.InRange( toDispel, 12 ) ) + RunTo( toDispel ); + } + else + { + RunTo( c ); + } + + if ( spell != null ) + spell.Cast(); + + TimeSpan delay; + + if ( SmartAI || ( spell is DispelSpell ) ) + delay = TimeSpan.FromSeconds( m_Mobile.ActiveSpeed ); + else + delay = GetDelay(); + + m_NextCastTime = DateTime.Now + delay; + } + else if ( m_Mobile.Spell == null || !m_Mobile.Spell.IsCasting ) + { + RunTo( c ); + } + + return true; + } + + public override bool DoActionGuard() + { + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.DebugSay( "I am going to attack {0}", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + if ( !m_Mobile.Controlled ) + { + ProcessTarget(); + + Spell spell = CheckCastHealingSpell(); + + if ( spell != null ) + spell.Cast(); + } + + base.DoActionGuard(); + } + + return true; + } + + public override bool DoActionFlee() + { + Mobile c = m_Mobile.Combatant; + + if ( (m_Mobile.Mana > 20 || m_Mobile.Mana == m_Mobile.ManaMax) && m_Mobile.Hits > (m_Mobile.HitsMax / 2) ) + { + m_Mobile.DebugSay( "I am stronger now, my guard is up" ); + Action = ActionType.Guard; + } + else if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I am scared of {0}", m_Mobile.FocusMob.Name ); + + RunFrom( m_Mobile.FocusMob ); + m_Mobile.FocusMob = null; + + if ( m_Mobile.Poisoned && Utility.Random( 0, 5 ) == 0 ) + new CureSpell( m_Mobile, null ).Cast(); + } + else + { + m_Mobile.DebugSay( "Area seems clear, but my guard is up" ); + + Action = ActionType.Guard; + m_Mobile.Warmode = true; + } + + return true; + } + + public Mobile FindDispelTarget( bool activeOnly ) + { + if ( m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel( m_Mobile ) || m_Mobile.AutoDispel ) + return null; + + if ( activeOnly ) + { + List aggressed = m_Mobile.Aggressed; + List aggressors = m_Mobile.Aggressors; + + Mobile active = null; + double activePrio = 0.0; + + Mobile comb = m_Mobile.Combatant; + + if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange( comb, 12 ) && CanDispel( comb ) ) + { + active = comb; + activePrio = m_Mobile.GetDistanceToSqrt( comb ); + + if ( activePrio <= 2 ) + return active; + } + + for ( int i = 0; i < aggressed.Count; ++i ) + { + AggressorInfo info = aggressed[i]; + Mobile m = info.Defender; + + if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) ) + { + double prio = m_Mobile.GetDistanceToSqrt( m ); + + if ( active == null || prio < activePrio ) + { + active = m; + activePrio = prio; + + if ( activePrio <= 2 ) + return active; + } + } + } + + for ( int i = 0; i < aggressors.Count; ++i ) + { + AggressorInfo info = aggressors[i]; + Mobile m = info.Attacker; + + if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) ) + { + double prio = m_Mobile.GetDistanceToSqrt( m ); + + if ( active == null || prio < activePrio ) + { + active = m; + activePrio = prio; + + if ( activePrio <= 2 ) + return active; + } + } + } + + return active; + } + else + { + Map map = m_Mobile.Map; + + if ( map != null ) + { + Mobile active = null, inactive = null; + double actPrio = 0.0, inactPrio = 0.0; + + Mobile comb = m_Mobile.Combatant; + + if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && CanDispel( comb ) ) + { + active = inactive = comb; + actPrio = inactPrio = m_Mobile.GetDistanceToSqrt( comb ); + } + + foreach ( Mobile m in m_Mobile.GetMobilesInRange( 12 ) ) + { + if ( m != m_Mobile && CanDispel( m ) ) + { + double prio = m_Mobile.GetDistanceToSqrt( m ); + + if ( !activeOnly && (inactive == null || prio < inactPrio) ) + { + inactive = m; + inactPrio = prio; + } + + if ( (m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio) ) + { + active = m; + actPrio = prio; + } + } + } + + return active != null ? active : inactive; + } + } + + return null; + } + + public bool CanDispel( Mobile m ) + { + return ( m is BaseCreature && ((BaseCreature)m).Summoned && m_Mobile.CanBeHarmful( m, false ) && !((BaseCreature)m).IsAnimatedDead ); + } + + private static int[] m_Offsets = new int[] + { + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1, + + -2, -2, + -2, -1, + -2, 0, + -2, 1, + -2, 2, + -1, -2, + -1, 2, + 0, -2, + 0, 2, + 1, -2, + 1, 2, + 2, -2, + 2, -1, + 2, 0, + 2, 1, + 2, 2 + }; + + private bool ProcessTarget() + { + Target targ = m_Mobile.Target; + + if ( targ == null ) + return false; + + bool isDispel = ( targ is DispelSpell.InternalTarget ); + bool isParalyze = ( targ is ParalyzeSpell.InternalTarget ); + bool isTeleport = ( targ is TeleportSpell.InternalTarget ); + bool teleportAway = false; + + Mobile toTarget; + + if ( isDispel ) + { + toTarget = FindDispelTarget( false ); + + if ( !SmartAI && toTarget != null ) + RunTo( toTarget ); + else if ( toTarget != null && m_Mobile.InRange( toTarget, 10 ) ) + RunFrom( toTarget ); + } + else if ( SmartAI && (isParalyze || isTeleport) ) + { + toTarget = FindDispelTarget( true ); + + if ( toTarget == null ) + { + toTarget = m_Mobile.Combatant; + + if ( toTarget != null ) + RunTo( toTarget ); + } + else if ( m_Mobile.InRange( toTarget, 10 ) ) + { + RunFrom( toTarget ); + teleportAway = true; + } + else + { + teleportAway = true; + } + } + else + { + toTarget = m_Mobile.Combatant; + + if ( toTarget != null ) + RunTo( toTarget ); + } + + if ( (targ.Flags & TargetFlags.Harmful) != 0 && toTarget != null ) + { + if ( (targ.Range == -1 || m_Mobile.InRange( toTarget, targ.Range )) && m_Mobile.CanSee( toTarget ) && m_Mobile.InLOS( toTarget ) ) + { + targ.Invoke( m_Mobile, toTarget ); + } + else if ( isDispel ) + { + targ.Cancel( m_Mobile, TargetCancelType.Canceled ); + } + } + else if ( (targ.Flags & TargetFlags.Beneficial) != 0 ) + { + targ.Invoke( m_Mobile, m_Mobile ); + } + else if ( isTeleport && toTarget != null ) + { + Map map = m_Mobile.Map; + + if ( map == null ) + { + targ.Cancel( m_Mobile, TargetCancelType.Canceled ); + return true; + } + + int px, py; + + if ( teleportAway ) + { + int rx = m_Mobile.X - toTarget.X; + int ry = m_Mobile.Y - toTarget.Y; + + double d = m_Mobile.GetDistanceToSqrt( toTarget ); + + px = toTarget.X + (int)(rx * (10 / d)); + py = toTarget.Y + (int)(ry * (10 / d)); + } + else + { + px = toTarget.X; + py = toTarget.Y; + } + + for ( int i = 0; i < m_Offsets.Length; i += 2 ) + { + int x = m_Offsets[i], y = m_Offsets[i + 1]; + + Point3D p = new Point3D( px + x, py + y, 0 ); + + LandTarget lt = new LandTarget( p, map ); + + if ( (targ.Range == -1 || m_Mobile.InRange( p, targ.Range )) && m_Mobile.InLOS( lt ) && map.CanSpawnMobile( px + x, py + y, lt.Z ) && !SpellHelper.CheckMulti( p, map ) ) + { + targ.Invoke( m_Mobile, lt ); + return true; + } + } + + int teleRange = targ.Range; + + if ( teleRange < 0 ) + teleRange = 12; + + for ( int i = 0; i < 10; ++i ) + { + Point3D randomPoint = new Point3D( m_Mobile.X - teleRange + Utility.Random( teleRange * 2 + 1 ), m_Mobile.Y - teleRange + Utility.Random( teleRange * 2 + 1 ), 0 ); + + LandTarget lt = new LandTarget( randomPoint, map ); + + if ( m_Mobile.InLOS( lt ) && map.CanSpawnMobile( lt.X, lt.Y, lt.Z ) && !SpellHelper.CheckMulti( randomPoint, map ) ) + { + targ.Invoke( m_Mobile, new LandTarget( randomPoint, map ) ); + return true; + } + } + + targ.Cancel( m_Mobile, TargetCancelType.Canceled ); + } + else + { + targ.Cancel( m_Mobile, TargetCancelType.Canceled ); + } + + return true; + } + } +} diff --git a/Scripts/Engines/AI/AI/MeleeAI.cs b/Scripts/Engines/AI/AI/MeleeAI.cs new file mode 100644 index 000000000..5e7c276e2 --- /dev/null +++ b/Scripts/Engines/AI/AI/MeleeAI.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +// +// This is a first simple AI +// +// + +namespace Server.Mobiles +{ + public class MeleeAI : BaseAI + { + public MeleeAI(BaseCreature m) : base (m) + { + } + + public override bool DoActionWander() + { + m_Mobile.DebugSay( "I have no combatant" ); + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + Mobile combatant = m_Mobile.Combatant; + + if ( combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet ) + { + m_Mobile.DebugSay( "My combatant is gone, so my guard is up" ); + + Action = ActionType.Guard; + + return true; + } + + if ( !m_Mobile.InRange( combatant, m_Mobile.RangePerception ) ) + { + // They are somewhat far away, can we find something else? + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.Combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else if ( !m_Mobile.InRange( combatant, m_Mobile.RangePerception * 3 ) ) + { + m_Mobile.Combatant = null; + } + + combatant = m_Mobile.Combatant; + + if ( combatant == null ) + { + m_Mobile.DebugSay( "My combatant has fled, so I am on guard" ); + Action = ActionType.Guard; + + return true; + } + } + + /*if ( !m_Mobile.InLOS( combatant ) ) + { + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.Combatant = combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + }*/ + + if ( MoveTo( combatant, true, m_Mobile.RangeFight ) ) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo( combatant ); + } + else if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + + return true; + } + else if ( m_Mobile.GetDistanceToSqrt( combatant ) > m_Mobile.RangePerception + 1 ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I cannot find {0}, so my guard is up", combatant.Name ); + + Action = ActionType.Guard; + + return true; + } + else + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I should be closer to {0}", combatant.Name ); + } + + if ( !m_Mobile.Controlled && !m_Mobile.Summoned && !m_Mobile.IsParagon ) + { + if ( m_Mobile.Hits < m_Mobile.HitsMax * 20/100 ) + { + // We are low on health, should we flee? + + bool flee = false; + + if ( m_Mobile.Hits < combatant.Hits ) + { + // We are more hurt than them + + int diff = combatant.Hits - m_Mobile.Hits; + + flee = ( Utility.Random( 0, 100 ) < (10 + diff) ); // (10 + diff)% chance to flee + } + else + { + flee = Utility.Random( 0, 100 ) < 10; // 10% chance to flee + } + + if ( flee ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I am going to flee from {0}", combatant.Name ); + + Action = ActionType.Flee; + } + } + } + + return true; + } + + public override bool DoActionGuard() + { + if ( AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); + } + + return true; + } + + public override bool DoActionFlee() + { + if ( m_Mobile.Hits > m_Mobile.HitsMax/2 ) + { + m_Mobile.DebugSay( "I am stronger now, so I will continue fighting" ); + Action = ActionType.Combat; + } + else + { + m_Mobile.FocusMob = m_Mobile.Combatant; + base.DoActionFlee(); + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/PredatorAI.cs b/Scripts/Engines/AI/AI/PredatorAI.cs new file mode 100644 index 000000000..12058916f --- /dev/null +++ b/Scripts/Engines/AI/AI/PredatorAI.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + + +/* + * PredatorAI, its an animal that can attack + * Dont flee but dont attack if not hurt or attacked + * + */ + +namespace Server.Mobiles +{ + public class PredatorAI : BaseAI + { + public PredatorAI(BaseCreature m) : base (m) + { + } + + public override bool DoActionWander() + { + if ( m_Mobile.Combatant != null ) + { + m_Mobile.DebugSay( "I am hurt or being attacked, I kill him" ); + Action = ActionType.Combat; + } + else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true)) + { + m_Mobile.DebugSay( "There is something near, I go away" ); + Action = ActionType.Backoff; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + Mobile combatant = m_Mobile.Combatant; + + if ( combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map ) + { + m_Mobile.DebugSay( "My combatant is gone, so my guard is up" ); + Action = ActionType.Wander; + return true; + } + + if ( WalkMobileRange( combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight ) ) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo( combatant ); + } + else + { + if ( m_Mobile.GetDistanceToSqrt( combatant ) > m_Mobile.RangePerception + 1 ) + { + m_Mobile.DebugSay( "I cannot find {0}", combatant.Name ); + + Action = ActionType.Wander; + return true; + } + else + { + m_Mobile.DebugSay( "I should be closer to {0}", combatant.Name ); + } + } + + return true; + } + + public override bool DoActionBackoff() + { + if ( m_Mobile.IsHurt() || m_Mobile.Combatant != null ) + { + Action = ActionType.Combat; + } + else + { + if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false , true)) + { + if ( WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2) ) + { + m_Mobile.DebugSay( "Well, here I am safe" ); + Action = ActionType.Wander; + } + } + else + { + m_Mobile.DebugSay( "I have lost my focus, lets relax" ); + Action = ActionType.Wander; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/ThiefAI.cs b/Scripts/Engines/AI/AI/ThiefAI.cs new file mode 100644 index 000000000..f6375c009 --- /dev/null +++ b/Scripts/Engines/AI/AI/ThiefAI.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +using Server.Items; + +// +// This is a first simple AI +// +// + +namespace Server.Mobiles +{ + public class ThiefAI : BaseAI + { + public ThiefAI(BaseCreature m) : base (m) + { + } + + private Item m_toDisarm; + public override bool DoActionWander() + { + m_Mobile.DebugSay( "I have no combatant" ); + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionWander(); + } + + return true; + } + + public override bool DoActionCombat() + { + Mobile combatant = m_Mobile.Combatant; + + if ( combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map ) + { + m_Mobile.DebugSay( "My combatant is gone, so my guard is up" ); + + Action = ActionType.Guard; + + return true; + } + + if ( WalkMobileRange( combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight ) ) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo( combatant ); + if ( m_toDisarm == null ) + m_toDisarm = combatant.FindItemOnLayer( Layer.OneHanded ); + + if ( m_toDisarm == null ) + m_toDisarm = combatant.FindItemOnLayer( Layer.TwoHanded ); + + if ( m_toDisarm != null && m_toDisarm.IsChildOf( m_Mobile.Backpack ) ) + { + m_toDisarm = combatant.FindItemOnLayer( Layer.OneHanded ); + if ( m_toDisarm == null ) + m_toDisarm = combatant.FindItemOnLayer( Layer.TwoHanded ); + } + if ( !m_Mobile.DisarmReady && m_Mobile.Skills[SkillName.Wrestling].Value >= 80.0 && m_Mobile.Skills[SkillName.ArmsLore].Value >= 80.0 && m_toDisarm != null ) + EventSink.InvokeDisarmRequest( new DisarmRequestEventArgs( m_Mobile ) ); + + if ( m_toDisarm != null && m_toDisarm.IsChildOf( combatant.Backpack ) && m_Mobile.NextSkillTime <= DateTime.Now && (m_toDisarm.LootType != LootType.Blessed && m_toDisarm.LootType != LootType.Newbied) ) + { + m_Mobile.DebugSay( "Trying to steal from combatant." ); + m_Mobile.UseSkill( SkillName.Stealing ); + if ( m_Mobile.Target != null ) + m_Mobile.Target.Invoke( m_Mobile, m_toDisarm ); + } + else if ( m_toDisarm == null && m_Mobile.NextSkillTime <= DateTime.Now ) + { + Container cpack = combatant.Backpack; + + if ( cpack != null ) + { + Item steala = cpack.FindItemByType( typeof ( Bandage ) ); + if ( steala != null ) + { + m_Mobile.DebugSay( "Trying to steal from combatant." ); + m_Mobile.UseSkill( SkillName.Stealing ); + if ( m_Mobile.Target != null ) + m_Mobile.Target.Invoke( m_Mobile, steala ); + } + Item stealb = cpack.FindItemByType( typeof ( Nightshade ) ); + if ( stealb != null ) + { + m_Mobile.DebugSay( "Trying to steal from combatant." ); + m_Mobile.UseSkill( SkillName.Stealing ); + if ( m_Mobile.Target != null ) + m_Mobile.Target.Invoke( m_Mobile, stealb ); + } + Item stealc = cpack.FindItemByType( typeof ( BlackPearl ) ); + if ( stealc != null ) + { + m_Mobile.DebugSay( "Trying to steal from combatant." ); + m_Mobile.UseSkill( SkillName.Stealing ); + if ( m_Mobile.Target != null ) + m_Mobile.Target.Invoke( m_Mobile, stealc ); + } + + Item steald = cpack.FindItemByType( typeof ( MandrakeRoot ) ); + if ( steald != null ) + { + m_Mobile.DebugSay( "Trying to steal from combatant." ); + m_Mobile.UseSkill( SkillName.Stealing ); + if ( m_Mobile.Target != null ) + m_Mobile.Target.Invoke( m_Mobile, steald ); + } + else if ( steala == null && stealb == null && stealc == null && steald == null ) + { + m_Mobile.DebugSay( "I am going to flee from {0}", combatant.Name ); + + Action = ActionType.Flee; + } + } + } + } + else + { + m_Mobile.DebugSay( "I should be closer to {0}", combatant.Name ); + } + + if ( m_Mobile.Hits < m_Mobile.HitsMax * 20/100 && !m_Mobile.IsParagon ) + { + // We are low on health, should we flee? + + bool flee = false; + + if ( m_Mobile.Hits < combatant.Hits ) + { + // We are more hurt than them + + int diff = combatant.Hits - m_Mobile.Hits; + + flee = ( Utility.Random( 0, 100 ) > (10 + diff) ); // (10 + diff)% chance to flee + } + else + { + flee = Utility.Random( 0, 100 ) > 10; // 10% chance to flee + } + + if ( flee ) + { + m_Mobile.DebugSay( "I am going to flee from {0}", combatant.Name ); + + Action = ActionType.Flee; + } + } + + return true; + } + + public override bool DoActionGuard() + { + if ( AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.DebugSay( "I have detected {0}, attacking", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + base.DoActionGuard(); + } + + return true; + } + + public override bool DoActionFlee() + { + if ( m_Mobile.Hits > m_Mobile.HitsMax/2 ) + { + m_Mobile.DebugSay( "I am stronger now, so I will continue fighting" ); + Action = ActionType.Combat; + } + else + { + m_Mobile.FocusMob = m_Mobile.Combatant; + base.DoActionFlee(); + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/AI/VendorAI.cs b/Scripts/Engines/AI/AI/VendorAI.cs new file mode 100644 index 000000000..f7dcaf070 --- /dev/null +++ b/Scripts/Engines/AI/AI/VendorAI.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +// +// This is a first simple AI +// +// + +namespace Server.Mobiles +{ + public class VendorAI : BaseAI + { + public VendorAI(BaseCreature m) : base (m) + { + } + + public override bool DoActionWander() + { + m_Mobile.DebugSay( "I'm fine" ); + + if ( m_Mobile.Combatant != null ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} is attacking me", m_Mobile.Combatant.Name ); + + m_Mobile.Say( Utility.RandomList( 1005305, 501603 ) ); + + Action = ActionType.Flee; + } + else + { + if ( m_Mobile.FocusMob != null ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} has talked to me", m_Mobile.FocusMob.Name ); + + Action = ActionType.Interact; + } + else + { + m_Mobile.Warmode = false; + + base.DoActionWander(); + } + } + + return true; + } + + public override bool DoActionInteract() + { + Mobile customer = m_Mobile.FocusMob; + + if ( m_Mobile.Combatant != null ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} is attacking me", m_Mobile.Combatant.Name ); + + m_Mobile.Say( Utility.RandomList( 1005305, 501603 ) ); + + Action = ActionType.Flee; + + return true; + } + + if ( customer == null || customer.Deleted || customer.Map != m_Mobile.Map ) + { + m_Mobile.DebugSay( "My customer have disapeared" ); + m_Mobile.FocusMob = null; + + Action = ActionType.Wander; + } + else + { + if ( customer.InRange( m_Mobile, m_Mobile.RangeFight ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "I am with {0}", customer.Name ); + + m_Mobile.Direction = m_Mobile.GetDirectionTo( customer ); + } + else + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "{0} is gone", customer.Name ); + + m_Mobile.FocusMob = null; + + Action = ActionType.Wander; + } + } + + return true; + } + + public override bool DoActionGuard() + { + m_Mobile.FocusMob = m_Mobile.Combatant; + return base.DoActionGuard(); + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.InRange( m_Mobile, 4 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + // Temporary + public override void OnSpeech( SpeechEventArgs e ) + { + base.OnSpeech( e ); + + Mobile from = e.Mobile; + + if ( m_Mobile is BaseVendor && from.InRange( m_Mobile, Core.AOS ? 1 : 4 ) && !e.Handled ) + { + if ( e.HasKeyword( 0x14D ) ) // *vendor sell* + { + e.Handled = true; + + ((BaseVendor)m_Mobile).VendorSell( from ); + m_Mobile.FocusMob = from; + } + else if ( e.HasKeyword( 0x3C ) ) + { + e.Handled = true; + + ((BaseVendor)m_Mobile).VendorBuy( from ); + m_Mobile.FocusMob = from; + } + else if ( WasNamed( e.Speech ) ) + { + e.Handled = true; + + if ( e.HasKeyword( 0x177 ) ) // *sell* + ((BaseVendor)m_Mobile).VendorSell( from ); + else if ( e.HasKeyword( 0x171 ) ) // *buy* + ((BaseVendor)m_Mobile).VendorBuy( from ); + + m_Mobile.FocusMob = from; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Creature/BaseCreature.cs b/Scripts/Engines/AI/Creature/BaseCreature.cs new file mode 100644 index 000000000..e188106d1 --- /dev/null +++ b/Scripts/Engines/AI/Creature/BaseCreature.cs @@ -0,0 +1,4792 @@ +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 description for MobileAI. + /// + /// + 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 m_arSpellAttack; // List of attack spell/power + List 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 m_Owners; + private List 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 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 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( 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; + + 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(); + m_arSpellDefense = new List(); + + m_bControlled = false; + m_ControlMaster = null; + m_ControlTarget = null; + m_ControlOrder = OrderType.None; + + m_bTamable = false; + + m_Owners = new List(); + + 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(); + m_arSpellDefense = new List(); + + 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(); + + 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 list ) + { + } + + public virtual bool CanDrop { get { return !Summoned; } } + + public override void GetContextMenuEntries( Mobile from, List 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 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.GetDaemonBonesFor( 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 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 GetLootingRights( List damageEntries, int hitsMax ) + { + List rights = new List(); + + 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 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 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 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 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 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 move = new List(); + + 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 toRelease = new List(); + + // added array for wild creatures in house regions to be removed + List toRemove = new List(); + + 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(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Creature/Dummy.cs b/Scripts/Engines/AI/Creature/Dummy.cs new file mode 100644 index 000000000..995e428f7 --- /dev/null +++ b/Scripts/Engines/AI/Creature/Dummy.cs @@ -0,0 +1,152 @@ +using System; +using Server.Items; +using Server.Mobiles; +using Server.Spells; + +namespace Server.Mobiles +{ + /// + /// This is a test creature + /// You can set its value in game + /// It die after 5 minutes, so your test server stay clean + /// Create a macro to help your creation "[add Dummy 1 15 7 -1 0.5 2" + /// + /// A iTeam of negative will set a faction at random + /// + /// Say Kill if you want them to die + /// + /// + public class Dummy : BaseCreature + { + public Timer m_Timer; + + [Constructable] + public Dummy(AIType iAI, FightMode iFightMode, int iRangePerception, int iRangeFight, double dActiveSpeed, double dPassiveSpeed) : base(iAI, iFightMode, iRangePerception, iRangeFight, dActiveSpeed, dPassiveSpeed) + { + this.Body = 400 + Utility.Random(2); + this.Hue = Utility.RandomSkinHue(); + + this.Skills[SkillName.DetectHidden].Base = 100; + this.Skills[SkillName.MagicResist].Base = 120; + + Team = Utility.Random(3); + + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + Utility.AssignRandomHair( this, iHue ); + + LeatherGloves glv = new LeatherGloves(); + glv.Hue = iHue; + glv.LootType = LootType.Newbied; + AddItem(glv); + + Container pack = new Backpack(); + + pack.Movable = false; + + AddItem( pack ); + + m_Timer = new AutokillTimer(this); + m_Timer.Start(); + } + + public Dummy( Serial serial ) : base( serial ) + { + m_Timer = new AutokillTimer(this); + m_Timer.Start(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + return base.HandlesOnSpeech( from ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + base.OnSpeech( e ); + + if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) + { + if (e.Speech == "kill") + { + m_Timer.Stop(); + m_Timer.Delay = TimeSpan.FromSeconds( Utility.Random(1, 5) ); + m_Timer.Start(); + } + } + } + + public override void OnTeamChange() + { + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + Item item = FindItemOnLayer( Layer.OuterTorso ); + + if ( item != null ) + item.Hue = jHue; + + item = FindItemOnLayer( Layer.Helm ); + + if ( item != null ) + item.Hue = iHue; + + item = FindItemOnLayer( Layer.Gloves ); + + if ( item != null ) + item.Hue = iHue; + + item = FindItemOnLayer( Layer.Shoes ); + + if ( item != null ) + item.Hue = iHue; + + HairHue = iHue; + + item = FindItemOnLayer( Layer.MiddleTorso ); + + if ( item != null ) + item.Hue = iHue; + + item = FindItemOnLayer( Layer.OuterLegs ); + + if ( item != null ) + item.Hue = iHue; + } + + private class AutokillTimer : Timer + { + private Dummy m_Owner; + + public AutokillTimer( Dummy owner ) : base( TimeSpan.FromMinutes(5.0) ) + { + m_Owner = owner; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Owner.Kill(); + Stop(); + } + } + } +} diff --git a/Scripts/Engines/AI/Creature/DummySpecific.cs b/Scripts/Engines/AI/Creature/DummySpecific.cs new file mode 100644 index 000000000..aca1538fa --- /dev/null +++ b/Scripts/Engines/AI/Creature/DummySpecific.cs @@ -0,0 +1,814 @@ +using System; +using Server.Items; +using Server.Mobiles; +using Server.Spells; + +namespace Server.Mobiles +{ + /// + /// This is a test creature + /// You can set its value in game + /// It die after 5 minutes, so your test server stay clean + /// Create a macro to help your creation "[add Dummy 1 15 7 -1 0.5 2" + /// + /// A iTeam of negative will set a faction at random + /// + /// Say Kill if you want them to die + /// + /// + + public class DummyMace : Dummy + { + + [Constructable] + public DummyMace() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Macer + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 125, 125, 90 ); + this.Skills[SkillName.Macing].Base = 120; + this.Skills[SkillName.Anatomy].Base = 120; + this.Skills[SkillName.Healing].Base = 120; + this.Skills[SkillName.Tactics].Base = 120; + + + // Name + this.Name = "Macer"; + + // Equip + WarHammer war = new WarHammer(); + war.Movable = true; + war.Crafter = this; + war.Quality = WeaponQuality.Regular; + AddItem( war ); + + Boots bts = new Boots(); + bts.Hue = iHue; + AddItem( bts ); + + ChainChest cht = new ChainChest(); + cht.Movable = false; + cht.LootType = LootType.Newbied; + cht.Crafter = this; + cht.Quality = ArmorQuality.Regular; + AddItem( cht ); + + ChainLegs chl = new ChainLegs(); + chl.Movable = false; + chl.LootType = LootType.Newbied; + chl.Crafter = this; + chl.Quality = ArmorQuality.Regular; + AddItem( chl ); + + PlateArms pla = new PlateArms(); + pla.Movable = false; + pla.LootType = LootType.Newbied; + pla.Crafter = this; + pla.Quality = ArmorQuality.Regular; + AddItem( pla ); + + Bandage band = new Bandage( 50 ); + AddToBackpack( band ); + } + + public DummyMace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummyFence : Dummy + { + + [Constructable] + public DummyFence() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Fencer + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 125, 125, 90 ); + this.Skills[SkillName.Fencing].Base = 120; + this.Skills[SkillName.Anatomy].Base = 120; + this.Skills[SkillName.Healing].Base = 120; + this.Skills[SkillName.Tactics].Base = 120; + + // Name + this.Name = "Fencer"; + + // Equip + Spear ssp = new Spear(); + ssp.Movable = true; + ssp.Crafter = this; + ssp.Quality = WeaponQuality.Regular; + AddItem( ssp ); + + Boots snd = new Boots(); + snd.Hue = iHue; + snd.LootType = LootType.Newbied; + AddItem( snd ); + + ChainChest cht = new ChainChest(); + cht.Movable = false; + cht.LootType = LootType.Newbied; + cht.Crafter = this; + cht.Quality = ArmorQuality.Regular; + AddItem( cht ); + + ChainLegs chl = new ChainLegs(); + chl.Movable = false; + chl.LootType = LootType.Newbied; + chl.Crafter = this; + chl.Quality = ArmorQuality.Regular; + AddItem( chl ); + + PlateArms pla = new PlateArms(); + pla.Movable = false; + pla.LootType = LootType.Newbied; + pla.Crafter = this; + pla.Quality = ArmorQuality.Regular; + AddItem( pla ); + + Bandage band = new Bandage( 50 ); + AddToBackpack( band ); + } + + public DummyFence( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummySword : Dummy + { + + [Constructable] + public DummySword() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Swordsman + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 125, 125, 90 ); + this.Skills[SkillName.Swords].Base = 120; + this.Skills[SkillName.Anatomy].Base = 120; + this.Skills[SkillName.Healing].Base = 120; + this.Skills[SkillName.Tactics].Base = 120; + this.Skills[SkillName.Parry].Base = 120; + + + // Name + this.Name = "Swordsman"; + + // Equip + Katana kat = new Katana(); + kat.Crafter = this; + kat.Movable = true; + kat.Quality = WeaponQuality.Regular; + AddItem( kat ); + + Boots bts = new Boots(); + bts.Hue = iHue; + AddItem( bts ); + + ChainChest cht = new ChainChest(); + cht.Movable = false; + cht.LootType = LootType.Newbied; + cht.Crafter = this; + cht.Quality = ArmorQuality.Regular; + AddItem( cht ); + + ChainLegs chl = new ChainLegs(); + chl.Movable = false; + chl.LootType = LootType.Newbied; + chl.Crafter = this; + chl.Quality = ArmorQuality.Regular; + AddItem( chl ); + + PlateArms pla = new PlateArms(); + pla.Movable = false; + pla.LootType = LootType.Newbied; + pla.Crafter = this; + pla.Quality = ArmorQuality.Regular; + AddItem( pla ); + + Bandage band = new Bandage( 50 ); + AddToBackpack( band ); + } + + public DummySword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummyNox : Dummy + { + + [Constructable] + public DummyNox() : base(AIType.AI_Mage, FightMode.Closest, 15, 1, 0.2, 0.6) + { + + // A Dummy Nox or Pure Mage + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 90, 90, 125 ); + this.Skills[SkillName.Magery].Base = 120; + this.Skills[SkillName.EvalInt].Base = 120; + this.Skills[SkillName.Inscribe].Base = 100; + this.Skills[SkillName.Wrestling].Base = 120; + this.Skills[SkillName.Meditation].Base = 120; + this.Skills[SkillName.Poisoning].Base = 100; + + + // Name + this.Name = "Nox Mage"; + + // Equip + Spellbook book = new Spellbook(); + book.Movable = false; + book.LootType = LootType.Newbied; + book.Content =0xFFFFFFFFFFFFFFFF; + AddItem( book ); + + Kilt kilt = new Kilt(); + kilt.Hue = jHue; + AddItem( kilt ); + + Sandals snd = new Sandals(); + snd.Hue = iHue; + snd.LootType = LootType.Newbied; + AddItem( snd ); + + SkullCap skc = new SkullCap(); + skc.Hue = iHue; + AddItem( skc ); + + // Spells + AddSpellAttack( typeof(Spells.First.MagicArrowSpell) ); + AddSpellAttack( typeof(Spells.First.WeakenSpell) ); + AddSpellAttack( typeof(Spells.Third.FireballSpell) ); + AddSpellDefense( typeof(Spells.Third.WallOfStoneSpell) ); + AddSpellDefense( typeof(Spells.First.HealSpell) ); + } + + public DummyNox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummyStun : Dummy + { + + [Constructable] + public DummyStun() : base(AIType.AI_Mage, FightMode.Closest, 15, 1, 0.2, 0.6) + { + + // A Dummy Stun Mage + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 90, 90, 125 ); + this.Skills[SkillName.Magery].Base = 100; + this.Skills[SkillName.EvalInt].Base = 120; + this.Skills[SkillName.Anatomy].Base = 80; + this.Skills[SkillName.Wrestling].Base = 80; + this.Skills[SkillName.Meditation].Base = 100; + this.Skills[SkillName.Poisoning].Base = 100; + + + // Name + this.Name = "Stun Mage"; + + // Equip + Spellbook book = new Spellbook(); + book.Movable = false; + book.LootType = LootType.Newbied; + book.Content =0xFFFFFFFFFFFFFFFF; + AddItem( book ); + + LeatherArms lea = new LeatherArms(); + lea.Movable = false; + lea.LootType = LootType.Newbied; + lea.Crafter = this; + lea.Quality = ArmorQuality.Regular; + AddItem( lea ); + + LeatherChest lec = new LeatherChest(); + lec.Movable = false; + lec.LootType = LootType.Newbied; + lec.Crafter = this; + lec.Quality = ArmorQuality.Regular; + AddItem( lec ); + + LeatherGorget leg = new LeatherGorget(); + leg.Movable = false; + leg.LootType = LootType.Newbied; + leg.Crafter = this; + leg.Quality = ArmorQuality.Regular; + AddItem( leg ); + + LeatherLegs lel = new LeatherLegs(); + lel.Movable = false; + lel.LootType = LootType.Newbied; + lel.Crafter = this; + lel.Quality = ArmorQuality.Regular; + AddItem( lel ); + + Boots bts = new Boots(); + bts.Hue = iHue; + AddItem( bts ); + + Cap cap = new Cap(); + cap.Hue = iHue; + AddItem( cap ); + + // Spells + AddSpellAttack( typeof(Spells.First.MagicArrowSpell) ); + AddSpellAttack( typeof(Spells.First.WeakenSpell) ); + AddSpellAttack( typeof(Spells.Third.FireballSpell) ); + AddSpellDefense( typeof(Spells.Third.WallOfStoneSpell) ); + AddSpellDefense( typeof(Spells.First.HealSpell) ); + } + + public DummyStun( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummySuper : Dummy + { + + [Constructable] + public DummySuper() : base(AIType.AI_Mage, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Super Mage + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 125, 125, 125 ); + this.Skills[SkillName.Magery].Base = 120; + this.Skills[SkillName.EvalInt].Base = 120; + this.Skills[SkillName.Anatomy].Base = 120; + this.Skills[SkillName.Wrestling].Base = 120; + this.Skills[SkillName.Meditation].Base = 120; + this.Skills[SkillName.Poisoning].Base = 100; + this.Skills[SkillName.Inscribe].Base = 100; + + // Name + this.Name = "Super Mage"; + + // Equip + Spellbook book = new Spellbook(); + book.Movable = false; + book.LootType = LootType.Newbied; + book.Content =0xFFFFFFFFFFFFFFFF; + AddItem( book ); + + LeatherArms lea = new LeatherArms(); + lea.Movable = false; + lea.LootType = LootType.Newbied; + lea.Crafter = this; + lea.Quality = ArmorQuality.Regular; + AddItem( lea ); + + LeatherChest lec = new LeatherChest(); + lec.Movable = false; + lec.LootType = LootType.Newbied; + lec.Crafter = this; + lec.Quality = ArmorQuality.Regular; + AddItem( lec ); + + LeatherGorget leg = new LeatherGorget(); + leg.Movable = false; + leg.LootType = LootType.Newbied; + leg.Crafter = this; + leg.Quality = ArmorQuality.Regular; + AddItem( leg ); + + LeatherLegs lel = new LeatherLegs(); + lel.Movable = false; + lel.LootType = LootType.Newbied; + lel.Crafter = this; + lel.Quality = ArmorQuality.Regular; + AddItem( lel ); + + Sandals snd = new Sandals(); + snd.Hue = iHue; + snd.LootType = LootType.Newbied; + AddItem( snd ); + + JesterHat jhat = new JesterHat(); + jhat.Hue = iHue; + AddItem( jhat ); + + Doublet dblt = new Doublet(); + dblt.Hue = iHue; + AddItem( dblt ); + + // Spells + AddSpellAttack( typeof(Spells.First.MagicArrowSpell) ); + AddSpellAttack( typeof(Spells.First.WeakenSpell) ); + AddSpellAttack( typeof(Spells.Third.FireballSpell) ); + AddSpellDefense( typeof(Spells.Third.WallOfStoneSpell) ); + AddSpellDefense( typeof(Spells.First.HealSpell) ); + } + + public DummySuper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummyHealer : Dummy + { + + [Constructable] + public DummyHealer() : base(AIType.AI_Healer, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Healer Mage + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 125, 125, 125 ); + this.Skills[SkillName.Magery].Base = 120; + this.Skills[SkillName.EvalInt].Base = 120; + this.Skills[SkillName.Anatomy].Base = 120; + this.Skills[SkillName.Wrestling].Base = 120; + this.Skills[SkillName.Meditation].Base = 120; + this.Skills[SkillName.Healing].Base = 100; + + // Name + this.Name = "Healer"; + + // Equip + Spellbook book = new Spellbook(); + book.Movable = false; + book.LootType = LootType.Newbied; + book.Content =0xFFFFFFFFFFFFFFFF; + AddItem( book ); + + LeatherArms lea = new LeatherArms(); + lea.Movable = false; + lea.LootType = LootType.Newbied; + lea.Crafter = this; + lea.Quality = ArmorQuality.Regular; + AddItem( lea ); + + LeatherChest lec = new LeatherChest(); + lec.Movable = false; + lec.LootType = LootType.Newbied; + lec.Crafter = this; + lec.Quality = ArmorQuality.Regular; + AddItem( lec ); + + LeatherGorget leg = new LeatherGorget(); + leg.Movable = false; + leg.LootType = LootType.Newbied; + leg.Crafter = this; + leg.Quality = ArmorQuality.Regular; + AddItem( leg ); + + LeatherLegs lel = new LeatherLegs(); + lel.Movable = false; + lel.LootType = LootType.Newbied; + lel.Crafter = this; + lel.Quality = ArmorQuality.Regular; + AddItem( lel ); + + Sandals snd = new Sandals(); + snd.Hue = iHue; + snd.LootType = LootType.Newbied; + AddItem( snd ); + + Cap cap = new Cap(); + cap.Hue = iHue; + AddItem( cap ); + + Robe robe = new Robe(); + robe.Hue = iHue; + AddItem( robe ); + + } + + public DummyHealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummyAssassin : Dummy + { + + [Constructable] + public DummyAssassin() : base(AIType.AI_Melee, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Hybrid Assassin + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 105, 105, 105 ); + this.Skills[SkillName.Magery].Base = 120; + this.Skills[SkillName.EvalInt].Base = 120; + this.Skills[SkillName.Swords].Base = 120; + this.Skills[SkillName.Tactics].Base = 120; + this.Skills[SkillName.Meditation].Base = 120; + this.Skills[SkillName.Poisoning].Base = 100; + + // Name + this.Name = "Hybrid Assassin"; + + // Equip + Spellbook book = new Spellbook(); + book.Movable = false; + book.LootType = LootType.Newbied; + book.Content =0xFFFFFFFFFFFFFFFF; + AddToBackpack( book ); + + Katana kat = new Katana(); + kat.Movable = false; + kat.LootType = LootType.Newbied; + kat.Crafter = this; + kat.Poison = Poison.Deadly; + kat.PoisonCharges = 12; + kat.Quality = WeaponQuality.Regular; + AddToBackpack( kat ); + + LeatherArms lea = new LeatherArms(); + lea.Movable = false; + lea.LootType = LootType.Newbied; + lea.Crafter = this; + lea.Quality = ArmorQuality.Regular; + AddItem( lea ); + + LeatherChest lec = new LeatherChest(); + lec.Movable = false; + lec.LootType = LootType.Newbied; + lec.Crafter = this; + lec.Quality = ArmorQuality.Regular; + AddItem( lec ); + + LeatherGorget leg = new LeatherGorget(); + leg.Movable = false; + leg.LootType = LootType.Newbied; + leg.Crafter = this; + leg.Quality = ArmorQuality.Regular; + AddItem( leg ); + + LeatherLegs lel = new LeatherLegs(); + lel.Movable = false; + lel.LootType = LootType.Newbied; + lel.Crafter = this; + lel.Quality = ArmorQuality.Regular; + AddItem( lel ); + + Sandals snd = new Sandals(); + snd.Hue = iHue; + snd.LootType = LootType.Newbied; + AddItem( snd ); + + Cap cap = new Cap(); + cap.Hue = iHue; + AddItem( cap ); + + Robe robe = new Robe(); + robe.Hue = iHue; + AddItem( robe ); + + DeadlyPoisonPotion pota = new DeadlyPoisonPotion(); + pota.LootType = LootType.Newbied; + AddToBackpack( pota ); + + DeadlyPoisonPotion potb = new DeadlyPoisonPotion(); + potb.LootType = LootType.Newbied; + AddToBackpack( potb ); + + DeadlyPoisonPotion potc = new DeadlyPoisonPotion(); + potc.LootType = LootType.Newbied; + AddToBackpack( potc ); + + DeadlyPoisonPotion potd = new DeadlyPoisonPotion(); + potd.LootType = LootType.Newbied; + AddToBackpack( potd ); + + Bandage band = new Bandage( 50 ); + AddToBackpack( band ); + + } + + public DummyAssassin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DummyTheif : Dummy + { + + [Constructable] + public DummyTheif() : base(AIType.AI_Thief, FightMode.Closest, 15, 1, 0.2, 0.6) + { + // A Dummy Hybrid Theif + int iHue = 20 + Team * 40; + int jHue = 25 + Team * 40; + + // Skills and Stats + this.InitStats( 105, 105, 105 ); + this.Skills[SkillName.Healing].Base = 120; + this.Skills[SkillName.Anatomy].Base = 120; + this.Skills[SkillName.Stealing].Base = 120; + this.Skills[SkillName.ArmsLore].Base = 100; + this.Skills[SkillName.Meditation].Base = 120; + this.Skills[SkillName.Wrestling].Base = 120; + + // Name + this.Name = "Hybrid Theif"; + + // Equip + Spellbook book = new Spellbook(); + book.Movable = false; + book.LootType = LootType.Newbied; + book.Content =0xFFFFFFFFFFFFFFFF; + AddItem( book ); + + LeatherArms lea = new LeatherArms(); + lea.Movable = false; + lea.LootType = LootType.Newbied; + lea.Crafter = this; + lea.Quality = ArmorQuality.Regular; + AddItem( lea ); + + LeatherChest lec = new LeatherChest(); + lec.Movable = false; + lec.LootType = LootType.Newbied; + lec.Crafter = this; + lec.Quality = ArmorQuality.Regular; + AddItem( lec ); + + LeatherGorget leg = new LeatherGorget(); + leg.Movable = false; + leg.LootType = LootType.Newbied; + leg.Crafter = this; + leg.Quality = ArmorQuality.Regular; + AddItem( leg ); + + LeatherLegs lel = new LeatherLegs(); + lel.Movable = false; + lel.LootType = LootType.Newbied; + lel.Crafter = this; + lel.Quality = ArmorQuality.Regular; + AddItem( lel ); + + Sandals snd = new Sandals(); + snd.Hue = iHue; + snd.LootType = LootType.Newbied; + AddItem( snd ); + + Cap cap = new Cap(); + cap.Hue = iHue; + AddItem( cap ); + + Robe robe = new Robe(); + robe.Hue = iHue; + AddItem( robe ); + + Bandage band = new Bandage( 50 ); + AddToBackpack( band ); + } + + public DummyTheif( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Creature/OppositionGroup.cs b/Scripts/Engines/AI/Creature/OppositionGroup.cs new file mode 100644 index 000000000..4729350d9 --- /dev/null +++ b/Scripts/Engines/AI/Creature/OppositionGroup.cs @@ -0,0 +1,140 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server +{ + public class OppositionGroup + { + private Type[][] m_Types; + + public OppositionGroup( Type[][] types ) + { + m_Types = types; + } + + public bool IsEnemy( object from, object target ) + { + int fromGroup = IndexOf( from ); + int targGroup = IndexOf( target ); + + return ( fromGroup != -1 && targGroup != -1 && fromGroup != targGroup ); + } + + public int IndexOf( object obj ) + { + if ( obj == null ) + return -1; + + Type type = obj.GetType(); + + for ( int i = 0; i < m_Types.Length; ++i ) + { + Type[] group = m_Types[i]; + + bool contains = false; + + for ( int j = 0; !contains && j < group.Length; ++j ) + contains = ( type == group[j] ); + + if ( contains ) + return i; + } + + return -1; + } + + private static OppositionGroup m_TerathansAndOphidians = new OppositionGroup( new Type[][] + { + new Type[] + { + typeof( TerathanAvenger ), + typeof( TerathanDrone ), + typeof( TerathanMatriarch ), + typeof( TerathanWarrior ) + }, + new Type[] + { + typeof( OphidianArchmage ), + typeof( OphidianKnight ), + typeof( OphidianMage ), + typeof( OphidianMatriarch ), + typeof( OphidianWarrior ) + } + } ); + + public static OppositionGroup TerathansAndOphidians + { + get{ return m_TerathansAndOphidians; } + } + + private static OppositionGroup m_SavagesAndOrcs = new OppositionGroup( new Type[][] + { + new Type[] + { + typeof( Orc ), + typeof( OrcBomber ), + typeof( OrcBrute ), + typeof( OrcCaptain ), + typeof( OrcishLord ), + typeof( OrcishMage ), + typeof( SpawnedOrcishLord ) + }, + new Type[] + { + typeof( Savage ), + typeof( SavageRider ), + typeof( SavageRidgeback ), + typeof( SavageShaman ) + } + } ); + + public static OppositionGroup SavagesAndOrcs + { + get{ return m_SavagesAndOrcs; } + } + + private static OppositionGroup m_FeyAndUndead = new OppositionGroup( new Type[][] + { + new Type[] + { + typeof( Centaur ), + typeof( EtherealWarrior ), + typeof( Kirin ), + typeof( LordOaks ), + typeof( Pixie ), + typeof( Silvani ), + typeof( Unicorn ), + typeof( Wisp ), + typeof( Treefellow ) + }, + new Type[] + { + typeof( AncientLich ), + typeof( Bogle ), + typeof( LichLord ), + typeof( Shade ), + typeof( Spectre ), + typeof( Wraith ), + typeof( BoneKnight ), + typeof( Ghoul ), + typeof( Mummy ), + typeof( SkeletalKnight ), + typeof( Skeleton ), + typeof( Zombie ), + typeof( ShadowKnight ), + typeof( DarknightCreeper ), + typeof( RevenantLion ), + typeof( LadyOfTheSnow ), + typeof( RottingCorpse ), + typeof( SkeletalDragon ), + typeof( Lich ) + } + } ); + + public static OppositionGroup FeyAndUndead + { + get{ return m_FeyAndUndead; } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Creature/Paragon.cs b/Scripts/Engines/AI/Creature/Paragon.cs new file mode 100644 index 000000000..a699c6f89 --- /dev/null +++ b/Scripts/Engines/AI/Creature/Paragon.cs @@ -0,0 +1,182 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class Paragon + { + public static double ChestChance = .10; // Chance that a paragon will carry a paragon chest + public static Map[] Maps = new Map[] // Maps that paragons will spawn on + { + Map.Ilshenar + }; + + public static Type[] Artifacts = new Type[] + { + typeof( GoldBricks ), typeof( PhillipsWoodenSteed ), + typeof( AlchemistsBauble ), typeof( ArcticDeathDealer ), + typeof( BlazeOfDeath ), typeof( BowOfTheJukaKing ), + typeof( BurglarsBandana ), typeof( CavortingClub ), + typeof( EnchantedTitanLegBone ), typeof( GwennosHarp ), + typeof( IolosLute ), typeof( LunaLance ), + typeof( NightsKiss ), typeof( NoxRangersHeavyCrossbow ), + typeof( OrcishVisage ), typeof( PolarBearMask ), + typeof( ShieldOfInvulnerability ), typeof( StaffOfPower ), + typeof( VioletCourage ), typeof( HeartOfTheLion ), + typeof( WrathOfTheDryad ), typeof( PixieSwatter ), + typeof( GlovesOfThePugilist ) + }; + + public static int Hue = 0x501; // Paragon hue + + // Buffs + public static double HitsBuff = 5.0; + public static double StrBuff = 1.05; + public static double IntBuff = 1.20; + public static double DexBuff = 1.20; + public static double SkillsBuff = 1.20; + public static double SpeedBuff = 1.20; + public static double FameBuff = 1.40; + public static double KarmaBuff = 1.40; + public static int DamageBuff = 5; + + public static void Convert( BaseCreature bc ) + { + if ( bc.IsParagon ) + return; + + bc.Hue = Hue; + + if ( bc.HitsMaxSeed >= 0 ) + bc.HitsMaxSeed = (int)( bc.HitsMaxSeed * HitsBuff ); + + bc.RawStr = (int)( bc.RawStr * StrBuff ); + bc.RawInt = (int)( bc.RawInt * IntBuff ); + bc.RawDex = (int)( bc.RawDex * DexBuff ); + + bc.Hits = bc.HitsMax; + bc.Mana = bc.ManaMax; + bc.Stam = bc.StamMax; + + for( int i = 0; i < bc.Skills.Length; i++ ) + { + Skill skill = (Skill)bc.Skills[i]; + + if ( skill.Base > 0.0 ) + skill.Base *= SkillsBuff; + } + + bc.PassiveSpeed /= SpeedBuff; + bc.ActiveSpeed /= SpeedBuff; + + bc.DamageMin += DamageBuff; + bc.DamageMax += DamageBuff; + + if ( bc.Fame > 0 ) + bc.Fame = (int)( bc.Fame * FameBuff ); + + if ( bc.Fame > 32000 ) + bc.Fame = 32000; + + // TODO: Mana regeneration rate = Sqrt( buffedFame ) / 4 + + if ( bc.Karma != 0 ) + { + bc.Karma = (int)( bc.Karma * KarmaBuff ); + + if( Math.Abs( bc.Karma ) > 32000 ) + bc.Karma = 32000 * Math.Sign( bc.Karma ); + } + } + + public static void UnConvert( BaseCreature bc ) + { + if ( !bc.IsParagon ) + return; + + bc.Hue = 0; + + if ( bc.HitsMaxSeed >= 0 ) + bc.HitsMaxSeed = (int)( bc.HitsMaxSeed / HitsBuff ); + + bc.RawStr = (int)( bc.RawStr / StrBuff ); + bc.RawInt = (int)( bc.RawInt / IntBuff ); + bc.RawDex = (int)( bc.RawDex / DexBuff ); + + bc.Hits = bc.HitsMax; + bc.Mana = bc.ManaMax; + bc.Stam = bc.StamMax; + + for( int i = 0; i < bc.Skills.Length; i++ ) + { + Skill skill = (Skill)bc.Skills[i]; + + if ( skill.Base > 0.0 ) + skill.Base /= SkillsBuff; + } + + bc.PassiveSpeed *= SpeedBuff; + bc.ActiveSpeed *= SpeedBuff; + + bc.DamageMin -= DamageBuff; + bc.DamageMax -= DamageBuff; + + if ( bc.Fame > 0 ) + bc.Fame = (int)( bc.Fame / FameBuff ); + if ( bc.Karma != 0 ) + bc.Karma = (int)( bc.Karma / KarmaBuff ); + } + + public static bool CheckConvert( BaseCreature bc ) + { + return CheckConvert( bc, bc.Location, bc.Map ); + } + + public static bool CheckConvert( BaseCreature bc, Point3D location, Map m ) + { + if ( !Core.AOS ) + return false; + + if ( Array.IndexOf( Maps, m ) == -1 ) + return false; + + if ( bc is BaseChampion || bc is Harrower || bc is BaseVendor || bc is BaseEscortable || bc is Clone ) + return false; + + int fame = bc.Fame; + + if ( fame > 32000 ) + fame = 32000; + + double chance = 1 / Math.Round( 20.0 - ( fame / 3200 )); + + return ( chance > Utility.RandomDouble() ); + } + + public static bool CheckArtifactChance( Mobile m, BaseCreature bc ) + { + if ( !Core.AOS ) + return false; + + double fame = (double)bc.Fame; + + if ( fame > 32000 ) + fame = 32000; + + double chance = 1 / ( Math.Max( 10, 100 * ( 0.83 - Math.Round( Math.Log( Math.Round( fame / 6000, 3 ) + 0.001, 10 ), 3 ) ) ) * ( 100 - Math.Sqrt( m.Luck ) ) / 100.0 ); + + return chance > Utility.RandomDouble(); + } + + public static void GiveArtifactTo( Mobile m ) + { + Item item = (Item)Activator.CreateInstance( Artifacts[Utility.Random(Artifacts.Length)] ); + + if ( m.AddToBackpack( item ) ) + m.SendMessage( "As a reward for slaying the mighty paragon, an artifact has been placed in your backpack." ); + else + m.SendMessage( "As your backpack is full, your reward for destroying the legendary paragon has been placed at your feet." ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Creature/SpeedInfo.cs b/Scripts/Engines/AI/Creature/SpeedInfo.cs new file mode 100644 index 000000000..dfddfb2c0 --- /dev/null +++ b/Scripts/Engines/AI/Creature/SpeedInfo.cs @@ -0,0 +1,213 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Factions; + +namespace Server +{ + public class SpeedInfo + { + // Should we use the new method of speeds? + private static bool Enabled = true; + + private double m_ActiveSpeed; + private double m_PassiveSpeed; + private Type[] m_Types; + + public double ActiveSpeed + { + get{ return m_ActiveSpeed; } + set{ m_ActiveSpeed = value; } + } + + public double PassiveSpeed + { + get{ return m_PassiveSpeed; } + set{ m_PassiveSpeed = value; } + } + + public Type[] Types + { + get{ return m_Types; } + set{ m_Types = value; } + } + + public SpeedInfo( double activeSpeed, double passiveSpeed, Type[] types ) + { + m_ActiveSpeed = activeSpeed; + m_PassiveSpeed = passiveSpeed; + m_Types = types; + } + + public static bool Contains( object obj ) + { + if ( !Enabled ) + return false; + + if ( m_Table == null ) + LoadTable(); + + SpeedInfo sp = (SpeedInfo)m_Table[obj.GetType()]; + + return ( sp != null ); + } + + public static bool GetSpeeds( object obj, ref double activeSpeed, ref double passiveSpeed ) + { + if ( !Enabled ) + return false; + + if ( m_Table == null ) + LoadTable(); + + SpeedInfo sp = (SpeedInfo)m_Table[obj.GetType()]; + + if ( sp == null ) + return false; + + activeSpeed = sp.ActiveSpeed; + passiveSpeed = sp.PassiveSpeed; + + return true; + } + + private static void LoadTable() + { + m_Table = new Hashtable(); + + for ( int i = 0; i < m_Speeds.Length; ++i ) + { + SpeedInfo info = m_Speeds[i]; + Type[] types = info.Types; + + for ( int j = 0; j < types.Length; ++j ) + m_Table[types[j]] = info; + } + } + + private static Hashtable m_Table; + + private static SpeedInfo[] m_Speeds = new SpeedInfo[] + { + /* Slow */ + new SpeedInfo( 0.3, 0.6, new Type[] + { + typeof( AntLion ), typeof( ArcticOgreLord ), typeof( BogThing ), + typeof( Bogle ), typeof( BoneKnight ), typeof( EarthElemental ), + typeof( Ettin ), typeof( FrostOoze ), typeof( FrostTroll ), + typeof( GazerLarva ), typeof( Ghoul ), typeof( Golem ), + typeof( HeadlessOne ), typeof( Jwilson ), typeof( Mummy ), + typeof( Ogre ), typeof( OgreLord ), typeof( PlagueBeast ), + typeof( Quagmire ), typeof( Rat ), typeof( RottingCorpse ), + typeof( Sewerrat ), typeof( Skeleton ), typeof( Slime ), + typeof( Zombie ), typeof( Walrus ), typeof( RestlessSoul ), + typeof( CrystalElemental ), typeof( DarknightCreeper ), typeof( MoundOfMaggots ), + typeof( Juggernaut ), typeof( Yamandon ), typeof( Serado ) + } ), + /* Fast */ + new SpeedInfo( 0.2, 0.4, new Type[] + { + typeof( LordOaks ), typeof( Silvani ), typeof( AirElemental ), + typeof( AncientWyrm ), typeof( Balron ), typeof( BladeSpirits ), + typeof( DreadSpider ), typeof( Efreet ), typeof( EtherealWarrior ), + typeof( Lich ), typeof( Nightmare ), typeof( OphidianArchmage ), + typeof( OphidianMage ), typeof( OphidianWarrior ), typeof( OphidianMatriarch ), + typeof( OphidianKnight ), typeof( PoisonElemental ), typeof( Revenant ), + typeof( SandVortex ), typeof( SavageRider ), typeof( SavageShaman ), + typeof( SnowElemental ), typeof( WhiteWyrm ), typeof( Wisp ), + typeof( DemonKnight ), typeof( GiantBlackWidow ), typeof( SummonedAirElemental ), + typeof( LesserHiryu ), typeof( Hiryu ), typeof( LadyOfTheSnow ), + typeof( RaiJu ), typeof( Ronin ) + } ), + /* Very Fast */ + new SpeedInfo( 0.175, 0.350, new Type[] + { + typeof( Barracoon ), typeof( Mephitis ), typeof( Neira ), + typeof( Rikktor ), typeof( Semidar ), typeof( EnergyVortex ), + typeof( Beetle ), typeof( Pixie ), typeof( SilverSerpent ), + typeof( VorpalBunny ), typeof( FleshRenderer ), typeof( KhaldunRevenant ), + typeof( FactionDragoon ), typeof( FactionKnight ), typeof( FactionPaladin ), + typeof( FactionHenchman ), typeof( FactionMercenary ), typeof( FactionNecromancer ), + typeof( FactionSorceress ), typeof( FactionWizard ), typeof( FactionBerserker ), + typeof( FactionPaladin ), typeof( Leviathan ), typeof( FireBeetle ), + typeof( FanDancer ), typeof( EliteNinja ) + } ), + /* Medium */ + new SpeedInfo( 0.25, 0.5, new Type[] + { + typeof( ToxicElemental ), typeof( AgapiteElemental ), typeof( Alligator ), + typeof( AncientLich ), typeof( Betrayer ), typeof( Bird ), + typeof( BlackBear ), typeof( BlackSolenInfiltratorQueen ), typeof( BlackSolenInfiltratorWarrior ), + typeof( BlackSolenQueen ), typeof( BlackSolenWarrior ), typeof( BlackSolenWorker ), + typeof( BloodElemental ), typeof( Boar ), typeof( Bogling ), + typeof( BoneMagi ), typeof( Brigand ), typeof( BronzeElemental ), + typeof( BrownBear ), typeof( Bull ), typeof( BullFrog ), + typeof( Cat ), typeof( Centaur ), typeof( ChaosDaemon ), + typeof( Chicken ), typeof( GolemController ), typeof( CopperElemental ), + typeof( CopperElemental ), typeof( Cougar ), typeof( Cow ), + typeof( Cyclops ), typeof( Daemon ), typeof( DeepSeaSerpent ), + typeof( DesertOstard ), typeof( DireWolf ), typeof( Dog ), + typeof( Dolphin ), typeof( Dragon ), typeof( Drake ), + typeof( DullCopperElemental ), typeof( Eagle ), typeof( ElderGazer ), + typeof( EvilMage ), typeof( EvilMageLord ), typeof( Executioner ), + typeof( Savage ), typeof( FireElemental ), typeof( FireGargoyle ), + typeof( FireSteed ), typeof( ForestOstard ), typeof( FrenziedOstard ), + typeof( FrostSpider ), typeof( Gargoyle ), typeof( Gazer ), + typeof( IceSerpent ), typeof( GiantRat ), typeof( GiantSerpent ), + typeof( GiantSpider ), typeof( GiantToad ), typeof( Goat ), + typeof( GoldenElemental ), typeof( Gorilla ), typeof( GreatHart ), + typeof( GreyWolf ), typeof( GrizzlyBear ), typeof( Guardian ), + typeof( Harpy ), typeof( Harrower ), typeof( HellHound ), + typeof( Hind ), typeof( HordeMinion ), typeof( Horse ), + typeof( Horse ), typeof( IceElemental ), typeof( IceFiend ), + typeof( IceSnake ), typeof( Imp ), typeof( JackRabbit ), + typeof( Kirin ), typeof( Kraken ), typeof( PredatorHellCat ), + typeof( LavaLizard ), typeof( LavaSerpent ), typeof( LavaSnake ), + typeof( Lizardman ), typeof( Llama ), typeof( Mongbat ), + typeof( StrongMongbat ), typeof( MountainGoat ), typeof( Orc ), + typeof( OrcBomber ), typeof( OrcBrute ), typeof( OrcCaptain ), + typeof( OrcishLord ), typeof( OrcishMage ), typeof( PackHorse ), + typeof( PackLlama ), typeof( Panther ), typeof( Pig ), + typeof( PlagueSpawn ), typeof( PolarBear ), typeof( Rabbit ), + typeof( Ratman ), typeof( RatmanArcher ), typeof( RatmanMage ), + typeof( RedSolenInfiltratorQueen ), typeof( RedSolenInfiltratorWarrior ), typeof( RedSolenQueen ), + typeof( RedSolenWarrior ), typeof( RedSolenWorker ), typeof( RidableLlama ), + typeof( Ridgeback ), typeof( Scorpion ), typeof( SeaSerpent ), + typeof( SerpentineDragon ), typeof( Shade ), typeof( ShadowIronElemental ), + typeof( ShadowWisp ), typeof( ShadowWyrm ), typeof( Sheep ), + typeof( SilverSteed ), typeof( SkeletalDragon ), typeof( SkeletalMage ), + typeof( SkeletalMount ), typeof( HellCat ), typeof( Snake ), + typeof( SnowLeopard ), typeof( SpectralArmour ), typeof( Spectre ), + typeof( StoneGargoyle ), typeof( StoneHarpy ), typeof( SwampDragon ), + typeof( ScaledSwampDragon ), typeof( SwampTentacle ), typeof( TerathanAvenger ), + typeof( TerathanDrone ), typeof( TerathanMatriarch ), typeof( TerathanWarrior ), + typeof( TimberWolf ), typeof( Titan ), typeof( Troll ), + typeof( Unicorn ), typeof( ValoriteElemental ), typeof( VeriteElemental ), + typeof( CoMWarHorse ), typeof( MinaxWarHorse ), typeof( SLWarHorse ), + typeof( TBWarHorse ), typeof( WaterElemental ), typeof( WhippingVine ), + typeof( WhiteWolf ), typeof( Wraith ), typeof( Wyvern ), + typeof( KhaldunZealot ), typeof( KhaldunSummoner ), typeof( SavageRidgeback ), + typeof( LichLord ), typeof( SkeletalKnight ), typeof( SummonedDaemon ), + typeof( SummonedEarthElemental ), typeof( SummonedWaterElemental ), typeof( SummonedFireElemental ), + typeof( MeerWarrior ), typeof( MeerEternal ), typeof( MeerMage ), + typeof( MeerCaptain ), typeof( JukaLord ), typeof( JukaMage ), + typeof( JukaWarrior ), typeof( AbysmalHorror ), typeof( BoneDemon ), + typeof( Devourer ), typeof( FleshGolem ), typeof( Gibberling ), + typeof( GoreFiend ), typeof( Impaler ), typeof( PatchworkSkeleton ), + typeof( Ravager ), typeof( ShadowKnight ), typeof( SkitteringHopper ), + typeof( Treefellow ), typeof( VampireBat ), typeof( WailingBanshee ), + typeof( WandererOfTheVoid ), typeof( Cursed ), typeof( GrimmochDrummel ), + typeof( LysanderGathenwale ), typeof( MorgBergen ), typeof( ShadowFiend ), + typeof( SpectralArmour ), typeof( TavaraSewel ), typeof( ArcaneDaemon ), + typeof( Doppleganger ), typeof( EnslavedGargoyle ), typeof( ExodusMinion ), + typeof( ExodusOverseer ), typeof( GargoyleDestroyer ), typeof( GargoyleEnforcer ), + typeof( Moloch ), typeof( BakeKitsune ), typeof( DeathwatchBeetleHatchling ), + typeof( Kappa ), typeof( KazeKemono ), typeof( DeathwatchBeetle ), + typeof( TsukiWolf ), typeof( YomotsuElder ), typeof( YomotsuPriest ), + typeof( YomotsuWarrior ), typeof( RevenantLion ), typeof( Oni ), + typeof( RuneBeetle ), typeof( Gaman ), typeof( Crane ) + } ) + }; + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Targets/AIControlMobileTarget.cs b/Scripts/Engines/AI/Targets/AIControlMobileTarget.cs new file mode 100644 index 000000000..117e31138 --- /dev/null +++ b/Scripts/Engines/AI/Targets/AIControlMobileTarget.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Targeting; +using Server.Mobiles; +using System.Collections; + +namespace Server.Targets +{ + public class AIControlMobileTarget : Target + { + private ArrayList m_List; + private OrderType m_Order; + + public OrderType Order + { + get + { + return m_Order; + } + } + + public AIControlMobileTarget( BaseAI ai, OrderType order ) : base( -1, false, ( order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None ) ) + { + m_List = new ArrayList(); + m_Order = order; + + AddAI( ai ); + } + + public void AddAI( BaseAI ai ) + { + if ( !m_List.Contains( ai ) ) + m_List.Add( ai ); + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + for ( int i = 0; i < m_List.Count; ++i ) + ((BaseAI)m_List[i]).EndPickTarget( from, (Mobile)o, m_Order ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Team/Team.cs b/Scripts/Engines/AI/Team/Team.cs new file mode 100644 index 000000000..61cb1b90c --- /dev/null +++ b/Scripts/Engines/AI/Team/Team.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +/* + * NPC could use a team objets.. + * + * -List of members + * -List of ennemy teams + * -List of ally team + * -Team could be set automaticaly at mobile creation by the region system + * -Team could be the owner of a common timer instead of one by creature + * + * + * + */ + +namespace Server.Mobiles +{ + public class Team + { + //private ArrayList m_arAlly; + //private ArrayList m_arFoe; + + //private ArrayList m_arMember; + + public static void Initialize() + { + + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/AI/Team/TeamRegistry.cs b/Scripts/Engines/AI/Team/TeamRegistry.cs new file mode 100644 index 000000000..3b74f4ab0 --- /dev/null +++ b/Scripts/Engines/AI/Team/TeamRegistry.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +/* + * NPC could use a faction objets.. + * + * -List of members + * -List of ennemy factions + * -List of neutral factions + * -List of ally faction + * -Team could be set automaticaly at mobile creation by the region system + * -Team could be the owner of a common timer instead of one by creature + * + * + * + */ + +namespace Server.Mobiles +{ + public class TeamRegistry + { + //private ArrayList m_arTeam; + + public static void Initialize() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BOBFilter.cs b/Scripts/Engines/BulkOrders/Books/BOBFilter.cs new file mode 100644 index 000000000..aa02c36e2 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BOBFilter.cs @@ -0,0 +1,88 @@ +using System; + +namespace Server.Engines.BulkOrders +{ + public class BOBFilter + { + private int m_Type; + private int m_Quality; + private int m_Material; + private int m_Quantity; + + public bool IsDefault + { + get{ return ( m_Type == 0 && m_Quality == 0 && m_Material == 0 && m_Quantity == 0 ); } + } + + public void Clear() + { + m_Type = 0; + m_Quality = 0; + m_Material = 0; + m_Quantity = 0; + } + + public int Type + { + get{ return m_Type; } + set{ m_Type = value; } + } + + public int Quality + { + get{ return m_Quality; } + set{ m_Quality = value; } + } + + public int Material + { + get{ return m_Material; } + set{ m_Material = value; } + } + + public int Quantity + { + get{ return m_Quantity; } + set{ m_Quantity = value; } + } + + public BOBFilter() + { + } + + public BOBFilter( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Type = reader.ReadEncodedInt(); + m_Quality = reader.ReadEncodedInt(); + m_Material = reader.ReadEncodedInt(); + m_Quantity = reader.ReadEncodedInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + if ( IsDefault ) + { + writer.WriteEncodedInt( 0 ); // version + } + else + { + writer.WriteEncodedInt( 1 ); // version + + writer.WriteEncodedInt( m_Type ); + writer.WriteEncodedInt( m_Quality ); + writer.WriteEncodedInt( m_Material ); + writer.WriteEncodedInt( m_Quantity ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BOBFilterGump.cs b/Scripts/Engines/BulkOrders/Books/BOBFilterGump.cs new file mode 100644 index 000000000..a7a7baf55 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BOBFilterGump.cs @@ -0,0 +1,215 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.BulkOrders +{ + public class BOBFilterGump : Gump + { + private PlayerMobile m_From; + private BulkOrderBook m_Book; + + private const int LabelColor = 0x7FFF; + + private static int[,] m_MaterialFilters = new int[,] + { + { 1044067, 1 }, // Blacksmithy + { 1062226, 3 }, // Iron + { 1018332, 4 }, // Dull Copper + { 1018333, 5 }, // Shadow Iron + { 1018334, 6 }, // Copper + { 1018335, 7 }, // Bronze + + { 0, 0 }, // --Blank-- + { 1018336, 8 }, // Golden + { 1018337, 9 }, // Agapite + { 1018338, 10 }, // Verite + { 1018339, 11 }, // Valorite + { 0, 0 }, // --Blank-- + + { 1044094, 2 }, // Tailoring + { 1044286, 12 }, // Cloth + { 1062235, 13 }, // Leather + { 1062236, 14 }, // Spined + { 1062237, 15 }, // Horned + { 1062238, 16 } // Barbed + }; + + private static int[,] m_TypeFilters = new int[,] + { + { 1062229, 0 }, // All + { 1062224, 1 }, // Small + { 1062225, 2 } // Large + }; + + private static int[,] m_QualityFilters = new int[,] + { + { 1062229, 0 }, // All + { 1011542, 1 }, // Normal + { 1060636, 2 } // Exceptional + }; + + private static int[,] m_AmountFilters = new int[,] + { + { 1062229, 0 }, // All + { 1049706, 1 }, // 10 + { 1016007, 2 }, // 15 + { 1062239, 3 } // 20 + }; + + private static int[][,] m_Filters = new int[][,] + { + m_TypeFilters, + m_QualityFilters, + m_MaterialFilters, + m_AmountFilters + }; + + private static int[] m_XOffsets_Type = new int[]{ 0, 75, 170 }; + private static int[] m_XOffsets_Quality = new int[]{ 0, 75, 170 }; + private static int[] m_XOffsets_Amount = new int[]{ 0, 75, 180, 275 }; + private static int[] m_XOffsets_Material = new int[]{ 0, 105, 210, 305, 390, 485 }; + + private static int[] m_XWidths_Small = new int[]{ 50, 50, 70, 50 }; + private static int[] m_XWidths_Large = new int[]{ 80, 50, 50, 50, 50, 50 }; + + private void AddFilterList( int x, int y, int[] xOffsets, int yOffset, int[,] filters, int[] xWidths, int filterValue, int filterIndex ) + { + for ( int i = 0; i < filters.GetLength( 0 ); ++i ) + { + int number = filters[i, 0]; + + if ( number == 0 ) + continue; + + bool isSelected = ( filters[i, 1] == filterValue ); + + if ( !isSelected && (i % xOffsets.Length) == 0 ) + isSelected = ( filterValue == 0 ); + + AddHtmlLocalized( x + 35 + xOffsets[i % xOffsets.Length], y + ((i / xOffsets.Length) * yOffset), xWidths[i % xOffsets.Length], 32, number, isSelected ? 16927 : LabelColor, false, false ); + AddButton( x + xOffsets[i % xOffsets.Length], y + ((i / xOffsets.Length) * yOffset), 4005, 4007, 4 + filterIndex + (i * 4), GumpButtonType.Reply, 0 ); + } + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + BOBFilter f = ( m_From.UseOwnFilter ? m_From.BOBFilter : m_Book.Filter ); + + int index = info.ButtonID; + + switch ( index ) + { + case 0: // Apply + { + m_From.SendGump( new BOBGump( m_From, m_Book ) ); + + break; + } + case 1: // Set Book Filter + { + m_From.UseOwnFilter = false; + m_From.SendGump( new BOBFilterGump( m_From, m_Book ) ); + + break; + } + case 2: // Set Your Filter + { + m_From.UseOwnFilter = true; + m_From.SendGump( new BOBFilterGump( m_From, m_Book ) ); + + break; + } + case 3: // Clear Filter + { + f.Clear(); + m_From.SendGump( new BOBFilterGump( m_From, m_Book ) ); + + break; + } + default: + { + index -= 4; + + int type = index % 4; + index /= 4; + + if ( type >= 0 && type < m_Filters.Length ) + { + int[,] filters = m_Filters[type]; + + if ( index >= 0 && index < filters.GetLength( 0 ) ) + { + if ( filters[index, 0] == 0 ) + break; + + switch ( type ) + { + case 0: f.Type = filters[index, 1]; break; + case 1: f.Quality = filters[index, 1]; break; + case 2: f.Material = filters[index, 1]; break; + case 3: f.Quantity = filters[index, 1]; break; + } + + m_From.SendGump( new BOBFilterGump( m_From, m_Book ) ); + } + } + + break; + } + } + } + + public BOBFilterGump( PlayerMobile from, BulkOrderBook book ) : base( 12, 24 ) + { + from.CloseGump( typeof( BOBGump ) ); + from.CloseGump( typeof( BOBFilterGump ) ); + + m_From = from; + m_Book = book; + + BOBFilter f = ( from.UseOwnFilter ? from.BOBFilter : book.Filter ); + + AddPage( 0 ); + + AddBackground( 10, 10, 600, 439, 5054 ); + + AddImageTiled( 18, 20, 583, 420, 2624 ); + AddAlphaRegion( 18, 20, 583, 420 ); + + AddImage( 5, 5, 10460 ); + AddImage( 585, 5, 10460 ); + AddImage( 5, 424, 10460 ); + AddImage( 585, 424, 10460 ); + + AddHtmlLocalized( 270, 32, 200, 32, 1062223, LabelColor, false, false ); // Filter Preference + + AddHtmlLocalized( 26, 64, 120, 32, 1062228, LabelColor, false, false ); // Bulk Order Type + AddFilterList( 25, 96, m_XOffsets_Type, 40, m_TypeFilters, m_XWidths_Small, f.Type, 0 ); + + AddHtmlLocalized( 320, 64, 50, 32, 1062215, LabelColor, false, false ); // Quality + AddFilterList( 320, 96, m_XOffsets_Quality, 40, m_QualityFilters, m_XWidths_Small, f.Quality, 1 ); + + AddHtmlLocalized( 26, 160, 120, 32, 1062232, LabelColor, false, false ); // Material Type + AddFilterList( 25, 192, m_XOffsets_Material, 40, m_MaterialFilters, m_XWidths_Large, f.Material, 2 ); + + AddHtmlLocalized( 26, 320, 120, 32, 1062217, LabelColor, false, false ); // Amount + AddFilterList( 25, 352, m_XOffsets_Amount, 40, m_AmountFilters, m_XWidths_Small, f.Quantity, 3 ); + + AddHtmlLocalized( 75, 416, 120, 32, 1062477, ( from.UseOwnFilter ? LabelColor : 16927 ), false, false ); // Set Book Filter + AddButton( 40, 416, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 235, 416, 120, 32, 1062478, ( from.UseOwnFilter ? 16927 : LabelColor ), false, false ); // Set Your Filter + AddButton( 200, 416, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 405, 416, 120, 32, 1062231, LabelColor, false, false ); // Clear Filter + AddButton( 370, 416, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 540, 416, 50, 32, 1011046, LabelColor, false, false ); // APPLY + AddButton( 505, 416, 4017, 4018, 0, GumpButtonType.Reply, 0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BOBGump.cs b/Scripts/Engines/BulkOrders/Books/BOBGump.cs new file mode 100644 index 000000000..25339b23c --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BOBGump.cs @@ -0,0 +1,611 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Prompts; + +namespace Server.Engines.BulkOrders +{ + public class BOBGump : Gump + { + private PlayerMobile m_From; + private BulkOrderBook m_Book; + private ArrayList m_List; + + private int m_Page; + + private const int LabelColor = 0x7FFF; + + public Item Reconstruct( object obj ) + { + Item item = null; + + if ( obj is BOBLargeEntry ) + item = ((BOBLargeEntry)obj).Reconstruct(); + else if ( obj is BOBSmallEntry ) + item = ((BOBSmallEntry)obj).Reconstruct(); + + return item; + } + + public bool CheckFilter( object obj ) + { + if ( obj is BOBLargeEntry ) + { + BOBLargeEntry e = (BOBLargeEntry)obj; + + return CheckFilter( e.Material, e.AmountMax, true, e.RequireExceptional, e.DeedType, ( e.Entries.Length > 0 ? e.Entries[0].ItemType : null ) ); + } + else if ( obj is BOBSmallEntry ) + { + BOBSmallEntry e = (BOBSmallEntry)obj; + + return CheckFilter( e.Material, e.AmountMax, false, e.RequireExceptional, e.DeedType, e.ItemType ); + } + + return false; + } + + public bool CheckFilter( BulkMaterialType mat, int amountMax, bool isLarge, bool reqExc, BODType deedType, Type itemType ) + { + BOBFilter f = ( m_From.UseOwnFilter ? m_From.BOBFilter : m_Book.Filter ); + + if ( f.IsDefault ) + return true; + + if ( f.Quality == 1 && reqExc ) + return false; + else if ( f.Quality == 2 && !reqExc ) + return false; + + if ( f.Quantity == 1 && amountMax != 10 ) + return false; + else if ( f.Quantity == 2 && amountMax != 15 ) + return false; + else if ( f.Quantity == 3 && amountMax != 20 ) + return false; + + if ( f.Type == 1 && isLarge ) + return false; + else if ( f.Type == 2 && !isLarge ) + return false; + + switch ( f.Material ) + { + default: + case 0: return true; + case 1: return ( deedType == BODType.Smith ); + case 2: return ( deedType == BODType.Tailor ); + + case 3: return ( mat == BulkMaterialType.None && BGTClassifier.Classify( deedType, itemType ) == BulkGenericType.Iron ); + case 4: return ( mat == BulkMaterialType.DullCopper ); + case 5: return ( mat == BulkMaterialType.ShadowIron ); + case 6: return ( mat == BulkMaterialType.Copper ); + case 7: return ( mat == BulkMaterialType.Bronze ); + case 8: return ( mat == BulkMaterialType.Gold ); + case 9: return ( mat == BulkMaterialType.Agapite ); + case 10: return ( mat == BulkMaterialType.Verite ); + case 11: return ( mat == BulkMaterialType.Valorite ); + + case 12: return ( mat == BulkMaterialType.None && BGTClassifier.Classify( deedType, itemType ) == BulkGenericType.Cloth ); + case 13: return ( mat == BulkMaterialType.None && BGTClassifier.Classify( deedType, itemType ) == BulkGenericType.Leather ); + case 14: return ( mat == BulkMaterialType.Spined ); + case 15: return ( mat == BulkMaterialType.Horned ); + case 16: return ( mat == BulkMaterialType.Barbed ); + } + } + + public int GetIndexForPage( int page ) + { + int index = 0; + + while ( page-- > 0 ) + index += GetCountForIndex( index ); + + return index; + } + + public int GetCountForIndex( int index ) + { + int slots = 0; + int count = 0; + + ArrayList list = m_List; + + for ( int i = index; i >= 0 && i < list.Count; ++i ) + { + object obj = list[i]; + + if ( CheckFilter( obj ) ) + { + int add; + + if ( obj is BOBLargeEntry ) + add = ((BOBLargeEntry)obj).Entries.Length; + else + add = 1; + + if ( (slots + add) > 10 ) + break; + + slots += add; + } + + ++count; + } + + return count; + } + + public object GetMaterialName( BulkMaterialType mat, BODType type, Type itemType ) + { + switch ( type ) + { + case BODType.Smith: + { + switch ( mat ) + { + case BulkMaterialType.None: return 1062226; + case BulkMaterialType.DullCopper: return 1018332; + case BulkMaterialType.ShadowIron: return 1018333; + case BulkMaterialType.Copper: return 1018334; + case BulkMaterialType.Bronze: return 1018335; + case BulkMaterialType.Gold: return 1018336; + case BulkMaterialType.Agapite: return 1018337; + case BulkMaterialType.Verite: return 1018338; + case BulkMaterialType.Valorite: return 1018339; + } + + break; + } + case BODType.Tailor: + { + switch ( mat ) + { + case BulkMaterialType.None: + { + if ( itemType.IsSubclassOf( typeof( BaseArmor ) ) || itemType.IsSubclassOf( typeof( BaseShoes ) ) ) + return 1062235; + + return 1044286; + } + case BulkMaterialType.Spined: return 1062236; + case BulkMaterialType.Horned: return 1062237; + case BulkMaterialType.Barbed: return 1062238; + } + + break; + } + } + + return "Invalid"; + } + + public BOBGump( PlayerMobile from, BulkOrderBook book ) : this( from, book, 0, null ) + { + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + int index = info.ButtonID; + + switch ( index ) + { + case 0: // EXIT + { + break; + } + case 1: // Set Filter + { + m_From.SendGump( new BOBFilterGump( m_From, m_Book ) ); + + break; + } + case 2: // Previous page + { + if ( m_Page > 0 ) + m_From.SendGump( new BOBGump( m_From, m_Book, m_Page - 1, m_List ) ); + + return; + } + case 3: // Next page + { + if ( GetIndexForPage( m_Page + 1 ) < m_List.Count ) + m_From.SendGump( new BOBGump( m_From, m_Book, m_Page + 1, m_List ) ); + + break; + } + case 4: // Price all + { + if ( m_Book.IsChildOf( m_From.Backpack ) ) + { + m_From.Prompt = new SetPricePrompt( m_Book, null, m_Page, m_List ); + m_From.SendMessage( "Type in a price for all deeds in the book:" ); + } + + break; + } + default: + { + bool canDrop = m_Book.IsChildOf( m_From.Backpack ); + bool canPrice = canDrop || (m_Book.RootParent is PlayerVendor); + + index -= 5; + + int type = index % 2; + index /= 2; + + if ( index < 0 || index >= m_List.Count ) + break; + + object obj = m_List[index]; + + if ( !m_Book.Entries.Contains( obj ) ) + { + m_From.SendLocalizedMessage( 1062382 ); // The deed selected is not available. + break; + } + + if ( type == 0 ) // Drop + { + if ( m_Book.IsChildOf( m_From.Backpack ) ) + { + Item item = Reconstruct( obj ); + + if ( item != null ) + { + m_From.AddToBackpack( item ); + m_From.SendLocalizedMessage( 1045152 ); // The bulk order deed has been placed in your backpack. + + m_Book.Entries.Remove( obj ); + m_Book.InvalidateProperties(); + + if ( m_Book.Entries.Count > 0 ) + m_From.SendGump( new BOBGump( m_From, m_Book, 0, null ) ); + else + m_From.SendLocalizedMessage( 1062381 ); // The book is empty. + } + else + { + m_From.SendMessage( "Internal error. The bulk order deed could not be reconstructed." ); + } + } + } + else // Set Price | Buy + { + if ( m_Book.IsChildOf( m_From.Backpack ) ) + { + m_From.Prompt = new SetPricePrompt( m_Book, obj, m_Page, m_List ); + m_From.SendLocalizedMessage( 1062383 ); // Type in a price for the deed: + } + else if ( m_Book.RootParent is PlayerVendor ) + { + PlayerVendor pv = (PlayerVendor)m_Book.RootParent; + + VendorItem vi = pv.GetVendorItem( m_Book ); + + int price = 0; + + if ( vi != null && !vi.IsForSale ) + { + if ( obj is BOBLargeEntry ) + price = ((BOBLargeEntry)obj).Price; + else if ( obj is BOBSmallEntry ) + price = ((BOBSmallEntry)obj).Price; + } + + if ( price == 0 ) + m_From.SendLocalizedMessage( 1062382 ); // The deed selected is not available. + else + m_From.SendGump( new BODBuyGump( m_From, m_Book, obj, price ) ); + } + } + + break; + } + } + } + + private class SetPricePrompt : Prompt + { + private BulkOrderBook m_Book; + private object m_Object; + private int m_Page; + private ArrayList m_List; + + public SetPricePrompt( BulkOrderBook book, object obj, int page, ArrayList list ) + { + m_Book = book; + m_Object = obj; + m_Page = page; + m_List = list; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Object != null && !m_Book.Entries.Contains( m_Object ) ) + { + from.SendLocalizedMessage( 1062382 ); // The deed selected is not available. + return; + } + + int price = Utility.ToInt32( text ); + + if ( price < 0 || price > 250000000 ) + { + from.SendLocalizedMessage( 1062390 ); // The price you requested is outrageous! + } + else if ( m_Object == null ) + { + for ( int i = 0; i < m_List.Count; ++i ) + { + object obj = m_List[i]; + + if ( !m_Book.Entries.Contains( obj ) ) + continue; + + if ( obj is BOBLargeEntry ) + ((BOBLargeEntry)obj).Price = price; + else if ( obj is BOBSmallEntry ) + ((BOBSmallEntry)obj).Price = price; + } + + from.SendMessage( "Deed prices set." ); + + if ( from is PlayerMobile ) + from.SendGump( new BOBGump( (PlayerMobile)from, m_Book, m_Page, m_List ) ); + } + else if ( m_Object is BOBLargeEntry ) + { + ((BOBLargeEntry)m_Object).Price = price; + + from.SendLocalizedMessage( 1062384 ); // Deed price set. + + if ( from is PlayerMobile ) + from.SendGump( new BOBGump( (PlayerMobile)from, m_Book, m_Page, m_List ) ); + } + else if ( m_Object is BOBSmallEntry ) + { + ((BOBSmallEntry)m_Object).Price = price; + + from.SendLocalizedMessage( 1062384 ); // Deed price set. + + if ( from is PlayerMobile ) + from.SendGump( new BOBGump( (PlayerMobile)from, m_Book, m_Page, m_List ) ); + } + } + } + + public BOBGump( PlayerMobile from, BulkOrderBook book, int page, ArrayList list ) : base( 12, 24 ) + { + from.CloseGump( typeof( BOBGump ) ); + from.CloseGump( typeof( BOBFilterGump ) ); + + m_From = from; + m_Book = book; + m_Page = page; + + if ( list == null ) + { + list = new ArrayList( book.Entries.Count ); + + for ( int i = 0; i < book.Entries.Count; ++i ) + { + object obj = book.Entries[i]; + + if ( CheckFilter( obj ) ) + list.Add( obj ); + } + } + + m_List = list; + + int index = GetIndexForPage( page ); + int count = GetCountForIndex( index ); + + int tableIndex = 0; + + PlayerVendor pv = book.RootParent as PlayerVendor; + + bool canDrop = book.IsChildOf( from.Backpack ); + bool canBuy = ( pv != null ); + bool canPrice = ( canDrop || canBuy ); + + if ( canBuy ) + { + VendorItem vi = pv.GetVendorItem( book ); + + canBuy = ( vi != null && !vi.IsForSale ); + } + + int width = 600; + + if ( !canPrice ) + width = 516; + + X = (624 - width) / 2; + + AddPage( 0 ); + + AddBackground( 10, 10, width, 439, 5054 ); + AddImageTiled( 18, 20, width - 17, 420, 2624 ); + + if ( canPrice ) + { + AddImageTiled( 573, 64, 24, 352, 200 ); + AddImageTiled( 493, 64, 78, 352, 1416 ); + } + + if ( canDrop ) + AddImageTiled( 24, 64, 32, 352, 1416 ); + + AddImageTiled( 58, 64, 36, 352, 200 ); + AddImageTiled( 96, 64, 133, 352, 1416 ); + AddImageTiled( 231, 64, 80, 352, 200 ); + AddImageTiled( 313, 64, 100, 352, 1416 ); + AddImageTiled( 415, 64, 76, 352, 200 ); + + for ( int i = index; i < (index + count) && i >= 0 && i < list.Count; ++i ) + { + object obj = list[i]; + + if ( !CheckFilter( obj ) ) + continue; + + AddImageTiled( 24, 94 + (tableIndex * 32), canPrice ? 573 : 489, 2, 2624 ); + + if ( obj is BOBLargeEntry ) + tableIndex += ((BOBLargeEntry)obj).Entries.Length; + else if ( obj is BOBSmallEntry ) + ++tableIndex; + } + + AddAlphaRegion( 18, 20, width - 17, 420 ); + AddImage( 5, 5, 10460 ); + AddImage( width - 15, 5, 10460 ); + AddImage( 5, 424, 10460 ); + AddImage( width - 15, 424, 10460 ); + + AddHtmlLocalized( canPrice ? 266 : 224, 32, 200, 32, 1062220, LabelColor, false, false ); // Bulk Order Book + AddHtmlLocalized( 63, 64, 200, 32, 1062213, LabelColor, false, false ); // Type + AddHtmlLocalized( 147, 64, 200, 32, 1062214, LabelColor, false, false ); // Item + AddHtmlLocalized( 246, 64, 200, 32, 1062215, LabelColor, false, false ); // Quality + AddHtmlLocalized( 336, 64, 200, 32, 1062216, LabelColor, false, false ); // Material + AddHtmlLocalized( 429, 64, 200, 32, 1062217, LabelColor, false, false ); // Amount + + AddButton( 35, 32, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 70, 32, 200, 32, 1062476, LabelColor, false, false ); // Set Filter + + BOBFilter f = ( from.UseOwnFilter ? from.BOBFilter : book.Filter ); + + if ( f.IsDefault ) + AddHtmlLocalized( canPrice ? 470 : 386, 32, 120, 32, 1062475, 16927, false, false ); // Using No Filter + else if ( from.UseOwnFilter ) + AddHtmlLocalized( canPrice ? 470 : 386, 32, 120, 32, 1062451, 16927, false, false ); // Using Your Filter + else + AddHtmlLocalized( canPrice ? 470 : 386, 32, 120, 32, 1062230, 16927, false, false ); // Using Book Filter + + AddButton( 375, 416, 4017, 4018, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 410, 416, 120, 20, 1011441, LabelColor, false, false ); // EXIT + + if ( canDrop ) + AddHtmlLocalized( 26, 64, 50, 32, 1062212, LabelColor, false, false ); // Drop + + if ( canPrice ) + { + AddHtmlLocalized( 516, 64, 200, 32, 1062218, LabelColor, false, false ); // Price + + if ( canBuy ) + { + AddHtmlLocalized( 576, 64, 200, 32, 1062219, LabelColor, false, false ); // Buy + } + else + { + AddHtmlLocalized( 576, 64, 200, 32, 1062227, LabelColor, false, false ); // Set + + AddButton( 450, 416, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + AddHtml( 485, 416, 120, 20, "Price all", false, false ); + } + } + + tableIndex = 0; + + if ( page > 0 ) + { + AddButton( 75, 416, 4014, 4016, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 110, 416, 150, 20, 1011067, LabelColor, false, false ); // Previous page + } + + if ( GetIndexForPage( page + 1 ) < list.Count ) + { + AddButton( 225, 416, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 260, 416, 150, 20, 1011066, LabelColor, false, false ); // Next page + } + + for ( int i = index; i < (index + count) && i >= 0 && i < list.Count; ++i ) + { + object obj = list[i]; + + if ( !CheckFilter( obj ) ) + continue; + + if ( obj is BOBLargeEntry ) + { + BOBLargeEntry e = (BOBLargeEntry)obj; + + int y = 96 + (tableIndex * 32); + + if ( canDrop ) + AddButton( 35, y + 2, 5602, 5606, 5 + (i * 2), GumpButtonType.Reply, 0 ); + + if ( canDrop || (canBuy && e.Price > 0) ) + { + AddButton( 579, y + 2, 2117, 2118, 6 + (i * 2), GumpButtonType.Reply, 0 ); + AddLabel( 495, y, 1152, e.Price.ToString() ); + } + + AddHtmlLocalized( 61, y, 50, 32, 1062225, LabelColor, false, false ); // Large + + for ( int j = 0; j < e.Entries.Length; ++j ) + { + BOBLargeSubEntry sub = e.Entries[j]; + + AddHtmlLocalized( 103, y, 130, 32, sub.Number, LabelColor, false, false ); + + if ( e.RequireExceptional ) + AddHtmlLocalized( 235, y, 80, 20, 1060636, LabelColor, false, false ); // exceptional + else + AddHtmlLocalized( 235, y, 80, 20, 1011542, LabelColor, false, false ); // normal + + object name = GetMaterialName( e.Material, e.DeedType, sub.ItemType ); + + if ( name is int ) + AddHtmlLocalized( 316, y, 100, 20, (int)name, LabelColor, false, false ); + else if ( name is string ) + AddLabel( 316, y, 1152, (string)name ); + + AddLabel( 421, y, 1152, String.Format( "{0} / {1}", sub.AmountCur, e.AmountMax ) ); + + ++tableIndex; + y += 32; + } + } + else if ( obj is BOBSmallEntry ) + { + BOBSmallEntry e = (BOBSmallEntry)obj; + + int y = 96 + (tableIndex++ * 32); + + if ( canDrop ) + AddButton( 35, y + 2, 5602, 5606, 5 + (i * 2), GumpButtonType.Reply, 0 ); + + if ( canDrop || (canBuy && e.Price > 0) ) + { + AddButton( 579, y + 2, 2117, 2118, 6 + (i * 2), GumpButtonType.Reply, 0 ); + AddLabel( 495, y, 1152, e.Price.ToString() ); + } + + AddHtmlLocalized( 61, y, 50, 32, 1062224, LabelColor, false, false ); // Small + + AddHtmlLocalized( 103, y, 130, 32, e.Number, LabelColor, false, false ); + + if ( e.RequireExceptional ) + AddHtmlLocalized( 235, y, 80, 20, 1060636, LabelColor, false, false ); // exceptional + else + AddHtmlLocalized( 235, y, 80, 20, 1011542, LabelColor, false, false ); // normal + + object name = GetMaterialName( e.Material, e.DeedType, e.ItemType ); + + if ( name is int ) + AddHtmlLocalized( 316, y, 100, 20, (int)name, LabelColor, false, false ); + else if ( name is string ) + AddLabel( 316, y, 1152, (string)name ); + + AddLabel( 421, y, 1152, String.Format( "{0} / {1}", e.AmountCur, e.AmountMax ) ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BOBLargeEntry.cs b/Scripts/Engines/BulkOrders/Books/BOBLargeEntry.cs new file mode 100644 index 000000000..d1e02c160 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BOBLargeEntry.cs @@ -0,0 +1,110 @@ +using System; + +namespace Server.Engines.BulkOrders +{ + public class BOBLargeEntry + { + private bool m_RequireExceptional; + private BODType m_DeedType; + private BulkMaterialType m_Material; + private int m_AmountMax; + private int m_Price; + private BOBLargeSubEntry[] m_Entries; + + public bool RequireExceptional{ get{ return m_RequireExceptional; } } + public BODType DeedType{ get{ return m_DeedType; } } + public BulkMaterialType Material{ get{ return m_Material; } } + public int AmountMax{ get{ return m_AmountMax; } } + public int Price{ get{ return m_Price; } set{ m_Price = value; } } + public BOBLargeSubEntry[] Entries{ get{ return m_Entries; } } + + public Item Reconstruct() + { + LargeBOD bod = null; + + if ( m_DeedType == BODType.Smith ) + bod = new LargeSmithBOD( m_AmountMax, m_RequireExceptional, m_Material, ReconstructEntries() ); + else if ( m_DeedType == BODType.Tailor ) + bod = new LargeTailorBOD( m_AmountMax, m_RequireExceptional, m_Material, ReconstructEntries() ); + + for ( int i = 0; bod != null && i < bod.Entries.Length; ++i ) + bod.Entries[i].Owner = bod; + + return bod; + } + + private LargeBulkEntry[] ReconstructEntries() + { + LargeBulkEntry[] entries = new LargeBulkEntry[m_Entries.Length]; + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + entries[i] = new LargeBulkEntry( null, new SmallBulkEntry( m_Entries[i].ItemType, m_Entries[i].Number, m_Entries[i].Graphic ) ); + entries[i].Amount = m_Entries[i].AmountCur; + } + + return entries; + } + + public BOBLargeEntry( LargeBOD bod ) + { + m_RequireExceptional = bod.RequireExceptional; + + if ( bod is LargeTailorBOD ) + m_DeedType = BODType.Tailor; + else if ( bod is LargeSmithBOD ) + m_DeedType = BODType.Smith; + + m_Material = bod.Material; + m_AmountMax = bod.AmountMax; + + m_Entries = new BOBLargeSubEntry[bod.Entries.Length]; + + for ( int i = 0; i < m_Entries.Length; ++i ) + m_Entries[i] = new BOBLargeSubEntry( bod.Entries[i] ); + } + + public BOBLargeEntry( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_RequireExceptional = reader.ReadBool(); + + m_DeedType = (BODType)reader.ReadEncodedInt(); + + m_Material = (BulkMaterialType)reader.ReadEncodedInt(); + m_AmountMax = reader.ReadEncodedInt(); + m_Price = reader.ReadEncodedInt(); + + m_Entries = new BOBLargeSubEntry[reader.ReadEncodedInt()]; + + for ( int i = 0; i < m_Entries.Length; ++i ) + m_Entries[i] = new BOBLargeSubEntry( reader ); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (bool) m_RequireExceptional ); + + writer.WriteEncodedInt( (int) m_DeedType ); + writer.WriteEncodedInt( (int) m_Material ); + writer.WriteEncodedInt( (int) m_AmountMax ); + writer.WriteEncodedInt( (int) m_Price ); + + writer.WriteEncodedInt( (int) m_Entries.Length ); + + for ( int i = 0; i < m_Entries.Length; ++i ) + m_Entries[i].Serialize( writer ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs b/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs new file mode 100644 index 000000000..715154eb3 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BOBLargeSubEntry.cs @@ -0,0 +1,58 @@ +using System; + +namespace Server.Engines.BulkOrders +{ + public class BOBLargeSubEntry + { + private Type m_ItemType; + private int m_AmountCur; + private int m_Number; + private int m_Graphic; + + public Type ItemType{ get{ return m_ItemType; } } + public int AmountCur{ get{ return m_AmountCur; } } + public int Number{ get{ return m_Number; } } + public int Graphic{ get{ return m_Graphic; } } + + public BOBLargeSubEntry( LargeBulkEntry lbe ) + { + m_ItemType = lbe.Details.Type; + m_AmountCur = lbe.Amount; + m_Number = lbe.Details.Number; + m_Graphic = lbe.Details.Graphic; + } + + public BOBLargeSubEntry( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + string type = reader.ReadString(); + + if ( type != null ) + m_ItemType = ScriptCompiler.FindTypeByFullName( type ); + + m_AmountCur = reader.ReadEncodedInt(); + m_Number = reader.ReadEncodedInt(); + m_Graphic = reader.ReadEncodedInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( m_ItemType == null ? null : m_ItemType.FullName ); + + writer.WriteEncodedInt( (int) m_AmountCur ); + writer.WriteEncodedInt( (int) m_Number ); + writer.WriteEncodedInt( (int) m_Graphic ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs b/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs new file mode 100644 index 000000000..7f3b76550 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BOBSmallEntry.cs @@ -0,0 +1,101 @@ +using System; + +namespace Server.Engines.BulkOrders +{ + public class BOBSmallEntry + { + private Type m_ItemType; + private bool m_RequireExceptional; + private BODType m_DeedType; + private BulkMaterialType m_Material; + private int m_AmountCur, m_AmountMax; + private int m_Number; + private int m_Graphic; + private int m_Price; + + public Type ItemType{ get{ return m_ItemType; } } + public bool RequireExceptional{ get{ return m_RequireExceptional; } } + public BODType DeedType{ get{ return m_DeedType; } } + public BulkMaterialType Material{ get{ return m_Material; } } + public int AmountCur{ get{ return m_AmountCur; } } + public int AmountMax{ get{ return m_AmountMax; } } + public int Number{ get{ return m_Number; } } + public int Graphic{ get{ return m_Graphic; } } + public int Price{ get{ return m_Price; } set{ m_Price = value; } } + + public Item Reconstruct() + { + SmallBOD bod = null; + + if ( m_DeedType == BODType.Smith ) + bod = new SmallSmithBOD( m_AmountCur, m_AmountMax, m_ItemType, m_Number, m_Graphic, m_RequireExceptional, m_Material ); + else if ( m_DeedType == BODType.Tailor ) + bod = new SmallTailorBOD( m_AmountCur, m_AmountMax, m_ItemType, m_Number, m_Graphic, m_RequireExceptional, m_Material ); + + return bod; + } + + public BOBSmallEntry( SmallBOD bod ) + { + m_ItemType = bod.Type; + m_RequireExceptional = bod.RequireExceptional; + + if ( bod is SmallTailorBOD ) + m_DeedType = BODType.Tailor; + else if ( bod is SmallSmithBOD ) + m_DeedType = BODType.Smith; + + m_Material = bod.Material; + m_AmountCur = bod.AmountCur; + m_AmountMax = bod.AmountMax; + m_Number = bod.Number; + m_Graphic = bod.Graphic; + } + + public BOBSmallEntry( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + string type = reader.ReadString(); + + if ( type != null ) + m_ItemType = ScriptCompiler.FindTypeByFullName( type ); + + m_RequireExceptional = reader.ReadBool(); + + m_DeedType = (BODType)reader.ReadEncodedInt(); + + m_Material = (BulkMaterialType)reader.ReadEncodedInt(); + m_AmountCur = reader.ReadEncodedInt(); + m_AmountMax = reader.ReadEncodedInt(); + m_Number = reader.ReadEncodedInt(); + m_Graphic = reader.ReadEncodedInt(); + m_Price = reader.ReadEncodedInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( m_ItemType == null ? null : m_ItemType.FullName ); + + writer.Write( (bool) m_RequireExceptional ); + + writer.WriteEncodedInt( (int) m_DeedType ); + writer.WriteEncodedInt( (int) m_Material ); + writer.WriteEncodedInt( (int) m_AmountCur ); + writer.WriteEncodedInt( (int) m_AmountMax ); + writer.WriteEncodedInt( (int) m_Number ); + writer.WriteEncodedInt( (int) m_Graphic ); + writer.WriteEncodedInt( (int) m_Price ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BODBuyGump.cs b/Scripts/Engines/BulkOrders/Books/BODBuyGump.cs new file mode 100644 index 000000000..459a98ebd --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BODBuyGump.cs @@ -0,0 +1,126 @@ +using System; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; + +namespace Server.Engines.BulkOrders +{ + public class BODBuyGump : Gump + { + private PlayerMobile m_From; + private BulkOrderBook m_Book; + private object m_Object; + private int m_Price; + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 2 ) + { + PlayerVendor pv = m_Book.RootParent as PlayerVendor; + + if ( m_Book.Entries.Contains( m_Object ) && pv != null ) + { + int price = 0; + + VendorItem vi = pv.GetVendorItem( m_Book ); + + if ( vi != null && !vi.IsForSale ) + { + if ( m_Object is BOBLargeEntry ) + price = ((BOBLargeEntry)m_Object).Price; + else if ( m_Object is BOBSmallEntry ) + price = ((BOBSmallEntry)m_Object).Price; + } + + if ( price != m_Price ) + { + pv.SayTo( m_From, "The price has been been changed. If you like, you may offer to purchase the item again." ); + } + else if ( price == 0 ) + { + pv.SayTo( m_From, 1062382 ); // The deed selected is not available. + } + else + { + Item item = null; + + if ( m_Object is BOBLargeEntry ) + item = ((BOBLargeEntry)m_Object).Reconstruct(); + else if ( m_Object is BOBSmallEntry ) + item = ((BOBSmallEntry)m_Object).Reconstruct(); + + if ( item == null ) + { + m_From.SendMessage( "Internal error. The bulk order deed could not be reconstructed." ); + } + else + { + pv.Say( m_From.Name ); + + Container pack = m_From.Backpack; + + if ( (pack != null && pack.ConsumeTotal( typeof( Gold ), price )) || Banker.Withdraw( m_From, price ) ) + { + m_Book.Entries.Remove( m_Object ); + m_Book.InvalidateProperties(); + + pv.HoldGold += price; + + if ( m_From.AddToBackpack( item ) ) + m_From.SendLocalizedMessage( 1045152 ); // The bulk order deed has been placed in your backpack. + else + pv.SayTo( m_From, 503204 ); // You do not have room in your backpack for this. + + if ( m_Book.Entries.Count > 0 ) + m_From.SendGump( new BOBGump( m_From, m_Book ) ); + else + m_From.SendLocalizedMessage( 1062381 ); // The book is empty. + } + else + { + pv.SayTo( m_From, 503205 ); // You cannot afford this item. + item.Delete(); + } + } + } + } + else + { + if ( pv == null ) + m_From.SendLocalizedMessage( 1062382 ); // The deed selected is not available. + else + pv.SayTo( m_From, 1062382 ); // The deed selected is not available. + } + } + else + { + m_From.SendLocalizedMessage( 503207 ); // Cancelled purchase. + } + } + + public BODBuyGump( PlayerMobile from, BulkOrderBook book, object obj, int price ) : base( 100, 200 ) + { + m_From = from; + m_Book = book; + m_Object = obj; + m_Price = price; + + AddPage( 0 ); + + AddBackground( 100, 10, 300, 150, 5054 ); + + AddHtmlLocalized( 125, 20, 250, 24, 1019070, false, false ); // You have agreed to purchase: + AddHtmlLocalized( 125, 45, 250, 24, 1045151, false, false ); // a bulk order deed + + AddHtmlLocalized( 125, 70, 250, 24, 1019071, false, false ); // for the amount of: + AddLabel( 125, 95, 0, price.ToString() ); + + AddButton( 250, 130, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 282, 130, 100, 24, 1011012, false, false ); // CANCEL + + AddButton( 120, 130, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 152, 130, 100, 24, 1011036, false, false ); // OKAY + } + } +} diff --git a/Scripts/Engines/BulkOrders/Books/BODType.cs b/Scripts/Engines/BulkOrders/Books/BODType.cs new file mode 100644 index 000000000..48009247e --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BODType.cs @@ -0,0 +1,10 @@ +using System; + +namespace Server.Engines.BulkOrders +{ + public enum BODType + { + Smith, + Tailor + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Books/BulkOrderBook.cs b/Scripts/Engines/BulkOrders/Books/BulkOrderBook.cs new file mode 100644 index 000000000..aac2ed6f4 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Books/BulkOrderBook.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Prompts; +using Server.Mobiles; +using Server.ContextMenus; + +namespace Server.Engines.BulkOrders +{ + public class BulkOrderBook : Item, ISecurable + { + private ArrayList m_Entries; + private BOBFilter m_Filter; + private string m_BookName; + private SecureLevel m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public string BookName + { + get{ return m_BookName; } + set{ m_BookName = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + public ArrayList Entries + { + get{ return m_Entries; } + } + + public BOBFilter Filter + { + get{ return m_Filter; } + } + + [Constructable] + public BulkOrderBook() : base( 0x2259 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + + m_Entries = new ArrayList(); + m_Filter = new BOBFilter(); + + m_Level = SecureLevel.CoOwners; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + from.LocalOverheadMessage( Network.MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else if ( m_Entries.Count == 0 ) + from.SendLocalizedMessage( 1062381 ); // The book is empty. + else if ( from is PlayerMobile ) + from.SendGump( new BOBGump( (PlayerMobile)from, this ) ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is LargeBOD ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062385 ); // You must have the book in your backpack to add deeds to it. + return false; + } + else if ( m_Entries.Count < 500 ) + { + m_Entries.Add( new BOBLargeEntry( (LargeBOD)dropped ) ); + InvalidateProperties(); + + from.SendLocalizedMessage( 1062386 ); // Deed added to book. + + if ( from is PlayerMobile ) + from.SendGump( new BOBGump( (PlayerMobile)from, this ) ); + + dropped.Delete(); + return true; + } + else + { + from.SendLocalizedMessage( 1062387 ); // The book is full of deeds. + return false; + } + } + else if ( dropped is SmallBOD ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062385 ); // You must have the book in your backpack to add deeds to it. + return false; + } + else if ( m_Entries.Count < 500 ) + { + m_Entries.Add( new BOBSmallEntry( (SmallBOD)dropped ) ); + InvalidateProperties(); + + from.SendLocalizedMessage( 1062386 ); // Deed added to book. + + if ( from is PlayerMobile ) + from.SendGump( new BOBGump( (PlayerMobile)from, this ) ); + + dropped.Delete(); + return true; + } + else + { + from.SendLocalizedMessage( 1062387 ); // The book is full of deeds. + return false; + } + } + + from.SendLocalizedMessage( 1062388 ); // That is not a bulk order deed. + return false; + } + + public BulkOrderBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Level ); + + writer.Write( m_BookName ); + + m_Filter.Serialize( writer ); + + writer.WriteEncodedInt( (int) m_Entries.Count ); + + for ( int i = 0; i < m_Entries.Count; ++i ) + { + object obj = m_Entries[i]; + + if ( obj is BOBLargeEntry ) + { + writer.WriteEncodedInt( 0 ); + ((BOBLargeEntry)obj).Serialize( writer ); + } + else if ( obj is BOBSmallEntry ) + { + writer.WriteEncodedInt( 1 ); + ((BOBSmallEntry)obj).Serialize( writer ); + } + else + { + writer.WriteEncodedInt( -1 ); + } + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + goto case 0; + } + case 0: + { + m_BookName = reader.ReadString(); + + m_Filter = new BOBFilter( reader ); + + int count = reader.ReadEncodedInt(); + + m_Entries = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + int v = reader.ReadEncodedInt(); + + switch ( v ) + { + case 0: m_Entries.Add( new BOBLargeEntry( reader ) ); break; + case 1: m_Entries.Add( new BOBSmallEntry( reader ) ); break; + } + } + + break; + } + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1062344, m_Entries.Count.ToString() ); // Deeds in book: ~1_val~ + + if ( m_BookName != null && m_BookName.Length > 0 ) + list.Add( 1062481, m_BookName ); // Book Name: ~1_val~ + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.CheckAlive() && IsChildOf( from.Backpack ) ) + list.Add( new NameBookEntry( from, this ) ); + + SetSecureLevelEntry.AddTo( from, this, list ); + } + + private class NameBookEntry : ContextMenuEntry + { + private Mobile m_From; + private BulkOrderBook m_Book; + + public NameBookEntry( Mobile from, BulkOrderBook book ) : base( 6216 ) + { + m_From = from; + m_Book = book; + } + + public override void OnClick() + { + if ( m_From.CheckAlive() && m_Book.IsChildOf( m_From.Backpack ) ) + { + m_From.Prompt = new NameBookPrompt( m_Book ); + m_From.SendLocalizedMessage( 1062479 ); // Type in the new name of the book: + } + } + } + + private class NameBookPrompt : Prompt + { + private BulkOrderBook m_Book; + + public NameBookPrompt( BulkOrderBook book ) + { + m_Book = book; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( text.Length > 40 ) + text = text.Substring( 0, 40 ); + + if ( from.CheckAlive() && m_Book.IsChildOf( from.Backpack ) ) + { + m_Book.BookName = Utility.FixHtml( text.Trim() ); + + from.SendLocalizedMessage( 1062480 ); // The bulk order book's name has been changed. + } + } + + public override void OnCancel( Mobile from ) + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/BulkMaterialType.cs b/Scripts/Engines/BulkOrders/BulkMaterialType.cs new file mode 100644 index 000000000..1b88380c9 --- /dev/null +++ b/Scripts/Engines/BulkOrders/BulkMaterialType.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.BulkOrders +{ + public enum BulkMaterialType + { + None, + DullCopper, + ShadowIron, + Copper, + Bronze, + Gold, + Agapite, + Verite, + Valorite, + Spined, + Horned, + Barbed + } + + public enum BulkGenericType + { + Iron, + Cloth, + Leather + } + + public class BGTClassifier + { + public static BulkGenericType Classify( BODType deedType, Type itemType ) + { + if ( deedType == BODType.Tailor ) + { + if ( itemType == null || itemType.IsSubclassOf( typeof( BaseArmor ) ) || itemType.IsSubclassOf( typeof( BaseShoes ) ) ) + return BulkGenericType.Leather; + + return BulkGenericType.Cloth; + } + + return BulkGenericType.Iron; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeBOD.cs b/Scripts/Engines/BulkOrders/LargeBOD.cs new file mode 100644 index 000000000..8e9aec103 --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeBOD.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.BulkOrders +{ + [TypeAlias( "Scripts.Engines.BulkOrders.LargeBOD" )] + public abstract class LargeBOD : Item + { + private int m_AmountMax; + private bool m_RequireExceptional; + private BulkMaterialType m_Material; + private LargeBulkEntry[] m_Entries; + + [CommandProperty( AccessLevel.GameMaster )] + public int AmountMax{ get{ return m_AmountMax; } set{ m_AmountMax = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool RequireExceptional{ get{ return m_RequireExceptional; } set{ m_RequireExceptional = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public BulkMaterialType Material{ get{ return m_Material; } set{ m_Material = value; InvalidateProperties(); } } + + public LargeBulkEntry[] Entries{ get{ return m_Entries; } set{ m_Entries = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Complete + { + get + { + for ( int i = 0; i < m_Entries.Length; ++i ) + { + if ( m_Entries[i].Amount < m_AmountMax ) + return false; + } + + return true; + } + } + + public abstract ArrayList ComputeRewards( bool full ); + public abstract int ComputeGold(); + public abstract int ComputeFame(); + + public virtual void GetRewards( out Item reward, out int gold, out int fame ) + { + reward = null; + gold = ComputeGold(); + fame = ComputeFame(); + + ArrayList rewards = ComputeRewards( false ); + + if ( rewards.Count > 0 ) + { + reward = (Item)rewards[Utility.Random( rewards.Count )]; + + for ( int i = 0; i < rewards.Count; ++i ) + { + if ( rewards[i] != reward ) + ((Item)rewards[i]).Delete(); + } + } + } + + public static BulkMaterialType GetRandomMaterial( BulkMaterialType start, double[] chances ) + { + double random = Utility.RandomDouble(); + + for ( int i = 0; i < chances.Length; ++i ) + { + if ( random < chances[i] ) + return ( i == 0 ? BulkMaterialType.None : start + (i - 1) ); + + random -= chances[i]; + } + + return BulkMaterialType.None; + } + + public override int LabelNumber{ get{ return 1045151; } } // a bulk order deed + + public LargeBOD( int hue, int amountMax, bool requireExeptional, BulkMaterialType material, LargeBulkEntry[] entries ) : base( Core.AOS ? 0x2258 : 0x14EF ) + { + Weight = 1.0; + Hue = hue; // Blacksmith: 0x44E; Tailoring: 0x483 + LootType = LootType.Blessed; + + m_AmountMax = amountMax; + m_RequireExceptional = requireExeptional; + m_Material = material; + m_Entries = entries; + } + + public LargeBOD() : base( Core.AOS ? 0x2258 : 0x14EF ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060655 ); // large bulk order + + if ( m_RequireExceptional ) + list.Add( 1045141 ); // All items must be exceptional. + + if ( m_Material != BulkMaterialType.None ) + list.Add( LargeBODGump.GetMaterialNumberFor( m_Material ) ); // All items must be made with x material. + + list.Add( 1060656, m_AmountMax.ToString() ); // amount to make: ~1_val~ + + for ( int i = 0; i < m_Entries.Length; ++i ) + list.Add( 1060658 + i, "#{0}\t{1}", m_Entries[i].Details.Number, m_Entries[i].Amount ); // ~1_val~: ~2_val~ + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + from.SendGump( new LargeBODGump( from, this ) ); + else + from.SendLocalizedMessage( 1045156 ); // You must have the deed in your backpack to use it. + } + + public void BeginCombine( Mobile from ) + { + if ( !Complete ) + from.Target = new LargeBODTarget( this ); + else + from.SendLocalizedMessage( 1045166 ); // The maximum amount of requested items have already been combined to this deed. + } + + public void EndCombine( Mobile from, object o ) + { + if ( o is Item && ((Item)o).IsChildOf( from.Backpack ) ) + { + if ( o is SmallBOD ) + { + SmallBOD small = (SmallBOD)o; + + LargeBulkEntry entry = null; + + for ( int i = 0; entry == null && i < m_Entries.Length; ++i ) + { + if ( m_Entries[i].Details.Type == small.Type ) + entry = m_Entries[i]; + } + + if ( entry == null ) + { + from.SendLocalizedMessage( 1045160 ); // That is not a bulk order for this large request. + } + else if ( m_RequireExceptional && !small.RequireExceptional ) + { + from.SendLocalizedMessage( 1045161 ); // Both orders must be of exceptional quality. + } + else if ( m_Material >= BulkMaterialType.DullCopper && m_Material <= BulkMaterialType.Valorite && small.Material != m_Material ) + { + from.SendLocalizedMessage( 1045162 ); // Both orders must use the same ore type. + } + else if ( m_Material >= BulkMaterialType.Spined && m_Material <= BulkMaterialType.Barbed && small.Material != m_Material ) + { + from.SendLocalizedMessage( 1049351 ); // Both orders must use the same leather type. + } + else if ( m_AmountMax != small.AmountMax ) + { + from.SendLocalizedMessage( 1045163 ); // The two orders have different requested amounts and cannot be combined. + } + else if ( small.AmountCur < small.AmountMax ) + { + from.SendLocalizedMessage( 1045164 ); // The order to combine with is not completed. + } + else if ( entry.Amount >= m_AmountMax ) + { + from.SendLocalizedMessage( 1045166 ); // The maximum amount of requested items have already been combined to this deed. + } + else + { + entry.Amount += small.AmountCur; + small.Delete(); + + from.SendLocalizedMessage( 1045165 ); // The orders have been combined. + + from.SendGump( new LargeBODGump( from, this ) ); + + if ( !Complete ) + BeginCombine( from ); + } + } + else + { + from.SendLocalizedMessage( 1045159 ); // That is not a bulk order. + } + } + else + { + from.SendLocalizedMessage( 1045158 ); // You must have the item in your backpack to target it. + } + } + + public LargeBOD( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_AmountMax ); + writer.Write( m_RequireExceptional ); + writer.Write( (int) m_Material ); + + writer.Write( (int) m_Entries.Length ); + + for ( int i = 0; i < m_Entries.Length; ++i ) + m_Entries[i].Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_AmountMax = reader.ReadInt(); + m_RequireExceptional = reader.ReadBool(); + m_Material = (BulkMaterialType)reader.ReadInt(); + + m_Entries = new LargeBulkEntry[reader.ReadInt()]; + + for ( int i = 0; i < m_Entries.Length; ++i ) + m_Entries[i] = new LargeBulkEntry( this, reader ); + + break; + } + } + + if ( Weight == 0.0 ) + Weight = 1.0; + + if ( Core.AOS && ItemID == 0x14EF ) + ItemID = 0x2258; + + if ( Parent == null && Map == Map.Internal && Location == Point3D.Zero ) + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeBODAcceptGump.cs b/Scripts/Engines/BulkOrders/LargeBODAcceptGump.cs new file mode 100644 index 000000000..631b372bf --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeBODAcceptGump.cs @@ -0,0 +1,106 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.BulkOrders +{ + public class LargeBODAcceptGump : Gump + { + private LargeBOD m_Deed; + private Mobile m_From; + + public LargeBODAcceptGump( Mobile from, LargeBOD deed ) : base( 50, 50 ) + { + m_From = from; + m_Deed = deed; + + m_From.CloseGump( typeof( LargeBODAcceptGump ) ); + m_From.CloseGump( typeof( SmallBODAcceptGump ) ); + + LargeBulkEntry[] entries = deed.Entries; + + AddPage( 0 ); + + AddBackground( 25, 10, 430, 240 + (entries.Length * 24), 5054 ); + + AddImageTiled( 33, 20, 413, 221 + (entries.Length * 24), 2624 ); + AddAlphaRegion( 33, 20, 413, 221 + (entries.Length * 24) ); + + AddImage( 20, 5, 10460 ); + AddImage( 430, 5, 10460 ); + AddImage( 20, 225 + (entries.Length * 24), 10460 ); + AddImage( 430, 225 + (entries.Length * 24), 10460 ); + + AddHtmlLocalized( 180, 25, 120, 20, 1045134, 0x7FFF, false, false ); // A large bulk order + + AddHtmlLocalized( 40, 48, 350, 20, 1045135, 0x7FFF, false, false ); // Ah! Thanks for the goods! Would you help me out? + + AddHtmlLocalized( 40, 72, 210, 20, 1045138, 0x7FFF, false, false ); // Amount to make: + AddLabel( 250, 72, 1152, deed.AmountMax.ToString() ); + + AddHtmlLocalized( 40, 96, 120, 20, 1045137, 0x7FFF, false, false ); // Items requested: + + int y = 120; + + for ( int i = 0; i < entries.Length; ++i, y += 24 ) + AddHtmlLocalized( 40, y, 210, 20, entries[i].Details.Number, 0x7FFF, false, false ); + + if ( deed.RequireExceptional || deed.Material != BulkMaterialType.None ) + { + AddHtmlLocalized( 40, y, 210, 20, 1045140, 0x7FFF, false, false ); // Special requirements to meet: + y += 24; + + if ( deed.RequireExceptional ) + { + AddHtmlLocalized( 40, y, 350, 20, 1045141, 0x7FFF, false, false ); // All items must be exceptional. + y += 24; + } + + if ( deed.Material != BulkMaterialType.None ) + { + AddHtmlLocalized( 40, y, 350, 20, GetMaterialNumberFor( deed.Material ), 0x7FFF, false, false ); // All items must be made with x material. + y += 24; + } + } + + AddHtmlLocalized( 40, 192 + (entries.Length * 24), 350, 20, 1045139, 0x7FFF, false, false ); // Do you want to accept this order? + + AddButton( 100, 216 + (entries.Length * 24), 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 135, 216 + (entries.Length * 24), 120, 20, 1006044, 0x7FFF, false, false ); // Ok + + AddButton( 275, 216 + (entries.Length * 24), 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 310, 216 + (entries.Length * 24), 120, 20, 1011012, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) // Ok + { + if ( m_From.PlaceInBackpack( m_Deed ) ) + { + m_From.SendLocalizedMessage( 1045152 ); // The bulk order deed has been placed in your backpack. + } + else + { + m_From.SendLocalizedMessage( 1045150 ); // There is not enough room in your backpack for the deed. + m_Deed.Delete(); + } + } + else + { + m_Deed.Delete(); + } + } + + public static int GetMaterialNumberFor( BulkMaterialType material ) + { + if ( material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Valorite ) + return 1045142 + (int)(material - BulkMaterialType.DullCopper); + else if ( material >= BulkMaterialType.Spined && material <= BulkMaterialType.Barbed ) + return 1049348 + (int)(material - BulkMaterialType.Spined); + + return 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeBODGump.cs b/Scripts/Engines/BulkOrders/LargeBODGump.cs new file mode 100644 index 000000000..057840863 --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeBODGump.cs @@ -0,0 +1,100 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.BulkOrders +{ + public class LargeBODGump : Gump + { + private LargeBOD m_Deed; + private Mobile m_From; + + public LargeBODGump( Mobile from, LargeBOD deed ) : base( 25, 25 ) + { + m_From = from; + m_Deed = deed; + + m_From.CloseGump( typeof( LargeBODGump ) ); + m_From.CloseGump( typeof( SmallBODGump ) ); + + LargeBulkEntry[] entries = deed.Entries; + + AddPage( 0 ); + + AddBackground( 50, 10, 455, 236 + (entries.Length * 24), 5054 ); + + AddImageTiled( 58, 20, 438, 217 + (entries.Length * 24), 2624 ); + AddAlphaRegion( 58, 20, 438, 217 + (entries.Length * 24) ); + + AddImage( 45, 5, 10460 ); + AddImage( 480, 5, 10460 ); + AddImage( 45, 221 + (entries.Length * 24), 10460 ); + AddImage( 480, 221 + (entries.Length * 24), 10460 ); + + AddHtmlLocalized( 225, 25, 120, 20, 1045134, 0x7FFF, false, false ); // A large bulk order + + AddHtmlLocalized( 75, 48, 250, 20, 1045138, 0x7FFF, false, false ); // Amount to make: + AddLabel( 275, 48, 1152, deed.AmountMax.ToString() ); + + AddHtmlLocalized( 75, 72, 120, 20, 1045137, 0x7FFF, false, false ); // Items requested: + AddHtmlLocalized( 275, 76, 200, 20, 1045153, 0x7FFF, false, false ); // Amount finished: + + int y = 96; + + for ( int i = 0; i < entries.Length; ++i ) + { + LargeBulkEntry entry = entries[i]; + SmallBulkEntry details = entry.Details; + + AddHtmlLocalized( 75, y, 210, 20, details.Number, 0x7FFF, false, false ); + AddLabel( 275, y, 0x480, entry.Amount.ToString() ); + + y += 24; + } + + if ( deed.RequireExceptional || deed.Material != BulkMaterialType.None ) + { + AddHtmlLocalized( 75, y, 200, 20, 1045140, 0x7FFF, false, false ); // Special requirements to meet: + y += 24; + } + + if ( deed.RequireExceptional ) + { + AddHtmlLocalized( 75, y, 300, 20, 1045141, 0x7FFF, false, false ); // All items must be exceptional. + y += 24; + } + + if ( deed.Material != BulkMaterialType.None ) + AddHtmlLocalized( 75, y, 300, 20, GetMaterialNumberFor( deed.Material ), 0x7FFF, false, false ); // All items must be made with x material. + + AddButton( 125, 168 + (entries.Length * 24), 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 160, 168 + (entries.Length * 24), 300, 20, 1045155, 0x7FFF, false, false ); // Combine this deed with another deed. + + AddButton( 125, 192 + (entries.Length * 24), 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 160, 192 + (entries.Length * 24), 120, 20, 1011441, 0x7FFF, false, false ); // EXIT + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted || !m_Deed.IsChildOf( m_From.Backpack ) ) + return; + + if ( info.ButtonID == 2 ) // Combine + { + m_From.SendGump( new LargeBODGump( m_From, m_Deed ) ); + m_Deed.BeginCombine( m_From ); + } + } + + public static int GetMaterialNumberFor( BulkMaterialType material ) + { + if ( material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Valorite ) + return 1045142 + (int)(material - BulkMaterialType.DullCopper); + else if ( material >= BulkMaterialType.Spined && material <= BulkMaterialType.Barbed ) + return 1049348 + (int)(material - BulkMaterialType.Spined); + + return 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeBODTarget.cs b/Scripts/Engines/BulkOrders/LargeBODTarget.cs new file mode 100644 index 000000000..6fe76abdb --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeBODTarget.cs @@ -0,0 +1,25 @@ +using System; +using Server; +using Server.Targeting; +using Server.Network; + +namespace Server.Engines.BulkOrders +{ + public class LargeBODTarget : Target + { + private LargeBOD m_Deed; + + public LargeBODTarget( LargeBOD deed ) : base( 18, false, TargetFlags.None ) + { + m_Deed = deed; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Deed.Deleted || !m_Deed.IsChildOf( from.Backpack ) ) + return; + + m_Deed.EndCombine( from, targeted ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeBulkEntry.cs b/Scripts/Engines/BulkOrders/LargeBulkEntry.cs new file mode 100644 index 000000000..40d89b6d1 --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeBulkEntry.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using System.Collections; +using Server; + +namespace Server.Engines.BulkOrders +{ + public class LargeBulkEntry + { + private LargeBOD m_Owner; + private int m_Amount; + private SmallBulkEntry m_Details; + + public LargeBOD Owner{ get{ return m_Owner; } set{ m_Owner = value; } } + public int Amount{ get{ return m_Amount; } set{ m_Amount = value; if ( m_Owner != null ) m_Owner.InvalidateProperties(); } } + public SmallBulkEntry Details{ get{ return m_Details; } } + + public static SmallBulkEntry[] LargeRing + { + get{ return GetEntries( "Blacksmith", "largering" ); } + } + + public static SmallBulkEntry[] LargePlate + { + get{ return GetEntries( "Blacksmith", "largeplate" ); } + } + + public static SmallBulkEntry[] LargeChain + { + get{ return GetEntries( "Blacksmith", "largechain" ); } + } + + public static SmallBulkEntry[] LargeAxes + { + get{ return GetEntries( "Blacksmith", "largeaxes" ); } + } + + public static SmallBulkEntry[] LargeFencing + { + get{ return GetEntries( "Blacksmith", "largefencing" ); } + } + + public static SmallBulkEntry[] LargeMaces + { + get{ return GetEntries( "Blacksmith", "largemaces" ); } + } + + public static SmallBulkEntry[] LargePolearms + { + get{ return GetEntries( "Blacksmith", "largepolearms" ); } + } + + public static SmallBulkEntry[] LargeSwords + { + get{ return GetEntries( "Blacksmith", "largeswords" ); } + } + + + public static SmallBulkEntry[] BoneSet + { + get{ return GetEntries( "Tailoring", "boneset" ); } + } + + public static SmallBulkEntry[] Farmer + { + get{ return GetEntries( "Tailoring", "farmer" ); } + } + + public static SmallBulkEntry[] FemaleLeatherSet + { + get{ return GetEntries( "Tailoring", "femaleleatherset" ); } + } + + public static SmallBulkEntry[] FisherGirl + { + get{ return GetEntries( "Tailoring", "fishergirl" ); } + } + + public static SmallBulkEntry[] Gypsy + { + get{ return GetEntries( "Tailoring", "gypsy" ); } + } + + public static SmallBulkEntry[] HatSet + { + get{ return GetEntries( "Tailoring", "hatset" ); } + } + + public static SmallBulkEntry[] Jester + { + get{ return GetEntries( "Tailoring", "jester" ); } + } + + public static SmallBulkEntry[] Lady + { + get{ return GetEntries( "Tailoring", "lady" ); } + } + + public static SmallBulkEntry[] MaleLeatherSet + { + get{ return GetEntries( "Tailoring", "maleleatherset" ); } + } + + public static SmallBulkEntry[] Pirate + { + get{ return GetEntries( "Tailoring", "pirate" ); } + } + + public static SmallBulkEntry[] ShoeSet + { + get{ return GetEntries( "Tailoring", "shoeset" ); } + } + + public static SmallBulkEntry[] StuddedSet + { + get{ return GetEntries( "Tailoring", "studdedset" ); } + } + + public static SmallBulkEntry[] TownCrier + { + get{ return GetEntries( "Tailoring", "towncrier" ); } + } + + public static SmallBulkEntry[] Wizard + { + get{ return GetEntries( "Tailoring", "wizard" ); } + } + + + private static Hashtable m_Cache; + + public static SmallBulkEntry[] GetEntries( string type, string name ) + { + if ( m_Cache == null ) + m_Cache = new Hashtable(); + + Hashtable table = (Hashtable)m_Cache[type]; + + if ( table == null ) + m_Cache[type] = table = new Hashtable(); + + SmallBulkEntry[] entries = (SmallBulkEntry[])table[name]; + + if ( entries == null ) + table[name] = entries = SmallBulkEntry.LoadEntries( type, name ); + + return entries; + } + + public static LargeBulkEntry[] ConvertEntries( LargeBOD owner, SmallBulkEntry[] small ) + { + LargeBulkEntry[] large = new LargeBulkEntry[small.Length]; + + for ( int i = 0; i < small.Length; ++i ) + large[i] = new LargeBulkEntry( owner, small[i] ); + + return large; + } + + public LargeBulkEntry( LargeBOD owner, SmallBulkEntry details ) + { + m_Owner = owner; + m_Details = details; + } + + public LargeBulkEntry( LargeBOD owner, GenericReader reader ) + { + m_Owner = owner; + m_Amount = reader.ReadInt(); + + Type realType = null; + + string type = reader.ReadString(); + + if ( type != null ) + realType = ScriptCompiler.FindTypeByFullName( type ); + + m_Details = new SmallBulkEntry( realType, reader.ReadInt(), reader.ReadInt() ); + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( m_Amount ); + writer.Write( m_Details.Type == null ? null : m_Details.Type.FullName ); + writer.Write( m_Details.Number ); + writer.Write( m_Details.Graphic ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeSmithBOD.cs b/Scripts/Engines/BulkOrders/LargeSmithBOD.cs new file mode 100644 index 000000000..02993a0f2 --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeSmithBOD.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Mat = Server.Engines.BulkOrders.BulkMaterialType; + +namespace Server.Engines.BulkOrders +{ + [TypeAlias( "Scripts.Engines.BulkOrders.LargeSmithBOD" )] + public class LargeSmithBOD : LargeBOD + { + public static double[] m_BlacksmithMaterialChances = new double[] + { + 0.501953125, // None + 0.250000000, // Dull Copper + 0.125000000, // Shadow Iron + 0.062500000, // Copper + 0.031250000, // Bronze + 0.015625000, // Gold + 0.007812500, // Agapite + 0.003906250, // Verite + 0.001953125 // Valorite + }; + + public override int ComputeFame() + { + return SmithRewardCalculator.Instance.ComputeFame( this ); + } + + public override int ComputeGold() + { + return SmithRewardCalculator.Instance.ComputeGold( this ); + } + + [Constructable] + public LargeSmithBOD() + { + LargeBulkEntry[] entries; + bool useMaterials = true; + + int rand = Utility.Random( 8 ); + + switch ( rand ) + { + default: + case 0: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargeRing ); break; + case 1: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargePlate ); break; + case 2: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargeChain ); break; + case 3: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargeAxes ); break; + case 4: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargeFencing ); break; + case 5: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargeMaces ); break; + case 6: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargePolearms ); break; + case 7: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.LargeSwords ); break; + } + + if( rand > 2 && rand < 8 ) + useMaterials = false; + + int hue = 0x44E; + int amountMax = Utility.RandomList( 10, 15, 20, 20 ); + bool reqExceptional = ( 0.825 > Utility.RandomDouble() ); + + BulkMaterialType material; + + if ( useMaterials ) + material = GetRandomMaterial( BulkMaterialType.DullCopper, m_BlacksmithMaterialChances ); + else + material = BulkMaterialType.None; + + this.Hue = hue; + this.AmountMax = amountMax; + this.Entries = entries; + this.RequireExceptional = reqExceptional; + this.Material = material; + } + + public LargeSmithBOD( int amountMax, bool reqExceptional, BulkMaterialType mat, LargeBulkEntry[] entries ) + { + this.Hue = 0x44E; + this.AmountMax = amountMax; + this.Entries = entries; + this.RequireExceptional = reqExceptional; + this.Material = mat; + } + + public override ArrayList ComputeRewards( bool full ) + { + ArrayList list = new ArrayList(); + + RewardGroup rewardGroup = SmithRewardCalculator.Instance.LookupRewards( SmithRewardCalculator.Instance.ComputePoints( this ) ); + + if ( rewardGroup != null ) + { + if ( full ) + { + for ( int i = 0; i < rewardGroup.Items.Length; ++i ) + { + Item item = rewardGroup.Items[i].Construct(); + + if ( item != null ) + list.Add( item ); + } + } + else + { + RewardItem rewardItem = rewardGroup.AcquireItem(); + + if ( rewardItem != null ) + { + Item item = rewardItem.Construct(); + + if ( item != null ) + list.Add( item ); + } + } + } + + return list; + } + + public LargeSmithBOD( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/LargeTailorBOD.cs b/Scripts/Engines/BulkOrders/LargeTailorBOD.cs new file mode 100644 index 000000000..9a6070511 --- /dev/null +++ b/Scripts/Engines/BulkOrders/LargeTailorBOD.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Mat = Server.Engines.BulkOrders.BulkMaterialType; + +namespace Server.Engines.BulkOrders +{ + public class LargeTailorBOD : LargeBOD + { + public static double[] m_TailoringMaterialChances = new double[] + { + 0.857421875, // None + 0.125000000, // Spined + 0.015625000, // Horned + 0.001953125 // Barbed + }; + + public override int ComputeFame() + { + return TailorRewardCalculator.Instance.ComputeFame( this ); + } + + public override int ComputeGold() + { + return TailorRewardCalculator.Instance.ComputeGold( this ); + } + + [Constructable] + public LargeTailorBOD() + { + LargeBulkEntry[] entries; + bool useMaterials = false; + + switch ( Utility.Random( 14 ) ) + { + default: + case 0: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.Farmer ); break; + case 1: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.FemaleLeatherSet ); useMaterials = true; break; + case 2: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.FisherGirl ); break; + case 3: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.Gypsy ); break; + case 4: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.HatSet ); break; + case 5: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.Jester ); break; + case 6: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.Lady ); break; + case 7: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.MaleLeatherSet ); useMaterials = true; break; + case 8: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.Pirate ); break; + case 9: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.ShoeSet ); break; + case 10: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.StuddedSet ); useMaterials = true; break; + case 11: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.TownCrier ); break; + case 12: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.Wizard ); break; + case 13: entries = LargeBulkEntry.ConvertEntries( this, LargeBulkEntry.BoneSet ); useMaterials = true; break; + } + + int hue = 0x483; + int amountMax = Utility.RandomList( 10, 15, 20, 20 ); + bool reqExceptional = ( 0.825 > Utility.RandomDouble() ); + + BulkMaterialType material; + + if ( useMaterials ) + material = GetRandomMaterial( BulkMaterialType.Spined, m_TailoringMaterialChances ); + else + material = BulkMaterialType.None; + + this.Hue = hue; + this.AmountMax = amountMax; + this.Entries = entries; + this.RequireExceptional = reqExceptional; + this.Material = material; + } + + public LargeTailorBOD( int amountMax, bool reqExceptional, BulkMaterialType mat, LargeBulkEntry[] entries ) + { + this.Hue = 0x483; + this.AmountMax = amountMax; + this.Entries = entries; + this.RequireExceptional = reqExceptional; + this.Material = mat; + } + + public override ArrayList ComputeRewards( bool full ) + { + ArrayList list = new ArrayList(); + + RewardGroup rewardGroup = TailorRewardCalculator.Instance.LookupRewards( TailorRewardCalculator.Instance.ComputePoints( this ) ); + + if ( rewardGroup != null ) + { + if ( full ) + { + for ( int i = 0; i < rewardGroup.Items.Length; ++i ) + { + Item item = rewardGroup.Items[i].Construct(); + + if ( item != null ) + list.Add( item ); + } + } + else + { + RewardItem rewardItem = rewardGroup.AcquireItem(); + + if ( rewardItem != null ) + { + Item item = rewardItem.Construct(); + + if ( item != null ) + list.Add( item ); + } + } + } + + return list; + } + + public LargeTailorBOD( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/Rewards.cs b/Scripts/Engines/BulkOrders/Rewards.cs new file mode 100644 index 000000000..abc5be2b0 --- /dev/null +++ b/Scripts/Engines/BulkOrders/Rewards.cs @@ -0,0 +1,739 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.BulkOrders +{ + public delegate Item ConstructCallback( int type ); + + public sealed class RewardType + { + private int m_Points; + private Type[] m_Types; + + public int Points{ get{ return m_Points; } } + public Type[] Types{ get{ return m_Types; } } + + public RewardType( int points, params Type[] types ) + { + m_Points = points; + m_Types = types; + } + + public bool Contains( Type type ) + { + for ( int i = 0; i < m_Types.Length; ++i ) + { + if ( m_Types[i] == type ) + return true; + } + + return false; + } + } + + public sealed class RewardItem + { + private int m_Weight; + private ConstructCallback m_Constructor; + private int m_Type; + + public int Weight{ get{ return m_Weight; } } + public ConstructCallback Constructor{ get{ return m_Constructor; } } + public int Type{ get{ return m_Type; } } + + public RewardItem( int weight, ConstructCallback constructor ) : this( weight, constructor, 0 ) + { + } + + public RewardItem( int weight, ConstructCallback constructor, int type ) + { + m_Weight = weight; + m_Constructor = constructor; + m_Type = type; + } + + public Item Construct() + { + try{ return m_Constructor( m_Type ); } + catch{ return null; } + } + } + + public sealed class RewardGroup + { + private int m_Points; + private RewardItem[] m_Items; + + public int Points{ get{ return m_Points; } } + public RewardItem[] Items{ get{ return m_Items; } } + + public RewardGroup( int points, params RewardItem[] items ) + { + m_Points = points; + m_Items = items; + } + + public RewardItem AcquireItem() + { + if ( m_Items.Length == 0 ) + return null; + else if ( m_Items.Length == 1 ) + return m_Items[0]; + + int totalWeight = 0; + + for ( int i = 0; i < m_Items.Length; ++i ) + totalWeight += m_Items[i].Weight; + + int randomWeight = Utility.Random( totalWeight ); + + for ( int i = 0; i < m_Items.Length; ++i ) + { + RewardItem item = m_Items[i]; + + if ( randomWeight < item.Weight ) + return item; + + randomWeight -= item.Weight; + } + + return null; + } + } + + public abstract class RewardCalculator + { + private RewardGroup[] m_Groups; + + public RewardGroup[] Groups{ get{ return m_Groups; } set{ m_Groups = value; } } + + public abstract int ComputePoints( int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type ); + public abstract int ComputeGold( int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type ); + + public virtual int ComputeFame( SmallBOD bod ) + { + int points = ComputePoints( bod ) / 50; + + return points * points; + } + + public virtual int ComputeFame( LargeBOD bod ) + { + int points = ComputePoints( bod ) / 50; + + return points * points; + } + + public virtual int ComputePoints( SmallBOD bod ) + { + return ComputePoints( bod.AmountMax, bod.RequireExceptional, bod.Material, 1, bod.Type ); + } + + public virtual int ComputePoints( LargeBOD bod ) + { + return ComputePoints( bod.AmountMax, bod.RequireExceptional, bod.Material, bod.Entries.Length, bod.Entries[0].Details.Type ); + } + + public virtual int ComputeGold( SmallBOD bod ) + { + return ComputeGold( bod.AmountMax, bod.RequireExceptional, bod.Material, 1, bod.Type ); + } + + public virtual int ComputeGold( LargeBOD bod ) + { + return ComputeGold( bod.AmountMax, bod.RequireExceptional, bod.Material, bod.Entries.Length, bod.Entries[0].Details.Type ); + } + + public virtual RewardGroup LookupRewards( int points ) + { + for ( int i = m_Groups.Length - 1; i >= 1; --i ) + { + RewardGroup group = m_Groups[i]; + + if ( points >= group.Points ) + return group; + } + + return m_Groups[0]; + } + + public virtual int LookupTypePoints( RewardType[] types, Type type ) + { + for ( int i = 0; i < types.Length; ++i ) + { + if ( types[i].Contains( type ) ) + return types[i].Points; + } + + return 0; + } + + public RewardCalculator() + { + } + } + + public sealed class SmithRewardCalculator : RewardCalculator + { + #region Constructors + private static readonly ConstructCallback SturdyShovel = new ConstructCallback( CreateSturdyShovel ); + private static readonly ConstructCallback SturdyPickaxe = new ConstructCallback( CreateSturdyPickaxe ); + private static readonly ConstructCallback MiningGloves = new ConstructCallback( CreateMiningGloves ); + private static readonly ConstructCallback GargoylesPickaxe = new ConstructCallback( CreateGargoylesPickaxe ); + private static readonly ConstructCallback ProspectorsTool = new ConstructCallback( CreateProspectorsTool ); + private static readonly ConstructCallback PowderOfTemperament = new ConstructCallback( CreatePowderOfTemperament ); + private static readonly ConstructCallback RunicHammer = new ConstructCallback( CreateRunicHammer ); + private static readonly ConstructCallback PowerScroll = new ConstructCallback( CreatePowerScroll ); + private static readonly ConstructCallback ColoredAnvil = new ConstructCallback( CreateColoredAnvil ); + private static readonly ConstructCallback AncientHammer = new ConstructCallback( CreateAncientHammer ); + + private static Item CreateSturdyShovel( int type ) + { + return new SturdyShovel(); + } + + private static Item CreateSturdyPickaxe( int type ) + { + return new SturdyPickaxe(); + } + + private static Item CreateMiningGloves( int type ) + { + if ( type == 1 ) + return new LeatherGlovesOfMining( 1 ); + else if ( type == 3 ) + return new StuddedGlovesOfMining( 3 ); + else if ( type == 5 ) + return new RingmailGlovesOfMining( 5 ); + + throw new InvalidOperationException(); + } + + private static Item CreateGargoylesPickaxe( int type ) + { + return new GargoylesPickaxe(); + } + + private static Item CreateProspectorsTool( int type ) + { + return new ProspectorsTool(); + } + + private static Item CreatePowderOfTemperament( int type ) + { + return new PowderOfTemperament(); + } + + private static Item CreateRunicHammer( int type ) + { + if ( type >= 1 && type <= 8 ) + return new RunicHammer( CraftResource.Iron + type, Core.AOS ? ( 55 - (type*5) ) : 50 ); + + throw new InvalidOperationException(); + } + + private static Item CreatePowerScroll( int type ) + { + if ( type == 5 || type == 10 || type == 15 || type == 20 ) + return new PowerScroll( SkillName.Blacksmith, 100 + type ); + + throw new InvalidOperationException(); + } + + private static Item CreateColoredAnvil( int type ) + { + // Generate an anvil deed, not an actual anvil. + //return new ColoredAnvilDeed(); + + return new ColoredAnvil(); + } + + private static Item CreateAncientHammer( int type ) + { + if ( type == 10 || type == 15 || type == 30 || type == 60 ) + return new AncientSmithyHammer( type ); + + throw new InvalidOperationException(); + } + #endregion + + public static readonly SmithRewardCalculator Instance = new SmithRewardCalculator(); + + private RewardType[] m_Types = new RewardType[] + { + // Armors + new RewardType( 200, typeof( RingmailGloves ), typeof( RingmailChest ), typeof( RingmailArms ), typeof( RingmailLegs ) ), + new RewardType( 300, typeof( ChainCoif ), typeof( ChainLegs ), typeof( ChainChest ) ), + new RewardType( 400, typeof( PlateArms ), typeof( PlateLegs ), typeof( PlateHelm ), typeof( PlateGorget ), typeof( PlateGloves ), typeof( PlateChest ) ), + + // Weapons + new RewardType( 200, typeof( Bardiche ), typeof( Halberd ) ), + new RewardType( 300, typeof( Dagger ), typeof( ShortSpear ), typeof( Spear ), typeof( WarFork ), typeof( Kryss ) ), //OSI put the dagger in there. Odd, ain't it. + new RewardType( 350, typeof( Axe ), typeof( BattleAxe ), typeof( DoubleAxe ), typeof( ExecutionersAxe ), typeof( LargeBattleAxe ), typeof( TwoHandedAxe ) ), + new RewardType( 350, typeof( Broadsword ), typeof( Cutlass ), typeof( Katana ), typeof( Longsword ), typeof( Scimitar ), typeof( ThinLongsword ), typeof( VikingSword ) ), + new RewardType( 350, typeof( WarAxe ), typeof( HammerPick ), typeof( Mace ), typeof( Maul ), typeof( WarHammer ), typeof( WarMace ) ) + }; + + public override int ComputePoints( int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type ) + { + int points = 0; + + if ( quantity == 10 ) + points += 10; + else if ( quantity == 15 ) + points += 25; + else if ( quantity == 20 ) + points += 50; + + if ( exceptional ) + points += 200; + + if ( itemCount > 1 ) + points += LookupTypePoints( m_Types, type ); + + if ( material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Valorite ) + points += 200 + (50 * (material - BulkMaterialType.DullCopper)); + + return points; + } + + private static int[][][] m_GoldTable = new int[][][] + { + new int[][] // 1-part (regular) + { + new int[]{ 150, 250, 250, 400, 400, 750, 750, 1200, 1200 }, + new int[]{ 225, 375, 375, 600, 600, 1125, 1125, 1800, 1800 }, + new int[]{ 300, 500, 750, 800, 1050, 1500, 2250, 2400, 4000 } + }, + new int[][] // 1-part (exceptional) + { + new int[]{ 250, 400, 400, 750, 750, 1500, 1500, 3000, 3000 }, + new int[]{ 375, 600, 600, 1125, 1125, 2250, 2250, 4500, 4500 }, + new int[]{ 500, 800, 1200, 1500, 2500, 3000, 6000, 6000, 12000 } + }, + new int[][] // Ringmail (regular) + { + new int[]{ 3000, 5000, 5000, 7500, 7500, 10000, 10000, 15000, 15000 }, + new int[]{ 4500, 7500, 7500, 11250, 11500, 15000, 15000, 22500, 22500 }, + new int[]{ 6000, 10000, 15000, 15000, 20000, 20000, 30000, 30000, 50000 } + }, + new int[][] // Ringmail (exceptional) + { + new int[]{ 5000, 10000, 10000, 15000, 15000, 25000, 25000, 50000, 50000 }, + new int[]{ 7500, 15000, 15000, 22500, 22500, 37500, 37500, 75000, 75000 }, + new int[]{ 10000, 20000, 30000, 30000, 50000, 50000, 100000, 100000, 200000 } + }, + new int[][] // Chainmail (regular) + { + new int[]{ 4000, 7500, 7500, 10000, 10000, 15000, 15000, 25000, 25000 }, + new int[]{ 6000, 11250, 11250, 15000, 15000, 22500, 22500, 37500, 37500 }, + new int[]{ 8000, 15000, 20000, 20000, 30000, 30000, 50000, 50000, 100000 } + }, + new int[][] // Chainmail (exceptional) + { + new int[]{ 7500, 15000, 15000, 25000, 25000, 50000, 50000, 100000, 100000 }, + new int[]{ 11250, 22500, 22500, 37500, 37500, 75000, 75000, 150000, 150000 }, + new int[]{ 15000, 30000, 50000, 50000, 100000, 100000, 200000, 200000, 200000 } + }, + new int[][] // Platemail (regular) + { + new int[]{ 5000, 10000, 10000, 15000, 15000, 25000, 25000, 50000, 50000 }, + new int[]{ 7500, 15000, 15000, 22500, 22500, 37500, 37500, 75000, 75000 }, + new int[]{ 10000, 20000, 30000, 30000, 50000, 50000, 100000, 100000, 200000 } + }, + new int[][] // Platemail (exceptional) + { + new int[]{ 10000, 25000, 25000, 50000, 50000, 100000, 100000, 100000, 100000 }, + new int[]{ 15000, 37500, 37500, 75000, 75000, 150000, 150000, 150000, 150000 }, + new int[]{ 20000, 50000, 100000, 100000, 200000, 200000, 200000, 200000, 200000 } + }, + new int[][] // 2-part weapons (regular) + { + new int[]{ 3000, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 4500, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 6000, 0, 0, 0, 0, 0, 0, 0, 0 } + }, + new int[][] // 2-part weapons (exceptional) + { + new int[]{ 5000, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 7500, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 10000, 0, 0, 0, 0, 0, 0, 0, 0 } + }, + new int[][] // 5-part weapons (regular) + { + new int[]{ 4000, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 6000, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 8000, 0, 0, 0, 0, 0, 0, 0, 0 } + }, + new int[][] // 5-part weapons (exceptional) + { + new int[]{ 7500, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 11250, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 15000, 0, 0, 0, 0, 0, 0, 0, 0 } + }, + new int[][] // 6-part weapons (regular) + { + new int[]{ 4000, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 6000, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 10000, 0, 0, 0, 0, 0, 0, 0, 0 } + }, + new int[][] // 6-part weapons (exceptional) + { + new int[]{ 7500, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 11250, 0, 0, 0, 0, 0, 0, 0, 0 }, + new int[]{ 15000, 0, 0, 0, 0, 0, 0, 0, 0 } + } + }; + + private int ComputeType( Type type, int itemCount ) + { + // Item count of 1 means it's a small BOD. + if ( itemCount == 1 ) + return 0; + + int typeIdx; + + // Loop through the RewardTypes defined earlier and find the correct one. + for ( typeIdx = 0; typeIdx < 7; ++typeIdx ) + { + if ( m_Types[typeIdx].Contains( type ) ) + break; + } + + // Types 5, 6 and 7 are Large Weapon BODs with the same rewards. + if ( typeIdx > 5 ) + typeIdx = 5; + + return ( typeIdx + 1 ) * 2; + } + + public override int ComputeGold( int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type ) + { + int[][][] goldTable = m_GoldTable; + + int typeIndex = ComputeType( type, itemCount ); + int quanIndex = ( quantity == 20 ? 2 : quantity == 15 ? 1 : 0 ); + int mtrlIndex = ( material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Valorite ) ? 1 + (int)(material - BulkMaterialType.DullCopper) : 0; + + if ( exceptional ) + typeIndex++; + + int gold = goldTable[typeIndex][quanIndex][mtrlIndex]; + + int min = (gold * 9) / 10; + int max = (gold * 10) / 9; + + return Utility.RandomMinMax( min, max ); + } + + public SmithRewardCalculator() + { + Groups = new RewardGroup[] + { + new RewardGroup( 0, new RewardItem( 1, SturdyShovel ) ), + new RewardGroup( 25, new RewardItem( 1, SturdyPickaxe ) ), + new RewardGroup( 50, new RewardItem( 45, SturdyShovel ), new RewardItem( 45, SturdyPickaxe ), new RewardItem( 10, MiningGloves, 1 ) ), + new RewardGroup( 200, new RewardItem( 45, GargoylesPickaxe ), new RewardItem( 45, ProspectorsTool ), new RewardItem( 10, MiningGloves, 3 ) ), + new RewardGroup( 400, new RewardItem( 2, GargoylesPickaxe ), new RewardItem( 2, ProspectorsTool ), new RewardItem( 1, PowderOfTemperament ) ), + new RewardGroup( 450, new RewardItem( 9, PowderOfTemperament ), new RewardItem( 1, MiningGloves, 5 ) ), + new RewardGroup( 500, new RewardItem( 1, RunicHammer, 1 ) ), + new RewardGroup( 550, new RewardItem( 3, RunicHammer, 1 ), new RewardItem( 2, RunicHammer, 2 ) ), + new RewardGroup( 600, new RewardItem( 1, RunicHammer, 2 ) ), + new RewardGroup( 625, new RewardItem( 3, RunicHammer, 2 ), new RewardItem( 6, PowerScroll, 5 ), new RewardItem( 1, ColoredAnvil ) ), + new RewardGroup( 650, new RewardItem( 1, RunicHammer, 3 ) ), + new RewardGroup( 675, new RewardItem( 1, ColoredAnvil ), new RewardItem( 6, PowerScroll, 10 ), new RewardItem( 3, RunicHammer, 3 ) ), + new RewardGroup( 700, new RewardItem( 1, RunicHammer, 4 ) ), + new RewardGroup( 750, new RewardItem( 1, AncientHammer, 10 ) ), + new RewardGroup( 800, new RewardItem( 1, PowerScroll, 15 ) ), + new RewardGroup( 850, new RewardItem( 1, AncientHammer, 15 ) ), + new RewardGroup( 900, new RewardItem( 1, PowerScroll, 20 ) ), + new RewardGroup( 950, new RewardItem( 1, RunicHammer, 5 ) ), + new RewardGroup( 1000, new RewardItem( 1, AncientHammer, 30 ) ), + new RewardGroup( 1050, new RewardItem( 1, RunicHammer, 6 ) ), + new RewardGroup( 1100, new RewardItem( 1, AncientHammer, 60 ) ), + new RewardGroup( 1150, new RewardItem( 1, RunicHammer, 7 ) ), + new RewardGroup( 1200, new RewardItem( 1, RunicHammer, 8 ) ) + }; + } + } + + public sealed class TailorRewardCalculator : RewardCalculator + { + #region Constructors + private static readonly ConstructCallback Cloth = new ConstructCallback( CreateCloth ); + private static readonly ConstructCallback Sandals = new ConstructCallback( CreateSandals ); + private static readonly ConstructCallback StretchedHide = new ConstructCallback( CreateStretchedHide ); + private static readonly ConstructCallback RunicKit = new ConstructCallback( CreateRunicKit ); + private static readonly ConstructCallback Tapestry = new ConstructCallback( CreateTapestry ); + private static readonly ConstructCallback PowerScroll = new ConstructCallback( CreatePowerScroll ); + private static readonly ConstructCallback BearRug = new ConstructCallback( CreateBearRug ); + private static readonly ConstructCallback ClothingBlessDeed = new ConstructCallback( CreateCBD ); + + private static int[][] m_ClothHues = new int[][] + { + new int[]{ 0x483, 0x48C, 0x488, 0x48A }, + new int[]{ 0x495, 0x48B, 0x486, 0x485 }, + new int[]{ 0x48D, 0x490, 0x48E, 0x491 }, + new int[]{ 0x48F, 0x494, 0x484, 0x497 }, + new int[]{ 0x489, 0x47F, 0x482, 0x47E } + }; + + private static Item CreateCloth( int type ) + { + if ( type >= 0 && type < m_ClothHues.Length ) + { + UncutCloth cloth = new UncutCloth( 100 ); + cloth.Hue = m_ClothHues[type][Utility.Random( m_ClothHues[type].Length )]; + return cloth; + } + + throw new InvalidOperationException(); + } + + private static int[] m_SandalHues = new int[] + { + 0x489, 0x47F, 0x482, + 0x47E, 0x48F, 0x494, + 0x484, 0x497 + }; + + private static Item CreateSandals( int type ) + { + return new Sandals( m_SandalHues[Utility.Random( m_SandalHues.Length )] ); + } + + private static Item CreateStretchedHide( int type ) + { + switch ( Utility.Random( 4 ) ) + { + default: + case 0: return new SmallStretchedHideEastDeed(); + case 1: return new SmallStretchedHideSouthDeed(); + case 2: return new MediumStretchedHideEastDeed(); + case 3: return new MediumStretchedHideSouthDeed(); + } + } + + private static Item CreateTapestry( int type ) + { + switch ( Utility.Random( 4 ) ) + { + default: + case 0: return new LightFlowerTapestryEastDeed(); + case 1: return new LightFlowerTapestrySouthDeed(); + case 2: return new DarkFlowerTapestryEastDeed(); + case 3: return new DarkFlowerTapestrySouthDeed(); + } + } + + private static Item CreateBearRug( int type ) + { + switch ( Utility.Random( 4 ) ) + { + default: + case 0: return new BrownBearRugEastDeed(); + case 1: return new BrownBearRugSouthDeed(); + case 2: return new PolarBearRugEastDeed(); + case 3: return new PolarBearRugSouthDeed(); + } + } + + private static Item CreateRunicKit( int type ) + { + if ( type >= 1 && type <= 3 ) + return new RunicSewingKit( CraftResource.RegularLeather + type, 60 - (type*15) ); + + throw new InvalidOperationException(); + } + + private static Item CreatePowerScroll( int type ) + { + if ( type == 5 || type == 10 || type == 15 || type == 20 ) + return new PowerScroll( SkillName.Tailoring, 100 + type ); + + throw new InvalidOperationException(); + } + + private static Item CreateCBD( int type ) + { + return new ClothingBlessDeed(); + } + #endregion + + public static readonly TailorRewardCalculator Instance = new TailorRewardCalculator(); + + public override int ComputePoints( int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type ) + { + int points = 0; + + if ( quantity == 10 ) + points += 10; + else if ( quantity == 15 ) + points += 25; + else if ( quantity == 20 ) + points += 50; + + if ( exceptional ) + points += 100; + + if ( itemCount == 4 ) + points += 300; + else if ( itemCount == 5 ) + points += 400; + else if ( itemCount == 6 ) + points += 500; + + if ( material == BulkMaterialType.Spined ) + points += 50; + else if ( material == BulkMaterialType.Horned ) + points += 100; + else if ( material == BulkMaterialType.Barbed ) + points += 150; + + return points; + } + + private static int[][][] m_AosGoldTable = new int[][][] + { + new int[][] // 1-part (regular) + { + new int[]{ 150, 150, 300, 300 }, + new int[]{ 225, 225, 450, 450 }, + new int[]{ 300, 400, 600, 750 } + }, + new int[][] // 1-part (exceptional) + { + new int[]{ 300, 300, 600, 600 }, + new int[]{ 450, 450, 900, 900 }, + new int[]{ 600, 750, 1200, 1800 } + }, + new int[][] // 4-part (regular) + { + new int[]{ 4000, 4000, 5000, 5000 }, + new int[]{ 6000, 6000, 7500, 7500 }, + new int[]{ 8000, 10000, 10000, 15000 } + }, + new int[][] // 4-part (exceptional) + { + new int[]{ 5000, 5000, 7500, 7500 }, + new int[]{ 7500, 7500, 11250, 11250 }, + new int[]{ 10000, 15000, 15000, 20000 } + }, + new int[][] // 5-part (regular) + { + new int[]{ 5000, 5000, 7500, 7500 }, + new int[]{ 7500, 7500, 11250, 11250 }, + new int[]{ 10000, 15000, 15000, 20000 } + }, + new int[][] // 5-part (exceptional) + { + new int[]{ 7500, 7500, 10000, 10000 }, + new int[]{ 11250, 11250, 15000, 15000 }, + new int[]{ 15000, 20000, 20000, 30000 } + }, + new int[][] // 6-part (regular) + { + new int[]{ 7500, 7500, 10000, 10000 }, + new int[]{ 11250, 11250, 15000, 15000 }, + new int[]{ 15000, 20000, 20000, 30000 } + }, + new int[][] // 6-part (exceptional) + { + new int[]{ 10000, 10000, 15000, 15000 }, + new int[]{ 15000, 15000, 22500, 22500 }, + new int[]{ 20000, 30000, 30000, 50000 } + } + }; + + private static int[][][] m_OldGoldTable = new int[][][] + { + new int[][] // 1-part (regular) + { + new int[]{ 150, 150, 300, 300 }, + new int[]{ 225, 225, 450, 450 }, + new int[]{ 300, 400, 600, 750 } + }, + new int[][] // 1-part (exceptional) + { + new int[]{ 300, 300, 600, 600 }, + new int[]{ 450, 450, 900, 900 }, + new int[]{ 600, 750, 1200, 1800 } + }, + new int[][] // 4-part (regular) + { + new int[]{ 3000, 3000, 4000, 4000 }, + new int[]{ 4500, 4500, 6000, 6000 }, + new int[]{ 6000, 8000, 8000, 10000 } + }, + new int[][] // 4-part (exceptional) + { + new int[]{ 4000, 4000, 5000, 5000 }, + new int[]{ 6000, 6000, 7500, 7500 }, + new int[]{ 8000, 10000, 10000, 15000 } + }, + new int[][] // 5-part (regular) + { + new int[]{ 4000, 4000, 5000, 5000 }, + new int[]{ 6000, 6000, 7500, 7500 }, + new int[]{ 8000, 10000, 10000, 15000 } + }, + new int[][] // 5-part (exceptional) + { + new int[]{ 5000, 5000, 7500, 7500 }, + new int[]{ 7500, 7500, 11250, 11250 }, + new int[]{ 10000, 15000, 15000, 20000 } + }, + new int[][] // 6-part (regular) + { + new int[]{ 5000, 5000, 7500, 7500 }, + new int[]{ 7500, 7500, 11250, 11250 }, + new int[]{ 10000, 15000, 15000, 20000 } + }, + new int[][] // 6-part (exceptional) + { + new int[]{ 7500, 7500, 10000, 10000 }, + new int[]{ 11250, 11250, 15000, 15000 }, + new int[]{ 15000, 20000, 20000, 30000 } + } + }; + + public override int ComputeGold( int quantity, bool exceptional, BulkMaterialType material, int itemCount, Type type ) + { + int[][][] goldTable = ( Core.AOS ? m_AosGoldTable : m_OldGoldTable ); + + int typeIndex = (( itemCount == 6 ? 3 : itemCount == 5 ? 2 : itemCount == 4 ? 1 : 0 ) * 2) + (exceptional ? 1 : 0); + int quanIndex = ( quantity == 20 ? 2 : quantity == 15 ? 1 : 0 ); + int mtrlIndex = ( material == BulkMaterialType.Barbed ? 3 : material == BulkMaterialType.Horned ? 2 : material == BulkMaterialType.Spined ? 1 : 0 ); + + int gold = goldTable[typeIndex][quanIndex][mtrlIndex]; + + int min = (gold * 9) / 10; + int max = (gold * 10) / 9; + + return Utility.RandomMinMax( min, max ); + } + + public TailorRewardCalculator() + { + Groups = new RewardGroup[] + { + new RewardGroup( 0, new RewardItem( 1, Cloth, 0 ) ), + new RewardGroup( 50, new RewardItem( 1, Cloth, 1 ) ), + new RewardGroup( 100, new RewardItem( 1, Cloth, 2 ) ), + new RewardGroup( 150, new RewardItem( 9, Cloth, 3 ), new RewardItem( 1, Sandals ) ), + new RewardGroup( 200, new RewardItem( 4, Cloth, 4 ), new RewardItem( 1, Sandals ) ), + new RewardGroup( 300, new RewardItem( 1, StretchedHide ) ), + new RewardGroup( 350, new RewardItem( 1, RunicKit, 1 ) ), + new RewardGroup( 400, new RewardItem( 2, PowerScroll, 5 ), new RewardItem( 3, Tapestry ) ), + new RewardGroup( 450, new RewardItem( 1, BearRug ) ), + new RewardGroup( 500, new RewardItem( 1, PowerScroll, 10 ) ), + new RewardGroup( 550, new RewardItem( 1, ClothingBlessDeed ) ), + new RewardGroup( 575, new RewardItem( 1, PowerScroll, 15 ) ), + new RewardGroup( 600, new RewardItem( 1, RunicKit, 2 ) ), + new RewardGroup( 650, new RewardItem( 1, PowerScroll, 20 ) ), + new RewardGroup( 700, new RewardItem( 1, RunicKit, 3 ) ) + }; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallBOD.cs b/Scripts/Engines/BulkOrders/SmallBOD.cs new file mode 100644 index 000000000..78e927500 --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallBOD.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.BulkOrders +{ + [TypeAlias( "Scripts.Engines.BulkOrders.SmallBOD" )] + public abstract class SmallBOD : Item + { + private int m_AmountCur, m_AmountMax; + private Type m_Type; + private int m_Number; + private int m_Graphic; + private bool m_RequireExceptional; + private BulkMaterialType m_Material; + + [CommandProperty( AccessLevel.GameMaster )] + public int AmountCur{ get{ return m_AmountCur; } set{ m_AmountCur = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int AmountMax{ get{ return m_AmountMax; } set{ m_AmountMax = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Type Type{ get{ return m_Type; } set{ m_Type = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Number{ get{ return m_Number; } set{ m_Number = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Graphic{ get{ return m_Graphic; } set{ m_Graphic = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool RequireExceptional{ get{ return m_RequireExceptional; } set{ m_RequireExceptional = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public BulkMaterialType Material{ get{ return m_Material; } set{ m_Material = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Complete{ get{ return ( m_AmountCur == m_AmountMax ); } } + + public override int LabelNumber{ get{ return 1045151; } } // a bulk order deed + + [Constructable] + public SmallBOD( int hue, int amountMax, Type type, int number, int graphic, bool requireExeptional, BulkMaterialType material ) : base( Core.AOS ? 0x2258 : 0x14EF ) + { + Weight = 1.0; + Hue = hue; // Blacksmith: 0x44E; Tailoring: 0x483 + LootType = LootType.Blessed; + + m_AmountMax = amountMax; + m_Type = type; + m_Number = number; + m_Graphic = graphic; + m_RequireExceptional = requireExeptional; + m_Material = material; + } + + public SmallBOD() : base( Core.AOS ? 0x2258 : 0x14EF ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public static BulkMaterialType GetRandomMaterial( BulkMaterialType start, double[] chances ) + { + double random = Utility.RandomDouble(); + + for ( int i = 0; i < chances.Length; ++i ) + { + if ( random < chances[i] ) + return ( i == 0 ? BulkMaterialType.None : start + (i - 1) ); + + random -= chances[i]; + } + + return BulkMaterialType.None; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060654 ); // small bulk order + + if ( m_RequireExceptional ) + list.Add( 1045141 ); // All items must be exceptional. + + if ( m_Material != BulkMaterialType.None ) + list.Add( SmallBODGump.GetMaterialNumberFor( m_Material ) ); // All items must be made with x material. + + list.Add( 1060656, m_AmountMax.ToString() ); // amount to make: ~1_val~ + list.Add( 1060658, "#{0}\t{1}", m_Number, m_AmountCur ); // ~1_val~: ~2_val~ + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + from.SendGump( new SmallBODGump( from, this ) ); + else + from.SendLocalizedMessage( 1045156 ); // You must have the deed in your backpack to use it. + } + + public void BeginCombine( Mobile from ) + { + if ( m_AmountCur < m_AmountMax ) + from.Target = new SmallBODTarget( this ); + else + from.SendLocalizedMessage( 1045166 ); // The maximum amount of requested items have already been combined to this deed. + } + + public abstract ArrayList ComputeRewards( bool full ); + public abstract int ComputeGold(); + public abstract int ComputeFame(); + + public virtual void GetRewards( out Item reward, out int gold, out int fame ) + { + reward = null; + gold = ComputeGold(); + fame = ComputeFame(); + + ArrayList rewards = ComputeRewards( false ); + + if ( rewards.Count > 0 ) + { + reward = (Item)rewards[Utility.Random( rewards.Count )]; + + for ( int i = 0; i < rewards.Count; ++i ) + { + if ( rewards[i] != reward ) + ((Item)rewards[i]).Delete(); + } + } + } + + public static BulkMaterialType GetMaterial( CraftResource resource ) + { + switch ( resource ) + { + case CraftResource.DullCopper: return BulkMaterialType.DullCopper; + case CraftResource.ShadowIron: return BulkMaterialType.ShadowIron; + case CraftResource.Copper: return BulkMaterialType.Copper; + case CraftResource.Bronze: return BulkMaterialType.Bronze; + case CraftResource.Gold: return BulkMaterialType.Gold; + case CraftResource.Agapite: return BulkMaterialType.Agapite; + case CraftResource.Verite: return BulkMaterialType.Verite; + case CraftResource.Valorite: return BulkMaterialType.Valorite; + case CraftResource.SpinedLeather: return BulkMaterialType.Spined; + case CraftResource.HornedLeather: return BulkMaterialType.Horned; + case CraftResource.BarbedLeather: return BulkMaterialType.Barbed; + } + + return BulkMaterialType.None; + } + + public void EndCombine( Mobile from, object o ) + { + if ( o is Item && ((Item)o).IsChildOf( from.Backpack ) ) + { + Type objectType = o.GetType(); + + if ( m_AmountCur >= m_AmountMax ) + { + from.SendLocalizedMessage( 1045166 ); // The maximum amount of requested items have already been combined to this deed. + } + else if ( m_Type == null || (objectType != m_Type && !objectType.IsSubclassOf( m_Type )) || (!(o is BaseWeapon) && !(o is BaseArmor) && !(o is BaseClothing)) ) + { + from.SendLocalizedMessage( 1045169 ); // The item is not in the request. + } + else + { + BulkMaterialType material = BulkMaterialType.None; + + if ( o is BaseArmor ) + material = GetMaterial( ((BaseArmor)o).Resource ); + else if ( o is BaseClothing ) + material = GetMaterial( ((BaseClothing)o).Resource ); + + if ( m_Material >= BulkMaterialType.DullCopper && m_Material <= BulkMaterialType.Valorite && material != m_Material ) + { + from.SendLocalizedMessage( 1045168 ); // The item is not made from the requested ore. + } + else if ( m_Material >= BulkMaterialType.Spined && m_Material <= BulkMaterialType.Barbed && material != m_Material ) + { + from.SendLocalizedMessage( 1049352 ); // The item is not made from the requested leather type. + } + else + { + bool isExceptional = false; + + if ( o is BaseWeapon ) + isExceptional = ( ((BaseWeapon)o).Quality == WeaponQuality.Exceptional ); + else if ( o is BaseArmor ) + isExceptional = ( ((BaseArmor)o).Quality == ArmorQuality.Exceptional ); + else if ( o is BaseClothing ) + isExceptional = ( ((BaseClothing)o).Quality == ClothingQuality.Exceptional ); + + if ( m_RequireExceptional && !isExceptional ) + { + from.SendLocalizedMessage( 1045167 ); // The item must be exceptional. + } + else + { + ((Item)o).Delete(); + ++AmountCur; + + from.SendLocalizedMessage( 1045170 ); // The item has been combined with the deed. + + from.SendGump( new SmallBODGump( from, this ) ); + + if ( m_AmountCur < m_AmountMax ) + BeginCombine( from ); + } + } + } + } + else + { + from.SendLocalizedMessage( 1045158 ); // You must have the item in your backpack to target it. + } + } + + public SmallBOD( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_AmountCur ); + writer.Write( m_AmountMax ); + writer.Write( m_Type == null ? null : m_Type.FullName ); + writer.Write( m_Number ); + writer.Write( m_Graphic ); + writer.Write( m_RequireExceptional ); + writer.Write( (int) m_Material ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_AmountCur = reader.ReadInt(); + m_AmountMax = reader.ReadInt(); + + string type = reader.ReadString(); + + if ( type != null ) + m_Type = ScriptCompiler.FindTypeByFullName( type ); + + m_Number = reader.ReadInt(); + m_Graphic = reader.ReadInt(); + m_RequireExceptional = reader.ReadBool(); + m_Material = (BulkMaterialType)reader.ReadInt(); + + break; + } + } + + if ( Weight == 0.0 ) + Weight = 1.0; + + if ( Core.AOS && ItemID == 0x14EF ) + ItemID = 0x2258; + + if ( Parent == null && Map == Map.Internal && Location == Point3D.Zero ) + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallBODAcceptGump.cs b/Scripts/Engines/BulkOrders/SmallBODAcceptGump.cs new file mode 100644 index 000000000..8e9aadb18 --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallBODAcceptGump.cs @@ -0,0 +1,93 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.BulkOrders +{ + public class SmallBODAcceptGump : Gump + { + private SmallBOD m_Deed; + private Mobile m_From; + + public SmallBODAcceptGump( Mobile from, SmallBOD deed ) : base( 50, 50 ) + { + m_From = from; + m_Deed = deed; + + m_From.CloseGump( typeof( LargeBODAcceptGump ) ); + m_From.CloseGump( typeof( SmallBODAcceptGump ) ); + + AddPage( 0 ); + + AddBackground( 25, 10, 430, 264, 5054 ); + + AddImageTiled( 33, 20, 413, 245, 2624 ); + AddAlphaRegion( 33, 20, 413, 245 ); + + AddImage( 20, 5, 10460 ); + AddImage( 430, 5, 10460 ); + AddImage( 20, 249, 10460 ); + AddImage( 430, 249, 10460 ); + + AddHtmlLocalized( 190, 25, 120, 20, 1045133, 0x7FFF, false, false ); // A bulk order + AddHtmlLocalized( 40, 48, 350, 20, 1045135, 0x7FFF, false, false ); // Ah! Thanks for the goods! Would you help me out? + + AddHtmlLocalized( 40, 72, 210, 20, 1045138, 0x7FFF, false, false ); // Amount to make: + AddLabel( 250, 72, 1152, deed.AmountMax.ToString() ); + + AddHtmlLocalized( 40, 96, 120, 20, 1045136, 0x7FFF, false, false ); // Item requested: + AddItem( 385, 96, deed.Graphic ); + AddHtmlLocalized( 40, 120, 210, 20, deed.Number, 0xFFFFFF, false, false ); + + if ( deed.RequireExceptional || deed.Material != BulkMaterialType.None ) + { + AddHtmlLocalized( 40, 144, 210, 20, 1045140, 0x7FFF, false, false ); // Special requirements to meet: + + if ( deed.RequireExceptional ) + AddHtmlLocalized( 40, 168, 350, 20, 1045141, 0x7FFF, false, false ); // All items must be exceptional. + + if ( deed.Material != BulkMaterialType.None ) + AddHtmlLocalized( 40, deed.RequireExceptional ? 192 : 168, 350, 20, GetMaterialNumberFor( deed.Material ), 0x7FFF, false, false ); // All items must be made with x material. + } + + AddHtmlLocalized( 40, 216, 350, 20, 1045139, 0x7FFF, false, false ); // Do you want to accept this order? + + AddButton( 100, 240, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 135, 240, 120, 20, 1006044, 0x7FFF, false, false ); // Ok + + AddButton( 275, 240, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 310, 240, 120, 20, 1011012, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) // Ok + { + if ( m_From.PlaceInBackpack( m_Deed ) ) + { + m_From.SendLocalizedMessage( 1045152 ); // The bulk order deed has been placed in your backpack. + } + else + { + m_From.SendLocalizedMessage( 1045150 ); // There is not enough room in your backpack for the deed. + m_Deed.Delete(); + } + } + else + { + m_Deed.Delete(); + } + } + + public static int GetMaterialNumberFor( BulkMaterialType material ) + { + if ( material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Valorite ) + return 1045142 + (int)(material - BulkMaterialType.DullCopper); + else if ( material >= BulkMaterialType.Spined && material <= BulkMaterialType.Barbed ) + return 1049348 + (int)(material - BulkMaterialType.Spined); + + return 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallBODGump.cs b/Scripts/Engines/BulkOrders/SmallBODGump.cs new file mode 100644 index 000000000..4deba0bb3 --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallBODGump.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.BulkOrders +{ + public class SmallBODGump : Gump + { + private SmallBOD m_Deed; + private Mobile m_From; + + public SmallBODGump( Mobile from, SmallBOD deed ) : base( 25, 25 ) + { + m_From = from; + m_Deed = deed; + + m_From.CloseGump( typeof( LargeBODGump ) ); + m_From.CloseGump( typeof( SmallBODGump ) ); + + AddPage( 0 ); + + AddBackground( 50, 10, 455, 260, 5054 ); + AddImageTiled( 58, 20, 438, 241, 2624 ); + AddAlphaRegion( 58, 20, 438, 241 ); + + AddImage( 45, 5, 10460 ); + AddImage( 480, 5, 10460 ); + AddImage( 45, 245, 10460 ); + AddImage( 480, 245, 10460 ); + + AddHtmlLocalized( 225, 25, 120, 20, 1045133, 0x7FFF, false, false ); // A bulk order + + AddHtmlLocalized( 75, 48, 250, 20, 1045138, 0x7FFF, false, false ); // Amount to make: + AddLabel( 275, 48, 1152, deed.AmountMax.ToString() ); + + AddHtmlLocalized( 275, 76, 200, 20, 1045153, 0x7FFF, false, false ); // Amount finished: + AddHtmlLocalized( 75, 72, 120, 20, 1045136, 0x7FFF, false, false ); // Item requested: + + AddItem( 410, 72, deed.Graphic ); + + AddHtmlLocalized( 75, 96, 210, 20, deed.Number, 0x7FFF, false, false ); + AddLabel( 275, 96, 0x480, deed.AmountCur.ToString() ); + + if ( deed.RequireExceptional || deed.Material != BulkMaterialType.None ) + AddHtmlLocalized( 75, 120, 200, 20, 1045140, 0x7FFF, false, false ); // Special requirements to meet: + + if ( deed.RequireExceptional ) + AddHtmlLocalized( 75, 144, 300, 20, 1045141, 0x7FFF, false, false ); // All items must be exceptional. + + if ( deed.Material != BulkMaterialType.None ) + AddHtmlLocalized( 75, deed.RequireExceptional ? 168 : 144, 300, 20, GetMaterialNumberFor( deed.Material ), 0x7FFF, false, false ); // All items must be made with x material. + + AddButton( 125, 192, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 160, 192, 300, 20, 1045154, 0x7FFF, false, false ); // Combine this deed with the item requested. + + AddButton( 125, 216, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 160, 216, 120, 20, 1011441, 0x7FFF, false, false ); // EXIT + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted || !m_Deed.IsChildOf( m_From.Backpack ) ) + return; + + if ( info.ButtonID == 2 ) // Combine + { + m_From.SendGump( new SmallBODGump( m_From, m_Deed ) ); + m_Deed.BeginCombine( m_From ); + } + } + + public static int GetMaterialNumberFor( BulkMaterialType material ) + { + if ( material >= BulkMaterialType.DullCopper && material <= BulkMaterialType.Valorite ) + return 1045142 + (int)(material - BulkMaterialType.DullCopper); + else if ( material >= BulkMaterialType.Spined && material <= BulkMaterialType.Barbed ) + return 1049348 + (int)(material - BulkMaterialType.Spined); + + return 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallBODTarget.cs b/Scripts/Engines/BulkOrders/SmallBODTarget.cs new file mode 100644 index 000000000..c59ead7ed --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallBODTarget.cs @@ -0,0 +1,25 @@ +using System; +using Server; +using Server.Targeting; +using Server.Network; + +namespace Server.Engines.BulkOrders +{ + public class SmallBODTarget : Target + { + private SmallBOD m_Deed; + + public SmallBODTarget( SmallBOD deed ) : base( 18, false, TargetFlags.None ) + { + m_Deed = deed; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Deed.Deleted || !m_Deed.IsChildOf( from.Backpack ) ) + return; + + m_Deed.EndCombine( from, targeted ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallBulkEntry.cs b/Scripts/Engines/BulkOrders/SmallBulkEntry.cs new file mode 100644 index 000000000..d0da3707f --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallBulkEntry.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Server; + +namespace Server.Engines.BulkOrders +{ + public class SmallBulkEntry + { + private Type m_Type; + private int m_Number; + private int m_Graphic; + + public Type Type{ get{ return m_Type; } } + public int Number{ get{ return m_Number; } } + public int Graphic{ get{ return m_Graphic; } } + + public SmallBulkEntry( Type type, int number, int graphic ) + { + m_Type = type; + m_Number = number; + m_Graphic = graphic; + } + + public static SmallBulkEntry[] BlacksmithWeapons + { + get{ return GetEntries( "Blacksmith", "weapons" ); } + } + + public static SmallBulkEntry[] BlacksmithArmor + { + get{ return GetEntries( "Blacksmith", "armor" ); } + } + + public static SmallBulkEntry[] TailorCloth + { + get{ return GetEntries( "Tailoring", "cloth" ); } + } + + public static SmallBulkEntry[] TailorLeather + { + get{ return GetEntries( "Tailoring", "leather" ); } + } + + private static Hashtable m_Cache; + + public static SmallBulkEntry[] GetEntries( string type, string name ) + { + if ( m_Cache == null ) + m_Cache = new Hashtable(); + + Hashtable table = (Hashtable)m_Cache[type]; + + if ( table == null ) + m_Cache[type] = table = new Hashtable(); + + SmallBulkEntry[] entries = (SmallBulkEntry[])table[name]; + + if ( entries == null ) + table[name] = entries = LoadEntries( type, name ); + + return entries; + } + + public static SmallBulkEntry[] LoadEntries( string type, string name ) + { + return LoadEntries( String.Format( "Data/Bulk Orders/{0}/{1}.cfg", type, name ) ); + } + + public static SmallBulkEntry[] LoadEntries( string path ) + { + path = Path.Combine( Core.BaseDirectory, path ); + + List list = new List(); + + if ( File.Exists( path ) ) + { + using ( StreamReader ip = new StreamReader( path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + if ( line.Length == 0 || line.StartsWith( "#" ) ) + continue; + + try + { + string[] split = line.Split( '\t' ); + + if ( split.Length >= 2 ) + { + Type type = ScriptCompiler.FindTypeByName( split[0] ); + int graphic = Utility.ToInt32( split[split.Length - 1] ); + + if ( type != null && graphic > 0 ) + list.Add( new SmallBulkEntry( type, 1020000 + graphic, graphic ) ); + } + } + catch + { + } + } + } + } + + return list.ToArray(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallSmithBOD.cs b/Scripts/Engines/BulkOrders/SmallSmithBOD.cs new file mode 100644 index 000000000..bf6f96b5c --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallSmithBOD.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.Craft; +using Mat = Server.Engines.BulkOrders.BulkMaterialType; + +namespace Server.Engines.BulkOrders +{ + [TypeAlias( "Scripts.Engines.BulkOrders.SmallSmithBOD" )] + public class SmallSmithBOD : SmallBOD + { + public static double[] m_BlacksmithMaterialChances = new double[] + { + 0.501953125, // None + 0.250000000, // Dull Copper + 0.125000000, // Shadow Iron + 0.062500000, // Copper + 0.031250000, // Bronze + 0.015625000, // Gold + 0.007812500, // Agapite + 0.003906250, // Verite + 0.001953125 // Valorite + }; + + public override int ComputeFame() + { + return SmithRewardCalculator.Instance.ComputeFame( this ); + } + + public override int ComputeGold() + { + return SmithRewardCalculator.Instance.ComputeGold( this ); + } + + public override ArrayList ComputeRewards( bool full ) + { + ArrayList list = new ArrayList(); + + RewardGroup rewardGroup = SmithRewardCalculator.Instance.LookupRewards( SmithRewardCalculator.Instance.ComputePoints( this ) ); + + if ( rewardGroup != null ) + { + if ( full ) + { + for ( int i = 0; i < rewardGroup.Items.Length; ++i ) + { + Item item = rewardGroup.Items[i].Construct(); + + if ( item != null ) + list.Add( item ); + } + } + else + { + RewardItem rewardItem = rewardGroup.AcquireItem(); + + if ( rewardItem != null ) + { + Item item = rewardItem.Construct(); + + if ( item != null ) + list.Add( item ); + } + } + } + + return list; + } + + public static SmallSmithBOD CreateRandomFor( Mobile m ) + { + SmallBulkEntry[] entries; + bool useMaterials; + + if ( useMaterials = Utility.RandomBool() ) + entries = SmallBulkEntry.BlacksmithArmor; + else + entries = SmallBulkEntry.BlacksmithWeapons; + + if ( entries.Length > 0 ) + { + double theirSkill = m.Skills[SkillName.Blacksmith].Base; + int amountMax; + + if ( theirSkill >= 70.1 ) + amountMax = Utility.RandomList( 10, 15, 20, 20 ); + else if ( theirSkill >= 50.1 ) + amountMax = Utility.RandomList( 10, 15, 15, 20 ); + else + amountMax = Utility.RandomList( 10, 10, 15, 20 ); + + BulkMaterialType material = BulkMaterialType.None; + + if ( useMaterials && theirSkill >= 70.1 ) + { + for ( int i = 0; i < 20; ++i ) + { + BulkMaterialType check = GetRandomMaterial( BulkMaterialType.DullCopper, m_BlacksmithMaterialChances ); + double skillReq = 0.0; + + switch ( check ) + { + case BulkMaterialType.DullCopper: skillReq = 65.0; break; + case BulkMaterialType.ShadowIron: skillReq = 70.0; break; + case BulkMaterialType.Copper: skillReq = 75.0; break; + case BulkMaterialType.Bronze: skillReq = 80.0; break; + case BulkMaterialType.Gold: skillReq = 85.0; break; + case BulkMaterialType.Agapite: skillReq = 90.0; break; + case BulkMaterialType.Verite: skillReq = 95.0; break; + case BulkMaterialType.Valorite: skillReq = 100.0; break; + case BulkMaterialType.Spined: skillReq = 65.0; break; + case BulkMaterialType.Horned: skillReq = 80.0; break; + case BulkMaterialType.Barbed: skillReq = 99.0; break; + } + + if ( theirSkill >= skillReq ) + { + material = check; + break; + } + } + } + + double excChance = 0.0; + + if ( theirSkill >= 70.1 ) + excChance = (theirSkill + 80.0) / 200.0; + + bool reqExceptional = ( excChance > Utility.RandomDouble() ); + + CraftSystem system = DefBlacksmithy.CraftSystem; + + ArrayList validEntries = new ArrayList(); + + for ( int i = 0; i < entries.Length; ++i ) + { + CraftItem item = system.CraftItems.SearchFor( entries[i].Type ); + + if ( item != null ) + { + bool allRequiredSkills = true; + double chance = item.GetSuccessChance( m, null, system, false, ref allRequiredSkills ); + + if ( allRequiredSkills && chance >= 0.0 ) + { + if ( reqExceptional ) + chance = item.GetExceptionalChance( system, chance, m ); + + if ( chance > 0.0 ) + validEntries.Add( entries[i] ); + } + } + } + + if ( validEntries.Count > 0 ) + { + SmallBulkEntry entry = (SmallBulkEntry)validEntries[Utility.Random( validEntries.Count )]; + return new SmallSmithBOD( entry, material, amountMax, reqExceptional ); + } + } + + return null; + } + + private SmallSmithBOD( SmallBulkEntry entry, BulkMaterialType material, int amountMax, bool reqExceptional ) + { + this.Hue = 0x44E; + this.AmountMax = amountMax; + this.Type = entry.Type; + this.Number = entry.Number; + this.Graphic = entry.Graphic; + this.RequireExceptional = reqExceptional; + this.Material = material; + } + + [Constructable] + public SmallSmithBOD() + { + SmallBulkEntry[] entries; + bool useMaterials; + + if ( useMaterials = Utility.RandomBool() ) + entries = SmallBulkEntry.BlacksmithArmor; + else + entries = SmallBulkEntry.BlacksmithWeapons; + + if ( entries.Length > 0 ) + { + int hue = 0x44E; + int amountMax = Utility.RandomList( 10, 15, 20 ); + + BulkMaterialType material; + + if ( useMaterials ) + material = GetRandomMaterial( BulkMaterialType.DullCopper, m_BlacksmithMaterialChances ); + else + material = BulkMaterialType.None; + + bool reqExceptional = Utility.RandomBool() || (material == BulkMaterialType.None); + + SmallBulkEntry entry = entries[Utility.Random( entries.Length )]; + + this.Hue = hue; + this.AmountMax = amountMax; + this.Type = entry.Type; + this.Number = entry.Number; + this.Graphic = entry.Graphic; + this.RequireExceptional = reqExceptional; + this.Material = material; + } + } + + public SmallSmithBOD( int amountCur, int amountMax, Type type, int number, int graphic, bool reqExceptional, BulkMaterialType mat ) + { + this.Hue = 0x44E; + this.AmountMax = amountMax; + this.AmountCur = amountCur; + this.Type = type; + this.Number = number; + this.Graphic = graphic; + this.RequireExceptional = reqExceptional; + this.Material = mat; + } + + public SmallSmithBOD( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/BulkOrders/SmallTailorBOD.cs b/Scripts/Engines/BulkOrders/SmallTailorBOD.cs new file mode 100644 index 000000000..73cbaaf26 --- /dev/null +++ b/Scripts/Engines/BulkOrders/SmallTailorBOD.cs @@ -0,0 +1,236 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.Craft; + +namespace Server.Engines.BulkOrders +{ + public class SmallTailorBOD : SmallBOD + { + public static double[] m_TailoringMaterialChances = new double[] + { + 0.857421875, // None + 0.125000000, // Spined + 0.015625000, // Horned + 0.001953125 // Barbed + }; + + public override int ComputeFame() + { + return TailorRewardCalculator.Instance.ComputeFame( this ); + } + + public override int ComputeGold() + { + return TailorRewardCalculator.Instance.ComputeGold( this ); + } + + public override ArrayList ComputeRewards( bool full ) + { + ArrayList list = new ArrayList(); + + RewardGroup rewardGroup = TailorRewardCalculator.Instance.LookupRewards( TailorRewardCalculator.Instance.ComputePoints( this ) ); + + if ( rewardGroup != null ) + { + if ( full ) + { + for ( int i = 0; i < rewardGroup.Items.Length; ++i ) + { + Item item = rewardGroup.Items[i].Construct(); + + if ( item != null ) + list.Add( item ); + } + } + else + { + RewardItem rewardItem = rewardGroup.AcquireItem(); + + if ( rewardItem != null ) + { + Item item = rewardItem.Construct(); + + if ( item != null ) + list.Add( item ); + } + } + } + + return list; + } + + public static SmallTailorBOD CreateRandomFor( Mobile m ) + { + SmallBulkEntry[] entries; + bool useMaterials; + + double theirSkill = m.Skills[SkillName.Tailoring].Base; + if ( useMaterials = Utility.RandomBool() && theirSkill >= 6.2 ) // Ugly, but the easiest leather BOD is Leather Cap which requires at least 6.2 skill. + entries = SmallBulkEntry.TailorLeather; + else + entries = SmallBulkEntry.TailorCloth; + + if ( entries.Length > 0 ) + { + int amountMax; + + if ( theirSkill >= 70.1 ) + amountMax = Utility.RandomList( 10, 15, 20, 20 ); + else if ( theirSkill >= 50.1 ) + amountMax = Utility.RandomList( 10, 15, 15, 20 ); + else + amountMax = Utility.RandomList( 10, 10, 15, 20 ); + + BulkMaterialType material = BulkMaterialType.None; + + if ( useMaterials && theirSkill >= 70.1 ) + { + for ( int i = 0; i < 20; ++i ) + { + BulkMaterialType check = GetRandomMaterial( BulkMaterialType.Spined, m_TailoringMaterialChances ); + double skillReq = 0.0; + + switch ( check ) + { + case BulkMaterialType.DullCopper: skillReq = 65.0; break; + case BulkMaterialType.Bronze: skillReq = 80.0; break; + case BulkMaterialType.Gold: skillReq = 85.0; break; + case BulkMaterialType.Agapite: skillReq = 90.0; break; + case BulkMaterialType.Verite: skillReq = 95.0; break; + case BulkMaterialType.Valorite: skillReq = 100.0; break; + case BulkMaterialType.Spined: skillReq = 65.0; break; + case BulkMaterialType.Horned: skillReq = 80.0; break; + case BulkMaterialType.Barbed: skillReq = 99.0; break; + } + + if ( theirSkill >= skillReq ) + { + material = check; + break; + } + } + } + + double excChance = 0.0; + + if ( theirSkill >= 70.1 ) + excChance = (theirSkill + 80.0) / 200.0; + + bool reqExceptional = ( excChance > Utility.RandomDouble() ); + + + CraftSystem system = DefTailoring.CraftSystem; + + ArrayList validEntries = new ArrayList(); + + for ( int i = 0; i < entries.Length; ++i ) + { + CraftItem item = system.CraftItems.SearchFor( entries[i].Type ); + + if ( item != null ) + { + bool allRequiredSkills = true; + double chance = item.GetSuccessChance( m, null, system, false, ref allRequiredSkills ); + + if ( allRequiredSkills && chance >= 0.0 ) + { + if ( reqExceptional ) + chance = item.GetExceptionalChance( system, chance, m ); + + if ( chance > 0.0 ) + validEntries.Add( entries[i] ); + } + } + } + + if ( validEntries.Count > 0 ) + { + SmallBulkEntry entry = (SmallBulkEntry)validEntries[Utility.Random( validEntries.Count )]; + return new SmallTailorBOD( entry, material, amountMax, reqExceptional ); + } + } + + return null; + } + + private SmallTailorBOD( SmallBulkEntry entry, BulkMaterialType material, int amountMax, bool reqExceptional ) + { + this.Hue = 0x483; + this.AmountMax = amountMax; + this.Type = entry.Type; + this.Number = entry.Number; + this.Graphic = entry.Graphic; + this.RequireExceptional = reqExceptional; + this.Material = material; + } + + [Constructable] + public SmallTailorBOD() + { + SmallBulkEntry[] entries; + bool useMaterials; + + if ( useMaterials = Utility.RandomBool() ) + entries = SmallBulkEntry.TailorLeather; + else + entries = SmallBulkEntry.TailorCloth; + + if ( entries.Length > 0 ) + { + int hue = 0x483; + int amountMax = Utility.RandomList( 10, 15, 20 ); + + BulkMaterialType material; + + if ( useMaterials ) + material = GetRandomMaterial( BulkMaterialType.Spined, m_TailoringMaterialChances ); + else + material = BulkMaterialType.None; + + bool reqExceptional = Utility.RandomBool() || (material == BulkMaterialType.None); + + SmallBulkEntry entry = entries[Utility.Random( entries.Length )]; + + this.Hue = hue; + this.AmountMax = amountMax; + this.Type = entry.Type; + this.Number = entry.Number; + this.Graphic = entry.Graphic; + this.RequireExceptional = reqExceptional; + this.Material = material; + } + } + + public SmallTailorBOD( int amountCur, int amountMax, Type type, int number, int graphic, bool reqExceptional, BulkMaterialType mat ) + { + this.Hue = 0x483; + this.AmountMax = amountMax; + this.AmountCur = amountCur; + this.Type = type; + this.Number = number; + this.Graphic = graphic; + this.RequireExceptional = reqExceptional; + this.Material = mat; + } + + public SmallTailorBOD( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionAltar.cs b/Scripts/Engines/CannedEvil/ChampionAltar.cs new file mode 100644 index 000000000..7b0c3df7c --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionAltar.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.CannedEvil +{ + public class ChampionAltar : PentagramAddon + { + private ChampionSpawn m_Spawn; + + public ChampionAltar( ChampionSpawn spawn ) + { + m_Spawn = spawn; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Spawn != null ) + m_Spawn.Delete(); + } + + public ChampionAltar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Spawn ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Spawn = reader.ReadItem() as ChampionSpawn; + + if ( m_Spawn == null ) + Delete(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionPlatform.cs b/Scripts/Engines/CannedEvil/ChampionPlatform.cs new file mode 100644 index 000000000..f808f2150 --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionPlatform.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.CannedEvil +{ + public class ChampionPlatform : BaseAddon + { + private ChampionSpawn m_Spawn; + + public ChampionPlatform( ChampionSpawn spawn ) + { + m_Spawn = spawn; + + for ( int x = -2; x <= 2; ++x ) + for ( int y = -2; y <= 2; ++y ) + AddComponent( 0x750, x, y, -5 ); + + for ( int x = -1; x <= 1; ++x ) + for ( int y = -1; y <= 1; ++y ) + AddComponent( 0x750, x, y, 0 ); + + for ( int i = -1; i <= 1; ++i ) + { + AddComponent( 0x751, i, 2, 0 ); + AddComponent( 0x752, 2, i, 0 ); + + AddComponent( 0x753, i, -2, 0 ); + AddComponent( 0x754, -2, i, 0 ); + } + + AddComponent( 0x759, -2, -2, 0 ); + AddComponent( 0x75A, 2, 2, 0 ); + AddComponent( 0x75B, -2, 2, 0 ); + AddComponent( 0x75C, 2, -2, 0 ); + } + + public void AddComponent( int id, int x, int y, int z ) + { + AddonComponent ac = new AddonComponent( id ); + + ac.Hue = 0x497; + + AddComponent( ac, x, y, z ); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Spawn != null ) + m_Spawn.Delete(); + } + + public ChampionPlatform( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Spawn ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Spawn = reader.ReadItem() as ChampionSpawn; + + if ( m_Spawn == null ) + Delete(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionSkull.cs b/Scripts/Engines/CannedEvil/ChampionSkull.cs new file mode 100644 index 000000000..ce0710f1f --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionSkull.cs @@ -0,0 +1,73 @@ +using System; +using Server; +using Server.Engines.CannedEvil; + +namespace Server.Items +{ + public class ChampionSkull : Item + { + private ChampionSkullType m_Type; + + [CommandProperty( AccessLevel.GameMaster )] + public ChampionSkullType Type{ get{ return m_Type; } set{ m_Type = value; InvalidateProperties(); } } + + public override int LabelNumber{ get{ return 1049479 + (int)m_Type; } } + + [Constructable] + public ChampionSkull( ChampionSkullType type ) : base( 0x1AE1 ) + { + m_Type = type; + LootType = LootType.Cursed; + + // TODO: All hue values + switch ( type ) + { + case ChampionSkullType.Power: Hue = 0x159; break; + case ChampionSkullType.Venom: Hue = 0x172; break; + case ChampionSkullType.Greed: Hue = 0x1EE; break; + case ChampionSkullType.Death: Hue = 0x025; break; + case ChampionSkullType.Pain: Hue = 0x035; break; + } + } + + public ChampionSkull( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Type ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_Type = (ChampionSkullType)reader.ReadInt(); + + break; + } + } + + if( version == 0 ) + { + if ( LootType != LootType.Cursed ) + LootType = LootType.Cursed; + + if ( Insured ) + Insured = false; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionSkullBrazier.cs b/Scripts/Engines/CannedEvil/ChampionSkullBrazier.cs new file mode 100644 index 000000000..c45e7d457 --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionSkullBrazier.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Engines.CannedEvil +{ + public class ChampionSkullBrazier : AddonComponent + { + private ChampionSkullPlatform m_Platform; + private ChampionSkullType m_Type; + private Item m_Skull; + + [CommandProperty( AccessLevel.GameMaster )] + public ChampionSkullPlatform Platform{ get{ return m_Platform; } } + + [CommandProperty( AccessLevel.GameMaster )] + public ChampionSkullType Type{ get{ return m_Type; } set{ m_Type = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Item Skull{ get{ return m_Skull; } set{ m_Skull = value; if ( m_Platform != null ) m_Platform.Validate(); } } + + public override int LabelNumber{ get{ return 1049489 + (int)m_Type; } } + + public ChampionSkullBrazier( ChampionSkullPlatform platform, ChampionSkullType type ) : base( 0x19BB ) + { + Hue = 0x455; + Light = LightType.Circle300; + + m_Platform = platform; + m_Type = type; + } + + public ChampionSkullBrazier( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Platform != null ) + m_Platform.Validate(); + + BeginSacrifice( from ); + } + + public void BeginSacrifice( Mobile from ) + { + if ( Deleted ) + return; + + if ( m_Skull != null && m_Skull.Deleted ) + Skull = null; + + if ( from.Map != this.Map || !from.InRange( GetWorldLocation(), 3 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if ( !Harrower.CanSpawn ) + { + from.SendMessage( "The harrower has already been spawned." ); + } + else if ( m_Skull == null ) + { + from.SendLocalizedMessage( 1049485 ); // What would you like to sacrifice? + from.Target = new SacrificeTarget( this ); + } + else + { + SendLocalizedMessageTo( from, 1049487, "" ); // I already have my champions awakening skull! + } + } + + public void EndSacrifice( Mobile from, ChampionSkull skull ) + { + if ( Deleted ) + return; + + if ( m_Skull != null && m_Skull.Deleted ) + Skull = null; + + if ( from.Map != this.Map || !from.InRange( GetWorldLocation(), 3 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if ( !Harrower.CanSpawn ) + { + from.SendMessage( "The harrower has already been spawned." ); + } + else if ( skull == null ) + { + SendLocalizedMessageTo( from, 1049488, "" ); // That is not my champions awakening skull! + } + else if ( m_Skull != null ) + { + SendLocalizedMessageTo( from, 1049487, "" ); // I already have my champions awakening skull! + } + else if ( !skull.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1049486 ); // You can only sacrifice items that are in your backpack! + } + else + { + if ( skull.Type == this.Type ) + { + skull.Movable = false; + skull.MoveToWorld( GetWorldTop(), this.Map ); + + this.Skull = skull; + } + else + { + SendLocalizedMessageTo( from, 1049488, "" ); // That is not my champions awakening skull! + } + } + } + + private class SacrificeTarget : Target + { + private ChampionSkullBrazier m_Brazier; + + public SacrificeTarget( ChampionSkullBrazier brazier ) : base( 12, false, TargetFlags.None ) + { + m_Brazier = brazier; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + m_Brazier.EndSacrifice( from, targeted as ChampionSkull ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Type ); + writer.Write( m_Platform ); + writer.Write( m_Skull ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Type = (ChampionSkullType)reader.ReadInt(); + m_Platform = reader.ReadItem() as ChampionSkullPlatform; + m_Skull = reader.ReadItem(); + + if ( m_Platform == null ) + Delete(); + + break; + } + } + + if ( Hue == 0x497 ) + Hue = 0x455; + + if ( Light != LightType.Circle300 ) + Light = LightType.Circle300; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionSkullPlatform.cs b/Scripts/Engines/CannedEvil/ChampionSkullPlatform.cs new file mode 100644 index 000000000..009c38d3e --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionSkullPlatform.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.CannedEvil +{ + public class ChampionSkullPlatform : BaseAddon + { + private ChampionSkullBrazier m_Power, m_Enlightenment, m_Venom, m_Pain, m_Greed, m_Death; + + [Constructable] + public ChampionSkullPlatform() + { + AddComponent( new AddonComponent( 0x71A ), -1, -1, -1 ); + AddComponent( new AddonComponent( 0x709 ), 0, -1, -1 ); + AddComponent( new AddonComponent( 0x709 ), 1, -1, -1 ); + AddComponent( new AddonComponent( 0x709 ), -1, 0, -1 ); + AddComponent( new AddonComponent( 0x709 ), 0, 0, -1 ); + AddComponent( new AddonComponent( 0x709 ), 1, 0, -1 ); + AddComponent( new AddonComponent( 0x709 ), -1, 1, -1 ); + AddComponent( new AddonComponent( 0x709 ), 0, 1, -1 ); + AddComponent( new AddonComponent( 0x71B ), 1, 1, -1 ); + + AddComponent( new AddonComponent( 0x50F ), 0, -1, 4 ); + AddComponent( m_Power = new ChampionSkullBrazier( this, ChampionSkullType.Power ), 0, -1, 5 ); + + AddComponent( new AddonComponent( 0x50F ), 1, -1, 4 ); + AddComponent( m_Enlightenment = new ChampionSkullBrazier( this, ChampionSkullType.Enlightenment ), 1, -1, 5 ); + + AddComponent( new AddonComponent( 0x50F ), -1, 0, 4 ); + AddComponent( m_Venom = new ChampionSkullBrazier( this, ChampionSkullType.Venom ), -1, 0, 5 ); + + AddComponent( new AddonComponent( 0x50F ), 1, 0, 4 ); + AddComponent( m_Pain = new ChampionSkullBrazier( this, ChampionSkullType.Pain ), 1, 0, 5 ); + + AddComponent( new AddonComponent( 0x50F ), -1, 1, 4 ); + AddComponent( m_Greed = new ChampionSkullBrazier( this, ChampionSkullType.Greed ), -1, 1, 5 ); + + AddComponent( new AddonComponent( 0x50F ), 0, 1, 4 ); + AddComponent( m_Death = new ChampionSkullBrazier( this, ChampionSkullType.Death ), 0, 1, 5 ); + + AddonComponent comp = new LocalizedAddonComponent( 0x20D2, 1049495 ); + comp.Hue = 0x482; + AddComponent( comp, 0, 0, 5 ); + + comp = new LocalizedAddonComponent( 0x0BCF, 1049496 ); + comp.Hue = 0x482; + AddComponent( comp, 0, 2, -7 ); + + comp = new LocalizedAddonComponent( 0x0BD0, 1049497 ); + comp.Hue = 0x482; + AddComponent( comp, 2, 0, -7 ); + } + + public void Validate() + { + if ( Validate( m_Power ) && Validate( m_Enlightenment ) && Validate( m_Venom ) && Validate( m_Pain ) && Validate( m_Greed ) && Validate( m_Death ) ) + { + Mobile harrower = Harrower.Spawn( new Point3D( X, Y, Z + 6 ), this.Map ); + + if ( harrower == null ) + return; + + Clear( m_Power ); + Clear( m_Enlightenment ); + Clear( m_Venom ); + Clear( m_Pain ); + Clear( m_Greed ); + Clear( m_Death ); + } + } + + public void Clear( ChampionSkullBrazier brazier ) + { + if ( brazier != null ) + { + Effects.SendBoltEffect( brazier ); + + if ( brazier.Skull != null ) + brazier.Skull.Delete(); + } + } + + public bool Validate( ChampionSkullBrazier brazier ) + { + return ( brazier != null && brazier.Skull != null && !brazier.Skull.Deleted ); + } + + public ChampionSkullPlatform( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Power ); + writer.Write( m_Enlightenment ); + writer.Write( m_Venom ); + writer.Write( m_Pain ); + writer.Write( m_Greed ); + writer.Write( m_Death ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Power = reader.ReadItem() as ChampionSkullBrazier; + m_Enlightenment = reader.ReadItem() as ChampionSkullBrazier; + m_Venom = reader.ReadItem() as ChampionSkullBrazier; + m_Pain = reader.ReadItem() as ChampionSkullBrazier; + m_Greed = reader.ReadItem() as ChampionSkullBrazier; + m_Death = reader.ReadItem() as ChampionSkullBrazier; + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionSkullType.cs b/Scripts/Engines/CannedEvil/ChampionSkullType.cs new file mode 100644 index 000000000..7fcd959e4 --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionSkullType.cs @@ -0,0 +1,15 @@ +using System; +using Server; + +namespace Server.Engines.CannedEvil +{ + public enum ChampionSkullType + { + Power, + Enlightenment, + Venom, + Pain, + Greed, + Death + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionSpawn.cs b/Scripts/Engines/CannedEvil/ChampionSpawn.cs new file mode 100644 index 000000000..a81e2f41c --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionSpawn.cs @@ -0,0 +1,1076 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Regions; + +namespace Server.Engines.CannedEvil +{ + public class ChampionSpawn : Item + { + private bool m_Active; + private bool m_RandomizeType; + private ChampionSpawnType m_Type; + private ArrayList m_Creatures; + private ArrayList m_RedSkulls; + private ArrayList m_WhiteSkulls; + private ChampionPlatform m_Platform; + private ChampionAltar m_Altar; + private int m_Kills; + private Mobile m_Champion; + + //private int m_SpawnRange; + private Rectangle2D m_SpawnArea; + private ChampionSpawnRegion m_Region; + + private TimeSpan m_ExpireDelay; + private DateTime m_ExpireTime; + + private TimeSpan m_RestartDelay; + private DateTime m_RestartTime; + + private Timer m_Timer, m_RestartTimer; + + private IdolOfTheChampion m_Idol; + + private bool m_HasBeenAdvanced; + private bool m_ConfinedRoaming; + + [CommandProperty( AccessLevel.GameMaster )] + public bool ConfinedRoaming + { + get { return m_ConfinedRoaming; } + set { m_ConfinedRoaming = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool HasBeenAdvanced + { + get { return m_HasBeenAdvanced; } + set { m_HasBeenAdvanced = value; } + } + + [Constructable] + public ChampionSpawn() : base( 0xBD2 ) + { + Movable = false; + Visible = false; + + m_Creatures = new ArrayList(); + m_RedSkulls = new ArrayList(); + m_WhiteSkulls = new ArrayList(); + + m_Platform = new ChampionPlatform( this ); + m_Altar = new ChampionAltar( this ); + m_Idol = new IdolOfTheChampion( this ); + + m_ExpireDelay = TimeSpan.FromMinutes( 10.0 ); + m_RestartDelay = TimeSpan.FromMinutes( 5.0 ); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( SetInitialSpawnArea ) ); + } + + public void SetInitialSpawnArea() + { + //Previous default used to be 24; + SpawnArea = new Rectangle2D( new Point2D( X - 24, Y - 24 ), new Point2D( X + 24, Y + 24 ) ); + } + + public void UpdateRegion() + { + if( m_Region != null ) + m_Region.Unregister(); + + if( !Deleted && this.Map != Map.Internal ) + { + m_Region = new ChampionSpawnRegion( this ); + m_Region.Register(); + } + + /* + if( m_Region == null ) + { + m_Region = new ChampionSpawnRegion( this ); + } + else + { + m_Region.Unregister(); + //Why doesn't Region allow me to set it's map/Area meself? >< + m_Region = new ChampionSpawnRegion( this ); + } + */ + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool RandomizeType + { + get { return m_RandomizeType; } + set { m_RandomizeType = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Kills + { + get + { + return m_Kills; + } + set + { + m_Kills = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Rectangle2D SpawnArea + { + get + { + return m_SpawnArea; + } + set + { + m_SpawnArea = value; + InvalidateProperties(); + UpdateRegion(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan RestartDelay + { + get + { + return m_RestartDelay; + } + set + { + m_RestartDelay = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime RestartTime + { + get + { + return m_RestartTime; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan ExpireDelay + { + get + { + return m_ExpireDelay; + } + set + { + m_ExpireDelay = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime ExpireTime + { + get + { + return m_ExpireTime; + } + set + { + m_ExpireTime = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ChampionSpawnType Type + { + get + { + return m_Type; + } + set + { + m_Type = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get + { + return m_Active; + } + set + { + if( value ) + Start(); + else + Stop(); + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Champion + { + get + { + return m_Champion; + } + set + { + m_Champion = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Level + { + get + { + return m_RedSkulls.Count; + } + set + { + for( int i = m_RedSkulls.Count - 1; i >= value; --i ) + { + ((Item)m_RedSkulls[i]).Delete(); + m_RedSkulls.RemoveAt( i ); + } + + for( int i = m_RedSkulls.Count; i < value; ++i ) + { + Item skull = new Item( 0x1854 ); + + skull.Hue = 0x26; + skull.Movable = false; + skull.Light = LightType.Circle150; + + skull.MoveToWorld( GetRedSkullLocation( i ), Map ); + + m_RedSkulls.Add( skull ); + } + + InvalidateProperties(); + } + } + + public int MaxKills + { + get + { + return 250 - (Level * 12); + } + } + + public void SetWhiteSkullCount( int val ) + { + for( int i = m_WhiteSkulls.Count - 1; i >= val; --i ) + { + ((Item)m_WhiteSkulls[i]).Delete(); + m_WhiteSkulls.RemoveAt( i ); + } + + for( int i = m_WhiteSkulls.Count; i < val; ++i ) + { + Item skull = new Item( 0x1854 ); + + skull.Movable = false; + skull.Light = LightType.Circle150; + + skull.MoveToWorld( GetWhiteSkullLocation( i ), Map ); + + m_WhiteSkulls.Add( skull ); + + Effects.PlaySound( skull.Location, skull.Map, 0x29 ); + Effects.SendLocationEffect( new Point3D( skull.X + 1, skull.Y + 1, skull.Z ), skull.Map, 0x3728, 10 ); + } + } + + public void Start() + { + if( m_Active || Deleted ) + return; + + m_Active = true; + m_HasBeenAdvanced = false; + + if( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = new SliceTimer( this ); + m_Timer.Start(); + + if( m_RestartTimer != null ) + m_RestartTimer.Stop(); + + m_RestartTimer = null; + + if( m_Altar != null ) + { + if ( m_Champion != null ) + m_Altar.Hue = 0x26; + else + m_Altar.Hue = 0; + } + + if ( m_Platform != null ) + m_Platform.Hue = 0x452; + } + + public void Stop() + { + if( !m_Active || Deleted ) + return; + + m_Active = false; + m_HasBeenAdvanced = false; + + if( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + if( m_RestartTimer != null ) + m_RestartTimer.Stop(); + + m_RestartTimer = null; + + if( m_Altar != null ) + m_Altar.Hue = 0; + + if ( m_Platform != null ) + m_Platform.Hue = 0x497; + } + + public void BeginRestart( TimeSpan ts ) + { + if( m_RestartTimer != null ) + m_RestartTimer.Stop(); + + m_RestartTime = DateTime.Now + ts; + + m_RestartTimer = new RestartTimer( this, ts ); + m_RestartTimer.Start(); + } + + public void EndRestart() + { + if( RandomizeType ) + { + switch( Utility.Random( 5 ) ) + { + case 0: Type = ChampionSpawnType.VerminHorde; break; + case 1: Type = ChampionSpawnType.UnholyTerror; break; + case 2: Type = ChampionSpawnType.ColdBlood; break; + case 3: Type = ChampionSpawnType.Abyss; break; + case 4: Type = ChampionSpawnType.Arachnid; break; + } + } + + m_HasBeenAdvanced = false; + + Start(); + } + + public void OnSlice() + { + if( !m_Active || Deleted ) + return; + + if( m_Champion != null ) + { + if( m_Champion.Deleted ) + { + if ( m_Platform != null ) + m_Platform.Hue = 0x497; + + if( m_Altar != null ) + { + m_Altar.Hue = 0; + new StarRoomGate( true, m_Altar.Location, m_Altar.Map ); + } + + m_Champion = null; + Stop(); + + BeginRestart( m_RestartDelay ); + } + } + else + { + int kills = m_Kills; + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + Mobile m = (Mobile)m_Creatures[i]; + + if ( m.Deleted ) + { + m_Creatures.RemoveAt( i ); + --i; + ++m_Kills; + + + Mobile killer = m.FindMostRecentDamager( false ); + + if( killer is BaseCreature ) + killer = ((BaseCreature)killer).GetMaster(); + + if( killer is PlayerMobile ) + { + int mobSubLevel = GetSubLevelFor( m ) + 1; + + if( mobSubLevel >= 0 ) + { + bool gainedPath = false; + + int pointsToGain = mobSubLevel * 40; + + if( VirtueHelper.Award( killer, VirtueName.Valor, pointsToGain, ref gainedPath ) ) + { + if( gainedPath ) + m.SendLocalizedMessage( 1054032 ); // You have gained a path in Valor! + else + m.SendLocalizedMessage( 1054030 ); // You have gained in Valor! + + //No delay on Valor gains + } + + PlayerMobile.ChampionTitleInfo info = ((PlayerMobile)killer).ChampionTitles; + + info.Award( m_Type, mobSubLevel ); + } + } + } + } + + // Only really needed once. + if ( m_Kills > kills ) + InvalidateProperties(); + + double n = m_Kills / (double)MaxKills; + int p = (int)(n * 100); + + if( p >= 90 ) + AdvanceLevel(); + else if( p > 0 ) + SetWhiteSkullCount( p / 20 ); + + if( DateTime.Now >= m_ExpireTime ) + Expire(); + + Respawn(); + } + } + + public void AdvanceLevel() + { + m_ExpireTime = DateTime.Now + m_ExpireDelay; + + if( Level < 16 ) + { + m_Kills = 0; + ++Level; + InvalidateProperties(); + SetWhiteSkullCount( 0 ); + + if( m_Altar != null ) + { + Effects.PlaySound( m_Altar.Location, m_Altar.Map, 0x29 ); + Effects.SendLocationEffect( new Point3D( m_Altar.X + 1, m_Altar.Y + 1, m_Altar.Z ), m_Altar.Map, 0x3728, 10 ); + } + } + else + { + SpawnChampion(); + } + } + + public void SpawnChampion() + { + if( m_Altar != null ) + m_Altar.Hue = 0x26; + + if ( m_Platform != null ) + m_Platform.Hue = 0x452; + + m_Kills = 0; + Level = 0; + InvalidateProperties(); + SetWhiteSkullCount( 0 ); + + try + { + m_Champion = Activator.CreateInstance( ChampionSpawnInfo.GetInfo( m_Type ).Champion ) as Mobile; + } + catch { } + + if( m_Champion != null ) + m_Champion.MoveToWorld( new Point3D( X, Y, Z - 15 ), Map ); + } + + public void Respawn() + { + if( !m_Active || Deleted || m_Champion != null ) + return; + + while( m_Creatures.Count < (200 - (GetSubLevel() * 40)) ) + { + Mobile m = Spawn(); + + if( m == null ) + return; + + Point3D loc = GetSpawnLocation(); + + // Allow creatures to turn into Paragons at Ilshenar champions. + m.OnBeforeSpawn( loc, Map ); + + m_Creatures.Add( m ); + m.MoveToWorld( loc, Map ); + + if( m is BaseCreature ) + { + BaseCreature bc = m as BaseCreature; + bc.Tamable = false; + + if( !m_ConfinedRoaming ) + { + bc.Home = this.Location; + bc.RangeHome = (int)(Math.Sqrt( m_SpawnArea.Width * m_SpawnArea.Width + m_SpawnArea.Height * m_SpawnArea.Height )/2); + } + else + { + bc.Home = bc.Location; + + Point2D xWall1 = new Point2D( m_SpawnArea.X, bc.Y ); + Point2D xWall2 = new Point2D( m_SpawnArea.X + m_SpawnArea.Width, bc.Y ); + Point2D yWall1 = new Point2D( bc.X, m_SpawnArea.Y ); + Point2D yWall2 = new Point2D( bc.X, m_SpawnArea.Y + m_SpawnArea.Height ); + + double minXDist = Math.Min( bc.GetDistanceToSqrt( xWall1 ), bc.GetDistanceToSqrt( xWall2 ) ); + double minYDist = Math.Min( bc.GetDistanceToSqrt( yWall1 ), bc.GetDistanceToSqrt( yWall2 ) ); + + bc.RangeHome = (int)Math.Min( minXDist, minYDist ); + } + } + } + } + + public Point3D GetSpawnLocation() + { + Map map = Map; + + if( map == null ) + return Location; + + // Try 20 times to find a spawnable location. + for( int i = 0; i < 20; i++ ) + { + /* + int x = Location.X + (Utility.Random( (m_SpawnRange * 2) + 1 ) - m_SpawnRange); + int y = Location.Y + (Utility.Random( (m_SpawnRange * 2) + 1 ) - m_SpawnRange); + */ + + int x = Utility.Random( m_SpawnArea.X, m_SpawnArea.Width ); + int y = Utility.Random( m_SpawnArea.Y, m_SpawnArea.Height ); + + int z = Map.GetAverageZ( x, y ); + + if( Map.CanSpawnMobile( new Point2D( x, y ), z ) ) + return new Point3D( x, y, z ); + } + + return Location; + } + + private const int Level1 = 5; // First spawn level from 0-5 red skulls + private const int Level2 = 9; // Second spawn level from 6-9 red skulls + private const int Level3 = 13; // Third spawn level from 10-13 red skulls + + public int GetSubLevel() + { + int level = this.Level; + + if( level <= Level1 ) + return 0; + else if( level <= Level2 ) + return 1; + else if( level <= Level3 ) + return 2; + + return 3; + } + + public int GetSubLevelFor( Mobile m ) + { + Type[][] types = ChampionSpawnInfo.GetInfo( m_Type ).SpawnTypes; + Type t = m.GetType(); + + for( int i = 0; i < types.GetLength( 0 ); i++ ) + { + Type[] individualTypes = types[i]; + + for( int j = 0; j < individualTypes.Length; j++ ) + { + if( t == individualTypes[j] ) + return i; + } + } + + return -1; + } + + public Mobile Spawn() + { + Type[][] types = ChampionSpawnInfo.GetInfo( m_Type ).SpawnTypes; + + int v = GetSubLevel(); + + if( v >= 0 && v < types.Length ) + return Spawn( types[v] ); + + return null; + } + + public Mobile Spawn( params Type[] types ) + { + try + { + return Activator.CreateInstance( types[Utility.Random( types.Length )] ) as Mobile; + } + catch + { + return null; + } + } + + public void Expire() + { + m_Kills = 0; + + if( m_WhiteSkulls.Count == 0 ) + { + // They didn't even get 20%, go back a level + + if( Level > 0 ) + --Level; + + InvalidateProperties(); + } + else + { + SetWhiteSkullCount( 0 ); + } + + m_ExpireTime = DateTime.Now + m_ExpireDelay; + } + + public Point3D GetRedSkullLocation( int index ) + { + int x, y; + + if( index < 5 ) + { + x = index - 2; + y = -2; + } + else if( index < 9 ) + { + x = 2; + y = index - 6; + } + else if( index < 13 ) + { + x = 10 - index; + y = 2; + } + else + { + x = -2; + y = 14 - index; + } + + return new Point3D( X + x, Y + y, Z - 15 ); + } + + public Point3D GetWhiteSkullLocation( int index ) + { + int x, y; + + switch( index ) + { + default: + case 0: x = -1; y = -1; break; + case 1: x = 1; y = -1; break; + case 2: x = 1; y = 1; break; + case 3: x = -1; y = 1; break; + } + + return new Point3D( X + x, Y + y, Z - 15 ); + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( "champion spawn" ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if( m_Active ) + { + list.Add( 1060742 ); // active + list.Add( 1060658, "Type\t{0}", m_Type ); // ~1_val~: ~2_val~ + list.Add( 1060659, "Level\t{0}", Level ); // ~1_val~: ~2_val~ + list.Add( 1060660, "Kills\t{0} of {1} ({2:F1}%)", m_Kills, MaxKills, 100.0 * ((double)m_Kills / MaxKills) ); // ~1_val~: ~2_val~ + //list.Add( 1060661, "Spawn Range\t{0}", m_SpawnRange ); // ~1_val~: ~2_val~ + } + else + { + list.Add( 1060743 ); // inactive + } + } + + public override void OnSingleClick( Mobile from ) + { + if( m_Active ) + LabelTo( from, "{0} (Active; Level: {1}; Kills: {2}/{3})", m_Type, Level, m_Kills, MaxKills ); + else + LabelTo( from, "{0} (Inactive)", m_Type ); + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendGump( new PropertiesGump( from, this ) ); + } + + public override void OnLocationChange( Point3D oldLoc ) + { + if( Deleted ) + return; + + if( m_Platform != null ) + m_Platform.Location = new Point3D( X, Y, Z - 20 ); + + if( m_Altar != null ) + m_Altar.Location = new Point3D( X, Y, Z - 15 ); + + if( m_Idol != null ) + m_Idol.Location = new Point3D( X, Y, Z - 15 ); + + if( m_RedSkulls != null ) + { + for( int i = 0; i < m_RedSkulls.Count; ++i ) + ((Item)m_RedSkulls[i]).Location = GetRedSkullLocation( i ); + } + + if( m_WhiteSkulls != null ) + { + for( int i = 0; i < m_WhiteSkulls.Count; ++i ) + ((Item)m_WhiteSkulls[i]).Location = GetWhiteSkullLocation( i ); + } + + m_SpawnArea.X += Location.X - oldLoc.X; + m_SpawnArea.Y += Location.Y - oldLoc.Y; + + UpdateRegion(); + } + + public override void OnMapChange() + { + if( Deleted ) + return; + + if( m_Platform != null ) + m_Platform.Map = Map; + + if( m_Altar != null ) + m_Altar.Map = Map; + + if( m_Idol != null ) + m_Idol.Map = Map; + + if( m_RedSkulls != null ) + { + for( int i = 0; i < m_RedSkulls.Count; ++i ) + ((Item)m_RedSkulls[i]).Map = Map; + } + + if( m_WhiteSkulls != null ) + { + for( int i = 0; i < m_WhiteSkulls.Count; ++i ) + ((Item)m_WhiteSkulls[i]).Map = Map; + } + + UpdateRegion(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if( m_Platform != null ) + m_Platform.Delete(); + + if( m_Altar != null ) + m_Altar.Delete(); + + if( m_Idol != null ) + m_Idol.Delete(); + + if( m_RedSkulls != null ) + { + for( int i = 0; i < m_RedSkulls.Count; ++i ) + ((Item)m_RedSkulls[i]).Delete(); + + m_RedSkulls.Clear(); + } + + if( m_WhiteSkulls != null ) + { + for( int i = 0; i < m_WhiteSkulls.Count; ++i ) + ((Item)m_WhiteSkulls[i]).Delete(); + + m_WhiteSkulls.Clear(); + } + + if( m_Creatures != null ) + { + for( int i = 0; i < m_Creatures.Count; ++i ) + { + Mobile mob = (Mobile)m_Creatures[i]; + + if( !mob.Player ) + mob.Delete(); + } + + m_Creatures.Clear(); + } + + if( m_Champion != null && !m_Champion.Player ) + m_Champion.Delete(); + + Stop(); + + UpdateRegion(); + } + + public ChampionSpawn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)4 ); // version + + writer.Write( m_ConfinedRoaming ); + writer.Write( m_Idol ); + writer.Write( m_HasBeenAdvanced ); + writer.Write( m_SpawnArea ); + + writer.Write( m_RandomizeType ); + + // writer.Write( m_SpawnRange ); + writer.Write( m_Kills ); + + writer.Write( (bool)m_Active ); + writer.Write( (int)m_Type ); + writer.WriteMobileList( m_Creatures, true ); + writer.WriteItemList( m_RedSkulls, true ); + writer.WriteItemList( m_WhiteSkulls, true ); + writer.Write( m_Platform ); + writer.Write( m_Altar ); + writer.Write( m_ExpireDelay ); + writer.WriteDeltaTime( m_ExpireTime ); + writer.Write( m_Champion ); + writer.Write( m_RestartDelay ); + + writer.Write( m_RestartTimer != null ); + + if( m_RestartTimer != null ) + writer.WriteDeltaTime( m_RestartTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 4: + { + m_ConfinedRoaming = reader.ReadBool(); + m_Idol = reader.ReadItem() as IdolOfTheChampion; + m_HasBeenAdvanced = reader.ReadBool(); + + goto case 3; + } + case 3: + { + m_SpawnArea = reader.ReadRect2D(); + + goto case 2; + } + case 2: + { + m_RandomizeType = reader.ReadBool(); + + goto case 1; + } + case 1: + { + if( version < 3 ) + { + int oldRange = reader.ReadInt(); + + m_SpawnArea = new Rectangle2D( new Point2D( X - oldRange, Y - oldRange ), new Point2D( X + oldRange, Y + oldRange ) ); + } + + m_Kills = reader.ReadInt(); + + goto case 0; + } + case 0: + { + if( version < 1 ) + m_SpawnArea = new Rectangle2D( new Point2D( X - 24, Y - 24 ), new Point2D( X + 24, Y + 24 ) ); //Default was 24 + + bool active = reader.ReadBool(); + m_Type = (ChampionSpawnType)reader.ReadInt(); + m_Creatures = reader.ReadMobileList(); + m_RedSkulls = reader.ReadItemList(); + m_WhiteSkulls = reader.ReadItemList(); + m_Platform = reader.ReadItem() as ChampionPlatform; + m_Altar = reader.ReadItem() as ChampionAltar; + m_ExpireDelay = reader.ReadTimeSpan(); + m_ExpireTime = reader.ReadDeltaTime(); + m_Champion = reader.ReadMobile(); + m_RestartDelay = reader.ReadTimeSpan(); + + if( reader.ReadBool() ) + { + m_RestartTime = reader.ReadDeltaTime(); + BeginRestart( m_RestartTime - DateTime.Now ); + } + + if( version < 4 ) + { + m_Idol = new IdolOfTheChampion( this ); + m_Idol.MoveToWorld( new Point3D( X, Y, Z - 15 ), Map ); + } + + if( m_Platform == null || m_Altar == null || m_Idol == null ) + Delete(); + else if( active ) + Start(); + + break; + } + } + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( UpdateRegion ) ); + } + } + + public class ChampionSpawnRegion : BaseRegion + { + private ChampionSpawn m_Spawn; + + public ChampionSpawn ChampionSpawn + { + get { return m_Spawn; } + } + + public ChampionSpawnRegion( ChampionSpawn spawn ) : base( null, spawn.Map, Region.Find( spawn.Location, spawn.Map ), spawn.SpawnArea ) + { + m_Spawn = spawn; + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return false; + } + + public override void AlterLightLevel( Mobile m, ref int global, ref int personal ) + { + base.AlterLightLevel( m, ref global, ref personal ); + global = Math.Max( global, 1 + m_Spawn.Level ); //This is a gusstimate. TODO: Verify & get exact values // OSI testing: at 2 red skulls, light = 0x3 ; 1 red = 0x3.; 3 = 8; 9 = 0xD 8 = 0xD 12 = 0x12 10 = 0xD + } + } + + public class IdolOfTheChampion : Item + { + private ChampionSpawn m_Spawn; + + public ChampionSpawn Spawn { get { return m_Spawn; } } + + public override string DefaultName + { + get { return "Idol of the Champion"; } + } + + + public IdolOfTheChampion( ChampionSpawn spawn ): base( 0x1F18 ) + { + m_Spawn = spawn; + Movable = false; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Spawn != null ) + m_Spawn.Delete(); + } + + public IdolOfTheChampion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Spawn ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Spawn = reader.ReadItem() as ChampionSpawn; + + if ( m_Spawn == null ) + Delete(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/ChampionSpawnType.cs b/Scripts/Engines/CannedEvil/ChampionSpawnType.cs new file mode 100644 index 000000000..77fb09879 --- /dev/null +++ b/Scripts/Engines/CannedEvil/ChampionSpawnType.cs @@ -0,0 +1,106 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.CannedEvil +{ + public enum ChampionSpawnType + { + Abyss, + Arachnid, + ColdBlood, + ForestLord, + VerminHorde, + UnholyTerror, + SleepingDragon + } + + public class ChampionSpawnInfo + { + private string m_Name; + private Type m_Champion; + private Type[][] m_SpawnTypes; + private string[] m_LevelNames; + + public string Name { get { return m_Name; } } + public Type Champion { get { return m_Champion; } } + public Type[][] SpawnTypes { get { return m_SpawnTypes; } } + public string[] LevelNames { get { return m_LevelNames; } } + + public ChampionSpawnInfo( string name, Type champion, string[] levelNames, Type[][] spawnTypes ) + { + m_Name = name; + m_Champion = champion; + m_LevelNames = levelNames; + m_SpawnTypes = spawnTypes; + } + + public static ChampionSpawnInfo[] Table{ get { return m_Table; } } + + private static readonly ChampionSpawnInfo[] m_Table = new ChampionSpawnInfo[] + { + new ChampionSpawnInfo( "Abyss", typeof( Semidar ), new string[]{ "Foe", "Assassin", "Conqueror" }, new Type[][] // Abyss + { // Abyss + new Type[]{ typeof( StrongMongbat ), typeof( Imp ) }, // Level 1 + new Type[]{ typeof( Gargoyle ), typeof( Harpy ) }, // Level 2 + new Type[]{ typeof( FireGargoyle ), typeof( StoneGargoyle ) }, // Level 3 + new Type[]{ typeof( Daemon ), typeof( Succubus ) } // Level 4 + } ), + new ChampionSpawnInfo( "Arachnid", typeof( Mephitis ), new string[]{ "Bane", "Killer", "Vanquisher" }, new Type[][] // Arachnid + { // Arachnid + new Type[]{ typeof( Scorpion ), typeof( GiantSpider ) }, // Level 1 + new Type[]{ typeof( TerathanDrone ), typeof( TerathanWarrior ) }, // Level 2 + new Type[]{ typeof( DreadSpider ), typeof( TerathanMatriarch ) }, // Level 3 + new Type[]{ typeof( PoisonElemental ), typeof( TerathanAvenger ) } // Level 4 + } ), + new ChampionSpawnInfo( "Cold Blood", typeof( Rikktor ), new string[]{ "Blight", "Slayer", "Destroyer" }, new Type[][] // Cold Blood + { // Cold Blood + new Type[]{ typeof( Lizardman ), typeof( Snake ) }, // Level 1 + new Type[]{ typeof( LavaLizard ), typeof( OphidianWarrior ) }, // Level 2 + new Type[]{ typeof( Drake ), typeof( OphidianArchmage ) }, // Level 3 + new Type[]{ typeof( Dragon ), typeof( OphidianKnight ) } // Level 4 + } ), + new ChampionSpawnInfo( "Forest Lord", typeof( LordOaks ), new string[]{ "Enemy", "Curse", "Slaughterer" }, new Type[][] // Forest Lord + { // Forest Lord + new Type[]{ typeof( Pixie ), typeof( ShadowWisp ) }, // Level 1 + new Type[]{ typeof( Kirin ), typeof( Wisp ) }, // Level 2 + new Type[]{ typeof( Centaur ), typeof( Unicorn ) }, // Level 3 + new Type[]{ typeof( EtherealWarrior ), typeof( SerpentineDragon ) } // Level 4 + } ), + new ChampionSpawnInfo( "Vermin Horde", typeof( Barracoon ), new string[]{ "Adversary", "Subjugator", "Eradicator" }, new Type[][] // Vermin Horde + { // Vermin Horde + new Type[]{ typeof( GiantRat ), typeof( Slime ) }, // Level 1 + new Type[]{ typeof( DireWolf ), typeof( Ratman ) }, // Level 2 + new Type[]{ typeof( HellHound ), typeof( RatmanMage ) }, // Level 3 + new Type[]{ typeof( RatmanArcher ), typeof( SilverSerpent ) } // Level 4 + } ), + new ChampionSpawnInfo( "Unholy Terror", typeof( Neira ), new string[]{ "Scourge", "Punisher", "Nemesis" }, new Type[][] // Unholy Terror + { // Unholy Terror + (Core.AOS ? + new Type[]{ typeof( Bogle ), typeof( Ghoul ), typeof( Shade ), typeof( Spectre ), typeof( Wraith ) } // Level 1 (Pre-AoS) + : new Type[]{ typeof( Ghoul ), typeof( Shade ), typeof( Spectre ), typeof( Wraith ) } ), // Level 1 + + new Type[]{ typeof( BoneMagi ), typeof( Mummy ), typeof( SkeletalMage ) }, // Level 2 + new Type[]{ typeof( BoneKnight ), typeof( Lich ), typeof( SkeletalKnight ) }, // Level 3 + new Type[]{ typeof( LichLord ), typeof( RottingCorpse ) } // Level 4 + } ), + new ChampionSpawnInfo( "Sleeping Dragon", typeof( Serado ), new string[]{ "Rival", "Challenger", "Antagonist" } , new Type[][] + { // Unholy Terror + new Type[]{ typeof( DeathwatchBeetleHatchling ), typeof( Lizardman ) }, + new Type[]{ typeof( DeathwatchBeetle ), typeof( Kappa ) }, + new Type[]{ typeof( LesserHiryu ), typeof( RevenantLion ) }, + new Type[]{ typeof( Hiryu ), typeof( Oni ) } + } ) + }; + + public static ChampionSpawnInfo GetInfo( ChampionSpawnType type ) + { + int v = (int)type; + + if( v < 0 || v >= m_Table.Length ) + v = 0; + + return m_Table[v]; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/HarrowerGate.cs b/Scripts/Engines/CannedEvil/HarrowerGate.cs new file mode 100644 index 000000000..9bf8e2e25 --- /dev/null +++ b/Scripts/Engines/CannedEvil/HarrowerGate.cs @@ -0,0 +1,59 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HarrowerGate : Moongate + { + private Mobile m_Harrower; + + public override int LabelNumber{ get{ return 1049498; } } // dark moongate + + public HarrowerGate( Mobile harrower, Point3D loc, Map map, Point3D targLoc, Map targMap ) : base( targLoc, targMap ) + { + m_Harrower = harrower; + + Dispellable = false; + ItemID = 0x1FD4; + Light = LightType.Circle300; + + MoveToWorld( loc, map ); + } + + public HarrowerGate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Harrower ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Harrower = reader.ReadMobile(); + + if ( m_Harrower == null ) + Delete(); + + break; + } + } + + if ( Light != LightType.Circle300 ) + Light = LightType.Circle300; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/RestartTimer.cs b/Scripts/Engines/CannedEvil/RestartTimer.cs new file mode 100644 index 000000000..705af4852 --- /dev/null +++ b/Scripts/Engines/CannedEvil/RestartTimer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.CannedEvil +{ + public class RestartTimer : Timer + { + private ChampionSpawn m_Spawn; + + public RestartTimer( ChampionSpawn spawn, TimeSpan delay ) : base( delay ) + { + m_Spawn = spawn; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Spawn.EndRestart(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/SliceTimer.cs b/Scripts/Engines/CannedEvil/SliceTimer.cs new file mode 100644 index 000000000..49eb818bf --- /dev/null +++ b/Scripts/Engines/CannedEvil/SliceTimer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.CannedEvil +{ + public class SliceTimer : Timer + { + private ChampionSpawn m_Spawn; + + public SliceTimer( ChampionSpawn spawn ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Spawn = spawn; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Spawn.OnSlice(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/CannedEvil/StarRoomGate.cs b/Scripts/Engines/CannedEvil/StarRoomGate.cs new file mode 100644 index 000000000..fe0b57cf9 --- /dev/null +++ b/Scripts/Engines/CannedEvil/StarRoomGate.cs @@ -0,0 +1,106 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StarRoomGate : Moongate + { + private bool m_Decays; + private DateTime m_DecayTime; + private Timer m_Timer; + + public override int LabelNumber{ get{ return 1049498; } } // dark moongate + + [Constructable] + public StarRoomGate() : this( false ) + { + } + + [Constructable] + public StarRoomGate( bool decays, Point3D loc, Map map ) : this( decays ) + { + MoveToWorld( loc, map ); + Effects.PlaySound( loc, map, 0x20E ); + } + + [Constructable] + public StarRoomGate( bool decays ) : base( new Point3D( 5143, 1774, 0 ), Map.Felucca ) + { + Dispellable = false; + ItemID = 0x1FD4; + + if ( decays ) + { + m_Decays = true; + m_DecayTime = DateTime.Now + TimeSpan.FromMinutes( 2.0 ); + + m_Timer = new InternalTimer( this, m_DecayTime ); + m_Timer.Start(); + } + } + + public StarRoomGate( Serial serial ) : base( serial ) + { + } + + public override void OnAfterDelete() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + base.OnAfterDelete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Decays ); + + if ( m_Decays ) + writer.WriteDeltaTime( m_DecayTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Decays = reader.ReadBool(); + + if ( m_Decays ) + { + m_DecayTime = reader.ReadDeltaTime(); + + m_Timer = new InternalTimer( this, m_DecayTime ); + m_Timer.Start(); + } + + break; + } + } + } + + private class InternalTimer : Timer + { + private Item m_Item; + + public InternalTimer( Item item, DateTime end ) : base( end - DateTime.Now ) + { + m_Item = item; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/Channel.cs b/Scripts/Engines/Chat/Channel.cs new file mode 100644 index 000000000..05c143ba6 --- /dev/null +++ b/Scripts/Engines/Chat/Channel.cs @@ -0,0 +1,548 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.Chat +{ + public class Channel + { + private string m_Name; + private string m_Password; + private ArrayList m_Users, m_Banned, m_Moderators, m_Voices; + private bool m_VoiceRestricted; + private bool m_AlwaysAvailable; + + public Channel( string name ) + { + m_Name = name; + + m_Users = new ArrayList(); + m_Banned = new ArrayList(); + m_Moderators = new ArrayList(); + m_Voices = new ArrayList(); + } + + public Channel( string name, string password ) : this( name ) + { + m_Password = password; + } + + public string Name + { + get + { + return m_Name; + } + set + { + SendCommand( ChatCommand.RemoveChannel, m_Name ); + m_Name = value; + SendCommand( ChatCommand.AddChannel, m_Name ); + SendCommand( ChatCommand.JoinedChannel, m_Name ); + } + } + + public string Password + { + get + { + return m_Password; + } + set + { + string newValue = null; + + if ( value != null ) + { + newValue = value.Trim(); + + if ( newValue == null || newValue == String.Empty ) + newValue = null; + } + + m_Password = newValue; + } + } + + public bool Contains( ChatUser user ) + { + return m_Users.Contains( user ); + } + + public bool IsBanned( ChatUser user ) + { + return m_Banned.Contains( user ); + } + + public bool CanTalk( ChatUser user ) + { + return ( !m_VoiceRestricted || m_Voices.Contains( user ) || m_Moderators.Contains( user ) ); + } + + public bool IsModerator( ChatUser user ) + { + return m_Moderators.Contains( user ); + } + + public bool IsVoiced( ChatUser user ) + { + return m_Voices.Contains( user ); + } + + public bool ValidatePassword( string password ) + { + return ( m_Password == null || Insensitive.Equals( m_Password, password ) ); + } + + public bool ValidateModerator( ChatUser user ) + { + if ( user != null && !IsModerator( user ) ) + { + user.SendMessage( 29 ); // You must have operator status to do this. + return false; + } + + return true; + } + + public bool ValidateAccess( ChatUser from, ChatUser target ) + { + if ( from != null && target != null && from.Mobile.AccessLevel < target.Mobile.AccessLevel ) + { + from.Mobile.SendMessage( "Your access level is too low to do this." ); + return false; + } + + return true; + } + + public bool AddUser( ChatUser user ) + { + return AddUser( user, null ); + } + + public bool AddUser( ChatUser user, string password ) + { + if ( Contains( user ) ) + { + user.SendMessage( 46, m_Name ); // You are already in the conference '%1'. + return true; + } + else if ( IsBanned( user ) ) + { + user.SendMessage( 64 ); // You have been banned from this conference. + return false; + } + else if ( !ValidatePassword( password ) ) + { + user.SendMessage( 34 ); // That is not the correct password. + return false; + } + else + { + if ( user.CurrentChannel != null ) + user.CurrentChannel.RemoveUser( user ); // Remove them from their current channel first + + ChatSystem.SendCommandTo( user.Mobile, ChatCommand.JoinedChannel, m_Name ); + + SendCommand( ChatCommand.AddUserToChannel, user.GetColorCharacter() + user.Username ); + + m_Users.Add( user ); + user.CurrentChannel = this; + + if ( user.Mobile.AccessLevel >= AccessLevel.GameMaster || (!m_AlwaysAvailable && m_Users.Count == 1) ) + AddModerator( user ); + + SendUsersTo( user ); + + return true; + } + } + + public void RemoveUser( ChatUser user ) + { + if ( Contains( user ) ) + { + m_Users.Remove( user ); + user.CurrentChannel = null; + + if ( m_Moderators.Contains( user ) ) + m_Moderators.Remove( user ); + + if ( m_Voices.Contains( user ) ) + m_Voices.Remove( user ); + + SendCommand( ChatCommand.RemoveUserFromChannel, user, user.Username ); + ChatSystem.SendCommandTo( user.Mobile, ChatCommand.LeaveChannel ); + + if ( m_Users.Count == 0 && !m_AlwaysAvailable ) + RemoveChannel( this ); + } + } + + public void AdBan( ChatUser user ) + { + AddBan( user, null ); + } + + public void AddBan( ChatUser user, ChatUser moderator ) + { + if ( !ValidateModerator( moderator ) || !ValidateAccess( moderator, user ) ) + return; + + if ( !m_Banned.Contains( user ) ) + m_Banned.Add( user ); + + Kick( user, moderator, true ); + } + + public void RemoveBan( ChatUser user ) + { + if ( m_Banned.Contains( user ) ) + m_Banned.Remove( user ); + } + + public void Kick( ChatUser user ) + { + Kick( user, null ); + } + + public void Kick( ChatUser user, ChatUser moderator ) + { + Kick( user, moderator, false ); + } + + public void Kick( ChatUser user, ChatUser moderator, bool wasBanned ) + { + if ( !ValidateModerator( moderator ) || !ValidateAccess( moderator, user ) ) + return; + + if ( Contains( user ) ) + { + if ( moderator != null ) + { + if ( wasBanned ) + user.SendMessage( 63, moderator.Username ); // %1, a conference moderator, has banned you from the conference. + else + user.SendMessage( 45, moderator.Username ); // %1, a conference moderator, has kicked you out of the conference. + } + + RemoveUser( user ); + ChatSystem.SendCommandTo( user.Mobile, ChatCommand.AddUserToChannel, user.GetColorCharacter() + user.Username ); + + SendMessage( 44, user.Username ) ; // %1 has been kicked out of the conference. + } + + if ( wasBanned && moderator != null ) + moderator.SendMessage( 62, user.Username ); // You are banning %1 from this conference. + } + + public bool VoiceRestricted + { + get + { + return m_VoiceRestricted; + } + set + { + m_VoiceRestricted = value; + + if ( value ) + SendMessage( 56 ); // From now on, only moderators will have speaking privileges in this conference by default. + else + SendMessage( 55 ); // From now on, everyone in the conference will have speaking privileges by default. + } + } + + public bool AlwaysAvailable + { + get + { + return m_AlwaysAvailable; + } + set + { + m_AlwaysAvailable = value; + } + } + + public void AddVoiced( ChatUser user ) + { + AddVoiced( user, null ); + } + + public void AddVoiced( ChatUser user, ChatUser moderator ) + { + if ( !ValidateModerator( moderator ) ) + return; + + if ( !IsBanned( user ) && !IsModerator( user ) && !IsVoiced( user ) ) + { + m_Voices.Add( user ); + + if ( moderator != null ) + user.SendMessage( 54, moderator.Username ); // %1, a conference moderator, has granted you speaking priviledges in this conference. + + SendMessage( 52, user, user.Username ); // %1 now has speaking privileges in this conference. + SendCommand( ChatCommand.AddUserToChannel, user, user.GetColorCharacter() + user.Username ); + } + } + + public void RemoveVoiced( ChatUser user, ChatUser moderator ) + { + if ( !ValidateModerator( moderator ) || !ValidateAccess( moderator, user ) ) + return; + + if ( !IsModerator( user ) && IsVoiced( user ) ) + { + m_Voices.Remove( user ); + + if ( moderator != null ) + user.SendMessage( 53, moderator.Username ); // %1, a conference moderator, has removed your speaking priviledges for this conference. + + SendMessage( 51, user, user.Username ); // %1 no longer has speaking privileges in this conference. + SendCommand( ChatCommand.AddUserToChannel, user, user.GetColorCharacter() + user.Username ); + } + } + + public void AddModerator( ChatUser user ) + { + AddModerator( user, null ); + } + + public void AddModerator( ChatUser user, ChatUser moderator ) + { + if ( !ValidateModerator( moderator ) ) + return; + + if ( IsBanned( user ) || IsModerator( user ) ) + return; + + if ( IsVoiced( user ) ) + m_Voices.Remove( user ); + + m_Moderators.Add( user ); + + if ( moderator != null ) + user.SendMessage( 50, moderator.Username ); // %1 has made you a conference moderator. + + SendMessage( 48, user, user.Username ); // %1 is now a conference moderator. + SendCommand( ChatCommand.AddUserToChannel, user.GetColorCharacter() + user.Username ); + } + + public void RemoveModerator( ChatUser user ) + { + RemoveModerator( user, null ); + } + + public void RemoveModerator( ChatUser user, ChatUser moderator ) + { + if ( !ValidateModerator( moderator ) || !ValidateAccess( moderator, user ) ) + return; + + if ( IsModerator( user ) ) + { + m_Moderators.Remove( user ); + + if ( moderator != null ) + user.SendMessage( 49, moderator.Username ); // %1 has removed you from the list of conference moderators. + + SendMessage( 47, user, user.Username ); // %1 is no longer a conference moderator. + SendCommand( ChatCommand.AddUserToChannel, user.GetColorCharacter() + user.Username ); + } + } + + public void SendMessage( int number ) + { + SendMessage( number, null, null, null ); + } + + public void SendMessage( int number, string param1 ) + { + SendMessage( number, null, param1, null ); + } + + public void SendMessage( int number, string param1, string param2 ) + { + SendMessage( number, null, param1, param2 ); + } + + public void SendMessage( int number, ChatUser initiator ) + { + SendMessage( number, initiator, null, null ); + } + + public void SendMessage( int number, ChatUser initiator, string param1 ) + { + SendMessage( number, initiator, param1, null ); + } + + public void SendMessage( int number, ChatUser initiator, string param1, string param2 ) + { + for ( int i = 0; i < m_Users.Count; ++i ) + { + ChatUser user = (ChatUser)m_Users[i]; + + if ( user == initiator ) + continue; + + if ( user.CheckOnline() ) + user.SendMessage( number, param1, param2 ); + else if ( !Contains( user ) ) + --i; + } + } + + public void SendIgnorableMessage( int number, ChatUser from, string param1, string param2 ) + { + for ( int i = 0; i < m_Users.Count; ++i ) + { + ChatUser user = (ChatUser)m_Users[i]; + + if ( user.IsIgnored( from ) ) + continue; + + if ( user.CheckOnline() ) + user.SendMessage( number, from.Mobile, param1, param2 ); + else if ( !Contains( user ) ) + --i; + } + } + + public void SendCommand( ChatCommand command ) + { + SendCommand( command, null, null, null ); + } + + public void SendCommand( ChatCommand command, string param1 ) + { + SendCommand( command, null, param1, null ); + } + + public void SendCommand( ChatCommand command, string param1, string param2 ) + { + SendCommand( command, null, param1, param2 ); + } + + public void SendCommand( ChatCommand command, ChatUser initiator ) + { + SendCommand( command, initiator, null, null ); + } + + public void SendCommand( ChatCommand command, ChatUser initiator, string param1 ) + { + SendCommand( command, initiator, param1, null ); + } + + public void SendCommand( ChatCommand command, ChatUser initiator, string param1, string param2 ) + { + for ( int i = 0; i < m_Users.Count; ++i ) + { + ChatUser user = (ChatUser)m_Users[i]; + + if ( user == initiator ) + continue; + + if ( user.CheckOnline() ) + ChatSystem.SendCommandTo( user.Mobile, command, param1, param2 ); + else if ( !Contains( user ) ) + --i; + } + } + + public void SendUsersTo( ChatUser to ) + { + for ( int i = 0; i < m_Users.Count; ++i ) + { + ChatUser user = (ChatUser)m_Users[i]; + + ChatSystem.SendCommandTo( to.Mobile, ChatCommand.AddUserToChannel, user.GetColorCharacter() + user.Username ); + } + } + + private static ArrayList m_Channels = new ArrayList(); + + public static ArrayList Channels + { + get + { + return m_Channels; + } + } + + public static void SendChannelsTo( ChatUser user ) + { + for ( int i = 0; i < m_Channels.Count; ++i ) + { + Channel channel = (Channel)m_Channels[i]; + + if ( !channel.IsBanned( user ) ) + ChatSystem.SendCommandTo( user.Mobile, ChatCommand.AddChannel, channel.Name, "0" ); + } + } + + public static Channel AddChannel( string name ) + { + return AddChannel( name, null ); + } + + public static Channel AddChannel( string name, string password ) + { + Channel channel = FindChannelByName( name ); + + if ( channel == null ) + { + channel = new Channel( name, password ); + m_Channels.Add( channel ); + } + + ChatUser.GlobalSendCommand( ChatCommand.AddChannel, name, "0" ) ; + + return channel; + } + + public static void RemoveChannel( string name ) + { + RemoveChannel( FindChannelByName( name ) ); + } + + public static void RemoveChannel( Channel channel ) + { + if ( channel == null ) + return; + + if ( m_Channels.Contains( channel ) && channel.m_Users.Count == 0 ) + { + ChatUser.GlobalSendCommand( ChatCommand.RemoveChannel, channel.Name ) ; + + channel.m_Moderators.Clear(); + channel.m_Voices.Clear(); + + m_Channels.Remove( channel ); + } + } + + public static Channel FindChannelByName( string name ) + { + for ( int i = 0; i < m_Channels.Count; ++i ) + { + Channel channel = (Channel)m_Channels[i]; + + if ( channel.m_Name == name ) + return channel; + } + + return null; + } + + public static void Initialize() + { + AddStaticChannel( "Newbie Help" ); + } + + public static void AddStaticChannel( string name ) + { + AddChannel( name ).AlwaysAvailable = true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/Chat.cs b/Scripts/Engines/Chat/Chat.cs new file mode 100644 index 000000000..93956e7a2 --- /dev/null +++ b/Scripts/Engines/Chat/Chat.cs @@ -0,0 +1,174 @@ +using System; +using Server; +using Server.Misc; +using Server.Network; +using Server.Accounting; + +namespace Server.Engines.Chat +{ + public class ChatSystem + { + private static bool m_Enabled = true; + + public static bool Enabled + { + get{ return m_Enabled; } + set{ m_Enabled = value; } + } + + public static void Initialize() + { + PacketHandlers.Register( 0xB5, 0x40, true, new OnPacketReceive( OpenChatWindowRequest ) ); + PacketHandlers.Register( 0xB3, 0, true, new OnPacketReceive( ChatAction ) ); + } + + public static void SendCommandTo( Mobile to, ChatCommand type ) + { + SendCommandTo( to, type, null, null ); + } + + public static void SendCommandTo( Mobile to, ChatCommand type, string param1 ) + { + SendCommandTo( to, type, param1, null ); + } + + public static void SendCommandTo( Mobile to, ChatCommand type, string param1, string param2 ) + { + if ( to != null ) + to.Send( new ChatMessagePacket( null, (int)type + 20, param1, param2 ) ); + } + + public static void OpenChatWindowRequest( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + if ( !m_Enabled ) + { + from.SendMessage( "The chat system has been disabled." ); + return; + } + + pvSrc.Seek( 2, System.IO.SeekOrigin.Begin ); + string chatName = pvSrc.ReadUnicodeStringSafe( ( 0x40 - 2 ) >> 1 ).Trim(); + + Account acct = state.Account as Account; + + string accountChatName = null; + + if ( acct != null ) + accountChatName = acct.GetTag( "ChatName" ); + + if ( accountChatName != null ) + accountChatName = accountChatName.Trim(); + + if ( accountChatName != null && accountChatName.Length > 0 ) + { + if ( chatName.Length > 0 && chatName != accountChatName ) + from.SendMessage( "You cannot change chat nickname once it has been set." ); + } + else + { + if ( chatName == null || chatName.Length == 0 ) + { + SendCommandTo( from, ChatCommand.AskNewNickname ); + return; + } + + if ( NameVerification.Validate( chatName, 2, 31, true, true, true, 0, NameVerification.SpaceDashPeriodQuote ) && chatName.ToLower().IndexOf( "system" ) == -1 ) + { + // TODO: Optimize this search + + foreach ( Account checkAccount in Accounts.GetAccounts() ) + { + string existingName = checkAccount.GetTag( "ChatName" ); + + if ( existingName != null ) + { + existingName = existingName.Trim(); + + if ( Insensitive.Equals( existingName, chatName ) ) + { + from.SendMessage( "Nickname already in use." ); + SendCommandTo( from, ChatCommand.AskNewNickname ); + return; + } + } + } + + accountChatName = chatName; + + if ( acct != null ) + acct.AddTag( "ChatName", chatName ); + } + else + { + from.SendLocalizedMessage( 501173 ); // That name is disallowed. + SendCommandTo( from, ChatCommand.AskNewNickname ); + return; + } + } + + SendCommandTo( from, ChatCommand.OpenChatWindow, accountChatName ); + ChatUser.AddChatUser( from ); + } + + public static ChatUser SearchForUser( ChatUser from, string name ) + { + ChatUser user = ChatUser.GetChatUser( name ); + + if ( user == null ) + from.SendMessage( 32, name ); // There is no player named '%1'. + + return user; + } + + public static void ChatAction( NetState state, PacketReader pvSrc ) + { + if ( !m_Enabled ) + return; + + try + { + Mobile from = state.Mobile; + ChatUser user = ChatUser.GetChatUser( from ); + + if ( user == null ) + return; + + string lang = pvSrc.ReadStringSafe( 4 ); + int actionID = pvSrc.ReadInt16(); + string param = pvSrc.ReadUnicodeString(); + + ChatActionHandler handler = ChatActionHandlers.GetHandler( actionID ); + + if ( handler != null ) + { + Channel channel = user.CurrentChannel; + + if ( handler.RequireConference && channel == null ) + { + user.SendMessage( 31 ); /* You must be in a conference to do this. + * To join a conference, select one from the Conference menu. + */ + } + else if ( handler.RequireModerator && !user.IsModerator ) + { + user.SendMessage( 29 ); // You must have operator status to do this. + } + else + { + handler.Callback( user, channel, param ); + } + } + else + { + Console.WriteLine( "Client: {0}: Unknown chat action 0x{1:X}: {2}", state, actionID, param ); + } + } + catch ( Exception e ) + { + Console.WriteLine( e ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/ChatActionHandler.cs b/Scripts/Engines/Chat/ChatActionHandler.cs new file mode 100644 index 000000000..c1bb6599b --- /dev/null +++ b/Scripts/Engines/Chat/ChatActionHandler.cs @@ -0,0 +1,24 @@ +using System; + +namespace Server.Engines.Chat +{ + public delegate void OnChatAction( ChatUser from, Channel channel, string param ); + + public class ChatActionHandler + { + private bool m_RequireModerator; + private bool m_RequireConference; + private OnChatAction m_Callback; + + public bool RequireModerator{ get{ return m_RequireModerator; } } + public bool RequireConference{ get{ return m_RequireConference; } } + public OnChatAction Callback{ get{ return m_Callback; } } + + public ChatActionHandler( bool requireModerator, bool requireConference, OnChatAction callback ) + { + m_RequireModerator = requireModerator; + m_RequireConference = requireConference; + m_Callback = callback; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/ChatActionHandlers.cs b/Scripts/Engines/Chat/ChatActionHandlers.cs new file mode 100644 index 000000000..92699938f --- /dev/null +++ b/Scripts/Engines/Chat/ChatActionHandlers.cs @@ -0,0 +1,359 @@ +using System; + +namespace Server.Engines.Chat +{ + public class ChatActionHandlers + { + private static ChatActionHandler[] m_Handlers; + + static ChatActionHandlers() + { + m_Handlers = new ChatActionHandler[0x100]; + + Register( 0x41, true, true, new OnChatAction( ChangeChannelPassword ) ); + + Register( 0x58, false, false, new OnChatAction( LeaveChat ) ); + + Register( 0x61, false, true, new OnChatAction( ChannelMessage ) ); + Register( 0x62, false, false, new OnChatAction( JoinChannel ) ); + Register( 0x63, false, false, new OnChatAction( JoinNewChannel ) ); + Register( 0x64, true, true, new OnChatAction( RenameChannel ) ); + Register( 0x65, false, false, new OnChatAction( PrivateMessage ) ); + Register( 0x66, false, false, new OnChatAction( AddIgnore ) ); + Register( 0x67, false, false, new OnChatAction( RemoveIgnore ) ); + Register( 0x68, false, false, new OnChatAction( ToggleIgnore ) ); + Register( 0x69, true, true, new OnChatAction( AddVoice ) ); + Register( 0x6A, true, true, new OnChatAction( RemoveVoice ) ); + Register( 0x6B, true, true, new OnChatAction( ToggleVoice ) ); + Register( 0x6C, true, true, new OnChatAction( AddModerator ) ); + Register( 0x6D, true, true, new OnChatAction( RemoveModerator ) ); + Register( 0x6E, true, true, new OnChatAction( ToggleModerator ) ); + Register( 0x6F, false, false, new OnChatAction( AllowPrivateMessages ) ); + Register( 0x70, false, false, new OnChatAction( DisallowPrivateMessages ) ); + Register( 0x71, false, false, new OnChatAction( TogglePrivateMessages ) ); + Register( 0x72, false, false, new OnChatAction( ShowCharacterName ) ); + Register( 0x73, false, false, new OnChatAction( HideCharacterName ) ); + Register( 0x74, false, false, new OnChatAction( ToggleCharacterName ) ); + Register( 0x75, false, false, new OnChatAction( QueryWhoIs ) ); + Register( 0x76, true, true, new OnChatAction( Kick ) ); + Register( 0x77, true, true, new OnChatAction( EnableDefaultVoice ) ); + Register( 0x78, true, true, new OnChatAction( DisableDefaultVoice ) ); + Register( 0x79, true, true, new OnChatAction( ToggleDefaultVoice ) ); + Register( 0x7A, false, true, new OnChatAction( EmoteMessage ) ); + } + + public static void Register( int actionID, bool requireModerator, bool requireConference, OnChatAction callback ) + { + if ( actionID >= 0 && actionID < m_Handlers.Length ) + m_Handlers[actionID] = new ChatActionHandler( requireModerator, requireConference, callback ); + } + + public static ChatActionHandler GetHandler( int actionID ) + { + if ( actionID >= 0 && actionID < m_Handlers.Length ) + return m_Handlers[actionID]; + + return null; + } + + public static void ChannelMessage( ChatUser from, Channel channel, string param ) + { + if ( channel.CanTalk( from ) ) + channel.SendIgnorableMessage( 57, from, from.GetColorCharacter() + from.Username, param ); // %1: %2 + else + from.SendMessage( 36 ); // The moderator of this conference has not given you speaking priviledges. + } + + public static void EmoteMessage( ChatUser from, Channel channel, string param ) + { + if ( channel.CanTalk( from ) ) + channel.SendIgnorableMessage( 58, from, from.GetColorCharacter() + from.Username, param ); // %1 %2 + else + from.SendMessage( 36 ); // The moderator of this conference has not given you speaking priviledges. + } + + public static void PrivateMessage( ChatUser from, Channel channel, string param ) + { + int indexOf = param.IndexOf( ' ' ); + + string name = param.Substring( 0, indexOf ); + string text = param.Substring( indexOf + 1 ); + + ChatUser target = ChatSystem.SearchForUser( from, name ); + + if ( target == null ) + return; + + if ( target.IsIgnored( from ) ) + from.SendMessage( 35, target.Username ); // %1 has chosen to ignore you. None of your messages to them will get through. + else if ( target.IgnorePrivateMessage ) + from.SendMessage( 42, target.Username ); // %1 has chosen to not receive private messages at the moment. + else + target.SendMessage( 59, from.Mobile, from.GetColorCharacter() + from.Username, text ); // [%1]: %2 + } + + public static void LeaveChat( ChatUser from, Channel channel, string param ) + { + ChatUser.RemoveChatUser( from ); + } + + public static void ChangeChannelPassword( ChatUser from, Channel channel, string param ) + { + channel.Password = param; + from.SendMessage( 60 ); // The password to the conference has been changed. + } + + public static void AllowPrivateMessages( ChatUser from, Channel channel, string param ) + { + from.IgnorePrivateMessage = false; + from.SendMessage( 37 ); // You can now receive private messages. + } + + public static void DisallowPrivateMessages( ChatUser from, Channel channel, string param ) + { + from.IgnorePrivateMessage = true; + from.SendMessage( 38 ); /* You will no longer receive private messages. + * Those who send you a message will be notified that you are blocking incoming messages. + */ + } + + public static void TogglePrivateMessages( ChatUser from, Channel channel, string param ) + { + from.IgnorePrivateMessage = !from.IgnorePrivateMessage; + from.SendMessage( from.IgnorePrivateMessage ? 38 : 37 ); // See above for messages + } + + public static void ShowCharacterName( ChatUser from, Channel channel, string param ) + { + from.Anonymous = false; + from.SendMessage( 39 ); // You are now showing your character name to any players who inquire with the whois command. + } + + public static void HideCharacterName( ChatUser from, Channel channel, string param ) + { + from.Anonymous = true; + from.SendMessage( 40 ); // You are no longer showing your character name to any players who inquire with the whois command. + } + + public static void ToggleCharacterName( ChatUser from, Channel channel, string param ) + { + from.Anonymous = !from.Anonymous; + from.SendMessage( from.Anonymous ? 40 : 39 ); // See above for messages + } + + public static void JoinChannel( ChatUser from, Channel channel, string param ) + { + string name; + string password = null; + + int start = param.IndexOf( '\"' ); + + if ( start >= 0 ) + { + int end = param.IndexOf( '\"', ++start ); + + if ( end >= 0 ) + { + name = param.Substring( start, end - start ); + password = param.Substring( ++end ); + } + else + { + name = param.Substring( start ); + } + } + else + { + int indexOf = param.IndexOf( ' ' ); + + if ( indexOf >= 0 ) + { + name = param.Substring( 0, indexOf++ ); + password = param.Substring( indexOf ); + } + else + { + name = param; + } + } + + if ( password != null ) + password = password.Trim(); + + if ( password != null && password.Length == 0 ) + password = null; + + Channel joined = Channel.FindChannelByName( name ); + + if ( joined == null ) + from.SendMessage( 33, name ); // There is no conference named '%1'. + else + joined.AddUser( from, password ); + } + + public static void JoinNewChannel( ChatUser from, Channel channel, string param ) + { + if ( (param = param.Trim()).Length == 0 ) + return; + + string name; + string password = null; + + int start = param.IndexOf( '{' ); + + if ( start >= 0 ) + { + name = param.Substring( 0, start++ ); + + int end = param.IndexOf( '}', start ); + + if ( end >= start ) + password = param.Substring( start, end - start ); + } + else + { + name = param; + } + + if ( password != null ) + password = password.Trim(); + + if ( password != null && password.Length == 0 ) + password = null; + + Channel.AddChannel( name, password ).AddUser( from, password ); + } + + public static void AddIgnore( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target == null ) + return; + + from.AddIgnored( target ); + } + + public static void RemoveIgnore( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target == null ) + return; + + from.RemoveIgnored( target ); + } + + public static void ToggleIgnore( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target == null ) + return; + + if ( from.IsIgnored( target ) ) + from.RemoveIgnored( target ); + else + from.AddIgnored( target ); + } + + public static void AddVoice( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target != null ) + channel.AddVoiced( target, from ); + } + + public static void RemoveVoice( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target != null ) + channel.RemoveVoiced( target, from ); + } + + public static void ToggleVoice( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target == null ) + return; + + if ( channel.IsVoiced( target ) ) + channel.RemoveVoiced( target, from ); + else + channel.AddVoiced( target, from ); + } + + public static void AddModerator( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target != null ) + channel.AddModerator( target, from ); + } + + public static void RemoveModerator( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target != null ) + channel.RemoveModerator( target, from ); + } + + public static void ToggleModerator( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target == null ) + return; + + if ( channel.IsModerator( target ) ) + channel.RemoveModerator( target, from ); + else + channel.AddModerator( target, from ); + } + + public static void RenameChannel( ChatUser from, Channel channel, string param ) + { + channel.Name = param; + } + + public static void QueryWhoIs( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target == null ) + return; + + if ( target.Anonymous ) + from.SendMessage( 41, target.Username ); // %1 is remaining anonymous. + else + from.SendMessage( 43, target.Username, target.Mobile.Name ); // %2 is known in the lands of Britannia as %2. + } + + public static void Kick( ChatUser from, Channel channel, string param ) + { + ChatUser target = ChatSystem.SearchForUser( from, param ); + + if ( target != null ) + channel.Kick( target, from ); + } + + public static void EnableDefaultVoice( ChatUser from, Channel channel, string param ) + { + channel.VoiceRestricted = false; + } + + public static void DisableDefaultVoice( ChatUser from, Channel channel, string param ) + { + channel.VoiceRestricted = true; + } + + public static void ToggleDefaultVoice( ChatUser from, Channel channel, string param ) + { + channel.VoiceRestricted = !channel.VoiceRestricted; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/ChatCommand.cs b/Scripts/Engines/Chat/ChatCommand.cs new file mode 100644 index 000000000..6694a553b --- /dev/null +++ b/Scripts/Engines/Chat/ChatCommand.cs @@ -0,0 +1,44 @@ +using System; + +namespace Server.Engines.Chat +{ + public enum ChatCommand + { + /// + /// Add a channel to top list. + /// + AddChannel = 0x3E8, + /// + /// Remove channel from top list. + /// + RemoveChannel = 0x3E9, + /// + /// Queries for a new chat nickname. + /// + AskNewNickname = 0x3EB, + /// + /// Closes the chat window. + /// + CloseChatWindow = 0x3EC, + /// + /// Opens the chat window. + /// + OpenChatWindow = 0x3ED, + /// + /// Add a user to current channel. + /// + AddUserToChannel = 0x3EE, + /// + /// Remove a user from current channel. + /// + RemoveUserFromChannel = 0x3EF, + /// + /// Send a message putting generic conference name at top when player leaves a channel. + /// + LeaveChannel = 0x3F0, + /// + /// Send a message putting Channel name at top and telling player he joined the channel. + /// + JoinedChannel = 0x3F1 + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/ChatUser.cs b/Scripts/Engines/Chat/ChatUser.cs new file mode 100644 index 000000000..c82863a94 --- /dev/null +++ b/Scripts/Engines/Chat/ChatUser.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections; +using Server; +using Server.Accounting; + +namespace Server.Engines.Chat +{ + public class ChatUser + { + private Mobile m_Mobile; + private Channel m_Channel; + private bool m_Anonymous; + private bool m_IgnorePrivateMessage; + private ArrayList m_Ignored, m_Ignoring; + + public ChatUser( Mobile m ) + { + m_Mobile = m; + m_Ignored = new ArrayList(); + m_Ignoring = new ArrayList(); + } + + public Mobile Mobile + { + get + { + return m_Mobile; + } + } + + public ArrayList Ignored + { + get + { + return m_Ignored; + } + } + + public ArrayList Ignoring + { + get + { + return m_Ignoring; + } + } + + public string Username + { + get + { + Account acct = m_Mobile.Account as Account; + + if ( acct != null ) + return acct.GetTag( "ChatName" ); + + return null; + } + set + { + Account acct = m_Mobile.Account as Account; + + if ( acct != null ) + acct.SetTag( "ChatName", value ); + } + } + + public Channel CurrentChannel + { + get + { + return m_Channel; + } + set + { + m_Channel = value; + } + } + + public bool IsOnline + { + get + { + return ( m_Mobile.NetState != null ); + } + } + + public bool Anonymous + { + get + { + return m_Anonymous; + } + set + { + m_Anonymous = value; + } + } + + public bool IgnorePrivateMessage + { + get + { + return m_IgnorePrivateMessage; + } + set + { + m_IgnorePrivateMessage = value; + } + } + + public const char NormalColorCharacter = '0'; + public const char ModeratorColorCharacter = '1'; + public const char VoicedColorCharacter = '2'; + + public char GetColorCharacter() + { + if ( m_Channel != null && m_Channel.IsModerator( this ) ) + return ModeratorColorCharacter; + + if ( m_Channel != null && m_Channel.IsVoiced( this ) ) + return VoicedColorCharacter; + + return NormalColorCharacter; + } + + public bool CheckOnline() + { + if ( IsOnline ) + return true; + + RemoveChatUser( this ); + return false; + } + + public void SendMessage( int number ) + { + SendMessage( number, null, null ); + } + + public void SendMessage( int number, string param1 ) + { + SendMessage( number, param1, null ); + } + + public void SendMessage( int number, string param1, string param2 ) + { + if ( m_Mobile.NetState != null ) + m_Mobile.Send( new ChatMessagePacket( m_Mobile, number, param1, param2 ) ); + } + + public void SendMessage( int number, Mobile from, string param1, string param2 ) + { + if ( m_Mobile.NetState != null ) + m_Mobile.Send( new ChatMessagePacket( from, number, param1, param2 ) ); + } + + public bool IsIgnored( ChatUser check ) + { + return m_Ignored.Contains( check ); + } + + public bool IsModerator + { + get + { + return ( m_Channel != null && m_Channel.IsModerator( this ) ); + } + } + + public void AddIgnored( ChatUser user ) + { + if ( IsIgnored( user ) ) + { + SendMessage( 22, user.Username ); // You are already ignoring %1. + } + else + { + m_Ignored.Add( user ); + user.m_Ignoring.Add( this ); + + SendMessage( 23, user.Username ); // You are now ignoring %1. + } + } + + public void RemoveIgnored( ChatUser user ) + { + if ( IsIgnored( user ) ) + { + m_Ignored.Remove( user ); + user.m_Ignoring.Remove( this ); + + SendMessage( 24, user.Username ); // You are no longer ignoring %1. + + if ( m_Ignored.Count == 0 ) + SendMessage( 26 ); // You are no longer ignoring anyone. + } + else + { + SendMessage( 25, user.Username ); // You are not ignoring %1. + } + } + + private static ArrayList m_Users = new ArrayList(); + private static Hashtable m_Table = new Hashtable(); + + public static ChatUser AddChatUser( Mobile from ) + { + ChatUser user = GetChatUser( from ); + + if ( user == null ) + { + user = new ChatUser( from ); + + m_Users.Add( user ); + m_Table[from] = user; + + Channel.SendChannelsTo( user ); + + ArrayList list = Channel.Channels; + + for ( int i = 0; i < list.Count; ++i ) + { + Channel c = (Channel)list[i]; + + if ( c.AddUser( user ) ) + break; + } + + //ChatSystem.SendCommandTo( user.m_Mobile, ChatCommand.AddUserToChannel, user.GetColorCharacter() + user.Username ); + } + + return user; + } + + public static void RemoveChatUser( ChatUser user ) + { + if ( user == null ) + return; + + for ( int i = 0; i < user.m_Ignoring.Count; ++i ) + ((ChatUser)user.m_Ignoring[i]).RemoveIgnored( user ); + + if ( m_Users.Contains( user ) ) + { + ChatSystem.SendCommandTo( user.Mobile, ChatCommand.CloseChatWindow ); + + if ( user.m_Channel != null ) + user.m_Channel.RemoveUser( user ); + + m_Users.Remove( user ); + m_Table.Remove( user.m_Mobile ); + } + } + + public static void RemoveChatUser( Mobile from ) + { + ChatUser user = GetChatUser( from ); + + RemoveChatUser( user ); + } + + public static ChatUser GetChatUser( Mobile from ) + { + return (ChatUser)m_Table[from]; + } + + public static ChatUser GetChatUser( string username ) + { + for ( int i = 0; i < m_Users.Count; ++i ) + { + ChatUser user = (ChatUser)m_Users[i]; + + if ( user.Username == username ) + return user; + } + + return null; + } + + public static void GlobalSendCommand( ChatCommand command ) + { + GlobalSendCommand( command, null, null, null ); + } + + public static void GlobalSendCommand( ChatCommand command, string param1 ) + { + GlobalSendCommand( command, null, param1, null ); + } + + public static void GlobalSendCommand( ChatCommand command, string param1, string param2 ) + { + GlobalSendCommand( command, null, param1, param2 ); + } + + public static void GlobalSendCommand( ChatCommand command, ChatUser initiator ) + { + GlobalSendCommand( command, initiator, null, null ); + } + + public static void GlobalSendCommand( ChatCommand command, ChatUser initiator, string param1 ) + { + GlobalSendCommand( command, initiator, param1, null ); + } + + public static void GlobalSendCommand( ChatCommand command, ChatUser initiator, string param1, string param2 ) + { + for ( int i = 0; i < m_Users.Count; ++i ) + { + ChatUser user = (ChatUser)m_Users[i]; + + if ( user == initiator ) + continue; + + if ( user.CheckOnline() ) + ChatSystem.SendCommandTo( user.m_Mobile, command, param1, param2 ); + else if ( !m_Users.Contains( i ) ) + --i; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/Chatold.cs b/Scripts/Engines/Chat/Chatold.cs new file mode 100644 index 000000000..ece5bbc0e --- /dev/null +++ b/Scripts/Engines/Chat/Chatold.cs @@ -0,0 +1,20 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Chat +{ + public class ChatSystem + { + public static void Initialize() + { + EventSink.ChatRequest += new ChatRequestEventHandler( EventSink_ChatRequest ); + } + + private static void EventSink_ChatRequest( ChatRequestEventArgs e ) + { + e.Mobile.SendMessage( "Chat is not currently supported." ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Chat/Packets.cs b/Scripts/Engines/Chat/Packets.cs new file mode 100644 index 000000000..ace0cc166 --- /dev/null +++ b/Scripts/Engines/Chat/Packets.cs @@ -0,0 +1,30 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Engines.Chat +{ + public sealed class ChatMessagePacket : Packet + { + public ChatMessagePacket( Mobile who, int number, string param1, string param2 ) : base( 0xB2 ) + { + if ( param1 == null ) + param1 = String.Empty; + + if ( param2 == null ) + param2 = String.Empty; + + EnsureCapacity( 13 + ((param1.Length + param2.Length) * 2) ); + + m_Stream.Write( (ushort) (number - 20) ); + + if ( who != null ) + m_Stream.WriteAsciiFixed( who.Language, 4 ); + else + m_Stream.Write( (int) 0 ); + + m_Stream.WriteBigUniNull( param1 ); + m_Stream.WriteBigUniNull( param2 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftContext.cs b/Scripts/Engines/Craft/Core/CraftContext.cs new file mode 100644 index 000000000..115554317 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftContext.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; + +namespace Server.Engines.Craft +{ + public enum CraftMarkOption + { + MarkItem, + DoNotMark, + PromptForMark + } + + public class CraftContext + { + private ArrayList m_Items; + private int m_LastResourceIndex; + private int m_LastResourceIndex2; + private int m_LastGroupIndex; + private bool m_DoNotColor; + private CraftMarkOption m_MarkOption; + + public ArrayList Items{ get{ return m_Items; } } + public int LastResourceIndex{ get{ return m_LastResourceIndex; } set{ m_LastResourceIndex = value; } } + public int LastResourceIndex2{ get{ return m_LastResourceIndex2; } set{ m_LastResourceIndex2 = value; } } + public int LastGroupIndex{ get{ return m_LastGroupIndex; } set{ m_LastGroupIndex = value; } } + public bool DoNotColor{ get{ return m_DoNotColor; } set{ m_DoNotColor = value; } } + public CraftMarkOption MarkOption{ get{ return m_MarkOption; } set{ m_MarkOption = value; } } + + public CraftContext() + { + m_Items = new ArrayList(); + m_LastResourceIndex = -1; + m_LastResourceIndex2 = -1; + m_LastGroupIndex = -1; + } + + public CraftItem LastMade + { + get + { + if ( m_Items.Count > 0 ) + return (CraftItem)m_Items[0]; + + return null; + } + } + + public void OnMade( CraftItem item ) + { + m_Items.Remove( item ); + + if ( m_Items.Count == 10 ) + m_Items.RemoveAt( 9 ); + + m_Items.Insert( 0, item ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftGroup.cs b/Scripts/Engines/Craft/Core/CraftGroup.cs new file mode 100644 index 000000000..10626b8a1 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftGroup.cs @@ -0,0 +1,39 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftGroup + { + private CraftItemCol m_arCraftItem; + + private string m_NameString; + private int m_NameNumber; + + public CraftGroup( TextDefinition groupName ) + { + m_NameNumber = groupName; + m_NameString = groupName; + m_arCraftItem = new CraftItemCol(); + } + + public void AddCraftItem( CraftItem craftItem ) + { + m_arCraftItem.Add( craftItem ); + } + + public CraftItemCol CraftItems + { + get { return m_arCraftItem; } + } + + public string NameString + { + get { return m_NameString; } + } + + public int NameNumber + { + get { return m_NameNumber; } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftGroupCol.cs b/Scripts/Engines/Craft/Core/CraftGroupCol.cs new file mode 100644 index 000000000..887ded50d --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftGroupCol.cs @@ -0,0 +1,48 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftGroupCol : System.Collections.CollectionBase + { + public CraftGroupCol() + { + } + + public int Add( CraftGroup craftGroup ) + { + return List.Add( craftGroup ); + } + + public void Remove( int index ) + { + if ( index > Count - 1 || index < 0 ) + { + } + else + { + List.RemoveAt( index ); + } + } + + public CraftGroup GetAt( int index ) + { + return ( CraftGroup ) List[index]; + } + + public int SearchFor( TextDefinition groupName ) + { + for ( int i = 0; i < List.Count; i++ ) + { + CraftGroup craftGroup = (CraftGroup)List[i]; + + int nameNumber = craftGroup.NameNumber; + string nameString = craftGroup.NameString; + + if ( ( nameNumber != 0 && nameNumber == groupName.Number ) || ( nameString != null && nameString == groupName.String ) ) + return i; + } + + return -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftGump.cs b/Scripts/Engines/Craft/Core/CraftGump.cs new file mode 100644 index 000000000..880e4f57c --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftGump.cs @@ -0,0 +1,582 @@ +using System; +using System.Collections; +using Server.Gumps; +using Server.Network; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class CraftGump : Gump + { + private Mobile m_From; + private CraftSystem m_CraftSystem; + private BaseTool m_Tool; + + private CraftPage m_Page; + + private const int LabelHue = 0x480; + private const int LabelColor = 0x7FFF; + private const int FontColor = 0xFFFFFF; + + private enum CraftPage + { + None, + PickResource, + PickResource2 + } + + /*public CraftGump( Mobile from, CraftSystem craftSystem, BaseTool tool ): this( from, craftSystem, -1, -1, tool, null ) + { + }*/ + + public CraftGump( Mobile from, CraftSystem craftSystem, BaseTool tool, object notice ) : this( from, craftSystem, tool, notice, CraftPage.None ) + { + } + + private CraftGump( Mobile from, CraftSystem craftSystem, BaseTool tool, object notice, CraftPage page ) : base( 40, 40 ) + { + m_From = from; + m_CraftSystem = craftSystem; + m_Tool = tool; + m_Page = page; + + CraftContext context = craftSystem.GetContext( from ); + + from.CloseGump( typeof( CraftGump ) ); + from.CloseGump( typeof( CraftGumpItem ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 530, 437, 5054 ); + AddImageTiled( 10, 10, 510, 22, 2624 ); + AddImageTiled( 10, 292, 150, 45, 2624 ); + AddImageTiled( 165, 292, 355, 45, 2624 ); + AddImageTiled( 10, 342, 510, 85, 2624 ); + AddImageTiled( 10, 37, 200, 250, 2624 ); + AddImageTiled( 215, 37, 305, 250, 2624 ); + AddAlphaRegion( 10, 10, 510, 417 ); + + if ( craftSystem.GumpTitleNumber > 0 ) + AddHtmlLocalized( 10, 12, 510, 20, craftSystem.GumpTitleNumber, LabelColor, false, false ); + else + AddHtml( 10, 12, 510, 20, craftSystem.GumpTitleString, false, false ); + + AddHtmlLocalized( 10, 37, 200, 22, 1044010, LabelColor, false, false ); //
CATEGORIES
+ AddHtmlLocalized( 215, 37, 305, 22, 1044011, LabelColor, false, false ); //
SELECTIONS
+ AddHtmlLocalized( 10, 302, 150, 25, 1044012, LabelColor, false, false ); //
NOTICES
+ + AddButton( 15, 402, 4017, 4019, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 405, 150, 18, 1011441, LabelColor, false, false ); // EXIT + + AddButton( 270, 402, 4005, 4007, GetButtonID( 6, 2 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 305, 405, 150, 18, 1044013, LabelColor, false, false ); // MAKE LAST + + // Mark option + if ( craftSystem.MarkOption ) + { + AddButton( 270, 362, 4005, 4007, GetButtonID( 6, 6 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 305, 365, 150, 18, 1044017 + (context == null ? 0 : (int)context.MarkOption), LabelColor, false, false ); // MARK ITEM + } + // **************************************** + + // Resmelt option + if ( craftSystem.Resmelt ) + { + AddButton( 15, 342, 4005, 4007, GetButtonID( 6, 1 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 345, 150, 18, 1044259, LabelColor, false, false ); // SMELT ITEM + } + // **************************************** + + // Repair option + if ( craftSystem.Repair ) + { + AddButton( 270, 342, 4005, 4007, GetButtonID( 6, 5 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 305, 345, 150, 18, 1044260, LabelColor, false, false ); // REPAIR ITEM + } + // **************************************** + + // Enhance option + if ( craftSystem.CanEnhance ) + { + AddButton( 270, 382, 4005, 4007, GetButtonID( 6, 8 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 305, 385, 150, 18, 1061001, LabelColor, false, false ); // ENHANCE ITEM + } + // **************************************** + + if ( notice is int && (int)notice > 0 ) + AddHtmlLocalized( 170, 295, 350, 40, (int)notice, LabelColor, false, false ); + else if ( notice is string ) + AddHtml( 170, 295, 350, 40, String.Format( "{1}", FontColor, notice ), false, false ); + + // If the system has more than one resource + if ( craftSystem.CraftSubRes.Init ) + { + string nameString = craftSystem.CraftSubRes.NameString; + int nameNumber = craftSystem.CraftSubRes.NameNumber; + + int resIndex = ( context == null ? -1 : context.LastResourceIndex ); + + if ( resIndex > -1 ) + { + CraftSubRes subResource = craftSystem.CraftSubRes.GetAt( resIndex ); + + nameString = subResource.NameString; + nameNumber = subResource.NameNumber; + } + + AddButton( 15, 362, 4005, 4007, GetButtonID( 6, 0 ), GumpButtonType.Reply, 0 ); + + if ( nameNumber > 0 ) + AddHtmlLocalized( 50, 365, 250, 18, nameNumber, LabelColor, false, false ); + else + AddLabel( 50, 362, LabelHue, nameString ); + } + // **************************************** + + // For dragon scales + if ( craftSystem.CraftSubRes2.Init ) + { + string nameString = craftSystem.CraftSubRes2.NameString; + int nameNumber = craftSystem.CraftSubRes2.NameNumber; + + int resIndex = ( context == null ? -1 : context.LastResourceIndex2 ); + + if ( resIndex > -1 ) + { + CraftSubRes subResource = craftSystem.CraftSubRes2.GetAt( resIndex ); + + nameString = subResource.NameString; + nameNumber = subResource.NameNumber; + } + + AddButton( 15, 382, 4005, 4007, GetButtonID( 6, 7 ), GumpButtonType.Reply, 0 ); + + if ( nameNumber > 0 ) + AddHtmlLocalized( 50, 385, 250, 18, nameNumber, LabelColor, false, false ); + else + AddLabel( 50, 385, LabelHue, nameString ); + } + // **************************************** + + CreateGroupList(); + + if ( page == CraftPage.PickResource ) + CreateResList( false ); + else if ( page == CraftPage.PickResource2 ) + CreateResList( true ); + else if ( context != null && context.LastGroupIndex > -1 ) + CreateItemList( context.LastGroupIndex ); + } + + public void CreateResList( bool opt ) + { + CraftSubResCol res = ( opt ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes ); + + for ( int i = 0; i < res.Count; ++i ) + { + int index = i % 10; + + CraftSubRes subResource = res.GetAt( i ); + + if ( index == 0 ) + { + if ( i > 0 ) + AddButton( 485, 260, 4005, 4007, 0, GumpButtonType.Page, (i / 10) + 1 ); + + AddPage( (i / 10) + 1 ); + + if ( i > 0 ) + AddButton( 455, 260, 4014, 4015, 0, GumpButtonType.Page, i / 10 ); + + CraftContext context = m_CraftSystem.GetContext( m_From ); + + AddButton( 220, 260, 4005, 4007, GetButtonID( 6, 4 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 255, 263, 200, 18, (context == null || !context.DoNotColor) ? 1061591 : 1061590, LabelColor, false, false ); + } + + AddButton( 220, 60 + (index * 20), 4005, 4007, GetButtonID( 5, i ), GumpButtonType.Reply, 0 ); + + if ( subResource.NameNumber > 0 ) + AddHtmlLocalized( 255, 63 + (index * 20), 250, 18, subResource.NameNumber, LabelColor, false, false ); + else + AddLabel( 255, 60 + (index * 20), LabelHue, subResource.NameString ); + } + } + + public void CreateMakeLastList() + { + CraftContext context = m_CraftSystem.GetContext( m_From ); + + if ( context == null ) + return; + + ArrayList items = context.Items; + + if ( items.Count > 0 ) + { + for ( int i = 0; i < items.Count; ++i ) + { + int index = i % 10; + + CraftItem craftItem = (CraftItem)items[i]; + + if ( index == 0 ) + { + if ( i > 0 ) + { + AddButton( 370, 260, 4005, 4007, 0, GumpButtonType.Page, (i / 10) + 1 ); + AddHtmlLocalized( 405, 263, 100, 18, 1044045, LabelColor, false, false ); // NEXT PAGE + } + + AddPage( (i / 10) + 1 ); + + if ( i > 0 ) + { + AddButton( 220, 260, 4014, 4015, 0, GumpButtonType.Page, i / 10 ); + AddHtmlLocalized( 255, 263, 100, 18, 1044044, LabelColor, false, false ); // PREV PAGE + } + } + + AddButton( 220, 60 + (index * 20), 4005, 4007, GetButtonID( 3, i ), GumpButtonType.Reply, 0 ); + + if ( craftItem.NameNumber > 0 ) + AddHtmlLocalized( 255, 63 + (index * 20), 220, 18, craftItem.NameNumber, LabelColor, false, false ); + else + AddLabel( 255, 60 + (index * 20), LabelHue, craftItem.NameString ); + + AddButton( 480, 60 + (index * 20), 4011, 4012, GetButtonID( 4, i ), GumpButtonType.Reply, 0 ); + } + } + else + { + // NOTE: This is not as OSI; it is an intentional difference + + AddHtmlLocalized( 230, 62, 200, 22, 1044165, LabelColor, false, false ); // You haven't made anything yet. + } + } + + public void CreateItemList( int selectedGroup ) + { + if ( selectedGroup == 501 ) // 501 : Last 10 + { + CreateMakeLastList(); + return; + } + + CraftGroupCol craftGroupCol = m_CraftSystem.CraftGroups; + CraftGroup craftGroup = craftGroupCol.GetAt( selectedGroup ); + CraftItemCol craftItemCol = craftGroup.CraftItems; + + for ( int i = 0; i < craftItemCol.Count; ++i ) + { + int index = i % 10; + + CraftItem craftItem = craftItemCol.GetAt( i ); + + if ( index == 0 ) + { + if ( i > 0 ) + { + AddButton( 370, 260, 4005, 4007, 0, GumpButtonType.Page, (i / 10) + 1 ); + AddHtmlLocalized( 405, 263, 100, 18, 1044045, LabelColor, false, false ); // NEXT PAGE + } + + AddPage( (i / 10) + 1 ); + + if ( i > 0 ) + { + AddButton( 220, 260, 4014, 4015, 0, GumpButtonType.Page, i / 10 ); + AddHtmlLocalized( 255, 263, 100, 18, 1044044, LabelColor, false, false ); // PREV PAGE + } + } + + AddButton( 220, 60 + (index * 20), 4005, 4007, GetButtonID( 1, i ), GumpButtonType.Reply, 0 ); + + if ( craftItem.NameNumber > 0 ) + AddHtmlLocalized( 255, 63 + (index * 20), 220, 18, craftItem.NameNumber, LabelColor, false, false ); + else + AddLabel( 255, 60 + (index * 20), LabelHue, craftItem.NameString ); + + AddButton( 480, 60 + (index * 20), 4011, 4012, GetButtonID( 2, i ), GumpButtonType.Reply, 0 ); + } + } + + public int CreateGroupList() + { + CraftGroupCol craftGroupCol = m_CraftSystem.CraftGroups; + + AddButton( 15, 60, 4005, 4007, GetButtonID( 6, 3 ), GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 63, 150, 18, 1044014, LabelColor, false, false ); // LAST TEN + + for ( int i = 0; i < craftGroupCol.Count; i++ ) + { + CraftGroup craftGroup = craftGroupCol.GetAt( i ); + + AddButton( 15, 80 + (i * 20), 4005, 4007, GetButtonID( 0, i ), GumpButtonType.Reply, 0 ); + + if ( craftGroup.NameNumber > 0 ) + AddHtmlLocalized( 50, 83 + (i * 20), 150, 18, craftGroup.NameNumber, LabelColor, false, false ); + else + AddLabel( 50, 80 + (i * 20), LabelHue, craftGroup.NameString ); + } + + return craftGroupCol.Count; + } + + public static int GetButtonID( int type, int index ) + { + return 1 + type + (index * 7); + } + + public void CraftItem( CraftItem item ) + { + int num = m_CraftSystem.CanCraft( m_From, m_Tool, item.ItemType ); + + if ( num > 0 ) + { + m_From.SendGump( new CraftGump( m_From, m_CraftSystem, m_Tool, num ) ); + } + else + { + Type type = null; + + CraftContext context = m_CraftSystem.GetContext( m_From ); + + if ( context != null ) + { + CraftSubResCol res = ( item.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes ); + int resIndex = ( item.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex ); + + if ( resIndex >= 0 && resIndex < res.Count ) + type = res.GetAt( resIndex ).ItemType; + } + + m_CraftSystem.CreateItem( m_From, item.ItemType, type, m_Tool, item ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID <= 0 ) + return; // Canceled + + int buttonID = info.ButtonID - 1; + int type = buttonID % 7; + int index = buttonID / 7; + + CraftSystem system = m_CraftSystem; + CraftGroupCol groups = system.CraftGroups; + CraftContext context = system.GetContext( m_From ); + + switch ( type ) + { + case 0: // Show group + { + if ( context == null ) + break; + + if ( index >= 0 && index < groups.Count ) + { + context.LastGroupIndex = index; + m_From.SendGump( new CraftGump( m_From, system, m_Tool, null ) ); + } + + break; + } + case 1: // Create item + { + if ( context == null ) + break; + + int groupIndex = context.LastGroupIndex; + + if ( groupIndex >= 0 && groupIndex < groups.Count ) + { + CraftGroup group = groups.GetAt( groupIndex ); + + if ( index >= 0 && index < group.CraftItems.Count ) + CraftItem( group.CraftItems.GetAt( index ) ); + } + + break; + } + case 2: // Item details + { + if ( context == null ) + break; + + int groupIndex = context.LastGroupIndex; + + if ( groupIndex >= 0 && groupIndex < groups.Count ) + { + CraftGroup group = groups.GetAt( groupIndex ); + + if ( index >= 0 && index < group.CraftItems.Count ) + m_From.SendGump( new CraftGumpItem( m_From, system, group.CraftItems.GetAt( index ), m_Tool ) ); + } + + break; + } + case 3: // Create item (last 10) + { + if ( context == null ) + break; + + ArrayList lastTen = context.Items; + + if ( index >= 0 && index < lastTen.Count ) + CraftItem( (CraftItem)lastTen[index] ); + + break; + } + case 4: // Item details (last 10) + { + if ( context == null ) + break; + + ArrayList lastTen = context.Items; + + if ( index >= 0 && index < lastTen.Count ) + m_From.SendGump( new CraftGumpItem( m_From, system, (CraftItem)lastTen[index], m_Tool ) ); + + break; + } + case 5: // Resource selected + { + if ( m_Page == CraftPage.PickResource && index >= 0 && index < system.CraftSubRes.Count ) + { + int groupIndex = ( context == null ? -1 : context.LastGroupIndex ); + + CraftSubRes res = system.CraftSubRes.GetAt( index ); + + if ( m_From.Skills[system.MainSkill].Base < res.RequiredSkill ) + { + m_From.SendGump( new CraftGump( m_From, system, m_Tool, res.Message ) ); + } + else + { + if ( context != null ) + context.LastResourceIndex = index; + + m_From.SendGump( new CraftGump( m_From, system, m_Tool, null ) ); + } + } + else if ( m_Page == CraftPage.PickResource2 && index >= 0 && index < system.CraftSubRes2.Count ) + { + int groupIndex = ( context == null ? -1 : context.LastGroupIndex ); + + CraftSubRes res = system.CraftSubRes2.GetAt( index ); + + if ( m_From.Skills[system.MainSkill].Base < res.RequiredSkill ) + { + m_From.SendGump( new CraftGump( m_From, system, m_Tool, res.Message ) ); + } + else + { + if ( context != null ) + context.LastResourceIndex2 = index; + + m_From.SendGump( new CraftGump( m_From, system, m_Tool, null ) ); + } + } + + break; + } + case 6: // Misc. buttons + { + switch ( index ) + { + case 0: // Resource selection + { + if ( system.CraftSubRes.Init ) + m_From.SendGump( new CraftGump( m_From, system, m_Tool, null, CraftPage.PickResource ) ); + + break; + } + case 1: // Smelt item + { + if ( system.Resmelt ) + Resmelt.Do( m_From, system, m_Tool ); + + break; + } + case 2: // Make last + { + if ( context == null ) + break; + + CraftItem item = context.LastMade; + + if ( item != null ) + CraftItem( item ); + else + m_From.SendGump( new CraftGump( m_From, m_CraftSystem, m_Tool, 1044165, m_Page ) ); // You haven't made anything yet. + + break; + } + case 3: // Last 10 + { + if ( context == null ) + break; + + context.LastGroupIndex = 501; + m_From.SendGump( new CraftGump( m_From, system, m_Tool, null ) ); + + break; + } + case 4: // Toggle use resource hue + { + if ( context == null ) + break; + + context.DoNotColor = !context.DoNotColor; + + m_From.SendGump( new CraftGump( m_From, m_CraftSystem, m_Tool, null, m_Page ) ); + + break; + } + case 5: // Repair item + { + if ( system.Repair ) + Repair.Do( m_From, system, m_Tool ); + + break; + } + case 6: // Toggle mark option + { + if ( context == null || !system.MarkOption ) + break; + + switch ( context.MarkOption ) + { + case CraftMarkOption.MarkItem: context.MarkOption = CraftMarkOption.DoNotMark; break; + case CraftMarkOption.DoNotMark: context.MarkOption = CraftMarkOption.PromptForMark; break; + case CraftMarkOption.PromptForMark: context.MarkOption = CraftMarkOption.MarkItem; break; + } + + m_From.SendGump( new CraftGump( m_From, m_CraftSystem, m_Tool, null, m_Page ) ); + + break; + } + case 7: // Resource selection 2 + { + if ( system.CraftSubRes2.Init ) + m_From.SendGump( new CraftGump( m_From, system, m_Tool, null, CraftPage.PickResource2 ) ); + + break; + } + case 8: // Enhance item + { + if ( system.CanEnhance ) + Enhance.BeginTarget( m_From, system, m_Tool ); + + break; + } + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftGumpItem.cs b/Scripts/Engines/Craft/Core/CraftGumpItem.cs new file mode 100644 index 000000000..9c32f7512 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftGumpItem.cs @@ -0,0 +1,287 @@ +using System; +using Server.Gumps; +using Server.Network; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Craft +{ + public class CraftGumpItem : Gump + { + private Mobile m_From; + private CraftSystem m_CraftSystem; + private CraftItem m_CraftItem; + private BaseTool m_Tool; + + private const int LabelHue = 0x480; // 0x384 + private const int RedLabelHue = 0x20; + + private const int LabelColor = 0x7FFF; + private const int RedLabelColor = 0x6400; + + private const int GreyLabelColor = 0x3DEF; + + private int m_OtherCount; + + public CraftGumpItem( Mobile from, CraftSystem craftSystem, CraftItem craftItem, BaseTool tool ) : base( 40, 40 ) + { + m_From = from; + m_CraftSystem = craftSystem; + m_CraftItem = craftItem; + m_Tool = tool; + + from.CloseGump( typeof( CraftGump ) ); + from.CloseGump( typeof( CraftGumpItem ) ); + + AddPage( 0 ); + AddBackground( 0, 0, 530, 417, 5054 ); + AddImageTiled( 10, 10, 510, 22, 2624 ); + AddImageTiled( 10, 37, 150, 148, 2624 ); + AddImageTiled( 165, 37, 355, 90, 2624 ); + AddImageTiled( 10, 190, 155, 22, 2624 ); + AddImageTiled( 10, 217, 150, 53, 2624 ); + AddImageTiled( 165, 132, 355, 80, 2624 ); + AddImageTiled( 10, 275, 155, 22, 2624 ); + AddImageTiled( 10, 302, 150, 53, 2624 ); + AddImageTiled( 165, 217, 355, 80, 2624 ); + AddImageTiled( 10, 360, 155, 22, 2624 ); + AddImageTiled( 165, 302, 355, 80, 2624 ); + AddImageTiled( 10, 387, 510, 22, 2624 ); + AddAlphaRegion( 10, 10, 510, 399 ); + + AddHtmlLocalized( 170, 40, 150, 20, 1044053, LabelColor, false, false ); // ITEM + AddHtmlLocalized( 10, 192, 150, 22, 1044054, LabelColor, false, false ); //
SKILLS
+ AddHtmlLocalized( 10, 277, 150, 22, 1044055, LabelColor, false, false ); //
MATERIALS
+ AddHtmlLocalized( 10, 362, 150, 22, 1044056, LabelColor, false, false ); //
OTHER
+ + if ( craftSystem.GumpTitleNumber > 0 ) + AddHtmlLocalized( 10, 12, 510, 20, craftSystem.GumpTitleNumber, LabelColor, false, false ); + else + AddHtml( 10, 12, 510, 20, craftSystem.GumpTitleString, false, false ); + + AddButton( 15, 387, 4014, 4016, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 390, 150, 18, 1044150, LabelColor, false, false ); // BACK + + bool needsRecipe = ( craftItem.Recipe != null && from is PlayerMobile && !((PlayerMobile)from).HasRecipe( craftItem.Recipe ) ); + + if( needsRecipe ) + { + AddButton( 270, 387, 4005, 4007, 0, GumpButtonType.Page, 0 ); + AddHtmlLocalized( 305, 390, 150, 18, 1044151, GreyLabelColor, false, false ); // MAKE NOW + } + else + { + AddButton( 270, 387, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 305, 390, 150, 18, 1044151, LabelColor, false, false ); // MAKE NOW + } + + if ( craftItem.NameNumber > 0 ) + AddHtmlLocalized( 330, 40, 180, 18, craftItem.NameNumber, LabelColor, false, false ); + else + AddLabel( 330, 40, LabelHue, craftItem.NameString ); + + if ( craftItem.UseAllRes ) + AddHtmlLocalized( 170, 302 + (m_OtherCount++ * 20), 310, 18, 1048176, LabelColor, false, false ); // Makes as many as possible at once + + DrawItem(); + DrawSkill(); + DrawRessource(); + + /* + if( craftItem.RequiresSE ) + AddHtmlLocalized( 170, 302 + (m_OtherCount++ * 20), 310, 18, 1063363, LabelColor, false, false ); //* Requires the "Samurai Empire" expansion + * */ + + if( craftItem.RequiredExpansion != Expansion.None ) + { + bool supportsEx = (from.NetState != null && from.NetState.SupportsExpansion( craftItem.RequiredExpansion )); + TextDefinition.AddHtmlText( this, 170, 302 + (m_OtherCount++ * 20), 310, 18, RequiredExpansionMessage( craftItem.RequiredExpansion ), false, false, supportsEx ? LabelColor : RedLabelColor, supportsEx ? LabelHue : RedLabelHue ); + } + + if( needsRecipe ) + AddHtmlLocalized( 170, 302 + (m_OtherCount++ * 20), 310, 18, 1073620, RedLabelColor, false, false ); // You have not learned this recipe. + + } + + private TextDefinition RequiredExpansionMessage( Expansion expansion ) + { + switch( expansion ) + { + case Expansion.SE: + return 1063363; // * Requires the "Samurai Empire" expansion + case Expansion.ML: + return 1072651; // * Requires the "Mondain's Legacy" expansion + default: + return String.Format( "* Requires the \"{0}\" expansion", ExpansionInfo.GetInfo( expansion ).Name ); + } + } + + private bool m_ShowExceptionalChance; + + public void DrawItem() + { + Type type = m_CraftItem.ItemType; + + AddItem( 20, 50, CraftItem.ItemIDOf( type ) ); + + if ( m_CraftItem.IsMarkable( type ) ) + { + AddHtmlLocalized( 170, 302 + (m_OtherCount++ * 20), 310, 18, 1044059, LabelColor, false, false ); // This item may hold its maker's mark + m_ShowExceptionalChance = true; + } + } + + public void DrawSkill() + { + for ( int i = 0; i < m_CraftItem.Skills.Count; i++ ) + { + CraftSkill skill = m_CraftItem.Skills.GetAt( i ); + double minSkill = skill.MinSkill, maxSkill = skill.MaxSkill; + + if ( minSkill < 0 ) + minSkill = 0; + + AddHtmlLocalized( 170, 132 + (i * 20), 200, 18, 1044060 + (int)skill.SkillToMake, LabelColor, false, false ); + AddLabel( 430, 132 + (i * 20), LabelHue, String.Format( "{0:F1}", minSkill ) ); + } + + CraftSubResCol res = ( m_CraftItem.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes ); + int resIndex = -1; + + CraftContext context = m_CraftSystem.GetContext( m_From ); + + if ( context != null ) + resIndex = ( m_CraftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex ); + + bool allRequiredSkills = true; + double chance = m_CraftItem.GetSuccessChance( m_From, resIndex > -1 ? res.GetAt( resIndex ).ItemType : null, m_CraftSystem, false, ref allRequiredSkills ); + double excepChance = m_CraftItem.GetExceptionalChance( m_CraftSystem, chance, m_From ); + + if ( chance < 0.0 ) + chance = 0.0; + else if ( chance > 1.0 ) + chance = 1.0; + + AddHtmlLocalized( 170, 80, 250, 18, 1044057, LabelColor, false, false ); // Success Chance: + AddLabel( 430, 80, LabelHue, String.Format( "{0:F1}%", chance * 100 ) ); + + if ( m_ShowExceptionalChance ) + { + if( excepChance < 0.0 ) + excepChance = 0.0; + else if( excepChance > 1.0 ) + excepChance = 1.0; + + AddHtmlLocalized( 170, 100, 250, 18, 1044058, 32767, false, false ); // Exceptional Chance: + AddLabel( 430, 100, LabelHue, String.Format( "{0:F1}%", excepChance * 100 ) ); + } + } + + private static Type typeofBlankScroll = typeof( BlankScroll ); + private static Type typeofSpellScroll = typeof( SpellScroll ); + + public void DrawRessource() + { + bool retainedColor = false; + + CraftContext context = m_CraftSystem.GetContext( m_From ); + + CraftSubResCol res = ( m_CraftItem.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes ); + int resIndex = -1; + + if ( context != null ) + resIndex = ( m_CraftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex ); + + bool cropScroll = ( m_CraftItem.Ressources.Count > 1 ) + && m_CraftItem.Ressources.GetAt( m_CraftItem.Ressources.Count - 1 ).ItemType == typeofBlankScroll + && typeofSpellScroll.IsAssignableFrom( m_CraftItem.ItemType ); + + for ( int i = 0; i < m_CraftItem.Ressources.Count - (cropScroll ? 1 : 0) && i < 4; i++ ) + { + Type type; + string nameString; + int nameNumber; + + CraftRes craftResource = m_CraftItem.Ressources.GetAt( i ); + + type = craftResource.ItemType; + nameString = craftResource.NameString; + nameNumber = craftResource.NameNumber; + + // Resource Mutation + if ( type == res.ResType && resIndex > -1 ) + { + CraftSubRes subResource = res.GetAt( resIndex ); + + type = subResource.ItemType; + + nameString = subResource.NameString; + nameNumber = subResource.GenericNameNumber; + + if ( nameNumber <= 0 ) + nameNumber = subResource.NameNumber; + } + // ****************** + + if ( !retainedColor && m_CraftItem.RetainsColorFrom( m_CraftSystem, type ) ) + { + retainedColor = true; + AddHtmlLocalized( 170, 302 + (m_OtherCount++ * 20), 310, 18, 1044152, LabelColor, false, false ); // * The item retains the color of this material + AddLabel( 500, 219 + (i * 20), LabelHue, "*" ); + } + + if ( nameNumber > 0 ) + AddHtmlLocalized( 170, 219 + (i * 20), 310, 18, nameNumber, LabelColor, false, false ); + else + AddLabel( 170, 219 + (i * 20), LabelHue, nameString ); + + AddLabel( 430, 219 + (i * 20), LabelHue, craftResource.Amount.ToString() ); + } + + if ( m_CraftItem.NameNumber == 1041267 ) // runebook + { + AddHtmlLocalized( 170, 219 + (m_CraftItem.Ressources.Count * 20), 310, 18, 1044447, LabelColor, false, false ); + AddLabel( 430, 219 + (m_CraftItem.Ressources.Count * 20), LabelHue, "1" ); + } + + if ( cropScroll ) + AddHtmlLocalized( 170, 302 + (m_OtherCount++ * 20), 360, 18, 1044379, LabelColor, false, false ); // Inscribing scrolls also requires a blank scroll and mana. + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + // Back Button + if ( info.ButtonID == 0 ) + { + CraftGump craftGump = new CraftGump( m_From, m_CraftSystem, m_Tool, null ); + m_From.SendGump( craftGump ); + } + else // Make Button + { + int num = m_CraftSystem.CanCraft( m_From, m_Tool, m_CraftItem.ItemType ); + + if ( num > 0 ) + { + m_From.SendGump( new CraftGump( m_From, m_CraftSystem, m_Tool, num ) ); + } + else + { + Type type = null; + + CraftContext context = m_CraftSystem.GetContext( m_From ); + + if ( context != null ) + { + CraftSubResCol res = ( m_CraftItem.UseSubRes2 ? m_CraftSystem.CraftSubRes2 : m_CraftSystem.CraftSubRes ); + int resIndex = ( m_CraftItem.UseSubRes2 ? context.LastResourceIndex2 : context.LastResourceIndex ); + + if ( resIndex > -1 ) + type = res.GetAt( resIndex ).ItemType; + } + + m_CraftSystem.CreateItem( m_From, m_CraftItem.ItemType, type, m_Tool, m_CraftItem ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftItem.cs b/Scripts/Engines/Craft/Core/CraftItem.cs new file mode 100644 index 000000000..e0a74114e --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftItem.cs @@ -0,0 +1,1309 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Factions; +using Server.Mobiles; + +namespace Server.Engines.Craft +{ + public enum ConsumeType + { + All, Half, None + } + + public interface ICraftable + { + int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ); + } + + public class CraftItem + { + private CraftResCol m_arCraftRes; + private CraftSkillCol m_arCraftSkill; + private Type m_Type; + + private string m_GroupNameString; + private int m_GroupNameNumber; + + private string m_NameString; + private int m_NameNumber; + + private int m_Mana; + private int m_Hits; + private int m_Stam; + + private bool m_UseAllRes; + + private bool m_NeedHeat; + private bool m_NeedOven; + private bool m_NeedMill; + + private bool m_UseSubRes2; + + private bool m_ForceNonExceptional; + + public bool ForceNonExceptional + { + get { return m_ForceNonExceptional; } + set { m_ForceNonExceptional = value; } + } + + + private Expansion m_RequiredExpansion; + + public Expansion RequiredExpansion + { + get { return m_RequiredExpansion; } + set { m_RequiredExpansion = value; } + } + + private Recipe m_Recipe; + + public Recipe Recipe + { + get { return m_Recipe; } + } + + public void AddRecipe( int id, CraftSystem system ) + { + if( m_Recipe != null ) + { + Console.WriteLine( "Warning: Attempted add of recipe #{0} to the crafting of {1} in CraftSystem {2}.", id, this.m_Type.Name, system ); + return; + } + + m_Recipe = new Recipe( id, system, this ); + } + + + private static Hashtable m_ItemIDs = new Hashtable(); + + public static int ItemIDOf( Type type ) + { + object obj = m_ItemIDs[type]; + + if ( obj != null ) + return (int)obj; + + int itemID = 0; + + if ( type == typeof( FactionExplosionTrap ) ) + itemID = 14034; + else if ( type == typeof( FactionGasTrap ) ) + itemID = 4523; + else if ( type == typeof( FactionSawTrap ) ) + itemID = 4359; + else if ( type == typeof( FactionSpikeTrap ) ) + itemID = 4517; + + if ( itemID == 0 ) + { + object[] attrs = type.GetCustomAttributes( typeof( CraftItemIDAttribute ), false ); + + if ( attrs.Length > 0 ) + { + CraftItemIDAttribute craftItemID = (CraftItemIDAttribute) attrs[0]; + itemID = craftItemID.ItemID; + } + } + + if ( itemID == 0 ) + { + Item item = null; + + try{ item = Activator.CreateInstance( type ) as Item; } + catch{} + + if ( item != null ) + { + itemID = item.ItemID; + item.Delete(); + } + } + + m_ItemIDs[type] = itemID; + + return itemID; + } + + public CraftItem( Type type, TextDefinition groupName, TextDefinition name ) + { + m_arCraftRes = new CraftResCol(); + m_arCraftSkill = new CraftSkillCol(); + + m_Type = type; + + m_GroupNameString = groupName; + m_NameString = name; + + m_GroupNameNumber = groupName; + m_NameNumber = name; + } + + public void AddRes( Type type, TextDefinition name, int amount ) + { + AddRes( type, name, amount, "" ); + } + + public void AddRes( Type type, TextDefinition name, int amount, TextDefinition message ) + { + CraftRes craftRes = new CraftRes( type, name, amount, message ); + m_arCraftRes.Add( craftRes ); + } + + + public void AddSkill( SkillName skillToMake, double minSkill, double maxSkill ) + { + CraftSkill craftSkill = new CraftSkill( skillToMake, minSkill, maxSkill ); + m_arCraftSkill.Add( craftSkill ); + } + + public int Mana + { + get { return m_Mana; } + set { m_Mana = value; } + } + + public int Hits + { + get { return m_Hits; } + set { m_Hits = value; } + } + + public int Stam + { + get { return m_Stam; } + set { m_Stam = value; } + } + + public bool UseSubRes2 + { + get { return m_UseSubRes2; } + set { m_UseSubRes2 = value; } + } + + public bool UseAllRes + { + get { return m_UseAllRes; } + set { m_UseAllRes = value; } + } + + public bool NeedHeat + { + get { return m_NeedHeat; } + set { m_NeedHeat = value; } + } + + public bool NeedOven + { + get { return m_NeedOven; } + set { m_NeedOven = value; } + } + + public bool NeedMill + { + get { return m_NeedMill; } + set { m_NeedMill = value; } + } + + public Type ItemType + { + get { return m_Type; } + } + + public string GroupNameString + { + get { return m_GroupNameString; } + } + + public int GroupNameNumber + { + get { return m_GroupNameNumber; } + } + + public string NameString + { + get { return m_NameString; } + } + + public int NameNumber + { + get { return m_NameNumber; } + } + + public CraftResCol Ressources + { + get { return m_arCraftRes; } + } + + public CraftSkillCol Skills + { + get { return m_arCraftSkill; } + } + + public bool ConsumeAttributes( Mobile from, ref object message, bool consume ) + { + bool consumMana = false; + bool consumHits = false; + bool consumStam = false; + + if ( Hits > 0 && from.Hits < Hits ) + { + message = "You lack the required hit points to make that."; + return false; + } + else + { + consumHits = consume; + } + + if ( Mana > 0 && from.Mana < Mana ) + { + message = "You lack the required mana to make that."; + return false; + } + else + { + consumMana = consume; + } + + if ( Stam > 0 && from.Stam < Stam ) + { + message = "You lack the required stamina to make that."; + return false; + } + else + { + consumStam = consume; + } + + if ( consumMana ) + from.Mana -= Mana; + + if ( consumHits ) + from.Hits -= Hits; + + if ( consumStam ) + from.Stam -= Stam; + + return true; + } + + #region Tables + private static int[] m_HeatSources = new int[] + { + 0x461, 0x48E, // Sandstone oven/fireplace + 0x92B, 0x96C, // Stone oven/fireplace + 0xDE3, 0xDE9, // Campfire + 0xFAC, 0xFAC, // Firepit + 0x184A, 0x184C, // Heating stand (left) + 0x184E, 0x1850, // Heating stand (right) + 0x398C, 0x399F, // Fire field + 0x2DDB, 0x2DDC //Elven stove + }; + + private static int[] m_Ovens = new int[] + { + 0x461, 0x46F, // Sandstone oven + 0x92B, 0x93F, // Stone oven + 0x2DDB, 0x2DDC //Elven stove + }; + + private static int[] m_Mills = new int[] + { + 0x1920, 0x1921, 0x1922, 0x1923, 0x1924, 0x1295, 0x1926, 0x1928, + 0x192C, 0x192D, 0x192E, 0x129F, 0x1930, 0x1931, 0x1932, 0x1934 + }; + + private static Type[][] m_TypesTable = new Type[][] + { + new Type[]{ typeof( Log ), typeof( Board ) }, + new Type[]{ typeof( Leather ), typeof( Hides ) }, + new Type[]{ typeof( SpinedLeather ), typeof( SpinedHides ) }, + new Type[]{ typeof( HornedLeather ), typeof( HornedHides ) }, + new Type[]{ typeof( BarbedLeather ), typeof( BarbedHides ) }, + new Type[]{ typeof( BlankMap ), typeof( BlankScroll ) }, + new Type[]{ typeof( Cloth ), typeof( UncutCloth ) }, + new Type[]{ typeof( CheeseWheel ), typeof( CheeseWedge ) }, + new Type[]{ typeof( Pumpkin ), typeof( SmallPumpkin ) }, + new Type[]{ typeof( WoodenBowlOfPeas ), typeof( PewterBowlOfPeas ) } + }; + + private static Type[] m_ColoredItemTable = new Type[] + { + typeof( BaseWeapon ), typeof( BaseArmor ), typeof( BaseClothing ), + typeof( BaseJewel ), typeof( DragonBardingDeed ) + }; + + private static Type[] m_ColoredResourceTable = new Type[] + { + typeof( BaseIngot ), typeof( BaseOre ), + typeof( BaseLeather ), typeof( BaseHides ), + typeof( UncutCloth ), typeof( Cloth ), + typeof( BaseGranite ), typeof( BaseScales ) + }; + + private static Type[] m_MarkableTable = new Type[] + { + typeof( BaseArmor ), + typeof( BaseWeapon ), + typeof( BaseClothing ), + typeof( BaseInstrument ), + typeof( DragonBardingDeed ), + typeof( BaseTool ), + typeof( BaseHarvestTool ), + typeof( FukiyaDarts ), typeof( Shuriken ), + typeof( Spellbook ), typeof( Runebook ) + }; + #endregion + + public bool IsMarkable( Type type ) + { + if( m_ForceNonExceptional ) //Don't even display the stuff for marking if it can't ever be exceptional. + return false; + + for ( int i = 0; i < m_MarkableTable.Length; ++i ) + { + if ( type == m_MarkableTable[i] || type.IsSubclassOf( m_MarkableTable[i] ) ) + return true; + } + + return false; + } + + public bool RetainsColorFrom( CraftSystem system, Type type ) + { + if ( system.RetainsColorFrom( this, type ) ) + return true; + + bool inItemTable = false, inResourceTable = false; + + for ( int i = 0; !inItemTable && i < m_ColoredItemTable.Length; ++i ) + inItemTable = ( m_Type == m_ColoredItemTable[i] || m_Type.IsSubclassOf( m_ColoredItemTable[i] ) ); + + for ( int i = 0; inItemTable && !inResourceTable && i < m_ColoredResourceTable.Length; ++i ) + inResourceTable = ( type == m_ColoredResourceTable[i] || type.IsSubclassOf( m_ColoredResourceTable[i] ) ); + + return ( inItemTable && inResourceTable ); + } + + public bool Find( Mobile from, int[] itemIDs ) + { + Map map = from.Map; + + if ( map == null ) + return false; + + IPooledEnumerable eable = map.GetItemsInRange( from.Location, 2 ); + + foreach ( Item item in eable ) + { + if ( (item.Z + 16) > from.Z && (from.Z + 16) > item.Z && Find( item.ItemID, itemIDs ) ) + { + eable.Free(); + return true; + } + } + + eable.Free(); + + for ( int x = -2; x <= 2; ++x ) + { + for ( int y = -2; y <= 2; ++y ) + { + int vx = from.X + x; + int vy = from.Y + y; + + Tile[] tiles = map.Tiles.GetStaticTiles( vx, vy, true ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + int z = tiles[i].Z; + int id = tiles[i].ID & 0x3FFF; + + if ( (z + 16) > from.Z && (from.Z + 16) > z && Find( id, itemIDs ) ) + return true; + } + } + } + + return false; + } + + public bool Find( int itemID, int[] itemIDs ) + { + bool contains = false; + + for ( int i = 0; !contains && i < itemIDs.Length; i += 2 ) + contains = ( itemID >= itemIDs[i] && itemID <= itemIDs[i + 1] ); + + return contains; + } + + public bool IsQuantityType( Type[][] types ) + { + for ( int i = 0; i < types.Length; ++i ) + { + Type[] check = types[i]; + + for ( int j = 0; j < check.Length; ++j ) + { + if ( typeof( IHasQuantity ).IsAssignableFrom( check[j] ) ) + return true; + } + } + + return false; + } + + public int ConsumeQuantity( Container cont, Type[][] types, int[] amounts ) + { + if ( types.Length != amounts.Length ) + throw new ArgumentException(); + + Item[][] items = new Item[types.Length][]; + int[] totals = new int[types.Length]; + + for ( int i = 0; i < types.Length; ++i ) + { + items[i] = cont.FindItemsByType( types[i], true ); + + for ( int j = 0; j < items[i].Length; ++j ) + { + IHasQuantity hq = items[i][j] as IHasQuantity; + + if ( hq == null ) + { + totals[i] += items[i][j].Amount; + } + else + { + if ( hq is BaseBeverage && ((BaseBeverage)hq).Content != BeverageType.Water ) + continue; + + totals[i] += hq.Quantity; + } + } + + if ( totals[i] < amounts[i] ) + return i; + } + + for ( int i = 0; i < types.Length; ++i ) + { + int need = amounts[i]; + + for ( int j = 0; j < items[i].Length; ++j ) + { + Item item = items[i][j]; + IHasQuantity hq = item as IHasQuantity; + + if ( hq == null ) + { + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + item.Delete(); + need -= theirAmount; + } + else + { + item.Consume( need ); + break; + } + } + else + { + if ( hq is BaseBeverage && ((BaseBeverage)hq).Content != BeverageType.Water ) + continue; + + int theirAmount = hq.Quantity; + + if ( theirAmount < need ) + { + hq.Quantity -= theirAmount; + need -= theirAmount; + } + else + { + hq.Quantity -= need; + break; + } + } + } + } + + return -1; + } + + public int GetQuantity( Container cont, Type[] types ) + { + Item[] items = cont.FindItemsByType( types, true ); + + int amount = 0; + + for ( int i = 0; i < items.Length; ++i ) + { + IHasQuantity hq = items[i] as IHasQuantity; + + if ( hq == null ) + { + amount += items[i].Amount; + } + else + { + if ( hq is BaseBeverage && ((BaseBeverage)hq).Content != BeverageType.Water ) + continue; + + amount += hq.Quantity; + } + } + + return amount; + } + + public bool ConsumeRes( Mobile from, Type typeRes, CraftSystem craftSystem, ref int resHue, ref int maxAmount, ConsumeType consumeType, ref object message ) + { + return ConsumeRes( from, typeRes, craftSystem, ref resHue, ref maxAmount, consumeType, ref message, false ); + } + + public bool ConsumeRes( Mobile from, Type typeRes, CraftSystem craftSystem, ref int resHue, ref int maxAmount, ConsumeType consumeType, ref object message, bool isFailure ) + { + Container ourPack = from.Backpack; + + if ( ourPack == null ) + return false; + + if ( m_NeedHeat && !Find( from, m_HeatSources ) ) + { + message = 1044487; // You must be near a fire source to cook. + return false; + } + + if ( m_NeedOven && !Find( from, m_Ovens ) ) + { + message = 1044493; // You must be near an oven to bake that. + return false; + } + + if ( m_NeedMill && !Find( from, m_Mills ) ) + { + message = 1044491; // You must be near a flour mill to do that. + return false; + } + + Type[][] types = new Type[m_arCraftRes.Count][]; + int[] amounts = new int[m_arCraftRes.Count]; + + maxAmount = int.MaxValue; + + CraftSubResCol resCol = ( m_UseSubRes2 ? craftSystem.CraftSubRes2 : craftSystem.CraftSubRes ); + + for ( int i = 0; i < types.Length; ++i ) + { + CraftRes craftRes = m_arCraftRes.GetAt( i ); + Type baseType = craftRes.ItemType; + + // Resource Mutation + if ( (baseType == resCol.ResType) && ( typeRes != null ) ) + { + baseType = typeRes; + + CraftSubRes subResource = resCol.SearchFor( baseType ); + + if ( subResource != null && from.Skills[craftSystem.MainSkill].Base < subResource.RequiredSkill ) + { + message = subResource.Message; + return false; + } + } + // ****************** + + for ( int j = 0; types[i] == null && j < m_TypesTable.Length; ++j ) + { + if ( m_TypesTable[j][0] == baseType ) + types[i] = m_TypesTable[j]; + } + + if ( types[i] == null ) + types[i] = new Type[]{ baseType }; + + /*if ( !retainedColor && RetainsColorFrom( craftSystem, baseType ) ) + { + retainedColor = true; + Item resItem = ourPack.FindItemByType( types[i] ); + + if ( resItem != null ) + resHue = resItem.Hue; + }*/ + + amounts[i] = craftRes.Amount; + + // For stackable items that can ben crafted more than one at a time + if ( UseAllRes ) + { + int tempAmount = ourPack.GetAmount( types[i] ); + tempAmount /= amounts[i]; + if ( tempAmount < maxAmount ) + { + maxAmount = tempAmount; + + if ( maxAmount == 0 ) + { + CraftRes res = m_arCraftRes.GetAt( i ); + + if ( res.MessageNumber > 0 ) + message = res.MessageNumber; + else if ( res.MessageString != null && res.MessageString != String.Empty ) + message = res.MessageString; + else + message = 502925; // You don't have the resources required to make that item. + + return false; + } + } + } + // **************************** + + if ( isFailure && !craftSystem.ConsumeOnFailure( from, types[i][0], this ) ) + amounts[i] = 0; + } + + // We adjust the amount of each resource to consume the max posible + if ( UseAllRes ) + { + for ( int i = 0; i < amounts.Length; ++i ) + amounts[i] *= maxAmount; + } + else + maxAmount = -1; + + Item consumeExtra = null; + + if ( m_NameNumber == 1041267 ) + { + // Runebooks are a special case, they need a blank recall rune + + Item[] runes = ourPack.FindItemsByType( typeof( RecallRune ) ); + + for ( int i = 0; i < runes.Length; ++i ) + { + RecallRune rune = runes[i] as RecallRune; + + if ( rune != null && !rune.Marked ) + { + consumeExtra = rune; + break; + } + } + + if ( consumeExtra == null ) + { + message = 1044253; // You don't have the components needed to make that. + return false; + } + } + + int index = 0; + + // Consume ALL + if ( consumeType == ConsumeType.All ) + { + m_ResHue = 0; m_ResAmount = 0; m_System = craftSystem; + + if ( IsQuantityType( types ) ) + index = ConsumeQuantity( ourPack, types, amounts ); + else + index = ourPack.ConsumeTotalGrouped( types, amounts, true, new OnItemConsumed( OnResourceConsumed ), new CheckItemGroup( CheckHueGrouping ) ); + + resHue = m_ResHue; + } + + // Consume Half ( for use all resource craft type ) + else if ( consumeType == ConsumeType.Half ) + { + for ( int i = 0; i < amounts.Length; i++ ) + { + amounts[i] /= 2; + + if ( amounts[i] < 1 ) + amounts[i] = 1; + } + + m_ResHue = 0; m_ResAmount = 0; m_System = craftSystem; + + if ( IsQuantityType( types ) ) + index = ConsumeQuantity( ourPack, types, amounts ); + else + index = ourPack.ConsumeTotalGrouped( types, amounts, true, new OnItemConsumed( OnResourceConsumed ), new CheckItemGroup( CheckHueGrouping ) ); + + resHue = m_ResHue; + } + + else // ConstumeType.None ( it's basicaly used to know if the crafter has enough resource before starting the process ) + { + index = -1; + + if ( IsQuantityType( types ) ) + { + for ( int i = 0; i < types.Length; i++ ) + { + if ( GetQuantity( ourPack, types[i] ) < amounts[i] ) + { + index = i; + break; + } + } + } + else + { + for ( int i = 0; i < types.Length; i++ ) + { + if ( ourPack.GetBestGroupAmount( types[i], true, new CheckItemGroup( CheckHueGrouping ) ) < amounts[i] ) + { + index = i; + break; + } + } + } + } + + if ( index == -1 ) + { + if ( consumeType != ConsumeType.None ) + if ( consumeExtra != null ) + consumeExtra.Delete(); + + return true; + } + else + { + CraftRes res = m_arCraftRes.GetAt( index ); + + if ( res.MessageNumber > 0 ) + message = res.MessageNumber; + else if ( res.MessageString != null && res.MessageString != String.Empty ) + message = res.MessageString; + else + message = 502925; // You don't have the resources required to make that item. + + return false; + } + } + + private int m_ResHue; + private int m_ResAmount; + private CraftSystem m_System; + + private void OnResourceConsumed( Item item, int amount ) + { + if ( !RetainsColorFrom( m_System, item.GetType() ) ) + return; + + if ( amount >= m_ResAmount ) + { + m_ResHue = item.Hue; + m_ResAmount = amount; + } + } + + private int CheckHueGrouping( Item a, Item b ) + { + return b.Hue.CompareTo( a.Hue ); + } + + public double GetExceptionalChance( CraftSystem system, double chance, Mobile from ) + { + if( m_ForceNonExceptional ) + return 0.0; + + switch ( system.ECA ) + { + default: + case CraftECA.ChanceMinusSixty: return chance - 0.6; + case CraftECA.FiftyPercentChanceMinusTenPercent: return (chance * 0.5) - 0.1; + case CraftECA.ChanceMinusSixtyToFourtyFive: + { + double offset = 0.60 - ((from.Skills[system.MainSkill].Value - 95.0) * 0.03); + + if ( offset < 0.45 ) + offset = 0.45; + else if ( offset > 0.60 ) + offset = 0.60; + + return chance - offset; + } + } + } + + public bool CheckSkills( Mobile from, Type typeRes, CraftSystem craftSystem, ref int quality, ref bool allRequiredSkills ) + { + return CheckSkills( from, typeRes, craftSystem, ref quality, ref allRequiredSkills, true ); + } + + public bool CheckSkills( Mobile from, Type typeRes, CraftSystem craftSystem, ref int quality, ref bool allRequiredSkills, bool gainSkills ) + { + double chance = GetSuccessChance( from, typeRes, craftSystem, gainSkills, ref allRequiredSkills ); + + if ( GetExceptionalChance( craftSystem, chance, from ) > Utility.RandomDouble() ) + quality = 2; + + return ( chance > Utility.RandomDouble() ); + } + + public double GetSuccessChance( Mobile from, Type typeRes, CraftSystem craftSystem, bool gainSkills, ref bool allRequiredSkills ) + { + double minMainSkill = 0.0; + double maxMainSkill = 0.0; + double valMainSkill = 0.0; + + allRequiredSkills = true; + + for ( int i = 0; i < m_arCraftSkill.Count; i++) + { + CraftSkill craftSkill = m_arCraftSkill.GetAt(i); + + double minSkill = craftSkill.MinSkill; + double maxSkill = craftSkill.MaxSkill; + double valSkill = from.Skills[craftSkill.SkillToMake].Value; + + if ( valSkill < minSkill ) + allRequiredSkills = false; + + if ( craftSkill.SkillToMake == craftSystem.MainSkill ) + { + minMainSkill = minSkill; + maxMainSkill = maxSkill; + valMainSkill = valSkill; + } + + if ( gainSkills ) // This is a passive check. Success chance is entirely dependant on the main skill + from.CheckSkill( craftSkill.SkillToMake, minSkill, maxSkill ); + } + + double chance; + + if ( allRequiredSkills ) + chance = craftSystem.GetChanceAtMin( this ) + ((valMainSkill - minMainSkill) / (maxMainSkill - minMainSkill) * (1.0 - craftSystem.GetChanceAtMin( this ))); + else + chance = 0.0; + + if ( allRequiredSkills && valMainSkill == maxMainSkill ) + chance = 1.0; + + return chance; + } + + public void Craft( Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool ) + { + if ( from.BeginAction( typeof( CraftSystem ) ) ) + { + if( RequiredExpansion == Expansion.None || ( from.NetState != null && from.NetState.SupportsExpansion( RequiredExpansion ) ) ) + { + bool allRequiredSkills = true; + double chance = GetSuccessChance( from, typeRes, craftSystem, false, ref allRequiredSkills ); + + if ( allRequiredSkills && chance >= 0.0 ) + { + if( this.Recipe == null || !(from is PlayerMobile) || ((PlayerMobile)from).HasRecipe( this.Recipe ) ) + { + int badCraft = craftSystem.CanCraft( from, tool, m_Type ); + + if( badCraft <= 0 ) + { + int resHue = 0; + int maxAmount = 0; + object message = null; + + if( ConsumeRes( from, typeRes, craftSystem, ref resHue, ref maxAmount, ConsumeType.None, ref message ) ) + { + message = null; + + if( ConsumeAttributes( from, ref message, false ) ) + { + CraftContext context = craftSystem.GetContext( from ); + + if( context != null ) + context.OnMade( this ); + + int iMin = craftSystem.MinCraftEffect; + int iMax = (craftSystem.MaxCraftEffect - iMin) + 1; + int iRandom = Utility.Random( iMax ); + iRandom += iMin + 1; + new InternalTimer( from, craftSystem, this, typeRes, tool, iRandom ).Start(); + } + else + { + from.EndAction( typeof( CraftSystem ) ); + from.SendGump( new CraftGump( from, craftSystem, tool, message ) ); + } + } + else + { + from.EndAction( typeof( CraftSystem ) ); + from.SendGump( new CraftGump( from, craftSystem, tool, message ) ); + } + } + else + { + from.EndAction( typeof( CraftSystem ) ); + from.SendGump( new CraftGump( from, craftSystem, tool, badCraft ) ); + } + } + else + { + from.EndAction( typeof( CraftSystem ) ); + from.SendGump( new CraftGump( from, craftSystem, tool, 1072847 ) ); // You must learn that recipe from a scroll. + } + } + else + { + from.EndAction( typeof( CraftSystem ) ); + from.SendGump( new CraftGump( from, craftSystem, tool, 1044153 ) ); // You don't have the required skills to attempt this item. + } + } + else + { + from.EndAction( typeof( CraftSystem ) ); + from.SendGump( new CraftGump( from, craftSystem, tool, RequiredExpansionMessage( RequiredExpansion ) ) ); //The {0} expansion is required to attempt this item. + } + } + else + { + from.SendLocalizedMessage( 500119 ); // You must wait to perform another action + } + } + + private object RequiredExpansionMessage( Expansion expansion ) //Eventually convert to TextDefinition, but that requires that we convert all the gumps to ues it too. Not that it wouldn't be a bad idea. + { + switch( expansion ) + { + case Expansion.SE: + return 1063307; // The "Samurai Empire" expansion is required to attempt this item. + case Expansion.ML: + return 1072650; // The "Mondain's Legacy" expansion is required to attempt this item. + default: + return String.Format( "The \"{0}\" expansion is required to attempt this item.", ExpansionInfo.GetInfo( expansion ).Name ); + } + } + + public void CompleteCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CustomCraft customCraft ) + { + int badCraft = craftSystem.CanCraft( from, tool, m_Type ); + + if ( badCraft > 0 ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, badCraft ) ); + else + from.SendLocalizedMessage( badCraft ); + + return; + } + + int checkResHue = 0, checkMaxAmount = 0; + object checkMessage = null; + + // Not enough resource to craft it + if ( !ConsumeRes( from, typeRes, craftSystem, ref checkResHue, ref checkMaxAmount, ConsumeType.None, ref checkMessage ) ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, checkMessage ) ); + else if ( checkMessage is int && (int)checkMessage > 0 ) + from.SendLocalizedMessage( (int)checkMessage ); + else if ( checkMessage is string ) + from.SendMessage( (string)checkMessage ); + + return; + } + else if ( !ConsumeAttributes( from, ref checkMessage, false ) ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, checkMessage ) ); + else if ( checkMessage is int && (int)checkMessage > 0 ) + from.SendLocalizedMessage( (int)checkMessage ); + else if ( checkMessage is string ) + from.SendMessage( (string)checkMessage ); + + return; + } + + bool toolBroken = false; + + int ignored = 1; + int endquality = 1; + + bool allRequiredSkills = true; + + if ( CheckSkills( from, typeRes, craftSystem, ref ignored, ref allRequiredSkills ) ) + { + // Resource + int resHue = 0; + int maxAmount = 0; + + object message = null; + + // Not enough resource to craft it + if ( !ConsumeRes( from, typeRes, craftSystem, ref resHue, ref maxAmount, ConsumeType.All, ref message ) ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, message ) ); + else if ( message is int && (int)message > 0 ) + from.SendLocalizedMessage( (int)message ); + else if ( message is string ) + from.SendMessage( (string)message ); + + return; + } + else if ( !ConsumeAttributes( from, ref message, true ) ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, message ) ); + else if ( message is int && (int)message > 0 ) + from.SendLocalizedMessage( (int)message ); + else if ( message is string ) + from.SendMessage( (string)message ); + + return; + } + + tool.UsesRemaining--; + + if ( craftSystem is DefBlacksmithy ) + { + AncientSmithyHammer hammer = from.FindItemOnLayer( Layer.OneHanded ) as AncientSmithyHammer; + if ( hammer != null && hammer != tool ) + { + hammer.UsesRemaining--; + if ( hammer.UsesRemaining < 1 ) + hammer.Delete(); + } + } + + if ( tool.UsesRemaining < 1 ) + toolBroken = true; + + if ( toolBroken ) + tool.Delete(); + + int num = 0; + + Item item; + if ( customCraft != null ) + { + item = customCraft.CompleteCraft( out num ); + } + else if ( typeof( MapItem ).IsAssignableFrom( ItemType ) && from.Map != Map.Trammel && from.Map != Map.Felucca ) + { + item = new IndecipherableMap(); + from.SendLocalizedMessage( 1070800 ); // The map you create becomes mysteriously indecipherable. + } + else + { + item = Activator.CreateInstance( ItemType ) as Item; + } + + if ( item != null ) + { + if( item is ICraftable ) + endquality = ((ICraftable)item).OnCraft( quality, makersMark, from, craftSystem, typeRes, tool, this, resHue ); + else if ( item.Hue == 0 ) + item.Hue = resHue; + + if ( maxAmount > 0 ) + { + if ( !item.Stackable && item is IUsesRemaining ) + ((IUsesRemaining)item).UsesRemaining *= maxAmount; + else + item.Amount = maxAmount; + } + + from.AddToBackpack( item ); + + //from.PlaySound( 0x57 ); + } + + if ( num == 0 ) + num = craftSystem.PlayEndingEffect( from, false, true, toolBroken, endquality, makersMark, this ); + + bool queryFactionImbue = false; + int availableSilver = 0; + FactionItemDefinition def = null; + Faction faction = null; + + if ( item is IFactionItem ) + { + def = FactionItemDefinition.Identify( item ); + + if ( def != null ) + { + faction = Faction.Find( from ); + + if ( faction != null ) + { + Town town = Town.FromRegion( from.Region ); + + if ( town != null && town.Owner == faction ) + { + Container pack = from.Backpack; + + if ( pack != null ) + { + availableSilver = pack.GetAmount( typeof( Silver ) ); + + if ( availableSilver >= def.SilverCost ) + queryFactionImbue = Faction.IsNearType( from, def.VendorType, 12 ); + } + } + } + } + } + + // TODO: Scroll imbuing + + if ( queryFactionImbue ) + from.SendGump( new FactionImbueGump( quality, item, from, craftSystem, tool, num, availableSilver, faction, def ) ); + else if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, num ) ); + else if ( num > 0 ) + from.SendLocalizedMessage( num ); + } + else if ( !allRequiredSkills ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, 1044153 ) ); + else + from.SendLocalizedMessage( 1044153 ); // You don't have the required skills to attempt this item. + } + else + { + ConsumeType consumeType = ( UseAllRes ? ConsumeType.Half : ConsumeType.All ); + int resHue = 0; + int maxAmount = 0; + + object message = null; + + // Not enough resource to craft it + if ( !ConsumeRes( from, typeRes, craftSystem, ref resHue, ref maxAmount, consumeType, ref message, true ) ) + { + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, message ) ); + else if ( message is int && (int)message > 0 ) + from.SendLocalizedMessage( (int)message ); + else if ( message is string ) + from.SendMessage( (string)message ); + + return; + } + + tool.UsesRemaining--; + + if ( tool.UsesRemaining < 1 ) + toolBroken = true; + + if ( toolBroken ) + tool.Delete(); + + // SkillCheck failed. + int num = craftSystem.PlayEndingEffect( from, true, true, toolBroken, endquality, false, this ); + + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, craftSystem, tool, num ) ); + else if ( num > 0 ) + from.SendLocalizedMessage( num ); + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private int m_iCount; + private int m_iCountMax; + private CraftItem m_CraftItem; + private CraftSystem m_CraftSystem; + private Type m_TypeRes; + private BaseTool m_Tool; + + public InternalTimer( Mobile from, CraftSystem craftSystem, CraftItem craftItem, Type typeRes, BaseTool tool, int iCountMax ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( craftSystem.Delay ), iCountMax ) + { + m_From = from; + m_CraftItem = craftItem; + m_iCount = 0; + m_iCountMax = iCountMax; + m_CraftSystem = craftSystem; + m_TypeRes = typeRes; + m_Tool = tool; + } + + protected override void OnTick() + { + m_iCount++; + + m_From.DisruptiveAction(); + + if ( m_iCount < m_iCountMax ) + { + m_CraftSystem.PlayCraftEffect( m_From ); + } + else + { + m_From.EndAction( typeof( CraftSystem ) ); + + int badCraft = m_CraftSystem.CanCraft( m_From, m_Tool, m_CraftItem.m_Type ); + + if ( badCraft > 0 ) + { + if ( m_Tool != null && !m_Tool.Deleted && m_Tool.UsesRemaining > 0 ) + m_From.SendGump( new CraftGump( m_From, m_CraftSystem, m_Tool, badCraft ) ); + else + m_From.SendLocalizedMessage( badCraft ); + + return; + } + + int quality = 1; + bool allRequiredSkills = true; + + m_CraftItem.CheckSkills( m_From, m_TypeRes, m_CraftSystem, ref quality, ref allRequiredSkills, false ); + + CraftContext context = m_CraftSystem.GetContext( m_From ); + + if ( context == null ) + return; + + if ( typeof( CustomCraft ).IsAssignableFrom( m_CraftItem.ItemType ) ) + { + CustomCraft cc = null; + + try{ cc = Activator.CreateInstance( m_CraftItem.ItemType, new object[] { m_From, m_CraftItem, m_CraftSystem, m_TypeRes, m_Tool, quality } ) as CustomCraft; } + catch{} + + if ( cc != null ) + cc.EndCraftAction(); + + return; + } + + bool makersMark = false; + + if ( quality == 2 && m_From.Skills[m_CraftSystem.MainSkill].Base >= 100.0 ) + makersMark = m_CraftItem.IsMarkable( m_CraftItem.ItemType ); + + if ( makersMark && context.MarkOption == CraftMarkOption.PromptForMark ) + { + m_From.SendGump( new QueryMakersMarkGump( quality, m_From, m_CraftItem, m_CraftSystem, m_TypeRes, m_Tool ) ); + } + else + { + if ( context.MarkOption == CraftMarkOption.DoNotMark ) + makersMark = false; + + m_CraftItem.CompleteCraft( quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null ); + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftItemCol.cs b/Scripts/Engines/Craft/Core/CraftItemCol.cs new file mode 100644 index 000000000..44e8fe835 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftItemCol.cs @@ -0,0 +1,58 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftItemCol : System.Collections.CollectionBase + { + public CraftItemCol() + { + } + + public int Add( CraftItem craftItem ) + { + return List.Add( craftItem ); + } + + public void Remove( int index ) + { + if ( index > Count - 1 || index < 0 ) + { + } + else + { + List.RemoveAt( index ); + } + } + + public CraftItem GetAt( int index ) + { + return ( CraftItem ) List[index]; + } + + public CraftItem SearchForSubclass( Type type ) + { + for ( int i = 0; i < List.Count; i++ ) + { + CraftItem craftItem = ( CraftItem )List[i]; + + if ( craftItem.ItemType == type || type.IsSubclassOf( craftItem.ItemType ) ) + return craftItem; + } + + return null; + } + + public CraftItem SearchFor( Type type ) + { + for ( int i = 0; i < List.Count; i++ ) + { + CraftItem craftItem = ( CraftItem )List[i]; + if ( craftItem.ItemType == type ) + { + return craftItem; + } + } + return null; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftItemIDAttribute.cs b/Scripts/Engines/Craft/Core/CraftItemIDAttribute.cs new file mode 100644 index 000000000..c8e4a3269 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftItemIDAttribute.cs @@ -0,0 +1,18 @@ +using System; +using Server; + +namespace Server.Engines.Craft +{ + [AttributeUsage( AttributeTargets.Class )] + public class CraftItemIDAttribute : Attribute + { + private int m_ItemID; + + public int ItemID{ get{ return m_ItemID; } } + + public CraftItemIDAttribute( int itemID ) + { + m_ItemID = itemID; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftRes.cs b/Scripts/Engines/Craft/Core/CraftRes.cs new file mode 100644 index 000000000..7e2b963fa --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftRes.cs @@ -0,0 +1,71 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftRes + { + private Type m_Type; + private int m_Amount; + + private string m_MessageString; + private int m_MessageNumber; + + private string m_NameString; + private int m_NameNumber; + + public CraftRes( Type type, int amount ) + { + m_Type = type; + m_Amount = amount; + } + + public CraftRes( Type type, TextDefinition name, int amount, TextDefinition message ): this ( type, amount ) + { + m_NameNumber = name; + m_MessageNumber = message; + + m_NameString = name; + m_MessageString = message; + } + + public void SendMessage( Mobile from ) + { + if ( m_MessageNumber > 0 ) + from.SendLocalizedMessage( m_MessageNumber ); + else if ( m_MessageString != null && m_MessageString != String.Empty ) + from.SendMessage( m_MessageString ); + else + from.SendLocalizedMessage( 502925 ); // You don't have the resources required to make that item. + } + + public Type ItemType + { + get { return m_Type; } + } + + public string MessageString + { + get { return m_MessageString; } + } + + public int MessageNumber + { + get { return m_MessageNumber; } + } + + public string NameString + { + get { return m_NameString; } + } + + public int NameNumber + { + get { return m_NameNumber; } + } + + public int Amount + { + get { return m_Amount; } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftResCol.cs b/Scripts/Engines/Craft/Core/CraftResCol.cs new file mode 100644 index 000000000..3491c2cff --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftResCol.cs @@ -0,0 +1,32 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftResCol : System.Collections.CollectionBase + { + public CraftResCol() + { + } + + public void Add( CraftRes craftRes ) + { + List.Add( craftRes ); + } + + public void Remove( int index ) + { + if ( index > Count - 1 || index < 0 ) + { + } + else + { + List.RemoveAt( index ); + } + } + + public CraftRes GetAt( int index ) + { + return ( CraftRes ) List[index]; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftSkill.cs b/Scripts/Engines/Craft/Core/CraftSkill.cs new file mode 100644 index 000000000..5c068ec2e --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftSkill.cs @@ -0,0 +1,33 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftSkill + { + private SkillName m_SkillToMake; + private double m_MinSkill; + private double m_MaxSkill; + + public CraftSkill( SkillName skillToMake, double minSkill, double maxSkill ) + { + m_SkillToMake = skillToMake; + m_MinSkill = minSkill; + m_MaxSkill = maxSkill; + } + + public SkillName SkillToMake + { + get { return m_SkillToMake; } + } + + public double MinSkill + { + get { return m_MinSkill; } + } + + public double MaxSkill + { + get { return m_MaxSkill; } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftSkillCol.cs b/Scripts/Engines/Craft/Core/CraftSkillCol.cs new file mode 100644 index 000000000..87914bd6e --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftSkillCol.cs @@ -0,0 +1,32 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftSkillCol : System.Collections.CollectionBase + { + public CraftSkillCol() + { + } + + public void Add( CraftSkill craftSkill ) + { + List.Add( craftSkill ); + } + + public void Remove( int index ) + { + if ( index > Count - 1 || index < 0 ) + { + } + else + { + List.RemoveAt( index ); + } + } + + public CraftSkill GetAt( int index ) + { + return ( CraftSkill ) List[index]; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftSubRes.cs b/Scripts/Engines/Craft/Core/CraftSubRes.cs new file mode 100644 index 000000000..acef6a6e4 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftSubRes.cs @@ -0,0 +1,58 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftSubRes + { + private Type m_Type; + private double m_ReqSkill; + private string m_NameString; + private int m_NameNumber; + private int m_GenericNameNumber; + private object m_Message; + + public CraftSubRes( Type type, TextDefinition name, double reqSkill, object message ) : this( type, name, reqSkill, 0, message ) + { + } + + public CraftSubRes( Type type, TextDefinition name, double reqSkill, int genericNameNumber, object message ) + { + m_Type = type; + m_NameNumber = name; + m_NameString = name; + m_ReqSkill = reqSkill; + m_GenericNameNumber = genericNameNumber; + m_Message = message; + } + + public Type ItemType + { + get { return m_Type; } + } + + public string NameString + { + get { return m_NameString; } + } + + public int NameNumber + { + get { return m_NameNumber; } + } + + public int GenericNameNumber + { + get { return m_GenericNameNumber; } + } + + public object Message + { + get { return m_Message; } + } + + public double RequiredSkill + { + get { return m_ReqSkill; } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftSubResCol.cs b/Scripts/Engines/Craft/Core/CraftSubResCol.cs new file mode 100644 index 000000000..2ffb18993 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftSubResCol.cs @@ -0,0 +1,75 @@ +using System; + +namespace Server.Engines.Craft +{ + public class CraftSubResCol : System.Collections.CollectionBase + { + private Type m_Type; + private string m_NameString; + private int m_NameNumber; + private bool m_Init; + + public bool Init + { + get { return m_Init; } + set { m_Init = value; } + } + + public Type ResType + { + get { return m_Type; } + set { m_Type = value; } + } + + public string NameString + { + get { return m_NameString; } + set { m_NameString = value; } + } + + public int NameNumber + { + get { return m_NameNumber; } + set { m_NameNumber = value; } + } + + public CraftSubResCol() + { + m_Init = false; + } + + public void Add( CraftSubRes craftSubRes ) + { + List.Add( craftSubRes ); + } + + public void Remove( int index ) + { + if ( index > Count - 1 || index < 0 ) + { + } + else + { + List.RemoveAt( index ); + } + } + + public CraftSubRes GetAt( int index ) + { + return ( CraftSubRes ) List[index]; + } + + public CraftSubRes SearchFor( Type type ) + { + for ( int i = 0; i < List.Count; i++ ) + { + CraftSubRes craftSubRes = ( CraftSubRes )List[i]; + if ( craftSubRes.ItemType == type ) + { + return craftSubRes; + } + } + return null; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CraftSystem.cs b/Scripts/Engines/Craft/Core/CraftSystem.cs new file mode 100644 index 000000000..b5d66966a --- /dev/null +++ b/Scripts/Engines/Craft/Core/CraftSystem.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Engines.Craft +{ + public enum CraftECA + { + ChanceMinusSixty, + FiftyPercentChanceMinusTenPercent, + ChanceMinusSixtyToFourtyFive + } + + public abstract class CraftSystem + { + private int m_MinCraftEffect; + private int m_MaxCraftEffect; + private double m_Delay; + private bool m_Resmelt; + private bool m_Repair; + private bool m_MarkOption; + private bool m_CanEnhance; + + private CraftItemCol m_CraftItems; + private CraftGroupCol m_CraftGroups; + private CraftSubResCol m_CraftSubRes; + private CraftSubResCol m_CraftSubRes2; + + public int MinCraftEffect { get { return m_MinCraftEffect; } } + public int MaxCraftEffect { get { return m_MaxCraftEffect; } } + public double Delay { get { return m_Delay; } } + + public CraftItemCol CraftItems{ get { return m_CraftItems; } } + public CraftGroupCol CraftGroups{ get { return m_CraftGroups; } } + public CraftSubResCol CraftSubRes{ get { return m_CraftSubRes; } } + public CraftSubResCol CraftSubRes2{ get { return m_CraftSubRes2; } } + + public abstract SkillName MainSkill{ get; } + + public virtual int GumpTitleNumber{ get{ return 0; } } + public virtual string GumpTitleString{ get{ return ""; } } + + public virtual CraftECA ECA{ get{ return CraftECA.ChanceMinusSixty; } } + + private Hashtable m_ContextTable = new Hashtable(); + + public abstract double GetChanceAtMin( CraftItem item ); + + public virtual bool RetainsColorFrom( CraftItem item, Type type ) + { + return false; + } + + public CraftContext GetContext( Mobile m ) + { + if ( m == null ) + return null; + + if ( m.Deleted ) + { + m_ContextTable.Remove( m ); + return null; + } + + CraftContext c = (CraftContext)m_ContextTable[m]; + + if ( c == null ) + m_ContextTable[m] = c = new CraftContext(); + + return c; + } + + public void OnMade( Mobile m, CraftItem item ) + { + CraftContext c = GetContext( m ); + + if ( c != null ) + c.OnMade( item ); + } + + public bool Resmelt + { + get { return m_Resmelt; } + set { m_Resmelt = value; } + } + + public bool Repair + { + get{ return m_Repair; } + set{ m_Repair = value; } + } + + public bool MarkOption + { + get{ return m_MarkOption; } + set{ m_MarkOption = value; } + } + + public bool CanEnhance + { + get{ return m_CanEnhance; } + set{ m_CanEnhance = value; } + } + + public CraftSystem( int minCraftEffect, int maxCraftEffect, double delay ) + { + m_MinCraftEffect = minCraftEffect; + m_MaxCraftEffect = maxCraftEffect; + m_Delay = delay; + + m_CraftItems = new CraftItemCol(); + m_CraftGroups = new CraftGroupCol(); + m_CraftSubRes = new CraftSubResCol(); + m_CraftSubRes2 = new CraftSubResCol(); + + InitCraftList(); + } + + public virtual bool ConsumeOnFailure( Mobile from, Type resourceType, CraftItem craftItem ) + { + return true; + } + + public void CreateItem( Mobile from, Type type, Type typeRes, BaseTool tool, CraftItem realCraftItem ) + { + // Verify if the type is in the list of the craftable item + CraftItem craftItem = m_CraftItems.SearchFor( type ); + if ( craftItem != null ) + { + // The item is in the list, try to create it + // Test code: items like sextant parts can be crafted either directly from ingots, or from different parts + realCraftItem.Craft( from, this, typeRes, tool ); + //craftItem.Craft( from, this, typeRes, tool ); + } + } + + + public int AddCraft( Type typeItem, TextDefinition group, TextDefinition name, double minSkill, double maxSkill, Type typeRes, TextDefinition nameRes, int amount ) + { + return AddCraft( typeItem, group, name, MainSkill, minSkill, maxSkill, typeRes, nameRes, amount, "" ); + } + + public int AddCraft( Type typeItem, TextDefinition group, TextDefinition name, double minSkill, double maxSkill, Type typeRes, TextDefinition nameRes, int amount, TextDefinition message ) + { + return AddCraft( typeItem, group, name, MainSkill, minSkill, maxSkill, typeRes, nameRes, amount, message ); + } + + public int AddCraft( Type typeItem, TextDefinition group, TextDefinition name, SkillName skillToMake, double minSkill, double maxSkill, Type typeRes, TextDefinition nameRes, int amount ) + { + return AddCraft( typeItem, group, name, skillToMake, minSkill, maxSkill, typeRes, nameRes, amount, "" ); + } + + public int AddCraft( Type typeItem, TextDefinition group, TextDefinition name, SkillName skillToMake, double minSkill, double maxSkill, Type typeRes, TextDefinition nameRes, int amount, TextDefinition message ) + { + CraftItem craftItem = new CraftItem( typeItem, group, name ); + craftItem.AddRes( typeRes, nameRes, amount, message ); + craftItem.AddSkill( skillToMake, minSkill, maxSkill ); + + DoGroup( group, craftItem ); + return m_CraftItems.Add( craftItem ); + } + + + private void DoGroup( TextDefinition groupName, CraftItem craftItem ) + { + int index = m_CraftGroups.SearchFor( groupName ); + + if ( index == -1) + { + CraftGroup craftGroup = new CraftGroup( groupName ); + craftGroup.AddCraftItem( craftItem ); + m_CraftGroups.Add( craftGroup ); + } + else + { + m_CraftGroups.GetAt( index ).AddCraftItem( craftItem ); + } + } + + + public void SetManaReq( int index, int mana ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.Mana = mana; + } + + public void SetStamReq( int index, int stam ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.Stam = stam; + } + + public void SetHitsReq( int index, int hits ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.Hits = hits; + } + + public void SetUseAllRes( int index, bool useAll ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.UseAllRes = useAll; + } + + public void SetNeedHeat( int index, bool needHeat ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.NeedHeat = needHeat; + } + + public void SetNeedOven( int index, bool needOven ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.NeedOven = needOven; + } + + public void SetNeedMill( int index, bool needMill ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.NeedMill = needMill; + } + + public void SetNeededExpansion( int index, Expansion expansion ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.RequiredExpansion = expansion; + } + + public void AddRes( int index, Type type, TextDefinition name, int amount ) + { + AddRes( index, type, name, amount, "" ); + } + + public void AddRes( int index, Type type, TextDefinition name, int amount, TextDefinition message ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.AddRes( type, name, amount, message ); + } + + public void AddSkill( int index, SkillName skillToMake, double minSkill, double maxSkill ) + { + CraftItem craftItem = m_CraftItems.GetAt(index); + craftItem.AddSkill(skillToMake, minSkill, maxSkill); + } + + public void SetUseSubRes2( int index, bool val ) + { + CraftItem craftItem = m_CraftItems.GetAt(index); + craftItem.UseSubRes2 = val; + } + + public void AddRecipe( int index, int id ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.AddRecipe( id, this ); + } + + public void ForceNonExceptional( int index ) + { + CraftItem craftItem = m_CraftItems.GetAt( index ); + craftItem.ForceNonExceptional = true; + } + + + public void SetSubRes( Type type, string name ) + { + m_CraftSubRes.ResType = type; + m_CraftSubRes.NameString = name; + m_CraftSubRes.Init = true; + } + + public void SetSubRes( Type type, int name ) + { + m_CraftSubRes.ResType = type; + m_CraftSubRes.NameNumber = name; + m_CraftSubRes.Init = true; + } + + public void AddSubRes( Type type, int name, double reqSkill, object message ) + { + CraftSubRes craftSubRes = new CraftSubRes( type, name, reqSkill, message ); + m_CraftSubRes.Add( craftSubRes ); + } + + public void AddSubRes( Type type, int name, double reqSkill, int genericName, object message ) + { + CraftSubRes craftSubRes = new CraftSubRes( type, name, reqSkill, genericName, message ); + m_CraftSubRes.Add( craftSubRes ); + } + + public void AddSubRes( Type type, string name, double reqSkill, object message ) + { + CraftSubRes craftSubRes = new CraftSubRes( type, name, reqSkill, message ); + m_CraftSubRes.Add( craftSubRes ); + } + + + public void SetSubRes2( Type type, string name ) + { + m_CraftSubRes2.ResType = type; + m_CraftSubRes2.NameString = name; + m_CraftSubRes2.Init = true; + } + + public void SetSubRes2( Type type, int name ) + { + m_CraftSubRes2.ResType = type; + m_CraftSubRes2.NameNumber = name; + m_CraftSubRes2.Init = true; + } + + public void AddSubRes2( Type type, int name, double reqSkill, object message ) + { + CraftSubRes craftSubRes = new CraftSubRes( type, name, reqSkill, message ); + m_CraftSubRes2.Add( craftSubRes ); + } + + public void AddSubRes2( Type type, int name, double reqSkill, int genericName, object message ) + { + CraftSubRes craftSubRes = new CraftSubRes( type, name, reqSkill, genericName, message ); + m_CraftSubRes2.Add( craftSubRes ); + } + + public void AddSubRes2( Type type, string name, double reqSkill, object message ) + { + CraftSubRes craftSubRes = new CraftSubRes( type, name, reqSkill, message ); + m_CraftSubRes2.Add( craftSubRes ); + } + + public abstract void InitCraftList(); + + public abstract void PlayCraftEffect( Mobile from ); + public abstract int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ); + + public abstract int CanCraft( Mobile from, BaseTool tool, Type itemType ); + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/CustomCraft.cs b/Scripts/Engines/Craft/Core/CustomCraft.cs new file mode 100644 index 000000000..368539458 --- /dev/null +++ b/Scripts/Engines/Craft/Core/CustomCraft.cs @@ -0,0 +1,36 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.Craft +{ + public abstract class CustomCraft + { + private Mobile m_From; + private CraftItem m_CraftItem; + private CraftSystem m_CraftSystem; + private Type m_TypeRes; + private BaseTool m_Tool; + private int m_Quality; + + public Mobile From{ get{ return m_From; } } + public CraftItem CraftItem{ get{ return m_CraftItem; } } + public CraftSystem CraftSystem{ get{ return m_CraftSystem; } } + public Type TypeRes{ get{ return m_TypeRes; } } + public BaseTool Tool{ get{ return m_Tool; } } + public int Quality{ get{ return m_Quality; } } + + public CustomCraft( Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality ) + { + m_From = from; + m_CraftItem = craftItem; + m_CraftSystem = craftSystem; + m_TypeRes = typeRes; + m_Tool = tool; + m_Quality = quality; + } + + public abstract void EndCraftAction(); + public abstract Item CompleteCraft( out int message ); + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/Enhance.cs b/Scripts/Engines/Craft/Core/Enhance.cs new file mode 100644 index 000000000..f3a895488 --- /dev/null +++ b/Scripts/Engines/Craft/Core/Enhance.cs @@ -0,0 +1,313 @@ +using System; +using Server; +using Server.Targeting; +using Server.Items; + +namespace Server.Engines.Craft +{ + public enum EnhanceResult + { + NotInBackpack, + BadItem, + BadResource, + AlreadyEnhanced, + Success, + Failure, + Broken, + NoResources, + NoSkill + } + + public class Enhance + { + public static EnhanceResult Invoke( Mobile from, CraftSystem craftSystem, BaseTool tool, Item item, CraftResource resource, Type resType, ref object resMessage ) + { + if ( item == null ) + return EnhanceResult.BadItem; + + if ( !item.IsChildOf( from.Backpack ) ) + return EnhanceResult.NotInBackpack; + + if ( !(item is BaseArmor) && !(item is BaseWeapon) ) + return EnhanceResult.BadItem; + + if ( CraftResources.IsStandard( resource ) ) + return EnhanceResult.BadResource; + + CraftItem craftItem = craftSystem.CraftItems.SearchFor( item.GetType() ); + + if ( craftItem == null || craftItem.Ressources.Count == 0 ) + return EnhanceResult.BadItem; + + int quality = 0; + bool allRequiredSkills = false; + + if ( !craftItem.CheckSkills( from, resType, craftSystem, ref quality, ref allRequiredSkills, false ) ) + return EnhanceResult.NoSkill; + + CraftResourceInfo info = CraftResources.GetInfo( resource ); + + if ( info == null || info.ResourceTypes.Length == 0 ) + return EnhanceResult.BadResource; + + CraftAttributeInfo attributes = info.AttributeInfo; + + if ( attributes == null ) + return EnhanceResult.BadResource; + + int resHue = 0, maxAmount = 0; + + if ( !craftItem.ConsumeRes( from, resType, craftSystem, ref resHue, ref maxAmount, ConsumeType.None, ref resMessage ) ) + return EnhanceResult.NoResources; + + if ( craftSystem is DefBlacksmithy ) + { + AncientSmithyHammer hammer = from.FindItemOnLayer( Layer.OneHanded ) as AncientSmithyHammer; + if ( hammer != null ) + { + hammer.UsesRemaining--; + if ( hammer.UsesRemaining < 1 ) + hammer.Delete(); + } + } + + int phys = 0, fire = 0, cold = 0, pois = 0, nrgy = 0; + int dura = 0, luck = 0, lreq = 0, dinc = 0; + int baseChance = 0; + + bool physBonus = false; + bool fireBonus = false; + bool coldBonus = false; + bool nrgyBonus = false; + bool poisBonus = false; + bool duraBonus = false; + bool luckBonus = false; + bool lreqBonus = false; + bool dincBonus = false; + + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( !CraftResources.IsStandard( weapon.Resource ) ) + return EnhanceResult.AlreadyEnhanced; + + baseChance = 20; + + dura = weapon.MaxHitPoints; + luck = weapon.Attributes.Luck; + lreq = weapon.WeaponAttributes.LowerStatReq; + dinc = weapon.Attributes.WeaponDamage; + + fireBonus = ( attributes.WeaponFireDamage > 0 ); + coldBonus = ( attributes.WeaponColdDamage > 0 ); + nrgyBonus = ( attributes.WeaponEnergyDamage > 0 ); + poisBonus = ( attributes.WeaponPoisonDamage > 0 ); + + duraBonus = ( attributes.WeaponDurability > 0 ); + luckBonus = ( attributes.WeaponLuck > 0 ); + lreqBonus = ( attributes.WeaponLowerRequirements > 0 ); + dincBonus = ( dinc > 0 ); + } + else + { + BaseArmor armor = (BaseArmor)item; + + if ( !CraftResources.IsStandard( armor.Resource ) ) + return EnhanceResult.AlreadyEnhanced; + + baseChance = 20; + + phys = armor.PhysicalResistance; + fire = armor.FireResistance; + cold = armor.ColdResistance; + pois = armor.PoisonResistance; + nrgy = armor.EnergyResistance; + + dura = armor.MaxHitPoints; + luck = armor.Attributes.Luck; + lreq = armor.ArmorAttributes.LowerStatReq; + + physBonus = ( attributes.ArmorPhysicalResist > 0 ); + fireBonus = ( attributes.ArmorFireResist > 0 ); + coldBonus = ( attributes.ArmorColdResist > 0 ); + nrgyBonus = ( attributes.ArmorEnergyResist > 0 ); + poisBonus = ( attributes.ArmorPoisonResist > 0 ); + + duraBonus = ( attributes.ArmorDurability > 0 ); + luckBonus = ( attributes.ArmorLuck > 0 ); + lreqBonus = ( attributes.ArmorLowerRequirements > 0 ); + dincBonus = false; + } + + int skill = from.Skills[craftSystem.MainSkill].Fixed / 10; + + if ( skill >= 100 ) + baseChance -= (skill - 90) / 10; + + EnhanceResult res = EnhanceResult.Success; + + if ( physBonus ) + CheckResult( ref res, baseChance + phys ); + + if ( fireBonus ) + CheckResult( ref res, baseChance + fire ); + + if ( coldBonus ) + CheckResult( ref res, baseChance + cold ); + + if ( nrgyBonus ) + CheckResult( ref res, baseChance + nrgy ); + + if ( poisBonus ) + CheckResult( ref res, baseChance + pois ); + + if ( duraBonus ) + CheckResult( ref res, baseChance + (dura / 40) ); + + if ( luckBonus ) + CheckResult( ref res, baseChance + 10 + (luck / 2) ); + + if ( lreqBonus ) + CheckResult( ref res, baseChance + (lreq / 4) ); + + if ( dincBonus ) + CheckResult( ref res, baseChance + (dinc / 4) ); + + switch ( res ) + { + case EnhanceResult.Broken: + { + if ( !craftItem.ConsumeRes( from, resType, craftSystem, ref resHue, ref maxAmount, ConsumeType.Half, ref resMessage ) ) + return EnhanceResult.NoResources; + + item.Delete(); + break; + } + case EnhanceResult.Success: + { + if ( !craftItem.ConsumeRes( from, resType, craftSystem, ref resHue, ref maxAmount, ConsumeType.All, ref resMessage ) ) + return EnhanceResult.NoResources; + + if( item is BaseWeapon ) + { + BaseWeapon w = (BaseWeapon)item; + + w.Resource = resource; + + int hue = w.GetElementalDamageHue(); + if( hue > 0 ) + w.Hue = hue; + } + else if( item is BaseArmor ) //Sanity + { + ((BaseArmor)item).Resource = resource; + } + + break; + } + case EnhanceResult.Failure: + { + if ( !craftItem.ConsumeRes( from, resType, craftSystem, ref resHue, ref maxAmount, ConsumeType.Half, ref resMessage ) ) + return EnhanceResult.NoResources; + + break; + } + } + + return res; + } + + public static void CheckResult( ref EnhanceResult res, int chance ) + { + if ( res != EnhanceResult.Success ) + return; // we've already failed.. + + int random = Utility.Random( 100 ); + + if ( 10 > random ) + res = EnhanceResult.Failure; + else if ( chance > random ) + res = EnhanceResult.Broken; + } + + public static void BeginTarget( Mobile from, CraftSystem craftSystem, BaseTool tool ) + { + CraftContext context = craftSystem.GetContext( from ); + + if ( context == null ) + return; + + int lastRes = context.LastResourceIndex; + CraftSubResCol subRes = craftSystem.CraftSubRes; + + if ( lastRes >= 0 && lastRes < subRes.Count ) + { + CraftSubRes res = subRes.GetAt( lastRes ); + + if ( from.Skills[craftSystem.MainSkill].Value < res.RequiredSkill ) + { + from.SendGump( new CraftGump( from, craftSystem, tool, res.Message ) ); + } + else + { + CraftResource resource = CraftResources.GetFromType( res.ItemType ); + + if ( resource != CraftResource.None ) + { + from.Target = new InternalTarget( craftSystem, tool, res.ItemType, resource ); + from.SendLocalizedMessage( 1061004 ); // Target an item to enhance with the properties of your selected material. + } + else + { + from.SendGump( new CraftGump( from, craftSystem, tool, 1061010 ) ); // You must select a special material in order to enhance an item with its properties. + } + } + } + else + { + from.SendGump( new CraftGump( from, craftSystem, tool, 1061010 ) ); // You must select a special material in order to enhance an item with its properties. + } + + } + + private class InternalTarget : Target + { + private CraftSystem m_CraftSystem; + private BaseTool m_Tool; + private Type m_ResourceType; + private CraftResource m_Resource; + + public InternalTarget( CraftSystem craftSystem, BaseTool tool, Type resourceType, CraftResource resource ) : base ( 2, false, TargetFlags.None ) + { + m_CraftSystem = craftSystem; + m_Tool = tool; + m_ResourceType = resourceType; + m_Resource = resource; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Item ) + { + object message = null; + EnhanceResult res = Enhance.Invoke( from, m_CraftSystem, m_Tool, (Item)targeted, m_Resource, m_ResourceType, ref message ); + + switch ( res ) + { + case EnhanceResult.NotInBackpack: message = 1061005; break; // The item must be in your backpack to enhance it. + case EnhanceResult.AlreadyEnhanced: message = 1061012; break; // This item is already enhanced with the properties of a special material. + case EnhanceResult.BadItem: message = 1061011; break; // You cannot enhance this type of item with the properties of the selected special material. + case EnhanceResult.BadResource: message = 1061010; break; // You must select a special material in order to enhance an item with its properties. + case EnhanceResult.Broken: message = 1061080; break; // You attempt to enhance the item, but fail catastrophically. The item is lost. + case EnhanceResult.Failure: message = 1061082; break; // You attempt to enhance the item, but fail. Some material is lost in the process. + case EnhanceResult.Success: message = 1061008; break; // You enhance the item with the properties of the special material. + case EnhanceResult.NoSkill: message = 1044153; break; // You don't have the required skills to attempt this item. + } + + from.SendGump( new CraftGump( from, m_CraftSystem, m_Tool, message ) ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/QueryMakersMarkGump.cs b/Scripts/Engines/Craft/Core/QueryMakersMarkGump.cs new file mode 100644 index 000000000..0f3121f80 --- /dev/null +++ b/Scripts/Engines/Craft/Core/QueryMakersMarkGump.cs @@ -0,0 +1,54 @@ +using System; +using Server; +using Server.Gumps; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class QueryMakersMarkGump : Gump + { + private int m_Quality; + private Mobile m_From; + private CraftItem m_CraftItem; + private CraftSystem m_CraftSystem; + private Type m_TypeRes; + private BaseTool m_Tool; + + public QueryMakersMarkGump( int quality, Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool ) : base( 100, 200 ) + { + from.CloseGump( typeof( QueryMakersMarkGump ) ); + + m_Quality = quality; + m_From = from; + m_CraftItem = craftItem; + m_CraftSystem = craftSystem; + m_TypeRes = typeRes; + m_Tool = tool; + + AddPage( 0 ); + + AddBackground( 0, 0, 220, 170, 5054 ); + AddBackground( 10, 10, 200, 150, 3000 ); + + AddHtmlLocalized( 20, 20, 180, 80, 1018317, false, false ); // Do you wish to place your maker's mark on this item? + + AddHtmlLocalized( 55, 100, 140, 25, 1011011, false, false ); // CONTINUE + AddButton( 20, 100, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 125, 140, 25, 1011012, false, false ); // CANCEL + AddButton( 20, 125, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + bool makersMark = ( info.ButtonID == 1 ); + + if ( makersMark ) + m_From.SendLocalizedMessage( 501808 ); // You mark the item. + else + m_From.SendLocalizedMessage( 501809 ); // Cancelled mark. + + m_CraftItem.CompleteCraft( m_Quality, makersMark, m_From, m_CraftSystem, m_TypeRes, m_Tool, null ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/Recipes.cs b/Scripts/Engines/Craft/Core/Recipes.cs new file mode 100644 index 000000000..dbdf26dbe --- /dev/null +++ b/Scripts/Engines/Craft/Core/Recipes.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using Server; +using Server.Mobiles; +using Server.Commands; + +namespace Server.Engines.Craft +{ + public class Recipe + { + public static void Initialize() + { + CommandSystem.Register( "LearnAllRecipes", AccessLevel.GameMaster, new CommandEventHandler( LearnAllRecipes_OnCommand ) ); + CommandSystem.Register( "ForgetAllRecipes", AccessLevel.GameMaster, new CommandEventHandler( ForgetAllRecipes_OnCommand ) ); + } + + [Usage( "LearnAllRecipes" )] + [Description( "Teaches a player all available recipes." )] + private static void LearnAllRecipes_OnCommand( CommandEventArgs e ) + { + Mobile m = e.Mobile; + m.SendMessage( "Target a player to teach them all of the recipies." ); + + m.BeginTarget( -1, false, Server.Targeting.TargetFlags.None, new TargetCallback( + delegate( Mobile from, object targeted ) + { + if( targeted is PlayerMobile ) + { + foreach( KeyValuePair kvp in m_Recipes ) + ((PlayerMobile)targeted).AcquireRecipe( kvp.Key ); + + m.SendMessage( "You teach them all of the recipies." ); + } + else + { + m.SendMessage( "That is not a player!" ); + } + } + ) ); + } + + [Usage( "ForgetAllRecipes" )] + [Description( "Makes a player forget all the recipies they've learned." )] + private static void ForgetAllRecipes_OnCommand( CommandEventArgs e ) + { + Mobile m = e.Mobile; + m.SendMessage( "Target a player to have them forget all of the recipies they've learned." ); + + m.BeginTarget( -1, false, Server.Targeting.TargetFlags.None, new TargetCallback( + delegate( Mobile from, object targeted ) + { + if( targeted is PlayerMobile ) + { + foreach( KeyValuePair kvp in m_Recipes ) + ((PlayerMobile)targeted).AcquireRecipe( kvp.Key ); + + m.SendMessage( "They forget all their recipies." ); + } + else + { + m.SendMessage( "That is not a player!" ); + } + } + ) ); + } + + + private static Dictionary m_Recipes = new Dictionary(); + + public static Dictionary Recipes { get { return m_Recipes; } } + + private static int m_LargestRecipeID; + public static int LargestRecipeID{ get{ return m_LargestRecipeID; } } + + private CraftSystem m_System; + + public CraftSystem CraftSystem + { + get { return m_System; } + set { m_System = value; } + } + + private CraftItem m_CraftItem; + + public CraftItem CraftItem + { + get { return m_CraftItem; } + set { m_CraftItem = value; } + } + + private int m_ID; + + public int ID + { + get { return m_ID; } + } + + private TextDefinition m_TD; + public TextDefinition TextDefinition + { + get + { + if( m_TD == null ) + m_TD = new TextDefinition( m_CraftItem.NameNumber, m_CraftItem.NameString ); + + return m_TD; + } + } + + public Recipe( int id, CraftSystem system, CraftItem item ) + { + m_ID = id; + m_System = system; + m_CraftItem = item; + + if( m_Recipes.ContainsKey( id ) ) + throw new Exception( "Attempting to create recipe with preexisting ID." ); + + m_Recipes.Add( id, this ); + m_LargestRecipeID = Math.Max( id, m_LargestRecipeID ); + } + } +} diff --git a/Scripts/Engines/Craft/Core/Repair.cs b/Scripts/Engines/Craft/Core/Repair.cs new file mode 100644 index 000000000..519ab519e --- /dev/null +++ b/Scripts/Engines/Craft/Core/Repair.cs @@ -0,0 +1,423 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class Repair + { + public Repair() + { + } + + public static void Do( Mobile from, CraftSystem craftSystem, BaseTool tool ) + { + from.Target = new InternalTarget( craftSystem, tool ); + from.SendLocalizedMessage( 1044276 ); // Target an item to repair. + } + + public static void Do( Mobile from, CraftSystem craftSystem, RepairDeed deed ) + { + from.Target = new InternalTarget( craftSystem, deed ); + from.SendLocalizedMessage( 1044276 ); // Target an item to repair. + } + + private class InternalTarget : Target + { + private CraftSystem m_CraftSystem; + private BaseTool m_Tool; + private RepairDeed m_Deed; + + public InternalTarget( CraftSystem craftSystem, BaseTool tool ) : base ( 2, false, TargetFlags.None ) + { + m_CraftSystem = craftSystem; + m_Tool = tool; + } + + public InternalTarget( CraftSystem craftSystem, RepairDeed deed ) : base( 2, false, TargetFlags.None ) + { + m_CraftSystem = craftSystem; + m_Deed = deed; + } + + private static void EndGolemRepair( object state ) + { + ((Mobile)state).EndAction( typeof( Golem ) ); + } + + private int GetWeakenChance( Mobile mob, SkillName skill, int curHits, int maxHits ) + { + // 40% - (1% per hp lost) - (1% per 10 craft skill) + return (40 + (maxHits - curHits)) - (int)(((m_Deed != null)? m_Deed.SkillLevel : mob.Skills[skill].Value) / 10); + } + + private bool CheckWeaken( Mobile mob, SkillName skill, int curHits, int maxHits ) + { + return ( GetWeakenChance( mob, skill, curHits, maxHits ) > Utility.Random( 100 ) ); + } + + private int GetRepairDifficulty( int curHits, int maxHits ) + { + return (((maxHits - curHits) * 1250) / Math.Max( maxHits, 1 )) - 250; + } + + private bool CheckRepairDifficulty( Mobile mob, SkillName skill, int curHits, int maxHits ) + { + double difficulty = GetRepairDifficulty( curHits, maxHits ) * 0.1; + + + if( m_Deed != null ) + { + double value = m_Deed.SkillLevel; + double minSkill = difficulty - 25.0; + double maxSkill = difficulty + 25; + + if( value < minSkill ) + return false; // Too difficult + else if( value >= maxSkill ) + return true; // No challenge + + double chance = (value - minSkill) / (maxSkill - minSkill); + + return (chance >= Utility.RandomDouble()); + } + else + { + return mob.CheckSkill( skill, difficulty - 25.0, difficulty + 25.0 ); + } + } + + private bool CheckDeed( Mobile from ) + { + if( m_Deed != null ) + { + return m_Deed.Check( from ); + } + + return true; + } + + private bool IsSpecialWeapon( BaseWeapon weapon ) + { + // Weapons repairable but not craftable + + if ( m_CraftSystem is DefTinkering ) + { + return ( weapon is Cleaver ) + || ( weapon is Hatchet ) + || ( weapon is Pickaxe ) + || ( weapon is ButcherKnife ) + || ( weapon is SkinningKnife ); + } + else if ( m_CraftSystem is DefCarpentry ) + { + return ( weapon is Club ) + || ( weapon is BlackStaff ) + || ( weapon is MagicWand ); + } + else if ( m_CraftSystem is DefBlacksmithy ) + { + return ( weapon is Pitchfork ); + } + + return false; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + int number; + + if( !CheckDeed( from ) ) + return; + + + bool usingDeed = (m_Deed != null); + bool toDelete = false; + + //TODO: Make a IRepairable + + if ( m_CraftSystem is DefTinkering && targeted is Golem ) + { + Golem g = (Golem)targeted; + int damage = g.HitsMax - g.Hits; + + if ( g.IsDeadBondedPet ) + { + number = 500426; // You can't repair that. + } + else if ( damage <= 0 ) + { + number = 500423; // That is already in full repair. + } + else + { + double skillValue = (usingDeed)? m_Deed.SkillLevel : from.Skills[SkillName.Tinkering].Value; + + if ( skillValue < 60.0 ) + { + number = 1044153; // You don't have the required skills to attempt this item. //TODO: How does OSI handle this with deeds with golems? + } + else if ( !from.CanBeginAction( typeof( Golem ) ) ) + { + number = 501789; // You must wait before trying again. + } + else + { + if ( damage > (int)(skillValue * 0.3) ) + damage = (int)(skillValue * 0.3); + + damage += 30; + + if ( !from.CheckSkill( SkillName.Tinkering, 0.0, 100.0 ) ) + damage /= 2; + + Container pack = from.Backpack; + + if ( pack != null ) + { + int v = pack.ConsumeUpTo( typeof( IronIngot ), (damage+4)/5 ); + + if ( v > 0 ) + { + g.Hits += v*5; + + number = 1044279; // You repair the item. + toDelete = true; + + from.BeginAction( typeof( Golem ) ); + Timer.DelayCall( TimeSpan.FromSeconds( 12.0 ), new TimerStateCallback( EndGolemRepair ), from ); + } + else + { + number = 1044037; // You do not have sufficient metal to make that. + } + } + else + { + number = 1044037; // You do not have sufficient metal to make that. + } + } + } + } + else if ( targeted is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)targeted; + SkillName skill = m_CraftSystem.MainSkill; + int toWeaken = 0; + + if ( Core.AOS ) + { + toWeaken = 1; + } + else if ( skill != SkillName.Tailoring ) + { + double skillLevel = (usingDeed)? m_Deed.SkillLevel : from.Skills[skill].Base; + + if ( skillLevel >= 90.0 ) + toWeaken = 1; + else if ( skillLevel >= 70.0 ) + toWeaken = 2; + else + toWeaken = 3; + } + + if ( m_CraftSystem.CraftItems.SearchForSubclass( weapon.GetType() ) == null && !IsSpecialWeapon( weapon ) ) + { + number = (usingDeed)? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. + } + else if ( !weapon.IsChildOf( from.Backpack ) ) + { + number = 1044275; // The item must be in your backpack to repair it. + } + else if ( weapon.MaxHitPoints <= 0 || weapon.HitPoints == weapon.MaxHitPoints ) + { + number = 1044281; // That item is in full repair + } + else if ( weapon.MaxHitPoints <= toWeaken ) + { + number = 1044278; // That item has been repaired many times, and will break if repairs are attempted again. + } + else + { + if ( CheckWeaken( from, skill, weapon.HitPoints, weapon.MaxHitPoints ) ) + { + weapon.MaxHitPoints -= toWeaken; + weapon.HitPoints = Math.Max( 0, weapon.HitPoints - toWeaken ); + } + + if ( CheckRepairDifficulty( from, skill, weapon.HitPoints, weapon.MaxHitPoints ) ) + { + number = 1044279; // You repair the item. + m_CraftSystem.PlayCraftEffect( from ); + weapon.HitPoints = weapon.MaxHitPoints; + } + else + { + number = (usingDeed)? 1061137 : 1044280; // You fail to repair the item. [And the contract is destroyed] + m_CraftSystem.PlayCraftEffect( from ); + } + + toDelete = true; + } + } + else if ( targeted is BaseArmor ) + { + BaseArmor armor = (BaseArmor)targeted; + SkillName skill = m_CraftSystem.MainSkill; + int toWeaken = 0; + + if ( Core.AOS ) + { + toWeaken = 1; + } + else if ( skill != SkillName.Tailoring ) + { + double skillLevel = (usingDeed)? m_Deed.SkillLevel : from.Skills[skill].Base; + + if ( skillLevel >= 90.0 ) + toWeaken = 1; + else if ( skillLevel >= 70.0 ) + toWeaken = 2; + else + toWeaken = 3; + } + + if ( m_CraftSystem.CraftItems.SearchForSubclass( armor.GetType() ) == null ) + { + number = (usingDeed)? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. + } + else if ( !armor.IsChildOf( from.Backpack ) ) + { + number = 1044275; // The item must be in your backpack to repair it. + } + else if ( armor.MaxHitPoints <= 0 || armor.HitPoints == armor.MaxHitPoints ) + { + number = 1044281; // That item is in full repair + } + else if ( armor.MaxHitPoints <= toWeaken ) + { + number = 1044278; // That item has been repaired many times, and will break if repairs are attempted again. + } + else + { + if ( CheckWeaken( from, skill, armor.HitPoints, armor.MaxHitPoints ) ) + { + armor.MaxHitPoints -= toWeaken; + armor.HitPoints = Math.Max( 0, armor.HitPoints - toWeaken ); + } + + if ( CheckRepairDifficulty( from, skill, armor.HitPoints, armor.MaxHitPoints ) ) + { + number = 1044279; // You repair the item. + m_CraftSystem.PlayCraftEffect( from ); + armor.HitPoints = armor.MaxHitPoints; + } + else + { + number = (usingDeed)? 1061137 : 1044280; // You fail to repair the item. [And the contract is destroyed] + m_CraftSystem.PlayCraftEffect( from ); + } + + toDelete = true; + } + } + else if ( targeted is BaseClothing ) + { + BaseClothing clothing = (BaseClothing)targeted; + SkillName skill = m_CraftSystem.MainSkill; + int toWeaken = 0; + + if ( Core.AOS ) + { + toWeaken = 1; + } + else if ( skill != SkillName.Tailoring ) + { + double skillLevel = (usingDeed) ? m_Deed.SkillLevel : from.Skills[skill].Base; + + if ( skillLevel >= 90.0 ) + toWeaken = 1; + else if ( skillLevel >= 70.0 ) + toWeaken = 2; + else + toWeaken = 3; + } + + if ( m_CraftSystem.CraftItems.SearchForSubclass( clothing.GetType() ) == null ) + { + number = (usingDeed) ? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. + } + else if ( !clothing.IsChildOf( from.Backpack ) ) + { + number = 1044275; // The item must be in your backpack to repair it. + } + else if ( clothing.MaxHitPoints <= 0 || clothing.HitPoints == clothing.MaxHitPoints ) + { + number = 1044281; // That item is in full repair + } + else if ( clothing.MaxHitPoints <= toWeaken ) + { + number = 1044278; // That item has been repaired many times, and will break if repairs are attempted again. + } + else + { + if ( CheckWeaken( from, skill, clothing.HitPoints, clothing.MaxHitPoints ) ) + { + clothing.MaxHitPoints -= toWeaken; + clothing.HitPoints = Math.Max( 0, clothing.HitPoints - toWeaken ); + } + + if ( CheckRepairDifficulty( from, skill, clothing.HitPoints, clothing.MaxHitPoints ) ) + { + number = 1044279; // You repair the item. + m_CraftSystem.PlayCraftEffect( from ); + clothing.HitPoints = clothing.MaxHitPoints; + } + else + { + number = (usingDeed) ? 1061137 : 1044280; // You fail to repair the item. [And the contract is destroyed] + m_CraftSystem.PlayCraftEffect( from ); + } + + toDelete = true; + } + } + else if( !usingDeed && targeted is BlankScroll ) + { + SkillName skill = m_CraftSystem.MainSkill; + + if( from.Skills[skill].Value >= 50.0 ) + { + ((BlankScroll)targeted).Consume( 1 ); + RepairDeed deed = new RepairDeed( RepairDeed.GetTypeFor( m_CraftSystem ), from.Skills[skill].Value, from ); + from.AddToBackpack( deed ); + + number = 500442; // You create the item and put it in your backpack. + } + else + number = 1047005; // You must be at least apprentice level to create a repair service contract. + } + else if ( targeted is Item ) + { + number = (usingDeed)? 1061136 : 1044277; // That item cannot be repaired. // You cannot repair that item with this type of repair contract. + } + else + { + number = 500426; // You can't repair that. + } + + if( !usingDeed ) + { + CraftContext context = m_CraftSystem.GetContext( from ); + from.SendGump( new CraftGump( from, m_CraftSystem, m_Tool, number ) ); + } + else if( toDelete ) + { + m_Deed.Delete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/Core/Resmelt.cs b/Scripts/Engines/Craft/Core/Resmelt.cs new file mode 100644 index 000000000..82ada6851 --- /dev/null +++ b/Scripts/Engines/Craft/Core/Resmelt.cs @@ -0,0 +1,121 @@ +using System; +using Server; +using Server.Targeting; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class Resmelt + { + public Resmelt() + { + } + + public static void Do( Mobile from, CraftSystem craftSystem, BaseTool tool ) + { + int num = craftSystem.CanCraft( from, tool, null ); + + if ( num > 0 ) + { + from.SendGump( new CraftGump( from, craftSystem, tool, num ) ); + } + else + { + from.Target = new InternalTarget( craftSystem, tool ); + from.SendLocalizedMessage( 1044273 ); // Target an item to recycle. + } + } + + private class InternalTarget : Target + { + private CraftSystem m_CraftSystem; + private BaseTool m_Tool; + + public InternalTarget( CraftSystem craftSystem, BaseTool tool ) : base ( 2, false, TargetFlags.None ) + { + m_CraftSystem = craftSystem; + m_Tool = tool; + } + + private bool Resmelt( Mobile from, Item item, CraftResource resource ) + { + try + { + if ( CraftResources.GetType( resource ) != CraftResourceType.Metal ) + return false; + + CraftResourceInfo info = CraftResources.GetInfo( resource ); + + if ( info == null || info.ResourceTypes.Length == 0 ) + return false; + + CraftItem craftItem = m_CraftSystem.CraftItems.SearchFor( item.GetType() ); + + if ( craftItem == null || craftItem.Ressources.Count == 0 ) + return false; + + CraftRes craftResource = craftItem.Ressources.GetAt( 0 ); + + if ( craftResource.Amount < 2 ) + return false; // Not enough metal to resmelt + + Type resourceType = info.ResourceTypes[0]; + Item ingot = (Item)Activator.CreateInstance( resourceType ); + + if ( item is DragonBardingDeed || (item is BaseArmor && ((BaseArmor)item).PlayerConstructed) || (item is BaseWeapon && ((BaseWeapon)item).PlayerConstructed) || (item is BaseClothing && ((BaseClothing)item).PlayerConstructed) ) + ingot.Amount = craftResource.Amount / 2; + else + ingot.Amount = 1; + + item.Delete(); + from.AddToBackpack( ingot ); + + from.PlaySound( 0x2A ); + from.PlaySound( 0x240 ); + return true; + } + catch + { + } + + return false; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + int num = m_CraftSystem.CanCraft( from, m_Tool, null ); + + if ( num > 0 ) + { + from.SendGump( new CraftGump( from, m_CraftSystem, m_Tool, num ) ); + } + else + { + bool success = false; + bool isStoreBought = false; + + if ( targeted is BaseArmor ) + { + success = Resmelt( from, (BaseArmor)targeted, ((BaseArmor)targeted).Resource ); + isStoreBought = !((BaseArmor)targeted).PlayerConstructed; + } + else if ( targeted is BaseWeapon ) + { + success = Resmelt( from, (BaseWeapon)targeted, ((BaseWeapon)targeted).Resource ); + isStoreBought = !((BaseWeapon)targeted).PlayerConstructed; + } + else if ( targeted is DragonBardingDeed ) + { + success = Resmelt( from, (DragonBardingDeed)targeted, ((DragonBardingDeed)targeted).Resource ); + isStoreBought = false; + } + + if ( success ) + from.SendGump( new CraftGump( from, m_CraftSystem, m_Tool, isStoreBought ? 500418 : 1044270 ) ); // You melt the item down into ingots. + else + from.SendGump( new CraftGump( from, m_CraftSystem, m_Tool, 1044272 ) ); // You can't melt that down into ingots. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefAlchemy.cs b/Scripts/Engines/Craft/DefAlchemy.cs new file mode 100644 index 000000000..9fb14bf43 --- /dev/null +++ b/Scripts/Engines/Craft/DefAlchemy.cs @@ -0,0 +1,165 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefAlchemy : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Alchemy; } + } + + public override int GumpTitleNumber + { + get { return 1044001; } //
ALCHEMY MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefAlchemy(); + + return m_CraftSystem; + } + } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private DefAlchemy() : base( 1, 1, 1.25 )// base( 1, 1, 3.1 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + from.PlaySound( 0x242 ); + } + + private static Type typeofPotion = typeof( BasePotion ); + + public static bool IsPotion( Type type ) + { + return typeofPotion.IsAssignableFrom( type ); + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( IsPotion( item.ItemType ) ) + { + from.AddToBackpack( new Bottle() ); + return 500287; // You fail to create a useful potion. + } + else + { + return 1044043; // You failed to create the item, and some of your materials are lost. + } + } + else + { + from.PlaySound( 0x240 ); // Sound of a filling bottle + + if ( IsPotion( item.ItemType ) ) + { + if ( quality == -1 ) + return 1048136; // You create the potion and pour it into a keg. + else + return 500279; // You pour the potion into a bottle... + } + else + { + return 1044154; // You create the item. + } + } + } + + public override void InitCraftList() + { + int index = -1; + + // Refresh Potion + index = AddCraft( typeof( RefreshPotion ), 1044530, 1044538, -25, 25.0, typeof( BlackPearl ), 1044353, 1, 1044361 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( TotalRefreshPotion ), 1044530, 1044539, 25.0, 75.0, typeof( BlackPearl ), 1044353, 5, 1044361 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Agility Potion + index = AddCraft( typeof( AgilityPotion ), 1044531, 1044540, 15.0, 65.0, typeof( Bloodmoss ), 1044354, 1, 1044362 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( GreaterAgilityPotion ), 1044531, 1044541, 35.0, 85.0, typeof( Bloodmoss ), 1044354, 3, 1044362 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Nightsight Potion + index = AddCraft( typeof( NightSightPotion ), 1044532, 1044542, -25.0, 25.0, typeof( SpidersSilk ), 1044360, 1, 1044368 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Heal Potion + index = AddCraft( typeof( LesserHealPotion ), 1044533, 1044543, -25.0, 25.0, typeof( Ginseng ), 1044356, 1, 1044364 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( HealPotion ), 1044533, 1044544, 15.0, 65.0, typeof( Ginseng ), 1044356, 3, 1044364 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( GreaterHealPotion ), 1044533, 1044545, 55.0, 105.0, typeof( Ginseng ), 1044356, 7, 1044364 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Strength Potion + index = AddCraft( typeof( StrengthPotion ), 1044534, 1044546, 25.0, 75.0, typeof( MandrakeRoot ), 1044357, 2, 1044365 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( GreaterStrengthPotion ), 1044534, 1044547, 45.0, 95.0, typeof( MandrakeRoot ), 1044357, 5, 1044365 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Poison Potion + index = AddCraft( typeof( LesserPoisonPotion ), 1044535, 1044548, -5.0, 45.0, typeof( Nightshade ), 1044358, 1, 1044366 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( PoisonPotion ), 1044535, 1044549, 15.0, 65.0, typeof( Nightshade ), 1044358, 2, 1044366 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( GreaterPoisonPotion ), 1044535, 1044550, 55.0, 105.0, typeof( Nightshade ), 1044358, 4, 1044366 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( DeadlyPoisonPotion ), 1044535, 1044551, 90.0, 140.0, typeof( Nightshade ), 1044358, 8, 1044366 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Cure Potion + index = AddCraft( typeof( LesserCurePotion ), 1044536, 1044552, -10.0, 40.0, typeof( Garlic ), 1044355, 1, 1044363 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( CurePotion ), 1044536, 1044553, 25.0, 75.0, typeof( Garlic ), 1044355, 3, 1044363 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( GreaterCurePotion ), 1044536, 1044554, 65.0, 115.0, typeof( Garlic ), 1044355, 6, 1044363 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + // Explosion Potion + index = AddCraft( typeof( LesserExplosionPotion ), 1044537, 1044555, 5.0, 55.0, typeof( SulfurousAsh ), 1044359, 3, 1044367 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( ExplosionPotion ), 1044537, 1044556, 35.0, 85.0, typeof( SulfurousAsh ), 1044359, 5, 1044367 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + index = AddCraft( typeof( GreaterExplosionPotion ), 1044537, 1044557, 65.0, 115.0, typeof( SulfurousAsh ), 1044359, 10, 1044367 ); + AddRes( index, typeof ( Bottle ), 1044529, 1, 500315 ); + + if( Core.SE ) + { + index = AddCraft( typeof( SmokeBomb ), 1044537, 1030248, 90.0, 120.0, typeof( Eggs ), 1044477, 1, 1044253 ); + AddRes( index, typeof ( Ginseng ), 1044356, 3, 1044364 ); + SetNeededExpansion( index, Expansion.SE ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefBlacksmithy.cs b/Scripts/Engines/Craft/DefBlacksmithy.cs new file mode 100644 index 000000000..f892b5076 --- /dev/null +++ b/Scripts/Engines/Craft/DefBlacksmithy.cs @@ -0,0 +1,547 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefBlacksmithy : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Blacksmith; } + } + + public override int GumpTitleNumber + { + get { return 1044002; } //
BLACKSMITHY MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefBlacksmithy(); + + return m_CraftSystem; + } + } + + public override CraftECA ECA{ get{ return CraftECA.ChanceMinusSixtyToFourtyFive; } } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private DefBlacksmithy() : base( 1, 1, 1.25 )// base( 1, 2, 1.7 ) + { + /* + + base( MinCraftEffect, MaxCraftEffect, Delay ) + + MinCraftEffect : The minimum number of time the mobile will play the craft effect + MaxCraftEffect : The maximum number of time the mobile will play the craft effect + Delay : The delay between each craft effect + + Example: (3, 6, 1.7) would make the mobile do the PlayCraftEffect override + function between 3 and 6 time, with a 1.7 second delay each time. + + */ + } + + private static Type typeofAnvil = typeof( AnvilAttribute ); + private static Type typeofForge = typeof( ForgeAttribute ); + + public static void CheckAnvilAndForge( Mobile from, int range, out bool anvil, out bool forge ) + { + anvil = false; + forge = false; + + Map map = from.Map; + + if ( map == null ) + return; + + IPooledEnumerable eable = map.GetItemsInRange( from.Location, range ); + + foreach ( Item item in eable ) + { + Type type = item.GetType(); + + bool isAnvil = ( type.IsDefined( typeofAnvil, false ) || item.ItemID == 4015 || item.ItemID == 4016 || item.ItemID == 0x2DD5 || item.ItemID == 0x2DD6 ); + bool isForge = ( type.IsDefined( typeofForge, false ) || item.ItemID == 4017 || (item.ItemID >= 6522 && item.ItemID <= 6569) || item.ItemID == 0x2DD8 ); + + if ( isAnvil || isForge ) + { + if ( (from.Z + 16) < item.Z || (item.Z + 16) < from.Z || !from.InLOS( item ) ) + continue; + + anvil = anvil || isAnvil; + forge = forge || isForge; + + if ( anvil && forge ) + break; + } + } + + eable.Free(); + + for ( int x = -range; (!anvil || !forge) && x <= range; ++x ) + { + for ( int y = -range; (!anvil || !forge) && y <= range; ++y ) + { + Tile[] tiles = map.Tiles.GetStaticTiles( from.X+x, from.Y+y, true ); + + for ( int i = 0; (!anvil || !forge) && i < tiles.Length; ++i ) + { + int id = tiles[i].ID & 0x3FFF; + + bool isAnvil = ( id == 4015 || id == 4016 || id == 0x2DD5 || id == 0x2DD6 ); + bool isForge = ( id == 4017 || (id >= 6522 && id <= 6569) || id == 0x2DD8 ); + + if ( isAnvil || isForge ) + { + if ( (from.Z + 16) < tiles[i].Z || (tiles[i].Z + 16) < from.Z || !from.InLOS( new Point3D( from.X+x, from.Y+y, tiles[i].Z + (tiles[i].Height/2) + 1 ) ) ) + continue; + + anvil = anvil || isAnvil; + forge = forge || isForge; + } + } + } + } + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if ( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckTool( tool, from ) ) + return 1048146; // If you have a tool equipped, you must use that tool. + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + bool anvil, forge; + CheckAnvilAndForge( from, 2, out anvil, out forge ); + + if ( anvil && forge ) + return 0; + + return 1044267; // You must be near an anvil and a forge to smith items. + } + + public override void PlayCraftEffect( Mobile from ) + { + // no animation, instant sound + //if ( from.Body.Type == BodyType.Human && !from.Mounted ) + // from.Animate( 9, 5, 1, true, false, 0 ); + //new InternalTimer( from ).Start(); + + from.PlaySound( 0x2A ); + } + + // Delay to synchronize the sound with the hit on the anvil + private class InternalTimer : Timer + { + private Mobile m_From; + + public InternalTimer( Mobile from ) : base( TimeSpan.FromSeconds( 0.7 ) ) + { + m_From = from; + } + + protected override void OnTick() + { + m_From.PlaySound( 0x2A ); + } + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + /* + Synthax for a SIMPLE craft item + AddCraft( ObjectType, Group, MinSkill, MaxSkill, RessourceType, Amount, Message ) + + ObjectType : The type of the object you want to add to the build list. + Group : The group in wich the object will be showed in the craft menu. + MinSkill : The minimum of skill value + MaxSkill : The maximum of skill value + RessourceType : The type of the ressource the mobile need to create the item + Amount : The amount of the RessourceType it need to create the item + Message : String or Int for Localized. The message that will be sent to the mobile, if the specified ressource is missing. + + Synthax for a COMPLEXE craft item. A complexe item is an item that need either more than + only one skill, or more than only one ressource. + + Coming soon.... + */ + + #region Ringmail + AddCraft( typeof( RingmailGloves ), 1011076, 1025099, 12.0, 62.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddCraft( typeof( RingmailLegs ), 1011076, 1025104, 19.4, 69.4, typeof( IronIngot ), 1044036, 16, 1044037 ); + AddCraft( typeof( RingmailArms ), 1011076, 1025103, 16.9, 66.9, typeof( IronIngot ), 1044036, 14, 1044037 ); + AddCraft( typeof( RingmailChest ), 1011076, 1025100, 21.9, 71.9, typeof( IronIngot ), 1044036, 18, 1044037 ); + #endregion + + #region Chainmail + AddCraft( typeof( ChainCoif ), 1011077, 1025051, 14.5, 64.5, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddCraft( typeof( ChainLegs ), 1011077, 1025054, 36.7, 86.7, typeof( IronIngot ), 1044036, 18, 1044037 ); + AddCraft( typeof( ChainChest ), 1011077, 1025055, 39.1, 89.1, typeof( IronIngot ), 1044036, 20, 1044037 ); + #endregion + + int index = -1; + + #region Platemail + AddCraft( typeof( PlateArms ), 1011078, 1025136, 66.3, 116.3, typeof( IronIngot ), 1044036, 18, 1044037 ); + AddCraft( typeof( PlateGloves ), 1011078, 1025140, 58.9, 108.9, typeof( IronIngot ), 1044036, 12, 1044037 ); + AddCraft( typeof( PlateGorget ), 1011078, 1025139, 56.4, 106.4, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddCraft( typeof( PlateLegs ), 1011078, 1025137, 68.8, 118.8, typeof( IronIngot ), 1044036, 20, 1044037 ); + AddCraft( typeof( PlateChest ), 1011078, 1046431, 75.0, 125.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + AddCraft( typeof( FemalePlateChest ), 1011078, 1046430, 44.1, 94.1, typeof( IronIngot ), 1044036, 20, 1044037 ); + + if ( Core.AOS ) // exact pre-aos functionality unknown + AddCraft( typeof( DragonBardingDeed ), 1011078, 1053012, 72.5, 122.5, typeof( IronIngot ), 1044036, 750, 1044037 ); + + if( Core.SE ) + { + + index = AddCraft( typeof( PlateMempo ), 1011078, 1030180, 80.0, 130.0, typeof( IronIngot ), 1044036, 18, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlateDo ), 1011078, 1030184, 80.0, 130.0, typeof( IronIngot ), 1044036, 28, 1044037 ); //Double check skill + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlateHiroSode ), 1011078, 1030187, 80.0, 130.0, typeof( IronIngot ), 1044036, 16, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlateSuneate ), 1011078, 1030195, 65.0, 115.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlateHaidate ), 1011078, 1030200, 65.0, 115.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + } + #endregion + + #region Helmets + AddCraft( typeof( Bascinet ), 1011079, 1025132, 8.3, 58.3, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddCraft( typeof( CloseHelm ), 1011079, 1025128, 37.9, 87.9, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddCraft( typeof( Helmet ), 1011079, 1025130, 37.9, 87.9, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddCraft( typeof( NorseHelm ), 1011079, 1025134, 37.9, 87.9, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddCraft( typeof( PlateHelm ), 1011079, 1025138, 62.6, 112.6, typeof( IronIngot ), 1044036, 15, 1044037 ); + + if( Core.SE ) + { + index = AddCraft( typeof( ChainHatsuburi ), 1011079, 1030175, 30.0, 80.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlateHatsuburi ), 1011079, 1030176, 45.0, 95.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( HeavyPlateJingasa ), 1011079, 1030178, 45.0, 95.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( LightPlateJingasa ), 1011079, 1030188, 45.0, 95.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( SmallPlateJingasa ), 1011079, 1030191, 45.0, 95.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( DecorativePlateKabuto ), 1011079, 1030179, 90.0, 140.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlateBattleKabuto ), 1011079, 1030192, 90.0, 140.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( StandardPlateKabuto ), 1011079, 1030196, 90.0, 140.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + if( Core.ML ) + { + index = AddCraft( typeof( Circlet ), 1011079, 1032645, 62.1, 112.1, typeof( IronIngot ), 1044036, 6, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( RoyalCirclet ), 1011079, 1032646, 70.0, 120.0, typeof( IronIngot ), 1044036, 6, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( GemmedCirclet ), 1011079, 1032647, 75.0, 125.0, typeof( IronIngot ), 1044036, 6, 1044037 ); + AddRes( index, typeof( Tourmaline ), 1044237, 1, 1044240 ); + AddRes( index, typeof( Amethyst ), 1044236, 1, 1044240 ); + AddRes( index, typeof( BlueDiamond ), 1032696, 1, 1044240 ); + SetNeededExpansion( index, Expansion.ML ); + + } + } + #endregion + + #region Shields + AddCraft( typeof( Buckler ), 1011080, 1027027, -25.0, 25.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddCraft( typeof( BronzeShield ), 1011080, 1027026, -15.2, 34.8, typeof( IronIngot ), 1044036, 12, 1044037 ); + AddCraft( typeof( HeaterShield ), 1011080, 1027030, 24.3, 74.3, typeof( IronIngot ), 1044036, 18, 1044037 ); + AddCraft( typeof( MetalShield ), 1011080, 1027035, -10.2, 39.8, typeof( IronIngot ), 1044036, 14, 1044037 ); + AddCraft( typeof( MetalKiteShield ), 1011080, 1027028, 4.6, 54.6, typeof( IronIngot ), 1044036, 16, 1044037 ); + AddCraft( typeof( WoodenKiteShield ), 1011080, 1027032, -15.2, 34.8, typeof( IronIngot ), 1044036, 8, 1044037 ); + + if ( Core.AOS ) + { + AddCraft( typeof( ChaosShield ), 1011080, 1027107, 85.0, 135.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + AddCraft( typeof( OrderShield ), 1011080, 1027108, 85.0, 135.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + } + #endregion + + #region Bladed + + if ( Core.AOS ) + AddCraft( typeof( BoneHarvester ), 1011081, 1029915, 33.0, 83.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + + AddCraft( typeof( Broadsword ), 1011081, 1023934, 35.4, 85.4, typeof( IronIngot ), 1044036, 10, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( CrescentBlade ), 1011081, 1029921, 45.0, 95.0, typeof( IronIngot ), 1044036, 14, 1044037 ); + + AddCraft( typeof( Cutlass ), 1011081, 1025185, 24.3, 74.3, typeof( IronIngot ), 1044036, 8, 1044037 ); + AddCraft( typeof( Dagger ), 1011081, 1023921, -0.4, 49.6, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddCraft( typeof( Katana ),1011081, 1025119, 44.1, 94.1, typeof( IronIngot ), 1044036, 8, 1044037 ); + AddCraft( typeof( Kryss ), 1011081, 1025121, 36.7, 86.7, typeof( IronIngot ), 1044036, 8, 1044037 ); + AddCraft( typeof( Longsword ), 1011081, 1023937, 28.0, 78.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + AddCraft( typeof( Scimitar ), 1011081, 1025046, 31.7, 81.7, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddCraft( typeof( VikingSword ), 1011081, 1025049, 24.3, 74.3, typeof( IronIngot ), 1044036, 14, 1044037 ); + + if( Core.SE ) + { + + index = AddCraft( typeof( NoDachi ), 1011081, 1030221, 75.0, 125.0, typeof( IronIngot ), 1044036, 18, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Wakizashi ), 1011081, 1030223, 50.0, 100.0, typeof( IronIngot ), 1044036, 8, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Lajatang ), 1011081, 1030226, 80.0, 130.0, typeof( IronIngot ), 1044036, 25, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Daisho ), 1011081, 1030228, 60.0, 110.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Tekagi ), 1011081, 1030230, 55.0, 105.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Shuriken ), 1011081, 1030231, 45.0, 95.0, typeof( IronIngot ), 1044036, 5, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Kama ), 1011081, 1030232, 40.0, 90.0, typeof( IronIngot ), 1044036, 14, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Sai ), 1011081, 1030234, 50.0, 100.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + if( Core.ML ) + { + index = AddCraft( typeof( RadiantScimitar ), 1011081, 1031571, 75.0, 125.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( WarCleaver ), 1011081, 1031567, 70.0, 120.0, typeof( IronIngot ), 1044036, 18, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( ElvenSpellblade ), 1011081, 1031564, 70.0, 120.0, typeof( IronIngot ), 1044036, 14, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( AssassinSpike ), 1011081, 1031564, 70.0, 120.0, typeof( IronIngot ), 1044036, 9, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( Leafblade ), 1011081, 1031565, 70.0, 120.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( RuneBlade ), 1011081, 1031570, 70.0, 120.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( ElvenMachete ), 1011081, 1031573, 70.0, 120.0, typeof( IronIngot ), 1044036, 14, 1044037 ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( RuneCarvingKnife ), 1011081, 1072915, 70.0, 120.0, typeof( IronIngot ), 1044036, 9, 1044037 ); + AddRes( index, typeof( DreadHornMane ), 1032682, 1, 1053098 ); + AddRes( index, typeof( Putrefication ), 1032678, 10, 1053098 ); + AddRes( index, typeof( Muculent ), 1032678, 10, 1053098 ); + AddRecipe( index, 0 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( ColdForgedBlade ), 1011081, 1072916, 70.0, 120.0, typeof( IronIngot ), 1044036, 18, 1044037 ); + AddRes( index, typeof( GrizzledBones ), 1032684, 1, 1053098 ); + AddRes( index, typeof( Taint ), 1032684, 10, 1053098 ); + AddRes( index, typeof( Blight ), 1032675, 10, 1053098 ); + AddRecipe( index, 1 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( OverseerSunderedBlade ), 1011081, 1072920, 70.0, 120.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddRes( index, typeof( GrizzledBones ), 1032684, 1, 1053098 ); + AddRes( index, typeof( Blight ), 1032675, 10, 1053098 ); + AddRes( index, typeof( Scourge ), 1032677, 10, 1053098 ); + AddRecipe( index, 2 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + + index = AddCraft( typeof( LuminousRuneBlade ), 1011081, 1072922, 70.0, 120.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddRes( index, typeof( GrizzledBones ), 1032684, 1, 1053098 ); + AddRes( index, typeof( Corruption ), 1032676, 10, 1053098 ); + AddRes( index, typeof( Putrefication ), 1032678, 10, 1053098 ); + AddRecipe( index, 3 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + + /* //TODO: true spellblade; + index = AddCraft( typeof( ), 1011081, 1072920, 70.0, 120.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + AddRes( index, typeof( GrizzledBones ), 1032684, 1, 1053098 ); + AddRes( index, typeof( Blight ), 1032675, 10, 1053098 ); + AddRes( index, typeof( Scourge ), 1032677, 10, 1053098 ); + AddRecipe( index, 4 ); + ForceNonExceptional( index ); + SetNeededExpansion( index, Expansion.ML ); + * */ + + } + } + #endregion + + #region Axes + AddCraft( typeof( Axe ), 1011082, 1023913, 34.2, 84.2, typeof( IronIngot ), 1044036, 14, 1044037 ); + AddCraft( typeof( BattleAxe ), 1011082, 1023911, 30.5, 80.5, typeof( IronIngot ), 1044036, 14, 1044037 ); + AddCraft( typeof( DoubleAxe ), 1011082, 1023915, 29.3, 79.3, typeof( IronIngot ), 1044036, 12, 1044037 ); + AddCraft( typeof( ExecutionersAxe ), 1011082, 1023909, 34.2, 84.2, typeof( IronIngot ), 1044036, 14, 1044037 ); + AddCraft( typeof( LargeBattleAxe ), 1011082, 1025115, 28.0, 78.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + AddCraft( typeof( TwoHandedAxe ), 1011082, 1025187, 33.0, 83.0, typeof( IronIngot ), 1044036, 16, 1044037 ); + AddCraft( typeof( WarAxe ), 1011082, 1025040, 39.1, 89.1, typeof( IronIngot ), 1044036, 16, 1044037 ); + #endregion + + #region Pole Arms + + AddCraft( typeof( Bardiche ), 1011083, 1023917, 31.7, 81.7, typeof( IronIngot ), 1044036, 18, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( BladedStaff ), 1011083, 1029917, 40.0, 90.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( DoubleBladedStaff ), 1011083, 1029919, 45.0, 95.0, typeof( IronIngot ), 1044036, 16, 1044037 ); + + AddCraft( typeof( Halberd ), 1011083, 1025183, 39.1, 89.1, typeof( IronIngot ), 1044036, 20, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( Lance ), 1011083, 1029920, 48.0, 98.0, typeof( IronIngot ), 1044036, 20, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( Pike ), 1011083, 1029918, 47.0, 97.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + + AddCraft( typeof( ShortSpear ), 1011083, 1025123, 45.3, 95.3, typeof( IronIngot ), 1044036, 6, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( Scythe ), 1011083, 1029914, 39.0, 89.0, typeof( IronIngot ), 1044036, 14, 1044037 ); + + AddCraft( typeof( Spear ), 1011083, 1023938, 49.0, 99.0, typeof( IronIngot ), 1044036, 12, 1044037 ); + AddCraft( typeof( WarFork ), 1011083, 1025125, 42.9, 92.9, typeof( IronIngot ), 1044036, 12, 1044037 ); + + // Not craftable (is this an AOS change ??) + //AddCraft( typeof( Pitchfork ), 1011083, 1023720, 36.1, 86.1, typeof( IronIngot ), 1044036, 12, 1044037 ); + #endregion + + #region Bashing + AddCraft( typeof( HammerPick ), 1011084, 1025181, 34.2, 84.2, typeof( IronIngot ), 1044036, 16, 1044037 ); + AddCraft( typeof( Mace ), 1011084, 1023932, 14.5, 64.5, typeof( IronIngot ), 1044036, 6, 1044037 ); + AddCraft( typeof( Maul ), 1011084, 1025179, 19.4, 69.4, typeof( IronIngot ), 1044036, 10, 1044037 ); + + if ( Core.AOS ) + AddCraft( typeof( Scepter ), 1011084, 1029916, 21.4, 71.4, typeof( IronIngot ), 1044036, 10, 1044037 ); + + AddCraft( typeof( WarMace ), 1011084, 1025127, 28.0, 78.0, typeof( IronIngot ), 1044036, 14, 1044037 ); + AddCraft( typeof( WarHammer ), 1011084, 1025177, 34.2, 84.2, typeof( IronIngot ), 1044036, 16, 1044037 ); + + if( Core.SE ) + { + index = AddCraft( typeof( Tessen ), 1011084, 1030222, 85.0, 135.0, typeof( IronIngot ), 1044036, 16, 1044037 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 10, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + #endregion + + #region Dragon Scale Armor + index = AddCraft( typeof( DragonGloves ), 1053114, 1029795, 68.9, 118.9, typeof( RedScales ), 1060883, 16, 1060884 ); + SetUseSubRes2( index, true ); + + index = AddCraft( typeof( DragonHelm ), 1053114, 1029797, 72.6, 122.6, typeof( RedScales ), 1060883, 20, 1060884 ); + SetUseSubRes2( index, true ); + + index = AddCraft( typeof( DragonLegs ), 1053114, 1029799, 78.8, 128.8, typeof( RedScales ), 1060883, 28, 1060884 ); + SetUseSubRes2( index, true ); + + index = AddCraft( typeof( DragonArms ), 1053114, 1029815, 76.3, 126.3, typeof( RedScales ), 1060883, 24, 1060884 ); + SetUseSubRes2( index, true ); + + index = AddCraft( typeof( DragonChest ), 1053114, 1029793, 85.0, 135.0, typeof( RedScales ), 1060883, 36, 1060884 ); + SetUseSubRes2( index, true ); + #endregion + + // Set the overridable material + SetSubRes( typeof( IronIngot ), 1044022 ); + + // Add every material you want the player to be able to choose from + // This will override the overridable material + AddSubRes( typeof( IronIngot ), 1044022, 00.0, 1044036, 1044267 ); + AddSubRes( typeof( DullCopperIngot ), 1044023, 65.0, 1044036, 1044268 ); + AddSubRes( typeof( ShadowIronIngot ), 1044024, 70.0, 1044036, 1044268 ); + AddSubRes( typeof( CopperIngot ), 1044025, 75.0, 1044036, 1044268 ); + AddSubRes( typeof( BronzeIngot ), 1044026, 80.0, 1044036, 1044268 ); + AddSubRes( typeof( GoldIngot ), 1044027, 85.0, 1044036, 1044268 ); + AddSubRes( typeof( AgapiteIngot ), 1044028, 90.0, 1044036, 1044268 ); + AddSubRes( typeof( VeriteIngot ), 1044029, 95.0, 1044036, 1044268 ); + AddSubRes( typeof( ValoriteIngot ), 1044030, 99.0, 1044036, 1044268 ); + + SetSubRes2( typeof( RedScales ), 1060875 ); + + AddSubRes2( typeof( RedScales ), 1060875, 0.0, 1053137, 1044268 ); + AddSubRes2( typeof( YellowScales ), 1060876, 0.0, 1053137, 1044268 ); + AddSubRes2( typeof( BlackScales ), 1060877, 0.0, 1053137, 1044268 ); + AddSubRes2( typeof( GreenScales ), 1060878, 0.0, 1053137, 1044268 ); + AddSubRes2( typeof( WhiteScales ), 1060879, 0.0, 1053137, 1044268 ); + AddSubRes2( typeof( BlueScales ), 1060880, 0.0, 1053137, 1044268 ); + + Resmelt = true; + Repair = true; + MarkOption = true; + CanEnhance = Core.AOS; + } + } + + public class ForgeAttribute : Attribute + { + public ForgeAttribute() + { + } + } + + public class AnvilAttribute : Attribute + { + public AnvilAttribute() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefBowFletching.cs b/Scripts/Engines/Craft/DefBowFletching.cs new file mode 100644 index 000000000..2d267e278 --- /dev/null +++ b/Scripts/Engines/Craft/DefBowFletching.cs @@ -0,0 +1,133 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefBowFletching : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Fletching; } + } + + public override int GumpTitleNumber + { + get { return 1044006; } //
BOWCRAFT AND FLETCHING MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefBowFletching(); + + return m_CraftSystem; + } + } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.5; // 50% + } + + private DefBowFletching() : base( 1, 1, 1.25 )// base( 1, 2, 1.7 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + // no animation + //if ( from.Body.Type == BodyType.Human && !from.Mounted ) + // from.Animate( 33, 5, 1, true, false, 0 ); + + from.PlaySound( 0x55 ); + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override CraftECA ECA{ get{ return CraftECA.FiftyPercentChanceMinusTenPercent; } } + + public override void InitCraftList() + { + int index = -1; + + // Materials + AddCraft( typeof( Kindling ), 1044457, 1023553, 0.0, 00.0, typeof( Log ), 1044041, 1, 1044351 ); + + index = AddCraft( typeof( Shaft ), 1044457, 1027124, 0.0, 40.0, typeof( Log ), 1044041, 1, 1044351 ); + SetUseAllRes( index, true ); + + // Ammunition + index = AddCraft( typeof( Arrow ), 1044565, 1023903, 0.0, 40.0, typeof( Shaft ), 1044560, 1, 1044561 ); + AddRes( index, typeof( Feather ), 1044562, 1, 1044563 ); + SetUseAllRes( index, true ); + + index = AddCraft( typeof( Bolt ), 1044565, 1027163, 0.0, 40.0, typeof( Shaft ), 1044560, 1, 1044561 ); + AddRes( index, typeof( Feather ), 1044562, 1, 1044563 ); + SetUseAllRes( index, true ); + + if( Core.SE ) + { + index = AddCraft( typeof( FukiyaDarts ), 1044565, 1030246, 50.0, 90.0, typeof( Log ), 1044041, 1, 1044351 ); + SetUseAllRes( index, true ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Weapons + AddCraft( typeof( Bow ), 1044566, 1025042, 30.0, 70.0, typeof( Log ), 1044041, 7, 1044351 ); + AddCraft( typeof( Crossbow ), 1044566, 1023919, 60.0, 100.0, typeof( Log ), 1044041, 7, 1044351 ); + AddCraft( typeof( HeavyCrossbow ), 1044566, 1025117, 80.0, 120.0, typeof( Log ), 1044041, 10, 1044351 ); + + if ( Core.AOS ) + { + AddCraft( typeof( CompositeBow ), 1044566, 1029922, 70.0, 110.0, typeof( Log ), 1044041, 7, 1044351 ); + AddCraft( typeof( RepeatingCrossbow ), 1044566, 1029923, 90.0, 130.0, typeof( Log ), 1044041, 10, 1044351 ); + } + + if( Core.SE ) + { + index = AddCraft( typeof( Yumi ), 1044566, 1030224, 90.0, 130.0, typeof( Log ), 1044041, 10, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + } + + MarkOption = true; + Repair = Core.AOS; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefCarpentry.cs b/Scripts/Engines/Craft/DefCarpentry.cs new file mode 100644 index 000000000..e8df13675 --- /dev/null +++ b/Scripts/Engines/Craft/DefCarpentry.cs @@ -0,0 +1,345 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefCarpentry : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Carpentry; } + } + + public override int GumpTitleNumber + { + get { return 1044004; } //
CARPENTRY MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefCarpentry(); + + return m_CraftSystem; + } + } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.5; // 50% + } + + private DefCarpentry() : base( 1, 1, 1.25 )// base( 1, 1, 3.0 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + // no animation + //if ( from.Body.Type == BodyType.Human && !from.Mounted ) + // from.Animate( 9, 5, 1, true, false, 0 ); + + from.PlaySound( 0x23D ); + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + int index = -1; + + // Other Items + index = AddCraft( typeof( Board ), 1044294, 1027127, 0.0, 0.0, typeof( Log ), 1044466, 1, 1044465 ); + SetUseAllRes( index, true ); + + AddCraft( typeof( BarrelStaves ), 1044294, 1027857, 00.0, 25.0, typeof( Log ), 1044041, 5, 1044351 ); + AddCraft( typeof( BarrelLid ), 1044294, 1027608, 11.0, 36.0, typeof( Log ), 1044041, 4, 1044351 ); + AddCraft( typeof( ShortMusicStand ), 1044294, 1044313, 78.9, 103.9, typeof( Log ), 1044041, 15, 1044351 ); + AddCraft( typeof( TallMusicStand ), 1044294, 1044315, 81.5, 106.5, typeof( Log ), 1044041, 20, 1044351 ); + AddCraft( typeof( Easle ), 1044294, 1044317, 86.8, 111.8, typeof( Log ), 1044041, 20, 1044351 ); + if( Core.SE ) + { + index = AddCraft( typeof( RedHangingLantern ), 1044294, 1029412, 65.0, 90.0, typeof( Log ), 1044041, 5, 1044351 ); + AddRes( index, typeof( BlankScroll ), 1044377, 10, 1044378 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( WhiteHangingLantern ), 1044294, 1029416, 65.0, 90.0, typeof( Log ), 1044041, 5, 1044351 ); + AddRes( index, typeof( BlankScroll ), 1044377, 10, 1044378 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( ShojiScreen ), 1044294, 1029423, 80.0, 105.0, typeof( Log ), 1044041, 75, 1044351 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 60, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( BambooScreen ), 1044294, 1029428, 80.0, 105.0, typeof( Log ), 1044041, 75, 1044351 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 60, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Furniture + AddCraft( typeof( FootStool ), 1044291, 1022910, 11.0, 36.0, typeof( Log ), 1044041, 9, 1044351 ); + AddCraft( typeof( Stool ), 1044291, 1022602, 11.0, 36.0, typeof( Log ), 1044041, 9, 1044351 ); + AddCraft( typeof( BambooChair ), 1044291, 1044300, 21.0, 46.0, typeof( Log ), 1044041, 13, 1044351 ); + AddCraft( typeof( WoodenChair ), 1044291, 1044301, 21.0, 46.0, typeof( Log ), 1044041, 13, 1044351 ); + AddCraft( typeof( FancyWoodenChairCushion ), 1044291, 1044302, 42.1, 67.1, typeof( Log ), 1044041, 15, 1044351 ); + AddCraft( typeof( WoodenChairCushion ), 1044291, 1044303, 42.1, 67.1, typeof( Log ), 1044041, 13, 1044351 ); + AddCraft( typeof( WoodenBench ), 1044291, 1022860, 52.6, 77.6, typeof( Log ), 1044041, 17, 1044351 ); + AddCraft( typeof( WoodenThrone ), 1044291, 1044304, 52.6, 77.6, typeof( Log ), 1044041, 17, 1044351 ); + AddCraft( typeof( Throne ), 1044291, 1044305, 73.6, 98.6, typeof( Log ), 1044041, 19, 1044351 ); + AddCraft( typeof( Nightstand ), 1044291, 1044306, 42.1, 67.1, typeof( Log ), 1044041, 17, 1044351 ); + AddCraft( typeof( WritingTable ), 1044291, 1022890, 63.1, 88.1, typeof( Log ), 1044041, 17, 1044351 ); + AddCraft( typeof( YewWoodTable ), 1044291, 1044307, 63.1, 88.1, typeof( Log ), 1044041, 23, 1044351 ); + AddCraft( typeof( LargeTable ), 1044291, 1044308, 84.2, 109.2, typeof( Log ), 1044041, 27, 1044351 ); + + if( Core.SE ) + { + index = AddCraft( typeof( ElegantLowTable ), 1044291, 1030265, 80.0, 105.0, typeof( Log ), 1044041, 35, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PlainLowTable ), 1044291, 1030266, 80.0, 105.0, typeof( Log ), 1044041, 35, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Containers + AddCraft( typeof( WoodenBox ), 1044292, 1023709, 21.0, 46.0, typeof( Log ), 1044041, 10, 1044351 ); + AddCraft( typeof( SmallCrate ), 1044292, 1044309, 10.0, 35.0, typeof( Log ), 1044041, 8 , 1044351 ); + AddCraft( typeof( MediumCrate ), 1044292, 1044310, 31.0, 56.0, typeof( Log ), 1044041, 15, 1044351 ); + AddCraft( typeof( LargeCrate ), 1044292, 1044311, 47.3, 72.3, typeof( Log ), 1044041, 18, 1044351 ); + AddCraft( typeof( WoodenChest ), 1044292, 1023650, 73.6, 98.6, typeof( Log ), 1044041, 20, 1044351 ); + AddCraft( typeof( EmptyBookcase ), 1044292, 1022718, 31.5, 56.5, typeof( Log ), 1044041, 25, 1044351 ); + AddCraft( typeof( FancyArmoire ), 1044292, 1044312, 84.2, 109.2, typeof( Log ), 1044041, 35, 1044351 ); + AddCraft( typeof( Armoire ), 1044292, 1022643, 84.2, 109.2, typeof( Log ), 1044041, 35, 1044351 ); + + if( Core.SE ) + { + index = AddCraft( typeof( PlainWoodenChest ), 1044292, 1030251, 90.0, 115.0, typeof( Log ), 1044041, 30, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( OrnateWoodenChest ), 1044292, 1030253, 90.0, 115.0, typeof( Log ), 1044041, 30, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( GildedWoodenChest ), 1044292, 1030255, 90.0, 115.0, typeof( Log ), 1044041, 30, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( WoodenFootLocker ), 1044292, 1030257, 90.0, 115.0, typeof( Log ), 1044041, 30, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( FinishedWoodenChest ),1044292, 1030259, 90.0, 115.0, typeof( Log ), 1044041, 30, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( TallCabinet ), 1044292, 1030261, 90.0, 115.0, typeof( Log ), 1044041, 35, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( ShortCabinet ), 1044292, 1030263, 90.0, 115.0, typeof( Log ), 1044041, 35, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( RedArmoire ), 1044292, 1030328, 90.0, 115.0, typeof( Log ), 1044041, 40, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( ElegantArmoire ), 1044292, 1030330, 90.0, 115.0, typeof( Log ), 1044041, 40, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( MapleArmoire ), 1044292, 1030328, 90.0, 115.0, typeof( Log ), 1044041, 40, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( CherryArmoire ), 1044292, 1030328, 90.0, 115.0, typeof( Log ), 1044041, 40, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + } + + index = AddCraft( typeof( Keg ), 1044292, 1023711, 57.8, 82.8, typeof( BarrelStaves ), 1044288, 3, 1044253 ); + AddRes( index, typeof( BarrelHoops ), 1044289, 1, 1044253 ); + AddRes( index, typeof( BarrelLid ), 1044251, 1, 1044253 ); + + // Staves and Shields + AddCraft( typeof( ShepherdsCrook ), 1044295, 1023713, 78.9, 103.9, typeof( Log ), 1044041, 7, 1044351 ); + AddCraft( typeof( QuarterStaff ), 1044295, 1023721, 73.6, 98.6, typeof( Log ), 1044041, 6, 1044351 ); + AddCraft( typeof( GnarledStaff ), 1044295, 1025112, 78.9, 103.9, typeof( Log ), 1044041, 7, 1044351 ); + AddCraft( typeof( WoodenShield ), 1044295, 1027034, 52.6, 77.6, typeof( Log ), 1044041, 9, 1044351 ); + + index = AddCraft( typeof( FishingPole ), Core.AOS ? 1044294 : 1044295, 1023519, 68.4, 93.4, typeof( Log ), 1044041, 5, 1044351 ); //This is in the categor of Other during AoS + AddSkill( index, SkillName.Tailoring, 40.0, 45.0 ); + AddRes( index, typeof( Cloth ), 1044286, 5, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( Bokuto ), 1044295, 1030227, 70.0, 95.0, typeof( Log ), 1044041, 6, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( Fukiya ), 1044295, 1030229, 60.0, 85.0, typeof( Log ), 1044041, 6, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( Tetsubo ), 1044295, 1030225, 85.0, 110.0, typeof( Log ), 1044041, 8, 1044351 ); + AddSkill( index, SkillName.Tinkering, 40.0, 45.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 5, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Instruments + index = AddCraft( typeof( LapHarp ), 1044293, 1023762, 63.1, 88.1, typeof( Log ), 1044041, 20, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + AddRes( index, typeof( Cloth ), 1044286, 10, 1044287 ); + + index = AddCraft( typeof( Harp ), 1044293, 1023761, 78.9, 103.9, typeof( Log ), 1044041, 35, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + AddRes( index, typeof( Cloth ), 1044286, 15, 1044287 ); + + index = AddCraft( typeof( Drums ), 1044293, 1023740, 57.8, 82.8, typeof( Log ), 1044041, 20, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + AddRes( index, typeof( Cloth ), 1044286, 10, 1044287 ); + + index = AddCraft( typeof( Lute ), 1044293, 1023763, 68.4, 93.4, typeof( Log ), 1044041, 25, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + AddRes( index, typeof( Cloth ), 1044286, 10, 1044287 ); + + index = AddCraft( typeof( Tambourine ), 1044293, 1023741, 57.8, 82.8, typeof( Log ), 1044041, 15, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + AddRes( index, typeof( Cloth ), 1044286, 10, 1044287 ); + + index = AddCraft( typeof( TambourineTassel ), 1044293, 1044320, 57.8, 82.8, typeof( Log ), 1044041, 15, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + AddRes( index, typeof( Cloth ), 1044286, 15, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( BambooFlute ), 1044293, 1030247, 80.0, 105.0, typeof( Log ), 1044041, 15, 1044351 ); + AddSkill( index, SkillName.Musicianship, 45.0, 50.0 ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Misc + index = AddCraft( typeof( SmallBedSouthDeed ), 1044290, 1044321, 94.7, 113.1, typeof( Log ), 1044041, 100, 1044351 ); + AddSkill( index, SkillName.Tailoring, 75.0, 80.0 ); + AddRes( index, typeof( Cloth ), 1044286, 100, 1044287 ); + index = AddCraft( typeof( SmallBedEastDeed ), 1044290, 1044322, 94.7, 113.1, typeof( Log ), 1044041, 100, 1044351 ); + AddSkill( index, SkillName.Tailoring, 75.0, 80.0 ); + AddRes( index, typeof( Cloth ), 1044286, 100, 1044287 ); + index = AddCraft( typeof( LargeBedSouthDeed ), 1044290,1044323, 94.7, 113.1, typeof( Log ), 1044041, 150, 1044351 ); + AddSkill( index, SkillName.Tailoring, 75.0, 80.0 ); + AddRes( index, typeof( Cloth ), 1044286, 150, 1044287 ); + index = AddCraft( typeof( LargeBedEastDeed ), 1044290, 1044324, 94.7, 113.1, typeof( Log ), 1044041, 150, 1044351 ); + AddSkill( index, SkillName.Tailoring, 75.0, 80.0 ); + AddRes( index, typeof( Cloth ), 1044286, 150, 1044287 ); + AddCraft( typeof( DartBoardSouthDeed ), 1044290, 1044325, 15.7, 40.7, typeof( Log ), 1044041, 5, 1044351 ); + AddCraft( typeof( DartBoardEastDeed ), 1044290, 1044326, 15.7, 40.7, typeof( Log ), 1044041, 5, 1044351 ); + AddCraft( typeof( BallotBoxDeed ), 1044290, 1044327, 47.3, 72.3, typeof( Log ), 1044041, 5, 1044351 ); + index = AddCraft( typeof( PentagramDeed ), 1044290, 1044328, 100.0, 125.0, typeof( Log ), 1044041, 100, 1044351 ); + AddSkill( index, SkillName.Magery, 75.0, 80.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 40, 1044037 ); + index = AddCraft( typeof( AbbatoirDeed ), 1044290, 1044329, 100.0, 125.0, typeof( Log ), 1044041, 100, 1044351 ); + AddSkill( index, SkillName.Magery, 50.0, 55.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 40, 1044037 ); + + if ( Core.AOS ) + { + AddCraft( typeof( PlayerBBEast ), 1044290, 1062420, 85.0, 110.0, typeof( Log ), 1044041, 50, 1044351 ); + AddCraft( typeof( PlayerBBSouth ), 1044290, 1062421, 85.0, 110.0, typeof( Log ), 1044041, 50, 1044351 ); + } + + // Blacksmithy + index = AddCraft( typeof( SmallForgeDeed ), 1044296, 1044330, 73.6, 98.6, typeof( Log ), 1044041, 5, 1044351 ); + AddSkill( index, SkillName.Blacksmith, 75.0, 80.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 75, 1044037 ); + index = AddCraft( typeof( LargeForgeEastDeed ), 1044296, 1044331, 78.9, 103.9, typeof( Log ), 1044041, 5, 1044351 ); + AddSkill( index, SkillName.Blacksmith, 80.0, 85.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 100, 1044037 ); + index = AddCraft( typeof( LargeForgeSouthDeed ), 1044296, 1044332, 78.9, 103.9, typeof( Log ), 1044041, 5, 1044351 ); + AddSkill( index, SkillName.Blacksmith, 80.0, 85.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 100, 1044037 ); + index = AddCraft( typeof( AnvilEastDeed ), 1044296, 1044333, 73.6, 98.6, typeof( Log ), 1044041, 5, 1044351 ); + AddSkill( index, SkillName.Blacksmith, 75.0, 80.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 150, 1044037 ); + index = AddCraft( typeof( AnvilSouthDeed ), 1044296, 1044334, 73.6, 98.6, typeof( Log ), 1044041, 5, 1044351 ); + AddSkill( index, SkillName.Blacksmith, 75.0, 80.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 150, 1044037 ); + + // Training + index = AddCraft( typeof( TrainingDummyEastDeed ), 1044297, 1044335, 68.4, 93.4, typeof( Log ), 1044041, 55, 1044351 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 60, 1044287 ); + index = AddCraft( typeof( TrainingDummySouthDeed ), 1044297, 1044336, 68.4, 93.4, typeof( Log ), 1044041, 55, 1044351 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 60, 1044287 ); + index = AddCraft( typeof( PickpocketDipEastDeed ), 1044297, 1044337, 73.6, 98.6, typeof( Log ), 1044041, 65, 1044351 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 60, 1044287 ); + index = AddCraft( typeof( PickpocketDipSouthDeed ), 1044297, 1044338, 73.6, 98.6, typeof( Log ), 1044041, 65, 1044351 ); + AddSkill( index, SkillName.Tailoring, 50.0, 55.0 ); + AddRes( index, typeof( Cloth ), 1044286, 60, 1044287 ); + + // Tailoring + index = AddCraft( typeof( Dressform ), 1044298, 1044339, 63.1, 88.1, typeof( Log ), 1044041, 25, 1044351 ); + AddSkill( index, SkillName.Tailoring, 65.0, 70.0 ); + AddRes( index, typeof( Cloth ), 1044286, 10, 1044287 ); + index = AddCraft( typeof( SpinningwheelEastDeed ), 1044298, 1044341, 73.6, 98.6, typeof( Log ), 1044041, 75, 1044351 ); + AddSkill( index, SkillName.Tailoring, 65.0, 70.0 ); + AddRes( index, typeof( Cloth ), 1044286, 25, 1044287 ); + index = AddCraft( typeof( SpinningwheelSouthDeed ), 1044298, 1044342, 73.6, 98.6, typeof( Log ), 1044041, 75, 1044351 ); + AddSkill( index, SkillName.Tailoring, 65.0, 70.0 ); + AddRes( index, typeof( Cloth ), 1044286, 25, 1044287 ); + index = AddCraft( typeof( LoomEastDeed ), 1044298, 1044343, 84.2, 109.2, typeof( Log ), 1044041, 85, 1044351 ); + AddSkill( index, SkillName.Tailoring, 65.0, 70.0 ); + AddRes( index, typeof( Cloth ), 1044286, 25, 1044287 ); + index = AddCraft( typeof( LoomSouthDeed ), 1044298, 1044344, 84.2, 109.2, typeof( Log ), 1044041, 85, 1044351 ); + AddSkill( index, SkillName.Tailoring, 65.0, 70.0 ); + AddRes( index, typeof( Cloth ), 1044286, 25, 1044287 ); + + // Cooking + index = AddCraft( typeof( StoneOvenEastDeed ), 1044299, 1044345, 68.4, 93.4, typeof( Log ), 1044041, 85, 1044351 ); + AddSkill( index, SkillName.Tinkering, 50.0, 55.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 125, 1044037 ); + index = AddCraft( typeof( StoneOvenSouthDeed ), 1044299, 1044346, 68.4, 93.4, typeof( Log ), 1044041, 85, 1044351 ); + AddSkill( index, SkillName.Tinkering, 50.0, 55.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 125, 1044037 ); + index = AddCraft( typeof( FlourMillEastDeed ), 1044299, 1044347, 94.7, 119.7, typeof( Log ), 1044041, 100, 1044351 ); + AddSkill( index, SkillName.Tinkering, 50.0, 55.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 50, 1044037 ); + index = AddCraft( typeof( FlourMillSouthDeed ), 1044299, 1044348, 94.7, 119.7, typeof( Log ), 1044041, 100, 1044351 ); + AddSkill( index, SkillName.Tinkering, 50.0, 55.0 ); + AddRes( index, typeof( IronIngot ), 1044036, 50, 1044037 ); + AddCraft( typeof( WaterTroughEastDeed ), 1044299, 1044349, 94.7, 119.7, typeof( Log ), 1044041, 150, 1044351 ); + AddCraft( typeof( WaterTroughSouthDeed ), 1044299, 1044350, 94.7, 119.7, typeof( Log ), 1044041, 150, 1044351 ); + + MarkOption = true; + Repair = Core.AOS; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefCartography.cs b/Scripts/Engines/Craft/DefCartography.cs new file mode 100644 index 000000000..c7ca403b5 --- /dev/null +++ b/Scripts/Engines/Craft/DefCartography.cs @@ -0,0 +1,88 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefCartography : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Cartography; } + } + + public override int GumpTitleNumber + { + get { return 1044008; } //
CARTOGRAPHY MENU
+ } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefCartography(); + + return m_CraftSystem; + } + } + + private DefCartography() : base( 1, 1, 1.25 )// base( 1, 1, 3.0 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + from.PlaySound( 0x249 ); + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + AddCraft( typeof( LocalMap ), 1044448, 1015230, 10.0, 70.0, typeof( BlankMap ), 1044449, 1, 1044450 ); + AddCraft( typeof( CityMap ), 1044448, 1015231, 25.0, 85.0, typeof( BlankMap ), 1044449, 1, 1044450 ); + AddCraft( typeof( SeaChart ), 1044448, 1015232, 35.0, 95.0, typeof( BlankMap ), 1044449, 1, 1044450 ); + AddCraft( typeof( WorldMap ), 1044448, 1015233, 39.5, 99.5, typeof( BlankMap ), 1044449, 1, 1044450 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefCooking.cs b/Scripts/Engines/Craft/DefCooking.cs new file mode 100644 index 000000000..34dc442f0 --- /dev/null +++ b/Scripts/Engines/Craft/DefCooking.cs @@ -0,0 +1,247 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefCooking : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Cooking; } + } + + public override int GumpTitleNumber + { + get { return 1044003; } //
COOKING MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefCooking(); + + return m_CraftSystem; + } + } + + public override CraftECA ECA{ get{ return CraftECA.ChanceMinusSixtyToFourtyFive; } } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private DefCooking() : base( 1, 1, 1.25 )// base( 1, 1, 1.5 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + int index = -1; + + /* Begin Ingredients */ + index = AddCraft( typeof( SackFlour ), 1044495, 1024153, 0.0, 100.0, typeof( WheatSheaf ), 1044489, 2, 1044490 ); + SetNeedMill( index, true ); + + index = AddCraft( typeof( Dough ), 1044495, 1024157, 0.0, 100.0, typeof( SackFlour ), 1044468, 1, 1044253 ); + AddRes( index, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + + index = AddCraft( typeof( SweetDough ), 1044495, 1041340, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( JarHoney ), 1044472, 1, 1044253 ); + + index = AddCraft( typeof( CakeMix ), 1044495, 1041002, 0.0, 100.0, typeof( SackFlour ), 1044468, 1, 1044253 ); + AddRes( index, typeof( SweetDough ), 1044475, 1, 1044253 ); + + index = AddCraft( typeof( CookieMix ), 1044495, 1024159, 0.0, 100.0, typeof( JarHoney ), 1044472, 1, 1044253 ); + AddRes( index, typeof( SweetDough ), 1044475, 1, 1044253 ); + /* End Ingredients */ + + /* Begin Preparations */ + index = AddCraft( typeof( UnbakedQuiche ), 1044496, 1041339, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( Eggs ), 1044477, 1, 1044253 ); + + // TODO: This must also support chicken and lamb legs + index = AddCraft( typeof( UnbakedMeatPie ), 1044496, 1041338, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( RawRibs ), 1044482, 1, 1044253 ); + + index = AddCraft( typeof( UncookedSausagePizza ), 1044496, 1041337, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( Sausage ), 1044483, 1, 1044253 ); + + index = AddCraft( typeof( UncookedCheesePizza ), 1044496, 1041341, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( CheeseWheel ), 1044486, 1, 1044253 ); + + index = AddCraft( typeof( UnbakedFruitPie ), 1044496, 1041334, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( Pear ), 1044481, 1, 1044253 ); + + index = AddCraft( typeof( UnbakedPeachCobbler ), 1044496, 1041335, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( Peach ), 1044480, 1, 1044253 ); + + index = AddCraft( typeof( UnbakedApplePie ), 1044496, 1041336, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( Apple ), 1044479, 1, 1044253 ); + + index = AddCraft( typeof( UnbakedPumpkinPie ), 1044496, 1041342, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + AddRes( index, typeof( Pumpkin ), 1044484, 1, 1044253 ); + + if ( Core.SE ) + { + index = AddCraft( typeof( GreenTea ), 1044496, 1030315, 80.0, 130.0, typeof( GreenTeaBasket ), 1030316, 1, 1044253 ); + AddRes( index, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( WasabiClumps ), 1044496, 1029451, 70.0, 120.0, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + AddRes( index, typeof( WoodenBowlOfPeas ), 1025633, 3, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( SushiRolls ), 1044496, 1030303, 90.0, 120.0, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + AddRes( index, typeof( RawFishSteak ), 1044476, 10, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( SushiPlatter ), 1044496, 1030305, 90.0, 120.0, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + AddRes( index, typeof( RawFishSteak ), 1044476, 10, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + } + + index = AddCraft( typeof( TribalPaint ), 1044496, 1040000, 80.0, 80.0, typeof( SackFlour ), 1044468, 1, 1044253 ); + AddRes( index, typeof( TribalBerry ), 1046460, 1, 1044253 ); + + if ( Core.SE ) + { + index = AddCraft( typeof( EggBomb ), 1044496, 1030249, 90.0, 120.0, typeof( Eggs ), 1044477, 1, 1044253 ); + AddRes( index, typeof( SackFlour ), 1044468, 3, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + } + /* End Preparations */ + + /* Begin Baking */ + index = AddCraft( typeof( BreadLoaf ), 1044497, 1024156, 0.0, 100.0, typeof( Dough ), 1044469, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( Cookies ), 1044497, 1025643, 0.0, 100.0, typeof( CookieMix ), 1044474, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( Cake ), 1044497, 1022537, 0.0, 100.0, typeof( CakeMix ), 1044471, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( Muffins ), 1044497, 1022539, 0.0, 100.0, typeof( SweetDough ), 1044475, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( Quiche ), 1044497, 1041345, 0.0, 100.0, typeof( UnbakedQuiche ), 1044518, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( MeatPie ), 1044497, 1041347, 0.0, 100.0, typeof( UnbakedMeatPie ), 1044519, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( SausagePizza ), 1044497, 1044517, 0.0, 100.0, typeof( UncookedSausagePizza ), 1044520, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( CheesePizza ), 1044497, 1044516, 0.0, 100.0, typeof( UncookedCheesePizza ), 1044521, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( FruitPie ), 1044497, 1041346, 0.0, 100.0, typeof( UnbakedFruitPie ), 1044522, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( PeachCobbler ), 1044497, 1041344, 0.0, 100.0, typeof( UnbakedPeachCobbler ), 1044523, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( ApplePie ), 1044497, 1041343, 0.0, 100.0, typeof( UnbakedApplePie ), 1044524, 1, 1044253 ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( PumpkinPie ), 1044497, 1041348, 0.0, 100.0, typeof( UnbakedPumpkinPie ), 1046461, 1, 1044253 ); + SetNeedOven( index, true ); + + if ( Core.SE ) + { + index = AddCraft( typeof( MisoSoup ), 1044497, 1030317, 60.0, 110.0, typeof( RawFishSteak ), 1044476, 1, 1044253 ); + AddRes( index, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( WhiteMisoSoup ), 1044497, 1030318, 60.0, 110.0, typeof( RawFishSteak ), 1044476, 1, 1044253 ); + AddRes( index, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( RedMisoSoup ), 1044497, 1030319, 60.0, 110.0, typeof( RawFishSteak ), 1044476, 1, 1044253 ); + AddRes( index, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + SetNeedOven( index, true ); + + index = AddCraft( typeof( AwaseMisoSoup ), 1044497, 1030320, 60.0, 110.0, typeof( RawFishSteak ), 1044476, 1, 1044253 ); + AddRes( index, typeof( BaseBeverage ), 1046458, 1, 1044253 ); + SetNeededExpansion( index, Expansion.SE ); + SetNeedOven( index, true ); + } + /* End Baking */ + + /* Begin Barbecue */ + index = AddCraft( typeof( CookedBird ), 1044498, 1022487, 0.0, 100.0, typeof( RawBird ), 1044470, 1, 1044253 ); + SetNeedHeat( index, true ); + SetUseAllRes( index, true ); + + index = AddCraft( typeof( ChickenLeg ), 1044498, 1025640, 0.0, 100.0, typeof( RawChickenLeg ), 1044473, 1, 1044253 ); + SetNeedHeat( index, true ); + SetUseAllRes( index, true ); + + index = AddCraft( typeof( FishSteak ), 1044498, 1022427, 0.0, 100.0, typeof( RawFishSteak ), 1044476, 1, 1044253 ); + SetNeedHeat( index, true ); + SetUseAllRes( index, true ); + + index = AddCraft( typeof( FriedEggs ), 1044498, 1022486, 0.0, 100.0, typeof( Eggs ), 1044477, 1, 1044253 ); + SetNeedHeat( index, true ); + SetUseAllRes( index, true ); + + index = AddCraft( typeof( LambLeg ), 1044498, 1025642, 0.0, 100.0, typeof( RawLambLeg ), 1044478, 1, 1044253 ); + SetNeedHeat( index, true ); + SetUseAllRes( index, true ); + + index = AddCraft( typeof( Ribs ), 1044498, 1022546, 0.0, 100.0, typeof( RawRibs ), 1044485, 1, 1044253 ); + SetNeedHeat( index, true ); + SetUseAllRes( index, true ); + /* End Barbecue */ + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefGlassblowing.cs b/Scripts/Engines/Craft/DefGlassblowing.cs new file mode 100644 index 000000000..d9ed9e656 --- /dev/null +++ b/Scripts/Engines/Craft/DefGlassblowing.cs @@ -0,0 +1,133 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Craft +{ + public class DefGlassblowing : CraftSystem + { + public override SkillName MainSkill + { + get{ return SkillName.Alchemy; } + } + + public override int GumpTitleNumber + { + get{ return 1044622; } //
Glassblowing MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefGlassblowing(); + + return m_CraftSystem; + } + } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private DefGlassblowing() : base( 1, 1, 1.25 )// base( 1, 2, 1.7 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckTool( tool, from ) ) + return 1048146; // If you have a tool equipped, you must use that tool. + else if ( !(from is PlayerMobile && ((PlayerMobile)from).Glassblowing && from.Skills[SkillName.Alchemy].Base >= 100.0) ) + return 1044634; // You havent learned glassblowing. + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + bool anvil, forge; + + DefBlacksmithy.CheckAnvilAndForge( from, 2, out anvil, out forge ); + + if ( forge ) + return 0; + + return 1044628; // You must be near a forge to blow glass. + } + + public override void PlayCraftEffect( Mobile from ) + { + from.PlaySound( 0x2B ); // bellows + + //if ( from.Body.Type == BodyType.Human && !from.Mounted ) + // from.Animate( 9, 5, 1, true, false, 0 ); + + //new InternalTimer( from ).Start(); + } + + // Delay to synchronize the sound with the hit on the anvil + private class InternalTimer : Timer + { + private Mobile m_From; + + public InternalTimer( Mobile from ) : base( TimeSpan.FromSeconds( 0.7 ) ) + { + m_From = from; + } + + protected override void OnTick() + { + m_From.PlaySound( 0x2A ); + } + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + from.PlaySound( 0x41 ); // glass breaking + + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + int index = AddCraft( typeof( Bottle ), 1044050, 1023854, 52.5, 102.5, typeof( Sand ), 1044625, 1, 1044627 ); + SetUseAllRes( index, true ); + + AddCraft( typeof( SmallFlask ), 1044050, 1044610, 52.5, 102.5, typeof( Sand ), 1044625, 2, 1044627 ); + AddCraft( typeof( MediumFlask ), 1044050, 1044611, 52.5, 102.5, typeof( Sand ), 1044625, 3, 1044627 ); + AddCraft( typeof( CurvedFlask ), 1044050, 1044612, 55.0, 105.0, typeof( Sand ), 1044625, 2, 1044627 ); + AddCraft( typeof( LongFlask ), 1044050, 1044613, 57.5, 107.5, typeof( Sand ), 1044625, 4, 1044627 ); + AddCraft( typeof( LargeFlask ), 1044050, 1044623, 60.0, 110.0, typeof( Sand ), 1044625, 5, 1044627 ); + AddCraft( typeof( AniSmallBlueFlask ), 1044050, 1044614, 60.0, 110.0, typeof( Sand ), 1044625, 5, 1044627 ); + AddCraft( typeof( AniLargeVioletFlask ), 1044050, 1044615, 60.0, 110.0, typeof( Sand ), 1044625, 5, 1044627 ); + AddCraft( typeof( AniRedRibbedFlask ), 1044050, 1044624, 60.0, 110.0, typeof( Sand ), 1044625, 7, 1044627 ); + AddCraft( typeof( EmptyVialsWRack ), 1044050, 1044616, 65.0, 115.0, typeof( Sand ), 1044625, 8, 1044627 ); + AddCraft( typeof( FullVialsWRack ), 1044050, 1044617, 65.0, 115.0, typeof( Sand ), 1044625, 9, 1044627 ); + AddCraft( typeof( SpinningHourglass ), 1044050, 1044618, 75.0, 125.0, typeof( Sand ), 1044625, 10, 1044627 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefInscription.cs b/Scripts/Engines/Craft/DefInscription.cs new file mode 100644 index 000000000..26430070b --- /dev/null +++ b/Scripts/Engines/Craft/DefInscription.cs @@ -0,0 +1,308 @@ +using System; +using Server.Items; +using Server.Mobiles; +using Server.Spells; + +namespace Server.Engines.Craft +{ + public class DefInscription : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Inscribe; } + } + + public override int GumpTitleNumber + { + get { return 1044009; } //
INSCRIPTION MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefInscription(); + + return m_CraftSystem; + } + } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private DefInscription() : base( 1, 1, 1.25 )// base( 1, 1, 3.0 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type typeItem ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + if ( typeItem != null ) + { + object o = Activator.CreateInstance( typeItem ); + + if ( o is SpellScroll ) + { + SpellScroll scroll = (SpellScroll)o; + Spellbook book = Spellbook.Find( from, scroll.SpellID ); + + bool hasSpell = ( book != null && book.HasSpell( scroll.SpellID ) ); + + scroll.Delete(); + + return ( hasSpell ? 0 : 1042404 ); // null : You don't have that spell! + } + else if ( o is Item ) + { + ((Item)o).Delete(); + } + } + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + from.PlaySound( 0x249 ); + } + + private static Type typeofSpellScroll = typeof( SpellScroll ); + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( !typeofSpellScroll.IsAssignableFrom( item.ItemType ) ) // not a scroll + { + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + else + { + if ( failed ) + return 501630; // You fail to inscribe the scroll, and the scroll is ruined. + else + return 501629; // You inscribe the spell and put the scroll in your backpack. + } + } + + private int m_Circle, m_Mana; + + private enum Reg{ BlackPearl, Bloodmoss, Garlic, Ginseng, MandrakeRoot, Nightshade, SulfurousAsh, SpidersSilk } + + private Type[] m_RegTypes = new Type[] + { + typeof( BlackPearl ), + typeof( Bloodmoss ), + typeof( Garlic ), + typeof( Ginseng ), + typeof( MandrakeRoot ), + typeof( Nightshade ), + typeof( SulfurousAsh ), + typeof( SpidersSilk ) + }; + + private int m_Index; + + private void AddSpell( Type type, params Reg[] regs ) + { + double minSkill, maxSkill; + + switch ( m_Circle ) + { + default: + case 0: minSkill = -25.0; maxSkill = 25.0; break; + case 1: minSkill = -10.8; maxSkill = 39.2; break; + case 2: minSkill = 03.5; maxSkill = 53.5; break; + case 3: minSkill = 17.8; maxSkill = 67.8; break; + case 4: minSkill = 32.1; maxSkill = 82.1; break; + case 5: minSkill = 46.4; maxSkill = 96.4; break; + case 6: minSkill = 60.7; maxSkill = 110.7; break; + case 7: minSkill = 75.0; maxSkill = 125.0; break; + } + + int index = AddCraft( type, 1044369 + m_Circle, 1044381 + m_Index++, minSkill, maxSkill, m_RegTypes[(int)regs[0]], 1044353 + (int)regs[0], 1, 1044361 + (int)regs[0] ); + + for ( int i = 1; i < regs.Length; ++i ) + AddRes( index, m_RegTypes[(int)regs[i]], 1044353 + (int)regs[i], 1, 1044361 + (int)regs[i] ); + + AddRes( index, typeof( BlankScroll ), 1044377, 1, 1044378 ); + + SetManaReq( index, m_Mana ); + } + + private void AddNecroSpell( int spell, int mana, double minSkill, Type type, params Type[] regs ) + { + int index = AddCraft( type, 1061677, 1060509 + spell, minSkill, minSkill + 1.0, regs[0], 1020000 + CraftItem.ItemIDOf( regs[0] ), 1, 501627 ); //Yes, on OSI it's only 1.0 skill diff'. Don't blame me, blame OSI. + + for ( int i = 1; i < regs.Length; ++i ) + AddRes( index, regs[i], 1020000 + CraftItem.ItemIDOf( regs[i] ), 1, 501627 ); + + AddRes( index, typeof( BlankScroll ), 1044377, 1, 1044378 ); + + SetManaReq( index, mana ); + } + + public override void InitCraftList() + { + m_Circle = 0; + m_Mana = 4; + + AddSpell( typeof( ReactiveArmorScroll ), Reg.Garlic, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( ClumsyScroll ), Reg.Bloodmoss, Reg.Nightshade ); + AddSpell( typeof( CreateFoodScroll ), Reg.Garlic, Reg.Ginseng, Reg.MandrakeRoot ); + AddSpell( typeof( FeeblemindScroll ), Reg.Nightshade, Reg.Ginseng ); + AddSpell( typeof( HealScroll ), Reg.Garlic, Reg.Ginseng, Reg.SpidersSilk ); + AddSpell( typeof( MagicArrowScroll ), Reg.SulfurousAsh ); + AddSpell( typeof( NightSightScroll ), Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( WeakenScroll ), Reg.Garlic, Reg.Nightshade ); + + m_Circle = 1; + m_Mana = 6; + + AddSpell( typeof( AgilityScroll ), Reg.Bloodmoss, Reg.MandrakeRoot ); + AddSpell( typeof( CunningScroll ), Reg.Nightshade, Reg.MandrakeRoot ); + AddSpell( typeof( CureScroll ), Reg.Garlic, Reg.Ginseng ); + AddSpell( typeof( HarmScroll ), Reg.Nightshade, Reg.SpidersSilk ); + AddSpell( typeof( MagicTrapScroll ), Reg.Garlic, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( MagicUnTrapScroll ), Reg.Bloodmoss, Reg.SulfurousAsh ); + AddSpell( typeof( ProtectionScroll ), Reg.Garlic, Reg.Ginseng, Reg.SulfurousAsh ); + AddSpell( typeof( StrengthScroll ), Reg.Nightshade, Reg.MandrakeRoot ); + + m_Circle = 2; + m_Mana = 9; + + AddSpell( typeof( BlessScroll ), Reg.Garlic, Reg.MandrakeRoot ); + AddSpell( typeof( FireballScroll ), Reg.BlackPearl ); + AddSpell( typeof( MagicLockScroll ), Reg.Bloodmoss, Reg.Garlic, Reg.SulfurousAsh ); + AddSpell( typeof( PoisonScroll ), Reg.Nightshade ); + AddSpell( typeof( TelekinisisScroll ), Reg.Garlic, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( TeleportScroll ), Reg.Bloodmoss, Reg.MandrakeRoot ); + AddSpell( typeof( UnlockScroll ), Reg.Bloodmoss, Reg.SulfurousAsh ); + AddSpell( typeof( WallOfStoneScroll ), Reg.Bloodmoss, Reg.Garlic ); + + m_Circle = 3; + m_Mana = 11; + + AddSpell( typeof( ArchCureScroll ), Reg.Garlic, Reg.Ginseng, Reg.MandrakeRoot ); + AddSpell( typeof( ArchProtectionScroll ), Reg.Garlic, Reg.Ginseng, Reg.MandrakeRoot, Reg.SulfurousAsh ); + AddSpell( typeof( CurseScroll ), Reg.Garlic, Reg.Nightshade, Reg.SulfurousAsh ); + AddSpell( typeof( FireFieldScroll ), Reg.BlackPearl, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( GreaterHealScroll ), Reg.Garlic, Reg.SpidersSilk, Reg.MandrakeRoot, Reg.Ginseng ); + AddSpell( typeof( LightningScroll ), Reg.MandrakeRoot, Reg.SulfurousAsh ); + AddSpell( typeof( ManaDrainScroll ), Reg.BlackPearl, Reg.SpidersSilk, Reg.MandrakeRoot ); + AddSpell( typeof( RecallScroll ), Reg.BlackPearl, Reg.Bloodmoss, Reg.MandrakeRoot ); + + m_Circle = 4; + m_Mana = 14; + + AddSpell( typeof( BladeSpiritsScroll ), Reg.BlackPearl, Reg.Nightshade, Reg.MandrakeRoot ); + AddSpell( typeof( DispelFieldScroll ), Reg.BlackPearl, Reg.Garlic, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( IncognitoScroll ), Reg.Bloodmoss, Reg.Garlic, Reg.Nightshade ); + AddSpell( typeof( MagicReflectScroll ), Reg.Garlic, Reg.MandrakeRoot, Reg.SpidersSilk ); + AddSpell( typeof( MindBlastScroll ), Reg.BlackPearl, Reg.MandrakeRoot, Reg.Nightshade, Reg.SulfurousAsh ); + AddSpell( typeof( ParalyzeScroll ), Reg.Garlic, Reg.MandrakeRoot, Reg.SpidersSilk ); + AddSpell( typeof( PoisonFieldScroll ), Reg.BlackPearl, Reg.Nightshade, Reg.SpidersSilk ); + AddSpell( typeof( SummonCreatureScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk ); + + m_Circle = 5; + m_Mana = 20; + + AddSpell( typeof( DispelScroll ), Reg.Garlic, Reg.MandrakeRoot, Reg.SulfurousAsh ); + AddSpell( typeof( EnergyBoltScroll ), Reg.BlackPearl, Reg.Nightshade ); + AddSpell( typeof( ExplosionScroll ), Reg.Bloodmoss, Reg.MandrakeRoot ); + AddSpell( typeof( InvisibilityScroll ), Reg.Bloodmoss, Reg.Nightshade ); + AddSpell( typeof( MarkScroll ), Reg.Bloodmoss, Reg.BlackPearl, Reg.MandrakeRoot ); + AddSpell( typeof( MassCurseScroll ), Reg.Garlic, Reg.MandrakeRoot, Reg.Nightshade, Reg.SulfurousAsh ); + AddSpell( typeof( ParalyzeFieldScroll ), Reg.BlackPearl, Reg.Ginseng, Reg.SpidersSilk ); + AddSpell( typeof( RevealScroll ), Reg.Bloodmoss, Reg.SulfurousAsh ); + + m_Circle = 6; + m_Mana = 40; + + AddSpell( typeof( ChainLightningScroll ), Reg.BlackPearl, Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SulfurousAsh ); + AddSpell( typeof( EnergyFieldScroll ), Reg.BlackPearl, Reg.MandrakeRoot, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( FlamestrikeScroll ), Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( GateTravelScroll ), Reg.BlackPearl, Reg.MandrakeRoot, Reg.SulfurousAsh ); + AddSpell( typeof( ManaVampireScroll ), Reg.BlackPearl, Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk ); + AddSpell( typeof( MassDispelScroll ), Reg.BlackPearl, Reg.Garlic, Reg.MandrakeRoot, Reg.SulfurousAsh ); + AddSpell( typeof( MeteorSwarmScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SulfurousAsh, Reg.SpidersSilk ); + AddSpell( typeof( PolymorphScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk ); + + m_Circle = 7; + m_Mana = 50; + + AddSpell( typeof( EarthquakeScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.Ginseng, Reg.SulfurousAsh ); + AddSpell( typeof( EnergyVortexScroll ), Reg.BlackPearl, Reg.Bloodmoss, Reg.MandrakeRoot, Reg.Nightshade ); + AddSpell( typeof( ResurrectionScroll ), Reg.Bloodmoss, Reg.Garlic, Reg.Ginseng ); + AddSpell( typeof( SummonAirElementalScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk ); + AddSpell( typeof( SummonDaemonScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( SummonEarthElementalScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk ); + AddSpell( typeof( SummonFireElementalScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk, Reg.SulfurousAsh ); + AddSpell( typeof( SummonWaterElementalScroll ), Reg.Bloodmoss, Reg.MandrakeRoot, Reg.SpidersSilk ); + + if ( Core.SE ) + { + AddNecroSpell( 0, 23, 39.6, typeof( AnimateDeadScroll ), Reagent.GraveDust, Reagent.DaemonBlood ); + AddNecroSpell( 1, 13, 19.6, typeof( BloodOathScroll ), Reagent.DaemonBlood ); + AddNecroSpell( 2, 11, 19.6, typeof( CorpseSkinScroll ), Reagent.BatWing, Reagent.GraveDust ); + AddNecroSpell( 3, 7, 19.6, typeof( CurseWeaponScroll ), Reagent.PigIron ); + AddNecroSpell( 4, 11, 19.6, typeof( EvilOmenScroll ), Reagent.BatWing, Reagent.NoxCrystal ); + AddNecroSpell( 5, 11, 39.6, typeof( HorrificBeastScroll ), Reagent.BatWing, Reagent.DaemonBlood ); + AddNecroSpell( 6, 23, 69.6, typeof( LichFormScroll ), Reagent.GraveDust, Reagent.DaemonBlood, Reagent.NoxCrystal ); + AddNecroSpell( 7, 17, 29.6, typeof( MindRotScroll ), Reagent.BatWing, Reagent.DaemonBlood, Reagent.PigIron ); + AddNecroSpell( 8, 5, 19.6, typeof( PainSpikeScroll ), Reagent.GraveDust, Reagent.PigIron ); + AddNecroSpell( 9, 17, 49.6, typeof( PoisonStrikeScroll ), Reagent.NoxCrystal ); + AddNecroSpell( 10, 29, 64.6, typeof( StrangleScroll ), Reagent.DaemonBlood, Reagent.NoxCrystal ); + AddNecroSpell( 11, 17, 29.6, typeof( SummonFamiliarScroll ), Reagent.BatWing, Reagent.GraveDust, Reagent.DaemonBlood ); + AddNecroSpell( 12, 23, 98.6, typeof( VampiricEmbraceScroll ), Reagent.BatWing, Reagent.NoxCrystal, Reagent.PigIron ); + AddNecroSpell( 13, 41, 79.6, typeof( VengefulSpiritScroll ), Reagent.BatWing, Reagent.GraveDust, Reagent.PigIron ); + AddNecroSpell( 14, 23, 59.6, typeof( WitherScroll ), Reagent.GraveDust, Reagent.NoxCrystal, Reagent.PigIron ); + AddNecroSpell( 15, 17, 79.6, typeof( WraithFormScroll ), Reagent.NoxCrystal, Reagent.PigIron ); + AddNecroSpell( 16, 40, 79.6, typeof( ExorcismScroll ), Reagent.NoxCrystal, Reagent.GraveDust ); + } + + // Runebook + int index = AddCraft( typeof( Runebook ), 1044294, 1041267, 45.0, 95.0, typeof( BlankScroll ), 1044377, 8, 1044378 ); + AddRes( index, typeof( RecallScroll ), 1044445, 1, 1044253 ); + AddRes( index, typeof( GateTravelScroll ), 1044446, 1, 1044253 ); + + if ( Core.AOS ) + { + // Bulk order book + AddCraft( typeof( Engines.BulkOrders.BulkOrderBook ), 1044294, 1028793, 65.0, 115.0, typeof( BlankScroll ), 1044377, 10, 1044378 ); + } + + if ( Core.SE ) + AddCraft( typeof( Spellbook ), 1044294, 1023834, 50.0, 150.0, typeof( BlankScroll ), 1044377, 10, 1044378 ); + + MarkOption = true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefMasonry.cs b/Scripts/Engines/Craft/DefMasonry.cs new file mode 100644 index 000000000..6c1674288 --- /dev/null +++ b/Scripts/Engines/Craft/DefMasonry.cs @@ -0,0 +1,150 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Craft +{ + public class DefMasonry : CraftSystem + { + public override SkillName MainSkill + { + get{ return SkillName.Carpentry; } + } + + public override int GumpTitleNumber + { + get{ return 1044500; } //
MASONRY MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefMasonry(); + + return m_CraftSystem; + } + } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.0; // 0% + } + + private DefMasonry() : base( 1, 1, 1.25 )// base( 1, 2, 1.7 ) + { + } + + public override bool RetainsColorFrom( CraftItem item, Type type ) + { + return true; + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckTool( tool, from ) ) + return 1048146; // If you have a tool equipped, you must use that tool. + else if ( !(from is PlayerMobile && ((PlayerMobile)from).Masonry && from.Skills[SkillName.Carpentry].Base >= 100.0) ) + return 1044633; // You havent learned stonecraft. + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + // no effects + //if ( from.Body.Type == BodyType.Human && !from.Mounted ) + // from.Animate( 9, 5, 1, true, false, 0 ); + //new InternalTimer( from ).Start(); + } + + // Delay to synchronize the sound with the hit on the anvil + private class InternalTimer : Timer + { + private Mobile m_From; + + public InternalTimer( Mobile from ) : base( TimeSpan.FromSeconds( 0.7 ) ) + { + m_From = from; + } + + protected override void OnTick() + { + m_From.PlaySound( 0x23D ); + } + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + // Decorations + AddCraft( typeof( Vase ), 1044501, 1022888, 52.5, 102.5, typeof( Granite ), 1044514, 1, 1044513 ); + AddCraft( typeof( LargeVase ), 1044501, 1022887, 52.5, 102.5, typeof( Granite ), 1044514, 3, 1044513 ); + + if( Core.SE ) + { + int index = AddCraft( typeof( SmallUrn ), 1044501, 1029244, 82.0, 132.0, typeof( Granite ), 1044514, 3, 1044513 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( SmallTowerSculpture ), 1044501, 1029242, 82.0, 132.0, typeof( Granite ), 1044514, 3, 1044513 ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Furniture + AddCraft( typeof( StoneChair ), 1044502, 1024635, 55.0, 105.0, typeof( Granite ), 1044514, 4, 1044513 ); + AddCraft( typeof( MediumStoneTableEastDeed ), 1044502, 1044508, 65.0, 115.0, typeof( Granite ), 1044514, 6, 1044513 ); + AddCraft( typeof( MediumStoneTableSouthDeed ), 1044502, 1044509, 65.0, 115.0, typeof( Granite ), 1044514, 6, 1044513 ); + AddCraft( typeof( LargeStoneTableEastDeed ), 1044502, 1044511, 75.0, 125.0, typeof( Granite ), 1044514, 9, 1044513 ); + AddCraft( typeof( LargeStoneTableSouthDeed ), 1044502, 1044512, 75.0, 125.0, typeof( Granite ), 1044514, 9, 1044513 ); + + // Statues + AddCraft( typeof( StatueSouth ), 1044503, 1044505, 60.0, 120.0, typeof( Granite ), 1044514, 3, 1044513 ); + AddCraft( typeof( StatueNorth ), 1044503, 1044506, 60.0, 120.0, typeof( Granite ), 1044514, 3, 1044513 ); + AddCraft( typeof( StatueEast ), 1044503, 1044507, 60.0, 120.0, typeof( Granite ), 1044514, 3, 1044513 ); + AddCraft( typeof( StatuePegasus ), 1044503, 1044510, 70.0, 130.0, typeof( Granite ), 1044514, 4, 1044513 ); + + SetSubRes( typeof( Granite ), 1044525 ); + + AddSubRes( typeof( Granite ), 1044525, 00.0, 1044514, 1044526 ); + AddSubRes( typeof( DullCopperGranite ), 1044023, 65.0, 1044514, 1044527 ); + AddSubRes( typeof( ShadowIronGranite ), 1044024, 70.0, 1044514, 1044527 ); + AddSubRes( typeof( CopperGranite ), 1044025, 75.0, 1044514, 1044527 ); + AddSubRes( typeof( BronzeGranite ), 1044026, 80.0, 1044514, 1044527 ); + AddSubRes( typeof( GoldGranite ), 1044027, 85.0, 1044514, 1044527 ); + AddSubRes( typeof( AgapiteGranite ), 1044028, 90.0, 1044514, 1044527 ); + AddSubRes( typeof( VeriteGranite ), 1044029, 95.0, 1044514, 1044527 ); + AddSubRes( typeof( ValoriteGranite ), 1044030, 99.0, 1044514, 1044527 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefTailoring.cs b/Scripts/Engines/Craft/DefTailoring.cs new file mode 100644 index 000000000..73477063d --- /dev/null +++ b/Scripts/Engines/Craft/DefTailoring.cs @@ -0,0 +1,324 @@ +using System; +using Server.Items; + +namespace Server.Engines.Craft +{ + public class DefTailoring : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Tailoring; } + } + + public override int GumpTitleNumber + { + get { return 1044005; } //
TAILORING MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefTailoring(); + + return m_CraftSystem; + } + } + + public override CraftECA ECA{ get{ return CraftECA.ChanceMinusSixtyToFourtyFive; } } + + public override double GetChanceAtMin( CraftItem item ) + { + return 0.5; // 50% + } + + private DefTailoring() : base( 1, 1, 1.25 )// base( 1, 1, 4.5 ) + { + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + from.PlaySound( 0x248 ); + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override void InitCraftList() + { + int index = -1; + + #region Hats + AddCraft( typeof( SkullCap ), 1011375, 1025444, 0.0, 25.0, typeof( Cloth ), 1044286, 2, 1044287 ); + AddCraft( typeof( Bandana ), 1011375, 1025440, 0.0, 25.0, typeof( Cloth ), 1044286, 2, 1044287 ); + AddCraft( typeof( FloppyHat ), 1011375, 1025907, 6.2, 31.2, typeof( Cloth ), 1044286, 11, 1044287 ); + AddCraft( typeof( Cap ), 1011375, 1025909, -18.8, 6.2, typeof( Cloth ), 1044286, 11, 1044287 ); + AddCraft( typeof( WideBrimHat ), 1011375, 1025908, 6.2, 31.2, typeof( Cloth ), 1044286, 12, 1044287 ); + AddCraft( typeof( StrawHat ), 1011375, 1025911, 6.2, 31.2, typeof( Cloth ), 1044286, 10, 1044287 ); + AddCraft( typeof( TallStrawHat ), 1011375, 1025910, 6.7, 31.7, typeof( Cloth ), 1044286, 13, 1044287 ); + AddCraft( typeof( WizardsHat ), 1011375, 1025912, 7.2, 32.2, typeof( Cloth ), 1044286, 15, 1044287 ); + AddCraft( typeof( Bonnet ), 1011375, 1025913, 6.2, 31.2, typeof( Cloth ), 1044286, 11, 1044287 ); + AddCraft( typeof( FeatheredHat ), 1011375, 1025914, 6.2, 31.2, typeof( Cloth ), 1044286, 12, 1044287 ); + AddCraft( typeof( TricorneHat ), 1011375, 1025915, 6.2, 31.2, typeof( Cloth ), 1044286, 12, 1044287 ); + AddCraft( typeof( JesterHat ), 1011375, 1025916, 7.2, 32.2, typeof( Cloth ), 1044286, 15, 1044287 ); + + if ( Core.AOS ) + AddCraft( typeof( FlowerGarland ), 1011375, 1028965, 10.0, 35.0, typeof( Cloth ), 1044286, 5, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( ClothNinjaHood ), 1011375, 1030202, 80.0, 105.0, typeof( Cloth ), 1044286, 13, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( Kasa ), 1011375, 1030211, 60.0, 85.0, typeof( Cloth ), 1044286, 12, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + #endregion + + #region Shirts + AddCraft( typeof( Doublet ), 1015269, 1028059, 0, 25.0, typeof( Cloth ), 1044286, 8, 1044287 ); + AddCraft( typeof( Shirt ), 1015269, 1025399, 20.7, 45.7, typeof( Cloth ), 1044286, 8, 1044287 ); + AddCraft( typeof( FancyShirt ), 1015269, 1027933, 24.8, 49.8, typeof( Cloth ), 1044286, 8, 1044287 ); + AddCraft( typeof( Tunic ), 1015269, 1028097, 00.0, 25.0, typeof( Cloth ), 1044286, 12, 1044287 ); + AddCraft( typeof( Surcoat ), 1015269, 1028189, 8.2, 33.2, typeof( Cloth ), 1044286, 14, 1044287 ); + AddCraft( typeof( PlainDress ), 1015269, 1027937, 12.4, 37.4, typeof( Cloth ), 1044286, 10, 1044287 ); + AddCraft( typeof( FancyDress ), 1015269, 1027935, 33.1, 58.1, typeof( Cloth ), 1044286, 12, 1044287 ); + AddCraft( typeof( Cloak ), 1015269, 1025397, 41.4, 66.4, typeof( Cloth ), 1044286, 14, 1044287 ); + AddCraft( typeof( Robe ), 1015269, 1027939, 53.9, 78.9, typeof( Cloth ), 1044286, 16, 1044287 ); + AddCraft( typeof( JesterSuit ), 1015269, 1028095, 8.2, 33.2, typeof( Cloth ), 1044286, 24, 1044287 ); + + if ( Core.AOS ) + { + AddCraft( typeof( FurCape ), 1015269, 1028969, 35.0, 60.0, typeof( Cloth ), 1044286, 13, 1044287 ); + AddCraft( typeof( GildedDress ), 1015269, 1028973, 37.5, 62.5, typeof( Cloth ), 1044286, 16, 1044287 ); + AddCraft( typeof( FormalShirt ), 1015269, 1028975, 26.0, 51.0, typeof( Cloth ), 1044286, 16, 1044287 ); + } + + if( Core.SE ) + { + index = AddCraft( typeof( ClothNinjaJacket ), 1015269, 1030207, 75.0, 100.0, typeof( Cloth ), 1044286, 12, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( Kamishimo ), 1015269, 1030212, 75.0, 100.0, typeof( Cloth ), 1044286, 15, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( HakamaShita ), 1015269, 1030215, 40.0, 65.0, typeof( Cloth ), 1044286, 14, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( MaleKimono ), 1015269, 1030189, 50.0, 75.0, typeof( Cloth ), 1044286, 16, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( FemaleKimono ), 1015269, 1030190, 50.0, 75.0, typeof( Cloth ), 1044286, 16, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( JinBaori ), 1015269, 1030220, 30.0, 55.0, typeof( Cloth ), 1044286, 12, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + + // Pants + #endregion + + #region Pants + AddCraft( typeof( ShortPants ), 1015279, 1025422, 24.8, 49.8, typeof( Cloth ), 1044286, 6, 1044287 ); + AddCraft( typeof( LongPants ), 1015279, 1025433, 24.8, 49.8, typeof( Cloth ), 1044286, 8, 1044287 ); + AddCraft( typeof( Kilt ), 1015279, 1025431, 20.7, 45.7, typeof( Cloth ), 1044286, 8, 1044287 ); + AddCraft( typeof( Skirt ), 1015279, 1025398, 29.0, 54.0, typeof( Cloth ), 1044286, 10, 1044287 ); + + if ( Core.AOS ) + AddCraft( typeof( FurSarong ), 1015279, 1028971, 35.0, 60.0, typeof( Cloth ), 1044286, 12, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( Hakama ), 1015279, 1030213, 50.0, 75.0, typeof( Cloth ), 1044286, 16, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( TattsukeHakama ), 1015279, 1030214, 50.0, 75.0, typeof( Cloth ), 1044286, 16, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + + #endregion + + #region Misc + AddCraft( typeof( BodySash ), 1015283, 1025441, 4.1, 29.1, typeof( Cloth ), 1044286, 4, 1044287 ); + AddCraft( typeof( HalfApron ), 1015283, 1025435, 20.7, 45.7, typeof( Cloth ), 1044286, 6, 1044287 ); + AddCraft( typeof( FullApron ), 1015283, 1025437, 29.0, 54.0, typeof( Cloth ), 1044286, 10, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( Obi ), 1015283, 1030219, 20.0, 45.0, typeof( Cloth ), 1044286, 6, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + + AddCraft( typeof( OilCloth ), 1015283, 1041498, 74.6, 99.6, typeof( Cloth ), 1044286, 1, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( GozaMatEastDeed ), 1015283, 1030404, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( GozaMatSouthDeed ), 1015283, 1030405, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( SquareGozaMatEastDeed ), 1015283, 1030407, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( SquareGozaMatSouthDeed ), 1015283, 1030406, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( BrocadeGozaMatEastDeed ), 1015283, 1030408, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( BrocadeGozaMatSouthDeed ), 1015283, 1030409, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( BrocadeSquareGozaMatEastDeed ), 1015283, 1030411, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( BrocadeSquareGozaMatSouthDeed ), 1015283, 1030410, 55.0, 80.0, typeof( Cloth ), 1044286, 25, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + + #endregion + + #region Footwear + if ( Core.AOS ) + AddCraft( typeof( FurBoots ), 1015288, 1028967, 50.0, 75.0, typeof( Cloth ), 1044286, 12, 1044287 ); + + if( Core.SE ) + { + index = AddCraft( typeof( NinjaTabi ), 1015288, 1030210, 70.0, 95.0, typeof( Cloth ), 1044286, 10, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( SamuraiTabi ), 1015288, 1030209, 20.0, 45.0, typeof( Cloth ), 1044286, 6, 1044287 ); + SetNeededExpansion( index, Expansion.SE ); + } + + AddCraft( typeof( Sandals ), 1015288, 1025901, 12.4, 37.4, typeof( Leather ), 1044462, 4, 1044463 ); + AddCraft( typeof( Shoes ), 1015288, 1025904, 16.5, 41.5, typeof( Leather ), 1044462, 6, 1044463 ); + AddCraft( typeof( Boots ), 1015288, 1025899, 33.1, 58.1, typeof( Leather ), 1044462, 8, 1044463 ); + AddCraft( typeof( ThighBoots ), 1015288, 1025906, 41.4, 66.4, typeof( Leather ), 1044462, 10, 1044463 ); + #endregion + + #region Leather Armor + + AddCraft( typeof( LeatherGorget ), 1015293, 1025063, 53.9, 78.9, typeof( Leather ), 1044462, 4, 1044463 ); + AddCraft( typeof( LeatherCap ), 1015293, 1027609, 6.2, 31.2, typeof( Leather ), 1044462, 2, 1044463 ); + AddCraft( typeof( LeatherGloves ), 1015293, 1025062, 51.8, 76.8, typeof( Leather ), 1044462, 3, 1044463 ); + AddCraft( typeof( LeatherArms ), 1015293, 1025061, 53.9, 78.9, typeof( Leather ), 1044462, 8, 1044463 ); + AddCraft( typeof( LeatherLegs ), 1015293, 1025067, 66.3, 91.3, typeof( Leather ), 1044462, 10, 1044463 ); + AddCraft( typeof( LeatherChest ), 1015293, 1025068, 70.5, 95.5, typeof( Leather ), 1044462, 12, 1044463 ); + + if( Core.SE ) + { + index = AddCraft( typeof( LeatherJingasa ), 1015293, 1030177, 45.0, 70.0, typeof( Leather ), 1044462, 4, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherMempo ), 1015293, 1030181, 80.0, 105.0, typeof( Leather ), 1044462, 8, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherDo ), 1015293, 1030182, 75.0, 100.0, typeof( Leather ), 1044462, 12, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherHiroSode ), 1015293, 1030185, 55.0, 80.0, typeof( Leather ), 1044462, 5, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherSuneate ), 1015293, 1030193, 68.0, 93.0, typeof( Leather ), 1044462, 12, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherHaidate ), 1015293, 1030197, 68.0, 93.0, typeof( Leather ), 1044462, 12, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherNinjaPants ), 1015293, 1030204, 80.0, 105.0, typeof( Leather ), 1044462, 13, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherNinjaJacket ), 1015293, 1030206, 85.0, 110.0, typeof( Leather ), 1044462, 13, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherNinjaBelt ), 1015293, 1030203, 50.0, 75.0, typeof( Leather ), 1044462, 15, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherNinjaMitts ), 1015293, 1030205, 65.0, 90.0, typeof( Leather ), 1044462, 12, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( LeatherNinjaHood ), 1015293, 1030201, 90.0, 115.0, typeof( Leather ), 1044462, 14, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + } + + #endregion + + #region Studded Armor + AddCraft( typeof( StuddedGorget ), 1015300, 1025078, 78.8, 103.8, typeof( Leather ), 1044462, 6, 1044463 ); + AddCraft( typeof( StuddedGloves ), 1015300, 1025077, 82.9, 107.9, typeof( Leather ), 1044462, 8, 1044463 ); + AddCraft( typeof( StuddedArms ), 1015300, 1025076, 87.1, 112.1, typeof( Leather ), 1044462, 10, 1044463 ); + AddCraft( typeof( StuddedLegs ), 1015300, 1025082, 91.2, 116.2, typeof( Leather ), 1044462, 12, 1044463 ); + AddCraft( typeof( StuddedChest ), 1015300, 1025083, 94.0, 119.0, typeof( Leather ), 1044462, 14, 1044463 ); + + if( Core.SE ) + { + index = AddCraft( typeof( StuddedMempo ), 1015300, 1030216, 80.0, 105.0, typeof( Leather ), 1044462, 8, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( StuddedDo ), 1015300, 1030183, 95.0, 120.0, typeof( Leather ), 1044462, 14, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( StuddedHiroSode ), 1015300, 1030186, 85.0, 110.0, typeof( Leather ), 1044462, 8, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( StuddedSuneate ), 1015300, 1030194, 92.0, 117.0, typeof( Leather ), 1044462, 14, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + index = AddCraft( typeof( StuddedHaidate ), 1015300, 1030198, 92.0, 117.0, typeof( Leather ), 1044462, 14, 1044463 ); + SetNeededExpansion( index, Expansion.SE ); + } + + #endregion + + #region Female Armor + AddCraft( typeof( LeatherShorts ), 1015306, 1027168, 62.2, 87.2, typeof( Leather ), 1044462, 8, 1044463 ); + AddCraft( typeof( LeatherSkirt ), 1015306, 1027176, 58.0, 83.0, typeof( Leather ), 1044462, 6, 1044463 ); + AddCraft( typeof( LeatherBustierArms ), 1015306, 1027178, 58.0, 83.0, typeof( Leather ), 1044462, 6, 1044463 ); + AddCraft( typeof( StuddedBustierArms ), 1015306, 1027180, 82.9, 107.9, typeof( Leather ), 1044462, 8, 1044463 ); + AddCraft( typeof( FemaleLeatherChest ), 1015306, 1027174, 62.2, 87.2, typeof( Leather ), 1044462, 8, 1044463 ); + AddCraft( typeof( FemaleStuddedChest ), 1015306, 1027170, 87.1, 112.1, typeof( Leather ), 1044462, 10, 1044463 ); + #endregion + + #region Bone Armor + index = AddCraft( typeof( BoneHelm ), 1049149, 1025206, 85.0, 110.0, typeof( Leather ), 1044462, 4, 1044463 ); + AddRes( index, typeof( Bone ), 1049064, 2, 1049063 ); + + index = AddCraft( typeof( BoneGloves ), 1049149, 1025205, 89.0, 114.0, typeof( Leather ), 1044462, 6, 1044463 ); + AddRes( index, typeof( Bone ), 1049064, 2, 1049063 ); + + index = AddCraft( typeof( BoneArms ), 1049149, 1025203, 92.0, 117.0, typeof( Leather ), 1044462, 8, 1044463 ); + AddRes( index, typeof( Bone ), 1049064, 4, 1049063 ); + + index = AddCraft( typeof( BoneLegs ), 1049149, 1025202, 95.0, 120.0, typeof( Leather ), 1044462, 10, 1044463 ); + AddRes( index, typeof( Bone ), 1049064, 6, 1049063 ); + + index = AddCraft( typeof( BoneChest ), 1049149, 1025199, 96.0, 121.0, typeof( Leather ), 1044462, 12, 1044463 ); + AddRes( index, typeof( Bone ), 1049064, 10, 1049063 ); + #endregion + + // Set the overridable material + SetSubRes( typeof( Leather ), 1049150 ); + + // Add every material you want the player to be able to choose from + // This will override the overridable material + AddSubRes( typeof( Leather ), 1049150, 00.0, 1044462, 1049311 ); + AddSubRes( typeof( SpinedLeather ), 1049151, 65.0, 1044462, 1049311 ); + AddSubRes( typeof( HornedLeather ), 1049152, 80.0, 1044462, 1049311 ); + AddSubRes( typeof( BarbedLeather ), 1049153, 99.0, 1044462, 1049311 ); + + MarkOption = true; + Repair = Core.AOS; + CanEnhance = Core.AOS; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Craft/DefTinkering.cs b/Scripts/Engines/Craft/DefTinkering.cs new file mode 100644 index 000000000..7a7092238 --- /dev/null +++ b/Scripts/Engines/Craft/DefTinkering.cs @@ -0,0 +1,493 @@ +using System; +using Server; +using Server.Items; +using Server.Factions; +using Server.Targeting; + +namespace Server.Engines.Craft +{ + public class DefTinkering : CraftSystem + { + public override SkillName MainSkill + { + get { return SkillName.Tinkering; } + } + + public override int GumpTitleNumber + { + get { return 1044007; } //
TINKERING MENU
+ } + + private static CraftSystem m_CraftSystem; + + public static CraftSystem CraftSystem + { + get + { + if ( m_CraftSystem == null ) + m_CraftSystem = new DefTinkering(); + + return m_CraftSystem; + } + } + + private DefTinkering() : base( 1, 1, 1.25 )// base( 1, 1, 3.0 ) + { + } + + public override double GetChanceAtMin( CraftItem item ) + { + if ( item.NameNumber == 1044258 || item.NameNumber == 1046445 ) // potion keg and faction trap removal kit + return 0.5; // 50% + + return 0.0; // 0% + } + + public override int CanCraft( Mobile from, BaseTool tool, Type itemType ) + { + if( tool == null || tool.Deleted || tool.UsesRemaining < 0 ) + return 1044038; // You have worn out your tool! + else if ( !BaseTool.CheckAccessible( tool, from ) ) + return 1044263; // The tool must be on your person to use. + else if ( itemType != null && ( itemType.IsSubclassOf( typeof( BaseFactionTrapDeed ) ) || itemType == typeof( FactionTrapRemovalKit ) ) && Faction.Find( from ) == null ) + return 1044573; // You have to be in a faction to do that. + + return 0; + } + + public override void PlayCraftEffect( Mobile from ) + { + // no sound + //from.PlaySound( 0x241 ); + } + + private static Type[] m_TinkerColorables = new Type[] + { + typeof( ForkLeft ), typeof( ForkRight ), + typeof( SpoonLeft ), typeof( SpoonRight ), + typeof( KnifeLeft ), typeof( KnifeRight ), + typeof( Plate ), + typeof( Goblet ), typeof( PewterMug ), + typeof( KeyRing ), + typeof( Candelabra ), typeof( Scales ), + typeof( Key ), typeof( Globe ), + typeof( Spyglass ), typeof( Lantern ), + typeof( HeatingStand ) + }; + + public override bool RetainsColorFrom( CraftItem item, Type type ) + { + if ( !type.IsSubclassOf( typeof( BaseIngot ) ) ) + return false; + + type = item.ItemType; + + bool contains = false; + + for ( int i = 0; !contains && i < m_TinkerColorables.Length; ++i ) + contains = ( m_TinkerColorables[i] == type ); + + return contains; + } + + public override int PlayEndingEffect( Mobile from, bool failed, bool lostMaterial, bool toolBroken, int quality, bool makersMark, CraftItem item ) + { + if ( toolBroken ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool + + if ( failed ) + { + if ( lostMaterial ) + return 1044043; // You failed to create the item, and some of your materials are lost. + else + return 1044157; // You failed to create the item, but no materials were lost. + } + else + { + if ( quality == 0 ) + return 502785; // You were barely able to make this item. It's quality is below average. + else if ( makersMark && quality == 2 ) + return 1044156; // You create an exceptional quality item and affix your maker's mark. + else if ( quality == 2 ) + return 1044155; // You create an exceptional quality item. + else + return 1044154; // You create the item. + } + } + + public override bool ConsumeOnFailure( Mobile from, Type resourceType, CraftItem craftItem ) + { + if ( resourceType == typeof( Silver ) ) + return false; + + return base.ConsumeOnFailure( from, resourceType, craftItem ); + } + + public void AddJewelrySet( GemType gemType, Type itemType ) + { + int offset = (int)gemType - 1; + + int index = AddCraft( typeof( GoldRing ), 1044049, 1044176 + offset, 40.0, 90.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddRes( index, itemType, 1044231 + offset, 1, 1044240 ); + + index = AddCraft( typeof( SilverBeadNecklace ), 1044049, 1044185 + offset, 40.0, 90.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddRes( index, itemType, 1044231 + offset, 1, 1044240 ); + + index = AddCraft( typeof( GoldNecklace ), 1044049, 1044194 + offset, 40.0, 90.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddRes( index, itemType, 1044231 + offset, 1, 1044240 ); + + index = AddCraft( typeof( GoldEarrings ), 1044049, 1044203 + offset, 40.0, 90.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddRes( index, itemType, 1044231 + offset, 1, 1044240 ); + + index = AddCraft( typeof( GoldBeadNecklace ), 1044049, 1044212 + offset, 40.0, 90.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddRes( index, itemType, 1044231 + offset, 1, 1044240 ); + + index = AddCraft( typeof( GoldBracelet ), 1044049, 1044221 + offset, 40.0, 90.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddRes( index, itemType, 1044231 + offset, 1, 1044240 ); + } + + public override void InitCraftList() + { + int index = -1; + + #region Wooden Items + AddCraft( typeof( JointingPlane ), 1044042, 1024144, 0.0, 50.0, typeof( Log ), 1044041, 4, 1044351 ); + AddCraft( typeof( MouldingPlane ), 1044042, 1024140, 0.0, 50.0, typeof( Log ), 1044041, 4, 1044351 ); + AddCraft( typeof( SmoothingPlane ), 1044042, 1024146, 0.0, 50.0, typeof( Log ), 1044041, 4, 1044351 ); + AddCraft( typeof( ClockFrame ), 1044042, 1024173, 0.0, 50.0, typeof( Log ), 1044041, 6, 1044351 ); + AddCraft( typeof( Axle ), 1044042, 1024187, -25.0, 25.0, typeof( Log ), 1044041, 2, 1044351 ); + AddCraft( typeof( RollingPin ), 1044042, 1024163, 0.0, 50.0, typeof( Log ), 1044041, 5, 1044351 ); + + if( Core.SE ) + { + index = AddCraft( typeof( Nunchaku ), 1044042, 1030158, 70.0, 120.0, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddRes( index, typeof( Log ), 1044041, 8, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + } + #endregion + + #region Tools + AddCraft( typeof( Scissors ), 1044046, 1023998, 5.0, 55.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( MortarPestle ), 1044046, 1023739, 20.0, 70.0, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddCraft( typeof( Scorp ), 1044046, 1024327, 30.0, 80.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( TinkerTools ), 1044046, 1044164, 10.0, 60.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( Hatchet ), 1044046, 1023907, 30.0, 80.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( DrawKnife ), 1044046, 1024324, 30.0, 80.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( SewingKit ), 1044046, 1023997, 10.0, 70.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( Saw ), 1044046, 1024148, 30.0, 80.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( DovetailSaw ), 1044046, 1024136, 30.0, 80.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Froe ), 1044046, 1024325, 30.0, 80.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( Shovel ), 1044046, 1023898, 40.0, 90.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Hammer ), 1044046, 1024138, 30.0, 80.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( Tongs ), 1044046, 1024028, 35.0, 85.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( SmithHammer ), 1044046, 1025091, 40.0, 90.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( SledgeHammer ), 1044046, 1024021, 40.0, 90.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Inshave ), 1044046, 1024326, 30.0, 80.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( Pickaxe ), 1044046, 1023718, 40.0, 90.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Lockpick ), 1044046, 1025371, 45.0, 95.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( Skillet ), 1044046, 1044567, 30.0, 80.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( FlourSifter ), 1044046, 1024158, 50.0, 100.0, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddCraft( typeof( FletcherTools ), 1044046, 1044166, 35.0, 85.0, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddCraft( typeof( MapmakersPen ), 1044046, 1044167, 25.0, 75.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( ScribesPen ), 1044046, 1044168, 25.0, 75.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + #endregion + + #region Parts + AddCraft( typeof( Gears ), 1044047, 1024179, 5.0, 55.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( ClockParts ), 1044047, 1024175, 25.0, 75.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( BarrelTap ), 1044047, 1024100, 35.0, 85.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( Springs ), 1044047, 1024189, 5.0, 55.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( SextantParts ), 1044047, 1024185, 30.0, 80.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( BarrelHoops ), 1044047, 1024321, -15.0, 35.0, typeof( IronIngot ), 1044036, 5, 1044037 ); + AddCraft( typeof( Hinge ), 1044047, 1024181, 5.0, 55.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( BolaBall ), 1044047, 1023699, 45.0, 95.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + #endregion + + #region Utensils + AddCraft( typeof( ButcherKnife ), 1044048, 1025110, 25.0, 75.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( SpoonLeft ), 1044048, 1044158, 0.0, 50.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( SpoonRight ), 1044048, 1044159, 0.0, 50.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( Plate ), 1044048, 1022519, 0.0, 50.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( ForkLeft ), 1044048, 1044160, 0.0, 50.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( ForkRight ), 1044048, 1044161, 0.0, 50.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( Cleaver ), 1044048, 1023778, 20.0, 70.0, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddCraft( typeof( KnifeLeft ), 1044048, 1044162, 0.0, 50.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( KnifeRight ), 1044048, 1044163, 0.0, 50.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddCraft( typeof( Goblet ), 1044048, 1022458, 10.0, 60.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( PewterMug ), 1044048, 1024097, 10.0, 60.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( SkinningKnife ), 1044048, 1023781, 25.0, 75.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + #endregion + + #region Misc + AddCraft( typeof( KeyRing ), 1044050, 1024113, 10.0, 60.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( Candelabra ), 1044050, 1022599, 55.0, 105.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Scales ), 1044050, 1026225, 60.0, 110.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Key ), 1044050, 1024112, 20.0, 70.0, typeof( IronIngot ), 1044036, 3, 1044037 ); + AddCraft( typeof( Globe ), 1044050, 1024167, 55.0, 105.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Spyglass ), 1044050, 1025365, 60.0, 110.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + AddCraft( typeof( Lantern ), 1044050, 1022597, 30.0, 80.0, typeof( IronIngot ), 1044036, 2, 1044037 ); + AddCraft( typeof( HeatingStand ), 1044050, 1026217, 60.0, 110.0, typeof( IronIngot ), 1044036, 4, 1044037 ); + + if ( Core.SE ) + { + index = AddCraft( typeof( ShojiLantern ), 1044050, 1029404, 65.0, 115.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( Log ), 1044041, 5, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( PaperLantern ), 1044050, 1029406, 65.0, 115.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( Log ), 1044041, 5, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( RoundPaperLantern ), 1044050, 1029418, 65.0, 115.0, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( Log ), 1044041, 5, 1044351 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( WindChimes ), 1044050, 1030290, 80.0, 130.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + index = AddCraft( typeof( FancyWindChimes ), 1044050, 1030291, 80.0, 130.0, typeof( IronIngot ), 1044036, 15, 1044037 ); + SetNeededExpansion( index, Expansion.SE ); + + } + #endregion + + #region Jewelry + AddJewelrySet( GemType.StarSapphire, typeof( StarSapphire ) ); + AddJewelrySet( GemType.Emerald, typeof( Emerald ) ); + AddJewelrySet( GemType.Sapphire, typeof( Sapphire ) ); + AddJewelrySet( GemType.Ruby, typeof( Ruby ) ); + AddJewelrySet( GemType.Citrine, typeof( Citrine ) ); + AddJewelrySet( GemType.Amethyst, typeof( Amethyst ) ); + AddJewelrySet( GemType.Tourmaline, typeof( Tourmaline ) ); + AddJewelrySet( GemType.Amber, typeof( Amber ) ); + AddJewelrySet( GemType.Diamond, typeof( Diamond ) ); + #endregion + + #region Multi-Component Items + index = AddCraft( typeof( AxleGears ), 1044051, 1024177, 0.0, 0.0, typeof( Axle ), 1044169, 1, 1044253 ); + AddRes( index, typeof( Gears ), 1044254, 1, 1044253 ); + + index = AddCraft( typeof( ClockParts ), 1044051, 1024175, 0.0, 0.0, typeof( AxleGears ), 1044170, 1, 1044253 ); + AddRes( index, typeof( Springs ), 1044171, 1, 1044253 ); + + index = AddCraft( typeof( SextantParts ), 1044051, 1024185, 0.0, 0.0, typeof( AxleGears ), 1044170, 1, 1044253 ); + AddRes( index, typeof( Hinge ), 1044172, 1, 1044253 ); + + index = AddCraft( typeof( ClockRight ), 1044051, 1044257, 0.0, 0.0, typeof( ClockFrame ), 1044174, 1, 1044253 ); + AddRes( index, typeof( ClockParts ), 1044173, 1, 1044253 ); + + index = AddCraft( typeof( ClockLeft ), 1044051, 1044256, 0.0, 0.0, typeof( ClockFrame ), 1044174, 1, 1044253 ); + AddRes( index, typeof( ClockParts ), 1044173, 1, 1044253 ); + + AddCraft( typeof( Sextant ), 1044051, 1024183, 0.0, 0.0, typeof( SextantParts ), 1044175, 1, 1044253 ); + + index = AddCraft( typeof( Bola ), 1044051, 1046441, 60.0, 80.0, typeof( BolaBall ), 1046440, 4, 1042613 ); + AddRes( index, typeof( Leather ), 1044462, 3, 1044463 ); + + index = AddCraft( typeof( PotionKeg ), 1044051, 1044258, 75.0, 100.0, typeof( Keg ), 1044255, 1, 1044253 ); + AddRes( index, typeof( Bottle ), 1044250, 10, 1044253 ); + AddRes( index, typeof( BarrelLid ), 1044251, 1, 1044253 ); + AddRes( index, typeof( BarrelTap ), 1044252, 1, 1044253 ); + + + #endregion + + #region Traps + // Dart Trap + index = AddCraft( typeof( DartTrapCraft ), 1044052, 1024396, 30.0, 80.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddRes( index, typeof( Bolt ), 1044570, 1, 1044253 ); + + // Poison Trap + index = AddCraft( typeof( PoisonTrapCraft ), 1044052, 1044593, 30.0, 80.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddRes( index, typeof( BasePoisonPotion ), 1044571, 1, 1044253 ); + + // Explosion Trap + index = AddCraft( typeof( ExplosionTrapCraft ), 1044052, 1044597, 55.0, 105.0, typeof( IronIngot ), 1044036, 1, 1044037 ); + AddRes( index, typeof( BaseExplosionPotion ), 1044569, 1, 1044253 ); + + // Faction Gas Trap + index = AddCraft( typeof( FactionGasTrapDeed ), 1044052, 1044598, 65.0, 115.0, typeof( Silver ), 1044572, Core.AOS ? 250 : 1000, 1044253 ); + AddRes( index, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( BasePoisonPotion ), 1044571, 1, 1044253 ); + + // Faction explosion Trap + index = AddCraft( typeof( FactionExplosionTrapDeed ), 1044052, 1044599, 65.0, 115.0, typeof( Silver ), 1044572, Core.AOS ? 250 : 1000, 1044253 ); + AddRes( index, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( BaseExplosionPotion ), 1044569, 1, 1044253 ); + + // Faction Saw Trap + index = AddCraft( typeof( FactionSawTrapDeed ), 1044052, 1044600, 65.0, 115.0, typeof( Silver ), 1044572, Core.AOS ? 250 : 1000, 1044253 ); + AddRes( index, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( Gears ), 1044254, 1, 1044253 ); + + // Faction Spike Trap + index = AddCraft( typeof( FactionSpikeTrapDeed ), 1044052, 1044601, 65.0, 115.0, typeof( Silver ), 1044572, Core.AOS ? 250 : 1000, 1044253 ); + AddRes( index, typeof( IronIngot ), 1044036, 10, 1044037 ); + AddRes( index, typeof( Springs ), 1044171, 1, 1044253 ); + + // Faction trap removal kit + index = AddCraft( typeof( FactionTrapRemovalKit ), 1044052, 1046445, 90.0, 115.0, typeof( Silver ), 1044572, 500, 1044253 ); + AddRes( index, typeof( IronIngot ), 1044036, 10, 1044037 ); + #endregion + + // Set the overridable material + SetSubRes( typeof( IronIngot ), 1044022 ); + + // Add every material you want the player to be able to choose from + // This will override the overridable material + AddSubRes( typeof( IronIngot ), 1044022, 00.0, 1044036, 1044267 ); + AddSubRes( typeof( DullCopperIngot ), 1044023, 65.0, 1044036, 1044268 ); + AddSubRes( typeof( ShadowIronIngot ), 1044024, 70.0, 1044036, 1044268 ); + AddSubRes( typeof( CopperIngot ), 1044025, 75.0, 1044036, 1044268 ); + AddSubRes( typeof( BronzeIngot ), 1044026, 80.0, 1044036, 1044268 ); + AddSubRes( typeof( GoldIngot ), 1044027, 85.0, 1044036, 1044268 ); + AddSubRes( typeof( AgapiteIngot ), 1044028, 90.0, 1044036, 1044268 ); + AddSubRes( typeof( VeriteIngot ), 1044029, 95.0, 1044036, 1044268 ); + AddSubRes( typeof( ValoriteIngot ), 1044030, 99.0, 1044036, 1044268 ); + + MarkOption = true; + Repair = true; + CanEnhance = Core.AOS; + } + } + + public abstract class TrapCraft : CustomCraft + { + private LockableContainer m_Container; + + public LockableContainer Container{ get{ return m_Container; } } + + public abstract TrapType TrapType{ get; } + + public TrapCraft( Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality ) : base( from, craftItem, craftSystem, typeRes, tool, quality ) + { + } + + private int Verify( LockableContainer container ) + { + if ( container == null || container.KeyValue == 0 ) + return 1005638; // You can only trap lockable chests. + if ( From.Map != container.Map || !From.InRange( container.GetWorldLocation(), 2 ) ) + return 500446; // That is too far away. + if ( !container.Movable ) + return 502944; // You cannot trap this item because it is locked down. + if ( !container.IsAccessibleTo( From ) ) + return 502946; // That belongs to someone else. + if ( container.Locked ) + return 502943; // You can only trap an unlocked object. + if ( container.TrapType != TrapType.None ) + return 502945; // You can only place one trap on an object at a time. + + return 0; + } + + private bool Acquire( object target, out int message ) + { + LockableContainer container = target as LockableContainer; + + message = Verify( container ); + + if ( message > 0 ) + { + return false; + } + else + { + m_Container = container; + return true; + } + } + + public override void EndCraftAction() + { + From.SendLocalizedMessage( 502921 ); // What would you like to set a trap on? + From.Target = new ContainerTarget( this ); + } + + private class ContainerTarget : Target + { + private TrapCraft m_TrapCraft; + + public ContainerTarget( TrapCraft trapCraft ) : base( -1, false, TargetFlags.None ) + { + m_TrapCraft = trapCraft; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + int message; + + if ( m_TrapCraft.Acquire( targeted, out message ) ) + m_TrapCraft.CraftItem.CompleteCraft( m_TrapCraft.Quality, false, m_TrapCraft.From, m_TrapCraft.CraftSystem, m_TrapCraft.TypeRes, m_TrapCraft.Tool, m_TrapCraft ); + else + Failure( message ); + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + if ( cancelType == TargetCancelType.Canceled ) + Failure( 0 ); + } + + private void Failure( int message ) + { + Mobile from = m_TrapCraft.From; + BaseTool tool = m_TrapCraft.Tool; + + if ( tool != null && !tool.Deleted && tool.UsesRemaining > 0 ) + from.SendGump( new CraftGump( from, m_TrapCraft.CraftSystem, tool, message ) ); + else if ( message > 0 ) + from.SendLocalizedMessage( message ); + } + } + + public override Item CompleteCraft( out int message ) + { + message = Verify( this.Container ); + + if ( message == 0 ) + { + int trapLevel = (int)(From.Skills.Tinkering.Value / 10); + + Container.TrapType = this.TrapType; + Container.TrapPower = trapLevel * 9; + Container.TrapLevel = trapLevel; + Container.TrapOnLockpick = true; + + message = 1005639; // Trap is disabled until you lock the chest. + } + + return null; + } + } + + [CraftItemID( 0x1BFC )] + public class DartTrapCraft : TrapCraft + { + public override TrapType TrapType{ get{ return TrapType.DartTrap; } } + + public DartTrapCraft( Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality ) : base( from, craftItem, craftSystem, typeRes, tool, quality ) + { + } + } + + [CraftItemID( 0x113E )] + public class PoisonTrapCraft : TrapCraft + { + public override TrapType TrapType{ get{ return TrapType.PoisonTrap; } } + + public PoisonTrapCraft( Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality ) : base( from, craftItem, craftSystem, typeRes, tool, quality ) + { + } + } + + [CraftItemID( 0x370C )] + public class ExplosionTrapCraft : TrapCraft + { + public override TrapType TrapType{ get{ return TrapType.ExplosionTrap; } } + + public ExplosionTrapCraft( Mobile from, CraftItem craftItem, CraftSystem craftSystem, Type typeRes, BaseTool tool, int quality ) : base( from, craftItem, craftSystem, typeRes, tool, quality ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Doom/GauntletSpawner.cs b/Scripts/Engines/Doom/GauntletSpawner.cs new file mode 100644 index 000000000..90a43d36e --- /dev/null +++ b/Scripts/Engines/Doom/GauntletSpawner.cs @@ -0,0 +1,719 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Regions; +using Server.Commands; + +namespace Server.Engines.Doom +{ + public enum GauntletSpawnerState + { + InSequence, + InProgress, + Completed + } + + public class GauntletSpawner : Item + { + public const int PlayersPerSpawn = 5; + + public const int InSequenceItemHue = 0x000; + public const int InProgressItemHue = 0x676; + public const int CompletedItemHue = 0x455; + + private GauntletSpawnerState m_State; + + private string m_TypeName; + private BaseDoor m_Door; + private BaseAddon m_Addon; + private GauntletSpawner m_Sequence; + private ArrayList m_Creatures; + + private Rectangle2D m_RegionBounds; + private ArrayList m_Traps; + + private Region m_Region; + + [CommandProperty( AccessLevel.GameMaster )] + public string TypeName + { + get{ return m_TypeName; } + set{ m_TypeName = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BaseDoor Door + { + get{ return m_Door; } + set{ m_Door = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BaseAddon Addon + { + get{ return m_Addon; } + set{ m_Addon = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public GauntletSpawner Sequence + { + get{ return m_Sequence; } + set{ m_Sequence = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool HasCompleted + { + get + { + if ( m_Creatures.Count == 0 ) + return false; + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + Mobile mob = (Mobile)m_Creatures[i]; + + if ( !mob.Deleted ) + return false; + } + + return true; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Rectangle2D RegionBounds + { + get{ return m_RegionBounds; } + set{ m_RegionBounds = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public GauntletSpawnerState State + { + get{ return m_State; } + set + { + if ( m_State == value ) + return; + + m_State = value; + + int hue = 0; + bool lockDoors = ( m_State == GauntletSpawnerState.InProgress ); + + switch ( m_State ) + { + case GauntletSpawnerState.InSequence: hue = InSequenceItemHue; break; + case GauntletSpawnerState.InProgress: hue = InProgressItemHue; break; + case GauntletSpawnerState.Completed: hue = CompletedItemHue; break; + } + + if ( m_Door != null ) + { + m_Door.Hue = hue; + m_Door.Locked = lockDoors; + + if ( lockDoors ) + { + m_Door.KeyValue = Key.RandomValue(); + m_Door.Open = false; + } + + if ( m_Door.Link != null ) + { + m_Door.Link.Hue = hue; + m_Door.Link.Locked = lockDoors; + + if ( lockDoors ) + { + m_Door.Link.KeyValue = Key.RandomValue(); + m_Door.Open = false; + } + } + } + + if ( m_Addon != null ) + m_Addon.Hue = hue; + + if ( m_State == GauntletSpawnerState.InProgress ) + { + CreateRegion(); + FullSpawn(); + + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ), new TimerCallback( Slice ) ); + } + else + { + ClearCreatures(); + ClearTraps(); + DestroyRegion(); + + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + } + } + } + + private Timer m_Timer; + + public ArrayList Creatures + { + get{ return m_Creatures; } + set{ m_Creatures = value; } + } + + public ArrayList Traps + { + get{ return m_Traps; } + set{ m_Traps = value; } + } + + public Region Region + { + get{ return m_Region; } + set{ m_Region = value; } + } + + public virtual void CreateRegion() + { + if ( m_Region != null ) + return; + + Map map = this.Map; + + if ( map == null || map == Map.Internal ) + return; + + m_Region = new GauntletRegion( this, map ); + } + + public virtual void DestroyRegion() + { + if ( m_Region != null ) + m_Region.Unregister(); + + m_Region = null; + } + + public virtual int ComputeTrapCount() + { + int area = m_RegionBounds.Width * m_RegionBounds.Height; + + return area / 100; + } + + public virtual void ClearTraps() + { + for ( int i = 0; i < m_Traps.Count; ++i ) + ((Item)m_Traps[i]).Delete(); + + m_Traps.Clear(); + } + + public virtual void SpawnTrap() + { + Map map = this.Map; + + if ( map == null ) + return; + + Item trap = null; + + int random = Utility.Random( 100 ); + + if ( 22 > random ) + trap = new SawTrap( Utility.RandomBool() ? SawTrapType.WestFloor : SawTrapType.NorthFloor ); + else if ( 44 > random ) + trap = new SpikeTrap( Utility.RandomBool() ? SpikeTrapType.WestFloor : SpikeTrapType.NorthFloor ); + else if ( 66 > random ) + trap = new GasTrap( Utility.RandomBool() ? GasTrapType.NorthWall : GasTrapType.WestWall ); + else if ( 88 > random ) + trap = new FireColumnTrap(); + else + trap = new MushroomTrap(); + + if ( trap == null ) + return; + + if ( trap is FireColumnTrap || trap is MushroomTrap ) + trap.Hue = 0x451; + + // try 10 times to find a valid location + for ( int i = 0; i < 10; ++i ) + { + int x = Utility.Random( m_RegionBounds.X, m_RegionBounds.Width ); + int y = Utility.Random( m_RegionBounds.Y, m_RegionBounds.Height ); + int z = this.Z; + + if ( !map.CanFit( x, y, z, 16, false, false ) ) + z = map.GetAverageZ( x, y ); + + if ( !map.CanFit( x, y, z, 16, false, false ) ) + continue; + + trap.MoveToWorld( new Point3D( x, y, z ), map ); + m_Traps.Add( trap ); + + return; + } + + trap.Delete(); + } + + public virtual int ComputeSpawnCount() + { + int playerCount = 0; + + Map map = this.Map; + + if ( map != null ) + { + Point3D loc = GetWorldLocation(); + + /* + Sector sec = map.GetSector( loc ); + ArrayList regions = sec.Regions; + + for ( int i = 0; playerCount == 0 && i < regions.Count; ++i ) + { + Region reg = (Region)regions[i]; + + if ( reg != null && reg != m_Region && reg.Contains( loc ) ) + playerCount = reg.GetPlayerCount(); + } + */ + + Region reg = Region.Find( loc, map ).GetRegion( "Doom Gauntlet" ); + + if ( reg != null ) + playerCount = reg.GetPlayerCount(); + } + + if ( playerCount == 0 && m_Region != null ) + playerCount = m_Region.GetPlayerCount(); + + int count = (playerCount + PlayersPerSpawn - 1) / PlayersPerSpawn; + + if ( count < 1 ) + count = 1; + + return count; + } + + public virtual void ClearCreatures() + { + for ( int i = 0; i < m_Creatures.Count; ++i ) + ((Mobile)m_Creatures[i]).Delete(); + + m_Creatures.Clear(); + } + + public virtual void FullSpawn() + { + ClearCreatures(); + + int count = ComputeSpawnCount(); + + for ( int i = 0; i < count; ++i ) + Spawn(); + + ClearTraps(); + + count = ComputeTrapCount(); + + for ( int i = 0; i < count; ++i ) + SpawnTrap(); + } + + public virtual void Spawn() + { + try + { + if ( m_TypeName == null ) + return; + + Type type = ScriptCompiler.FindTypeByName( m_TypeName, true ); + + if ( type == null ) + return; + + object obj = Activator.CreateInstance( type ); + + if ( obj == null ) + return; + + if ( obj is Item ) + { + ((Item)obj).Delete(); + } + else if ( obj is Mobile ) + { + Mobile mob = (Mobile)obj; + + mob.MoveToWorld( GetWorldLocation(), this.Map ); + + m_Creatures.Add( mob ); + } + } + catch + { + } + } + + public virtual void RecurseReset() + { + if ( m_State != GauntletSpawnerState.InSequence ) + { + State = GauntletSpawnerState.InSequence; + + if ( m_Sequence != null && !m_Sequence.Deleted ) + m_Sequence.RecurseReset(); + } + } + + public virtual void Slice() + { + if ( m_State != GauntletSpawnerState.InProgress ) + return; + + int count = ComputeSpawnCount(); + + for ( int i = m_Creatures.Count; i < count; ++i ) + Spawn(); + + if ( HasCompleted ) + { + State = GauntletSpawnerState.Completed; + + if ( m_Sequence != null && !m_Sequence.Deleted ) + { + if ( m_Sequence.State == GauntletSpawnerState.Completed ) + RecurseReset(); + + m_Sequence.State = GauntletSpawnerState.InProgress; + } + } + } + + public override string DefaultName + { + get { return "doom spawner"; } + } + + [Constructable] + public GauntletSpawner() : this( null ) + { + } + + [Constructable] + public GauntletSpawner( string typeName ) : base( 0x36FE ) + { + Visible = false; + Movable = false; + + m_TypeName = typeName; + m_Creatures = new ArrayList(); + m_Traps = new ArrayList(); + } + + public GauntletSpawner( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_RegionBounds ); + + writer.WriteItemList( m_Traps, false ); + + writer.WriteMobileList( m_Creatures, false ); + + writer.Write( m_TypeName ); + writer.Write( m_Door ); + writer.Write( m_Addon ); + writer.Write( m_Sequence ); + + writer.Write( (int) m_State ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_RegionBounds = reader.ReadRect2D(); + m_Traps = reader.ReadItemList(); + + goto case 0; + } + case 0: + { + if ( version < 1 ) + { + m_Traps = new ArrayList(); + m_RegionBounds = new Rectangle2D( X - 40, Y - 40, 80, 80 ); + } + + m_Creatures = reader.ReadMobileList(); + + m_TypeName = reader.ReadString(); + m_Door = reader.ReadItem() as BaseDoor; + m_Addon = reader.ReadItem() as BaseAddon; + m_Sequence = reader.ReadItem() as GauntletSpawner; + + State = (GauntletSpawnerState)reader.ReadInt(); + + break; + } + } + } + + public static void Initialize() + { + CommandSystem.Register( "GenGauntlet", AccessLevel.Administrator, new CommandEventHandler( GenGauntlet_OnCommand ) ); + } + + public static void CreateTeleporter( int xFrom, int yFrom, int xTo, int yTo ) + { + Static telePad = new Static( 0x1822 ); + Teleporter teleItem = new Teleporter( new Point3D( xTo, yTo, -1 ), Map.Malas, false ); + + telePad.Hue = 0x482; + telePad.MoveToWorld( new Point3D( xFrom, yFrom, -1 ), Map.Malas ); + + teleItem.MoveToWorld( new Point3D( xFrom, yFrom, -1 ), Map.Malas ); + + teleItem.SourceEffect = true; + teleItem.DestEffect = true; + teleItem.SoundID = 0x1FE; + } + + public static BaseDoor CreateDoorSet( int xDoor, int yDoor, bool doorEastToWest, int hue ) + { + BaseDoor hiDoor = new MetalDoor( doorEastToWest ? DoorFacing.NorthCCW : DoorFacing.WestCW ); + BaseDoor loDoor = new MetalDoor( doorEastToWest ? DoorFacing.SouthCW : DoorFacing.EastCCW ); + + hiDoor.MoveToWorld( new Point3D( xDoor, yDoor, -1 ), Map.Malas ); + loDoor.MoveToWorld( new Point3D( xDoor + (doorEastToWest ? 0 : 1), yDoor + (doorEastToWest ? 1 : 0), -1 ), Map.Malas ); + + hiDoor.Link = loDoor; + loDoor.Link = hiDoor; + + hiDoor.Hue = hue; + loDoor.Hue = hue; + + return hiDoor; + } + + public static GauntletSpawner CreateSpawner( string typeName, int xSpawner, int ySpawner, int xDoor, int yDoor, int xPentagram, int yPentagram, bool doorEastToWest, int xStart, int yStart, int xWidth, int yHeight ) + { + GauntletSpawner spawner = new GauntletSpawner( typeName ); + + spawner.MoveToWorld( new Point3D( xSpawner, ySpawner, -1 ), Map.Malas ); + + if ( xDoor > 0 && yDoor > 0 ) + spawner.Door = CreateDoorSet( xDoor, yDoor, doorEastToWest, 0 ); + + spawner.RegionBounds = new Rectangle2D( xStart, yStart, xWidth, yHeight ); + + if ( xPentagram > 0 && yPentagram > 0 ) + { + PentagramAddon pentagram = new PentagramAddon(); + + pentagram.MoveToWorld( new Point3D( xPentagram, yPentagram, -1 ), Map.Malas ); + + spawner.Addon = pentagram; + } + + return spawner; + } + + public static void CreatePricedHealer( int price, int x, int y ) + { + PricedHealer healer = new PricedHealer( price ); + + healer.MoveToWorld( new Point3D( x, y, -1 ), Map.Malas ); + + healer.Home = healer.Location; + healer.RangeHome = 5; + } + + public static void CreateMorphItem( int x, int y, int inactiveItemID, int activeItemID, int range, int hue ) + { + MorphItem item = new MorphItem( inactiveItemID, activeItemID, range ); + + item.Hue = hue; + item.MoveToWorld( new Point3D( x, y, -1 ), Map.Malas ); + } + + public static void CreateVarietyDealer( int x, int y ) + { + VarietyDealer dealer = new VarietyDealer(); + + /* Begin outfit */ + dealer.Name = "Nix"; + dealer.Title = "the Variety Dealer"; + + dealer.Body = 400; + dealer.Female = false; + dealer.Hue = 0x8835; + + ArrayList items = new ArrayList( dealer.Items ); + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = (Item)items[i]; + + if ( item.Layer != Layer.ShopBuy && item.Layer != Layer.ShopResale && item.Layer != Layer.ShopSell ) + item.Delete(); + } + + dealer.HairItemID = 0x2049; // Pig Tails + dealer.HairHue = 0x482; + + dealer.FacialHairItemID = 0x203E; + dealer.FacialHairHue = 0x482; + + dealer.AddItem( new FloppyHat( 1 ) ); + dealer.AddItem( new Robe( 1 ) ); + + dealer.AddItem( new LanternOfSouls() ); + + dealer.AddItem( new Sandals( 0x482 ) ); + /* End outfit */ + + dealer.MoveToWorld( new Point3D( x, y, -1 ), Map.Malas ); + + dealer.Home = dealer.Location; + dealer.RangeHome = 2; + } + + public static void GenGauntlet_OnCommand( CommandEventArgs e ) + { + /* Begin healer room */ + CreatePricedHealer( 5000, 387, 400 ); + CreateTeleporter( 390, 407, 394, 405 ); + + BaseDoor healerDoor = CreateDoorSet( 393, 404, true, 0x44E ); + + healerDoor.Locked = true; + healerDoor.KeyValue = Key.RandomValue(); + + if ( healerDoor.Link != null ) + { + healerDoor.Link.Locked = true; + healerDoor.Link.KeyValue = Key.RandomValue(); + } + /* End healer room */ + + /* Begin supply room */ + CreateMorphItem( 433, 371, 0x29F, 0x116, 3, 0x44E ); + CreateMorphItem( 433, 372, 0x29F, 0x115, 3, 0x44E ); + + CreateVarietyDealer( 492, 369 ); + + for ( int x = 434; x <= 478; ++x ) + { + for ( int y = 371; y <= 372; ++y ) + { + Static item = new Static( 0x524 ); + + item.Hue = 1; + item.MoveToWorld( new Point3D( x, y, -1 ), Map.Malas ); + } + } + /* End supply room */ + + /* Begin gauntlet cycle */ + CreateTeleporter( 471, 428, 474, 428 ); + CreateTeleporter( 462, 494, 462, 498 ); + CreateTeleporter( 403, 502, 399, 506 ); + CreateTeleporter( 357, 476, 356, 480 ); + CreateTeleporter( 361, 433, 357, 434 ); + + GauntletSpawner sp1 = CreateSpawner( "DarknightCreeper", 491, 456, 473, 432, 417, 426, true, 473, 412, 39, 60 ); + GauntletSpawner sp2 = CreateSpawner( "FleshRenderer", 482, 520, 468, 496, 426, 422, false, 448, 496, 56, 48 ); + GauntletSpawner sp3 = CreateSpawner( "Impaler", 406, 538, 408, 504, 432, 430, false, 376, 504, 64, 48 ); + GauntletSpawner sp4 = CreateSpawner( "ShadowKnight", 335, 512, 360, 478, 424, 439, false, 300, 478, 72, 64 ); + GauntletSpawner sp5 = CreateSpawner( "AbysmalHorror", 326, 433, 360, 429, 416, 435, true, 300, 408, 60, 56 ); + GauntletSpawner sp6 = CreateSpawner( "DemonKnight", 423, 430, 0, 0, 423, 430, true, 392, 392, 72, 96 ); + + sp1.Sequence = sp2; + sp2.Sequence = sp3; + sp3.Sequence = sp4; + sp4.Sequence = sp5; + sp5.Sequence = sp6; + sp6.Sequence = sp1; + + sp1.State = GauntletSpawnerState.InProgress; + /* End gauntlet cycle */ + + /* Begin exit gate */ + ConfirmationMoongate gate = new ConfirmationMoongate(); + + gate.Dispellable = false; + + gate.Target = new Point3D( 2350, 1270, -85 ); + gate.TargetMap = Map.Malas; + + gate.GumpWidth = 420; + gate.GumpHeight = 280; + + gate.MessageColor = 0x7F00; + gate.MessageNumber = 1062109; // You are about to exit Dungeon Doom. Do you wish to continue? + + gate.TitleColor = 0x7800; + gate.TitleNumber = 1062108; // Please verify... + + gate.Hue = 0x44E; + + gate.MoveToWorld( new Point3D( 433, 326, 4 ), Map.Malas ); + /* End exit gate */ + } + } + + + public class GauntletRegion : BaseRegion + { + private GauntletSpawner m_Spawner; + + public GauntletRegion( GauntletSpawner spawner, Map map ) + : base( null, map, Region.Find( spawner.Location, spawner.Map ), spawner.RegionBounds ) + { + m_Spawner = spawner; + + GoLocation = spawner.Location; + + Register(); + } + + public override void AlterLightLevel( Mobile m, ref int global, ref int personal ) + { + global = 12; + } + + public override void OnEnter( Mobile m ) + { + } + + public override void OnExit( Mobile m ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Core/Ethic.cs b/Scripts/Engines/Ethics/Core/Ethic.cs new file mode 100644 index 000000000..9f6bbfaef --- /dev/null +++ b/Scripts/Engines/Ethics/Core/Ethic.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Mobiles; + +namespace Server.Ethics +{ + public abstract class Ethic + { + public static readonly bool Enabled = false; + + public static Ethic Find( Item item ) + { + if ( ( item.SavedFlags & 0x100 ) != 0 ) + { + if ( item.Hue == Hero.Definition.PrimaryHue ) + return Hero; + + item.SavedFlags &= ~0x100; + } + + if ( ( item.SavedFlags & 0x200 ) != 0 ) + { + if ( item.Hue == Evil.Definition.PrimaryHue ) + return Evil; + + item.SavedFlags &= ~0x200; + } + + return null; + } + + public static bool CheckTrade( Mobile from, Mobile to, Mobile newOwner, Item item ) + { + Ethic itemEthic = Find( item ); + + if ( itemEthic == null || Find( newOwner ) == itemEthic ) + return true; + + if ( itemEthic == Hero ) + ( from == newOwner ? to : from ).SendMessage( "Only heros may receive this item." ); + else if ( itemEthic == Evil ) + ( from == newOwner ? to : from ).SendMessage( "Only the evil may receive this item." ); + + return false; + } + + public static bool CheckEquip( Mobile from, Item item ) + { + Ethic itemEthic = Find( item ); + + if ( itemEthic == null || Find( from ) == itemEthic ) + return true; + + if ( itemEthic == Hero ) + from.SendMessage( "Only heros may wear this item." ); + else if ( itemEthic == Evil ) + from.SendMessage( "Only the evil may wear this item." ); + + return false; + } + + public static bool IsImbued( Item item ) + { + return IsImbued( item, false ); + } + + public static bool IsImbued( Item item, bool recurse ) + { + if ( Find( item ) != null ) + return true; + + if ( recurse ) + { + foreach ( Item child in item.Items ) + { + if ( IsImbued( child, true ) ) + return true; + } + } + + return false; + } + + public static void Initialize() + { + if( Enabled ) + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + } + + public static void EventSink_Speech( SpeechEventArgs e ) + { + if ( e.Blocked || e.Handled ) + return; + + Player pl = Player.Find( e.Mobile ); + + if ( pl == null ) + { + for ( int i = 0; i < Ethics.Length; ++i ) + { + Ethic ethic = Ethics[i]; + + if ( !ethic.IsEligible( e.Mobile ) ) + continue; + + if ( !Insensitive.Equals( ethic.Definition.JoinPhrase.String, e.Speech ) ) + continue; + + bool isNearAnkh = false; + + foreach ( Item item in e.Mobile.GetItemsInRange( 2 ) ) + { + if ( item is Items.AnkhEast || item is Items.AnkhWest ) + { + isNearAnkh = true; + break; + } + } + + if ( !isNearAnkh ) + continue; + + pl = new Player( ethic, e.Mobile ); + + pl.Attach(); + + e.Mobile.FixedEffect( 0x373A, 10, 30 ); + e.Mobile.PlaySound( 0x209 ); + + e.Handled = true; + break; + } + } + else + { + Ethic ethic = pl.Ethic; + + for ( int i = 0; i < ethic.Definition.Powers.Length; ++i ) + { + Power power = ethic.Definition.Powers[i]; + + if ( !Insensitive.Equals( power.Definition.Phrase.String, e.Speech ) ) + continue; + + if ( !power.CheckInvoke( pl ) ) + continue; + + power.BeginInvoke( pl ); + e.Handled = true; + + break; + } + } + } + + protected EthicDefinition m_Definition; + + protected PlayerCollection m_Players; + + public EthicDefinition Definition + { + get { return m_Definition; } + } + + public PlayerCollection Players + { + get { return m_Players; } + } + + public static Ethic Find( Mobile mob ) + { + return Find( mob, false, false ); + } + + public static Ethic Find( Mobile mob, bool inherit ) + { + return Find( mob, inherit, false ); + } + + public static Ethic Find( Mobile mob, bool inherit, bool allegiance ) + { + Player pl = Player.Find( mob ); + + if ( pl != null ) + return pl.Ethic; + + if ( inherit && mob is BaseCreature ) + { + BaseCreature bc = (BaseCreature) mob; + + if ( bc.Controlled ) + return Find( bc.ControlMaster, false ); + else if ( bc.Summoned ) + return Find( bc.SummonMaster, false ); + else if ( allegiance ) + return bc.EthicAllegiance; + } + + return null; + } + + public Ethic() + { + m_Players = new PlayerCollection(); + } + + public abstract bool IsEligible( Mobile mob ); + + public virtual void Deserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + int playerCount = reader.ReadEncodedInt(); + + for ( int i = 0; i < playerCount; ++i ) + { + Player pl = new Player( this, reader ); + + if ( pl.Mobile != null ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( pl.CheckAttach ) ); + } + + break; + } + } + } + + public virtual void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.WriteEncodedInt( m_Players.Count ); + + for ( int i = 0; i < m_Players.Count; ++i ) + m_Players[i].Serialize( writer ); + } + + public static readonly Ethic Hero = new Hero.HeroEthic(); + public static readonly Ethic Evil = new Evil.EvilEthic(); + + public static readonly Ethic[] Ethics = new Ethic[] + { + Hero, + Evil + }; + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Core/Persistance.cs b/Scripts/Engines/Ethics/Core/Persistance.cs new file mode 100644 index 000000000..953ba3738 --- /dev/null +++ b/Scripts/Engines/Ethics/Core/Persistance.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Ethics +{ + public class EthicsPersistance : Item + { + private static EthicsPersistance m_Instance; + + public static EthicsPersistance Instance { get { return m_Instance; } } + + public override string DefaultName + { + get { return "Ethics Persistance - Internal"; } + } + + [Constructable] + public EthicsPersistance() + : base( 1 ) + { + Movable = false; + + if ( m_Instance == null || m_Instance.Deleted ) + m_Instance = this; + else + base.Delete(); + } + + public EthicsPersistance( Serial serial ) + : base( serial ) + { + m_Instance = this; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + for ( int i = 0; i < Ethics.Ethic.Ethics.Length; ++i ) + Ethics.Ethic.Ethics[i].Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + for ( int i = 0; i < Ethics.Ethic.Ethics.Length; ++i ) + Ethics.Ethic.Ethics[i].Deserialize( reader ); + + break; + } + } + } + + public override void Delete() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Core/Player.cs b/Scripts/Engines/Ethics/Core/Player.cs new file mode 100644 index 000000000..1f73b3ce5 --- /dev/null +++ b/Scripts/Engines/Ethics/Core/Player.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Mobiles; + +namespace Server.Ethics +{ + public class PlayerCollection : System.Collections.ObjectModel.Collection + { + } + + [PropertyObject] + public class Player + { + public static Player Find( Mobile mob ) + { + return Find( mob, false ); + } + + public static Player Find( Mobile mob, bool inherit ) + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm == null ) + { + if ( inherit && mob is BaseCreature ) + { + BaseCreature bc = mob as BaseCreature; + + if ( bc != null && bc.Controlled ) + pm = bc.ControlMaster as PlayerMobile; + else if ( bc != null && bc.Summoned ) + pm = bc.SummonMaster as PlayerMobile; + } + + if ( pm == null ) + return null; + } + + Player pl = pm.EthicPlayer; + + if ( pl != null && !pl.Ethic.IsEligible( pl.Mobile ) ) + pm.EthicPlayer = pl = null; + + return pl; + } + + private Ethic m_Ethic; + private Mobile m_Mobile; + + private int m_Power; + private int m_History; + + private Mobile m_Steed; + private Mobile m_Familiar; + + private DateTime m_Shield; + + public Ethic Ethic { get { return m_Ethic; } } + public Mobile Mobile { get { return m_Mobile; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public int Power { get { return m_Power; } set { m_Power = value; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public int History { get { return m_History; } set { m_History = value; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public Mobile Steed { get { return m_Steed; } set { m_Steed = value; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public Mobile Familiar { get { return m_Familiar; } set { m_Familiar = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsShielded + { + get + { + if ( m_Shield == DateTime.MinValue ) + return false; + + if ( DateTime.Now < ( m_Shield + TimeSpan.FromHours( 1.0 ) ) ) + return true; + + FinishShield(); + return false; + } + } + + public void BeginShield() + { + m_Shield = DateTime.Now; + } + + public void FinishShield() + { + m_Shield = DateTime.MinValue; + } + + public Player( Ethic ethic, Mobile mobile ) + { + m_Ethic = ethic; + m_Mobile = mobile; + + m_Power = 5; + m_History = 5; + } + + public void CheckAttach() + { + if ( m_Ethic.IsEligible( m_Mobile ) ) + Attach(); + } + + public void Attach() + { + if ( m_Mobile is PlayerMobile ) + ( m_Mobile as PlayerMobile ).EthicPlayer = this; + + m_Ethic.Players.Add( this ); + } + + public void Detach() + { + if ( m_Mobile is PlayerMobile ) + ( m_Mobile as PlayerMobile ).EthicPlayer = null; + + m_Ethic.Players.Remove( this ); + } + + public Player( Ethic ethic, GenericReader reader ) + { + m_Ethic = ethic; + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_Mobile = reader.ReadMobile(); + + m_Power = reader.ReadEncodedInt(); + m_History = reader.ReadEncodedInt(); + + m_Steed = reader.ReadMobile(); + m_Familiar = reader.ReadMobile(); + + m_Shield = reader.ReadDeltaTime(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( m_Mobile ); + + writer.WriteEncodedInt( m_Power ); + writer.WriteEncodedInt( m_History ); + + writer.Write( m_Steed ); + writer.Write( m_Familiar ); + + writer.WriteDeltaTime( m_Shield ); + } + } +} diff --git a/Scripts/Engines/Ethics/Core/Power.cs b/Scripts/Engines/Ethics/Core/Power.cs new file mode 100644 index 000000000..bc5b4ba96 --- /dev/null +++ b/Scripts/Engines/Ethics/Core/Power.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics +{ + public abstract class Power + { + protected PowerDefinition m_Definition; + + public PowerDefinition Definition { get { return m_Definition; } } + + public virtual bool CheckInvoke( Player from ) + { + if ( !from.Mobile.CheckAlive() ) + return false; + + if ( from.Power < m_Definition.Power ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You lack the power to invoke this ability." ); + return false; + } + + return true; + } + + public abstract void BeginInvoke( Player from ); + + public virtual void FinishInvoke( Player from ) + { + from.Power -= m_Definition.Power; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Definitions/EthicDefinition.cs b/Scripts/Engines/Ethics/Definitions/EthicDefinition.cs new file mode 100644 index 000000000..39b680320 --- /dev/null +++ b/Scripts/Engines/Ethics/Definitions/EthicDefinition.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics +{ + public class EthicDefinition + { + private int m_PrimaryHue; + + private TextDefinition m_Title; + private TextDefinition m_Adjunct; + + private TextDefinition m_JoinPhrase; + + private Power[] m_Powers; + + public int PrimaryHue { get { return m_PrimaryHue; } } + + public TextDefinition Title { get { return m_Title; } } + public TextDefinition Adjunct { get { return m_Adjunct; } } + + public TextDefinition JoinPhrase { get { return m_JoinPhrase; } } + + public Power[] Powers { get { return m_Powers; } } + + public EthicDefinition( int primaryHue, TextDefinition title, TextDefinition adjunct, TextDefinition joinPhrase, Power[] powers ) + { + m_PrimaryHue = primaryHue; + + m_Title = title; + m_Adjunct = adjunct; + + m_JoinPhrase = joinPhrase; + + m_Powers = powers; + } + } +} diff --git a/Scripts/Engines/Ethics/Definitions/PowerDefinition.cs b/Scripts/Engines/Ethics/Definitions/PowerDefinition.cs new file mode 100644 index 000000000..86af47a4d --- /dev/null +++ b/Scripts/Engines/Ethics/Definitions/PowerDefinition.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics +{ + public class PowerDefinition + { + private int m_Power; + + private TextDefinition m_Name; + private TextDefinition m_Phrase; + private TextDefinition m_Description; + + public int Power { get { return m_Power; } } + + public TextDefinition Name { get { return m_Name; } } + public TextDefinition Phrase { get { return m_Phrase; } } + public TextDefinition Description { get { return m_Description; } } + + public PowerDefinition( int power, TextDefinition name, TextDefinition phrase, TextDefinition description ) + { + m_Power = power; + + m_Name = name; + m_Phrase = phrase; + m_Description = description; + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Ethic.cs b/Scripts/Engines/Ethics/Evil/Ethic.cs new file mode 100644 index 000000000..e79f25587 --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Ethic.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Factions; + +namespace Server.Ethics.Evil +{ + public sealed class EvilEthic : Ethic + { + public EvilEthic() + { + m_Definition = new EthicDefinition( + 0x455, + "Evil", "(Evil)", + "I am evil incarnate", + new Power[] + { + new UnholySense(), + new UnholyItem(), + new SummonFamiliar(), + new VileBlade(), + new Blight(), + new UnholyShield(), + new UnholySteed(), + new UnholyWord() + } + ); + } + + public override bool IsEligible( Mobile mob ) + { + Faction fac = Faction.Find( mob ); + + return ( fac is Minax || fac is Shadowlords ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs b/Scripts/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs new file mode 100644 index 000000000..983482549 --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Mobiles/UnholyFamiliar.cs @@ -0,0 +1,86 @@ +using System; +using Server; +using Server.Ethics; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an evil corpse" )] + public class UnholyFamiliar : BaseCreature + { + public override bool IsDispellable { get { return false; } } + public override bool IsBondable { get { return false; } } + + [Constructable] + public UnholyFamiliar() + : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a dark wolf"; + Body = 99; + BaseSoundID = 0xE5; + + SetStr( 96, 120 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + SetMana( 0 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 57.6, 75.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 2500; + Karma = 2500; + + VirtualArmor = 22; + + Tamable = false; + ControlSlots = 1; + } + + public override int Meat { get { return 1; } } + public override int Hides { get { return 7; } } + public override FoodType FavoriteFood { get { return FoodType.Meat; } } + public override PackInstinct PackInstinct { get { return PackInstinct.Canine; } } + + public UnholyFamiliar( Serial serial ) + : base( serial ) + { + } + + public override string ApplyNameSuffix( string suffix ) + { + if ( suffix.Length == 0 ) + suffix = Ethic.Evil.Definition.Adjunct.String; + else + suffix = String.Concat( suffix, " ", Ethic.Evil.Definition.Adjunct.String ); + + return base.ApplyNameSuffix( suffix ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Evil/Mobiles/UnholySteed.cs b/Scripts/Engines/Ethics/Evil/Mobiles/UnholySteed.cs new file mode 100644 index 000000000..02d85b8f7 --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Mobiles/UnholySteed.cs @@ -0,0 +1,91 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Ethics; + +namespace Server.Mobiles +{ + [CorpseName( "an unholy corpse" )] + public class UnholySteed : BaseMount + { + public override bool IsDispellable { get { return false; } } + public override bool IsBondable { get { return false; } } + + public override bool HasBreath { get { return true; } } + public override bool CanBreath { get { return true; } } + + [Constructable] + public UnholySteed() + : base( "a dark steed", 0x74, 0x3EA7, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + SetStr( 496, 525 ); + SetDex( 86, 105 ); + SetInt( 86, 125 ); + + SetHits( 298, 315 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Fire, 40 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 80.5, 92.5 ); + + Fame = 14000; + Karma = -14000; + + VirtualArmor = 60; + + Tamable = false; + ControlSlots = 1; + } + + public override FoodType FavoriteFood { get { return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public UnholySteed( Serial serial ) + : base( serial ) + { + } + + public override string ApplyNameSuffix( string suffix ) + { + if ( suffix.Length == 0 ) + suffix = Ethic.Evil.Definition.Adjunct.String; + else + suffix = String.Concat( suffix, " ", Ethic.Evil.Definition.Adjunct.String ); + + return base.ApplyNameSuffix( suffix ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( Ethic.Find( from ) != Ethic.Evil ) + from.SendMessage( "You may not ride this steed." ); + else + base.OnDoubleClick( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Evil/Powers/Blight.cs b/Scripts/Engines/Ethics/Evil/Powers/Blight.cs new file mode 100644 index 000000000..2000b80e9 --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/Blight.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Spells; + +namespace Server.Ethics.Evil +{ + public sealed class Blight : Power + { + public Blight() + { + m_Definition = new PowerDefinition( + 15, + "Blight", + "Velgo Ontawl", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + from.Mobile.BeginTarget( 12, true, Targeting.TargetFlags.None, new TargetStateCallback( Power_OnTarget ), from ); + from.Mobile.SendMessage( "Where do you wish to blight?" ); + } + + private void Power_OnTarget( Mobile fromMobile, object obj, object state ) + { + Player from = state as Player; + + IPoint3D p = obj as IPoint3D; + + if ( p == null ) + return; + + if ( !CheckInvoke( from ) ) + return; + + bool powerFunctioned = false; + + SpellHelper.GetSurfaceTop( ref p ); + + foreach ( Mobile mob in from.Mobile.GetMobilesInRange( 6 ) ) + { + if ( mob == from.Mobile || !SpellHelper.ValidIndirectTarget( from.Mobile, mob ) ) + continue; + + if ( mob.GetStatMod( "Holy Curse" ) != null ) + continue; + + if ( !from.Mobile.CanBeHarmful( mob, false ) ) + continue; + + from.Mobile.DoHarmful( mob, true ); + + mob.AddStatMod( new StatMod( StatType.All, "Holy Curse", -10, TimeSpan.FromMinutes( 30.0 ) ) ); + + mob.FixedParticles( 0x374A, 10, 15, 5028, EffectLayer.Waist ); + mob.PlaySound( 0x1FB ); + + powerFunctioned = true; + } + + if ( powerFunctioned ) + { + SpellHelper.Turn( from.Mobile, p ); + + Effects.PlaySound( p, from.Mobile.Map, 0x1FB ); + + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You curse the area." ); + + FinishInvoke( from ); + } + else + { + from.Mobile.FixedEffect( 0x3735, 6, 30 ); + from.Mobile.PlaySound( 0x5C ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/SummonFamiliar.cs b/Scripts/Engines/Ethics/Evil/Powers/SummonFamiliar.cs new file mode 100644 index 000000000..c6fc3dc04 --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/SummonFamiliar.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Mobiles; + +namespace Server.Ethics.Evil +{ + public sealed class SummonFamiliar : Power + { + public SummonFamiliar() + { + m_Definition = new PowerDefinition( + 5, + "Summon Familiar", + "Trubechs Vingir", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + if ( from.Familiar != null && from.Familiar.Deleted ) + from.Familiar = null; + + if ( from.Familiar != null ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You already have an unholy familiar." ); + return; + } + + if ( ( from.Mobile.Followers + 1 ) > from.Mobile.FollowersMax ) + { + from.Mobile.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return; + } + + UnholyFamiliar familiar = new UnholyFamiliar(); + + if ( Mobiles.BaseCreature.Summon( familiar, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours( 1.0 ) ) ) + { + from.Familiar = familiar; + + FinishInvoke( from ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/UnholyItem.cs b/Scripts/Engines/Ethics/Evil/Powers/UnholyItem.cs new file mode 100644 index 000000000..1fc8ff291 --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/UnholyItem.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Items; + +namespace Server.Ethics.Evil +{ + public sealed class UnholyItem : Power + { + public UnholyItem() + { + m_Definition = new PowerDefinition( + 5, + "Unholy Item", + "Vidda K'balc", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + from.Mobile.BeginTarget( 12, false, Targeting.TargetFlags.None, new TargetStateCallback( Power_OnTarget ), from ); + from.Mobile.SendMessage( "Which item do you wish to imbue?" ); + } + + private void Power_OnTarget( Mobile fromMobile, object obj, object state ) + { + Player from = state as Player; + + Item item = obj as Item; + + if ( item == null ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You may not imbue that." ); + return; + } + + if ( item.Parent != from.Mobile ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You may only imbue items you are wearing." ); + return; + } + + if ( ( item.SavedFlags & 0x300 ) != 0 ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "That has already beem imbued." ); + return; + } + + bool canImbue = ( item is Spellbook || item is BaseClothing || item is BaseArmor || item is BaseWeapon ) && ( item.Name == null ); + + if ( canImbue ) + { + if ( !CheckInvoke( from ) ) + return; + + item.Hue = Ethic.Evil.Definition.PrimaryHue; + item.SavedFlags |= 0x200; + + from.Mobile.FixedEffect( 0x375A, 10, 20 ); + from.Mobile.PlaySound( 0x209 ); + + FinishInvoke( from ); + } + else + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You may not imbue that." ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/UnholySense.cs b/Scripts/Engines/Ethics/Evil/Powers/UnholySense.cs new file mode 100644 index 000000000..335643fdc --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/UnholySense.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Evil +{ + public sealed class UnholySense : Power + { + public UnholySense() + { + m_Definition = new PowerDefinition( + 0, + "Unholy Sense", + "Drewrok Velgo", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + Ethic opposition = Ethic.Hero; + + int enemyCount = 0; + + int maxRange = 18 + from.Power; + + Player primary = null; + + foreach ( Player pl in opposition.Players ) + { + Mobile mob = pl.Mobile; + + if ( mob == null || mob.Map != from.Mobile.Map || !mob.Alive ) + continue; + + if ( !mob.InRange( from.Mobile, Math.Max( 18, maxRange - pl.Power ) ) ) + continue; + + if ( primary == null || pl.Power > primary.Power ) + primary = pl; + + ++enemyCount; + } + + StringBuilder sb = new StringBuilder(); + + sb.Append( "You sense " ); + sb.Append( enemyCount == 0 ? "no" : enemyCount.ToString() ); + sb.Append( enemyCount == 1 ? " enemy" : " enemies" ); + + if ( primary != null ) + { + sb.Append( ", and a strong presense" ); + + switch ( from.Mobile.GetDirectionTo( primary.Mobile ) ) + { + case Direction.West: + sb.Append( " to the west." ); + break; + case Direction.East: + sb.Append( " to the east." ); + break; + case Direction.North: + sb.Append( " to the north." ); + break; + case Direction.South: + sb.Append( " to the south." ); + break; + + case Direction.Up: + sb.Append( " to the north-west." ); + break; + case Direction.Down: + sb.Append( " to the south-east." ); + break; + case Direction.Left: + sb.Append( " to the south-west." ); + break; + case Direction.Right: + sb.Append( " to the north-east." ); + break; + } + } + else + { + sb.Append( '.' ); + } + + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x59, false, sb.ToString() ); + + FinishInvoke( from ); + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/UnholyShield.cs b/Scripts/Engines/Ethics/Evil/Powers/UnholyShield.cs new file mode 100644 index 000000000..876f7209e --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/UnholyShield.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Evil +{ + public sealed class UnholyShield : Power + { + public UnholyShield() + { + m_Definition = new PowerDefinition( + 20, + "Unholy Shield", + "Velgo K'blac", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + if ( from.IsShielded ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You are already under the protection of an unholy shield." ); + return; + } + + from.BeginShield(); + + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You are now under the protection of an unholy shield." ); + + FinishInvoke( from ); + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/UnholySteed.cs b/Scripts/Engines/Ethics/Evil/Powers/UnholySteed.cs new file mode 100644 index 000000000..268d3533d --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/UnholySteed.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Mobiles; + +namespace Server.Ethics.Evil +{ + public sealed class UnholySteed : Power + { + public UnholySteed() + { + m_Definition = new PowerDefinition( + 30, + "Unholy Steed", + "Trubechs Yeliab", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + if ( from.Steed != null && from.Steed.Deleted ) + from.Steed = null; + + if ( from.Steed != null ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You already have an unholy steed." ); + return; + } + + if ( ( from.Mobile.Followers + 1 ) > from.Mobile.FollowersMax ) + { + from.Mobile.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return; + } + + Mobiles.UnholySteed steed = new Mobiles.UnholySteed(); + + if ( Mobiles.BaseCreature.Summon( steed, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours( 1.0 ) ) ) + { + from.Steed = steed; + + FinishInvoke( from ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/UnholyWord.cs b/Scripts/Engines/Ethics/Evil/Powers/UnholyWord.cs new file mode 100644 index 000000000..eb110483d --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/UnholyWord.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Evil +{ + public sealed class UnholyWord : Power + { + public UnholyWord() + { + m_Definition = new PowerDefinition( + 100, + "Unholy Word", + "Velgo Oostrac", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + } + } +} diff --git a/Scripts/Engines/Ethics/Evil/Powers/VileBlade.cs b/Scripts/Engines/Ethics/Evil/Powers/VileBlade.cs new file mode 100644 index 000000000..31775dc0a --- /dev/null +++ b/Scripts/Engines/Ethics/Evil/Powers/VileBlade.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Evil +{ + public sealed class VileBlade : Power + { + public VileBlade() + { + m_Definition = new PowerDefinition( + 10, + "Vile Blade", + "Velgo Reyam", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Ethic.cs b/Scripts/Engines/Ethics/Hero/Ethic.cs new file mode 100644 index 000000000..729302c2c --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Ethic.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Factions; + +namespace Server.Ethics.Hero +{ + public sealed class HeroEthic : Ethic + { + public HeroEthic() + { + m_Definition = new EthicDefinition( + 0x482, + "Hero", "(Hero)", + "I will defend the virtues", + new Power[] + { + new HolySense(), + new HolyItem(), + new SummonFamiliar(), + new HolyBlade(), + new Bless(), + new HolyShield(), + new HolySteed(), + new HolyWord() + } + ); + } + + public override bool IsEligible( Mobile mob ) + { + if ( mob.Kills >= 5 ) + return false; + + Faction fac = Faction.Find( mob ); + + return ( fac is TrueBritannians || fac is CouncilOfMages ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs b/Scripts/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs new file mode 100644 index 000000000..8e9b8f338 --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Mobiles/HolyFamiliar.cs @@ -0,0 +1,86 @@ +using System; +using Server; +using Server.Ethics; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a holy corpse" )] + public class HolyFamiliar : BaseCreature + { + public override bool IsDispellable { get { return false; } } + public override bool IsBondable { get { return false; } } + + [Constructable] + public HolyFamiliar() + : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a silver wolf"; + Body = 100; + BaseSoundID = 0xE5; + + SetStr( 96, 120 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + SetMana( 0 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 57.6, 75.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 2500; + Karma = 2500; + + VirtualArmor = 22; + + Tamable = false; + ControlSlots = 1; + } + + public override int Meat { get { return 1; } } + public override int Hides { get { return 7; } } + public override FoodType FavoriteFood { get { return FoodType.Meat; } } + public override PackInstinct PackInstinct { get { return PackInstinct.Canine; } } + + public HolyFamiliar( Serial serial ) + : base( serial ) + { + } + + public override string ApplyNameSuffix( string suffix ) + { + if ( suffix.Length == 0 ) + suffix = Ethic.Hero.Definition.Adjunct.String; + else + suffix = String.Concat( suffix, " ", Ethic.Hero.Definition.Adjunct.String ); + + return base.ApplyNameSuffix( suffix ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Hero/Mobiles/HolySteed.cs b/Scripts/Engines/Ethics/Hero/Mobiles/HolySteed.cs new file mode 100644 index 000000000..bc1f24a95 --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Mobiles/HolySteed.cs @@ -0,0 +1,91 @@ +using System; +using Server; +using Server.Ethics; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a holy corpse" )] + public class HolySteed : BaseMount + { + public override bool IsDispellable { get{ return false; } } + public override bool IsBondable { get { return false; } } + + public override bool HasBreath { get { return true; } } + public override bool CanBreath { get { return true; } } + + [Constructable] + public HolySteed() + : base( "a silver steed", 0x75, 0x3EA8, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + SetStr( 496, 525 ); + SetDex( 86, 105 ); + SetInt( 86, 125 ); + + SetHits( 298, 315 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Fire, 40 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 80.5, 92.5 ); + + Fame = 14000; + Karma = 14000; + + VirtualArmor = 60; + + Tamable = false; + ControlSlots = 1; + } + + public override FoodType FavoriteFood { get { return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public HolySteed( Serial serial ) + : base( serial ) + { + } + + public override string ApplyNameSuffix( string suffix ) + { + if ( suffix.Length == 0 ) + suffix = Ethic.Hero.Definition.Adjunct.String; + else + suffix = String.Concat( suffix, " ", Ethic.Hero.Definition.Adjunct.String ); + + return base.ApplyNameSuffix( suffix ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( Ethic.Find( from ) != Ethic.Hero ) + from.SendMessage( "You may not ride this steed." ); + else + base.OnDoubleClick( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Ethics/Hero/Powers/Bless.cs b/Scripts/Engines/Ethics/Hero/Powers/Bless.cs new file mode 100644 index 000000000..f2e487b38 --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/Bless.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Spells; + +namespace Server.Ethics.Hero +{ + public sealed class Bless : Power + { + public Bless() + { + m_Definition = new PowerDefinition( + 15, + "Bless", + "Erstok Ontawl", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + from.Mobile.BeginTarget( 12, true, Targeting.TargetFlags.None, new TargetStateCallback( Power_OnTarget ), from ); + from.Mobile.SendMessage( "Where do you wish to bless?" ); + } + + private void Power_OnTarget( Mobile fromMobile, object obj, object state ) + { + Player from = state as Player; + + IPoint3D p = obj as IPoint3D; + + if ( p == null ) + return; + + if ( !CheckInvoke( from ) ) + return; + + bool powerFunctioned = false; + + SpellHelper.GetSurfaceTop( ref p ); + + foreach ( Mobile mob in from.Mobile.GetMobilesInRange( 6 ) ) + { + if ( mob != from.Mobile && SpellHelper.ValidIndirectTarget( from.Mobile, mob ) ) + continue; + + if ( mob.GetStatMod( "Holy Bless" ) != null ) + continue; + + if ( !from.Mobile.CanBeBeneficial( mob, false ) ) + continue; + + from.Mobile.DoBeneficial( mob ); + + mob.AddStatMod( new StatMod( StatType.All, "Holy Bless", 10, TimeSpan.FromMinutes( 30.0 ) ) ); + + mob.FixedParticles( 0x373A, 10, 15, 5018, EffectLayer.Waist ); + mob.PlaySound( 0x1EA ); + + powerFunctioned = true; + } + + if ( powerFunctioned ) + { + SpellHelper.Turn( from.Mobile, p ); + + Effects.PlaySound( p, from.Mobile.Map, 0x299 ); + + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You consecrate the area." ); + + FinishInvoke( from ); + } + else + { + from.Mobile.FixedEffect( 0x3735, 6, 30 ); + from.Mobile.PlaySound( 0x5C ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/HolyBlade.cs b/Scripts/Engines/Ethics/Hero/Powers/HolyBlade.cs new file mode 100644 index 000000000..19557a64a --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/HolyBlade.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Hero +{ + public sealed class HolyBlade : Power + { + public HolyBlade() + { + m_Definition = new PowerDefinition( + 10, + "Holy Blade", + "Erstok Reyam", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/HolyItem.cs b/Scripts/Engines/Ethics/Hero/Powers/HolyItem.cs new file mode 100644 index 000000000..5f9f5fcde --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/HolyItem.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Items; + +namespace Server.Ethics.Hero +{ + public sealed class HolyItem : Power + { + public HolyItem() + { + m_Definition = new PowerDefinition( + 5, + "Holy Item", + "Vidda K'balc", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + from.Mobile.BeginTarget( 12, false, Targeting.TargetFlags.None, new TargetStateCallback( Power_OnTarget ), from ); + from.Mobile.SendMessage( "Which item do you wish to imbue?" ); + } + + private void Power_OnTarget( Mobile fromMobile, object obj, object state ) + { + Player from = state as Player; + + Item item = obj as Item; + + if ( item == null ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You may not imbue that." ); + return; + } + + if ( item.Parent != from.Mobile ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You may only imbue items you are wearing." ); + return; + } + + if ( ( item.SavedFlags & 0x300 ) != 0 ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "That has already beem imbued." ); + return; + } + + bool canImbue = ( item is Spellbook || item is BaseClothing || item is BaseArmor || item is BaseWeapon ) && ( item.Name == null ); + + if ( canImbue ) + { + if ( !CheckInvoke( from ) ) + return; + + item.Hue = Ethic.Hero.Definition.PrimaryHue; + item.SavedFlags |= 0x100; + + from.Mobile.FixedEffect( 0x375A, 10, 20 ); + from.Mobile.PlaySound( 0x209 ); + + FinishInvoke( from ); + } + else + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You may not imbue that." ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/HolySense.cs b/Scripts/Engines/Ethics/Hero/Powers/HolySense.cs new file mode 100644 index 000000000..6f75167ad --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/HolySense.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Hero +{ + public sealed class HolySense : Power + { + public HolySense() + { + m_Definition = new PowerDefinition( + 0, + "Holy Sense", + "Drewrok Erstok", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + Ethic opposition = Ethic.Evil; + + int enemyCount = 0; + + int maxRange = 18 + from.Power; + + Player primary = null; + + foreach ( Player pl in opposition.Players ) + { + Mobile mob = pl.Mobile; + + if ( mob == null || mob.Map != from.Mobile.Map || !mob.Alive ) + continue; + + if ( !mob.InRange( from.Mobile, Math.Max( 18, maxRange - pl.Power ) ) ) + continue; + + if ( primary == null || pl.Power > primary.Power ) + primary = pl; + + ++enemyCount; + } + + StringBuilder sb = new StringBuilder(); + + sb.Append( "You sense " ); + sb.Append( enemyCount == 0 ? "no" : enemyCount.ToString() ); + sb.Append( enemyCount == 1 ? " enemy" : " enemies" ); + + if ( primary != null ) + { + sb.Append( ", and a strong presense" ); + + switch ( from.Mobile.GetDirectionTo( primary.Mobile ) ) + { + case Direction.West: + sb.Append( " to the west." ); + break; + case Direction.East: + sb.Append( " to the east." ); + break; + case Direction.North: + sb.Append( " to the north." ); + break; + case Direction.South: + sb.Append( " to the south." ); + break; + + case Direction.Up: + sb.Append( " to the north-west." ); + break; + case Direction.Down: + sb.Append( " to the south-east." ); + break; + case Direction.Left: + sb.Append( " to the south-west." ); + break; + case Direction.Right: + sb.Append( " to the north-east." ); + break; + } + } + else + { + sb.Append( '.' ); + } + + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x59, false, sb.ToString() ); + + FinishInvoke( from ); + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/HolyShield.cs b/Scripts/Engines/Ethics/Hero/Powers/HolyShield.cs new file mode 100644 index 000000000..80ad0e6a3 --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/HolyShield.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Hero +{ + public sealed class HolyShield : Power + { + public HolyShield() + { + m_Definition = new PowerDefinition( + 20, + "Holy Shield", + "Erstok K'blac", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + if ( from.IsShielded ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You are already under the protection of a holy shield." ); + return; + } + + from.BeginShield(); + + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You are now under the protection of a holy shield." ); + + FinishInvoke( from ); + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/HolySteed.cs b/Scripts/Engines/Ethics/Hero/Powers/HolySteed.cs new file mode 100644 index 000000000..5e019f5bb --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/HolySteed.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Mobiles; + +namespace Server.Ethics.Hero +{ + public sealed class HolySteed : Power + { + public HolySteed() + { + m_Definition = new PowerDefinition( + 30, + "Holy Steed", + "Trubechs Yeliab", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + if ( from.Steed != null && from.Steed.Deleted ) + from.Steed = null; + + if ( from.Steed != null ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You already have a holy steed." ); + return; + } + + if ( ( from.Mobile.Followers + 1 ) > from.Mobile.FollowersMax ) + { + from.Mobile.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return; + } + + Mobiles.HolySteed steed = new Mobiles.HolySteed(); + + if ( Mobiles.BaseCreature.Summon( steed, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours( 1.0 ) ) ) + { + from.Steed = steed; + + FinishInvoke( from ); + } + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/HolyWord.cs b/Scripts/Engines/Ethics/Hero/Powers/HolyWord.cs new file mode 100644 index 000000000..8e98e1243 --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/HolyWord.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Server.Ethics.Hero +{ + public sealed class HolyWord : Power + { + public HolyWord() + { + m_Definition = new PowerDefinition( + 100, + "Holy Word", + "Erstok Oostrac", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + } + } +} diff --git a/Scripts/Engines/Ethics/Hero/Powers/SummonFamiliar.cs b/Scripts/Engines/Ethics/Hero/Powers/SummonFamiliar.cs new file mode 100644 index 000000000..8ebc770b4 --- /dev/null +++ b/Scripts/Engines/Ethics/Hero/Powers/SummonFamiliar.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Server.Mobiles; + +namespace Server.Ethics.Hero +{ + public sealed class SummonFamiliar : Power + { + public SummonFamiliar() + { + m_Definition = new PowerDefinition( + 5, + "Summon Familiar", + "Trubechs Vingir", + "" + ); + } + + public override void BeginInvoke( Player from ) + { + if ( from.Familiar != null && from.Familiar.Deleted ) + from.Familiar = null; + + if ( from.Familiar != null ) + { + from.Mobile.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, false, "You already have a holy familiar." ); + return; + } + + if ( ( from.Mobile.Followers + 1 ) > from.Mobile.FollowersMax ) + { + from.Mobile.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return; + } + + HolyFamiliar familiar = new HolyFamiliar(); + + if ( Mobiles.BaseCreature.Summon( familiar, from.Mobile, from.Mobile.Location, 0x217, TimeSpan.FromHours( 1.0 ) ) ) + { + from.Familiar = familiar; + + FinishInvoke( from ); + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/CandidateCollection.cs b/Scripts/Engines/Factions/Collections/CandidateCollection.cs new file mode 100644 index 000000000..93ce4d0f9 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/CandidateCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.Candidate. + /// + public class CandidateCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public CandidateCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.Candidate at a specific position in the CandidateCollection. + /// + public Server.Factions.Candidate this[int index] + { + get + { + return ((Server.Factions.Candidate)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.Candidate entry to this collection. + /// + /// Server.Factions.Candidate instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.Candidate value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.Candidate instance is in this collection. + /// + /// Server.Factions.Candidate instance to search for. + /// True if the Server.Factions.Candidate instance is in the collection; otherwise false. + public bool Contains(Server.Factions.Candidate value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.Candidate instance is in this collection. + /// + /// Server.Factions.Candidate instance to find. + /// The zero-based index of the specified Server.Factions.Candidate instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.Candidate value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.Candidate instance from this collection. + /// + /// The Server.Factions.Candidate instance to remove. + public void Remove(Server.Factions.Candidate value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.Candidate instance. + /// + /// An Server.Factions.Candidate's enumerator. + public new CandidateCollectionEnumerator GetEnumerator() + { + return new CandidateCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.Candidate instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.Candidate instance to insert. + public void Insert(int index, Server.Factions.Candidate value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.Candidate. + /// + public class CandidateCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.Candidate _currentElement; + + /// + /// Collection to enumerate. + /// + private CandidateCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal CandidateCollectionEnumerator(CandidateCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.Candidate object in the enumerated CandidateCollection currently indexed by this instance. + /// + public Server.Factions.Candidate Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/FactionCollection.cs b/Scripts/Engines/Factions/Collections/FactionCollection.cs new file mode 100644 index 000000000..afcab53a1 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/FactionCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.Faction. + /// + public class FactionCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public FactionCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.Faction at a specific position in the FactionCollection. + /// + public Server.Factions.Faction this[int index] + { + get + { + return ((Server.Factions.Faction)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.Faction entry to this collection. + /// + /// Server.Factions.Faction instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.Faction value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.Faction instance is in this collection. + /// + /// Server.Factions.Faction instance to search for. + /// True if the Server.Factions.Faction instance is in the collection; otherwise false. + public bool Contains(Server.Factions.Faction value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.Faction instance is in this collection. + /// + /// Server.Factions.Faction instance to find. + /// The zero-based index of the specified Server.Factions.Faction instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.Faction value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.Faction instance from this collection. + /// + /// The Server.Factions.Faction instance to remove. + public void Remove(Server.Factions.Faction value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.Faction instance. + /// + /// An Server.Factions.Faction's enumerator. + public new FactionCollectionEnumerator GetEnumerator() + { + return new FactionCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.Faction instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.Faction instance to insert. + public void Insert(int index, Server.Factions.Faction value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.Faction. + /// + public class FactionCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.Faction _currentElement; + + /// + /// Collection to enumerate. + /// + private FactionCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal FactionCollectionEnumerator(FactionCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.Faction object in the enumerated FactionCollection currently indexed by this instance. + /// + public Server.Factions.Faction Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/FactionGuardCollection.cs b/Scripts/Engines/Factions/Collections/FactionGuardCollection.cs new file mode 100644 index 000000000..24eecfa34 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/FactionGuardCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.BaseFactionGuard. + /// + public class FactionGuardCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public FactionGuardCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.BaseFactionGuard at a specific position in the FactionGuardCollection. + /// + public Server.Factions.BaseFactionGuard this[int index] + { + get + { + return ((Server.Factions.BaseFactionGuard)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.BaseFactionGuard entry to this collection. + /// + /// Server.Factions.BaseFactionGuard instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.BaseFactionGuard value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.BaseFactionGuard instance is in this collection. + /// + /// Server.Factions.BaseFactionGuard instance to search for. + /// True if the Server.Factions.BaseFactionGuard instance is in the collection; otherwise false. + public bool Contains(Server.Factions.BaseFactionGuard value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.BaseFactionGuard instance is in this collection. + /// + /// Server.Factions.BaseFactionGuard instance to find. + /// The zero-based index of the specified Server.Factions.BaseFactionGuard instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.BaseFactionGuard value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.BaseFactionGuard instance from this collection. + /// + /// The Server.Factions.BaseFactionGuard instance to remove. + public void Remove(Server.Factions.BaseFactionGuard value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.BaseFactionGuard instance. + /// + /// An Server.Factions.BaseFactionGuard's enumerator. + public new FactionGuardCollectionEnumerator GetEnumerator() + { + return new FactionGuardCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.BaseFactionGuard instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.BaseFactionGuard instance to insert. + public void Insert(int index, Server.Factions.BaseFactionGuard value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.BaseFactionGuard. + /// + public class FactionGuardCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.BaseFactionGuard _currentElement; + + /// + /// Collection to enumerate. + /// + private FactionGuardCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal FactionGuardCollectionEnumerator(FactionGuardCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.BaseFactionGuard object in the enumerated FactionGuardCollection currently indexed by this instance. + /// + public Server.Factions.BaseFactionGuard Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/FactionItemCollection.cs b/Scripts/Engines/Factions/Collections/FactionItemCollection.cs new file mode 100644 index 000000000..10cd9c102 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/FactionItemCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.FactionItem. + /// + public class FactionItemCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public FactionItemCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.FactionItem at a specific position in the FactionItemCollection. + /// + public Server.Factions.FactionItem this[int index] + { + get + { + return ((Server.Factions.FactionItem)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.FactionItem entry to this collection. + /// + /// Server.Factions.FactionItem instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.FactionItem value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.FactionItem instance is in this collection. + /// + /// Server.Factions.FactionItem instance to search for. + /// True if the Server.Factions.FactionItem instance is in the collection; otherwise false. + public bool Contains(Server.Factions.FactionItem value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.FactionItem instance is in this collection. + /// + /// Server.Factions.FactionItem instance to find. + /// The zero-based index of the specified Server.Factions.FactionItem instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.FactionItem value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.FactionItem instance from this collection. + /// + /// The Server.Factions.FactionItem instance to remove. + public void Remove(Server.Factions.FactionItem value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.FactionItem instance. + /// + /// An Server.Factions.FactionItem's enumerator. + public new FactionItemCollectionEnumerator GetEnumerator() + { + return new FactionItemCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.FactionItem instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.FactionItem instance to insert. + public void Insert(int index, Server.Factions.FactionItem value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.FactionItem. + /// + public class FactionItemCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.FactionItem _currentElement; + + /// + /// Collection to enumerate. + /// + private FactionItemCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal FactionItemCollectionEnumerator(FactionItemCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.FactionItem object in the enumerated FactionItemCollection currently indexed by this instance. + /// + public Server.Factions.FactionItem Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/FactionTrapCollection.cs b/Scripts/Engines/Factions/Collections/FactionTrapCollection.cs new file mode 100644 index 000000000..e7356f783 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/FactionTrapCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.BaseFactionTrap. + /// + public class FactionTrapCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public FactionTrapCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.BaseFactionTrap at a specific position in the FactionTrapCollection. + /// + public Server.Factions.BaseFactionTrap this[int index] + { + get + { + return ((Server.Factions.BaseFactionTrap)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.BaseFactionTrap entry to this collection. + /// + /// Server.Factions.BaseFactionTrap instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.BaseFactionTrap value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.BaseFactionTrap instance is in this collection. + /// + /// Server.Factions.BaseFactionTrap instance to search for. + /// True if the Server.Factions.BaseFactionTrap instance is in the collection; otherwise false. + public bool Contains(Server.Factions.BaseFactionTrap value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.BaseFactionTrap instance is in this collection. + /// + /// Server.Factions.BaseFactionTrap instance to find. + /// The zero-based index of the specified Server.Factions.BaseFactionTrap instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.BaseFactionTrap value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.BaseFactionTrap instance from this collection. + /// + /// The Server.Factions.BaseFactionTrap instance to remove. + public void Remove(Server.Factions.BaseFactionTrap value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.BaseFactionTrap instance. + /// + /// An Server.Factions.BaseFactionTrap's enumerator. + public new FactionTrapCollectionEnumerator GetEnumerator() + { + return new FactionTrapCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.BaseFactionTrap instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.BaseFactionTrap instance to insert. + public void Insert(int index, Server.Factions.BaseFactionTrap value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.BaseFactionTrap. + /// + public class FactionTrapCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.BaseFactionTrap _currentElement; + + /// + /// Collection to enumerate. + /// + private FactionTrapCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal FactionTrapCollectionEnumerator(FactionTrapCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.BaseFactionTrap object in the enumerated FactionTrapCollection currently indexed by this instance. + /// + public Server.Factions.BaseFactionTrap Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/FactionVendorCollection.cs b/Scripts/Engines/Factions/Collections/FactionVendorCollection.cs new file mode 100644 index 000000000..55c15b5f5 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/FactionVendorCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.BaseFactionVendor. + /// + public class FactionVendorCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public FactionVendorCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.BaseFactionVendor at a specific position in the FactionVendorCollection. + /// + public Server.Factions.BaseFactionVendor this[int index] + { + get + { + return ((Server.Factions.BaseFactionVendor)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.BaseFactionVendor entry to this collection. + /// + /// Server.Factions.BaseFactionVendor instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.BaseFactionVendor value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.BaseFactionVendor instance is in this collection. + /// + /// Server.Factions.BaseFactionVendor instance to search for. + /// True if the Server.Factions.BaseFactionVendor instance is in the collection; otherwise false. + public bool Contains(Server.Factions.BaseFactionVendor value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.BaseFactionVendor instance is in this collection. + /// + /// Server.Factions.BaseFactionVendor instance to find. + /// The zero-based index of the specified Server.Factions.BaseFactionVendor instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.BaseFactionVendor value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.BaseFactionVendor instance from this collection. + /// + /// The Server.Factions.BaseFactionVendor instance to remove. + public void Remove(Server.Factions.BaseFactionVendor value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.BaseFactionVendor instance. + /// + /// An Server.Factions.BaseFactionVendor's enumerator. + public new FactionVendorCollectionEnumerator GetEnumerator() + { + return new FactionVendorCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.BaseFactionVendor instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.BaseFactionVendor instance to insert. + public void Insert(int index, Server.Factions.BaseFactionVendor value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.BaseFactionVendor. + /// + public class FactionVendorCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.BaseFactionVendor _currentElement; + + /// + /// Collection to enumerate. + /// + private FactionVendorCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal FactionVendorCollectionEnumerator(FactionVendorCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.BaseFactionVendor object in the enumerated FactionVendorCollection currently indexed by this instance. + /// + public Server.Factions.BaseFactionVendor Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/GuardListCollection.cs b/Scripts/Engines/Factions/Collections/GuardListCollection.cs new file mode 100644 index 000000000..283b28d67 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/GuardListCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.GuardList. + /// + public class GuardListCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public GuardListCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.GuardList at a specific position in the GuardListCollection. + /// + public Server.Factions.GuardList this[int index] + { + get + { + return ((Server.Factions.GuardList)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.GuardList entry to this collection. + /// + /// Server.Factions.GuardList instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.GuardList value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.GuardList instance is in this collection. + /// + /// Server.Factions.GuardList instance to search for. + /// True if the Server.Factions.GuardList instance is in the collection; otherwise false. + public bool Contains(Server.Factions.GuardList value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.GuardList instance is in this collection. + /// + /// Server.Factions.GuardList instance to find. + /// The zero-based index of the specified Server.Factions.GuardList instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.GuardList value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.GuardList instance from this collection. + /// + /// The Server.Factions.GuardList instance to remove. + public void Remove(Server.Factions.GuardList value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.GuardList instance. + /// + /// An Server.Factions.GuardList's enumerator. + public new GuardListCollectionEnumerator GetEnumerator() + { + return new GuardListCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.GuardList instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.GuardList instance to insert. + public void Insert(int index, Server.Factions.GuardList value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.GuardList. + /// + public class GuardListCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.GuardList _currentElement; + + /// + /// Collection to enumerate. + /// + private GuardListCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal GuardListCollectionEnumerator(GuardListCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.GuardList object in the enumerated GuardListCollection currently indexed by this instance. + /// + public Server.Factions.GuardList Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/MonolithCollection.cs b/Scripts/Engines/Factions/Collections/MonolithCollection.cs new file mode 100644 index 000000000..b28f0fdfd --- /dev/null +++ b/Scripts/Engines/Factions/Collections/MonolithCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.BaseMonolith. + /// + public class MonolithCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public MonolithCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.BaseMonolith at a specific position in the MonolithCollection. + /// + public Server.Factions.BaseMonolith this[int index] + { + get + { + return ((Server.Factions.BaseMonolith)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.BaseMonolith entry to this collection. + /// + /// Server.Factions.BaseMonolith instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.BaseMonolith value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.BaseMonolith instance is in this collection. + /// + /// Server.Factions.BaseMonolith instance to search for. + /// True if the Server.Factions.BaseMonolith instance is in the collection; otherwise false. + public bool Contains(Server.Factions.BaseMonolith value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.BaseMonolith instance is in this collection. + /// + /// Server.Factions.BaseMonolith instance to find. + /// The zero-based index of the specified Server.Factions.BaseMonolith instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.BaseMonolith value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.BaseMonolith instance from this collection. + /// + /// The Server.Factions.BaseMonolith instance to remove. + public void Remove(Server.Factions.BaseMonolith value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.BaseMonolith instance. + /// + /// An Server.Factions.BaseMonolith's enumerator. + public new MonolithCollectionEnumerator GetEnumerator() + { + return new MonolithCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.BaseMonolith instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.BaseMonolith instance to insert. + public void Insert(int index, Server.Factions.BaseMonolith value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.BaseMonolith. + /// + public class MonolithCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.BaseMonolith _currentElement; + + /// + /// Collection to enumerate. + /// + private MonolithCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal MonolithCollectionEnumerator(MonolithCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.BaseMonolith object in the enumerated MonolithCollection currently indexed by this instance. + /// + public Server.Factions.BaseMonolith Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/PlayerStateCollection.cs b/Scripts/Engines/Factions/Collections/PlayerStateCollection.cs new file mode 100644 index 000000000..a68fa49d8 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/PlayerStateCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.PlayerState. + /// + public class PlayerStateCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public PlayerStateCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.PlayerState at a specific position in the PlayerStateCollection. + /// + public Server.Factions.PlayerState this[int index] + { + get + { + return ((Server.Factions.PlayerState)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.PlayerState entry to this collection. + /// + /// Server.Factions.PlayerState instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.PlayerState value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.PlayerState instance is in this collection. + /// + /// Server.Factions.PlayerState instance to search for. + /// True if the Server.Factions.PlayerState instance is in the collection; otherwise false. + public bool Contains(Server.Factions.PlayerState value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.PlayerState instance is in this collection. + /// + /// Server.Factions.PlayerState instance to find. + /// The zero-based index of the specified Server.Factions.PlayerState instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.PlayerState value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.PlayerState instance from this collection. + /// + /// The Server.Factions.PlayerState instance to remove. + public void Remove(Server.Factions.PlayerState value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.PlayerState instance. + /// + /// An Server.Factions.PlayerState's enumerator. + public new PlayerStateCollectionEnumerator GetEnumerator() + { + return new PlayerStateCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.PlayerState instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.PlayerState instance to insert. + public void Insert(int index, Server.Factions.PlayerState value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.PlayerState. + /// + public class PlayerStateCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.PlayerState _currentElement; + + /// + /// Collection to enumerate. + /// + private PlayerStateCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal PlayerStateCollectionEnumerator(PlayerStateCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.PlayerState object in the enumerated PlayerStateCollection currently indexed by this instance. + /// + public Server.Factions.PlayerState Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/SigilCollection.cs b/Scripts/Engines/Factions/Collections/SigilCollection.cs new file mode 100644 index 000000000..5898d8472 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/SigilCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.Sigil. + /// + public class SigilCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public SigilCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.Sigil at a specific position in the SigilCollection. + /// + public Server.Factions.Sigil this[int index] + { + get + { + return ((Server.Factions.Sigil)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.Sigil entry to this collection. + /// + /// Server.Factions.Sigil instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.Sigil value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.Sigil instance is in this collection. + /// + /// Server.Factions.Sigil instance to search for. + /// True if the Server.Factions.Sigil instance is in the collection; otherwise false. + public bool Contains(Server.Factions.Sigil value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.Sigil instance is in this collection. + /// + /// Server.Factions.Sigil instance to find. + /// The zero-based index of the specified Server.Factions.Sigil instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.Sigil value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.Sigil instance from this collection. + /// + /// The Server.Factions.Sigil instance to remove. + public void Remove(Server.Factions.Sigil value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.Sigil instance. + /// + /// An Server.Factions.Sigil's enumerator. + public new SigilCollectionEnumerator GetEnumerator() + { + return new SigilCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.Sigil instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.Sigil instance to insert. + public void Insert(int index, Server.Factions.Sigil value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.Sigil. + /// + public class SigilCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.Sigil _currentElement; + + /// + /// Collection to enumerate. + /// + private SigilCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal SigilCollectionEnumerator(SigilCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.Sigil object in the enumerated SigilCollection currently indexed by this instance. + /// + public Server.Factions.Sigil Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/SilverGivenCollection.cs b/Scripts/Engines/Factions/Collections/SilverGivenCollection.cs new file mode 100644 index 000000000..ccc2d7fa9 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/SilverGivenCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.SilverGivenEntry. + /// + public class SilverGivenCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public SilverGivenCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.SilverGivenEntry at a specific position in the SilverGivenCollection. + /// + public Server.Factions.SilverGivenEntry this[int index] + { + get + { + return ((Server.Factions.SilverGivenEntry)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.SilverGivenEntry entry to this collection. + /// + /// Server.Factions.SilverGivenEntry instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.SilverGivenEntry value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.SilverGivenEntry instance is in this collection. + /// + /// Server.Factions.SilverGivenEntry instance to search for. + /// True if the Server.Factions.SilverGivenEntry instance is in the collection; otherwise false. + public bool Contains(Server.Factions.SilverGivenEntry value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.SilverGivenEntry instance is in this collection. + /// + /// Server.Factions.SilverGivenEntry instance to find. + /// The zero-based index of the specified Server.Factions.SilverGivenEntry instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.SilverGivenEntry value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.SilverGivenEntry instance from this collection. + /// + /// The Server.Factions.SilverGivenEntry instance to remove. + public void Remove(Server.Factions.SilverGivenEntry value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.SilverGivenEntry instance. + /// + /// An Server.Factions.SilverGivenEntry's enumerator. + public new SilverGivenCollectionEnumerator GetEnumerator() + { + return new SilverGivenCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.SilverGivenEntry instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.SilverGivenEntry instance to insert. + public void Insert(int index, Server.Factions.SilverGivenEntry value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.SilverGivenEntry. + /// + public class SilverGivenCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.SilverGivenEntry _currentElement; + + /// + /// Collection to enumerate. + /// + private SilverGivenCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal SilverGivenCollectionEnumerator(SilverGivenCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.SilverGivenEntry object in the enumerated SilverGivenCollection currently indexed by this instance. + /// + public Server.Factions.SilverGivenEntry Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/TownCollection.cs b/Scripts/Engines/Factions/Collections/TownCollection.cs new file mode 100644 index 000000000..f3e901054 --- /dev/null +++ b/Scripts/Engines/Factions/Collections/TownCollection.cs @@ -0,0 +1,199 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.Town. + /// + public class TownCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public TownCollection() + { + } + + /// + /// Gets or sets the value of the Server.Factions.Town at a specific position in the TownCollection. + /// + public Server.Factions.Town this[int index] + { + get + { + return ((Server.Factions.Town)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.Town entry to this collection. + /// + /// Server.Factions.Town instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.Town value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.Town instance is in this collection. + /// + /// Server.Factions.Town instance to search for. + /// True if the Server.Factions.Town instance is in the collection; otherwise false. + public bool Contains(Server.Factions.Town value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.Town instance is in this collection. + /// + /// Server.Factions.Town instance to find. + /// The zero-based index of the specified Server.Factions.Town instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.Town value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.Town instance from this collection. + /// + /// The Server.Factions.Town instance to remove. + public void Remove(Server.Factions.Town value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.Town instance. + /// + /// An Server.Factions.Town's enumerator. + public new TownCollectionEnumerator GetEnumerator() + { + return new TownCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.Town instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.Town instance to insert. + public void Insert(int index, Server.Factions.Town value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.Town. + /// + public class TownCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.Town _currentElement; + + /// + /// Collection to enumerate. + /// + private TownCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal TownCollectionEnumerator(TownCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.Town object in the enumerated TownCollection currently indexed by this instance. + /// + public Server.Factions.Town Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Collections/VendorListCollection.cs b/Scripts/Engines/Factions/Collections/VendorListCollection.cs new file mode 100644 index 000000000..a110acf8e --- /dev/null +++ b/Scripts/Engines/Factions/Collections/VendorListCollection.cs @@ -0,0 +1,200 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Factions +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Factions.VendorList. + /// + public class VendorListCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public VendorListCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Factions.VendorList at a specific position in the VendorListCollection. + /// + public Server.Factions.VendorList this[int index] + { + get + { + return ((Server.Factions.VendorList)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Factions.VendorList entry to this collection. + /// + /// Server.Factions.VendorList instance. + /// The position into which the new element was inserted. + public int Add(Server.Factions.VendorList value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Factions.VendorList instance is in this collection. + /// + /// Server.Factions.VendorList instance to search for. + /// True if the Server.Factions.VendorList instance is in the collection; otherwise false. + public bool Contains(Server.Factions.VendorList value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Factions.VendorList instance is in this collection. + /// + /// Server.Factions.VendorList instance to find. + /// The zero-based index of the specified Server.Factions.VendorList instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Factions.VendorList value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Factions.VendorList instance from this collection. + /// + /// The Server.Factions.VendorList instance to remove. + public void Remove(Server.Factions.VendorList value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Factions.VendorList instance. + /// + /// An Server.Factions.VendorList's enumerator. + public new VendorListCollectionEnumerator GetEnumerator() + { + return new VendorListCollectionEnumerator(this); + } + + /// + /// Insert a Server.Factions.VendorList instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Factions.VendorList instance to insert. + public void Insert(int index, Server.Factions.VendorList value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Factions.VendorList. + /// + public class VendorListCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Factions.VendorList _currentElement; + + /// + /// Collection to enumerate. + /// + private VendorListCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal VendorListCollectionEnumerator(VendorListCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Factions.VendorList object in the enumerated VendorListCollection currently indexed by this instance. + /// + public Server.Factions.VendorList Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Factions/Core/Election.cs b/Scripts/Engines/Factions/Core/Election.cs new file mode 100644 index 000000000..45eb2d948 --- /dev/null +++ b/Scripts/Engines/Factions/Core/Election.cs @@ -0,0 +1,563 @@ +using System; +using System.Net; +using System.Collections; +using Server; +using Server.Mobiles; + +namespace Server.Factions +{ + public class Election + { + public static readonly TimeSpan PendingPeriod = TimeSpan.FromDays( 5.0 ); + public static readonly TimeSpan CampaignPeriod = TimeSpan.FromDays( 1.0 ); + public static readonly TimeSpan VotingPeriod = TimeSpan.FromDays( 3.0 ); + + public const int MaxCandidates = 10; + public const int CandidateRank = 5; + + private Faction m_Faction; + private CandidateCollection m_Candidates; + + private ElectionState m_State; + private DateTime m_LastStateTime; + + public Faction Faction{ get{ return m_Faction; } } + + public CandidateCollection Candidates{ get{ return m_Candidates; } } + + public ElectionState State{ get{ return m_State; } set{ m_State = value; m_LastStateTime = DateTime.Now; } } + public DateTime LastStateTime{ get{ return m_LastStateTime; } } + + [CommandProperty( AccessLevel.GameMaster )] + public ElectionState CurrentState{ get{ return m_State; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public TimeSpan NextStateTime + { + get + { + TimeSpan period; + + switch ( m_State ) + { + default: + case ElectionState.Pending: period = PendingPeriod; break; + case ElectionState.Election: period = VotingPeriod; break; + case ElectionState.Campaign: period = CampaignPeriod; break; + } + + TimeSpan until = (m_LastStateTime + period) - DateTime.Now; + + if ( until < TimeSpan.Zero ) + until = TimeSpan.Zero; + + return until; + } + set + { + TimeSpan period; + + switch ( m_State ) + { + default: + case ElectionState.Pending: period = PendingPeriod; break; + case ElectionState.Election: period = VotingPeriod; break; + case ElectionState.Campaign: period = CampaignPeriod; break; + } + + m_LastStateTime = DateTime.Now - period + value; + } + } + + private Timer m_Timer; + + public void StartTimer() + { + m_Timer = Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.0 ), new TimerCallback( Slice ) ); + } + + public Election( Faction faction ) + { + m_Faction = faction; + m_Candidates = new CandidateCollection(); + + StartTimer(); + } + + public Election( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_Faction = Faction.ReadReference( reader ); + + m_LastStateTime = reader.ReadDateTime(); + m_State = (ElectionState)reader.ReadEncodedInt(); + + m_Candidates = new CandidateCollection(); + + int count = reader.ReadEncodedInt(); + + for ( int i = 0; i < count; ++i ) + { + Candidate cd = new Candidate( reader ); + + if ( cd.Mobile != null ) + m_Candidates.Add( cd ); + } + + break; + } + } + + StartTimer(); + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + + writer.Write( (DateTime) m_LastStateTime ); + writer.WriteEncodedInt( (int) m_State ); + + writer.WriteEncodedInt( m_Candidates.Count ); + + for ( int i = 0; i < m_Candidates.Count; ++i ) + m_Candidates[i].Serialize( writer ); + } + + public void AddCandidate( Mobile mob ) + { + if ( IsCandidate( mob ) ) + return; + + m_Candidates.Add( new Candidate( mob ) ); + mob.SendLocalizedMessage( 1010117 ); // You are now running for office. + } + + public void RemoveVoter( Mobile mob ) + { + if ( m_State == ElectionState.Election ) + { + for ( int i = 0; i < m_Candidates.Count; ++i ) + { + ArrayList voters = m_Candidates[i].Voters; + + for ( int j = 0; j < voters.Count; ++j ) + { + Voter voter = (Voter)voters[j]; + + if ( voter.From == mob ) + voters.RemoveAt( j-- ); + } + } + } + } + + public void RemoveCandidate( Mobile mob ) + { + Candidate cd = FindCandidate( mob ); + + if ( cd == null ) + return; + + m_Candidates.Remove( cd ); + mob.SendLocalizedMessage( 1038031 ); + + if ( m_State == ElectionState.Election ) + { + if ( m_Candidates.Count == 1 ) + { + m_Faction.Broadcast( 1038031 ); // There are no longer any valid candidates in the Faction Commander election. + + Candidate winner = m_Candidates[0]; + + Mobile winMob = winner.Mobile; + PlayerState pl = PlayerState.Find( winMob ); + + if ( pl == null || pl.Faction != m_Faction || winMob == m_Faction.Commander ) + { + m_Faction.Broadcast( 1038026 ); // Faction leadership has not changed. + } + else + { + m_Faction.Broadcast( 1038028 ); // The faction has a new commander. + m_Faction.Commander = winMob; + } + + m_Candidates.Clear(); + State = ElectionState.Pending; + } + else if ( m_Candidates.Count == 0 ) // well, I guess this'll never happen + { + m_Faction.Broadcast( 1038031 ); // There are no longer any valid candidates in the Faction Commander election. + + m_Candidates.Clear(); + State = ElectionState.Pending; + } + } + } + + public bool IsCandidate( Mobile mob ) + { + return ( FindCandidate( mob ) != null ); + } + + public bool CanVote( Mobile mob ) + { + return ( m_State == ElectionState.Election && !HasVoted( mob ) ); + } + + public bool HasVoted( Mobile mob ) + { + return ( FindVoter( mob ) != null ); + } + + public Candidate FindCandidate( Mobile mob ) + { + for ( int i = 0; i < m_Candidates.Count; ++i ) + { + if ( m_Candidates[i].Mobile == mob ) + return m_Candidates[i]; + } + + return null; + } + + public Candidate FindVoter( Mobile mob ) + { + for ( int i = 0; i < m_Candidates.Count; ++i ) + { + ArrayList voters = m_Candidates[i].Voters; + + for ( int j = 0; j < voters.Count; ++j ) + { + Voter voter = (Voter)voters[j]; + + if ( voter.From == mob ) + return m_Candidates[i]; + } + } + + return null; + } + + public bool CanBeCandidate( Mobile mob ) + { + if ( IsCandidate( mob ) ) + return false; + + if ( m_Candidates.Count >= MaxCandidates ) + return false; + + if ( m_State != ElectionState.Campaign ) + return false; // sanity.. + + PlayerState pl = PlayerState.Find( mob ); + + return ( pl != null && pl.Faction == m_Faction && pl.Rank.Rank >= CandidateRank ); + } + + public void Slice() + { + if ( m_Faction.Election != this ) + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + return; + } + + switch ( m_State ) + { + case ElectionState.Pending: + { + if ( (m_LastStateTime + PendingPeriod) > DateTime.Now ) + break; + + m_Faction.Broadcast( 1038023 ); // Campaigning for the Faction Commander election has begun. + + m_Candidates.Clear(); + State = ElectionState.Campaign; + + break; + } + case ElectionState.Campaign: + { + if ( (m_LastStateTime + CampaignPeriod) > DateTime.Now ) + break; + + if ( m_Candidates.Count == 0 ) + { + m_Faction.Broadcast( 1038025 ); // Nobody ran for office. + State = ElectionState.Pending; + } + else if ( m_Candidates.Count == 1 ) + { + m_Faction.Broadcast( 1038029 ); // Only one member ran for office. + + Candidate winner = m_Candidates[0]; + + Mobile mob = winner.Mobile; + PlayerState pl = PlayerState.Find( mob ); + + if ( pl == null || pl.Faction != m_Faction || mob == m_Faction.Commander ) + { + m_Faction.Broadcast( 1038026 ); // Faction leadership has not changed. + } + else + { + m_Faction.Broadcast( 1038028 ); // The faction has a new commander. + m_Faction.Commander = mob; + } + + m_Candidates.Clear(); + State = ElectionState.Pending; + } + else + { + m_Faction.Broadcast( 1038030 ); + State = ElectionState.Election; + } + + break; + } + case ElectionState.Election: + { + if ( (m_LastStateTime + VotingPeriod) > DateTime.Now ) + break; + + m_Faction.Broadcast( 1038024 ); // The results for the Faction Commander election are in + + Candidate winner = null; + + for ( int i = 0; i < m_Candidates.Count; ++i ) + { + Candidate cd = m_Candidates[i]; + + PlayerState pl = PlayerState.Find( cd.Mobile ); + + if ( pl == null || pl.Faction != m_Faction ) + continue; + + //cd.CleanMuleVotes(); + + if ( winner == null || cd.Votes > winner.Votes ) + winner = cd; + } + + if ( winner == null ) + { + m_Faction.Broadcast( 1038026 ); // Faction leadership has not changed. + } + else if ( winner.Mobile == m_Faction.Commander ) + { + m_Faction.Broadcast( 1038027 ); // The incumbent won the election. + } + else + { + m_Faction.Broadcast( 1038028 ); // The faction has a new commander. + m_Faction.Commander = winner.Mobile; + } + + m_Candidates.Clear(); + State = ElectionState.Pending; + + break; + } + } + } + } + + public class Voter + { + private Mobile m_From; + private Mobile m_Candidate; + + private IPAddress m_Address; + private DateTime m_Time; + + public Mobile From + { + get{ return m_From; } + } + + public Mobile Candidate + { + get{ return m_Candidate; } + } + + public IPAddress Address + { + get{ return m_Address; } + } + + public DateTime Time + { + get{ return m_Time; } + } + + public object[] AcquireFields() + { + TimeSpan gameTime = TimeSpan.Zero; + + if ( m_From is PlayerMobile ) + gameTime = ((PlayerMobile)m_From).GameTime; + + int kp = 0; + + PlayerState pl = PlayerState.Find( m_From ); + + if ( pl != null ) + kp = pl.KillPoints; + + int sk = m_From.Skills.Total; + + int factorSkills = 50 + ( (sk * 100 ) / 10000 ); + int factorKillPts = 100 + (kp*2); + int factorGameTime = 50 + (int) ( (gameTime.Ticks * 100) / TimeSpan.TicksPerDay ); + + int totalFactor = ( factorSkills * factorKillPts * Math.Max( factorGameTime, 100 ) ) / 10000; + + if ( totalFactor > 100 ) + totalFactor = 100; + else if ( totalFactor < 0 ) + totalFactor = 0; + + return new object[]{ m_From, m_Address, m_Time, totalFactor }; + } + + public Voter( Mobile from, Mobile candidate ) + { + m_From = from; + m_Candidate = candidate; + + if ( m_From.NetState != null ) + m_Address = m_From.NetState.Address; + else + m_Address = IPAddress.None; + + m_Time = DateTime.Now; + } + + public Voter( GenericReader reader, Mobile candidate ) + { + m_Candidate = candidate; + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_From = reader.ReadMobile(); + m_Address = reader.ReadIPAddress(); + m_Time = reader.ReadDateTime(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); + + writer.Write( (Mobile) m_From ); + writer.Write( (IPAddress) m_Address ); + writer.Write( (DateTime) m_Time ); + } + } + + public class Candidate + { + private Mobile m_Mobile; + private ArrayList m_Voters; + + public Mobile Mobile{ get{ return m_Mobile; } } + public ArrayList Voters{ get{ return m_Voters; } } + + public int Votes{ get{ return m_Voters.Count; } } + + public void CleanMuleVotes() + { + for ( int i = 0; i < m_Voters.Count; ++i ) + { + Voter voter = (Voter)m_Voters[i]; + + if ( (int)voter.AcquireFields()[3] < 90 ) + m_Voters.RemoveAt( i-- ); + } + } + + public Candidate( Mobile mob ) + { + m_Mobile = mob; + m_Voters = new ArrayList(); + } + + public Candidate( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Mobile = reader.ReadMobile(); + + int count = reader.ReadEncodedInt(); + m_Voters = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + Voter voter = new Voter( reader, m_Mobile ); + + if ( voter.From != null ) + m_Voters.Add( voter ); + } + + break; + } + case 0: + { + m_Mobile = reader.ReadMobile(); + + ArrayList mobs = reader.ReadMobileList(); + m_Voters = new ArrayList( mobs.Count ); + + for ( int i = 0; i < mobs.Count; ++i ) + m_Voters.Add( new Voter( (Mobile)mobs[i], m_Mobile ) ); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 1 ); // version + + writer.Write( (Mobile) m_Mobile ); + + writer.WriteEncodedInt( (int) m_Voters.Count ); + + for ( int i = 0; i < m_Voters.Count; ++i ) + ((Voter)m_Voters[i]).Serialize( writer ); + } + } + + public enum ElectionState + { + Pending, + Campaign, + Election + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/Faction.cs b/Scripts/Engines/Factions/Core/Faction.cs new file mode 100644 index 000000000..e2f1fd6a1 --- /dev/null +++ b/Scripts/Engines/Factions/Core/Faction.cs @@ -0,0 +1,1390 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Guilds; +using Server.Mobiles; +using Server.Prompts; +using Server.Targeting; +using Server.Accounting; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Factions +{ + [CustomEnum( new string[]{ "Minax", "Council of Mages", "True Britannians", "Shadowlords" } )] + public abstract class Faction : IComparable + { + private FactionDefinition m_Definition; + private FactionState m_State; + private StrongholdRegion m_StrongholdRegion; + + public StrongholdRegion StrongholdRegion + { + get{ return m_StrongholdRegion; } + set{ m_StrongholdRegion = value; } + } + + public FactionDefinition Definition + { + get{ return m_Definition; } + set + { + m_Definition = value; + m_StrongholdRegion = new StrongholdRegion( this ); + } + } + + public FactionState State + { + get{ return m_State; } + set{ m_State = value; } + } + + public Election Election + { + get{ return m_State.Election; } + set{ m_State.Election = value; } + } + + public Mobile Commander + { + get{ return m_State.Commander; } + set{ m_State.Commander = value; } + } + + public int Tithe + { + get{ return m_State.Tithe; } + set{ m_State.Tithe = value; } + } + + public int Silver + { + get{ return m_State.Silver; } + set{ m_State.Silver = value; } + } + + public PlayerStateCollection Members + { + get{ return m_State.Members; } + set{ m_State.Members = value; } + } + + public static readonly TimeSpan LeavePeriod = TimeSpan.FromDays( 7.0 ); + + public bool FactionMessageReady + { + get{ return m_State.FactionMessageReady; } + } + + public void Broadcast( string text ) + { + Broadcast( 0x3B2, text ); + } + + public void Broadcast( int hue, string text ) + { + PlayerStateCollection members = Members; + + for ( int i = 0; i < members.Count; ++i ) + members[i].Mobile.SendMessage( hue, text ); + } + + public void Broadcast( int number ) + { + PlayerStateCollection members = Members; + + for ( int i = 0; i < members.Count; ++i ) + members[i].Mobile.SendLocalizedMessage( number ); + } + + public void Broadcast( string format, params object[] args ) + { + Broadcast( String.Format( format, args ) ); + } + + public void Broadcast( int hue, string format, params object[] args ) + { + Broadcast( hue, String.Format( format, args ) ); + } + + public void BeginBroadcast( Mobile from ) + { + from.SendLocalizedMessage( 1010265 ); // Enter Faction Message + from.Prompt = new BroadcastPrompt( this ); + } + + public void EndBroadcast( Mobile from, string text ) + { + if ( from.AccessLevel == AccessLevel.Player ) + m_State.RegisterBroadcast(); + + Broadcast( Definition.HueBroadcast, "{0} [Commander] {1} : {2}", from.Name, Definition.FriendlyName, text ); + } + + private class BroadcastPrompt : Prompt + { + private Faction m_Faction; + + public BroadcastPrompt( Faction faction ) + { + m_Faction = faction; + } + + public override void OnResponse( Mobile from, string text ) + { + m_Faction.EndBroadcast( from, text ); + } + } + + public void BeginHonorLeadership( Mobile from ) + { + from.SendLocalizedMessage( 502090 ); // Click on the player whom you wish to honor. + from.BeginTarget( 12, false, TargetFlags.None, new TargetCallback( HonorLeadership_OnTarget ) ); + } + + public void HonorLeadership_OnTarget( Mobile from, object obj ) + { + if ( obj is Mobile ) + { + Mobile recv = (Mobile) obj; + + PlayerState giveState = PlayerState.Find( from ); + PlayerState recvState = PlayerState.Find( recv ); + + if ( giveState == null ) + return; + + if ( recvState == null || recvState.Faction != giveState.Faction ) + { + from.SendLocalizedMessage( 1042497 ); // Only faction mates can be honored this way. + } + else if ( giveState.KillPoints < 5 ) + { + from.SendLocalizedMessage( 1042499 ); // You must have at least five kill points to honor them. + } + else + { + giveState.KillPoints -= 5; + recvState.KillPoints += 4; + + // TODO: Confirm no message sent to giver + recv.SendLocalizedMessage( 1042500 ); // You have been honored with four kill points. + } + } + else + { + from.SendLocalizedMessage( 1042496 ); // You may only honor another player. + } + } + + public virtual void AddMember( Mobile mob ) + { + PlayerState ps = new PlayerState( mob, this, Members ); + Members.Add( ps ); + + mob.AddToBackpack( FactionItem.Imbue( new Robe(), this, false, Definition.HuePrimary ) ); + mob.SendLocalizedMessage( 1010374 ); // You have been granted a robe which signifies your faction + + mob.InvalidateProperties(); + mob.Delta( MobileDelta.Noto ); + + mob.FixedEffect( 0x373A, 10, 30 ); + mob.PlaySound( 0x209 ); + + OnRankUpdate( ps ); + } + + public static bool IsNearType( Mobile mob, Type type, int range ) + { + bool mobs = type.IsSubclassOf( typeof( Mobile ) ); + bool items = type.IsSubclassOf( typeof( Item ) ); + + IPooledEnumerable eable; + + if ( mobs ) + eable = mob.GetMobilesInRange( range ); + else if ( items ) + eable = mob.GetItemsInRange( range ); + else + return false; + + foreach ( object obj in eable ) + { + if ( type.IsAssignableFrom( obj.GetType() ) ) + { + eable.Free(); + return true; + } + } + + eable.Free(); + return false; + } + + public static bool IsNearType( Mobile mob, Type[] types, int range ) + { + /* + bool mobs = type.IsSubclassOf( typeof( Mobile ) ); + bool items = type.IsSubclassOf( typeof( Item ) ); + + IPooledEnumerable eable; + + if( mobs ) + eable = mob.GetMobilesInRange( range ); + else if( items ) + eable = mob.GetItemsInRange( range ); + else + return false; + * */ + + IPooledEnumerable eable = mob.GetObjectsInRange( range ); + + foreach( object obj in eable ) + { + Type objType = obj.GetType(); + + for( int i = 0; i < types.Length; i++ ) + { + if( types[i].IsAssignableFrom( objType ) ) + { + eable.Free(); + return true; + } + } + } + + eable.Free(); + return false; + } + + public void RemovePlayerState( PlayerState pl ) + { + if ( pl == null || !Members.Contains( pl ) ) + return; + + Members.Remove( pl ); + + PlayerMobile pm = (PlayerMobile) pl.Mobile; + if( pm == null ) + return; + + Mobile mob = pl.Mobile; + if ( pm.FactionPlayerState == pl ) + { + pm.FactionPlayerState = null; + + mob.InvalidateProperties(); + mob.Delta( MobileDelta.Noto ); + + if ( Election.IsCandidate( mob ) ) + Election.RemoveCandidate( mob ); + + if ( pl.Finance != null ) + pl.Finance.Finance = null; + + if ( pl.Sheriff != null ) + pl.Sheriff.Sheriff = null; + + Election.RemoveVoter( mob ); + + if ( Commander == mob ) + Commander = null; + + pm.ValidateEquipment(); + } + } + + public void RemoveMember( Mobile mob ) + { + PlayerState pl = PlayerState.Find( mob ); + + if ( pl == null || !Members.Contains( pl ) ) + return; + + if( mob.Backpack != null ) + { + //Ordinarily, through normal faction removal, this will never find any sigils. + //Only with a leave delay less than the ReturnPeriod or a Faction Kick/Ban, will this ever do anything + Item[] sigils = mob.Backpack.FindItemsByType( typeof( Sigil ) ); + + for ( int i = 0; i < sigils.Length; ++i ) + ((Sigil)sigils[i]).ReturnHome(); + } + + Members.Remove( pl ); + + if ( mob is PlayerMobile ) + ((PlayerMobile)mob).FactionPlayerState = null; + + mob.InvalidateProperties(); + mob.Delta( MobileDelta.Noto ); + + if ( Election.IsCandidate( mob ) ) + Election.RemoveCandidate( mob ); + + Election.RemoveVoter( mob ); + + if ( pl.Finance != null ) + pl.Finance.Finance = null; + + if ( pl.Sheriff != null ) + pl.Sheriff.Sheriff = null; + + if ( Commander == mob ) + Commander = null; + + if ( mob is PlayerMobile ) + ((PlayerMobile)mob).ValidateEquipment(); + } + + public void JoinGuilded( PlayerMobile mob, Guild guild ) + { + if ( mob.Young ) + { + guild.RemoveMember( mob ); + mob.SendLocalizedMessage( 1042283 ); // You have been kicked out of your guild! Young players may not remain in a guild which is allied with a faction. + } + else if ( AlreadyHasCharInFaction( mob ) ) + { + guild.RemoveMember( mob ); + mob.SendLocalizedMessage( 1005281 ); // You have been kicked out of your guild due to factional overlap + } + else if ( IsFactionBanned( mob ) ) + { + guild.RemoveMember( mob ); + mob.SendLocalizedMessage( 1005052 ); // You are currently banned from the faction system + } + else + { + AddMember( mob ); + mob.SendLocalizedMessage( 1042756, true, " " + m_Definition.FriendlyName ); // You are now joining a faction: + } + } + + public void JoinAlone( Mobile mob ) + { + AddMember( mob ); + mob.SendLocalizedMessage( 1005058 ); // You have joined the faction + } + + private bool AlreadyHasCharInFaction( Mobile mob ) + { + Account acct = mob.Account as Account; + + if ( acct != null ) + { + for ( int i = 0; i < acct.Length; ++i ) + { + Mobile c = acct[i]; + + if ( Find( c ) != null ) + return true; + } + } + + return false; + } + + public static bool IsFactionBanned( Mobile mob ) + { + Account acct = mob.Account as Account; + + if ( acct == null ) + return false; + + return ( acct.GetTag( "FactionBanned" ) != null ); + } + + public void OnJoinAccepted( Mobile mob ) + { + PlayerMobile pm = mob as PlayerMobile; + + if ( pm == null ) + return; // sanity + + PlayerState pl = PlayerState.Find( pm ); + + if ( pm.Young ) + pm.SendLocalizedMessage( 1010104 ); // You cannot join a faction as a young player + else if ( pl != null && pl.IsLeaving ) + pm.SendLocalizedMessage( 1005051 ); // You cannot use the faction stone until you have finished quitting your current faction + else if ( AlreadyHasCharInFaction( pm ) ) + pm.SendLocalizedMessage( 1005059 ); // You cannot join a faction because you already declared your allegiance with another character + else if ( IsFactionBanned( mob ) ) + pm.SendLocalizedMessage( 1005052 ); // You are currently banned from the faction system + else if ( pm.Guild != null ) + { + Guild guild = pm.Guild as Guild; + + if ( guild.Leader != pm ) + pm.SendLocalizedMessage( 1005057 ); // You cannot join a faction because you are in a guild and not the guildmaster + else if ( !Guild.NewGuildSystem && guild.Type != GuildType.Regular ) + pm.SendLocalizedMessage( 1042161 ); // You cannot join a faction because your guild is an Order or Chaos type. + else if ( !Guild.NewGuildSystem && guild.Enemies != null && guild.Enemies.Count > 0 ) //CAN join w/wars in new system + pm.SendLocalizedMessage( 1005056 ); // You cannot join a faction with active Wars + else if ( !CanHandleInflux( guild.Members.Count ) ) + pm.SendLocalizedMessage( 1018031 ); // In the interest of faction stability, this faction declines to accept new members for now. + else + { + ArrayList members = new ArrayList( guild.Members ); + + for ( int i = 0; i < members.Count; ++i ) + { + PlayerMobile member = members[i] as PlayerMobile; + + if ( member == null ) + continue; + + JoinGuilded( member, guild ); + } + } + } + else if ( !CanHandleInflux( 1 ) ) + { + pm.SendLocalizedMessage( 1018031 ); // In the interest of faction stability, this faction declines to accept new members for now. + } + else + { + JoinAlone( mob ); + } + } + + public bool IsCommander( Mobile mob ) + { + if ( mob == null ) + return false; + + return ( mob.AccessLevel >= AccessLevel.GameMaster || mob == Commander ); + } + + public Faction() + { + m_State = new FactionState( this ); + } + + public override string ToString() + { + return m_Definition.FriendlyName; + } + + public int CompareTo( object obj ) + { + return m_Definition.Sort - ((Faction)obj).m_Definition.Sort; + } + + public static bool CheckLeaveTimer( Mobile mob ) + { + PlayerState pl = PlayerState.Find( mob ); + + if ( pl == null || !pl.IsLeaving ) + return false; + + if ( (pl.Leaving + LeavePeriod) >= DateTime.Now ) + return false; + + mob.SendLocalizedMessage( 1005163 ); // You have now quit your faction + + pl.Faction.RemoveMember( mob ); + + return true; + } + + protected virtual void OnRankUpdate( PlayerState pl ) + { + } + + public void UpdateRanks() + { + PlayerStateCollection members = Members; + + ArrayList list = new ArrayList( members ); + + list.Sort(); + + RankDefinition[] ranks = m_Definition.Ranks; + + for ( int i = 0; i < list.Count; ++i ) + { + PlayerState pl = (PlayerState)list[i]; + + int percent; + + if ( list.Count == 1 ) + percent = 1000; + else + percent = (i * 1000) / (list.Count - 1); + + RankDefinition rank = null; + + for ( int j = 0; j < ranks.Length; ++j ) + { + RankDefinition check = ranks[j]; + + if ( percent >= check.Required ) + { + rank = check; + break; + } + } + + if ( pl.Rank != rank ) + { + pl.Rank = rank; + OnRankUpdate( pl ); + } + } + } + + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( EventSink_Login ); + EventSink.Logout += new LogoutEventHandler( EventSink_Logout ); + + Timer.DelayCall( TimeSpan.FromSeconds( 30.0 ), TimeSpan.FromSeconds( 30.0 ), new TimerCallback( ProcessTick ) ); + + CommandSystem.Register( "FactionElection", AccessLevel.GameMaster, new CommandEventHandler( FactionElection_OnCommand ) ); + CommandSystem.Register( "FactionCommander", AccessLevel.Administrator, new CommandEventHandler( FactionCommander_OnCommand ) ); + CommandSystem.Register( "FactionItemReset", AccessLevel.Administrator, new CommandEventHandler( FactionItemReset_OnCommand ) ); + CommandSystem.Register( "FactionReset", AccessLevel.Administrator, new CommandEventHandler( FactionReset_OnCommand ) ); + CommandSystem.Register( "FactionTownReset", AccessLevel.Administrator, new CommandEventHandler( FactionTownReset_OnCommand ) ); + } + + public static void FactionTownReset_OnCommand( CommandEventArgs e ) + { + MonolithCollection monoliths = BaseMonolith.Monoliths; + + for ( int i = 0; i < monoliths.Count; ++i ) + monoliths[i].Sigil = null; + + TownCollection towns = Town.Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + towns[i].Silver = 0; + towns[i].Sheriff = null; + towns[i].Finance = null; + towns[i].Tax = 0; + towns[i].Owner = null; + } + + SigilCollection sigils = Sigil.Sigils; + + for ( int i = 0; i < sigils.Count; ++i ) + { + sigils[i].Corrupted = null; + sigils[i].Corrupting = null; + sigils[i].LastStolen = DateTime.MinValue; + sigils[i].GraceStart = DateTime.MinValue; + sigils[i].CorruptionStart = DateTime.MinValue; + sigils[i].PurificationStart = DateTime.MinValue; + sigils[i].LastMonolith = null; + sigils[i].ReturnHome(); + } + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction f = factions[i]; + + ArrayList list = new ArrayList( f.State.FactionItems ); + + for ( int j = 0; j < list.Count; ++j ) + { + FactionItem fi = (FactionItem)list[j]; + + if ( fi.Expiration == DateTime.MinValue ) + fi.Item.Delete(); + else + fi.Detach(); + } + } + } + + public static void FactionReset_OnCommand( CommandEventArgs e ) + { + MonolithCollection monoliths = BaseMonolith.Monoliths; + + for ( int i = 0; i < monoliths.Count; ++i ) + monoliths[i].Sigil = null; + + TownCollection towns = Town.Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + towns[i].Silver = 0; + towns[i].Sheriff = null; + towns[i].Finance = null; + towns[i].Tax = 0; + towns[i].Owner = null; + } + + SigilCollection sigils = Sigil.Sigils; + + for ( int i = 0; i < sigils.Count; ++i ) + { + sigils[i].Corrupted = null; + sigils[i].Corrupting = null; + sigils[i].LastStolen = DateTime.MinValue; + sigils[i].GraceStart = DateTime.MinValue; + sigils[i].CorruptionStart = DateTime.MinValue; + sigils[i].PurificationStart = DateTime.MinValue; + sigils[i].LastMonolith = null; + sigils[i].ReturnHome(); + } + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction f = factions[i]; + + ArrayList list = new ArrayList( f.Members ); + + for ( int j = 0; j < list.Count; ++j ) + f.RemoveMember( ((PlayerState)list[j]).Mobile ); + + list = new ArrayList( f.State.FactionItems ); + + for ( int j = 0; j < list.Count; ++j ) + { + FactionItem fi = (FactionItem)list[j]; + + if ( fi.Expiration == DateTime.MinValue ) + fi.Item.Delete(); + else + fi.Detach(); + } + + list = new ArrayList( f.Traps ); + + for ( int j = 0; j < list.Count; ++j ) + ((BaseFactionTrap)list[j]).Delete(); + } + } + + public static void FactionItemReset_OnCommand( CommandEventArgs e ) + { + ArrayList pots = new ArrayList(); + + foreach ( Item item in World.Items.Values ) + { + if ( item is IFactionItem && !(item is HoodedShroudOfShadows) ) + pots.Add( item ); + } + + int[] hues = new int[Factions.Count * 2]; + + for ( int i = 0; i < Factions.Count; ++i ) + { + hues[0+(i*2)] = Factions[i].Definition.HuePrimary; + hues[1+(i*2)] = Factions[i].Definition.HueSecondary; + } + + int count = 0; + + for ( int i = 0; i < pots.Count; ++i ) + { + Item item = (Item)pots[i]; + IFactionItem fci = (IFactionItem)item; + + if ( fci.FactionItemState != null || item.LootType != LootType.Blessed ) + continue; + + bool isHued = false; + + for ( int j = 0; j < hues.Length; ++j ) + { + if ( item.Hue == hues[j] ) + { + isHued = true; + break; + } + } + + if ( isHued ) + { + fci.FactionItemState = null; + ++count; + } + } + + e.Mobile.SendMessage( "{0} items reset", count ); + } + + public static void FactionCommander_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendMessage( "Target a player to make them the faction commander." ); + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( FactionCommander_OnTarget ) ); + } + + public static void FactionCommander_OnTarget( Mobile from, object obj ) + { + if ( obj is PlayerMobile ) + { + Mobile targ = (Mobile)obj; + PlayerState pl = PlayerState.Find( targ ); + + if ( pl != null ) + { + pl.Faction.Commander = targ; + from.SendMessage( "You have appointed them as the faction commander." ); + } + else + { + from.SendMessage( "They are not in a faction." ); + } + } + else + { + from.SendMessage( "That is not a player." ); + } + } + + public static void FactionElection_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendMessage( "Target a faction stone to open its election properties." ); + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( FactionElection_OnTarget ) ); + } + + public static void FactionElection_OnTarget( Mobile from, object obj ) + { + if ( obj is FactionStone ) + { + Faction faction = ((FactionStone)obj).Faction; + + if ( faction != null ) + from.SendGump( new ElectionManagementGump( faction.Election ) ); + //from.SendGump( new Gumps.PropertiesGump( from, faction.Election ) ); + else + from.SendMessage( "That stone has no faction assigned." ); + } + else + { + from.SendMessage( "That is not a faction stone." ); + } + } + + public static void FactionKick_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendMessage( "Target a player to remove them from their faction." ); + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( FactionKick_OnTarget ) ); + } + + public static void FactionKick_OnTarget( Mobile from, object obj ) + { + if ( obj is Mobile ) + { + Mobile mob = (Mobile) obj; + PlayerState pl = PlayerState.Find( (Mobile) mob ); + + if ( pl != null ) + { + pl.Faction.RemoveMember( mob ); + + mob.SendMessage( "You have been kicked from your faction." ); + from.SendMessage( "They have been kicked from their faction." ); + } + else + { + from.SendMessage( "They are not in a faction." ); + } + } + else + { + from.SendMessage( "That is not a player." ); + } + } + + public static void ProcessTick() + { + SigilCollection sigils = Sigil.Sigils; + + for ( int i = 0; i < sigils.Count; ++i ) + { + Sigil sigil = sigils[i]; + + if ( !sigil.IsBeingCorrupted && sigil.GraceStart != DateTime.MinValue && (sigil.GraceStart + Sigil.CorruptionGrace) < DateTime.Now ) + { + if ( sigil.LastMonolith is StrongholdMonolith && ( sigil.Corrupted == null || sigil.LastMonolith.Faction != sigil.Corrupted )) + { + sigil.Corrupting = sigil.LastMonolith.Faction; + sigil.CorruptionStart = DateTime.Now; + } + else + { + sigil.Corrupting = null; + sigil.CorruptionStart = DateTime.MinValue; + } + + sigil.GraceStart = DateTime.MinValue; + } + + if ( sigil.LastMonolith == null || sigil.LastMonolith.Sigil == null ) + { + if ( (sigil.LastStolen + Sigil.ReturnPeriod) < DateTime.Now ) + sigil.ReturnHome(); + } + else + { + if ( sigil.IsBeingCorrupted && (sigil.CorruptionStart + Sigil.CorruptionPeriod) < DateTime.Now ) + { + sigil.Corrupted = sigil.Corrupting; + sigil.Corrupting = null; + sigil.CorruptionStart = DateTime.MinValue; + sigil.GraceStart = DateTime.MinValue; + } + else if ( sigil.IsPurifying && (sigil.PurificationStart + Sigil.PurificationPeriod) < DateTime.Now ) + { + sigil.PurificationStart = DateTime.MinValue; + sigil.Corrupted = null; + sigil.Corrupting = null; + sigil.CorruptionStart = DateTime.MinValue; + sigil.GraceStart = DateTime.MinValue; + } + } + } + } + + public static void HandleDeath( Mobile mob ) + { + HandleDeath( mob, null ); + } + + #region Skill Loss + public const double SkillLossFactor = 1.0 / 3; + public static readonly TimeSpan SkillLossPeriod = TimeSpan.FromMinutes( 20.0 ); + + private static Hashtable m_SkillLoss = new Hashtable(); + + private class SkillLossContext + { + public Timer m_Timer; + public ArrayList m_Mods; + } + + public static void ApplySkillLoss( Mobile mob ) + { + SkillLossContext context = (SkillLossContext)m_SkillLoss[mob]; + + if ( context != null ) + return; + + context = new SkillLossContext(); + m_SkillLoss[mob] = context; + + ArrayList mods = context.m_Mods = new ArrayList(); + + for ( int i = 0; i < mob.Skills.Length; ++i ) + { + Skill sk = mob.Skills[i]; + double baseValue = sk.Base; + + if ( baseValue > 0 ) + { + SkillMod mod = new DefaultSkillMod( sk.SkillName, true, -(baseValue * SkillLossFactor) ); + + mods.Add( mod ); + mob.AddSkillMod( mod ); + } + } + + context.m_Timer = Timer.DelayCall( SkillLossPeriod, new TimerStateCallback( ClearSkillLoss_Callback ), mob ); + } + + private static void ClearSkillLoss_Callback( object state ) + { + ClearSkillLoss( (Mobile) state ); + } + + public static void ClearSkillLoss( Mobile mob ) + { + SkillLossContext context = (SkillLossContext)m_SkillLoss[mob]; + + if ( context == null ) + return; + + m_SkillLoss.Remove( mob ); + + ArrayList mods = context.m_Mods; + + for ( int i = 0; i < mods.Count; ++i ) + mob.RemoveSkillMod( (SkillMod) mods[i] ); + + context.m_Timer.Stop(); + } + #endregion + + public int AwardSilver( Mobile mob, int silver ) + { + if ( silver <= 0 ) + return 0; + + int tithed = ( silver * Tithe ) / 100; + + Silver += tithed; + + silver = silver - tithed; + + if ( silver > 0 ) + mob.AddToBackpack( new Silver( silver ) ); + + return silver; + } + + public virtual int MaximumTraps{ get{ return 15; } } + + public FactionTrapCollection Traps + { + get{ return m_State.Traps; } + set{ m_State.Traps = value; } + } + + public const int StabilityFactor = 300; // 300% greater (3 times) than smallest faction + public const int StabilityActivation = 200; // Stablity code goes into effect when largest faction has > 200 people + + public static Faction FindSmallestFaction() + { + FactionCollection factions = Factions; + Faction smallest = null; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction faction = factions[i]; + + if ( smallest == null || faction.Members.Count < smallest.Members.Count ) + smallest = faction; + } + + return smallest; + } + + public static bool StabilityActive() + { + FactionCollection factions = Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction faction = factions[i]; + + if ( faction.Members.Count > StabilityActivation ) + return true; + } + + return false; + } + + public bool CanHandleInflux( int influx ) + { + if( !StabilityActive()) + return true; + + Faction smallest = FindSmallestFaction(); + + if ( smallest == null ) + return true; // sanity + + if ( StabilityFactor > 0 && (((this.Members.Count + influx) * 100) / StabilityFactor) > smallest.Members.Count ) + return false; + + return true; + } + + public static void HandleDeath( Mobile victim, Mobile killer ) + { + if ( killer == null ) + killer = victim.FindMostRecentDamager( true ); + + PlayerState killerState = PlayerState.Find( killer ); + + Container pack = victim.Backpack; + + if ( pack != null ) + { + Container killerPack = ( killer == null ? null : killer.Backpack ); + Item[] sigils = pack.FindItemsByType( typeof( Sigil ) ); + + for ( int i = 0; i < sigils.Length; ++i ) + { + Sigil sigil = (Sigil)sigils[i]; + + if ( killerState != null && killerPack != null ) + { + if ( Sigil.ExistsOn( killer ) ) + { + sigil.ReturnHome(); + killer.SendLocalizedMessage( 1010258 ); // The sigil has gone back to its home location because you already have a sigil. + } + else if ( !killerPack.TryDropItem( killer, sigil, false ) ) + { + sigil.ReturnHome(); + killer.SendLocalizedMessage( 1010259 ); // The sigil has gone home because your backpack is full. + } + } + else + { + sigil.ReturnHome(); + } + } + } + + if ( killerState == null ) + return; + + if ( victim is BaseCreature ) + { + BaseCreature bc = (BaseCreature)victim; + Faction victimFaction = bc.FactionAllegiance; + + if ( bc.Map == Faction.Facet && victimFaction != null && killerState.Faction != victimFaction ) + { + int silver = killerState.Faction.AwardSilver( killer, bc.FactionSilverWorth ); + + if ( silver > 0 ) + killer.SendLocalizedMessage( 1042748, silver.ToString( "N0" ) ); // Thou hast earned ~1_AMOUNT~ silver for vanquishing the vile creature. + + } + + #region Ethics + if ( bc.Map == Faction.Facet && bc.GetEthicAllegiance( killer ) == BaseCreature.Allegiance.Enemy ) + { + Ethics.Player killerEPL = Ethics.Player.Find( killer ); + + if ( killerEPL != null && ( 100 - killerEPL.Power ) > Utility.Random( 100 ) ) + { + ++killerEPL.Power; + ++killerEPL.History; + } + } + #endregion + + return; + } + + PlayerState victimState = PlayerState.Find( victim ); + + if ( victimState == null ) + return; + + if ( killer == victim || killerState.Faction != victimState.Faction ) + ApplySkillLoss( victim ); + + if ( killerState.Faction != victimState.Faction ) + { + if ( victimState.KillPoints <= -6 ) + { + killer.SendLocalizedMessage( 501693 ); // This victim is not worth enough to get kill points from. + + #region Ethics + Ethics.Player killerEPL = Ethics.Player.Find( killer ); + Ethics.Player victimEPL = Ethics.Player.Find( victim ); + + if ( killerEPL != null && victimEPL != null && victimEPL.Power > 0 && victimState.CanGiveSilverTo( killer ) ) + { + int powerTransfer = Math.Max( 1, victimEPL.Power / 5 ); + + if ( powerTransfer > ( 100 - killerEPL.Power ) ) + powerTransfer = 100 - killerEPL.Power; + + if ( powerTransfer > 0 ) + { + victimEPL.Power -= ( powerTransfer + 1 ) / 2; + killerEPL.Power += powerTransfer; + + killerEPL.History += powerTransfer; + + victimState.OnGivenSilverTo( killer ); + } + } + #endregion + } + else + { + int award = Math.Max( victimState.KillPoints / 10, 1 ); + + if ( award > 40 ) + award = 40; + + if ( victimState.CanGiveSilverTo( killer ) ) + { + if ( victimState.KillPoints > 0 ) + { + int silver = 0; + + silver = killerState.Faction.AwardSilver( killer, award * 40 ); + + if ( silver > 0 ) + killer.SendLocalizedMessage( 1042736, String.Format( "{0:N0} silver\t{1}", silver, victim.Name ) ); // You have earned ~1_SILVER_AMOUNT~ pieces for vanquishing ~2_PLAYER_NAME~! + } + + victimState.KillPoints -= award; + killerState.KillPoints += award; + + int offset = ( award != 1 ? 0 : 2 ); // for pluralization + + string args = String.Format( "{0}\t{1}\t{2}", award, victim.Name, killer.Name ); + + killer.SendLocalizedMessage( 1042737 + offset, args ); // Thou hast been honored with ~1_KILL_POINTS~ kill point(s) for vanquishing ~2_DEAD_PLAYER~! + victim.SendLocalizedMessage( 1042738 + offset, args ); // Thou has lost ~1_KILL_POINTS~ kill point(s) to ~3_ATTACKER_NAME~ for being vanquished! + + #region Ethics + Ethics.Player killerEPL = Ethics.Player.Find( killer ); + Ethics.Player victimEPL = Ethics.Player.Find( victim ); + + if ( killerEPL != null && victimEPL != null && victimEPL.Power > 0 ) + { + int powerTransfer = Math.Max( 1, victimEPL.Power / 5 ); + + if ( powerTransfer > ( 100 - killerEPL.Power ) ) + powerTransfer = 100 - killerEPL.Power; + + if ( powerTransfer > 0 ) + { + victimEPL.Power -= ( powerTransfer + 1 ) / 2; + killerEPL.Power += powerTransfer; + + killerEPL.History += powerTransfer; + } + } + #endregion + + victimState.OnGivenSilverTo( killer ); + } + else + { + killer.SendLocalizedMessage( 1042231 ); // You have recently defeated this enemy and thus their death brings you no honor. + } + } + } + } + + private static void EventSink_Logout( LogoutEventArgs e ) + { + Mobile mob = e.Mobile; + + Container pack = mob.Backpack; + + if ( pack == null ) + return; + + Item[] sigils = pack.FindItemsByType( typeof( Sigil ) ); + + for ( int i = 0; i < sigils.Length; ++i ) + ((Sigil)sigils[i]).ReturnHome(); + } + + private static void EventSink_Login( LoginEventArgs e ) + { + Mobile mob = e.Mobile; + + CheckLeaveTimer( mob ); + } + + public static readonly Map Facet = Map.Felucca; + + public static void WriteReference( GenericWriter writer, Faction fact ) + { + int idx = Factions.IndexOf( fact ); + + writer.WriteEncodedInt( (int) (idx + 1) ); + } + + public static FactionCollection Factions{ get{ return Reflector.Factions; } } + + public static Faction ReadReference( GenericReader reader ) + { + int idx = reader.ReadEncodedInt() - 1; + + if ( idx >= 0 && idx < Factions.Count ) + return Factions[idx]; + + return null; + } + + public static Faction Find( Mobile mob ) + { + return Find( mob, false, false ); + } + + public static Faction Find( Mobile mob, bool inherit ) + { + return Find( mob, inherit, false ); + } + + public static Faction Find( Mobile mob, bool inherit, bool creatureAllegiances ) + { + PlayerState pl = PlayerState.Find( mob ); + + if ( pl != null ) + return pl.Faction; + + if ( inherit && mob is BaseCreature ) + { + BaseCreature bc = (BaseCreature)mob; + + if ( bc.Controlled ) + return Find( bc.ControlMaster, false ); + else if ( bc.Summoned ) + return Find( bc.SummonMaster, false ); + else if ( creatureAllegiances && mob is BaseFactionGuard ) + return ((BaseFactionGuard)mob).Faction; + else if ( creatureAllegiances ) + return bc.FactionAllegiance; + } + + return null; + } + + public static Faction Parse( string name ) + { + FactionCollection factions = Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction faction = factions[i]; + + if ( Insensitive.Equals( faction.Definition.FriendlyName, name ) ) + return faction; + } + + return null; + } + } + + public enum FactionKickType + { + Kick, + Ban, + Unban + } + + public class FactionKickCommand : BaseCommand + { + private FactionKickType m_KickType; + + public FactionKickCommand( FactionKickType kickType ) + { + m_KickType = kickType; + + AccessLevel = AccessLevel.GameMaster; + Supports = CommandSupport.AllMobiles; + ObjectTypes = ObjectTypes.Mobiles; + + switch ( m_KickType ) + { + case FactionKickType.Kick: + { + Commands = new string[]{ "FactionKick" }; + Usage = "FactionKick"; + Description = "Kicks the targeted player out of his current faction. This does not prevent them from rejoining."; + break; + } + case FactionKickType.Ban: + { + Commands = new string[]{ "FactionBan" }; + Usage = "FactionBan"; + Description = "Bans the account of a targeted player from joining factions. All players on the account are removed from their current faction, if any."; + break; + } + case FactionKickType.Unban: + { + Commands = new string[]{ "FactionUnban" }; + Usage = "FactionUnban"; + Description = "Unbans the account of a targeted player from joining factions."; + break; + } + } + } + + public override void Execute( CommandEventArgs e, object obj ) + { + Mobile mob = (Mobile)obj; + + switch ( m_KickType ) + { + case FactionKickType.Kick: + { + PlayerState pl = PlayerState.Find( mob ); + + if ( pl != null ) + { + pl.Faction.RemoveMember( mob ); + mob.SendMessage( "You have been kicked from your faction." ); + AddResponse( "They have been kicked from their faction." ); + } + else + { + LogFailure( "They are not in a faction." ); + } + + break; + } + case FactionKickType.Ban: + { + Account acct = mob.Account as Account; + + if ( acct != null ) + { + if ( acct.GetTag( "FactionBanned" ) == null ) + { + acct.SetTag( "FactionBanned", "true" ); + AddResponse( "The account has been banned from joining factions." ); + } + else + { + AddResponse( "The account is already banned from joining factions." ); + } + + for ( int i = 0; i < acct.Length; ++i ) + { + mob = acct[i]; + + if ( mob != null ) + { + PlayerState pl = PlayerState.Find( mob ); + + if ( pl != null ) + { + pl.Faction.RemoveMember( mob ); + mob.SendMessage( "You have been kicked from your faction." ); + AddResponse( "They have been kicked from their faction." ); + } + } + } + } + else + { + LogFailure( "They have no assigned account." ); + } + + break; + } + case FactionKickType.Unban: + { + Account acct = mob.Account as Account; + + if ( acct != null ) + { + if ( acct.GetTag( "FactionBanned" ) == null ) + { + AddResponse( "The account is not already banned from joining factions." ); + } + else + { + acct.RemoveTag( "FactionBanned" ); + AddResponse( "The account may now freely join factions." ); + } + } + else + { + LogFailure( "They have no assigned account." ); + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/FactionItem.cs b/Scripts/Engines/Factions/Core/FactionItem.cs new file mode 100644 index 000000000..a238498a5 --- /dev/null +++ b/Scripts/Engines/Factions/Core/FactionItem.cs @@ -0,0 +1,138 @@ +using System; + +namespace Server.Factions +{ + public interface IFactionItem + { + FactionItem FactionItemState{ get; set; } + } + + public class FactionItem + { + public static readonly TimeSpan ExpirationPeriod = TimeSpan.FromDays( 21.0 ); + + private Item m_Item; + private Faction m_Faction; + private DateTime m_Expiration; + + public Item Item{ get{ return m_Item; } } + public Faction Faction{ get{ return m_Faction; } } + public DateTime Expiration{ get{ return m_Expiration; } } + + public bool HasExpired + { + get + { + if ( m_Item == null || m_Item.Deleted ) + return true; + + return ( m_Expiration != DateTime.MinValue && DateTime.Now >= m_Expiration ); + } + } + + public void StartExpiration() + { + m_Expiration = DateTime.Now + ExpirationPeriod; + } + + public void Attach() + { + if ( m_Item is IFactionItem ) + ((IFactionItem)m_Item).FactionItemState = this; + + if ( m_Faction != null ) + m_Faction.State.FactionItems.Add( this ); + } + + public void Detach() + { + if ( m_Item is IFactionItem ) + ((IFactionItem)m_Item).FactionItemState = null; + + if ( m_Faction != null && m_Faction.State.FactionItems.Contains( this ) ) + m_Faction.State.FactionItems.Remove( this ); + } + + public FactionItem( Item item, Faction faction ) + { + m_Item = item; + m_Faction = faction; + } + + public FactionItem( GenericReader reader, Faction faction ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_Item = reader.ReadItem(); + m_Expiration = reader.ReadDateTime(); + break; + } + } + + m_Faction = faction; + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); + + writer.Write( (Item) m_Item ); + writer.Write( (DateTime) m_Expiration ); + } + + public static int GetMaxWearables( Mobile mob ) + { + PlayerState pl = PlayerState.Find( mob ); + + if ( pl == null ) + return 0; + + if ( pl.Faction.IsCommander( mob ) ) + return 9; + + return pl.Rank.MaxWearables; + } + + public static FactionItem Find( Item item ) + { + if ( item is IFactionItem ) + { + FactionItem state = ((IFactionItem)item).FactionItemState; + + if ( state != null && state.HasExpired ) + { + state.Detach(); + state = null; + } + + return state; + } + + return null; + } + + public static Item Imbue( Item item, Faction faction, bool expire, int hue ) + { + if ( !(item is IFactionItem) ) + return item; + + FactionItem state = Find( item ); + + if ( state == null ) + { + state = new FactionItem( item, faction ); + state.Attach(); + } + + if ( expire ) + state.StartExpiration(); + + item.Hue = hue; + return item; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/FactionState.cs b/Scripts/Engines/Factions/Core/FactionState.cs new file mode 100644 index 000000000..9c6f38933 --- /dev/null +++ b/Scripts/Engines/Factions/Core/FactionState.cs @@ -0,0 +1,258 @@ +using System; + +namespace Server.Factions +{ + public class FactionState + { + private Faction m_Faction; + private Mobile m_Commander; + private int m_Tithe; + private int m_Silver; + private PlayerStateCollection m_Members; + private Election m_Election; + private FactionItemCollection m_FactionItems; + private FactionTrapCollection m_FactionTraps; + + private const int BroadcastsPerPeriod = 2; + private static readonly TimeSpan BroadcastPeriod = TimeSpan.FromHours( 1.0 ); + + private DateTime[] m_LastBroadcasts = new DateTime[BroadcastsPerPeriod]; + + public bool FactionMessageReady + { + get + { + for ( int i = 0; i < m_LastBroadcasts.Length; ++i ) + { + if ( DateTime.Now >= (m_LastBroadcasts[i] + BroadcastPeriod) ) + return true; + } + + return false; + } + } + + public void RegisterBroadcast() + { + for ( int i = 0; i < m_LastBroadcasts.Length; ++i ) + { + if ( DateTime.Now >= (m_LastBroadcasts[i] + BroadcastPeriod) ) + { + m_LastBroadcasts[i] = DateTime.Now; + break; + } + } + } + + public FactionItemCollection FactionItems + { + get{ return m_FactionItems; } + set{ m_FactionItems = value; } + } + + public FactionTrapCollection Traps + { + get{ return m_FactionTraps; } + set{ m_FactionTraps = value; } + } + + public Election Election + { + get{ return m_Election; } + set{ m_Election = value; } + } + + public Mobile Commander + { + get{ return m_Commander; } + set + { + if ( m_Commander != null ) + m_Commander.InvalidateProperties(); + + m_Commander = value; + + if ( m_Commander != null ) + { + m_Commander.SendLocalizedMessage( 1042227 ); // You have been elected Commander of your faction + + m_Commander.InvalidateProperties(); + + PlayerState pl = PlayerState.Find( m_Commander ); + + if ( pl != null && pl.Finance != null ) + pl.Finance.Finance = null; + + if ( pl != null && pl.Sheriff != null ) + pl.Sheriff.Sheriff = null; + } + } + } + + public int Tithe + { + get{ return m_Tithe; } + set{ m_Tithe = value; } + } + + public int Silver + { + get{ return m_Silver; } + set{ m_Silver = value; } + } + + public PlayerStateCollection Members + { + get{ return m_Members; } + set{ m_Members = value; } + } + + public FactionState( Faction faction ) + { + m_Faction = faction; + m_Tithe = 50; + m_Members = new PlayerStateCollection(); + m_Election = new Election( faction ); + m_FactionItems = new FactionItemCollection(); + m_FactionTraps = new FactionTrapCollection(); + } + + public FactionState( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 4: + { + int count = reader.ReadEncodedInt(); + + for ( int i = 0; i < count; ++i ) + { + DateTime time = reader.ReadDateTime(); + + if ( i < m_LastBroadcasts.Length ) + m_LastBroadcasts[i] = time; + } + + goto case 3; + } + case 3: + case 2: + case 1: + { + m_Election = new Election( reader ); + + goto case 0; + } + case 0: + { + m_Faction = Faction.ReadReference( reader ); + + m_Commander = reader.ReadMobile(); + + if ( version < 4 ) + { + DateTime time = reader.ReadDateTime(); + + if ( m_LastBroadcasts.Length > 0 ) + m_LastBroadcasts[0] = time; + } + + m_Tithe = reader.ReadEncodedInt(); + m_Silver = reader.ReadEncodedInt(); + + int memberCount = reader.ReadEncodedInt(); + + m_Members = new PlayerStateCollection(); + + for ( int i = 0; i < memberCount; ++i ) + { + PlayerState pl = new PlayerState( reader, m_Faction, m_Members ); + + if ( pl.Mobile != null ) + m_Members.Add( pl ); + } + + m_Faction.State = this; + m_Faction.UpdateRanks(); + + m_FactionItems = new FactionItemCollection(); + + if ( version >= 2 ) + { + int factionItemCount = reader.ReadEncodedInt(); + + for ( int i = 0; i < factionItemCount; ++i ) + { + FactionItem factionItem = new FactionItem( reader, m_Faction ); + + if ( !factionItem.HasExpired ) + factionItem.Attach(); + else + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( factionItem.Detach ) ); // sandbox detachment + } + } + + m_FactionTraps = new FactionTrapCollection(); + + if ( version >= 3 ) + { + int factionTrapCount = reader.ReadEncodedInt(); + + for ( int i = 0; i < factionTrapCount; ++i ) + { + BaseFactionTrap trap = reader.ReadItem() as BaseFactionTrap; + + if ( trap != null && !trap.CheckDecay() ) + m_FactionTraps.Add( trap ); + } + } + + break; + } + } + + if ( version < 1 ) + m_Election = new Election( m_Faction ); + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 4 ); // version + + writer.WriteEncodedInt( (int) m_LastBroadcasts.Length ); + + for ( int i = 0; i < m_LastBroadcasts.Length; ++i ) + writer.Write( (DateTime) m_LastBroadcasts[i] ); + + m_Election.Serialize( writer ); + + Faction.WriteReference( writer, m_Faction ); + + writer.Write( (Mobile) m_Commander ); + + writer.WriteEncodedInt( (int) m_Tithe ); + writer.WriteEncodedInt( (int) m_Silver ); + + writer.WriteEncodedInt( (int) m_Members.Count ); + + for ( int i = 0; i < m_Members.Count; ++i ) + { + PlayerState pl = (PlayerState) m_Members[i]; + + pl.Serialize( writer ); + } + + writer.WriteEncodedInt( (int) m_FactionItems.Count ); + + for ( int i = 0; i < m_FactionItems.Count; ++i ) + m_FactionItems[i].Serialize( writer ); + + writer.WriteEncodedInt( (int) m_FactionTraps.Count ); + + for ( int i = 0; i < m_FactionTraps.Count; ++i ) + writer.Write( (Item) m_FactionTraps[i] ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/Generator.cs b/Scripts/Engines/Factions/Core/Generator.cs new file mode 100644 index 000000000..f1b2b48af --- /dev/null +++ b/Scripts/Engines/Factions/Core/Generator.cs @@ -0,0 +1,79 @@ +using System; +using Server.Commands; + +namespace Server.Factions +{ + public class Generator + { + public static void Initialize() + { + CommandSystem.Register( "GenerateFactions", AccessLevel.Administrator, new CommandEventHandler( GenerateFactions_OnCommand ) ); + } + + public static void GenerateFactions_OnCommand( CommandEventArgs e ) + { + new FactionPersistance(); + + FactionCollection factions = Faction.Factions; + + foreach ( Faction faction in factions ) + Generate( faction ); + + TownCollection towns = Town.Towns; + + foreach ( Town town in towns ) + Generate( town ); + } + + public static void Generate( Town town ) + { + Map facet = Faction.Facet; + + TownDefinition def = town.Definition; + + if ( !CheckExistance( def.Monolith, facet, typeof( TownMonolith ) ) ) + { + TownMonolith mono = new TownMonolith( town ); + mono.MoveToWorld( def.Monolith, facet ); + mono.Sigil = new Sigil( town ); + } + + if ( !CheckExistance( def.TownStone, facet, typeof( TownStone ) ) ) + new TownStone( town ).MoveToWorld( def.TownStone, facet ); + } + + public static void Generate( Faction faction ) + { + Map facet = Faction.Facet; + + TownCollection towns = Town.Towns; + + StrongholdDefinition stronghold = faction.Definition.Stronghold; + + if ( !CheckExistance( stronghold.JoinStone, facet, typeof( JoinStone ) ) ) + new JoinStone( faction ).MoveToWorld( stronghold.JoinStone, facet ); + + if ( !CheckExistance( stronghold.FactionStone, facet, typeof( FactionStone ) ) ) + new FactionStone( faction ).MoveToWorld( stronghold.FactionStone, facet ); + + for ( int i = 0; i < stronghold.Monoliths.Length; ++i ) + { + Point3D monolith = stronghold.Monoliths[i]; + + if ( !CheckExistance( monolith, facet, typeof( StrongholdMonolith ) ) ) + new StrongholdMonolith( towns[i], faction ).MoveToWorld( monolith, facet ); + } + } + + private static bool CheckExistance( Point3D loc, Map facet, Type type ) + { + foreach ( Item item in facet.GetItemsInRange( loc, 0 ) ) + { + if ( type.IsAssignableFrom( item.GetType() ) ) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/GuardList.cs b/Scripts/Engines/Factions/Core/GuardList.cs new file mode 100644 index 000000000..3f63532eb --- /dev/null +++ b/Scripts/Engines/Factions/Core/GuardList.cs @@ -0,0 +1,26 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class GuardList + { + private GuardDefinition m_Definition; + private FactionGuardCollection m_Guards; + + public GuardDefinition Definition{ get{ return m_Definition; } } + public FactionGuardCollection Guards{ get{ return m_Guards; } } + + public BaseFactionGuard Construct() + { + try{ return Activator.CreateInstance( m_Definition.Type ) as BaseFactionGuard; } + catch{ return null; } + } + + public GuardList( GuardDefinition definition ) + { + m_Definition = definition; + m_Guards = new FactionGuardCollection(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/Keywords.cs b/Scripts/Engines/Factions/Core/Keywords.cs new file mode 100644 index 000000000..16e67faa1 --- /dev/null +++ b/Scripts/Engines/Factions/Core/Keywords.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Factions; +using Server.Mobiles; + +namespace Server.Factions +{ + public class Keywords + { + public static void Initialize() + { + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + } + + private static void ShowScore_Sandbox( object state ) + { + PlayerState pl = (PlayerState)state; + + if ( pl != null ) + pl.Mobile.PublicOverheadMessage( MessageType.Regular, pl.Mobile.SpeechHue, true, pl.KillPoints.ToString( "N0" ) ); // NOTE: Added 'N0' + } + + private static void EventSink_Speech( SpeechEventArgs e ) + { + Mobile from = e.Mobile; + int[] keywords = e.Keywords; + + for ( int i = 0; i < keywords.Length; ++i ) + { + switch ( keywords[i] ) + { + case 0x00E4: // *i wish to access the city treasury* + { + Town town = Town.FromRegion( from.Region ); + + if ( town == null || !town.IsFinance( from ) || !from.Alive ) + break; + + if ( FactionGump.Exists( from ) ) + from.SendLocalizedMessage( 1042160 ); // You already have a faction menu open. + else if ( town.Owner != null && from is PlayerMobile ) + from.SendGump( new FinanceGump( (PlayerMobile)from, town.Owner, town ) ); + + break; + } + case 0x0ED: // *i am sheriff* + { + Town town = Town.FromRegion( from.Region ); + + if ( town == null || !town.IsSheriff( from ) || !from.Alive ) + break; + + if ( FactionGump.Exists( from ) ) + from.SendLocalizedMessage( 1042160 ); // You already have a faction menu open. + else if ( town.Owner != null ) + from.SendGump( new SheriffGump( (PlayerMobile)from, town.Owner, town ) ); + + break; + } + case 0x00EF: // *you are fired* + { + Town town = Town.FromRegion( from.Region ); + + if ( town == null ) + break; + + if ( town.IsFinance( from ) || town.IsSheriff( from ) ) + town.BeginOrderFiring( from ); + + break; + } + case 0x00E5: // *i wish to resign as finance minister* + { + PlayerState pl = PlayerState.Find( from ); + + if ( pl != null && pl.Finance != null ) + { + pl.Finance.Finance = null; + from.SendLocalizedMessage( 1005081 ); // You have been fired as Finance Minister + } + + break; + } + case 0x00EE: // *i wish to resign as sheriff* + { + PlayerState pl = PlayerState.Find( from ); + + if ( pl != null && pl.Sheriff != null ) + { + pl.Sheriff.Sheriff = null; + from.SendLocalizedMessage( 1010270 ); // You have been fired as Sheriff + } + + break; + } + case 0x00E9: // *what is my faction term status* + { + PlayerState pl = PlayerState.Find( from ); + + if ( pl != null && pl.IsLeaving ) + { + if ( Faction.CheckLeaveTimer( from ) ) + break; + + TimeSpan remaining = ( pl.Leaving + Faction.LeavePeriod ) - DateTime.Now; + + if( remaining.TotalDays >= 1 ) + from.SendLocalizedMessage( 1042743, remaining.TotalDays.ToString( "N0" ) ) ;// Your term of service will come to an end in ~1_DAYS~ days. + else if( remaining.TotalHours >= 1 ) + from.SendLocalizedMessage( 1042741, remaining.TotalHours.ToString( "N0" ) ); // Your term of service will come to an end in ~1_HOURS~ hours. + else + from.SendLocalizedMessage( 1042742 ); // Your term of service will come to an end in less than one hour. + } + else if ( pl != null ) + { + from.SendLocalizedMessage( 1042233 ); // You are not in the process of quitting the faction. + } + + break; + } + case 0x00EA: // *message faction* + { + Faction faction = Faction.Find( from ); + + if ( faction == null || !faction.IsCommander( from ) ) + break; + + if ( from.AccessLevel == AccessLevel.Player && !faction.FactionMessageReady ) + from.SendLocalizedMessage( 1010264 ); // The required time has not yet passed since the last message was sent + else + faction.BeginBroadcast( from ); + + break; + } + case 0x00EC: // *showscore* + { + PlayerState pl = PlayerState.Find( from ); + + if ( pl != null ) + Timer.DelayCall( TimeSpan.Zero, new TimerStateCallback( ShowScore_Sandbox ), pl ); + + break; + } + case 0x0178: // i honor your leadership + { + Faction faction = Faction.Find( from ); + + if ( faction != null ) + faction.BeginHonorLeadership( from ); + + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/MerchantTitles.cs b/Scripts/Engines/Factions/Core/MerchantTitles.cs new file mode 100644 index 000000000..38282e9e9 --- /dev/null +++ b/Scripts/Engines/Factions/Core/MerchantTitles.cs @@ -0,0 +1,87 @@ +using System; + +namespace Server.Factions +{ + public enum MerchantTitle + { + None, + Scribe, + Carpenter, + Blacksmith, + Bowyer, + Tialor + } + + public class MerchantTitleInfo + { + private SkillName m_Skill; + private double m_Requirement; + private TextDefinition m_Title; + private TextDefinition m_Label; + private TextDefinition m_Assigned; + + public SkillName Skill{ get{ return m_Skill; } } + public double Requirement{ get{ return m_Requirement; } } + public TextDefinition Title{ get{ return m_Title; } } + public TextDefinition Label{ get{ return m_Label; } } + public TextDefinition Assigned{ get{ return m_Assigned; } } + + public MerchantTitleInfo( SkillName skill, double requirement, TextDefinition title, TextDefinition label, TextDefinition assigned ) + { + m_Skill = skill; + m_Requirement = requirement; + m_Title = title; + m_Label = label; + m_Assigned = assigned; + } + } + + public class MerchantTitles + { + private static MerchantTitleInfo[] m_Info = new MerchantTitleInfo[] + { + new MerchantTitleInfo( SkillName.Inscribe, 90.0, new TextDefinition( 1060773, "Scribe" ), new TextDefinition( 1011468, "SCRIBE" ), new TextDefinition( 1010121, "You now have the faction title of scribe" ) ), + new MerchantTitleInfo( SkillName.Carpentry, 90.0, new TextDefinition( 1060774, "Carpenter" ), new TextDefinition( 1011469, "CARPENTER" ), new TextDefinition( 1010122, "You now have the faction title of carpenter" ) ), + new MerchantTitleInfo( SkillName.Tinkering, 90.0, new TextDefinition( 1022984, "Tinker" ), new TextDefinition( 1011470, "TINKER" ), new TextDefinition( 1010123, "You now have the faction title of tinker" ) ), + new MerchantTitleInfo( SkillName.Blacksmith, 90.0, new TextDefinition( 1023016, "Blacksmith" ), new TextDefinition( 1011471, "BLACKSMITH" ), new TextDefinition( 1010124, "You now have the faction title of blacksmith" ) ), + new MerchantTitleInfo( SkillName.Fletching, 90.0, new TextDefinition( 1023022, "Bowyer" ), new TextDefinition( 1011472, "BOWYER" ), new TextDefinition( 1010125, "You now have the faction title of Bowyer" ) ), + new MerchantTitleInfo( SkillName.Tailoring, 90.0, new TextDefinition( 1022982, "Tailor" ), new TextDefinition( 1018300, "TAILOR" ), new TextDefinition( 1042162, "You now have the faction title of Tailor" ) ), + }; + + public static MerchantTitleInfo[] Info{ get{ return m_Info; } } + + public static MerchantTitleInfo GetInfo( MerchantTitle title ) + { + int idx = (int)title - 1; + + if ( idx >= 0 && idx < m_Info.Length ) + return m_Info[idx]; + + return null; + } + + public static bool HasMerchantQualifications( Mobile mob ) + { + for ( int i = 0; i < m_Info.Length; ++i ) + { + if ( IsQualified( mob, m_Info[i] ) ) + return true; + } + + return false; + } + + public static bool IsQualified( Mobile mob, MerchantTitle title ) + { + return IsQualified( mob, GetInfo( title ) ); + } + + public static bool IsQualified( Mobile mob, MerchantTitleInfo info ) + { + if ( mob == null || info == null ) + return false; + + return ( mob.Skills[info.Skill].Value >= info.Requirement ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/Persistance.cs b/Scripts/Engines/Factions/Core/Persistance.cs new file mode 100644 index 000000000..ab6880a27 --- /dev/null +++ b/Scripts/Engines/Factions/Core/Persistance.cs @@ -0,0 +1,93 @@ +using System; + +namespace Server.Factions +{ + public class FactionPersistance : Item + { + private static FactionPersistance m_Instance; + + public static FactionPersistance Instance{ get{ return m_Instance; } } + + public override string DefaultName + { + get { return "Faction Persistance - Internal"; } + } + + public FactionPersistance() : base( 1 ) + { + Movable = false; + + if ( m_Instance == null || m_Instance.Deleted ) + m_Instance = this; + else + base.Delete(); + } + + private enum PersistedType + { + Terminator, + Faction, + Town + } + + public FactionPersistance( Serial serial ) : base( serial ) + { + m_Instance = this; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + writer.WriteEncodedInt( (int) PersistedType.Faction ); + factions[i].State.Serialize( writer ); + } + + TownCollection towns = Town.Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + writer.WriteEncodedInt( (int) PersistedType.Town ); + towns[i].State.Serialize( writer ); + } + + writer.WriteEncodedInt( (int) PersistedType.Terminator ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + PersistedType type; + + while ( (type = (PersistedType)reader.ReadEncodedInt()) != PersistedType.Terminator ) + { + switch ( type ) + { + case PersistedType.Faction: new FactionState( reader ); break; + case PersistedType.Town: new TownState( reader ); break; + } + } + + break; + } + } + } + + public override void Delete() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/PlayerState.cs b/Scripts/Engines/Factions/Core/PlayerState.cs new file mode 100644 index 000000000..8059398ab --- /dev/null +++ b/Scripts/Engines/Factions/Core/PlayerState.cs @@ -0,0 +1,134 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Factions +{ + public class PlayerState : IComparable + { + private Mobile m_Mobile; + private Faction m_Faction; + private PlayerStateCollection m_Owner; + private int m_KillPoints; + private DateTime m_Leaving; + private MerchantTitle m_MerchantTitle; + private RankDefinition m_Rank; + private SilverGivenCollection m_SilverGiven; + + private Town m_Sheriff; + private Town m_Finance; + + public Mobile Mobile{ get{ return m_Mobile; } } + public Faction Faction{ get{ return m_Faction; } } + public PlayerStateCollection Owner{ get{ return m_Owner; } } + public int KillPoints{ get{ return m_KillPoints; } set{ m_KillPoints = value; } } + public MerchantTitle MerchantTitle{ get{ return m_MerchantTitle; } set{ m_MerchantTitle = value; Invalidate(); } } + public RankDefinition Rank{ get{ return m_Rank; } set{ m_Rank = value; Invalidate(); } } + public Town Sheriff{ get{ return m_Sheriff; } set{ m_Sheriff = value; Invalidate(); } } + public Town Finance{ get{ return m_Finance; } set{ m_Finance = value; Invalidate(); } } + public SilverGivenCollection SilverGiven{ get{ return m_SilverGiven; } } + + public DateTime Leaving{ get{ return m_Leaving; } set{ m_Leaving = value; } } + public bool IsLeaving{ get{ return ( m_Leaving > DateTime.MinValue ); } } + + public bool CanGiveSilverTo( Mobile mob ) + { + if ( m_SilverGiven == null ) + return true; + + for ( int i = 0; i < m_SilverGiven.Count; ++i ) + { + SilverGivenEntry sge = m_SilverGiven[i]; + + if ( sge.IsExpired ) + m_SilverGiven.RemoveAt( i-- ); + else if ( sge.GivenTo == mob ) + return false; + } + + return true; + } + + public void OnGivenSilverTo( Mobile mob ) + { + if ( m_SilverGiven == null ) + m_SilverGiven = new SilverGivenCollection(); + + m_SilverGiven.Add( new SilverGivenEntry( mob ) ); + } + + public void Invalidate() + { + if ( m_Mobile is PlayerMobile ) + ((PlayerMobile)m_Mobile).InvalidateProperties(); + } + + public void Attach() + { + if ( m_Mobile is PlayerMobile ) + ((PlayerMobile)m_Mobile).FactionPlayerState = this; + } + + public PlayerState( Mobile mob, Faction faction, PlayerStateCollection owner ) + { + m_Mobile = mob; + m_Faction = faction; + m_Owner = owner; + + m_Rank = faction.Definition.Ranks[faction.Definition.Ranks.Length - 1]; + + Attach(); + Invalidate(); + } + + public PlayerState( GenericReader reader, Faction faction, PlayerStateCollection owner ) + { + m_Faction = faction; + m_Owner = owner; + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_Mobile = reader.ReadMobile(); + + m_KillPoints = reader.ReadEncodedInt(); + m_MerchantTitle = (MerchantTitle)reader.ReadEncodedInt(); + + m_Leaving = reader.ReadDateTime(); + + break; + } + } + + Attach(); + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Mobile) m_Mobile ); + + writer.WriteEncodedInt( (int) m_KillPoints ); + writer.WriteEncodedInt( (int) m_MerchantTitle ); + + writer.Write( (DateTime) m_Leaving ); + } + + public static PlayerState Find( Mobile mob ) + { + if ( mob is PlayerMobile ) + return ((PlayerMobile)mob).FactionPlayerState; + + return null; + } + + public int CompareTo( object obj ) + { + return m_KillPoints - ((PlayerState)obj).m_KillPoints; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/Reflector.cs b/Scripts/Engines/Factions/Core/Reflector.cs new file mode 100644 index 000000000..7dbf63c42 --- /dev/null +++ b/Scripts/Engines/Factions/Core/Reflector.cs @@ -0,0 +1,123 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; + +namespace Server.Factions +{ + public class Reflector + { + private static ArrayList m_Types = new ArrayList(); + + private static TownCollection m_Towns; + + public static TownCollection Towns + { + get + { + if ( m_Towns == null ) + ProcessTypes(); + + return m_Towns; + } + } + + private static FactionCollection m_Factions; + + public static FactionCollection Factions + { + get + { + if ( m_Factions == null ) + Reflector.ProcessTypes(); + + return m_Factions; + } + } + + public static void Configure() + { + EventSink.WorldSave += new WorldSaveEventHandler( EventSink_WorldSave ); + } + + private static void EventSink_WorldSave( WorldSaveEventArgs e ) + { + m_Types.Clear(); + } + + public static void Serialize( GenericWriter writer, Type type ) + { + int index = m_Types.IndexOf( type ); + + writer.WriteEncodedInt( (int) (index + 1) ); + + if ( index == -1 ) + { + writer.Write( type == null ? null : type.FullName ); + m_Types.Add( type ); + } + } + + public static Type Deserialize( GenericReader reader ) + { + int index = reader.ReadEncodedInt(); + + if ( index == 0 ) + { + string typeName = reader.ReadString(); + + if ( typeName == null ) + m_Types.Add( null ); + else + m_Types.Add( ScriptCompiler.FindTypeByFullName( typeName, false ) ); + + return (Type) m_Types[m_Types.Count - 1]; + } + else + { + return (Type)m_Types[index - 1]; + } + } + + private static object Construct( Type type ) + { + try{ return Activator.CreateInstance( type ); } + catch{ return null; } + } + + private static void ProcessTypes() + { + m_Factions = new FactionCollection(); + m_Towns = new TownCollection(); + + Assembly[] asms = ScriptCompiler.Assemblies; + + for ( int i = 0; i < asms.Length; ++i ) + { + Assembly asm = asms[i]; + TypeCache tc = ScriptCompiler.GetTypeCache( asm ); + Type[] types = tc.Types; + + for ( int j = 0; j < types.Length; ++j ) + { + Type type = types[j]; + + if ( type.IsSubclassOf( typeof( Faction ) ) ) + { + Faction faction = Construct( type ) as Faction; + + if ( faction != null ) + Faction.Factions.Add( faction ); + } + else if ( type.IsSubclassOf( typeof( Town ) ) ) + { + Town town = Construct( type ) as Town; + + if ( town != null ) + Town.Towns.Add( town ); + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/SilverGivenEntry.cs b/Scripts/Engines/Factions/Core/SilverGivenEntry.cs new file mode 100644 index 000000000..3f05e9bf6 --- /dev/null +++ b/Scripts/Engines/Factions/Core/SilverGivenEntry.cs @@ -0,0 +1,23 @@ +using System; + +namespace Server.Factions +{ + public class SilverGivenEntry + { + public static readonly TimeSpan ExpirePeriod = TimeSpan.FromHours( 3.0 ); + + private Mobile m_GivenTo; + private DateTime m_TimeOfGift; + + public Mobile GivenTo{ get{ return m_GivenTo; } } + public DateTime TimeOfGift{ get{ return m_TimeOfGift; } } + + public bool IsExpired{ get{ return ( m_TimeOfGift + ExpirePeriod ) < DateTime.Now; } } + + public SilverGivenEntry( Mobile givenTo ) + { + m_GivenTo = givenTo; + m_TimeOfGift = DateTime.Now; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/StrongholdRegion.cs b/Scripts/Engines/Factions/Core/StrongholdRegion.cs new file mode 100644 index 000000000..03b76fdf6 --- /dev/null +++ b/Scripts/Engines/Factions/Core/StrongholdRegion.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server; +using Server.Regions; + +namespace Server.Factions +{ + public class StrongholdRegion : BaseRegion + { + private Faction m_Faction; + + public Faction Faction + { + get{ return m_Faction; } + set{ m_Faction = value; } + } + + public StrongholdRegion( Faction faction ) : base( faction.Definition.FriendlyName, Faction.Facet, Region.DefaultPriority, faction.Definition.Stronghold.Area ) + { + m_Faction = faction; + + Register(); + } + + public override bool OnMoveInto( Mobile m, Direction d, Point3D newLocation, Point3D oldLocation ) + { + if ( !base.OnMoveInto( m, d, newLocation, oldLocation ) ) + return false; + + if ( m.AccessLevel >= AccessLevel.Counselor || Contains( oldLocation ) ) + return true; + + return ( Faction.Find( m, true, true ) != null ); + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/Town.cs b/Scripts/Engines/Factions/Core/Town.cs new file mode 100644 index 000000000..b72ab9a43 --- /dev/null +++ b/Scripts/Engines/Factions/Core/Town.cs @@ -0,0 +1,547 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Mobiles; +using Server.Commands; + +namespace Server.Factions +{ + [CustomEnum( new string[]{ "Britain", "Magincia", "Minoc", "Moonglow", "Skara Brae", "Trinsic", "Vesper", "Yew" } )] + public abstract class Town : IComparable + { + private TownDefinition m_Definition; + private TownState m_State; + + public TownDefinition Definition + { + get{ return m_Definition; } + set{ m_Definition = value; } + } + + public TownState State + { + get{ return m_State; } + set{ m_State = value; ConstructGuardLists(); } + } + + public int Silver + { + get{ return m_State.Silver; } + set{ m_State.Silver = value; } + } + + public Faction Owner + { + get{ return m_State.Owner; } + set{ Capture( value ); } + } + + public Mobile Sheriff + { + get{ return m_State.Sheriff; } + set{ m_State.Sheriff = value; } + } + + public Mobile Finance + { + get{ return m_State.Finance; } + set{ m_State.Finance = value; } + } + + public int Tax + { + get{ return m_State.Tax; } + set{ m_State.Tax = value; } + } + + public DateTime LastTaxChange + { + get{ return m_State.LastTaxChange; } + set{ m_State.LastTaxChange = value; } + } + + public static readonly TimeSpan TaxChangePeriod = TimeSpan.FromHours( 12.0 ); + public static readonly TimeSpan IncomePeriod = TimeSpan.FromDays( 1.0 ); + + public bool TaxChangeReady + { + get{ return ( m_State.LastTaxChange + TaxChangePeriod ) < DateTime.Now; } + } + + public static Town FromRegion( Region reg ) + { + if ( reg.Map != Faction.Facet ) + return null; + + TownCollection towns = Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + Town town = towns[i]; + + if ( reg.IsPartOf( town.Definition.Region ) ) + return town; + } + + return null; + } + + public int FinanceUpkeep + { + get + { + VendorListCollection vendorLists = VendorLists; + int upkeep = 0; + + for ( int i = 0; i < vendorLists.Count; ++i ) + upkeep += vendorLists[i].Vendors.Count * vendorLists[i].Definition.Upkeep; + + return upkeep; + } + } + + public int SheriffUpkeep + { + get + { + GuardListCollection guardLists = GuardLists; + int upkeep = 0; + + for ( int i = 0; i < guardLists.Count; ++i ) + upkeep += guardLists[i].Guards.Count * guardLists[i].Definition.Upkeep; + + return upkeep; + } + } + + public int DailyIncome + { + get{ return (10000 * (100 + m_State.Tax)) / 100; } + } + + public int NetCashFlow + { + get{ return DailyIncome - FinanceUpkeep - SheriffUpkeep; } + } + + public TownMonolith Monolith + { + get + { + MonolithCollection monoliths = BaseMonolith.Monoliths; + + foreach ( BaseMonolith monolith in monoliths ) + { + if ( monolith is TownMonolith ) + { + TownMonolith townMonolith = (TownMonolith)monolith; + + if ( townMonolith.Town == this ) + return townMonolith; + } + } + + return null; + } + } + + public DateTime LastIncome + { + get{ return m_State.LastIncome; } + set{ m_State.LastIncome = value; } + } + + public void BeginOrderFiring( Mobile from ) + { + bool isFinance = IsFinance( from ); + bool isSheriff = IsSheriff( from ); + string type = null; + + // NOTE: Messages not OSI-accurate, intentional + if ( isFinance && isSheriff ) // GM only + type = "vendor or guard"; + else if ( isFinance ) + type = "vendor"; + else if ( isSheriff ) + type = "guard"; + + from.SendMessage( "Target the {0} you wish to dismiss.", type ); + from.BeginTarget( 12, false, TargetFlags.None, new TargetCallback( EndOrderFiring ) ); + } + + public void EndOrderFiring( Mobile from, object obj ) + { + bool isFinance = IsFinance( from ); + bool isSheriff = IsSheriff( from ); + string type = null; + + if ( isFinance && isSheriff ) // GM only + type = "vendor or guard"; + else if ( isFinance ) + type = "vendor"; + else if ( isSheriff ) + type = "guard"; + + if ( obj is BaseFactionVendor ) + { + BaseFactionVendor vendor = (BaseFactionVendor)obj; + + if ( vendor.Town == this && isFinance ) + vendor.Delete(); + } + else if ( obj is BaseFactionGuard ) + { + BaseFactionGuard guard = (BaseFactionGuard)obj; + + if ( guard.Town == this && isSheriff ) + guard.Delete(); + } + else + { + from.SendMessage( "That is not a {0}!", type ); + } + } + + private Timer m_IncomeTimer; + + public void StartIncomeTimer() + { + if ( m_IncomeTimer != null ) + m_IncomeTimer.Stop(); + + m_IncomeTimer = Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.0 ), new TimerCallback( CheckIncome ) ); + } + + public void StopIncomeTimer() + { + if ( m_IncomeTimer != null ) + m_IncomeTimer.Stop(); + + m_IncomeTimer = null; + } + + public void CheckIncome() + { + if ( (LastIncome + IncomePeriod) > DateTime.Now || Owner == null ) + return; + + ProcessIncome(); + } + + public void ProcessIncome() + { + LastIncome = DateTime.Now; + + int flow = NetCashFlow; + + if ( (Silver + flow) < 0 ) + { + ArrayList toDelete = BuildFinanceList(); + + while ( (Silver + flow) < 0 && toDelete.Count > 0 ) + { + int index = Utility.Random( toDelete.Count ); + Mobile mob = (Mobile)toDelete[index]; + + mob.Delete(); + + toDelete.RemoveAt( index ); + flow = NetCashFlow; + } + } + + Silver += flow; + } + + public ArrayList BuildFinanceList() + { + ArrayList list = new ArrayList(); + + VendorListCollection vendorLists = VendorLists; + + for ( int i = 0; i < vendorLists.Count; ++i ) + list.AddRange( vendorLists[i].Vendors ); + + GuardListCollection guardLists = GuardLists; + + for ( int i = 0; i < guardLists.Count; ++i ) + list.AddRange( guardLists[i].Guards ); + + return list; + } + + private VendorListCollection m_VendorLists; + private GuardListCollection m_GuardLists; + + public VendorListCollection VendorLists + { + get{ return m_VendorLists; } + set{ m_VendorLists = value; } + } + + public GuardListCollection GuardLists + { + get{ return m_GuardLists; } + set{ m_GuardLists = value; } + } + + public void ConstructGuardLists() + { + GuardDefinition[] defs = ( Owner == null ? new GuardDefinition[0] : Owner.Definition.Guards ); + + m_GuardLists = new GuardListCollection(); + + for ( int i = 0; i < defs.Length; ++i ) + m_GuardLists.Add( new GuardList( defs[i] ) ); + } + + public GuardList FindGuardList( Type type ) + { + GuardListCollection guardLists = GuardLists; + + for ( int i = 0; i < guardLists.Count; ++i ) + { + GuardList guardList = guardLists[i]; + + if ( guardList.Definition.Type == type ) + return guardList; + } + + return null; + } + + public void ConstructVendorLists() + { + VendorDefinition[] defs = VendorDefinition.Definitions; + + m_VendorLists = new VendorListCollection(); + + for ( int i = 0; i < defs.Length; ++i ) + m_VendorLists.Add( new VendorList( defs[i] ) ); + } + + public VendorList FindVendorList( Type type ) + { + VendorListCollection vendorLists = VendorLists; + + for ( int i = 0; i < vendorLists.Count; ++i ) + { + VendorList vendorList = vendorLists[i]; + + if ( vendorList.Definition.Type == type ) + return vendorList; + } + + return null; + } + + public bool RegisterGuard( BaseFactionGuard guard ) + { + if ( guard == null ) + return false; + + GuardList guardList = FindGuardList( guard.GetType() ); + + if ( guardList == null ) + return false; + + guardList.Guards.Add( guard ); + return true; + } + + public bool UnregisterGuard( BaseFactionGuard guard ) + { + if ( guard == null ) + return false; + + GuardList guardList = FindGuardList( guard.GetType() ); + + if ( guardList == null ) + return false; + + if ( !guardList.Guards.Contains( guard ) ) + return false; + + guardList.Guards.Remove( guard ); + return true; + } + + public bool RegisterVendor( BaseFactionVendor vendor ) + { + if ( vendor == null ) + return false; + + VendorList vendorList = FindVendorList( vendor.GetType() ); + + if ( vendorList == null ) + return false; + + vendorList.Vendors.Add( vendor ); + return true; + } + + public bool UnregisterVendor( BaseFactionVendor vendor ) + { + if ( vendor == null ) + return false; + + VendorList vendorList = FindVendorList( vendor.GetType() ); + + if ( vendorList == null ) + return false; + + if ( !vendorList.Vendors.Contains( vendor ) ) + return false; + + vendorList.Vendors.Remove( vendor ); + return true; + } + + public static void Initialize() + { + TownCollection towns = Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + towns[i].Sheriff = towns[i].Sheriff; + towns[i].Finance = towns[i].Finance; + } + + CommandSystem.Register( "GrantTownSilver", AccessLevel.Administrator, new CommandEventHandler( GrantTownSilver_OnCommand ) ); + } + + public Town() + { + m_State = new TownState( this ); + ConstructVendorLists(); + ConstructGuardLists(); + StartIncomeTimer(); + } + + public bool IsSheriff( Mobile mob ) + { + if ( mob == null || mob.Deleted ) + return false; + + return ( mob.AccessLevel >= AccessLevel.GameMaster || mob == Sheriff ); + } + + public bool IsFinance( Mobile mob ) + { + if ( mob == null || mob.Deleted ) + return false; + + return ( mob.AccessLevel >= AccessLevel.GameMaster || mob == Finance ); + } + + public static TownCollection Towns{ get{ return Reflector.Towns; } } + + public const int SilverCaptureBonus = 10000; + + public void Capture( Faction f ) + { + f.Silver += SilverCaptureBonus; + + if ( m_State.Owner == f ) + return; + + if ( m_State.Owner == null ) // going from unowned to owned + LastIncome = DateTime.Now; + else if ( f == null ) // going from owned to unowned + LastIncome = DateTime.MinValue; + // otherwise changing hands, income timer doesn't change + + m_State.Owner = f; + + Sheriff = null; + Finance = null; + + TownMonolith monolith = this.Monolith; + + if ( monolith != null ) + monolith.Faction = f; + + VendorListCollection vendorLists = VendorLists; + + for ( int i = 0; i < vendorLists.Count; ++i ) + { + VendorList vendorList = vendorLists[i]; + FactionVendorCollection vendors = vendorList.Vendors; + + for ( int j = vendors.Count - 1; j >= 0; --j ) + vendors[j].Delete(); + } + + GuardListCollection guardLists = GuardLists; + + for ( int i = 0; i < guardLists.Count; ++i ) + { + GuardList guardList = guardLists[i]; + FactionGuardCollection guards = guardList.Guards; + + for ( int j = guards.Count - 1; j >= 0; --j ) + guards[j].Delete(); + } + + ConstructGuardLists(); + } + + public int CompareTo( object obj ) + { + return m_Definition.Sort - ((Town)obj).m_Definition.Sort; + } + + public override string ToString() + { + return m_Definition.FriendlyName; + } + + public static void WriteReference( GenericWriter writer, Town town ) + { + int idx = Towns.IndexOf( town ); + + writer.WriteEncodedInt( (int) (idx + 1) ); + } + + public static Town ReadReference( GenericReader reader ) + { + int idx = reader.ReadEncodedInt() - 1; + + if ( idx >= 0 && idx < Towns.Count ) + return Towns[idx]; + + return null; + } + + public static Town Parse( string name ) + { + TownCollection towns = Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + Town town = towns[i]; + + if ( Insensitive.Equals( town.Definition.FriendlyName, name ) ) + return town; + } + + return null; + } + + public static void GrantTownSilver_OnCommand( CommandEventArgs e ) + { + Town town = FromRegion( e.Mobile.Region ); + + if ( town == null ) + e.Mobile.SendMessage( "You are not in a faction town." ); + else if ( e.Length == 0 ) + e.Mobile.SendMessage( "Format: GrantTownSilver " ); + else + { + town.Silver += e.GetInt32( 0 ); + e.Mobile.SendMessage( "You have granted {0:N0} silver to the town. It now has {1:N0} silver.", e.GetInt32( 0 ), town.Silver ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/TownState.cs b/Scripts/Engines/Factions/Core/TownState.cs new file mode 100644 index 000000000..eb3eb8ce1 --- /dev/null +++ b/Scripts/Engines/Factions/Core/TownState.cs @@ -0,0 +1,168 @@ +using System; + +namespace Server.Factions +{ + public class TownState + { + private Town m_Town; + private Faction m_Owner; + + private Mobile m_Sheriff; + private Mobile m_Finance; + + private int m_Silver; + private int m_Tax; + + private DateTime m_LastTaxChange; + private DateTime m_LastIncome; + + public Town Town + { + get{ return m_Town; } + set{ m_Town = value; } + } + + public Faction Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + public Mobile Sheriff + { + get{ return m_Sheriff; } + set + { + if ( m_Sheriff != null ) + { + PlayerState pl = PlayerState.Find( m_Sheriff ); + + if ( pl != null ) + pl.Sheriff = null; + } + + m_Sheriff = value; + + if ( m_Sheriff != null ) + { + PlayerState pl = PlayerState.Find( m_Sheriff ); + + if ( pl != null ) + pl.Sheriff = m_Town; + } + } + } + + public Mobile Finance + { + get{ return m_Finance; } + set + { + if ( m_Finance != null ) + { + PlayerState pl = PlayerState.Find( m_Finance ); + + if ( pl != null ) + pl.Finance = null; + } + + m_Finance = value; + + if ( m_Finance != null ) + { + PlayerState pl = PlayerState.Find( m_Finance ); + + if ( pl != null ) + pl.Finance = m_Town; + } + } + } + + public int Silver + { + get{ return m_Silver; } + set{ m_Silver = value; } + } + + public int Tax + { + get{ return m_Tax; } + set{ m_Tax = value; } + } + + public DateTime LastTaxChange + { + get{ return m_LastTaxChange; } + set{ m_LastTaxChange = value; } + } + + public DateTime LastIncome + { + get{ return m_LastIncome; } + set{ m_LastIncome = value; } + } + + public TownState( Town town ) + { + m_Town = town; + } + + public TownState( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 3: + { + m_LastIncome = reader.ReadDateTime(); + + goto case 2; + } + case 2: + { + m_Tax = reader.ReadEncodedInt(); + m_LastTaxChange = reader.ReadDateTime(); + + goto case 1; + } + case 1: + { + m_Silver = reader.ReadEncodedInt(); + + goto case 0; + } + case 0: + { + m_Town = Town.ReadReference( reader ); + m_Owner = Faction.ReadReference( reader ); + + m_Sheriff = reader.ReadMobile(); + m_Finance = reader.ReadMobile(); + + m_Town.State = this; + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 3 ); // version + + writer.Write( (DateTime) m_LastIncome ); + + writer.WriteEncodedInt( (int) m_Tax ); + writer.Write( (DateTime) m_LastTaxChange ); + + writer.WriteEncodedInt( (int) m_Silver ); + + Town.WriteReference( writer, m_Town ); + Faction.WriteReference( writer, m_Owner ); + + writer.Write( (Mobile) m_Sheriff ); + writer.Write( (Mobile) m_Finance ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Core/VendorList.cs b/Scripts/Engines/Factions/Core/VendorList.cs new file mode 100644 index 000000000..ddd77695b --- /dev/null +++ b/Scripts/Engines/Factions/Core/VendorList.cs @@ -0,0 +1,26 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class VendorList + { + private VendorDefinition m_Definition; + private FactionVendorCollection m_Vendors; + + public VendorDefinition Definition{ get{ return m_Definition; } } + public FactionVendorCollection Vendors{ get{ return m_Vendors; } } + + public BaseFactionVendor Construct( Town town, Faction faction ) + { + try{ return Activator.CreateInstance( m_Definition.Type, new object[]{ town, faction } ) as BaseFactionVendor; } + catch{ return null; } + } + + public VendorList( VendorDefinition definition ) + { + m_Definition = definition; + m_Vendors = new FactionVendorCollection(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/FactionDefinition.cs b/Scripts/Engines/Factions/Definitions/FactionDefinition.cs new file mode 100644 index 000000000..1ab9d19db --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/FactionDefinition.cs @@ -0,0 +1,97 @@ +using System; + +namespace Server.Factions +{ + public class FactionDefinition + { + private int m_Sort; + + private int m_HuePrimary; + private int m_HueSecondary; + private int m_HueJoin; + private int m_HueBroadcast; + + private int m_WarHorseBody; + private int m_WarHorseItem; + + private string m_FriendlyName; + private string m_Keyword; + + private TextDefinition m_Name; + private TextDefinition m_PropName; + private TextDefinition m_Header; + private TextDefinition m_About; + private TextDefinition m_CityControl; + private TextDefinition m_SigilControl; + private TextDefinition m_SignupName; + private TextDefinition m_FactionStoneName; + private TextDefinition m_OwnerLabel; + + private TextDefinition m_GuardIgnore, m_GuardWarn, m_GuardAttack; + + private StrongholdDefinition m_Stronghold; + + private RankDefinition[] m_Ranks; + private GuardDefinition[] m_Guards; + + public int Sort{ get{ return m_Sort; } } + + public int HuePrimary{ get{ return m_HuePrimary; } } + public int HueSecondary{ get{ return m_HueSecondary; } } + public int HueJoin{ get{ return m_HueJoin; } } + public int HueBroadcast{ get{ return m_HueBroadcast; } } + + public int WarHorseBody{ get{ return m_WarHorseBody; } } + public int WarHorseItem{ get{ return m_WarHorseItem; } } + + public string FriendlyName{ get{ return m_FriendlyName; } } + public string Keyword{ get{ return m_Keyword; } } + + public TextDefinition Name{ get{ return m_Name; } } + public TextDefinition PropName{ get{ return m_PropName; } } + public TextDefinition Header{ get{ return m_Header; } } + public TextDefinition About{ get{ return m_About; } } + public TextDefinition CityControl{ get{ return m_CityControl; } } + public TextDefinition SigilControl{ get{ return m_SigilControl; } } + public TextDefinition SignupName{ get{ return m_SignupName; } } + public TextDefinition FactionStoneName{ get{ return m_FactionStoneName; } } + public TextDefinition OwnerLabel{ get{ return m_OwnerLabel; } } + + public TextDefinition GuardIgnore{ get{ return m_GuardIgnore; } } + public TextDefinition GuardWarn{ get{ return m_GuardWarn; } } + public TextDefinition GuardAttack{ get{ return m_GuardAttack; } } + + public StrongholdDefinition Stronghold{ get{ return m_Stronghold; } } + + public RankDefinition[] Ranks{ get{ return m_Ranks; } } + public GuardDefinition[] Guards{ get{ return m_Guards; } } + + public FactionDefinition( int sort, int huePrimary, int hueSecondary, int hueJoin, int hueBroadcast, int warHorseBody, int warHorseItem, string friendlyName, string keyword, TextDefinition name, TextDefinition propName, TextDefinition header, TextDefinition about, TextDefinition cityControl, TextDefinition sigilControl, TextDefinition signupName, TextDefinition factionStoneName, TextDefinition ownerLabel, TextDefinition guardIgnore, TextDefinition guardWarn, TextDefinition guardAttack, StrongholdDefinition stronghold, RankDefinition[] ranks, GuardDefinition[] guards ) + { + m_Sort = sort; + m_HuePrimary = huePrimary; + m_HueSecondary = hueSecondary; + m_HueJoin = hueJoin; + m_HueBroadcast = hueBroadcast; + m_WarHorseBody = warHorseBody; + m_WarHorseItem = warHorseItem; + m_FriendlyName = friendlyName; + m_Keyword = keyword; + m_Name = name; + m_PropName = propName; + m_Header = header; + m_About = about; + m_CityControl = cityControl; + m_SigilControl = sigilControl; + m_SignupName = signupName; + m_FactionStoneName = factionStoneName; + m_OwnerLabel = ownerLabel; + m_GuardIgnore = guardIgnore; + m_GuardWarn = guardWarn; + m_GuardAttack = guardAttack; + m_Stronghold = stronghold; + m_Ranks = ranks; + m_Guards = guards; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/FactionItemDefinition.cs b/Scripts/Engines/Factions/Definitions/FactionItemDefinition.cs new file mode 100644 index 000000000..344b273fb --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/FactionItemDefinition.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionItemDefinition + { + private int m_SilverCost; + private Type m_VendorType; + + public int SilverCost{ get{ return m_SilverCost; } } + public Type VendorType{ get{ return m_VendorType; } } + + public FactionItemDefinition( int silverCost, Type vendorType ) + { + m_SilverCost = silverCost; + m_VendorType = vendorType; + } + + private static FactionItemDefinition m_MetalArmor = new FactionItemDefinition( 1000, typeof( Blacksmith ) ); + private static FactionItemDefinition m_Weapon = new FactionItemDefinition( 1000, typeof( Blacksmith ) ); + private static FactionItemDefinition m_RangedWeapon = new FactionItemDefinition( 1000, typeof( Bowyer ) ); + private static FactionItemDefinition m_LeatherArmor = new FactionItemDefinition( 750, typeof( Tailor ) ); + private static FactionItemDefinition m_Clothing = new FactionItemDefinition( 200, typeof( Tailor ) ); + private static FactionItemDefinition m_Scroll = new FactionItemDefinition( 500, typeof( Mage ) ); + + public static FactionItemDefinition Identify( Item item ) + { + if ( item is BaseArmor ) + { + if ( CraftResources.GetType( ((BaseArmor)item).Resource ) == CraftResourceType.Leather ) + return m_LeatherArmor; + + return m_MetalArmor; + } + + if ( item is BaseRanged ) + return m_RangedWeapon; + else if ( item is BaseWeapon ) + return m_Weapon; + else if ( item is BaseClothing ) + return m_Clothing; + else if ( item is SpellScroll ) + return m_Scroll; + + return null; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/GuardDefinition.cs b/Scripts/Engines/Factions/Definitions/GuardDefinition.cs new file mode 100644 index 000000000..fa0204ab8 --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/GuardDefinition.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class GuardDefinition + { + private Type m_Type; + + private int m_Price; + private int m_Upkeep; + private int m_Maximum; + + private int m_ItemID; + + private TextDefinition m_Header; + private TextDefinition m_Label; + + public Type Type{ get{ return m_Type; } } + + public int Price{ get{ return m_Price; } } + public int Upkeep{ get{ return m_Upkeep; } } + public int Maximum{ get{ return m_Maximum; } } + public int ItemID{ get{ return m_ItemID; } } + + public TextDefinition Header{ get{ return m_Header; } } + public TextDefinition Label{ get{ return m_Label; } } + + public GuardDefinition( Type type, int itemID, int price, int upkeep, int maximum, TextDefinition header, TextDefinition label ) + { + m_Type = type; + + m_Price = price; + m_Upkeep = upkeep; + m_Maximum = maximum; + m_ItemID = itemID; + + m_Header = header; + m_Label = label; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/RankDefinition.cs b/Scripts/Engines/Factions/Definitions/RankDefinition.cs new file mode 100644 index 000000000..1922596bf --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/RankDefinition.cs @@ -0,0 +1,25 @@ +using System; + +namespace Server.Factions +{ + public class RankDefinition + { + private int m_Rank; + private int m_Required; + private int m_MaxWearables; + private TextDefinition m_Title; + + public int Rank{ get{ return m_Rank; } } + public int Required{ get{ return m_Required; } } + public int MaxWearables{ get{ return m_MaxWearables; } } + public TextDefinition Title{ get{ return m_Title; } } + + public RankDefinition( int rank, int required, int maxWearables, TextDefinition title ) + { + m_Rank = rank; + m_Required = required; + m_Title = title; + m_MaxWearables = maxWearables; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/StrongholdDefintion.cs b/Scripts/Engines/Factions/Definitions/StrongholdDefintion.cs new file mode 100644 index 000000000..2307543f2 --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/StrongholdDefintion.cs @@ -0,0 +1,27 @@ +using System; + +namespace Server.Factions +{ + public class StrongholdDefinition + { + private Rectangle2D[] m_Area; + private Point3D m_JoinStone; + private Point3D m_FactionStone; + private Point3D[] m_Monoliths; + + public Rectangle2D[] Area{ get{ return m_Area; } } + + public Point3D JoinStone{ get{ return m_JoinStone; } } + public Point3D FactionStone{ get{ return m_FactionStone; } } + + public Point3D[] Monoliths{ get{ return m_Monoliths; } } + + public StrongholdDefinition( Rectangle2D[] area, Point3D joinStone, Point3D factionStone, Point3D[] monoliths ) + { + m_Area = area; + m_JoinStone = joinStone; + m_FactionStone = factionStone; + m_Monoliths = monoliths; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/TownDefinition.cs b/Scripts/Engines/Factions/Definitions/TownDefinition.cs new file mode 100644 index 000000000..48190334e --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/TownDefinition.cs @@ -0,0 +1,59 @@ +using System; + +namespace Server.Factions +{ + public class TownDefinition + { + private int m_Sort; + private int m_SigilID; + + private string m_Region; + + private string m_FriendlyName; + + private TextDefinition m_TownName; + private TextDefinition m_TownStoneHeader; + private TextDefinition m_StrongholdMonolithName; + private TextDefinition m_TownMonolithName; + private TextDefinition m_TownStoneName; + private TextDefinition m_SigilName; + private TextDefinition m_CorruptedSigilName; + + private Point3D m_Monolith; + private Point3D m_TownStone; + + public int Sort{ get{ return m_Sort; } } + public int SigilID{ get{ return m_SigilID; } } + + public string Region{ get{ return m_Region; } } + public string FriendlyName{ get{ return m_FriendlyName; } } + + public TextDefinition TownName{ get{ return m_TownName; } } + public TextDefinition TownStoneHeader{ get{ return m_TownStoneHeader; } } + public TextDefinition StrongholdMonolithName{ get{ return m_StrongholdMonolithName; } } + public TextDefinition TownMonolithName{ get{ return m_TownMonolithName; } } + public TextDefinition TownStoneName{ get{ return m_TownStoneName; } } + public TextDefinition SigilName{ get{ return m_SigilName; } } + public TextDefinition CorruptedSigilName{ get{ return m_CorruptedSigilName; } } + + public Point3D Monolith{ get{ return m_Monolith; } } + public Point3D TownStone{ get{ return m_TownStone; } } + + public TownDefinition( int sort, int sigilID, string region, string friendlyName, TextDefinition townName, TextDefinition townStoneHeader, TextDefinition strongholdMonolithName, TextDefinition townMonolithName, TextDefinition townStoneName, TextDefinition sigilName, TextDefinition corruptedSigilName, Point3D monolith, Point3D townStone ) + { + m_Sort = sort; + m_SigilID = sigilID; + m_Region = region; + m_FriendlyName = friendlyName; + m_TownName = townName; + m_TownStoneHeader = townStoneHeader; + m_StrongholdMonolithName = strongholdMonolithName; + m_TownMonolithName = townMonolithName; + m_TownStoneName = townStoneName; + m_SigilName = sigilName; + m_CorruptedSigilName = corruptedSigilName; + m_Monolith = monolith; + m_TownStone = townStone; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Definitions/VendorDefinition.cs b/Scripts/Engines/Factions/Definitions/VendorDefinition.cs new file mode 100644 index 000000000..219b70b57 --- /dev/null +++ b/Scripts/Engines/Factions/Definitions/VendorDefinition.cs @@ -0,0 +1,83 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class VendorDefinition + { + private Type m_Type; + + private int m_Price; + private int m_Upkeep; + private int m_Maximum; + + private int m_ItemID; + + private TextDefinition m_Header; + private TextDefinition m_Label; + + public Type Type{ get{ return m_Type; } } + + public int Price{ get{ return m_Price; } } + public int Upkeep{ get{ return m_Upkeep; } } + public int Maximum{ get{ return m_Maximum; } } + public int ItemID{ get{ return m_ItemID; } } + + public TextDefinition Header{ get{ return m_Header; } } + public TextDefinition Label{ get{ return m_Label; } } + + public VendorDefinition( Type type, int itemID, int price, int upkeep, int maximum, TextDefinition header, TextDefinition label ) + { + m_Type = type; + + m_Price = price; + m_Upkeep = upkeep; + m_Maximum = maximum; + m_ItemID = itemID; + + m_Header = header; + m_Label = label; + } + + private static VendorDefinition[] m_Definitions = new VendorDefinition[] + { + new VendorDefinition( typeof( FactionBottleVendor ), 0xF0E, + 5000, + 1000, + 10, + new TextDefinition( 1011549, "POTION BOTTLE VENDOR" ), + new TextDefinition( 1011544, "Buy Potion Bottle Vendor" ) + ), + new VendorDefinition( typeof( FactionBoardVendor ), 0x1BD7, + 3000, + 500, + 10, + new TextDefinition( 1011552, "WOOD VENDOR" ), + new TextDefinition( 1011545, "Buy Wooden Board Vendor" ) + ), + new VendorDefinition( typeof( FactionOreVendor ), 0x19B8, + 3000, + 500, + 10, + new TextDefinition( 1011553, "IRON ORE VENDOR" ), + new TextDefinition( 1011546, "Buy Iron Ore Vendor" ) + ), + new VendorDefinition( typeof( FactionReagentVendor ), 0xF86, + 5000, + 1000, + 10, + new TextDefinition( 1011554, "REAGENT VENDOR" ), + new TextDefinition( 1011547, "Buy Reagent Vendor" ) + ), + new VendorDefinition( typeof( FactionHorseVendor ), 0x20DD, + 5000, + 1000, + 1, + new TextDefinition( 1011556, "HORSE BREEDER" ), + new TextDefinition( 1011555, "Buy Horse Breeder" ) + ) + }; + + public static VendorDefinition[] Definitions{ get{ return m_Definitions; } } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/ElectionGump.cs b/Scripts/Engines/Factions/Gumps/ElectionGump.cs new file mode 100644 index 000000000..47862690c --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/ElectionGump.cs @@ -0,0 +1,128 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class ElectionGump : FactionGump + { + private PlayerMobile m_From; + private Election m_Election; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 0: // back + { + m_From.SendGump( new FactionStoneGump( m_From, m_Election.Faction ) ); + break; + } + case 1: // vote + { + if ( m_Election.State == ElectionState.Election ) + m_From.SendGump( new VoteGump( m_From, m_Election ) ); + + break; + } + case 2: // campaign + { + if ( m_Election.CanBeCandidate( m_From ) ) + m_Election.AddCandidate( m_From ); + + break; + } + } + } + + public ElectionGump( PlayerMobile from, Election election ) : base( 50, 50 ) + { + m_From = from; + m_Election = election; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 180, 5054 ); + AddBackground( 10, 10, 400, 160, 3000 ); + + AddHtmlText( 20, 20, 380, 20, election.Faction.Definition.Header, false, false ); + + // NOTE: Gump not entirely OSI-accurate, intentionally so + + switch ( election.State ) + { + case ElectionState.Pending: + { + TimeSpan toGo = ( election.LastStateTime + Election.PendingPeriod ) - DateTime.Now; + int days = (int) (toGo.TotalDays + 0.5); + + AddHtmlLocalized( 20, 40, 380, 20, 1038034, false, false ); // A new election campaign is pending + + if ( days > 0 ) + { + AddHtmlLocalized( 20, 60, 280, 20, 1018062, false, false ); // Days until next election : + AddLabel( 300, 60, 0, days.ToString() ); + } + else + { + AddHtmlLocalized( 20, 60, 280, 20, 1018059, false, false ); // Election campaigning begins tonight. + } + + break; + } + case ElectionState.Campaign: + { + TimeSpan toGo = ( election.LastStateTime + Election.CampaignPeriod ) - DateTime.Now; + int days = (int) (toGo.TotalDays + 0.5); + + AddHtmlLocalized( 20, 40, 380, 20, 1018058, false, false ); // There is an election campaign in progress. + + if ( days > 0 ) + { + AddHtmlLocalized( 20, 60, 280, 20, 1038033, false, false ); // Days to go: + AddLabel( 300, 60, 0, days.ToString() ); + } + else + { + AddHtmlLocalized( 20, 60, 280, 20, 1018061, false, false ); // Campaign in progress. Voting begins tonight. + } + + if ( m_Election.CanBeCandidate( m_From ) ) + { + AddButton( 20, 110, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 110, 350, 20, 1011427, false, false ); // CAMPAIGN FOR LEADERSHIP + } + else + { + PlayerState pl = PlayerState.Find( m_From ); + + if ( pl == null || pl.Rank.Rank < Election.CandidateRank ) + AddHtmlLocalized( 20, 100, 380, 20, 1010118, false, false ); // You must have a higher rank to run for office + } + + break; + } + case ElectionState.Election: + { + TimeSpan toGo = ( election.LastStateTime + Election.VotingPeriod ) - DateTime.Now; + int days = (int) Math.Ceiling( toGo.TotalDays ); + + AddHtmlLocalized( 20, 40, 380, 20, 1018060, false, false ); // There is an election vote in progress. + + AddHtmlLocalized( 20, 60, 280, 20, 1038033, false, false ); + AddLabel( 300, 60, 0, days.ToString() ); + + AddHtmlLocalized( 55, 100, 380, 20, 1011428, false, false ); // VOTE FOR LEADERSHIP + AddButton( 20, 100, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + break; + } + } + + AddButton( 20, 140, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 140, 350, 20, 1011012, false, false ); // CANCEL + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/ElectionManagementGump.cs b/Scripts/Engines/Factions/Gumps/ElectionManagementGump.cs new file mode 100644 index 000000000..e1bee4ec7 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/ElectionManagementGump.cs @@ -0,0 +1,218 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class ElectionManagementGump : Gump + { + public string Right( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public static string FormatTimeSpan( TimeSpan ts ) + { + return String.Format( "{0:D2}:{1:D2}:{2:D2}:{3:D2}", ts.Days, ts.Hours % 24, ts.Minutes % 60, ts.Seconds % 60 ); + } + + public const int LabelColor = 0xFFFFFF; + + private Election m_Election; + private Candidate m_Candidate; + private int m_Page; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + int bid = info.ButtonID; + + if ( m_Candidate == null ) + { + if ( bid == 0 ) + { + } + else if ( bid == 1 ) + { + } + else + { + bid -= 2; + + if ( bid >= 0 && bid < m_Election.Candidates.Count ) + from.SendGump( new ElectionManagementGump( m_Election, m_Election.Candidates[bid], 0 ) ); + } + } + else + { + if ( bid == 0 ) + { + from.SendGump( new ElectionManagementGump( m_Election ) ); + } + else if ( bid == 1 ) + { + m_Election.RemoveCandidate( m_Candidate.Mobile ); + from.SendGump( new ElectionManagementGump( m_Election ) ); + } + else if ( bid == 2 && m_Page > 0 ) + { + from.SendGump( new ElectionManagementGump( m_Election, m_Candidate, m_Page - 1 ) ); + } + else if ( bid == 3 && (m_Page + 1) * 10 < m_Candidate.Voters.Count ) + { + from.SendGump( new ElectionManagementGump( m_Election, m_Candidate, m_Page + 1 ) ); + } + else + { + bid -= 4; + + if ( bid >= 0 && bid < m_Candidate.Voters.Count ) + { + m_Candidate.Voters.RemoveAt( bid ); + from.SendGump( new ElectionManagementGump( m_Election, m_Candidate, m_Page ) ); + } + } + } + } + + public ElectionManagementGump( Election election ) : this( election, null, 0 ) + { + } + + public ElectionManagementGump( Election election, Candidate candidate, int page ) : base( 40, 40 ) + { + m_Election = election; + m_Candidate = candidate; + m_Page = page; + + AddPage( 0 ); + + if ( candidate != null ) + { + AddBackground( 0, 0, 448, 354, 9270 ); + AddAlphaRegion( 10, 10, 428, 334 ); + + AddHtml( 10, 10, 428, 20, Color( Center( "Candidate Management" ), LabelColor ), false, false ); + + AddHtml( 45, 35, 100, 20, Color( "Player Name:", LabelColor ), false, false ); + AddHtml( 145, 35, 100, 20, Color( candidate.Mobile == null ? "null" : candidate.Mobile.Name, LabelColor ), false, false ); + + AddHtml( 45, 55, 100, 20, Color( "Vote Count:", LabelColor ), false, false ); + AddHtml( 145, 55, 100, 20, Color( candidate.Votes.ToString(), LabelColor ), false, false ); + + AddButton( 12, 73, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtml( 45, 75, 100, 20, Color( "Drop Candidate", LabelColor ), false, false ); + + AddImageTiled( 13, 99, 422, 242, 9264 ); + AddImageTiled( 14, 100, 420, 240, 9274 ); + AddAlphaRegion( 14, 100, 420, 240 ); + + AddHtml( 14, 100, 420, 20, Color( Center( "Voters" ), LabelColor ), false, false ); + + if ( page > 0 ) + AddButton( 397, 104, 0x15E3, 0x15E7, 2, GumpButtonType.Reply, 0 ); + else + AddImage( 397, 104, 0x25EA ); + + if ( (page + 1) * 10 < candidate.Voters.Count ) + AddButton( 414, 104, 0x15E1, 0x15E5, 3, GumpButtonType.Reply, 0 ); + else + AddImage( 414, 104, 0x25E6 ); + + + AddHtml( 14, 120, 30, 20, Color( Center( "DEL" ), LabelColor ), false, false ); + AddHtml( 47, 120, 150, 20, Color( "Name", LabelColor ), false, false ); + AddHtml( 195, 120, 100, 20, Color( Center( "Address" ), LabelColor ), false, false ); + AddHtml( 295, 120, 80, 20, Color( Center( "Time" ), LabelColor ), false, false ); + AddHtml( 355, 120, 60, 20, Color( Center( "Legit" ), LabelColor ), false, false ); + + int idx = 0; + + for ( int i = page*10; i >= 0 && i < candidate.Voters.Count && i < (page+1)*10; ++i, ++idx ) + { + Voter voter = (Voter)candidate.Voters[i]; + + AddButton( 13, 138 + (idx * 20), 4002, 4004, 4 + i, GumpButtonType.Reply, 0 ); + + object[] fields = voter.AcquireFields(); + + int x = 45; + + for ( int j = 0; j < fields.Length; ++j ) + { + object obj = fields[j]; + + if ( obj is Mobile ) + { + AddHtml( x + 2, 140 + (idx * 20), 150, 20, Color( ((Mobile)obj).Name, LabelColor ), false, false ); + x += 150; + } + else if ( obj is System.Net.IPAddress ) + { + AddHtml( x, 140 + (idx * 20), 100, 20, Color( Center( obj.ToString() ), LabelColor ), false, false ); + x += 100; + } + else if ( obj is DateTime ) + { + AddHtml( x, 140 + (idx * 20), 80, 20, Color( Center( FormatTimeSpan( ((DateTime)obj) - election.LastStateTime ) ), LabelColor ), false, false ); + x += 80; + } + else if ( obj is int ) + { + AddHtml( x, 140 + (idx * 20), 60, 20, Color( Center( (int)obj + "%" ), LabelColor ), false, false ); + x += 60; + } + } + } + } + else + { + AddBackground( 0, 0, 288, 334, 9270 ); + AddAlphaRegion( 10, 10, 268, 314 ); + + AddHtml( 10, 10, 268, 20, Color( Center( "Election Management" ), LabelColor ), false, false ); + + AddHtml( 45, 35, 100, 20, Color( "Current State:", LabelColor ), false, false ); + AddHtml( 145, 35, 100, 20, Color( election.State.ToString(), LabelColor ), false, false ); + + AddButton( 12, 53, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtml( 45, 55, 100, 20, Color( "Transition Time:", LabelColor ), false, false ); + AddHtml( 145, 55, 100, 20, Color( FormatTimeSpan( election.NextStateTime ), LabelColor ), false, false ); + + AddImageTiled( 13, 79, 262, 242, 9264 ); + AddImageTiled( 14, 80, 260, 240, 9274 ); + AddAlphaRegion( 14, 80, 260, 240 ); + + AddHtml( 14, 80, 260, 20, Color( Center( "Candidates" ), LabelColor ), false, false ); + AddHtml( 14, 100, 30, 20, Color( Center( "-->" ), LabelColor ), false, false ); + AddHtml( 47, 100, 150, 20, Color( "Name", LabelColor ), false, false ); + AddHtml( 195, 100, 80, 20, Color( Center( "Votes" ), LabelColor ), false, false ); + + for ( int i = 0; i < election.Candidates.Count; ++i ) + { + Candidate cd = election.Candidates[i]; + Mobile mob = cd.Mobile; + + if ( mob == null ) + continue; + + AddButton( 13, 118 + (i * 20), 4005, 4007, 2 + i, GumpButtonType.Reply, 0 ); + AddHtml( 47, 120 + (i * 20), 150, 20, Color( mob.Name, LabelColor ), false, false ); + AddHtml( 195, 120 + (i * 20), 80, 20, Color( Center( cd.Votes.ToString() ), LabelColor ), false, false ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/FactionGump.cs b/Scripts/Engines/Factions/Gumps/FactionGump.cs new file mode 100644 index 000000000..d08b96a55 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/FactionGump.cs @@ -0,0 +1,62 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Factions +{ + public abstract class FactionGump : Gump + { + public virtual int ButtonTypes{ get{ return 10; } } + + public int ToButtonID( int type, int index ) + { + return 1 + (index * ButtonTypes) + type; + } + + public bool FromButtonID( int buttonID, out int type, out int index ) + { + int offset = buttonID - 1; + + if ( offset >= 0 ) + { + type = offset % ButtonTypes; + index = offset / ButtonTypes; + return true; + } + else + { + type = index = 0; + return false; + } + } + + public static bool Exists( Mobile mob ) + { + NetState ns = mob.NetState; + + if ( ns == null ) + return false; + + for ( int i = 0; i < ns.Gumps.Count; ++i ) + { + if ( ns.Gumps[i] is FactionGump ) + return true; + } + + return false; + } + + public void AddHtmlText( int x, int y, int width, int height, TextDefinition text, bool back, bool scroll ) + { + if ( text != null && text.Number > 0 ) + AddHtmlLocalized( x, y, width, height, text.Number, back, scroll ); + else if ( text != null && text.String != null ) + AddHtml( x, y, width, height, text.String, back, scroll ); + } + + public FactionGump( int x, int y ) : base( x, y ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/FactionImbueGump.cs b/Scripts/Engines/Factions/Gumps/FactionImbueGump.cs new file mode 100644 index 000000000..aad98a4fc --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/FactionImbueGump.cs @@ -0,0 +1,104 @@ +using System; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; +using Server.Engines.Craft; + +namespace Server.Factions +{ + public class FactionImbueGump : FactionGump + { + private Item m_Item; + private Mobile m_Mobile; + private Faction m_Faction; + private CraftSystem m_CraftSystem; + private BaseTool m_Tool; + private object m_Notice; + private int m_Quality; + + private FactionItemDefinition m_Definition; + + public FactionImbueGump( int quality, Item item, Mobile from, CraftSystem craftSystem, BaseTool tool, object notice, int availableSilver, Faction faction, FactionItemDefinition def ) : base( 100, 200 ) + { + m_Item = item; + m_Mobile = from; + m_Faction = faction; + m_CraftSystem = craftSystem; + m_Tool = tool; + m_Notice = notice; + m_Quality = quality; + m_Definition = def; + + AddPage( 0 ); + + AddBackground( 0, 0, 320, 270, 5054 ); + AddBackground( 10, 10, 300, 250, 3000 ); + + AddHtmlLocalized( 20, 20, 210, 25, 1011569, false, false ); // Imbue with Faction properties? + + + AddHtmlLocalized( 20, 60, 170, 25, 1018302, false, false ); // Item quality: + AddHtmlLocalized( 175, 60, 100, 25, 1018305 - m_Quality, false, false ); // Exceptional, Average, Low + + AddHtmlLocalized( 20, 80, 170, 25, 1011572, false, false ); // Item Cost : + AddLabel( 175, 80, 0x34, def.SilverCost.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 100, 170, 25, 1011573, false, false ); // Your Silver : + AddLabel( 175, 100, 0x34, availableSilver.ToString( "N0" ) ); // NOTE: Added 'N0' + + + AddRadio( 20, 140, 210, 211, true, 1 ); + AddLabel( 55, 140, m_Faction.Definition.HuePrimary - 1, "*****" ); + AddHtmlLocalized( 150, 140, 150, 25, 1011570, false, false ); // Primary Color + + AddRadio( 20, 160, 210, 211, false, 2 ); + AddLabel( 55, 160, m_Faction.Definition.HueSecondary - 1, "*****" ); + AddHtmlLocalized( 150, 160, 150, 25, 1011571, false, false ); // Secondary Color + + + AddHtmlLocalized( 55, 200, 200, 25, 1011011, false, false ); // CONTINUE + AddButton( 20, 200, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 230, 200, 25, 1011012, false, false ); // CANCEL + AddButton( 20, 230, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + { + Container pack = m_Mobile.Backpack; + + if ( pack != null && m_Item.IsChildOf( pack ) ) + { + if ( pack.ConsumeTotal( typeof( Silver ), m_Definition.SilverCost ) ) + { + int hue; + + if ( m_Item is SpellScroll ) + hue = 0; + else if ( info.IsSwitched( 1 ) ) + hue = m_Faction.Definition.HuePrimary; + else + hue = m_Faction.Definition.HueSecondary; + + FactionItem.Imbue( m_Item, m_Faction, true, hue ); + } + else + { + m_Mobile.SendLocalizedMessage( 1042204 ); // You do not have enough silver. + } + } + } + + if ( m_Tool != null && !m_Tool.Deleted && m_Tool.UsesRemaining > 0 ) + m_Mobile.SendGump( new CraftGump( m_Mobile, m_CraftSystem, m_Tool, m_Notice ) ); + else if ( m_Notice is string ) + m_Mobile.SendMessage( (string) m_Notice ); + else if ( m_Notice is int && ((int)m_Notice) > 0 ) + m_Mobile.SendLocalizedMessage( (int) m_Notice ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/FactionStoneGump.cs b/Scripts/Engines/Factions/Gumps/FactionStoneGump.cs new file mode 100644 index 000000000..2eaaccff0 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/FactionStoneGump.cs @@ -0,0 +1,346 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class FactionStoneGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + + public override int ButtonTypes{ get{ return 4; } } + + public FactionStoneGump( PlayerMobile from, Faction faction ) : base( 20, 30 ) + { + m_From = from; + m_Faction = faction; + + AddPage( 0 ); + + AddBackground( 0, 0, 550, 440, 5054 ); + AddBackground( 10, 10, 530, 420, 3000 ); + + #region General + AddPage( 1 ); + + AddHtmlText( 20, 30, 510, 20, faction.Definition.Header, false, false ); + + AddHtmlLocalized( 20, 60, 100, 20, 1011429, false, false ); // Led By : + AddHtml( 125, 60, 200, 20, faction.Commander != null ? faction.Commander.Name : "Nobody", false, false ); + + AddHtmlLocalized( 20, 80, 100, 20, 1011457, false, false ); // Tithe rate : + if ( faction.Tithe >= 0 && faction.Tithe <= 100 && (faction.Tithe % 10) == 0 ) + AddHtmlLocalized( 125, 80, 350, 20, 1011480 + (faction.Tithe / 10), false, false ); + else + AddHtml( 125, 80, 350, 20, faction.Tithe + "%", false, false ); + + AddHtmlLocalized( 20, 100, 100, 20, 1011458, false, false ); // Traps placed : + AddHtml( 125, 100, 50, 20, faction.Traps.Count.ToString(), false, false ); + + AddHtmlLocalized( 55, 225, 200, 20, 1011428, false, false ); // VOTE FOR LEADERSHIP + AddButton( 20, 225, 4005, 4007, ToButtonID( 0, 0 ), GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 150, 100, 20, 1011430, false, false ); // CITY STATUS + AddButton( 20, 150, 4005, 4007, 0, GumpButtonType.Page, 2 ); + + AddHtmlLocalized( 55, 175, 100, 20, 1011444, false, false ); // STATISTICS + AddButton( 20, 175, 4005, 4007, 0, GumpButtonType.Page, 4 ); + + bool isMerchantQualified = MerchantTitles.HasMerchantQualifications( from ); + + PlayerState pl = PlayerState.Find( from ); + + if ( pl != null && pl.MerchantTitle != MerchantTitle.None ) + { + AddHtmlLocalized( 55, 200, 250, 20, 1011460, false, false ); // UNDECLARE FACTION MERCHANT + AddButton( 20, 200, 4005, 4007, ToButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + } + else if ( isMerchantQualified ) + { + AddHtmlLocalized( 55, 200, 250, 20, 1011459, false, false ); // DECLARE FACTION MERCHANT + AddButton( 20, 200, 4005, 4007, 0, GumpButtonType.Page, 5 ); + } + else + { + AddHtmlLocalized( 55, 200, 250, 20, 1011467, false, false ); // MERCHANT OPTIONS + AddImage( 20, 200, 4020 ); + } + + AddHtmlLocalized( 55, 250, 300, 20, 1011461, false, false ); // COMMANDER OPTIONS + if ( faction.IsCommander( from ) ) + AddButton( 20, 250, 4005, 4007, 0, GumpButtonType.Page, 6 ); + else + AddImage( 20, 250, 4020 ); + + AddHtmlLocalized( 55, 275, 300, 20, 1011426, false, false ); // LEAVE THIS FACTION + AddButton( 20, 275, 4005, 4007, ToButtonID( 0, 1 ), GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 300, 200, 20, 1011441, false, false ); // EXIT + AddButton( 20, 300, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + #endregion + + #region City Status + AddPage( 2 ); + + AddHtmlLocalized( 20, 30, 250, 20, 1011430, false, false ); // CITY STATUS + + TownCollection towns = Town.Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + Town town = towns[i]; + + AddHtmlText( 40, 55 + (i * 30), 150, 20, town.Definition.TownName, false, false ); + + if ( town.Owner == null ) + { + AddHtmlLocalized( 200, 55 + (i * 30), 150, 20, 1011462, false, false ); // : Neutral + } + else + { + AddHtmlLocalized( 200, 55 + (i * 30), 150, 20, town.Owner.Definition.OwnerLabel, false, false ); + + BaseMonolith monolith = town.Monolith; + + AddImage( 20, 60 + (i * 30), ( monolith != null && monolith.Sigil != null && monolith.Sigil.IsPurifying ) ? 0x938 : 0x939 ); + } + } + + + AddImage( 20, 300, 2361 ); + AddHtmlLocalized( 45, 295, 300, 20, 1011491, false, false ); // sigil may be recaptured + + AddImage( 20, 320, 2360 ); + AddHtmlLocalized( 45, 315, 300, 20, 1011492, false, false ); // sigil may not be recaptured + + AddHtmlLocalized( 55, 350, 100, 20, 1011447, false, false ); // BACK + AddButton( 20, 350, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Statistics + AddPage( 4 ); + + AddHtmlLocalized( 20, 30, 150, 20, 1011444, false, false ); // STATISTICS + + AddHtmlLocalized( 20, 100, 100, 20, 1011445, false, false ); // Name : + AddHtml( 120, 100, 150, 20, from.Name, false, false ); + + AddHtmlLocalized( 20, 130, 100, 20, 1018064, false, false ); // score : + AddHtml( 120, 130, 100, 20, (pl != null ? pl.KillPoints : 0).ToString(), false, false ); + + AddHtmlLocalized( 20, 160, 100, 20, 1011446, false, false ); // Rank : + AddHtml( 120, 160, 100, 20, (pl != null ? pl.Rank.Rank : 0).ToString(), false, false ); + + AddHtmlLocalized( 55, 250, 100, 20, 1011447, false, false ); // BACK + AddButton( 20, 250, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Merchant Options + if ( ( pl == null || pl.MerchantTitle == MerchantTitle.None ) && isMerchantQualified ) + { + AddPage( 5 ); + + AddHtmlLocalized( 20, 30, 250, 20, 1011467, false, false ); // MERCHANT OPTIONS + + AddHtmlLocalized( 20, 80, 300, 20, 1011473, false, false ); // Select the title you wish to display + + MerchantTitleInfo[] infos = MerchantTitles.Info; + + for ( int i = 0; i < infos.Length; ++i ) + { + MerchantTitleInfo info = infos[i]; + + if ( MerchantTitles.IsQualified( from, info ) ) + AddButton( 20, 100 + (i * 30), 4005, 4007, ToButtonID( 1, i + 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 20, 100 + (i * 30), 4020 ); + + AddHtmlText( 55, 100 + (i * 30), 200, 20, info.Label, false, false ); + } + + AddHtmlLocalized( 55, 340, 100, 20, 1011447, false, false ); // BACK + AddButton( 20, 340, 4005, 4007, 0, GumpButtonType.Page, 1 ); + } + #endregion + + #region Commander Options + if ( faction.IsCommander( from ) ) + { + #region General + AddPage( 6 ); + + AddHtmlLocalized( 20, 30, 200, 20, 1011461, false, false ); // COMMANDER OPTIONS + + AddHtmlLocalized( 20, 70, 120, 20, 1011457, false, false ); // Tithe rate : + if ( faction.Tithe >= 0 && faction.Tithe <= 100 && (faction.Tithe % 10) == 0 ) + AddHtmlLocalized( 140, 70, 250, 20, 1011480 + (faction.Tithe / 10), false, false ); + else + AddHtml( 140, 70, 250, 20, faction.Tithe + "%", false, false ); + + AddHtmlLocalized( 20, 100, 120, 20, 1011474, false, false ); // Silver available : + AddHtml( 140, 100, 50, 20, faction.Silver.ToString( "N0" ), false, false ); // NOTE: Added 'N0' formatting + + AddHtmlLocalized( 55, 130, 200, 20, 1011478, false, false ); // CHANGE TITHE RATE + AddButton( 20, 130, 4005, 4007, 0, GumpButtonType.Page, 8 ); + + AddHtmlLocalized( 55, 160, 200, 20, 1018301, false, false ); // TRANSFER SILVER + if ( faction.Silver >= 10000 ) + AddButton( 20, 160, 4005, 4007, 0, GumpButtonType.Page, 7 ); + else + AddImage( 20, 160, 4020 ); + + AddHtmlLocalized( 55, 310, 100, 20, 1011447, false, false ); // BACK + AddButton( 20, 310, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Town Finance + if ( faction.Silver >= 10000 ) + { + AddPage( 7 ); + + AddHtmlLocalized( 20, 30, 250, 20, 1011476, false, false ); // TOWN FINANCE + + AddHtmlLocalized( 20, 50, 400, 20, 1011477, false, false ); // Select a town to transfer 10000 silver to + + for ( int i = 0; i < towns.Count; ++i ) + { + Town town = towns[i]; + + AddHtmlText( 55, 75 + (i * 30), 200, 20, town.Definition.TownName, false, false ); + + if ( town.Owner == faction ) + AddButton( 20, 75 + (i * 30), 4005, 4007, ToButtonID( 2, i ), GumpButtonType.Reply, 0 ); + else + AddImage( 20, 75 + (i * 30), 4020 ); + } + + AddHtmlLocalized( 55, 310, 100, 20, 1011447, false, false ); // BACK + AddButton( 20, 310, 4005, 4007, 0, GumpButtonType.Page, 1 ); + } + #endregion + + #region Change Tithe Rate + AddPage( 8 ); + + AddHtmlLocalized( 20, 30, 400, 20, 1011479, false, false ); // Select the % for the new tithe rate + + int y = 55; + + for ( int i = 0; i <= 10; ++i ) + { + if ( i == 5 ) + y += 5; + + AddHtmlLocalized( 55, y, 300, 20, 1011480 + i, false, false ); + AddButton( 20, y, 4005, 4007, ToButtonID( 3, i ), GumpButtonType.Reply, 0 ); + + y += 20; + + if ( i == 5 ) + y += 5; + } + + AddHtmlLocalized( 55, 310, 300, 20, 1011447, false, false ); // BACK + AddButton( 20, 310, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + } + #endregion + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int type, index; + + if ( !FromButtonID( info.ButtonID, out type, out index ) ) + return; + + switch ( type ) + { + case 0: // general + { + switch ( index ) + { + case 0: // vote + { + m_From.SendGump( new ElectionGump( m_From, m_Faction.Election ) ); + break; + } + case 1: // leave + { + m_From.SendGump( new LeaveFactionGump( m_From, m_Faction ) ); + break; + } + } + + break; + } + case 1: // merchant title + { + if ( index >= 0 && index <= MerchantTitles.Info.Length ) + { + PlayerState pl = PlayerState.Find( m_From ); + + MerchantTitle newTitle = (MerchantTitle)index; + MerchantTitleInfo mti = MerchantTitles.GetInfo( newTitle ); + + if ( mti == null ) + { + m_From.SendLocalizedMessage( 1010120 ); // Your merchant title has been removed + + if ( pl != null ) + pl.MerchantTitle = newTitle; + } + else if ( MerchantTitles.IsQualified( m_From, mti ) ) + { + m_From.SendLocalizedMessage( mti.Assigned ); + + if ( pl != null ) + pl.MerchantTitle = newTitle; + } + } + + break; + } + case 2: // transfer silver + { + if ( !m_Faction.IsCommander( m_From ) ) + return; + + TownCollection towns = Town.Towns; + + if ( index >= 0 && index < towns.Count ) + { + Town town = towns[index]; + + if ( town.Owner == m_Faction ) + { + if ( m_Faction.Silver >= 10000 ) + { + m_Faction.Silver -= 10000; + town.Silver += 10000; + + // 10k in silver has been received by: + m_From.SendLocalizedMessage( 1042726, true, " " + town.Definition.FriendlyName ); + } + } + } + + break; + } + case 3: // change tithe + { + if ( !m_Faction.IsCommander( m_From ) ) + return; + + if ( index >= 0 && index <= 10 ) + m_Faction.Tithe = index*10; + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/FinanceGump.cs b/Scripts/Engines/Factions/Gumps/FinanceGump.cs new file mode 100644 index 000000000..b078da292 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/FinanceGump.cs @@ -0,0 +1,276 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Factions +{ + public class FinanceGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + private Town m_Town; + + private static int[] m_PriceOffsets = new int[] + { + -30, -25, -20, -15, -10, -5, + +50, +100, +150, +200, +250, +300 + }; + + public override int ButtonTypes{ get{ return 2; } } + + public FinanceGump( PlayerMobile from, Faction faction, Town town ) : base( 50, 50 ) + { + m_From = from; + m_Faction = faction; + m_Town = town; + + + AddPage( 0 ); + + AddBackground( 0, 0, 320, 410, 5054 ); + AddBackground( 10, 10, 300, 390, 3000 ); + + #region General + AddPage( 1 ); + + AddHtmlLocalized( 20, 30, 260, 25, 1011541, false, false ); // FINANCE MINISTER + + + AddHtmlLocalized( 55, 90, 200, 25, 1011539, false, false ); // CHANGE PRICES + AddButton( 20, 90, 4005, 4007, 0, GumpButtonType.Page, 2 ); + + AddHtmlLocalized( 55, 120, 200, 25, 1011540, false, false ); // BUY SHOPKEEPERS + AddButton( 20, 120, 4005, 4007, 0, GumpButtonType.Page, 3 ); + + AddHtmlLocalized( 55, 150, 200, 25, 1011495, false, false ); // VIEW FINANCES + AddButton( 20, 150, 4005, 4007, 0, GumpButtonType.Page, 4 ); + + AddHtmlLocalized( 55, 360, 200, 25, 1011441, false, false ); // EXIT + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + #endregion + + #region Change Prices + AddPage( 2 ); + + AddHtmlLocalized( 20, 30, 200, 25, 1011539, false, false ); // CHANGE PRICES + + for ( int i = 0; i < m_PriceOffsets.Length; ++i ) + { + int ofs = m_PriceOffsets[i]; + + int x = 20 + ((i / 6) * 150); + int y = 90 + ((i % 6) * 30); + + AddRadio( x, y, 208, 209, ( town.Tax == ofs ), i+1 ); + + if ( ofs < 0 ) + AddLabel( x + 35, y, 0x26, String.Concat( "- ", -ofs, "%" ) ); + else + AddLabel( x + 35, y, 0x12A, String.Concat( "+ ", ofs, "%" ) ); + } + + AddRadio( 20, 270, 208, 209, ( town.Tax == 0 ), 0 ); + AddHtmlLocalized( 55, 270, 90, 25, 1011542, false, false ); // normal + + AddHtmlLocalized( 55, 330, 200, 25, 1011509, false, false ); // Set Prices + AddButton( 20, 330, 4005, 4007, ToButtonID( 0, 0 ), GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Buy Shopkeepers + AddPage( 3 ); + + AddHtmlLocalized( 20, 30, 200, 25, 1011540, false, false ); // BUY SHOPKEEPERS + + VendorListCollection vendorLists = town.VendorLists; + + for ( int i = 0; i < vendorLists.Count; ++i ) + { + VendorList list = vendorLists[i]; + + AddButton( 20, 90 + (i * 40), 4005, 4007, 0, GumpButtonType.Page, 5 + i ); + AddItem( 55, 90 + (i * 40), list.Definition.ItemID ); + AddHtmlText( 100, 90 + (i * 40), 200, 25, list.Definition.Label, false, false ); + } + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region View Finances + AddPage( 4 ); + + int financeUpkeep = town.FinanceUpkeep; + int sheriffUpkeep = town.SheriffUpkeep; + int dailyIncome = town.DailyIncome; + int netCashFlow = town.NetCashFlow; + + + AddHtmlLocalized( 20, 30, 300, 25, 1011524, false, false ); // FINANCE STATEMENT + + AddHtmlLocalized( 20, 80, 300, 25, 1011538, false, false ); // Current total money for town : + AddLabel( 20, 100, 0x44, town.Silver.ToString() ); + + AddHtmlLocalized( 20, 130, 300, 25, 1011520, false, false ); // Finance Minister Upkeep : + AddLabel( 20, 150, 0x44, financeUpkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 180, 300, 25, 1011521, false, false ); // Sheriff Upkeep : + AddLabel( 20, 200, 0x44, sheriffUpkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 230, 300, 25, 1011522, false, false ); // Town Income : + AddLabel( 20, 250, 0x44, dailyIncome.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 280, 300, 25, 1011523, false, false ); // Net Cash flow per day : + AddLabel( 20, 300, 0x44, netCashFlow.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Shopkeeper Pages + for ( int i = 0; i < vendorLists.Count; ++i ) + { + VendorList vendorList = vendorLists[i]; + + AddPage( 5 + i ); + + AddHtmlText( 60, 30, 300, 25, vendorList.Definition.Header, false, false ); + AddItem( 20, 30, vendorList.Definition.ItemID ); + + AddHtmlLocalized( 20, 90, 200, 25, 1011514, false, false ); // You have : + AddLabel( 230, 90, 0x26, vendorList.Vendors.Count.ToString() ); + + AddHtmlLocalized( 20, 120, 200, 25, 1011515, false, false ); // Maximum : + AddLabel( 230, 120, 0x256, vendorList.Definition.Maximum.ToString() ); + + AddHtmlLocalized( 20, 150, 200, 25, 1011516, false, false ); // Cost : + AddLabel( 230, 150, 0x44, vendorList.Definition.Price.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 180, 200, 25, 1011517, false, false ); // Daily Pay : + AddLabel( 230, 180, 0x37, vendorList.Definition.Upkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 210, 200, 25, 1011518, false, false ); // Current Silver : + AddLabel( 230, 210, 0x44, town.Silver.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 240, 200, 25, 1011519, false, false ); // Current Payroll : + AddLabel( 230, 240, 0x44, financeUpkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlText( 55, 300, 200, 25, vendorList.Definition.Label, false, false ); + if ( town.Silver >= vendorList.Definition.Price ) + AddButton( 20, 300, 4005, 4007, ToButtonID( 1, i ), GumpButtonType.Reply, 0 ); + else + AddImage( 20, 300, 4020 ); + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 3 ); + } + #endregion + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( !m_Town.IsFinance( m_From ) || m_Town.Owner != m_Faction ) + { + m_From.SendLocalizedMessage( 1010339 ); // You no longer control this city + return; + } + + int type, index; + + if ( !FromButtonID( info.ButtonID, out type, out index ) ) + return; + + switch ( type ) + { + case 0: // general + { + switch ( index ) + { + case 0: // set price + { + int[] switches = info.Switches; + + if ( switches.Length == 0 ) + break; + + int opt = switches[0]; + int newTax = 0; + + if ( opt >= 1 && opt <= m_PriceOffsets.Length ) + newTax = m_PriceOffsets[opt - 1]; + + if ( m_Town.Tax == newTax ) + break; + + if ( m_From.AccessLevel == AccessLevel.Player && !m_Town.TaxChangeReady ) + { + TimeSpan remaining = DateTime.Now - ( m_Town.LastTaxChange + Town.TaxChangePeriod ); + + if ( remaining.TotalMinutes < 4 ) + m_From.SendLocalizedMessage( 1042165 ); // You must wait a short while before changing prices again. + else if ( remaining.TotalMinutes < 10 ) + m_From.SendLocalizedMessage( 1042166 ); // You must wait several minutes before changing prices again. + else if ( remaining.TotalHours < 1 ) + m_From.SendLocalizedMessage( 1042167 ); // You must wait up to an hour before changing prices again. + else if ( remaining.TotalHours < 4 ) + m_From.SendLocalizedMessage( 1042168 ); // You must wait a few hours before changing prices again. + else + m_From.SendLocalizedMessage( 1042169 ); // You must wait several hours before changing prices again. + } + else + { + m_Town.Tax = newTax; + + if ( m_From.AccessLevel == AccessLevel.Player ) + m_Town.LastTaxChange = DateTime.Now; + } + + break; + } + } + + break; + } + case 1: // make vendor + { + VendorListCollection vendorLists = m_Town.VendorLists; + + if ( index >= 0 && index < vendorLists.Count ) + { + VendorList vendorList = vendorLists[index]; + + Town town = Town.FromRegion( m_From.Region ); + + if ( Town.FromRegion( m_From.Region ) != m_Town ) + { + m_From.SendLocalizedMessage( 1010305 ); // You must be in your controlled city to buy Items + } + else if ( vendorList.Vendors.Count >= vendorList.Definition.Maximum ) + { + m_From.SendLocalizedMessage( 1010306 ); // You currently have too many of this enhancement type to place another + } + else if ( m_Town.Silver >= vendorList.Definition.Price ) + { + BaseFactionVendor vendor = vendorList.Construct( m_Town, m_Faction ); + + if ( vendor != null ) + { + m_Town.Silver -= vendorList.Definition.Price; + + vendor.MoveToWorld( m_From.Location, m_From.Map ); + vendor.Home = vendor.Location; + } + } + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/HorseBreederGump.cs b/Scripts/Engines/Factions/Gumps/HorseBreederGump.cs new file mode 100644 index 000000000..8cbd24e66 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/HorseBreederGump.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class HorseBreederGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + + public HorseBreederGump( PlayerMobile from, Faction faction ) : base( 20, 30 ) + { + m_From = from; + m_Faction = faction; + + AddPage( 0 ); + + AddBackground( 0, 0, 320, 280, 5054 ); + AddBackground( 10, 10, 300, 260, 3000 ); + + AddHtmlText( 20, 30, 300, 25, faction.Definition.Header, false, false ); + + AddHtmlLocalized( 20, 60, 300, 25, 1018306, false, false ); // Purchase a Faction War Horse + AddItem( 70, 120, 0x3FFE ); + + AddItem( 150, 120, 0xEF2 ); + AddLabel( 190, 122, 0x3E3, FactionWarHorse.SilverPrice.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddItem( 150, 150, 0xEEF ); + AddLabel( 190, 152, 0x3E3, FactionWarHorse.GoldPrice.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 55, 210, 200, 25, 1011011, false, false ); // CONTINUE + AddButton( 20, 210, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 240, 200, 25, 1011012, false, false ); // CANCEL + AddButton( 20, 240, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID != 1 ) + return; + + if ( Faction.Find( m_From ) != m_Faction ) + return; + + Container pack = m_From.Backpack; + + if ( pack == null ) + return; + + FactionWarHorse horse = new FactionWarHorse( m_Faction ); + + if ( (m_From.Followers + horse.ControlSlots) > m_From.FollowersMax ) + { + // TODO: Message? + horse.Delete(); + } + else + { + if ( pack.GetAmount( typeof( Silver ) ) < FactionWarHorse.SilverPrice ) + { + sender.Mobile.SendLocalizedMessage( 1042204 ); // You do not have enough silver. + horse.Delete(); + } + else if ( pack.GetAmount( typeof( Gold ) ) < FactionWarHorse.GoldPrice ) + { + sender.Mobile.SendLocalizedMessage( 1042205 ); // You do not have enough gold. + horse.Delete(); + } + else if ( pack.ConsumeTotal( typeof( Silver ), FactionWarHorse.SilverPrice ) && pack.ConsumeTotal( typeof( Gold ), FactionWarHorse.GoldPrice ) ) + { + horse.Controlled = true; + horse.ControlMaster = m_From; + + horse.ControlOrder = OrderType.Follow; + horse.ControlTarget = m_From; + + horse.MoveToWorld( m_From.Location, m_From.Map ); + } + else + { + horse.Delete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/JoinStoneGump.cs b/Scripts/Engines/Factions/Gumps/JoinStoneGump.cs new file mode 100644 index 000000000..93f45c215 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/JoinStoneGump.cs @@ -0,0 +1,52 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class JoinStoneGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + + public JoinStoneGump( PlayerMobile from, Faction faction ) : base( 20, 30 ) + { + m_From = from; + m_Faction = faction; + + AddPage( 0 ); + + AddBackground( 0, 0, 550, 440, 5054 ); + AddBackground( 10, 10, 530, 420, 3000 ); + + + AddHtmlText( 20, 30, 510, 20, faction.Definition.Header, false, false ); + AddHtmlText( 20, 130, 510, 100, faction.Definition.About, true, true ); + + + AddHtmlLocalized( 20, 60, 100, 20, 1011429, false, false ); // Led By : + AddHtml( 125, 60, 200, 20, faction.Commander != null ? faction.Commander.Name : "Nobody", false, false ); + + AddHtmlLocalized( 20, 80, 100, 20, 1011457, false, false ); // Tithe rate : + if ( faction.Tithe >= 0 && faction.Tithe <= 100 && (faction.Tithe % 10) == 0 ) + AddHtmlLocalized( 125, 80, 350, 20, 1011480 + (faction.Tithe / 10), false, false ); + else + AddHtml( 125, 80, 350, 20, faction.Tithe + "%", false, false ); + + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 200, 20, 1011425, false, false ); // JOIN THIS FACTION + + AddButton( 300, 400, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 200, 20, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_Faction.OnJoinAccepted( m_From ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/LeaveFactionGump.cs b/Scripts/Engines/Factions/Gumps/LeaveFactionGump.cs new file mode 100644 index 000000000..4da7d3110 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/LeaveFactionGump.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Gumps; +using Server.Guilds; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class LeaveFactionGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + + public LeaveFactionGump( PlayerMobile from, Faction faction ) : base( 20, 30 ) + { + m_From = from; + m_Faction = faction; + + AddBackground( 0, 0, 270, 120, 5054 ); + AddBackground( 10, 10, 250, 100, 3000 ); + + if ( from.Guild is Guild && ((Guild)from.Guild).Leader == from ) + AddHtmlLocalized( 20, 15, 230, 60, 1018057, true, true ); // Are you sure you want your entire guild to leave this faction? + else + AddHtmlLocalized( 20, 15, 230, 60, 1018063, true, true ); // Are you sure you want to leave this faction? + + AddHtmlLocalized( 55, 80, 75, 20, 1011011, false, false ); // CONTINUE + AddButton( 20, 80, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 170, 80, 75, 20, 1011012, false, false ); // CANCEL + AddButton( 135, 80, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 1: // continue + { + Guild guild = m_From.Guild as Guild; + + if ( guild == null ) + { + PlayerState pl = PlayerState.Find( m_From ); + + if ( pl != null ) + { + pl.Leaving = DateTime.Now; + + if ( Faction.LeavePeriod == TimeSpan.FromDays( 7.0 ) ) + m_From.SendLocalizedMessage( 1005065 ); // You will be removed from the faction in 7 days + else + m_From.SendMessage( "You will be removed from the faction in {0} days.", Faction.LeavePeriod.TotalDays ); + } + } + else if ( guild.Leader != m_From ) + { + m_From.SendLocalizedMessage( 1005061 ); // You cannot quit the faction because you are not the guild master + } + else + { + m_From.SendLocalizedMessage( 1042285 ); // Your guild is now quitting the faction. + + for ( int i = 0; i < guild.Members.Count; ++i ) + { + Mobile mob = (Mobile) guild.Members[i]; + PlayerState pl = PlayerState.Find( mob ); + + if ( pl != null ) + { + pl.Leaving = DateTime.Now; + + if ( Faction.LeavePeriod == TimeSpan.FromDays( 7.0 ) ) + mob.SendLocalizedMessage( 1005060 ); // Your guild will quit the faction in 7 days + else + mob.SendMessage( "Your guild will quit the faction in {0} days.", Faction.LeavePeriod.TotalDays ); + } + } + } + + break; + } + case 2: // cancel + { + m_From.SendLocalizedMessage( 500737 ); // Canceled resignation. + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/SheriffGump.cs b/Scripts/Engines/Factions/Gumps/SheriffGump.cs new file mode 100644 index 000000000..d81145288 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/SheriffGump.cs @@ -0,0 +1,177 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Factions +{ + public class SheriffGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + private Town m_Town; + + private void CenterItem( int itemID, int x, int y, int w, int h ) + { + Rectangle2D rc = ItemBounds.Table[itemID]; + AddItem( x + ((w - rc.Width) / 2) - rc.X, y + ((h - rc.Height) / 2) - rc.Y, itemID ); + } + + public SheriffGump( PlayerMobile from, Faction faction, Town town ) : base( 50, 50 ) + { + m_From = from; + m_Faction = faction; + m_Town = town; + + + AddPage( 0 ); + + AddBackground( 0, 0, 320, 410, 5054 ); + AddBackground( 10, 10, 300, 390, 3000 ); + + #region General + AddPage( 1 ); + + AddHtmlLocalized( 20, 30, 260, 25, 1011431, false, false ); // Sheriff + + AddHtmlLocalized( 55, 90, 200, 25, 1011494, false, false ); // HIRE GUARDS + AddButton( 20, 90, 4005, 4007, 0, GumpButtonType.Page, 3 ); + + AddHtmlLocalized( 55, 120, 200, 25, 1011495, false, false ); // VIEW FINANCES + AddButton( 20, 120, 4005, 4007, 0, GumpButtonType.Page, 2 ); + + AddHtmlLocalized( 55, 360, 200, 25, 1011441, false, false ); // Exit + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + #endregion + + #region Finances + AddPage( 2 ); + + int financeUpkeep = town.FinanceUpkeep; + int sheriffUpkeep = town.SheriffUpkeep; + int dailyIncome = town.DailyIncome; + int netCashFlow = town.NetCashFlow; + + AddHtmlLocalized( 20, 30, 300, 25, 1011524, false, false ); // FINANCE STATEMENT + + AddHtmlLocalized( 20, 80, 300, 25, 1011538, false, false ); // Current total money for town : + AddLabel( 20, 100, 0x44, town.Silver.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 130, 300, 25, 1011520, false, false ); // Finance Minister Upkeep : + AddLabel( 20, 150, 0x44, financeUpkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 180, 300, 25, 1011521, false, false ); // Sheriff Upkeep : + AddLabel( 20, 200, 0x44, sheriffUpkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 230, 300, 25, 1011522, false, false ); // Town Income : + AddLabel( 20, 250, 0x44, dailyIncome.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 280, 300, 25, 1011523, false, false ); // Net Cash flow per day : + AddLabel( 20, 300, 0x44, netCashFlow.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Hire Guards + AddPage( 3 ); + + AddHtmlLocalized( 20, 30, 300, 25, 1011494, false, false ); // HIRE GUARDS + + GuardListCollection guardLists = town.GuardLists; + + for ( int i = 0; i < guardLists.Count; ++i ) + { + GuardList guardList = guardLists[i]; + int y = 90 + (i * 60); + + AddButton( 20, y, 4005, 4007, 0, GumpButtonType.Page, 4 + i ); + CenterItem( guardList.Definition.ItemID, 50, y - 20, 70, 60 ); + AddHtmlText( 120, y, 200, 25, guardList.Definition.Header, false, false ); + } + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 1 ); + #endregion + + #region Guard Pages + for ( int i = 0; i < guardLists.Count; ++i ) + { + GuardList guardList = guardLists[i]; + + AddPage( 4 + i ); + + AddHtmlText( 90, 30, 300, 25, guardList.Definition.Header, false, false ); + CenterItem( guardList.Definition.ItemID, 10, 10, 80, 80 ); + + AddHtmlLocalized( 20, 90, 200, 25, 1011514, false, false ); // You have : + AddLabel( 230, 90, 0x26, guardList.Guards.Count.ToString() ); + + AddHtmlLocalized( 20, 120, 200, 25, 1011515, false, false ); // Maximum : + AddLabel( 230, 120, 0x12A, guardList.Definition.Maximum.ToString() ); + + AddHtmlLocalized( 20, 150, 200, 25, 1011516, false, false ); // Cost : + AddLabel( 230, 150, 0x44, guardList.Definition.Price.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 180, 200, 25, 1011517, false, false ); // Daily Pay : + AddLabel( 230, 180, 0x37, guardList.Definition.Upkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 210, 200, 25, 1011518, false, false ); // Current Silver : + AddLabel( 230, 210, 0x44, town.Silver.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlLocalized( 20, 240, 200, 25, 1011519, false, false ); // Current Payroll : + AddLabel( 230, 240, 0x44, sheriffUpkeep.ToString( "N0" ) ); // NOTE: Added 'N0' + + AddHtmlText( 55, 300, 200, 25, guardList.Definition.Label, false, false ); + AddButton( 20, 300, 4005, 4007, 1 + i, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 360, 200, 25, 1011067, false, false ); // Previous page + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Page, 3 ); + } + #endregion + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( !m_Town.IsSheriff( m_From ) || m_Town.Owner != m_Faction ) + { + m_From.SendLocalizedMessage( 1010339 ); // You no longer control this city + return; + } + + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_Town.GuardLists.Count ) + { + GuardList guardList = m_Town.GuardLists[index]; + Town town = Town.FromRegion( m_From.Region ); + + if ( Town.FromRegion( m_From.Region ) != m_Town ) + { + m_From.SendLocalizedMessage( 1010305 ); // You must be in your controlled city to buy Items + } + else if ( guardList.Guards.Count >= guardList.Definition.Maximum ) + { + m_From.SendLocalizedMessage( 1010306 ); // You currently have too many of this enhancement type to place another + } + else if ( m_Town.Silver >= guardList.Definition.Price ) + { + BaseFactionGuard guard = guardList.Construct(); + + if ( guard != null ) + { + guard.Faction = m_Faction; + guard.Town = m_Town; + + m_Town.Silver -= guardList.Definition.Price; + + guard.MoveToWorld( m_From.Location, m_From.Map ); + guard.Home = guard.Location; + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/TownStoneGump.cs b/Scripts/Engines/Factions/Gumps/TownStoneGump.cs new file mode 100644 index 000000000..2dc887f2a --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/TownStoneGump.cs @@ -0,0 +1,206 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Factions +{ + public class TownStoneGump : FactionGump + { + private PlayerMobile m_From; + private Faction m_Faction; + private Town m_Town; + + public TownStoneGump( PlayerMobile from, Faction faction, Town town ) : base( 50, 50 ) + { + m_From = from; + m_Faction = faction; + m_Town = town; + + AddPage( 0 ); + + AddBackground( 0, 0, 320, 250, 5054 ); + AddBackground( 10, 10, 300, 230, 3000 ); + + AddHtmlText( 25, 30, 250, 25, town.Definition.TownStoneHeader, false, false ); + + AddHtmlLocalized( 55, 60, 150, 25, 1011557, false, false ); // Hire Sheriff + AddButton( 20, 60, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 90, 150, 25, 1011559, false, false ); // Hire Finance Minister + AddButton( 20, 90, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 120, 150, 25, 1011558, false, false ); // Fire Sheriff + AddButton( 20, 120, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 150, 150, 25, 1011560, false, false ); // Fire Finance Minister + AddButton( 20, 150, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 210, 150, 25, 1011441, false, false ); // EXIT + AddButton( 20, 210, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Town.Owner != m_Faction || !m_Faction.IsCommander( m_From ) ) + { + m_From.SendLocalizedMessage( 1010339 ); // You no longer control this city + return; + } + + switch ( info.ButtonID ) + { + case 1: // hire sheriff + { + if ( m_Town.Sheriff != null ) + { + m_From.SendLocalizedMessage( 1010342 ); // You must fire your Sheriff before you can elect a new one + } + else + { + m_From.SendLocalizedMessage( 1010347 ); // Who shall be your new sheriff + m_From.BeginTarget( 12, false, TargetFlags.None, new TargetCallback( HireSheriff_OnTarget ) ); + } + + break; + } + case 2: // hire finance minister + { + if ( m_Town.Finance != null ) + { + m_From.SendLocalizedMessage( 1010345 ); // You must fire your finance minister before you can elect a new one + } + else + { + m_From.SendLocalizedMessage( 1010348 ); // Who shall be your new Minister of Finances? + m_From.BeginTarget( 12, false, TargetFlags.None, new TargetCallback( HireFinanceMinister_OnTarget ) ); + } + + break; + } + case 3: // fire sheriff + { + if ( m_Town.Sheriff == null ) + { + m_From.SendLocalizedMessage( 1010350 ); // You need to elect a sheriff before you can fire one + } + else + { + m_From.SendLocalizedMessage( 1010349 ); // You have fired your sheriff + m_Town.Sheriff.SendLocalizedMessage( 1010270 ); // You have been fired as Sheriff + m_Town.Sheriff = null; + } + + break; + } + case 4: // fire finance minister + { + if ( m_Town.Finance == null ) + { + m_From.SendLocalizedMessage( 1010352 ); // You need to elect a financial minister before you can fire one + } + else + { + m_From.SendLocalizedMessage( 1010351 ); // You have fired your financial Minister + m_Town.Finance.SendLocalizedMessage( 1010151 ); // You have been fired as Finance Minister + m_Town.Finance = null; + } + + break; + } + } + } + + private void HireSheriff_OnTarget( Mobile from, object obj ) + { + if ( m_Town.Owner != m_Faction || !m_Faction.IsCommander( from ) ) + { + from.SendLocalizedMessage( 1010339 ); // You no longer control this city + return; + } + else if ( m_Town.Sheriff != null ) + { + from.SendLocalizedMessage( 1010342 ); // You must fire your Sheriff before you can elect a new one + } + else if ( obj is Mobile ) + { + Mobile targ = (Mobile)obj; + PlayerState pl = PlayerState.Find( targ ); + + if ( pl == null ) + { + from.SendLocalizedMessage( 1010337 ); // You must pick someone in a faction + } + else if ( pl.Faction != m_Faction ) + { + from.SendLocalizedMessage( 1010338 ); // You must pick someone in the correct faction + } + else if ( m_Faction.Commander == targ ) + { + from.SendLocalizedMessage( 1010335 ); // You cannot elect a commander to a town position + } + else if ( pl.Sheriff != null || pl.Finance != null ) + { + from.SendLocalizedMessage( 1005245 ); // You must pick someone who does not already hold a city post + } + else + { + m_Town.Sheriff = targ; + targ.SendLocalizedMessage( 1010340 ); // You are now the Sheriff + from.SendLocalizedMessage( 1010341 ); // You have elected a Sheriff + } + } + else + { + from.SendLocalizedMessage( 1010334 ); // You must select a player to hold a city position! + } + } + + private void HireFinanceMinister_OnTarget( Mobile from, object obj ) + { + if ( m_Town.Owner != m_Faction || !m_Faction.IsCommander( from ) ) + { + from.SendLocalizedMessage( 1010339 ); // You no longer control this city + return; + } + else if ( m_Town.Finance != null ) + { + from.SendLocalizedMessage( 1010342 ); // You must fire your Sheriff before you can elect a new one + } + else if ( obj is Mobile ) + { + Mobile targ = (Mobile)obj; + PlayerState pl = PlayerState.Find( targ ); + + if ( pl == null ) + { + from.SendLocalizedMessage( 1010337 ); // You must pick someone in a faction + } + else if ( pl.Faction != m_Faction ) + { + from.SendLocalizedMessage( 1010338 ); // You must pick someone in the correct faction + } + else if ( m_Faction.Commander == targ ) + { + from.SendLocalizedMessage( 1010335 ); // You cannot elect a commander to a town position + } + else if ( pl.Sheriff != null || pl.Finance != null ) + { + from.SendLocalizedMessage( 1005245 ); // You must pick someone who does not already hold a city post + } + else + { + m_Town.Finance = targ; + targ.SendLocalizedMessage( 1010343 ); // You are now the Financial Minister + from.SendLocalizedMessage( 1010344 ); // You have elected a Financial Minister + } + } + else + { + from.SendLocalizedMessage( 1010334 ); // You must select a player to hold a city position! + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Gumps/VoteGump.cs b/Scripts/Engines/Factions/Gumps/VoteGump.cs new file mode 100644 index 000000000..ac39bc2d3 --- /dev/null +++ b/Scripts/Engines/Factions/Gumps/VoteGump.cs @@ -0,0 +1,68 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class VoteGump : FactionGump + { + private PlayerMobile m_From; + private Election m_Election; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 ) + { + m_From.SendGump( new FactionStoneGump( m_From, m_Election.Faction ) ); + } + else + { + if ( !m_Election.CanVote( m_From ) ) + return; + + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_Election.Candidates.Count ) + m_Election.Candidates[index].Voters.Add( new Voter( m_From, m_Election.Candidates[index].Mobile ) ); + + m_From.SendGump( new VoteGump( m_From, m_Election ) ); + } + } + + public VoteGump( PlayerMobile from, Election election ) : base( 50, 50 ) + { + m_From = from; + m_Election = election; + + bool canVote = election.CanVote( from ); + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 350, 5054 ); + AddBackground( 10, 10, 400, 330, 3000 ); + + AddHtmlText( 20, 20, 380, 20, election.Faction.Definition.Header, false, false ); + + if ( canVote ) + AddHtmlLocalized( 20, 60, 380, 20, 1011428, false, false ); // VOTE FOR LEADERSHIP + else + AddHtmlLocalized( 20, 60, 380, 20, 1038032, false, false ); // You have already voted in this election. + + for ( int i = 0; i < election.Candidates.Count; ++i ) + { + Candidate cd = election.Candidates[i]; + + if ( canVote ) + AddButton( 20, 100 + (i * 20), 4005, 4007, i + 1, GumpButtonType.Reply, 0 ); + + AddLabel( 55, 100 + (i * 20), 0, cd.Mobile.Name ); + AddLabel( 300, 100 + (i * 20), 0, cd.Votes.ToString() ); + } + + AddButton( 20, 310, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 310, 100, 20, 1011012, false, false ); // CANCEL + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Factions/CouncilOfMages.cs b/Scripts/Engines/Factions/Instances/Factions/CouncilOfMages.cs new file mode 100644 index 000000000..9c9f94a47 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Factions/CouncilOfMages.cs @@ -0,0 +1,86 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class CouncilOfMages : Faction + { + private static Faction m_Instance; + + public static Faction Instance{ get{ return m_Instance; } } + + public CouncilOfMages() + { + m_Instance = this; + + Definition = + new FactionDefinition( + 1, + 1325, // blue + 1310, // bluish white + 1325, // join stone : blue + 1325, // broadcast : blue + 0x77, 0x3EB1, // war horse + "Council of Mages", "council", + new TextDefinition( 1011535, "COUNCIL OF MAGES" ), + new TextDefinition( 1060770, "Council of Mages faction" ), + new TextDefinition( 1011422, "
COUNCIL OF MAGES
" ), + new TextDefinition( 1011449, + "The council of Mages have their roots in the city of Moonglow, where " + + "they once convened. They began as a small movement, dedicated to " + + "calling forth the Stranger, who saved the lands once before. A " + + "series of war and murders and misbegotten trials by those loyal to " + + "Lord British has caused the group to take up the banner of war." ), + new TextDefinition( 1011455, "This city is controlled by the Council of Mages." ), + new TextDefinition( 1042253, "This sigil has been corrupted by the Council of Mages" ), + new TextDefinition( 1041044, "The faction signup stone for the Council of Mages" ), + new TextDefinition( 1041382, "The Faction Stone of the Council of Mages" ), + new TextDefinition( 1011464, ": Council of Mages" ), + new TextDefinition( 1005187, "Members of the Council of Mages will now be ignored." ), + new TextDefinition( 1005188, "Members of the Council of Mages will now be warned to leave." ), + new TextDefinition( 1005189, "Members of the Council of Mages will now be beaten with a stick." ), + new StrongholdDefinition( + new Rectangle2D[] + { + new Rectangle2D( 3756, 2232, 4, 23 ), + new Rectangle2D( 3760, 2227, 60, 28 ), + new Rectangle2D( 3782, 2219, 18, 8 ), + new Rectangle2D( 3778, 2255, 35, 17 ) + }, + new Point3D( 3750, 2241, 20 ), + new Point3D( 3795, 2259, 20 ), + new Point3D[] + { + new Point3D( 3793, 2255, 20 ), + new Point3D( 3793, 2252, 20 ), + new Point3D( 3793, 2249, 20 ), + new Point3D( 3793, 2246, 20 ), + new Point3D( 3797, 2255, 20 ), + new Point3D( 3797, 2252, 20 ), + new Point3D( 3797, 2249, 20 ), + new Point3D( 3797, 2246, 20 ) + } ), + new RankDefinition[] + { + new RankDefinition( 10, 991, 8, new TextDefinition( 1060789, "Inquisitor of the Council" ) ), + new RankDefinition( 9, 950, 7, new TextDefinition( 1060788, "Archon of Principle" ) ), + new RankDefinition( 8, 900, 6, new TextDefinition( 1060787, "Luminary" ) ), + new RankDefinition( 7, 800, 6, new TextDefinition( 1060787, "Luminary" ) ), + new RankDefinition( 6, 700, 5, new TextDefinition( 1060786, "Diviner" ) ), + new RankDefinition( 5, 600, 5, new TextDefinition( 1060786, "Diviner" ) ), + new RankDefinition( 4, 500, 5, new TextDefinition( 1060786, "Diviner" ) ), + new RankDefinition( 3, 400, 4, new TextDefinition( 1060785, "Mystic" ) ), + new RankDefinition( 2, 200, 4, new TextDefinition( 1060785, "Mystic" ) ), + new RankDefinition( 1, 0, 4, new TextDefinition( 1060785, "Mystic" ) ) + }, + new GuardDefinition[] + { + new GuardDefinition( typeof( FactionHenchman ), 0x1403, 5000, 1000, 10, new TextDefinition( 1011526, "HENCHMAN" ), new TextDefinition( 1011510, "Hire Henchman" ) ), + new GuardDefinition( typeof( FactionMercenary ), 0x0F62, 6000, 2000, 10, new TextDefinition( 1011527, "MERCENARY" ), new TextDefinition( 1011511, "Hire Mercenary" ) ), + new GuardDefinition( typeof( FactionSorceress ), 0x0E89, 7000, 3000, 10, new TextDefinition( 1011507, "SORCERESS" ), new TextDefinition( 1011501, "Hire Sorceress" ) ), + new GuardDefinition( typeof( FactionWizard ), 0x13F8, 8000, 4000, 10, new TextDefinition( 1011508, "ELDER WIZARD" ), new TextDefinition( 1011502, "Hire Elder Wizard" ) ), + } + ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Factions/Minax.cs b/Scripts/Engines/Factions/Instances/Factions/Minax.cs new file mode 100644 index 000000000..525164ef8 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Factions/Minax.cs @@ -0,0 +1,84 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class Minax : Faction + { + private static Faction m_Instance; + + public static Faction Instance{ get{ return m_Instance; } } + + public Minax() + { + m_Instance = this; + + Definition = + new FactionDefinition( + 0, + 1645, // dark red + 1109, // shadow + 1645, // join stone : dark red + 1645, // broadcast : dark red + 0x78, 0x3EAF, // war horse + "Minax", "minax", + new TextDefinition( 1011534, "MINAX" ), + new TextDefinition( 1060769, "Minax faction" ), + new TextDefinition( 1011421, "
FOLLOWERS OF MINAX
" ), + new TextDefinition( 1011448, + "The followers of Minax have taken control in the old lands, " + + "and intend to hold it for as long as they can. Allying themselves " + + "with orcs, headless, gazers, trolls, and other beasts, they seek " + + "revenge against Lord British, for slights both real and imagined, " + + "though some of the followers wish only to wreak havoc on the " + + "unsuspecting populace." ), + new TextDefinition( 1011453, "This city is controlled by Minax." ), + new TextDefinition( 1042252, "This sigil has been corrupted by the Followers of Minax" ), + new TextDefinition( 1041043, "The faction signup stone for the Followers of Minax" ), + new TextDefinition( 1041381, "The Faction Stone of Minax" ), + new TextDefinition( 1011463, ": Minax" ), + new TextDefinition( 1005190, "Followers of Minax will now be ignored." ), + new TextDefinition( 1005191, "Followers of Minax will now be told to go away." ), + new TextDefinition( 1005192, "Followers of Minax will now be hanged by their toes." ), + new StrongholdDefinition( + new Rectangle2D[] + { + new Rectangle2D( 1097, 2570, 70, 50 ) + }, + new Point3D( 1172, 2593, 0 ), + new Point3D( 1117, 2587, 18 ), + new Point3D[] + { + new Point3D( 1113, 2601, 18 ), + new Point3D( 1113, 2598, 18 ), + new Point3D( 1113, 2595, 18 ), + new Point3D( 1113, 2592, 18 ), + new Point3D( 1116, 2601, 18 ), + new Point3D( 1116, 2598, 18 ), + new Point3D( 1116, 2595, 18 ), + new Point3D( 1116, 2592, 18 ) + } ), + new RankDefinition[] + { + new RankDefinition( 10, 991, 8, new TextDefinition( 1060784, "Avenger of Mondain" ) ), + new RankDefinition( 9, 950, 7, new TextDefinition( 1060783, "Dread Knight" ) ), + new RankDefinition( 8, 900, 6, new TextDefinition( 1060782, "Warlord" ) ), + new RankDefinition( 7, 800, 6, new TextDefinition( 1060782, "Warlord" ) ), + new RankDefinition( 6, 700, 5, new TextDefinition( 1060781, "Executioner" ) ), + new RankDefinition( 5, 600, 5, new TextDefinition( 1060781, "Executioner" ) ), + new RankDefinition( 4, 500, 5, new TextDefinition( 1060781, "Executioner" ) ), + new RankDefinition( 3, 400, 4, new TextDefinition( 1060780, "Defiler" ) ), + new RankDefinition( 2, 200, 4, new TextDefinition( 1060780, "Defiler" ) ), + new RankDefinition( 1, 0, 4, new TextDefinition( 1060780, "Defiler" ) ) + }, + new GuardDefinition[] + { + new GuardDefinition( typeof( FactionHenchman ), 0x1403, 5000, 1000, 10, new TextDefinition( 1011526, "HENCHMAN" ), new TextDefinition( 1011510, "Hire Henchman" ) ), + new GuardDefinition( typeof( FactionMercenary ), 0x0F62, 6000, 2000, 10, new TextDefinition( 1011527, "MERCENARY" ), new TextDefinition( 1011511, "Hire Mercenary" ) ), + new GuardDefinition( typeof( FactionBerserker ), 0x0F4B, 7000, 3000, 10, new TextDefinition( 1011505, "BERSERKER" ), new TextDefinition( 1011499, "Hire Berserker" ) ), + new GuardDefinition( typeof( FactionDragoon ), 0x1439, 8000, 4000, 10, new TextDefinition( 1011506, "DRAGOON" ), new TextDefinition( 1011500, "Hire Dragoon" ) ), + } + ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Factions/Shadowlords.cs b/Scripts/Engines/Factions/Instances/Factions/Shadowlords.cs new file mode 100644 index 000000000..fd5bcc809 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Factions/Shadowlords.cs @@ -0,0 +1,84 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class Shadowlords : Faction + { + private static Faction m_Instance; + + public static Faction Instance{ get{ return m_Instance; } } + + public Shadowlords() + { + m_Instance = this; + + Definition = + new FactionDefinition( + 3, + 1109, // shadow + 2211, // green + 1109, // join stone : shadow + 2211, // broadcast : green + 0x79, 0x3EB0, // war horse + "Shadowlords", "shadow", + new TextDefinition( 1011537, "SHADOWLORDS" ), + new TextDefinition( 1060772, "Shadowlords faction" ), + new TextDefinition( 1011424, "
SHADES OF DARKNESS
" ), + new TextDefinition( 1011451, + "The Shadow Lords are a faction that has sprung up within the ranks of " + + "Minax. Comprised mostly of undead and those who would seek to be " + + "necromancers, they pose a threat to both the sides of good and evil. " + + "Their plans have disrupted the hold Minax has over Felucca, and their " + + "ultimate goal is to destroy all life." ), + new TextDefinition( 1011456, "This city is controlled by the Shadow Lords." ), + new TextDefinition( 1042255, "This sigil has been corrupted by the Shadowlords" ), + new TextDefinition( 1041046, "The faction signup stone for the Shadowlords" ), + new TextDefinition( 1041384, "The Faction Stone of the Shadowlords" ), + new TextDefinition( 1011466, ": Shadowlords" ), + new TextDefinition( 1005184, "Minions of the Shadowlords will now be ignored." ), + new TextDefinition( 1005185, "Minions of the Shadowlords will now be warned of their impending deaths." ), + new TextDefinition( 1005186, "Minions of the Shadowlords will now be attacked at will." ), + new StrongholdDefinition( + new Rectangle2D[] + { + new Rectangle2D( 960, 688, 8, 9 ), + new Rectangle2D( 944, 697, 24, 23 ) + }, + new Point3D( 969, 768, 0 ), + new Point3D( 947, 713, 0 ), + new Point3D[] + { + new Point3D( 953, 713, 20 ), + new Point3D( 953, 709, 20 ), + new Point3D( 953, 705, 20 ), + new Point3D( 953, 701, 20 ), + new Point3D( 957, 713, 20 ), + new Point3D( 957, 709, 20 ), + new Point3D( 957, 705, 20 ), + new Point3D( 957, 701, 20 ) + } ), + new RankDefinition[] + { + new RankDefinition( 10, 991, 8, new TextDefinition( 1060799, "Purveyor of Darkness" ) ), + new RankDefinition( 9, 950, 7, new TextDefinition( 1060798, "Agent of Evil" ) ), + new RankDefinition( 8, 900, 6, new TextDefinition( 1060797, "Bringer of Sorrow" ) ), + new RankDefinition( 7, 800, 6, new TextDefinition( 1060797, "Bringer of Sorrow" ) ), + new RankDefinition( 6, 700, 5, new TextDefinition( 1060796, "Keeper of Lies" ) ), + new RankDefinition( 5, 600, 5, new TextDefinition( 1060796, "Keeper of Lies" ) ), + new RankDefinition( 4, 500, 5, new TextDefinition( 1060796, "Keeper of Lies" ) ), + new RankDefinition( 3, 400, 4, new TextDefinition( 1060795, "Servant" ) ), + new RankDefinition( 2, 200, 4, new TextDefinition( 1060795, "Servant" ) ), + new RankDefinition( 1, 0, 4, new TextDefinition( 1060795, "Servant" ) ) + }, + new GuardDefinition[] + { + new GuardDefinition( typeof( FactionHenchman ), 0x1403, 5000, 1000, 10, new TextDefinition( 1011526, "HENCHMAN" ), new TextDefinition( 1011510, "Hire Henchman" ) ), + new GuardDefinition( typeof( FactionMercenary ), 0x0F62, 6000, 2000, 10, new TextDefinition( 1011527, "MERCENARY" ), new TextDefinition( 1011511, "Hire Mercenary" ) ), + new GuardDefinition( typeof( FactionDeathKnight ), 0x0F45, 7000, 3000, 10, new TextDefinition( 1011512, "DEATH KNIGHT" ), new TextDefinition( 1011503, "Hire Death Knight" ) ), + new GuardDefinition( typeof( FactionNecromancer ), 0x13F8, 8000, 4000, 10, new TextDefinition( 1011513, "SHADOW MAGE" ), new TextDefinition( 1011504, "Hire Shadow Mage" ) ), + } + ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Factions/TrueBritannians.cs b/Scripts/Engines/Factions/Instances/Factions/TrueBritannians.cs new file mode 100644 index 000000000..ed30a150a --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Factions/TrueBritannians.cs @@ -0,0 +1,89 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class TrueBritannians : Faction + { + private static Faction m_Instance; + + public static Faction Instance{ get{ return m_Instance; } } + + public TrueBritannians() + { + m_Instance = this; + + Definition = + new FactionDefinition( + 2, + 1254, // dark purple + 2125, // gold + 2214, // join stone : gold + 2125, // broadcast : gold + 0x76, 0x3EB2, // war horse + "True Britannians", "true", + new TextDefinition( 1011536, "LORD BRITISH" ), + new TextDefinition( 1060771, "True Britannians faction" ), + new TextDefinition( 1011423, "
TRUE BRITANNIANS
" ), + new TextDefinition( 1011450, + "True Britannians are loyal to the throne of Lord British. They refuse " + + "to give up their homelands to the vile Minax, and detest the Shadowlords " + + "for their evil ways. In addition, the Council of Mages threatens the " + + "existence of their ruler, and as such they have armed themselves, and " + + "prepare for war with all." ), + new TextDefinition( 1011454, "This city is controlled by Lord British." ), + new TextDefinition( 1042254, "This sigil has been corrupted by the True Britannians" ), + new TextDefinition( 1041045, "The faction signup stone for the True Britannians" ), + new TextDefinition( 1041383, "The Faction Stone of the True Britannians" ), + new TextDefinition( 1011465, ": True Britannians" ), + new TextDefinition( 1005181, "Followers of Lord British will now be ignored." ), + new TextDefinition( 1005182, "Followers of Lord British will now be warned of their impending doom." ), + new TextDefinition( 1005183, "Followers of Lord British will now be attacked on sight." ), + new StrongholdDefinition( + new Rectangle2D[] + { + new Rectangle2D( 1292, 1556, 25, 25 ), + new Rectangle2D( 1292, 1676, 120, 25 ), + new Rectangle2D( 1388, 1556, 25, 25 ), + new Rectangle2D( 1317, 1563, 71, 18 ), + new Rectangle2D( 1300, 1581, 105, 95 ), + new Rectangle2D( 1405, 1612, 12, 21 ), + new Rectangle2D( 1405, 1633, 11, 5 ) + }, + new Point3D( 1419, 1622, 20 ), + new Point3D( 1330, 1621, 50 ), + new Point3D[] + { + new Point3D( 1328, 1627, 50 ), + new Point3D( 1328, 1621, 50 ), + new Point3D( 1334, 1627, 50 ), + new Point3D( 1334, 1621, 50 ), + new Point3D( 1340, 1627, 50 ), + new Point3D( 1340, 1621, 50 ), + new Point3D( 1345, 1621, 50 ), + new Point3D( 1345, 1627, 50 ) + } ), + new RankDefinition[] + { + new RankDefinition( 10, 991, 8, new TextDefinition( 1060794, "Knight of the Codex" ) ), + new RankDefinition( 9, 950, 7, new TextDefinition( 1060793, "Knight of Virtue" ) ), + new RankDefinition( 8, 900, 6, new TextDefinition( 1060792, "Crusader" ) ), + new RankDefinition( 7, 800, 6, new TextDefinition( 1060792, "Crusader" ) ), + new RankDefinition( 6, 700, 5, new TextDefinition( 1060791, "Sentinel" ) ), + new RankDefinition( 5, 600, 5, new TextDefinition( 1060791, "Sentinel" ) ), + new RankDefinition( 4, 500, 5, new TextDefinition( 1060791, "Sentinel" ) ), + new RankDefinition( 3, 400, 4, new TextDefinition( 1060790, "Defender" ) ), + new RankDefinition( 2, 200, 4, new TextDefinition( 1060790, "Defender" ) ), + new RankDefinition( 1, 0, 4, new TextDefinition( 1060790, "Defender" ) ) + }, + new GuardDefinition[] + { + new GuardDefinition( typeof( FactionHenchman ), 0x1403, 5000, 1000, 10, new TextDefinition( 1011526, "HENCHMAN" ), new TextDefinition( 1011510, "Hire Henchman" ) ), + new GuardDefinition( typeof( FactionMercenary ), 0x0F62, 6000, 2000, 10, new TextDefinition( 1011527, "MERCENARY" ), new TextDefinition( 1011511, "Hire Mercenary" ) ), + new GuardDefinition( typeof( FactionKnight ), 0x0F4D, 7000, 3000, 10, new TextDefinition( 1011528, "KNIGHT" ), new TextDefinition( 1011497, "Hire Knight" ) ), + new GuardDefinition( typeof( FactionPaladin ), 0x143F, 8000, 4000, 10, new TextDefinition( 1011529, "PALADIN" ), new TextDefinition( 1011498, "Hire Paladin" ) ), + } + ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Britain.cs b/Scripts/Engines/Factions/Instances/Towns/Britain.cs new file mode 100644 index 000000000..0e84b0569 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Britain.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Britain : Town + { + public Britain() + { + Definition = + new TownDefinition( + 0, + 0x1869, + "Britain", + "Britain", + new TextDefinition( 1011433, "BRITAIN" ), + new TextDefinition( 1011561, "TOWN STONE FOR BRITAIN" ), + new TextDefinition( 1041034, "The Faction Sigil Monolith of Britain" ), + new TextDefinition( 1041404, "The Faction Town Sigil Monolith of Britain" ), + new TextDefinition( 1041413, "Faction Town Stone of Britain" ), + new TextDefinition( 1041395, "Faction Town Sigil of Britain" ), + new TextDefinition( 1041386, "Corrupted Faction Town Sigil of Britain" ), + new Point3D( 1592, 1680, 10 ), + new Point3D( 1588, 1676, 10 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Magincia.cs b/Scripts/Engines/Factions/Instances/Towns/Magincia.cs new file mode 100644 index 000000000..c19f9e1d3 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Magincia.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Magincia : Town + { + public Magincia() + { + Definition = + new TownDefinition( + 7, + 0x1870, + "Magincia", + "Magincia", + new TextDefinition( 1011440, "MAGINCIA" ), + new TextDefinition( 1011568, "TOWN STONE FOR MAGINCIA" ), + new TextDefinition( 1041041, "The Faction Sigil Monolith of Magincia" ), + new TextDefinition( 1041411, "The Faction Town Sigil Monolith of Magincia" ), + new TextDefinition( 1041420, "Faction Town Stone of Magincia" ), + new TextDefinition( 1041402, "Faction Town Sigil of Magincia" ), + new TextDefinition( 1041393, "Corrupted Faction Town Sigil of Magincia" ), + new Point3D( 3714, 2235, 20 ), + new Point3D( 3712, 2230, 20 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Minoc.cs b/Scripts/Engines/Factions/Instances/Towns/Minoc.cs new file mode 100644 index 000000000..7370e57ab --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Minoc.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Minoc : Town + { + public Minoc() + { + Definition = + new TownDefinition( + 2, + 0x186B, + "Minoc", + "Minoc", + new TextDefinition( 1011437, "MINOC" ), + new TextDefinition( 1011564, "TOWN STONE FOR MINOC" ), + new TextDefinition( 1041036, "The Faction Sigil Monolith of Minoc" ), + new TextDefinition( 1041406, "The Faction Town Sigil Monolith Minoc" ), + new TextDefinition( 1041415, "Faction Town Stone of Minoc" ), + new TextDefinition( 1041397, "Faction Town Sigil of Minoc" ), + new TextDefinition( 1041388, "Corrupted Faction Town Sigil of Minoc" ), + new Point3D( 2471, 439, 15 ), + new Point3D( 2469, 445, 15 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Moonglow.cs b/Scripts/Engines/Factions/Instances/Towns/Moonglow.cs new file mode 100644 index 000000000..083b61882 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Moonglow.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Moonglow : Town + { + public Moonglow() + { + Definition = + new TownDefinition( + 3, + 0x186C, + "Moonglow", + "Moonglow", + new TextDefinition( 1011435, "MOONGLOW" ), + new TextDefinition( 1011563, "TOWN STONE FOR MOONGLOW" ), + new TextDefinition( 1041037, "The Faction Sigil Monolith of Moonglow" ), + new TextDefinition( 1041407, "The Faction Town Sigil Monolith of Moonglow" ), + new TextDefinition( 1041416, "Faction Town Stone of Moonglow" ), + new TextDefinition( 1041398, "Faction Town Sigil of Moonglow" ), + new TextDefinition( 1041389, "Corrupted Faction Town Sigil of Moonglow" ), + new Point3D( 4436, 1083, 0 ), + new Point3D( 4432, 1086, 0 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/SkaraBrae.cs b/Scripts/Engines/Factions/Instances/Towns/SkaraBrae.cs new file mode 100644 index 000000000..0d9d2ebb5 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/SkaraBrae.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class SkaraBrae : Town + { + public SkaraBrae() + { + Definition = + new TownDefinition( + 6, + 0x186F, + "Skara Brae", + "Skara Brae", + new TextDefinition( 1011439, "SKARA BRAE" ), + new TextDefinition( 1011567, "TOWN STONE FOR SKARA BRAE" ), + new TextDefinition( 1041040, "The Faction Sigil Monolith of Skara Brae" ), + new TextDefinition( 1041410, "The Faction Town Sigil Monolith of Skara Brae" ), + new TextDefinition( 1041419, "Faction Town Stone of Skara Brae" ), + new TextDefinition( 1041401, "Faction Town Sigil of Skara Brae" ), + new TextDefinition( 1041392, "Corrupted Faction Town Sigil of Skara Brae" ), + new Point3D( 576, 2200, 0 ), + new Point3D( 572, 2196, 0 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Trinsic.cs b/Scripts/Engines/Factions/Instances/Towns/Trinsic.cs new file mode 100644 index 000000000..e46272887 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Trinsic.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Trinsic : Town + { + public Trinsic() + { + Definition = + new TownDefinition( + 1, + 0x186A, + "Trinsic", + "Trinsic", + new TextDefinition( 1011434, "TRINSIC" ), + new TextDefinition( 1011562, "TOWN STONE FOR TRINSIC" ), + new TextDefinition( 1041035, "The Faction Sigil Monolith of Trinsic" ), + new TextDefinition( 1041405, "The Faction Town Sigil Monolith of Trinsic" ), + new TextDefinition( 1041414, "Faction Town Stone of Trinsic" ), + new TextDefinition( 1041396, "Faction Town Sigil of Trinsic" ), + new TextDefinition( 1041387, "Corrupted Faction Town Sigil of Trinsic" ), + new Point3D( 1914, 2717, 20 ), + new Point3D( 1909, 2720, 20 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Vesper.cs b/Scripts/Engines/Factions/Instances/Towns/Vesper.cs new file mode 100644 index 000000000..000dd7233 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Vesper.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Vesper : Town + { + public Vesper() + { + Definition = + new TownDefinition( + 5, + 0x186E, + "Vesper", + "Vesper", + new TextDefinition( 1016413, "VESPER" ), + new TextDefinition( 1011566, "TOWN STONE FOR VESPER" ), + new TextDefinition( 1041039, "The Faction Sigil Monolith of Vesper" ), + new TextDefinition( 1041409, "The Faction Town Sigil Monolith of Vesper" ), + new TextDefinition( 1041418, "Faction Town Stone of Vesper" ), + new TextDefinition( 1041400, "Faction Town Sigil of Vesper" ), + new TextDefinition( 1041391, "Corrupted Faction Town Sigil of Vesper" ), + new Point3D( 2982, 818, 0 ), + new Point3D( 2985, 821, 0 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Instances/Towns/Yew.cs b/Scripts/Engines/Factions/Instances/Towns/Yew.cs new file mode 100644 index 000000000..25a669070 --- /dev/null +++ b/Scripts/Engines/Factions/Instances/Towns/Yew.cs @@ -0,0 +1,26 @@ +using System; + +namespace Server.Factions +{ + public class Yew : Town + { + public Yew() + { + Definition = + new TownDefinition( + 4, + 0x186D, + "Yew", + "Yew", + new TextDefinition( 1011438, "YEW" ), + new TextDefinition( 1011565, "TOWN STONE FOR YEW" ), + new TextDefinition( 1041038, "The Faction Sigil Monolith of Yew" ), + new TextDefinition( 1041408, "The Faction Town Sigil Monolith of Yew" ), + new TextDefinition( 1041417, "Faction Town Stone of Yew" ), + new TextDefinition( 1041399, "Faction Town Sigil of Yew" ), + new TextDefinition( 1041390, "Corrupted Faction Town Sigil of Yew" ), + new Point3D( 548, 979, 0 ), + new Point3D( 542, 980, 0 ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/BaseMonolith.cs b/Scripts/Engines/Factions/Items/BaseMonolith.cs new file mode 100644 index 000000000..38ced2235 --- /dev/null +++ b/Scripts/Engines/Factions/Items/BaseMonolith.cs @@ -0,0 +1,135 @@ +using System; + +namespace Server.Factions +{ + public abstract class BaseMonolith : BaseSystemController + { + private Town m_Town; + private Faction m_Faction; + private Sigil m_Sigil; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Sigil Sigil + { + get{ return m_Sigil; } + set + { + if ( m_Sigil == value ) + return; + + m_Sigil = value; + + if ( m_Sigil != null && m_Sigil.LastMonolith != null && m_Sigil.LastMonolith != this && m_Sigil.LastMonolith.Sigil == m_Sigil ) + m_Sigil.LastMonolith.Sigil = null; + + if ( m_Sigil != null ) + m_Sigil.LastMonolith = this; + + UpdateSigil(); + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Town Town + { + get{ return m_Town; } + set + { + m_Town = value; + OnTownChanged(); + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Faction Faction + { + get{ return m_Faction; } + set + { + m_Faction = value; + Hue = ( m_Faction == null ? 0 : m_Faction.Definition.HuePrimary ); + } + } + + public override void OnLocationChange( Point3D oldLocation ) + { + base.OnLocationChange( oldLocation ); + UpdateSigil(); + } + + public override void OnMapChange() + { + base.OnMapChange(); + UpdateSigil(); + } + + public virtual void UpdateSigil() + { + if ( m_Sigil == null || m_Sigil.Deleted ) + return; + + m_Sigil.MoveToWorld( new Point3D( X, Y, Z + 18 ), Map ); + } + + public virtual void OnTownChanged() + { + } + + public BaseMonolith( Town town, Faction faction ) : base( 0x1183 ) + { + Movable = false; + Town = town; + Faction = faction; + m_Monoliths.Add( this ); + } + + public BaseMonolith( Serial serial ) : base( serial ) + { + m_Monoliths.Add( this ); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + m_Monoliths.Remove( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Town.WriteReference( writer, m_Town ); + Faction.WriteReference( writer, m_Faction ); + + writer.Write( (Item) m_Sigil ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Town = Town.ReadReference( reader ); + Faction = Faction.ReadReference( reader ); + m_Sigil = reader.ReadItem() as Sigil; + break; + } + } + } + + private static MonolithCollection m_Monoliths = new MonolithCollection(); + + public static MonolithCollection Monoliths + { + get{ return m_Monoliths; } + set{ m_Monoliths = value; } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/BaseSystemController.cs b/Scripts/Engines/Factions/Items/BaseSystemController.cs new file mode 100644 index 000000000..c47e4c61f --- /dev/null +++ b/Scripts/Engines/Factions/Items/BaseSystemController.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Factions +{ + public abstract class BaseSystemController : Item + { + private int m_LabelNumber; + + public virtual int DefaultLabelNumber{ get{ return base.LabelNumber; } } + public new virtual string DefaultName{ get{ return null; } } + + public override int LabelNumber + { + get + { + if ( m_LabelNumber > 0 ) + return m_LabelNumber; + + return DefaultLabelNumber; + } + } + + public virtual void AssignName( TextDefinition name ) + { + if ( name != null && name.Number > 0 ) + { + m_LabelNumber = name.Number; + Name = null; + } + else if ( name != null && name.String != null ) + { + m_LabelNumber = 0; + Name = name.String; + } + else + { + m_LabelNumber = 0; + Name = DefaultName; + } + + InvalidateProperties(); + } + + public BaseSystemController( int itemID ) : base( itemID ) + { + } + + public BaseSystemController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/FactionStone.cs b/Scripts/Engines/Factions/Items/FactionStone.cs new file mode 100644 index 000000000..ad4fc710f --- /dev/null +++ b/Scripts/Engines/Factions/Items/FactionStone.cs @@ -0,0 +1,105 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class FactionStone : BaseSystemController + { + private Faction m_Faction; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Faction Faction + { + get{ return m_Faction; } + set + { + m_Faction = value; + + AssignName( m_Faction == null ? null : m_Faction.Definition.FactionStoneName ); + } + } + + public override string DefaultName { get { return "faction stone"; } } + + [Constructable] + public FactionStone() : this( null ) + { + } + + [Constructable] + public FactionStone( Faction faction ) : base( 0xEDC ) + { + Movable = false; + Faction = faction; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Faction == null ) + return; + + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + else if ( FactionGump.Exists( from ) ) + { + from.SendLocalizedMessage( 1042160 ); // You already have a faction menu open. + } + else if ( from is PlayerMobile ) + { + Faction existingFaction = Faction.Find( from ); + + if ( existingFaction == m_Faction || from.AccessLevel >= AccessLevel.GameMaster ) + { + PlayerState pl = PlayerState.Find( from ); + + if ( pl != null && pl.IsLeaving ) + from.SendLocalizedMessage( 1005051 ); // You cannot use the faction stone until you have finished quitting your current faction + else + from.SendGump( new FactionStoneGump( (PlayerMobile) from, m_Faction ) ); + } + else if ( existingFaction != null ) + { + // TODO: Validate + from.SendLocalizedMessage( 1005053 ); // This is not your faction stone! + } + else + { + from.SendGump( new JoinStoneGump( (PlayerMobile) from, m_Faction ) ); + } + } + } + + public FactionStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Faction = Faction.ReadReference( reader ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/JoinStone.cs b/Scripts/Engines/Factions/Items/JoinStone.cs new file mode 100644 index 000000000..e2663bc2a --- /dev/null +++ b/Scripts/Engines/Factions/Items/JoinStone.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class JoinStone : BaseSystemController + { + private Faction m_Faction; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Faction Faction + { + get{ return m_Faction; } + set + { + m_Faction = value; + + Hue = ( m_Faction == null ? 0 : m_Faction.Definition.HueJoin ); + AssignName( m_Faction == null ? null : m_Faction.Definition.SignupName ); + } + } + + public override string DefaultName { get { return "faction signup stone"; } } + + [Constructable] + public JoinStone() : this( null ) + { + } + + [Constructable] + public JoinStone( Faction faction ) : base( 0xEDC ) + { + Movable = false; + Faction = faction; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Faction == null ) + return; + + if ( !from.InRange( GetWorldLocation(), 2 ) ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else if ( FactionGump.Exists( from ) ) + from.SendLocalizedMessage( 1042160 ); // You already have a faction menu open. + else if ( Faction.Find( from ) == null && from is PlayerMobile ) + from.SendGump( new JoinStoneGump( (PlayerMobile) from, m_Faction ) ); + } + + public JoinStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Faction = Faction.ReadReference( reader ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Sigil.cs b/Scripts/Engines/Factions/Items/Sigil.cs new file mode 100644 index 000000000..c52d3a11f --- /dev/null +++ b/Scripts/Engines/Factions/Items/Sigil.cs @@ -0,0 +1,470 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class Sigil : BaseSystemController + { + public const int OwnershipHue = 0xB; + + // ?? time corrupting faction has to return the sigil before corruption time resets ? + public static readonly TimeSpan CorruptionGrace = TimeSpan.FromMinutes( (Core.SE) ? 30.0 : 15.0 ); + + // Sigil must be held at a stronghold for this amount of time in order to become corrupted + public static readonly TimeSpan CorruptionPeriod = ( (Core.SE) ? TimeSpan.FromHours( 10.0 ) : TimeSpan.FromHours( 24.0 ) ); + + // After a sigil has been corrupted it must be returned to the town within this period of time + public static readonly TimeSpan ReturnPeriod = TimeSpan.FromHours( 1.0 ); + + // Once it's been returned the corrupting faction owns the town for this period of time + public static readonly TimeSpan PurificationPeriod = TimeSpan.FromDays( 3.0 ); + + private BaseMonolith m_LastMonolith; + + private Town m_Town; + private Faction m_Corrupted; + private Faction m_Corrupting; + + private DateTime m_LastStolen; + private DateTime m_GraceStart; + private DateTime m_CorruptionStart; + private DateTime m_PurificationStart; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public DateTime LastStolen + { + get{ return m_LastStolen; } + set{ m_LastStolen = value; } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public DateTime GraceStart + { + get{ return m_GraceStart; } + set{ m_GraceStart = value; } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public DateTime CorruptionStart + { + get{ return m_CorruptionStart; } + set{ m_CorruptionStart = value; } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public DateTime PurificationStart + { + get{ return m_PurificationStart; } + set{ m_PurificationStart = value; } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Town Town + { + get{ return m_Town; } + set{ m_Town = value; Update(); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Faction Corrupted + { + get{ return m_Corrupted; } + set{ m_Corrupted = value; Update(); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Faction Corrupting + { + get{ return m_Corrupting; } + set{ m_Corrupting = value; Update(); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public BaseMonolith LastMonolith + { + get{ return m_LastMonolith; } + set{ m_LastMonolith = value; } + } + + [CommandProperty( AccessLevel.Counselor )] + public bool IsBeingCorrupted + { + get{ return ( m_LastMonolith is StrongholdMonolith && m_LastMonolith.Faction == m_Corrupting && m_Corrupting != null ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public bool IsCorrupted + { + get{ return ( m_Corrupted != null ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public bool IsPurifying + { + get{ return ( m_PurificationStart != DateTime.MinValue ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public bool IsCorrupting + { + get{ return ( m_Corrupting != null && m_Corrupting != m_Corrupted ); } + } + + public void Update() + { + ItemID = ( m_Town == null ? 0x1869 : m_Town.Definition.SigilID ); + + if ( m_Town == null ) + AssignName( null ); + else if ( IsCorrupted || IsPurifying ) + AssignName( m_Town.Definition.CorruptedSigilName ); + else + AssignName( m_Town.Definition.SigilName ); + + InvalidateProperties(); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( IsCorrupted ) + TextDefinition.AddTo( list, m_Corrupted.Definition.SigilControl ); + else + list.Add( 1042256 ); // This sigil is not corrupted. + + if ( IsCorrupting ) + list.Add( 1042257 ); // This sigil is in the process of being corrupted. + else if ( IsPurifying ) + list.Add( 1042258 ); // This sigil has recently been corrupted, and is undergoing purification. + else + list.Add( 1042259 ); // This sigil is not in the process of being corrupted. + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( IsCorrupted ) + { + if ( m_Corrupted.Definition.SigilControl.Number > 0 ) + LabelTo( from, m_Corrupted.Definition.SigilControl.Number ); + else if ( m_Corrupted.Definition.SigilControl.String != null ) + LabelTo( from, m_Corrupted.Definition.SigilControl.String ); + } + else + { + LabelTo( from, 1042256 ); // This sigil is not corrupted. + } + + if ( IsCorrupting ) + LabelTo( from, 1042257 ); // This sigil is in the process of being corrupted. + else if ( IsPurifying ) + LabelTo( from, 1042258 ); // This sigil has been recently corrupted, and is undergoing purification. + else + LabelTo( from, 1042259 ); // This sigil is not in the process of being corrupted. + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + from.SendLocalizedMessage( 1005225 ); // You must use the stealing skill to pick up the sigil + return false; + } + + private Mobile FindOwner( object parent ) + { + if ( parent is Item ) + return ((Item)parent).RootParent as Mobile; + + if ( parent is Mobile ) + return (Mobile) parent; + + return null; + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + Mobile mob = FindOwner( parent ); + + if ( mob != null ) + mob.SolidHueOverride = OwnershipHue; + } + + public override void OnRemoved( object parent ) + { + base.OnRemoved( parent ); + + Mobile mob = FindOwner( parent ); + + if ( mob != null ) + mob.SolidHueOverride = -1; + } + + public Sigil( Town town ) : base( 0x1869 ) + { + Movable = false; + Town = town; + + m_Sigils.Add( this ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.BeginTarget( 1, false, Targeting.TargetFlags.None, new TargetCallback( Sigil_OnTarget ) ); + from.SendLocalizedMessage( 1042251 ); // Click on a sigil monolith or player + } + } + + public static bool ExistsOn( Mobile mob ) + { + Container pack = mob.Backpack; + + return ( pack != null && pack.FindItemByType( typeof( Sigil ) ) != null ); + } + + private void BeginCorrupting( Faction faction ) + { + m_Corrupting = faction; + m_CorruptionStart = DateTime.Now; + } + + private void ClearCorrupting() + { + m_Corrupting = null; + m_CorruptionStart = DateTime.MinValue; + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan TimeUntilCorruption + { + get + { + if ( !IsBeingCorrupted ) + return TimeSpan.Zero; + + TimeSpan ts = ( m_CorruptionStart + CorruptionPeriod ) - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + return ts; + } + } + + private void Sigil_OnTarget( Mobile from, object obj ) + { + if ( Deleted || !IsChildOf( from.Backpack ) ) + return; + + #region Give To Mobile + if ( obj is Mobile ) + { + if ( obj is PlayerMobile ) + { + PlayerMobile targ = (PlayerMobile)obj; + + Faction toFaction = Faction.Find( targ ); + Faction fromFaction = Faction.Find( from ); + + if ( toFaction == null ) + from.SendLocalizedMessage( 1005223 ); // You cannot give the sigil to someone not in a faction + else if ( fromFaction != toFaction ) + from.SendLocalizedMessage( 1005222 ); // You cannot give the sigil to someone not in your faction + else if ( Sigil.ExistsOn( targ ) ) + from.SendLocalizedMessage( 1005220 ); // You cannot give this sigil to someone who already has a sigil + else if( !targ.Alive ) + from.SendLocalizedMessage( 1042248 ); // You cannot give a sigil to a dead person. + else if ( from.NetState != null && targ.NetState != null ) + { + Container pack = targ.Backpack; + + if ( pack != null ) + pack.DropItem( this ); + } + } + else + { + from.SendLocalizedMessage( 1005221 ); //You cannot give the sigil to them + } + } + #endregion + else if ( obj is BaseMonolith ) + { + #region Put in Stronghold + if ( obj is StrongholdMonolith ) + { + StrongholdMonolith m = (StrongholdMonolith)obj; + + if ( m.Faction == null || m.Faction != Faction.Find( from ) ) + from.SendLocalizedMessage( 1042246 ); // You can't place that on an enemy monolith + else if ( m.Town == null || m.Town != m_Town ) + from.SendLocalizedMessage( 1042247 ); // That is not the correct faction monolith + else + { + m.Sigil = this; + + Faction newController = m.Faction; + Faction oldController = m_Corrupting; + + if ( oldController == null ) + { + if ( m_Corrupted != newController ) + BeginCorrupting( newController ); + } + else if ( m_GraceStart > DateTime.MinValue && (m_GraceStart + CorruptionGrace) < DateTime.Now ) + { + if ( m_Corrupted != newController ) + BeginCorrupting( newController ); // grace time over, reset period + else + ClearCorrupting(); + + m_GraceStart = DateTime.MinValue; + } + else if ( newController == oldController ) + { + m_GraceStart = DateTime.MinValue; // returned within grace period + } + else if ( m_GraceStart == DateTime.MinValue ) + { + m_GraceStart = DateTime.Now; + } + + m_PurificationStart = DateTime.MinValue; + } + } + #endregion + + #region Put in Town + else if ( obj is TownMonolith ) + { + TownMonolith m = (TownMonolith)obj; + + if ( m.Town == null || m.Town != m_Town ) + from.SendLocalizedMessage( 1042245 ); // This is not the correct town sigil monolith + else if ( m_Corrupted == null || m_Corrupted != Faction.Find( from ) ) + from.SendLocalizedMessage( 1042244 ); // Your faction did not corrupt this sigil. Take it to your stronghold. + else + { + m.Sigil = this; + + m_Corrupting = null; + m_PurificationStart = DateTime.Now; + m_CorruptionStart = DateTime.MinValue; + + m_Town.Capture( m_Corrupted ); + m_Corrupted = null; + } + } + #endregion + } + else + { + from.SendLocalizedMessage( 1005224 ); // You can't use the sigil on that + } + + Update(); + } + + public Sigil( Serial serial ) : base( serial ) + { + m_Sigils.Add( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Town.WriteReference( writer, m_Town ); + Faction.WriteReference( writer, m_Corrupted ); + Faction.WriteReference( writer, m_Corrupting ); + + writer.Write( (Item) m_LastMonolith ); + + writer.Write( m_LastStolen ); + writer.Write( m_GraceStart ); + writer.Write( m_CorruptionStart ); + writer.Write( m_PurificationStart ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Town = Town.ReadReference( reader ); + m_Corrupted = Faction.ReadReference( reader ); + m_Corrupting = Faction.ReadReference( reader ); + + m_LastMonolith = reader.ReadItem() as BaseMonolith; + + m_LastStolen = reader.ReadDateTime(); + m_GraceStart = reader.ReadDateTime(); + m_CorruptionStart = reader.ReadDateTime(); + m_PurificationStart = reader.ReadDateTime(); + + Update(); + + Mobile mob = RootParent as Mobile; + + if ( mob != null ) + mob.SolidHueOverride = OwnershipHue; + + break; + } + } + } + + public bool ReturnHome() + { + BaseMonolith monolith = m_LastMonolith; + + if ( monolith == null && m_Town != null ) + monolith = m_Town.Monolith; + + if ( monolith != null && !monolith.Deleted ) + monolith.Sigil = this; + + return ( monolith != null && !monolith.Deleted ); + } + + public override void OnParentDeleted( object parent ) + { + base.OnParentDeleted( parent ); + + ReturnHome(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + m_Sigils.Remove( this ); + } + + public override void Delete() + { + if ( ReturnHome() ) + return; + + base.Delete(); + } + + private static SigilCollection m_Sigils = new SigilCollection(); + + public static SigilCollection Sigils{ get{ return m_Sigils; } } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Silver.cs b/Scripts/Engines/Factions/Items/Silver.cs new file mode 100644 index 000000000..8b4bf3d0f --- /dev/null +++ b/Scripts/Engines/Factions/Items/Silver.cs @@ -0,0 +1,58 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class Silver : Item + { + public override double DefaultWeight + { + get { return 0.02; } + } + + [Constructable] + public Silver() : this( 1 ) + { + } + + [Constructable] + public Silver( int amountFrom, int amountTo ) : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Silver( int amount ) : base( 0xEF0 ) + { + Stackable = true; + Amount = amount; + } + + public Silver( Serial serial ) : base( serial ) + { + } + + public override int GetDropSound() + { + if ( Amount <= 1 ) + return 0x2E4; + else if ( Amount <= 5 ) + return 0x2E5; + else + return 0x2E6; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/StrongholdMonolith.cs b/Scripts/Engines/Factions/Items/StrongholdMonolith.cs new file mode 100644 index 000000000..f65729eea --- /dev/null +++ b/Scripts/Engines/Factions/Items/StrongholdMonolith.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server.Factions +{ + public class StrongholdMonolith : BaseMonolith + { + public override int DefaultLabelNumber{ get{ return 1041042; } } // A Faction Sigil Monolith + + public override void OnTownChanged() + { + AssignName( Town == null ? null : Town.Definition.StrongholdMonolithName ); + } + + public StrongholdMonolith() : this( null, null ) + { + } + + public StrongholdMonolith( Town town, Faction faction ) : base( town, faction ) + { + } + + public StrongholdMonolith( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/TownMonolith.cs b/Scripts/Engines/Factions/Items/TownMonolith.cs new file mode 100644 index 000000000..75cd500a5 --- /dev/null +++ b/Scripts/Engines/Factions/Items/TownMonolith.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server.Factions +{ + public class TownMonolith : BaseMonolith + { + public override int DefaultLabelNumber{ get{ return 1041403; } } // A Faction Town Sigil Monolith + + public override void OnTownChanged() + { + AssignName( Town == null ? null : Town.Definition.TownMonolithName ); + } + + public TownMonolith() : this( null ) + { + } + + public TownMonolith( Town town ) : base( town, null ) + { + } + + public TownMonolith( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/TownStone.cs b/Scripts/Engines/Factions/Items/TownStone.cs new file mode 100644 index 000000000..ccf8a46b2 --- /dev/null +++ b/Scripts/Engines/Factions/Items/TownStone.cs @@ -0,0 +1,87 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class TownStone : BaseSystemController + { + private Town m_Town; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Town Town + { + get{ return m_Town; } + set + { + m_Town = value; + + AssignName( m_Town == null ? null : m_Town.Definition.TownStoneName ); + } + } + + public override string DefaultName { get { return "faction town stone"; } } + + [Constructable] + public TownStone() : this( null ) + { + } + + [Constructable] + public TownStone( Town town ) : base( 0xEDE ) + { + Movable = false; + Town = town; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Town == null ) + return; + + Faction faction = Faction.Find( from ); + + if ( faction == null && from.AccessLevel < AccessLevel.GameMaster ) + return; // TODO: Message? + + if ( m_Town.Owner == null || ( from.AccessLevel < AccessLevel.GameMaster && faction != m_Town.Owner ) ) + from.SendLocalizedMessage( 1010332 ); // Your faction does not control this town + else if ( !m_Town.Owner.IsCommander( from ) ) + from.SendLocalizedMessage( 1005242 ); // Only faction Leaders can use townstones + else if ( FactionGump.Exists( from ) ) + from.SendLocalizedMessage( 1042160 ); // You already have a faction menu open. + else if ( from is PlayerMobile ) + from.SendGump( new TownStoneGump( (PlayerMobile)from, m_Town.Owner, m_Town ) ); + } + + public TownStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Town.WriteReference( writer, m_Town ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Town = Town.ReadReference( reader ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs b/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs new file mode 100644 index 000000000..ac5b2d553 --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/BaseFactionTrap.cs @@ -0,0 +1,294 @@ +using System; +using Server; +using Server.Items; +using Server.Network; +using Server.Regions; + +namespace Server.Factions +{ + public enum AllowedPlacing + { + Everywhere, + + AnyFactionTown, + ControlledFactionTown, + FactionStronghold + } + + public abstract class BaseFactionTrap : BaseTrap + { + private Faction m_Faction; + private Mobile m_Placer; + private DateTime m_TimeOfPlacement; + + private Timer m_Concealing; + + [CommandProperty( AccessLevel.GameMaster )] + public Faction Faction + { + get{ return m_Faction; } + set{ m_Faction = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Placer + { + get{ return m_Placer; } + set{ m_Placer = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime TimeOfPlacement + { + get{ return m_TimeOfPlacement; } + set{ m_TimeOfPlacement = value; } + } + + public virtual int EffectSound{ get{ return 0; } } + + public virtual int SilverFromDisarm{ get{ return 100; } } + + public virtual int MessageHue{ get{ return 0; } } + + public virtual int AttackMessage{ get{ return 0; } } + public virtual int DisarmMessage{ get{ return 0; } } + + public virtual AllowedPlacing AllowedPlacing{ get{ return AllowedPlacing.Everywhere; } } + + public virtual TimeSpan ConcealPeriod + { + get{ return TimeSpan.FromMinutes( 1.0 ); } + } + + public virtual TimeSpan DecayPeriod + { + get + { + if ( Core.AOS ) + return TimeSpan.FromDays( 1.0 ); + + return TimeSpan.MaxValue; // no decay + } + } + + public override void OnTrigger( Mobile from ) + { + if ( !IsEnemy( from ) ) + return; + + Conceal(); + + DoVisibleEffect(); + Effects.PlaySound( this.Location, this.Map, this.EffectSound ); + DoAttackEffect( from ); + + int silverToAward = ( from.Alive ? 20 : 40 ); + + if ( silverToAward > 0 && m_Placer != null && m_Faction != null ) + { + PlayerState victimState = PlayerState.Find( from ); + + if ( victimState != null && victimState.CanGiveSilverTo( m_Placer ) && victimState.KillPoints > 0 ) + { + int silverGiven = m_Faction.AwardSilver( m_Placer, silverToAward ); + + if ( silverGiven > 0 ) + { + // TODO: Get real message + if ( from.Alive ) + m_Placer.SendMessage( "You have earned {0} silver pieces because {1} fell for your trap.", silverGiven, from.Name ); + else + m_Placer.SendLocalizedMessage( 1042736, String.Format( "{0} silver\t{1}", silverGiven, from.Name ) ); // You have earned ~1_SILVER_AMOUNT~ pieces for vanquishing ~2_PLAYER_NAME~! + } + + victimState.OnGivenSilverTo( m_Placer ); + } + } + + from.LocalOverheadMessage( MessageType.Regular, MessageHue, AttackMessage ); + } + + public abstract void DoVisibleEffect(); + public abstract void DoAttackEffect( Mobile m ); + + public virtual int IsValidLocation() + { + return IsValidLocation( GetWorldLocation(), Map ); + } + + public virtual int IsValidLocation( Point3D p, Map m ) + { + if( m == null ) + return 502956; // You cannot place a trap on that. + + if( Core.ML ) + { + foreach( Item item in m.GetItemsInRange( p, 0 ) ) + { + if( item is BaseFactionTrap && ((BaseFactionTrap)item).Faction == this.Faction ) + return 1075263; // There is already a trap belonging to your faction at this location.; + } + } + + switch( AllowedPlacing ) + { + case AllowedPlacing.FactionStronghold: + { + StrongholdRegion region = (StrongholdRegion) Region.Find( p, m ).GetRegion( typeof( StrongholdRegion ) ); + + if ( region != null && region.Faction == m_Faction ) + return 0; + + return 1010355; // This trap can only be placed in your stronghold + } + case AllowedPlacing.AnyFactionTown: + { + Town town = Town.FromRegion( Region.Find( p, m ) ); + + if ( town != null ) + return 0; + + return 1010356; // This trap can only be placed in a faction town + } + case AllowedPlacing.ControlledFactionTown: + { + Town town = Town.FromRegion( Region.Find( p, m ) ); + + if ( town != null && town.Owner == m_Faction ) + return 0; + + return 1010357; // This trap can only be placed in a town your faction controls + } + } + + return 0; + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( !CheckDecay() && CheckRange( m.Location, oldLocation, 6 ) ) + { + if ( Faction.Find( m ) != null && ((m.Skills[SkillName.DetectHidden].Value - 80.0) / 20.0) > Utility.RandomDouble() ) + PrivateOverheadLocalizedMessage( m, 1010154, MessageHue, "", "" ); // [Faction Trap] + } + } + + public void PrivateOverheadLocalizedMessage( Mobile to, int number, int hue, string name, string args ) + { + if ( to == null ) + return; + + NetState ns = to.NetState; + + if ( ns != null ) + ns.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, hue, 3, number, name, args ) ); + } + + public BaseFactionTrap( Faction f, Mobile m, int itemID ) : base( itemID ) + { + Visible = false; + + m_Faction = f; + m_TimeOfPlacement = DateTime.Now; + m_Placer = m; + } + + public BaseFactionTrap( Serial serial ) : base( serial ) + { + } + + public virtual bool CheckDecay() + { + TimeSpan decayPeriod = DecayPeriod; + + if ( decayPeriod == TimeSpan.MaxValue ) + return false; + + if ( (m_TimeOfPlacement + decayPeriod) < DateTime.Now ) + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Delete ) ); + return true; + } + + return false; + } + + public virtual void BeginConceal() + { + if ( m_Concealing != null ) + m_Concealing.Stop(); + + m_Concealing = Timer.DelayCall( ConcealPeriod, new TimerCallback( Conceal ) ); + } + + public virtual void Conceal() + { + if ( m_Concealing != null ) + m_Concealing.Stop(); + + m_Concealing = null; + + if ( !Deleted ) + Visible = false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + writer.Write( (Mobile) m_Placer ); + writer.Write( (DateTime) m_TimeOfPlacement ); + + if ( Visible ) + BeginConceal(); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Faction = Faction.ReadReference( reader ); + m_Placer = reader.ReadMobile(); + m_TimeOfPlacement = reader.ReadDateTime(); + + if ( Visible ) + BeginConceal(); + + CheckDecay(); + } + + public override void OnDelete() + { + if ( m_Faction != null && m_Faction.Traps.Contains( this ) ) + m_Faction.Traps.Remove( this ); + + base.OnDelete(); + } + + public virtual bool IsEnemy( Mobile mob ) + { + if ( mob.Hidden && mob.AccessLevel > AccessLevel.Player ) + return false; + + if ( !mob.Alive ) + return false; + + Faction faction = Faction.Find( mob, true ); + + if ( faction == null && mob is BaseFactionGuard ) + faction = ((BaseFactionGuard)mob).Faction; + + if ( faction == null ) + return false; + + return ( faction != m_Faction ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs b/Scripts/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs new file mode 100644 index 000000000..98f2768b2 --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs @@ -0,0 +1,112 @@ +using System; +using Server.Mobiles; +using Server.Targeting; +using Server.Items; +using Server; +using Server.Engines.Craft; + +namespace Server.Factions +{ + public abstract class BaseFactionTrapDeed : Item, ICraftable + { + public abstract Type TrapType{ get; } + + private Faction m_Faction; + + [CommandProperty( AccessLevel.GameMaster )] + public Faction Faction + { + get{ return m_Faction; } + set + { + m_Faction = value; + + if ( m_Faction != null ) + Hue = m_Faction.Definition.HuePrimary; + } + } + + public BaseFactionTrapDeed( int itemID ) : base( itemID ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public BaseFactionTrapDeed( bool createdFromDeed ) : this( 0x14F0 ) + { + } + + public BaseFactionTrapDeed( Serial serial ) : base( serial ) + { + } + + public virtual BaseFactionTrap Construct( Mobile from ) + { + try{ return Activator.CreateInstance( TrapType, new object[]{ m_Faction, from } ) as BaseFactionTrap; } + catch{ return null; } + } + + public override void OnDoubleClick( Mobile from ) + { + Faction faction = Faction.Find( from ); + + if ( faction == null ) + from.SendLocalizedMessage( 1010353, "", 0x23 ); // Only faction members may place faction traps + else if ( faction != m_Faction ) + from.SendLocalizedMessage( 1010354, "", 0x23 ); // You may only place faction traps created by your faction + else if( faction.Traps.Count >= faction.MaximumTraps ) + from.SendLocalizedMessage( 1010358, "", 0x23 ); // Your faction already has the maximum number of traps placed + else + { + BaseFactionTrap trap = Construct( from ); + + if ( trap == null ) + return; + + int message = trap.IsValidLocation( from.Location, from.Map ); + + if ( message > 0 ) + { + from.SendLocalizedMessage( message, "", 0x23 ); + trap.Delete(); + } + else + { + from.SendLocalizedMessage( 1010360 ); // You arm the trap and carefully hide it from view + trap.MoveToWorld( from.Location, from.Map ); + faction.Traps.Add( trap ); + Delete(); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Faction = Faction.ReadReference( reader ); + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + ItemID = 0x14F0; + Faction = Faction.Find( from ); + + return 1; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/FactionExplosionTrap.cs b/Scripts/Engines/Factions/Items/Traps/FactionExplosionTrap.cs new file mode 100644 index 000000000..455b6ae1e --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/FactionExplosionTrap.cs @@ -0,0 +1,85 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class FactionExplosionTrap : BaseFactionTrap + { + public override int LabelNumber{ get{ return 1044599; } } // faction explosion trap + + public override int AttackMessage{ get{ return 1010543; } } // You are enveloped in an explosion of fire! + public override int DisarmMessage{ get{ return 1010539; } } // You carefully remove the pressure trigger and disable the trap. + public override int EffectSound{ get{ return 0x307; } } + public override int MessageHue{ get{ return 0x78; } } + + public override AllowedPlacing AllowedPlacing{ get{ return AllowedPlacing.AnyFactionTown; } } + + public override void DoVisibleEffect() + { + Effects.SendLocationEffect( GetWorldLocation(), Map, 0x36BD, 15, 10 ); + } + + public override void DoAttackEffect( Mobile m ) + { + m.Damage( Utility.Dice( 6, 10, 40 ), m ); + } + + [Constructable] + public FactionExplosionTrap() : this( null ) + { + } + + public FactionExplosionTrap( Faction f ) : this( f, null ) + { + } + + public FactionExplosionTrap( Faction f, Mobile m ) : base( f, m, 0x11C1 ) + { + } + + public FactionExplosionTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FactionExplosionTrapDeed : BaseFactionTrapDeed + { + public override Type TrapType{ get{ return typeof( FactionExplosionTrap ); } } + public override int LabelNumber{ get{ return 1044603; } } // faction explosion trap deed + + public FactionExplosionTrapDeed() : base( 0x36D2 ) + { + } + + public FactionExplosionTrapDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/FactionGasTrap.cs b/Scripts/Engines/Factions/Items/Traps/FactionGasTrap.cs new file mode 100644 index 000000000..9a9857af8 --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/FactionGasTrap.cs @@ -0,0 +1,85 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class FactionGasTrap : BaseFactionTrap + { + public override int LabelNumber{ get{ return 1044598; } } // faction gas trap + + public override int AttackMessage{ get{ return 1010542; } } // A noxious green cloud of poison gas envelops you! + public override int DisarmMessage{ get{ return 502376; } } // The poison leaks harmlessly away due to your deft touch. + public override int EffectSound{ get{ return 0x230; } } + public override int MessageHue{ get{ return 0x44; } } + + public override AllowedPlacing AllowedPlacing{ get{ return AllowedPlacing.FactionStronghold; } } + + public override void DoVisibleEffect() + { + Effects.SendLocationEffect( this.Location, this.Map, 0x3709, 28, 10, 0x1D3, 5 ); + } + + public override void DoAttackEffect( Mobile m ) + { + m.ApplyPoison( m, Poison.Lethal ); + } + + [Constructable] + public FactionGasTrap() : this( null ) + { + } + + public FactionGasTrap( Faction f ) : this( f, null ) + { + } + + public FactionGasTrap( Faction f, Mobile m ) : base( f, m, 0x113C ) + { + } + + public FactionGasTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FactionGasTrapDeed : BaseFactionTrapDeed + { + public override Type TrapType{ get{ return typeof( FactionGasTrap ); } } + public override int LabelNumber{ get{ return 1044602; } } // faction gas trap deed + + public FactionGasTrapDeed() : base( 0x11AB ) + { + } + + public FactionGasTrapDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/FactionSawTrap.cs b/Scripts/Engines/Factions/Items/Traps/FactionSawTrap.cs new file mode 100644 index 000000000..e33eb538b --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/FactionSawTrap.cs @@ -0,0 +1,85 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class FactionSawTrap : BaseFactionTrap + { + public override int LabelNumber{ get{ return 1041047; } } // faction saw trap + + public override int AttackMessage{ get{ return 1010544; } } // The blade cuts deep into your skin! + public override int DisarmMessage{ get{ return 1010540; } } // You carefully dismantle the saw mechanism and disable the trap. + public override int EffectSound{ get{ return 0x218; } } + public override int MessageHue{ get{ return 0x5A; } } + + public override AllowedPlacing AllowedPlacing{ get{ return AllowedPlacing.ControlledFactionTown; } } + + public override void DoVisibleEffect() + { + Effects.SendLocationEffect( this.Location, this.Map, 0x11AD, 25, 10 ); + } + + public override void DoAttackEffect( Mobile m ) + { + m.Damage( Utility.Dice( 6, 10, 40 ), m ); + } + + [Constructable] + public FactionSawTrap() : this( null ) + { + } + + public FactionSawTrap( Serial serial ) : base( serial ) + { + } + + public FactionSawTrap( Faction f ) : this( f, null ) + { + } + + public FactionSawTrap( Faction f, Mobile m ) : base( f, m, 0x11AC ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FactionSawTrapDeed : BaseFactionTrapDeed + { + public override Type TrapType{ get{ return typeof( FactionSawTrap ); } } + public override int LabelNumber{ get{ return 1044604; } } // faction saw trap deed + + public FactionSawTrapDeed() : base( 0x1107 ) + { + } + + public FactionSawTrapDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/FactionSpikeTrap.cs b/Scripts/Engines/Factions/Items/Traps/FactionSpikeTrap.cs new file mode 100644 index 000000000..aed401aaa --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/FactionSpikeTrap.cs @@ -0,0 +1,85 @@ +using System; +using Server; + +namespace Server.Factions +{ + public class FactionSpikeTrap : BaseFactionTrap + { + public override int LabelNumber{ get{ return 1044601; } } // faction spike trap + + public override int AttackMessage{ get{ return 1010545; } } // Large spikes in the ground spring up piercing your skin! + public override int DisarmMessage{ get{ return 1010541; } } // You carefully dismantle the trigger on the spikes and disable the trap. + public override int EffectSound{ get{ return 0x22E; } } + public override int MessageHue{ get{ return 0x5A; } } + + public override AllowedPlacing AllowedPlacing{ get{ return AllowedPlacing.ControlledFactionTown; } } + + public override void DoVisibleEffect() + { + Effects.SendLocationEffect( this.Location, this.Map, 0x11A4, 12, 6 ); + } + + public override void DoAttackEffect( Mobile m ) + { + m.Damage( Utility.Dice( 6, 10, 40 ), m ); + } + + [Constructable] + public FactionSpikeTrap() : this( null ) + { + } + + public FactionSpikeTrap( Faction f ) : this( f, null ) + { + } + + public FactionSpikeTrap( Faction f, Mobile m ) : base( f, m, 0x11A0 ) + { + } + + public FactionSpikeTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FactionSpikeTrapDeed : BaseFactionTrapDeed + { + public override Type TrapType{ get{ return typeof( FactionSpikeTrap ); } } + public override int LabelNumber{ get{ return 1044605; } } // faction spike trap deed + + public FactionSpikeTrapDeed() : base( 0x11A5 ) + { + } + + public FactionSpikeTrapDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs b/Scripts/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs new file mode 100644 index 000000000..ec53752d2 --- /dev/null +++ b/Scripts/Engines/Factions/Items/Traps/FactionTrapRemovalKit.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionTrapRemovalKit : Item + { + private int m_Charges; + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set{ m_Charges = value; } + } + + public override int LabelNumber{ get{ return 1041508; } } // a faction trap removal kit + + [Constructable] + public FactionTrapRemovalKit() : base( 7867 ) + { + LootType = LootType.Blessed; + m_Charges = 25; + } + + public void ConsumeCharge( Mobile consumer ) + { + --m_Charges; + + if ( m_Charges <= 0 ) + { + Delete(); + + if ( consumer != null ) + consumer.SendLocalizedMessage( 1042531 ); // You have used all of the parts in your trap removal kit. + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + // NOTE: OSI does not list uses remaining; intentional difference + list.Add( 1060584, m_Charges.ToString() ); // uses remaining: ~1_val~ + } + + public FactionTrapRemovalKit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.WriteEncodedInt( (int) m_Charges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Charges = reader.ReadEncodedInt(); + break; + } + case 0: + { + m_Charges = 25; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/FactionWarHorse.cs b/Scripts/Engines/Factions/Mobiles/FactionWarHorse.cs new file mode 100644 index 000000000..a787d53a2 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/FactionWarHorse.cs @@ -0,0 +1,112 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Factions +{ + [CorpseName( "a war horse corpse" )] + public class FactionWarHorse : BaseMount + { + private Faction m_Faction; + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public Faction Faction + { + get{ return m_Faction; } + set + { + m_Faction = value; + + Body = ( m_Faction == null ? 0xE2 : m_Faction.Definition.WarHorseBody ); + ItemID = ( m_Faction == null ? 0x3EA0 : m_Faction.Definition.WarHorseItem ); + } + } + + public const int SilverPrice = 500; + public const int GoldPrice = 3000; + + [Constructable] + public FactionWarHorse() : this( null ) + { + } + + public FactionWarHorse( Faction faction ) : base( "a war horse", 0xE2, 0x3EA0, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0xA8; + + SetStr( 400 ); + SetDex( 125 ); + SetInt( 51, 55 ); + + SetHits( 240 ); + SetMana( 0 ); + + SetDamage( 5, 8 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 300; + Karma = 300; + + Tamable = true; + ControlSlots = 1; + + Faction = faction; + } + + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public FactionWarHorse( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerState pl = PlayerState.Find( from ); + + if ( pl == null ) + from.SendLocalizedMessage( 1010366 ); // You cannot mount a faction war horse! + else if ( pl.Faction != this.Faction ) + from.SendLocalizedMessage( 1010367 ); // You cannot ride an opposing faction's war horse! + else if ( pl.Rank.Rank < 2 ) + from.SendLocalizedMessage( 1010368 ); // You must achieve a faction rank of at least two before riding a war horse! + else + base.OnDoubleClick( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Faction = Faction.ReadReference( reader ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs b/Scripts/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs new file mode 100644 index 000000000..398ad76ca --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/BaseFactionGuard.cs @@ -0,0 +1,515 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Factions.AI; + +namespace Server.Factions +{ + public abstract class BaseFactionGuard : BaseCreature + { + private Faction m_Faction; + private Town m_Town; + private Orders m_Orders; + + public override bool BardImmune{ get{ return true; } } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public Faction Faction + { + get{ return m_Faction; } + set{ Unregister(); m_Faction = value; Register(); } + } + + public Orders Orders + { + get{ return m_Orders; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public Town Town + { + get{ return m_Town; } + set{ Unregister(); m_Town = value; Register(); } + } + + public void Register() + { + if ( m_Town != null && m_Faction != null ) + m_Town.RegisterGuard( this ); + } + + public void Unregister() + { + if ( m_Town != null ) + m_Town.UnregisterGuard( this ); + } + + public abstract GuardAI GuardAI{ get; } + + protected override BaseAI ForcedAI + { + get { return new FactionGuardAI( this ); } + } + + public override TimeSpan ReacquireDelay{ get{ return TimeSpan.FromSeconds( 2.0 ); } } + + public override bool IsEnemy( Mobile m ) + { + Faction ourFaction = m_Faction; + Faction theirFaction = Faction.Find( m ); + + if ( theirFaction == null && m is BaseFactionGuard ) + theirFaction = ((BaseFactionGuard)m).Faction; + + if ( ourFaction != null && theirFaction != null && ourFaction != theirFaction ) + { + ReactionType reactionType = Orders.GetReaction( theirFaction ).Type; + + if ( reactionType == ReactionType.Attack ) + return true; + + if ( theirFaction != null ) + { + List list = m.Aggressed; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo ai = list[i]; + + if ( ai.Defender is BaseFactionGuard ) + { + BaseFactionGuard bf = (BaseFactionGuard)ai.Defender; + + if ( bf.Faction == ourFaction ) + return true; + } + } + } + } + + return false; + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m.Player && m.Alive && InRange( m, 10 ) && !InRange( oldLocation, 10 ) && InLOS( m ) && m_Orders.GetReaction( Faction.Find( m ) ).Type == ReactionType.Warn ) + { + Direction = GetDirectionTo( m ); + + string warning = null; + + switch ( Utility.Random( 6 ) ) + { + case 0: warning = "I warn you, {0}, you would do well to leave this area before someone shows you the world of gray."; break; + case 1: warning = "It would be wise to leave this area, {0}, lest your head become my commanders' trophy."; break; + case 2: warning = "You are bold, {0}, for one of the meager {1}. Leave now, lest you be taught the taste of dirt."; break; + case 3: warning = "Your presence here is an insult, {0}. Be gone now, knave."; break; + case 4: warning = "Dost thou wish to be hung by your toes, {0}? Nay? Then come no closer."; break; + case 5: warning = "Hey, {0}. Yeah, you. Get out of here before I beat you with a stick."; break; + } + + Faction faction = Faction.Find( m ); + + Say( warning, m.Name, faction == null ? "civilians" : faction.Definition.FriendlyName ); + } + } + + private const int ListenRange = 12; + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( InRange( from, ListenRange ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + private DateTime m_OrdersEnd; + + private void ChangeReaction( Faction faction, ReactionType type ) + { + if ( faction == null ) + { + switch ( type ) + { + case ReactionType.Ignore: Say( 1005179 ); break; // Civilians will now be ignored. + case ReactionType.Warn: Say( 1005180 ); break; // Civilians will now be warned of their impending deaths. + case ReactionType.Attack: return; + } + } + else + { + TextDefinition def = null; + + switch ( type ) + { + case ReactionType.Ignore: def = faction.Definition.GuardIgnore; break; + case ReactionType.Warn: def = faction.Definition.GuardWarn; break; + case ReactionType.Attack: def = faction.Definition.GuardAttack; break; + } + + if ( def != null && def.Number > 0 ) + Say( def.Number ); + else if ( def != null && def.String != null ) + Say( def.String ); + } + + m_Orders.SetReaction( faction, type ); + } + + private bool WasNamed( string speech ) + { + string name = this.Name; + + return ( name != null && Insensitive.StartsWith( speech, name ) ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + base.OnSpeech( e ); + + Mobile from = e.Mobile; + + if ( !e.Handled && InRange( from, ListenRange ) && from.Alive ) + { + if ( e.HasKeyword( 0xE6 ) && (Insensitive.Equals( e.Speech, "orders" ) || WasNamed( e.Speech )) ) // *orders* + { + if ( m_Town == null || !m_Town.IsSheriff( from ) ) + { + this.Say( 1042189 ); // I don't work for you! + } + else if ( Town.FromRegion( this.Region ) == m_Town ) + { + this.Say( 1042180 ); // Your orders, sire? + m_OrdersEnd = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + } + } + else if ( DateTime.Now < m_OrdersEnd ) + { + if ( m_Town != null && m_Town.IsSheriff( from ) && Town.FromRegion( this.Region ) == m_Town ) + { + m_OrdersEnd = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + + bool understood = true; + ReactionType newType = 0; + + if ( Insensitive.Contains( e.Speech, "attack" ) ) + newType = ReactionType.Attack; + else if ( Insensitive.Contains( e.Speech, "warn" ) ) + newType = ReactionType.Warn; + else if ( Insensitive.Contains( e.Speech, "ignore" ) ) + newType = ReactionType.Ignore; + else + understood = false; + + if ( understood ) + { + understood = false; + + if ( Insensitive.Contains( e.Speech, "civil" ) ) + { + ChangeReaction( null, newType ); + understood = true; + } + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction faction = factions[i]; + + if ( faction != m_Faction && Insensitive.Contains( e.Speech, faction.Definition.Keyword ) ) + { + ChangeReaction( faction, newType ); + understood = true; + } + } + } + else if ( Insensitive.Contains( e.Speech, "patrol" ) ) + { + Home = Location; + RangeHome = 6; + Combatant = null; + m_Orders.Movement = MovementType.Patrol; + Say( 1005146 ); // This spot looks like it needs protection! I shall guard it with my life. + understood = true; + } + else if ( Insensitive.Contains( e.Speech, "follow" ) ) + { + Home = Location; + RangeHome = 6; + Combatant = null; + m_Orders.Follow = from; + m_Orders.Movement = MovementType.Follow; + Say( 1005144 ); // Yes, Sire. + understood = true; + } + + if ( !understood ) + Say( 1042183 ); // I'm sorry, I don't understand your orders... + } + } + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Faction != null && Map == Faction.Facet ) + list.Add( 1060846, m_Faction.Definition.PropName ); // Guard: ~1_val~ + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Faction != null && Map == Faction.Facet ) + { + string text = String.Concat( "(Guard, ", m_Faction.Definition.FriendlyName, ")" ); + + int hue = ( Faction.Find( from ) == m_Faction ? 98 : 38 ); + + PrivateOverheadMessage( MessageType.Label, hue, true, text, from.NetState ); + } + + base.OnSingleClick( from ); + } + + public virtual void GenerateRandomHair() + { + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, HairHue ); + } + + private static Type[] m_StrongPotions = new Type[] + { + typeof( GreaterHealPotion ), typeof( GreaterHealPotion ), typeof( GreaterHealPotion ), + typeof( GreaterCurePotion ), typeof( GreaterCurePotion ), typeof( GreaterCurePotion ), + typeof( GreaterStrengthPotion ), typeof( GreaterStrengthPotion ), + typeof( GreaterAgilityPotion ), typeof( GreaterAgilityPotion ), + typeof( TotalRefreshPotion ), typeof( TotalRefreshPotion ), + typeof( GreaterExplosionPotion ) + }; + + private static Type[] m_WeakPotions = new Type[] + { + typeof( HealPotion ), typeof( HealPotion ), typeof( HealPotion ), + typeof( CurePotion ), typeof( CurePotion ), typeof( CurePotion ), + typeof( StrengthPotion ), typeof( StrengthPotion ), + typeof( AgilityPotion ), typeof( AgilityPotion ), + typeof( RefreshPotion ), typeof( RefreshPotion ), + typeof( ExplosionPotion ) + }; + + public void PackStrongPotions( int min, int max ) + { + PackStrongPotions( Utility.RandomMinMax( min, max ) ); + } + + public void PackStrongPotions( int count ) + { + for ( int i = 0; i < count; ++i ) + PackStrongPotion(); + } + + public void PackStrongPotion() + { + PackItem( Loot.Construct( m_StrongPotions ) ); + } + + public void PackWeakPotions( int min, int max ) + { + PackWeakPotions( Utility.RandomMinMax( min, max ) ); + } + + public void PackWeakPotions( int count ) + { + for ( int i = 0; i < count; ++i ) + PackWeakPotion(); + } + + public void PackWeakPotion() + { + PackItem( Loot.Construct( m_WeakPotions ) ); + } + + public Item Immovable( Item item ) + { + item.Movable = false; + return item; + } + + public Item Newbied( Item item ) + { + item.LootType = LootType.Newbied; + return item; + } + + public Item Rehued( Item item, int hue ) + { + item.Hue = hue; + return item; + } + + public Item Layered( Item item, Layer layer ) + { + item.Layer = layer; + return item; + } + + public Item Resourced( BaseWeapon weapon, CraftResource resource ) + { + weapon.Resource = resource; + return weapon; + } + + public Item Resourced( BaseArmor armor, CraftResource resource ) + { + armor.Resource = resource; + return armor; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + Unregister(); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + c.Delete(); + } + + public virtual void GenerateBody( bool isFemale, bool randomHair ) + { + Hue = Utility.RandomSkinHue(); + + if ( isFemale ) + { + Female = true; + Body = 401; + Name = NameList.RandomName( "female" ); + } + else + { + Female = false; + Body = 400; + Name = NameList.RandomName( "male" ); + } + + if ( randomHair ) + GenerateRandomHair(); + } + + public override bool ClickTitle{ get{ return false; } } + + public BaseFactionGuard( string title ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + m_Orders = new Orders( this ); + Title = title; + + RangeHome = 6; + } + + public BaseFactionGuard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + Town.WriteReference( writer, m_Town ); + + m_Orders.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Faction = Faction.ReadReference( reader ); + m_Town = Town.ReadReference( reader ); + m_Orders = new Orders( this, reader ); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Register ) ); + } + } + + public class VirtualMount : IMount + { + private VirtualMountItem m_Item; + + public Mobile Rider + { + get{ return m_Item.Rider; } + set{} + } + + public VirtualMount( VirtualMountItem item ) + { + m_Item = item; + } + + public virtual void OnRiderDamaged( int amount, Mobile from, bool willKill ) + { + } + } + + public class VirtualMountItem : Item, IMountItem + { + private Mobile m_Rider; + private VirtualMount m_Mount; + + public Mobile Rider{ get{ return m_Rider; } } + + public VirtualMountItem( Mobile mob ) : base( 0x3EA0 ) + { + Layer = Layer.Mount; + + m_Rider = mob; + m_Mount = new VirtualMount( this ); + } + + public IMount Mount + { + get{ return m_Mount; } + } + + public VirtualMountItem( Serial serial ) : base( serial ) + { + m_Mount = new VirtualMount( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Rider ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Rider = reader.ReadMobile(); + + if ( m_Rider == null ) + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Scripts/Engines/Factions/Mobiles/Guards/GuardAI.cs new file mode 100644 index 000000000..72765ebee --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -0,0 +1,779 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; +using Server.Factions.AI; +using Server.Spells; +using Server.Spells.First; +using Server.Spells.Second; +using Server.Spells.Third; +using Server.Spells.Fourth; +using Server.Spells.Fifth; +using Server.Spells.Sixth; +using Server.Spells.Seventh; + +namespace Server.Factions +{ + public enum GuardAI + { + Bless = 0x01, // heal, cure, +stats + Curse = 0x02, // poison, -stats + Melee = 0x04, // weapons + Magic = 0x08, // damage spells + Smart = 0x10 // smart weapons/damage spells + } + + public class ComboEntry + { + private Type m_Spell; + private TimeSpan m_Hold; + private int m_Chance; + + public Type Spell{ get{ return m_Spell; } } + public TimeSpan Hold{ get{ return m_Hold; } } + public int Chance{ get{ return m_Chance; } } + + public ComboEntry( Type spell ) : this( spell, 100, TimeSpan.Zero ) + { + } + + public ComboEntry( Type spell, int chance ) : this( spell, chance, TimeSpan.Zero ) + { + } + + public ComboEntry( Type spell, int chance, TimeSpan hold ) + { + m_Spell = spell; + m_Chance = chance; + m_Hold = hold; + } + } + + public class SpellCombo + { + private int m_Mana; + private ComboEntry[] m_Entries; + + public int Mana{ get{ return m_Mana; } } + public ComboEntry[] Entries{ get{ return m_Entries; } } + + public SpellCombo( int mana, params ComboEntry[] entries ) + { + m_Mana = mana; + m_Entries = entries; + } + + public static readonly SpellCombo Simple = new SpellCombo( 50, + new ComboEntry( typeof( ParalyzeSpell ), 20 ), + new ComboEntry( typeof( ExplosionSpell ), 100, TimeSpan.FromSeconds( 2.8 ) ), + new ComboEntry( typeof( PoisonSpell ), 30 ), + new ComboEntry( typeof( EnergyBoltSpell ) ) + ); + + public static readonly SpellCombo Strong = new SpellCombo( 90, + new ComboEntry( typeof( ParalyzeSpell ), 20 ), + new ComboEntry( typeof( ExplosionSpell ), 50, TimeSpan.FromSeconds( 2.8 ) ), + new ComboEntry( typeof( PoisonSpell ), 30 ), + new ComboEntry( typeof( ExplosionSpell ), 100, TimeSpan.FromSeconds( 2.8 ) ), + new ComboEntry( typeof( EnergyBoltSpell ) ), + new ComboEntry( typeof( PoisonSpell ), 30 ), + new ComboEntry( typeof( EnergyBoltSpell ) ) + ); + + public static Spell Process( Mobile mob, Mobile targ, ref SpellCombo combo, ref int index, ref DateTime releaseTime ) + { + while ( ++index < combo.m_Entries.Length ) + { + ComboEntry entry = combo.m_Entries[index]; + + if ( entry.Spell == typeof( PoisonSpell ) && targ.Poisoned ) + continue; + + if ( entry.Chance > Utility.Random( 100 ) ) + { + releaseTime = DateTime.Now + entry.Hold; + return (Spell) Activator.CreateInstance( entry.Spell, new object[]{ mob, null } ); + } + } + + combo = null; + index = -1; + return null; + } + } + + public class FactionGuardAI : BaseAI + { + private BaseFactionGuard m_Guard; + + private BandageContext m_Bandage; + private DateTime m_BandageStart; + + private SpellCombo m_Combo; + private int m_ComboIndex = -1; + private DateTime m_ReleaseTarget; + + private const int ManaReserve = 30; + + public bool IsAllowed( GuardAI flag ) + { + return ( ( m_Guard.GuardAI & flag ) == flag ); + } + + public bool IsDamaged + { + get{ return ( m_Guard.Hits < m_Guard.HitsMax ); } + } + + public bool IsPoisoned + { + get{ return m_Guard.Poisoned; } + } + + public TimeSpan TimeUntilBandage + { + get + { + if ( m_Bandage != null && m_Bandage.Timer == null ) + m_Bandage = null; + + if ( m_Bandage == null ) + return TimeSpan.MaxValue; + + TimeSpan ts = ( m_BandageStart + m_Bandage.Timer.Delay ) - DateTime.Now; + + if ( ts < TimeSpan.FromSeconds( -1.0 ) ) + { + m_Bandage = null; + return TimeSpan.MaxValue; + } + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + return ts; + } + } + + public bool DequipWeapon() + { + Container pack = m_Guard.Backpack; + + if ( pack == null ) + return false; + + Item weapon = m_Guard.Weapon as Item; + + if ( weapon != null && weapon.Parent == m_Guard && !(weapon is Fists) ) + { + pack.DropItem( weapon ); + return true; + } + + return false; + } + + public bool EquipWeapon() + { + Container pack = m_Guard.Backpack; + + if ( pack == null ) + return false; + + Item weapon = pack.FindItemByType( typeof( BaseWeapon ) ); + + if ( weapon == null ) + return false; + + return m_Guard.EquipItem( weapon ); + } + + public bool StartBandage() + { + m_Bandage = null; + + Container pack = m_Guard.Backpack; + + if ( pack == null ) + return false; + + Item bandage = pack.FindItemByType( typeof( Bandage ) ); + + if ( bandage == null ) + return false; + + m_Bandage = BandageContext.BeginHeal( m_Guard, m_Guard ); + m_BandageStart = DateTime.Now; + return ( m_Bandage != null ); + } + + public bool UseItemByType( Type type ) + { + Container pack = m_Guard.Backpack; + + if ( pack == null ) + return false; + + Item item = pack.FindItemByType( type ); + + if ( item == null ) + return false; + + bool requip = DequipWeapon(); + + item.OnDoubleClick( m_Guard ); + + if ( requip ) + EquipWeapon(); + + return true; + } + + public int GetStatMod( Mobile mob, StatType type ) + { + StatMod mod = mob.GetStatMod( String.Format( "[Magic] {0} Offset", type ) ); + + if ( mod == null ) + return 0; + + return mod.Offset; + } + + public Spell RandomOffenseSpell() + { + int maxCircle = (int)((m_Guard.Skills.Magery.Value + 20.0) / (100.0 / 7.0)); + + if ( maxCircle < 1 ) + maxCircle = 1; + + switch ( Utility.Random( maxCircle*2 ) ) + { + case 0: case 1: return new MagicArrowSpell( m_Guard, null ); + case 2: case 3: return new HarmSpell( m_Guard, null ); + case 4: case 5: return new FireballSpell( m_Guard, null ); + case 6: case 7: return new LightningSpell( m_Guard, null ); + case 8: return new MindBlastSpell( m_Guard, null ); + case 9: return new ParalyzeSpell( m_Guard, null ); + case 10: return new EnergyBoltSpell( m_Guard, null ); + case 11: return new ExplosionSpell( m_Guard, null ); + default: return new FlameStrikeSpell( m_Guard, null ); + } + } + + public Mobile FindDispelTarget( bool activeOnly ) + { + if ( m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel( m_Mobile ) || m_Mobile.AutoDispel ) + return null; + + if ( activeOnly ) + { + List aggressed = m_Mobile.Aggressed; + List aggressors = m_Mobile.Aggressors; + + Mobile active = null; + double activePrio = 0.0; + + Mobile comb = m_Mobile.Combatant; + + if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange( comb, 12 ) && CanDispel( comb ) ) + { + active = comb; + activePrio = m_Mobile.GetDistanceToSqrt( comb ); + + if ( activePrio <= 2 ) + return active; + } + + for ( int i = 0; i < aggressed.Count; ++i ) + { + AggressorInfo info = aggressed[i]; + Mobile m = info.Defender; + + if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) ) + { + double prio = m_Mobile.GetDistanceToSqrt( m ); + + if ( active == null || prio < activePrio ) + { + active = m; + activePrio = prio; + + if ( activePrio <= 2 ) + return active; + } + } + } + + for ( int i = 0; i < aggressors.Count; ++i ) + { + AggressorInfo info = aggressors[i]; + Mobile m = info.Attacker; + + if ( m != comb && m.Combatant == m_Mobile && m_Mobile.InRange( m, 12 ) && CanDispel( m ) ) + { + double prio = m_Mobile.GetDistanceToSqrt( m ); + + if ( active == null || prio < activePrio ) + { + active = m; + activePrio = prio; + + if ( activePrio <= 2 ) + return active; + } + } + } + + return active; + } + else + { + Map map = m_Mobile.Map; + + if ( map != null ) + { + Mobile active = null, inactive = null; + double actPrio = 0.0, inactPrio = 0.0; + + Mobile comb = m_Mobile.Combatant; + + if ( comb != null && !comb.Deleted && comb.Alive && !comb.IsDeadBondedPet && CanDispel( comb ) ) + { + active = inactive = comb; + actPrio = inactPrio = m_Mobile.GetDistanceToSqrt( comb ); + } + + foreach ( Mobile m in m_Mobile.GetMobilesInRange( 12 ) ) + { + if ( m != m_Mobile && CanDispel( m ) ) + { + double prio = m_Mobile.GetDistanceToSqrt( m ); + + if ( !activeOnly && (inactive == null || prio < inactPrio) ) + { + inactive = m; + inactPrio = prio; + } + + if ( (m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio) ) + { + active = m; + actPrio = prio; + } + } + } + + return active != null ? active : inactive; + } + } + + return null; + } + + public bool CanDispel( Mobile m ) + { + return ( m is BaseCreature && ((BaseCreature)m).Summoned && m_Mobile.CanBeHarmful( m, false ) && !((BaseCreature)m).IsAnimatedDead ); + } + + public void RunTo( Mobile m ) + { + /*if ( m.Paralyzed || m.Frozen ) + { + if ( m_Mobile.InRange( m, 1 ) ) + RunFrom( m ); + else if ( !m_Mobile.InRange( m, m_Mobile.RangeFight > 2 ? m_Mobile.RangeFight : 2 ) && !MoveTo( m, true, 1 ) ) + OnFailedMove(); + } + else + {*/ + if ( !m_Mobile.InRange( m, m_Mobile.RangeFight ) ) + { + if ( !MoveTo( m, true, 1 ) ) + OnFailedMove(); + } + else if ( m_Mobile.InRange( m, m_Mobile.RangeFight - 1 ) ) + { + RunFrom( m ); + } + /*}*/ + } + + public void RunFrom( Mobile m ) + { + Run( (m_Mobile.GetDirectionTo( m ) - 4) & Direction.Mask ); + } + + public void OnFailedMove() + { + /*if ( !m_Mobile.DisallowAllMoves && 20 > Utility.Random( 100 ) && IsAllowed( GuardAI.Magic ) ) + { + if ( m_Mobile.Target != null ) + m_Mobile.Target.Cancel( m_Mobile, TargetCancelType.Canceled ); + + new TeleportSpell( m_Mobile, null ).Cast(); + + m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" ); + } + else*/ if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + if ( m_Mobile.Debug ) + m_Mobile.DebugSay( "My move is blocked, so I am going to attack {0}", m_Mobile.FocusMob.Name ); + + m_Mobile.Combatant = m_Mobile.FocusMob; + Action = ActionType.Combat; + } + else + { + m_Mobile.DebugSay( "I am stuck" ); + } + } + + public void Run( Direction d ) + { + if ( (m_Mobile.Spell != null && m_Mobile.Spell.IsCasting) || m_Mobile.Paralyzed || m_Mobile.Frozen || m_Mobile.DisallowAllMoves ) + return; + + m_Mobile.Direction = d | Direction.Running; + + if ( !DoMove( m_Mobile.Direction, true ) ) + OnFailedMove(); + } + + public FactionGuardAI( BaseFactionGuard guard ) : base( guard ) + { + m_Guard = guard; + } + + public override bool Think() + { + if ( m_Mobile.Deleted ) + return false; + + Mobile combatant = m_Guard.Combatant; + + if ( combatant == null || combatant.Deleted || !combatant.Alive || combatant.IsDeadBondedPet || !m_Mobile.CanSee( combatant ) || !m_Mobile.CanBeHarmful( combatant, false ) || combatant.Map != m_Mobile.Map ) + { + // Our combatant is deleted, dead, hidden, or we cannot hurt them + // Try to find another combatant + + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.Combatant = combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else + { + m_Mobile.Combatant = combatant = null; + } + } + + if ( combatant != null && (!m_Mobile.InLOS( combatant ) || !m_Mobile.InRange( combatant, 12 )) ) + { + if ( AcquireFocusMob( m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true ) ) + { + m_Mobile.Combatant = combatant = m_Mobile.FocusMob; + m_Mobile.FocusMob = null; + } + else if ( !m_Mobile.InRange( combatant, 36 ) ) + { + m_Mobile.Combatant = combatant = null; + } + } + + Mobile dispelTarget = FindDispelTarget( true ); + + if ( m_Guard.Target != null && m_ReleaseTarget == DateTime.MinValue ) + m_ReleaseTarget = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + + if ( m_Guard.Target != null && DateTime.Now > m_ReleaseTarget ) + { + Target targ = m_Guard.Target; + + Mobile toHarm = ( dispelTarget == null ? combatant : dispelTarget ); + + if ( (targ.Flags & TargetFlags.Harmful) != 0 && toHarm != null ) + { + if ( m_Guard.Map == toHarm.Map && ( targ.Range < 0 || m_Guard.InRange( toHarm, targ.Range ) ) && m_Guard.CanSee( toHarm ) && m_Guard.InLOS( toHarm ) ) + targ.Invoke( m_Guard, toHarm ); + else if ( targ is DispelSpell.InternalTarget ) + targ.Cancel( m_Guard, TargetCancelType.Canceled ); + } + else if ( (targ.Flags & TargetFlags.Beneficial) != 0 ) + { + targ.Invoke( m_Guard, m_Guard ); + } + else + { + targ.Cancel( m_Guard, TargetCancelType.Canceled ); + } + + m_ReleaseTarget = DateTime.MinValue; + } + + if ( dispelTarget != null ) + { + if ( Action != ActionType.Combat ) + Action = ActionType.Combat; + + m_Guard.Warmode = true; + + RunFrom( dispelTarget ); + } + else if ( combatant != null ) + { + if ( Action != ActionType.Combat ) + Action = ActionType.Combat; + + m_Guard.Warmode = true; + + RunTo( combatant ); + } + else if ( m_Guard.Orders.Movement != MovementType.Stand ) + { + Mobile toFollow = null; + + if ( m_Guard.Town != null && m_Guard.Orders.Movement == MovementType.Follow ) + { + toFollow = m_Guard.Orders.Follow; + + if ( toFollow == null ) + toFollow = m_Guard.Town.Sheriff; + } + + if ( toFollow != null && toFollow.Map == m_Guard.Map && toFollow.InRange( m_Guard, m_Guard.RangePerception * 3 ) && Town.FromRegion( toFollow.Region ) == m_Guard.Town ) + { + if ( Action != ActionType.Combat ) + Action = ActionType.Combat; + + if ( m_Mobile.CurrentSpeed != m_Mobile.ActiveSpeed ) + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + + m_Guard.Warmode = true; + + RunTo( toFollow ); + } + else + { + if ( Action != ActionType.Wander ) + Action = ActionType.Wander; + + if ( m_Mobile.CurrentSpeed != m_Mobile.PassiveSpeed ) + m_Mobile.CurrentSpeed = m_Mobile.PassiveSpeed; + + m_Guard.Warmode = false; + + WalkRandomInHome( 2, 2, 1 ); + } + } + else + { + if ( Action != ActionType.Wander ) + Action = ActionType.Wander; + + m_Guard.Warmode = false; + } + + if ( (IsDamaged || IsPoisoned) && m_Guard.Skills.Healing.Base > 20.0 ) + { + TimeSpan ts = TimeUntilBandage; + + if ( ts == TimeSpan.MaxValue ) + StartBandage(); + } + + if ( m_Mobile.Spell == null && DateTime.Now >= m_Mobile.NextSpellTime ) + { + Spell spell = null; + + DateTime toRelease = DateTime.MinValue; + + if ( IsPoisoned ) + { + Poison p = m_Guard.Poison; + + TimeSpan ts = TimeUntilBandage; + + if ( p != Poison.Lesser || ts == TimeSpan.MaxValue || TimeUntilBandage < TimeSpan.FromSeconds( 1.5 ) || (m_Guard.HitsMax - m_Guard.Hits) > Utility.Random( 250 ) ) + { + if ( IsAllowed( GuardAI.Bless ) ) + spell = new CureSpell( m_Guard, null ); + else + UseItemByType( typeof( BaseCurePotion ) ); + } + } + else if ( IsDamaged && (m_Guard.HitsMax - m_Guard.Hits) > Utility.Random( 200 ) ) + { + if( IsAllowed( GuardAI.Magic ) && ((m_Guard.Hits * 100) / Math.Max( m_Guard.HitsMax, 1 )) < 10 && m_Guard.Home != Point3D.Zero && !Utility.InRange( m_Guard.Location, m_Guard.Home, 15 ) && m_Guard.Mana >= 11 ) + { + spell = new RecallSpell( m_Guard, null, new RunebookEntry( m_Guard.Home, m_Guard.Map, "Guard's Home", null ), null ); + } + else if ( IsAllowed( GuardAI.Bless ) ) + { + if ( m_Guard.Mana >= 11 && (m_Guard.Hits + 30) < m_Guard.HitsMax ) + spell = new GreaterHealSpell( m_Guard, null ); + else if ( (m_Guard.Hits + 10) < m_Guard.HitsMax && (m_Guard.Mana < 11 || (m_Guard.NextCombatTime - DateTime.Now) > TimeSpan.FromSeconds( 2.0 )) ) + spell = new HealSpell( m_Guard, null ); + } + else if ( m_Guard.CanBeginAction( typeof( BaseHealPotion ) ) ) + { + UseItemByType( typeof( BaseHealPotion ) ); + } + } + else if ( dispelTarget != null && (IsAllowed( GuardAI.Magic ) || IsAllowed( GuardAI.Bless ) || IsAllowed( GuardAI.Curse )) ) + { + if ( !dispelTarget.Paralyzed && m_Guard.Mana > (ManaReserve + 20) && 40 > Utility.Random( 100 ) ) + spell = new ParalyzeSpell( m_Guard, null ); + else + spell = new DispelSpell( m_Guard, null ); + } + + if ( combatant != null ) + { + if ( m_Combo != null ) + { + if ( spell == null ) + { + spell = SpellCombo.Process( m_Guard, combatant, ref m_Combo, ref m_ComboIndex, ref toRelease ); + } + else + { + m_Combo = null; + m_ComboIndex = -1; + } + } + else if ( 20 > Utility.Random( 100 ) && IsAllowed( GuardAI.Magic ) ) + { + if ( 80 > Utility.Random( 100 ) ) + { + m_Combo = ( IsAllowed( GuardAI.Smart ) ? SpellCombo.Simple : SpellCombo.Strong ); + m_ComboIndex = -1; + + if ( m_Guard.Mana >= (ManaReserve + m_Combo.Mana) ) + spell = SpellCombo.Process( m_Guard, combatant, ref m_Combo, ref m_ComboIndex, ref toRelease ); + else + { + m_Combo = null; + + if ( m_Guard.Mana >= (ManaReserve + 40) ) + spell = RandomOffenseSpell(); + } + } + else if ( m_Guard.Mana >= (ManaReserve + 40) ) + { + spell = RandomOffenseSpell(); + } + } + + if ( spell == null && 2 > Utility.Random( 100 ) && m_Guard.Mana >= (ManaReserve + 10) ) + { + int strMod = GetStatMod( m_Guard, StatType.Str ); + int dexMod = GetStatMod( m_Guard, StatType.Dex ); + int intMod = GetStatMod( m_Guard, StatType.Int ); + + ArrayList types = new ArrayList(); + + if ( strMod <= 0 ) + types.Add( typeof( StrengthSpell ) ); + + if ( dexMod <= 0 && IsAllowed( GuardAI.Melee ) ) + types.Add( typeof( AgilitySpell ) ); + + if ( intMod <= 0 && IsAllowed( GuardAI.Magic ) ) + types.Add( typeof( CunningSpell ) ); + + if ( IsAllowed( GuardAI.Bless ) ) + { + if ( types.Count > 1 ) + spell = new BlessSpell( m_Guard, null ); + else if ( types.Count == 1 ) + spell = (Spell) Activator.CreateInstance( (Type) types[0], new object[]{ m_Guard, null } ); + } + else if ( types.Count > 0 ) + { + if ( types[0] == typeof( StrengthSpell ) ) + UseItemByType( typeof( BaseStrengthPotion ) ); + else if ( types[0] == typeof( AgilitySpell ) ) + UseItemByType( typeof( BaseAgilityPotion ) ); + } + } + + if ( spell == null && 2 > Utility.Random( 100 ) && m_Guard.Mana >= (ManaReserve + 10) && IsAllowed( GuardAI.Curse ) ) + { + if ( !combatant.Poisoned && 40 > Utility.Random( 100 ) ) + { + spell = new PoisonSpell( m_Guard, null ); + } + else + { + int strMod = GetStatMod( combatant, StatType.Str ); + int dexMod = GetStatMod( combatant, StatType.Dex ); + int intMod = GetStatMod( combatant, StatType.Int ); + + ArrayList types = new ArrayList(); + + if ( strMod >= 0 ) + types.Add( typeof( WeakenSpell ) ); + + if ( dexMod >= 0 && IsAllowed( GuardAI.Melee ) ) + types.Add( typeof( ClumsySpell ) ); + + if ( intMod >= 0 && IsAllowed( GuardAI.Magic ) ) + types.Add( typeof( FeeblemindSpell ) ); + + if ( types.Count > 1 ) + spell = new CurseSpell( m_Guard, null ); + else if ( types.Count == 1 ) + spell = (Spell) Activator.CreateInstance( (Type) types[0], new object[]{ m_Guard, null } ); + } + } + } + + if ( spell != null && (m_Guard.HitsMax - m_Guard.Hits + 10) > Utility.Random( 100 ) ) + { + Type type = null; + + if ( spell is GreaterHealSpell ) + type = typeof( BaseHealPotion ); + else if ( spell is CureSpell ) + type = typeof( BaseCurePotion ); + else if ( spell is StrengthSpell ) + type = typeof( BaseStrengthPotion ); + else if ( spell is AgilitySpell ) + type = typeof( BaseAgilityPotion ); + + if ( type == typeof( BaseHealPotion ) && !m_Guard.CanBeginAction( type ) ) + type = null; + + if ( type != null && m_Guard.Target == null && UseItemByType( type ) ) + { + if ( spell is GreaterHealSpell ) + { + if ( (m_Guard.Hits + 30) > m_Guard.HitsMax && (m_Guard.Hits + 10) < m_Guard.HitsMax ) + spell = new HealSpell( m_Guard, null ); + } + else + { + spell = null; + } + } + } + else if ( spell == null && m_Guard.Stam < (m_Guard.StamMax / 3) && IsAllowed( GuardAI.Melee ) ) + { + UseItemByType( typeof( BaseRefreshPotion ) ); + } + + if ( spell == null || !spell.Cast() ) + EquipWeapon(); + } + else if ( m_Mobile.Spell is Spell && ((Spell)m_Mobile.Spell).State == SpellState.Sequencing ) + { + EquipWeapon(); + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Orders.cs b/Scripts/Engines/Factions/Mobiles/Guards/Orders.cs new file mode 100644 index 000000000..d3b95b3bc --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Orders.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections; + +namespace Server.Factions.AI +{ + public enum ReactionType + { + Ignore, + Warn, + Attack + } + + public enum MovementType + { + Stand, + Patrol, + Follow + } + + public class Reaction + { + private Faction m_Faction; + private ReactionType m_Type; + + public Faction Faction{ get{ return m_Faction; } } + public ReactionType Type{ get{ return m_Type; } set{ m_Type = value; } } + + public Reaction( Faction faction, ReactionType type ) + { + m_Faction = faction; + m_Type = type; + } + + public Reaction( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_Faction = Faction.ReadReference( reader ); + m_Type = (ReactionType) reader.ReadEncodedInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + Faction.WriteReference( writer, m_Faction ); + writer.WriteEncodedInt( (int) m_Type ); + } + } + + public class Orders + { + private BaseFactionGuard m_Guard; + + private ArrayList m_Reactions; + private MovementType m_Movement; + private Mobile m_Follow; + + public BaseFactionGuard Guard{ get{ return m_Guard; } } + + public MovementType Movement{ get{ return m_Movement; } set{ m_Movement = value; } } + public Mobile Follow{ get{ return m_Follow; } set{ m_Follow = value; } } + + public Reaction GetReaction( Faction faction ) + { + Reaction reaction; + + for ( int i = 0; i < m_Reactions.Count; ++i ) + { + reaction = (Reaction) m_Reactions[i]; + + if ( reaction.Faction == faction ) + return reaction; + } + + reaction = new Reaction( faction, ( faction == null || faction == m_Guard.Faction ) ? ReactionType.Ignore : ReactionType.Attack ); + m_Reactions.Add( reaction ); + + return reaction; + } + + public void SetReaction( Faction faction, ReactionType type ) + { + Reaction reaction = GetReaction( faction ); + + reaction.Type = type; + } + + public Orders( BaseFactionGuard guard ) + { + m_Guard = guard; + m_Reactions = new ArrayList(); + m_Movement = MovementType.Patrol; + } + + public Orders( BaseFactionGuard guard, GenericReader reader ) + { + m_Guard = guard; + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Follow = reader.ReadMobile(); + goto case 0; + } + case 0: + { + int count = reader.ReadEncodedInt(); + m_Reactions = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + m_Reactions.Add( new Reaction( reader ) ); + + m_Movement = (MovementType)reader.ReadEncodedInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 1 ); // version + + writer.Write( (Mobile) m_Follow ); + + writer.WriteEncodedInt( (int) m_Reactions.Count ); + + for ( int i = 0; i < m_Reactions.Count; ++i ) + ((Reaction)m_Reactions[i]).Serialize( writer ); + + writer.WriteEncodedInt( (int) m_Movement ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionBerserker.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionBerserker.cs new file mode 100644 index 000000000..443542ef1 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionBerserker.cs @@ -0,0 +1,74 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionBerserker : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Melee | GuardAI.Curse | GuardAI.Bless; } } + + [Constructable] + public FactionBerserker() : base( "the berserker" ) + { + GenerateBody( false, false ); + + SetStr( 126, 150 ); + SetDex( 61, 85 ); + SetInt( 81, 95 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 50 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 30, 50 ); + SetResistance( ResistanceType.Energy, 30, 50 ); + SetResistance( ResistanceType.Poison, 30, 50 ); + + VirtualArmor = 24; + + SetSkill( SkillName.Swords, 100.0, 110.0 ); + SetSkill( SkillName.Wrestling, 100.0, 110.0 ); + SetSkill( SkillName.Tactics, 100.0, 110.0 ); + SetSkill( SkillName.MagicResist, 100.0, 110.0 ); + SetSkill( SkillName.Healing, 100.0, 110.0 ); + SetSkill( SkillName.Anatomy, 100.0, 110.0 ); + + SetSkill( SkillName.Magery, 100.0, 110.0 ); + SetSkill( SkillName.EvalInt, 100.0, 110.0 ); + SetSkill( SkillName.Meditation, 100.0, 110.0 ); + + AddItem( Immovable( Rehued( new BodySash(), 1645 ) ) ); + AddItem( Immovable( Rehued( new Kilt(), 1645 ) ) ); + AddItem( Immovable( Rehued( new Sandals(), 1645 ) ) ); + AddItem( Newbied( new DoubleAxe() ) ); + + HairItemID = 0x2047; // Afro + HairHue = 0x29; + + FacialHairItemID = 0x204B; // Medium Short Beard + FacialHairHue = 0x29; + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionBerserker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDeathKnight.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDeathKnight.cs new file mode 100644 index 000000000..d7d5fe2fb --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDeathKnight.cs @@ -0,0 +1,70 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionDeathKnight : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Melee | GuardAI.Curse | GuardAI.Bless; } } + + [Constructable] + public FactionDeathKnight() : base( "the death knight" ) + { + GenerateBody( false, false ); + Hue = 1; + + SetStr( 126, 150 ); + SetDex( 61, 85 ); + SetInt( 81, 95 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 50 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 30, 50 ); + SetResistance( ResistanceType.Energy, 30, 50 ); + SetResistance( ResistanceType.Poison, 30, 50 ); + + VirtualArmor = 24; + + SetSkill( SkillName.Swords, 100.0, 110.0 ); + SetSkill( SkillName.Wrestling, 100.0, 110.0 ); + SetSkill( SkillName.Tactics, 100.0, 110.0 ); + SetSkill( SkillName.MagicResist, 100.0, 110.0 ); + SetSkill( SkillName.Healing, 100.0, 110.0 ); + SetSkill( SkillName.Anatomy, 100.0, 110.0 ); + + SetSkill( SkillName.Magery, 100.0, 110.0 ); + SetSkill( SkillName.EvalInt, 100.0, 110.0 ); + SetSkill( SkillName.Meditation, 100.0, 110.0 ); + + Item shroud = new Item( 0x204E ); + shroud.Layer = Layer.OuterTorso; + + AddItem( Immovable( Rehued( shroud, 1109 ) ) ); + AddItem( Newbied( Rehued( new ExecutionersAxe(), 2211 ) ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionDeathKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDragoon.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDragoon.cs new file mode 100644 index 000000000..fa6b2a2e1 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionDragoon.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionDragoon : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Magic | GuardAI.Melee | GuardAI.Smart | GuardAI.Bless | GuardAI.Curse; } } + + [Constructable] + public FactionDragoon() : base( "the dragoon" ) + { + GenerateBody( false, false ); + + SetStr( 151, 175 ); + SetDex( 61, 85 ); + SetInt( 151, 175 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 40, 60 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + + VirtualArmor = 32; + + SetSkill( SkillName.Macing, 110.0, 120.0 ); + SetSkill( SkillName.Wrestling, 110.0, 120.0 ); + SetSkill( SkillName.Tactics, 110.0, 120.0 ); + SetSkill( SkillName.MagicResist, 110.0, 120.0 ); + SetSkill( SkillName.Healing, 110.0, 120.0 ); + SetSkill( SkillName.Anatomy, 110.0, 120.0 ); + + SetSkill( SkillName.Magery, 110.0, 120.0 ); + SetSkill( SkillName.EvalInt, 110.0, 120.0 ); + SetSkill( SkillName.Meditation, 110.0, 120.0 ); + + AddItem( Immovable( Rehued( new Cloak(), 1645 ) ) ); + + AddItem( Immovable( Rehued( new PlateChest(), 1645 ) ) ); + AddItem( Immovable( Rehued( new PlateLegs(), 1109 ) ) ); + AddItem( Immovable( Rehued( new PlateArms(), 1109 ) ) ); + AddItem( Immovable( Rehued( new PlateGloves(), 1109 ) ) ); + AddItem( Immovable( Rehued( new PlateGorget(), 1109 ) ) ); + AddItem( Immovable( Rehued( new PlateHelm(), 1109 ) ) ); + + AddItem( Newbied( new WarHammer() ) ); + + AddItem( Immovable( Rehued( new VirtualMountItem( this ), 1109 ) ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionDragoon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionHenchman.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionHenchman.cs new file mode 100644 index 000000000..6809a97b8 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionHenchman.cs @@ -0,0 +1,67 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionHenchman : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Melee; } } + + [Constructable] + public FactionHenchman() : base( "the henchman" ) + { + GenerateBody( false, true ); + + SetStr( 91, 115 ); + SetDex( 61, 85 ); + SetInt( 81, 95 ); + + SetDamage( 10, 14 ); + + SetResistance( ResistanceType.Physical, 10, 30 ); + SetResistance( ResistanceType.Fire, 10, 30 ); + SetResistance( ResistanceType.Cold, 10, 30 ); + SetResistance( ResistanceType.Energy, 10, 30 ); + SetResistance( ResistanceType.Poison, 10, 30 ); + + VirtualArmor = 8; + + SetSkill( SkillName.Fencing, 80.0, 90.0 ); + SetSkill( SkillName.Wrestling, 80.0, 90.0 ); + SetSkill( SkillName.Tactics, 80.0, 90.0 ); + SetSkill( SkillName.MagicResist, 80.0, 90.0 ); + SetSkill( SkillName.Healing, 80.0, 90.0 ); + SetSkill( SkillName.Anatomy, 80.0, 90.0 ); + + AddItem( new StuddedChest() ); + AddItem( new StuddedLegs() ); + AddItem( new StuddedArms() ); + AddItem( new StuddedGloves() ); + AddItem( new StuddedGorget() ); + AddItem( new Boots() ); + AddItem( Newbied( new Spear() ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 10, 20 ) ) ); + PackWeakPotions( 1, 4 ); + } + + public FactionHenchman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionKnight.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionKnight.cs new file mode 100644 index 000000000..56782ea2b --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionKnight.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionKnight : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Magic | GuardAI.Melee | GuardAI.Smart | GuardAI.Curse | GuardAI.Bless; } } + + [Constructable] + public FactionKnight() : base( "the knight" ) + { + GenerateBody( false, false ); + + SetStr( 126, 150 ); + SetDex( 61, 85 ); + SetInt( 81, 95 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 50 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 30, 50 ); + SetResistance( ResistanceType.Energy, 30, 50 ); + SetResistance( ResistanceType.Poison, 30, 50 ); + + VirtualArmor = 24; + + SetSkill( SkillName.Swords, 100.0, 110.0 ); + SetSkill( SkillName.Wrestling, 100.0, 110.0 ); + SetSkill( SkillName.Tactics, 100.0, 110.0 ); + SetSkill( SkillName.MagicResist, 100.0, 110.0 ); + SetSkill( SkillName.Healing, 100.0, 110.0 ); + SetSkill( SkillName.Anatomy, 100.0, 110.0 ); + + SetSkill( SkillName.Magery, 100.0, 110.0 ); + SetSkill( SkillName.EvalInt, 100.0, 110.0 ); + SetSkill( SkillName.Meditation, 100.0, 110.0 ); + + AddItem( Immovable( Rehued( new ChainChest(), 2125 ) ) ); + AddItem( Immovable( Rehued( new ChainLegs(), 2125 ) ) ); + AddItem( Immovable( Rehued( new ChainCoif(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateArms(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateGloves(), 2125 ) ) ); + + AddItem( Immovable( Rehued( new BodySash(), 1254 ) ) ); + AddItem( Immovable( Rehued( new Kilt(), 1254 ) ) ); + AddItem( Immovable( Rehued( new Sandals(), 1254 ) ) ); + + AddItem( Newbied( new Bardiche() ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionMercenary.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionMercenary.cs new file mode 100644 index 000000000..6b8428d4f --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionMercenary.cs @@ -0,0 +1,65 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionMercenary : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Melee | GuardAI.Smart; } } + + [Constructable] + public FactionMercenary() : base( "the mercenary" ) + { + GenerateBody( false, true ); + + SetStr( 116, 125 ); + SetDex( 61, 85 ); + SetInt( 81, 95 ); + + SetResistance( ResistanceType.Physical, 20, 40 ); + SetResistance( ResistanceType.Fire, 20, 40 ); + SetResistance( ResistanceType.Cold, 20, 40 ); + SetResistance( ResistanceType.Energy, 20, 40 ); + SetResistance( ResistanceType.Poison, 20, 40 ); + + VirtualArmor = 16; + + SetSkill( SkillName.Fencing, 90.0, 100.0 ); + SetSkill( SkillName.Wrestling, 90.0, 100.0 ); + SetSkill( SkillName.Tactics, 90.0, 100.0 ); + SetSkill( SkillName.MagicResist, 90.0, 100.0 ); + SetSkill( SkillName.Healing, 90.0, 100.0 ); + SetSkill( SkillName.Anatomy, 90.0, 100.0 ); + + AddItem( new ChainChest() ); + AddItem( new ChainLegs() ); + AddItem( new RingmailArms() ); + AddItem( new RingmailGloves() ); + AddItem( new ChainCoif() ); + AddItem( new Boots() ); + AddItem( Newbied( new ShortSpear() ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 20, 30 ) ) ); + PackStrongPotions( 3, 8 ); + } + + public FactionMercenary( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionNecromancer.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionNecromancer.cs new file mode 100644 index 000000000..526f3417c --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionNecromancer.cs @@ -0,0 +1,68 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionNecromancer : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Magic | GuardAI.Smart | GuardAI.Bless | GuardAI.Curse; } } + + [Constructable] + public FactionNecromancer() : base( "the necromancer" ) + { + GenerateBody( false, false ); + Hue = 1; + + SetStr( 151, 175 ); + SetDex( 61, 85 ); + SetInt( 151, 175 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 40, 60 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + + VirtualArmor = 32; + + SetSkill( SkillName.Macing, 110.0, 120.0 ); + SetSkill( SkillName.Wrestling, 110.0, 120.0 ); + SetSkill( SkillName.Tactics, 110.0, 120.0 ); + SetSkill( SkillName.MagicResist, 110.0, 120.0 ); + SetSkill( SkillName.Healing, 110.0, 120.0 ); + SetSkill( SkillName.Anatomy, 110.0, 120.0 ); + + SetSkill( SkillName.Magery, 110.0, 120.0 ); + SetSkill( SkillName.EvalInt, 110.0, 120.0 ); + SetSkill( SkillName.Meditation, 110.0, 120.0 ); + + Item shroud = new Item( 0x204E ); + shroud.Layer = Layer.OuterTorso; + + AddItem( Immovable( Rehued( shroud, 1109 ) ) ); + AddItem( Newbied( Rehued( new GnarledStaff(), 2211 ) ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionNecromancer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionPaladin.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionPaladin.cs new file mode 100644 index 000000000..025a89ab1 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionPaladin.cs @@ -0,0 +1,76 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionPaladin : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Magic | GuardAI.Melee | GuardAI.Smart | GuardAI.Curse | GuardAI.Bless; } } + + [Constructable] + public FactionPaladin() : base( "the paladin" ) + { + GenerateBody( false, false ); + + SetStr( 151, 175 ); + SetDex( 61, 85 ); + SetInt( 81, 95 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 40, 60 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + + VirtualArmor = 32; + + SetSkill( SkillName.Swords, 110.0, 120.0 ); + SetSkill( SkillName.Wrestling, 110.0, 120.0 ); + SetSkill( SkillName.Tactics, 110.0, 120.0 ); + SetSkill( SkillName.MagicResist, 110.0, 120.0 ); + SetSkill( SkillName.Healing, 110.0, 120.0 ); + SetSkill( SkillName.Anatomy, 110.0, 120.0 ); + + SetSkill( SkillName.Magery, 110.0, 120.0 ); + SetSkill( SkillName.EvalInt, 110.0, 120.0 ); + SetSkill( SkillName.Meditation, 110.0, 120.0 ); + + AddItem( Immovable( Rehued( new PlateChest(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateLegs(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateHelm(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateGorget(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateArms(), 2125 ) ) ); + AddItem( Immovable( Rehued( new PlateGloves(), 2125 ) ) ); + + AddItem( Immovable( Rehued( new BodySash(), 1254 ) ) ); + AddItem( Immovable( Rehued( new Cloak(), 1254 ) ) ); + + AddItem( Newbied( new Halberd() ) ); + + AddItem( Immovable( Rehued( new VirtualMountItem( this ), 1254 ) ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionPaladin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionSorceress.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionSorceress.cs new file mode 100644 index 000000000..cd4f9ed37 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionSorceress.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionSorceress : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Magic | GuardAI.Bless | GuardAI.Curse; } } + + [Constructable] + public FactionSorceress() : base( "the sorceress" ) + { + GenerateBody( true, false ); + + SetStr( 126, 150 ); + SetDex( 61, 85 ); + SetInt( 126, 150 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 50 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 30, 50 ); + SetResistance( ResistanceType.Energy, 30, 50 ); + SetResistance( ResistanceType.Poison, 30, 50 ); + + VirtualArmor = 24; + + SetSkill( SkillName.Macing, 100.0, 110.0 ); + SetSkill( SkillName.Wrestling, 100.0, 110.0 ); + SetSkill( SkillName.Tactics, 100.0, 110.0 ); + SetSkill( SkillName.MagicResist, 100.0, 110.0 ); + SetSkill( SkillName.Healing, 100.0, 110.0 ); + SetSkill( SkillName.Anatomy, 100.0, 110.0 ); + + SetSkill( SkillName.Magery, 100.0, 110.0 ); + SetSkill( SkillName.EvalInt, 100.0, 110.0 ); + SetSkill( SkillName.Meditation, 100.0, 110.0 ); + + AddItem( Immovable( Rehued( new WizardsHat(), 1325 ) ) ); + AddItem( Immovable( Rehued( new Sandals(), 1325 ) ) ); + AddItem( Immovable( Rehued( new LeatherGorget(), 1325 ) ) ); + AddItem( Immovable( Rehued( new LeatherGloves(), 1325 ) ) ); + AddItem( Immovable( Rehued( new LeatherLegs(), 1325 ) ) ); + AddItem( Immovable( Rehued( new Skirt(), 1325 ) ) ); + AddItem( Immovable( Rehued( new FemaleLeatherChest(), 1325 ) ) ); + AddItem( Newbied( Rehued( new QuarterStaff(), 1310 ) ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionSorceress( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionWizard.cs b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionWizard.cs new file mode 100644 index 000000000..e423d4eed --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Guards/Types/FactionWizard.cs @@ -0,0 +1,69 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Factions +{ + public class FactionWizard : BaseFactionGuard + { + public override GuardAI GuardAI{ get{ return GuardAI.Magic | GuardAI.Smart | GuardAI.Bless | GuardAI.Curse; } } + + [Constructable] + public FactionWizard() : base( "the wizard" ) + { + GenerateBody( false, false ); + + SetStr( 151, 175 ); + SetDex( 61, 85 ); + SetInt( 151, 175 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 40, 60 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + + VirtualArmor = 32; + + SetSkill( SkillName.Macing, 110.0, 120.0 ); + SetSkill( SkillName.Wrestling, 110.0, 120.0 ); + SetSkill( SkillName.Tactics, 110.0, 120.0 ); + SetSkill( SkillName.MagicResist, 110.0, 120.0 ); + SetSkill( SkillName.Healing, 110.0, 120.0 ); + SetSkill( SkillName.Anatomy, 110.0, 120.0 ); + + SetSkill( SkillName.Magery, 110.0, 120.0 ); + SetSkill( SkillName.EvalInt, 110.0, 120.0 ); + SetSkill( SkillName.Meditation, 110.0, 120.0 ); + + AddItem( Immovable( Rehued( new WizardsHat(), 1325 ) ) ); + AddItem( Immovable( Rehued( new Sandals(), 1325 ) ) ); + AddItem( Immovable( Rehued( new Robe(), 1310 ) ) ); + AddItem( Immovable( Rehued( new LeatherGloves(), 1325 ) ) ); + AddItem( Newbied( Rehued( new GnarledStaff(), 1310 ) ) ); + + PackItem( new Bandage( Utility.RandomMinMax( 30, 40 ) ) ); + PackStrongPotions( 6, 12 ); + } + + public FactionWizard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Vendors/BaseFactionVendor.cs b/Scripts/Engines/Factions/Mobiles/Vendors/BaseFactionVendor.cs new file mode 100644 index 000000000..06718d9e3 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Vendors/BaseFactionVendor.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; + +namespace Server.Factions +{ + public abstract class BaseFactionVendor : BaseVendor + { + private Town m_Town; + private Faction m_Faction; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Town Town + { + get{ return m_Town; } + set{ Unregister(); m_Town = value; Register(); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public Faction Faction + { + get{ return m_Faction; } + set{ Unregister(); m_Faction = value; Register(); } + } + + public void Register() + { + if ( m_Town != null && m_Faction != null ) + m_Town.RegisterVendor( this ); + } + + public void Unregister() + { + if ( m_Town != null ) + m_Town.UnregisterVendor( this ); + } + + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override void InitSBInfo() + { + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + Unregister(); + } + + public override bool CheckVendorAccess( Mobile from ) + { + return true; + } + + public BaseFactionVendor( Town town, Faction faction, string title ) : base( title ) + { + Frozen = true; + CantWalk = true; + Female = false; + BodyValue = 400; + Name = NameList.RandomName( "male" ); + + RangeHome = 0; + + m_Town = town; + m_Faction = faction; + Register(); + } + + public BaseFactionVendor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Town.WriteReference( writer, m_Town ); + Faction.WriteReference( writer, m_Faction ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Town = Town.ReadReference( reader ); + m_Faction = Faction.ReadReference( reader ); + Register(); + break; + } + } + + Frozen = true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Vendors/FactionBoardVendor.cs b/Scripts/Engines/Factions/Mobiles/Vendors/FactionBoardVendor.cs new file mode 100644 index 000000000..584082448 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Vendors/FactionBoardVendor.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionBoardVendor : BaseFactionVendor + { + public FactionBoardVendor( Town town, Faction faction ) : base( town, faction, "the LumberMan" ) // NOTE: title inconsistant, as OSI + { + SetSkill( SkillName.Carpentry, 85.0, 100.0 ); + SetSkill( SkillName.Lumberjacking, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + SBInfos.Add( new SBFactionBoard() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new HalfApron() ); + } + + public FactionBoardVendor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SBFactionBoard : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFactionBoard() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + for ( int i = 0; i < 5; ++i ) + Add( new GenericBuyInfo( typeof( Board ), 3, 20, 0x1BD7, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Vendors/FactionBottleVendor.cs b/Scripts/Engines/Factions/Mobiles/Vendors/FactionBottleVendor.cs new file mode 100644 index 000000000..2884d669d --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Vendors/FactionBottleVendor.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionBottleVendor : BaseFactionVendor + { + public FactionBottleVendor( Town town, Faction faction ) : base( town, faction, "the Bottle Seller" ) + { + SetSkill( SkillName.Alchemy, 85.0, 100.0 ); + SetSkill( SkillName.TasteID, 65.0, 88.0 ); + } + + public override void InitSBInfo() + { + SBInfos.Add( new SBFactionBottle() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Robe( Utility.RandomPinkHue() ) ); + } + + public FactionBottleVendor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SBFactionBottle : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFactionBottle() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + for ( int i = 0; i < 5; ++i ) + Add( new GenericBuyInfo( typeof( Bottle ), 5, 20, 0xF0E, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Vendors/FactionHorseVendor.cs b/Scripts/Engines/Factions/Mobiles/Vendors/FactionHorseVendor.cs new file mode 100644 index 000000000..9d5d3a071 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Vendors/FactionHorseVendor.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Factions +{ + public class FactionHorseVendor : BaseFactionVendor + { + public FactionHorseVendor( Town town, Faction faction ) : base( town, faction, "the Horse Breeder" ) + { + SetSkill( SkillName.AnimalLore, 64.0, 100.0 ); + SetSkill( SkillName.AnimalTaming, 90.0, 100.0 ); + SetSkill( SkillName.Veterinary, 65.0, 88.0 ); + } + + public override void InitSBInfo() + { + } + + public override VendorShoeType ShoeType + { + get{ return Female ? VendorShoeType.ThighBoots : VendorShoeType.Boots; } + } + + public override int GetShoeHue() + { + return 0; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( Utility.RandomBool() ? (Item)new QuarterStaff() : (Item)new ShepherdsCrook() ); + } + + public FactionHorseVendor( Serial serial ) : base( serial ) + { + } + + public override void VendorBuy( Mobile from ) + { + if ( this.Faction == null || Faction.Find( from, true ) != this.Faction ) + PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042201, from.NetState ); // You are not in my faction, I cannot sell you a horse! + else if ( FactionGump.Exists( from ) ) + from.SendLocalizedMessage( 1042160 ); // You already have a faction menu open. + else if ( from is PlayerMobile ) + from.SendGump( new HorseBreederGump( (PlayerMobile) from, this.Faction ) ); + } + + public override void VendorSell( Mobile from ) + { + } + + public override bool OnBuyItems( Mobile buyer, ArrayList list ) + { + return false; + } + + public override bool OnSellItems( Mobile seller, ArrayList list ) + { + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Vendors/FactionOreVendor.cs b/Scripts/Engines/Factions/Mobiles/Vendors/FactionOreVendor.cs new file mode 100644 index 000000000..5e8e6d957 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Vendors/FactionOreVendor.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionOreVendor : BaseFactionVendor + { + public FactionOreVendor( Town town, Faction faction ) : base( town, faction, "the Ore Man" ) + { + // NOTE: Skills verified + SetSkill( SkillName.Carpentry, 85.0, 100.0 ); + SetSkill( SkillName.Lumberjacking, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + SBInfos.Add( new SBFactionOre() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new HalfApron() ); + } + + public FactionOreVendor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SBFactionOre : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFactionOre() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + for ( int i = 0; i < 5; ++i ) + Add( new GenericBuyInfo( typeof( IronOre ), 16, 20, 0x19B8, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Factions/Mobiles/Vendors/FactionReagentVendor.cs b/Scripts/Engines/Factions/Mobiles/Vendors/FactionReagentVendor.cs new file mode 100644 index 000000000..03f4ae0d0 --- /dev/null +++ b/Scripts/Engines/Factions/Mobiles/Vendors/FactionReagentVendor.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Factions +{ + public class FactionReagentVendor : BaseFactionVendor + { + public FactionReagentVendor( Town town, Faction faction ) : base( town, faction, "the Reagent Man" ) + { + SetSkill( SkillName.EvalInt, 65.0, 88.0 ); + SetSkill( SkillName.Inscribe, 60.0, 83.0 ); + SetSkill( SkillName.Magery, 64.0, 100.0 ); + SetSkill( SkillName.Meditation, 60.0, 83.0 ); + SetSkill( SkillName.MagicResist, 65.0, 88.0 ); + SetSkill( SkillName.Wrestling, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + SBInfos.Add( new SBFactionReagent() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Robe( Utility.RandomBlueHue() ) ); + AddItem( new GnarledStaff() ); + } + + public FactionReagentVendor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SBFactionReagent : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFactionReagent() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + for ( int i = 0; i < 2; ++i ) + { + Add( new GenericBuyInfo( typeof( BlackPearl ), 5, 20, 0xF7A, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 20, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( SulfurousAsh ), 3, 20, 0xF8C, 0 ) ); + } + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestBank.cs b/Scripts/Engines/Harvest/Core/HarvestBank.cs new file mode 100644 index 000000000..0b406e002 --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestBank.cs @@ -0,0 +1,82 @@ +using System; + +namespace Server.Engines.Harvest +{ + public class HarvestBank + { + private int m_Current; + private int m_Maximum; + private DateTime m_NextRespawn; + private HarvestVein m_Vein, m_DefaultVein; + + public int Current + { + get + { + CheckRespawn(); + return m_Current; + } + } + + public HarvestVein Vein + { + get + { + CheckRespawn(); + return m_Vein; + } + set + { + m_Vein = value; + } + } + + public HarvestVein DefaultVein + { + get + { + CheckRespawn(); + return m_DefaultVein; + } + } + + public void CheckRespawn() + { + if ( m_Current == m_Maximum || m_NextRespawn > DateTime.Now ) + return; + + m_Current = m_Maximum; + m_Vein = m_DefaultVein; + } + + public void Consume( HarvestDefinition def, int amount ) + { + CheckRespawn(); + + if ( m_Current == m_Maximum ) + { + double min = def.MinRespawn.TotalMinutes; + double max = def.MaxRespawn.TotalMinutes; + double rnd = Utility.RandomDouble(); + + m_Current = m_Maximum - amount; + m_NextRespawn = DateTime.Now + TimeSpan.FromMinutes( min + (rnd * (max - min)) ); + } + else + { + m_Current -= amount; + } + + if ( m_Current < 0 ) + m_Current = 0; + } + + public HarvestBank( HarvestDefinition def, HarvestVein defaultVein ) + { + m_Maximum = Utility.RandomMinMax( def.MinTotal, def.MaxTotal ); + m_Current = m_Maximum; + m_DefaultVein = defaultVein; + m_Vein = m_DefaultVein; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestDefinition.cs b/Scripts/Engines/Harvest/Core/HarvestDefinition.cs new file mode 100644 index 000000000..695f37e80 --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestDefinition.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; + +namespace Server.Engines.Harvest +{ + public class HarvestDefinition + { + private int m_BankWidth, m_BankHeight; + private int m_MinTotal, m_MaxTotal; + private int[] m_Tiles; + private bool m_RangedTiles; + private TimeSpan m_MinRespawn, m_MaxRespawn; + private int m_MaxRange; + private int m_ConsumedPerHarvest, m_ConsumedPerFeluccaHarvest; + private bool m_PlaceAtFeetIfFull; + private SkillName m_Skill; + private int[] m_EffectActions; + private int[] m_EffectCounts; + private int[] m_EffectSounds; + private TimeSpan m_EffectSoundDelay; + private TimeSpan m_EffectDelay; + private object m_NoResourcesMessage, m_OutOfRangeMessage, m_TimedOutOfRangeMessage, m_DoubleHarvestMessage, m_FailMessage, m_PackFullMessage, m_ToolBrokeMessage; + private HarvestResource[] m_Resources; + private HarvestVein[] m_Veins; + + public int BankWidth{ get{ return m_BankWidth; } set{ m_BankWidth = value; } } + public int BankHeight{ get{ return m_BankHeight; } set{ m_BankHeight = value; } } + public int MinTotal{ get{ return m_MinTotal; } set{ m_MinTotal = value; } } + public int MaxTotal{ get{ return m_MaxTotal; } set{ m_MaxTotal = value; } } + public int[] Tiles{ get{ return m_Tiles; } set{ m_Tiles = value; } } + public bool RangedTiles{ get{ return m_RangedTiles; } set{ m_RangedTiles = value; } } + public TimeSpan MinRespawn{ get{ return m_MinRespawn; } set{ m_MinRespawn = value; } } + public TimeSpan MaxRespawn{ get{ return m_MaxRespawn; } set{ m_MaxRespawn = value; } } + public int MaxRange{ get{ return m_MaxRange; } set{ m_MaxRange = value; } } + public int ConsumedPerHarvest{ get{ return m_ConsumedPerHarvest; } set{ m_ConsumedPerHarvest = value; } } + public int ConsumedPerFeluccaHarvest{ get{ return m_ConsumedPerFeluccaHarvest; } set{ m_ConsumedPerFeluccaHarvest = value; } } + public bool PlaceAtFeetIfFull{ get{ return m_PlaceAtFeetIfFull; } set{ m_PlaceAtFeetIfFull = value; } } + public SkillName Skill{ get{ return m_Skill; } set{ m_Skill = value; } } + public int[] EffectActions{ get{ return m_EffectActions; } set{ m_EffectActions = value; } } + public int[] EffectCounts{ get{ return m_EffectCounts; } set{ m_EffectCounts = value; } } + public int[] EffectSounds{ get{ return m_EffectSounds; } set{ m_EffectSounds = value; } } + public TimeSpan EffectSoundDelay{ get{ return m_EffectSoundDelay; } set{ m_EffectSoundDelay = value; } } + public TimeSpan EffectDelay{ get{ return m_EffectDelay; } set{ m_EffectDelay = value; } } + public object NoResourcesMessage{ get{ return m_NoResourcesMessage; } set{ m_NoResourcesMessage = value; } } + public object OutOfRangeMessage{ get{ return m_OutOfRangeMessage; } set{ m_OutOfRangeMessage = value; } } + public object TimedOutOfRangeMessage{ get{ return m_TimedOutOfRangeMessage; } set{ m_TimedOutOfRangeMessage = value; } } + public object DoubleHarvestMessage{ get{ return m_DoubleHarvestMessage; } set{ m_DoubleHarvestMessage = value; } } + public object FailMessage{ get{ return m_FailMessage; } set{ m_FailMessage = value; } } + public object PackFullMessage{ get{ return m_PackFullMessage; } set{ m_PackFullMessage = value; } } + public object ToolBrokeMessage{ get{ return m_ToolBrokeMessage; } set{ m_ToolBrokeMessage = value; } } + public HarvestResource[] Resources{ get{ return m_Resources; } set{ m_Resources = value; } } + public HarvestVein[] Veins{ get{ return m_Veins; } set{ m_Veins = value; } } + + private Hashtable m_BanksByMap; + + public Hashtable Banks{ get{ return m_BanksByMap; } set{ m_BanksByMap = value; } } + + public void SendMessageTo( Mobile from, object message ) + { + if ( message is int ) + from.SendLocalizedMessage( (int)message ); + else if ( message is string ) + from.SendMessage( (string)message ); + } + + public HarvestBank GetBank( Map map, int x, int y ) + { + if ( map == null || map == Map.Internal ) + return null; + + x /= m_BankWidth; + y /= m_BankHeight; + + Hashtable banks = (Hashtable)m_BanksByMap[map]; + + if ( banks == null ) + m_BanksByMap[map] = banks = new Hashtable(); + + Point2D key = new Point2D( x, y ); + HarvestBank bank = (HarvestBank)banks[key]; + + if ( bank == null ) + banks[key] = bank = new HarvestBank( this, GetVeinAt( map, x, y ) ); + + return bank; + } + + public HarvestVein GetVeinAt( Map map, int x, int y ) + { + if ( m_Veins.Length == 1 ) + return m_Veins[0]; + + Random random = new Random( (x*17)+(y*11)+(map.MapID * 3) ); + double randomValue = random.NextDouble() * 100; + + for ( int i = 0; i < m_Veins.Length; ++i ) + { + if ( randomValue <= m_Veins[i].VeinChance ) + return m_Veins[i]; + + randomValue -= m_Veins[i].VeinChance; + } + + return null; + } + + public HarvestDefinition() + { + m_BanksByMap = new Hashtable(); + } + + public bool Validate( int tileID ) + { + if ( m_RangedTiles ) + { + bool contains = false; + + for ( int i = 0; !contains && i < m_Tiles.Length; i += 2 ) + contains = ( tileID >= m_Tiles[i] && tileID <= m_Tiles[i + 1] ); + + return contains; + } + else + { + int dist = -1; + + for ( int i = 0; dist < 0 && i < m_Tiles.Length; ++i ) + dist = ( m_Tiles[i] - tileID ); + + return ( dist == 0 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestResource.cs b/Scripts/Engines/Harvest/Core/HarvestResource.cs new file mode 100644 index 000000000..05ff7d453 --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestResource.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Engines.Harvest +{ + public class HarvestResource + { + private Type[] m_Types; + private double m_ReqSkill, m_MinSkill, m_MaxSkill; + private object m_SuccessMessage; + + public Type[] Types{ get{ return m_Types; } set{ m_Types = value; } } + public double ReqSkill{ get{ return m_ReqSkill; } set{ m_ReqSkill = value; } } + public double MinSkill{ get{ return m_MinSkill; } set{ m_MinSkill = value; } } + public double MaxSkill{ get{ return m_MaxSkill; } set{ m_MaxSkill = value; } } + public object SuccessMessage{ get{ return m_SuccessMessage; } } + + public void SendSuccessTo( Mobile m ) + { + if ( m_SuccessMessage is int ) + m.SendLocalizedMessage( (int)m_SuccessMessage ); + else if ( m_SuccessMessage is string ) + m.SendMessage( (string)m_SuccessMessage ); + } + + public HarvestResource( double reqSkill, double minSkill, double maxSkill, object message, params Type[] types ) + { + m_ReqSkill = reqSkill; + m_MinSkill = minSkill; + m_MaxSkill = maxSkill; + m_Types = types; + m_SuccessMessage = message; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestSoundTimer.cs b/Scripts/Engines/Harvest/Core/HarvestSoundTimer.cs new file mode 100644 index 000000000..ddb6af8ac --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestSoundTimer.cs @@ -0,0 +1,33 @@ +using System; + +namespace Server.Engines.Harvest +{ + public class HarvestSoundTimer : Timer + { + private Mobile m_From; + private Item m_Tool; + private HarvestSystem m_System; + private HarvestDefinition m_Definition; + private object m_ToHarvest, m_Locked; + private bool m_Last; + + public HarvestSoundTimer( Mobile from, Item tool, HarvestSystem system, HarvestDefinition def, object toHarvest, object locked, bool last ) : base( def.EffectSoundDelay ) + { + m_From = from; + m_Tool = tool; + m_System = system; + m_Definition = def; + m_ToHarvest = toHarvest; + m_Locked = locked; + m_Last = last; + } + + protected override void OnTick() + { + m_System.DoHarvestingSound( m_From, m_Tool, m_Definition, m_ToHarvest ); + + if ( m_Last ) + m_System.FinishHarvesting( m_From, m_Tool, m_Definition, m_ToHarvest, m_Locked ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestSystem.cs b/Scripts/Engines/Harvest/Core/HarvestSystem.cs new file mode 100644 index 000000000..f3f39a24c --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestSystem.cs @@ -0,0 +1,473 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Targeting; + +namespace Server.Engines.Harvest +{ + public abstract class HarvestSystem + { + private ArrayList m_Definitions; + + public ArrayList Definitions{ get{ return m_Definitions; } } + + public HarvestSystem() + { + m_Definitions = new ArrayList(); + } + + public virtual bool CheckTool( Mobile from, Item tool ) + { + bool wornOut = ( tool == null || tool.Deleted || (tool is IUsesRemaining && ((IUsesRemaining)tool).UsesRemaining <= 0) ); + + if ( wornOut ) + from.SendLocalizedMessage( 1044038 ); // You have worn out your tool! + + return !wornOut; + } + + public virtual bool CheckHarvest( Mobile from, Item tool ) + { + return CheckTool( from, tool ); + } + + public virtual bool CheckHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + return CheckTool( from, tool ); + } + + public virtual bool CheckRange( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed ) + { + bool inRange = ( from.Map == map && from.InRange( loc, def.MaxRange ) ); + + if ( !inRange ) + def.SendMessageTo( from, timed ? def.TimedOutOfRangeMessage : def.OutOfRangeMessage ); + + return inRange; + } + + public virtual bool CheckResources( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed ) + { + HarvestBank bank = def.GetBank( map, loc.X, loc.Y ); + bool available = ( bank != null && bank.Current >= def.ConsumedPerHarvest ); + + if ( !available ) + def.SendMessageTo( from, timed ? def.DoubleHarvestMessage : def.NoResourcesMessage ); + + return available; + } + + public virtual void OnBadHarvestTarget( Mobile from, Item tool, object toHarvest ) + { + } + + public virtual object GetLock( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + /* Here we prevent multiple harvesting. + * + * Some options: + * - 'return tool;' : This will allow the player to harvest more than once concurrently, but only if they use multiple tools. This seems to be as OSI. + * - 'return GetType();' : This will disallow multiple harvesting of the same type. That is, we couldn't mine more than once concurrently, but we could be both mining and lumberjacking. + * - 'return typeof( HarvestSystem );' : This will completely restrict concurrent harvesting. + */ + + return tool; + } + + public virtual void OnConcurrentHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + } + + public virtual void OnHarvestStarted( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + } + + public virtual bool BeginHarvesting( Mobile from, Item tool ) + { + if ( !CheckHarvest( from, tool ) ) + return false; + + from.Target = new HarvestTarget( tool, this ); + return true; + } + + public virtual void FinishHarvesting( Mobile from, Item tool, HarvestDefinition def, object toHarvest, object locked ) + { + from.EndAction( locked ); + + if ( !CheckHarvest( from, tool ) ) + return; + + int tileID; + Map map; + Point3D loc; + + if ( !GetHarvestDetails( from, tool, toHarvest, out tileID, out map, out loc ) ) + { + OnBadHarvestTarget( from, tool, toHarvest ); + return; + } + else if ( !def.Validate( tileID ) ) + { + OnBadHarvestTarget( from, tool, toHarvest ); + return; + } + + if ( !CheckRange( from, tool, def, map, loc, true ) ) + return; + else if ( !CheckResources( from, tool, def, map, loc, true ) ) + return; + else if ( !CheckHarvest( from, tool, def, toHarvest ) ) + return; + + if ( SpecialHarvest( from, tool, def, map, loc ) ) + return; + + HarvestBank bank = def.GetBank( map, loc.X, loc.Y ); + + if ( bank == null ) + return; + + HarvestVein vein = bank.Vein; + + if ( vein != null ) + vein = MutateVein( from, tool, def, bank, toHarvest, vein ); + + if ( vein == null ) + return; + + HarvestResource primary = vein.PrimaryResource; + HarvestResource fallback = vein.FallbackResource; + HarvestResource resource = MutateResource( from, tool, def, map, loc, vein, primary, fallback ); + + double skillBase = from.Skills[def.Skill].Base; + double skillValue = from.Skills[def.Skill].Value; + + Type type = null; + + if ( skillBase >= resource.ReqSkill && from.CheckSkill( def.Skill, resource.MinSkill, resource.MaxSkill ) ) + { + type = GetResourceType( from, tool, def, map, loc, resource ); + + if ( type != null ) + type = MutateType( type, from, tool, def, map, loc, resource ); + + if ( type != null ) + { + Item item = Construct( type, from ); + + if ( item == null ) + { + type = null; + } + else + { + if ( item.Stackable ) + { + if ( map == Map.Felucca && bank.Current >= def.ConsumedPerFeluccaHarvest ) + item.Amount = def.ConsumedPerFeluccaHarvest; + else + item.Amount = def.ConsumedPerHarvest; + } + + bank.Consume( def, item.Amount ); + + if ( Give( from, item, def.PlaceAtFeetIfFull ) ) + { + SendSuccessTo( from, item, resource ); + } + else + { + SendPackFullTo( from, item, def, resource ); + item.Delete(); + } + + if ( tool is IUsesRemaining ) + { + IUsesRemaining toolWithUses = (IUsesRemaining)tool; + + toolWithUses.ShowUsesRemaining = true; + + if ( toolWithUses.UsesRemaining > 0 ) + --toolWithUses.UsesRemaining; + + if ( toolWithUses.UsesRemaining < 1 ) + { + tool.Delete(); + def.SendMessageTo( from, def.ToolBrokeMessage ); + } + } + } + } + } + + if ( type == null ) + def.SendMessageTo( from, def.FailMessage ); + + OnHarvestFinished( from, tool, def, vein, bank, resource, toHarvest ); + } + + public virtual void OnHarvestFinished( Mobile from, Item tool, HarvestDefinition def, HarvestVein vein, HarvestBank bank, HarvestResource resource, object harvested ) + { + } + + public virtual bool SpecialHarvest( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc ) + { + return false; + } + + public virtual Item Construct( Type type, Mobile from ) + { + try{ return Activator.CreateInstance( type ) as Item; } + catch{ return null; } + } + + public virtual HarvestVein MutateVein( Mobile from, Item tool, HarvestDefinition def, HarvestBank bank, object toHarvest, HarvestVein vein ) + { + return vein; + } + + public virtual void SendSuccessTo( Mobile from, Item item, HarvestResource resource ) + { + resource.SendSuccessTo( from ); + } + + public virtual void SendPackFullTo( Mobile from, Item item, HarvestDefinition def, HarvestResource resource ) + { + def.SendMessageTo( from, def.PackFullMessage ); + } + + public virtual bool Give( Mobile m, Item item, bool placeAtFeet ) + { + if ( m.PlaceInBackpack( item ) ) + return true; + + if ( !placeAtFeet ) + return false; + + Map map = m.Map; + + if ( map == null ) + return false; + + ArrayList atFeet = new ArrayList(); + + foreach ( Item obj in m.GetItemsInRange( 0 ) ) + atFeet.Add( obj ); + + for ( int i = 0; i < atFeet.Count; ++i ) + { + Item check = (Item)atFeet[i]; + + if ( check.StackWith( m, item, false ) ) + return true; + } + + item.MoveToWorld( m.Location, map ); + return true; + } + + public virtual Type MutateType( Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource ) + { + return from.Region.GetResource( type ); + } + + public virtual Type GetResourceType( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource ) + { + if ( resource.Types.Length > 0 ) + return resource.Types[Utility.Random( resource.Types.Length )]; + + return null; + } + + public virtual HarvestResource MutateResource( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestVein vein, HarvestResource primary, HarvestResource fallback ) + { + if ( vein.ChanceToFallback > Utility.RandomDouble() ) + return fallback; + + double skillValue = from.Skills[def.Skill].Value; + + if ( fallback != null && (skillValue < primary.ReqSkill || skillValue < primary.MinSkill) ) + return fallback; + + return primary; + } + + public virtual bool OnHarvesting( Mobile from, Item tool, HarvestDefinition def, object toHarvest, object locked, bool last ) + { + if ( !CheckHarvest( from, tool ) ) + { + from.EndAction( locked ); + return false; + } + + int tileID; + Map map; + Point3D loc; + + if ( !GetHarvestDetails( from, tool, toHarvest, out tileID, out map, out loc ) ) + { + from.EndAction( locked ); + OnBadHarvestTarget( from, tool, toHarvest ); + return false; + } + else if ( !def.Validate( tileID ) ) + { + from.EndAction( locked ); + OnBadHarvestTarget( from, tool, toHarvest ); + return false; + } + else if ( !CheckRange( from, tool, def, map, loc, true ) ) + { + from.EndAction( locked ); + return false; + } + else if ( !CheckResources( from, tool, def, map, loc, true ) ) + { + from.EndAction( locked ); + return false; + } + else if ( !CheckHarvest( from, tool, def, toHarvest ) ) + { + from.EndAction( locked ); + return false; + } + + DoHarvestingEffect( from, tool, def, map, loc ); + + new HarvestSoundTimer( from, tool, this, def, toHarvest, locked, last ).Start(); + + return !last; + } + + public virtual void DoHarvestingSound( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + if ( def.EffectSounds.Length > 0 ) + from.PlaySound( Utility.RandomList( def.EffectSounds ) ); + } + + public virtual void DoHarvestingEffect( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc ) + { + from.Direction = from.GetDirectionTo( loc ); + + if ( !from.Mounted ) + from.Animate( Utility.RandomList( def.EffectActions ), 5, 1, true, false, 0 ); + } + + public virtual HarvestDefinition GetDefinition( int tileID ) + { + HarvestDefinition def = null; + + for ( int i = 0; def == null && i < m_Definitions.Count; ++i ) + { + HarvestDefinition check = (HarvestDefinition)m_Definitions[i]; + + if ( check.Validate( tileID ) ) + def = check; + } + + return def; + } + + public virtual void StartHarvesting( Mobile from, Item tool, object toHarvest ) + { + if ( !CheckHarvest( from, tool ) ) + return; + + int tileID; + Map map; + Point3D loc; + + if ( !GetHarvestDetails( from, tool, toHarvest, out tileID, out map, out loc ) ) + { + OnBadHarvestTarget( from, tool, toHarvest ); + return; + } + + HarvestDefinition def = GetDefinition( tileID ); + + if ( def == null ) + { + OnBadHarvestTarget( from, tool, toHarvest ); + return; + } + + if ( !CheckRange( from, tool, def, map, loc, false ) ) + return; + else if ( !CheckResources( from, tool, def, map, loc, false ) ) + return; + else if ( !CheckHarvest( from, tool, def, toHarvest ) ) + return; + + object toLock = GetLock( from, tool, def, toHarvest ); + + if ( !from.BeginAction( toLock ) ) + { + OnConcurrentHarvest( from, tool, def, toHarvest ); + return; + } + + new HarvestTimer( from, tool, this, def, toHarvest, toLock ).Start(); + OnHarvestStarted( from, tool, def, toHarvest ); + } + + public virtual bool GetHarvestDetails( Mobile from, Item tool, object toHarvest, out int tileID, out Map map, out Point3D loc ) + { + if ( toHarvest is Static && !((Static)toHarvest).Movable ) + { + Static obj = (Static)toHarvest; + + tileID = (obj.ItemID & 0x3FFF) | 0x4000; + map = obj.Map; + loc = obj.GetWorldLocation(); + } + else if ( toHarvest is StaticTarget ) + { + StaticTarget obj = (StaticTarget)toHarvest; + + tileID = (obj.ItemID & 0x3FFF) | 0x4000; + map = from.Map; + loc = obj.Location; + } + else if ( toHarvest is LandTarget ) + { + LandTarget obj = (LandTarget)toHarvest; + + tileID = obj.TileID & 0x3FFF; + map = from.Map; + loc = obj.Location; + } + else + { + tileID = 0; + map = null; + loc = Point3D.Zero; + return false; + } + + return ( map != null && map != Map.Internal ); + } + } +} + +namespace Server +{ + public interface IChopable + { + void OnChop( Mobile from ); + } + + [AttributeUsage( AttributeTargets.Class )] + public class FurnitureAttribute : Attribute + { + public static bool Check( Item item ) + { + return ( item != null && item.GetType().IsDefined( typeof( FurnitureAttribute ), false ) ); + } + + public FurnitureAttribute() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestTarget.cs b/Scripts/Engines/Harvest/Core/HarvestTarget.cs new file mode 100644 index 000000000..24e6fce1e --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestTarget.cs @@ -0,0 +1,95 @@ +using System; +using Server; +using Server.Items; +using Server.Targeting; +using Server.Multis; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Hag; + +namespace Server.Engines.Harvest +{ + public class HarvestTarget : Target + { + private Item m_Tool; + private HarvestSystem m_System; + + public HarvestTarget( Item tool, HarvestSystem system ) : base( -1, true, TargetFlags.None ) + { + m_Tool = tool; + m_System = system; + + DisallowMultis = true; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_System is Mining && targeted is StaticTarget ) + { + int itemID = ((StaticTarget)targeted).ItemID; + + // grave + if ( itemID == 0xED3 || itemID == 0xEDF || itemID == 0xEE0 || itemID == 0xEE1 || itemID == 0xEE2 || itemID == 0xEE8 ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindIngredientObjective obj = qs.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null && !obj.Completed && obj.Ingredient == Ingredient.Bones ) + { + player.SendLocalizedMessage( 1055037 ); // You finish your grim work, finding some of the specific bones listed in the Hag's recipe. + obj.Complete(); + + return; + } + } + } + } + } + + if ( m_System is Lumberjacking && targeted is IChopable ) + ((IChopable)targeted).OnChop( from ); + else if ( m_System is Lumberjacking && FurnitureAttribute.Check( targeted as Item ) ) + DestroyFurniture( from, (Item)targeted ); + else if ( m_System is Mining && targeted is TreasureMap ) + ((TreasureMap)targeted).OnBeginDig( from ); + else + m_System.StartHarvesting( from, m_Tool, targeted ); + } + + private void DestroyFurniture( Mobile from, Item item ) + { + if ( !from.InRange( item.GetWorldLocation(), 3 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + return; + } + else if ( !item.IsChildOf( from.Backpack ) && !item.Movable ) + { + from.SendLocalizedMessage( 500462 ); // You can't destroy that while it is here. + return; + } + + from.SendLocalizedMessage( 500461 ); // You destroy the item. + Effects.PlaySound( item.GetWorldLocation(), item.Map, 0x3B3 ); + + if ( item is Container ) + { + if ( item is TrapableContainer ) + (item as TrapableContainer).ExecuteTrap( from ); + + ((Container)item).Destroy(); + } + else + { + item.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestTimer.cs b/Scripts/Engines/Harvest/Core/HarvestTimer.cs new file mode 100644 index 000000000..a91c6f7f0 --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestTimer.cs @@ -0,0 +1,31 @@ +using System; + +namespace Server.Engines.Harvest +{ + public class HarvestTimer : Timer + { + private Mobile m_From; + private Item m_Tool; + private HarvestSystem m_System; + private HarvestDefinition m_Definition; + private object m_ToHarvest, m_Locked; + private int m_Index, m_Count; + + public HarvestTimer( Mobile from, Item tool, HarvestSystem system, HarvestDefinition def, object toHarvest, object locked ) : base( TimeSpan.Zero, def.EffectDelay ) + { + m_From = from; + m_Tool = tool; + m_System = system; + m_Definition = def; + m_ToHarvest = toHarvest; + m_Locked = locked; + m_Count = Utility.RandomList( def.EffectCounts ); + } + + protected override void OnTick() + { + if ( !m_System.OnHarvesting( m_From, m_Tool, m_Definition, m_ToHarvest, m_Locked, ++m_Index == m_Count ) ) + Stop(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Core/HarvestVein.cs b/Scripts/Engines/Harvest/Core/HarvestVein.cs new file mode 100644 index 000000000..c793657ae --- /dev/null +++ b/Scripts/Engines/Harvest/Core/HarvestVein.cs @@ -0,0 +1,25 @@ +using System; + +namespace Server.Engines.Harvest +{ + public class HarvestVein + { + private double m_VeinChance; + private double m_ChanceToFallback; + private HarvestResource m_PrimaryResource; + private HarvestResource m_FallbackResource; + + public double VeinChance{ get{ return m_VeinChance; } set{ m_VeinChance = value; } } + public double ChanceToFallback{ get{ return m_ChanceToFallback; } set{ m_ChanceToFallback = value; } } + public HarvestResource PrimaryResource{ get{ return m_PrimaryResource; } set{ m_PrimaryResource = value; } } + public HarvestResource FallbackResource{ get{ return m_FallbackResource; } set{ m_FallbackResource = value; } } + + public HarvestVein( double veinChance, double chanceToFallback, HarvestResource primaryResource, HarvestResource fallbackResource ) + { + m_VeinChance = veinChance; + m_ChanceToFallback = chanceToFallback; + m_PrimaryResource = primaryResource; + m_FallbackResource = fallbackResource; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Fishing.cs b/Scripts/Engines/Harvest/Fishing.cs new file mode 100644 index 000000000..7db60f7d5 --- /dev/null +++ b/Scripts/Engines/Harvest/Fishing.cs @@ -0,0 +1,530 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Collector; + +namespace Server.Engines.Harvest +{ + public class Fishing : HarvestSystem + { + private static Fishing m_System; + + public static Fishing System + { + get + { + if ( m_System == null ) + m_System = new Fishing(); + + return m_System; + } + } + + private HarvestDefinition m_Definition; + + public HarvestDefinition Definition + { + get{ return m_Definition; } + } + + private Fishing() + { + HarvestResource[] res; + HarvestVein[] veins; + + #region Fishing + HarvestDefinition fish = new HarvestDefinition(); + + // Resource banks are every 8x8 tiles + fish.BankWidth = 8; + fish.BankHeight = 8; + + // Every bank holds from 5 to 15 fish + fish.MinTotal = 5; + fish.MaxTotal = 15; + + // A resource bank will respawn its content every 10 to 20 minutes + fish.MinRespawn = TimeSpan.FromMinutes( 10.0 ); + fish.MaxRespawn = TimeSpan.FromMinutes( 20.0 ); + + // Skill checking is done on the Fishing skill + fish.Skill = SkillName.Fishing; + + // Set the list of harvestable tiles + fish.Tiles = m_WaterTiles; + fish.RangedTiles = true; + + // Players must be within 4 tiles to harvest + fish.MaxRange = 4; + + // One fish per harvest action + fish.ConsumedPerHarvest = 1; + fish.ConsumedPerFeluccaHarvest = 1; + + // The fishing + fish.EffectActions = new int[]{ 12 }; + fish.EffectSounds = new int[0]; + fish.EffectCounts = new int[]{ 1 }; + fish.EffectDelay = TimeSpan.Zero; + fish.EffectSoundDelay = TimeSpan.FromSeconds( 8.0 ); + + fish.NoResourcesMessage = 503172; // The fish don't seem to be biting here. + fish.FailMessage = 503171; // You fish a while, but fail to catch anything. + fish.TimedOutOfRangeMessage = 500976; // You need to be closer to the water to fish! + fish.OutOfRangeMessage = 500976; // You need to be closer to the water to fish! + fish.PackFullMessage = 503176; // You do not have room in your backpack for a fish. + fish.ToolBrokeMessage = 503174; // You broke your fishing pole. + + res = new HarvestResource[] + { + new HarvestResource( 00.0, 00.0, 100.0, 1043297, typeof( Fish ) ) + }; + + veins = new HarvestVein[] + { + new HarvestVein( 100.0, 0.0, res[0], null ) + }; + + fish.Resources = res; + fish.Veins = veins; + + m_Definition = fish; + Definitions.Add( fish ); + #endregion + } + + public override void OnConcurrentHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + from.SendLocalizedMessage( 500972 ); // You are already fishing. + } + + private class MutateEntry + { + public double m_ReqSkill, m_MinSkill, m_MaxSkill; + public bool m_DeepWater; + public Type[] m_Types; + + public MutateEntry( double reqSkill, double minSkill, double maxSkill, bool deepWater, params Type[] types ) + { + m_ReqSkill = reqSkill; + m_MinSkill = minSkill; + m_MaxSkill = maxSkill; + m_DeepWater = deepWater; + m_Types = types; + } + } + + private static MutateEntry[] m_MutateTable = new MutateEntry[] + { + new MutateEntry( 80.0, 80.0, 4080.0, true, typeof( SpecialFishingNet ) ), + new MutateEntry( 80.0, 80.0, 4080.0, true, typeof( BigFish ) ), + new MutateEntry( 90.0, 80.0, 4080.0, true, typeof( TreasureMap ) ), + new MutateEntry( 100.0, 80.0, 4080.0, true, typeof( MessageInABottle ) ), + new MutateEntry( 0.0, 125.0, -2375.0, false, typeof( PrizedFish ), typeof( WondrousFish ), typeof( TrulyRareFish ), typeof( PeculiarFish ) ), + new MutateEntry( 0.0, 105.0, -420.0, false, typeof( Boots ), typeof( Shoes ), typeof( Sandals ), typeof( ThighBoots ) ), + new MutateEntry( 0.0, 200.0, -200.0, false, new Type[1]{ null } ) + }; + + public override bool SpecialHarvest( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FishPearlsObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( Utility.RandomDouble() < 0.5 ) + { + player.SendLocalizedMessage( 1055086, "", 0x59 ); // You pull a shellfish out of the water, and find a rainbow pearl inside of it. + + obj.CurProgress++; + } + else + { + player.SendLocalizedMessage( 1055087, "", 0x2C ); // You pull a shellfish out of the water, but it doesn't have a rainbow pearl. + } + + return true; + } + } + } + + return false; + } + + public override Type MutateType( Type type, Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource ) + { + bool deepWater = SpecialFishingNet.FullValidation( map, loc.X, loc.Y ); + + double skillBase = from.Skills[SkillName.Fishing].Base; + double skillValue = from.Skills[SkillName.Fishing].Value; + + for ( int i = 0; i < m_MutateTable.Length; ++i ) + { + MutateEntry entry = m_MutateTable[i]; + + if ( !deepWater && entry.m_DeepWater ) + continue; + + if ( skillBase >= entry.m_ReqSkill ) + { + double chance = (skillValue - entry.m_MinSkill) / (entry.m_MaxSkill - entry.m_MinSkill); + + if ( chance > Utility.RandomDouble() ) + return entry.m_Types[Utility.Random( entry.m_Types.Length )]; + } + } + + return type; + } + + private static Map SafeMap( Map map ) + { + if ( map == null || map == Map.Internal ) + return Map.Trammel; + + return map; + } + + public override bool CheckResources( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, bool timed ) + { + Container pack = from.Backpack; + + if ( pack != null ) + { + Item[] messages = pack.FindItemsByType( typeof( SOS ) ); + + for ( int i = 0; i < messages.Length; ++i ) + { + SOS sos = (SOS)messages[i]; + + if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) ) + return true; + } + } + + return base.CheckResources( from, tool, def, map, loc, timed ); + } + + public override Item Construct( Type type, Mobile from ) + { + if ( type == typeof( TreasureMap ) ) + { + int level; + if ( from is PlayerMobile && ((PlayerMobile)from).Young && from.Map == Map.Trammel && TreasureMap.IsInHavenIsland( from ) ) + level = 0; + else + level = 1; + + return new TreasureMap( level, from.Map == Map.Felucca ? Map.Felucca : Map.Trammel ); + } + else if ( type == typeof( MessageInABottle ) ) + { + return new MessageInABottle( SafeMap( from.Map ) ); + } + + Container pack = from.Backpack; + + if ( pack != null ) + { + Item[] messages = pack.FindItemsByType( typeof( SOS ) ); + + for ( int i = 0; i < messages.Length; ++i ) + { + SOS sos = (SOS)messages[i]; + + if ( from.Map == sos.TargetMap && from.InRange( sos.TargetLocation, 60 ) ) + { + Item preLoot = null; + + switch ( Utility.Random( 7 ) ) + { + case 0: // Body parts + { + int[] list = new int[] + { + 0x1CDD, 0x1CE5, // arm + 0x1CE0, 0x1CE8, // torso + 0x1CE1, 0x1CE9, // head + 0x1CE2, 0x1CEC // leg + }; + + preLoot = new ShipwreckedItem( Utility.RandomList( list ) ); + break; + } + case 1: // Bone parts + { + int[] list = new int[] + { + 0x1AE0, 0x1AE1, 0x1AE2, 0x1AE3, 0x1AE4, // skulls + 0x1B09, 0x1B0A, 0x1B0B, 0x1B0C, 0x1B0D, 0x1B0E, 0x1B0F, 0x1B10, // bone piles + 0x1B15, 0x1B16 // pelvis bones + }; + + preLoot = new ShipwreckedItem( Utility.RandomList( list ) ); + break; + } + case 2: // Paintings and portraits + { + preLoot = new ShipwreckedItem( Utility.Random( 0xE9F, 10 ) ); + break; + } + case 3: // Pillows + { + preLoot = new ShipwreckedItem( Utility.Random( 0x13A4, 11 ) ); + break; + } + case 4: // Shells + { + preLoot = new ShipwreckedItem( Utility.Random( 0xFC4, 9 ) ); + break; + } + case 5: // Misc + { + int[] list = new int[] + { + 0x1EB5, // unfinished barrel + 0xA2A, // stool + 0xC1F, // broken clock + 0x1047, 0x1048, // globe + 0x1EB1, 0x1EB2, 0x1EB3, 0x1EB4 // barrel staves + }; + + preLoot = new ShipwreckedItem( Utility.RandomList( list ) ); + break; + } + } + + if ( preLoot != null ) + return preLoot; + + LockableContainer chest; + + if ( Utility.RandomBool() ) + chest = new MetalGoldenChest(); + else + chest = new WoodenChest(); + + if ( sos.IsAncient ) + chest.Hue = 0x481; + + TreasureMapChest.Fill( chest, Math.Max( 1, Math.Max( 4, sos.Level ) ) ); + + if ( sos.IsAncient ) + chest.DropItem( new FabledFishingNet() ); + else + chest.DropItem( new SpecialFishingNet() ); + + chest.Movable = true; + chest.Locked = false; + chest.TrapType = TrapType.None; + chest.TrapPower = 0; + chest.TrapLevel = 0; + + sos.Delete(); + + return chest; + } + } + } + + return base.Construct( type, from ); + } + + public override bool Give( Mobile m, Item item, bool placeAtFeet ) + { + if ( item is TreasureMap || item is MessageInABottle || item is SpecialFishingNet ) + { + BaseCreature serp; + + if ( 0.25 > Utility.RandomDouble() ) + serp = new DeepSeaSerpent(); + else + serp = new SeaSerpent(); + + int x = m.X, y = m.Y; + + Map map = m.Map; + + for ( int i = 0; map != null && i < 20; ++i ) + { + int tx = m.X - 10 + Utility.Random( 21 ); + int ty = m.Y - 10 + Utility.Random( 21 ); + + Tile t = map.Tiles.GetLandTile( tx, ty ); + + if ( t.Z == -5 && ( (t.ID >= 0xA8 && t.ID <= 0xAB) || (t.ID >= 0x136 && t.ID <= 0x137) ) && !Spells.SpellHelper.CheckMulti( new Point3D( tx, ty, -5 ), map ) ) + { + x = tx; + y = ty; + break; + } + } + + serp.MoveToWorld( new Point3D( x, y, -5 ), map ); + + serp.Home = serp.Location; + serp.RangeHome = 10; + + serp.PackItem( item ); + + m.SendLocalizedMessage( 503170 ); // Uh oh! That doesn't look like a fish! + + return true; // we don't want to give the item to the player, it's on the serpent + } + + if ( item is BigFish || item is WoodenChest || item is MetalGoldenChest ) + placeAtFeet = true; + + return base.Give( m, item, placeAtFeet ); + } + + public override void SendSuccessTo( Mobile from, Item item, HarvestResource resource ) + { + if ( item is BigFish ) + { + from.SendLocalizedMessage( 1042635 ); // Your fishing pole bends as you pull a big fish from the depths! + + ((BigFish)item).Fisher = from; + } + else if ( item is WoodenChest || item is MetalGoldenChest ) + { + from.SendLocalizedMessage( 503175 ); // You pull up a heavy chest from the depths of the ocean! + } + else + { + int number; + string name; + + if ( item is BaseMagicFish ) + { + number = 1008124; + name = "a mess of small fish"; + } + else if ( item is Fish ) + { + number = 1008124; + name = "a fish"; + } + else if ( item is BaseShoes ) + { + number = 1008124; + name = item.ItemData.Name; + } + else if ( item is TreasureMap ) + { + number = 1008125; + name = "a sodden piece of parchment"; + } + else if ( item is MessageInABottle ) + { + number = 1008125; + name = "a bottle, with a message in it"; + } + else if ( item is SpecialFishingNet ) + { + number = 1008125; + name = "a special fishing net"; // TODO: this is just a guess--what should it really be named? + } + else + { + number = 1043297; + + if ( (item.ItemData.Flags & TileFlag.ArticleA) != 0 ) + name = "a " + item.ItemData.Name; + else if ( (item.ItemData.Flags & TileFlag.ArticleAn) != 0 ) + name = "an " + item.ItemData.Name; + else + name = item.ItemData.Name; + } + + if ( number == 1043297 ) + from.SendLocalizedMessage( number, name ); + else + from.SendLocalizedMessage( number, true, name ); + } + } + + public override void OnHarvestStarted( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + base.OnHarvestStarted( from, tool, def, toHarvest ); + + int tileID; + Map map; + Point3D loc; + + if ( GetHarvestDetails( from, tool, toHarvest, out tileID, out map, out loc ) ) + Timer.DelayCall( TimeSpan.FromSeconds( 1.5 ), new TimerStateCallback( Splash_Callback ), new object[]{ loc, map } ); + } + + private void Splash_Callback( object state ) + { + object[] args = (object[])state; + Point3D loc = (Point3D)args[0]; + Map map = (Map)args[1]; + + Effects.SendLocationEffect( loc, map, 0x352D, 16, 4 ); + Effects.PlaySound( loc, map, 0x364 ); + } + + public override object GetLock( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + return this; + } + + public override bool BeginHarvesting( Mobile from, Item tool ) + { + if ( !base.BeginHarvesting( from, tool ) ) + return false; + + from.SendLocalizedMessage( 500974 ); // What water do you want to fish in? + return true; + } + + public override bool CheckHarvest( Mobile from, Item tool ) + { + if ( !base.CheckHarvest( from, tool ) ) + return false; + + if ( from.Mounted ) + { + from.SendLocalizedMessage( 500971 ); // You can't fish while riding! + return false; + } + + return true; + } + + public override bool CheckHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + if ( !base.CheckHarvest( from, tool, def, toHarvest ) ) + return false; + + if ( from.Mounted ) + { + from.SendLocalizedMessage( 500971 ); // You can't fish while riding! + return false; + } + + return true; + } + + private static int[] m_WaterTiles = new int[] + { + 0x00A8, 0x00AB, + 0x0136, 0x0137, + 0x5797, 0x579C, + 0x746E, 0x7485, + 0x7490, 0x74AB, + 0x74B5, 0x75D5 + }; + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Lumberjacking.cs b/Scripts/Engines/Harvest/Lumberjacking.cs new file mode 100644 index 000000000..ba8e2dacc --- /dev/null +++ b/Scripts/Engines/Harvest/Lumberjacking.cs @@ -0,0 +1,158 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.Harvest +{ + public class Lumberjacking : HarvestSystem + { + private static Lumberjacking m_System; + + public static Lumberjacking System + { + get + { + if ( m_System == null ) + m_System = new Lumberjacking(); + + return m_System; + } + } + + private HarvestDefinition m_Definition; + + public HarvestDefinition Definition + { + get{ return m_Definition; } + } + + private Lumberjacking() + { + HarvestResource[] res; + HarvestVein[] veins; + + #region Lumberjacking + HarvestDefinition lumber = new HarvestDefinition(); + + // Resource banks are every 4x3 tiles + lumber.BankWidth = 4; + lumber.BankHeight = 3; + + // Every bank holds from 20 to 45 logs + lumber.MinTotal = 20; + lumber.MaxTotal = 45; + + // A resource bank will respawn its content every 20 to 30 minutes + lumber.MinRespawn = TimeSpan.FromMinutes( 20.0 ); + lumber.MaxRespawn = TimeSpan.FromMinutes( 30.0 ); + + // Skill checking is done on the Lumberjacking skill + lumber.Skill = SkillName.Lumberjacking; + + // Set the list of harvestable tiles + lumber.Tiles = m_TreeTiles; + + // Players must be within 2 tiles to harvest + lumber.MaxRange = 2; + + // Ten logs per harvest action + lumber.ConsumedPerHarvest = 10; + lumber.ConsumedPerFeluccaHarvest = 20; + + // The chopping effect + lumber.EffectActions = new int[]{ 13 }; + lumber.EffectSounds = new int[]{ 0x13E }; + lumber.EffectCounts = new int[]{ 1, 2, 2, 2, 3 }; + lumber.EffectDelay = TimeSpan.FromSeconds( 1.6 ); + lumber.EffectSoundDelay = TimeSpan.FromSeconds( 0.9 ); + + lumber.NoResourcesMessage = 500493; // There's not enough wood here to harvest. + lumber.FailMessage = 500495; // You hack at the tree for a while, but fail to produce any useable wood. + lumber.OutOfRangeMessage = 500446; // That is too far away. + lumber.PackFullMessage = 500497; // You can't place any wood into your backpack! + lumber.ToolBrokeMessage = 500499; // You broke your axe. + + res = new HarvestResource[] + { + new HarvestResource( 00.0, 00.0, 100.0, 500498, typeof( Log ) ) + }; + + veins = new HarvestVein[] + { + new HarvestVein( 100.0, 0.0, res[0], null ) + }; + + lumber.Resources = res; + lumber.Veins = veins; + + m_Definition = lumber; + Definitions.Add( lumber ); + #endregion + } + + public override bool CheckHarvest( Mobile from, Item tool ) + { + if ( !base.CheckHarvest( from, tool ) ) + return false; + + if ( tool.Parent != from ) + { + from.SendLocalizedMessage( 500487 ); // The axe must be equipped for any serious wood chopping. + return false; + } + + return true; + } + + public override bool CheckHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + if ( !base.CheckHarvest( from, tool, def, toHarvest ) ) + return false; + + if ( tool.Parent != from ) + { + from.SendLocalizedMessage( 500487 ); // The axe must be equipped for any serious wood chopping. + return false; + } + + return true; + } + + public override void OnBadHarvestTarget( Mobile from, Item tool, object toHarvest ) + { + from.SendLocalizedMessage( 500489 ); // You can't use an axe on that. + } + + public static void Initialize() + { + Array.Sort( m_TreeTiles ); + } + + #region Tile lists + private static int[] m_TreeTiles = new int[] + { + 0x4CCA, 0x4CCB, 0x4CCC, 0x4CCD, 0x4CD0, 0x4CD3, 0x4CD6, 0x4CD8, + 0x4CDA, 0x4CDD, 0x4CE0, 0x4CE3, 0x4CE6, 0x4CF8, 0x4CFB, 0x4CFE, + 0x4D01, 0x4D41, 0x4D42, 0x4D43, 0x4D44, 0x4D57, 0x4D58, 0x4D59, + 0x4D5A, 0x4D5B, 0x4D6E, 0x4D6F, 0x4D70, 0x4D71, 0x4D72, 0x4D84, + 0x4D85, 0x4D86, 0x52B5, 0x52B6, 0x52B7, 0x52B8, 0x52B9, 0x52BA, + 0x52BB, 0x52BC, 0x52BD, + + 0x4CCE, 0x4CCF, 0x4CD1, 0x4CD2, 0x4CD4, 0x4CD5, 0x4CD7, 0x4CD9, + 0x4CDB, 0x4CDC, 0x4CDE, 0x4CDF, 0x4CE1, 0x4CE2, 0x4CE4, 0x4CE5, + 0x4CE7, 0x4CE8, 0x4CF9, 0x4CFA, 0x4CFC, 0x4CFD, 0x4CFF, 0x4D00, + 0x4D02, 0x4D03, 0x4D45, 0x4D46, 0x4D47, 0x4D48, 0x4D49, 0x4D4A, + 0x4D4B, 0x4D4C, 0x4D4D, 0x4D4E, 0x4D4F, 0x4D50, 0x4D51, 0x4D52, + 0x4D53, 0x4D5C, 0x4D5D, 0x4D5E, 0x4D5F, 0x4D60, 0x4D61, 0x4D62, + 0x4D63, 0x4D64, 0x4D65, 0x4D66, 0x4D67, 0x4D68, 0x4D69, 0x4D73, + 0x4D74, 0x4D75, 0x4D76, 0x4D77, 0x4D78, 0x4D79, 0x4D7A, 0x4D7B, + 0x4D7C, 0x4D7D, 0x4D7E, 0x4D7F, 0x4D87, 0x4D88, 0x4D89, 0x4D8A, + 0x4D8B, 0x4D8C, 0x4D8D, 0x4D8E, 0x4D8F, 0x4D90, 0x4D95, 0x4D96, + 0x4D97, 0x4D99, 0x4D9A, 0x4D9B, 0x4D9D, 0x4D9E, 0x4D9F, 0x4DA1, + 0x4DA2, 0x4DA3, 0x4DA5, 0x4DA6, 0x4DA7, 0x4DA9, 0x4DAA, 0x4DAB, + 0x52BE, 0x52BF, 0x52C0, 0x52C1, 0x52C2, 0x52C3, 0x52C4, 0x52C5, + 0x52C6, 0x52C7 + }; + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Engines/Harvest/Mining.cs b/Scripts/Engines/Harvest/Mining.cs new file mode 100644 index 000000000..1eb358a03 --- /dev/null +++ b/Scripts/Engines/Harvest/Mining.cs @@ -0,0 +1,404 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Engines.Harvest +{ + public class Mining : HarvestSystem + { + private static Mining m_System; + + public static Mining System + { + get + { + if ( m_System == null ) + m_System = new Mining(); + + return m_System; + } + } + + private HarvestDefinition m_OreAndStone, m_Sand; + + public HarvestDefinition OreAndStone + { + get{ return m_OreAndStone; } + } + + public HarvestDefinition Sand + { + get{ return m_Sand; } + } + + private Mining() + { + HarvestResource[] res; + HarvestVein[] veins; + + #region Mining for ore and stone + HarvestDefinition oreAndStone = m_OreAndStone = new HarvestDefinition(); + + // Resource banks are every 8x8 tiles + oreAndStone.BankWidth = 8; + oreAndStone.BankHeight = 8; + + // Every bank holds from 10 to 34 ore + oreAndStone.MinTotal = 10; + oreAndStone.MaxTotal = 34; + + // A resource bank will respawn its content every 10 to 20 minutes + oreAndStone.MinRespawn = TimeSpan.FromMinutes( 10.0 ); + oreAndStone.MaxRespawn = TimeSpan.FromMinutes( 20.0 ); + + // Skill checking is done on the Mining skill + oreAndStone.Skill = SkillName.Mining; + + // Set the list of harvestable tiles + oreAndStone.Tiles = m_MountainAndCaveTiles; + + // Players must be within 2 tiles to harvest + oreAndStone.MaxRange = 2; + + // One ore per harvest action + oreAndStone.ConsumedPerHarvest = 1; + oreAndStone.ConsumedPerFeluccaHarvest = 2; + + // The digging effect + oreAndStone.EffectActions = new int[]{ 11 }; + oreAndStone.EffectSounds = new int[]{ 0x125, 0x126 }; + oreAndStone.EffectCounts = new int[]{ 1 }; + oreAndStone.EffectDelay = TimeSpan.FromSeconds( 1.6 ); + oreAndStone.EffectSoundDelay = TimeSpan.FromSeconds( 0.9 ); + + oreAndStone.NoResourcesMessage = 503040; // There is no metal here to mine. + oreAndStone.DoubleHarvestMessage = 503042; // Someone has gotten to the metal before you. + oreAndStone.TimedOutOfRangeMessage = 503041; // You have moved too far away to continue mining. + oreAndStone.OutOfRangeMessage = 500446; // That is too far away. + oreAndStone.FailMessage = 503043; // You loosen some rocks but fail to find any useable ore. + oreAndStone.PackFullMessage = 1010481; // Your backpack is full, so the ore you mined is lost. + oreAndStone.ToolBrokeMessage = 1044038; // You have worn out your tool! + + res = new HarvestResource[] + { + new HarvestResource( 00.0, 00.0, 100.0, 1007072, typeof( IronOre ), typeof( Granite ) ), + new HarvestResource( 65.0, 25.0, 105.0, 1007073, typeof( DullCopperOre ), typeof( DullCopperGranite ), typeof( DullCopperElemental ) ), + new HarvestResource( 70.0, 30.0, 110.0, 1007074, typeof( ShadowIronOre ), typeof( ShadowIronGranite ), typeof( ShadowIronElemental ) ), + new HarvestResource( 75.0, 35.0, 115.0, 1007075, typeof( CopperOre ), typeof( CopperGranite ), typeof( CopperElemental ) ), + new HarvestResource( 80.0, 40.0, 120.0, 1007076, typeof( BronzeOre ), typeof( BronzeGranite ), typeof( BronzeElemental ) ), + new HarvestResource( 85.0, 45.0, 125.0, 1007077, typeof( GoldOre ), typeof( GoldGranite ), typeof( GoldenElemental ) ), + new HarvestResource( 90.0, 50.0, 130.0, 1007078, typeof( AgapiteOre ), typeof( AgapiteGranite ), typeof( AgapiteElemental ) ), + new HarvestResource( 95.0, 55.0, 135.0, 1007079, typeof( VeriteOre ), typeof( VeriteGranite ), typeof( VeriteElemental ) ), + new HarvestResource( 99.0, 59.0, 139.0, 1007080, typeof( ValoriteOre ), typeof( ValoriteGranite ), typeof( ValoriteElemental ) ) + }; + + veins = new HarvestVein[] + { + new HarvestVein( 49.6, 0.0, res[0], null ), // Iron + new HarvestVein( 11.2, 0.5, res[1], res[0] ), // Dull Copper + new HarvestVein( 09.8, 0.5, res[2], res[0] ), // Shadow Iron + new HarvestVein( 08.4, 0.5, res[3], res[0] ), // Copper + new HarvestVein( 07.0, 0.5, res[4], res[0] ), // Bronze + new HarvestVein( 05.6, 0.5, res[5], res[0] ), // Gold + new HarvestVein( 04.2, 0.5, res[6], res[0] ), // Agapite + new HarvestVein( 02.8, 0.5, res[7], res[0] ), // Verite + new HarvestVein( 01.4, 0.5, res[8], res[0] ) // Valorite + }; + + oreAndStone.Resources = res; + oreAndStone.Veins = veins; + + Definitions.Add( oreAndStone ); + #endregion + + #region Mining for sand + HarvestDefinition sand = m_Sand = new HarvestDefinition(); + + // Resource banks are every 8x8 tiles + sand.BankWidth = 8; + sand.BankHeight = 8; + + // Every bank holds from 6 to 12 sand + sand.MinTotal = 6; + sand.MaxTotal = 12; + + // A resource bank will respawn its content every 10 to 20 minutes + sand.MinRespawn = TimeSpan.FromMinutes( 10.0 ); + sand.MaxRespawn = TimeSpan.FromMinutes( 20.0 ); + + // Skill checking is done on the Mining skill + sand.Skill = SkillName.Mining; + + // Set the list of harvestable tiles + sand.Tiles = m_SandTiles; + + // Players must be within 2 tiles to harvest + sand.MaxRange = 2; + + // One sand per harvest action + sand.ConsumedPerHarvest = 1; + sand.ConsumedPerFeluccaHarvest = 1; + + // The digging effect + sand.EffectActions = new int[]{ 11 }; + sand.EffectSounds = new int[]{ 0x125, 0x126 }; + sand.EffectCounts = new int[]{ 6 }; + sand.EffectDelay = TimeSpan.FromSeconds( 1.6 ); + sand.EffectSoundDelay = TimeSpan.FromSeconds( 0.9 ); + + sand.NoResourcesMessage = 1044629; // There is no sand here to mine. + sand.DoubleHarvestMessage = 1044629; // There is no sand here to mine. + sand.TimedOutOfRangeMessage = 503041; // You have moved too far away to continue mining. + sand.OutOfRangeMessage = 500446; // That is too far away. + sand.FailMessage = 1044630; // You dig for a while but fail to find any of sufficient quality for glassblowing. + sand.PackFullMessage = 1044632; // Your backpack can't hold the sand, and it is lost! + sand.ToolBrokeMessage = 1044038; // You have worn out your tool! + + res = new HarvestResource[] + { + new HarvestResource( 100.0, 70.0, 400.0, 1044631, typeof( Sand ) ) + }; + + veins = new HarvestVein[] + { + new HarvestVein( 100.0, 0.0, res[0], null ) + }; + + sand.Resources = res; + sand.Veins = veins; + + Definitions.Add( sand ); + #endregion + } + + public override Type GetResourceType( Mobile from, Item tool, HarvestDefinition def, Map map, Point3D loc, HarvestResource resource ) + { + if ( def == m_OreAndStone ) + { + if ( from is PlayerMobile && ((PlayerMobile)from).StoneMining && ((PlayerMobile)from).ToggleMiningStone && from.Skills[SkillName.Mining].Base >= 100.0 && 0.1 > Utility.RandomDouble() ) + return resource.Types[1]; + + return resource.Types[0]; + } + + return base.GetResourceType( from, tool, def, map, loc, resource ); + } + + public override bool CheckHarvest( Mobile from, Item tool ) + { + if ( !base.CheckHarvest( from, tool ) ) + return false; + + if ( from.Mounted ) + { + from.SendLocalizedMessage( 501864 ); // You can't mine while riding. + return false; + } + else if ( from.IsBodyMod && !from.Body.IsHuman ) + { + from.SendLocalizedMessage( 501865 ); // You can't mine while polymorphed. + return false; + } + + return true; + } + + public override void SendSuccessTo( Mobile from, Item item, HarvestResource resource ) + { + if ( item is BaseGranite ) + from.SendLocalizedMessage( 1044606 ); // You carefully extract some workable stone from the ore vein! + else + base.SendSuccessTo( from, item, resource ); + } + + public override bool CheckHarvest( Mobile from, Item tool, HarvestDefinition def, object toHarvest ) + { + if ( !base.CheckHarvest( from, tool, def, toHarvest ) ) + return false; + + if ( def == m_Sand && !(from is PlayerMobile && from.Skills[SkillName.Mining].Base >= 100.0 && ((PlayerMobile)from).SandMining) ) + { + OnBadHarvestTarget( from, tool, toHarvest ); + return false; + } + else if ( from.Mounted ) + { + from.SendLocalizedMessage( 501864 ); // You can't mine while riding. + return false; + } + else if ( from.IsBodyMod && !from.Body.IsHuman ) + { + from.SendLocalizedMessage( 501865 ); // You can't mine while polymorphed. + return false; + } + + return true; + } + + public override HarvestVein MutateVein( Mobile from, Item tool, HarvestDefinition def, HarvestBank bank, object toHarvest, HarvestVein vein ) + { + if ( tool is GargoylesPickaxe && def == m_OreAndStone ) + { + int veinIndex = Array.IndexOf( def.Veins, vein ); + + if ( veinIndex >= 0 && veinIndex < (def.Veins.Length - 1) ) + return def.Veins[veinIndex + 1]; + } + + return base.MutateVein( from, tool, def, bank, toHarvest, vein ); + } + + private static int[] m_Offsets = new int[] + { + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1 + }; + + public override void OnHarvestFinished( Mobile from, Item tool, HarvestDefinition def, HarvestVein vein, HarvestBank bank, HarvestResource resource, object harvested ) + { + if ( tool is GargoylesPickaxe && def == m_OreAndStone && 0.1 > Utility.RandomDouble() ) + { + HarvestResource res = vein.PrimaryResource; + + if ( res == resource && res.Types.Length >= 3 ) + { + try + { + Map map = from.Map; + + if ( map == null ) + return; + + BaseCreature spawned = Activator.CreateInstance( res.Types[2], new object[]{ 25 } ) as BaseCreature; + + if ( spawned != null ) + { + int offset = Utility.Random( 8 ) * 2; + + for ( int i = 0; i < m_Offsets.Length; i += 2 ) + { + int x = from.X + m_Offsets[(offset + i) % m_Offsets.Length]; + int y = from.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length]; + + if ( map.CanSpawnMobile( x, y, from.Z ) ) + { + spawned.MoveToWorld( new Point3D( x, y, from.Z ), map ); + spawned.Combatant = from; + return; + } + else + { + int z = map.GetAverageZ( x, y ); + + if ( map.CanSpawnMobile( x, y, z ) ) + { + spawned.MoveToWorld( new Point3D( x, y, z ), map ); + spawned.Combatant = from; + return; + } + } + } + + spawned.MoveToWorld( from.Location, from.Map ); + spawned.Combatant = from; + } + } + catch + { + } + } + } + } + + public override bool BeginHarvesting( Mobile from, Item tool ) + { + if ( !base.BeginHarvesting( from, tool ) ) + return false; + + from.SendLocalizedMessage( 503033 ); // Where do you wish to dig? + return true; + } + + public override void OnBadHarvestTarget( Mobile from, Item tool, object toHarvest ) + { + if ( toHarvest is LandTarget ) + from.SendLocalizedMessage( 501862 ); // You can't mine there. + else + from.SendLocalizedMessage( 501863 ); // You can't mine that. + } + + #region Tile lists + private static int[] m_MountainAndCaveTiles = new int[] + { + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 296, 296, 297, + 321, 322, 323, 324, 467, 468, 469, 470, 471, 472, + 473, 474, 476, 477, 478, 479, 480, 481, 482, 483, + 484, 485, 486, 487, 492, 493, 494, 495, 543, 544, + 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, + 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 579, 581, 582, 583, 584, 585, + 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, + 596, 597, 598, 599, 600, 601, 610, 611, 612, 613, + + 1010, 1741, 1742, 1743, 1744, 1745, 1746, 1747, 1748, 1749, + 1750, 1751, 1752, 1753, 1754, 1755, 1756, 1757, 1771, 1772, + 1773, 1774, 1775, 1776, 1777, 1778, 1779, 1780, 1781, 1782, + 1783, 1784, 1785, 1786, 1787, 1788, 1789, 1790, 1801, 1802, + 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1811, 1812, 1813, + 1814, 1815, 1816, 1817, 1818, 1819, 1820, 1821, 1822, 1823, + 1824, 1831, 1832, 1833, 1834, 1835, 1836, 1837, 1838, 1839, + 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, + 1850, 1851, 1852, 1853, 1854, 1861, 1862, 1863, 1864, 1865, + 1866, 1867, 1868, 1869, 1870, 1871, 1872, 1873, 1874, 1875, + 1876, 1877, 1878, 1879, 1880, 1881, 1882, 1883, 1884, 1981, + 1982, 1983, 1984, 1985, 1986, 1987, 1988, 1989, 1990, 1991, + 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, + 2002, 2003, 2004, 2028, 2029, 2030, 2031, 2032, 2033, 2100, + 2101, 2102, 2103, 2104, 2105, + + 0x453B, 0x453C, 0x453D, 0x453E, 0x453F, 0x4540, 0x4541, + 0x4542, 0x4543, 0x4544, 0x4545, 0x4546, 0x4547, 0x4548, + 0x4549, 0x454A, 0x454B, 0x454C, 0x454D, 0x454E, 0x454F + }; + + private static int[] m_SandTiles = new int[] + { + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 68, 69, 70, 71, 72, 73, 74, 75, + + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 402, 424, 425, 426, + 427, 441, 442, 443, 444, 445, 446, 447, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, + 460, 461, 462, 463, 464, 465, 642, 643, 644, 645, + 650, 651, 652, 653, 654, 655, 656, 657, 821, 822, + 823, 824, 825, 826, 827, 828, 833, 834, 835, 836, + 845, 846, 847, 848, 849, 850, 851, 852, 857, 858, + 859, 860, 951, 952, 953, 954, 955, 956, 957, 958, + 967, 968, 969, 970, + + 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454, 1455, + 1456, 1457, 1458, 1611, 1612, 1613, 1614, 1615, 1616, + 1617, 1618, 1623, 1624, 1625, 1626, 1635, 1636, 1637, + 1638, 1639, 1640, 1641, 1642, 1647, 1648, 1649, 1650 + }; + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/HelpGump.cs b/Scripts/Engines/Help/HelpGump.cs new file mode 100644 index 000000000..6ec040f29 --- /dev/null +++ b/Scripts/Engines/Help/HelpGump.cs @@ -0,0 +1,313 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Menus; +using Server.Menus.Questions; +using Server.Accounting; +using Server.Multis; +using Server.Mobiles; + +namespace Server.Engines.Help +{ + public class ContainedMenu : QuestionMenu + { + private Mobile m_From; + + public ContainedMenu( Mobile from ) : base( "You already have an open help request. We will have someone assist you as soon as possible. What would you like to do?", new string[]{ "Leave my old help request like it is.", "Remove my help request from the queue." } ) + { + m_From = from; + } + + public override void OnCancel( NetState state ) + { + m_From.SendLocalizedMessage( 1005306, "", 0x35 ); // Help request unchanged. + } + + public override void OnResponse( NetState state, int index ) + { + if ( index == 0 ) + { + m_From.SendLocalizedMessage( 1005306, "", 0x35 ); // Help request unchanged. + } + else if ( index == 1 ) + { + PageEntry entry = PageQueue.GetEntry( m_From ); + + if ( entry != null && entry.Handler == null ) + { + m_From.SendLocalizedMessage( 1005307, "", 0x35 ); // Removed help request. + entry.AddResponse( entry.Sender, "[Canceled]" ); + PageQueue.Remove( entry ); + } + else + { + m_From.SendLocalizedMessage( 1005306, "", 0x35 ); // Help request unchanged. + } + } + } + } + + public class HelpGump : Gump + { + public static void Initialize() + { + EventSink.HelpRequest += new HelpRequestEventHandler( EventSink_HelpRequest ); + } + + private static void EventSink_HelpRequest( HelpRequestEventArgs e ) + { + foreach ( Gump g in e.Mobile.NetState.Gumps ) + { + if ( g is HelpGump ) + return; + } + + if ( !PageQueue.CheckAllowedToPage( e.Mobile ) ) + return; + + if ( PageQueue.Contains( e.Mobile ) ) + e.Mobile.SendMenu( new ContainedMenu( e.Mobile ) ); + else + e.Mobile.SendGump( new HelpGump( e.Mobile ) ); + } + + private static bool IsYoung( Mobile m ) + { + if ( m is PlayerMobile ) + return ((PlayerMobile)m).Young; + + return false; + } + + public static bool CheckCombat( Mobile m ) + { + for ( int i = 0; i < m.Aggressed.Count; ++i ) + { + AggressorInfo info = m.Aggressed[i]; + + if ( DateTime.Now - info.LastCombatTime < TimeSpan.FromSeconds( 30.0 ) ) + return true; + } + + return false; + } + + public HelpGump( Mobile from ) : base( 0, 0 ) + { + from.CloseGump( typeof( HelpGump ) ); + + bool isYoung = IsYoung( from ); + + AddBackground( 50, 25, 540, 430, 2600 ); + + AddPage( 0 ); + + AddHtmlLocalized( 150, 50, 360, 40, 1001002, false, false ); //
Ultima Online Help Menu
+ AddButton( 425, 415, 2073, 2072, 0, GumpButtonType.Reply, 0 ); // Close + + AddPage( 1 ); + + if ( isYoung ) + { + AddButton( 80, 75, 5540, 5541, 9, GumpButtonType.Reply, 2 ); + AddHtml( 110, 75, 450, 58, @"Young Player Haven Transport. Select this option if you want to be transported to Haven.", true, true ); + + AddButton( 80, 140, 5540, 5541, 1, GumpButtonType.Reply, 2 ); + AddHtml( 110, 140, 450, 58, @"General question about Ultima Online. Select this option if you have a general gameplay question, need help learning to use a skill, or if you would like to search the UO Knowledge Base.", true, true ); + + AddButton( 80, 205, 5540, 5541, 2, GumpButtonType.Reply, 0 ); + AddHtml( 110, 205, 450, 58, @"My character is physically stuck in the game. This choice only covers cases where your character is physically stuck in a location they cannot move out of. This option will only work two times in 24 hours.", true, true ); + + AddButton( 80, 270, 5540, 5541, 0, GumpButtonType.Page, 3 ); + AddHtml( 110, 270, 450, 58, @"Another player is harassing me. Another player is verbally harassing your character. When you select this option you will be sending a text log to Origin Systems. To see what constitutes harassment please visit http://support.uo.com/gm_9.html.", true, true ); + + AddButton( 80, 335, 5540, 5541, 0, GumpButtonType.Page, 2 ); + AddHtml( 110, 335, 450, 58, @"Other. If you are experiencing a problem in the game that does not fall into one of the other categories or is not addressed on the Support web page (located at http://support.uo.com), please use this option.", true, true ); + } + else + { + AddButton( 80, 90, 5540, 5541, 1, GumpButtonType.Reply, 2 ); + AddHtml( 110, 90, 450, 74, @"General question about Ultima Online. Select this option if you have a general gameplay question, need help learning to use a skill, or if you would like to search the UO Knowledge Base.", true, true ); + + AddButton( 80, 170, 5540, 5541, 2, GumpButtonType.Reply, 0 ); + AddHtml( 110, 170, 450, 74, @"My character is physically stuck in the game. This choice only covers cases where your character is physically stuck in a location they cannot move out of. This option will only work two times in 24 hours.", true, true ); + + AddButton( 80, 250, 5540, 5541, 0, GumpButtonType.Page, 3 ); + AddHtml( 110, 250, 450, 74, @"Another player is harassing me. Another player is verbally harassing your character. When you select this option you will be sending a text log to Origin Systems. To see what constitutes harassment please visit http://support.uo.com/gm_9.html.", true, true ); + + AddButton( 80, 330, 5540, 5541, 0, GumpButtonType.Page, 2 ); + AddHtml( 110, 330, 450, 74, @"Other. If you are experiencing a problem in the game that does not fall into one of the other categories or is not addressed on the Support web page (located at http://support.uo.com), please use this option.", true, true ); + } + + AddPage( 2 ); + + AddButton( 80, 90, 5540, 5541, 3, GumpButtonType.Reply, 0 ); + AddHtml( 110, 90, 450, 74, @"Report a bug or contact Origin. Use this option to launch your web browser and mail in a bug report. Your report will be read by our Quality Assurance Staff. We apologize for not being able to reply to individual reports. ", true, true ); + + AddButton( 80, 170, 5540, 5541, 4, GumpButtonType.Reply, 0 ); + AddHtml( 110, 170, 450, 74, @"Suggestion for the Game. If you'd like to make a suggestion for the game, it should be directed to the Development Team Members who participate in the discussion forums on the UO.Com web site. Choosing this option will take you to the Discussion Forums. ", true, true ); + + AddButton( 80, 250, 5540, 5541, 5, GumpButtonType.Reply, 0 ); + AddHtml( 110, 250, 450, 74, @"Account Management For questions regarding your account such as forgotten passwords, payment options, account activation, and account transfer, please choose this option.", true, true ); + + AddButton( 80, 330, 5540, 5541, 6, GumpButtonType.Reply, 0 ); + AddHtml( 110, 330, 450, 74, @"Other. If you are experiencing a problem in the game that does not fall into one of the other categories or is not addressed on the Support web page (located at http://support.uo.com), and requires in-game assistance, use this option. ", true, true ); + + AddPage( 3 ); + + AddButton( 80, 90, 5540, 5541, 7, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 110, 90, 450, 145, 1062572, true, true ); /*
Another player is harassing me (or Exploiting).

+ * VERBAL HARASSMENT
+ * Use this option when another player is verbally harassing your character. + * Verbal harassment behaviors include but are not limited to, using bad language, threats etc.. + * Before you submit a complaint be sure you understand what constitutes harassment + * – what is verbal harassment? - + * and that you have followed these steps:
+ * 1. You have asked the player to stop and they have continued.
+ * 2. You have tried to remove yourself from the situation.
+ * 3. You have done nothing to instigate or further encourage the harassment.
+ * 4. You have added the player to your ignore list. + * - How do I ignore a player?
+ * 5. You have read and understand Origin’s definition of harassment.
+ * 6. Your account information is up to date. (Including a current email address)
+ * *If these steps have not been taken, GMs may be unable to take action against the offending player.
+ * **A chat log will be review by a GM to assess the validity of this complaint. + * Abuse of this system is a violation of the Rules of Conduct.
+ * EXPLOITING
+ * Use this option to report someone who may be exploiting or cheating. + * – What constitutes an exploit? + */ + + AddButton( 80, 240, 5540, 5541, 8, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 110, 240, 450, 145, 1062573, true, true ); /*
Another player is harassing me using game mechanics.

+ *
+ * PHYSICAL HARASSMENT
+ * Use this option when another player is harassing your character using game mechanics. + * Physical harassment includes but is not limited to luring, Kill Stealing, and any act that causes a players death in Trammel. + * Before you submit a complaint be sure you understand what constitutes harassment + * – what is physical harassment? + * and that you have followed these steps:
+ * 1. You have asked the player to stop and they have continued.
+ * 2. You have tried to remove yourself from the situation.
+ * 3. You have done nothing to instigate or further encourage the harassment.
+ * 4. You have added the player to your ignore list. + * - how do I ignore a player?
+ * 5. You have read and understand Origin’s definition of harassment.
+ * 6. Your account information is up to date. (Including a current email address)
+ * *If these steps have not been taken, GMs may be unable to take action against the offending player.
+ * **This issue will be reviewed by a GM to assess the validity of this complaint. + * Abuse of this system is a violation of the Rules of Conduct. + */ + + AddButton( 150, 390, 5540, 5541, 0, GumpButtonType.Page, 1 ); + AddHtmlLocalized( 180, 390, 335, 40, 1001015, false, false ); // NO - I meant to ask for help with another matter. + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + PageType type = (PageType)(-1); + + switch ( info.ButtonID ) + { + case 0: // Close/Cancel + { + from.SendLocalizedMessage( 501235, "", 0x35 ); // Help request aborted. + + break; + } + case 1: // General question + { + type = PageType.Question; + break; + } + case 2: // Stuck + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house != null && house.IsAosRules ) + { + from.Location = house.BanLocation; + } + else if ( from.Region.IsPartOf( typeof( Server.Regions.Jail ) ) ) + { + from.SendLocalizedMessage( 1041530, "", 0x35 ); // You'll need a better jailbreak plan then that! + } + else if ( Factions.Sigil.ExistsOn( from ) ) + { + from.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( from.CanUseStuckMenu() && from.Region.CanUseStuckMenu( from ) && !CheckCombat( from ) && !from.Frozen && !from.Criminal && (Core.AOS || from.Kills < 5) ) + { + StuckMenu menu = new StuckMenu( from, from, true ); + + menu.BeginClose(); + + from.SendGump( menu ); + } + else + { + type = PageType.Stuck; + } + + break; + } + case 3: // Report bug or contact Origin + { + type = PageType.Bug; + break; + } + case 4: // Game suggestion + { + type = PageType.Suggestion; + break; + } + case 5: // Account management + { + type = PageType.Account; + break; + } + case 6: // Other + { + type = PageType.Other; + break; + } + case 7: // Harassment: verbal/exploit + { + type = PageType.VerbalHarassment; + break; + } + case 8: // Harassment: physical + { + type = PageType.PhysicalHarassment; + break; + } + case 9: // Young player transport + { + if ( IsYoung( from ) ) + { + if ( from.Region.IsPartOf( typeof( Regions.Jail ) ) ) + { + from.SendLocalizedMessage( 1041530, "", 0x35 ); // You'll need a better jailbreak plan then that! + } + else if ( from.Region.IsPartOf( "Haven Island" ) ) + { + from.SendLocalizedMessage( 1041529 ); // You're already in Haven + } + else + { + from.MoveToWorld( new Point3D( 3618, 2587, 0 ), Map.Trammel ); + } + } + + break; + } + } + + if ( type != (PageType)(-1) && PageQueue.CheckAllowedToPage( from ) ) + from.SendGump( new PagePromptGump( from, type ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/PagePrompt.cs b/Scripts/Engines/Help/PagePrompt.cs new file mode 100644 index 000000000..372ab0c86 --- /dev/null +++ b/Scripts/Engines/Help/PagePrompt.cs @@ -0,0 +1,30 @@ +using System; +using Server.Network; +using Server.Prompts; + +namespace Server.Engines.Help +{ + public class PagePrompt : Prompt + { + private PageType m_Type; + + public PagePrompt( PageType type ) + { + m_Type = type; + } + + public override void OnCancel( Mobile from ) + { + from.SendLocalizedMessage( 501235, "", 0x35 ); // Help request aborted. + } + + public override void OnResponse( Mobile from, string text ) + { + from.SendLocalizedMessage( 501234, "", 0x35 ); /* The next available Counselor/Game Master will respond as soon as possible. + * Please check your Journal for messages every few minutes. + */ + + PageQueue.Enqueue( new PageEntry( from, text, m_Type ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/PagePromptGump.cs b/Scripts/Engines/Help/PagePromptGump.cs new file mode 100644 index 000000000..a4e93ad16 --- /dev/null +++ b/Scripts/Engines/Help/PagePromptGump.cs @@ -0,0 +1,61 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Help +{ + public class PagePromptGump : Gump + { + private Mobile m_From; + private PageType m_Type; + + public PagePromptGump( Mobile from, PageType type ) : base( 0, 0 ) + { + m_From = from; + m_Type = type; + + from.CloseGump( typeof( PagePromptGump ) ); + + AddBackground( 50, 50, 540, 350, 2600 ); + + AddPage( 0 ); + + AddHtmlLocalized( 264, 80, 200, 24, 1062524, false, false ); // Enter Description + AddHtmlLocalized( 120, 108, 420, 48, 1062638, false, false ); // Please enter a brief description (up to 200 characters) of your problem: + + AddBackground( 100, 148, 440, 200, 3500 ); + AddTextEntry( 120, 168, 400, 200, 1153, 0, "" ); + + AddButton( 175, 355, 2074, 2075, 1, GumpButtonType.Reply, 0 ); // Okay + AddButton( 405, 355, 2073, 2072, 0, GumpButtonType.Reply, 0 ); // Cancel + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 ) + { + m_From.SendLocalizedMessage( 501235, "", 0x35 ); // Help request aborted. + } + else + { + TextRelay entry = info.GetTextEntry( 0 ); + string text = ( entry == null ? "" : entry.Text.Trim() ); + + if ( text.Length == 0 ) + { + m_From.SendMessage( 0x35, "You must enter a description." ); + m_From.SendGump( new PagePromptGump( m_From, m_Type ) ); + } + else + { + m_From.SendLocalizedMessage( 501234, "", 0x35 ); /* The next available Counselor/Game Master will respond as soon as possible. + * Please check your Journal for messages every few minutes. + */ + + PageQueue.Enqueue( new PageEntry( m_From, text, m_Type ) ); + } + } + } + } +} diff --git a/Scripts/Engines/Help/PageQueue.cs b/Scripts/Engines/Help/PageQueue.cs new file mode 100644 index 000000000..6e306603b --- /dev/null +++ b/Scripts/Engines/Help/PageQueue.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections; +using System.Net.Mail; +using System.IO; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Misc; +using Server.Accounting; +using Server.Engines.Reports; +using Server.Commands; +using System.Collections.Generic; + +namespace Server.Engines.Help +{ + public enum PageType + { + Bug, + Stuck, + Account, + Question, + Suggestion, + Other, + VerbalHarassment, + PhysicalHarassment + } + + public class PageEntry + { + // What page types should have a speech log as attachment? + public static readonly PageType[] SpeechLogAttachment = new PageType[] + { + PageType.VerbalHarassment + }; + + private Mobile m_Sender; + private Mobile m_Handler; + private DateTime m_Sent; + private string m_Message; + private PageType m_Type; + private Point3D m_PageLocation; + private Map m_PageMap; + private List m_SpeechLog; + + private PageInfo m_PageInfo; + + public PageInfo PageInfo + { + get{ return m_PageInfo; } + } + + public Mobile Sender + { + get + { + return m_Sender; + } + } + + public Mobile Handler + { + get + { + return m_Handler; + } + set + { + PageQueue.OnHandlerChanged( m_Handler, value, this ); + m_Handler = value; + } + } + + public DateTime Sent + { + get + { + return m_Sent; + } + } + + public string Message + { + get + { + return m_Message; + } + } + + public PageType Type + { + get + { + return m_Type; + } + } + + public Point3D PageLocation + { + get + { + return m_PageLocation; + } + } + + public Map PageMap + { + get + { + return m_PageMap; + } + } + + public List SpeechLog + { + get + { + return m_SpeechLog; + } + } + + private Timer m_Timer; + + public void Stop() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + } + + public void AddResponse( Mobile mob, string text ) + { + if ( m_PageInfo != null ) + { + lock ( m_PageInfo ) + m_PageInfo.Responses.Add( PageInfo.GetAccount( mob ), text ); + + if ( PageInfo.ResFromResp( text ) != PageResolution.None ) + m_PageInfo.UpdateResolver(); + } + } + + public PageEntry( Mobile sender, string message, PageType type ) + { + m_Sender = sender; + m_Sent = DateTime.Now; + m_Message = Utility.FixHtml( message ); + m_Type = type; + m_PageLocation = sender.Location; + m_PageMap = sender.Map; + + PlayerMobile pm = sender as PlayerMobile; + if ( pm != null && pm.SpeechLog != null && Array.IndexOf( SpeechLogAttachment, type ) >= 0 ) + m_SpeechLog = new List( pm.SpeechLog ); + + m_Timer = new InternalTimer( this ); + m_Timer.Start(); + + StaffHistory history = Reports.Reports.StaffHistory; + + if ( history != null ) + { + m_PageInfo = new PageInfo( this ); + + history.AddPage( m_PageInfo ); + } + } + + private class InternalTimer : Timer + { + private static TimeSpan StatusDelay = TimeSpan.FromMinutes( 2.0 ); + + private PageEntry m_Entry; + + public InternalTimer( PageEntry entry ) : base( TimeSpan.FromSeconds( 1.0 ), StatusDelay ) + { + m_Entry = entry; + } + + protected override void OnTick() + { + int index = PageQueue.IndexOf( m_Entry ); + + if ( m_Entry.Sender.NetState != null && index != -1 ) + { + m_Entry.Sender.SendLocalizedMessage( 1008077, true, (index + 1).ToString() ); // Thank you for paging. Queue status : + m_Entry.Sender.SendLocalizedMessage( 1008084 ); // You can reference our website at www.uo.com or contact us at support@uo.com. To cancel your page, please select the help button again and select cancel. + } + else + { + if ( index != -1 ) + m_Entry.AddResponse( m_Entry.Sender, "[Logout]" ); + + PageQueue.Remove( m_Entry ); + } + } + } + } + + public class PageQueue + { + private static ArrayList m_List = new ArrayList(); + private static Hashtable m_KeyedByHandler = new Hashtable(); + private static Hashtable m_KeyedBySender = new Hashtable(); + + public static void Initialize() + { + CommandSystem.Register( "Pages", AccessLevel.Counselor, new CommandEventHandler( Pages_OnCommand ) ); + } + + public static bool CheckAllowedToPage( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return true; + + if ( pm.DesignContext != null ) + { + from.SendLocalizedMessage( 500182 ); // You cannot request help while customizing a house or transferring a character. + return false; + } + else if ( pm.PagingSquelched ) + { + from.SendMessage( "You cannot request help, sorry." ); + return false; + } + + return true; + } + + public static string GetPageTypeName( PageType type ) + { + if ( type == PageType.VerbalHarassment ) + return "Verbal Harassment"; + else if ( type == PageType.PhysicalHarassment ) + return "Physical Harassment"; + else + return type.ToString(); + } + + public static void OnHandlerChanged( Mobile old, Mobile value, PageEntry entry ) + { + if ( old != null ) + m_KeyedByHandler.Remove( old ); + + if ( value != null ) + m_KeyedByHandler[value] = entry; + } + + [Usage( "Pages" )] + [Description( "Opens the page queue menu." )] + private static void Pages_OnCommand( CommandEventArgs e ) + { + PageEntry entry = (PageEntry)m_KeyedByHandler[e.Mobile]; + + if ( entry != null ) + { + e.Mobile.SendGump( new PageEntryGump( e.Mobile, entry ) ); + } + else if ( m_List.Count > 0 ) + { + e.Mobile.SendGump( new PageQueueGump() ); + } + else + { + e.Mobile.SendMessage( "The page queue is empty." ); + } + } + + public static bool IsHandling( Mobile check ) + { + return m_KeyedByHandler.ContainsKey( check ); + } + + public static bool Contains( Mobile sender ) + { + return m_KeyedBySender.ContainsKey( sender ); + } + + public static int IndexOf( PageEntry e ) + { + return m_List.IndexOf( e ); + } + + public static void Cancel( Mobile sender ) + { + Remove( (PageEntry) m_KeyedBySender[sender] ); + } + + public static void Remove( PageEntry e ) + { + if ( e == null ) + return; + + e.Stop(); + + m_List.Remove( e ); + m_KeyedBySender.Remove( e.Sender ); + + if ( e.Handler != null ) + m_KeyedByHandler.Remove( e.Handler ); + } + + public static PageEntry GetEntry( Mobile sender ) + { + return (PageEntry)m_KeyedBySender[sender]; + } + + public static void Remove( Mobile sender ) + { + Remove( GetEntry( sender ) ); + } + + public static ArrayList List + { + get + { + return m_List; + } + } + + public static void Enqueue( PageEntry entry ) + { + m_List.Add( entry ); + m_KeyedBySender[entry.Sender] = entry; + + bool isStaffOnline = false; + + foreach ( NetState ns in NetState.Instances ) + { + Mobile m = ns.Mobile; + + if ( m != null && m.AccessLevel >= AccessLevel.Counselor && m.AutoPageNotify && !IsHandling( m ) ) + m.SendMessage( "A new page has been placed in the queue." ); + + if ( m != null && m.AccessLevel >= AccessLevel.Counselor && m.AutoPageNotify && m.LastMoveTime >= (DateTime.Now - TimeSpan.FromMinutes( 10.0 )) ) + isStaffOnline = true; + } + + if ( !isStaffOnline ) + entry.Sender.SendMessage( "We are sorry, but no staff members are currently available to assist you. Your page will remain in the queue until one becomes available, or until you cancel it manually." ); + + if ( Email.SpeechLogPageAddresses != null && entry.SpeechLog != null ) + SendEmail( entry ); + } + + private static void SendEmail( PageEntry entry ) + { + Mobile sender = entry.Sender; + DateTime time = DateTime.Now; + + MailMessage mail = new MailMessage( "RunUO", Email.SpeechLogPageAddresses ); + + mail.Subject = "RunUO Speech Log Page Forwarding"; + + using ( StringWriter writer = new StringWriter() ) + { + writer.WriteLine( "RunUO Speech Log Page - {0}", PageQueue.GetPageTypeName( entry.Type ) ); + writer.WriteLine(); + + writer.WriteLine( "From: '{0}', Account: '{1}'", sender.RawName, sender.Account is Account ? sender.Account.Username : "???" ); + writer.WriteLine( "Location: {0} [{1}]", sender.Location, sender.Map ); + writer.WriteLine( "Sent on: {0}/{1:00}/{2:00} {3}:{4:00}:{5:00}", time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second ); + writer.WriteLine(); + + writer.WriteLine( "Message:" ); + writer.WriteLine( "'{0}'", entry.Message ); + writer.WriteLine(); + + writer.WriteLine( "Speech Log" ); + writer.WriteLine( "==========" ); + + foreach ( SpeechLogEntry logEntry in entry.SpeechLog ) + { + Mobile from = logEntry.From; + string fromName = from.RawName; + string fromAccount = from.Account is Account ? from.Account.Username : "???"; + DateTime created = logEntry.Created; + string speech = logEntry.Speech; + + writer.WriteLine( "{0}:{1:00}:{2:00} - {3} ({4}): '{5}'", created.Hour, created.Minute, created.Second, fromName, fromAccount, speech ); + } + + mail.Body = writer.ToString(); + } + + Email.AsyncSend( mail ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/PageQueueGump.cs b/Scripts/Engines/Help/PageQueueGump.cs new file mode 100644 index 000000000..562eb2152 --- /dev/null +++ b/Scripts/Engines/Help/PageQueueGump.cs @@ -0,0 +1,834 @@ +using System; +using System.IO; +using System.Collections; +using Server; +using Server.Network; +using Server.Gumps; + +namespace Server.Engines.Help +{ + public class MessageSentGump : Gump + { + private string m_Name, m_Text; + private Mobile m_Mobile; + + public MessageSentGump( Mobile mobile, string name, string text ) : base( 30, 30 ) + { + m_Name = name; + m_Text = text; + m_Mobile = mobile; + + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, 92, 75, 0xA3C ); + + AddImageTiled( 5, 7, 82, 61, 0xA40 ); + AddAlphaRegion( 5, 7, 82, 61 ); + + AddImageTiled( 9, 11, 21, 53, 0xBBC ); + + AddButton( 10, 12, 0x7D2, 0x7D2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 34, 28, 65, 24, 3001002, 0xFFFFFF, false, false ); // Message + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + m_Mobile.SendGump( new PageResponseGump( m_Mobile, m_Name, m_Text ) ); + + //m_Mobile.SendMessage( 0x482, "{0} tells you:", m_Name ); + //m_Mobile.SendMessage( 0x482, m_Text ); + } + } + + public class PageQueueGump : Gump + { + private PageEntry[] m_List; + + public PageQueueGump() : base( 30, 30 ) + { + Add( new GumpPage( 0 ) ); + //Add( new GumpBackground( 0, 0, 410, 448, 9200 ) ); + Add( new GumpImageTiled( 0, 0, 410, 448, 0xA40 ) ); + Add( new GumpAlphaRegion( 1, 1, 408, 446 ) ); + + Add( new GumpLabel( 180, 12, 2100, "Page Queue" ) ); + + ArrayList list = PageQueue.List; + + for ( int i = 0; i < list.Count; ) + { + PageEntry e = (PageEntry)list[i]; + + if ( e.Sender.Deleted || e.Sender.NetState == null ) + { + e.AddResponse( e.Sender, "[Logout]" ); + PageQueue.Remove( e ); + } + else + { + ++i; + } + } + + m_List = (PageEntry[])list.ToArray( typeof( PageEntry ) ); + + if ( m_List.Length > 0 ) + { + Add( new GumpPage( 1 ) ); + + for ( int i = 0; i < m_List.Length; ++i ) + { + PageEntry e = m_List[i]; + + if ( i >= 5 && (i % 5) == 0 ) + { + Add( new GumpButton( 368, 12, 0xFA5, 0xFA7, 0, GumpButtonType.Page, (i / 5) + 1 ) ); + Add( new GumpLabel( 298, 12, 2100, "Next Page" ) ); + Add( new GumpPage( (i / 5) + 1 ) ); + Add( new GumpButton( 12, 12, 0xFAE, 0xFB0, 0, GumpButtonType.Page, (i / 5) ) ); + Add( new GumpLabel( 48, 12, 2100, "Previous Page" ) ); + } + + string typeString = PageQueue.GetPageTypeName( e.Type ); + + string html = String.Format( "[{0}] {1} [{3}]", typeString, e.Message, e.Handler == null ? 0xFF0000 : 0xFF, e.Handler == null ? "Unhandled" : "Handling" ); + + Add( new GumpHtml( 12, 44 + ((i % 5) * 80), 350, 70, html, true, true ) ); + Add( new GumpButton( 370, 44 + ((i % 5) * 80) + 24, 0xFA5, 0xFA7, i + 1, GumpButtonType.Reply, 0 ) ); + } + } + else + { + Add( new GumpLabel( 12, 44, 2100, "The page queue is empty." ) ); + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID >= 1 && info.ButtonID <= m_List.Length ) + { + if ( PageQueue.List.IndexOf( m_List[info.ButtonID - 1] ) >= 0 ) + { + PageEntryGump g = new PageEntryGump( state.Mobile, m_List[info.ButtonID - 1] ); + + g.SendTo( state ); + } + else + { + state.Mobile.SendGump( new PageQueueGump() ); + state.Mobile.SendMessage( "That page has been removed." ); + } + } + } + } + + public class PredefinedResponse + { + private string m_Title; + private string m_Message; + + public string Title{ get{ return m_Title; } set{ m_Title = value; } } + public string Message{ get{ return m_Message; } set{ m_Message = value; } } + + public PredefinedResponse( string title, string message ) + { + m_Title = title; + m_Message = message; + } + + private static ArrayList m_List; + + public static ArrayList List + { + get + { + if ( m_List == null ) + m_List = Load(); + + return m_List; + } + } + + public static PredefinedResponse Add( string title, string message ) + { + if ( m_List == null ) + m_List = Load(); + + PredefinedResponse resp = new PredefinedResponse( title, message ); + + m_List.Add( resp ); + Save(); + + return resp; + } + + public static void Save() + { + if ( m_List == null ) + m_List = Load(); + + try + { + string path = Path.Combine( Core.BaseDirectory, "Data/pageresponse.cfg" ); + + using ( StreamWriter op = new StreamWriter( path ) ) + { + for ( int i = 0; i < m_List.Count; ++i ) + { + PredefinedResponse resp = (PredefinedResponse)m_List[i]; + + op.WriteLine( "{0}\t{1}", resp.Title, resp.Message ); + } + } + } + catch ( Exception e ) + { + Console.WriteLine( e ); + } + } + + public static ArrayList Load() + { + ArrayList list = new ArrayList(); + + string path = Path.Combine( Core.BaseDirectory, "Data/pageresponse.cfg" ); + + if ( File.Exists( path ) ) + { + try + { + using ( StreamReader ip = new StreamReader( path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + try + { + line = line.Trim(); + + if ( line.Length == 0 || line.StartsWith( "#" ) ) + continue; + + string[] split = line.Split( '\t' ); + + if ( split.Length == 2 ) + list.Add( new PredefinedResponse( split[0], split[1] ) ); + } + catch + { + } + } + } + } + catch ( Exception e ) + { + Console.WriteLine( e ); + } + } + + return list; + } + } + + public class PredefGump : Gump + { + private const int LabelColor32 = 0xFFFFFF; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public void AddTextInput( int x, int y, int w, int h, int id, string def ) + { + AddImageTiled( x, y, w, h, 0xA40 ); + AddImageTiled( x + 1, y + 1, w - 2, h - 2, 0xBBC ); + AddTextEntry( x + 3, y + 1, w - 4, h - 2, 0x480, id, def ); + } + + private Mobile m_From; + private PredefinedResponse m_Response; + + public PredefGump( Mobile from, PredefinedResponse response ) : base( 30, 30 ) + { + m_From = from; + m_Response = response; + + from.CloseGump( typeof( PredefGump ) ); + + bool canEdit = ( from.AccessLevel >= AccessLevel.GameMaster ); + + AddPage( 0 ); + + if ( response == null ) + { + AddImageTiled( 0, 0, 410, 448, 0xA40 ); + AddAlphaRegion( 1, 1, 408, 446 ); + + AddHtml( 10, 10, 390, 20, Color( Center( "Predefined Responses" ), LabelColor32 ), false, false ); + + ArrayList list = PredefinedResponse.List; + + AddPage( 1 ); + + int i; + + for ( i = 0; i < list.Count; ++i ) + { + if ( i >= 5 && (i % 5) == 0 ) + { + AddButton( 368, 10, 0xFA5, 0xFA7, 0, GumpButtonType.Page, (i / 5) + 1 ); + AddLabel( 298, 10, 2100, "Next Page" ); + AddPage( (i / 5) + 1 ); + AddButton( 12, 10, 0xFAE, 0xFB0, 0, GumpButtonType.Page, i / 5 ); + AddLabel( 48, 10, 2100, "Previous Page" ); + } + + PredefinedResponse resp = (PredefinedResponse)list[i]; + + string html = String.Format( "{0}
{1}", resp.Title, resp.Message ); + + AddHtml( 12, 44 + ((i % 5) * 80), 350, 70, html, true, true ); + + if ( canEdit ) + { + AddButton( 370, 44 + ((i % 5) * 80) + 24, 0xFA5, 0xFA7, 2 + (i * 3), GumpButtonType.Reply, 0 ); + + if ( i > 0 ) + AddButton( 377, 44 + ((i % 5) * 80) + 2, 0x15E0, 0x15E4, 3 + (i * 3), GumpButtonType.Reply, 0 ); + else + AddImage( 377, 44 + ((i % 5) * 80) + 2, 0x25E4 ); + + if ( i < (list.Count - 1) ) + AddButton( 377, 44 + ((i % 5) * 80) + 70 - 2 - 16, 0x15E2, 0x15E6, 4 + (i * 3), GumpButtonType.Reply, 0 ); + else + AddImage( 377, 44 + ((i % 5) * 80) + 70 - 2 - 16, 0x25E8 ); + } + } + + if ( canEdit ) + { + if ( i >= 5 && (i % 5) == 0 ) + { + AddButton( 368, 10, 0xFA5, 0xFA7, 0, GumpButtonType.Page, (i / 5) + 1 ); + AddLabel( 298, 10, 2100, "Next Page" ); + AddPage( (i / 5) + 1 ); + AddButton( 12, 10, 0xFAE, 0xFB0, 0, GumpButtonType.Page, i / 5 ); + AddLabel( 48, 10, 2100, "Previous Page" ); + } + + AddButton( 12, 44 + ((i % 5) * 80), 0xFAB, 0xFAD, 1, GumpButtonType.Reply, 0 ); + AddHtml( 45, 44 + ((i % 5) * 80), 200, 20, Color( "New Response", LabelColor32 ), false, false ); + } + } + else if ( canEdit ) + { + AddImageTiled( 0, 0, 410, 250, 0xA40 ); + AddAlphaRegion( 1, 1, 408, 248 ); + + AddHtml( 10, 10, 390, 20, Color( Center( "Predefined Response Editor" ), LabelColor32 ), false, false ); + + AddButton( 10, 40, 0xFB1, 0xFB3, 1, GumpButtonType.Reply, 0 ); + AddHtml( 45, 40, 200, 20, Color( "Remove", LabelColor32 ), false, false ); + + AddButton( 10, 70, 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); + AddHtml( 45, 70, 200, 20, Color( "Title:", LabelColor32 ), false, false ); + AddTextInput( 10, 90, 300, 20, 0, response.Title ); + + AddButton( 10, 120, 0xFA5, 0xFA7, 3, GumpButtonType.Reply, 0 ); + AddHtml( 45, 120, 200, 20, Color( "Message:", LabelColor32 ), false, false ); + AddTextInput( 10, 140, 390, 100, 1, response.Message ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_From.AccessLevel < AccessLevel.Administrator ) + return; + + if ( m_Response == null ) + { + int index = info.ButtonID - 1; + + if ( index == 0 ) + { + PredefinedResponse resp = new PredefinedResponse( "", "" ); + + ArrayList list = PredefinedResponse.List; + list.Add( resp ); + + m_From.SendGump( new PredefGump( m_From, resp ) ); + } + else + { + --index; + + int type = index % 3; + index /= 3; + + ArrayList list = PredefinedResponse.List; + + if ( index >= 0 && index < list.Count ) + { + PredefinedResponse resp = (PredefinedResponse)list[index]; + + switch ( type ) + { + case 0: // edit + { + m_From.SendGump( new PredefGump( m_From, resp ) ); + break; + } + case 1: // move up + { + if ( index > 0 ) + { + list.RemoveAt( index ); + list.Insert( index - 1, resp ); + + PredefinedResponse.Save(); + m_From.SendGump( new PredefGump( m_From, null ) ); + } + + break; + } + case 2: // move down + { + if ( index < (list.Count - 1) ) + { + list.RemoveAt( index ); + list.Insert( index + 1, resp ); + + PredefinedResponse.Save(); + m_From.SendGump( new PredefGump( m_From, null ) ); + } + + break; + } + } + } + } + } + else + { + ArrayList list = PredefinedResponse.List; + + switch ( info.ButtonID ) + { + case 1: + { + list.Remove( m_Response ); + + PredefinedResponse.Save(); + m_From.SendGump( new PredefGump( m_From, null ) ); + break; + } + case 2: + { + TextRelay te = info.GetTextEntry( 0 ); + + if ( te != null ) + m_Response.Title = te.Text; + + PredefinedResponse.Save(); + m_From.SendGump( new PredefGump( m_From, m_Response ) ); + + break; + } + case 3: + { + TextRelay te = info.GetTextEntry( 1 ); + + if ( te != null ) + m_Response.Message = te.Text; + + PredefinedResponse.Save(); + m_From.SendGump( new PredefGump( m_From, m_Response ) ); + + break; + } + } + } + } + } + + public class PageEntryGump : Gump + { + private PageEntry m_Entry; + private Mobile m_Mobile; + + private static int[] m_AccessLevelHues = new int[] + { + 2100, + 2122, + 2117, + 2129, + 2415, + 2415, + 2415 + }; + + public PageEntryGump( Mobile m, PageEntry entry ) : base( 30, 30 ) + { + try + { + m_Mobile = m; + m_Entry = entry; + + int buttons = 0; + + int bottom = 356; + + AddPage( 0 ); + + AddImageTiled( 0, 0, 410, 456, 0xA40 ); + AddAlphaRegion( 1, 1, 408, 454 ); + + AddPage( 1 ); + + AddLabel( 18, 18, 2100, "Sent:" ); + AddLabelCropped( 128, 18, 264, 20, 2100, entry.Sent.ToString() ); + + AddLabel( 18, 38, 2100, "Sender:" ); + AddLabelCropped( 128, 38, 264, 20, 2100, String.Format( "{0} {1} [{2}]", entry.Sender.RawName, entry.Sender.Location, entry.Sender.Map ) ); + + AddButton( 18, bottom - (buttons * 22), 0xFAB, 0xFAD, 8, GumpButtonType.Reply, 0 ); + AddImageTiled( 52, bottom - (buttons * 22) + 1, 340, 80, 0xA40/*0xBBC*//*0x2458*/ ); + AddImageTiled( 53, bottom - (buttons * 22) + 2, 338, 78, 0xBBC/*0x2426*/ ); + AddTextEntry( 55, bottom - (buttons++ * 22) + 2, 336, 78, 0x480, 0, "" ); + + AddButton( 18, bottom - (buttons * 22), 0xFA5, 0xFA7, 0, GumpButtonType.Page, 2 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Predefined Response" ); + + if ( entry.Sender != m ) + { + AddButton( 18, bottom - (buttons * 22), 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Go to Sender" ); + } + + AddLabel( 18, 58, 2100, "Handler:" ); + + if ( entry.Handler == null ) + { + AddLabelCropped( 128, 58, 264, 20, 2100, "Unhandled" ); + + AddButton( 18, bottom - (buttons * 22), 0xFB1, 0xFB3, 5, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Delete Page" ); + + AddButton( 18, bottom - (buttons * 22), 0xFB7, 0xFB9, 4, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Handle Page" ); + } + else + { + AddLabelCropped( 128, 58, 264, 20, m_AccessLevelHues[(int)entry.Handler.AccessLevel], entry.Handler.Name ); + + if ( entry.Handler != m ) + { + AddButton( 18, bottom - (buttons * 22), 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Go to Handler" ); + } + else + { + AddButton( 18, bottom - (buttons * 22), 0xFA2, 0xFA4, 6, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Abandon Page" ); + + AddButton( 18, bottom - (buttons * 22), 0xFB7, 0xFB9, 7, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Page Handled" ); + } + } + + AddLabel( 18, 78, 2100, "Page Location:" ); + AddLabelCropped( 128, 78, 264, 20, 2100, String.Format( "{0} [{1}]", entry.PageLocation, entry.PageMap ) ); + + AddButton( 18, bottom - (buttons * 22), 0xFA5, 0xFA7, 3, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "Go to Page Location" ); + + if ( entry.SpeechLog != null ) + { + AddButton( 18, bottom - (buttons * 22), 0xFA5, 0xFA7, 10, GumpButtonType.Reply, 0 ); + AddLabel( 52, bottom - (buttons++ * 22), 2100, "View Speech Log" ); + } + + AddLabel( 18, 98, 2100, "Page Type:" ); + AddLabelCropped( 128, 98, 264, 20, 2100, PageQueue.GetPageTypeName( entry.Type ) ); + + AddLabel( 18, 118, 2100, "Message:" ); + AddHtml( 128, 118, 250, 100, entry.Message, true, true ); + + AddPage( 2 ); + + ArrayList preresp = PredefinedResponse.List; + + AddButton( 18, 18, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 1 ); + AddButton( 410 - 18 - 32, 18, 0xFAB, 0xFAC, 9, GumpButtonType.Reply, 0 ); + + if ( preresp.Count == 0 ) + { + AddLabel( 52, 18, 2100, "There are no predefined responses." ); + } + else + { + AddLabel( 52, 18, 2100, "Back" ); + + for ( int i = 0; i < preresp.Count; ++i ) + { + AddButton( 18, 40 + (i * 22), 0xFA5, 0xFA7, 100 + i, GumpButtonType.Reply, 0 ); + AddLabel( 52, 40 + (i * 22), 2100, ((PredefinedResponse)preresp[i]).Title ); + } + } + } + catch ( Exception e ) + { + Console.WriteLine(e); + } + } + + public void Resend( NetState state ) + { + PageEntryGump g = new PageEntryGump( m_Mobile, m_Entry ); + + g.SendTo( state ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID != 0 && PageQueue.List.IndexOf( m_Entry ) < 0 ) + { + state.Mobile.SendGump( new PageQueueGump() ); + state.Mobile.SendMessage( "That page has been removed." ); + return; + } + + switch ( info.ButtonID ) + { + case 0: // close + { + if ( m_Entry.Handler != state.Mobile ) + { + PageQueueGump g = new PageQueueGump(); + + g.SendTo( state ); + } + + break; + } + case 1: // go to sender + { + Mobile m = state.Mobile; + + if ( m_Entry.Sender.Deleted ) + { + m.SendMessage( "That character no longer exists." ); + } + else if ( m_Entry.Sender.Map == null || m_Entry.Sender.Map == Map.Internal ) + { + m.SendMessage( "That character is not in the world." ); + } + else + { + m_Entry.AddResponse( state.Mobile, "[Go Sender]" ); + m.MoveToWorld( m_Entry.Sender.Location, m_Entry.Sender.Map ); + + m.SendMessage( "You have been teleported to that pages sender." ); + + Resend( state ); + } + + break; + } + case 2: // go to handler + { + Mobile m = state.Mobile; + Mobile h = m_Entry.Handler; + + if ( h != null ) + { + if ( h.Deleted ) + { + m.SendMessage( "That character no longer exists." ); + } + else if ( h.Map == null || h.Map == Map.Internal ) + { + m.SendMessage( "That character is not in the world." ); + } + else + { + m_Entry.AddResponse( state.Mobile, "[Go Handler]" ); + m.MoveToWorld( h.Location, h.Map ); + + m.SendMessage( "You have been teleported to that pages handler." ); + Resend( state ); + } + } + else + { + m.SendMessage( "Nobody is handling that page." ); + Resend( state ); + } + + break; + } + case 3: // go to page location + { + Mobile m = state.Mobile; + + if ( m_Entry.PageMap == null || m_Entry.PageMap == Map.Internal ) + { + m.SendMessage( "That location is not in the world." ); + } + else + { + m_Entry.AddResponse( state.Mobile, "[Go PageLoc]" ); + m.MoveToWorld( m_Entry.PageLocation, m_Entry.PageMap ); + + state.Mobile.SendMessage( "You have been teleported to the original page location." ); + + Resend( state ); + } + + break; + } + case 4: // handle page + { + if ( m_Entry.Handler == null ) + { + m_Entry.AddResponse( state.Mobile, "[Handling]" ); + m_Entry.Handler = state.Mobile; + + state.Mobile.SendMessage( "You are now handling the page." ); + } + else + { + state.Mobile.SendMessage( "Someone is already handling that page." ); + } + + Resend( state ); + + break; + } + case 5: // delete page + { + if ( m_Entry.Handler == null ) + { + m_Entry.AddResponse( state.Mobile, "[Deleting]" ); + PageQueue.Remove( m_Entry ); + + state.Mobile.SendMessage( "You delete the page." ); + + PageQueueGump g = new PageQueueGump(); + + g.SendTo( state ); + } + else + { + state.Mobile.SendMessage( "Someone is handling that page, it can not be deleted." ); + + Resend( state ); + } + + break; + } + case 6: // abandon page + { + if ( m_Entry.Handler == state.Mobile ) + { + m_Entry.AddResponse( state.Mobile, "[Abandoning]" ); + state.Mobile.SendMessage( "You abandon the page." ); + + m_Entry.Handler = null; + } + else + { + state.Mobile.SendMessage( "You are not handling that page." ); + } + + Resend( state ); + + break; + } + case 7: // page handled + { + if ( m_Entry.Handler == state.Mobile ) + { + m_Entry.AddResponse( state.Mobile, "[Handled]" ); + PageQueue.Remove( m_Entry ); + + m_Entry.Handler = null; + + state.Mobile.SendMessage( "You mark the page as handled, and remove it from the queue." ); + + PageQueueGump g = new PageQueueGump(); + + g.SendTo( state ); + } + else + { + state.Mobile.SendMessage( "You are not handling that page." ); + + Resend( state ); + } + + break; + } + case 8: // Send message + { + TextRelay text = info.GetTextEntry( 0 ); + + if ( text != null ) + { + m_Entry.AddResponse( state.Mobile, "[Response] " + text.Text ); + m_Entry.Sender.SendGump( new MessageSentGump( m_Entry.Sender, state.Mobile.Name, text.Text ) ); + //m_Entry.Sender.SendMessage( 0x482, "{0} tells you:", state.Mobile.Name ); + //m_Entry.Sender.SendMessage( 0x482, text.Text ); + } + + Resend( state ); + + break; + } + case 9: // predef overview + { + Resend( state ); + state.Mobile.SendGump( new PredefGump( state.Mobile, null ) ); + + break; + } + case 10: // View Speech Log + { + Resend( state ); + + if ( m_Entry.SpeechLog != null ) + { + Gump gump = new SpeechLogGump( m_Entry.Sender, m_Entry.SpeechLog ); + state.Mobile.SendGump( gump ); + } + + break; + } + default: + { + int index = info.ButtonID - 100; + ArrayList preresp = PredefinedResponse.List; + + if ( index >= 0 && index < preresp.Count ) + { + m_Entry.AddResponse( state.Mobile, "[PreDef] " + ((PredefinedResponse)preresp[index]).Title ); + m_Entry.Sender.SendGump( new MessageSentGump( m_Entry.Sender, state.Mobile.Name, ((PredefinedResponse)preresp[index]).Message ) ); + } + + Resend( state ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/PageResponseGump.cs b/Scripts/Engines/Help/PageResponseGump.cs new file mode 100644 index 000000000..5b39e8934 --- /dev/null +++ b/Scripts/Engines/Help/PageResponseGump.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Help +{ + public class PageResponseGump : Gump + { + private Mobile m_From; + private string m_Name, m_Text; + + public PageResponseGump( Mobile from, string name, string text ) : base( 0, 0 ) + { + m_From = from; + m_Name = name; + m_Text = text; + + AddBackground( 50, 25, 540, 430, 2600 ); + + AddPage( 0 ); + + AddHtmlLocalized( 150, 40, 360, 40, 1062610, false, false ); //
Ultima Online Help Response
+ + AddHtml( 80, 90, 480, 290, String.Format( "{0} tells {1}: {2}", name, from.Name, text ), true, true ); + + AddHtmlLocalized( 80, 390, 480, 40, 1062611, false, false ); // Clicking the OKAY button will remove the reponse you have received. + AddButton( 400, 417, 2074, 2075, 1, GumpButtonType.Reply, 0 ); // OKAY + + AddButton( 475, 417, 2073, 2072, 0, GumpButtonType.Reply, 0 ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID != 1 ) + m_From.SendGump( new MessageSentGump( m_From, m_Name, m_Text ) ); + } + } +} diff --git a/Scripts/Engines/Help/SpeechLog.cs b/Scripts/Engines/Help/SpeechLog.cs new file mode 100644 index 000000000..2c8913701 --- /dev/null +++ b/Scripts/Engines/Help/SpeechLog.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Gumps; +using Server.Commands; +using System.Collections.Generic; + +namespace Server.Engines.Help +{ + public class SpeechLog : IEnumerable + { + // Are speech logs enabled? + public static readonly bool Enabled = true; + + // How long should we maintain each speech entry? + public static readonly TimeSpan EntryDuration = TimeSpan.FromMinutes( 10.0 ); + + // What is the maximum number of entries a log can contain? (0 -> no limit) + public static readonly int MaxLength = 0; + + public static void Initialize() + { + CommandSystem.Register( "SpeechLog", AccessLevel.GameMaster, new CommandEventHandler( SpeechLog_OnCommand ) ); + } + + [Usage( "SpeechLog" )] + [Description( "Opens the speech log of a given target." )] + private static void SpeechLog_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + + from.SendMessage( "Target a player to view his speech log." ); + e.Mobile.Target = new SpeechLogTarget(); + } + + private class SpeechLogTarget : Target + { + public SpeechLogTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + PlayerMobile pm = targeted as PlayerMobile; + + if ( pm == null ) + { + from.SendMessage( "Speech logs aren't supported on that target." ); + } + else if ( from != targeted && from.AccessLevel <= pm.AccessLevel ) + { + from.SendMessage( "You don't have the required access level to view {0} speech log.", pm.Female ? "her" : "his" ); + } + else if ( pm.SpeechLog == null ) + { + from.SendMessage( "{0} has no speech log.", pm.Female ? "She" : "He" ); + } + else + { + CommandLogging.WriteLine( from, "{0} {1} viewing speech log of {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( targeted ) ); + + Gump gump = new SpeechLogGump( pm, pm.SpeechLog ); + from.SendGump( gump ); + } + } + } + + private Queue m_Queue; + + public int Count{ get{ return m_Queue.Count; } } + + public SpeechLog() + { + m_Queue = new Queue(); + } + + public void Add( Mobile from, string speech ) + { + Add( new SpeechLogEntry( from, speech ) ); + } + + public void Add( SpeechLogEntry entry ) + { + if ( MaxLength > 0 && m_Queue.Count >= MaxLength ) + m_Queue.Dequeue(); + + Clean(); + + m_Queue.Enqueue( entry ); + } + + public void Clean() + { + while ( m_Queue.Count > 0 ) + { + SpeechLogEntry entry = (SpeechLogEntry) m_Queue.Peek(); + + if ( DateTime.Now - entry.Created > EntryDuration ) + m_Queue.Dequeue(); + else + break; + } + } + + public void CopyTo( SpeechLogEntry[] array, int index ) + { + m_Queue.CopyTo( array, index ); + } + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return m_Queue.GetEnumerator(); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return m_Queue.GetEnumerator(); + } + + #endregion + } + + public class SpeechLogEntry + { + private Mobile m_From; + private string m_Speech; + private DateTime m_Created; + + public Mobile From{ get{ return m_From; } } + public string Speech{ get{ return m_Speech; } } + public DateTime Created{ get{ return m_Created; } } + + public SpeechLogEntry( Mobile from, string speech ) + { + m_From = from; + m_Speech = speech; + m_Created = DateTime.Now; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/SpeechLogGump.cs b/Scripts/Engines/Help/SpeechLogGump.cs new file mode 100644 index 000000000..9d8f6495a --- /dev/null +++ b/Scripts/Engines/Help/SpeechLogGump.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Server; +using Server.Gumps; +using Server.Accounting; +using Server.Network; + +namespace Server.Engines.Help +{ + public class SpeechLogGump : Gump + { + public static readonly int MaxEntriesPerPage = 30; + + private Mobile m_Player; + private List m_Log; + private int m_Page; + + public SpeechLogGump( Mobile player, SpeechLog log ) + : this( player, new List( log ) ) + { + } + + public SpeechLogGump( Mobile player, List log ) : this( player, log, ( log.Count - 1 ) / MaxEntriesPerPage ) + { + } + + public SpeechLogGump( Mobile player, List log, int page ) + : base( 500, 30 ) + { + m_Player = player; + m_Log = log; + m_Page = page; + + AddImageTiled( 0, 0, 300, 425, 0xA40 ); + AddAlphaRegion( 1, 1, 298, 423 ); + + string playerName = player.Name; + string playerAccount = player.Account is Account ? player.Account.Username : "???"; + + AddHtml( 10, 10, 280, 20, String.Format( "
SPEECH LOG - {0} ({1})
", playerName, Utility.FixHtml( playerAccount ) ), false, false ); + + int lastPage = ( log.Count - 1 ) / MaxEntriesPerPage; + + string sLog; + + if ( page < 0 || page > lastPage ) + { + sLog = ""; + } + else + { + int max = log.Count - ( lastPage - page ) * MaxEntriesPerPage; + int min = Math.Max( max - MaxEntriesPerPage, 0 ); + + StringBuilder builder = new StringBuilder(); + + for ( int i = min; i < max; i++ ) + { + SpeechLogEntry entry = log[i]; + + Mobile m = entry.From; + + string name = m.Name; + string account = m.Account is Account ? m.Account.Username : "???"; + string speech = entry.Speech; + + if ( i != min ) + builder.Append( "
" ); + + builder.AppendFormat( "{0} ({1}): {2}", name, Utility.FixHtml( account ), Utility.FixHtml( speech ) ); + } + + sLog = builder.ToString(); + } + + AddHtml( 10, 40, 280, 350, sLog, false, true ); + + if ( page > 0 ) + AddButton( 10, 395, 0xFAE, 0xFB0, 1, GumpButtonType.Reply, 0 ); // Previous page + + AddLabel( 45, 395, 0x481, String.Format( "Current page: {0}/{1}", page + 1, lastPage + 1 ) ); + + if ( page < lastPage ) + AddButton( 261, 395, 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); // Next page + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + switch ( info.ButtonID ) + { + case 1: // Previous page + { + if ( m_Page - 1 >= 0 ) + from.SendGump( new SpeechLogGump( m_Player, m_Log, m_Page - 1 ) ); + + break; + } + case 2: // Next page + { + if ( ( m_Page + 1 ) * MaxEntriesPerPage < m_Log.Count ) + from.SendGump( new SpeechLogGump( m_Player, m_Log, m_Page + 1 ) ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Help/StuckMenu.cs b/Scripts/Engines/Help/StuckMenu.cs new file mode 100644 index 000000000..fee2f78ce --- /dev/null +++ b/Scripts/Engines/Help/StuckMenu.cs @@ -0,0 +1,290 @@ +using System; +using Server.Network; +using Server.Gumps; + +namespace Server.Menus.Questions +{ + public class StuckMenuEntry + { + private int m_Name; + private Point3D[] m_Locations; + + public int Name{ get{ return m_Name; } } + public Point3D[] Locations{ get{ return m_Locations; } } + + public StuckMenuEntry( int name, Point3D[] locations ) + { + m_Name = name; + m_Locations = locations; + } + } + + public class StuckMenu : Gump + { + private static StuckMenuEntry[] m_Entries = new StuckMenuEntry[] + { + // Britain + new StuckMenuEntry( 1011028, new Point3D[] + { + new Point3D( 1522, 1757, 28 ), + new Point3D( 1519, 1619, 10 ), + new Point3D( 1457, 1538, 30 ), + new Point3D( 1607, 1568, 20 ), + new Point3D( 1643, 1680, 18 ) + } ), + + // Trinsic + new StuckMenuEntry( 1011029, new Point3D[] + { + new Point3D( 2005, 2754, 30 ), + new Point3D( 1993, 2827, 0 ), + new Point3D( 2044, 2883, 0 ), + new Point3D( 1876, 2859, 20 ), + new Point3D( 1865, 2687, 0 ) + } ), + + // Vesper + new StuckMenuEntry( 1011030, new Point3D[] + { + new Point3D( 2973, 891, 0 ), + new Point3D( 3003, 776, 0 ), + new Point3D( 2910, 727, 0 ), + new Point3D( 2865, 804, 0 ), + new Point3D( 2832, 927, 0 ) + } ), + + // Minoc + new StuckMenuEntry( 1011031, new Point3D[] + { + new Point3D( 2498, 392, 0 ), + new Point3D( 2433, 541, 0 ), + new Point3D( 2445, 501, 15 ), + new Point3D( 2501, 469, 15 ), + new Point3D( 2444, 420, 15 ) + } ), + + // Yew + new StuckMenuEntry( 1011032, new Point3D[] + { + new Point3D( 490, 1166, 0 ), + new Point3D( 652, 1098, 0 ), + new Point3D( 650, 1013, 0 ), + new Point3D( 536, 979, 0 ), + new Point3D( 464, 970, 0 ) + } ), + + // Cove + new StuckMenuEntry( 1011033, new Point3D[] + { + new Point3D( 2230, 1159, 0 ), + new Point3D( 2218, 1203, 0 ), + new Point3D( 2247, 1194, 0 ), + new Point3D( 2236, 1224, 0 ), + new Point3D( 2273, 1231, 0 ) + } ) + }; + + private static StuckMenuEntry[] m_T2AEntries = new StuckMenuEntry[] + { + // Papua + new StuckMenuEntry( 1011057, new Point3D[] + { + new Point3D( 5720, 3109, -1 ), + new Point3D( 5677, 3176, -3 ), + new Point3D( 5678, 3227, 0 ), + new Point3D( 5769, 3206, -2 ), + new Point3D( 5777, 3270, -1 ) + } ), + + // Delucia + new StuckMenuEntry( 1011058, new Point3D[] + { + new Point3D( 5216, 4033, 37 ), + new Point3D( 5262, 4049, 37 ), + new Point3D( 5284, 4006, 37 ), + new Point3D( 5189, 3971, 39 ), + new Point3D( 5243, 3960, 37 ) + } ) + }; + + private static bool IsInSecondAgeArea( Mobile m ) + { + if ( m.Map != Map.Trammel && m.Map != Map.Felucca ) + return false; + + if ( m.X >= 5120 && m.Y >= 2304 ) + return true; + + if ( m.Region.IsPartOf( "Terathan Keep" ) ) + return true; + + return false; + } + + private Mobile m_Mobile, m_Sender; + private bool m_MarkUse; + + private Timer m_Timer; + + public StuckMenu( Mobile beholder, Mobile beheld, bool markUse ) : base( 150, 50 ) + { + m_Sender = beholder; + m_Mobile = beheld; + m_MarkUse = markUse; + + Closable = false; + Dragable = false; + Disposable = false; + + AddBackground( 0, 0, 270, 320, 2600 ); + + AddHtmlLocalized( 50, 20, 250, 35, 1011027, false, false ); // Chose a town: + + StuckMenuEntry[] entries = IsInSecondAgeArea( beheld ) ? m_T2AEntries : m_Entries; + + for ( int i = 0; i < entries.Length; i++ ) + { + StuckMenuEntry entry = entries[i]; + + AddButton( 50, 55 + 35 * i, 208, 209, i + 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 75, 55 + 35 * i, 335, 40, entry.Name, false, false ); + } + + AddButton( 55, 263, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 90, 265, 200, 35, 1011012, false, false ); // CANCEL + } + + public void BeginClose() + { + StopClose(); + + m_Timer = new CloseTimer( m_Mobile ); + m_Timer.Start(); + + m_Mobile.Frozen = true; + } + + public void StopClose() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Mobile.Frozen = false; + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + StopClose(); + + if ( Factions.Sigil.ExistsOn( m_Mobile ) ) + { + m_Mobile.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( info.ButtonID == 0 ) + { + if ( m_Mobile == m_Sender ) + m_Mobile.SendLocalizedMessage( 1010588 ); // You choose not to go to any city. + } + else + { + int index = info.ButtonID - 1; + StuckMenuEntry[] entries = IsInSecondAgeArea( m_Mobile ) ? m_T2AEntries : m_Entries; + + if ( index >= 0 && index < entries.Length ) + Teleport( entries[index] ); + } + } + + private void Teleport( StuckMenuEntry entry ) + { + if ( m_MarkUse ) + { + m_Mobile.SendLocalizedMessage( 1010589 ); // You will be teleported within the next two minutes. + + new TeleportTimer( m_Mobile, entry, TimeSpan.FromSeconds( 10.0 + (Utility.RandomDouble() * 110.0) ) ).Start(); + + m_Mobile.UsedStuckMenu(); + } + else + { + new TeleportTimer( m_Mobile, entry, TimeSpan.Zero ).Start(); + } + } + + private class CloseTimer : Timer + { + private Mobile m_Mobile; + private DateTime m_End; + + public CloseTimer( Mobile m ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_End = DateTime.Now + TimeSpan.FromMinutes( 3.0 ); + } + + protected override void OnTick() + { + if ( m_Mobile.NetState == null || DateTime.Now > m_End ) + { + m_Mobile.Frozen = false; + m_Mobile.CloseGump( typeof( StuckMenu ) ); + + Stop(); + } + else + { + m_Mobile.Frozen = true; + } + } + } + + private class TeleportTimer : Timer + { + private Mobile m_Mobile; + private StuckMenuEntry m_Destination; + private DateTime m_End; + + public TeleportTimer( Mobile mobile, StuckMenuEntry destination, TimeSpan delay ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.0 ) ) + { + Priority = TimerPriority.TwoFiftyMS; + + m_Mobile = mobile; + m_Destination = destination; + m_End = DateTime.Now + delay; + } + + protected override void OnTick() + { + if ( DateTime.Now < m_End ) + { + m_Mobile.Frozen = true; + } + else + { + m_Mobile.Frozen = false; + Stop(); + + if ( Factions.Sigil.ExistsOn( m_Mobile ) ) + { + m_Mobile.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return; + } + + int idx = Utility.Random( m_Destination.Locations.Length ); + Point3D dest = m_Destination.Locations[idx]; + + Map destMap; + if ( m_Mobile.Map == Map.Trammel ) + destMap = Map.Trammel; + else if ( m_Mobile.Map == Map.Felucca ) + destMap = Map.Felucca; + else + destMap = m_Mobile.Kills >= 5 ? Map.Felucca : Map.Trammel; + + Mobiles.BaseCreature.TeleportPets( m_Mobile, dest, destMap ); + m_Mobile.MoveToWorld( dest, destMap ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Books/GrimmochJournal.cs b/Scripts/Engines/Khaldun/Books/GrimmochJournal.cs new file mode 100644 index 000000000..90695eddb --- /dev/null +++ b/Scripts/Engines/Khaldun/Books/GrimmochJournal.cs @@ -0,0 +1,779 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GrimmochJournal1 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day One :", + "", + "'Tis a grand sight, this", + "primeval tomb, I agree", + "with Tavara on that.", + "And we've a good crew", + "here, they've strong", + "backs and a good" + ), + new BookPageInfo + ( + "attitude. I'm a bit", + "concerned by those", + "that worked as guides", + "for us, however. All", + "seemed well enough", + "until we revealed the", + "immense stone doors", + "of the tomb structure" + ), + new BookPageInfo + ( + "itself. Seemed to send", + "a shiver up their", + "spines and get them all", + "stirred up with", + "whispering. I'll", + "watch the lot of them", + "with a close eye, but", + "I'm confident we won't" + ), + new BookPageInfo + ( + "have any real", + "problems on the dig.", + "I'm especially proud to", + "see Thomas standing", + "out - he was a good", + "hire, despite the", + "warnings from his", + "previous employers." + ), + new BookPageInfo + ( + "He's drummed up the", + "workers into a", + "furious pace - we've", + "nearly halved the", + "estimate on the", + "timeline for", + "excavating the Tomb's", + "entrance." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal1() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal2 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Two :", + "", + "We managed to dig out", + "the last of the", + "remaining rubble", + "today, revealing the", + "entirety of the giant", + "stone doors that sealed" + ), + new BookPageInfo + ( + "ol' Khal Ankur and", + "his folk up ages ago.", + "Actually getting them", + "open was another", + "matter altogether,", + "however. As the", + "workers set to the", + "task with picks and" + ), + new BookPageInfo + ( + "crowbars, I could have", + "sworn I saw Lysander", + "Gathenwale fiddling", + "with something in that", + "musty old tome of his.", + " I've no great", + "knowledge of things", + "magical, but the way" + ), + new BookPageInfo + ( + "his hand moved over", + "that book, and the look", + "of concentration on his", + "face as he whispered", + "something to himself", + "looked like every", + "description of an", + "incantation I've ever" + ), + new BookPageInfo + ( + "heard. The strange", + "thing is, this set of", + "doors that an entire", + "crew of excavators", + "was laboring over for", + "hours, right when", + "Gathenwale finishes", + "with his mumbling..." + ), + new BookPageInfo + ( + "well, I swore the doors", + "just gave open at the", + "exact moment he", + "spoke his last bit of", + "whisper and shut the", + "tome tight in his", + "hands. When he", + "looked up, it was" + ), + new BookPageInfo + ( + "almost as if he was", + "expecting the doors to", + "be open, rather than", + "shocked that they'd", + "finally given way." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal2() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal3 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Three - Day Five:", + "", + "I might have", + "written too hastily in", + "my first entry - this", + "place doesn't seem too", + "bent on giving up any", + "secrets. Though the" + ), + new BookPageInfo + ( + "main antechamber is", + "open to us, the main", + "exit hall is blocked by", + "yet another pile of", + "rubble. Doesn't look a", + "bit like anything", + "caused by a quake or", + "instability in the" + ), + new BookPageInfo + ( + "stonework... I swear it", + "looks as if someone", + "actually piled the", + "stones up themselves,", + "some time after the", + "tomb was built. The", + "stones aren't of the", + "same set nor quality" + ), + new BookPageInfo + ( + "of the carved work", + "that surrounds them", + "- if anything, they", + "resemble the grade of", + "common rock we saw", + "in great quantities on", + "the trip here. Which", + "makes it feel all the" + ), + new BookPageInfo + ( + "more like someone", + "hauled them in and", + "deliberately covered", + "this passage. But then", + "why not decorate them", + "in the same ornate", + "manner as the rest of", + "the stone in this" + ), + new BookPageInfo + ( + "place? Lysander", + "wouldn't hear a word", + "of what I had to say -", + "to him, it was a quake", + "some time in the", + "history of the tomb,", + "and that was it, shut", + "up and move on. So I" + ), + new BookPageInfo + ( + "shut up, and got back", + "to work." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal3() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal6 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Six :", + "", + "The camp was", + "attacked last night by", + "a pack of, well, I don't", + "have a clue. I've never", + "seen the like of these", + "beasts anywhere." + ), + new BookPageInfo + ( + "Huge things, with", + "fangs the size of your", + "forefinger, covered in", + "hair and with the", + "strangest arched back", + "I've ever seen. And so", + "many of them. We", + "were forced back into" + ), + new BookPageInfo + ( + "the Tomb for the", + "night, just to keep our", + "hides on us. And", + "today Gathenwale", + "practically orders us", + "all to move the entire", + "exterior camp into the", + "Tomb. Now, I don't" + ), + new BookPageInfo + ( + "disagree that we'd be", + "well off to use the", + "place as a point of", + "fortification... but I", + "don't like it one bit, in", + "any case. I don't like", + "the look of this place,", + "nor the sound of it." + ), + new BookPageInfo + ( + "The way the wind", + "gets into the", + "passageways,", + "whistling up the", + "strangest noises.", + "Deep, sustained echoes", + "of the wind, not so", + "much flute-like as..." + ), + new BookPageInfo + ( + "well, it sounds", + "ridiculous. In any", + "case, we've set to work", + "moving the bulk of the", + "exterior camp into the", + "main antechamber, so", + "there's no use moaning", + "about it now." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal6() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal6( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal7 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Seven - Day Ten:", + "", + "I cannot stand this", + "place, I cannot bear it.", + "I've got to get out.", + "Something evil lurks", + "in this ancient place,", + "something best left" + ), + new BookPageInfo + ( + "alone. I hear them,", + "yet none of the others", + "do. And yet they", + "must. Hands, claws,", + "scratching at stone,", + "the awful scratching", + "and the piteous cries", + "that sound almost like" + ), + new BookPageInfo + ( + "laughter. I can hear", + "them above even the", + "cracks of the", + "workmen's picks, and", + "at night they are all I", + "can hear. And yet the", + "others hear nothing.", + "We must leave this" + ), + new BookPageInfo + ( + "place, we must.", + "Three workers have", + "gone missing - Tavara", + "expects they've", + "abandoned us - and I", + "count them lucky if", + "they have. I don't care", + "what the others say," + ), + new BookPageInfo + ( + "we must leave this", + "place. We must do as", + "those before and pile", + "up the stones, block all", + "access to this primeval", + "crypt, seal it up again", + "for all eternity." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal7() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal7( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal11 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Eleven - Day", + "Thirteen :", + "", + "Lysander is gone, and", + "two more workers", + "with him. Good", + "riddance to the first.", + "He knows something." + ), + new BookPageInfo + ( + "He heard them too, I", + "know he did - and yet", + "he scowled at me", + "when I mentioned", + "them. I cannot stop", + "the noise in my head,", + "the scratching, the", + "clawing tears at my" + ), + new BookPageInfo + ( + "senses. What is it?", + "What does Lysander", + "seek that I can only", + "turn from? Where", + "has he gone? The", + "only answer to my", + "questions comes as", + "laughter from behind" + ), + new BookPageInfo + ( + "the stones." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal11() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal11( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal14 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Fourteen - Day", + "Sixteen :", + "", + "We are lost... we are", + "lost... all is lost. The", + "dead are piled up at", + "my feet. Bergen and I", + "somehow managed in" + ), + new BookPageInfo + ( + "the madness to piece", + "together a barricade,", + "barring access to the", + "camp antechamber.", + "He knows as well as I", + "that we cannot hold it", + "forever. The dead", + "come. They took" + ), + new BookPageInfo + ( + "Lysander before our", + "eyes. I pity the soul", + "of even such a", + "madman - no one", + "should die in such a", + "manner. And yet so", + "many have. We're", + "trapped here in this" + ), + new BookPageInfo + ( + "horror. So many have", + "died, and for what?", + "What curse have we", + "stumbled upon? I", + "cannot bear it, the", + "moaning, wailing cries", + "of the dead. Poor", + "Thomas, cut to pieces" + ), + new BookPageInfo + ( + "by their blades. We", + "had only an hour to", + "properly bury those", + "we could, before the", + "undead legions struck", + "again. I cannot go on...", + "I cannot go on." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal14() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal14( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal17 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Seventeen - Day", + "Twenty-Two :", + "", + "The fighting never", + "ceases... the blood", + "never stops flowing,", + "like a river through", + "the bloated corpses of" + ), + new BookPageInfo + ( + "the dead. And yet", + "there are still more.", + "Always more, with", + "the red fire gleaming", + "in their eyes. My", + "arm aches, I've taken", + "to the sword as my", + "bow seems to do little" + ), + new BookPageInfo + ( + "good... the dull ache in", + "my arm... so many", + "swings, cleaving a", + "mountain of decaying", + "flesh. And Thomas...", + "he was there, in the", + "thick of it... Thomas", + "was beside me..." + ), + new BookPageInfo + ( + "his face cleaved in", + "twain - and yet beside", + "me, fighting with us", + "against the horde until", + "he was cut down once", + "again. And I swear I", + "see him even now,", + "there in the dark" + ), + new BookPageInfo + ( + "corner of the", + "antechamber, his eyes", + "flickering in the last", + "dying embers of the", + "fire... and he stares at", + "me, and a scream fills", + "the vault - whether", + "his or mine, I can no" + ), + new BookPageInfo + ( + "longer tell." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal17() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal17( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class GrimmochJournal23 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The daily journal of Grimmoch Drummel", "Grimmoch", + new BookPageInfo + ( + "Day Twenty-Three :", + "", + "We no longer bury the", + "dead." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrimmochJournal23() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public GrimmochJournal23( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Books/LysanderNotebook.cs b/Scripts/Engines/Khaldun/Books/LysanderNotebook.cs new file mode 100644 index 000000000..8a4b0c552 --- /dev/null +++ b/Scripts/Engines/Khaldun/Books/LysanderNotebook.cs @@ -0,0 +1,539 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LysanderNotebook1 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Lysander's Notebook", "L. Gathenwale", + new BookPageInfo + ( + "Day One :", + "", + "At last, it stands", + "before me. The doors", + "of Thy Sanctum will", + "open to me now, after", + "all these years of", + "searching. I give" + ), + new BookPageInfo + ( + "myself unto Thee,", + "Khal Ankur, I have", + "come for Thy secrets", + "and I will kneel", + "prostrate before Thee.", + " Blessed are the", + "Keepers, praise unto", + "Thee, a thousand" + ), + new BookPageInfo + ( + "fortunes in the night." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LysanderNotebook1() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public LysanderNotebook1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class LysanderNotebook2 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Lysander's Notebook", "L. Gathenwale", + new BookPageInfo + ( + "Day Two:", + "", + "The woman, Tavara", + "Sewel, is unbearable.", + "Her entire demeanor", + "sickens me. I would", + "take her life for Thee", + "now, my Lord. But I" + ), + new BookPageInfo + ( + "cannot alert the", + "others. Progress is", + "made too slowly, I", + "cannot stand this", + "perpetual waiting.", + "Today I knelt down", + "with the workers,", + "tossing stones and dirt" + ), + new BookPageInfo + ( + "aside with my very", + "hands as they dug at", + "the last of the rubble", + "covering the entrance", + "to Thy Sanctum. The", + "Sewel woman was", + "shocked at my", + "demeanor, dirtying" + ), + new BookPageInfo + ( + "my robes, on my", + "knees in the muck as I", + "clawed at the rocks.", + "She thought I did this", + "for those sickly", + "scholars, or for her,", + "or for what she", + "laughably calls 'The" + ), + new BookPageInfo + ( + "Gift of Discovery', of", + "learning. As if I did", + "not know what I went", + "to find! I come for", + "Thee, Master. Soon", + "shall I receive Thy", + "gifts, Thy blessings.", + "Patience, eternal" + ), + new BookPageInfo + ( + "patience. I must take", + "my lessons well. I", + "have learned from", + "Thee, Master, I have." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LysanderNotebook2() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public LysanderNotebook2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class LysanderNotebook3 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Lysander's Notebook", "L. Gathenwale", + new BookPageInfo + ( + "Day Three - Day Six:", + "", + "What are these Beasts", + "that dare to defy our", + "presence here? Hast", + "Thou sent them,", + "Master? To tear", + "apart these foolish" + ), + new BookPageInfo + ( + "ones that accompany", + "me? That repugnant", + "pustule, Drummel, put", + "forth his absurd little", + "theories as to the", + "nature of the Beasts", + "that attacked our", + "camp, but I'll have" + ), + new BookPageInfo + ( + "none of his words. He", + "asks too many", + "questions. He is a", + "taint upon the grounds", + "of Thy Sanctum,", + "Master - I will deal", + "with him after the", + "Sewel woman." + ), + new BookPageInfo + ( + "Speaking of Sewel, I", + "have convinced that", + "empty-headed harlot", + "that we should move", + "our encampment", + "within the", + "antechamber. She", + "thinks I worry for" + ), + new BookPageInfo + ( + "her safety. I come", + "for thee, Master. I", + "make my camp in Thy", + "chambers. I sleep", + "under Thy roof. I can", + "feel Thine presence", + "even now. Soon,", + "Master. Soon." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LysanderNotebook3() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public LysanderNotebook3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class LysanderNotebook7 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Lysander's Notebook", "L. Gathenwale", + new BookPageInfo + ( + "Day Seven :", + "", + "The Sewel woman", + "pratters on endlessly.", + "And she dares to", + "speak Thy Name,", + "Master! I wish so", + "vehemently to take a" + ), + new BookPageInfo + ( + "knife to that little", + "neck of hers. She", + "struts around the", + "chambers of Thy", + "Sanctum with her", + "repugnant airs, her", + "scholarly conjecture", + "on this or that. That I" + ), + new BookPageInfo + ( + "could peel the skin", + "from her face and", + "show her how vile and", + "ugly she truly is, how", + "unworthy of entrance", + "to Thy Sanctum. I", + "must take her,", + "Master. I must rend" + ), + new BookPageInfo + ( + "that little wench to", + "pieces. I ask this gift", + "of Thee, that I might", + "cleanse Thy Sanctum", + "of her presence. Give", + "me the Sewel woman", + "and I shall show you", + "my mastery of Death," + ), + new BookPageInfo + ( + "Master. I shall cut", + "her to bits and scatter", + "them before the", + "others as a warning.", + "I cannot stand her", + "presence, I cannot", + "abide it. And", + "Drummel! He is a" + ), + new BookPageInfo + ( + "pustule that must be", + "lanced, a sickness that", + "I must cure by blade", + "and fire. Not a trace", + "of him will be left", + "when I'm done with", + "him. Praises to Thee,", + "Master. I shall honor" + ), + new BookPageInfo + ( + "Thee with many", + "sacrifices, soon", + "enough." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LysanderNotebook7() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public LysanderNotebook7( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class LysanderNotebook8 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Lysander's Notebook", "L. Gathenwale", + new BookPageInfo + ( + "Day Eight - Day Ten :", + "", + "Have you taken them,", + "Master? They could", + "not have found a way", + "past the stones that", + "block our path! The", + "three workers, My" + ), + new BookPageInfo + ( + "Master, where have", + "they gone? Curses", + "upon them! I'll cut", + "them all to pieces if", + "they show their faces", + "again, then burn the", + "rest alive upon a pyre,", + "for all to see, as a" + ), + new BookPageInfo + ( + "warning of Thy", + "Power. How could", + "they have gotten past", + "me? I sleep against", + "the very walls, to", + "hear Thy Words, to", + "feel Thy Breath. I", + "can find no egress" + ), + new BookPageInfo + ( + "from the chambers", + "that the Sewel woman", + "does not know of nor", + "have men working at", + "excavating. Where", + "have they gone,", + "Master? Have you", + "taken them, or do they" + ), + new BookPageInfo + ( + "truly flee from Thy", + "Presence? I will kill", + "them if they show", + "their faces again.", + "Give me Strength, my", + "Master, to let them", + "live a while longer,", + "until they have" + ), + new BookPageInfo + ( + "fulfilled their", + "purpose and I kneel", + "before Thee, covered", + "in their blood." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LysanderNotebook8() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public LysanderNotebook8( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class LysanderNotebook11 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Lysander's Notebook", "L. Gathenwale", + new BookPageInfo + ( + "Day Eleven - Day", + "Thirteen:", + "", + "I come for Thee, my", + "Master. I come! The", + "way is clear, I have", + "found Thy path and", + "washed it in the blood" + ), + new BookPageInfo + ( + "of the two workers", + "that caught sight of", + "me. Ah, how sweet it", + "was to cut them open,", + "to see the blood pour", + "out in great torrents, to", + "stand in it, to revel in", + "it. If only I had time" + ), + new BookPageInfo + ( + "for the Sewel woman.", + "But there will be time", + "enough for her. I", + "have learned Thy", + "Patience, Master. I", + "come for Thee. I walk", + "Thy halls in penance,", + "my last steps in this" + ), + new BookPageInfo + ( + "repulsive living", + "frame. I come for", + "Thee and Thy Gifts,", + "my Master. Glory", + "Unto Thee, Khal", + "Ankur, Keeper of the", + "Seventh Death,", + "Master, Leader of the" + ), + new BookPageInfo + ( + "Chosen, the Khaldun.", + "Praises in Thy", + "Name, Master of Life", + "and Death, Lord of All.", + " Khal Ankur, Master,", + "Prophet, I join Thy", + "ranks this night, a", + "member of the" + ), + new BookPageInfo + ( + "Khaldun at last!" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LysanderNotebook11() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public LysanderNotebook11( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Books/TavarasJournal.cs b/Scripts/Engines/Khaldun/Books/TavarasJournal.cs new file mode 100644 index 000000000..d5d4f0605 --- /dev/null +++ b/Scripts/Engines/Khaldun/Books/TavarasJournal.cs @@ -0,0 +1,1333 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TavarasJournal1 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day One:", + "", + "The workers continue", + "tirelessly in their", + "efforts to unload our", + "supplies even as light", + "fades. I feel I should", + "lend a hand in the" + ), + new BookPageInfo + ( + "effort, and yet I", + "cannot bear to take my", + "attention away from", + "the magnificent stone", + "doors of the tomb.", + "Every inch of their", + "massive frame is", + "covered with" + ), + new BookPageInfo + ( + "intricately carved", + "design work - 'tis", + "truly a sight to see.", + "I've spent the day", + "sketching and", + "cataloging what I can", + "of them while my", + "companions set up our" + ), + new BookPageInfo + ( + "camp and make", + "preparations for", + "tomorrow's work.", + "Though the stonework", + "symbols inspire me to", + "new flights of fancy,", + "some of the workers", + "seem strangely" + ), + new BookPageInfo + ( + "fearful of them. I", + "cannot wait 'til the", + "morrow when those", + "ancient works of stone", + "shall swing open and", + "deliver unto me", + "everything I have", + "dreamed of for the" + ), + new BookPageInfo + ( + "last ten years of my", + "life." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal1() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal2 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Two:", + "", + "Everything we'd", + "heard and read of the", + "tomb has proved", + "correct - and yet,", + "nothing could prepare", + "me for the sight of it" + ), + new BookPageInfo + ( + "with my own eyes.", + "The Tomb of Khal", + "Ankur has given up", + "its secrets at last! The", + "intricate stonework", + "that covered the tomb", + "doors seems to", + "continue throughout" + ), + new BookPageInfo + ( + "the entirety of the", + "catacombs, each", + "hallway and room", + "yielding a seemingly", + "endless amount of", + "information for my", + "companions and I to", + "record. It will take" + ), + new BookPageInfo + ( + "years to catalogue the", + "entirety of the Tomb,", + "if those legends of its", + "massive size prove", + "true. Sadly, a good", + "deal of the Tomb's", + "interior has been", + "damaged or utterly" + ), + new BookPageInfo + ( + "destroyed, whether", + "by seismic activity in", + "the surrounding", + "mountainside or", + "merely the slow", + "efforts of Time", + "itself, I do not know.", + "A good deal of the" + ), + new BookPageInfo + ( + "stonework has been", + "cracked or collapsed", + "entirely, especially", + "near the entrance", + "supports of the main", + "hall. Our passage has", + "indeed already been", + "entirely blocked in the" + ), + new BookPageInfo + ( + "first major room", + "we've discovered, a", + "massive pile of", + "boulders and stones", + "blocking any exit", + "from the", + "antechamber. What", + "could have caused" + ), + new BookPageInfo + ( + "such a localized", + "disruption of the", + "support structures,", + "one can only guess -", + "but it will surely take", + "an entire afternoon's", + "effort to remove even", + "a fraction of it. I look" + ), + new BookPageInfo + ( + "forward to more", + "progress tomorrow", + "once the workers have", + "set to excavating the", + "hall." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal2() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal3 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Three - Day Five:", + "", + "I do not understand", + "this place... not as I", + "once thought I did.", + "Something palatable", + "seems to hinder our", + "every attempt to" + ), + new BookPageInfo + ( + "investigate this", + "ancient site.", + "Excavation work on", + "the first major", + "hallway finished only", + "yesterday - the", + "amount of stone and", + "rubble blocking the" + ), + new BookPageInfo + ( + "egress was", + "astounding, it stands", + "in immense piles", + "outside the Tomb's", + "entrance, as if we", + "were digging the", + "tunnels of this", + "abhorred place" + ), + new BookPageInfo + ( + "ourselves! The", + "satisfaction of", + "completing our efforts", + "was quickly thwarted,", + "however, as we", + "discovered the end of", + "the hallway we had", + "just revealed was" + ), + new BookPageInfo + ( + "blocked by yet another", + "colossal pile of stone.", + "I've had a few of the", + "workers set up", + "primitive scaffolding", + "in the main", + "antechamber so that I", + "can spend my time" + ), + new BookPageInfo + ( + "pouring over the detail", + "work on the stone", + "carvings while the", + "rest of our crew", + "continue excavating", + "the inner halls." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal3() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal6 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Six:", + "", + "Late last night our", + "camp was set upon by", + "a pack of wild beasts", + "- behemoth creatures", + "with a speed and", + "viciousness I'd n'ere" + ), + new BookPageInfo + ( + "before seen. Even", + "Grimmoch, well", + "versed in all manner", + "of wildlife, was", + "unsure as to their", + "nature - though I lay", + "blame upon the", + "darkness covering" + ), + new BookPageInfo + ( + "their movements", + "rather than on his", + "skill as a huntsman.", + "The attacks did not let", + "up the entire night,", + "and we were", + "eventually forced to", + "flee into the Tomb" + ), + new BookPageInfo + ( + "itself to take refuge", + "from the ravenous", + "creatures - e'en", + "Lysander's spells", + "could not keep the foul", + "things from attacking", + "in great numbers.", + "The Tomb performed" + ), + new BookPageInfo + ( + "well as an impromptu", + "fortress, and we", + "managed to spend the", + "night unscathed.", + "Morning's light", + "seemed to have", + "scattered the beasts,", + "as not a single one of" + ), + new BookPageInfo + ( + "them was to be seen as", + "we exited the Tomb -", + "not even a carcass of", + "the few that were", + "slain a'fore we fled.", + "Lysander set the crew", + "to work, moving our", + "supplies and gear into" + ), + new BookPageInfo + ( + "the Tomb, in case the", + "creatures did opt to", + "return. Such savage", + "fury had the beasts -", + "and not a single one", + "ever turned to run,", + "even in the face of", + "certain death." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal6() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal6( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal7 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Seven:", + "", + "T'was written that,", + "upon his death, Khal", + "Ankur's followers,", + "those known as the", + "Keepers of the", + "Seventh Death, sealed" + ), + new BookPageInfo + ( + "themselves within the", + "Sanctum they had", + "carved from the", + "mountains in his", + "honor. The Zealots of", + "his order entombed", + "the lesser followers", + "alive, then, when all" + ), + new BookPageInfo + ( + "but two remained, slit", + "their throats and", + "joined Khal Ankur in", + "death. Surely this is", + "not surprising for a", + "Cult that worshipped", + "death and sacrifice so", + "vehemently as it is" + ), + new BookPageInfo + ( + "said that the Keepers", + "did - and yet, to be in", + "this Tomb, to know", + "that somewhere in its", + "depths hundreds upon", + "hundreds of bodies", + "lay, sealed alive at", + "their own behest..." + ), + new BookPageInfo + ( + "I must confess that", + "the very thought of it", + "troubles my dreams at", + "night. I've asked", + "Lysander if we might", + "reestablish the camp", + "outside the Tomb,", + "setting up night" + ), + new BookPageInfo + ( + "watches and some sort", + "of fortification, but", + "he'll have none of it. I", + "did not press the", + "issue, as I suddenly", + "felt foolish even at", + "my askance." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal7() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal7( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal8 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Eight :", + "", + "Astounding progress", + "was made today, and", + "my very head spins", + "with the excitement", + "of it. Upon full", + "excavation of the far" + ), + new BookPageInfo + ( + "western hall, another", + "large antechamber", + "was revealed. By the", + "larger, mosaic style of", + "the wall carvings and", + "their framing, as well", + "as the numerous", + "vellum scrolls and" + ), + new BookPageInfo + ( + "tomes held within, the", + "room appears to have", + "been a great museum", + "or library of sorts.", + "The sheer amount of", + "written information", + "encased within this", + "room would surely" + ), + new BookPageInfo + ( + "take me decades to", + "study e'en if I could", + "immediately decipher", + "the strange text with", + "which it was written.", + "My sheer joy at the", + "discovery was quickly", + "noted by the brute" + ), + new BookPageInfo + ( + "known as Morg", + "Bergen, who, even in", + "his simple way,", + "seemed just as", + "delighted as I that", + "some progress had", + "been made. I must", + "confess, upon his" + ), + new BookPageInfo + ( + "inclusion in our party", + "at the beginning of", + "this journey I was", + "somewhat suspect of", + "his nature, but he has", + "a startlingly quick wit", + "about him for such a", + "massive, calloused" + ), + new BookPageInfo + ( + "warrior. While", + "Lysander and e'en", + "Grimmoch always", + "seem to investigate the", + "tomb with a scowling", + "determination, Bergen", + "seems to feel the same", + "thrill of discovery as" + ), + new BookPageInfo + ( + "I. I am proud to now", + "count him as a friend,", + "and am thankful for", + "his laughter as well", + "as his strength." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal8() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal8( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal9 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Nine - Day Ten:", + "", + "The excavation of the", + "next set of tunnels", + "has ceased, as three", + "of the workers have", + "gone missing in the", + "night. Bergen voiced" + ), + new BookPageInfo + ( + "the opinion that they", + "had most likely", + "abandoned our group", + "altogether and headed", + "back, as they were of", + "the number that", + "seemed especially", + "disturbed by the" + ), + new BookPageInfo + ( + "Tomb. Lysander had", + "other ideas, however.", + "In the middle of our", + "discussion on the", + "matter, he went into a", + "wild tirade on the", + "possibility that they", + "had somehow" + ), + new BookPageInfo + ( + "infiltrated the tomb's", + "interior without us.", + "The pure, hateful", + "venom in his voice", + "when he spoke of the", + "workers shocked me,", + "as I had always", + "thought him to be a" + ), + new BookPageInfo + ( + "levelheaded man of", + "great learning. As we", + "are still at work", + "digging out the rubble", + "that blocks all access", + "to the inner chambers,", + "I cannot help but", + "believe the workers" + ), + new BookPageInfo + ( + "must have fled the", + "site altogether, as", + "Bergen said." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal9() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal9( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal11 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Eleven - Day", + "Thirteen:", + "", + "Two more workers", + "have gone missing.", + "Even more disturbing", + "is the fact that", + "Lysander has joined" + ), + new BookPageInfo + ( + "them. Late last night", + "the workers finished", + "excavating the next", + "main hall, and we", + "retired to the main", + "antechamber and our", + "camp to rest up for", + "exploration on the" + ), + new BookPageInfo + ( + "'morrow. In the", + "middle of the night we", + "woke to a strange", + "howling sound, and as", + "the men prepared", + "themselves for", + "another onslaught of", + "the beasts that had" + ), + new BookPageInfo + ( + "troubled our outer", + "camp, it was noticed", + "that Lysander was", + "nowhere in our", + "number. I cannot", + "fathom where he has", + "gone - the newly", + "revealed chamber" + ), + new BookPageInfo + ( + "holds no immediate", + "egress, blocked again", + "by piles of stone and", + "rubble, and I cannot", + "believe that Lysander,", + "of all people, would", + "have fled this site -", + "indeed, he had lately" + ), + new BookPageInfo + ( + "grown almost fanatical", + "in his work to", + "discover more of the", + "secrets barred to us", + "by the consistently", + "slow progress of", + "excavating each new", + "hallway. The men are" + ), + new BookPageInfo + ( + "at work even now, and", + "as the ceaseless", + "thumps and cracks of", + "their picks", + "reverberate", + "throughout the", + "entirety of the tomb,", + "the dust continues to" + ), + new BookPageInfo + ( + "pour down from the", + "ancient stonework", + "above us like some", + "horrible, eldritch", + "curse upon us all." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal11() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal11( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal14 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Fourteen - Day", + "Fifteen:", + "", + "Lysander has", + "returned... and yet,", + "how can I describe the", + "horror of it? He", + "stands across the" + ), + new BookPageInfo + ( + "chamber from me", + "even now, a changed", + "man. His hair hangs", + "in grimy knots across", + "his face, his clothes", + "filthy and torn in", + "places... and the blood", + "- covered in blood, his" + ), + new BookPageInfo + ( + "skin shining in", + "scarlet reflections of", + "the torchlight. He", + "will let no one", + "approach; a thick,", + "rusted dagger in his", + "hand warding off any", + "attempts to overcome" + ), + new BookPageInfo + ( + "him. And the blood,", + "which runs down in", + "great rivulets from", + "his arms and hands -", + "it is not his own, and", + "this is enough to keep", + "us at a wary distance.", + "Morg Bergen wishes" + ), + new BookPageInfo + ( + "to subdue him", + "quickly, but there is", + "something in", + "Lysander's eyes - and", + "I remember the power", + "of his spells, even as", + "he swings the jagged", + "dagger back and forth" + ), + new BookPageInfo + ( + "in a wide swath", + "before him.", + "Something about the", + "sight of it makes my", + "stomach churn.", + "Something has", + "happened, something", + "that changes" + ), + new BookPageInfo + ( + "everything. Lysander", + "has lost his sanity to", + "this tomb... or to", + "something within it.", + "Do we dare approach?", + "We must make a", + "decision soon." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal14() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal14( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal16 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Sixteen:", + "", + "Why do I write? I", + "must... not so much", + "because there must be", + "some record of", + "this... what's", + "happened here... as" + ), + new BookPageInfo + ( + "for my own sanity.", + "The act of putting pen", + "to paper calms me,", + "focuses me, even in", + "this madness.", + "Lysander is dead. So", + "many are dead. And", + "we're trapped here," + ), + new BookPageInfo + ( + "trapped forever in", + "this nightmare. He", + "would not let us pass,", + "wild in his psychosis,", + "furious, spitting,", + "covered in blood, he", + "swung the ancient", + "dagger at any who" + ), + new BookPageInfo + ( + "approached. He", + "babbled incoherently,", + "cursed at us, the most", + "hateful curses,", + "prophecy, doom upon", + "us. Bergen would", + "have none of it.", + "Finally, he leapt at" + ), + new BookPageInfo + ( + "Lysander, his", + "massive axe at his", + "side. But he would not", + "be the end of the mad", + "mage... no... they", + "were... those hands,", + "covered in the dirt of", + "the grave, maggots," + ), + new BookPageInfo + ( + "filth. They rose up", + "behind Lysander.", + "That look of curiosity", + "on the mage's face as", + "Bergen skidded to a", + "halt... t'was almost a", + "moment of sanity for", + "him, surely, to" + ), + new BookPageInfo + ( + "attempt to comprehend", + "what could have", + "stopped the warrior in", + "his tracks. And then", + "they were upon him.", + "Skeletal hands, arms", + "and faces with loose,", + "corrupted flesh" + ), + new BookPageInfo + ( + "hanging from yellow", + "bone. Inhuman, yet", + "once human,", + "staggering towards us", + "as their companions", + "tore at Lysander,", + "coming towards us in", + "droves." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal16() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal16( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal16b : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Sixteen, Later :", + "", + "We ran. What could", + "we do? We ran back", + "towards the entrance,", + "cutting at them when", + "we could. T'was a", + "nightmare, and yet" + ), + new BookPageInfo + ( + "nothing to prepare us", + "for what would come.", + "We were almost there,", + "the entrance to this", + "abhorred crypt in", + "sight. Then the earth", + "shook with such a", + "force that we were" + ), + new BookPageInfo + ( + "dropped to our hands", + "and knees, stumbling", + "in the darkness with", + "those... those things", + "surely behind us.", + "The noise of falling", + "rock and crumbling", + "stone drowned out our" + ), + new BookPageInfo + ( + "piteous cries. No sign", + "of the entrance", + "remained.", + "We owe our lives to", + "Bergen, whose wits", + "returned quickly.", + "That he could make us", + "hurry back into the" + ), + new BookPageInfo + ( + "main antechamber...", + "actually run back", + "towards those eldritch", + "dead that stalked us.", + "But we did, the", + "strength of his", + "convictions enough for", + "us in the moment." + ), + new BookPageInfo + ( + "And at our campsite", + "we erected our last", + "defense, a pitiable", + "wall of wood and", + "stone, anything at", + "hand that might block", + "the tide of those", + "nightmare creatures." + ), + new BookPageInfo + ( + "And I sit against it", + "even now. I can hear", + "their moans, their", + "wailing cries in the", + "distance - they'll be", + "here soon, even at the", + "unhurried pace of the", + "shuffling dead." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal16b() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal16b( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal17 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Seventeen - Day", + "Eighteen :", + "", + "I cannot go on much", + "longer. I know now", + "t'was no work of the", + "earth that trapped us", + "here - I can feel His" + ), + new BookPageInfo + ( + "force in it. It was His", + "will, His power that", + "has sealed us here in", + "this nightmare. The", + "barricade will not be", + "enough. So many of", + "them. They come like", + "unto the ocean's waves" + ), + new BookPageInfo + ( + "- ceaseless,", + "neverending. For", + "every five we strike", + "down, another ten rise", + "up against us. And", + "like the sands we", + "cannot help but be", + "brought down, wasted" + ), + new BookPageInfo + ( + "away in this ocean of", + "blood." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal17() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal17( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TavarasJournal19 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal: Discovery of the Tomb", "Tavara Sewel", + new BookPageInfo + ( + "Day Nineteen - Day", + "Twenty-One :", + "", + "The barricade won't", + "hold - never, and", + "they'll come, they", + "come even now. I", + "would tear the last of" + ), + new BookPageInfo + ( + "it down, let them in to", + "devour us all, if only", + "to stop the screaming", + "- the awful, wailing", + "cries that fill the tomb", + "with their presence.", + "May my ancestors", + "forgive me, but it" + ), + new BookPageInfo + ( + "must be done. I must", + "end this." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TavarasJournal19() : base( Utility.Random( 0xFF1, 2 ), false ) + { + } + + public TavarasJournal19( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/KhaldunGen.cs b/Scripts/Engines/Khaldun/KhaldunGen.cs new file mode 100644 index 000000000..7c8f867c7 --- /dev/null +++ b/Scripts/Engines/Khaldun/KhaldunGen.cs @@ -0,0 +1,211 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Commands +{ + public class GenKhaldun + { + private static int m_Count; + + public static void Initialize() + { + CommandSystem.Register( "GenKhaldun", AccessLevel.Administrator, new CommandEventHandler( GenKhaldun_OnCommand ) ); + } + + public static bool FindMorphItem( int x, int y, int z, int inactiveItemID, int activeItemID ) + { + IPooledEnumerable eable = Map.Felucca.GetItemsInRange( new Point3D( x, y, z ), 0 ); + + foreach ( Item item in eable ) + { + if ( item is MorphItem && item.Z == z && ((MorphItem)item).InactiveItemID == inactiveItemID && ((MorphItem)item).ActiveItemID == activeItemID ) + { + eable.Free(); + return true; + } + } + + eable.Free(); + return false; + } + + public static bool FindEffectController( int x, int y, int z ) + { + IPooledEnumerable eable = Map.Felucca.GetItemsInRange( new Point3D( x, y, z ), 0 ); + + foreach ( Item item in eable ) + { + if ( item is EffectController && item.Z == z ) + { + eable.Free(); + return true; + } + } + + eable.Free(); + return false; + } + + public static Item TryCreateItem( int x, int y, int z, Item srcItem ) + { + IPooledEnumerable eable = Map.Felucca.GetItemsInBounds( new Rectangle2D( x, y, 1, 1 ) ); + + foreach ( Item item in eable ) + { + if ( item.GetType() == srcItem.GetType() ) + { + eable.Free(); + srcItem.Delete(); + return item; + } + } + + eable.Free(); + srcItem.MoveToWorld( new Point3D( x, y, z ), Map.Felucca ); + m_Count++; + + return srcItem; + } + + public static void CreateMorphItem( int x, int y, int z, int inactiveItemID, int activeItemID, int range ) + { + if ( FindMorphItem( x, y, z, inactiveItemID, activeItemID ) ) + return; + + MorphItem item = new MorphItem( inactiveItemID, activeItemID, range, 3 ); + + item.MoveToWorld( new Point3D( x, y, z ), Map.Felucca ); + m_Count++; + } + + public static void CreateApproachLight( int x, int y, int z, int off, int on, LightType light ) + { + if ( FindMorphItem( x, y, z, off, on ) ) + return; + + MorphItem item = new MorphItem( off, on, 2, 3 ); + item.Light = light; + + item.MoveToWorld( new Point3D( x, y, z ), Map.Felucca ); + m_Count++; + } + + public static void CreateSoundEffect( int x, int y, int z, int sound, int range ) + { + if ( FindEffectController( x, y, z ) ) + return; + + EffectController item = new EffectController(); + item.SoundID = sound; + item.TriggerType = EffectTriggerType.InRange; + item.TriggerRange = range; + + item.MoveToWorld( new Point3D( x, y, z ), Map.Felucca ); + m_Count++; + } + + public static void CreateBigTeleporterItem( int x, int y, bool reverse ) + { + if ( FindMorphItem( x, y, 0, reverse ? 0x17DC : 0x17EE, reverse ? 0x17EE : 0x17DC ) ) + return; + + MorphItem item = new MorphItem( reverse ? 0x17DC : 0x17EE, reverse ? 0x17EE : 0x17DC, 1, 3 ); + + item.MoveToWorld( new Point3D( x, y, 0 ), Map.Felucca ); + m_Count++; + } + + public static void GenKhaldun_OnCommand( CommandEventArgs e ) + { + m_Count = 0; + + // Generate Morph Items + CreateMorphItem( 5459, 1416, 0, 0x1D0, 0x1, 1 ); + CreateMorphItem( 5460, 1416, 0, 0x1D0, 0x1, 1 ); + CreateMorphItem( 5459, 1416, 0, 0x1, 0x53D, 1 ); + CreateMorphItem( 5460, 1416, 0, 0x1, 0x53B, 1 ); + + CreateMorphItem( 5459, 1425, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5459, 1426, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5459, 1427, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5460, 1425, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5460, 1426, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5460, 1427, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5461, 1427, 0, 0x1, 0x53B, 2 ); + CreateMorphItem( 5460, 1422, 0, 0x1, 0x544, 2 ); + CreateMorphItem( 5460, 1419, 0, 0x1, 0x545, 2 ); + CreateMorphItem( 5460, 1420, 0, 0x1, 0x545, 2 ); + CreateMorphItem( 5460, 1423, 0, 0x1, 0x545, 2 ); + CreateMorphItem( 5460, 1424, 0, 0x1, 0x545, 2 ); + CreateMorphItem( 5461, 1426, 0, 0x1, 0x545, 2 ); + CreateMorphItem( 5460, 1417, 0, 0x1, 0x546, 1 ); + CreateMorphItem( 5460, 1418, 0, 0x1, 0x546, 2 ); + CreateMorphItem( 5460, 1421, 0, 0x1, 0x546, 2 ); + CreateMorphItem( 5461, 1425, 0, 0x1, 0x548, 2 ); + CreateMorphItem( 5459, 1420, 0, 0x1, 0x54A, 2 ); + CreateMorphItem( 5459, 1421, 0, 0x1, 0x54A, 2 ); + CreateMorphItem( 5459, 1423, 0, 0x1, 0x54A, 2 ); + CreateMorphItem( 5459, 1418, 0, 0x1, 0x54B, 2 ); + CreateMorphItem( 5459, 1422, 0, 0x1, 0x54B, 2 ); + CreateMorphItem( 5459, 1417, 0, 0x1, 0x54C, 1 ); + CreateMorphItem( 5459, 1419, 0, 0x1, 0x54C, 2 ); + CreateMorphItem( 5459, 1424, 0, 0x1, 0x54C, 2 ); + + CreateMorphItem( 5458, 1426, 0, 0x1, 0x1D1, 2 ); + CreateMorphItem( 5459, 1427, 0, 0x1, 0x1E3, 2 ); + CreateMorphItem( 5458, 1425, 3, 0x1, 0x1E4, 2 ); + CreateMorphItem( 5458, 1427, 6, 0x1, 0x1E5, 2 ); + CreateMorphItem( 5461, 1427, 0, 0x1, 0x1E8, 2 ); + CreateMorphItem( 5460, 1427, 0, 0x1, 0x1E9, 2 ); + CreateMorphItem( 5458, 1425, 0, 0x1, 0x1EA, 2 ); + CreateMorphItem( 5458, 1427, 0, 0x1, 0x1EA, 2 ); + CreateMorphItem( 5458, 1427, 3, 0x1, 0x1EA, 2 ); + + // Generate Approach Lights + CreateApproachLight( 5393, 1417, 0, 0x1857, 0x1858, LightType.Circle150 ); + CreateApproachLight( 5393, 1420, 0, 0x1857, 0x1858, LightType.Circle150 ); + CreateApproachLight( 5395, 1421, 0, 0x1857, 0x1858, LightType.Circle150 ); + CreateApproachLight( 5396, 1417, 0, 0x1857, 0x1858, LightType.Circle150 ); + CreateApproachLight( 5397, 1419, 0, 0x1857, 0x1858, LightType.Circle150 ); + + CreateApproachLight( 5441, 1393, 5, 0x1F2B, 0x19BB, LightType.Circle225 ); + CreateApproachLight( 5446, 1393, 5, 0x1F2B, 0x19BB, LightType.Circle225 ); + + // Generate Sound Effects + CreateSoundEffect( 5425, 1489, 5, 0x102, 1 ); + CreateSoundEffect( 5425, 1491, 5, 0x102, 1 ); + + CreateSoundEffect( 5449, 1499, 10, 0xF5, 1 ); + CreateSoundEffect( 5451, 1499, 10, 0xF5, 1 ); + CreateSoundEffect( 5453, 1499, 10, 0xF5, 1 ); + + CreateSoundEffect( 5524, 1367, 0, 0x102, 1 ); + + CreateSoundEffect( 5450, 1370, 0, 0x220, 2 ); + CreateSoundEffect( 5450, 1372, 0, 0x220, 2 ); + + CreateSoundEffect( 5460, 1416, 0, 0x244, 2 ); + + CreateSoundEffect( 5483, 1439, 5, 0x14, 3 ); + + // Generate Big Teleporter + CreateBigTeleporterItem( 5387, 1325, true ); + CreateBigTeleporterItem( 5388, 1326, true ); + CreateBigTeleporterItem( 5388, 1325, false ); + CreateBigTeleporterItem( 5387, 1326, false ); + + // Generate Central Khaldun entrance + DisappearingRaiseSwitch sw = TryCreateItem( 5459, 1426, 10, new DisappearingRaiseSwitch() ) as DisappearingRaiseSwitch; + RaiseSwitch lv = TryCreateItem( 5403, 1359, 0, new RaiseSwitch() ) as RaiseSwitch; + + RaisableItem stone = TryCreateItem( 5403, 1360, 0, new RaisableItem( 0x788, 10, 0x477, 0x475, TimeSpan.FromMinutes( 1.5 ) ) ) as RaisableItem; + RaisableItem door = TryCreateItem( 5524, 1367, 0, new RaisableItem( 0x1D0, 20, 0x477, 0x475, TimeSpan.FromMinutes( 5.0 ) ) ) as RaisableItem; + + sw.RaisableItem = stone; + lv.RaisableItem = door; + + e.Mobile.SendMessage( String.Format( "{0} dynamic Khaldun item{1} generated.", m_Count, m_Count == 1 ? "" : "s" ) ); + } + } +} diff --git a/Scripts/Engines/Khaldun/KhaldunPitTeleporter.cs b/Scripts/Engines/Khaldun/KhaldunPitTeleporter.cs new file mode 100644 index 000000000..4f5853237 --- /dev/null +++ b/Scripts/Engines/Khaldun/KhaldunPitTeleporter.cs @@ -0,0 +1,108 @@ +using System; + +namespace Server.Items +{ + public class KhaldunPitTeleporter : Item + { + private bool m_Active; + private Point3D m_PointDest; + private Map m_MapDest; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get { return m_Active; } + set { m_Active = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D PointDest + { + get { return m_PointDest; } + set { m_PointDest = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map MapDest + { + get { return m_MapDest; } + set { m_MapDest = value; } + } + + public override int LabelNumber{ get{ return 1016511; } } // the floor of the cavern seems to have collapsed here - a faint light is visible at the bottom of the pit + + [Constructable] + public KhaldunPitTeleporter() : this( new Point3D( 5451, 1374, 0 ), Map.Felucca ) + { + } + + [Constructable] + public KhaldunPitTeleporter( Point3D pointDest, Map mapDest ) : base( 0x053B ) + { + Movable = false; + Hue = 1; + + m_Active = true; + m_PointDest = pointDest; + m_MapDest = mapDest; + } + + public KhaldunPitTeleporter( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile m ) + { + if ( !m_Active ) + return; + + Map map = m_MapDest; + + if ( map == null || map == Map.Internal ) + map = m.Map; + + Point3D p = m_PointDest; + + if ( p == Point3D.Zero ) + p = m.Location; + + if ( m.InRange( this, 3 ) ) + { + Server.Mobiles.BaseCreature.TeleportPets( m, m_PointDest, m_MapDest ); + + m.MoveToWorld( m_PointDest, m_MapDest ); + } + else + { + m.SendLocalizedMessage( 1019045 ); // I can't reach that. + } + } + + public override void OnDoubleClickDead( Mobile m ) + { + OnDoubleClick( m ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( m_Active ); + writer.Write( m_PointDest ); + writer.Write( m_MapDest ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Active = reader.ReadBool(); + m_PointDest = reader.ReadPoint3D(); + m_MapDest = reader.ReadMap(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Mobiles/GrimmochDrummel.cs b/Scripts/Engines/Khaldun/Mobiles/GrimmochDrummel.cs new file mode 100644 index 000000000..ce18780c3 --- /dev/null +++ b/Scripts/Engines/Khaldun/Mobiles/GrimmochDrummel.cs @@ -0,0 +1,131 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + public class GrimmochDrummel : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + [Constructable] + public GrimmochDrummel() : base( AIType.AI_Archer, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Title = "the Cursed"; + + Hue = 0x8596; + Body = 0x190; + Name = "Grimmoch Drummel"; + + HairItemID = 0x204A; //Krisna + + Bow bow = new Bow(); + bow.Movable = false; + AddItem( bow ); + + AddItem( new Boots( 0x8A4 ) ); + AddItem( new BodySash( 0x8A4 ) ); + + Backpack backpack = new Backpack(); + backpack.Movable = false; + AddItem( backpack ); + + LeatherGloves gloves = new LeatherGloves(); + LeatherChest chest = new LeatherChest(); + gloves.Hue = 0x96F; + chest.Hue = 0x96F; + + AddItem( gloves ); + AddItem( chest ); + + SetStr( 111, 120 ); + SetDex( 151, 160 ); + SetInt( 41, 50 ); + + SetHits( 180, 207 ); + SetMana( 0 ); + + SetDamage( 13, 16 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 45, 55 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 25 ); + + SetSkill( SkillName.Archery, 90.1, 110.0 ); + SetSkill( SkillName.Swords, 60.1, 70.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 60.1, 70.0 ); + SetSkill( SkillName.Anatomy, 90.1, 100.0 ); + + Fame = 5000; + Karma = -1000; + + PackItem( new Arrow( 40 ) ); + + if ( 3 > Utility.Random( 100 ) ) + PackItem( new FireHorn() ); + + if ( 1 > Utility.Random( 3 ) ) + PackItem( Loot.RandomGrimmochJournal() ); + } + + public override int GetIdleSound() + { + return 0x178; + } + + public override int GetAngerSound() + { + return 0x1AC; + } + + public override int GetDeathSound() + { + return 0x27E; + } + + public override int GetHurtSound() + { + return 0x177; + } + + public override bool OnBeforeDeath() + { + Gold gold = new Gold( Utility.RandomMinMax( 190, 230 ) ); + gold.MoveToWorld( Location, Map ); + + Container pack = this.Backpack; + if ( pack != null ) + { + pack.Movable = true; + pack.MoveToWorld( Location, Map ); + } + + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public GrimmochDrummel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Mobiles/LysanderGathenwale.cs b/Scripts/Engines/Khaldun/Mobiles/LysanderGathenwale.cs new file mode 100644 index 000000000..b9b1a6c45 --- /dev/null +++ b/Scripts/Engines/Khaldun/Mobiles/LysanderGathenwale.cs @@ -0,0 +1,132 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + public class LysanderGathenwale : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + [Constructable] + public LysanderGathenwale() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Title = "the Cursed"; + + Hue = 0x8838; + Body = 0x190; + Name = "Lysander Gathenwale"; + + AddItem( new Boots( 0x599 ) ); + AddItem( new Cloak( 0x96F ) ); + + Spellbook spellbook = new Spellbook(); + RingmailGloves gloves = new RingmailGloves(); + StuddedChest chest = new StuddedChest(); + PlateArms arms = new PlateArms(); + + spellbook.Hue = 0x599; + gloves.Hue = 0x599; + chest.Hue = 0x96F; + arms.Hue = 0x599; + + AddItem( spellbook ); + AddItem( gloves ); + AddItem( chest ); + AddItem( arms ); + + SetStr( 111, 120 ); + SetDex( 71, 80 ); + SetInt( 121, 130 ); + + SetHits( 180, 207 ); + SetMana( 227, 265 ); + + SetDamage( 5, 13 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 80.1, 90.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.EvalInt, 95.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + + Fame = 5000; + Karma = -10000; + + Item reags = Loot.RandomReagent(); + reags.Amount = 30; + PackItem( reags ); + } + + public override int GetIdleSound() + { + return 0x1CE; + } + + public override int GetAngerSound() + { + return 0x1AC; + } + + public override int GetDeathSound() + { + return 0x182; + } + + public override int GetHurtSound() + { + return 0x28D; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + if ( Backpack != null ) + Backpack.Destroy(); + + if ( Utility.Random( 3 ) == 0 ) + { + BaseBook notebook = Loot.RandomLysanderNotebook(); + notebook.MoveToWorld( Location, Map ); + } + + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public LysanderGathenwale( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Mobiles/MorgBergen.cs b/Scripts/Engines/Khaldun/Mobiles/MorgBergen.cs new file mode 100644 index 000000000..870b4d8ba --- /dev/null +++ b/Scripts/Engines/Khaldun/Mobiles/MorgBergen.cs @@ -0,0 +1,111 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + public class MorgBergen : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + [Constructable] + public MorgBergen() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Title = "the Cursed"; + + Hue = 0x8596; + Body = 0x190; + Name = "Morg Bergen"; + + AddItem( new ShortPants( 0x59C ) ); + + Bardiche bardiche = new Bardiche(); + LeatherGloves gloves = new LeatherGloves(); + LeatherArms arms = new LeatherArms(); + + bardiche.Hue = 0x96F; bardiche.Movable = false; + gloves.Hue = 0x96F; + arms.Hue = 0x96F; + + AddItem( bardiche ); + AddItem( gloves ); + AddItem( arms ); + + SetStr( 111, 120 ); + SetDex( 111, 120 ); + SetInt( 51, 60 ); + + SetHits( 180, 207 ); + SetMana( 0 ); + + SetDamage( 9, 17 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Cold, 60 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Swords, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 80.1, 90.0 ); + SetSkill( SkillName.Anatomy, 90.1, 100.0 ); + + Fame = 5000; + Karma = -1000; + } + + public override int GetIdleSound() + { + return 0x1CE; + } + + public override int GetAngerSound() + { + return 0x263; + } + + public override int GetDeathSound() + { + return 0x1D1; + } + + public override int GetHurtSound() + { + return 0x25E; + } + + public override bool OnBeforeDeath() + { + Gold gold = new Gold( Utility.RandomMinMax( 190, 230 ) ); + gold.MoveToWorld( Location, Map ); + + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public MorgBergen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/Mobiles/TavaraSewel.cs b/Scripts/Engines/Khaldun/Mobiles/TavaraSewel.cs new file mode 100644 index 000000000..5a53f2d09 --- /dev/null +++ b/Scripts/Engines/Khaldun/Mobiles/TavaraSewel.cs @@ -0,0 +1,120 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + public class TavaraSewel : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + [Constructable] + public TavaraSewel () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Title = "the Cursed"; + + Hue = 0x8838; + Female = true; + Body = 0x191; + Name = "Tavara Sewel"; + + AddItem( new Kilt( 0x59C ) ); + AddItem( new Sandals( 0x599 ) ); + + Kryss kryss = new Kryss(); + Buckler buckler = new Buckler(); + RingmailGloves gloves = new RingmailGloves(); + FemalePlateChest chest = new FemalePlateChest(); + + kryss.Hue = 0x96F; kryss.Movable = false; + buckler.Hue = 0x96F; buckler.Movable = false; + gloves.Hue = 0x599; + chest.Hue = 0x96F; + + AddItem( kryss ); + AddItem( buckler ); + AddItem( gloves ); + AddItem( chest ); + + SetStr( 111, 120 ); + SetDex( 111, 120 ); + SetInt( 111, 120 ); + + SetHits( 180, 207 ); + SetStam( 126, 150 ); + SetMana( 0 ); + + SetDamage( 13, 16 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Fencing, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 80.1, 90.0 ); + SetSkill( SkillName.Anatomy, 90.1, 100.0 ); + + Fame = 5000; + Karma = -1000; + } + + public override int GetIdleSound() + { + return 0x27F; + } + + public override int GetAngerSound() + { + return 0x258; + } + + public override int GetDeathSound() + { + return 0x25B; + } + + public override int GetHurtSound() + { + return 0x257; + } + + public override bool OnBeforeDeath() + { + Gold gold = new Gold( Utility.RandomMinMax( 190, 230 ) ); + gold.MoveToWorld( Location, Map ); + + if ( Utility.Random( 3 ) == 0 ) + { + BaseBook journal = Loot.RandomTavarasJournal(); + journal.MoveToWorld( Location, Map ); + } + + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public TavaraSewel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/PuzzleChest.cs b/Scripts/Engines/Khaldun/PuzzleChest.cs new file mode 100644 index 000000000..ed65f9bfb --- /dev/null +++ b/Scripts/Engines/Khaldun/PuzzleChest.cs @@ -0,0 +1,788 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public enum PuzzleChestCylinder + { + None = 0xE73, + LightBlue = 0x186F, + Blue = 0x186A, + Green = 0x186B, + Orange = 0x186C, + Purple = 0x186D, + Red = 0x186E, + DarkBlue = 0x1869, + Yellow = 0x1870 + } + + public class PuzzleChestSolution + { + public const int Length = 5; + + private PuzzleChestCylinder[] m_Cylinders = new PuzzleChestCylinder[Length]; + + public PuzzleChestCylinder[] Cylinders{ get{ return m_Cylinders; } } + + public PuzzleChestCylinder First{ get{ return m_Cylinders[0]; } set{ m_Cylinders[0] = value; } } + public PuzzleChestCylinder Second{ get{ return m_Cylinders[1]; } set{ m_Cylinders[1] = value; } } + public PuzzleChestCylinder Third{ get{ return m_Cylinders[2]; } set{ m_Cylinders[2] = value; } } + public PuzzleChestCylinder Fourth{ get{ return m_Cylinders[3]; } set{ m_Cylinders[3] = value; } } + public PuzzleChestCylinder Fifth{ get{ return m_Cylinders[4]; } set{ m_Cylinders[4] = value; } } + + public static PuzzleChestCylinder RandomCylinder() + { + switch ( Utility.Random( 8 ) ) + { + case 0: return PuzzleChestCylinder.LightBlue; + case 1: return PuzzleChestCylinder.Blue; + case 2: return PuzzleChestCylinder.Green; + case 3: return PuzzleChestCylinder.Orange; + case 4: return PuzzleChestCylinder.Purple; + case 5: return PuzzleChestCylinder.Red; + case 6: return PuzzleChestCylinder.DarkBlue; + default: return PuzzleChestCylinder.Yellow; + } + } + + public PuzzleChestSolution() + { + for ( int i = 0; i < m_Cylinders.Length; i++ ) + { + m_Cylinders[i] = RandomCylinder(); + } + } + + public PuzzleChestSolution( PuzzleChestCylinder first, PuzzleChestCylinder second, PuzzleChestCylinder third, PuzzleChestCylinder fourth, PuzzleChestCylinder fifth ) + { + First = first; + Second = second; + Third = third; + Fourth = fourth; + Fifth = fifth; + } + + public PuzzleChestSolution( PuzzleChestSolution solution ) + { + for ( int i = 0; i < m_Cylinders.Length; i++ ) + { + m_Cylinders[i] = solution.m_Cylinders[i]; + } + } + + public bool Matches( PuzzleChestSolution solution, out int cylinders, out int colors ) + { + cylinders = 0; + colors = 0; + + bool[] matchesSrc = new bool[solution.m_Cylinders.Length]; + bool[] matchesDst = new bool[solution.m_Cylinders.Length]; + + for ( int i = 0; i < m_Cylinders.Length; i++ ) + { + if ( m_Cylinders[i] == solution.m_Cylinders[i] ) + { + cylinders++; + + matchesSrc[i] = true; + matchesDst[i] = true; + } + } + + for ( int i = 0; i < m_Cylinders.Length; i++ ) + { + if ( !matchesSrc[i] ) + { + for ( int j = 0; j < solution.m_Cylinders.Length; j++ ) + { + if ( m_Cylinders[i] == solution.m_Cylinders[j] && !matchesDst[j] ) + { + colors++; + + matchesDst[j] = true; + } + } + } + } + + return cylinders == m_Cylinders.Length; + } + + public virtual void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Cylinders.Length ); + for ( int i = 0; i < m_Cylinders.Length; i++ ) + { + writer.Write( (int) m_Cylinders[i] ); + } + } + + public PuzzleChestSolution( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + int length = reader.ReadEncodedInt(); + for ( int i = 0; ; i++ ) + { + if ( i < length ) + { + PuzzleChestCylinder cylinder = (PuzzleChestCylinder) reader.ReadInt(); + + if ( i < m_Cylinders.Length ) + m_Cylinders[i] = cylinder; + } + else if ( i < m_Cylinders.Length ) + { + m_Cylinders[i] = RandomCylinder(); + } + else + { + break; + } + } + } + } + + public class PuzzleChestSolutionAndTime : PuzzleChestSolution + { + private DateTime m_When; + + public DateTime When{ get{ return m_When; } } + + public PuzzleChestSolutionAndTime( DateTime when, PuzzleChestSolution solution ) : base( solution ) + { + m_When = when; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteDeltaTime( m_When ); + } + + public PuzzleChestSolutionAndTime( GenericReader reader ) : base( reader ) + { + int version = reader.ReadEncodedInt(); + + m_When = reader.ReadDeltaTime(); + } + } + + public abstract class PuzzleChest : BaseTreasureChest + { + public const int HintsCount = 3; + public readonly TimeSpan CleanupTime = TimeSpan.FromHours( 1.0 ); + + private PuzzleChestSolution m_Solution; + private PuzzleChestCylinder[] m_Hints = new PuzzleChestCylinder[HintsCount]; + private Hashtable m_Guesses = new Hashtable(); + + public PuzzleChestSolution Solution + { + get{ return m_Solution; } + set + { + m_Solution = value; + InitHints(); + } + } + + public PuzzleChestCylinder[] Hints{ get{ return m_Hints; } } + + public PuzzleChestCylinder FirstHint{ get{ return m_Hints[0]; } set{ m_Hints[0] = value; } } + public PuzzleChestCylinder SecondHint{ get{ return m_Hints[1]; } set{ m_Hints[1] = value; } } + public PuzzleChestCylinder ThirdHint{ get{ return m_Hints[2]; } set{ m_Hints[2] = value; } } + + public override string DefaultName + { + get { return null; } + } + + public PuzzleChest( int itemID ) : base( itemID ) + { + } + + private void InitHints() + { + ArrayList list = new ArrayList( Solution.Cylinders.Length - 1 ); + for ( int i = 1; i < Solution.Cylinders.Length; i++ ) + { + list.Add( Solution.Cylinders[i] ); + } + + m_Hints = new PuzzleChestCylinder[HintsCount]; + for ( int i = 0; i < m_Hints.Length; i++ ) + { + int pos = Utility.Random( list.Count ); + m_Hints[i] = (PuzzleChestCylinder)list[pos]; + list.RemoveAt( pos ); + } + } + + protected override void SetLockLevel() + { + LockLevel = 0; // Can't be unlocked + } + + public override bool CheckLocked( Mobile from ) + { + if ( Locked ) + { + PuzzleChestSolution solution = GetLastGuess( from ); + if ( solution != null ) + solution = new PuzzleChestSolution( solution ); + else + solution = new PuzzleChestSolution( PuzzleChestCylinder.None, PuzzleChestCylinder.None, PuzzleChestCylinder.None, PuzzleChestCylinder.None, PuzzleChestCylinder.None ); + + from.CloseGump( typeof( PuzzleGump ) ); + from.CloseGump( typeof( StatusGump ) ); + from.SendGump( new PuzzleGump( from, this, solution, 0 ) ); + + return true; + } + else + { + return false; + } + } + + public PuzzleChestSolutionAndTime GetLastGuess( Mobile m ) + { + return (PuzzleChestSolutionAndTime)m_Guesses[m]; + } + + public void SubmitSolution( Mobile m, PuzzleChestSolution solution ) + { + int correctCylinders, correctColors; + + if ( solution.Matches( this.Solution, out correctCylinders, out correctColors ) ) + { + LockPick( m ); + + DisplayTo( m ); + } + else + { + m_Guesses[m] = new PuzzleChestSolutionAndTime( DateTime.Now, solution ); + + m.SendGump( new StatusGump( correctCylinders, correctColors ) ); + + DoDamage( m ); + } + } + + public void DoDamage( Mobile to ) + { + switch ( Utility.Random( 4 ) ) + { + case 0: + { + Effects.SendLocationEffect( to, to.Map, 0x113A, 20, 10 ); + to.PlaySound( 0x231 ); + to.LocalOverheadMessage( MessageType.Regular, 0x44, 1010523 ); // A toxic vapor envelops thee. + + to.ApplyPoison( to, Poison.Regular ); + + break; + } + case 1: + { + Effects.SendLocationEffect( to, to.Map, 0x3709, 30 ); + to.PlaySound( 0x54 ); + to.LocalOverheadMessage( MessageType.Regular, 0xEE, 1010524 ); // Searing heat scorches thy skin. + + AOS.Damage( to, to, Utility.RandomMinMax( 10, 40 ), 0, 100, 0, 0, 0 ); + + break; + } + case 2: + { + to.PlaySound( 0x223 ); + to.LocalOverheadMessage( MessageType.Regular, 0x62, 1010525 ); // Pain lances through thee from a sharp metal blade. + + AOS.Damage( to, to, Utility.RandomMinMax( 10, 40 ), 100, 0, 0, 0, 0 ); + + break; + } + default: + { + to.BoltEffect( 0 ); + to.LocalOverheadMessage( MessageType.Regular, 0xDA, 1010526 ); // Lightning arcs through thy body. + + AOS.Damage( to, to, Utility.RandomMinMax( 10, 40 ), 0, 0, 0, 0, 100 ); + + break; + } + } + } + + private class PuzzleGump : Gump + { + private Mobile m_From; + private PuzzleChest m_Chest; + private PuzzleChestSolution m_Solution; + + public PuzzleGump( Mobile from, PuzzleChest chest, PuzzleChestSolution solution, int check ) : base( 50, 50 ) + { + m_From = from; + m_Chest = chest; + m_Solution = solution; + + Dragable = false; + + AddBackground( 25, 0, 500, 410, 0x53 ); + + AddImage( 62, 20, 0x67 ); + + AddHtmlLocalized( 80, 36, 110, 70, 1018309, true, false ); // A Puzzle Lock + + /* Correctly choose the sequence of cylinders needed to open the latch. Each cylinder + * may potentially be used more than once. Beware! A false attempt could be deadly! + */ + AddHtmlLocalized( 214, 26, 270, 90, 1018310, true, true ); + + AddLeftCylinderButton( 62, 130, PuzzleChestCylinder.LightBlue, 10 ); + AddLeftCylinderButton( 62, 180, PuzzleChestCylinder.Blue, 11 ); + AddLeftCylinderButton( 62, 230, PuzzleChestCylinder.Green, 12 ); + AddLeftCylinderButton( 62, 280, PuzzleChestCylinder.Orange, 13 ); + + AddRightCylinderButton( 451, 130, PuzzleChestCylinder.Purple, 14 ); + AddRightCylinderButton( 451, 180, PuzzleChestCylinder.Red, 15 ); + AddRightCylinderButton( 451, 230, PuzzleChestCylinder.DarkBlue, 16 ); + AddRightCylinderButton( 451, 280, PuzzleChestCylinder.Yellow, 17 ); + + double lockpicking = from.Skills.Lockpicking.Base; + if ( lockpicking >= 60.0 ) + { + AddHtmlLocalized( 160, 125, 230, 24, 1018308, false, false ); // Lockpicking hint: + + AddBackground( 159, 150, 230, 95, 0x13EC ); + + if ( lockpicking >= 80.0 ) + { + AddHtmlLocalized( 165, 157, 200, 40, 1018312, false, false ); // In the first slot: + AddCylinder( 350, 165, chest.Solution.First ); + + AddHtmlLocalized( 165, 197, 200, 40, 1018313, false, false ); // Used in unknown slot: + AddCylinder( 350, 200, chest.FirstHint ); + + if ( lockpicking >= 90.0 ) + AddCylinder( 350, 212, chest.SecondHint ); + + if ( lockpicking >= 100.0 ) + AddCylinder( 350, 224, chest.ThirdHint ); + } + else + { + AddHtmlLocalized( 165, 157, 200, 40, 1018313, false, false ); // Used in unknown slot: + AddCylinder( 350, 160, chest.FirstHint ); + + if ( lockpicking >= 70.0 ) + AddCylinder( 350, 172, chest.SecondHint ); + } + } + + PuzzleChestSolution lastGuess = chest.GetLastGuess( from ); + if ( lastGuess != null ) + { + AddHtmlLocalized( 127, 249, 170, 20, 1018311, false, false ); // Thy previous guess: + + AddBackground( 290, 247, 115, 25, 0x13EC ); + + AddCylinder( 281, 254, lastGuess.First ); + AddCylinder( 303, 254, lastGuess.Second ); + AddCylinder( 325, 254, lastGuess.Third ); + AddCylinder( 347, 254, lastGuess.Fourth ); + AddCylinder( 369, 254, lastGuess.Fifth ); + } + + AddPedestal( 140, 270, solution.First, 0, check == 0 ); + AddPedestal( 195, 270, solution.Second, 1, check == 1 ); + AddPedestal( 250, 270, solution.Third, 2, check == 2 ); + AddPedestal( 305, 270, solution.Fourth, 3, check == 3 ); + AddPedestal( 360, 270, solution.Fifth, 4, check == 4 ); + + AddButton( 258, 370, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + } + + private void AddLeftCylinderButton( int x, int y, PuzzleChestCylinder cylinder, int buttonID ) + { + AddBackground( x, y, 30, 30, 0x13EC ); + AddCylinder( x - 7, y + 10, cylinder ); + AddButton( x + 38, y + 9, 0x13A8, 0x4B9, buttonID, GumpButtonType.Reply, 0 ); + } + + private void AddRightCylinderButton( int x, int y, PuzzleChestCylinder cylinder, int buttonID ) + { + AddBackground( x, y, 30, 30, 0x13EC ); + AddCylinder( x - 7, y + 10, cylinder ); + AddButton( x - 26, y + 9, 0x13A8, 0x4B9, buttonID, GumpButtonType.Reply, 0 ); + } + + private void AddPedestal( int x, int y, PuzzleChestCylinder cylinder, int switchID, bool initialState ) + { + AddItem( x, y, 0xB10 ); + AddItem( x - 23, y + 12, 0xB12 ); + AddItem( x + 23, y + 12, 0xB13 ); + AddItem( x, y + 23, 0xB11 ); + + if ( cylinder != PuzzleChestCylinder.None ) + { + AddItem( x, y + 2, 0x51A ); + AddCylinder( x - 1, y + 19, cylinder ); + } + else + { + AddItem( x, y + 2, 0x521 ); + } + + AddRadio( x + 7, y + 65, 0x867, 0x86A, initialState, switchID ); + } + + private void AddCylinder( int x, int y, PuzzleChestCylinder cylinder ) + { + if ( cylinder != PuzzleChestCylinder.None ) + AddItem( x, y, (int)cylinder ); + else + AddItem( x + 9, y, (int)cylinder ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Chest.Deleted || info.ButtonID == 0 || !m_From.CheckAlive() ) + return; + + if ( m_From.AccessLevel == AccessLevel.Player && ( m_From.Map != m_Chest.Map || !m_From.InRange( m_Chest.GetWorldLocation(), 2 ) ) ) + { + m_From.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500446 ); // That is too far away. + return; + } + + if ( info.ButtonID == 1 ) + { + m_Chest.SubmitSolution( m_From, m_Solution ); + } + else + { + if ( info.Switches.Length == 0 ) + return; + + int pedestal = info.Switches[0]; + if ( pedestal < 0 || pedestal >= m_Solution.Cylinders.Length ) + return; + + PuzzleChestCylinder cylinder; + switch ( info.ButtonID ) + { + case 10: cylinder = PuzzleChestCylinder.LightBlue; break; + case 11: cylinder = PuzzleChestCylinder.Blue; break; + case 12: cylinder = PuzzleChestCylinder.Green; break; + case 13: cylinder = PuzzleChestCylinder.Orange; break; + case 14: cylinder = PuzzleChestCylinder.Purple; break; + case 15: cylinder = PuzzleChestCylinder.Red; break; + case 16: cylinder = PuzzleChestCylinder.DarkBlue; break; + case 17: cylinder = PuzzleChestCylinder.Yellow; break; + default: return; + } + + m_Solution.Cylinders[pedestal] = cylinder; + + m_From.SendGump( new PuzzleGump( m_From, m_Chest, m_Solution, pedestal ) ); + } + } + } + + private class StatusGump : Gump + { + public StatusGump( int correctCylinders, int correctColors ) : base( 50, 50 ) + { + AddBackground( 15, 250, 305, 163, 0x53 ); + AddBackground( 28, 265, 280, 133, 0xBB8 ); + + AddHtmlLocalized( 35, 271, 270, 24, 1018314, false, false ); // Thou hast failed to solve the puzzle! + + AddHtmlLocalized( 35, 297, 250, 24, 1018315, false, false ); // Correctly placed colors: + AddLabel( 285, 297, 0x44, correctCylinders.ToString() ); + + AddHtmlLocalized( 35, 323, 250, 24, 1018316, false, false ); // Used colors in wrong slots: + AddLabel( 285, 323, 0x44, correctColors.ToString() ); + + AddButton( 152, 369, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + } + } + + public override void LockPick( Mobile from ) + { + base.LockPick( from ); + + m_Guesses.Clear(); + } + + private static void GetRandomAOSStats( out int attributeCount, out int min, out int max ) + { + int rnd = Utility.Random( 15 ); + + if ( rnd < 1 ) + { + attributeCount = Utility.RandomMinMax( 2, 6 ); + min = 20; max = 70; + } + else if ( rnd < 3 ) + { + attributeCount = Utility.RandomMinMax( 2, 4 ); + min = 20; max = 50; + } + else if ( rnd < 6 ) + { + attributeCount = Utility.RandomMinMax( 2, 3 ); + min = 20; max = 40; + } + else if ( rnd < 10 ) + { + attributeCount = Utility.RandomMinMax( 1, 2 ); + min = 10; max = 30; + } + else + { + attributeCount = 1; + min = 10; max = 20; + } + } + + protected override void GenerateTreasure() + { + DropItem( new Gold( 600, 900 ) ); + + ArrayList gems = new ArrayList(); + for ( int i = 0; i < 9; i++ ) + { + Item gem = Loot.RandomGem(); + Type gemType = gem.GetType(); + + foreach ( Item listGem in gems ) + { + if ( listGem.GetType() == gemType ) + { + listGem.Amount++; + gem.Delete(); + break; + } + } + + if ( !gem.Deleted ) + gems.Add( gem ); + } + + foreach ( Item gem in gems ) + { + DropItem( gem ); + } + + + if ( 0.2 > Utility.RandomDouble() ) + DropItem( new BagOfReagents( 50 ) ); + + for ( int i = 0; i < 2; i++ ) + { + Item item; + + if ( Core.AOS ) + item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(); + else + item = Loot.RandomArmorOrShieldOrWeapon(); + + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( Core.AOS ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( weapon, attributeCount, min, max ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random( 6 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random( 6 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random( 6 ); + } + + DropItem( item ); + } + else if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( Core.AOS ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( armor, attributeCount, min, max ); + } + else + { + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random( 6 ); + armor.Durability = (ArmorDurabilityLevel)Utility.Random( 6 ); + } + + DropItem( item ); + } + else if ( item is BaseJewel ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, attributeCount, min, max ); + + DropItem( item ); + } + } + + Solution = new PuzzleChestSolution(); + } + + public void CleanupGuesses() + { + ArrayList toDelete = new ArrayList(); + + foreach ( DictionaryEntry entry in m_Guesses ) + { + if ( DateTime.Now - ((PuzzleChestSolutionAndTime)entry.Value).When > CleanupTime ) + toDelete.Add( entry.Key ); + } + + foreach ( Mobile m in toDelete ) + { + m_Guesses.Remove( m ); + } + } + + public PuzzleChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + CleanupGuesses(); + + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + m_Solution.Serialize( writer ); + + writer.WriteEncodedInt( (int) m_Hints.Length ); + for ( int i = 0; i < m_Hints.Length; i++ ) + { + writer.Write( (int) m_Hints[i] ); + } + + writer.WriteEncodedInt( (int) m_Guesses.Count ); + foreach ( DictionaryEntry entry in m_Guesses ) + { + writer.Write( (Mobile) entry.Key ); + ((PuzzleChestSolutionAndTime)entry.Value).Serialize( writer ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Solution = new PuzzleChestSolution( reader ); + + int length = reader.ReadEncodedInt(); + for ( int i = 0; i < length; i++ ) + { + PuzzleChestCylinder cylinder = (PuzzleChestCylinder) reader.ReadInt(); + + if ( length == m_Hints.Length ) + m_Hints[i] = cylinder; + } + if ( length != m_Hints.Length ) + InitHints(); + + int guesses = reader.ReadEncodedInt(); + for ( int i = 0; i < guesses; i++ ) + { + Mobile m = reader.ReadMobile(); + PuzzleChestSolutionAndTime sol = new PuzzleChestSolutionAndTime( reader ); + + m_Guesses[m] = sol; + } + } + } + + [FlipableAttribute( 0xE41, 0xE40 )] + public class MetalGoldenPuzzleChest : PuzzleChest + { + [Constructable] + public MetalGoldenPuzzleChest() : base( 0xE41 ) + { + } + + public MetalGoldenPuzzleChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + [FlipableAttribute( 0xE80, 0x9A8 )] + public class StrongBoxPuzzle : PuzzleChest + { + [Constructable] + public StrongBoxPuzzle() : base( 0xE80 ) + { + } + + public StrongBoxPuzzle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/RaisableItem.cs b/Scripts/Engines/Khaldun/RaisableItem.cs new file mode 100644 index 000000000..bf1196675 --- /dev/null +++ b/Scripts/Engines/Khaldun/RaisableItem.cs @@ -0,0 +1,177 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RaisableItem : Item + { + private int m_MaxElevation; + private int m_MoveSound; + private int m_StopSound; + private TimeSpan m_CloseDelay; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxElevation + { + get{ return m_MaxElevation; } + set + { + if ( value <= 0 ) + m_MaxElevation = 0; + else if ( value >= 60 ) + m_MaxElevation = 60; + else + m_MaxElevation = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MoveSound{ get{ return m_MoveSound; } set{ m_MoveSound = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int StopSound{ get{ return m_StopSound; } set{ m_StopSound = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan CloseDelay{ get{ return m_CloseDelay; } set{ m_CloseDelay = value; } } + + [Constructable] + public RaisableItem( int itemID ) : this( itemID, 20, -1, -1, TimeSpan.FromMinutes( 1.0 ) ) + { + } + + [Constructable] + public RaisableItem( int itemID, int maxElevation, TimeSpan closeDelay ) : this( itemID, maxElevation, -1, -1, closeDelay ) + { + } + + [Constructable] + public RaisableItem( int itemID, int maxElevation, int moveSound, int stopSound, TimeSpan closeDelay ) : base( itemID ) + { + Movable = false; + + m_MaxElevation = maxElevation; + m_MoveSound = moveSound; + m_StopSound = stopSound; + m_CloseDelay = closeDelay; + } + + private int m_Elevation; + private RaiseTimer m_RaiseTimer; + + public bool IsRaisable + { + get{ return m_RaiseTimer == null; } + } + + public void Raise() + { + if ( !IsRaisable ) + return; + + m_RaiseTimer = new RaiseTimer( this ); + m_RaiseTimer.Start(); + } + + private class RaiseTimer : Timer + { + private RaisableItem m_Item; + private DateTime m_CloseTime; + private bool m_Up; + private int m_Step; + + public RaiseTimer( RaisableItem item ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 0.5 ) ) + { + m_Item = item; + m_CloseTime = DateTime.Now + item.CloseDelay; + m_Up = true; + + Priority = TimerPriority.TenMS; + } + + protected override void OnTick() + { + if ( m_Item.Deleted ) + { + Stop(); + return; + } + + if ( m_Step++ % 3 == 0 ) + { + if ( m_Up ) + { + m_Item.Z++; + + if ( ++m_Item.m_Elevation >= m_Item.MaxElevation ) + { + Stop(); + + if ( m_Item.StopSound >= 0 ) + Effects.PlaySound( m_Item.Location, m_Item.Map, m_Item.StopSound ); + + m_Up = false; + m_Step = 0; + + TimeSpan delay = m_CloseTime - DateTime.Now; + Timer.DelayCall( delay > TimeSpan.Zero ? delay : TimeSpan.Zero, new TimerCallback( Start ) ); + + return; + } + } + else + { + m_Item.Z--; + + if ( --m_Item.m_Elevation <= 0 ) + { + Stop(); + + if ( m_Item.StopSound >= 0 ) + Effects.PlaySound( m_Item.Location, m_Item.Map, m_Item.StopSound ); + + m_Item.m_RaiseTimer = null; + + return; + } + } + } + + if ( m_Item.MoveSound >= 0 ) + Effects.PlaySound( m_Item.Location, m_Item.Map, m_Item.MoveSound ); + } + } + + public RaisableItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_MaxElevation ); + writer.WriteEncodedInt( (int) m_MoveSound ); + writer.WriteEncodedInt( (int) m_StopSound ); + writer.Write( (TimeSpan) m_CloseDelay ); + + writer.WriteEncodedInt( (int) m_Elevation ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_MaxElevation = reader.ReadEncodedInt(); + m_MoveSound = reader.ReadEncodedInt(); + m_StopSound = reader.ReadEncodedInt(); + m_CloseDelay = reader.ReadTimeSpan(); + + int elevation = reader.ReadEncodedInt(); + this.Z -= elevation; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Khaldun/RaiseSwitch.cs b/Scripts/Engines/Khaldun/RaiseSwitch.cs new file mode 100644 index 000000000..13c1211d5 --- /dev/null +++ b/Scripts/Engines/Khaldun/RaiseSwitch.cs @@ -0,0 +1,223 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class RaiseSwitch : Item + { + private RaisableItem m_RaisableItem; + + [CommandProperty( AccessLevel.GameMaster )] + public RaisableItem RaisableItem + { + get{ return m_RaisableItem; } + set{ m_RaisableItem = value; } + } + + [Constructable] + public RaiseSwitch() : this( 0x1093 ) + { + } + + protected RaiseSwitch( int itemID ) : base( itemID ) + { + Movable = false; + } + + public override void OnDoubleClick( Mobile m ) + { + if ( !m.InRange( this, 2 ) ) + { + m.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + if ( RaisableItem != null && RaisableItem.Deleted ) + RaisableItem = null; + + Flip(); + + if ( RaisableItem != null ) + { + if ( RaisableItem.IsRaisable ) + { + RaisableItem.Raise(); + m.LocalOverheadMessage( MessageType.Regular, 0x5A, true, "You hear a grinding noise echoing in the distance." ); + } + else + { + m.LocalOverheadMessage( MessageType.Regular, 0x5A, true, "You flip the switch again, but nothing happens." ); + } + } + } + + protected virtual void Flip() + { + if ( ItemID != 0x1093 ) + { + ItemID = 0x1093; + + StopResetTimer(); + } + else + { + ItemID = 0x1095; + + if ( RaisableItem != null && RaisableItem.CloseDelay >= TimeSpan.Zero ) + StartResetTimer( RaisableItem.CloseDelay ); + else + StartResetTimer( TimeSpan.FromMinutes( 2.0 ) ); + } + + Effects.PlaySound( Location, Map, 0x3E8 ); + } + + private ResetTimer m_ResetTimer; + + protected void StartResetTimer( TimeSpan delay ) + { + StopResetTimer(); + + m_ResetTimer = new ResetTimer( this, delay ); + m_ResetTimer.Start(); + } + + protected void StopResetTimer() + { + if ( m_ResetTimer != null ) + { + m_ResetTimer.Stop(); + m_ResetTimer = null; + } + } + + protected virtual void Reset() + { + if ( ItemID != 0x1093 ) + Flip(); + } + + private class ResetTimer : Timer + { + private RaiseSwitch m_RaiseSwitch; + + public ResetTimer( RaiseSwitch raiseSwitch, TimeSpan delay ) : base( delay ) + { + m_RaiseSwitch = raiseSwitch; + + Priority = ComputePriority( delay ); + } + + protected override void OnTick() + { + if ( m_RaiseSwitch.Deleted ) + return; + + m_RaiseSwitch.m_ResetTimer = null; + + m_RaiseSwitch.Reset(); + } + } + + public RaiseSwitch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Item) m_RaisableItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_RaisableItem = (RaisableItem) reader.ReadItem(); + + Reset(); + } + } + + public class DisappearingRaiseSwitch : RaiseSwitch + { + public int CurrentRange{ get{ return Visible ? 3 : 2; } } + + [Constructable] + public DisappearingRaiseSwitch() : base( 0x108F ) + { + } + + protected override void Flip() + { + } + + protected override void Reset() + { + } + + public override bool HandlesOnMovement{ get{ return true; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( Utility.InRange( m.Location, Location, CurrentRange ) || Utility.InRange( oldLocation, Location, CurrentRange ) ) + Refresh(); + } + + public override void OnMapChange() + { + if ( !Deleted ) + Refresh(); + } + + public override void OnLocationChange( Point3D oldLoc ) + { + if ( !Deleted ) + Refresh(); + } + + public void Refresh() + { + bool found = false; + foreach ( Mobile mob in GetMobilesInRange( CurrentRange ) ) + { + if ( mob.Hidden && mob.AccessLevel > AccessLevel.Player ) + continue; + + found = true; + break; + } + + Visible = found; + } + + public DisappearingRaiseSwitch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + if ( RaisableItem != null && RaisableItem.Deleted ) + RaisableItem = null; + + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Refresh ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/MyRunUO/Config.cs b/Scripts/Engines/MyRunUO/Config.cs new file mode 100644 index 000000000..90d3fd18e --- /dev/null +++ b/Scripts/Engines/MyRunUO/Config.cs @@ -0,0 +1,51 @@ +using System; +using System.Text; +using System.Threading; + +namespace Server.Engines.MyRunUO +{ + public class Config + { + // Is MyRunUO enabled? + public static bool Enabled = false; + + // Details required for database connection string + public static string DatabaseDriver = "{MySQL ODBC 3.51 Driver}"; + public static string DatabaseServer = "localhost"; + public static string DatabaseName = "MyRunUO"; + public static string DatabaseUserID = "username"; + public static string DatabasePassword = "password"; + + // Should the database use transactions? This is recommended + public static bool UseTransactions = true; + + // Use optimized table loading techniques? (LOAD DATA INFILE) + public static bool LoadDataInFile = true; + + // This must be enabled if the database server is on a remote machine. + public static bool DatabaseNonLocal = ( DatabaseServer != "localhost" ); + + // Text encoding used + public static Encoding EncodingIO = Encoding.ASCII; + + // Database communication is done in a seperate thread. This value is the 'priority' of that thread, or, how much CPU it will try to use + public static ThreadPriority DatabaseThreadPriority = ThreadPriority.BelowNormal; + + // Any character with an AccessLevel equal to or higher than this will not be displayed + public static AccessLevel HiddenAccessLevel = AccessLevel.Counselor; + + // Export character database every 30 minutes + public static TimeSpan CharacterUpdateInterval = TimeSpan.FromMinutes( 30.0 ); + + // Export online list database every 5 minutes + public static TimeSpan StatusUpdateInterval = TimeSpan.FromMinutes( 5.0 ); + + public static string CompileConnectionString() + { + string connectionString = String.Format( "DRIVER={0};SERVER={1};DATABASE={2};UID={3};PASSWORD={4};", + DatabaseDriver, DatabaseServer, DatabaseName, DatabaseUserID, DatabasePassword ); + + return connectionString; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/MyRunUO/DatabaseCommandQueue.cs b/Scripts/Engines/MyRunUO/DatabaseCommandQueue.cs new file mode 100644 index 000000000..8437e9382 --- /dev/null +++ b/Scripts/Engines/MyRunUO/DatabaseCommandQueue.cs @@ -0,0 +1,202 @@ +using System; +using System.Threading; +using System.Collections; +using System.Data; +using System.Data.Odbc; + +namespace Server.Engines.MyRunUO +{ + public class DatabaseCommandQueue + { + private Queue m_Queue; + private ManualResetEvent m_Sync; + private Thread m_Thread; + + private bool m_HasCompleted; + + private string m_CompletionString; + private string m_ConnectionString; + + public bool HasCompleted + { + get{ return m_HasCompleted; } + } + + public void Enqueue( object obj ) + { + lock ( m_Queue.SyncRoot ) + { + m_Queue.Enqueue( obj ); + try{ m_Sync.Set(); } + catch{} + } + } + + public DatabaseCommandQueue( string completionString, string threadName ) : this( Config.CompileConnectionString(), completionString, threadName ) + { + } + + public DatabaseCommandQueue( string connectionString, string completionString, string threadName ) + { + m_CompletionString = completionString; + m_ConnectionString = connectionString; + + m_Queue = Queue.Synchronized( new Queue() ); + + m_Queue.Enqueue( null ); // signal connect + + /*m_Queue.Enqueue( "DELETE FROM myrunuo_characters" ); + m_Queue.Enqueue( "DELETE FROM myrunuo_characters_layers" ); + m_Queue.Enqueue( "DELETE FROM myrunuo_characters_skills" ); + m_Queue.Enqueue( "DELETE FROM myrunuo_guilds" ); + m_Queue.Enqueue( "DELETE FROM myrunuo_guilds_wars" );*/ + + m_Sync = new ManualResetEvent( true ); + + m_Thread = new Thread( new ThreadStart( Thread_Start ) ); + m_Thread.Name = threadName;//"MyRunUO Database Command Queue"; + m_Thread.Priority = Config.DatabaseThreadPriority; + m_Thread.Start(); + } + + private void Thread_Start() + { + bool connected = false; + + OdbcConnection connection = null; + OdbcCommand command = null; + OdbcTransaction transact = null; + + DateTime start = DateTime.Now; + + bool shouldWriteException = true; + + while ( true ) + { + m_Sync.WaitOne(); + + while ( m_Queue.Count > 0 ) + { + try + { + object obj = m_Queue.Dequeue(); + + if ( obj == null ) + { + if ( connected ) + { + if ( transact != null ) + { + try{ transact.Commit(); } + catch ( Exception commitException ) + { + Console.WriteLine( "MyRunUO: Exception caught when committing transaction" ); + Console.WriteLine( commitException ); + + try + { + transact.Rollback(); + Console.WriteLine( "MyRunUO: Transaction has been rolled back" ); + } + catch ( Exception rollbackException ) + { + Console.WriteLine( "MyRunUO: Exception caught when rolling back transaction" ); + Console.WriteLine( rollbackException ); + } + } + } + + try{ connection.Close(); } + catch{} + + try{ connection.Dispose(); } + catch{} + + try{ command.Dispose(); } + catch{} + + try{ m_Sync.Close(); } + catch{} + + Console.WriteLine( m_CompletionString, (DateTime.Now - start).TotalSeconds ); + m_HasCompleted = true; + + return; + } + else + { + try + { + connected = true; + connection = new OdbcConnection( m_ConnectionString ); + connection.Open(); + command = connection.CreateCommand(); + + if ( Config.UseTransactions ) + { + transact = connection.BeginTransaction(); + command.Transaction = transact; + } + } + catch ( Exception e ) + { + try{ if ( transact != null ) transact.Rollback(); } + catch{} + + try{ if ( connection != null ) connection.Close(); } + catch{} + + try{ if ( connection != null ) connection.Dispose(); } + catch{} + + try{ if ( command != null ) command.Dispose(); } + catch{} + + try{ m_Sync.Close(); } + catch{} + + Console.WriteLine( "MyRunUO: Unable to connect to the database" ); + Console.WriteLine( e ); + m_HasCompleted = true; + return; + } + } + } + else if ( obj is string ) + { + command.CommandText = (string)obj; + command.ExecuteNonQuery(); + } + else + { + string[] parms = (string[])obj; + + command.CommandText = parms[0]; + + if ( command.ExecuteScalar() == null ) + { + command.CommandText = parms[1]; + command.ExecuteNonQuery(); + } + } + } + catch ( Exception e ) + { + if ( shouldWriteException ) + { + Console.WriteLine( "MyRunUO: Exception caught in database thread" ); + Console.WriteLine( e ); + shouldWriteException = false; + } + } + } + + lock ( m_Queue.SyncRoot ) + { + if ( m_Queue.Count == 0 ) + m_Sync.Reset(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/MyRunUO/LayerComparer.cs b/Scripts/Engines/MyRunUO/LayerComparer.cs new file mode 100644 index 000000000..2a2458889 --- /dev/null +++ b/Scripts/Engines/MyRunUO/LayerComparer.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections; + +namespace Server.Engines.MyRunUO +{ + public class LayerComparer : IComparer + { + private static Layer PlateArms = (Layer)255; + private static Layer ChainTunic = (Layer)254; + private static Layer LeatherShorts = (Layer)253; + + private static Layer[] m_DesiredLayerOrder = new Layer[] + { + Layer.Cloak, + Layer.Bracelet, + Layer.Ring, + Layer.Shirt, + Layer.Pants, + Layer.InnerLegs, + Layer.Shoes, + LeatherShorts, + Layer.Arms, + Layer.InnerTorso, + LeatherShorts, + PlateArms, + Layer.MiddleTorso, + Layer.OuterLegs, + Layer.Neck, + Layer.Waist, + Layer.Gloves, + Layer.OuterTorso, + Layer.OneHanded, + Layer.TwoHanded, + Layer.FacialHair, + Layer.Hair, + Layer.Helm, + Layer.Talisman + }; + + private static int[] m_TranslationTable; + + public static int[] TranslationTable + { + get{ return m_TranslationTable; } + } + + static LayerComparer() + { + m_TranslationTable = new int[256]; + + for ( int i = 0; i < m_DesiredLayerOrder.Length; ++i ) + m_TranslationTable[(int)m_DesiredLayerOrder[i]] = m_DesiredLayerOrder.Length - i; + } + + public static bool IsValid( Item item ) + { + return ( m_TranslationTable[(int)item.Layer] > 0 ); + } + + public static readonly IComparer Instance = new LayerComparer(); + + public LayerComparer() + { + } + + public Layer Fix( int itemID, Layer oldLayer ) + { + if ( itemID == 0x1410 || itemID == 0x1417 ) // platemail arms + return PlateArms; + + if ( itemID == 0x13BF || itemID == 0x13C4 ) // chainmail tunic + return ChainTunic; + + if ( itemID == 0x1C08 || itemID == 0x1C09 ) // leather skirt + return LeatherShorts; + + if ( itemID == 0x1C00 || itemID == 0x1C01 ) // leather shorts + return LeatherShorts; + + return oldLayer; + } + + public int Compare( object x, object y ) + { + Item a = (Item)x; + Item b = (Item)y; + + Layer aLayer = a.Layer; + Layer bLayer = b.Layer; + + aLayer = Fix( a.ItemID, aLayer ); + bLayer = Fix( b.ItemID, bLayer ); + + return m_TranslationTable[(int)bLayer] - m_TranslationTable[(int)aLayer]; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/MyRunUO/MyRunUO.cs b/Scripts/Engines/MyRunUO/MyRunUO.cs new file mode 100644 index 000000000..36a5bff9a --- /dev/null +++ b/Scripts/Engines/MyRunUO/MyRunUO.cs @@ -0,0 +1,640 @@ +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Misc; +using Server.Items; +using Server.Guilds; +using Server.Mobiles; +using Server.Accounting; +using Server.Commands; + +namespace Server.Engines.MyRunUO +{ + public class MyRunUO : Timer + { + private static double CpuInterval = 0.1; // Processor runs every 0.1 seconds + private static double CpuPercent = 0.25; // Processor runs for 25% of Interval, or ~25ms. This should take around 25% cpu + + public static void Initialize() + { + if ( Config.Enabled ) + { + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), Config.CharacterUpdateInterval, new TimerCallback( Begin ) ); + + CommandSystem.Register( "UpdateMyRunUO", AccessLevel.Administrator, new CommandEventHandler( UpdateMyRunUO_OnCommand ) ); + + CommandSystem.Register( "PublicChar", AccessLevel.Player, new CommandEventHandler( PublicChar_OnCommand ) ); + CommandSystem.Register( "PrivateChar", AccessLevel.Player, new CommandEventHandler( PrivateChar_OnCommand ) ); + } + } + + [Usage( "PublicChar" )] + [Description( "Enables showing extended character stats and skills in MyRunUO." )] + public static void PublicChar_OnCommand( CommandEventArgs e ) + { + PlayerMobile pm = e.Mobile as PlayerMobile; + + if ( pm != null ) + { + if ( pm.PublicMyRunUO ) + { + pm.SendMessage( "You have already chosen to show your skills and stats." ); + } + else + { + pm.PublicMyRunUO = true; + pm.SendMessage( "All of your skills and stats will now be shown publicly in MyRunUO." ); + } + } + } + + [Usage( "PublicChar" )] + [Description( "Disables showing extended character stats and skills in MyRunUO." )] + public static void PrivateChar_OnCommand( CommandEventArgs e ) + { + PlayerMobile pm = e.Mobile as PlayerMobile; + + if ( pm != null ) + { + if ( !pm.PublicMyRunUO ) + { + pm.SendMessage( "You have already chosen to not show your skills and stats." ); + } + else + { + pm.PublicMyRunUO = false; + pm.SendMessage( "Only a general level of your top three skills will be shown in MyRunUO." ); + } + } + } + + [Usage( "UpdateMyRunUO" )] + [Description( "Starts the process of updating the MyRunUO character and guild database." )] + public static void UpdateMyRunUO_OnCommand( CommandEventArgs e ) + { + if ( m_Command != null && m_Command.HasCompleted ) + m_Command = null; + + if ( m_Timer == null && m_Command == null ) + { + Begin(); + e.Mobile.SendMessage( "MyRunUO update process has been started." ); + } + else + { + e.Mobile.SendMessage( "MyRunUO database is already being updated." ); + } + } + + public static void Begin() + { + if ( m_Command != null && m_Command.HasCompleted ) + m_Command = null; + + if ( m_Timer != null || m_Command != null ) + return; + + m_Timer = new MyRunUO(); + m_Timer.Start(); + } + + private static Timer m_Timer; + + private Stage m_Stage; + private ArrayList m_List; + private List m_Collecting; + private int m_Index; + + private static DatabaseCommandQueue m_Command; + + private string m_SkillsPath; + private string m_LayersPath; + private string m_MobilesPath; + + private StreamWriter m_OpSkills; + private StreamWriter m_OpLayers; + private StreamWriter m_OpMobiles; + + private DateTime m_StartTime; + + public MyRunUO() : base( TimeSpan.FromSeconds( CpuInterval ), TimeSpan.FromSeconds( CpuInterval ) ) + { + m_List = new ArrayList(); + m_Collecting = new List(); + + m_StartTime = DateTime.Now; + Console.WriteLine( "MyRunUO: Updating character database" ); + } + + protected override void OnTick() + { + bool shouldExit = false; + + try + { + shouldExit = Process( DateTime.Now + TimeSpan.FromSeconds( CpuInterval * CpuPercent ) ); + + if ( shouldExit ) + Console.WriteLine( "MyRunUO: Database statements compiled in {0:F2} seconds", (DateTime.Now - m_StartTime).TotalSeconds ); + } + catch ( Exception e ) + { + Console.WriteLine( "MyRunUO: {0}: Exception cought while processing", m_Stage ); + Console.WriteLine( e ); + shouldExit = true; + } + + if ( shouldExit ) + { + m_Command.Enqueue( null ); + + Stop(); + m_Timer = null; + } + } + + private enum Stage + { + CollectingMobiles, + DumpingMobiles, + CollectingGuilds, + DumpingGuilds, + Complete + } + + public bool Process( DateTime endTime ) + { + switch ( m_Stage ) + { + case Stage.CollectingMobiles: CollectMobiles( endTime ); break; + case Stage.DumpingMobiles: DumpMobiles( endTime ); break; + case Stage.CollectingGuilds: CollectGuilds( endTime ); break; + case Stage.DumpingGuilds: DumpGuilds( endTime ); break; + } + + return ( m_Stage == Stage.Complete ); + } + + private static ArrayList m_MobilesToUpdate = new ArrayList(); + + public static void QueueMobileUpdate( Mobile m ) + { + if ( !Config.Enabled || Config.LoadDataInFile ) + return; + + m_MobilesToUpdate.Add( m ); + } + + public void CollectMobiles( DateTime endTime ) + { + if ( Config.LoadDataInFile ) + { + if ( m_Index == 0 ) + m_Collecting.AddRange( Accounts.GetAccounts() ); + + for ( int i = m_Index; i < m_Collecting.Count; ++i ) + { + IAccount acct = m_Collecting[i]; + + for ( int j = 0; j < acct.Length; ++j ) + { + Mobile mob = acct[j]; + + if ( mob != null && mob.AccessLevel < Config.HiddenAccessLevel ) + m_List.Add( mob ); + } + + ++m_Index; + + if ( DateTime.Now >= endTime ) + break; + } + + if ( m_Index == m_Collecting.Count ) + { + m_Collecting = new List(); + m_Stage = Stage.DumpingMobiles; + m_Index = 0; + } + } + else + { + m_List = m_MobilesToUpdate; + m_MobilesToUpdate = new ArrayList(); + m_Stage = Stage.DumpingMobiles; + m_Index = 0; + } + } + + public void CheckConnection() + { + if ( m_Command == null ) + { + m_Command = new DatabaseCommandQueue( "MyRunUO: Characeter database updated in {0:F1} seconds", "MyRunUO Character Database Thread" ); + + if ( Config.LoadDataInFile ) + { + m_OpSkills = GetUniqueWriter( "skills", out m_SkillsPath ); + m_OpLayers = GetUniqueWriter( "layers", out m_LayersPath ); + m_OpMobiles = GetUniqueWriter( "mobiles", out m_MobilesPath ); + + m_Command.Enqueue( "TRUNCATE TABLE myrunuo_characters" ); + m_Command.Enqueue( "TRUNCATE TABLE myrunuo_characters_layers" ); + m_Command.Enqueue( "TRUNCATE TABLE myrunuo_characters_skills" ); + } + + m_Command.Enqueue( "TRUNCATE TABLE myrunuo_guilds" ); + m_Command.Enqueue( "TRUNCATE TABLE myrunuo_guilds_wars" ); + } + } + + public void ExecuteNonQuery( string text ) + { + m_Command.Enqueue( text ); + } + + public void ExecuteNonQuery( string format, params string[] args ) + { + ExecuteNonQuery( String.Format( format, args ) ); + } + + public void ExecuteNonQueryIfNull( string select, string insert ) + { + m_Command.Enqueue( new string[]{ select, insert } ); + } + + private void AppendCharEntity( string input, int charIndex, ref StringBuilder sb, char c ) + { + if ( sb == null ) + { + if ( charIndex > 0 ) + sb = new StringBuilder( input, 0, charIndex, input.Length + 20 ); + else + sb = new StringBuilder( input.Length + 20 ); + } + + sb.Append( "&#" ); + sb.Append( (int)c ); + sb.Append( ";" ); + } + + private void AppendEntityRef( string input, int charIndex, ref StringBuilder sb, string ent ) + { + if ( sb == null ) + { + if ( charIndex > 0 ) + sb = new StringBuilder( input, 0, charIndex, input.Length + 20 ); + else + sb = new StringBuilder( input.Length + 20 ); + } + + sb.Append( ent ); + } + + private string SafeString( string input ) + { + if ( input == null ) + return ""; + + StringBuilder sb = null; + + for ( int i = 0; i < input.Length; ++i ) + { + char c = input[i]; + + if ( c < 0x20 || c > 0x80 ) + { + AppendCharEntity( input, i, ref sb, c ); + } + else + { + switch ( c ) + { + case '&': AppendEntityRef( input, i, ref sb, "&" ); break; + case '>': AppendEntityRef( input, i, ref sb, ">" ); break; + case '<': AppendEntityRef( input, i, ref sb, "<" ); break; + case '"': AppendEntityRef( input, i, ref sb, """ ); break; + case '\'': + case ':': + case '/': + case '\\': AppendCharEntity( input, i, ref sb, c ); break; + default: + { + if ( sb != null ) + sb.Append( c ); + + break; + } + } + } + } + + if ( sb != null ) + return sb.ToString(); + + return input; + } + + public const char LineStart = '\"'; + public const string EntrySep = "\",\""; + public const string LineEnd = "\"\n"; + + public void InsertMobile( Mobile mob ) + { + string guildTitle = mob.GuildTitle; + + if ( guildTitle == null || (guildTitle = guildTitle.Trim()).Length == 0 ) + guildTitle = "NULL"; + else + guildTitle = SafeString( guildTitle ); + + string notoTitle = SafeString( Titles.ComputeTitle( null, mob ) ); + string female = ( mob.Female ? "1" : "0" ); + + bool pubBool = ( mob is PlayerMobile ) && ( ((PlayerMobile)mob).PublicMyRunUO ); + + string pubString = ( pubBool ? "1" : "0" ); + + string guildId = ( mob.Guild == null ? "NULL" : mob.Guild.Id.ToString() ); + + if ( Config.LoadDataInFile ) + { + m_OpMobiles.Write( LineStart ); + m_OpMobiles.Write( mob.Serial.Value ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( SafeString( mob.Name ) ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( mob.RawStr ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( mob.RawDex ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( mob.RawInt ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( female ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( mob.Kills ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( guildId ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( guildTitle ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( notoTitle ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( mob.Hue ); + m_OpMobiles.Write( EntrySep ); + m_OpMobiles.Write( pubString ); + m_OpMobiles.Write( LineEnd ); + } + else + { + ExecuteNonQuery( "INSERT INTO myrunuo_characters (char_id, char_name, char_str, char_dex, char_int, char_female, char_counts, char_guild, char_guildtitle, char_nototitle, char_bodyhue, char_public ) VALUES ({0}, '{1}', {2}, {3}, {4}, {5}, {6}, {7}, {8}, '{9}', {10}, {11})", mob.Serial.Value.ToString(), SafeString( mob.Name ), mob.RawStr.ToString(), mob.RawDex.ToString(), mob.RawInt.ToString(), female, mob.Kills.ToString(), guildId, guildTitle, notoTitle, mob.Hue.ToString(), pubString ); + } + } + + public void InsertSkills( Mobile mob ) + { + Skills skills = mob.Skills; + string serial = mob.Serial.Value.ToString(); + + for ( int i = 0; i < skills.Length; ++i ) + { + Skill skill = skills[i]; + + if ( skill.BaseFixedPoint > 0 ) + { + if ( Config.LoadDataInFile ) + { + m_OpSkills.Write( LineStart ); + m_OpSkills.Write( serial ); + m_OpSkills.Write( EntrySep ); + m_OpSkills.Write( i ); + m_OpSkills.Write( EntrySep ); + m_OpSkills.Write( skill.BaseFixedPoint ); + m_OpSkills.Write( LineEnd ); + } + else + { + ExecuteNonQuery( "INSERT INTO myrunuo_characters_skills (char_id, skill_id, skill_value) VALUES ({0}, {1}, {2})", serial, i.ToString(), skill.BaseFixedPoint.ToString() ); + } + } + } + } + + private ArrayList m_Items = new ArrayList(); + + private void InsertItem( string serial, int index, int itemID, int hue ) + { + if ( Config.LoadDataInFile ) + { + m_OpLayers.Write( LineStart ); + m_OpLayers.Write( serial ); + m_OpLayers.Write( EntrySep ); + m_OpLayers.Write( index ); + m_OpLayers.Write( EntrySep ); + m_OpLayers.Write( itemID ); + m_OpLayers.Write( EntrySep ); + m_OpLayers.Write( hue ); + m_OpLayers.Write( LineEnd ); + } + else + { + ExecuteNonQuery( "INSERT INTO myrunuo_characters_layers (char_id, layer_id, item_id, item_hue) VALUES ({0}, {1}, {2}, {3})", serial, index.ToString(), itemID.ToString(), hue.ToString() ); + } + } + + public void InsertItems( Mobile mob ) + { + ArrayList items = m_Items; + items.AddRange( mob.Items ); + string serial = mob.Serial.Value.ToString(); + + items.Sort( LayerComparer.Instance ); + + int index = 0; + + bool hidePants = false; + bool alive = mob.Alive; + bool hideHair = !alive; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = (Item)items[i]; + + if ( !LayerComparer.IsValid( item ) ) + break; + + if ( !alive && item.ItemID != 8270 ) + continue; + + if ( item.ItemID == 0x1411 || item.ItemID == 0x141A ) // plate legs + hidePants = true; + else if ( hidePants && item.Layer == Layer.Pants ) + continue; + + if ( !hideHair && item.Layer == Layer.Helm ) + hideHair = true; + + InsertItem( serial, index++, item.ItemID, item.Hue ); + } + + if ( mob.FacialHairItemID != 0 && alive ) + InsertItem( serial, index++, mob.FacialHairItemID, mob.FacialHairHue ); + + if ( mob.HairItemID != 0 && !hideHair ) + InsertItem( serial, index++, mob.HairItemID, mob.HairHue ); + + items.Clear(); + } + + public void DeleteMobile( Mobile mob ) + { + ExecuteNonQuery( "DELETE FROM myrunuo_characters WHERE char_id = {0}", mob.Serial.Value.ToString() ); + ExecuteNonQuery( "DELETE FROM myrunuo_characters_skills WHERE char_id = {0}", mob.Serial.Value.ToString() ); + ExecuteNonQuery( "DELETE FROM myrunuo_characters_layers WHERE char_id = {0}", mob.Serial.Value.ToString() ); + } + + public StreamWriter GetUniqueWriter( string type, out string filePath ) + { + filePath = Path.Combine( Core.BaseDirectory, String.Format( "myrunuodb_{0}.txt", type ) ).Replace( Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar ); + + try + { + return new StreamWriter( filePath ); + } + catch + { + for ( int i = 0; i < 100; ++i ) + { + try + { + filePath = Path.Combine( Core.BaseDirectory, String.Format( "myrunuodb_{0}_{1}.txt", type, i ) ).Replace( Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar ); + return new StreamWriter( filePath ); + } + catch + { + } + } + } + + return null; + } + + public void DumpMobiles( DateTime endTime ) + { + CheckConnection(); + + for ( int i = m_Index; i < m_List.Count; ++i ) + { + Mobile mob = (Mobile)m_List[i]; + + if ( mob is PlayerMobile ) + ((PlayerMobile)mob).ChangedMyRunUO = false; + + if ( !mob.Deleted && mob.AccessLevel < Config.HiddenAccessLevel ) + { + if ( !Config.LoadDataInFile ) + DeleteMobile( mob ); + + InsertMobile( mob ); + InsertSkills( mob ); + InsertItems( mob ); + } + else if ( !Config.LoadDataInFile ) + { + DeleteMobile( mob ); + } + + ++m_Index; + + if ( DateTime.Now >= endTime ) + break; + } + + if ( m_Index == m_List.Count ) + { + m_List.Clear(); + m_Stage = Stage.CollectingGuilds; + m_Index = 0; + + if ( Config.LoadDataInFile ) + { + m_OpSkills.Close(); + m_OpLayers.Close(); + m_OpMobiles.Close(); + + ExecuteNonQuery( "LOAD DATA {0}INFILE '{1}' INTO TABLE myrunuo_characters FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\n'", Config.DatabaseNonLocal ? "LOCAL " : "", m_MobilesPath ); + ExecuteNonQuery( "LOAD DATA {0}INFILE '{1}' INTO TABLE myrunuo_characters_skills FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\n'", Config.DatabaseNonLocal ? "LOCAL " : "", m_SkillsPath ); + ExecuteNonQuery( "LOAD DATA {0}INFILE '{1}' INTO TABLE myrunuo_characters_layers FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\n'", Config.DatabaseNonLocal ? "LOCAL " : "", m_LayersPath ); + } + } + } + + public void CollectGuilds( DateTime endTime ) + { + m_List.AddRange( Guild.List.Values ); + m_Stage = Stage.DumpingGuilds; + m_Index = 0; + } + + public void InsertGuild( Guild guild ) + { + string guildType = "Standard"; + + switch ( guild.Type ) + { + case GuildType.Chaos: guildType = "Chaos"; break; + case GuildType.Order: guildType = "Order"; break; + } + + ExecuteNonQuery( "INSERT INTO myrunuo_guilds (guild_id, guild_name, guild_abbreviation, guild_website, guild_charter, guild_type, guild_wars, guild_members, guild_master) VALUES ({0}, '{1}', {2}, {3}, {4}, '{5}', {6}, {7}, {8})", guild.Id.ToString(), SafeString( guild.Name ), guild.Abbreviation == "none" ? "NULL" : "'" + SafeString( guild.Abbreviation ) + "'", guild.Website == null ? "NULL" : "'" + SafeString( guild.Website ) + "'", guild.Charter == null ? "NULL" : "'" + SafeString( guild.Charter ) + "'", guildType, guild.Enemies.Count.ToString(), guild.Members.Count.ToString(), guild.Leader.Serial.Value.ToString() ); + } + + public void InsertWars( Guild guild ) + { + List wars = guild.Enemies; + + string ourId = guild.Id.ToString(); + + for ( int i = 0; i < wars.Count; ++i ) + { + Guild them = wars[i]; + string theirId = them.Id.ToString(); + + ExecuteNonQueryIfNull( + String.Format( "SELECT guild_1 FROM myrunuo_guilds_wars WHERE (guild_1={0} AND guild_2={1}) OR (guild_1={1} AND guild_2={0})", ourId, theirId ), + String.Format( "INSERT INTO myrunuo_guilds_wars (guild_1, guild_2) VALUES ({0}, {1})", ourId, theirId ) ); + } + } + + public void DumpGuilds( DateTime endTime ) + { + CheckConnection(); + + for ( int i = m_Index; i < m_List.Count; ++i ) + { + Guild guild = (Guild)m_List[i]; + + if ( !guild.Disbanded ) + { + InsertGuild( guild ); + InsertWars( guild ); + } + + ++m_Index; + + if ( DateTime.Now >= endTime ) + break; + } + + if ( m_Index == m_List.Count ) + { + m_List.Clear(); + m_Stage = Stage.Complete; + m_Index = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/MyRunUO/MyRunUOStatus.cs b/Scripts/Engines/MyRunUO/MyRunUOStatus.cs new file mode 100644 index 000000000..0730fb6c9 --- /dev/null +++ b/Scripts/Engines/MyRunUO/MyRunUOStatus.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Commands; +using Server.Network; + +namespace Server.Engines.MyRunUO +{ + public class MyRunUOStatus + { + public static void Initialize() + { + if ( Config.Enabled ) + { + Timer.DelayCall( TimeSpan.FromSeconds( 20.0 ), Config.StatusUpdateInterval, new TimerCallback( Begin ) ); + + CommandSystem.Register( "UpdateWebStatus", AccessLevel.Administrator, new CommandEventHandler( UpdateWebStatus_OnCommand ) ); + } + } + + [Usage( "UpdateWebStatus" )] + [Description( "Starts the process of updating the MyRunUO online status database." )] + public static void UpdateWebStatus_OnCommand( CommandEventArgs e ) + { + if ( m_Command == null || m_Command.HasCompleted ) + { + Begin(); + e.Mobile.SendMessage( "Web status update process has been started." ); + } + else + { + e.Mobile.SendMessage( "Web status database is already being updated." ); + } + } + + private static DatabaseCommandQueue m_Command; + + public static void Begin() + { + if ( m_Command != null && !m_Command.HasCompleted ) + return; + + DateTime start = DateTime.Now; + Console.WriteLine( "MyRunUO: Updating status database" ); + + try + { + m_Command = new DatabaseCommandQueue( "MyRunUO: Status database updated in {0:F1} seconds", "MyRunUO Status Database Thread" ); + + m_Command.Enqueue( "DELETE FROM myrunuo_status" ); + + List online = NetState.Instances; + + for ( int i = 0; i < online.Count; ++i ) + { + NetState ns = online[i]; + Mobile mob = ns.Mobile; + + if ( mob != null ) + m_Command.Enqueue( String.Format( "INSERT INTO myrunuo_status VALUES ({0})", mob.Serial.Value.ToString() ) ); + } + } + catch ( Exception e ) + { + Console.WriteLine( "MyRunUO: Error updating status database" ); + Console.WriteLine( e ); + } + + if ( m_Command != null ) + m_Command.Enqueue( null ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/AddPartyTarget.cs b/Scripts/Engines/Party/AddPartyTarget.cs new file mode 100644 index 000000000..8217e28f1 --- /dev/null +++ b/Scripts/Engines/Party/AddPartyTarget.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Engines.PartySystem +{ + public class AddPartyTarget : Target + { + public AddPartyTarget( Mobile from ) : base( 8, false, TargetFlags.None ) + { + from.SendLocalizedMessage( 1005454 ); // Who would you like to add to your party? + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + Party p = Party.Get( from ); + Party mp = Party.Get( m ); + + if ( from == m ) + from.SendLocalizedMessage( 1005439 ); // You cannot add yourself to a party. + else if ( p != null && p.Leader != from ) + from.SendLocalizedMessage( 1005453 ); // You may only add members to the party if you are the leader. + else if ( m.Party is Mobile ) + return; + else if ( p != null && (p.Members.Count + p.Candidates.Count) >= Party.Capacity ) + from.SendLocalizedMessage( 1008095 ); // You may only have 10 in your party (this includes candidates). + else if ( !m.Player && m.Body.IsHuman ) + m.SayTo( from, 1005443 ); // Nay, I would rather stay here and watch a nail rust. + else if ( !m.Player ) + from.SendLocalizedMessage( 1005444 ); // The creature ignores your offer. + else if ( mp != null && mp == p ) + from.SendLocalizedMessage( 1005440 ); // This person is already in your party! + else if ( mp != null ) + from.SendLocalizedMessage( 1005441 ); // This person is already in a party! + else + Party.Invite( from, m ); + } + else + { + from.SendLocalizedMessage( 1005442 ); // You may only add living things to your party! + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/DeclineTimer.cs b/Scripts/Engines/Party/DeclineTimer.cs new file mode 100644 index 000000000..7bad8eff1 --- /dev/null +++ b/Scripts/Engines/Party/DeclineTimer.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.PartySystem +{ + public class DeclineTimer : Timer + { + private Mobile m_Mobile, m_Leader; + + private static Hashtable m_Table = new Hashtable(); + + public static void Start( Mobile m, Mobile leader ) + { + DeclineTimer t = (DeclineTimer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table[m] = t = new DeclineTimer( m, leader ); + t.Start(); + } + + private DeclineTimer( Mobile m, Mobile leader ) : base( TimeSpan.FromSeconds( 10.0 ) ) + { + m_Mobile = m; + m_Leader = leader; + } + + protected override void OnTick() + { + m_Table.Remove( m_Mobile ); + + if ( m_Mobile.Party == m_Leader && PartyCommands.Handler != null ) + PartyCommands.Handler.OnDecline( m_Mobile, m_Leader ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/Packets.cs b/Scripts/Engines/Party/Packets.cs new file mode 100644 index 000000000..c9ee9df4f --- /dev/null +++ b/Scripts/Engines/Party/Packets.cs @@ -0,0 +1,79 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Engines.PartySystem +{ + public sealed class PartyEmptyList : Packet + { + public PartyEmptyList( Mobile m ) : base( 0xBF ) + { + EnsureCapacity( 7 ); + + m_Stream.Write( (short) 0x0006 ); + m_Stream.Write( (byte) 0x02 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (int) m.Serial ); + } + } + + public sealed class PartyMemberList : Packet + { + public PartyMemberList( Party p ) : base( 0xBF ) + { + EnsureCapacity( 7 + p.Count*4 ); + + m_Stream.Write( (short) 0x0006 ); + m_Stream.Write( (byte) 0x01 ); + m_Stream.Write( (byte) p.Count ); + + for ( int i = 0; i < p.Count; ++i ) + m_Stream.Write( (int) p[i].Mobile.Serial ); + } + } + + public sealed class PartyRemoveMember : Packet + { + public PartyRemoveMember( Mobile removed, Party p ) : base( 0xBF ) + { + EnsureCapacity( 11 + p.Count*4 ); + + m_Stream.Write( (short) 0x0006 ); + m_Stream.Write( (byte) 0x02 ); + m_Stream.Write( (byte) p.Count ); + + m_Stream.Write( (int) removed.Serial ); + + for ( int i = 0; i < p.Count; ++i ) + m_Stream.Write( (int) p[i].Mobile.Serial ); + } + } + + public sealed class PartyTextMessage : Packet + { + public PartyTextMessage( bool toAll, Mobile from, string text ) : base( 0xBF ) + { + if ( text == null ) + text = ""; + + EnsureCapacity( 12 + text.Length*2 ); + + m_Stream.Write( (short) 0x0006 ); + m_Stream.Write( (byte) (toAll ? 0x04 : 0x03) ); + m_Stream.Write( (int) from.Serial ); + m_Stream.WriteBigUniNull( text ); + } + } + + public sealed class PartyInvitation : Packet + { + public PartyInvitation( Mobile leader ) : base( 0xBF ) + { + EnsureCapacity( 10 ); + + m_Stream.Write( (short) 0x0006 ); + m_Stream.Write( (byte) 0x07 ); + m_Stream.Write( (int) leader.Serial ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/Party.cs b/Scripts/Engines/Party/Party.cs new file mode 100644 index 000000000..d771d99ec --- /dev/null +++ b/Scripts/Engines/Party/Party.cs @@ -0,0 +1,477 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Targeting; +using Server.Factions; +using Server.Commands; + +namespace Server.Engines.PartySystem +{ + public class Party : IParty + { + private Mobile m_Leader; + private ArrayList m_Members; + private ArrayList m_Candidates; + private ArrayList m_Listeners; // staff listening + + public const int Capacity = 10; + + public static void Initialize() + { + EventSink.Logout += new LogoutEventHandler( EventSink_Logout ); + EventSink.Login += new LoginEventHandler( EventSink_Login ); + EventSink.PlayerDeath += new PlayerDeathEventHandler( EventSink_PlayerDeath ); + + CommandSystem.Register( "ListenToParty", AccessLevel.GameMaster, new CommandEventHandler( ListenToParty_OnCommand ) ); + } + + public static void ListenToParty_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ListenToParty_OnTarget ) ); + e.Mobile.SendMessage( "Target a partied player." ); + } + + public static void ListenToParty_OnTarget( Mobile from, object obj ) + { + if ( obj is Mobile ) + { + Party p = Party.Get( (Mobile) obj ); + + if ( p == null ) + { + from.SendMessage( "They are not in a party." ); + } + else if ( p.m_Listeners.Contains( from ) ) + { + p.m_Listeners.Remove( from ); + from.SendMessage( "You are no longer listening to that party." ); + } + else + { + p.m_Listeners.Add( from ); + from.SendMessage( "You are now listening to that party." ); + } + } + } + + public static void EventSink_PlayerDeath( PlayerDeathEventArgs e ) + { + Mobile from = e.Mobile; + Party p = Party.Get( from ); + + if ( p != null ) + { + Mobile m = from.LastKiller; + + if ( m == from ) + p.SendPublicMessage( from, "I killed myself !!" ); + else if ( m == null ) + p.SendPublicMessage( from, "I was killed !!" ); + else + p.SendPublicMessage( from, String.Format( "I was killed by {0} !!", m.Name ) ); + } + } + + private class RejoinTimer : Timer + { + private Mobile m_Mobile; + + public RejoinTimer( Mobile m ) : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + } + + protected override void OnTick() + { + Party p = Party.Get( m_Mobile ); + + if ( p == null ) + return; + + m_Mobile.SendLocalizedMessage( 1005437 ); // You have rejoined the party. + m_Mobile.Send( new PartyMemberList( p ) ); + + Packet message = Packet.Acquire( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008087, "", AffixType.Prepend | AffixType.System, m_Mobile.Name, "" ) ); + Packet attrs = Packet.Acquire( new MobileAttributesN( m_Mobile ) ); + + foreach ( PartyMemberInfo mi in p.Members ) + { + Mobile m = mi.Mobile; + + if ( m != m_Mobile ) + { + m.Send( message ); + m.Send( new MobileStatusCompact( m_Mobile.CanBeRenamedBy( m ), m_Mobile ) ); + m.Send( attrs ); + m_Mobile.Send( new MobileStatusCompact( m.CanBeRenamedBy( m_Mobile ), m ) ); + m_Mobile.Send( new MobileAttributesN( m ) ); + } + } + + Packet.Release( message ); + Packet.Release( attrs ); + } + } + + public static void EventSink_Login( LoginEventArgs e ) + { + Mobile from = e.Mobile; + Party p = Party.Get( from ); + + if ( p != null ) + new RejoinTimer( from ).Start(); + else + from.Party = null; + } + + public static void EventSink_Logout( LogoutEventArgs e ) + { + Mobile from = e.Mobile; + Party p = Party.Get( from ); + + if ( p != null ) + p.Remove( from ); + + from.Party = null; + } + + public static Party Get( Mobile m ) + { + if ( m == null ) + return null; + + return m.Party as Party; + } + + public Party( Mobile leader ) + { + m_Leader = leader; + + m_Members = new ArrayList(); + m_Candidates = new ArrayList(); + m_Listeners = new ArrayList(); + + m_Members.Add( new PartyMemberInfo( leader ) ); + } + + public void Add( Mobile m ) + { + PartyMemberInfo mi = this[m]; + + if ( mi == null ) + { + m_Members.Add( new PartyMemberInfo( m ) ); + m.Party = this; + + Packet memberList = Packet.Acquire( new PartyMemberList( this ) ); + Packet attrs = Packet.Acquire( new MobileAttributesN( m ) ); + + for ( int i = 0; i < m_Members.Count; ++i ) + { + Mobile f = ((PartyMemberInfo)m_Members[i]).Mobile; + + f.Send( memberList ); + + if ( f != m ) + { + f.Send( new MobileStatusCompact( m.CanBeRenamedBy( f ), m ) ); + f.Send( attrs ); + m.Send( new MobileStatusCompact( f.CanBeRenamedBy( m ), f ) ); + m.Send( new MobileAttributesN( f ) ); + } + } + + Packet.Release( memberList ); + Packet.Release( attrs ); + } + } + + public void OnAccept( Mobile from ) + { + OnAccept( from, false ); + } + + public void OnAccept( Mobile from, bool force ) + { + Faction ourFaction = Faction.Find( m_Leader ); + Faction theirFaction = Faction.Find( from ); + + if ( !force && ourFaction != null && theirFaction != null && ourFaction != theirFaction ) + return; + + // : joined the party. + SendToAll( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008094, "", AffixType.Prepend | AffixType.System, from.Name, "" ) ); + + from.SendLocalizedMessage( 1005445 ); // You have been added to the party. + + m_Candidates.Remove( from ); + Add( from ); + } + + public void OnDecline( Mobile from, Mobile leader ) + { + // : Does not wish to join the party. + leader.SendLocalizedMessage( 1008091, false, from.Name ); + + from.SendLocalizedMessage( 1008092 ); // You notify them that you do not wish to join the party. + + m_Candidates.Remove( from ); + from.Send( new PartyEmptyList( from ) ); + + if ( m_Candidates.Count == 0 && m_Members.Count <= 1 ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + { + this[i].Mobile.Send( new PartyEmptyList( this[i].Mobile ) ); + this[i].Mobile.Party = null; + } + + m_Members.Clear(); + } + } + + public void Remove( Mobile m ) + { + if ( m == m_Leader ) + { + Disband(); + } + else + { + for ( int i = 0; i < m_Members.Count; ++i ) + { + if ( ((PartyMemberInfo)m_Members[i]).Mobile == m ) + { + m_Members.RemoveAt( i ); + + m.Party = null; + m.Send( new PartyEmptyList( m ) ); + + m.SendLocalizedMessage( 1005451 ); // You have been removed from the party. + + SendToAll( new PartyRemoveMember( m, this ) ); + SendToAll( 1005452 ); // A player has been removed from your party. + + break; + } + } + + if ( m_Members.Count == 1 ) + { + SendToAll( 1005450 ); // The last person has left the party... + Disband(); + } + } + } + + public bool Contains( Mobile m ) + { + return ( this[m] != null ); + } + + public void Disband() + { + SendToAll( 1005449 ); // Your party has disbanded. + + for ( int i = 0; i < m_Members.Count; ++i ) + { + this[i].Mobile.Send( new PartyEmptyList( this[i].Mobile ) ); + this[i].Mobile.Party = null; + } + + m_Members.Clear(); + } + + public static void Invite( Mobile from, Mobile target ) + { + Faction ourFaction = Faction.Find( from ); + Faction theirFaction = Faction.Find( target ); + + if ( ourFaction != null && theirFaction != null && ourFaction != theirFaction ) + { + from.SendLocalizedMessage( 1008088 ); // You cannot have players from opposing factions in the same party! + target.SendLocalizedMessage( 1008093 ); // The party cannot have members from opposing factions. + return; + } + + Party p = Party.Get( from ); + + if ( p == null ) + from.Party = p = new Party( from ); + + if ( !p.Candidates.Contains( target ) ) + p.Candidates.Add( target ); + + // : You are invited to join the party. Type /accept to join or /decline to decline the offer. + target.Send( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008089, "", AffixType.Prepend | AffixType.System, from.Name, "" ) ); + + from.SendLocalizedMessage( 1008090 ); // You have invited them to join the party. + + target.Send( new PartyInvitation( from ) ); + target.Party = from; + + DeclineTimer.Start( target, from ); + } + + public void SendToAll( int number ) + { + SendToAll( number, "", 0x3B2 ); + } + + public void SendToAll( int number, string args ) + { + SendToAll( number, args, 0x3B2 ); + } + + public void SendToAll( int number, string args, int hue ) + { + SendToAll( new MessageLocalized( Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", args ) ); + } + + public void SendPublicMessage( Mobile from, string text ) + { + SendToAll( new PartyTextMessage( true, from, text ) ); + + for ( int i = 0; i < m_Listeners.Count; ++i ) + { + Mobile mob = (Mobile)m_Listeners[i]; + + if ( mob.Party != this ) + ((Mobile)m_Listeners[i]).SendMessage( "[{0}]: {1}", from.Name, text ); + } + + SendToStaffMessage( from, "[Party]: {0}", text ); + } + + public void SendPrivateMessage( Mobile from, Mobile to, string text ) + { + to.Send( new PartyTextMessage( false, from, text ) ); + + for ( int i = 0; i < m_Listeners.Count; ++i ) + { + Mobile mob = (Mobile)m_Listeners[i]; + + if ( mob.Party != this ) + ((Mobile)m_Listeners[i]).SendMessage( "[{0}]->[{1}]: {2}", from.Name, to.Name, text ); + } + + SendToStaffMessage( from, "[Party]->[{0}]: {1}", to.Name, text ); + } + + private void SendToStaffMessage( Mobile from, string text ) + { + Packet p = null; + + foreach( NetState ns in from.GetClientsInRange( 8 ) ) + { + Mobile mob = ns.Mobile; + + if( mob != null && mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > from.AccessLevel && mob.Party != this && !m_Listeners.Contains( mob ) ) + { + if( p == null ) + p = Packet.Acquire( new UnicodeMessage( from.Serial, from.Body, MessageType.Regular, from.SpeechHue, 3, from.Language, from.Name, text ) ); + + ns.Send( p ); + } + } + + Packet.Release( p ); + } + private void SendToStaffMessage( Mobile from, string format, params object[] args ) + { + SendToStaffMessage( from, String.Format( format, args ) ); + } + + public void SendToAll( Packet p ) + { + p.Acquire(); + + for ( int i = 0; i < m_Members.Count; ++i ) + ((PartyMemberInfo)m_Members[i]).Mobile.Send( p ); + + if ( p is MessageLocalized || p is MessageLocalizedAffix || p is UnicodeMessage || p is AsciiMessage ) + { + for ( int i = 0; i < m_Listeners.Count; ++i ) + { + Mobile mob = (Mobile)m_Listeners[i]; + + if ( mob.Party != this ) + mob.Send( p ); + } + } + + p.Release(); + } + + public void OnStamChanged( Mobile m ) + { + Packet p = null; + + for ( int i = 0; i < m_Members.Count; ++i ) + { + Mobile c = ((PartyMemberInfo)m_Members[i]).Mobile; + + if ( c != m && m.Map == c.Map && Utility.InUpdateRange( c, m ) && c.CanSee( m ) ) + { + if ( p == null ) + p = Packet.Acquire( new MobileStamN( m ) ); + + c.Send( p ); + } + } + + Packet.Release( p ); + } + + public void OnManaChanged( Mobile m ) + { + Packet p = null; + + for ( int i = 0; i < m_Members.Count; ++i ) + { + Mobile c = ((PartyMemberInfo)m_Members[i]).Mobile; + + if ( c != m && m.Map == c.Map && Utility.InUpdateRange( c, m ) && c.CanSee( m ) ) + { + if ( p == null ) + p = Packet.Acquire( new MobileManaN( m ) ); + + c.Send( p ); + } + } + + Packet.Release( p ); + } + + public void OnStatsQuery( Mobile beholder, Mobile beheld ) + { + if ( beholder != beheld && Contains( beholder ) && beholder.Map == beheld.Map && Utility.InUpdateRange( beholder, beheld ) ) + { + if ( !beholder.CanSee( beheld ) ) + beholder.Send( new MobileStatusCompact( beheld.CanBeRenamedBy( beholder ), beheld ) ); + + beholder.Send( new MobileAttributesN( beheld ) ); + } + } + + public int Count{ get{ return m_Members.Count; } } + public bool Active{ get{ return m_Members.Count > 1; } } + public Mobile Leader{ get{ return m_Leader; } } + public ArrayList Members{ get{ return m_Members; } } + public ArrayList Candidates{ get{ return m_Candidates; } } + + public PartyMemberInfo this[int index]{ get{ return (PartyMemberInfo)m_Members[index]; } } + public PartyMemberInfo this[Mobile m] + { + get + { + for ( int i = 0; i < m_Members.Count; ++i ) + if ( ((PartyMemberInfo)m_Members[i]).Mobile == m ) + return (PartyMemberInfo)m_Members[i]; + + return null; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/PartyCommands.cs b/Scripts/Engines/Party/PartyCommands.cs new file mode 100644 index 000000000..7edf939ba --- /dev/null +++ b/Scripts/Engines/Party/PartyCommands.cs @@ -0,0 +1,123 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Engines.PartySystem +{ + public class PartyCommandHandlers : PartyCommands + { + public static void Initialize() + { + PartyCommands.Handler = new PartyCommandHandlers(); + } + + public override void OnAdd( Mobile from ) + { + Party p = Party.Get( from ); + + if ( p != null && p.Leader != from ) + from.SendLocalizedMessage( 1005453 ); // You may only add members to the party if you are the leader. + else if ( p != null && (p.Members.Count + p.Candidates.Count) >= Party.Capacity ) + from.SendLocalizedMessage( 1008095 ); // You may only have 10 in your party (this includes candidates). + else + from.Target = new AddPartyTarget( from ); + } + + public override void OnRemove( Mobile from, Mobile target ) + { + Party p = Party.Get( from ); + + if ( p == null ) + { + from.SendLocalizedMessage( 3000211 ); // You are not in a party. + return; + } + + if ( p.Leader == from && target == null ) + { + from.SendLocalizedMessage( 1005455 ); // Who would you like to remove from your party? + from.Target = new RemovePartyTarget(); + } + else if ( (p.Leader == from || from == target) && p.Contains( target ) ) + { + p.Remove( target ); + } + } + + public override void OnPrivateMessage( Mobile from, Mobile target, string text ) + { + if ( text.Length > 128 || (text = text.Trim()).Length == 0 ) + return; + + Party p = Party.Get( from ); + + if ( p != null && p.Contains( target ) ) + p.SendPrivateMessage( from, target, text ); + else + from.SendLocalizedMessage( 3000211 ); // You are not in a party. + } + + public override void OnPublicMessage( Mobile from, string text ) + { + if ( text.Length > 128 || (text = text.Trim()).Length == 0 ) + return; + + Party p = Party.Get( from ); + + if ( p != null ) + p.SendPublicMessage( from, text ); + else + from.SendLocalizedMessage( 3000211 ); // You are not in a party. + } + + public override void OnSetCanLoot( Mobile from, bool canLoot ) + { + Party p = Party.Get( from ); + + if ( p == null ) + { + from.SendLocalizedMessage( 3000211 ); // You are not in a party. + } + else + { + PartyMemberInfo mi = p[from]; + + if ( mi != null ) + { + mi.CanLoot = canLoot; + + if ( canLoot ) + from.SendLocalizedMessage( 1005447 ); // You have chosen to allow your party to loot your corpse. + else + from.SendLocalizedMessage( 1005448 ); // You have chosen to prevent your party from looting your corpse. + } + } + } + + public override void OnAccept( Mobile from, Mobile sentLeader ) + { + Mobile leader = from.Party as Mobile; + from.Party = null; + + Party p = Party.Get( leader ); + + if ( leader == null || p == null || !p.Candidates.Contains( from ) ) + from.SendLocalizedMessage( 3000222 ); // No one has invited you to be in a party. + else if ( (p.Members.Count + p.Candidates.Count) <= Party.Capacity ) + p.OnAccept( from ); + } + + public override void OnDecline( Mobile from, Mobile sentLeader ) + { + Mobile leader = from.Party as Mobile; + from.Party = null; + + Party p = Party.Get( leader ); + + if ( leader == null || p == null || !p.Candidates.Contains( from ) ) + from.SendLocalizedMessage( 3000222 ); // No one has invited you to be in a party. + else + p.OnDecline( from, leader ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/PartyMemberInfo.cs b/Scripts/Engines/Party/PartyMemberInfo.cs new file mode 100644 index 000000000..cd9de7c06 --- /dev/null +++ b/Scripts/Engines/Party/PartyMemberInfo.cs @@ -0,0 +1,20 @@ +using System; +using Server; + +namespace Server.Engines.PartySystem +{ + public class PartyMemberInfo + { + private Mobile m_Mobile; + private bool m_CanLoot; + + public Mobile Mobile{ get{ return m_Mobile; } } + public bool CanLoot{ get{ return m_CanLoot; } set{ m_CanLoot = value; } } + + public PartyMemberInfo( Mobile m ) + { + m_Mobile = m; + m_CanLoot = !Core.ML; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Party/RemovePartyTarget.cs b/Scripts/Engines/Party/RemovePartyTarget.cs new file mode 100644 index 000000000..b06945bde --- /dev/null +++ b/Scripts/Engines/Party/RemovePartyTarget.cs @@ -0,0 +1,30 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Engines.PartySystem +{ + public class RemovePartyTarget : Target + { + public RemovePartyTarget() : base( 8, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + Party p = Party.Get( from ); + + if ( p == null || p.Leader != from || !p.Contains( m ) ) + return; + + if ( from == m ) + from.SendLocalizedMessage( 1005446 ); // You may only remove yourself from a party if you are not the leader. + else + p.Remove( m ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Pathing/FastAStarAlgorithm.cs b/Scripts/Engines/Pathing/FastAStarAlgorithm.cs new file mode 100644 index 000000000..a39e8e779 --- /dev/null +++ b/Scripts/Engines/Pathing/FastAStarAlgorithm.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.PathAlgorithms; +using CalcMoves = Server.Movement.Movement; +using MoveImpl = Server.Movement.MovementImpl; + +namespace Server.PathAlgorithms.FastAStar +{ + public struct PathNode + { + public int cost, total; + public int parent, next, prev; + public int z; + } + + public class FastAStarAlgorithm : PathAlgorithm + { + public static PathAlgorithm Instance = new FastAStarAlgorithm(); + + private const int MaxDepth = 300; + private const int AreaSize = 38; + + private const int NodeCount = AreaSize * AreaSize * PlaneCount; + + private const int PlaneOffset = 128; + private const int PlaneCount = 13; + private const int PlaneHeight = 20; + + private static Direction[] m_Path = new Direction[AreaSize * AreaSize]; + private static PathNode[] m_Nodes = new PathNode[NodeCount]; + private static BitArray m_Touched = new BitArray( NodeCount ); + private static BitArray m_OnOpen = new BitArray( NodeCount ); + private static int[] m_Successors = new int[8]; + + private static int m_xOffset, m_yOffset; + private static int m_OpenList; + + private Point3D m_Goal; + + public int Heuristic( int x, int y, int z ) + { + x -= m_Goal.X - m_xOffset; + y -= m_Goal.Y - m_yOffset; + z -= m_Goal.Z; + + x *= 11; + y *= 11; + + return (x*x)+(y*y)+(z*z); + } + + public override bool CheckCondition( Mobile m, Map map, Point3D start, Point3D goal ) + { + return Utility.InRange( start, goal, AreaSize ); + } + + private void RemoveFromChain( int node ) + { + if ( node < 0 || node >= NodeCount ) + return; + + if ( !m_Touched[node] || !m_OnOpen[node] ) + return; + + int prev = m_Nodes[node].prev; + int next = m_Nodes[node].next; + + if ( m_OpenList == node ) + m_OpenList = next; + + if ( prev != -1 ) + m_Nodes[prev].next = next; + + if ( next != -1 ) + m_Nodes[next].prev = prev; + + m_Nodes[node].prev = -1; + m_Nodes[node].next = -1; + } + + private void AddToChain( int node ) + { + if ( node < 0 || node >= NodeCount ) + return; + + RemoveFromChain( node ); + + if ( m_OpenList != -1 ) + m_Nodes[m_OpenList].prev = node; + + m_Nodes[node].next = m_OpenList; + m_Nodes[node].prev = -1; + + m_OpenList = node; + + m_Touched[node] = true; + m_OnOpen[node] = true; + } + + public override Direction[] Find( Mobile m, Map map, Point3D start, Point3D goal ) + { + if ( !Utility.InRange( start, goal, AreaSize ) ) + return null; + + m_Touched.SetAll( false ); + + m_Goal = goal; + + m_xOffset = (start.X + goal.X - AreaSize) / 2; + m_yOffset = (start.Y + goal.Y - AreaSize) / 2; + + int fromNode = GetIndex( start.X, start.Y, start.Z ); + int destNode = GetIndex( goal.X, goal.Y, goal.Z ); + + m_OpenList = fromNode; + + m_Nodes[m_OpenList].cost = 0; + m_Nodes[m_OpenList].total = Heuristic( start.X - m_xOffset, start.Y - m_yOffset, start.Z ); + m_Nodes[m_OpenList].parent = -1; + m_Nodes[m_OpenList].next = -1; + m_Nodes[m_OpenList].prev = -1; + m_Nodes[m_OpenList].z = start.Z; + + m_OnOpen[m_OpenList] = true; + m_Touched[m_OpenList] = true; + + BaseCreature bc = m as BaseCreature; + + int pathCount, parent; + int backtrack = 0, depth = 0; + + Direction[] path = m_Path; + + while ( m_OpenList != -1 ) + { + int bestNode = FindBest( m_OpenList ); + + if ( ++depth > MaxDepth ) + break; + + if ( bc != null ) + { + MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; + MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; + } + + int[] vals = m_Successors; + int count = GetSuccessors( bestNode, m, map ); + + MoveImpl.AlwaysIgnoreDoors = false; + MoveImpl.IgnoreMovableImpassables = false; + + if ( count == 0 ) + break; + + for ( int i = 0; i < count; ++i ) + { + int newNode = vals[i]; + + bool wasTouched = m_Touched[newNode]; + + if ( !wasTouched ) + { + int newCost = m_Nodes[bestNode].cost + 1; + int newTotal = newCost + Heuristic( newNode % AreaSize, (newNode / AreaSize) % AreaSize, m_Nodes[newNode].z ); + + if ( !wasTouched || m_Nodes[newNode].total > newTotal ) + { + m_Nodes[newNode].parent = bestNode; + m_Nodes[newNode].cost = newCost; + m_Nodes[newNode].total = newTotal; + + if ( !wasTouched || !m_OnOpen[newNode] ) + { + AddToChain( newNode ); + + if ( newNode == destNode ) + { + pathCount = 0; + parent = m_Nodes[newNode].parent; + + while ( parent != -1 ) + { + path[pathCount++] = GetDirection( parent % AreaSize, (parent / AreaSize) % AreaSize, newNode % AreaSize, (newNode / AreaSize) % AreaSize ); + newNode = parent; + parent = m_Nodes[newNode].parent; + + if ( newNode == fromNode ) + break; + } + + Direction[] dirs = new Direction[pathCount]; + + while ( pathCount > 0 ) + dirs[backtrack++] = path[--pathCount]; + + return dirs; + } + } + } + } + } + } + + return null; + } + + private int GetIndex( int x, int y, int z ) + { + x -= m_xOffset; + y -= m_yOffset; + z += PlaneOffset; + z /= PlaneHeight; + + return x + (y * AreaSize) + (z * AreaSize * AreaSize); + } + + private int FindBest( int node ) + { + int least = m_Nodes[node].total; + int leastNode = node; + + while ( node != -1 ) + { + if ( m_Nodes[node].total < least ) + { + least = m_Nodes[node].total; + leastNode = node; + } + + node = m_Nodes[node].next; + } + + RemoveFromChain( leastNode ); + + m_Touched[leastNode] = true; + m_OnOpen[leastNode] = false; + + return leastNode; + } + + public int GetSuccessors( int p, Mobile m, Map map ) + { + int px = p % AreaSize; + int py = (p / AreaSize) % AreaSize; + int pz = m_Nodes[p].z; + int x, y, z; + + Point3D p3D = new Point3D( px + m_xOffset, py + m_yOffset, pz ); + + int[] vals = m_Successors; + int count = 0; + + for ( int i = 0; i < 8; ++i ) + { + switch ( i ) + { + default: + case 0: x = 0; y = -1; break; + case 1: x = 1; y = -1; break; + case 2: x = 1; y = 0; break; + case 3: x = 1; y = 1; break; + case 4: x = 0; y = 1; break; + case 5: x = -1; y = 1; break; + case 6: x = -1; y = 0; break; + case 7: x = -1; y = -1; break; + } + + x += px; + y += py; + + if ( x < 0 || x >= AreaSize || y < 0 || y >= AreaSize ) + continue; + + if ( CalcMoves.CheckMovement( m, map, p3D, (Direction)i, out z ) ) + { + int idx = GetIndex( x + m_xOffset, y + m_yOffset, z ); + + if ( idx >= 0 && idx < NodeCount ) + { + m_Nodes[idx].z = z; + vals[count++] = idx; + } + } + } + + return count; + } + } +} diff --git a/Scripts/Engines/Pathing/MoveResult.cs b/Scripts/Engines/Pathing/MoveResult.cs new file mode 100644 index 000000000..075ee1c86 --- /dev/null +++ b/Scripts/Engines/Pathing/MoveResult.cs @@ -0,0 +1,14 @@ +using System; + +namespace Server +{ + public delegate MoveResult MoveMethod( Direction d ); + + public enum MoveResult + { + BadState, + Blocked, + Success, + SuccessAutoTurn + } +} \ No newline at end of file diff --git a/Scripts/Engines/Pathing/Movement.cs b/Scripts/Engines/Pathing/Movement.cs new file mode 100644 index 000000000..d1744932a --- /dev/null +++ b/Scripts/Engines/Pathing/Movement.cs @@ -0,0 +1,516 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; + +namespace Server.Movement +{ + public class MovementImpl : IMovementImpl + { + private const int PersonHeight = 16; + private const int StepHeight = 2; + + private const TileFlag ImpassableSurface = TileFlag.Impassable | TileFlag.Surface; + + private static bool m_AlwaysIgnoreDoors; + private static bool m_IgnoreMovableImpassables; + + public static bool AlwaysIgnoreDoors{ get{ return m_AlwaysIgnoreDoors; } set{ m_AlwaysIgnoreDoors = value; } } + public static bool IgnoreMovableImpassables{ get{ return m_IgnoreMovableImpassables; } set{ m_IgnoreMovableImpassables = value; } } + + public static void Configure() + { + Movement.Impl = new MovementImpl(); + } + + private MovementImpl() + { + } + + private bool IsOk( bool ignoreDoors, int ourZ, int ourTop, Tile[] tiles, List items ) + { + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile check = tiles[i]; + ItemData itemData = TileData.ItemTable[check.ID & 0x3FFF]; + + if ( (itemData.Flags & ImpassableSurface) != 0 ) // Impassable || Surface + { + int checkZ = check.Z; + int checkTop = checkZ + itemData.CalcHeight; + + if ( checkTop > ourZ && ourTop > checkZ ) + return false; + } + } + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + int itemID = item.ItemID & 0x3FFF; + ItemData itemData = TileData.ItemTable[itemID]; + TileFlag flags = itemData.Flags; + + if ( (flags & ImpassableSurface) != 0 ) // Impassable || Surface + { + if ( ignoreDoors && ((flags & TileFlag.Door) != 0 || itemID == 0x692 || itemID == 0x846 || itemID == 0x873 || (itemID >= 0x6F5 && itemID <= 0x6F6)) ) + continue; + + int checkZ = item.Z; + int checkTop = checkZ + itemData.CalcHeight; + + if ( checkTop > ourZ && ourTop > checkZ ) + return false; + } + } + + return true; + } + + private List[] m_Pools = new List[4] + { + new List(), new List(), + new List(), new List(), + }; + + private List m_Sectors = new List(); + + private bool Check( Map map, Mobile m, List items, int x, int y, int startTop, int startZ, bool canSwim, bool cantWalk, out int newZ ) + { + newZ = 0; + + Tile[] tiles = map.Tiles.GetStaticTiles( x, y, true ); + Tile landTile = map.Tiles.GetLandTile( x, y ); + + bool landBlocks = (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) != 0; + bool considerLand = !landTile.Ignored; + + if ( landBlocks && canSwim && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Wet) != 0 ) + landBlocks = false; + else if ( cantWalk && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Wet) == 0 ) + landBlocks = true; + + int landZ = 0, landCenter = 0, landTop = 0; + + map.GetAverageZ( x, y, ref landZ, ref landCenter, ref landTop ); + + bool moveIsOk = false; + + int stepTop = startTop + StepHeight; + int checkTop = startZ + PersonHeight; + + bool ignoreDoors = ( m_AlwaysIgnoreDoors || !m.Alive || m.Body.BodyID == 0x3DB || m.IsDeadBondedPet ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + ItemData itemData = TileData.ItemTable[tile.ID & 0x3FFF]; + TileFlag flags = itemData.Flags; + + if ( (flags & ImpassableSurface) == TileFlag.Surface || (canSwim && (flags & TileFlag.Wet) != 0) ) // Surface && !Impassable + { + if ( cantWalk && (flags & TileFlag.Wet) == 0 ) + continue; + + int itemZ = tile.Z; + int itemTop = itemZ; + int ourZ = itemZ + itemData.CalcHeight; + int ourTop = ourZ + PersonHeight; + int testTop = checkTop; + + if ( moveIsOk ) + { + int cmp = Math.Abs( ourZ - m.Z ) - Math.Abs( newZ - m.Z ); + + if ( cmp > 0 || (cmp == 0 && ourZ > newZ) ) + continue; + } + + if ( ourZ + PersonHeight > testTop ) + testTop = ourZ + PersonHeight; + + if ( !itemData.Bridge ) + itemTop += itemData.Height; + + if ( stepTop >= itemTop ) + { + int landCheck = itemZ; + + if ( itemData.Height >= StepHeight ) + landCheck += StepHeight; + else + landCheck += itemData.Height; + + if ( considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ ) + continue; + + if ( IsOk( ignoreDoors, ourZ, testTop, tiles, items ) ) + { + newZ = ourZ; + moveIsOk = true; + } + } + } + } + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + ItemData itemData = item.ItemData; + TileFlag flags = itemData.Flags; + + if ( !item.Movable && ((flags & ImpassableSurface) == TileFlag.Surface || (m.CanSwim && (flags & TileFlag.Wet) != 0)) ) // Surface && !Impassable && !Movable + { + if ( cantWalk && (flags & TileFlag.Wet) == 0 ) + continue; + + int itemZ = item.Z; + int itemTop = itemZ; + int ourZ = itemZ + itemData.CalcHeight; + int ourTop = ourZ + PersonHeight; + int testTop = checkTop; + + if ( moveIsOk ) + { + int cmp = Math.Abs( ourZ - m.Z ) - Math.Abs( newZ - m.Z ); + + if ( cmp > 0 || (cmp == 0 && ourZ > newZ) ) + continue; + } + + if ( ourZ + PersonHeight > testTop ) + testTop = ourZ + PersonHeight; + + if ( !itemData.Bridge ) + itemTop += itemData.Height; + + if ( stepTop >= itemTop ) + { + int landCheck = itemZ; + + if ( itemData.Height >= StepHeight ) + landCheck += StepHeight; + else + landCheck += itemData.Height; + + if ( considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ ) + continue; + + if ( IsOk( ignoreDoors, ourZ, testTop, tiles, items ) ) + { + newZ = ourZ; + moveIsOk = true; + } + } + } + } + + if ( considerLand && !landBlocks && stepTop >= landZ ) + { + int ourZ = landCenter; + int ourTop = ourZ + PersonHeight; + int testTop = checkTop; + + if ( ourZ + PersonHeight > testTop ) + testTop = ourZ + PersonHeight; + + bool shouldCheck = true; + + if ( moveIsOk ) + { + int cmp = Math.Abs( ourZ - m.Z ) - Math.Abs( newZ - m.Z ); + + if ( cmp > 0 || (cmp == 0 && ourZ > newZ) ) + shouldCheck = false; + } + + if ( shouldCheck && IsOk( ignoreDoors, ourZ, testTop, tiles, items ) ) + { + newZ = ourZ; + moveIsOk = true; + } + } + + return moveIsOk; + } + + public bool CheckMovement( Mobile m, Map map, Point3D loc, Direction d, out int newZ ) + { + if ( map == null || map == Map.Internal ) + { + newZ = 0; + return false; + } + + int xStart = loc.X; + int yStart = loc.Y; + int xForward = xStart, yForward = yStart; + int xRight = xStart, yRight = yStart; + int xLeft = xStart, yLeft = yStart; + + bool checkDiagonals = ((int)d & 0x1) == 0x1; + + Offset( d, ref xForward, ref yForward ); + Offset( (Direction)(((int)d - 1) & 0x7), ref xLeft, ref yLeft ); + Offset( (Direction)(((int)d + 1) & 0x7), ref xRight, ref yRight ); + + if ( xForward < 0 || yForward < 0 || xForward >= map.Width || yForward >= map.Height ) + { + newZ = 0; + return false; + } + + int startZ, startTop; + + List itemsStart = m_Pools[0]; + List itemsForward = m_Pools[1]; + List itemsLeft = m_Pools[2]; + List itemsRight = m_Pools[3]; + + bool ignoreMovableImpassables = m_IgnoreMovableImpassables; + TileFlag reqFlags = ImpassableSurface; + + if ( m.CanSwim ) + reqFlags |= TileFlag.Wet; + + if ( checkDiagonals ) + { + Sector sectorStart = map.GetSector( xStart, yStart ); + Sector sectorForward = map.GetSector( xForward, yForward ); + Sector sectorLeft = map.GetSector( xLeft, yLeft ); + Sector sectorRight = map.GetSector( xRight, yRight ); + + List sectors = m_Sectors; + + sectors.Add( sectorStart ); + + if ( !sectors.Contains( sectorForward ) ) + sectors.Add( sectorForward ); + + if ( !sectors.Contains( sectorLeft ) ) + sectors.Add( sectorLeft ); + + if ( !sectors.Contains( sectorRight ) ) + sectors.Add( sectorRight ); + + for ( int i = 0; i < sectors.Count; ++i ) + { + Sector sector = sectors[i]; + + for ( int j = 0; j < sector.Items.Count; ++j ) + { + Item item = sector.Items[j]; + + if ( ignoreMovableImpassables && item.Movable && item.ItemData.Impassable ) + continue; + + if ( (item.ItemData.Flags & reqFlags) == 0 ) + continue; + + if ( sector == sectorStart && item.AtWorldPoint( xStart, yStart ) && item.ItemID < 0x4000 ) + itemsStart.Add( item ); + else if ( sector == sectorForward && item.AtWorldPoint( xForward, yForward ) && item.ItemID < 0x4000 ) + itemsForward.Add( item ); + else if ( sector == sectorLeft && item.AtWorldPoint( xLeft, yLeft ) && item.ItemID < 0x4000 ) + itemsLeft.Add( item ); + else if ( sector == sectorRight && item.AtWorldPoint( xRight, yRight ) && item.ItemID < 0x4000 ) + itemsRight.Add( item ); + } + } + + if ( m_Sectors.Count > 0 ) + m_Sectors.Clear(); + } + else + { + Sector sectorStart = map.GetSector( xStart, yStart ); + Sector sectorForward = map.GetSector( xForward, yForward ); + + if ( sectorStart == sectorForward ) + { + for ( int i = 0; i < sectorStart.Items.Count; ++i ) + { + Item item = sectorStart.Items[i]; + + if ( ignoreMovableImpassables && item.Movable && item.ItemData.Impassable ) + continue; + + if ( (item.ItemData.Flags & reqFlags) == 0 ) + continue; + + if ( item.AtWorldPoint( xStart, yStart ) && item.ItemID < 0x4000 ) + itemsStart.Add( item ); + else if ( item.AtWorldPoint( xForward, yForward ) && item.ItemID < 0x4000 ) + itemsForward.Add( item ); + } + } + else + { + for ( int i = 0; i < sectorForward.Items.Count; ++i ) + { + Item item = sectorForward.Items[i]; + + if ( ignoreMovableImpassables && item.Movable && item.ItemData.Impassable ) + continue; + + if ( (item.ItemData.Flags & reqFlags) == 0 ) + continue; + + if ( item.AtWorldPoint( xForward, yForward ) && item.ItemID < 0x4000 ) + itemsForward.Add( item ); + } + + for ( int i = 0; i < sectorStart.Items.Count; ++i ) + { + Item item = sectorStart.Items[i]; + + if ( ignoreMovableImpassables && item.Movable && item.ItemData.Impassable ) + continue; + + if ( (item.ItemData.Flags & reqFlags) == 0 ) + continue; + + if ( item.AtWorldPoint( xStart, yStart ) && item.ItemID < 0x4000 ) + itemsStart.Add( item ); + } + } + } + + GetStartZ( m, map, loc, itemsStart, out startZ, out startTop ); + + bool moveIsOk = Check( map, m, itemsForward, xForward, yForward, startTop, startZ, m.CanSwim, m.CantWalk, out newZ ); + + if ( moveIsOk && checkDiagonals ) + { + int hold; + + if ( m.Player && m.AccessLevel < AccessLevel.GameMaster ) { + if ( !Check( map, m, itemsLeft, xLeft, yLeft, startTop, startZ, m.CanSwim, m.CantWalk, out hold ) || !Check( map, m, itemsRight, xRight, yRight, startTop, startZ, m.CanSwim, m.CantWalk, out hold ) ) + moveIsOk = false; + } else { + if ( !Check( map, m, itemsLeft, xLeft, yLeft, startTop, startZ, m.CanSwim, m.CantWalk, out hold ) && !Check( map, m, itemsRight, xRight, yRight, startTop, startZ, m.CanSwim, m.CantWalk, out hold ) ) + moveIsOk = false; + } + } + + for ( int i = 0; i < (checkDiagonals ? 4 : 2); ++i ) + { + if ( m_Pools[i].Count > 0 ) + m_Pools[i].Clear(); + } + + if ( !moveIsOk ) + newZ = startZ; + + return moveIsOk; + } + + public bool CheckMovement( Mobile m, Direction d, out int newZ ) + { + return CheckMovement( m, m.Map, m.Location, d, out newZ ); + } + + private void GetStartZ( Mobile m, Map map, Point3D loc, List itemList, out int zLow, out int zTop ) + { + int xCheck = loc.X, yCheck = loc.Y; + + Tile landTile = map.Tiles.GetLandTile( xCheck, yCheck ); + int landZ = 0, landCenter = 0, landTop = 0; + bool landBlocks = (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) != 0; + + if ( landBlocks && m.CanSwim && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Wet) != 0 ) + landBlocks = false; + else if ( m.CantWalk && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Wet) == 0 ) + landBlocks = true; + + map.GetAverageZ( xCheck, yCheck, ref landZ, ref landCenter, ref landTop ); + + bool considerLand = !landTile.Ignored; + + int zCenter = zLow = zTop = 0; + bool isSet = false; + + if ( considerLand && !landBlocks && loc.Z >= landCenter ) + { + zLow = landZ; + zCenter = landCenter; + + if ( !isSet || landTop > zTop ) + zTop = landTop; + + isSet = true; + } + + Tile[] staticTiles = map.Tiles.GetStaticTiles( xCheck, yCheck, true ); + + for ( int i = 0; i < staticTiles.Length; ++i ) + { + Tile tile = staticTiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + int calcTop = (tile.Z + id.CalcHeight); + + if ( (!isSet || calcTop >= zCenter) && ( (id.Flags & TileFlag.Surface) != 0 || ( m.CanSwim && (id.Flags&TileFlag.Wet) != 0 ) ) && loc.Z >= calcTop ) + { + if ( m.CantWalk && (id.Flags & TileFlag.Wet) == 0 ) + continue; + + zLow = tile.Z; + zCenter = calcTop; + + int top = tile.Z + id.Height; + + if ( !isSet || top > zTop ) + zTop = top; + + isSet = true; + } + } + + for ( int i = 0; i < itemList.Count; ++i ) + { + Item item = itemList[i]; + + ItemData id = item.ItemData; + + int calcTop = item.Z + id.CalcHeight; + + if ( (!isSet || calcTop >= zCenter) && ( (id.Flags & TileFlag.Surface) != 0 || ( m.CanSwim && (id.Flags&TileFlag.Wet) != 0 ) ) && loc.Z >= calcTop ) + { + if ( m.CantWalk && (id.Flags & TileFlag.Wet) == 0 ) + continue; + + zLow = item.Z; + zCenter = calcTop; + + int top = item.Z + id.Height; + + if ( !isSet || top > zTop ) + zTop = top; + + isSet = true; + } + } + + if ( !isSet ) + zLow = zTop = loc.Z; + else if ( loc.Z > zTop ) + zTop = loc.Z; + } + + public void Offset( Direction d, ref int x, ref int y ) + { + switch ( d & Direction.Mask ) + { + case Direction.North: --y; break; + case Direction.South: ++y; break; + case Direction.West: --x; break; + case Direction.East: ++x; break; + case Direction.Right: ++x; --y; break; + case Direction.Left: --x; ++y; break; + case Direction.Down: ++x; ++y; break; + case Direction.Up: --x; --y; break; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Pathing/MovementPath.cs b/Scripts/Engines/Pathing/MovementPath.cs new file mode 100644 index 000000000..5f4bc430c --- /dev/null +++ b/Scripts/Engines/Pathing/MovementPath.cs @@ -0,0 +1,165 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.PathAlgorithms; +using Server.PathAlgorithms.SlowAStar; +using Server.PathAlgorithms.FastAStar; +using Server.Commands; + +namespace Server +{ + public sealed class MovementPath + { + private Map m_Map; + private Point3D m_Start; + private Point3D m_Goal; + private Direction[] m_Directions; + + public Map Map{ get{ return m_Map; } } + public Point3D Start{ get{ return m_Start; } } + public Point3D Goal{ get{ return m_Goal; } } + public Direction[] Directions{ get{ return m_Directions; } } + public bool Success{ get{ return ( m_Directions != null && m_Directions.Length > 0 ); } } + + public static void Initialize() + { + CommandSystem.Register( "Path", AccessLevel.GameMaster, new CommandEventHandler( Path_OnCommand ) ); + } + + public static void Path_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, true, TargetFlags.None, new TargetCallback( Path_OnTarget ) ); + e.Mobile.SendMessage( "Target a location and a path will be drawn there." ); + } + + private static void Path( Mobile from, IPoint3D p, PathAlgorithm alg, string name, int zOffset ) + { + m_OverrideAlgorithm = alg; + + long start = DateTime.Now.Ticks; + MovementPath path = new MovementPath( from, new Point3D( p ) ); + long end = DateTime.Now.Ticks; + double len = Math.Round( (end-start) / 10000.0, 2 ); + + if ( !path.Success ) + { + from.SendMessage( "{0} path failed: {1}ms", name, len ); + } + else + { + from.SendMessage( "{0} path success: {1}ms", name, len ); + + int x = from.X; + int y = from.Y; + int z = from.Z; + + for ( int i = 0; i < path.Directions.Length; ++i ) + { + Movement.Movement.Offset( path.Directions[i], ref x, ref y ); + + new Items.RecallRune().MoveToWorld( new Point3D( x, y, z+zOffset ), from.Map ); + } + } + } + + public static void Path_OnTarget( Mobile from, object obj ) + { + IPoint3D p = obj as IPoint3D; + + if ( p == null ) + return; + + Spells.SpellHelper.GetSurfaceTop( ref p ); + + Path( from, p, FastAStarAlgorithm.Instance, "Fast", 0 ); + Path( from, p, SlowAStarAlgorithm.Instance, "Slow", 2 ); + m_OverrideAlgorithm = null; + + /*MovementPath path = new MovementPath( from, new Point3D( p ) ); + + if ( !path.Success ) + { + from.SendMessage( "No path to there could be found." ); + } + else + { + //for ( int i = 0; i < path.Directions.Length; ++i ) + // Timer.DelayCall( TimeSpan.FromSeconds( 0.1 + (i * 0.3) ), new TimerStateCallback( Pathfind ), new object[]{ from, path.Directions[i] } ); + int x = from.X; + int y = from.Y; + int z = from.Z; + + for ( int i = 0; i < path.Directions.Length; ++i ) + { + Movement.Movement.Offset( path.Directions[i], ref x, ref y ); + + new Items.RecallRune().MoveToWorld( new Point3D( x, y, z ), from.Map ); + } + }*/ + } + + public static void Pathfind( object state ) + { + object[] states = (object[])state; + Mobile from = (Mobile) states[0]; + Direction d = (Direction) states[1]; + + try + { + from.Direction = d; + from.NetState.BlockAllPackets=true; + from.Move( d ); + from.NetState.BlockAllPackets=false; + from.ProcessDelta(); + } + catch + { + } + } + + private static PathAlgorithm m_OverrideAlgorithm; + + public static PathAlgorithm OverrideAlgorithm + { + get{ return m_OverrideAlgorithm; } + set{ m_OverrideAlgorithm = value; } + } + + public MovementPath( Mobile m, Point3D goal ) + { + Point3D start = m.Location; + Map map = m.Map; + + m_Map = map; + m_Start = start; + m_Goal = goal; + + if ( map == null || map == Map.Internal ) + return; + + if ( Utility.InRange( start, goal, 1 ) ) + return; + + try + { + PathAlgorithm alg = m_OverrideAlgorithm; + + if ( alg == null ) + { + alg = FastAStarAlgorithm.Instance; + + if ( !alg.CheckCondition( m, map, start, goal ) ) + alg = SlowAStarAlgorithm.Instance; + } + + if ( alg != null && alg.CheckCondition( m, map, start, goal ) ) + m_Directions = alg.Find( m, map, start, goal ); + } + catch ( Exception e ) + { + Console.WriteLine( "Warning: {0}: Pathing error from {1} to {2}", e.GetType().Name, start, goal ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Pathing/PathAlgorithm.cs b/Scripts/Engines/Pathing/PathAlgorithm.cs new file mode 100644 index 000000000..d7e7a51f5 --- /dev/null +++ b/Scripts/Engines/Pathing/PathAlgorithm.cs @@ -0,0 +1,35 @@ +using System; + +namespace Server.PathAlgorithms +{ + public abstract class PathAlgorithm + { + public abstract bool CheckCondition( Mobile m, Map map, Point3D start, Point3D goal ); + public abstract Direction[] Find( Mobile m, Map map, Point3D start, Point3D goal ); + + private static Direction[] m_CalcDirections = new Direction[9] + { + Direction.Up, + Direction.North, + Direction.Right, + Direction.West, + Direction.North, + Direction.East, + Direction.Left, + Direction.South, + Direction.Down + }; + + public Direction GetDirection( int xSource, int ySource, int xDest, int yDest ) + { + int x = xDest + 1 - xSource; + int y = yDest + 1 - ySource; + int v = (y * 3) + x; + + if ( v < 0 || v >= 9 ) + return Direction.North; + + return m_CalcDirections[v]; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Pathing/PathFollower.cs b/Scripts/Engines/Pathing/PathFollower.cs new file mode 100644 index 000000000..c814bf331 --- /dev/null +++ b/Scripts/Engines/Pathing/PathFollower.cs @@ -0,0 +1,204 @@ +using System; +using Server; +using Server.Movement; +using CalcMoves = Server.Movement.Movement; + +namespace Server +{ + public class PathFollower + { + // Should we use pathfinding? 'false' for not + private static bool Enabled = true; + + private Mobile m_From; + private IPoint3D m_Goal; + private MovementPath m_Path; + private int m_Index; + private Point3D m_Next, m_LastGoalLoc; + private DateTime m_LastPathTime; + private MoveMethod m_Mover; + + public MoveMethod Mover + { + get{ return m_Mover; } + set{ m_Mover = value; } + } + + public IPoint3D Goal + { + get{ return m_Goal; } + } + + public PathFollower( Mobile from, IPoint3D goal ) + { + m_From = from; + m_Goal = goal; + } + + public MoveResult Move( Direction d ) + { + if ( m_Mover == null ) + return ( m_From.Move( d ) ? MoveResult.Success : MoveResult.Blocked ); + + return m_Mover( d ); + } + + public Point3D GetGoalLocation() + { + if ( m_Goal is Item ) + return ((Item)m_Goal).GetWorldLocation(); + + return new Point3D( m_Goal ); + } + + private static TimeSpan RepathDelay = TimeSpan.FromSeconds( 2.0 ); + + public void Advance( ref Point3D p, int index ) + { + if ( m_Path != null && m_Path.Success ) + { + Direction[] dirs = m_Path.Directions; + + if ( index >= 0 && index < dirs.Length ) + { + int x = p.X, y = p.Y; + + CalcMoves.Offset( dirs[index], ref x, ref y ); + + p.X = x; + p.Y = y; + } + } + } + + public void ForceRepath() + { + m_Path = null; + } + + public bool CheckPath() + { + if ( !Enabled ) + return false; + + bool repath = false; + + Point3D goal = GetGoalLocation(); + + if ( m_Path == null ) + repath = true; + else if ( (!m_Path.Success || goal != m_LastGoalLoc) && (m_LastPathTime + RepathDelay) <= DateTime.Now ) + repath = true; + else if ( m_Path.Success && Check( m_From.Location, m_LastGoalLoc, 0 ) ) + repath = true; + + if ( !repath ) + return false; + + m_LastPathTime = DateTime.Now; + m_LastGoalLoc = goal; + + m_Path = new MovementPath( m_From, goal ); + + m_Index = 0; + m_Next = m_From.Location; + + Advance( ref m_Next, m_Index ); + + return true; + } + + public bool Check( Point3D loc, Point3D goal, int range ) + { + if ( !Utility.InRange( loc, goal, range ) ) + return false; + + if ( range <= 1 && Math.Abs( loc.Z - goal.Z ) >= 16 ) + return false; + + return true; + } + + public bool Follow( bool run, int range ) + { + Point3D goal = GetGoalLocation(); + Direction d; + + if ( Check( m_From.Location, goal, range ) ) + return true; + + bool repathed = CheckPath(); + + if ( !Enabled || !m_Path.Success ) + { + d = m_From.GetDirectionTo( goal ); + + if ( run ) + d |= Direction.Running; + + m_From.SetDirection( d ); + Move( d ); + + return Check( m_From.Location, goal, range ); + } + + d = m_From.GetDirectionTo( m_Next ); + + if ( run ) + d |= Direction.Running; + + m_From.SetDirection( d ); + + MoveResult res = Move( d ); + + if ( res == MoveResult.Blocked ) + { + if ( repathed ) + return false; + + m_Path = null; + CheckPath(); + + if ( !m_Path.Success ) + { + d = m_From.GetDirectionTo( goal ); + + if ( run ) + d |= Direction.Running; + + m_From.SetDirection( d ); + Move( d ); + + return Check( m_From.Location, goal, range ); + } + + d = m_From.GetDirectionTo( m_Next ); + + if ( run ) + d |= Direction.Running; + + m_From.SetDirection( d ); + + res = Move( d ); + + if ( res == MoveResult.Blocked ) + return false; + } + + if ( m_From.X == m_Next.X && m_From.Y == m_Next.Y ) + { + if ( m_From.Z == m_Next.Z ) + { + ++m_Index; + Advance( ref m_Next, m_Index ); + } + else + { + m_Path = null; + } + } + + return Check( m_From.Location, goal, range ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Pathing/SlowAStarAlgorithm.cs b/Scripts/Engines/Pathing/SlowAStarAlgorithm.cs new file mode 100644 index 000000000..a558c74ce --- /dev/null +++ b/Scripts/Engines/Pathing/SlowAStarAlgorithm.cs @@ -0,0 +1,260 @@ +using System; +using Server; +using Server.Mobiles; +using Server.PathAlgorithms; +using CalcMoves = Server.Movement.Movement; +using MoveImpl = Server.Movement.MovementImpl; + +namespace Server.PathAlgorithms.SlowAStar +{ + public struct PathNode + { + public int x, y, z; + public int g, h; + public int px, py, pz; + public int dir; + } + + public class SlowAStarAlgorithm : PathAlgorithm + { + public static PathAlgorithm Instance = new SlowAStarAlgorithm(); + + private const int MaxDepth = 300; + private const int MaxNodes = MaxDepth * 16; + + private static PathNode[] m_Closed = new PathNode[MaxNodes]; + private static PathNode[] m_Open = new PathNode[MaxNodes]; + private static PathNode[] m_Successors = new PathNode[8]; + private static Direction[] m_Path = new Direction[MaxNodes]; + + private Point3D m_Goal; + + public int Heuristic( int x, int y, int z ) + { + x -= m_Goal.X; + y -= m_Goal.Y; + z -= m_Goal.Z; + + x *= 11; + y *= 11; + + return (x*x)+(y*y)+(z*z); + } + + public override bool CheckCondition( Mobile m, Map map, Point3D start, Point3D goal ) + { + return false; + } + + public override Direction[] Find( Mobile m, Map map, Point3D start, Point3D goal ) + { + m_Goal = goal; + + BaseCreature bc = m as BaseCreature; + + PathNode curNode; + + PathNode goalNode = new PathNode(); + goalNode.x = goal.X; + goalNode.y = goal.Y; + goalNode.z = goal.Z; + + PathNode startNode = new PathNode(); + startNode.x = start.X; + startNode.y = start.Y; + startNode.z = start.Z; + startNode.h = Heuristic( startNode.x, startNode.y, startNode.z ); + + PathNode[] closed = m_Closed, open = m_Open, successors = m_Successors; + Direction[] path = m_Path; + + int closedCount = 0, openCount = 0, sucCount = 0, pathCount = 0; + int popIndex, curF; + int x, y, z; + int depth = 0; + + int xBacktrack, yBacktrack, zBacktrack, iBacktrack = 0; + + open[openCount++] = startNode; + + while ( openCount > 0 ) + { + curNode = open[0]; + curF = curNode.g + curNode.h; + popIndex = 0; + + for ( int i = 1; i < openCount; ++i ) + { + if ( (open[i].g + open[i].h) < curF ) + { + curNode = open[i]; + curF = curNode.g + curNode.h; + popIndex = i; + } + } + + if ( curNode.x == goalNode.x && curNode.y == goalNode.y && Math.Abs( curNode.z-goalNode.z ) < 16 ) + { + if ( closedCount == MaxNodes ) + break; + + closed[closedCount++] = curNode; + + xBacktrack = curNode.px; + yBacktrack = curNode.py; + zBacktrack = curNode.pz; + + if ( pathCount == MaxNodes ) + break; + + path[pathCount++] = (Direction)curNode.dir; + + while ( xBacktrack != startNode.x || yBacktrack != startNode.y || zBacktrack != startNode.z ) + { + bool found = false; + + for ( int j = 0; !found && j < closedCount; ++j ) + { + if ( closed[j].x == xBacktrack && closed[j].y == yBacktrack && closed[j].z == zBacktrack ) + { + if ( pathCount == MaxNodes ) + break; + + curNode = closed[j]; + path[pathCount++] = (Direction)curNode.dir; + xBacktrack = curNode.px; + yBacktrack = curNode.py; + zBacktrack = curNode.pz; + found = true; + } + } + + if ( !found ) + { + Console.WriteLine( "bugaboo.." ); + return null; + } + + if ( pathCount == MaxNodes ) + break; + } + + if ( pathCount == MaxNodes ) + break; + + Direction[] dirs = new Direction[pathCount]; + + while ( pathCount > 0 ) + dirs[iBacktrack++] = path[--pathCount]; + + return dirs; + } + + --openCount; + + for ( int i = popIndex; i < openCount; ++i ) + open[i] = open[i + 1]; + + sucCount = 0; + + if ( bc != null ) + { + MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; + MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; + } + + for ( int i = 0; i < 8; ++i ) + { + switch ( i ) + { + default: + case 0: x = 0; y = -1; break; + case 1: x = 1; y = -1; break; + case 2: x = 1; y = 0; break; + case 3: x = 1; y = 1; break; + case 4: x = 0; y = 1; break; + case 5: x = -1; y = 1; break; + case 6: x = -1; y = 0; break; + case 7: x = -1; y = -1; break; + } + + if ( CalcMoves.CheckMovement( m, map, new Point3D( curNode.x, curNode.y, curNode.z ), (Direction)i, out z ) ) + { + successors[sucCount].x = x + curNode.x; + successors[sucCount].y = y + curNode.y; + successors[sucCount++].z = z; + } + } + + MoveImpl.AlwaysIgnoreDoors = false; + MoveImpl.IgnoreMovableImpassables = false; + + if ( sucCount == 0 || ++depth > MaxDepth ) + break; + + for ( int i = 0; i < sucCount; ++i ) + { + x = successors[i].x; + y = successors[i].y; + z = successors[i].z; + + successors[i].g = curNode.g + 1; + + int openIndex = -1, closedIndex = -1; + + for ( int j = 0; openIndex == -1 && j < openCount; ++j ) + { + if ( open[j].x == x && open[j].y == y && open[j].z == z ) + openIndex = j; + } + + if ( openIndex >= 0 && open[openIndex].g < successors[i].g ) + continue; + + for ( int j = 0; closedIndex == -1 && j < closedCount; ++j ) + { + if ( closed[j].x == x && closed[j].y == y && closed[j].z == z ) + closedIndex = j; + } + + if ( closedIndex >= 0 && closed[closedIndex].g < successors[i].g ) + continue; + + if ( openIndex >= 0 ) + { + --openCount; + + for ( int j = openIndex; j < openCount; ++j ) + open[j] = open[j + 1]; + } + + if ( closedIndex >= 0 ) + { + --closedCount; + + for ( int j = closedIndex; j < closedCount; ++j ) + closed[j] = closed[j + 1]; + } + + successors[i].px = curNode.x; + successors[i].py = curNode.y; + successors[i].pz = curNode.z; + successors[i].dir = (int)GetDirection( curNode.x, curNode.y, x, y ); + successors[i].h = Heuristic( x, y, z ); + + if ( openCount == MaxNodes ) + break; + + open[openCount++] = successors[i]; + } + + if ( openCount == MaxNodes || closedCount == MaxNodes ) + break; + + closed[closedCount++] = curNode; + } + + return null; + } + } +} diff --git a/Scripts/Engines/Plants/EmptyTheBowlGump.cs b/Scripts/Engines/Plants/EmptyTheBowlGump.cs new file mode 100644 index 000000000..6d662a5ca --- /dev/null +++ b/Scripts/Engines/Plants/EmptyTheBowlGump.cs @@ -0,0 +1,117 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Plants +{ + public class EmptyTheBowlGump : Gump + { + private PlantItem m_Plant; + + public EmptyTheBowlGump( PlantItem plant ) : base( 20, 20 ) + { + m_Plant = plant; + + DrawBackground(); + + AddLabel( 90, 70, 0x44, "Empty the bowl?" ); + + DrawPicture(); + + AddButton( 98, 150, 0x47E, 0x480, 1, GumpButtonType.Reply, 0 ); // Cancel + + AddButton( 138, 151, 0xD2, 0xD2, 2, GumpButtonType.Reply, 0 ); // Help + AddLabel( 143, 151, 0x835, "?" ); + + AddButton( 168, 150, 0x481, 0x483, 3, GumpButtonType.Reply, 0 ); // Ok + } + + private void DrawBackground() + { + AddBackground( 50, 50, 200, 150, 0xE10 ); + + AddItem( 45, 45, 0xCEF ); + AddItem( 45, 118, 0xCF0 ); + + AddItem( 211, 45, 0xCEB ); + AddItem( 211, 118, 0xCEC ); + } + + private void DrawPicture() + { + AddItem( 90, 100, 0x1602 ); + AddImage( 140, 102, 0x15E1 ); + AddItem( 160, 100, 0x15FD ); + + if ( m_Plant.PlantStatus != PlantStatus.BowlOfDirt && m_Plant.PlantStatus < PlantStatus.Plant ) + AddItem( 156, 130, 0xDCF ); // Seed + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant || !from.InRange( m_Plant.GetWorldLocation(), 3 ) ) + return; + + if ( !m_Plant.IsUsableBy( from ) ) + { + m_Plant.LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch ( info.ButtonID ) + { + case 1: // Cancel + { + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 2: // Help + { + from.Send( new DisplayHelpTopic( 71, true ) ); // EMPTYING THE BOWL + + from.SendGump( new EmptyTheBowlGump( m_Plant ) ); + + break; + } + case 3: // Ok + { + PlantBowl bowl = new PlantBowl(); + + if ( !from.PlaceInBackpack( bowl ) ) + { + bowl.Delete(); + + m_Plant.LabelTo( from, 1053047 ); // You cannot empty a bowl with a full pack! + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + + if ( m_Plant.PlantStatus != PlantStatus.BowlOfDirt && m_Plant.PlantStatus < PlantStatus.Plant ) + { + Seed seed = new Seed( m_Plant.PlantType, m_Plant.PlantHue, m_Plant.ShowType ); + + if ( !from.PlaceInBackpack( seed ) ) + { + bowl.Delete(); + seed.Delete(); + + m_Plant.LabelTo( from, 1053047 ); // You cannot empty a bowl with a full pack! + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + } + + m_Plant.Delete(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/MainPlantGump.cs b/Scripts/Engines/Plants/MainPlantGump.cs new file mode 100644 index 000000000..9adf40943 --- /dev/null +++ b/Scripts/Engines/Plants/MainPlantGump.cs @@ -0,0 +1,388 @@ +using System; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Plants +{ + public class MainPlantGump : Gump + { + private PlantItem m_Plant; + + public MainPlantGump( PlantItem plant ) : base( 20, 20 ) + { + m_Plant = plant; + + DrawBackground(); + + DrawPlant(); + + AddButton( 71, 67, 0xD4, 0xD4, 1, GumpButtonType.Reply, 0 ); // Reproduction menu + AddItem( 59, 68, 0xD08 ); + + PlantSystem system = plant.PlantSystem; + + AddButton( 71, 91, 0xD4, 0xD4, 2, GumpButtonType.Reply, 0 ); // Infestation + AddItem( 8, 96, 0x372 ); + AddPlus( 95, 92, system.Infestation ); + + AddButton( 71, 115, 0xD4, 0xD4, 3, GumpButtonType.Reply, 0 ); // Fungus + AddItem( 58, 115, 0xD16 ); + AddPlus( 95, 116, system.Fungus ); + + AddButton( 71, 139, 0xD4, 0xD4, 4, GumpButtonType.Reply, 0 ); // Poison + AddItem( 59, 143, 0x1AE4 ); + AddPlus( 95, 140, system.Poison ); + + AddButton( 71, 163, 0xD4, 0xD4, 5, GumpButtonType.Reply, 0 ); // Disease + AddItem( 55, 167, 0x1727 ); + AddPlus( 95, 164, system.Disease ); + + AddButton( 209, 67, 0xD2, 0xD2, 6, GumpButtonType.Reply, 0 ); // Water + AddItem( 193, 67, 0x1F9D ); + AddPlusMinus( 196, 67, system.Water ); + + AddButton( 209, 91, 0xD4, 0xD4, 7, GumpButtonType.Reply, 0 ); // Poison potion + AddItem( 197, 91, 0xF0A ); + AddLevel( 196, 91, system.PoisonPotion ); + + AddButton( 209, 115, 0xD4, 0xD4, 8, GumpButtonType.Reply, 0 ); // Cure potion + AddItem( 192, 115, 0xF07 ); + AddLevel( 196, 115, system.CurePotion ); + + AddButton( 209, 139, 0xD4, 0xD4, 9, GumpButtonType.Reply, 0 ); // Heal potion + AddItem( 190, 139, 0xF0C ); + AddLevel( 196, 139, system.HealPotion ); + + AddButton( 209, 163, 0xD4, 0xD4, 10, GumpButtonType.Reply, 0 ); // Strength potion + AddItem( 193, 163, 0xF09 ); + AddLevel( 196, 163, system.StrengthPotion ); + + AddImage( 48, 47, 0xD2 ); + AddLevel( 54, 47, (int)m_Plant.PlantStatus ); + + AddImage( 232, 47, 0xD2 ); + AddGrowthIndicator( 239, 47 ); + + AddButton( 48, 183, 0xD2, 0xD2, 11, GumpButtonType.Reply, 0 ); // Help + AddLabel( 54, 183, 0x835, "?" ); + + AddButton( 232, 183, 0xD4, 0xD4, 12, GumpButtonType.Reply, 0 ); // Empty the bowl + AddItem( 219, 180, 0x15FD ); + } + + private void DrawBackground() + { + AddBackground( 50, 50, 200, 150, 0xE10 ); + + AddItem( 45, 45, 0xCEF ); + AddItem( 45, 118, 0xCF0 ); + + AddItem( 211, 45, 0xCEB ); + AddItem( 211, 118, 0xCEC ); + } + + private void DrawPlant() + { + PlantStatus status = m_Plant.PlantStatus; + + if ( status < PlantStatus.FullGrownPlant ) + { + AddImage( 110, 85, 0x589 ); + + AddItem( 122, 94, 0x914 ); + AddItem( 135, 94, 0x914 ); + AddItem( 120, 112, 0x914 ); + AddItem( 135, 112, 0x914 ); + + if ( status >= PlantStatus.Stage2 ) + { + AddItem( 127, 112, 0xC62 ); + } + if ( status == PlantStatus.Stage3 || status == PlantStatus.Stage4 ) + { + AddItem( 129, 85, 0xC7E ); + } + if ( status >= PlantStatus.Stage4 ) + { + AddItem( 121, 117, 0xC62 ); + AddItem( 133, 117, 0xC62 ); + } + if ( status >= PlantStatus.Stage5 ) + { + AddItem( 110, 100, 0xC62 ); + AddItem( 140, 100, 0xC62 ); + AddItem( 110, 130, 0xC62 ); + AddItem( 140, 130, 0xC62 ); + } + if ( status >= PlantStatus.Stage6 ) + { + AddItem( 105, 115, 0xC62 ); + AddItem( 145, 115, 0xC62 ); + AddItem( 125, 90, 0xC62 ); + AddItem( 125, 135, 0xC62 ); + } + } + else + { + PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo( m_Plant.PlantType ); + PlantHueInfo hueInfo = PlantHueInfo.GetInfo( m_Plant.PlantHue ); + + AddItem( 130 + typeInfo.OffsetX, 96 + typeInfo.OffsetY, typeInfo.ItemID, hueInfo.Hue ); + } + + if ( status != PlantStatus.BowlOfDirt ) + { + int message = m_Plant.PlantSystem.GetLocalizedHealth(); + + switch ( m_Plant.PlantSystem.Health ) + { + case PlantHealth.Dying: + { + AddItem( 92, 167, 0x1B9D ); + AddItem( 161, 167, 0x1B9D ); + + AddHtmlLocalized( 136, 167, 42, 20, message, 0x00FC00, false, false ); + + break; + } + case PlantHealth.Wilted: + { + AddItem( 91, 164, 0x18E6 ); + AddItem( 161, 164, 0x18E6 ); + + AddHtmlLocalized( 132, 167, 42, 20, message, 0x00C207, false, false ); + + break; + } + case PlantHealth.Healthy: + { + AddItem( 96, 168, 0xC61 ); + AddItem( 162, 168, 0xC61 ); + + AddHtmlLocalized( 129, 167, 42, 20, message, 0x008200, false, false ); + + break; + } + case PlantHealth.Vibrant: + { + AddItem( 93, 162, 0x1A99 ); + AddItem( 162, 162, 0x1A99 ); + + AddHtmlLocalized( 129, 167, 42, 20, message, 0x0083E0, false, false ); + + break; + } + } + } + } + + private void AddPlus( int x, int y, int value ) + { + switch ( value ) + { + case 1: AddLabel( x, y, 0x35, "+" ); break; + case 2: AddLabel( x, y, 0x21, "+" ); break; + } + } + + private void AddPlusMinus( int x, int y, int value ) + { + switch ( value ) + { + case 0: AddLabel( x, y, 0x21, "-" ); break; + case 1: AddLabel( x, y, 0x35, "-" ); break; + case 3: AddLabel( x, y, 0x35, "+" ); break; + case 4: AddLabel( x, y, 0x21, "+" ); break; + } + } + + private void AddLevel( int x, int y, int value ) + { + AddLabel( x, y, 0x835, value.ToString() ); + } + + private void AddGrowthIndicator( int x, int y ) + { + if ( !m_Plant.IsGrowable ) + return; + + switch ( m_Plant.PlantSystem.GrowthIndicator ) + { + case PlantGrowthIndicator.InvalidLocation : AddLabel( x, y, 0x21, "!" ); break; + case PlantGrowthIndicator.NotHealthy : AddLabel( x, y, 0x21, "-" ); break; + case PlantGrowthIndicator.Delay : AddLabel( x, y, 0x35, "-" ); break; + case PlantGrowthIndicator.Grown : AddLabel( x, y, 0x3, "+" ); break; + case PlantGrowthIndicator.DoubleGrown : AddLabel( x, y, 0x3F, "+" ); break; + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant || !from.InRange( m_Plant.GetWorldLocation(), 3 ) ) + return; + + if ( !m_Plant.IsUsableBy( from ) ) + { + m_Plant.LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch ( info.ButtonID ) + { + case 1: // Reproduction menu + { + if ( m_Plant.PlantStatus > PlantStatus.BowlOfDirt ) + { + from.SendGump( new ReproductionGump( m_Plant ) ); + } + else + { + from.SendLocalizedMessage( 1061885 ); // You need to plant a seed in the bowl first. + + from.SendGump( new MainPlantGump( m_Plant ) ); + } + + break; + } + case 2: // Infestation + { + from.Send( new DisplayHelpTopic( 54, true ) ); // INFESTATION LEVEL + + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 3: // Fungus + { + from.Send( new DisplayHelpTopic( 56, true ) ); // FUNGUS LEVEL + + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 4: // Poison + { + from.Send( new DisplayHelpTopic( 58, true ) ); // POISON LEVEL + + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 5: // Disease + { + from.Send( new DisplayHelpTopic( 60, true ) ); // DISEASE LEVEL + + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 6: // Water + { + from.Target = new PlantPourTarget( m_Plant ); + from.SendLocalizedMessage( 1060808, "#" + m_Plant.GetLocalizedPlantStatus().ToString() ); // Target the container you wish to use to water the ~1_val~. + + break; + } + case 7: // Poison potion + { + AddPotion( from, PotionEffect.PoisonGreater, PotionEffect.PoisonDeadly ); + + break; + } + case 8: // Cure potion + { + AddPotion( from, PotionEffect.CureGreater ); + + break; + } + case 9: // Heal potion + { + AddPotion( from, PotionEffect.HealGreater ); + + break; + } + case 10: // Strength potion + { + AddPotion( from, PotionEffect.StrengthGreater ); + + break; + } + case 11: // Help + { + from.Send( new DisplayHelpTopic( 48, true ) ); // PLANT GROWING + + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 12: // Empty the bowl + { + from.SendGump( new EmptyTheBowlGump( m_Plant ) ); + + break; + } + } + } + + private void AddPotion( Mobile from, params PotionEffect[] effects ) + { + Item item = GetPotion( from, effects ); + + if ( item != null ) + { + m_Plant.Pour( from, item ); + } + else + { + int message; + if ( m_Plant.ApplyPotion( effects[0], true, out message ) ) + { + from.SendLocalizedMessage( 1061884 ); // You don't have any strong potions of that type in your pack. + + from.Target = new PlantPourTarget( m_Plant ); + from.SendLocalizedMessage( 1060808, "#" + m_Plant.GetLocalizedPlantStatus().ToString() ); // Target the container you wish to use to water the ~1_val~. + + return; + } + else + { + m_Plant.LabelTo( from, message ); + } + } + + from.SendGump( new MainPlantGump( m_Plant ) ); + } + + public static Item GetPotion( Mobile from, PotionEffect[] effects ) + { + if ( from.Backpack == null ) + return null; + + Item[] items = from.Backpack.FindItemsByType( new Type[] { typeof( BasePotion ), typeof( PotionKeg ) } ); + + foreach ( Item item in items ) + { + if ( item is BasePotion ) + { + BasePotion potion = (BasePotion)item; + + if ( Array.IndexOf( effects, potion.PotionEffect ) >= 0 ) + return potion; + } + else + { + PotionKeg keg = (PotionKeg)item; + + if ( keg.Held > 0 && Array.IndexOf( effects, keg.Type ) >= 0 ) + return keg; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/MiscItems/FertileDirt.cs b/Scripts/Engines/Plants/MiscItems/FertileDirt.cs new file mode 100644 index 000000000..a484786bb --- /dev/null +++ b/Scripts/Engines/Plants/MiscItems/FertileDirt.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + public class FertileDirt : Item + { + [Constructable] + public FertileDirt() : this( 1 ) + { + } + + [Constructable] + public FertileDirt( int amount ) : base( 0xF81 ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + public FertileDirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/MiscItems/GreenThorns.cs b/Scripts/Engines/Plants/MiscItems/GreenThorns.cs new file mode 100644 index 000000000..151eafd11 --- /dev/null +++ b/Scripts/Engines/Plants/MiscItems/GreenThorns.cs @@ -0,0 +1,727 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Network; + +namespace Server.Items +{ + public class GreenThorns : Item + { + public override int LabelNumber { get { return 1060837; } } // green thorns + + [Constructable] + public GreenThorns() : this( 1 ) + { + } + + [Constructable] + public GreenThorns( int amount ) : base( 0xF42 ) + { + Stackable = true; + Weight = 1.0; + Hue = 0x42; + Amount = amount; + } + + public GreenThorns( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + if ( !from.CanBeginAction( typeof( GreenThorns ) ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061908 ); // * You must wait a while before planting another thorn. * + return; + } + + from.Target = new InternalTarget( this ); + from.SendLocalizedMessage( 1061906 ); // Choose a spot to plant the thorn. + } + + private class InternalTarget : Target + { + private GreenThorns m_Thorn; + + public InternalTarget( GreenThorns thorn ) : base( 3, true, TargetFlags.None ) + { + m_Thorn = thorn; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Thorn.Deleted ) + return; + + if ( !m_Thorn.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + if ( !from.CanBeginAction( typeof( GreenThorns ) ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061908 ); // * You must wait a while before planting another thorn. * + return; + } + + if ( from.Map != Map.Trammel && from.Map != Map.Felucca ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x2B2, true, "No solen lairs exist on this facet. Try again in Trammel or Felucca." ); + return; + } + + LandTarget land = targeted as LandTarget; + + if ( land == null ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061912 ); // * You cannot plant a green thorn there! * + } + else + { + GreenThornsEffect effect = GreenThornsEffect.Create( from, land ); + + if ( effect == null ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061913 ); // * You sense it would be useless to plant a green thorn there. * + } + else + { + m_Thorn.Consume(); + + from.LocalOverheadMessage( MessageType.Emote, 0x961, 1061914 ); // * You push the strange green thorn into the ground * + from.NonlocalOverheadMessage( MessageType.Emote, 0x961, 1061915, from.Name ); // * ~1_PLAYER_NAME~ pushes a strange green thorn into the ground. * + + from.BeginAction( typeof( GreenThorns ) ); + new GreenThorns.EndActionTimer( from ).Start(); + + effect.Start(); + } + } + } + + protected override void OnTargetOutOfRange( Mobile from, object targeted ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502825 ); // That location is too far away + } + } + + private class EndActionTimer : Timer + { + private Mobile m_From; + + public EndActionTimer( Mobile from ) : base( TimeSpan.FromMinutes( 3.0 ) ) + { + m_From = from; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_From.EndAction( typeof( GreenThorns ) ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public abstract class GreenThornsEffect : Timer + { + private class TilesAndEffect + { + private int[] m_Tiles; + private Type m_Effect; + + public int[] Tiles { get { return m_Tiles; } } + public Type Effect { get { return m_Effect; } } + + public TilesAndEffect( int[] tiles, Type effect ) + { + m_Tiles = tiles; + m_Effect = effect; + } + } + + private static TilesAndEffect[] m_Table = new TilesAndEffect[] + { + new TilesAndEffect( new int[] + { + 0x71, 0x7C, + 0x82, 0xA7, + 0xDC, 0xE3, + 0xE8, 0xEB, + 0x141, 0x144, + 0x14C, 0x14F, + 0x169, 0x174, + 0x1DC, 0x1E7, + 0x1EC, 0x1EF, + 0x272, 0x275, + 0x27E, 0x281, + 0x2D0, 0x2D7, + 0x2E5, 0x2FF, + 0x303, 0x31F, + 0x32C, 0x32F, + 0x33D, 0x340, + 0x345, 0x34C, + 0x355, 0x358, + 0x367, 0x36E, + 0x377, 0x37A, + 0x38D, 0x390, + 0x395, 0x39C, + 0x3A5, 0x3A8, + 0x3F6, 0x405, + 0x547, 0x54E, + 0x553, 0x556, + 0x597, 0x59E, + 0x623, 0x63A, + 0x6F3, 0x6FA, + 0x777, 0x791, + 0x79A, 0x7A9, + 0x7AE, 0x7B1, + }, + typeof( DirtGreenThornsEffect ) ), + + new TilesAndEffect( new int[] + { + 0x9, 0x15, + 0x150, 0x15C + }, + typeof( FurrowsGreenThornsEffect ) ), + + new TilesAndEffect( new int[] + { + 0x9C4, 0x9EB, + 0x3D65, 0x3D65, + 0x3DC0, 0x3DD9, + 0x3DDB, 0x3DDC, + 0x3DDE, 0x3EF0, + 0x3FF6, 0x3FF6, + 0x3FFC, 0x3FFE, + }, + typeof( SwampGreenThornsEffect ) ), + + new TilesAndEffect( new int[] + { + 0x10C, 0x10F, + 0x114, 0x117, + 0x119, 0x11D, + 0x179, 0x18A, + 0x385, 0x38C, + 0x391, 0x394, + 0x39D, 0x3A4, + 0x3A9, 0x3AC, + 0x5BF, 0x5D6, + 0x5DF, 0x5E2, + 0x745, 0x748, + 0x751, 0x758, + 0x75D, 0x760, + 0x76D, 0x773 + }, + typeof( SnowGreenThornsEffect ) ), + + new TilesAndEffect( new int[] + { + 0x16, 0x3A, + 0x44, 0x4B, + 0x11E, 0x121, + 0x126, 0x12D, + 0x192, 0x192, + 0x1A8, 0x1AB, + 0x1B9, 0x1D1, + 0x282, 0x285, + 0x28A, 0x291, + 0x335, 0x33C, + 0x341, 0x344, + 0x34D, 0x354, + 0x359, 0x35C, + 0x3B7, 0x3BE, + 0x3C7, 0x3CA, + 0x5A7, 0x5B2, + 0x64B, 0x652, + 0x657, 0x65A, + 0x663, 0x66A, + 0x66F, 0x672, + 0x7BD, 0x7D0 + }, + typeof( SandGreenThornsEffect ) ) + }; + + public static GreenThornsEffect Create( Mobile from, LandTarget land ) + { + if ( !from.Map.CanSpawnMobile( land.Location ) ) + return null; + + int tileID = land.TileID & 0x3FFF; + + foreach ( TilesAndEffect taep in m_Table ) + { + bool contains = false; + + for ( int i = 0; !contains && i < taep.Tiles.Length; i += 2 ) + contains = ( tileID >= taep.Tiles[i] && tileID <= taep.Tiles[i + 1] ); + + if ( contains ) + { + GreenThornsEffect effect = (GreenThornsEffect)Activator.CreateInstance( taep.Effect, new object[] { land.Location, from.Map, from } ); + return effect; + } + } + + return null; + } + + private Point3D m_Location; + private Map m_Map; + private Mobile m_From; + + public Point3D Location { get { return m_Location; } } + public Map Map { get { return m_Map; } } + public Mobile From { get { return m_From; } } + + public GreenThornsEffect( Point3D location, Map map, Mobile from ) : base( TimeSpan.FromSeconds( 2.5 ) ) + { + m_Location = location; + m_Map = map; + m_From = from; + + Priority = TimerPriority.TwoFiftyMS; + } + + private int m_Step; + + protected override void OnTick() + { + TimeSpan nextDelay = Play( m_Step++ ); + + if ( nextDelay > TimeSpan.Zero ) + { + Delay = nextDelay; + + Start(); + } + } + + protected abstract TimeSpan Play( int step ); + + protected bool SpawnItem( Item item ) + { + for ( int i = 0; i < 5; i++ ) // Try 5 times + { + int x = Location.X + Utility.RandomMinMax( -1, 1 ); + int y = Location.Y + Utility.RandomMinMax( -1, 1 ); + int z = Map.GetAverageZ( x, y ); + + if ( Map.CanFit( x, y, Location.Z, 1 ) ) + { + item.MoveToWorld( new Point3D( x, y, Location.Z ), Map ); + return true; + } + else if ( Map.CanFit( x, y, z, 1 ) ) + { + item.MoveToWorld( new Point3D( x, y, z ), Map ); + return true; + } + } + + return false; + } + + protected bool SpawnCreature( BaseCreature creature ) + { + for ( int i = 0; i < 5; i++ ) // Try 5 times + { + int x = Location.X + Utility.RandomMinMax( -1, 1 ); + int y = Location.Y + Utility.RandomMinMax( -1, 1 ); + int z = Map.GetAverageZ( x, y ); + + if ( Map.CanSpawnMobile( x, y, Location.Z ) ) + { + creature.MoveToWorld( new Point3D( x, y, Location.Z ), Map ); + creature.Combatant = From; + return true; + } + else if ( Map.CanSpawnMobile( x, y, z ) ) + { + creature.MoveToWorld( new Point3D( x, y, z ), Map ); + creature.Combatant = From; + return true; + } + } + + return false; + } + } + + public class DirtGreenThornsEffect : GreenThornsEffect + { + public DirtGreenThornsEffect( Point3D location, Map map, Mobile from ) : base( location, map, from ) + { + } + + protected override TimeSpan Play( int step ) + { + switch ( step ) + { + case 0: + { + Effects.PlaySound( Location, Map, 0x106 ); + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 1: + { + Effects.PlaySound( Location, Map, 0x222 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 2: + { + Effects.PlaySound( Location, Map, 0x21F ); + + return TimeSpan.FromSeconds( 5.0 ); + } + case 3: + { + EffectItem dummy = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 20.0 ) ); + dummy.PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The ground erupts with chaotic growth! *" ); + + Effects.PlaySound( Location, Map, 0x12D ); + + SpawnReagents(); + SpawnReagents(); + + return TimeSpan.FromSeconds( 2.0 ); + } + case 4: + { + Effects.PlaySound( Location, Map, 0x12D ); + + SpawnReagents(); + SpawnReagents(); + + return TimeSpan.FromSeconds( 2.0 ); + } + case 5: + { + Effects.PlaySound( Location, Map, 0x12D ); + + SpawnReagents(); + SpawnReagents(); + + return TimeSpan.FromSeconds( 3.0 ); + } + default: + { + Effects.PlaySound( Location, Map, 0x12D ); + + SpawnReagents(); + SpawnReagents(); + + return TimeSpan.Zero; + } + } + } + + private void SpawnReagents() + { + Item reagents; + int amount = Utility.RandomMinMax( 10, 25 ); + + switch ( Utility.Random( 9 ) ) + { + case 0: reagents = new BlackPearl( amount ); break; + case 1: reagents = new Bloodmoss( amount ); break; + case 2: reagents = new Garlic( amount ); break; + case 3: reagents = new Ginseng( amount ); break; + case 4: reagents = new MandrakeRoot( amount ); break; + case 5: reagents = new Nightshade( amount ); break; + case 6: reagents = new SulfurousAsh( amount ); break; + case 7: reagents = new SpidersSilk( amount ); break; + default: reagents = new FertileDirt( amount ); break; + } + + if ( !SpawnItem( reagents ) ) + reagents.Delete(); + } + } + + public class FurrowsGreenThornsEffect : GreenThornsEffect + { + public FurrowsGreenThornsEffect( Point3D location, Map map, Mobile from ) : base( location, map, from ) + { + } + + protected override TimeSpan Play( int step ) + { + switch ( step ) + { + case 0: + { + Effects.PlaySound( Location, Map, 0x106 ); + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 1: + { + EffectItem hole = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 10.0 ) ); + hole.ItemID = 0x913; + + Effects.PlaySound( Location, Map, 0x222 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 2: + { + Effects.PlaySound( Location, Map, 0x21F ); + + return TimeSpan.FromSeconds( 4.0 ); + } + default: + { + EffectItem dummy = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 20.0 ) ); + dummy.PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* A magical bunny leaps out of its hole, disturbed by the thorn's effect! *" ); + + BaseCreature spawn = new VorpalBunny(); + if ( !SpawnCreature( spawn ) ) + spawn.Delete(); + + return TimeSpan.Zero; + } + } + } + } + + public class SwampGreenThornsEffect : GreenThornsEffect + { + public SwampGreenThornsEffect( Point3D location, Map map, Mobile from ) : base( location, map, from ) + { + } + + protected override TimeSpan Play( int step ) + { + switch ( step ) + { + case 0: + { + Effects.PlaySound( Location, Map, 0x106 ); + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 1: + { + Effects.PlaySound( Location, Map, 0x222 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 2: + { + Effects.PlaySound( Location, Map, 0x21F ); + + return TimeSpan.FromSeconds( 1.0 ); + } + default: + { + EffectItem dummy = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 20.0 ) ); + dummy.PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* Strange green tendrils rise from the ground, whipping wildly! *" ); + Effects.PlaySound( Location, Map, 0x2B0 ); + + BaseCreature spawn = new WhippingVine(); + if ( !SpawnCreature( spawn ) ) + spawn.Delete(); + + return TimeSpan.Zero; + } + } + } + } + + public class SnowGreenThornsEffect : GreenThornsEffect + { + public SnowGreenThornsEffect( Point3D location, Map map, Mobile from ) : base( location, map, from ) + { + } + + protected override TimeSpan Play( int step ) + { + switch ( step ) + { + case 0: + { + Effects.PlaySound( Location, Map, 0x106 ); + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 1: + { + Effects.PlaySound( Location, Map, 0x222 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 2: + { + Effects.PlaySound( Location, Map, 0x21F ); + + return TimeSpan.FromSeconds( 4.0 ); + } + default: + { + EffectItem dummy = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 20.0 ) ); + dummy.PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* Slithering ice serpents rise to the surface to investigate the disturbance! *" ); + + BaseCreature spawn = new GiantIceWorm(); + if ( !SpawnCreature( spawn ) ) + spawn.Delete(); + + for ( int i = 0; i < 3; i++ ) + { + BaseCreature snake = new IceSnake(); + if ( !SpawnCreature( snake ) ) + snake.Delete(); + } + + return TimeSpan.Zero; + } + } + } + } + + public class SandGreenThornsEffect : GreenThornsEffect + { + public SandGreenThornsEffect( Point3D location, Map map, Mobile from ) : base( location, map, from ) + { + } + + protected override TimeSpan Play( int step ) + { + switch ( step ) + { + case 0: + { + Effects.PlaySound( Location, Map, 0x106 ); + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3735, 1, 182, 0xBE3 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 1: + { + Effects.PlaySound( Location, Map, 0x222 ); + + return TimeSpan.FromSeconds( 4.0 ); + } + case 2: + { + Effects.PlaySound( Location, Map, 0x21F ); + + return TimeSpan.FromSeconds( 5.0 ); + } + default: + { + EffectItem dummy = EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 20.0 ) ); + dummy.PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The sand collapses, revealing a dark hole. *" ); + + GreenThornsSHTeleporter.Create( Location, Map ); + + return TimeSpan.Zero; + } + } + } + } + + public class GreenThornsSHTeleporter : Item + { + public static readonly Point3D Destination = new Point3D( 5738, 1856, 0 ); + + public static void Create( Point3D location, Map map ) + { + GreenThornsSHTeleporter tele = new GreenThornsSHTeleporter(); + + tele.MoveToWorld( location, map ); + + new InternalTimer( tele ).Start(); + } + + public override string DefaultName + { + get { return "a hole"; } + } + + private GreenThornsSHTeleporter() : base( 0x913 ) + { + Movable = false; + Hue = 0x1; + } + + public GreenThornsSHTeleporter( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this, 3 ) ) + { + BaseCreature.TeleportPets( from, Destination, Map ); + + from.Location = Destination; + } + else + { + from.SendLocalizedMessage( 1019045 ); // I can't reach that. + } + } + + private class InternalTimer : Timer + { + private GreenThornsSHTeleporter m_Teleporter; + + public InternalTimer( GreenThornsSHTeleporter teleporter ) : base( TimeSpan.FromMinutes( 1.0 ) ) + { + m_Teleporter = teleporter; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Teleporter.Delete(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/MiscItems/OrangePetals.cs b/Scripts/Engines/Plants/MiscItems/OrangePetals.cs new file mode 100644 index 000000000..54d75bef2 --- /dev/null +++ b/Scripts/Engines/Plants/MiscItems/OrangePetals.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Network; + +namespace Server.Items +{ + public class OrangePetals : Item + { + public override int LabelNumber { get { return 1053122; } } // orange petals + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public OrangePetals() : this( 1 ) + { + } + + [Constructable] + public OrangePetals( int amount ) : base( 0x1021 ) + { + Stackable = true; + Hue = 0x2B; + Amount = amount; + } + + public OrangePetals( Serial serial ) : base( serial ) + { + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + if ( item != this ) + return base.CheckItemUse( from, item ); + + if ( from != this.RootParent ) + { + from.SendLocalizedMessage( 1042038 ); // You must have the object in your backpack to use it. + return false; + } + + return base.CheckItemUse( from, item ); + } + + public override void OnDoubleClick( Mobile from ) + { + OrangePetalsContext context = GetContext( from ); + + if ( context != null ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061904 ); + return; + } + + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061905 ); + from.PlaySound( 0x3B ); + + Timer timer = new OrangePetalsTimer( from ); + timer.Start(); + + AddContext( from, new OrangePetalsContext( timer ) ); + + this.Consume(); + } + + private static Hashtable m_Table = new Hashtable(); + + private static void AddContext( Mobile m, OrangePetalsContext context ) + { + m_Table[m] = context; + } + + public static void RemoveContext( Mobile m ) + { + OrangePetalsContext context = GetContext( m ); + + if ( context != null ) + RemoveContext( m, context ); + } + + private static void RemoveContext( Mobile m, OrangePetalsContext context ) + { + m_Table.Remove( m ); + + context.Timer.Stop(); + } + + private static OrangePetalsContext GetContext( Mobile m ) + { + return ( m_Table[m] as OrangePetalsContext ); + } + + public static bool UnderEffect( Mobile m ) + { + return ( GetContext( m ) != null ); + } + + private class OrangePetalsTimer : Timer + { + private Mobile m_Mobile; + + public OrangePetalsTimer( Mobile from ) : base ( TimeSpan.FromMinutes( 5.0 ) ) + { + m_Mobile = from; + } + + protected override void OnTick() + { + if ( !m_Mobile.Deleted ) + { + m_Mobile.LocalOverheadMessage( MessageType.Regular, 0x3F, true, + "* You feel the effects of your poison resistance wearing off *" ); + } + + RemoveContext( m_Mobile ); + } + } + + private class OrangePetalsContext + { + private Timer m_Timer; + + public Timer Timer{ get{ return m_Timer; } } + + public OrangePetalsContext( Timer timer ) + { + m_Timer = timer; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/MiscItems/RedLeaves.cs b/Scripts/Engines/Plants/MiscItems/RedLeaves.cs new file mode 100644 index 000000000..1b851ee58 --- /dev/null +++ b/Scripts/Engines/Plants/MiscItems/RedLeaves.cs @@ -0,0 +1,108 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Items +{ + public class RedLeaves : Item + { + public override int LabelNumber { get { return 1053123; } } // red leaves + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public RedLeaves() : this( 1 ) + { + } + + [Constructable] + public RedLeaves( int amount ) : base( 0x1E85 ) + { + Stackable = true; + Hue = 0x21; + Amount = amount; + } + + public RedLeaves( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + from.Target = new InternalTarget( this ); + from.SendLocalizedMessage( 1061907 ); // Choose a book you wish to seal with the wax from the red leaf. + } + + private class InternalTarget : Target + { + private RedLeaves m_RedLeaves; + + public InternalTarget( RedLeaves redLeaves ) : base( 3, false, TargetFlags.None ) + { + m_RedLeaves = redLeaves; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_RedLeaves.Deleted ) + return; + + if ( !m_RedLeaves.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + Item item = targeted as Item; + + if ( item == null || !item.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else if ( !(item is BaseBook) ) + { + item.LabelTo( from, 1061911 ); // You can only use red leaves to seal the ink into book pages! + } + else + { + BaseBook book = (BaseBook)item; + + if ( !book.Writable ) + { + book.LabelTo( from, 1061909 ); // The ink in this book has already been sealed. + } + else + { + m_RedLeaves.Consume(); + book.Writable = false; + + book.LabelTo( from, 1061910 ); // You seal the ink to the page using wax from the red leaf. + } + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/MiscMobiles/GiantIceWorm.cs b/Scripts/Engines/Plants/MiscMobiles/GiantIceWorm.cs new file mode 100644 index 000000000..160c2c4ef --- /dev/null +++ b/Scripts/Engines/Plants/MiscMobiles/GiantIceWorm.cs @@ -0,0 +1,74 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + [CorpseName( "a giant ice worm corpse" )] + public class GiantIceWorm : BaseCreature + { + public override bool SubdueBeforeTame { get { return true; } } + + [Constructable] + public GiantIceWorm() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 89; + Name = "a giant ice worm"; + BaseSoundID = 0xDC; + + SetStr( 216, 245 ); + SetDex( 76, 100 ); + SetInt( 66, 85 ); + + SetHits( 130, 147 ); + + SetDamage( 7, 17 ); + + SetDamageType( ResistanceType.Physical, 10 ); + SetDamageType( ResistanceType.Cold, 90 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 0 ); + SetResistance( ResistanceType.Cold, 80, 90 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Poisoning, 75.1, 95.0 ); + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 75.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 40; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 71.1; + } + + public override Poison PoisonImmune { get { return Poison.Greater; } } + + public override Poison HitPoison { get { return Poison.Greater; } } + + public override FoodType FavoriteFood { get { return FoodType.Meat; } } + + public GiantIceWorm( Serial serial ) : base ( serial ) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/Network/DisplayHelpTopic.cs b/Scripts/Engines/Plants/Network/DisplayHelpTopic.cs new file mode 100644 index 000000000..0311feb43 --- /dev/null +++ b/Scripts/Engines/Plants/Network/DisplayHelpTopic.cs @@ -0,0 +1,18 @@ +using System; +using Server; + +namespace Server.Network +{ + public class DisplayHelpTopic : Packet + { + public DisplayHelpTopic( int topicID, bool display ) : base( 0xBF ) + { + EnsureCapacity( 11 ); + + m_Stream.Write( (short) 0x17 ); + m_Stream.Write( (byte) 1 ); + m_Stream.Write( (int) topicID ); + m_Stream.Write( (bool) display ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantBowl.cs b/Scripts/Engines/Plants/PlantBowl.cs new file mode 100644 index 000000000..28672f707 --- /dev/null +++ b/Scripts/Engines/Plants/PlantBowl.cs @@ -0,0 +1,191 @@ +using System; +using Server; +using Server.Items; +using Server.Targeting; +using Server.Network; + +namespace Server.Engines.Plants +{ + public class PlantBowl : Item + { + public override int LabelNumber { get { return 1060834; } } // a plant bowl + + [Constructable] + public PlantBowl() : base( 0x15FD ) + { + Weight = 1.0; + } + + public PlantBowl( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + from.Target = new InternalTarget( this ); + from.SendLocalizedMessage( 1061897 ); // Choose a patch of dirt to scoop up. + } + + private class InternalTarget : Target + { + private PlantBowl m_PlantBowl; + + public InternalTarget( PlantBowl plantBowl ) : base( 3, true, TargetFlags.None ) + { + m_PlantBowl = plantBowl; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_PlantBowl.Deleted ) + return; + + if ( !m_PlantBowl.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + if ( targeted is FertileDirt ) + { + FertileDirt dirt = (FertileDirt)targeted; + + if ( !dirt.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else if ( dirt.Amount < 40 ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061896 ); // You need more dirt to fill a plant bowl! + } + else + { + PlantItem fullBowl = new PlantItem( true ); + + if ( from.PlaceInBackpack( fullBowl ) ) + { + dirt.Consume( 40 ); + m_PlantBowl.Delete(); + + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061895 ); // You fill the bowl with fresh dirt. + } + else + { + fullBowl.Delete(); + + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061894 ); // There is no room in your backpack for a bowl full of dirt! + } + } + } + else if ( PlantBowl.IsDirtPatch( targeted ) ) + { + PlantItem fullBowl = new PlantItem( false ); + + if ( from.PlaceInBackpack( fullBowl ) ) + { + m_PlantBowl.Delete(); + + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061895 ); // You fill the bowl with fresh dirt. + } + else + { + fullBowl.Delete(); + + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061894 ); // There is no room in your backpack for a bowl full of dirt! + } + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061893 ); // You'll want to gather fresh dirt in order to raise a healthy plant! + } + } + + protected override void OnTargetOutOfRange( Mobile from, object targeted ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502825 ); // That location is too far away + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public static bool IsDirtPatch( object obj ) + { + int tileID; + + if ( obj is Static && !((Static)obj).Movable ) + tileID = (((Static)obj).ItemID & 0x3FFF) | 0x4000; + else if ( obj is StaticTarget ) + tileID = (((StaticTarget)obj).ItemID & 0x3FFF) | 0x4000; + else if ( obj is LandTarget ) + tileID = ((LandTarget)obj).TileID & 0x3FFF; + else + return false; + + bool contains = false; + + for ( int i = 0; !contains && i < m_DirtPatchTiles.Length; i += 2 ) + contains = ( tileID >= m_DirtPatchTiles[i] && tileID <= m_DirtPatchTiles[i + 1] ); + + return contains; + } + + private static int[] m_DirtPatchTiles = new int[] + { + 0x9, 0x15, + 0x71, 0x7C, + 0x82, 0xA7, + 0xDC, 0xE3, + 0xE8, 0xEB, + 0x141, 0x144, + 0x14C, 0x15C, + 0x169, 0x174, + 0x1DC, 0x1EF, + 0x272, 0x275, + 0x27E, 0x281, + 0x2D0, 0x2D7, + 0x2E5, 0x2FF, + 0x303, 0x31F, + 0x32C, 0x32F, + 0x33D, 0x340, + 0x345, 0x34C, + 0x355, 0x358, + 0x367, 0x36E, + 0x377, 0x37A, + 0x38D, 0x390, + 0x395, 0x39C, + 0x3A5, 0x3A8, + 0x3F6, 0x405, + 0x547, 0x54E, + 0x553, 0x556, + 0x597, 0x59E, + 0x623, 0x63A, + 0x6F3, 0x6FA, + 0x777, 0x791, + 0x79A, 0x7A9, + 0x7AE, 0x7B1, + 0x98C, 0x99F, + 0x9AC, 0x9BF, + 0x5B27, 0x5B3E, + 0x71F4, 0x71FB, + 0x72C9, 0x72CA, + }; + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantHue.cs b/Scripts/Engines/Plants/PlantHue.cs new file mode 100644 index 000000000..8e90745b9 --- /dev/null +++ b/Scripts/Engines/Plants/PlantHue.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.Plants +{ + [Flags] + public enum PlantHue + { + Plain = 0x1 | Crossable, + + Red = 0x2 | Crossable, + Blue = 0x4 | Crossable, + Yellow = 0x8 | Crossable, + + BrightRed = Red | Bright, + BrightBlue = Blue | Bright, + BrightYellow = Yellow | Bright, + + Purple = Red | Blue, + Green = Blue | Yellow, + Orange = Red | Yellow, + + BrightPurple = Purple | Bright, + BrightGreen = Green | Bright, + BrightOrange = Orange | Bright, + + Black = 0x10, + White = 0x20, + Pink = 0x40, + Magenta = 0x80, + Aqua = 0x100, + FireRed = 0x200, + + None = 0, + Crossable = 0x4000000, + Bright = 0x8000000 + } + + public class PlantHueInfo + { + private static Hashtable m_Table; + + static PlantHueInfo() + { + m_Table = new Hashtable(); + + m_Table[PlantHue.Plain] = new PlantHueInfo( 0, 1060813, PlantHue.Plain, 0x835 ); + m_Table[PlantHue.Red] = new PlantHueInfo( 0x66D, 1060814, PlantHue.Red, 0x24 ); + m_Table[PlantHue.Blue] = new PlantHueInfo( 0x53D, 1060815, PlantHue.Blue, 0x6 ); + m_Table[PlantHue.Yellow] = new PlantHueInfo( 0x8A5, 1060818, PlantHue.Yellow, 0x38 ); + m_Table[PlantHue.BrightRed] = new PlantHueInfo( 0x21, 1060814, PlantHue.BrightRed, 0x21 ); + m_Table[PlantHue.BrightBlue] = new PlantHueInfo( 0x5, 1060815, PlantHue.BrightBlue, 0x6 ); + m_Table[PlantHue.BrightYellow] = new PlantHueInfo( 0x38, 1060818, PlantHue.BrightYellow, 0x35 ); + m_Table[PlantHue.Purple] = new PlantHueInfo( 0xD, 1060816, PlantHue.Purple, 0x10 ); + m_Table[PlantHue.Green] = new PlantHueInfo( 0x59B, 1060819, PlantHue.Green, 0x42 ); + m_Table[PlantHue.Orange] = new PlantHueInfo( 0x46F, 1060817, PlantHue.Orange, 0x2E ); + m_Table[PlantHue.BrightPurple] = new PlantHueInfo( 0x10, 1060816, PlantHue.BrightPurple, 0xD ); + m_Table[PlantHue.BrightGreen] = new PlantHueInfo( 0x42, 1060819, PlantHue.BrightGreen, 0x3F ); + m_Table[PlantHue.BrightOrange] = new PlantHueInfo( 0x2B, 1060817, PlantHue.BrightOrange, 0x2B ); + m_Table[PlantHue.Black] = new PlantHueInfo( 0x455, 1060820, PlantHue.Black, 0 ); + m_Table[PlantHue.White] = new PlantHueInfo( 0x481, 1060821, PlantHue.White, 0x481 ); + m_Table[PlantHue.Pink] = new PlantHueInfo( 0x48E, 1061854, PlantHue.Pink ); + m_Table[PlantHue.Magenta] = new PlantHueInfo( 0x486, 1061852, PlantHue.Magenta ); + m_Table[PlantHue.Aqua] = new PlantHueInfo( 0x495, 1061853, PlantHue.Aqua ); + m_Table[PlantHue.FireRed] = new PlantHueInfo( 0x489, 1061855, PlantHue.FireRed ); + } + + public static PlantHueInfo GetInfo( PlantHue plantHue ) + { + PlantHueInfo info = m_Table[plantHue] as PlantHueInfo; + + if ( info != null ) + return info; + else + return (PlantHueInfo)m_Table[PlantHue.Plain]; + } + + public static PlantHue RandomFirstGeneration() + { + switch ( Utility.Random( 4 ) ) + { + case 0: return PlantHue.Plain; + case 1: return PlantHue.Red; + case 2: return PlantHue.Blue; + default: return PlantHue.Yellow; + } + } + + public static bool IsCrossable( PlantHue plantHue ) + { + return (plantHue & PlantHue.Crossable) != PlantHue.None; + } + + public static bool IsBright( PlantHue plantHue ) + { + return (plantHue & PlantHue.Bright) != PlantHue.None; + } + + public static PlantHue GetNotBright( PlantHue plantHue ) + { + return plantHue & ~PlantHue.Bright; + } + + public static bool IsPrimary( PlantHue plantHue ) + { + return plantHue == PlantHue.Red || plantHue == PlantHue.Blue || plantHue == PlantHue.Yellow; + } + + public static PlantHue Cross( PlantHue first, PlantHue second ) + { + if ( !IsCrossable( first ) || !IsCrossable( second ) ) + return PlantHue.None; + + if ( Utility.RandomDouble() < 0.01 ) + return Utility.RandomBool() ? PlantHue.Black : PlantHue.White; + + if ( first == PlantHue.Plain || second == PlantHue.Plain ) + return PlantHue.Plain; + + PlantHue notBrightFirst = GetNotBright( first ); + PlantHue notBrightSecond = GetNotBright( second ); + + if ( notBrightFirst == notBrightSecond ) + return first | PlantHue.Bright; + + bool firstPrimary = IsPrimary( notBrightFirst ); + bool secondPrimary = IsPrimary( notBrightSecond ); + + if ( firstPrimary && secondPrimary ) + return notBrightFirst | notBrightSecond; + + if ( firstPrimary && !secondPrimary ) + return notBrightFirst; + + if ( !firstPrimary && secondPrimary ) + return notBrightSecond; + + return notBrightFirst & notBrightSecond; + } + + private int m_Hue; + private int m_Name; + private PlantHue m_PlantHue; + private int m_GumpHue; + + public int Hue { get { return m_Hue; } } + public int Name { get { return m_Name; } } + public PlantHue PlantHue { get { return m_PlantHue; } } + public int GumpHue { get { return m_GumpHue; } } + + private PlantHueInfo( int hue, int name, PlantHue plantHue ) : this( hue, name, plantHue, hue ) + { + } + + private PlantHueInfo( int hue, int name, PlantHue plantHue, int gumpHue ) + { + m_Hue = hue; + m_Name = name; + m_PlantHue = plantHue; + m_GumpHue = gumpHue; + } + + public bool IsCrossable() + { + return IsCrossable( m_PlantHue ); + } + + public bool IsBright() + { + return IsBright( m_PlantHue ); + } + + public PlantHue GetNotBright() + { + return GetNotBright( m_PlantHue ); + } + + public bool IsPrimary() + { + return IsPrimary( m_PlantHue ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantItem.cs b/Scripts/Engines/Plants/PlantItem.cs new file mode 100644 index 000000000..fceb90011 --- /dev/null +++ b/Scripts/Engines/Plants/PlantItem.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Multis; +using Server.ContextMenus; + +namespace Server.Engines.Plants +{ + public enum PlantStatus + { + BowlOfDirt = 0, + Seed = 1, + Sapling = 2, + Plant = 4, + FullGrownPlant = 7, + DecorativePlant = 10, + DeadTwigs = 11, + + Stage1 = 1, + Stage2 = 2, + Stage3 = 3, + Stage4 = 4, + Stage5 = 5, + Stage6 = 6, + Stage7 = 7, + Stage8 = 8, + Stage9 = 9 + } + + public class PlantItem : Item, ISecurable + { + private PlantSystem m_PlantSystem; + + private PlantStatus m_PlantStatus; + private PlantType m_PlantType; + private PlantHue m_PlantHue; + private bool m_ShowType; + + private SecureLevel m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + public PlantSystem PlantSystem { get { return m_PlantSystem; } } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override int LabelNumber + { + get + { + if ( m_PlantStatus >= PlantStatus.DeadTwigs ) + return base.LabelNumber; + else if ( m_PlantStatus >= PlantStatus.DecorativePlant ) + return 1061924; // a decorative plant + else if ( m_PlantStatus >= PlantStatus.FullGrownPlant ) + return PlantTypeInfo.GetInfo( m_PlantType ).Name; + else + return 1029913; // plant bowl + } + } + + + [CommandProperty( AccessLevel.GameMaster )] + public PlantStatus PlantStatus + { + get { return m_PlantStatus; } + set + { + if ( m_PlantStatus == value || value < PlantStatus.BowlOfDirt || value > PlantStatus.DeadTwigs ) + return; + + double ratio; + if ( m_PlantSystem != null ) + ratio = (double) m_PlantSystem.Hits / m_PlantSystem.MaxHits; + else + ratio = 1.0; + + m_PlantStatus = value; + + if ( m_PlantStatus >= PlantStatus.DecorativePlant ) + { + m_PlantSystem = null; + } + else + { + if ( m_PlantSystem == null ) + m_PlantSystem = new PlantSystem( this, false ); + + int hits = (int)( m_PlantSystem.MaxHits * ratio ); + + if ( hits == 0 && m_PlantStatus > PlantStatus.BowlOfDirt ) + m_PlantSystem.Hits = hits + 1; + else + m_PlantSystem.Hits = hits; + } + + Update(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public PlantType PlantType + { + get { return m_PlantType; } + set + { + m_PlantType = value; + Update(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public PlantHue PlantHue + { + get { return m_PlantHue; } + set + { + m_PlantHue = value; + Update(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ShowType + { + get { return m_ShowType; } + set + { + m_ShowType = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ValidGrowthLocation + { + get + { + if ( IsLockedDown && RootParent == null ) + return true; + + + Mobile owner = RootParent as Mobile; + if ( owner == null ) + return false; + + if ( owner.Backpack != null && IsChildOf( owner.Backpack ) ) + return true; + if ( owner.BankBox != null && IsChildOf( owner.BankBox ) ) + return true; + + return false; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsGrowable + { + get { return m_PlantStatus >= PlantStatus.BowlOfDirt && m_PlantStatus <= PlantStatus.Stage9; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsCrossable + { + get { return PlantHueInfo.IsCrossable( this.PlantHue ) && PlantTypeInfo.IsCrossable( this.PlantType ); } + } + + private static ArrayList m_Instances = new ArrayList(); + + public static ArrayList Plants{ get{ return m_Instances; } } + + [Constructable] + public PlantItem() : this( false ) + { + } + + [Constructable] + public PlantItem( bool fertileDirt ) : base( 0x1602 ) + { + Weight = 1.0; + + m_PlantStatus = PlantStatus.BowlOfDirt; + m_PlantSystem = new PlantSystem( this, fertileDirt ); + m_Level = SecureLevel.CoOwners; + + m_Instances.Add( this ); + } + + public PlantItem( Serial serial ) : base( serial ) + { + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public int GetLocalizedPlantStatus() + { + if ( m_PlantStatus >= PlantStatus.Plant ) + return 1060812; // plant + else if ( m_PlantStatus >= PlantStatus.Sapling ) + return 1023305; // sapling + else + return 1060810; // seed + } + + private void Update() + { + if ( m_PlantStatus >= PlantStatus.DeadTwigs ) + { + ItemID = 0x1B9D; + Hue = PlantHueInfo.GetInfo( m_PlantHue ).Hue; + } + else if ( m_PlantStatus >= PlantStatus.FullGrownPlant ) + { + ItemID = PlantTypeInfo.GetInfo( m_PlantType ).ItemID; + Hue = PlantHueInfo.GetInfo( m_PlantHue ).Hue; + } + else if ( m_PlantStatus >= PlantStatus.Plant ) + { + ItemID = 0x1600; + Hue = 0; + } + else + { + ItemID = 0x1602; + Hue = 0; + } + + InvalidateProperties(); + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( m_PlantStatus >= PlantStatus.DeadTwigs ) + { + base.AddNameProperty( list ); + } + else if ( m_PlantStatus >= PlantStatus.FullGrownPlant ) + { + PlantHueInfo hueInfo = PlantHueInfo.GetInfo( m_PlantHue ); + PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo( m_PlantType ); + + int title = PlantTypeInfo.GetBonsaiTitle( m_PlantType ); + if ( title == 0 ) // Not a bonsai + title = hueInfo.Name; + + if ( m_PlantStatus < PlantStatus.DecorativePlant ) + { + string args = string.Format( "#{0}\t#{1}\t#{2}", m_PlantSystem.GetLocalizedHealth(), title, typeInfo.Name ); + + if ( typeInfo.ContainsPlant ) + { + // a ~1_HEALTH~ [bright] ~2_COLOR~ ~3_NAME~ + list.Add( hueInfo.IsBright() ? 1061891 : 1061889, args ); + } + else + { + // a ~1_HEALTH~ [bright] ~2_COLOR~ ~3_NAME~ plant + list.Add( hueInfo.IsBright() ? 1061892 : 1061890, args ); + } + } + else + { + // a decorative ~1_COLOR~ ~2_TYPE~ plant + list.Add( 1070973, string.Format( "#{0}\t#{1}", title, typeInfo.Name ) ); + } + } + else if ( m_PlantStatus >= PlantStatus.Seed ) + { + PlantHueInfo hueInfo = PlantHueInfo.GetInfo( m_PlantHue ); + + int title = PlantTypeInfo.GetBonsaiTitle( m_PlantType ); + if ( title == 0 ) // Not a bonsai + title = hueInfo.Name; + + string args = string.Format( "#{0}\t#{1}\t#{2}", m_PlantSystem.GetLocalizedDirtStatus(), m_PlantSystem.GetLocalizedHealth(), title ); + + if ( m_ShowType ) + { + PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo( m_PlantType ); + args += "\t#" + typeInfo.Name.ToString(); + + if ( typeInfo.ContainsPlant && m_PlantStatus == PlantStatus.Plant ) + { + // a bowl of ~1_val~ dirt with a ~2_val~ [bright] ~3_val~ ~4_val~ + list.Add( hueInfo.IsBright() ? 1060832 : 1060831, args ); + } + else + { + // a bowl of ~1_val~ dirt with a ~2_val~ [bright] ~3_val~ ~4_val~ ~5_val~ + list.Add( hueInfo.IsBright() ? 1061887 : 1061888, args + "\t#" + GetLocalizedPlantStatus().ToString() ); + } + } + else + { + // a bowl of ~1_val~ dirt with a ~2_val~ [bright] ~3_val~ ~4_val~ + list.Add( hueInfo.IsBright() ? 1060832 : 1060831, args + "\t#" + GetLocalizedPlantStatus().ToString() ); + } + } + else + { + list.Add( 1060830, "#" + m_PlantSystem.GetLocalizedDirtStatus() ); // a bowl of ~1_val~ dirt + } + } + + public bool IsUsableBy( Mobile from ) + { + Item root = RootParent as Item; + return IsChildOf( from.Backpack ) || IsChildOf( from.BankBox ) || IsLockedDown && IsAccessibleTo( from ) || root != null && root.IsSecure && root.IsAccessibleTo( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_PlantStatus >= PlantStatus.DecorativePlant ) + return; + + if ( !IsUsableBy( from ) ) + { + LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + return; + } + + from.SendGump( new MainPlantGump( this ) ); + } + + public void PlantSeed( Mobile from, Seed seed ) + { + if ( m_PlantStatus >= PlantStatus.FullGrownPlant ) + { + LabelTo( from, 1061919 ); // You must use a seed on a bowl of dirt! + } + else if ( !IsUsableBy( from ) ) + { + LabelTo( from, 1061921 ); // The bowl of dirt must be in your pack, or you must lock it down. + } + else if ( m_PlantStatus != PlantStatus.BowlOfDirt ) + { + if ( m_PlantStatus >= PlantStatus.Plant ) + LabelTo( from, "This bowl of dirt already has a plant in it!" ); + else if ( m_PlantStatus >= PlantStatus.Sapling ) + LabelTo( from, "This bowl of dirt already has a sapling in it!" ); + else + LabelTo( from, "This bowl of dirt already has a seed in it!" ); + } + else if ( m_PlantSystem.Water < 2 ) + { + LabelTo( from, 1061920 ); // The dirt in this bowl needs to be softened first. + } + else + { + m_PlantType = seed.PlantType; + m_PlantHue = seed.PlantHue; + m_ShowType = seed.ShowType; + + seed.Delete(); + + PlantStatus = PlantStatus.Seed; + + m_PlantSystem.Reset( false ); + + LabelTo( from, 1061922 ); // You plant the seed in the bowl of dirt. + } + } + + public void Die() + { + if ( m_PlantStatus >= PlantStatus.FullGrownPlant ) + { + PlantStatus = PlantStatus.DeadTwigs; + } + else + { + PlantStatus = PlantStatus.BowlOfDirt; + m_PlantSystem.Reset( true ); + } + } + + public void Pour( Mobile from, Item item ) + { + if ( m_PlantStatus >= PlantStatus.DeadTwigs ) + return; + + if ( m_PlantStatus == PlantStatus.DecorativePlant ) + { + LabelTo( from, 1053049 ); // This is a decorative plant, it does not need watering! + return; + } + + if ( !IsUsableBy( from ) ) + { + LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + return; + } + + if ( item is BaseBeverage ) + { + BaseBeverage beverage = (BaseBeverage)item; + + if ( beverage.IsEmpty || !beverage.Pourable || beverage.Content != BeverageType.Water ) + { + LabelTo( from, 1053069 ); // You can't use that on a plant! + return; + } + + if ( !beverage.ValidateUse( from, true ) ) + return; + + beverage.Quantity--; + m_PlantSystem.Water++; + + from.PlaySound( 0x4E ); + LabelTo( from, 1061858 ); // You soften the dirt with water. + } + else if ( item is BasePotion ) + { + BasePotion potion = (BasePotion)item; + + int message; + if ( ApplyPotion( potion.PotionEffect, false, out message ) ) + { + potion.Delete(); + from.PlaySound( 0x240 ); + from.AddToBackpack( new Bottle() ); + } + LabelTo( from, message ); + } + else if ( item is PotionKeg ) + { + PotionKeg keg = (PotionKeg)item; + + if ( keg.Held <= 0 ) + { + LabelTo( from, 1053069 ); // You can't use that on a plant! + return; + } + + int message; + if ( ApplyPotion( keg.Type, false, out message ) ) + { + keg.Held--; + from.PlaySound( 0x240 ); + } + LabelTo( from, message ); + } + else + { + LabelTo( from, 1053069 ); // You can't use that on a plant! + } + } + + public bool ApplyPotion( PotionEffect effect, bool testOnly, out int message ) + { + if ( m_PlantStatus >= PlantStatus.DecorativePlant ) + { + message = 1053049; // This is a decorative plant, it does not need watering! + return false; + } + + if ( m_PlantStatus == PlantStatus.BowlOfDirt ) + { + message = 1053066; // You should only pour potions on a plant or seed! + return false; + } + + bool full = false; + + if ( effect == PotionEffect.PoisonGreater || effect == PotionEffect.PoisonDeadly ) + { + if ( m_PlantSystem.IsFullPoisonPotion ) + full = true; + else if ( !testOnly ) + m_PlantSystem.PoisonPotion++; + } + else if ( effect == PotionEffect.CureGreater ) + { + if ( m_PlantSystem.IsFullCurePotion ) + full = true; + else if ( !testOnly ) + m_PlantSystem.CurePotion++; + } + else if ( effect == PotionEffect.HealGreater ) + { + if ( m_PlantSystem.IsFullHealPotion ) + full = true; + else if ( !testOnly ) + m_PlantSystem.HealPotion++; + } + else if ( effect == PotionEffect.StrengthGreater ) + { + if ( m_PlantSystem.IsFullStrengthPotion ) + full = true; + else if ( !testOnly ) + m_PlantSystem.StrengthPotion++; + } + else if ( effect == PotionEffect.PoisonLesser || effect == PotionEffect.Poison || effect == PotionEffect.CureLesser || effect == PotionEffect.Cure || + effect == PotionEffect.HealLesser || effect == PotionEffect.Heal || effect == PotionEffect.Strength ) + { + message = 1053068; // This potion is not powerful enough to use on a plant! + return false; + } + else + { + message = 1053069; // You can't use that on a plant! + return false; + } + + if ( full ) + { + message = 1053065; // The plant is already soaked with this type of potion! + return false; + } + else + { + message = 1053067; // You pour the potion over the plant. + return true; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Level ); + + writer.Write( (int) m_PlantStatus ); + writer.Write( (int) m_PlantType ); + writer.Write( (int) m_PlantHue ); + writer.Write( (bool) m_ShowType ); + + if ( m_PlantStatus < PlantStatus.DecorativePlant ) + m_PlantSystem.Save( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + goto case 0; + } + case 0: + { + if ( version < 1 ) + m_Level = SecureLevel.CoOwners; + + m_PlantStatus = (PlantStatus)reader.ReadInt(); + m_PlantType = (PlantType)reader.ReadInt(); + m_PlantHue = (PlantHue)reader.ReadInt(); + m_ShowType = reader.ReadBool(); + + if ( m_PlantStatus < PlantStatus.DecorativePlant ) + m_PlantSystem = new PlantSystem( this, reader ); + + break; + } + } + + m_Instances.Add( this ); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + m_Instances.Remove( this ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantPourTarget.cs b/Scripts/Engines/Plants/PlantPourTarget.cs new file mode 100644 index 000000000..a27b2ac9c --- /dev/null +++ b/Scripts/Engines/Plants/PlantPourTarget.cs @@ -0,0 +1,32 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Engines.Plants +{ + public class PlantPourTarget : Target + { + private PlantItem m_Plant; + + public PlantPourTarget( PlantItem plant ) : base( 3, true, TargetFlags.None ) + { + m_Plant = plant; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Plant.Deleted && from.InRange( m_Plant.GetWorldLocation(), 3 ) && targeted is Item ) + { + m_Plant.Pour( from, (Item)targeted ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( !m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant && from.InRange( m_Plant.GetWorldLocation(), 3 ) && m_Plant.IsUsableBy( from ) ) + { + from.SendGump( new MainPlantGump( m_Plant ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantResources.cs b/Scripts/Engines/Plants/PlantResources.cs new file mode 100644 index 000000000..cc0b9a11b --- /dev/null +++ b/Scripts/Engines/Plants/PlantResources.cs @@ -0,0 +1,52 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.Plants +{ + public class PlantResourceInfo + { + private static PlantResourceInfo[] m_ResourceList = new PlantResourceInfo[] + { + new PlantResourceInfo( PlantType.ElephantEarPlant, PlantHue.BrightRed, typeof( RedLeaves ) ), + new PlantResourceInfo( PlantType.PonytailPalm, PlantHue.BrightRed, typeof( RedLeaves ) ), + new PlantResourceInfo( PlantType.CenturyPlant, PlantHue.BrightRed, typeof( RedLeaves ) ), + new PlantResourceInfo( PlantType.Poppies, PlantHue.BrightOrange, typeof( OrangePetals ) ), + new PlantResourceInfo( PlantType.Bulrushes, PlantHue.BrightOrange, typeof( OrangePetals ) ), + new PlantResourceInfo( PlantType.PampasGrass, PlantHue.BrightOrange, typeof( OrangePetals ) ), + new PlantResourceInfo( PlantType.SnakePlant, PlantHue.BrightGreen, typeof( GreenThorns ) ), + new PlantResourceInfo( PlantType.BarrelCactus, PlantHue.BrightGreen, typeof( GreenThorns ) ) + }; + + public static PlantResourceInfo GetInfo( PlantType plantType, PlantHue plantHue ) + { + foreach ( PlantResourceInfo info in m_ResourceList ) + { + if ( info.PlantType == plantType && info.PlantHue == plantHue ) + return info; + } + + return null; + } + + private PlantType m_PlantType; + private PlantHue m_PlantHue; + private Type m_ResourceType; + + public PlantType PlantType { get { return m_PlantType; } } + public PlantHue PlantHue { get { return m_PlantHue; } } + public Type ResourceType { get { return m_ResourceType; } } + + private PlantResourceInfo( PlantType plantType, PlantHue plantHue, Type resourceType ) + { + m_PlantType = plantType; + m_PlantHue = plantHue; + m_ResourceType = resourceType; + } + + public Item CreateResource() + { + return (Item)Activator.CreateInstance( m_ResourceType ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantSystem.cs b/Scripts/Engines/Plants/PlantSystem.cs new file mode 100644 index 000000000..7761e6190 --- /dev/null +++ b/Scripts/Engines/Plants/PlantSystem.cs @@ -0,0 +1,709 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Engines.Plants +{ + public enum PlantHealth + { + Dying, + Wilted, + Healthy, + Vibrant + } + + public enum PlantGrowthIndicator + { + None, + InvalidLocation, + NotHealthy, + Delay, + Grown, + DoubleGrown + } + + public class PlantSystem + { + public static readonly TimeSpan CheckDelay = TimeSpan.FromHours( 23.0 ); + + private PlantItem m_Plant; + private bool m_FertileDirt; + + private DateTime m_NextGrowth; + private PlantGrowthIndicator m_GrowthIndicator; + + private int m_Water; + + private int m_Hits; + private int m_Infestation; + private int m_Fungus; + private int m_Poison; + private int m_Disease; + private int m_PoisonPotion; + private int m_CurePotion; + private int m_HealPotion; + private int m_StrengthPotion; + + private bool m_Pollinated; + private PlantType m_SeedType; + private PlantHue m_SeedHue; + private int m_AvailableSeeds; + private int m_LeftSeeds; + + private int m_AvailableResources; + private int m_LeftResources; + + public PlantItem Plant { get { return m_Plant; } } + + public bool FertileDirt + { + get { return m_FertileDirt; } + set { m_FertileDirt = value; } + } + + public DateTime NextGrowth + { + get { return m_NextGrowth; } + } + + public PlantGrowthIndicator GrowthIndicator + { + get { return m_GrowthIndicator; } + } + + public bool IsFullWater { get { return m_Water >= 4; } } + public int Water + { + get { return m_Water; } + set + { + if ( value < 0 ) + m_Water = 0; + else if ( value > 4 ) + m_Water = 4; + else + m_Water = value; + + m_Plant.InvalidateProperties(); + } + } + + public int Hits + { + get { return m_Hits; } + set + { + if ( m_Hits == value ) + return; + + if ( value < 0 ) + m_Hits = 0; + else if ( value > MaxHits ) + m_Hits = MaxHits; + else + m_Hits = value; + + if ( m_Hits == 0 ) + m_Plant.Die(); + + m_Plant.InvalidateProperties(); + } + } + + public int MaxHits + { + get { return 10 + (int)m_Plant.PlantStatus * 2; } + } + + public PlantHealth Health + { + get + { + int perc = m_Hits * 100 / MaxHits; + + if ( perc < 33 ) + return PlantHealth.Dying; + else if ( perc < 66 ) + return PlantHealth.Wilted; + else if ( perc < 100 ) + return PlantHealth.Healthy; + else + return PlantHealth.Vibrant; + } + } + + public int Infestation + { + get { return m_Infestation; } + set + { + if ( value < 0 ) + m_Infestation = 0; + else if ( value > 2 ) + m_Infestation = 2; + else + m_Infestation = value; + } + } + + public int Fungus + { + get { return m_Fungus; } + set + { + if ( value < 0 ) + m_Fungus = 0; + else if ( value > 2 ) + m_Fungus = 2; + else + m_Fungus = value; + } + } + + public int Poison + { + get { return m_Poison; } + set + { + if ( value < 0 ) + m_Poison = 0; + else if ( value > 2 ) + m_Poison = 2; + else + m_Poison = value; + } + } + + public int Disease + { + get { return m_Disease; } + set + { + if ( value < 0 ) + m_Disease = 0; + else if ( value > 2 ) + m_Disease = 2; + else + m_Disease = value; + } + } + + public bool IsFullPoisonPotion { get { return m_PoisonPotion >= 2; } } + public int PoisonPotion + { + get { return m_PoisonPotion; } + set + { + if ( value < 0 ) + m_PoisonPotion = 0; + else if ( value > 2 ) + m_PoisonPotion = 2; + else + m_PoisonPotion = value; + } + } + + public bool IsFullCurePotion { get { return m_CurePotion >= 2; } } + public int CurePotion + { + get { return m_CurePotion; } + set + { + if ( value < 0 ) + m_CurePotion = 0; + else if ( value > 2 ) + m_CurePotion = 2; + else + m_CurePotion = value; + } + } + + public bool IsFullHealPotion { get { return m_HealPotion >= 2; } } + public int HealPotion + { + get { return m_HealPotion; } + set + { + if ( value < 0 ) + m_HealPotion = 0; + else if ( value > 2 ) + m_HealPotion = 2; + else + m_HealPotion = value; + } + } + + public bool IsFullStrengthPotion { get { return m_StrengthPotion >= 2; } } + public int StrengthPotion + { + get { return m_StrengthPotion; } + set + { + if ( value < 0 ) + m_StrengthPotion = 0; + else if ( value > 2 ) + m_StrengthPotion = 2; + else + m_StrengthPotion = value; + } + } + + public bool HasMaladies + { + get { return Infestation > 0 || Fungus > 0 || Poison > 0 || Disease > 0 || Water != 2; } + } + + public bool PollenProducing + { + get { return m_Plant.IsCrossable && m_Plant.PlantStatus >= PlantStatus.FullGrownPlant; } + } + + public bool Pollinated + { + get { return m_Pollinated; } + set { m_Pollinated = value; } + } + + public PlantType SeedType + { + get + { + if ( m_Pollinated ) + return m_SeedType; + else + return m_Plant.PlantType; + } + set { m_SeedType = value; } + } + + public PlantHue SeedHue + { + get + { + if ( m_Pollinated ) + return m_SeedHue; + else + return m_Plant.PlantHue; + } + set { m_SeedHue = value; } + } + + public int AvailableSeeds + { + get { return m_AvailableSeeds; } + set { if ( value >= 0 ) m_AvailableSeeds = value; } + } + + public int LeftSeeds + { + get { return m_LeftSeeds; } + set { if ( value >= 0 ) m_LeftSeeds = value; } + } + + public int AvailableResources + { + get { return m_AvailableResources; } + set { if ( value >= 0 ) m_AvailableResources = value; } + } + + public int LeftResources + { + get { return m_LeftResources; } + set { if ( value >= 0 ) m_LeftResources = value; } + } + + public PlantSystem( PlantItem plant, bool fertileDirt ) + { + m_Plant = plant; + m_FertileDirt = fertileDirt; + + m_NextGrowth = DateTime.Now + CheckDelay; + m_GrowthIndicator = PlantGrowthIndicator.None; + m_Hits = MaxHits; + m_LeftSeeds = 8; + m_LeftResources = 8; + } + + public void Reset( bool potions ) + { + m_NextGrowth = DateTime.Now + CheckDelay; + m_GrowthIndicator = PlantGrowthIndicator.None; + + Hits = MaxHits; + m_Infestation = 0; + m_Fungus = 0; + m_Poison = 0; + m_Disease = 0; + + if ( potions ) + { + m_PoisonPotion = 0; + m_CurePotion = 0; + m_HealPotion = 0; + m_StrengthPotion = 0; + } + + m_Pollinated = false; + m_AvailableSeeds = 0; + m_LeftSeeds = 8; + + m_AvailableResources = 0; + m_LeftResources = 8; + } + + public int GetLocalizedDirtStatus() + { + if ( Water <= 1 ) + return 1060826; // hard + else if ( Water <= 2 ) + return 1060827; // soft + else if ( Water <= 3 ) + return 1060828; // squishy + else + return 1060829; // sopping wet + } + + public int GetLocalizedHealth() + { + switch ( Health ) + { + case PlantHealth.Dying: return 1060825; // dying + case PlantHealth.Wilted: return 1060824; // wilted + case PlantHealth.Healthy: return 1060823; // healthy + default: return 1060822; // vibrant + } + } + + public static void Configure() + { + EventSink.WorldLoad += new WorldLoadEventHandler( EventSink_WorldLoad ); + + if ( !Misc.AutoRestart.Enabled ) + EventSink.WorldSave += new WorldSaveEventHandler( EventSink_WorldSave ); + + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static void EventSink_Login( LoginEventArgs args ) + { + Mobile from = args.Mobile; + + if ( from.Backpack != null ) + { + Item[] plants = from.Backpack.FindItemsByType( typeof( PlantItem ) ); + + foreach ( PlantItem plant in plants ) + { + if ( plant.IsGrowable ) + plant.PlantSystem.DoGrowthCheck(); + } + } + + if ( from.BankBox != null ) + { + Item[] plants = from.BankBox.FindItemsByType( typeof( PlantItem ) ); + + foreach ( PlantItem plant in plants ) + { + if ( plant.IsGrowable ) + plant.PlantSystem.DoGrowthCheck(); + } + } + } + + public static void GrowAll() + { + ArrayList plants = PlantItem.Plants; + DateTime now = DateTime.Now; + + for ( int i = plants.Count - 1; i >= 0; --i ) + { + PlantItem plant = (PlantItem) plants[i]; + + if ( plant.IsGrowable && (plant.RootParent as Mobile) == null && now >= plant.PlantSystem.NextGrowth ) + plant.PlantSystem.DoGrowthCheck(); + } + } + + private static void EventSink_WorldLoad() + { + GrowAll(); + } + + private static void EventSink_WorldSave( WorldSaveEventArgs args) + { + GrowAll(); + } + + public void DoGrowthCheck() + { + if ( !m_Plant.IsGrowable ) + return; + + if ( DateTime.Now < m_NextGrowth ) + { + m_GrowthIndicator = PlantGrowthIndicator.Delay; + return; + } + + m_NextGrowth = DateTime.Now + CheckDelay; + + if ( !m_Plant.ValidGrowthLocation ) + { + m_GrowthIndicator = PlantGrowthIndicator.InvalidLocation; + return; + } + + if ( m_Plant.PlantStatus == PlantStatus.BowlOfDirt) + { + if ( Water > 2 || Utility.RandomDouble() < 0.9 ) + Water--; + return; + } + + ApplyBeneficEffects(); + + if ( !ApplyMaladiesEffects() ) // Dead + return; + + Grow(); + + UpdateMaladies(); + } + + private void ApplyBeneficEffects() + { + if ( PoisonPotion >= Infestation ) + { + PoisonPotion -= Infestation; + Infestation = 0; + } + else + { + Infestation -= PoisonPotion; + PoisonPotion = 0; + } + + if ( CurePotion >= Fungus ) + { + CurePotion -= Fungus; + Fungus = 0; + } + else + { + Fungus -= CurePotion; + CurePotion = 0; + } + + if ( HealPotion >= Poison ) + { + HealPotion -= Poison; + Poison = 0; + } + else + { + Poison -= HealPotion; + HealPotion = 0; + } + + if ( HealPotion >= Disease ) + { + HealPotion -= Disease; + Disease = 0; + } + else + { + Disease -= HealPotion; + HealPotion = 0; + } + + if ( !HasMaladies ) + { + if ( HealPotion > 0 ) + Hits += HealPotion * 7; + else + Hits += 2; + } + + HealPotion = 0; + } + + private bool ApplyMaladiesEffects() + { + int damage = 0; + + if ( Infestation > 0 ) + damage += Infestation * Utility.RandomMinMax( 3, 6 ); + + if ( Fungus > 0 ) + damage += Fungus * Utility.RandomMinMax( 3, 6 ); + + if ( Poison > 0 ) + damage += Poison * Utility.RandomMinMax( 3, 6 ); + + if ( Disease > 0 ) + damage += Disease * Utility.RandomMinMax( 3, 6 ); + + if ( Water > 2 ) + damage += ( Water - 2 ) * Utility.RandomMinMax( 3, 6 ); + else if ( Water < 2 ) + damage += ( 2 - Water ) * Utility.RandomMinMax( 3, 6 ); + + Hits -= damage; + + return m_Plant.IsGrowable && m_Plant.PlantStatus != PlantStatus.BowlOfDirt; + } + + private void Grow() + { + if ( Health < PlantHealth.Healthy ) + { + m_GrowthIndicator = PlantGrowthIndicator.NotHealthy; + } + else if ( m_FertileDirt && m_Plant.PlantStatus <= PlantStatus.Stage5 && Utility.RandomDouble() < 0.1 ) + { + int curStage = (int)m_Plant.PlantStatus; + m_Plant.PlantStatus = (PlantStatus)( curStage + 2 ); + + m_GrowthIndicator = PlantGrowthIndicator.DoubleGrown; + } + else if ( m_Plant.PlantStatus < PlantStatus.Stage9 ) + { + int curStage = (int)m_Plant.PlantStatus; + m_Plant.PlantStatus = (PlantStatus)( curStage + 1 ); + + m_GrowthIndicator = PlantGrowthIndicator.Grown; + } + else + { + if ( Pollinated && LeftSeeds > 0 && m_Plant.IsCrossable ) + { + LeftSeeds--; + AvailableSeeds++; + } + + if ( LeftResources > 0 && PlantResourceInfo.GetInfo( m_Plant.PlantType, m_Plant.PlantHue ) != null ) + { + LeftResources--; + AvailableResources++; + } + + m_GrowthIndicator = PlantGrowthIndicator.Grown; + } + + if ( m_Plant.PlantStatus >= PlantStatus.Stage9 && !Pollinated ) + { + Pollinated = true; + SeedType = m_Plant.PlantType; + SeedHue = m_Plant.PlantHue; + } + } + + private void UpdateMaladies() + { + double infestationChance = 0.30 - StrengthPotion * 0.075 + ( Water - 2 ) * 0.10; + + PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo( m_Plant.PlantType ); + if ( typeInfo.Flowery ) + infestationChance += 0.10; + + if ( PlantHueInfo.IsBright( m_Plant.PlantHue ) ) + infestationChance += 0.10; + + if ( Utility.RandomDouble() < infestationChance ) + Infestation++; + + + double fungusChance = 0.15 - StrengthPotion * 0.075 + ( Water - 2 ) * 0.10; + + if ( Utility.RandomDouble() < fungusChance ) + Fungus++; + + if ( Water > 2 || Utility.RandomDouble() < 0.9 ) + Water--; + + if ( PoisonPotion > 0 ) + { + Poison += PoisonPotion; + PoisonPotion = 0; + } + + if ( CurePotion > 0 ) + { + Disease += CurePotion; + CurePotion = 0; + } + + StrengthPotion = 0; + } + + public void Save( GenericWriter writer ) + { + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_FertileDirt ); + + writer.Write( (DateTime) m_NextGrowth ); + writer.Write( (int) m_GrowthIndicator ); + + writer.Write( (int) m_Water ); + + writer.Write( (int) m_Hits ); + writer.Write( (int) m_Infestation ); + writer.Write( (int) m_Fungus ); + writer.Write( (int) m_Poison ); + writer.Write( (int) m_Disease ); + writer.Write( (int) m_PoisonPotion ); + writer.Write( (int) m_CurePotion ); + writer.Write( (int) m_HealPotion ); + writer.Write( (int) m_StrengthPotion ); + + writer.Write( (bool) m_Pollinated ); + writer.Write( (int) m_SeedType ); + writer.Write( (int) m_SeedHue ); + writer.Write( (int) m_AvailableSeeds ); + writer.Write( (int) m_LeftSeeds ); + + writer.Write( (int) m_AvailableResources ); + writer.Write( (int) m_LeftResources ); + } + + public PlantSystem( PlantItem plant, GenericReader reader ) + { + m_Plant = plant; + + int version = reader.ReadInt(); + + m_FertileDirt = reader.ReadBool(); + + if ( version >= 1 ) + m_NextGrowth = reader.ReadDateTime(); + else + m_NextGrowth = reader.ReadDeltaTime(); + + m_GrowthIndicator = (PlantGrowthIndicator)reader.ReadInt(); + + m_Water = reader.ReadInt(); + + m_Hits = reader.ReadInt(); + m_Infestation = reader.ReadInt(); + m_Fungus = reader.ReadInt(); + m_Poison = reader.ReadInt(); + m_Disease = reader.ReadInt(); + m_PoisonPotion = reader.ReadInt(); + m_CurePotion = reader.ReadInt(); + m_HealPotion = reader.ReadInt(); + m_StrengthPotion = reader.ReadInt(); + + m_Pollinated = reader.ReadBool(); + m_SeedType = (PlantType)reader.ReadInt(); + m_SeedHue = (PlantHue)reader.ReadInt(); + m_AvailableSeeds = reader.ReadInt(); + m_LeftSeeds = reader.ReadInt(); + + m_AvailableResources = reader.ReadInt(); + m_LeftResources = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PlantType.cs b/Scripts/Engines/Plants/PlantType.cs new file mode 100644 index 000000000..d118587b9 --- /dev/null +++ b/Scripts/Engines/Plants/PlantType.cs @@ -0,0 +1,206 @@ +using System; +using Server; + +namespace Server.Engines.Plants +{ + public enum PlantType + { + CampionFlowers, + Poppies, + Snowdrops, + Bulrushes, + Lilies, + PampasGrass, + Rushes, + ElephantEarPlant, + Fern, + PonytailPalm, + SmallPalm, + CenturyPlant, + WaterPlant, + SnakePlant, + PricklyPearCactus, + BarrelCactus, + TribarrelCactus, + CommonGreenBonsai, + CommonPinkBonsai, + UncommonGreenBonsai, + UncommonPinkBonsai, + RareGreenBonsai, + RarePinkBonsai, + ExceptionalBonsai, + ExoticBonsai + } + + public class PlantTypeInfo + { + private static PlantTypeInfo[] m_Table = new PlantTypeInfo[] + { + new PlantTypeInfo( 0xC83, 0, 0, PlantType.CampionFlowers, false, true, true ), + new PlantTypeInfo( 0xC86, 0, 0, PlantType.Poppies, false, true, true ), + new PlantTypeInfo( 0xC88, 0, 10, PlantType.Snowdrops, false, true, true ), + new PlantTypeInfo( 0xC94, -15, 0, PlantType.Bulrushes, false, true, true ), + new PlantTypeInfo( 0xC8B, 0, 0, PlantType.Lilies, false, true, true ), + new PlantTypeInfo( 0xCA5, -8, 0, PlantType.PampasGrass, false, true, true ), + new PlantTypeInfo( 0xCA7, -10, 0, PlantType.Rushes, false, true, true ), + new PlantTypeInfo( 0xC97, -20, 0, PlantType.ElephantEarPlant, true, false, true ), + new PlantTypeInfo( 0xC9F, -20, 0, PlantType.Fern, false, false, true ), + new PlantTypeInfo( 0xCA6, -16, -5, PlantType.PonytailPalm, false, false, true ), + new PlantTypeInfo( 0xC9C, -5, -10, PlantType.SmallPalm, false, false, true ), + new PlantTypeInfo( 0xD31, 0, -27, PlantType.CenturyPlant, true, false, true ), + new PlantTypeInfo( 0xD04, 0, 10, PlantType.WaterPlant, true, false, true ), + new PlantTypeInfo( 0xCA9, 0, 0, PlantType.SnakePlant, true, false, true ), + new PlantTypeInfo( 0xD2C, 0, 10, PlantType.PricklyPearCactus, false, false, true ), + new PlantTypeInfo( 0xD26, 0, 10, PlantType.BarrelCactus, false, false, true ), + new PlantTypeInfo( 0xD27, 0, 10, PlantType.TribarrelCactus, false, false, true ), + new PlantTypeInfo( 0x28DC, -5, 5, PlantType.CommonGreenBonsai, true, false, false ), + new PlantTypeInfo( 0x28DF, -5, 5, PlantType.CommonPinkBonsai, true, false, false ), + new PlantTypeInfo( 0x28DD, -5, 5, PlantType.UncommonGreenBonsai, true, false, false ), + new PlantTypeInfo( 0x28E0, -5, 5, PlantType.UncommonPinkBonsai, true, false, false ), + new PlantTypeInfo( 0x28DE, -5, 5, PlantType.RareGreenBonsai, true, false, false ), + new PlantTypeInfo( 0x28E1, -5, 5, PlantType.RarePinkBonsai, true, false, false ), + new PlantTypeInfo( 0x28E2, -5, 5, PlantType.ExceptionalBonsai, true, false, false ), + new PlantTypeInfo( 0x28E3, -5, 5, PlantType.ExoticBonsai, true, false, false ) + }; + + public static PlantTypeInfo GetInfo( PlantType plantType ) + { + int index = (int)plantType; + + if ( index >= 0 && index < m_Table.Length ) + return m_Table[index]; + else + return m_Table[0]; + } + + public static PlantType RandomFirstGeneration() + { + switch ( Utility.Random( 3 ) ) + { + case 0: return PlantType.CampionFlowers; + case 1: return PlantType.Fern; + default: return PlantType.TribarrelCactus; + } + } + + public static PlantType RandomBonsai( double increaseRatio ) + { + /* Chances of each plant type are equal to the chances of the previous plant type * increaseRatio: + * E.g.: + * chances_of_uncommon = chances_of_common * increaseRatio + * chances_of_rare = chances_of_uncommon * increaseRatio + * ... + * + * If increaseRatio < 1 -> rare plants are actually rarer than the others + * If increaseRatio > 1 -> rare plants are actually more common than the others (it might be the case with certain monsters) + * + * If a plant type (common, uncommon, ...) has 2 different colors, they have the same chances: + * chances_of_green_common = chances_of_pink_common = chances_of_common / 2 + * ... + */ + + double k1 = increaseRatio >= 0.0 ? increaseRatio : 0.0; + double k2 = k1 * k1; + double k3 = k2 * k1; + double k4 = k3 * k1; + + double exp1 = k1 + 1.0; + double exp2 = k2 + exp1; + double exp3 = k3 + exp2; + double exp4 = k4 + exp3; + + double rand = Utility.RandomDouble(); + + if ( rand < 0.5 / exp4 ) + return PlantType.CommonGreenBonsai; + else if ( rand < 1.0 / exp4 ) + return PlantType.CommonPinkBonsai; + else if ( rand < (k1 * 0.5 + 1.0) / exp4 ) + return PlantType.UncommonGreenBonsai; + else if ( rand < exp1 / exp4 ) + return PlantType.UncommonPinkBonsai; + else if ( rand < (k2 * 0.5 + exp1) / exp4 ) + return PlantType.RareGreenBonsai; + else if ( rand < exp2 / exp4 ) + return PlantType.RarePinkBonsai; + else if ( rand < exp3 / exp4 ) + return PlantType.ExceptionalBonsai; + else + return PlantType.ExoticBonsai; + } + + public static bool IsCrossable( PlantType plantType ) + { + return GetInfo( plantType ).Crossable; + } + + public static PlantType Cross( PlantType first, PlantType second ) + { + if ( !IsCrossable( first ) || !IsCrossable( second ) ) + return PlantType.CampionFlowers; + + int firstIndex = (int)first; + int secondIndex = (int)second; + + if ( firstIndex + 1 == secondIndex || firstIndex == secondIndex + 1 ) + return Utility.RandomBool() ? first : second; + else + return (PlantType)( (firstIndex + secondIndex) / 2 ); + } + + public static int GetBonsaiTitle( PlantType plantType ) + { + switch ( plantType ) + { + case PlantType.CommonGreenBonsai: + case PlantType.CommonPinkBonsai: + return 1063335; // common + + case PlantType.UncommonGreenBonsai: + case PlantType.UncommonPinkBonsai: + return 1063336; // uncommon + + case PlantType.RareGreenBonsai: + case PlantType.RarePinkBonsai: + return 1063337; // rare + + case PlantType.ExceptionalBonsai: + return 1063341; // exceptional + + case PlantType.ExoticBonsai: + return 1063342; // exotic + + default: + return 0; + } + } + + private int m_ItemID; + private int m_OffsetX; + private int m_OffsetY; + private PlantType m_PlantType; + private bool m_ContainsPlant; + private bool m_Flowery; + private bool m_Crossable; + + public int ItemID { get { return m_ItemID; } } + public int OffsetX { get { return m_OffsetX; } } + public int OffsetY { get { return m_OffsetY; } } + public PlantType PlantType { get { return m_PlantType; } } + public int Name { get { return 1020000 + m_ItemID; } } + public bool ContainsPlant { get { return m_ContainsPlant; } } + public bool Flowery { get { return m_Flowery; } } + public bool Crossable { get { return m_Crossable; } } + + private PlantTypeInfo( int itemID, int offsetX, int offsetY, PlantType plantType, bool containsPlant, bool flowery, bool crossable ) + { + m_ItemID = itemID; + m_OffsetX = offsetX; + m_OffsetY = offsetY; + m_PlantType = plantType; + m_ContainsPlant = containsPlant; + m_Flowery = flowery; + m_Crossable = crossable; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/PollinateTarget.cs b/Scripts/Engines/Plants/PollinateTarget.cs new file mode 100644 index 000000000..73a5b3452 --- /dev/null +++ b/Scripts/Engines/Plants/PollinateTarget.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Engines.Plants +{ + public class PollinateTarget : Target + { + private PlantItem m_Plant; + + public PollinateTarget( PlantItem plant ) : base( 3, true, TargetFlags.None ) + { + m_Plant = plant; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant && from.InRange( m_Plant.GetWorldLocation(), 3 ) ) + { + if ( !m_Plant.IsUsableBy( from ) ) + { + m_Plant.LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + } + else if ( !m_Plant.IsCrossable ) + { + m_Plant.LabelTo( from, 1053050 ); // You cannot gather pollen from a mutated plant! + } + else if ( !m_Plant.PlantSystem.PollenProducing ) + { + m_Plant.LabelTo( from, 1053051 ); // You cannot gather pollen from a plant in this stage of development! + } + else if ( m_Plant.PlantSystem.Health < PlantHealth.Healthy ) + { + m_Plant.LabelTo( from, 1053052 ); // You cannot gather pollen from an unhealthy plant! + } + else + { + PlantItem targ = targeted as PlantItem; + + if ( targ == null || targ.PlantStatus >= PlantStatus.DecorativePlant || targ.PlantStatus <= PlantStatus.BowlOfDirt ) + { + m_Plant.LabelTo( from, 1053070 ); // You can only pollinate other specially grown plants! + } + else if ( !targ.IsUsableBy( from ) ) + { + targ.LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + } + else if ( !targ.IsCrossable ) + { + targ.LabelTo( from, 1053073 ); // You cannot cross-pollinate with a mutated plant! + } + else if ( !targ.PlantSystem.PollenProducing ) + { + targ.LabelTo( from, 1053074 ); // This plant is not in the flowering stage. You cannot pollinate it! + } + else if ( targ.PlantSystem.Health < PlantHealth.Healthy ) + { + targ.LabelTo( from, 1053075 ); // You cannot pollinate an unhealthy plant! + } + else if ( targ.PlantSystem.Pollinated ) + { + targ.LabelTo( from, 1053072 ); // This plant has already been pollinated! + } + else if ( targ == m_Plant ) + { + targ.PlantSystem.Pollinated = true; + targ.PlantSystem.SeedType = m_Plant.PlantType; + targ.PlantSystem.SeedHue = m_Plant.PlantHue; + + targ.LabelTo( from, 1053071 ); // You pollinate the plant with its own pollen. + } + else + { + targ.PlantSystem.Pollinated = true; + targ.PlantSystem.SeedType = PlantTypeInfo.Cross( m_Plant.PlantType, targ.PlantType ); + targ.PlantSystem.SeedHue = PlantHueInfo.Cross( m_Plant.PlantHue, targ.PlantHue ); + + targ.LabelTo( from, 1053076 ); // You successfully cross-pollinate the plant. + } + } + } + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( !m_Plant.Deleted && m_Plant.PlantStatus < PlantStatus.DecorativePlant && m_Plant.PlantStatus != PlantStatus.BowlOfDirt && from.InRange( m_Plant.GetWorldLocation(), 3 ) && m_Plant.IsUsableBy( from ) ) + { + from.SendGump( new ReproductionGump( m_Plant ) ); + } + } + } +} diff --git a/Scripts/Engines/Plants/ReproductionGump.cs b/Scripts/Engines/Plants/ReproductionGump.cs new file mode 100644 index 000000000..6105f909b --- /dev/null +++ b/Scripts/Engines/Plants/ReproductionGump.cs @@ -0,0 +1,266 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Plants +{ + public class ReproductionGump : Gump + { + private PlantItem m_Plant; + + public ReproductionGump( PlantItem plant ) : base( 20, 20 ) + { + m_Plant = plant; + + DrawBackground(); + + AddButton( 70, 67, 0xD4, 0xD4, 1, GumpButtonType.Reply, 0 ); // Main menu + AddItem( 57, 65, 0x1600 ); + + AddLabel( 108, 67, 0x835, "Reproduction" ); + + if ( m_Plant.PlantStatus == PlantStatus.Stage9 ) + { + AddButton( 212, 67, 0xD4, 0xD4, 2, GumpButtonType.Reply, 0 ); // Set to decorative + AddItem( 202, 68, 0xC61 ); + AddLabel( 216, 66, 0x21, "/" ); + } + + AddButton( 80, 116, 0xD4, 0xD4, 3, GumpButtonType.Reply, 0 ); // Pollination + AddItem( 66, 117, 0x1AA2 ); + AddPollinationState( 106, 116 ); + + AddButton( 128, 116, 0xD4, 0xD4, 4, GumpButtonType.Reply, 0 ); // Resources + AddItem( 113, 120, 0x1021 ); + AddResourcesState( 149, 116 ); + + AddButton( 177, 116, 0xD4, 0xD4, 5, GumpButtonType.Reply, 0 ); // Seeds + AddItem( 160, 121, 0xDCF ); + AddSeedsState( 199, 116 ); + + AddButton( 70, 163, 0xD2, 0xD2, 6, GumpButtonType.Reply, 0 ); // Gather pollen + AddItem( 56, 164, 0x1AA2 ); + + AddButton( 138, 163, 0xD2, 0xD2, 7, GumpButtonType.Reply, 0 ); // Gather resources + AddItem( 123, 167, 0x1021 ); + + AddButton( 212, 163, 0xD2, 0xD2, 8, GumpButtonType.Reply, 0 ); // Gather seeds + AddItem( 195, 168, 0xDCF ); + } + + private void DrawBackground() + { + AddBackground( 50, 50, 200, 150, 0xE10 ); + + AddImage( 60, 90, 0xE17 ); + AddImage( 120, 90, 0xE17 ); + + AddImage( 60, 145, 0xE17 ); + AddImage( 120, 145, 0xE17 ); + + AddItem( 45, 45, 0xCEF ); + AddItem( 45, 118, 0xCF0 ); + + AddItem( 211, 45, 0xCEB ); + AddItem( 211, 118, 0xCEC ); + } + + private void AddPollinationState( int x, int y ) + { + PlantSystem system = m_Plant.PlantSystem; + + if ( !system.PollenProducing ) + AddLabel( x, y, 0x35, "-" ); + else if ( !system.Pollinated ) + AddLabel( x, y, 0x21, "!" ); + else + AddLabel( x, y, 0x3F, "+" ); + } + + private void AddResourcesState( int x, int y ) + { + PlantResourceInfo resInfo = PlantResourceInfo.GetInfo( m_Plant.PlantType, m_Plant.PlantHue ); + + PlantSystem system = m_Plant.PlantSystem; + int totalResources = system.AvailableResources + system.LeftResources; + + if ( resInfo == null || totalResources == 0 ) + { + AddLabel( x + 5, y, 0x21, "X" ); + } + else + { + AddLabel( x, y, PlantHueInfo.GetInfo( m_Plant.PlantHue ).GumpHue, + string.Format( "{0}/{1}", system.AvailableResources, totalResources ) ); + } + } + + private void AddSeedsState( int x, int y ) + { + PlantSystem system = m_Plant.PlantSystem; + int totalSeeds = system.AvailableSeeds + system.LeftSeeds; + + if ( !m_Plant.IsCrossable || totalSeeds == 0 ) + { + AddLabel( x + 5, y, 0x21, "X" ); + } + else + { + AddLabel( x, y, PlantHueInfo.GetInfo( system.SeedHue ).GumpHue, + string.Format( "{0}/{1}", system.AvailableSeeds, totalSeeds ) ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus >= PlantStatus.DecorativePlant || m_Plant.PlantStatus == PlantStatus.BowlOfDirt || !from.InRange( m_Plant.GetWorldLocation(), 3 ) ) + return; + + if ( !m_Plant.IsUsableBy( from ) ) + { + m_Plant.LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch ( info.ButtonID ) + { + case 1: // Main menu + { + from.SendGump( new MainPlantGump( m_Plant ) ); + + break; + } + case 2: // Set to decorative + { + if ( m_Plant.PlantStatus == PlantStatus.Stage9 ) + { + from.SendGump( new SetToDecorativeGump( m_Plant ) ); + } + + break; + } + case 3: // Pollination + { + from.Send( new DisplayHelpTopic( 67, true ) ); // POLLINATION STATE + + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + case 4: // Resources + { + from.Send( new DisplayHelpTopic( 69, true ) ); // RESOURCE PRODUCTION + + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + case 5: // Seeds + { + from.Send( new DisplayHelpTopic( 68, true ) ); // SEED PRODUCTION + + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + case 6: // Gather pollen + { + if ( !m_Plant.IsCrossable ) + { + m_Plant.LabelTo( from, 1053050 ); // You cannot gather pollen from a mutated plant! + } + else if ( !m_Plant.PlantSystem.PollenProducing ) + { + m_Plant.LabelTo( from, 1053051 ); // You cannot gather pollen from a plant in this stage of development! + } + else if ( m_Plant.PlantSystem.Health < PlantHealth.Healthy ) + { + m_Plant.LabelTo( from, 1053052 ); // You cannot gather pollen from an unhealthy plant! + } + else + { + from.Target = new PollinateTarget( m_Plant ); + from.SendLocalizedMessage( 1053054 ); // Target the plant you wish to cross-pollinate to. + + break; + } + + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + case 7: // Gather resources + { + PlantResourceInfo resInfo = PlantResourceInfo.GetInfo( m_Plant.PlantType, m_Plant.PlantHue ); + PlantSystem system = m_Plant.PlantSystem; + + if ( resInfo == null ) + { + if ( m_Plant.IsCrossable ) + m_Plant.LabelTo( from, 1053056 ); // This plant has no resources to gather! + else + m_Plant.LabelTo( from, 1053055 ); // Mutated plants do not produce resources! + } + else if ( system.AvailableResources == 0 ) + { + m_Plant.LabelTo( from, 1053056 ); // This plant has no resources to gather! + } + else + { + Item resource = resInfo.CreateResource(); + + if ( from.PlaceInBackpack( resource ) ) + { + system.AvailableResources--; + m_Plant.LabelTo( from, 1053059 ); // You gather resources from the plant. + } + else + { + resource.Delete(); + m_Plant.LabelTo( from, 1053058 ); // You attempt to gather as many resources as you can hold, but your backpack is full. + } + } + + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + case 8: // Gather seeds + { + PlantSystem system = m_Plant.PlantSystem; + + if ( !m_Plant.IsCrossable ) + { + m_Plant.LabelTo( from, 1053060 ); // Mutated plants do not produce seeds! + } + else if ( system.AvailableSeeds == 0 ) + { + m_Plant.LabelTo( from, 1053061 ); // This plant has no seeds to gather! + } + else + { + Seed seed = new Seed( system.SeedType, system.SeedHue, true ); + + if ( from.PlaceInBackpack( seed ) ) + { + system.AvailableSeeds--; + m_Plant.LabelTo( from, 1053063 ); // You gather seeds from the plant. + } + else + { + seed.Delete(); + m_Plant.LabelTo( from, 1053062 ); // You attempt to gather as many seeds as you can hold, but your backpack is full. + } + } + + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/Seed.cs b/Scripts/Engines/Plants/Seed.cs new file mode 100644 index 000000000..97d615826 --- /dev/null +++ b/Scripts/Engines/Plants/Seed.cs @@ -0,0 +1,190 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Engines.Plants +{ + public class Seed : Item + { + private PlantType m_PlantType; + private PlantHue m_PlantHue; + private bool m_ShowType; + + [CommandProperty( AccessLevel.GameMaster )] + public PlantType PlantType + { + get { return m_PlantType; } + set + { + m_PlantType = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public PlantHue PlantHue + { + get { return m_PlantHue; } + set + { + m_PlantHue = value; + Hue = PlantHueInfo.GetInfo( value ).Hue; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ShowType + { + get { return m_ShowType; } + set + { + m_ShowType = value; + InvalidateProperties(); + } + } + + public override int LabelNumber{ get { return 1060810; } } // seed + + public static Seed RandomBonsaiSeed() + { + return RandomBonsaiSeed( 0.5 ); + } + + public static Seed RandomBonsaiSeed( double increaseRatio ) + { + return new Seed( PlantTypeInfo.RandomBonsai( increaseRatio ), PlantHue.Plain, false ); + } + + [Constructable] + public Seed() : this( PlantTypeInfo.RandomFirstGeneration(), PlantHueInfo.RandomFirstGeneration(), false ) + { + } + + [Constructable] + public Seed( PlantType plantType, PlantHue plantHue, bool showType ) : base( 0xDCF ) + { + Weight = 1.0; + + m_PlantType = plantType; + m_PlantHue = plantHue; + m_ShowType = showType; + + Hue = PlantHueInfo.GetInfo( plantHue ).Hue; + } + + public Seed( Serial serial ) : base( serial ) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void AddNameProperty( ObjectPropertyList list ) + { + PlantHueInfo hueInfo = PlantHueInfo.GetInfo( m_PlantHue ); + + int title = PlantTypeInfo.GetBonsaiTitle( m_PlantType ); + if ( title == 0 ) // Not a bonsai + title = hueInfo.Name; + + if ( m_ShowType ) + { + PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo( m_PlantType ); + list.Add( hueInfo.IsBright() ? 1061918 : 1061917, String.Concat( "#", title.ToString(), "\t#", typeInfo.Name.ToString() ) ); // [bright] ~1_COLOR~ ~2_TYPE~ seed + } + else + { + list.Add( hueInfo.IsBright() ? 1060839 : 1060838, String.Concat( "#", title.ToString() ) ); // [bright] ~1_val~ seed + } + } + + public override void OnSingleClick ( Mobile from ) + { + PlantHueInfo hueInfo = PlantHueInfo.GetInfo( m_PlantHue ); + + if ( m_ShowType ) + { + PlantTypeInfo typeInfo = PlantTypeInfo.GetInfo( m_PlantType ); + LabelTo( from, hueInfo.IsBright() ? 1061918 : 1061917, String.Concat( "#", hueInfo.Name.ToString(), "\t#", typeInfo.Name.ToString() ) ); // [bright] ~1_COLOR~ ~2_TYPE~ seed + } + else + { + LabelTo( from, hueInfo.IsBright() ? 1060839 : 1060838, String.Concat( "#", hueInfo.Name.ToString() ) ); // [bright] ~1_val~ seed + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + from.Target = new InternalTarget( this ); + LabelTo( from, 1061916 ); // Choose a bowl of dirt to plant this seed in. + } + + private class InternalTarget : Target + { + private Seed m_Seed; + + public InternalTarget( Seed seed ) : base( 3, false, TargetFlags.None ) + { + m_Seed = seed; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Seed.Deleted ) + return; + + if ( !m_Seed.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + return; + } + + if ( targeted is PlantItem ) + { + PlantItem plant = (PlantItem)targeted; + + plant.PlantSeed( from, m_Seed ); + } + else if ( targeted is Item ) + { + ((Item)targeted).LabelTo( from, 1061919 ); // You must use a seed on a bowl of dirt! + } + else + { + from.SendLocalizedMessage( 1061919 ); // You must use a seed on a bowl of dirt! + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_PlantType ); + writer.Write( (int) m_PlantHue ); + writer.Write( (bool) m_ShowType ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_PlantType = (PlantType)reader.ReadInt(); + m_PlantHue = (PlantHue)reader.ReadInt(); + m_ShowType = reader.ReadBool(); + + if ( Weight != 1.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Plants/SetToDecorativeGump.cs b/Scripts/Engines/Plants/SetToDecorativeGump.cs new file mode 100644 index 000000000..f4c44379a --- /dev/null +++ b/Scripts/Engines/Plants/SetToDecorativeGump.cs @@ -0,0 +1,79 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Plants +{ + public class SetToDecorativeGump : Gump + { + private PlantItem m_Plant; + + public SetToDecorativeGump( PlantItem plant ) : base( 20, 20 ) + { + m_Plant = plant; + + DrawBackground(); + + AddLabel( 115, 85, 0x44, "Set plant" ); + AddLabel( 82, 105, 0x44, "to decorative mode?" ); + + AddButton( 98, 140, 0x47E, 0x480, 1, GumpButtonType.Reply, 0 ); // Cancel + + AddButton( 138, 141, 0xD2, 0xD2, 2, GumpButtonType.Reply, 0 ); // Help + AddLabel( 143, 141, 0x835, "?" ); + + AddButton( 168, 140, 0x481, 0x483, 3, GumpButtonType.Reply, 0 ); // Ok + } + + private void DrawBackground() + { + AddBackground( 50, 50, 200, 150, 0xE10 ); + + AddItem( 25, 45, 0xCEB ); + AddItem( 25, 118, 0xCEC ); + + AddItem( 227, 45, 0xCEF ); + AddItem( 227, 118, 0xCF0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 0 || m_Plant.Deleted || m_Plant.PlantStatus != PlantStatus.Stage9 || !from.InRange( m_Plant.GetWorldLocation(), 3 ) ) + return; + + if ( !m_Plant.IsUsableBy( from ) ) + { + m_Plant.LabelTo( from, 1061856 ); // You must have the item in your backpack or locked down in order to use it. + return; + } + + switch ( info.ButtonID ) + { + case 1: // Cancel + { + from.SendGump( new ReproductionGump( m_Plant ) ); + + break; + } + case 2: // Help + { + from.Send( new DisplayHelpTopic( 70, true ) ); // DECORATIVE MODE + + from.SendGump( new SetToDecorativeGump( m_Plant ) ); + + break; + } + case 3: // Ok + { + m_Plant.PlantStatus = PlantStatus.DecorativePlant; + m_Plant.LabelTo( from, 1053077 ); // You prune the plant. This plant will no longer produce resources or seeds, but will require no upkeep. + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs b/Scripts/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs new file mode 100644 index 000000000..b65769b45 --- /dev/null +++ b/Scripts/Engines/Quests/Ambitious Solen Queen/AmbitiousQueenQuest.cs @@ -0,0 +1,157 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Ambitious +{ + public class AmbitiousQueenQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Ambitious.DontOfferConversation ), + typeof( Ambitious.AcceptConversation ), + typeof( Ambitious.DuringKillQueensConversation ), + typeof( Ambitious.GatherFungiConversation ), + typeof( Ambitious.DuringFungiGatheringConversation ), + typeof( Ambitious.EndConversation ), + typeof( Ambitious.FullBackpackConversation ), + typeof( Ambitious.End2Conversation ), + typeof( Ambitious.KillQueensObjective ), + typeof( Ambitious.ReturnAfterKillsObjective ), + typeof( Ambitious.GatherFungiObjective ), + typeof( Ambitious.GetRewardObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // Ambitious Solen Queen Quest + return 1054146; + } + } + + public override object OfferMessage + { + get + { + /* The Solen queen considers you eagerly for a moment then says,

+ * + * Yes. Yes, I think you could be of use. Normally, of course, I would handle + * these things on my own, but these are busy times. Much to do, much to do. + * And besides, if I am to one day become the Matriarch, then it will be good to + * have experience trusting others to carry out various tasks for me. Yes.

+ * + * That is my plan, you see - I will become the next Matriarch. Our current + * Matriarch is fine and all, but she won't be around forever. And when she steps + * down, I intend to be the next in line. Ruling others is my destiny, you see.

+ * + * What I ask of you is quite simple. First, I need you to remove some of the + * - well - competition, I suppose. Though I dare say most are hardly competent to + * live up to such a title. I'm referring to the other queens of this colony, + * of course. My dear sisters, so to speak. If you could remove 5 of them, I would + * be most pleased. *sighs* By remove, I mean kill them. Don't make that face + * at me - this is how things work in a proper society, and ours has been more proper + * than most since the dawn of time. It's them or me, and whenever I give it + * any thought, I'm quite sure I'd prefer it to be them.

+ * + * I also need you to gather some zoogi fungus for me - 50 should do the trick.

+ * + * Will you accept my offer? + */ + return 1054060; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.Zero; } } + public override bool IsTutorial{ get{ return false; } } + + public override int Picture{ get{ return 0x15C9; } } + + private bool m_RedSolen; + + public bool RedSolen{ get{ return m_RedSolen; } } + + public AmbitiousQueenQuest( PlayerMobile from, bool redSolen ) : base( from ) + { + m_RedSolen = redSolen; + } + + // Serialization + public AmbitiousQueenQuest() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_RedSolen = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_RedSolen ); + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + public static void GiveRewardTo( PlayerMobile player, ref bool bagOfSending, ref bool powderOfTranslocation, ref bool gold ) + { + if ( bagOfSending ) + { + Item reward = new BagOfSending(); + + if ( player.PlaceInBackpack( reward ) ) + { + player.SendLocalizedMessage( 1054074, "", 0x59 ); // You have been given a bag of sending. + bagOfSending = false; + } + else + { + reward.Delete(); + } + } + + if ( powderOfTranslocation ) + { + Item reward = new PowderOfTranslocation( Utility.RandomMinMax( 10, 12 ) ); + + if ( player.PlaceInBackpack( reward ) ) + { + player.SendLocalizedMessage( 1054075, "", 0x59 ); // You have been given some powder of translocation. + powderOfTranslocation = false; + } + else + { + reward.Delete(); + } + } + + if ( gold ) + { + Item reward = new Gold( Utility.RandomMinMax( 250, 350 ) ); + + if ( player.PlaceInBackpack( reward ) ) + { + player.SendLocalizedMessage( 1054076, "", 0x59 ); // You have been given some gold. + gold = false; + } + else + { + reward.Delete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Ambitious Solen Queen/Conversations.cs b/Scripts/Engines/Quests/Ambitious Solen Queen/Conversations.cs new file mode 100644 index 000000000..d2257c01a --- /dev/null +++ b/Scripts/Engines/Quests/Ambitious Solen Queen/Conversations.cs @@ -0,0 +1,274 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Ambitious +{ + public class DontOfferConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen considers you for a moment then says,

+ * + * Hmmm... I could perhaps benefit from your assistance, but you seem to be + * busy with another task at the moment. Return to me when you complete whatever + * it is that you're working on and maybe I can still put you to good use. + */ + return 1054059; + } + } + + public override bool Logged{ get{ return false; } } + + public DontOfferConversation() + { + } + } + + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen smiles as you decide to help her.

+ * + * Excellent. We'll worry about the zoogi fungus later - start by eliminating + * 5 queens from my colony.

That part's important, by the way; they must + * be queens from my colony. Killing queens from the other solen colony does + * little to help me become Matriarch of this colony and will not count + * toward your task.

+ * + * Oh, and none of those nasty infiltrator queens either. They perform a necessary + * duty, I suppose, spying on the other colony. I fail to see why that couldn't be + * left totally to the warriors, though. Nevertheless, they do not count as well.

+ * + * Very well. Carry on. I'll be waiting for your return. + */ + return 1054061; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new KillQueensObjective() ); + } + } + + public class DuringKillQueensConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen looks up as you approach.

+ * + * You're back, but you have not yet eliminated 5 queens from my colony. + * Return when you have completed this task.

+ * + * Remember, by the way, that queens from the other solen colony and + * infiltrator queens do not count toward your task.

+ * + * Very well. Carry on. I'll be waiting for your return. + */ + return 1054066; + } + } + + public override bool Logged{ get{ return false; } } + + public DuringKillQueensConversation() + { + } + } + + public class GatherFungiConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen looks pleased to see you.

+ * + * Splendid! You've done quite well in reducing my competition to become + * the next Matriarch. Now I must ask that you gather some zoogi fungus for me. + * I must practice processing it into powder of translocation.

+ * + * I believe the amount we agreed upon earlier was 50. Please return when + * you have that amount and then give them to me.

+ * + * Farewell for now. + */ + return 1054068; + } + } + + public GatherFungiConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GatherFungiObjective() ); + } + } + + public class DuringFungiGatheringConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen looks up as you approach.

+ * + * Do you have the zoogi fungus?

+ * + * If so, give them to me. Otherwise, go gather some and then return to me. + */ + return 1054070; + } + } + + public override bool Logged{ get{ return false; } } + + public DuringFungiGatheringConversation() + { + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen smiles as she takes the zoogi fungus from you.

+ * + * Wonderful! I greatly appreciate your help with these tasks. My plans are beginning + * to take shape ensuring that I will be the next Matriarch. But there is still + * much to be done until then.

+ * + * You've done what I've asked of you and for that I thank you. Please accept this + * bag of sending and some powder of translocation as a reward. Oh, and I suppose + * I should give you some gold as well. Yes, yes. Of course. + */ + return 1054073; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + bool bagOfSending = true; + bool powderOfTranslocation = true; + bool gold = true; + + AmbitiousQueenQuest.GiveRewardTo( System.From, ref bagOfSending, ref powderOfTranslocation, ref gold ); + + if ( !bagOfSending && !powderOfTranslocation && !gold ) + { + System.Complete(); + } + else + { + System.AddConversation( new FullBackpackConversation( true, bagOfSending, powderOfTranslocation, gold ) ); + } + } + } + + public class FullBackpackConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen looks at you with a smile.

+ * + * While I'd like to finish conducting our business, it seems that you're a + * bit overloaded with equipment at the moment.

+ * + * Perhaps you should free some room in your backpack before we proceed. + */ + return 1054077; + } + } + + private bool m_Logged; + private bool m_BagOfSending; + private bool m_PowderOfTranslocation; + private bool m_Gold; + + public override bool Logged{ get{ return m_Logged; } } + + public FullBackpackConversation( bool logged, bool bagOfSending, bool powderOfTranslocation, bool gold ) + { + m_Logged = logged; + + m_BagOfSending = bagOfSending; + m_PowderOfTranslocation = powderOfTranslocation; + m_Gold = gold; + } + + public FullBackpackConversation() + { + m_Logged = true; + } + + public override void OnRead() + { + if ( m_Logged ) + System.AddObjective( new GetRewardObjective( m_BagOfSending, m_PowderOfTranslocation, m_Gold ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_BagOfSending = reader.ReadBool(); + m_PowderOfTranslocation = reader.ReadBool(); + m_Gold = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_BagOfSending ); + writer.Write( (bool) m_PowderOfTranslocation ); + writer.Write( (bool) m_Gold ); + } + } + + public class End2Conversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen queen looks up as you approach.

+ * + * Ah good, you've returned. I will conclude our business by giving you any + * remaining rewards I owe you for aiding me. + */ + return 1054078; + } + } + + public End2Conversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Ambitious Solen Queen/Mobiles/AmbitiousSolenQueen.cs b/Scripts/Engines/Quests/Ambitious Solen Queen/Mobiles/AmbitiousSolenQueen.cs new file mode 100644 index 000000000..0c7c6c2be --- /dev/null +++ b/Scripts/Engines/Quests/Ambitious Solen Queen/Mobiles/AmbitiousSolenQueen.cs @@ -0,0 +1,225 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Ambitious +{ + public abstract class BaseAmbitiousSolenQueen : BaseQuester + { + public abstract bool RedSolen{ get; } + + public override bool DisallowAllMoves{ get{ return false; } } + + public BaseAmbitiousSolenQueen() + { + } + + public override void InitBody() + { + Name = "an ambitious solen queen"; + + Body = 0x30F; + + if ( !RedSolen ) + Hue = 0x453; + + SpeechHue = 0; + } + + public override int GetIdleSound() + { + return 0x10D; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + this.Direction = GetDirectionTo( player ); + + AmbitiousQueenQuest qs = player.Quest as AmbitiousQueenQuest; + + if ( qs != null && qs.RedSolen == this.RedSolen ) + { + if ( qs.IsObjectiveInProgress( typeof( KillQueensObjective ) ) ) + { + qs.AddConversation( new DuringKillQueensConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( ReturnAfterKillsObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( GatherFungiObjective ) ) ) + { + qs.AddConversation( new DuringFungiGatheringConversation() ); + } + else + { + GetRewardObjective lastObj = qs.FindObjective( typeof( GetRewardObjective ) ) as GetRewardObjective; + + if ( lastObj != null && !lastObj.Completed ) + { + bool bagOfSending = lastObj.BagOfSending; + bool powderOfTranslocation = lastObj.PowderOfTranslocation; + bool gold = lastObj.Gold; + + AmbitiousQueenQuest.GiveRewardTo( player, ref bagOfSending, ref powderOfTranslocation, ref gold ); + + lastObj.BagOfSending = bagOfSending; + lastObj.PowderOfTranslocation = powderOfTranslocation; + lastObj.Gold = gold; + + if ( !bagOfSending && !powderOfTranslocation && !gold ) + { + lastObj.Complete(); + } + else + { + qs.AddConversation( new FullBackpackConversation( false, lastObj.BagOfSending, lastObj.PowderOfTranslocation, lastObj.Gold ) ); + } + } + } + } + } + else + { + QuestSystem newQuest = new AmbitiousQueenQuest( player, this.RedSolen ); + + if ( player.Quest == null && QuestSystem.CanOfferQuest( player, typeof( AmbitiousQueenQuest ) ) ) + { + newQuest.SendOffer(); + } + else + { + newQuest.AddConversation( new DontOfferConversation() ); + } + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + this.Direction = GetDirectionTo( from ); + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + AmbitiousQueenQuest qs = player.Quest as AmbitiousQueenQuest; + + if ( qs != null && qs.RedSolen == this.RedSolen ) + { + QuestObjective obj = qs.FindObjective( typeof( GatherFungiObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( dropped is ZoogiFungus ) + { + ZoogiFungus fungi = (ZoogiFungus)dropped; + + if ( fungi.Amount >= 50 ) + { + obj.Complete(); + + fungi.Amount -= 50; + + if ( fungi.Amount == 0 ) + { + fungi.Delete(); + return true; + } + else + { + return false; + } + } + else + { + SayTo( player, 1054072 ); // Our arrangement was for 50 of the zoogi fungus. Please return to me when you have that amount. + return false; + } + } + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + public BaseAmbitiousSolenQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class RedAmbitiousSolenQueen : BaseAmbitiousSolenQueen + { + public override bool RedSolen{ get{ return true; } } + + [Constructable] + public RedAmbitiousSolenQueen() + { + } + + public RedAmbitiousSolenQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class BlackAmbitiousSolenQueen : BaseAmbitiousSolenQueen + { + public override bool RedSolen{ get{ return false; } } + + [Constructable] + public BlackAmbitiousSolenQueen() + { + } + + public BlackAmbitiousSolenQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Ambitious Solen Queen/Objectives.cs b/Scripts/Engines/Quests/Ambitious Solen Queen/Objectives.cs new file mode 100644 index 000000000..f2f8f158b --- /dev/null +++ b/Scripts/Engines/Quests/Ambitious Solen Queen/Objectives.cs @@ -0,0 +1,175 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Ambitious +{ + public class KillQueensObjective : QuestObjective + { + public override object Message + { + get + { + // Kill 5 red/black solen queens. + return ((AmbitiousQueenQuest)System).RedSolen ? 1054062 : 1054063; + } + } + + public override int MaxProgress{ get{ return 5; } } + + public KillQueensObjective() + { + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Red/Black Solen Queens killed: + gump.AddHtmlLocalized( 70, 260, 270, 100, ((AmbitiousQueenQuest)System).RedSolen ? 1054064 : 1054065, BaseQuestGump.Blue, false, false ); + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, MaxProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + if ( Completed ) + return false; + + bool redSolen = ((AmbitiousQueenQuest)System).RedSolen; + + if ( redSolen ) + return from is RedSolenQueen; + else + return from is BlackSolenQueen; + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + bool redSolen = ((AmbitiousQueenQuest)System).RedSolen; + + if ( redSolen ) + { + if ( creature is RedSolenQueen ) + CurProgress++; + } + else + { + if ( creature is BlackSolenQueen ) + CurProgress++; + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnAfterKillsObjective() ); + } + } + + public class ReturnAfterKillsObjective : QuestObjective + { + public override object Message + { + get + { + /* You've completed your task of slaying solen queens. Return to + * the ambitious queen who asked for your help. + */ + return 1054067; + } + } + + public ReturnAfterKillsObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new GatherFungiConversation() ); + } + } + + public class GatherFungiObjective : QuestObjective + { + public override object Message + { + get + { + /* Gather zoogi fungus until you have 50 of them, then give them + * to the ambitious queen you are helping. + */ + return 1054069; + } + } + + public GatherFungiObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new EndConversation() ); + } + } + + public class GetRewardObjective : QuestObjective + { + public override object Message + { + get + { + // Return to the ambitious solen queen for your reward. + return 1054148; + } + } + + private bool m_BagOfSending; + private bool m_PowderOfTranslocation; + private bool m_Gold; + + public bool BagOfSending{ get{ return m_BagOfSending; } set{ m_BagOfSending = value; } } + public bool PowderOfTranslocation{ get{ return m_PowderOfTranslocation; } set{ m_PowderOfTranslocation = value; } } + public bool Gold{ get{ return m_Gold; } set{ m_Gold = value; } } + + public GetRewardObjective( bool bagOfSending, bool powderOfTranslocation, bool gold) + { + m_BagOfSending = bagOfSending; + m_PowderOfTranslocation = powderOfTranslocation; + m_Gold = gold; + } + + public GetRewardObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new End2Conversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_BagOfSending = reader.ReadBool(); + m_PowderOfTranslocation = reader.ReadBool(); + m_Gold = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_BagOfSending ); + writer.Write( (bool) m_PowderOfTranslocation ); + writer.Write( (bool) m_Gold ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/CollectorQuest.cs b/Scripts/Engines/Quests/Collector/CollectorQuest.cs new file mode 100644 index 000000000..b98946204 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/CollectorQuest.cs @@ -0,0 +1,122 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Collector +{ + public class CollectorQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Collector.DontOfferConversation ), + typeof( Collector.DeclineConversation ), + typeof( Collector.AcceptConversation ), + typeof( Collector.ElwoodDuringFishConversation ), + typeof( Collector.ReturnPearlsConversation ), + typeof( Collector.AlbertaPaintingConversation ), + typeof( Collector.AlbertaStoolConversation ), + typeof( Collector.AlbertaEndPaintingConversation ), + typeof( Collector.AlbertaAfterPaintingConversation ), + typeof( Collector.ElwoodDuringPainting1Conversation ), + typeof( Collector.ElwoodDuringPainting2Conversation ), + typeof( Collector.ReturnPaintingConversation ), + typeof( Collector.GabrielAutographConversation ), + typeof( Collector.GabrielNoSheetMusicConversation ), + typeof( Collector.NoSheetMusicConversation ), + typeof( Collector.GetSheetMusicConversation ), + typeof( Collector.GabrielSheetMusicConversation ), + typeof( Collector.GabrielIgnoreConversation ), + typeof( Collector.ElwoodDuringAutograph1Conversation ), + typeof( Collector.ElwoodDuringAutograph2Conversation ), + typeof( Collector.ElwoodDuringAutograph3Conversation ), + typeof( Collector.ReturnAutographConversation ), + typeof( Collector.TomasToysConversation ), + typeof( Collector.TomasDuringCollectingConversation ), + typeof( Collector.ReturnImagesConversation ), + typeof( Collector.ElwoodDuringToys1Conversation ), + typeof( Collector.ElwoodDuringToys2Conversation ), + typeof( Collector.ElwoodDuringToys3Conversation ), + typeof( Collector.FullEndConversation ), + typeof( Collector.FishPearlsObjective ), + typeof( Collector.ReturnPearlsObjective ), + typeof( Collector.FindAlbertaObjective ), + typeof( Collector.SitOnTheStoolObjective ), + typeof( Collector.ReturnPaintingObjective ), + typeof( Collector.FindGabrielObjective ), + typeof( Collector.FindSheetMusicObjective ), + typeof( Collector.ReturnSheetMusicObjective ), + typeof( Collector.ReturnAutographObjective ), + typeof( Collector.FindTomasObjective ), + typeof( Collector.CaptureImagesObjective ), + typeof( Collector.ReturnImagesObjective ), + typeof( Collector.ReturnToysObjective ), + typeof( Collector.MakeRoomObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + //return 1020549; // This localized message seems broken... + return "Collector's Quest"; + } + } + + public override object OfferMessage + { + get + { + /* Elwood greets you warmly, like an old friend he's not + * quite sure he ever had.

+ * + * Hello. Yes. Sit down. Please. Good. Okay, stand. Up to you.

+ * + * So, what brings you to these parts... hey, wait. Just had a thought. + * Would you like to do me a favor? Yes, really. You know, for old + * times sake. The good ole days. You were always one of my best + * suppliers. Or maybe you weren't, who knows any more. Anyway, + * could use some help supplementing my stock. You know me. Always + * looking for something new to add to the collection. Or sometimes + * not so new - just more of the same. But don't have to tell you that. + * You know, don't you. Yes. Just like old times. That's what it'll be. + * You and me - together again. Ah, it's been too long.

+ * + * So what do you think? The fee will be the same as always. I'm a fair + * man. You know that. So what do you say? + */ + return 1055081; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.Zero; } } + public override bool IsTutorial{ get{ return false; } } + + public override int Picture{ get{ return 0x15A9; } } + + public CollectorQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public CollectorQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + public override void Decline() + { + base.Decline(); + + AddConversation( new DeclineConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Conversations.cs b/Scripts/Engines/Quests/Collector/Conversations.cs new file mode 100644 index 000000000..1881725fd --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Conversations.cs @@ -0,0 +1,874 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Collector +{ + public class DontOfferConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood looks up from his ale as you greet him.

+ * + * What's that? Who me? No, no. You must be looking for someone else. + */ + return 1055080; + } + } + + public override bool Logged{ get{ return false; } } + + public DontOfferConversation() + { + } + } + + public class DeclineConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood looks a bit flustered and nearly knocks over his + * bottle of ale.

+ * + * Well, I see. It's like that, is it? Yes. Well then. Okay. + * You've changed. Yes. Yes, you have. Something's changed. I know + * I haven't. Not me. Not good ole Elwood.

+ * + * Elwood trails off, though you can still hear him muttering softly. + */ + return 1055082; + } + } + + public override bool Logged{ get{ return false; } } + + public DeclineConversation() + { + } + } + + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood slaps his knee and grins at you.

+ * + * Yes. Yes. That's the spirit. I knew it. Knew it when I + * first saw you. You remind me so much of your dear departed + * father. Or someone. Not sure. Maybe no one. But that's okay. + * Ah, good times.

+ * + * Anyway, so as you know, I'm a collector. Got all kinds of + * interesting things laying around back at my warehouse. + * You know. You've seen it. Haven't you? Yes? No? Nevermind. + * Not important.

+ * + * Right. So, always trying to add new things to my collection. + * Or sometimes just get more of something. Can't have too many. + * Right? Yeah? Sure.

+ * + * Let's see. Where to start. Oh, I know. Pearls. Yes. But not + * just any pearls. Rainbow pearls. Yes. From the lake here in + * Haven. Seems all that magic Uzeraan was throwing around when + * he transformed the island had an interesting effect on some + * of the shellfish down there. Exactly, rainbow pearls. Useless + * for magic, but an item worth collecting. Trust me on this. + * Trust me.

+ * + * Need you to go fish some up for me. Down at the lake. Lake Haven. + * Off ya go. Happy fishing!

+ * + * Elwood turns back to his ale and now seems oblivious to you. + */ + return 1055083; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FishPearlsObjective() ); + } + } + + public class ElwoodDuringFishConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood looks up as you tap him on the shoulder.

+ * + * Good. Good. You're back. Wait. You don't have the rainbow pearls I + * need. Taking a break? Yeah. Sure. There's no hurry. Let me know when + * you've got all those pearls, though. I'll be here. + */ + return 1055089; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringFishConversation() + { + } + } + + public class ReturnPearlsConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood jumps slightly when you call his name.

+ * + * What. I'm awake. Oh, It's you. Hey, look at those pearls. + * Beautiful. Wow. Where'd you get those... oh right. I sent you to get + * them. From the lake. Lake Haven. Great job. Gotta love rainbow + * pearls. Oooh, Colors.

+ * + * Okay, let's see. Next. Need a painting. Go to the Colored Canvas and + * speak to Alberta. Alberta Giacco. Best painter I've ever met. Ask + * her to do a painting of you. Yes. Of you. A portrait. Never know if + * you might up and become famous one day. Need to have a painting of you + * in my collection. From now. Before all the fame. Go. Alberta awaits. + * She's in Vesper.

+ * + * Elwood starts playing with the pearls you brought him and seems + * to have forgotten you're there. + */ + return 1055090; + } + } + + public ReturnPearlsConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindAlbertaObjective() ); + } + } + + public class AlbertaPaintingConversation : QuestConversation + { + public override object Message + { + get + { + /* Alberta looks up from the painting she is working on and + * faces you.

+ * + * Excuse me. I don't mean to be rude, but I'm in the middle + * of something, and can't... oh wait, I see. You must be the one + * Elwood sent over.

+ * + * Very well. If you'll have a seat on the stool over there, we'll + * get started. This will just take a few seconds. I paint quite + * quickly, you see. I'll start once you are seated.

+ * + * Alberta exchanges the painting she was working on for a blank canvas. + */ + return 1055092; + } + } + + public AlbertaPaintingConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SitOnTheStoolObjective() ); + } + } + + public class AlbertaStoolConversation : QuestConversation + { + public override object Message + { + get + { + /* Alberta looks at you sympathetically.

+ * + * Don't worry, this will only take a few seconds. I realize + * that stool can be uncomfortable, and I apologize for that. + * Perhaps I should replace it with a more comfortable chair. + * But then again, it's that very discomfort that helps produce + * such wonderful facial expressions for my paintings. Ah well. + */ + return 1055096; + } + } + + public override bool Logged{ get{ return false; } } + + public AlbertaStoolConversation() + { + } + } + + public class AlbertaEndPaintingConversation : QuestConversation + { + public override object Message + { + get + { + /* Alberta stands back from the canvas and surveys her + * work.

+ * + * Not too bad. Quite good even, if I do say so myself. As always, + * of course.

+ * + * Oh, you're still here. Please let Elwood know that the painting + * has been completed. I'll have it sent to him once it dries.

+ * + * Alberta removes the portrait from her easel and sets + * it aside to dry. + */ + return 1055098; + } + } + + public AlbertaEndPaintingConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnPaintingObjective() ); + } + } + + public class AlbertaAfterPaintingConversation : QuestConversation + { + public override object Message + { + get + { + /* Alberta stops cleaning her brushes and looks your way.

+ * + * Don't worry, I'll send the painting to Elwood once it's dry. Please + * let him know that the painting has been finished. + */ + return 1055102; + } + } + + public override bool Logged{ get{ return false; } } + + public AlbertaAfterPaintingConversation() + { + } + } + + public class ElwoodDuringPainting1Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood yawns and stretches, then focuses his gaze + * on you.

+ * + * Hello. Do I know you? Hold on a second. Yes. Yes, I do. + * You were going to bring me some rainbow pearls. Wait. No. + * You already did that. I remember now. Right. So go get that + * portrait painted. Alberta is in Vesper. Go to her. Alberta + * Giacco. Come back when she's done. + */ + return 1055094; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringPainting1Conversation() + { + } + } + + public class ElwoodDuringPainting2Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood drums his fingers on the counter then looks + * up at you expectantly.

+ * + * Ah, finally. I'm famished. This so-called tavern doesn't + * even serve pizza, so one must have it delivered. It's an + * outrage. Er... wait. Where's my pizza? Yes, my pizza. + * What kind of delivery is this? Didn't even bring my pizza. + * This will severely impact your tip, I'm afraid.

+ * + * Hold on a moment. You were helping me with something else, + * weren't you? Ah. Yes. I've got it now. You were to have a + * portrait done. Well. Good. Yes. But let's not dawdle. + * Off you go. If you happen to see anyone with my pizza, + * please insist they hurry.

+ */ + return 1055097; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringPainting2Conversation() + { + } + } + + public class ReturnPaintingConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood notices you immediately and waves you over.

+ * + * You're back. Good. That's good. Hmm. You don't seem to have that + * painting yet. Don't tell me Alberta refused. That's no good. I made + * her what she is! She was living in the gutter when I found her. The + * gutter. And she has the... what's that? Oh, she finished the portrait. + * Right. Good. Can always count on Alberta. Always. Best painter in + * the land.

+ * + * Just have a couple... er... wait... make that... two more task for you. + * Then our business will be concluded. For now. Old chums like us will + * always work together again. Yes. We go so far back. Right. I think.

+ * + * Anyway. There's a musician. A minstrel. His name's Gabriel Piete. Quite + * the good singer. One of my favorites. Absolute favorite. What I'd like + * is his autograph. Simple. Just his autograph. You're likely to find him + * at the Conservatory of Music in Britain. He's often there. Often. Between + * performances. Hurry now. There ya go.

+ * + * Elwood falls silent though his lips are still moving. It looks like + * he's quietly repeating the word, "autograph." + */ + return 1055100; + } + } + + public ReturnPaintingConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindGabrielObjective() ); + } + } + + public class GabrielAutographConversation : QuestConversation + { + public override object Message + { + get + { + /* Gabriel sighs loudly as you address him and stops whatever + * it was that he was doing.

+ * + * WHAT?!? Can you not see that I'm working here? Ugh, sometimes + * I wish they'd just lock the outer doors to anyone who doesn't + * belong here. So go ahead and fawn. Get it over with, and then + * leave. Sooner you're out of here, the sooner I'm back to work.

+ * + * I see. So you want an autograph. Fine. If it'll get rid of you, + * I'll give you my autograph. But I'll only sign some sheet music + * for one of my songs. Until then, please let me get back to work.

+ * + * You can probably find some of my sheet music at one of the theaters + * in the land. When I perform there, they often sell it as a souvenir. + * Speak to the impresario... the theater manager. My last three + * performances were in Nujel'm, Jhelom, and here in Britian. + */ + return 1055103; + } + } + + public GabrielAutographConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindSheetMusicObjective( true ) ); + } + } + + public class GabrielNoSheetMusicConversation : QuestConversation + { + public override object Message + { + get + { + /* Gabriel does not look happy to see you.

+ * + * Do you have any sheet music? No. Please return when you do, + * but until then please leave me to my work. + */ + return 1055111; + } + } + + public override bool Logged{ get{ return false; } } + + public GabrielNoSheetMusicConversation() + { + } + } + + public class NoSheetMusicConversation : QuestConversation + { + public override object Message + { + get + { + /* Sheet music for a Gabriel Piete song? No, I'm sorry, but we've run out. + * We might get some more after he performs here again, but right now + * we don't have any. My apologies. + */ + return 1055106; + } + } + + public override bool Logged{ get{ return false; } } + + public NoSheetMusicConversation() + { + } + } + + public class GetSheetMusicConversation : QuestConversation + { + public override object Message + { + get + { + // The theater impresario hands you some sheet of music for one of Gabriel Piete's songs. + return 1055109; + } + } + + public GetSheetMusicConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnSheetMusicObjective() ); + } + } + + public class GabrielSheetMusicConversation : QuestConversation + { + public override object Message + { + get + { + /* Gabriel looks up impatiently as you approach.

+ * + * Good. We can finally be done with one another. Here, let me + * sign that and have this business completed.

+ * + * Gabriel takes the sheet music, autographs it, and then hands it back to you. + */ + return 1055113; + } + } + + public GabrielSheetMusicConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnAutographObjective() ); + } + } + + public class GabrielIgnoreConversation : QuestConversation + { + public override object Message + { + get + { + // Gabriel ignores you. + return 1055118; + } + } + + public override bool Logged{ get{ return false; } } + + public GabrielIgnoreConversation() + { + } + } + + public class ElwoodDuringAutograph1Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood jumps and almost falls from his stool when you + * tap his shoulder.

+ * + * Oh my. Don't do that. Scared me half to death. Sneaking up on + * people like that. I don't even know you. Yes, I do. I do know you. + * Ordered a pizza from you and never received it. I'm rather miffed + * about that.

+ * + * No. No. Wait. Of course. Not the pizza. You're here with the + * moonfire brew I wanted. Magical moonfire brew. Not sure what it is. + * Just want some.

+ * + * What's that? Oh. The autograph. Gabriel Piete. Yes. Of course. + * Do you have it? No. Well. Hmm. Don't sneak up on people like that. Not polite. + */ + return 1055105; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringAutograph1Conversation() + { + } + } + + public class ElwoodDuringAutograph2Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood seems to be studying the bottom of his bottle of ale + * as you approach.

+ * + * What's this? Strange. Quite strange. I could have sworn I was + * drinking wine.

+ * + * Oh, hello. Yes. Good to see you. Any luck with that autograph? + * Bet you thought I'd forgotten. No. Not me. Mind like a steel trap. + * Can't get it open no matter how hard you try. Or something. No luck + * yet? Ah well. keep trying. I have faith in you. Whoever you are. + */ + return 1055112; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringAutograph2Conversation() + { + } + } + + public class ElwoodDuringAutograph3Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood scratches his ear then notices you.

+ * + * Good day. What brings you to the Albatross? Me? An autograph? + * You want my autograph? Well, I suppose. What's that? Oh. Yes. + * Gabriel Piete. Yes. Get his autograph and return to me. Good day. + */ + return 1055115; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringAutograph3Conversation() + { + } + } + + public class ReturnAutographConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood looks up eagerly as you tell him about the autographed + * sheet music.

+ * + * Quite good work. Not an easy one to deal with, that one. Gabriel Piete. + * Yes. Well done. Nice autograph.

+ * + * One last task. I would like a set of toy monster figurines made by the + * famous toymaker, Tomas O'Neerlan. You'll find him in Trinsic. He's often + * at the Tinker's Guild. Try looking there.

+ * + * You're doing quite well. Quite well indeed. Knew you would. Just like old + * times. Yes. Quite good. + */ + return 1055116; + } + } + + public ReturnAutographConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindTomasObjective() ); + } + } + + public class TomasToysConversation : QuestConversation + { + public override object Message + { + get + { + /* Tomas smiles freely as you speak to him.

+ * + * Ah, to be sure I can make you some toy monster figurines. + * That's my work, making toys. Worry not, we'll put together + * a good set of monsters for you and your figurines.

+ * + * But I'll be needing something from you before I can begin. + * Here, take these enchanted paints. Using them, you can capture + * a set of images of the creatures you wish me to make into toys. + * It'll only work on the group of creatures I select for your + * set of figurines. Oh, and I'll be needing those enchanted paints + * back when all is said and done.

+ */ + return 1055119; + } + } + + public TomasToysConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new CaptureImagesObjective( true ) ); + } + } + + public class TomasDuringCollectingConversation : QuestConversation + { + public override object Message + { + get + { + /* Tomas greets you warmly as you approach.

+ * + * 'Tis good to see you. I see that you have not yet collected + * all of the images we need. 'Tis fine, but I'll be needing + * those before I can make the toy figurines. Return when you have + * the complete set of images.

+ */ + return 1055129; + } + } + + public override bool Logged{ get{ return false; } } + + public TomasDuringCollectingConversation() + { + } + } + + public class ReturnImagesConversation : QuestConversation + { + public override object Message + { + get + { + /* Tomas grins as you walk toward him.

+ * + * I see that you have collected all of the images we need. + * 'Tis good. I'll begin straight away on the toy figurines. + * I'll have them delivered to you when ready. Where shall I send + * them? To Elwood in Haven? Yes, I know him. We've done business + * in the past. Odd fellow.

Tomas smiles as you return his + * enchanted paints back to him.

+ */ + return 1055131; + } + } + + public ReturnImagesConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnToysObjective() ); + } + } + + public class ElwoodDuringToys1Conversation : QuestConversation + { + public override object Message + { + get + { + /* You watch as Elwood spins around blissfully on his stool.

+ * + * Oh. Forgive me. Didn't see you there. Whoo. Dizzy. Can't see straight. + * Have you gotten those figurines yet? No. Ah. Not to worry. Keep at it. + * No doubt you'll come through with those.

+ * + * Excuse me a moment. I think I need to sit down. Wait. I am sitting down. + * Good then. Yes. Sitting down.

Elwood reaches out and takes hold + * of the counter as if to steady himself. + */ + return 1055123; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringToys1Conversation() + { + } + } + + public class ElwoodDuringToys2Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood suddenly stops and beckons you over to him.

+ * + * Over here. Come here. Don't be alarmed, but I think one of the + * tavernkeepers used to be a wandering healer. Said something about + * the good ole days when people would just walk up and hand gold to + * them. Piles of gold. Out of the blue. Can you imagine? Got so much + * gold, this ex-healer decided to buy a tavern and settle down. Kind + * of sad really. Can't even cure my hangover now. Nice tavern though.

+ * + * Right. Anyway. Let me know when those toy figurines are ready. + * I'll be here. As always. + */ + return 1055130; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringToys2Conversation() + { + } + } + + public class ElwoodDuringToys3Conversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood is singing as you greet him.

+ * + * Come. Join in. It's a Gabriel Piete song. I have the sheet music + * for it. It's even autographed by Gabriel Piete himself. Yes. + * One of his songs. Brilliant.

+ * + * So let me see the toys. Figurines. Let's see them. Oh. You don't + * have them yet. I see. Well. Okay. That's too bad. + */ + return 1055133; + } + } + + public override bool Logged{ get{ return false; } } + + public ElwoodDuringToys3Conversation() + { + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* Elwood takes a sip of his ale as you address him.

+ * + * Ah. That's the stuff. Ale. Nothing better. What's that? Toy figurines + * will be delivered. Right. Yes. Perhaps better than Ale. Tough one.

+ * + * Very good work. Quite. Think we're all done now. You completed everything + * I asked. Would work with you again. Yes. We should.

+ * + * Ah. Yes. your payment. The usual. I think you'll be pleased.

+ * + * With that Elwood rummages around in his backpack. He eventually + * pulls out a small bag and hands it to you. + */ + return 1055134; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class FullEndConversation : QuestConversation + { + private bool m_Logged; + + public override object Message + { + get + { + /* Elwood stares at you as you approach.

+ * + * I know you. Oh yes. You've been running some errands for me. + * We are about done. Noticed that your backpack is a bit full. + * Might want to make some room. Won't be able to hold your payment. + * Come back when you have more room, and we'll conclude our business. + */ + return 1055135; + } + } + + public override bool Logged{ get{ return m_Logged; } } + + public FullEndConversation( bool logged ) + { + m_Logged = logged; + } + + public FullEndConversation() + { + m_Logged = true; + } + + public override void OnRead() + { + if ( m_Logged ) + System.AddObjective( new MakeRoomObjective() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Items/EnchantedPaints.cs b/Scripts/Engines/Quests/Collector/Items/EnchantedPaints.cs new file mode 100644 index 000000000..bffd399f6 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Items/EnchantedPaints.cs @@ -0,0 +1,141 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Collector +{ + public class EnchantedPaints : QuestItem + { + [Constructable] + public EnchantedPaints() : base( 0xFC1 ) + { + LootType = LootType.Blessed; + + Weight = 1.0; + } + + public EnchantedPaints( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + CollectorQuest qs = player.Quest as CollectorQuest; + + if ( qs == null ) + return true; + + /*return !( qs.IsObjectiveInProgress( typeof( CaptureImagesObjective ) ) + || qs.IsObjectiveInProgress( typeof( ReturnImagesObjective ) ) );*/ + return false; + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( CaptureImagesObjective ) ) ) + { + player.SendAsciiMessage( 0x59, "Target the creature whose image you wish to create." ); + player.Target = new InternalTarget( this ); + + return; + } + } + } + + from.SendLocalizedMessage( 1010085 ); // You cannot use this. + } + + private class InternalTarget : Target + { + private EnchantedPaints m_Paints; + + public InternalTarget( EnchantedPaints paints ) : base( -1, false, TargetFlags.None ) + { + CheckLOS = false; + m_Paints = paints; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Paints.Deleted || !m_Paints.IsChildOf( from.Backpack ) ) + return; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + CaptureImagesObjective obj = qs.FindObjective( typeof( CaptureImagesObjective ) ) as CaptureImagesObjective; + + if ( obj != null && !obj.Completed ) + { + if ( targeted is Mobile ) + { + ImageType image; + CaptureResponse response = obj.CaptureImage( targeted.GetType(), out image ); + + switch ( response ) + { + case CaptureResponse.Valid: + { + player.SendLocalizedMessage( 1055125 ); // The enchanted paints swirl for a moment then an image begins to take shape. *Click* + player.AddToBackpack( new PaintedImage( image ) ); + + break; + } + case CaptureResponse.AlreadyDone: + { + player.SendAsciiMessage( 0x2C, "You have already captured the image of this creature" ); + + break; + } + case CaptureResponse.Invalid: + { + player.SendLocalizedMessage( 1055124 ); // You have no interest in capturing the image of this creature. + + break; + } + } + } + else + { + player.SendAsciiMessage( 0x35, "You have no interest in that." ); + } + + return; + } + } + } + + from.SendLocalizedMessage( 1010085 ); // You cannot use this. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Items/ImageType.cs b/Scripts/Engines/Quests/Collector/Items/ImageType.cs new file mode 100644 index 000000000..71d381350 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Items/ImageType.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Collector +{ + public enum ImageType + { + Betrayer, + Bogling, + BogThing, + Gazer, + Beetle, + GiantBlackWidow, + Scorpion, + JukaMage, + JukaWarrior, + Lich, + MeerMage, + MeerWarrior, + Mongbat, + Mummy, + Pixie, + PlagueBeast, + SandVortex, + StoneGargoyle, + SwampDragon, + Wisp, + Juggernaut + } + + public class ImageTypeInfo + { + private static readonly ImageTypeInfo[] m_Table = new ImageTypeInfo[] + { + new ImageTypeInfo( 9734, typeof( Betrayer ), 75, 45 ), + new ImageTypeInfo( 9735, typeof( Bogling ), 75, 45 ), + new ImageTypeInfo( 9736, typeof( BogThing ), 60, 47 ), + new ImageTypeInfo( 9615, typeof( Gazer ), 75, 45 ), + new ImageTypeInfo( 9743, typeof( Beetle ), 60, 55 ), + new ImageTypeInfo( 9667, typeof( GiantBlackWidow ), 55, 52 ), + new ImageTypeInfo( 9657, typeof( Scorpion ), 65, 47 ), + new ImageTypeInfo( 9758, typeof( JukaMage ), 75, 45 ), + new ImageTypeInfo( 9759, typeof( JukaWarrior ), 75, 45 ), + new ImageTypeInfo( 9636, typeof( Lich ), 75, 45 ), + new ImageTypeInfo( 9756, typeof( MeerMage ), 75, 45 ), + new ImageTypeInfo( 9757, typeof( MeerWarrior ), 75, 45 ), + new ImageTypeInfo( 9638, typeof( Mongbat ), 70, 50 ), + new ImageTypeInfo( 9639, typeof( Mummy ), 75, 45 ), + new ImageTypeInfo( 9654, typeof( Pixie ), 75, 45 ), + new ImageTypeInfo( 9747, typeof( PlagueBeast ), 60, 45 ), + new ImageTypeInfo( 9750, typeof( SandVortex ), 60, 43 ), + new ImageTypeInfo( 9614, typeof( StoneGargoyle ), 75, 45 ), + new ImageTypeInfo( 9753, typeof( SwampDragon ), 50, 55 ), + new ImageTypeInfo( 8448, typeof( Wisp ), 75, 45 ), + new ImageTypeInfo( 9746, typeof( Juggernaut ), 55, 38 ) + }; + + public static ImageTypeInfo Get( ImageType image ) + { + int index = (int)image; + if ( index >= 0 && index < m_Table.Length ) + return m_Table[index]; + else + return m_Table[0]; + } + + public static ImageType[] RandomList( int count ) + { + ArrayList list = new ArrayList( m_Table.Length ); + for ( int i = 0; i < m_Table.Length; i++ ) + list.Add( (ImageType)i ); + + ImageType[] images = new ImageType[count]; + + for ( int i = 0; i < images.Length; i++ ) + { + int index = Utility.Random( list.Count ); + images[i] = (ImageType)list[index]; + + list.RemoveAt( index ); + } + + return images; + } + + private int m_Figurine; + private Type m_Type; + private int m_X; + private int m_Y; + + public int Figurine { get { return m_Figurine; } } + public Type Type { get { return m_Type; } } + public int Name { get { return 1020000 + m_Figurine; } } + public int X { get { return m_X; } } + public int Y { get { return m_Y; } } + + public ImageTypeInfo( int figurine, Type type, int x, int y ) + { + m_Figurine = figurine; + m_Type = type; + m_X = x; + m_Y = y; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Items/Obsidian.cs b/Scripts/Engines/Quests/Collector/Items/Obsidian.cs new file mode 100644 index 000000000..0afd5e281 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Items/Obsidian.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Engines.Quests.Collector +{ + public class Obsidian : Item + { + private static readonly string[] m_Names = new string[] + { + null, + "an aggressive cavalier", + "a beguiling rogue", + "a benevolent physician", + "a brilliant artisan", + "a capricious adventurer", + "a clever beggar", + "a convincing charlatan", + "a creative inventor", + "a creative tinker", + "a cunning knave", + "a dauntless explorer", + "a despicable ruffian", + "an earnest malcontent", + "an exultant animal tamer", + "a famed adventurer", + "a fanatical crusader", + "a fastidious clerk", + "a fearless hunter", + "a festive harlequin", + "a fidgety assassin", + "a fierce soldier", + "a fierce warrior", + "a frugal magnate", + "a glib pundit", + "a gnomic shaman", + "a graceful noblewoman", + "a idiotic madman", + "a imaginative designer", + "an inept conjurer", + "an innovative architect", + "an inventive blacksmith", + "a judicious mayor", + "a masterful chef", + "a masterful woodworker", + "a melancholy clown", + "a melodic bard", + "a merciful guard", + "a mirthful jester", + "a nervous surgeon", + "a peaceful scholar", + "a prolific gardener", + "a quixotic knight", + "a regal aristocrat", + "a resourceful smith", + "a reticent alchemist", + "a sanctified priest", + "a scheming patrician", + "a shrewd mage", + "a singing minstrel", + "a skilled tailor", + "a squeamish assassin", + "a stoic swordsman", + "a studious scribe", + "a thought provoking writer", + "a treacherous scoundrel", + "a troubled poet", + "an unflappable wizard", + "a valiant warrior", + "a wayward fool" + }; + + public static string RandomName( Mobile from ) + { + int index = Utility.Random( m_Names.Length ); + if ( m_Names[index] == null ) + return from.Name; + else + return m_Names[index]; + } + + private const int m_Partial = 2; + private const int m_Completed = 10; + + private int m_Quantity; + private string m_StatueName; + + [CommandProperty( AccessLevel.GameMaster )] + public int Quantity + { + get { return m_Quantity; } + set + { + if ( value <= 1 ) + m_Quantity = 1; + else if ( value >= m_Completed ) + m_Quantity = m_Completed; + else + m_Quantity = value; + + if ( m_Quantity < m_Partial ) + ItemID = 0x1EA7; + else if ( m_Quantity < m_Completed ) + ItemID = 0x1F13; + else + ItemID = 0x12CB; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string StatueName + { + get { return m_StatueName; } + set { m_StatueName = value; InvalidateProperties(); } + } + + public override bool ForceShowProperties { get { return ObjectPropertyList.Enabled; } } + + [Constructable] + public Obsidian() : base( 0x1EA7 ) + { + Hue = 0x497; + + m_Quantity = 1; + m_StatueName = ""; + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( m_Quantity < m_Partial ) + list.Add( 1055137 ); // a section of an obsidian statue + else if ( m_Quantity < m_Completed ) + list.Add( 1055138 ); // a partially reconstructed obsidian statue + else + list.Add( 1055139, m_StatueName ); // an obsidian statue of ~1_STATUE_NAME~ + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Quantity < m_Partial ) + LabelTo( from, 1055137 ); // a section of an obsidian statue + else if ( m_Quantity < m_Completed ) + LabelTo( from, 1055138 ); // a partially reconstructed obsidian statue + else + LabelTo( from, 1055139, m_StatueName ); // an obsidian statue of ~1_STATUE_NAME~ + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive && m_Quantity >= m_Partial && m_Quantity < m_Completed && IsChildOf( from.Backpack ) ) + list.Add( new DisassembleEntry( this ) ); + } + + private class DisassembleEntry : ContextMenuEntry + { + private Obsidian m_Obsidian; + + public DisassembleEntry( Obsidian obsidian ) : base( 6142 ) + { + m_Obsidian = obsidian; + } + + public override void OnClick() + { + Mobile from = Owner.From; + if ( !m_Obsidian.Deleted && m_Obsidian.Quantity >= Obsidian.m_Partial && m_Obsidian.Quantity < Obsidian.m_Completed && m_Obsidian.IsChildOf( from.Backpack ) && from.CheckAlive() ) + { + for ( int i = 0; i < m_Obsidian.Quantity - 1; i++ ) + from.AddToBackpack( new Obsidian() ); + + m_Obsidian.Quantity = 1; + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Quantity < m_Completed ) + { + if ( !IsChildOf( from.Backpack ) ) + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x2C, 3, 500309, "", "" ) ); // Nothing Happens. + else + from.Target = new InternalTarget( this ); + } + } + + private class InternalTarget : Target + { + private Obsidian m_Obsidian; + + public InternalTarget( Obsidian obsidian ) : base( -1, false, TargetFlags.None ) + { + m_Obsidian = obsidian; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + Item targ = targeted as Item; + if ( m_Obsidian.Deleted || m_Obsidian.Quantity >= Obsidian.m_Completed || targ == null ) + return; + + if ( m_Obsidian.IsChildOf( from.Backpack ) && targ.IsChildOf( from.Backpack ) && targ is Obsidian && targ != m_Obsidian ) + { + Obsidian targObsidian = (Obsidian)targ; + if ( targObsidian.Quantity < Obsidian.m_Completed ) + { + if ( targObsidian.Quantity + m_Obsidian.Quantity <= Obsidian.m_Completed ) + { + targObsidian.Quantity += m_Obsidian.Quantity; + m_Obsidian.Delete(); + } + else + { + int delta = Obsidian.m_Completed - targObsidian.Quantity; + targObsidian.Quantity += delta; + m_Obsidian.Quantity -= delta; + } + + if ( targObsidian.Quantity >= Obsidian.m_Completed ) + targObsidian.StatueName = Obsidian.RandomName( from ); + + from.Send( new AsciiMessage( targObsidian.Serial, targObsidian.ItemID, MessageType.Regular, 0x59, 3, m_Obsidian.Name, "Something Happened." ) ); + + return; + } + } + + from.Send( new MessageLocalized( m_Obsidian.Serial, m_Obsidian.ItemID, MessageType.Regular, 0x2C, 3, 500309, m_Obsidian.Name, "" ) ); // Nothing Happens. + } + } + + public Obsidian( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteEncodedInt( m_Quantity ); + writer.Write( (string) m_StatueName ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Quantity = reader.ReadEncodedInt(); + m_StatueName = Utility.Intern( reader.ReadString() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Items/PaintedImage.cs b/Scripts/Engines/Quests/Collector/Items/PaintedImage.cs new file mode 100644 index 000000000..5216b99d2 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Items/PaintedImage.cs @@ -0,0 +1,86 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.Quests.Collector +{ + public class PaintedImage : Item + { + private ImageType m_Image; + + [CommandProperty( AccessLevel.GameMaster )] + public ImageType Image + { + get { return m_Image; } + set { m_Image = value; InvalidateProperties(); } + } + + [Constructable] + public PaintedImage( ImageType image ) : base( 0xFF3 ) + { + Weight = 1.0; + Hue = 0x8FD; + + m_Image = image; + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + ImageTypeInfo info = ImageTypeInfo.Get( m_Image ); + list.Add( 1060847, "#1055126\t#" + info.Name ); // a painted image of: + } + + public override void OnSingleClick( Mobile from ) + { + ImageTypeInfo info = ImageTypeInfo.Get( m_Image ); + LabelTo( from, 1060847, "#1055126\t#" + info.Name ); // a painted image of: + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + from.SendGump( new InternalGump( m_Image ) ); + } + + private class InternalGump : Gump + { + public InternalGump( ImageType image ) : base( 75, 25 ) + { + ImageTypeInfo info = ImageTypeInfo.Get( image ); + + AddBackground( 45, 20, 100, 100, 0xA3C ); + AddBackground( 52, 29, 86, 82, 0xBB8 ); + + AddItem( info.X, info.Y, info.Figurine ); + } + } + + public PaintedImage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Image ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Image = (ImageType) reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Mobiles/AlbertaGiacco.cs b/Scripts/Engines/Quests/Collector/Mobiles/AlbertaGiacco.cs new file mode 100644 index 000000000..da14c012b --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Mobiles/AlbertaGiacco.cs @@ -0,0 +1,94 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Collector +{ + public class AlbertaGiacco : BaseQuester + { + [Constructable] + public AlbertaGiacco() : base( "the respected painter" ) + { + } + + public AlbertaGiacco( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83F2; + + Female = true; + Body = 0x191; + Name = "Alberta Giacco"; + } + + public override void InitOutfit() + { + AddItem( new FancyShirt() ); + AddItem( new Skirt( 0x59B ) ); + AddItem( new Boots() ); + AddItem( new FeatheredHat( 0x59B ) ); + AddItem( new FullApron( 0x59B ) ); + + HairItemID = 0x203D; // Pony Tail + HairHue = 0x457; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + QuestSystem qs = to.Quest as CollectorQuest; + + if ( qs == null ) + return false; + + return ( qs.IsObjectiveInProgress( typeof( FindAlbertaObjective ) ) + || qs.IsObjectiveInProgress( typeof( SitOnTheStoolObjective ) ) + || qs.IsObjectiveInProgress( typeof( ReturnPaintingObjective ) ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + Direction = GetDirectionTo( player ); + + QuestObjective obj = qs.FindObjective( typeof( FindAlbertaObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( SitOnTheStoolObjective ) ) ) + { + qs.AddConversation( new AlbertaStoolConversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( ReturnPaintingObjective ) ) ) + { + qs.AddConversation( new AlbertaAfterPaintingConversation() ); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Mobiles/ElwoodMcCarrin.cs b/Scripts/Engines/Quests/Collector/Mobiles/ElwoodMcCarrin.cs new file mode 100644 index 000000000..6d5bde82c --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Mobiles/ElwoodMcCarrin.cs @@ -0,0 +1,243 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Collector +{ + public class ElwoodMcCarrin : BaseQuester + { + [Constructable] + public ElwoodMcCarrin() : base( "the well-known collector" ) + { + } + + public ElwoodMcCarrin( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83ED; + + Female = false; + Body = 0x190; + Name = "Elwood McCarrin"; + } + + public override void InitOutfit() + { + AddItem( new FancyShirt() ); + AddItem( new LongPants( 0x544 ) ); + AddItem( new Shoes( 0x454 ) ); + AddItem( new JesterHat( 0x4D2 ) ); + AddItem( new FullApron( 0x4D2 ) ); + + HairItemID = 0x203D; // Pony Tail + HairHue = 0x47D; + + FacialHairItemID = 0x2040; // Goatee + FacialHairHue = 0x47D; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + Direction = GetDirectionTo( player ); + + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FishPearlsObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringFishConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( ReturnPearlsObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( FindAlbertaObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringPainting1Conversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( SitOnTheStoolObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringPainting2Conversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnPaintingObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( FindGabrielObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringAutograph1Conversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( FindSheetMusicObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringAutograph2Conversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( ReturnSheetMusicObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringAutograph3Conversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnAutographObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( FindTomasObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringToys1Conversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( CaptureImagesObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringToys2Conversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( ReturnImagesObjective ) ) ) + { + qs.AddConversation( new ElwoodDuringToys3Conversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnToysObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + + if ( GiveReward( player ) ) + { + qs.AddConversation( new EndConversation() ); + } + else + { + qs.AddConversation( new FullEndConversation( true ) ); + } + } + else + { + obj = qs.FindObjective( typeof( MakeRoomObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( GiveReward( player ) ) + { + obj.Complete(); + qs.AddConversation( new EndConversation() ); + } + else + { + qs.AddConversation( new FullEndConversation( false ) ); + } + } + } + } + } + } + } + } + else + { + QuestSystem newQuest = new CollectorQuest( player ); + + if ( qs == null && QuestSystem.CanOfferQuest( player, typeof( CollectorQuest ) ) ) + { + newQuest.SendOffer(); + } + else + { + newQuest.AddConversation( new DontOfferConversation() ); + } + } + } + + public bool GiveReward( Mobile to ) + { + Bag bag = new Bag(); + + bag.DropItem( new Gold( Utility.RandomMinMax( 500, 1000 ) ) ); + + if ( Utility.RandomBool() ) + { + BaseWeapon weapon = Loot.RandomWeapon(); + + if ( Core.AOS ) + { + BaseRunicTool.ApplyAttributesTo( weapon, 2, 20, 30 ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + } + + bag.DropItem( weapon ); + } + else + { + Item item; + + if ( Core.AOS ) + { + item = Loot.RandomArmorOrShieldOrJewelry(); + + if ( item is BaseArmor ) + BaseRunicTool.ApplyAttributesTo( (BaseArmor)item, 2, 20, 30 ); + else if ( item is BaseJewel ) + BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, 2, 20, 30 ); + } + else + { + BaseArmor armor = Loot.RandomArmorOrShield(); + item = armor; + + armor.ProtectionLevel = (ArmorProtectionLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + armor.Durability = (ArmorDurabilityLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + } + + bag.DropItem( item ); + } + + bag.DropItem( new Obsidian() ); + + if ( to.PlaceInBackpack( bag ) ) + { + return true; + } + else + { + bag.Delete(); + return false; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Mobiles/GabrielPiete.cs b/Scripts/Engines/Quests/Collector/Mobiles/GabrielPiete.cs new file mode 100644 index 000000000..64fe8330b --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Mobiles/GabrielPiete.cs @@ -0,0 +1,105 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Collector +{ + public class GabrielPiete : BaseQuester + { + [Constructable] + public GabrielPiete() : base( "the renowned minstrel" ) + { + } + + public GabrielPiete( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83EF; + + Female = false; + Body = 0x190; + Name = "Gabriel Piete"; + } + + public override void InitOutfit() + { + AddItem( new FancyShirt() ); + AddItem( new LongPants( 0x5F7 ) ); + AddItem( new Shoes( 0x5F7 ) ); + + HairItemID = 0x2049; // Pig Tails + HairHue = 0x460; + + FacialHairItemID = 0x2041; // Mustache + FacialHairHue = 0x460; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + QuestSystem qs = to.Quest as CollectorQuest; + + if ( qs == null ) + return false; + + return ( qs.IsObjectiveInProgress( typeof( FindGabrielObjective ) ) + || qs.IsObjectiveInProgress( typeof( FindSheetMusicObjective ) ) + || qs.IsObjectiveInProgress( typeof( ReturnSheetMusicObjective ) ) + || qs.IsObjectiveInProgress( typeof( ReturnAutographObjective ) ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + Direction = GetDirectionTo( player ); + + QuestObjective obj = qs.FindObjective( typeof( FindGabrielObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( FindSheetMusicObjective ) ) ) + { + qs.AddConversation( new GabrielNoSheetMusicConversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnSheetMusicObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( ReturnAutographObjective ) ) ) + { + qs.AddConversation( new GabrielIgnoreConversation() ); + } + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Mobiles/Impresario.cs b/Scripts/Engines/Quests/Collector/Mobiles/Impresario.cs new file mode 100644 index 000000000..026808690 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Mobiles/Impresario.cs @@ -0,0 +1,164 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Gumps; +using Server.Network; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Collector +{ + public class Impresario : BaseQuester + { + [Constructable] + public Impresario() : base( "the impresario" ) + { + } + + public Impresario( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = Utility.RandomSkinHue(); + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + public override void InitOutfit() + { + AddItem( new FancyShirt( Utility.RandomDyedHue() ) ); + AddItem( new LongPants( Utility.RandomNondyedHue() ) ); + AddItem( new Shoes( Utility.RandomNeutralHue() ) ); + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this ); + } + + public override bool CanTalkTo( PlayerMobile to ) + { + QuestSystem qs = to.Quest as CollectorQuest; + + if ( qs == null ) + return false; + + return qs.IsObjectiveInProgress( typeof( FindSheetMusicObjective ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + FindSheetMusicObjective obj = qs.FindObjective( typeof( FindSheetMusicObjective ) ) as FindSheetMusicObjective; + + if ( obj != null && !obj.Completed ) + { + Direction = GetDirectionTo( player ); + + if ( obj.IsInRightTheater() ) + { + player.CloseGump( typeof( SheetMusicOfferGump ) ); + player.SendGump( new SheetMusicOfferGump() ); + } + else + { + qs.AddConversation( new NoSheetMusicConversation() ); + } + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SheetMusicOfferGump : BaseQuestGump + { + public SheetMusicOfferGump() : base( 75, 25 ) + { + Closable = false; + + AddImage( 349, 10, 0x24B0 ); + AddImageTiled( 349, 130, 100, 120, 0x24B3 ); + AddImageTiled( 149, 10, 200, 140, 0x24AF ); + AddImageTiled( 149, 300, 200, 140, 0x24B5 ); + AddImage( 349, 300, 0x24B6 ); + AddImage( 35, 10, 0x24AE ); + AddImageTiled( 35, 150, 120, 100, 0x24B1 ); + AddImage( 35, 300, 0x24B4 ); + + AddHtmlLocalized( 110, 60, 200, 20, 1049069, White, false, false ); // Conversation Event + + AddImage( 65, 14, 0x2776 ); + AddImageTiled( 81, 14, 349, 17, 0x2775 ); + AddImage( 426, 14, 0x2778 ); + + AddImageTiled( 50, 37, 400, 376, 0xA40 ); + AddAlphaRegion( 50, 37, 400, 376 ); + + AddImage( 0, 0, 0x28C8 ); + + AddImageTiled( 75, 90, 200, 1, 0x238D ); + AddImage( 75, 58, 0x2635 ); + AddImage( 380, 45, 0xDF ); + + AddHtmlLocalized( 98, 140, 312, 200, 1055107, LightGreen, false, true ); // Sure, I have some sheet music for a Gabriel Piete song. I'd be happy to sell you a copy for 10 gold. + + AddRadio( 85, 350, 0x25F8, 0x25FB, true, 1 ); + AddHtmlLocalized( 120, 356, 280, 20, 1014088, White, false, false ); // I accept. + + AddRadio( 85, 385, 0x25F8, 0x25FB, false, 0 ); + AddHtmlLocalized( 120, 391, 280, 20, 1049012, White, false, false ); // No thanks, I decline. + + AddButton( 340, 390, 0xF7, 0xF8, 1, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 && info.IsSwitched( 1 ) ) + { + PlayerMobile player = sender.Mobile as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + FindSheetMusicObjective obj = qs.FindObjective( typeof( FindSheetMusicObjective ) ) as FindSheetMusicObjective; + + if ( obj != null && !obj.Completed ) + { + if ( player.BankBox != null && player.BankBox.ConsumeTotal( typeof( Gold ), 10 ) ) + { + obj.Complete(); + } + else + { + player.SendLocalizedMessage( 1055108 ); // You don't have enough gold to buy the sheet music. + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Mobiles/TomasONeerlan.cs b/Scripts/Engines/Quests/Collector/Mobiles/TomasONeerlan.cs new file mode 100644 index 000000000..ed5959a17 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Mobiles/TomasONeerlan.cs @@ -0,0 +1,111 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Collector +{ + public class TomasONeerlan : BaseQuester + { + [Constructable] + public TomasONeerlan() : base( "the famed toymaker" ) + { + } + + public TomasONeerlan( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83F8; + + Female = false; + Body = 0x190; + Name = "Tomas O'Neerlan"; + } + + public override void InitOutfit() + { + AddItem( new FancyShirt() ); + AddItem( new LongPants( 0x546 ) ); + AddItem( new Boots( 0x452 ) ); + AddItem( new FullApron( 0x455 ) ); + + HairItemID = 0x203B; //ShortHair + HairHue = 0x455; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + QuestSystem qs = to.Quest as CollectorQuest; + + if ( qs == null ) + return false; + + return ( qs.IsObjectiveInProgress( typeof( FindTomasObjective ) ) + || qs.IsObjectiveInProgress( typeof( CaptureImagesObjective ) ) + || qs.IsObjectiveInProgress( typeof( ReturnImagesObjective ) ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is CollectorQuest ) + { + Direction = GetDirectionTo( player ); + + QuestObjective obj = qs.FindObjective( typeof( FindTomasObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item paints = new EnchantedPaints(); + + if ( !player.PlaceInBackpack( paints ) ) + { + paints.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + else + { + obj.Complete(); + } + } + else if ( qs.IsObjectiveInProgress( typeof( CaptureImagesObjective ) ) ) + { + qs.AddConversation( new TomasDuringCollectingConversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnImagesObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( player.Backpack != null ) + player.Backpack.ConsumeUpTo( typeof( EnchantedPaints ), 1 ); + + obj.Complete(); + } + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Collector/Objectives.cs b/Scripts/Engines/Quests/Collector/Objectives.cs new file mode 100644 index 000000000..431a42948 --- /dev/null +++ b/Scripts/Engines/Quests/Collector/Objectives.cs @@ -0,0 +1,533 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Collector +{ + public class FishPearlsObjective : QuestObjective + { + public override object Message + { + get + { + // Fish up shellfish from Lake Haven and collect rainbow pearls. + return 1055084; + } + } + + public override int MaxProgress{ get{ return 6; } } + + public FishPearlsObjective() + { + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Rainbow pearls collected: + gump.AddHtmlObject( 70, 260, 270, 100, 1055085, BaseQuestGump.Blue, false, false ); + + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, MaxProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnPearlsObjective() ); + } + } + + public class ReturnPearlsObjective : QuestObjective + { + public override object Message + { + get + { + /* You've collected enough rainbow pearls. Speak to + * Elwood to give them to him and get your next task. + */ + return 1055088; + } + } + + public ReturnPearlsObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ReturnPearlsConversation() ); + } + } + + public class FindAlbertaObjective : QuestObjective + { + public override object Message + { + get + { + // Go to Vesper and speak to Alberta Giacco at the Colored Canvas. + return 1055091; + } + } + + public FindAlbertaObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new AlbertaPaintingConversation() ); + } + } + + public class SitOnTheStoolObjective : QuestObjective + { + private static readonly Point3D m_StoolLocation = new Point3D( 2899, 706, 0 ); + private static readonly Map m_StoolMap = Map.Trammel; + + private DateTime m_Begin; + + public override object Message + { + get + { + /* Sit on the stool in front of Alberta's easel so that she can + * paint your portrait. You'll need to sit there for about 30 seconds. + */ + return 1055093; + } + } + + public SitOnTheStoolObjective() + { + m_Begin = DateTime.MaxValue; + } + + public override void CheckProgress() + { + PlayerMobile pm = System.From; + + if ( pm.Map == m_StoolMap && pm.Location == m_StoolLocation ) + { + if ( m_Begin == DateTime.MaxValue ) + { + m_Begin = DateTime.Now; + } + else if ( DateTime.Now - m_Begin > TimeSpan.FromSeconds( 30.0 ) ) + { + Complete(); + } + } + else if ( m_Begin != DateTime.MaxValue ) + { + m_Begin = DateTime.MaxValue; + pm.SendLocalizedMessage( 1055095, "", 0x26 ); // You must remain seated on the stool until the portrait is complete. Alberta will now have to start again with a fresh canvas. + } + } + + public override void OnComplete() + { + System.AddConversation( new AlbertaEndPaintingConversation() ); + } + } + + public class ReturnPaintingObjective : QuestObjective + { + public override object Message + { + get + { + // Return to Elwood and let him know that the painting is complete. + return 1055099; + } + } + + public ReturnPaintingObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ReturnPaintingConversation() ); + } + } + + public class FindGabrielObjective : QuestObjective + { + public override object Message + { + get + { + /* Go to Britain and obtain the autograph of renowned + * minstrel, Gabriel Piete. He is often found at the Conservatory of Music. + */ + return 1055101; + } + } + + public FindGabrielObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new GabrielAutographConversation() ); + } + } + + public enum Theater + { + Britain, + Nujelm, + Jhelom + } + + public class FindSheetMusicObjective : QuestObjective + { + private Theater m_Theater; + + public override object Message + { + get + { + /* Find some sheet music for one of Gabriel's songs. + * Try speaking to an impresario from one of the theaters in the land. + */ + return 1055104; + } + } + + public FindSheetMusicObjective( bool init ) + { + if ( init ) + InitTheater(); + } + + public FindSheetMusicObjective() + { + } + + public void InitTheater() + { + switch ( Utility.Random( 3 ) ) + { + case 1: m_Theater = Theater.Britain; break; + case 2: m_Theater = Theater.Nujelm; break; + default: m_Theater = Theater.Jhelom; break; + } + } + + public bool IsInRightTheater() + { + PlayerMobile player = System.From; + + Region region = Region.Find( player.Location, player.Map ); + + if ( region == null ) + return false; + + switch ( m_Theater ) + { + case Theater.Britain: return region.IsPartOf( "Britain" ); + case Theater.Nujelm: return region.IsPartOf( "Nujel'm" ); + case Theater.Jhelom: return region.IsPartOf( "Jhelom" ); + + default: return false; + } + } + + public override void OnComplete() + { + System.AddConversation( new GetSheetMusicConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Theater = (Theater) reader.ReadEncodedInt(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Theater ); + } + } + + public class ReturnSheetMusicObjective : QuestObjective + { + public override object Message + { + get + { + // Speak to Gabriel to have him autograph the sheet music. + return 1055110; + } + } + + public ReturnSheetMusicObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new GabrielSheetMusicConversation() ); + } + } + + public class ReturnAutographObjective : QuestObjective + { + public override object Message + { + get + { + // Speak to Elwood to give him the autographed sheet music. + return 1055114; + } + } + + public ReturnAutographObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ReturnAutographConversation() ); + } + } + + public class FindTomasObjective : QuestObjective + { + public override object Message + { + get + { + // Go to Trinsic and speak to Tomas O'Neerlan, the famous toymaker. + return 1055117; + } + } + + public FindTomasObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new TomasToysConversation() ); + } + } + + public enum CaptureResponse + { + Valid, + AlreadyDone, + Invalid + } + + public class CaptureImagesObjective : QuestObjective + { + private ImageType[] m_Images; + private bool[] m_Done; + + public override object Message + { + get + { + // Use the enchanted paints to capture the image of all of the creatures listed below. + return 1055120; + } + } + + public override bool Completed + { + get + { + for ( int i = 0; i < m_Done.Length; i++ ) + { + if ( !m_Done[i] ) + return false; + } + + return true; + } + } + + public CaptureImagesObjective( bool init ) + { + if ( init ) + { + m_Images = ImageTypeInfo.RandomList( 4 ); + m_Done = new bool[4]; + } + } + + public CaptureImagesObjective() + { + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + if ( Completed ) + return false; + + Type fromType = from.GetType(); + + for ( int i = 0; i < m_Images.Length; i++ ) + { + ImageTypeInfo info = ImageTypeInfo.Get( m_Images[i] ); + + if ( info.Type == fromType ) + return true; + } + + return false; + } + + public CaptureResponse CaptureImage( Type type, out ImageType image ) + { + for ( int i = 0; i < m_Images.Length; i++ ) + { + ImageTypeInfo info = ImageTypeInfo.Get( m_Images[i] ); + + if ( info.Type == type ) + { + image = m_Images[i]; + + if ( m_Done[i] ) + { + return CaptureResponse.AlreadyDone; + } + else + { + m_Done[i] = true; + + CheckCompletionStatus(); + + return CaptureResponse.Valid; + } + } + } + + image = (ImageType)0; + return CaptureResponse.Invalid; + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + for ( int i = 0; i < m_Images.Length; i++ ) + { + ImageTypeInfo info = ImageTypeInfo.Get( m_Images[i] ); + + gump.AddHtmlObject( 70, 260 + 20 * i, 200, 100, info.Name, BaseQuestGump.Blue, false, false ); + gump.AddLabel( 200, 260 + 20 * i, 0x64, " : " ); + gump.AddHtmlObject( 220, 260 + 20 * i, 100, 100, m_Done[i] ? 1055121 : 1055122, BaseQuestGump.Blue, false, false ); + } + } + else + { + base.RenderProgress( gump ); + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnImagesObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + int count = reader.ReadEncodedInt(); + + m_Images = new ImageType[count]; + m_Done = new bool[count]; + + for ( int i = 0; i < count; i++ ) + { + m_Images[i] = (ImageType) reader.ReadEncodedInt(); + m_Done[i] = reader.ReadBool(); + } + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Images.Length ); + + for ( int i = 0; i < m_Images.Length; i++ ) + { + writer.WriteEncodedInt( (int) m_Images[i] ); + writer.Write( (bool) m_Done[i] ); + } + } + } + + public class ReturnImagesObjective : QuestObjective + { + public override object Message + { + get + { + /* You now have all of the creature images you need. + * Return to Tomas O'Neerlan so that he can make the toy figurines. + */ + return 1055128; + } + } + + public ReturnImagesObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ReturnImagesConversation() ); + } + } + + public class ReturnToysObjective : QuestObjective + { + public override object Message + { + get + { + // Return to Elwood with news that the toy figurines will be delivered when ready. + return 1055132; + } + } + + public ReturnToysObjective() + { + } + } + + public class MakeRoomObjective : QuestObjective + { + public override object Message + { + get + { + // Return to Elwood for your reward when you have some room in your backpack. + return 1055136; + } + } + + public MakeRoomObjective() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/BaseQuester.cs b/Scripts/Engines/Quests/Core/BaseQuester.cs new file mode 100644 index 000000000..be45c7448 --- /dev/null +++ b/Scripts/Engines/Quests/Core/BaseQuester.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.ContextMenus; + +namespace Server.Engines.Quests +{ + public class TalkEntry : ContextMenuEntry + { + private BaseQuester m_Quester; + + public TalkEntry( BaseQuester quester ) : base( quester.TalkNumber ) + { + m_Quester = quester; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( from.CheckAlive() && from is PlayerMobile && m_Quester.CanTalkTo( (PlayerMobile)from ) ) + m_Quester.OnTalk( (PlayerMobile)from, true ); + } + } + + public abstract class BaseQuester : BaseVendor + { + protected ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override bool IsActiveVendor{ get{ return false; } } + public override bool IsInvulnerable{ get{ return true; } } + public override bool DisallowAllMoves{ get{ return true; } } + public override bool ClickTitle{ get { return false; } } + public override bool CanTeach{ get{ return false; } } + + public virtual int TalkNumber{ get{ return 6146; } } // Talk + + public override void InitSBInfo() + { + } + + public BaseQuester() : this( null ) + { + } + + public BaseQuester( string title ) : base( title ) + { + } + + public BaseQuester( Serial serial ) : base( serial ) + { + } + + public abstract void OnTalk( PlayerMobile player, bool contextMenu ); + + public virtual bool CanTalkTo( PlayerMobile to ) + { + return true; + } + + public virtual int GetAutoTalkRange( PlayerMobile m ) + { + return -1; + } + + public override bool CanBeDamaged() + { + return false; + } + + protected Item SetHue( Item item, int hue ) + { + item.Hue = hue; + return item; + } + + public override void AddCustomContextEntries( Mobile from, List list ) + { + base.AddCustomContextEntries( from, list ); + + if ( from.Alive && from is PlayerMobile && TalkNumber > 0 && CanTalkTo( (PlayerMobile)from ) ) + list.Add( new TalkEntry( this ) ); + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m.Alive && m is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m; + + int range = GetAutoTalkRange( pm ); + + if ( m.Alive && range >= 0 && InRange( m, range ) && !InRange( oldLocation, range ) && CanTalkTo( pm ) ) + OnTalk( pm, false ); + } + } + + public void FocusTo( Mobile to ) + { + QuestSystem.FocusTo( this, to ); + } + + public static Container GetNewContainer() + { + Bag bag = new Bag(); + bag.Hue = QuestSystem.RandomBrightHue(); + return bag; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Items/DynamicTeleporter.cs b/Scripts/Engines/Quests/Core/Items/DynamicTeleporter.cs new file mode 100644 index 000000000..9cab9f30f --- /dev/null +++ b/Scripts/Engines/Quests/Core/Items/DynamicTeleporter.cs @@ -0,0 +1,70 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests +{ + public abstract class DynamicTeleporter : Item + { + public override int LabelNumber{ get{ return 1049382; } } // a magical teleporter + + public DynamicTeleporter() : this( 0x1822, 0x482 ) + { + } + + public DynamicTeleporter( int itemID, int hue ) : base( itemID ) + { + Movable = false; + Hue = hue; + } + + public abstract bool GetDestination( PlayerMobile player, ref Point3D loc, ref Map map ); + + public virtual int NotWorkingMessage{ get{ return 500309; } } // Nothing Happens. + + public override bool OnMoveOver( Mobile m ) + { + PlayerMobile pm = m as PlayerMobile; + + if ( pm != null ) + { + Point3D loc = Point3D.Zero; + Map map = null; + + if ( GetDestination( pm, ref loc, ref map ) ) + { + BaseCreature.TeleportPets( pm, loc, map ); + + pm.PlaySound( 0x1FE ); + pm.MoveToWorld( loc, map ); + + return false; + } + else + { + pm.SendLocalizedMessage( this.NotWorkingMessage ); + } + } + + return base.OnMoveOver( m ); + } + + public DynamicTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Items/EnchantedSextant.cs b/Scripts/Engines/Quests/Core/Items/EnchantedSextant.cs new file mode 100644 index 000000000..a22a80ca9 --- /dev/null +++ b/Scripts/Engines/Quests/Core/Items/EnchantedSextant.cs @@ -0,0 +1,192 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class EnchantedSextant : Item + { + private static readonly Point2D[] m_TrammelBanks = new Point2D[] + { + new Point2D( 652, 820 ), + new Point2D( 1813, 2825 ), + new Point2D( 3734, 2149 ), + new Point2D( 2503, 552 ), + new Point2D( 3764, 1317 ), + new Point2D( 587, 2146 ), + new Point2D( 1655, 1606 ), + new Point2D( 1425, 1690 ), + new Point2D( 4471, 1156 ), + new Point2D( 1317, 3773 ), + new Point2D( 2881, 684 ), + new Point2D( 2731, 2192 ), + new Point2D( 3620, 2617 ), + new Point2D( 2880, 3472 ), + new Point2D( 1897, 2684 ), + new Point2D( 5346, 74 ), + new Point2D( 5275, 3977 ), + new Point2D( 5669, 3131 ) + }; + + private static readonly Point2D[] m_FeluccaBanks = new Point2D[] + { + new Point2D( 652, 820 ), + new Point2D( 1813, 2825 ), + new Point2D( 3734, 2149 ), + new Point2D( 2503, 552 ), + new Point2D( 3764, 1317 ), + new Point2D( 3695, 2511 ), + new Point2D( 587, 2146 ), + new Point2D( 1655, 1606 ), + new Point2D( 1425, 1690 ), + new Point2D( 4471, 1156 ), + new Point2D( 1317, 3773 ), + new Point2D( 2881, 684 ), + new Point2D( 2731, 2192 ), + new Point2D( 2880, 3472 ), + new Point2D( 1897, 2684 ), + new Point2D( 5346, 74 ), + new Point2D( 5275, 3977 ), + new Point2D( 5669, 3131 ) + }; + + private static readonly Point2D[] m_IlshenarBanks = new Point2D[] + { + new Point2D( 854, 680 ), + new Point2D( 855, 603 ), + new Point2D( 1226, 554 ), + new Point2D( 1610, 556 ) + }; + + private static readonly Point2D[] m_MalasBanks = new Point2D[] + { + new Point2D( 996, 519 ), + new Point2D( 2048, 1345 ) + }; + + private const double m_LongDistance = 300.0; + private const double m_ShortDistance = 5.0; + + public override int LabelNumber { get { return 1046226; } } // an enchanted sextant + + [Constructable] + public EnchantedSextant() : base( 0x1058 ) + { + Weight = 2.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + Point2D[] banks; + PMList moongates; + if ( from.Map == Map.Trammel ) + { + banks = m_TrammelBanks; + moongates = PMList.Trammel; + } + else if ( from.Map == Map.Felucca ) + { + banks = m_FeluccaBanks; + moongates = PMList.Felucca; + } + else if ( from.Map == Map.Ilshenar ) + { +#if false + banks = m_IlshenarBanks; + moongates = PMList.Ilshenar; +#else + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, 0x482, 3, 1061684, "", "" ) ); // The magic of the sextant fails... + return; +#endif + } + else if ( from.Map == Map.Malas ) + { + banks = m_MalasBanks; + moongates = PMList.Malas; + } + else + { + banks = null; + moongates = null; + } + + Point3D closestMoongate = Point3D.Zero; + double moongateDistance = double.MaxValue; + if ( moongates != null ) + { + foreach ( PMEntry entry in moongates.Entries ) + { + double dist = from.GetDistanceToSqrt( entry.Location ); + if ( moongateDistance > dist ) + { + closestMoongate = entry.Location; + moongateDistance = dist; + } + } + } + + Point2D closestBank = Point2D.Zero; + double bankDistance = double.MaxValue; + if ( banks != null ) + { + foreach ( Point2D p in banks ) + { + double dist = from.GetDistanceToSqrt( p ); + if ( bankDistance > dist ) + { + closestBank = p; + bankDistance = dist; + } + } + } + + int moonMsg; + if ( moongateDistance == double.MaxValue ) + moonMsg = 1048021; // The sextant fails to find a Moongate nearby. + else if ( moongateDistance > m_LongDistance ) + moonMsg = 1046449 + (int)from.GetDirectionTo( closestMoongate ); // A moongate is * from here + else if ( moongateDistance > m_ShortDistance ) + moonMsg = 1048010 + (int)from.GetDirectionTo( closestMoongate ); // There is a Moongate * of here. + else + moonMsg = 1048018; // You are next to a Moongate at the moment. + + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, 0x482, 3, moonMsg, "", "" ) ); + + int bankMsg; + if ( bankDistance == double.MaxValue ) + bankMsg = 1048020; // The sextant fails to find a Bank nearby. + else if ( bankDistance > m_LongDistance ) + bankMsg = 1046462 + (int)from.GetDirectionTo( closestBank ); // A town is * from here + else if ( bankDistance > m_ShortDistance ) + bankMsg = 1048002 + (int)from.GetDirectionTo( closestBank ); // There is a city Bank * of here. + else + bankMsg = 1048019; // You are next to a Bank at the moment. + + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, 0x5AA, 3, bankMsg, "", "" ) ); + } + + public EnchantedSextant( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Items/HornOfRetreat.cs b/Scripts/Engines/Quests/Core/Items/HornOfRetreat.cs new file mode 100644 index 000000000..f1bbc87de --- /dev/null +++ b/Scripts/Engines/Quests/Core/Items/HornOfRetreat.cs @@ -0,0 +1,202 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests +{ + public class HornOfRetreat : Item + { + private Point3D m_DestLoc; + private Map m_DestMap; + private int m_Charges; + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D DestLoc + { + get{ return m_DestLoc; } + set{ m_DestLoc = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map DestMap + { + get{ return m_DestMap; } + set{ m_DestMap = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set{ m_Charges = value; InvalidateProperties(); } + } + + public override int LabelNumber{ get{ return 1049117; } } // Horn of Retreat + + [Constructable] + public HornOfRetreat() : base( 0xFC4 ) + { + Hue = 0x482; + Weight = 1.0; + Charges = 10; + } + + public virtual bool ValidateUse( Mobile from ) + { + return true; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060741, m_Charges.ToString() ); // charges: ~1_val~ + } + + private Timer m_PlayTimer; + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + if ( !ValidateUse( from ) ) + { + SendLocalizedMessageTo( from, 500309 ); // Nothing Happens. + } + else if ( m_PlayTimer != null ) + { + SendLocalizedMessageTo( from, 1042144 ); // This is currently in use. + } + else if ( Charges > 0 ) + { + from.Animate( 34, 7, 1, true, false, 0 ); + from.PlaySound( 0xFF ); + from.SendLocalizedMessage( 1049115 ); // You play the horn and a sense of peace overcomes you... + + --Charges; + + m_PlayTimer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( PlayTimer_Callback ), from ); + } + else + { + SendLocalizedMessageTo( from, 1042544 ); // This item is out of charges. + } + } + else + { + SendLocalizedMessageTo( from, 1042001 ); // That must be in your pack for you to use it. + } + } + + public virtual void PlayTimer_Callback( object state ) + { + Mobile from = (Mobile)state; + + m_PlayTimer = null; + + HornOfRetreatMoongate gate = new HornOfRetreatMoongate( this.DestLoc, this.DestMap, from, this.Hue ); + + gate.MoveToWorld( from.Location, from.Map ); + + from.PlaySound( 0x20E ); + + gate.SendLocalizedMessageTo( from, 1049102, from.Name ); // Quickly ~1_NAME~! Onward through the gate! + } + + public HornOfRetreat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_DestLoc ); + writer.Write( m_DestMap ); + writer.Write( m_Charges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_DestLoc = reader.ReadPoint3D(); + m_DestMap = reader.ReadMap(); + m_Charges = reader.ReadInt(); + break; + } + } + } + } + + public class HornOfRetreatMoongate : Moongate + { + public override int LabelNumber{ get{ return 1049114; } } // Sanctuary Gate + + private Mobile m_Caster; + + public HornOfRetreatMoongate( Point3D destLoc, Map destMap, Mobile caster, int hue ) + { + m_Caster = caster; + + Target = destLoc; + TargetMap = destMap; + + Hue = hue; + Light = LightType.Circle300; + + Dispellable = false; + + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Delete ) ); + } + + public override void BeginConfirmation( Mobile from ) + { + EndConfirmation( from ); + } + + public override void UseGate( Mobile m ) + { + if ( m.Region.IsPartOf( typeof( Regions.Jail ) ) ) + { + m.SendLocalizedMessage( 1042632 ); // You'll need a better jailbreak plan then that! + } + else if ( m == m_Caster ) + { + base.UseGate( m ); + Delete(); + } + } + + public HornOfRetreatMoongate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Items/QuestItem.cs b/Scripts/Engines/Quests/Core/Items/QuestItem.cs new file mode 100644 index 000000000..bc059c9b7 --- /dev/null +++ b/Scripts/Engines/Quests/Core/Items/QuestItem.cs @@ -0,0 +1,121 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests +{ + public abstract class QuestItem : Item + { + public QuestItem( int itemID ) : base( itemID ) + { + } + + public QuestItem( Serial serial ) : base( serial ) + { + } + + public abstract bool CanDrop( PlayerMobile pm ); + + public virtual bool Accepted { get { return Deleted; } } + + public override bool DropToWorld( Mobile from, Point3D p ) + { + bool ret = base.DropToWorld( from, p ); + + if ( ret && !Accepted && Parent != from.Backpack ) + { + if ( from.AccessLevel > AccessLevel.Player ) + { + return true; + } + else if ( !(from is PlayerMobile) || CanDrop( (PlayerMobile)from ) ) + { + return true; + } + else + { + from.SendLocalizedMessage( 1049343 ); // You can only drop quest items into the top-most level of your backpack while you still need them for your quest. + return false; + } + } + else + { + return ret; + } + } + + public override bool DropToMobile( Mobile from, Mobile target, Point3D p ) + { + bool ret = base.DropToMobile( from, target, p ); + + if ( ret && !Accepted && Parent != from.Backpack ) + { + if ( from.AccessLevel > AccessLevel.Player ) + { + return true; + } + else if ( !(from is PlayerMobile) || CanDrop( (PlayerMobile)from ) ) + { + return true; + } + else + { + from.SendLocalizedMessage( 1049344 ); // You decide against trading the item. You still need it for your quest. + return false; + } + } + else + { + return ret; + } + } + + public override bool DropToItem( Mobile from, Item target, Point3D p ) + { + bool ret = base.DropToItem( from, target, p ); + + if ( ret && !Accepted && Parent != from.Backpack ) + { + if ( from.AccessLevel > AccessLevel.Player ) + { + return true; + } + else if ( !(from is PlayerMobile) || CanDrop( (PlayerMobile)from ) ) + { + return true; + } + else + { + from.SendLocalizedMessage( 1049343 ); // You can only drop quest items into the top-most level of your backpack while you still need them for your quest. + return false; + } + } + else + { + return ret; + } + } + + public override DeathMoveResult OnParentDeath( Mobile parent ) + { + if ( parent is PlayerMobile && !CanDrop( (PlayerMobile)parent ) ) + return DeathMoveResult.MoveToBackpack; + else + return base.OnParentDeath( parent ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestCallbackEntry.cs b/Scripts/Engines/Quests/Core/QuestCallbackEntry.cs new file mode 100644 index 000000000..561f49785 --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestCallbackEntry.cs @@ -0,0 +1,26 @@ +using System; +using Server; +using Server.ContextMenus; + +namespace Server.Engines.Quests +{ + public class QuestCallbackEntry : ContextMenuEntry + { + private QuestCallback m_Callback; + + public QuestCallbackEntry( int number, QuestCallback callback ) : this( number, -1, callback ) + { + } + + public QuestCallbackEntry( int number, int range, QuestCallback callback ) : base( number, range ) + { + m_Callback = callback; + } + + public override void OnClick() + { + if ( m_Callback != null ) + m_Callback(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestConversation.cs b/Scripts/Engines/Quests/Core/QuestConversation.cs new file mode 100644 index 000000000..75fbbe680 --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestConversation.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server.Engines.Quests +{ + public abstract class QuestConversation + { + private QuestSystem m_System; + private bool m_HasBeenRead; + + public abstract object Message{ get; } + + public virtual QuestItemInfo[] Info{ get{ return null; } } + public virtual bool Logged{ get{ return true; } } + + public QuestSystem System + { + get{ return m_System; } + set{ m_System = value; } + } + + public bool HasBeenRead + { + get{ return m_HasBeenRead; } + set{ m_HasBeenRead = value; } + } + + public QuestConversation() + { + } + + public virtual void BaseDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_HasBeenRead = reader.ReadBool(); + + break; + } + } + + ChildDeserialize( reader ); + } + + public virtual void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + } + + public virtual void BaseSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_HasBeenRead ); + + ChildSerialize( writer ); + } + + public virtual void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + } + + public virtual void OnRead() + { + } + } + + public class QuestConversationsGump : BaseQuestGump + { + private ArrayList m_Conversations; + + public QuestConversationsGump( QuestConversation conv ) : this( BuildList( conv ) ) + { + } + + public QuestConversationsGump( ArrayList conversations ) : base( 30, 50 ) + { + m_Conversations = conversations; + + Closable = false; + + AddPage( 0 ); + + AddImage( 349, 10, 9392 ); + AddImageTiled( 349, 130, 100, 120, 9395 ); + AddImageTiled( 149, 10, 200, 140, 9391 ); + AddImageTiled( 149, 250, 200, 140, 9397 ); + AddImage( 349, 250, 9398 ); + AddImage( 35, 10, 9390 ); + AddImageTiled( 35, 150, 120, 100, 9393 ); + AddImage( 35, 250, 9396 ); + + AddHtmlLocalized( 110, 60, 200, 20, 1049069, White, false, false ); // Conversation Event + + AddImage( 65, 14, 10102 ); + AddImageTiled( 81, 14, 349, 17, 10101 ); + AddImage( 426, 14, 10104 ); + + AddImageTiled( 55, 40, 388, 323, 2624 ); + AddAlphaRegion( 55, 40, 388, 323 ); + + AddImageTiled( 75, 90, 200, 1, 9101 ); + AddImage( 75, 58, 9781 ); + AddImage( 380, 45, 223 ); + + AddButton( 220, 335, 2313, 2312, 1, GumpButtonType.Reply, 0 ); + AddImage( 0, 0, 10440 ); + + AddPage( 1 ); + + for ( int i = 0; i < conversations.Count; ++i ) + { + QuestConversation conv = (QuestConversation)conversations[conversations.Count - 1 - i]; + + if ( i > 0 ) + { + AddButton( 65, 366, 9909, 9911, 0, GumpButtonType.Page, 1 + i ); + AddHtmlLocalized( 90, 367, 50, 20, 1043354, Black, false, false ); // Previous + + AddPage( 1 + i ); + } + + AddHtmlObject( 70, 110, 365, 220, conv.Message, LightGreen, false, true ); + + if ( i > 0 ) + { + AddButton( 420, 366, 9903, 9905, 0, GumpButtonType.Page, i ); + AddHtmlLocalized( 370, 367, 50, 20, 1043353, Black, false, false ); // Next + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + for ( int i = m_Conversations.Count - 1; i >= 0; --i ) + { + QuestConversation qc = (QuestConversation)m_Conversations[i]; + + if ( !qc.HasBeenRead ) + { + qc.HasBeenRead = true; + qc.OnRead(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestItemInfo.cs b/Scripts/Engines/Quests/Core/QuestItemInfo.cs new file mode 100644 index 000000000..81c816eb4 --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestItemInfo.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Gumps; + +namespace Server.Engines.Quests +{ + public class QuestItemInfo + { + private object m_Name; + private int m_ItemID; + + public object Name + { + get{ return m_Name; } + set{ m_Name = value; } + } + + public int ItemID + { + get{ return m_ItemID; } + set{ m_ItemID = value; } + } + + public QuestItemInfo( object name, int itemID ) + { + m_Name = name; + m_ItemID = itemID; + } + } + + public class QuestItemInfoGump : BaseQuestGump + { + public QuestItemInfoGump( QuestItemInfo[] info ) : base( 485, 75 ) + { + int height = 100 + (info.Length * 75); + + AddPage( 0 ); + + AddBackground( 5, 10, 145, height, 5054 ); + + AddImageTiled( 13, 20, 125, 10, 2624 ); + AddAlphaRegion( 13, 20, 125, 10 ); + + AddImageTiled( 13, height - 10, 128, 10, 2624 ); + AddAlphaRegion( 13, height - 10, 128, 10 ); + + AddImageTiled( 13, 20, 10, height - 30, 2624 ); + AddAlphaRegion( 13, 20, 10, height - 30 ); + + AddImageTiled( 131, 20, 10, height - 30, 2624 ); + AddAlphaRegion( 131, 20, 10, height - 30 ); + + AddHtmlLocalized( 67, 35, 120, 20, 1011233, White, false, false ); // INFO + + AddImage( 62, 52, 9157 ); + AddImage( 72, 52, 9157 ); + AddImage( 82, 52, 9157 ); + + AddButton( 25, 31, 1209, 1210, 777, GumpButtonType.Reply, 0 ); + + AddPage( 1 ); + + for ( int i = 0; i < info.Length; ++i ) + { + QuestItemInfo cur = info[i]; + + AddHtmlObject( 25, 65 + (i * 75), 110, 20, cur.Name, 1153, false, false ); + AddItem( 45, 85 + (i * 75), cur.ItemID ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestObjective.cs b/Scripts/Engines/Quests/Core/QuestObjective.cs new file mode 100644 index 000000000..f870f9c55 --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestObjective.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; +using Server.Items; + +namespace Server.Engines.Quests +{ + public abstract class QuestObjective + { + private QuestSystem m_System; + private bool m_HasBeenRead; + private int m_CurProgress; + private bool m_HasCompleted; + + public abstract object Message{ get; } + + public virtual int MaxProgress{ get{ return 1; } } + public virtual QuestItemInfo[] Info{ get{ return null; } } + + public QuestSystem System + { + get{ return m_System; } + set{ m_System = value; } + } + + public bool HasBeenRead + { + get{ return m_HasBeenRead; } + set{ m_HasBeenRead = value; } + } + + public int CurProgress + { + get{ return m_CurProgress; } + set{ m_CurProgress = value; CheckCompletionStatus(); } + } + + public bool HasCompleted + { + get{ return m_HasCompleted; } + set{ m_HasCompleted = value; } + } + + public virtual bool Completed + { + get{ return m_CurProgress >= MaxProgress; } + } + + public bool IsSingleObjective + { + get{ return ( MaxProgress == 1 ); } + } + + public QuestObjective() + { + } + + public virtual void BaseDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_HasBeenRead = reader.ReadBool(); + goto case 0; + } + case 0: + { + m_CurProgress = reader.ReadEncodedInt(); + m_HasCompleted = reader.ReadBool(); + + break; + } + } + + ChildDeserialize( reader ); + } + + public virtual void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + } + + public virtual void BaseSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 1 ); // version + + writer.Write( (bool) m_HasBeenRead ); + writer.WriteEncodedInt( (int) m_CurProgress ); + writer.Write( (bool) m_HasCompleted ); + + ChildSerialize( writer ); + } + + public virtual void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + } + + public virtual void Complete() + { + CurProgress = MaxProgress; + } + + public virtual void RenderMessage( BaseQuestGump gump ) + { + gump.AddHtmlObject( 70, 130, 300, 100, this.Message, BaseQuestGump.Blue, false, false ); + } + + public virtual void RenderProgress( BaseQuestGump gump ) + { + gump.AddHtmlObject( 70, 260, 270, 100, this.Completed ? 1049077 : 1049078, BaseQuestGump.Blue, false, false ); + } + + public virtual void CheckCompletionStatus() + { + if ( Completed && !HasCompleted ) + { + HasCompleted = true; + OnComplete(); + } + } + + public virtual void OnRead() + { + } + + public virtual bool GetTimerEvent() + { + return !Completed; + } + + public virtual void CheckProgress() + { + } + + public virtual void OnComplete() + { + } + + public virtual bool GetKillEvent( BaseCreature creature, Container corpse ) + { + return !Completed; + } + + public virtual void OnKill( BaseCreature creature, Container corpse ) + { + } + + public virtual bool IgnoreYoungProtection( Mobile from ) + { + return false; + } + } + + public class QuestLogUpdatedGump : BaseQuestGump + { + private QuestSystem m_System; + + public QuestLogUpdatedGump( QuestSystem system ) : base( 3, 30 ) + { + m_System = system; + + AddPage( 0 ); + + AddImage( 20, 5, 1417 ); + + AddHtmlLocalized( 0, 78, 120, 40, 1049079, White, false, false ); // Quest Log Updated + + AddImageTiled( 0, 78, 120, 40, 2624 ); + AddAlphaRegion( 0, 78, 120, 40 ); + + AddButton( 30, 15, 5575, 5576, 1, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_System.ShowQuestLog(); + } + } + + public class QuestObjectivesGump : BaseQuestGump + { + private ArrayList m_Objectives; + + public QuestObjectivesGump( QuestObjective obj ) : this( BuildList( obj ) ) + { + } + + public QuestObjectivesGump( ArrayList objectives ) : base( 90, 50 ) + { + m_Objectives = objectives; + + Closable = false; + + AddPage( 0 ); + + AddImage( 0, 0, 3600 ); + AddImageTiled( 0, 14, 15, 375, 3603 ); + AddImageTiled( 380, 14, 14, 375, 3605 ); + AddImage( 0, 376, 3606 ); + AddImageTiled( 15, 376, 370, 16, 3607 ); + AddImageTiled( 15, 0, 370, 16, 3601 ); + AddImage( 380, 0, 3602 ); + AddImage( 380, 376, 3608 ); + + AddImageTiled( 15, 15, 365, 365, 2624 ); + AddAlphaRegion( 15, 15, 365, 365 ); + + AddImage( 20, 87, 1231 ); + AddImage( 75, 62, 9307 ); + + AddHtmlLocalized( 117, 35, 230, 20, 1046026, Blue, false, false ); // Quest Log + + AddImage( 77, 33, 9781 ); + AddImage( 65, 110, 2104 ); + + AddHtmlLocalized( 79, 106, 230, 20, 1049073, Blue, false, false ); // Objective: + + AddImageTiled( 68, 125, 120, 1, 9101 ); + AddImage( 65, 240, 2104 ); + + AddHtmlLocalized( 79, 237, 230, 20, 1049076, Blue, false, false ); // Progress details: + + AddImageTiled( 68, 255, 120, 1, 9101 ); + AddButton( 175, 355, 2313, 2312, 1, GumpButtonType.Reply, 0 ); + + AddImage( 341, 15, 10450 ); + AddImage( 341, 330, 10450 ); + AddImage( 15, 330, 10450 ); + AddImage( 15, 15, 10450 ); + + AddPage( 1 ); + + for ( int i = 0; i < objectives.Count; ++i ) + { + QuestObjective obj = (QuestObjective)objectives[objectives.Count - 1 - i]; + + if ( i > 0 ) + { + AddButton( 55, 346, 9909, 9911, 0, GumpButtonType.Page, 1 + i ); + AddHtmlLocalized( 82, 347, 50, 20, 1043354, White, false, false ); // Previous + + AddPage( 1 + i ); + } + + obj.RenderMessage( this ); + obj.RenderProgress( this ); + + if ( i > 0 ) + { + AddButton( 317, 346, 9903, 9905, 0, GumpButtonType.Page, i ); + AddHtmlLocalized( 278, 347, 50, 20, 1043353, White, false, false ); // Next + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + for ( int i = m_Objectives.Count - 1; i >= 0; --i ) + { + QuestObjective obj = (QuestObjective)m_Objectives[i]; + + if ( !obj.HasBeenRead ) + { + obj.HasBeenRead = true; + obj.OnRead(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestRestartInfo.cs b/Scripts/Engines/Quests/Core/QuestRestartInfo.cs new file mode 100644 index 000000000..c33cc3555 --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestRestartInfo.cs @@ -0,0 +1,42 @@ +using System; + +namespace Server.Engines.Quests +{ + public class QuestRestartInfo + { + private Type m_QuestType; + private DateTime m_RestartTime; + + public Type QuestType + { + get{ return m_QuestType; } + set{ m_QuestType = value; } + } + + public DateTime RestartTime + { + get{ return m_RestartTime; } + set{ m_RestartTime = value; } + } + + public void Reset( TimeSpan restartDelay ) + { + if ( restartDelay < TimeSpan.MaxValue ) + m_RestartTime = DateTime.Now + restartDelay; + else + m_RestartTime = DateTime.MaxValue; + } + + public QuestRestartInfo( Type questType, TimeSpan restartDelay ) + { + m_QuestType = questType; + Reset( restartDelay ); + } + + public QuestRestartInfo( Type questType, DateTime restartTime ) + { + m_QuestType = questType; + m_RestartTime = restartTime; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestSerializer.cs b/Scripts/Engines/Quests/Core/QuestSerializer.cs new file mode 100644 index 000000000..bd3c69fa4 --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestSerializer.cs @@ -0,0 +1,197 @@ +using System; + +namespace Server.Engines.Quests +{ + public class QuestSerializer + { + public static object Construct( Type type ) + { + try + { + return Activator.CreateInstance( type ); + } + catch + { + return null; + } + } + + public static void Write( Type type, Type[] referenceTable, GenericWriter writer ) + { + if ( type == null ) + { + writer.WriteEncodedInt( (int) 0x00 ); + } + else + { + for ( int i = 0; i < referenceTable.Length; ++i ) + { + if ( referenceTable[i] == type ) + { + writer.WriteEncodedInt( (int) 0x01 ); + writer.WriteEncodedInt( (int) i ); + return; + } + } + + writer.WriteEncodedInt( (int) 0x02 ); + writer.Write( type.FullName ); + } + } + + public static Type ReadType( Type[] referenceTable, GenericReader reader ) + { + int encoding = reader.ReadEncodedInt(); + + switch ( encoding ) + { + default: + case 0x00: // null + { + return null; + } + case 0x01: // indexed + { + int index = reader.ReadEncodedInt(); + + if ( index >= 0 && index < referenceTable.Length ) + return referenceTable[index]; + + return null; + } + case 0x02: // by name + { + string fullName = reader.ReadString(); + + if ( fullName == null ) + return null; + + return ScriptCompiler.FindTypeByFullName( fullName, false ); + } + } + } + + public static QuestSystem DeserializeQuest( GenericReader reader ) + { + int encoding = reader.ReadEncodedInt(); + + switch ( encoding ) + { + default: + case 0x00: // null + { + return null; + } + case 0x01: + { + Type type = ReadType( QuestSystem.QuestTypes, reader ); + + QuestSystem qs = Construct( type ) as QuestSystem; + + if ( qs != null ) + qs.BaseDeserialize( reader ); + + return qs; + } + } + } + + public static void Serialize( QuestSystem qs, GenericWriter writer ) + { + if ( qs == null ) + { + writer.WriteEncodedInt( 0x00 ); + } + else + { + writer.WriteEncodedInt( 0x01 ); + + Write( qs.GetType(), QuestSystem.QuestTypes, writer ); + + qs.BaseSerialize( writer ); + } + } + + public static QuestObjective DeserializeObjective( Type[] referenceTable, GenericReader reader ) + { + int encoding = reader.ReadEncodedInt(); + + switch ( encoding ) + { + default: + case 0x00: // null + { + return null; + } + case 0x01: + { + Type type = ReadType( referenceTable, reader ); + + QuestObjective obj = Construct( type ) as QuestObjective; + + if ( obj != null ) + obj.BaseDeserialize( reader ); + + return obj; + } + } + } + + public static void Serialize( Type[] referenceTable, QuestObjective obj, GenericWriter writer ) + { + if ( obj == null ) + { + writer.WriteEncodedInt( 0x00 ); + } + else + { + writer.WriteEncodedInt( 0x01 ); + + Write( obj.GetType(), referenceTable, writer ); + + obj.BaseSerialize( writer ); + } + } + + public static QuestConversation DeserializeConversation( Type[] referenceTable, GenericReader reader ) + { + int encoding = reader.ReadEncodedInt(); + + switch ( encoding ) + { + default: + case 0x00: // null + { + return null; + } + case 0x01: + { + Type type = ReadType( referenceTable, reader ); + + QuestConversation conv = Construct( type ) as QuestConversation; + + if ( conv != null ) + conv.BaseDeserialize( reader ); + + return conv; + } + } + } + + public static void Serialize( Type[] referenceTable, QuestConversation conv, GenericWriter writer ) + { + if ( conv == null ) + { + writer.WriteEncodedInt( 0x00 ); + } + else + { + writer.WriteEncodedInt( 0x01 ); + + Write( conv.GetType(), referenceTable, writer ); + + conv.BaseSerialize( writer ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/QuestSystem.cs b/Scripts/Engines/Quests/Core/QuestSystem.cs new file mode 100644 index 000000000..7e5ba7d4d --- /dev/null +++ b/Scripts/Engines/Quests/Core/QuestSystem.cs @@ -0,0 +1,686 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; +using Server.ContextMenus; + +namespace Server.Engines.Quests +{ + public delegate void QuestCallback(); + + public abstract class QuestSystem + { + public static readonly Type[] QuestTypes = new Type[] + { + typeof( Doom.TheSummoningQuest ), + typeof( Necro.DarkTidesQuest ), + typeof( Haven.UzeraanTurmoilQuest ), + typeof( Collector.CollectorQuest ), + typeof( Hag.WitchApprenticeQuest ), + typeof( Naturalist.StudyOfSolenQuest ), + typeof( Matriarch.SolenMatriarchQuest ), + typeof( Ambitious.AmbitiousQueenQuest ), + typeof( Ninja.EminosUndertakingQuest ), + typeof( Samurai.HaochisTrialsQuest ), + typeof( Zento.TerribleHatchlingsQuest ) + }; + + public abstract object Name{ get; } + public abstract object OfferMessage{ get; } + + public abstract int Picture{ get; } + + public abstract bool IsTutorial{ get; } + public abstract TimeSpan RestartDelay{ get; } + + public abstract Type[] TypeReferenceTable{ get; } + + private PlayerMobile m_From; + private ArrayList m_Objectives; + private ArrayList m_Conversations; + + public PlayerMobile From + { + get{ return m_From; } + set{ m_From = value; } + } + + public ArrayList Objectives + { + get{ return m_Objectives; } + set{ m_Objectives = value; } + } + + public ArrayList Conversations + { + get{ return m_Conversations; } + set{ m_Conversations = value; } + } + + private Timer m_Timer; + + public virtual void StartTimer() + { + if ( m_Timer != null ) + return; + + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 0.5 ), TimeSpan.FromSeconds( 0.5 ), new TimerCallback( Slice ) ); + } + + public virtual void StopTimer() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + } + + public virtual void Slice() + { + for ( int i = m_Objectives.Count - 1; i >= 0; --i ) + { + QuestObjective obj = (QuestObjective)m_Objectives[i]; + + if ( obj.GetTimerEvent() ) + obj.CheckProgress(); + } + } + + public virtual void OnKill( BaseCreature creature, Container corpse ) + { + for ( int i = m_Objectives.Count - 1; i >= 0; --i ) + { + QuestObjective obj = (QuestObjective)m_Objectives[i]; + + if ( obj.GetKillEvent( creature, corpse ) ) + obj.OnKill( creature, corpse ); + } + } + + public virtual bool IgnoreYoungProtection( Mobile from ) + { + for ( int i = m_Objectives.Count - 1; i >= 0; --i ) + { + QuestObjective obj = (QuestObjective)m_Objectives[i]; + + if ( obj.IgnoreYoungProtection( from ) ) + return true; + } + + return false; + } + + public QuestSystem( PlayerMobile from ) + { + m_From = from; + m_Objectives = new ArrayList(); + m_Conversations = new ArrayList(); + } + + public QuestSystem() + { + } + + public virtual void BaseDeserialize( GenericReader reader ) + { + Type[] referenceTable = this.TypeReferenceTable; + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + int count = reader.ReadEncodedInt(); + + m_Objectives = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + QuestObjective obj = QuestSerializer.DeserializeObjective( referenceTable, reader ); + + if ( obj != null ) + { + obj.System = this; + m_Objectives.Add( obj ); + } + } + + count = reader.ReadEncodedInt(); + + m_Conversations = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + QuestConversation conv = QuestSerializer.DeserializeConversation( referenceTable, reader ); + + if ( conv != null ) + { + conv.System = this; + m_Conversations.Add( conv ); + } + } + + break; + } + } + + ChildDeserialize( reader ); + } + + public virtual void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + } + + public virtual void BaseSerialize( GenericWriter writer ) + { + Type[] referenceTable = this.TypeReferenceTable; + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Objectives.Count ); + + for ( int i = 0; i < m_Objectives.Count; ++i ) + QuestSerializer.Serialize( referenceTable, (QuestObjective) m_Objectives[i], writer ); + + writer.WriteEncodedInt( (int) m_Conversations.Count ); + + for ( int i = 0; i < m_Conversations.Count; ++i ) + QuestSerializer.Serialize( referenceTable, (QuestConversation) m_Conversations[i], writer ); + + ChildSerialize( writer ); + } + + public virtual void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + } + + public bool IsObjectiveInProgress( Type type ) + { + QuestObjective obj = FindObjective( type ); + + return ( obj != null && !obj.Completed ); + } + + public QuestObjective FindObjective( Type type ) + { + for ( int i = m_Objectives.Count - 1; i >= 0; --i ) + { + QuestObjective obj = (QuestObjective)m_Objectives[i]; + + if ( obj.GetType() == type ) + return obj; + } + + return null; + } + + public virtual void SendOffer() + { + m_From.SendGump( new QuestOfferGump( this ) ); + } + + public virtual void GetContextMenuEntries( List list ) + { + if ( m_Objectives.Count > 0 ) + list.Add( new QuestCallbackEntry( 6154, new QuestCallback( ShowQuestLog ) ) ); // View Quest Log + + if ( m_Conversations.Count > 0 ) + list.Add( new QuestCallbackEntry( 6156, new QuestCallback( ShowQuestConversation ) ) ); // Quest Conversation + + list.Add( new QuestCallbackEntry( 6155, new QuestCallback( BeginCancelQuest ) ) ); // Cancel Quest + } + + public virtual void ShowQuestLogUpdated() + { + m_From.CloseGump( typeof( QuestLogUpdatedGump ) ); + m_From.SendGump( new QuestLogUpdatedGump( this ) ); + } + + public virtual void ShowQuestLog() + { + if ( m_Objectives.Count > 0 ) + { + m_From.CloseGump( typeof( QuestItemInfoGump ) ); + m_From.CloseGump( typeof( QuestLogUpdatedGump ) ); + m_From.CloseGump( typeof( QuestObjectivesGump ) ); + m_From.CloseGump( typeof( QuestConversationsGump ) ); + + m_From.SendGump( new QuestObjectivesGump( m_Objectives ) ); + + QuestObjective last = (QuestObjective)m_Objectives[m_Objectives.Count - 1]; + + if ( last.Info != null ) + m_From.SendGump( new QuestItemInfoGump( last.Info ) ); + } + } + + public virtual void ShowQuestConversation() + { + if ( m_Conversations.Count > 0 ) + { + m_From.CloseGump( typeof( QuestItemInfoGump ) ); + m_From.CloseGump( typeof( QuestObjectivesGump ) ); + m_From.CloseGump( typeof( QuestConversationsGump ) ); + + m_From.SendGump( new QuestConversationsGump( m_Conversations ) ); + + QuestConversation last = (QuestConversation)m_Conversations[m_Conversations.Count - 1]; + + if ( last.Info != null ) + m_From.SendGump( new QuestItemInfoGump( last.Info ) ); + } + } + + public virtual void BeginCancelQuest() + { + m_From.SendGump( new QuestCancelGump( this ) ); + } + + public virtual void EndCancelQuest( bool shouldCancel ) + { + if ( m_From.Quest != this ) + return; + + if ( shouldCancel ) + { + m_From.SendLocalizedMessage( 1049015 ); // You have canceled your quest. + Cancel(); + } + else + { + m_From.SendLocalizedMessage( 1049014 ); // You have chosen not to cancel your quest. + } + } + + public virtual void Cancel() + { + ClearQuest( false ); + } + + public virtual void Complete() + { + ClearQuest( true ); + } + + public virtual void ClearQuest( bool completed ) + { + StopTimer(); + + if ( m_From.Quest == this ) + { + m_From.Quest = null; + + TimeSpan restartDelay = this.RestartDelay; + + if ( ( completed && restartDelay > TimeSpan.Zero ) || ( !completed && restartDelay == TimeSpan.MaxValue ) ) + { + List doneQuests = m_From.DoneQuests; + + if ( doneQuests == null ) + m_From.DoneQuests = doneQuests = new List(); + + bool found = false; + + Type ourQuestType = this.GetType(); + + for ( int i = 0; i < doneQuests.Count; ++i ) + { + QuestRestartInfo restartInfo = doneQuests[i]; + + if ( restartInfo.QuestType == ourQuestType ) + { + restartInfo.Reset( restartDelay ); + found = true; + break; + } + } + + if ( !found ) + doneQuests.Add( new QuestRestartInfo( ourQuestType, restartDelay ) ); + } + } + } + + public virtual void AddConversation( QuestConversation conv ) + { + conv.System = this; + + if ( conv.Logged ) + m_Conversations.Add( conv ); + + m_From.CloseGump( typeof( QuestItemInfoGump ) ); + m_From.CloseGump( typeof( QuestObjectivesGump ) ); + m_From.CloseGump( typeof( QuestConversationsGump ) ); + + if ( conv.Logged ) + m_From.SendGump( new QuestConversationsGump( m_Conversations ) ); + else + m_From.SendGump( new QuestConversationsGump( conv ) ); + + if ( conv.Info != null ) + m_From.SendGump( new QuestItemInfoGump( conv.Info ) ); + } + + public virtual void AddObjective( QuestObjective obj ) + { + obj.System = this; + m_Objectives.Add( obj ); + + ShowQuestLogUpdated(); + } + + public virtual void Accept() + { + if ( m_From.Quest != null ) + return; + + m_From.Quest = this; + m_From.SendLocalizedMessage( 1049019 ); // You have accepted the Quest. + + StartTimer(); + } + + public virtual void Decline() + { + m_From.SendLocalizedMessage( 1049018 ); // You have declined the Quest. + } + + public static bool CanOfferQuest( Mobile check, Type questType ) + { + bool inRestartPeriod; + + return CanOfferQuest( check, questType, out inRestartPeriod ); + } + + public static bool CanOfferQuest( Mobile check, Type questType, out bool inRestartPeriod ) + { + inRestartPeriod = false; + + PlayerMobile pm = check as PlayerMobile; + + if ( pm == null ) + return false; + + if ( pm.HasGump( typeof( QuestOfferGump ) ) ) + return false; + + if ( questType == typeof( Necro.DarkTidesQuest ) && pm.Profession != 4 ) // necromancer + return false; + + if ( questType == typeof( Haven.UzeraanTurmoilQuest ) && pm.Profession != 1 && pm.Profession != 2 && pm.Profession != 5 ) // warrior / magician / paladin + return false; + + if ( questType == typeof( Samurai.HaochisTrialsQuest ) && pm.Profession != 6 ) // samurai + return false; + + if ( questType == typeof( Ninja.EminosUndertakingQuest ) && pm.Profession != 7 ) // ninja + return false; + + List doneQuests = pm.DoneQuests; + + if ( doneQuests != null ) + { + for ( int i = 0; i < doneQuests.Count; ++i ) + { + QuestRestartInfo restartInfo = doneQuests[i]; + + if ( restartInfo.QuestType == questType ) + { + DateTime endTime = restartInfo.RestartTime; + + if ( DateTime.Now < endTime ) + { + inRestartPeriod = true; + return false; + } + + doneQuests.RemoveAt( i-- ); + return true; + } + } + } + + return true; + } + + public static void FocusTo( Mobile who, Mobile to ) + { + if ( Utility.RandomBool() ) + { + who.Animate( 17, 7, 1, true, false, 0 ); + } + else + { + switch ( Utility.Random( 3 ) ) + { + case 0: who.Animate( 32, 7, 1, true, false, 0 ); break; + case 1: who.Animate( 33, 7, 1, true, false, 0 ); break; + case 2: who.Animate( 34, 7, 1, true, false, 0 ); break; + } + } + + who.Direction = who.GetDirectionTo( to ); + } + + public static int RandomBrightHue() + { + if ( 0.1 > Utility.RandomDouble() ) + return Utility.RandomList( 0x62, 0x71 ); + + return Utility.RandomList( 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 ); + } + } + + public class QuestCancelGump : BaseQuestGump + { + private QuestSystem m_System; + + public QuestCancelGump( QuestSystem system ) : base( 120, 50 ) + { + m_System = system; + + Closable = false; + + AddPage( 0 ); + + AddImageTiled( 0, 0, 348, 262, 2702 ); + AddAlphaRegion( 0, 0, 348, 262 ); + + AddImage( 0, 15, 10152 ); + AddImageTiled( 0, 30, 17, 200, 10151 ); + AddImage( 0, 230, 10154 ); + + AddImage( 15, 0, 10252 ); + AddImageTiled( 30, 0, 300, 17, 10250 ); + AddImage( 315, 0, 10254 ); + + AddImage( 15, 244, 10252 ); + AddImageTiled( 30, 244, 300, 17, 10250 ); + AddImage( 315, 244, 10254 ); + + AddImage( 330, 15, 10152 ); + AddImageTiled( 330, 30, 17, 200, 10151 ); + AddImage( 330, 230, 10154 ); + + AddImage( 333, 2, 10006 ); + AddImage( 333, 248, 10006 ); + AddImage( 2, 248, 10006 ); + AddImage( 2, 2, 10006 ); + + AddHtmlLocalized( 25, 22, 200, 20, 1049000, 32000, false, false ); // Confirm Quest Cancellation + AddImage( 25, 40, 3007 ); + + if ( system.IsTutorial ) + { + AddHtmlLocalized( 25, 55, 300, 120, 1060836, White, false, false ); // This quest will give you valuable information, skills and equipment that will help you advance in the game at a quicker pace.

Are you certain you wish to cancel at this time? + } + else + { + AddHtmlLocalized( 25, 60, 300, 20, 1049001, White, false, false ); // You have chosen to abort your quest: + AddImage( 25, 81, 0x25E7 ); + AddHtmlObject( 48, 80, 280, 20, system.Name, DarkGreen, false, false ); + + AddHtmlLocalized( 25, 120, 280, 20, 1049002, White, false, false ); // Can this quest be restarted after quitting? + AddImage( 25, 141, 0x25E7 ); + AddHtmlLocalized( 48, 140, 280, 20, (system.RestartDelay < TimeSpan.MaxValue) ? 1049016 : 1049017, DarkGreen, false, false ); // Yes/No + } + + AddRadio( 25, 175, 9720, 9723, true, 1 ); + AddHtmlLocalized( 60, 180, 280, 20, 1049005, White, false, false ); // Yes, I really want to quit! + + AddRadio( 25, 210, 9720, 9723, false, 0 ); + AddHtmlLocalized( 60, 215, 280, 20, 1049006, White, false, false ); // No, I don't want to quit. + + AddButton( 265, 220, 247, 248, 1, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_System.EndCancelQuest( info.IsSwitched( 1 ) ); + } + } + + public class QuestOfferGump : BaseQuestGump + { + private QuestSystem m_System; + + public QuestOfferGump( QuestSystem system ) : base( 75, 25 ) + { + m_System = system; + + Closable = false; + + AddPage( 0 ); + + AddImageTiled( 50, 20, 400, 400, 2624 ); + AddAlphaRegion( 50, 20, 400, 400 ); + + AddImage( 90, 33, 9005 ); + AddHtmlLocalized( 130, 45, 270, 20, 1049010, White, false, false ); // Quest Offer + AddImageTiled( 130, 65, 175, 1, 9101 ); + + AddImage( 140, 110, 1209 ); + AddHtmlObject( 160, 108, 250, 20, system.Name, DarkGreen, false, false ); + + AddHtmlObject( 98, 140, 312, 200, system.OfferMessage, LightGreen, false, true ); + + AddRadio( 85, 350, 9720, 9723, true, 1 ); + AddHtmlLocalized( 120, 356, 280, 20, 1049011, White, false, false ); // I accept! + + AddRadio( 85, 385, 9720, 9723, false, 0 ); + AddHtmlLocalized( 120, 391, 280, 20, 1049012, White, false, false ); // No thanks, I decline. + + AddButton( 340, 390, 247, 248, 1, GumpButtonType.Reply, 0 ); + + AddImageTiled( 50, 29, 30, 390, 10460 ); + AddImageTiled( 34, 140, 17, 279, 9263 ); + + AddImage( 48, 135, 10411 ); + AddImage( -16, 285, 10402 ); + AddImage( 0, 10, 10421 ); + AddImage( 25, 0, 10420 ); + + AddImageTiled( 83, 15, 350, 15, 10250 ); + + AddImage( 34, 419, 10306 ); + AddImage( 442, 419, 10304 ); + AddImageTiled( 51, 419, 392, 17, 10101 ); + + AddImageTiled( 415, 29, 44, 390, 2605 ); + AddImageTiled( 415, 29, 30, 390, 10460 ); + AddImage( 425, 0, 10441 ); + + AddImage( 370, 50, 1417 ); + AddImage( 379, 60, system.Picture ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + { + if ( info.IsSwitched( 1 ) ) + m_System.Accept(); + else + m_System.Decline(); + } + } + } + + public abstract class BaseQuestGump : Gump + { + public const int Black = 0x0000; + public const int White = 0x7FFF; + public const int DarkGreen = 10000; + public const int LightGreen = 90000; + public const int Blue = 19777215; + + public static int C16232( int c16 ) + { + c16 &= 0x7FFF; + + int r = ( ((c16 >> 10) & 0x1F) << 3 ); + int g = ( ((c16 >> 05) & 0x1F) << 3 ); + int b = ( ((c16 >> 00) & 0x1F) << 3 ); + + return (r << 16) | (g << 8) | (b << 0); + } + + public static int C16216( int c16 ) + { + return c16 & 0x7FFF; + } + + public static int C32216( int c32 ) + { + c32 &= 0xFFFFFF; + + int r = ( ((c32 >> 16) & 0xFF) >> 3 ); + int g = ( ((c32 >> 08) & 0xFF) >> 3 ); + int b = ( ((c32 >> 00) & 0xFF) >> 3 ); + + return (r << 10) | (g << 5) | (b << 0); + } + + public static string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public static ArrayList BuildList( object obj ) + { + ArrayList list = new ArrayList(); + + list.Add( obj ); + + return list; + } + + public void AddHtmlObject( int x, int y, int width, int height, object message, int color, bool back, bool scroll ) + { + if ( message is string ) + { + string html = (string)message; + + AddHtml( x, y, width, height, Color( html, C16232( color ) ), back, scroll ); + } + else if ( message is int ) + { + int html = (int)message; + + AddHtmlLocalized( x, y, width, height, html, C16216( color ), back, scroll ); + } + } + + public BaseQuestGump( int x, int y ) : base( x, y ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Regions/CancelQuestRegion.cs b/Scripts/Engines/Quests/Core/Regions/CancelQuestRegion.cs new file mode 100644 index 000000000..e387a9b58 --- /dev/null +++ b/Scripts/Engines/Quests/Core/Regions/CancelQuestRegion.cs @@ -0,0 +1,44 @@ +using System; +using System.Xml; +using Server; +using Server.Regions; +using Server.Mobiles; + +namespace Server.Engines.Quests +{ + public class CancelQuestRegion : BaseRegion + { + private Type m_Quest; + + public Type Quest{ get{ return m_Quest; } } + + public CancelQuestRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + ReadType( xml["quest"], "type", ref m_Quest ); + } + + public override bool OnMoveInto( Mobile m, Direction d, Point3D newLocation, Point3D oldLocation ) + { + if ( !base.OnMoveInto ( m, d, newLocation, oldLocation ) ) + return false; + + if ( m.AccessLevel > AccessLevel.Player ) + return true; + + if ( m_Quest == null ) + return true; + + PlayerMobile player = m as PlayerMobile; + + if ( player != null && player.Quest != null && player.Quest.GetType() == m_Quest ) + { + if ( !player.HasGump( typeof( QuestCancelGump ) ) ) + player.Quest.BeginCancelQuest(); + + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Regions/QuestCompleteObjectiveRegion.cs b/Scripts/Engines/Quests/Core/Regions/QuestCompleteObjectiveRegion.cs new file mode 100644 index 000000000..811aae452 --- /dev/null +++ b/Scripts/Engines/Quests/Core/Regions/QuestCompleteObjectiveRegion.cs @@ -0,0 +1,43 @@ +using System; +using System.Xml; +using Server; +using Server.Regions; +using Server.Mobiles; + +namespace Server.Engines.Quests +{ + public class QuestCompleteObjectiveRegion : BaseRegion + { + private Type m_Quest; + private Type m_Objective; + + public Type Quest{ get{ return m_Quest ; } } + public Type Objective{ get{ return m_Objective; } } + + public QuestCompleteObjectiveRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + XmlElement questEl = xml["quest"]; + + ReadType( questEl, "type", ref m_Quest ); + ReadType( questEl, "complete", ref m_Objective ); + } + + public override void OnEnter( Mobile m ) + { + base.OnEnter( m ); + + if ( m_Quest != null && m_Objective != null ) + { + PlayerMobile player = m as PlayerMobile; + + if ( player != null && player.Quest != null && player.Quest.GetType() == m_Quest ) + { + QuestObjective obj = player.Quest.FindObjective( m_Objective ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Regions/QuestNoEntryRegion.cs b/Scripts/Engines/Quests/Core/Regions/QuestNoEntryRegion.cs new file mode 100644 index 000000000..56f988787 --- /dev/null +++ b/Scripts/Engines/Quests/Core/Regions/QuestNoEntryRegion.cs @@ -0,0 +1,67 @@ +using System; +using System.Xml; +using Server; +using Server.Regions; +using Server.Mobiles; + +namespace Server.Engines.Quests +{ + public class QuestNoEntryRegion : BaseRegion + { + private Type m_Quest; + private Type m_MinObjective; + private Type m_MaxObjective; + private int m_Message; + + public Type Quest{ get{ return m_Quest; } } + public Type MinObjective{ get{ return m_MinObjective; } } + public Type MaxObjective{ get{ return m_MaxObjective; } } + public int Message{ get{ return m_Message; } } + + public QuestNoEntryRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + XmlElement questEl = xml["quest"]; + + ReadType( questEl, "type", ref m_Quest ); + ReadType( questEl, "min", ref m_MinObjective, false ); + ReadType( questEl, "max", ref m_MaxObjective, false ); + ReadInt32( questEl, "message", ref m_Message, false ); + } + + public override bool OnMoveInto( Mobile m, Direction d, Point3D newLocation, Point3D oldLocation ) + { + if ( !base.OnMoveInto ( m, d, newLocation, oldLocation ) ) + return false; + + if ( m.AccessLevel > AccessLevel.Player ) + return true; + + if( m is BaseCreature ) + { + BaseCreature bc = m as BaseCreature; + + if( !bc.Controlled && !bc.Summoned ) + return true; + } + + if ( m_Quest == null ) + return true; + + PlayerMobile player = m as PlayerMobile; + + if ( player != null && player.Quest != null && player.Quest.GetType() == m_Quest + && ( m_MinObjective == null || player.Quest.FindObjective( m_MinObjective ) != null ) + && ( m_MaxObjective == null || player.Quest.FindObjective( m_MaxObjective ) == null ) ) + { + return true; + } + else + { + if ( m_Message != 0 ) + m.SendLocalizedMessage( m_Message ); + + return false; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Core/Regions/QuestOfferRegion.cs b/Scripts/Engines/Quests/Core/Regions/QuestOfferRegion.cs new file mode 100644 index 000000000..ca0bdaf00 --- /dev/null +++ b/Scripts/Engines/Quests/Core/Regions/QuestOfferRegion.cs @@ -0,0 +1,43 @@ +using System; +using System.Xml; +using Server; +using Server.Regions; +using Server.Mobiles; + +namespace Server.Engines.Quests +{ + public class QuestOfferRegion : BaseRegion + { + private Type m_Quest; + + public Type Quest{ get{ return m_Quest ; } } + + public QuestOfferRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + ReadType( xml["quest"], "type", ref m_Quest ); + } + + public override void OnEnter( Mobile m ) + { + base.OnEnter( m ); + + if ( m_Quest == null ) + return; + + PlayerMobile player = m as PlayerMobile; + + if ( player != null && player.Quest == null && QuestSystem.CanOfferQuest( m, m_Quest ) ) + { + try + { + QuestSystem qs = (QuestSystem) Activator.CreateInstance( m_Quest, new object[] { player } ); + qs.SendOffer(); + } + catch ( Exception ex ) + { + Console.WriteLine( "Error creating quest {0}: {1}", m_Quest, ex ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Conversations.cs b/Scripts/Engines/Quests/Dark Tides/Conversations.cs new file mode 100644 index 000000000..6d7e1b90e --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Conversations.cs @@ -0,0 +1,607 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Necro +{ + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Important Quest Information

+ * + * During your quest, any important information that a + * NPC gives you, will appear in a + * window such as this one. You can review the information at + * any time during your quest.

Getting Help

+ * + * Some of the text you will come across during your quest, will + * be underlined links to the codex of + * wisdom, or online help system. You can click on the text + * to get detailed information on the underlined subject. You + * may also access the Codex Of Wisdom by pressing "F1" or by + * clicking on the "?" on the toolbar at the top of your screen.

+ * + * Context Menus

Context menus can be called up by + * single left-clicking (or Shift + single left-click, if you + * changed it) on most objects or NPCs in the world. Nearly + * everything, including your own avatar will have context menus + * available. Bringing up your avatar's context menu will give + * you options to cancel your quest and review various quest + * information.

+ */ + return 1049092; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + Container bag = Mardoth.GetNewContainer(); + + bag.DropItem( new DarkTidesHorn() ); + + System.From.AddToBackpack( bag ); + + System.AddConversation( new ReanimateMaabusConversation() ); + } + } + + public class ReanimateMaabusConversation : QuestConversation + { + public override object Message + { + get + { + /* Excellent choice, young apprentice of evil!

+ * + * I will not waste our time with pleasantries. There is much work + * to be done – especially in light of the recent Paladin ambushes + * that we have suffered. The necromantic brotherhood is working + * towards the summoning of the elder daemon Kronus, who will rise + * from the Well of Tears to help us finally crush the Paladin forces + * that have plagued our lands for so long now.

+ * + * To summon Kronus, we must energize the Well of Tears with a series + * of dark rituals. Unfortunately the rituals needed to sufficiently + * energize the Well of Tears have been lost to us. Your task will be + * to recover one of the ritual scrolls needed for the summoning.

+ * + * You will need to find the corpse of the Arch Necromancer Maabus, which + * has been laid to rest in the tomb of elders. We believe his spirit may + * have memory of where we may find the scrolls needed for the summoning. + * You will need to awaken him from the slumber of death, using your + * Animate Dead spell, of course.

+ * + * To reach the tomb, step onto the magical teleporter just to the + * West of where I am standing.

+ * + * Once you have been teleported, follow the path, which will lead you to + * the tomb of Maabus.

One more thing before you go:

+ * + * Should you get into trouble out there or should you lose your way, do + * not worry. I have also given you a magical horn - a Horn of Retreat. + * Play the horn at any time to open a magical gateway that leads back to this + * tower.

+ * + * Should your horn run out of charges, simply + * hand me the horn to have it recharged.

+ * + * Good luck friend. + */ + return 1060099; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1026153, 6178 ), // teleporter + new QuestItemInfo( 1049117, 4036 ), // Horn of Retreat + new QuestItemInfo( 1048032, 3702 ) // a bag + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public ReanimateMaabusConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindMaabusTombObjective() ); + } + } + + public class MaabasConversation : QuestConversation + { + public override object Message + { + get + { + /* Maabus emits an ear-crawling screech as his body reanimates. + * He turns and angrily screams at you:

+ * + * YOU INFIDEL! HOW DARE YOU AWAKEN MAABUS!?!

+ * + * Maabus continues to scream at you angrily for some time. + * As he settles down, you explain to him the purpose of your visit. + * Once you explain that you are on a quest to summon the elder daemon + * Kronus, Maabus begins to cooperate, and begins to speak in a more + * reasonable tone:

+ * + * Well, why didn’t you say so? If you’re going to raise Kronus from + * the Well of Tears, you must first complete a long series of dark + * rituals. I once owned one of the scrolls needed for the summoning, + * but alas it was lost to me when I lost my life to a cowardly Paladin + * ambush near the Paladin city of Light. They would have probably + * hidden the scroll in their precious crystal cave near the city.

+ * + * There is a teleporter in the corner of this tomb. It will transport + * you near the crystal cave at which I believe one of the calling scrolls + * is hidden. Good luck.

+ * + * Maabus' body slumps back into the coffinas your magic expires. + */ + return 1060103; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1026153, 6178 ) // teleporter + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public MaabasConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindCrystalCaveObjective() ); + } + } + + public class HorusConversation : QuestConversation + { + public override object Message + { + get + { + /* An old man, dressed in slightly tattered armor, whom you recognize + * to be a Paladin stands before the Crystal Cave staring blankly into + * the space in front of him. As he begins to speak to you, you realize + * this man is blind. You attempt to persuade the blind man that you are + * a Paladin seeking to inspect the scroll of calling...

+ * + * Greetings traveler!

+ * + * You seek entrance to the Crystal Cave, home of the Calling Scroll? Hmm. + * You reak of death and decay, brother. You reak of death like a Necromancer, + * but yet you claim to be a Paladin in hopes that I will grant thee passage + * into the cave?

+ * + * Please don’t think ill of me for this, but I’m just a blind, old man looking + * to keep the brotherhood of Paladins safe from the clutches of the elder daemon + * Kronus. The Necromancers have been after this particular scroll for quite some + * time, so we must take all the security precautions we can.

+ * + * Before I can let you pass into the Crystal Cave, you must speak to me the secret + * word that is kept in the Scroll of Abraxus in the Vault of Secrets at the Paladin + * city of Light. It’s the only way that I can be sure you are who you claim to be, + * since Necromancers cannot enter the Vault due to powerful protective magic that + * the brotherhood has blessed the vault with. + */ + return 1060105; + } + } + + public HorusConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindMardothAboutVaultObjective() ); + } + } + + public class MardothVaultConversation : QuestConversation + { + public override object Message + { + get + { + /* Mardoth looks at you expectantly until you tell him that you failed + * to retrieve the scroll...

+ * + * You failed? Very unfortunate... So now you must find your way into + * the paladin’s Vault of Secrets, eh? Well, you won't be able to get in + * – there is a powerful magic aura that protects the Vault from all + * Necromancers. We simply cannot enter. However, that's not to say you + * familiar spirit can't.

+ * + * Mardoth grins with obvious satisfaction + * as he explains the details of the Summon + * Familiar spell to you..., which will allow you to summon a + * scavenging Horde Minion to steal the scroll.

+ * + * Very well. You are prepared to go. Take the teleporter just to the + * West of where I am standing to transport + * to the Paladin city of Light. Once you have arrived in the city, follow + * the road of glowing runes to the Vault of Secrets. You know what to do. + */ + return 1060107; + } + } + + public MardothVaultConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindCityOfLightObjective() ); + } + } + + public class VaultOfSecretsConversation : QuestConversation + { + public override object Message + { + get + { + /* You have arrived in the Vault of Secrets. You can feel the + * protective magic in this place restricting you, making you + * feel nearly claustrophobic.

+ * + * Just ahead of you and out of your reach, you see a collection + * of scrolls and books, one of them being entitled + * 'Scroll of Abraxus' . You can only assume that this scroll + * holds the current password required to enter the Crystal Cave.

+ * + * This would be a good opportunity to summon + * your familiar. Since your familiar is not a Necromancer, it + * will not be affected by the anti-magic aura that surrounds the Vault.

+ * + * Summon your familiar with the Summon Familiar spell. + */ + return 1060110; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1023643, 8787 ) // spellbook + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public VaultOfSecretsConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FetchAbraxusScrollObjective() ); + } + } + + public class ReadAbraxusScrollConversation : QuestConversation + { + public override object Message + { + get + { + /* You have obtained the Scroll of Abraxus, which contains the secret + * password needed to gain passage into the Crystal Cave where the + * Scroll of Calling is kept. Read the scroll (double click) and + * figure out the password.

+ * + * Once you have the password, return to the Crystal Cave and speak + * the password to the guard.

+ * + * If you do not know the way to the Crystal Cave from the Paladin City, + * you can use the magic teleporter located just outside of the vault. + */ + return 1060114; + } + } + + public ReadAbraxusScrollConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReadAbraxusScrollObjective() ); + } + } + + public class SecondHorusConversation : QuestConversation + { + public override object Message + { + get + { + /* Very well Paladin, you have proven to me your identity. + * I grant thee passage.

+ * + * Be careful, however – I’ve heard that the cave has been + * infested with a vermin of some sort. Our High Lord + * Melkeer was supposed to send some troops to clear the + * vermin out of the cave, but that was last week already. + * I fear that he forgot.

+ * + * If you can find it in your goodness to dispose of at + * least 5 of those vermin in there, I shall reward your + * efforts. If however you are too busy, and I would + * understand if you were, don’t bother with the vermin.

+ * + * You may now pass through the energy barrier to enter the + * Crystal Cave. Take care honorable Paladin soul. + * Walk in the light my friend. + */ + return 1060118; + } + } + + public SecondHorusConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindCallingScrollObjective() ); + } + } + + public class HealConversation : QuestConversation + { + public override object Message + { + get + { + /* You've just slain a creature. Now is a good time to learn how + * to heal yourself as a Necromancer.

+ * + * As a follower of the dark path, you are able to recover lost + * hitpoints by communing with the spirit world via the skill + * Spirit Speak. Learn more about it now, + * in the codex of Wisdom. + */ + return 1061610; + } + } + + public HealConversation() + { + } + } + + public class HorusRewardConversation : QuestConversation + { + public override object Message + { + get + { + /* I thank you for going out of your way to clean out some + * of the vermin in that cave – here is your reward: a bag + * containing 500 gold coins plus a strange and magical artifact + * that should come in handy in your travels.

+ * + * Take care young Paladin! + */ + return 1060717; + } + } + + public override bool Logged{ get{ return false; } } + + public HorusRewardConversation() + { + } + } + + public class LostCallingScrollConversation : QuestConversation + { + private bool m_FromMardoth; + + public override object Message + { + get + { + if ( m_FromMardoth ) + { + /* You return without the scroll of Calling? I'm afraid that + * won't do. You must return to the Crystal Cave and fetch + * another scroll. Use the teleporter to the West of me to + * get there. Return here when you have the scroll. Do not + * fail me this time, young apprentice of evil. + */ + return 1062058; + } + else // from well of tears + { + /* You have arrived at the well, but no longer have the scroll + * of calling. Use Mardoth's teleporter to return to the + * Crystal Cave and fetch another scroll from the box. + */ + return 1060129; + } + } + } + + public override bool Logged{ get{ return false; } } + + public LostCallingScrollConversation( bool fromMardoth ) + { + m_FromMardoth = fromMardoth; + } + + // Serialization + public LostCallingScrollConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_FromMardoth = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_FromMardoth ); + } + } + + public class MardothKronusConversation : QuestConversation + { + public override object Message + { + get + { + /* You have returned with the scroll! I knew I could count on you. + * You can now perform the rite of calling at the Well of Tears. + * This ritual will help charge the Well to prepare for the coming + * of Kronus. You are prepared to do your part young Necromancer!

+ * + * Just outside of this tower, you will find a path lined with red + * lanterns. Follow this path to get to the Well of Tears. Once + * you have arrived at the Well, use the scroll to perform the + * ritual of calling. Performing the rite will empower the well + * and bring us that much closer to the arrival of Kronus.

+ * + * Once you have completed the ritual, return here for your + * promised reward. + */ + return 1060121; + } + } + + public MardothKronusConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindWellOfTearsObjective() ); + } + } + + public class MardothEndConversation : QuestConversation + { + public override object Message + { + get + { + /* You have done as I asked... I knew I could count on you from + * the moment you walked in here!

+ * + * The forces of evil are strong within you. You will become + * a great Necromancer in this life - perhaps even the greatest.

+ * + * My work for you is done here. I release you from my service + * to go into the world and fight for our cause...

+ * + * Oh...I almost forgot - your reward. Here is a magical + * weapon and 2000 gold for you, in the form of a check. Don't + * spend it all in one place though, eh?

+ * + * Actually, before you can spend any of it at all, you will + * have to cash the check at the + * nearest bank. Shopkeepers never accept checks for payment, + * they require cash.

+ * + * In your pack, you will find an enchanted sextant. Use this + * sextant to guide you to the nearest bank.

+ * + * Farewell, and stay true to the ways of the shadow... + */ + return 1060133; + } + } + + public MardothEndConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindBankObjective() ); + } + } + + public class BankerConversation : QuestConversation + { + public override object Message + { + get + { + /* The banker smiles at you and greets you in a loud and robust voice...

+ * + * Well hello there adventurer! I see you've learned how to cash checks. Wonderful! + * Let me tell you a bit about the banks in this world...

+ * + * Anything that you place into any bank box, can be retrieved from any other + * bank box in the land. For instance, if you place an item into a bank box in + * Britain, it can be retrieved from your bank box in Moonglow or any other city.

+ * + * Bank boxes are very secure. So secure, in fact, that no one can ever get into + * your bank box except for yourself. Security is hard to come by these days, + * but you can trust in the banking system of Britannia! We shall not let you down!

+ * + * I hope to be seeing much more of you as your riches grow! May your bank box overflow + * with the spoils of your adventures.

Farewell adventurer, you are now free to + * explore the world on your own. + */ + return 1060137; + } + } + + public BankerConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class RadarConversation : QuestConversation + { + public override object Message + { + get + { + /* If you are leaving the tower, you should learn about the Radar Map.

+ * + * The Radar Map (or Overhead View) can be opened by pressing 'ALT-R' + * on your keyboard. It shows your immediate surroundings from a bird's + * eye view.

Pressing ALT-R twice, will enlarge the Radar Map a + * little. Use the Radar Map often as you travel throughout the world + * to familiarize yourself with your surroundings. + */ + return 1061692; + } + } + + public override bool Logged{ get{ return false; } } + + public RadarConversation() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/DarkTidesQuest.cs b/Scripts/Engines/Quests/Dark Tides/DarkTidesQuest.cs new file mode 100644 index 000000000..c249a0b0a --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/DarkTidesQuest.cs @@ -0,0 +1,153 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Necro +{ + public class DarkTidesQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Necro.AcceptConversation ), + typeof( Necro.AnimateMaabusCorpseObjective ), + typeof( Necro.BankerConversation ), + typeof( Necro.CashBankCheckObjective ), + typeof( Necro.FetchAbraxusScrollObjective ), + typeof( Necro.FindBankObjective ), + typeof( Necro.FindCallingScrollObjective ), + typeof( Necro.FindCityOfLightObjective ), + typeof( Necro.FindCrystalCaveObjective ), + typeof( Necro.FindMaabusCorpseObjective ), + typeof( Necro.FindMaabusTombObjective ), + typeof( Necro.FindMardothAboutKronusObjective ), + typeof( Necro.FindMardothAboutVaultObjective ), + typeof( Necro.FindMardothEndObjective ), + typeof( Necro.FindVaultOfSecretsObjective ), + typeof( Necro.FindWellOfTearsObjective ), + typeof( Necro.HorusConversation ), + typeof( Necro.LostCallingScrollConversation ), + typeof( Necro.MaabasConversation ), + typeof( Necro.MardothEndConversation ), + typeof( Necro.MardothKronusConversation ), + typeof( Necro.MardothVaultConversation ), + typeof( Necro.RadarConversation ), + typeof( Necro.ReadAbraxusScrollConversation ), + typeof( Necro.ReadAbraxusScrollObjective ), + typeof( Necro.ReanimateMaabusConversation ), + typeof( Necro.RetrieveAbraxusScrollObjective ), + typeof( Necro.ReturnToCrystalCaveObjective ), + typeof( Necro.SecondHorusConversation ), + typeof( Necro.SpeakCavePasswordObjective ), + typeof( Necro.UseCallingScrollObjective ), + typeof( Necro.VaultOfSecretsConversation ), + typeof( Necro.FindHorusAboutRewardObjective ), + typeof( Necro.HealConversation ), + typeof( Necro.HorusRewardConversation ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // Dark Tides + return 1060095; + } + } + + public override object OfferMessage + { + get + { + /* An old man who looks to be 200 years old from the looks + * of his translucently pale and heavily wrinkled skin, turns + * to you and gives you a half-cocked grin that makes you + * feel somewhat uneasy.

+ * + * After a short pause, he begins to speak to you...


+ * + * Hmm. What's this? Another budding Necromancer to join the + * ranks of Evil? Here... let me take a look at you... Ah + * yes... Very Good! I sense the forces of evil are strong + * within you, child – but you need training so that you can + * learn to focus your skills against those aligned against + * our cause. You are destined to become a legendary + * Necromancer - with the proper training, that only I can + * give you.

+ * + * Mardoth pauses just long enough to give you a wide, + * skin-crawling grin.

+ * + * Let me introduce myself. I am Mardoth, the guildmaster of + * the Necromantic Brotherhood. I have taken it upon myself + * to train anyone willing to learn the dark arts of Necromancy. + * The path of destruction, decay and obliteration is not an + * easy one. Only the most evil and the most dedicated can + * hope to master the sinister art of death.

+ * + * I can lend you training and help supply you with equipment – + * in exchange for a few services rendered by you, of course. + * Nothing major, just a little death and destruction here and + * there - the tasks should be easy as a tasty meat pie for one + * as treacherous and evil as yourself.

+ * + * What do you say? Do we have a deal? + */ + return 1060094; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.MaxValue; } } + public override bool IsTutorial{ get{ return true; } } + + public override int Picture{ get{ return 0x15B5; } } + + public DarkTidesQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public DarkTidesQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + if ( from is SummonedPaladin ) + return true; + + return base.IgnoreYoungProtection( from ); + } + + public static bool HasLostCallingScroll( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FindMardothAboutKronusObjective ) ) || qs.IsObjectiveInProgress( typeof( FindWellOfTearsObjective ) ) || qs.IsObjectiveInProgress( typeof( UseCallingScrollObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( KronusScroll ) ) == null ); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/CrystalCaveBarrier.cs b/Scripts/Engines/Quests/Dark Tides/Items/CrystalCaveBarrier.cs new file mode 100644 index 000000000..dacedda00 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/CrystalCaveBarrier.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class CrystalCaveBarrier : Item + { + [Constructable] + public CrystalCaveBarrier() : base( 0x3967 ) + { + Movable = false; + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m.AccessLevel > AccessLevel.Player ) + return true; + + bool sendMessage = m.Player; + + if ( m is BaseCreature ) + m = ((BaseCreature)m).ControlMaster; + + PlayerMobile pm = m as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SpeakCavePasswordObjective ) ); + + if ( obj != null && obj.Completed ) + { + if ( sendMessage ) + m.SendLocalizedMessage( 1060648 ); // With Horus' permission, you are able to pass through the barrier. + + return true; + } + } + } + + if ( sendMessage ) + m.SendLocalizedMessage( 1060649, "", 0x66D ); // Without the permission of the guardian Horus, the magic of the barrier prevents your passage. + + return false; + } + + public CrystalCaveBarrier( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/DarkTidesHorn.cs b/Scripts/Engines/Quests/Dark Tides/Items/DarkTidesHorn.cs new file mode 100644 index 000000000..a8eab4ce0 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/DarkTidesHorn.cs @@ -0,0 +1,44 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class DarkTidesHorn : HornOfRetreat + { + public override bool ValidateUse( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + return ( pm != null && pm.Quest is DarkTidesQuest ); + } + + [Constructable] + public DarkTidesHorn() + { + DestLoc = new Point3D( 2103, 1319, -68 ); + DestMap = Map.Malas; + } + + public DarkTidesHorn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/DarkTidesTeleporter.cs b/Scripts/Engines/Quests/Dark Tides/Items/DarkTidesTeleporter.cs new file mode 100644 index 000000000..662a80c39 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/DarkTidesTeleporter.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class DarkTidesTeleporter : DynamicTeleporter + { + [Constructable] + public DarkTidesTeleporter() + { + } + + public override bool GetDestination( PlayerMobile player, ref Point3D loc, ref Map map ) + { + QuestSystem qs = player.Quest; + + if ( qs is DarkTidesQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FindMaabusTombObjective ) ) ) + { + loc = new Point3D( 2038, 1263, -90 ); + map = Map.Malas; + qs.AddConversation( new RadarConversation() ); + return true; + } + else if ( qs.IsObjectiveInProgress( typeof( FindCrystalCaveObjective ) ) ) + { + loc = new Point3D( 1194, 521, -90 ); + map = Map.Malas; + return true; + } + else if ( qs.IsObjectiveInProgress( typeof( FindCityOfLightObjective ) ) ) + { + loc = new Point3D( 1091, 519, -90 ); + map = Map.Malas; + return true; + } + else if ( qs.IsObjectiveInProgress( typeof( ReturnToCrystalCaveObjective ) ) ) + { + loc = new Point3D( 1194, 521, -90 ); + map = Map.Malas; + return true; + } + else if ( DarkTidesQuest.HasLostCallingScroll( player ) ) + { + loc = new Point3D( 1194, 521, -90 ); + map = Map.Malas; + return true; + } + } + + return false; + } + + public DarkTidesTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/KronusScroll.cs b/Scripts/Engines/Quests/Dark Tides/Items/KronusScroll.cs new file mode 100644 index 000000000..17af9976e --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/KronusScroll.cs @@ -0,0 +1,166 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; + +namespace Server.Engines.Quests.Necro +{ + public class KronusScroll : QuestItem + { + public override int LabelNumber { get { return 1060149; } } // Calling of Kronus + + [Constructable] + public KronusScroll() : base( 0x227A ) + { + Weight = 1.0; + Hue = 0x44E; + } + + public KronusScroll( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + DarkTidesQuest qs = player.Quest as DarkTidesQuest; + + if ( qs == null ) + return true; + + /*return !( qs.IsObjectiveInProgress( typeof( FindCallingScrollObjective ) ) + || qs.IsObjectiveInProgress( typeof( FindMardothAboutKronusObjective ) ) + || qs.IsObjectiveInProgress( typeof( FindWellOfTearsObjective ) ) + || qs.IsObjectiveInProgress( typeof( UseCallingScrollObjective ) ) );*/ + return false; + } + + private static readonly Rectangle2D m_WellOfTearsArea = new Rectangle2D( 2080, 1346, 10, 10 ); + private static readonly Map m_WellOfTearsMap = Map.Malas; + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from ) ) + return; + + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FindMardothAboutKronusObjective ) ) ) + { + pm.SendLocalizedMessage( 1060151, "", 0x41 ); // You read the scroll, but decide against performing the calling until you are instructed to do so by Mardoth. + } + else if ( qs.IsObjectiveInProgress( typeof( FindWellOfTearsObjective ) ) ) + { + pm.SendLocalizedMessage( 1060152, "", 0x41 ); // You must be at the Well of Tears in the city of Necromancers to use this scroll. + } + else if ( qs.IsObjectiveInProgress( typeof( UseCallingScrollObjective ) ) ) + { + if ( pm.Map == m_WellOfTearsMap && m_WellOfTearsArea.Contains( pm ) ) + { + QuestObjective obj = qs.FindObjective( typeof( UseCallingScrollObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + + Delete(); + new CallingTimer( pm ).Start(); + } + else + { + pm.SendLocalizedMessage( 1060152, "", 0x41 ); // You must be at the Well of Tears in the city of Necromancers to use this scroll. + } + } + else + { + pm.SendLocalizedMessage( 1060150, "", 0x41 ); // A strange terror grips your heart as you attempt to read the scroll. You decide it would be a bad idea to read it out loud. + } + } + } + } + + private class CallingTimer : Timer + { + private PlayerMobile m_Player; + private int m_Step; + + public CallingTimer( PlayerMobile player ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.0 ), 6 ) + { + Priority = TimerPriority.TwentyFiveMS; + + m_Player = player; + m_Step = 0; + } + + protected override void OnTick() + { + if ( m_Player.Deleted ) + { + Stop(); + return; + } + + if ( !m_Player.Mounted ) + m_Player.Animate( Utility.RandomBool() ? 16 : 17, 7, 1, true, false, 0 ); + + if ( m_Step == 4 ) + { + int baseX = KronusScroll.m_WellOfTearsArea.X; + int baseY = KronusScroll.m_WellOfTearsArea.Y; + int width = KronusScroll.m_WellOfTearsArea.Width; + int height = KronusScroll.m_WellOfTearsArea.Height; + Map map = KronusScroll.m_WellOfTearsMap; + + Effects.SendLocationParticles( EffectItem.Create( m_Player.Location, m_Player.Map, TimeSpan.FromSeconds( 1.0 ) ), 0, 0, 0, 0x13C4 ); + Effects.PlaySound( m_Player.Location, m_Player.Map, 0x243 ); + + for ( int i = 0; i < 15; i++ ) + { + int x = baseX + Utility.Random( width ); + int y = baseY + Utility.Random( height ); + int z = map.GetAverageZ( x, y ); + + Point3D from = new Point3D( x, y, z + Utility.RandomMinMax( 5, 20 ) ); + Point3D to = new Point3D( x, y, z ); + + int hue = Utility.RandomList( 0x481, 0x482, 0x489, 0x497, 0x66D ); + + Effects.SendPacket( from, map, new HuedEffect( EffectType.Moving, Serial.Zero, Serial.Zero, 0x36D4, from, to, 0, 0, false, true, hue, 0 ) ); + } + } + + if ( m_Step < 5 ) + { + m_Player.Frozen = true; + } + else // Cast completed + { + m_Player.Frozen = false; + + SummonedPaladin.BeginSummon( m_Player ); + } + + m_Step++; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs b/Scripts/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs new file mode 100644 index 000000000..d6014e53b --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/KronusScrollBox.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Necro +{ + public class KronusScrollBox : MetalBox + { + [Constructable] + public KronusScrollBox() + { + ItemID = 0xE80; + Movable = false; + + for ( int i = 0; i < 40; i++ ) + { + Item scroll = Loot.RandomScroll( 0, 15, SpellbookType.Necromancer ); + scroll.Movable = false; + DropItem( scroll ); + } + } + + public KronusScrollBox( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null && pm.InRange( GetWorldLocation(), 2 ) ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FindCallingScrollObjective ) ); + + if ( (obj != null && !obj.Completed) || DarkTidesQuest.HasLostCallingScroll( from ) ) + { + Item scroll = new KronusScroll(); + + if ( pm.PlaceInBackpack( scroll ) ) + { + pm.SendLocalizedMessage( 1060120, "", 0x41 ); // You rummage through the scrolls until you find the Scroll of Calling. You quickly put it in your pack. + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + else + { + pm.SendLocalizedMessage( 1060148, "", 0x41 ); // You were unable to take the scroll. + scroll.Delete(); + } + } + } + } + + base.OnDoubleClick( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs b/Scripts/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs new file mode 100644 index 000000000..50256a2c1 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/MaabusCoffin.cs @@ -0,0 +1,158 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.Quests.Necro +{ + public class MaabusCoffin : BaseAddon + { + private Maabus m_Maabus; + private Point3D m_SpawnLocation; + + [CommandProperty( AccessLevel.GameMaster )] + public Maabus Maabus { get { return m_Maabus; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D SpawnLocation { get { return m_SpawnLocation; } set { m_SpawnLocation = value; } } + + [Constructable] + public MaabusCoffin() + { + AddComponent( new MaabusCoffinComponent( 0x1C2B, 0x1C2B ), -1, -1, 0 ); + + AddComponent( new MaabusCoffinComponent( 0x1D16, 0x1C2C ), 0, -1, 0 ); + AddComponent( new MaabusCoffinComponent( 0x1D17, 0x1C2D ), 1, -1, 0 ); + AddComponent( new MaabusCoffinComponent( 0x1D51, 0x1C2E ), 2, -1, 0 ); + + AddComponent( new MaabusCoffinComponent( 0x1D4E, 0x1C2A ), 0, 0, 0 ); + AddComponent( new MaabusCoffinComponent( 0x1D4D, 0x1C29 ), 1, 0, 0 ); + AddComponent( new MaabusCoffinComponent( 0x1D4C, 0x1C28 ), 2, 0, 0 ); + } + + public void Awake( Mobile caller ) + { + if ( m_Maabus != null || m_SpawnLocation == Point3D.Zero ) + return; + + foreach ( MaabusCoffinComponent c in Components ) + c.TurnToEmpty(); + + m_Maabus = new Maabus(); + + m_Maabus.Location = m_SpawnLocation; + m_Maabus.Map = Map; + + m_Maabus.Direction = m_Maabus.GetDirectionTo( caller ); + + Timer.DelayCall( TimeSpan.FromSeconds( 7.5 ), new TimerCallback( BeginSleep ) ); + } + + public void BeginSleep() + { + if ( m_Maabus == null ) + return; + + Effects.PlaySound( m_Maabus.Location, m_Maabus.Map, 0x48E ); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerCallback( Sleep ) ); + } + + public void Sleep() + { + if ( m_Maabus == null ) + return; + + Effects.SendLocationParticles( EffectItem.Create( m_Maabus.Location, m_Maabus.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 0x7E7 ); + Effects.PlaySound( m_Maabus.Location, m_Maabus.Map, 0x1FE ); + + m_Maabus.Delete(); + m_Maabus = null; + + foreach ( MaabusCoffinComponent c in Components ) + c.TurnToFull(); + } + + public MaabusCoffin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Maabus ); + writer.Write( (Point3D) m_SpawnLocation ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Maabus = reader.ReadMobile() as Maabus; + m_SpawnLocation = reader.ReadPoint3D(); + + Sleep(); + } + } + + public class MaabusCoffinComponent : AddonComponent + { + private int m_FullItemID; + private int m_EmptyItemID; + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D SpawnLocation + { + get { return Addon is MaabusCoffin ? ((MaabusCoffin)Addon).SpawnLocation : Point3D.Zero; } + set { if ( Addon is MaabusCoffin ) ((MaabusCoffin)Addon).SpawnLocation = value; } + } + + public MaabusCoffinComponent( int itemID ) : this( itemID, itemID ) + { + } + + public MaabusCoffinComponent( int fullItemID, int emptyItemID ) : base( fullItemID ) + { + m_FullItemID = fullItemID; + m_EmptyItemID = emptyItemID; + } + + public void TurnToEmpty() + { + ItemID = m_EmptyItemID; + } + + public void TurnToFull() + { + ItemID = m_FullItemID; + } + + public MaabusCoffinComponent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_FullItemID ); + writer.Write( (int) m_EmptyItemID ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_FullItemID = reader.ReadInt(); + m_EmptyItemID = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs b/Scripts/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs new file mode 100644 index 000000000..a8dab9a9b --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs @@ -0,0 +1,146 @@ +using System; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class ScrollOfAbraxus : QuestItem + { + public override int LabelNumber { get { return 1028827; } } // Scroll of Abraxus + + [Constructable] + public ScrollOfAbraxus() : base( 0x227B ) + { + Weight = 1.0; + } + + public override bool CanDrop( PlayerMobile player ) + { + DarkTidesQuest qs = player.Quest as DarkTidesQuest; + + if ( qs == null ) + return true; + + //return !( qs.IsObjectiveInProgress( typeof( RetrieveAbraxusScrollObjective ) ) || qs.IsObjectiveInProgress( typeof( ReadAbraxusScrollObjective ) ) ); + return false; + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + PlayerMobile pm = RootParent as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( RetrieveAbraxusScrollObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.SendGump( new ScrollOfAbraxusGump() ); + + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( ReadAbraxusScrollObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public ScrollOfAbraxus( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ScrollOfAbraxusGump : Gump + { + public ScrollOfAbraxusGump() : base( 150, 50 ) + { + AddPage( 0 ); + + AddImage( 0, 0, 1228 ); + AddImage( 340, 255, 9005 ); + + /* Security at the Crystal Cave

+ * + * We have taken great measuresto ensure the safety of the + * Scroll of Calling, which we have so valiantly taken from + * the Necromancer Maabus during the battle of the wood + * nearly 200 years ago.

+ * + * The scroll must never fall into the hands of the + * Necromancers again, lest they use it to summon the ancient + * daemon Kronus. The scroll of calling is a necessity in the + * series of dark rites the Necromancers must perform to once again + * re-awaken Kronus.

+ * + * Should Kronus ever rise again, the days of the Paladins, and + * indeed humanity as we know it will be numbered.

+ * + * For this reason, we have posted the honorable Horus, former + * General of the Northern Legions to guard the entrance of the + * Crystal Cave where we keep the Scroll of Calling. Horus was + * infused with magical life from the tree Urywen during his last + * battle. The power gave him eternal life, but it also, + * unfortunately, took his eye sight.

+ * + * Since Horus cannot see those he admits to the Crystal Cave, + * he will only allow those that know the secret password to enter. + * Speak the following word to Horus and he shall grant you passage + * to the Crystal Cave:

+ * + * Urywen

+ * + * Do not speak this password anywhere except when seeking passage + * into the Crystal Cave, as our adversaries are lurking in the + * shadows – they are everywhere.

Go with the light, friend.

+ * + * - Frater Melkeer + */ + AddHtmlLocalized( 25, 36, 350, 210, 1060116, 1, false, true ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Items/VaultOfSecretsBarrier.cs b/Scripts/Engines/Quests/Dark Tides/Items/VaultOfSecretsBarrier.cs new file mode 100644 index 000000000..cf9446558 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Items/VaultOfSecretsBarrier.cs @@ -0,0 +1,53 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class VaultOfSecretsBarrier : Item + { + [Constructable] + public VaultOfSecretsBarrier() : base( 0x49E ) + { + Movable = false; + Visible = false; + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m.AccessLevel > AccessLevel.Player ) + return true; + + PlayerMobile pm = m as PlayerMobile; + + if ( pm != null && pm.Profession == 4 ) + { + m.SendLocalizedMessage( 1060188, "", 0x24 ); // The wicked may not enter! + return false; + } + + return base.OnMoveOver( m ); + } + + public VaultOfSecretsBarrier( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Mobiles/Horus.cs b/Scripts/Engines/Quests/Dark Tides/Mobiles/Horus.cs new file mode 100644 index 000000000..18d093044 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Mobiles/Horus.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.ContextMenus; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class Horus : BaseQuester + { + [Constructable] + public Horus() : base( "the Guardian" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83F3; + Body = 0x190; + + Name = "Horus"; + } + + public override void InitOutfit() + { + AddItem( SetHue( new PlateLegs(), 0x849 ) ); + AddItem( SetHue( new PlateChest(), 0x849 ) ); + AddItem( SetHue( new PlateArms(), 0x849 ) ); + AddItem( SetHue( new PlateGloves(), 0x849 ) ); + AddItem( SetHue( new PlateGorget(), 0x849 ) ); + + AddItem( SetHue( new Bardiche(), 0x482 ) ); + + AddItem( SetHue( new Boots(), 0x001 ) ); + AddItem( SetHue( new Cloak(), 0x482 ) ); + + Utility.AssignRandomHair( this, false ); + Utility.AssignRandomFacialHair( this, false ); + } + + public override int GetAutoTalkRange( PlayerMobile m ) + { + return 3; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + QuestSystem qs = to.Quest; + + return ( qs is DarkTidesQuest && qs.IsObjectiveInProgress( typeof( FindCrystalCaveObjective ) ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FindCrystalCaveObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( InRange( m.Location, 2 ) && !InRange( oldLocation, 2 ) && m is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m; + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( ReturnToCrystalCaveObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + else + { + obj = qs.FindObjective( typeof( FindHorusAboutRewardObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + cont.DropItem( new Gold( 500 ) ); + + BaseJewel jewel = new GoldBracelet(); + if ( Core.AOS ) + BaseRunicTool.ApplyAttributesTo( jewel, 3, 20, 40 ); + cont.DropItem( jewel ); + + if ( !pm.PlaceInBackpack( cont ) ) + { + cont.Delete(); + pm.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + else + { + obj.Complete(); + } + } + } + } + } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SpeakCavePasswordObjective ) ); + bool enabled = ( obj != null && !obj.Completed ); + + list.Add( new SpeakPasswordEntry( this, pm, enabled ) ); + } + } + } + } + + public virtual void OnPasswordSpoken( PlayerMobile from ) + { + QuestSystem qs = from.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SpeakCavePasswordObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + return; + } + } + + from.SendLocalizedMessage( 1060185 ); // Horus ignores you. + } + + private class SpeakPasswordEntry : ContextMenuEntry + { + private Horus m_Horus; + private PlayerMobile m_From; + + public SpeakPasswordEntry( Horus horus, PlayerMobile from, bool enabled ) : base( 6193, 3 ) + { + m_Horus = horus; + m_From = from; + + if ( !enabled ) + Flags |= CMEFlags.Disabled; + } + + public override void OnClick() + { + if ( m_From.Alive ) + m_Horus.OnPasswordSpoken( m_From ); + } + } + + public Horus( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Mobiles/Maabus.cs b/Scripts/Engines/Quests/Dark Tides/Mobiles/Maabus.cs new file mode 100644 index 000000000..7cc7707f7 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Mobiles/Maabus.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Necro +{ + public class Maabus : BaseQuester + { + public Maabus() + { + } + + public override void InitBody() + { + Body = 0x94; + Name = "Maabus"; + } + + public Maabus( Serial serial ) : base( serial ) + { + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return false; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs b/Scripts/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs new file mode 100644 index 000000000..e9175203f --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Mobiles/Mardoth.cs @@ -0,0 +1,230 @@ +using System; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Necro +{ + public class Mardoth : BaseQuester + { + [Constructable] + public Mardoth() : base( "the Ancient Necromancer" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x8849; + Body = 0x190; + + Name = "Mardoth"; + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is DarkTidesQuest ) + { + if ( dropped is DarkTidesHorn ) + { + if ( player.Young ) + { + DarkTidesHorn horn = (DarkTidesHorn)dropped; + + if ( horn.Charges < 10 ) + { + SayTo( from, 1049384 ); // I have recharged the item for you. + horn.Charges = 10; + } + else + { + SayTo( from, 1049385 ); // That doesn't need recharging yet. + } + } + else + { + player.SendMessage( "You must be young to have this item recharged." ); + } + + return false; + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override void InitOutfit() + { + AddItem( new Sandals( 0x1 ) ); + AddItem( new Robe( 0x66D ) ); + AddItem( new BlackStaff() ); + AddItem( new WizardsHat( 0x1 ) ); + + FacialHairItemID = 0x2041; + FacialHairHue = 0x482; + + HairItemID = 0x203C; + HairHue = 0x482; + + Item gloves = new BoneGloves(); + gloves.Hue = 0x66D; + AddItem( gloves ); + + Item gorget = new PlateGorget(); + gorget.Hue = 0x1; + AddItem( gorget ); + } + + public override int GetAutoTalkRange( PlayerMobile m ) + { + return 3; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + DarkTidesQuest qs = to.Quest as DarkTidesQuest; + + if ( qs == null ) + return ( to.Quest == null && QuestSystem.CanOfferQuest( to, typeof( DarkTidesQuest ) ) ); + + return ( qs.FindObjective( typeof( FindMardothAboutVaultObjective ) ) != null ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is DarkTidesQuest ) + { + if ( DarkTidesQuest.HasLostCallingScroll( player ) ) + { + qs.AddConversation( new LostCallingScrollConversation( true ) ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( FindMardothAboutVaultObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else + { + obj = qs.FindObjective( typeof( FindMardothAboutKronusObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else + { + obj = qs.FindObjective( typeof( FindMardothEndObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + cont.DropItem( new PigIron( 20 ) ); + cont.DropItem( new NoxCrystal( 20 ) ); + cont.DropItem( new BatWing( 25 ) ); + cont.DropItem( new DaemonBlood( 20 ) ); + cont.DropItem( new GraveDust( 20 ) ); + + BaseWeapon weapon = new BoneHarvester(); + + weapon.Slayer = SlayerName.OrcSlaying; + + if ( Core.AOS ) + { + BaseRunicTool.ApplyAttributesTo( weapon, 3, 20, 40 ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)BaseCreature.RandomMinMaxScaled( 2, 4 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)BaseCreature.RandomMinMaxScaled( 2, 4 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)BaseCreature.RandomMinMaxScaled( 2, 4 ); + } + + cont.DropItem( weapon ); + + cont.DropItem( new BankCheck( 2000 ) ); + cont.DropItem( new EnchantedSextant() ); + + if ( !player.PlaceInBackpack( cont ) ) + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + else + { + obj.Complete(); + } + } + else if ( contextMenu ) + { + FocusTo( player ); + player.SendLocalizedMessage( 1061821 ); // Mardoth has nothing more for you at this time. + } + } + } + } + } + else if ( qs == null && QuestSystem.CanOfferQuest( player, typeof( DarkTidesQuest ) ) ) + { + new DarkTidesQuest( player ).SendOffer(); + } + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( m is PlayerMobile && !m.Frozen && !m.Alive && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && InLOS( m ) ) + { + if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else + { + Direction = GetDirectionTo( m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.Healer ) ); + } + } + } + + public Mardoth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs b/Scripts/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs new file mode 100644 index 000000000..b9d1fda91 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs @@ -0,0 +1,241 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Necro +{ + public class SummonedPaladin : BaseCreature + { + private PlayerMobile m_Necromancer; + private bool m_ToDelete; + + public SummonedPaladin( PlayerMobile necromancer ) : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + m_Necromancer = necromancer; + + InitStats( 45, 30, 5 ); + Title = "the Paladin"; + + Hue = 0x83F3; + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, false ); + + FacialHairHue = HairHue; + + AddItem( new Boots( 0x1 ) ); + AddItem( new ChainChest() ); + AddItem( new ChainLegs() ); + AddItem( new RingmailArms() ); + AddItem( new PlateHelm() ); + AddItem( new PlateGloves() ); + AddItem( new PlateGorget() ); + + AddItem( new Cloak( 0xCF ) ); + + AddItem( new ThinLongsword() ); + + SetSkill( SkillName.Swords, 50.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + + PackGold( 500 ); + } + + public override bool IsHarmfulCriminal(Mobile target) + { + if ( target == m_Necromancer ) + return false; + + return base.IsHarmfulCriminal( target ); + } + + public override bool ClickTitle{ get { return false; } } + + public override bool PlayerRangeSensitive{ get { return false; } } + + public override void OnThink() + { + if ( !m_ToDelete && !Frozen ) + { + if ( m_Necromancer == null || m_Necromancer.Deleted || m_Necromancer.Map == Map.Internal ) + { + Delete(); + return; + } + + if ( Combatant != m_Necromancer ) + Combatant = m_Necromancer; + + if ( !m_Necromancer.Alive ) + { + QuestSystem qs = m_Necromancer.Quest; + + if ( qs is DarkTidesQuest && qs.FindObjective( typeof( FindMardothEndObjective ) ) == null ) + qs.AddObjective( new FindMardothEndObjective( false ) ); + + Say( 1060139, m_Necromancer.Name ); // You have made my work easy for me, ~1_NAME~. My task here is done. + + m_ToDelete = true; + + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerCallback( Delete ) ); + } + else if ( m_Necromancer.Map != Map || GetDistanceToSqrt( m_Necromancer ) > RangePerception + 1 ) + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( m_Necromancer.Location, m_Necromancer.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + + Map = m_Necromancer.Map; + Location = m_Necromancer.Location; + + PlaySound( 0x1FE ); + + Say( 1060140 ); // You cannot escape me, knave of evil! + } + } + + base.OnThink(); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + QuestSystem qs = m_Necromancer.Quest; + + if ( qs is DarkTidesQuest && qs.FindObjective( typeof( FindMardothEndObjective ) ) == null ) + qs.AddObjective( new FindMardothEndObjective( true ) ); + } + + public SummonedPaladin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Necromancer ); + writer.Write( (bool) m_ToDelete ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Necromancer = reader.ReadMobile() as PlayerMobile; + m_ToDelete = reader.ReadBool(); + + if ( m_ToDelete ) + Delete(); + } + + public static void BeginSummon( PlayerMobile player ) + { + new SummonTimer( player ).Start(); + } + + private class SummonTimer : Timer + { + private PlayerMobile m_Player; + private SummonedPaladin m_Paladin; + private int m_Step; + + public SummonTimer( PlayerMobile player ) : base( TimeSpan.FromSeconds( 4.0 ) ) + { + Priority = TimerPriority.FiftyMS; + + m_Player = player; + } + + protected override void OnTick() + { + if ( m_Player.Deleted ) + { + if ( m_Step > 0 ) + m_Paladin.Delete(); + + return; + } + + if ( m_Step > 0 && m_Paladin.Deleted ) + return; + + if ( m_Step == 0 ) + { + SummonedPaladinMoongate moongate = new SummonedPaladinMoongate(); + moongate.MoveToWorld( new Point3D( 2091, 1348, -90 ), Map.Malas ); + + Effects.PlaySound( moongate.Location, moongate.Map, 0x20E ); + + m_Paladin = new SummonedPaladin( m_Player ); + m_Paladin.Frozen = true; + + m_Paladin.Location = moongate.Location; + m_Paladin.Map = moongate.Map; + + Delay = TimeSpan.FromSeconds( 2.0 ); + Start(); + } + else if ( m_Step == 1 ) + { + m_Paladin.Direction = m_Paladin.GetDirectionTo( m_Player ); + m_Paladin.Say( 1060122 ); // STOP WICKED ONE! + + Delay = TimeSpan.FromSeconds( 3.0 ); + Start(); + } + else + { + m_Paladin.Frozen = false; + + m_Paladin.Say( 1060123 ); // I will slay you before I allow you to complete your evil rites! + + m_Paladin.Combatant = m_Player; + } + + m_Step++; + } + } + } + + public class SummonedPaladinMoongate : Item + { + public SummonedPaladinMoongate() : base( 0xF6C ) + { + Movable = false; + Hue = 0x482; + Light = LightType.Circle300; + + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Delete ) ); + } + + public SummonedPaladinMoongate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Dark Tides/Objectives.cs b/Scripts/Engines/Quests/Dark Tides/Objectives.cs new file mode 100644 index 000000000..2733d83f8 --- /dev/null +++ b/Scripts/Engines/Quests/Dark Tides/Objectives.cs @@ -0,0 +1,642 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Necro +{ + public class AnimateMaabusCorpseObjective : QuestObjective + { + public override object Message + { + get + { + /* Re-animate the corpse of Maabus using your Animate Dead + * spell and question him about the Kronus rituals. + */ + return 1060102; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1023643, 8787 ) // spellbook + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public AnimateMaabusCorpseObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new MaabasConversation() ); + } + } + + public class FindCrystalCaveObjective : QuestObjective + { + public override object Message + { + get + { + /* Take the teleporter in the corner of Maabus' tomb to + * the crystal cave where the calling scroll is kept. + */ + return 1060104; + } + } + + public FindCrystalCaveObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new HorusConversation() ); + } + } + + public class FindMardothAboutVaultObjective : QuestObjective + { + public override object Message + { + get + { + /* Infiltrate the city of the Paladins and figure out a way into + * the Vault. See Mardoth for help with this objective. + */ + return 1060106; + } + } + + public FindMardothAboutVaultObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new MardothVaultConversation() ); + } + } + + public class FindMaabusTombObjective : QuestObjective + { + public override object Message + { + get + { + /* Step onto the teleporter near Mardoth and follow the path + * of glowing runes to the tomb of Maabus. + */ + return 1060124; + } + } + + public FindMaabusTombObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 2024, 1240, -90 ), 3 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddObjective( new FindMaabusCorpseObjective() ); + } + } + + public class FindMaabusCorpseObjective : QuestObjective + { + public override object Message + { + get + { + /* This is the tomb of Maabus. Enter within and find + * the corpse of the ancient necromancer. + */ + return 1061142; + } + } + + public FindMaabusCorpseObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 2024, 1223, -90 ), 3 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddObjective( new AnimateMaabusCorpseObjective() ); + } + } + + public class FindCityOfLightObjective : QuestObjective + { + public override object Message + { + get + { + /* Use the teleporter near Mardoth to be transported + * to the Paladin City of Light. + */ + return 1060108; + } + } + + public FindCityOfLightObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 1076, 519, -90 ), 5 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddObjective( new FindVaultOfSecretsObjective() ); + } + } + + public class FindVaultOfSecretsObjective : QuestObjective + { + public override object Message + { + get + { + /* Follow the road paved with glowing runes to + * find the Vault of Secrets. Be careful not + * to give yourself away as a Necromancer while + * in the city. + */ + return 1060109; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1023676, 3679 ) // glowing rune + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public FindVaultOfSecretsObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 1072, 455, -90 ), 1 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddConversation( new VaultOfSecretsConversation() ); + } + } + + public class FetchAbraxusScrollObjective : QuestObjective + { + public override object Message + { + get + { + // Summon your Horde Minion familiar to fetch the scroll for you. + return 1060196; + } + } + + public FetchAbraxusScrollObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 1076, 450, -84 ), 5 ) ) + { + HordeMinionFamiliar hmf = Spells.Necromancy.SummonFamiliarSpell.Table[System.From] as HordeMinionFamiliar; + + if ( hmf != null && hmf.InRange( System.From, 5 ) && hmf.TargetLocation == Point2D.Zero ) + { + System.From.SendLocalizedMessage( 1060113 ); // You instinctively will your familiar to fetch the scroll for you. + hmf.TargetLocation = new Point2D( 1076, 450 ); + } + } + } + + public override void OnComplete() + { + System.AddObjective( new RetrieveAbraxusScrollObjective() ); + } + } + + public class RetrieveAbraxusScrollObjective : QuestObjective + { + public override object Message + { + get + { + /* Double click your Horde Minion to open his pack and retrieve + * the Scroll of Abraxus that he looted for you. + */ + return 1060199; + } + } + + public RetrieveAbraxusScrollObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ReadAbraxusScrollConversation() ); + } + } + + public class ReadAbraxusScrollObjective : QuestObjective + { + public override object Message + { + get + { + /* Find the Crystal Cave password by reading (double click) + * the golden scroll entitled "Scroll of Abraxus" that you + * got from your familiar.. + */ + return 1060125; + } + } + + public ReadAbraxusScrollObjective() + { + } + + public override void OnComplete() + { + System.AddObjective( new ReturnToCrystalCaveObjective() ); + } + } + + public class ReturnToCrystalCaveObjective : QuestObjective + { + public override object Message + { + get + { + /* Now that you have the password, return to the Crystal Cave + * to speak with the guard there. Use the teleporter outside + * of the vault to get there if necessary. + */ + return 1060115; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1026153, 6178 ) // teleporter + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public ReturnToCrystalCaveObjective() + { + } + + public override void OnComplete() + { + System.AddObjective( new SpeakCavePasswordObjective() ); + } + } + + public class SpeakCavePasswordObjective : QuestObjective + { + public override object Message + { + get + { + /* Speak the secret word that you read in the scroll + * stolen from the Vault to Horus the guard, using + * his context menu. + */ + return 1060117; + } + } + + public SpeakCavePasswordObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new SecondHorusConversation() ); + } + } + + public class FindCallingScrollObjective : QuestObjective + { + public override object Message + { + get + { + /* Enter the Crystal Cave and find the Scroll of Calling. + * The barrier will now allow you to pass. + */ + return 1060119; + } + } + + private int m_SkitteringHoppersKilled; + private bool m_HealConversationShown; + private bool m_SkitteringHoppersDisposed; + + public FindCallingScrollObjective() + { + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + return !m_SkitteringHoppersDisposed && from is SkitteringHopper; + } + + public override bool GetKillEvent( BaseCreature creature, Container corpse ) + { + return !m_SkitteringHoppersDisposed; + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is SkitteringHopper ) + { + if ( !m_HealConversationShown ) + { + m_HealConversationShown = true; + System.AddConversation( new HealConversation() ); + } + + if ( ++m_SkitteringHoppersKilled >= 5 ) + { + m_SkitteringHoppersDisposed = true; + System.AddObjective( new FindHorusAboutRewardObjective() ); + } + } + } + + public override void OnComplete() + { + System.AddObjective( new FindMardothAboutKronusObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_SkitteringHoppersKilled = reader.ReadEncodedInt(); + m_HealConversationShown = reader.ReadBool(); + m_SkitteringHoppersDisposed = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_SkitteringHoppersKilled ); + writer.Write( (bool) m_HealConversationShown ); + writer.Write( (bool) m_SkitteringHoppersDisposed ); + } + } + + public class FindHorusAboutRewardObjective : QuestObjective + { + public override object Message + { + get + { + /* You have disposed of the creatures as Horus has asked. + * See him on your way out of the Crystal Cave to claim your reward. + */ + return 1060126; + } + } + + public FindHorusAboutRewardObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new HorusRewardConversation() ); + } + } + + public class FindMardothAboutKronusObjective : QuestObjective + { + public override object Message + { + get + { + /* You have obtained the scroll of calling. See Mardoth + * for further instructions. + */ + return 1060127; + } + } + + public FindMardothAboutKronusObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new MardothKronusConversation() ); + } + } + + public class FindWellOfTearsObjective : QuestObjective + { + public override object Message + { + get + { + /* Follow the red lanterns to the Well of Tears where + * you will perform the calling of Kronus. + */ + return 1060128; + } + } + + public FindWellOfTearsObjective() + { + } + + private static readonly Rectangle2D m_WellOfTearsArea = new Rectangle2D( 2080, 1346, 10, 10 ); + + private bool m_Inside; + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && m_WellOfTearsArea.Contains( System.From.Location ) ) + { + if ( DarkTidesQuest.HasLostCallingScroll( System.From ) ) + { + if ( !m_Inside ) + System.AddConversation( new LostCallingScrollConversation( false ) ); + } + else + { + Complete(); + } + + m_Inside = true; + } + else + { + m_Inside = false; + } + } + + public override void OnComplete() + { + System.AddObjective( new UseCallingScrollObjective() ); + } + } + + public class UseCallingScrollObjective : QuestObjective + { + public override object Message + { + get + { + /* Use the Scroll of Calling (double click) near the + * Well of Tears to charge the waters for the arrival + * of Kronus. + */ + return 1060130; + } + } + + public UseCallingScrollObjective() + { + } + } + + public class FindMardothEndObjective : QuestObjective + { + private bool m_Victory; + + public override object Message + { + get + { + if ( m_Victory ) + { + /* Victory! You have done as Mardoth has asked of you. + * Take as much of your foe's loot as you can carry + * and return to Mardoth for your reward. + */ + return 1060131; + } + else + { + /* Although you were slain by the cowardly paladin, + * you managed to complete the rite of calling as + * instructed. Return to Mardoth. + */ + return 1060132; + } + } + } + + public FindMardothEndObjective( bool victory ) + { + m_Victory = victory; + } + + // Serialization + public FindMardothEndObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new MardothEndConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Victory = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Victory ); + } + } + + public class FindBankObjective : QuestObjective + { + public override object Message + { + get + { + /* Use the enchanted sextant in your pack to locate + * the nearest bank. Go there and speak with the + * Banker. + */ + return 1060134; + } + } + + public FindBankObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 2048, 1345, -84 ), 5 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddObjective( new CashBankCheckObjective() ); + } + } + + public class CashBankCheckObjective : QuestObjective + { + public override object Message + { + get + { + /* You have arrived at the Bank. Open your bank box + * and then cash the check that Mardoth gave you. + */ + return 1060644; + } + } + + public CashBankCheckObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new BankerConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Conversations.cs b/Scripts/Engines/Quests/Emino's Undertaking/Conversations.cs new file mode 100644 index 000000000..ca8ea6495 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Conversations.cs @@ -0,0 +1,610 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Important Quest Information

+ * + * During your quest, any important information that a + * NPC gives you, will appear in a + * window such as this one. You can review the information at + * any time during your quest.

Getting Help

+ * + * Some of the text you will come across during your quest, will + * be underlined links to the codex of + * wisdom, or online help system. You can click on the text + * to get detailed information on the underlined subject. You + * may also access the Codex Of Wisdom by pressing "F1" or by + * clicking on the "?" on the toolbar at the top of your screen.

+ * + * Context Menus

Context menus can be called up by + * single left-clicking (or Shift + single left-click, if you + * changed it) on most objects or NPCs in the world. Nearly + * everything, including your own avatar will have context menus + * available. Bringing up your avatar's context menu will give + * you options to cancel your quest and review various quest + * information.

+ */ + return 1049092; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindEminoBeginObjective() ); + } + } + + public class FindZoelConversation : QuestConversation + { + public override object Message + { + get + { + /* Ah, another fledgling unfurls its wings. Welcome to my + * home young one.

+ * + * I am Daimyo Emino, a passionate collector of sorts. + * One who is vengeful towards those impeding my reign.

+ * + * You have the look of someone who could help me but + * your skills are untested. Are you willing to prove + * your mettle as my hireling?

+ * + * Elite Ninja Zoel awaits you in the backyard. He will + * lead you to the first trial. You will be directed + * further when you arrive at your destination. You + * should speak to him before exploring the yard or cave entrance. + */ + return 1063175; + } + } + + public FindZoelConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindZoelObjective() ); + } + } + + public class RadarConversation : QuestConversation + { + public override object Message + { + get + { + /* To view the surrounding area, you should learn about the Radar Map.

+ * + * The Radar Map (or Overhead View) can be opened by pressing 'ALT-R' + * on your keyboard. It shows your immediate surroundings from a + * bird's eye view.

+ * + * Pressing ALT-R twice, will enlarge the Radar Map a little. + * Use the Radar Map often as you travel throughout the world + * to familiarize yourself with your surroundings. + * */ + return 1063033; + } + } + + public override bool Logged{ get{ return false; } } + + public RadarConversation() + { + } + } + + public class EnterCaveConversation : QuestConversation + { + public override object Message + { + get + { + /* Zoel studies your face as you approach him. Wryly, he says:

+ * + * Daimyo Emino has sent another already? The stains from the + * last have not yet dried!

+ * + * No matter, we’ll finish you off and clean it all at once, eh?

+ * + * Now to the point, your only task is to survive in the abandoned inn.

+ * + * You will be instructed when you need to act and when you should + * return to one of us.

+ * + * Only a true Ninja is deft enough to finish and remain alive.

+ * + * Your future... or your demise... lies in this cave beyond.

+ * + * Now go. + */ + return 1063177; + } + } + + public EnterCaveConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new EnterCaveObjective() ); + } + } + + public class SneakPastGuardiansConversation : QuestConversation + { + public override object Message + { + get + { + /* A glowing archway stands before you.

+ * + * To either side of its frame are mounted demon heads, + * breathing fire and watching your every move.

+ * + * To pass through, you must first vanish from the demons’ + * sight. Only then can you slowly traverse the entryway. + */ + return 1063180; + } + } + + public SneakPastGuardiansConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SneakPastGuardiansObjective() ); + } + } + + public class NeedToHideConversation : QuestConversation + { + public override object Message + { + get + { + /* You’ll need to hide in order to pass through the door.

+ * + * To find out how to use active skills, visit the + * Codex of Wisdom. + * To activate a skill, locate it on your skills + * list and click the blue button located to the + * left of the skill's name.

Once you have + * successfully hidden, you may move slowly through the door. + * + */ + return 1063181; + } + } + + public NeedToHideConversation() + { + } + } + + public class UseTeleporterConversation : QuestConversation + { + public override object Message + { + get + { + /* + * Through the door lies a short passageway. + * The path ends abruptly at a strange tile on the floor. + * The special tile is known as a teleporter. + * Step on the teleporter tile and you will be transported + * to a new location. + */ + return 1063182; + } + } + + public UseTeleporterConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new UseTeleporterObjective() ); + } + } + + public class GiveZoelNoteConversation : QuestConversation + { + public override object Message + { + get + { + /* Daimyo Emino smiles as you approach him:

+ * + * I see that you have survived both the first trial and Zoel’s temper.

+ * + * For that you have been rewarded with Leggings and Gloves + * befitting your occupation. The material is the only armor + * a True Ninja needs.

+ * + * You have yet to prove yourself fully, young hireling. + * Another trial must be met. Off to Zoel you go. + * Bring him this note so he knows we have spoken. + */ + return 1063184; + } + } + + public GiveZoelNoteConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GiveZoelNoteObjective() ); + } + } + + public class LostNoteConversation : QuestConversation + { + public override object Message + { + get + { + // You have lost my note? I will scribe another for you. Try not to lose this one. + return 1063187; + } + } + + public override bool Logged{ get{ return false; } } + + public LostNoteConversation() + { + } + } + + public class GainInnInformationConversation : QuestConversation + { + public override object Message + { + get + { + /* Zoel quickly grabs the scroll from your + * hand and reads the note:

+ * + * Still alive then? You’ll have to impress me + * further before I will give my approval of + * you to Daimyo Emino.

+ * + * You must return to the inn and begin the next trial.

+ * + * We believe an associate has, shall we say, + * inadvertently negated our contract.

+ * + * Find out what information you can and return + * to Daimyo Emino with the news. And be careful + * not to lose your head.

+ * + * The Blue Teleporter Tile in Daimyo Emino’s + * residence will lead you to your fate. + * I suggest you hurry.

+ * + * ...And take care to tread softly. There + * is no greater traitor than a heavy footfall upon a path. + */ + return 1063189; + } + } + + public GainInnInformationConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GainInnInformationObjective() ); + } + } + + public class ReturnFromInnConversation : QuestConversation + { + public override object Message + { + get + { + /* You quietly approach the door and see + * a woman named Jedah Entille speaking + * to a shady figure in dark clothing. + * You move closer so you can overhear + * the conversation. Fortunately, your + * entrance did not alert the preoccupied party.

+ * + * Jedah’s brash voice permeates the air:

+ * + * Now that it is hidden, we’ll hide out here + * until Daimyo Emino forgets about us. + * Once he realizes his beloved sword is missing, + * he’ll surely start looking for the thieves. + * We will be long gone by that time.


+ * + * After overhearing the conversation, + * you understand why you were sent on + * this trial. You must immediately tell + * Daimyo Emino what you have learned. + * */ + return 1063196; + } + } + + public ReturnFromInnConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnFromInnObjective() ); + } + } + + public class SearchForSwordConversation : QuestConversation + { + public override object Message + { + get + { + /* Daimyo Emino frowns as you relay the information. + * He pauses for a moment before speaking to you:

+ * + * Jedah was once one of my most promising students, + * but her greed will be her downfall.

+ * + * I will send one of my disciples to deal with her later. + * It is more important to get that sword back first.

+ * + * I’m counting on you to find it. She would have kept it + * close to her. Take the White Teleporter, located in my + * backyard, and check inside boxes and chests around the + * treasure room of the inn and return it to me when you + * find it.

+ * + * Be very careful. Jedah was an expert with traps and no + * doubt she’s protecting the sword with them.

+ * + * If you find a trap, try timing it and you may be able + * to avoid damage.

+ * + * I’ve provided you with several heal potions in case you + * become injured.

+ * + * In the bag you will also find more clothing appropriate + * to your new found profession.

+ * + * Please return the sword to me. I implore you not to take + * anything else that may be hidden in the Inn.

+ * + * Thank you. + */ + return 1063199; + } + } + + public SearchForSwordConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SearchForSwordObjective() ); + } + } + + public class HallwayWalkConversation : QuestConversation + { + public override object Message + { + get + { + /* A narrow hallway greets the teleporter. + * The enclosed space is the perfect setting + * for dangerous traps. Walk through the + * hallway being careful to avoid the traps. + * You may be able to time the traps to avoid injury. + */ + return 1063201; + } + } + + public HallwayWalkConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new HallwayWalkObjective() ); + } + } + + public class ReturnSwordConversation : QuestConversation + { + public override object Message + { + get + { + /* The lid of the chest refuses to budge at first, + * but slowly you are able to pry the lid open.

+ * + * Inside lies the sword you have been in search of. + * You quickly take the sword and stash it in your backpack. + * Bring the sword back to Daimyo Emino. + */ + return 1063203; + } + } + + public ReturnSwordConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnSwordObjective() ); + } + } + + public class SlayHenchmenConversation : QuestConversation + { + public override object Message + { + get + { + /* Screams echo through the chamber as you walk + * away from the chest. Jedah’s Henchmen have + * become cognizant of your presence.

+ * + * It is time for your Ninja Spirit to come alive. + * Slay 3 of the Henchmen before returning to Daimyo Emino. + */ + return 1063205; + } + } + + public SlayHenchmenConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SlayHenchmenObjective() ); + } + } + + public class ContinueSlayHenchmenConversation : QuestConversation + { + public override object Message + { + get + { + // Continue killing the henchmen! + return 1063208; + } + } + + public override bool Logged{ get{ return false; } } + + public ContinueSlayHenchmenConversation() + { + } + } + + public class GiveEminoSwordConversation : QuestConversation + { + public override object Message + { + get + { + /* Go to Daimyo Emino. Go back through the chamber the way you came.

+ * + * Give Daimyo Emino the sword when you've returned to his side. + */ + return 1063211; + } + } + + public GiveEminoSwordConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GiveEminoSwordObjective() ); + } + } + + public class LostSwordConversation : QuestConversation + { + public override object Message + { + get + { + // What? You have returned without the sword? You need to go back and get it again! + return 1063212; + } + } + + public override bool Logged{ get{ return false; } } + + public LostSwordConversation() + { + } + } + + public class EarnGiftsConversation : QuestConversation + { + public override object Message + { + get + { + /* Beyond this path lies Zento City, your future home. + * To the right of the cave entrance you will find a luminous oval + * object known as a Moongate, step through it and you'll + * find yourself in Zento.

+ * + * You may want to visit Ansella Gryen when you arrive.

+ * + * Please accept the gifts I have placed in your pack. You + * have earned them. Farewell for now. + */ + return 1063216; + } + } + + public EarnGiftsConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class EarnLessGiftsConversation : QuestConversation + { + public override object Message + { + get + { + /* You have earned these gifts for returning the sword. + * For that I thank you.

+ * + * However, your reward has been lessened by your greed + * in the treasure room. Do not think I did not notice your full pockets. + */ + return 1063217; + } + } + + public EarnLessGiftsConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/EminosUndertakingQuest.cs b/Scripts/Engines/Quests/Emino's Undertaking/EminosUndertakingQuest.cs new file mode 100644 index 000000000..6f800ed53 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/EminosUndertakingQuest.cs @@ -0,0 +1,159 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class EminosUndertakingQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Ninja.AcceptConversation ), + typeof( Ninja.FindZoelConversation ), + typeof( Ninja.RadarConversation ), + typeof( Ninja.EnterCaveConversation ), + typeof( Ninja.SneakPastGuardiansConversation ), + typeof( Ninja.NeedToHideConversation ), + typeof( Ninja.UseTeleporterConversation ), + typeof( Ninja.GiveZoelNoteConversation ), + typeof( Ninja.LostNoteConversation ), + typeof( Ninja.GainInnInformationConversation ), + typeof( Ninja.ReturnFromInnConversation ), + typeof( Ninja.SearchForSwordConversation ), + typeof( Ninja.HallwayWalkConversation ), + typeof( Ninja.ReturnSwordConversation ), + typeof( Ninja.SlayHenchmenConversation ), + typeof( Ninja.ContinueSlayHenchmenConversation ), + typeof( Ninja.GiveEminoSwordConversation ), + typeof( Ninja.LostSwordConversation ), + typeof( Ninja.EarnGiftsConversation ), + typeof( Ninja.EarnLessGiftsConversation ), + typeof( Ninja.FindEminoBeginObjective ), + typeof( Ninja.FindZoelObjective ), + typeof( Ninja.EnterCaveObjective ), + typeof( Ninja.SneakPastGuardiansObjective ), + typeof( Ninja.UseTeleporterObjective ), + typeof( Ninja.GiveZoelNoteObjective ), + typeof( Ninja.GainInnInformationObjective ), + typeof( Ninja.ReturnFromInnObjective ), + typeof( Ninja.SearchForSwordObjective ), + typeof( Ninja.HallwayWalkObjective ), + typeof( Ninja.ReturnSwordObjective ), + typeof( Ninja.SlayHenchmenObjective ), + typeof( Ninja.GiveEminoSwordObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // Emino's Undertaking + return 1063173; + } + } + + public override object OfferMessage + { + get + { + // Your value as a Ninja must be proven. Find Daimyo Emino and accept the test he offers. + return 1063174; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.MaxValue; } } + public override bool IsTutorial{ get{ return true; } } + + public override int Picture{ get{ return 0x15D5; } } + + public EminosUndertakingQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public EminosUndertakingQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + private bool m_SentRadarConversion; + + public override void Slice() + { + if ( !m_SentRadarConversion && ( From.Map != Map.Malas || From.X < 407 || From.X > 431 || From.Y < 801 || From.Y > 830 ) ) + { + m_SentRadarConversion = true; + AddConversation( new RadarConversation() ); + } + + base.Slice(); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_SentRadarConversion = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_SentRadarConversion ); + } + + public static bool HasLostNoteForZoel( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is EminosUndertakingQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( GiveZoelNoteObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( NoteForZoel ) ) == null ); + } + } + + return false; + } + + public static bool HasLostEminosKatana( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is EminosUndertakingQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( GiveEminoSwordObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( EminosKatana ) ) == null ); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/BlueNinjaQuestTeleporter.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/BlueNinjaQuestTeleporter.cs new file mode 100644 index 000000000..da44677dc --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/BlueNinjaQuestTeleporter.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class BlueNinjaQuestTeleporter : DynamicTeleporter + { + public override int LabelNumber{ get{ return 1026157; } } // teleporter + + [Constructable] + public BlueNinjaQuestTeleporter() : base( 0x51C, 0x2 ) + { + } + + public override int NotWorkingMessage{ get{ return 1063198; } } // You stand on the strange floor tile but nothing happens. + + public override bool GetDestination( PlayerMobile player, ref Point3D loc, ref Map map ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest && qs.FindObjective( typeof( GainInnInformationObjective ) ) != null ) + { + loc = new Point3D( 411, 1116, 0 ); + map = Map.Malas; + + return true; + } + + return false; + } + + public BlueNinjaQuestTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs new file mode 100644 index 000000000..0ea9aea56 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs @@ -0,0 +1,48 @@ +using System; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Ninja +{ + public class EminosKatana : QuestItem + { + public override int LabelNumber{ get{ return 1063214; } } // Daimyo Emino's Katana + + [Constructable] + public EminosKatana() : base( 0x13FF ) + { + Weight = 1.0; + } + + public EminosKatana( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + EminosUndertakingQuest qs = player.Quest as EminosUndertakingQuest; + + if ( qs == null ) + return true; + + /*return !qs.IsObjectiveInProgress( typeof( ReturnSwordObjective ) ) + && !qs.IsObjectiveInProgress( typeof( SlayHenchmenObjective ) ) + && !qs.IsObjectiveInProgress( typeof( GiveEminoSwordObjective ) );*/ + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs new file mode 100644 index 000000000..ae15c5ef2 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/EminosKatanaChest.cs @@ -0,0 +1,148 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Engines.Quests.Ninja +{ + public class EminosKatanaChest : WoodenChest + { + [Constructable] + public EminosKatanaChest() + { + Movable = false; + ItemID = 0xE42; + + GenerateTreasure(); + } + + public EminosKatanaChest( Serial serial ) : base( serial ) + { + } + + public override bool IsDecoContainer{ get{ return false; } } + + private void GenerateTreasure() + { + for ( int i = this.Items.Count - 1; i >= 0; i-- ) + this.Items[i].Delete(); + + for ( int i = 0; i < 75; i++ ) + { + switch ( Utility.Random( 3 ) ) + { + case 0: DropItem( new GoldBracelet() ); break; + case 1: DropItem( new GoldRing() ); break; + case 2: DropItem( Loot.RandomGem() ); break; + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.InRange( GetWorldLocation(), 2 ) ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest ) + { + if ( EminosUndertakingQuest.HasLostEminosKatana( from ) ) + { + Item katana = new EminosKatana(); + + if ( !player.PlaceInBackpack( katana ) ) + { + katana.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + else + { + QuestObjective obj = qs.FindObjective( typeof( HallwayWalkObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item katana = new EminosKatana(); + + if ( player.PlaceInBackpack( katana ) ) + { + GenerateTreasure(); + obj.Complete(); + } + else + { + katana.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + } + } + } + + base.OnDoubleClick( from ); + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + return false; + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + return item == this; + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.Quest is EminosUndertakingQuest ) + { + HallwayWalkObjective obj = player.Quest.FindObjective( typeof( HallwayWalkObjective ) ) as HallwayWalkObjective; + + if ( obj != null ) + { + if ( obj.StolenTreasure ) + from.SendLocalizedMessage( 1063247 ); // The guard is watching you carefully! It would be unwise to remove another item from here. + else + return true; + } + } + + return false; + } + + public override void OnItemLifted( Mobile from, Item item ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.Quest is EminosUndertakingQuest ) + { + HallwayWalkObjective obj = player.Quest.FindObjective( typeof( HallwayWalkObjective ) ) as HallwayWalkObjective; + + if ( obj != null ) + obj.StolenTreasure = true; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/GreenNinjaQuestTeleporter.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/GreenNinjaQuestTeleporter.cs new file mode 100644 index 000000000..e18910e48 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/GreenNinjaQuestTeleporter.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class GreenNinjaQuestTeleporter : DynamicTeleporter + { + public override int LabelNumber{ get{ return 1026157; } } // teleporter + + [Constructable] + public GreenNinjaQuestTeleporter() : base( 0x51C, 0x17E ) + { + } + + public override int NotWorkingMessage{ get{ return 1063198; } } // You stand on the strange floor tile but nothing happens. + + public override bool GetDestination( PlayerMobile player, ref Point3D loc, ref Map map ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest && qs.FindObjective( typeof( UseTeleporterObjective ) ) != null ) + { + loc = new Point3D( 410, 1125, 0 ); + map = Map.Malas; + + return true; + } + + return false; + } + + public GreenNinjaQuestTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/GuardianBarrier.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/GuardianBarrier.cs new file mode 100644 index 000000000..54969742a --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/GuardianBarrier.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class GuardianBarrier : Item + { + [Constructable] + public GuardianBarrier() : base( 0x3967 ) + { + Movable = false; + Visible = false; + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m.AccessLevel > AccessLevel.Player ) + return true; + + // If the mobile is to the north of the barrier, allow him to pass + if ( this.Y >= m.Y ) + return true; + + if ( m is BaseCreature ) + { + Mobile master = ((BaseCreature)m).GetMaster(); + + // Allow creatures to cross from the south to the north only if their master is near to the north + if ( master != null && this.Y >= master.Y && master.InRange( this, 4 ) ) + return true; + else + return false; + } + + PlayerMobile pm = m as PlayerMobile; + + if ( pm != null ) + { + EminosUndertakingQuest qs = pm.Quest as EminosUndertakingQuest; + + if ( qs != null ) + { + SneakPastGuardiansObjective obj = qs.FindObjective( typeof( SneakPastGuardiansObjective ) ) as SneakPastGuardiansObjective; + + if ( obj != null ) + { + if ( m.Hidden ) + return true; // Hidden ninjas can pass + + if ( !obj.TaughtHowToUseSkills ) + { + obj.TaughtHowToUseSkills = true; + qs.AddConversation( new NeedToHideConversation() ); + } + } + } + } + + return false; + } + + public GuardianBarrier( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs new file mode 100644 index 000000000..20f30d438 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs @@ -0,0 +1,48 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Ninja +{ + public class NoteForZoel : QuestItem + { + public override int LabelNumber{ get{ return 1063186; } } // A Note for Zoel + + [Constructable] + public NoteForZoel() : base( 0x14EF ) + { + Weight = 1.0; + Hue = 0x6B9; + } + + public NoteForZoel( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + EminosUndertakingQuest qs = player.Quest as EminosUndertakingQuest; + + if ( qs == null ) + return true; + + //return !qs.IsObjectiveInProgress( typeof( GiveZoelNoteObjective ) ); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Items/WhiteNinjaQuestTeleporter.cs b/Scripts/Engines/Quests/Emino's Undertaking/Items/WhiteNinjaQuestTeleporter.cs new file mode 100644 index 000000000..c27c8407c --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Items/WhiteNinjaQuestTeleporter.cs @@ -0,0 +1,59 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class WhiteNinjaQuestTeleporter : DynamicTeleporter + { + public override int LabelNumber{ get{ return 1026157; } } // teleporter + + [Constructable] + public WhiteNinjaQuestTeleporter() : base( 0x51C, 0x47E ) + { + } + + public override int NotWorkingMessage{ get{ return 1063198; } } // You stand on the strange floor tile but nothing happens. + + public override bool GetDestination( PlayerMobile player, ref Point3D loc, ref Map map ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SearchForSwordObjective ) ); + + if ( obj != null ) + { + if ( !obj.Completed ) + obj.Complete(); + + loc = new Point3D( 411, 1085, 0 ); + map = Map.Malas; + + return true; + } + } + + return false; + } + + public WhiteNinjaQuestTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs new file mode 100644 index 000000000..435a9eb3e --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Emino.cs @@ -0,0 +1,227 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Items; + +namespace Server.Engines.Quests.Ninja +{ + public class Emino : BaseQuester + { + [Constructable] + public Emino() : base( "the Notorious" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83FE; + + Female = false; + Body = 0x190; + Name = "Daimyo Emino"; + } + + public override void InitOutfit() + { + HairItemID = 0x203B; + HairHue = 0x901; + + AddItem( new MaleKimono() ); + AddItem( new SamuraiTabi() ); + AddItem( new Bandana() ); + + AddItem( new PlateHaidate() ); + AddItem( new PlateDo() ); + AddItem( new PlateHiroSode() ); + + Nunchaku nunchaku = new Nunchaku(); + nunchaku.Movable = false; + AddItem( nunchaku ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 2; + } + + public override int TalkNumber{ get { return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest ) + { + if ( EminosUndertakingQuest.HasLostNoteForZoel( player ) ) + { + Item note = new NoteForZoel(); + + if ( player.PlaceInBackpack( note ) ) + { + qs.AddConversation( new LostNoteConversation() ); + } + else + { + note.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + else if ( EminosUndertakingQuest.HasLostEminosKatana( player ) ) + { + qs.AddConversation( new LostSwordConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( FindEminoBeginObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else + { + obj = qs.FindObjective( typeof( UseTeleporterObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item note = new NoteForZoel(); + + if ( player.PlaceInBackpack( note ) ) + { + obj.Complete(); + + player.AddToBackpack( new LeatherNinjaPants() ); + player.AddToBackpack( new LeatherNinjaMitts() ); + } + else + { + note.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + else + { + obj = qs.FindObjective( typeof( ReturnFromInnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + for ( int i = 0; i < 10; i++ ) + cont.DropItem( new LesserHealPotion() ); + + cont.DropItem( new LeatherNinjaHood() ); + cont.DropItem( new LeatherNinjaJacket() ); + + if ( player.PlaceInBackpack( cont ) ) + { + obj.Complete(); + } + else + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + else + { + if ( qs.IsObjectiveInProgress( typeof( SlayHenchmenObjective ) ) ) + { + qs.AddConversation( new ContinueSlayHenchmenConversation() ); + } + else + { + obj = qs.FindObjective( typeof( GiveEminoSwordObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item katana = null; + + if ( player.Backpack != null ) + katana = player.Backpack.FindItemByType( typeof( EminosKatana ) ); + + if ( katana != null ) + { + bool stolenTreasure = false; + + HallwayWalkObjective walk = qs.FindObjective( typeof( HallwayWalkObjective ) ) as HallwayWalkObjective; + if ( walk != null ) + stolenTreasure = walk.StolenTreasure; + + Kama kama = new Kama(); + + if ( stolenTreasure ) + BaseRunicTool.ApplyAttributesTo( kama, 1, 10, 20 ); + else + BaseRunicTool.ApplyAttributesTo( kama, 1, 10, 30 ); + + if ( player.PlaceInBackpack( kama ) ) + { + katana.Delete(); + obj.Complete(); + + if ( stolenTreasure ) + qs.AddConversation( new EarnLessGiftsConversation() ); + else + qs.AddConversation( new EarnGiftsConversation() ); + } + else + { + kama.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + } + } + } + } + } + } + } + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( !m.Frozen && !m.Alive && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && InLOS( m ) ) + { + if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else + { + Direction = GetDirectionTo( m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.Healer ) ); + } + } + } + + public Emino( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/EnshroudedFigure.cs b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/EnshroudedFigure.cs new file mode 100644 index 000000000..73ec21f71 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/EnshroudedFigure.cs @@ -0,0 +1,56 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; + +namespace Server.Engines.Quests.Ninja +{ + public class EnshroudedFigure : BaseQuester + { + [Constructable] + public EnshroudedFigure() + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x8401; + Female = false; + Body = 0x190; + Name = "enshrouded figure"; + } + + public override void InitOutfit() + { + AddItem( new DeathShroud() ); + AddItem( new ThighBoots() ); + } + + public override int TalkNumber{ get{ return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public EnshroudedFigure( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs new file mode 100644 index 000000000..c162e778e --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Henchman.cs @@ -0,0 +1,55 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Ninja +{ + public class Henchman : BaseCreature + { + [Constructable] + public Henchman() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + InitStats( 45, 30, 5 ); + + Hue = Utility.RandomSkinHue(); + Body = 0x190; + Name = "a henchman"; + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this ); + + AddItem( new LeatherNinjaJacket() ); + AddItem( new LeatherNinjaPants() ); + AddItem( new NinjaTabi() ); + + if ( Utility.RandomBool() ) + AddItem( new Kama() ); + else + AddItem( new Tessen() ); + + SetSkill( SkillName.Swords, 50.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public Henchman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/HiddenFigure.cs b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/HiddenFigure.cs new file mode 100644 index 000000000..ac876cfb9 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/HiddenFigure.cs @@ -0,0 +1,96 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; + +namespace Server.Engines.Quests.Ninja +{ + public class HiddenFigure : BaseQuester + { + public static int[] Messages = new int[] + { + 1063191, // They won’t find me here. + 1063192 // Ah, a quiet hideout. + }; + + private int m_Message; + + [CommandProperty( AccessLevel.GameMaster )] + public int Message{ get{ return m_Message; } set{ m_Message = value; } } + + [Constructable] + public HiddenFigure() + { + m_Message = Utility.RandomList( Messages ); + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = Utility.RandomSkinHue(); + + Female = Utility.RandomBool(); + + if ( Female ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + } + + public override void InitOutfit() + { + Utility.AssignRandomHair( this ); + + AddItem( new TattsukeHakama( GetRandomHue() ) ); + AddItem( new Kasa() ); + AddItem( new HakamaShita( GetRandomHue() ) ); + + if ( Utility.RandomBool() ) + AddItem( new Shoes( GetShoeHue() ) ); + else + AddItem( new Sandals( GetShoeHue() ) ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 3; + } + + public override int TalkNumber{ get{ return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + PrivateOverheadMessage( MessageType.Regular, 0x3B2, m_Message, player.NetState ); + } + + public HiddenFigure( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (int) m_Message ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Message = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/JedahEntille.cs b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/JedahEntille.cs new file mode 100644 index 000000000..8cf80c6e3 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/JedahEntille.cs @@ -0,0 +1,60 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; + +namespace Server.Engines.Quests.Ninja +{ + public class JedahEntille : BaseQuester + { + [Constructable] + public JedahEntille() : base( "the Silent" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83FE; + Female = true; + Body = 0x191; + Name = "Jedah Entille"; + } + + public override void InitOutfit() + { + HairItemID = 0x203C; + HairHue = 0x6BE; + + AddItem( new PlainDress( 0x528 ) ); + AddItem( new ThighBoots() ); + AddItem( new FloppyHat() ); + } + + public override int TalkNumber{ get{ return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public JedahEntille( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Zoel.cs b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Zoel.cs new file mode 100644 index 000000000..f5d2673c1 --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Mobiles/Zoel.cs @@ -0,0 +1,138 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Gumps; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Ninja +{ + public class Zoel : BaseQuester + { + [Constructable] + public Zoel() : base( "the Masterful Tactician" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83FE; + + Female = false; + Body = 0x190; + Name = "Elite Ninja Zoel"; + } + + public override void InitOutfit() + { + HairItemID = 0x203B; + HairHue = 0x901; + + AddItem( new HakamaShita( 0x1 ) ); + AddItem( new NinjaTabi() ); + AddItem( new TattsukeHakama() ); + AddItem( new Bandana() ); + + AddItem( new LeatherNinjaBelt() ); + + Tekagi tekagi = new Tekagi(); + tekagi.Movable = false; + AddItem( tekagi ); + } + + public override int TalkNumber{ get { return -1; } } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 2; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return to.Quest is EminosUndertakingQuest; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FindZoelObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is EminosUndertakingQuest ) + { + if ( dropped is NoteForZoel ) + { + QuestObjective obj = qs.FindObjective( typeof( GiveZoelNoteObjective ) ); + + if ( obj != null && !obj.Completed ) + { + dropped.Delete(); + obj.Complete(); + return true; + } + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( !m.Frozen && !m.Alive && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && InLOS( m ) ) + { + if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else + { + Direction = GetDirectionTo( m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.Healer ) ); + } + } + } + + public Zoel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Emino's Undertaking/Objectives.cs b/Scripts/Engines/Quests/Emino's Undertaking/Objectives.cs new file mode 100644 index 000000000..bd812bb9c --- /dev/null +++ b/Scripts/Engines/Quests/Emino's Undertaking/Objectives.cs @@ -0,0 +1,393 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Ninja +{ + public class FindEminoBeginObjective : QuestObjective + { + public override object Message + { + get + { + // Your value as a Ninja must be proven. Find Daimyo Emino and accept the test he offers. + return 1063174; + } + } + + public FindEminoBeginObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FindZoelConversation() ); + } + } + + public class FindZoelObjective : QuestObjective + { + public override object Message + { + get + { + // Find Elite Ninja Zoel immediately! + return 1063176; + } + } + + public FindZoelObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new EnterCaveConversation() ); + } + } + + public class EnterCaveObjective : QuestObjective + { + public override object Message + { + get + { + // Enter the cave and walk through it. You will be tested as you travel along the path. + return 1063179; + } + } + + public EnterCaveObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 406, 1141, 0 ), 2 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddConversation( new SneakPastGuardiansConversation() ); + } + } + + public class SneakPastGuardiansObjective : QuestObjective + { + private bool m_TaughtHowToUseSkills; + + public bool TaughtHowToUseSkills + { + get{ return m_TaughtHowToUseSkills; } + set{ m_TaughtHowToUseSkills = value; } + } + + public override object Message + { + get + { + // Use your Ninja training to move invisibly past the magical guardians. + return 1063261; + } + } + + public SneakPastGuardiansObjective() + { + } + + public override void CheckProgress() + { + if ( System.From.Map == Map.Malas && System.From.InRange( new Point3D( 412, 1123, 0 ), 3 ) ) + Complete(); + } + + public override void OnComplete() + { + System.AddConversation( new UseTeleporterConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_TaughtHowToUseSkills = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_TaughtHowToUseSkills ); + } + } + + public class UseTeleporterObjective : QuestObjective + { + public override object Message + { + get + { + /* The special tile is known as a teleporter. + * Step on the teleporter tile and you will be transported to a new location. + */ + return 1063183; + } + } + + public UseTeleporterObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new GiveZoelNoteConversation() ); + } + } + + public class GiveZoelNoteObjective : QuestObjective + { + public override object Message + { + get + { + /* Bring the note to Elite Ninja Zoel and speak with him again. + * He is near the cave entrance. You can hand the note to Zoel + * by dragging it and dropping it on his body. + */ + return 1063185; + } + } + + public GiveZoelNoteObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new GainInnInformationConversation() ); + } + } + + public class GainInnInformationObjective : QuestObjective + { + public override object Message + { + get + { + /* Take the Blue Teleporter Tile from Daimyo Emino's + * house to the Abandoned Inn. Quietly look around + * to gain information. + */ + return 1063190; + } + } + + public GainInnInformationObjective() + { + } + + public override void CheckProgress() + { + Mobile from = System.From; + + if ( from.Map == Map.Malas && from.X > 399 && from.X < 408 && from.Y > 1091 && from.Y < 1099 ) + Complete(); + } + + public override void OnComplete() + { + System.AddConversation( new ReturnFromInnConversation() ); + } + } + + public class ReturnFromInnObjective : QuestObjective + { + public override object Message + { + get + { + // Go back through the blue teleporter and tell Daimyo Emino what you’ve overheard. + return 1063197; + } + } + + public ReturnFromInnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new SearchForSwordConversation() ); + } + } + + public class SearchForSwordObjective : QuestObjective + { + public override object Message + { + get + { + /* Take the white teleporter and check the chests for the sword. + * Leave everything else behind. Avoid damage from traps you may + * encounter. To use a potion, make sure at least one hand is + * free and double click on the bottle. + */ + return 1063200; + } + } + + public SearchForSwordObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new HallwayWalkConversation() ); + } + } + + public class HallwayWalkObjective : QuestObjective + { + private bool m_StolenTreasure; + + public bool StolenTreasure + { + get{ return m_StolenTreasure; } + set{ m_StolenTreasure = value; } + } + + public override object Message + { + get + { + /* Walk through the hallway being careful + * to avoid the traps. You may be able to + * time the traps to avoid injury. + */ + return 1063202; + } + } + + public HallwayWalkObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ReturnSwordConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_StolenTreasure = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_StolenTreasure ); + } + } + + public class ReturnSwordObjective : QuestObjective + { + public override object Message + { + get + { + // Take the sword and bring it back to Daimyo Emino. + return 1063204; + } + } + + public ReturnSwordObjective() + { + } + + public override void CheckProgress() + { + Mobile from = System.From; + + if ( from.Map != Map.Malas || from.Y > 992 ) + Complete(); + } + + public override void OnComplete() + { + System.AddConversation( new SlayHenchmenConversation() ); + } + } + + public class SlayHenchmenObjective : QuestObjective + { + public override object Message + { + get + { + // Kill three henchmen. + return 1063206; + } + } + + public SlayHenchmenObjective() + { + } + + public override int MaxProgress{ get{ return 3; } } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Henchmen killed: + gump.AddHtmlLocalized( 70, 260, 270, 100, 1063207, BaseQuestGump.Blue, false, false ); + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, MaxProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is Henchman ) + CurProgress++; + } + + public override void OnComplete() + { + System.AddConversation( new GiveEminoSwordConversation() ); + } + } + + public class GiveEminoSwordObjective : QuestObjective + { + public override object Message + { + get + { + /* You have proven your fighting skills. Bring the Sword to + * Daimyo Emino immediately. Be sure to follow the + * path back to the teleporter. + */ + return 1063210; + } + } + + public GiveEminoSwordObjective() + { + } + + public override void OnComplete() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Conversations.cs b/Scripts/Engines/Quests/Haochi's Trials/Conversations.cs new file mode 100644 index 000000000..5d6c5e5af --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Conversations.cs @@ -0,0 +1,713 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Important Quest Information

+ * + * During your quest, any important information that a + * NPC gives you, will appear in a window + * such as this one. You can review the information at any time during your + * quest.

+ * + * Getting Help

+ * + * Some of the text you will come across during your quest, + * will be underlined links to the codex of wisdom, + * or online help system. You can click on the text to get detailed information + * on the underlined subject. You may also access the Codex Of Wisdom by + * pressing "F1" or by clicking on the "?" on the toolbar at the top of + * your screen.

Context Menus

+ * + * Context menus can be called up by single left-clicking (or Shift + single + * left-click, if you changed it) on most objects or NPCs in the world. + * Nearly everything, including your own avatar will have context menus available. + * Bringing up your avatar's context menu will give you options to cancel your quest + * and review various quest information.

+ */ + return 1049092; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindHaochiObjective() ); + } + } + + public class RadarConversation : QuestConversation + { + public override object Message + { + get + { + /* To view the surrounding area, you should learn about the Radar Map.

+ * + * The Radar Map (or Overhead View) can be opened by pressing 'ALT-R' + * on your keyboard. It shows your immediate surroundings from a + * bird's eye view.

+ * + * Pressing ALT-R twice, will enlarge the Radar Map a little. + * Use the Radar Map often as you travel throughout the world + * to familiarize yourself with your surroundings. + * */ + return 1063033; + } + } + + public override bool Logged{ get{ return false; } } + + public RadarConversation() + { + } + } + + public class FirstTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + /* Greetings. I am Daimyo Haochi, the Feudal Lord of this region.

+ * + * Since you are here at my side, you must wish to learn the ways of the Samurai.

+ * + * Wielding a blade is easy, anyone can grasp a sword’s hilt. Learning how to + * fight properly and skillfully is to become an Armsman.

+ * + * Learning how to master weapons, and even more importantly when not to use + * them, is the Way of the Warrior. The Way of the Samurai. The Code of the + * Bushido. That is why you are here.

+ * + * You will go through 7 trials to prove your adherence to the Samurai code.

+ * + * The first trial will test your decision making skills. You only have to enter + * the area beyond the green passageway.

+ * + * Do not attempt to hurry your trials. The guards will only let you through + * to each trial when I have deemed you ready.

+ * + * As a last resort you may use the golden teleporter tiles in each trial area + * but do so at your own risk. You may not be able to return and complete your + * trials once you have chosen to escape. + */ + return 1063029; + } + } + + public FirstTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FirstTrialIntroObjective() ); + } + } + + public class FirstTrialKillConversation : QuestConversation + { + public override object Message + { + get + { + /* Beyond you are two troubled groups.

+ * + * The Cursed Souls were once proud warriors that were ensorcelled + * by an evil mage. The mage trapped and killed them later but the spell + * has not lifted from their souls in death.

+ * + * The Young Ronin were former Samurai in training who lost their way. + * They are loyal only to those with enough coin in their pocket.

+ * + * You must decide who needs to be fought to the death. You may wish to + * review combat techniques as well as + * information on healing yourself.

+ * + * Return to Daimyo Haochi after you have finished with your trial.

+ * + * If you should die during any of your trials, visit one of the Ankh Shrines + * and you will be resurrected. You should retrieve your belongings from your + * body before returning to the Daimyo or you may not be able to return to your corpse. + */ + return 1063031; + } + } + + public FirstTrialKillConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FirstTrialKillObjective() ); + } + } + + public class GainKarmaConversation : QuestConversation + { + public override object Message + { + get + { + if ( m_CursedSoul ) + { + // You have just gained some Karma for killing a Cursed Soul. + return 1063040; + } + else + { + // You have just gained some Karma for killing a Young Ronin. + return 1063041; + } + } + } + + public override bool Logged{ get{ return false; } } + + private bool m_CursedSoul; + + public GainKarmaConversation( bool cursedSoul ) + { + m_CursedSoul = cursedSoul; + } + + public GainKarmaConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_CursedSoul = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_CursedSoul ); + } + } + + public class SecondTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + if ( m_CursedSoul ) + { + /* It is good that you rid the land of the Cursed Souls so they can + * be at peace in death. They had been cursed for doing what they + * thought was an honorable deed. Now they can have respect in their death.

+ * + * I have placed a reward in your pack.

+ * + * The second trial will test your courage. You only have to follow the yellow + * path to see what awaits you. + */ + return 1063045; + } + else + { + /* It is good that you rid the land of those dishonorable Samurai. + * Perhaps they will learn a greater lesson in death.

+ * + * I have placed a reward in your pack.

+ * + * The second trial will test your courage. You only have to follow + * the yellow path to see what awaits you. + */ + return 1063046; + } + } + } + + private bool m_CursedSoul; + + public SecondTrialIntroConversation( bool cursedSoul ) + { + m_CursedSoul = cursedSoul; + } + + public SecondTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SecondTrialIntroObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_CursedSoul = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_CursedSoul ); + } + } + + public class SecondTrialAttackConversation : QuestConversation + { + public override object Message + { + get + { + /* Beyond the guards is a test of courage. You must face your fear and attack a + * great beast. You must choose which beast to slay for there is more than + * one beyond the courtyard doors.

+ * + * The imp entered the courtyard unaware of its surroundings. The dragon came + * knowingly, hunting for the flesh of humans – A feast for the beast.

+ * + * You must rid the courtyard of these beasts but you may only choose one to + * attack. Go and choose wisely. + */ + return 1063057; + } + } + + public SecondTrialAttackConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SecondTrialAttackObjective() ); + } + } + + public class ThirdTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + if ( m_Dragon ) + { + /* You faced the dragon knowing it would be your certain death. + * That is the courage of a Samurai.

+ * + * Your spirit speaks as a Samurai already.

+ * + * In these lands, death is not forever. The shrines can make you whole + * again as can a helpful mage or healer.

+ * + * Seek them out when you have been mortally wounded.

+ * + * The next trial will test your benevolence. You only have to walk the blue path. + */ + return 1063060; + } + else + { + /* Fear remains in your eyes but you have learned that not all is + * what it appears to be.

+ * + * You must have known the dragon would slay you instantly. + * You elected the weaker opponent though the imp did not come + * here to destroy. You have much to learn.

+ * + * In these lands, death is not forever. The shrines can make you whole + * again as can a helpful mage or healer.

+ * + * Seek them out when you have been mortally wounded.

+ * + * The next trial will test your benevolence. You only have to walk the blue path. + */ + return 1063059; + } + } + } + + private bool m_Dragon; + + public ThirdTrialIntroConversation( bool dragon ) + { + m_Dragon = dragon; + } + + public ThirdTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ThirdTrialIntroObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Dragon = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Dragon ); + } + } + + public class ThirdTrialKillConversation : QuestConversation + { + public override object Message + { + get + { + /* A pack of wolves circle your feet. They have been injured and are in pain. + * A quick death will end their suffering.

+ * + * Use your Honorable Execution skill or other means to finish off a wounded wolf. + * Do so and return to Daimyo Haochi. + */ + return 1063062; + } + } + + public ThirdTrialKillConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ThirdTrialKillObjective() ); + } + } + + public class FourthTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + /* Daimyo Haochi smiles as you walk up to him. Quietly he says:

+ * + * A Samurai understands the need to help others even as he wields a blade against + * them.

+ * + * You have shown compassion. A true Samurai is benevolent even to an enemy. + * For this you have been rewarded.

+ * + * And now you must prove yourself again. Walk the red path. + * We will talk again later. + */ + return 1063065; + } + } + + public FourthTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FourthTrialIntroObjective() ); + } + } + + public class FourthTrialCatsConversation : QuestConversation + { + public override object Message + { + get + { + /* You approach a disheveled gypsy standing near a small shed. + * You sense that she has not eaten nor bathed in quite some time.

+ * + * Around her is a large colony of mangy and diseased cats. It appears + * she has spent what little money she’s earned to feed the cats instead + * of herself.

+ * + * You have a decision to make. You can give her gold so she can buy some + * food for her animals and herself. You can also remove the necessity + * of the extra mouths to feed so she may concentrate on saving herself.


+ * + * If you elect to give the gypsy money, you can do so by clicking your stack + * of gold and selecting ‘1’. Then dragging it and dropping it on the Gypsy. + */ + return 1063067; + } + } + + public FourthTrialCatsConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FourthTrialCatsObjective() ); + } + } + + public class FifthTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + if ( m_KilledCat ) + { + /* Respect comes from allowing another to make their own decisions. + * By denying the gypsy her animals, you negate the respect she is due. + * Perhaps you will have learned something to use next time a similar + * situation arises.

+ * + * And now you must prove yourself again. Please retrieve my katana + * from the treasure room and return it to me. + */ + return 1063071; + } + else + { + /* You showed respect by helping another out while allowing the gypsy + * what little dignity she has left.

+ * + * Now she will be able to feed herself and gain enough energy to walk + * to her camp.

+ * + * The cats are her family members– cursed by an evil mage.

+ * + * Once she has enough strength to walk back to the camp, she will be + * able to undo the spell.

+ * + * You have been rewarded for completing your trial. And now you must + * prove yourself again.

Please retrieve my katana from the + * treasure room and return it to me. + */ + return 1063070; + } + } + } + + private bool m_KilledCat; + + public FifthTrialIntroConversation( bool killedCat ) + { + m_KilledCat = killedCat; + } + + public FifthTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FifthTrialIntroObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_KilledCat = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_KilledCat ); + } + } + + public class FifthTrialReturnConversation : QuestConversation + { + public override object Message + { + get + { + /* The guards let you through without question, and pay you no mind as + * you walk into the Daimyo's treasure cache. A vast fortune in gold, + * gemstones, and jewelry is stored here! Surely, the Daimyo wouldn't + * miss a single small item...

+ * + * You spot the sword quickly amongst the cache of gemstones and other valuables. + * In one quick motion you retrieve it and stash it in your pack.
+ */ + return 1063248; + } + } + + public FifthTrialReturnConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FifthTrialReturnObjective() ); + } + } + + public class LostSwordConversation : QuestConversation + { + public override object Message + { + get + { + // What? You have returned without the sword? Go back and look for it again. + return 1063074; + } + } + + public override bool Logged{ get{ return false; } } + + public LostSwordConversation() + { + } + } + + public class SixthTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + if ( m_StolenTreasure ) + { + /* I thank you for returning this sword. However, you should admonished + * for also taking treasure that was not asked for nor given back.

+ * + * Think about your actions youngling.

+ * + * Your training is nearly complete. Before you have your final trial, + * you should pay homage to Samurai who came before you.

+ * + * Go into the Altar Room and light a candle for them. + * Afterwards, return to me. + */ + return 1063077; + } + else + { + /* Thank you for returning this sword to me and leaving the remaining + * treasure alone.

+ * + * Your training is nearly complete. Before you have your final trial, + * you should pay homage to Samurai who came before you.

+ * + * Go into the Altar Room and light a candle for them. Afterwards, return to me. + */ + return 1063076; + } + } + } + + private bool m_StolenTreasure; + + public SixthTrialIntroConversation( bool stolenTreasure ) + { + m_StolenTreasure = stolenTreasure; + } + + public SixthTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SixthTrialIntroObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_StolenTreasure = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_StolenTreasure ); + } + } + + public class SeventhTrialIntroConversation : QuestConversation + { + public override object Message + { + get + { + /* You have done well young Samurai. There is but one thing left to do.

+ * + * In the final room is the holding cell containing young Ninjas who came to + * take my life. They were caught and placed in my custody.

+ * + * Take care of these miscreants and show them where your loyalty lies.

+ * + * This is your final act as a Samurai in training. + */ + return 1063079; + } + } + + public SeventhTrialIntroConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new SeventhTrialIntroObjective() ); + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* You have proven yourself young one. You will continue to improve as your skills + * are honed with age.

+ * + * Now it is time for you to explore the lands. Beyond this path lies Zento + * City, your future home. On these grounds you will find a golden oval object + * known as a Moongate, step through it and you'll find yourself in Zento.

+ * + * You may want to visit Ansella Gryen when you arrive.

+ * + * You have learned the ways. You are an honorable warrior, a Samurai in + * the highest regards.

+ * + * Please accept the gifts I have placed in your pack. You have earned them. + * Farewell for now. + */ + return 1063125; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/HaochisTrialsQuest.cs b/Scripts/Engines/Quests/Haochi's Trials/HaochisTrialsQuest.cs new file mode 100644 index 000000000..fcf4b5d38 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/HaochisTrialsQuest.cs @@ -0,0 +1,151 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class HaochisTrialsQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Samurai.AcceptConversation ), + typeof( Samurai.RadarConversation ), + typeof( Samurai.FirstTrialIntroConversation ), + typeof( Samurai.FirstTrialKillConversation ), + typeof( Samurai.GainKarmaConversation ), + typeof( Samurai.SecondTrialIntroConversation ), + typeof( Samurai.SecondTrialAttackConversation ), + typeof( Samurai.ThirdTrialIntroConversation ), + typeof( Samurai.ThirdTrialKillConversation ), + typeof( Samurai.FourthTrialIntroConversation ), + typeof( Samurai.FourthTrialCatsConversation ), + typeof( Samurai.FifthTrialIntroConversation ), + typeof( Samurai.FifthTrialReturnConversation ), + typeof( Samurai.LostSwordConversation ), + typeof( Samurai.SixthTrialIntroConversation ), + typeof( Samurai.SeventhTrialIntroConversation ), + typeof( Samurai.EndConversation ), + typeof( Samurai.FindHaochiObjective ), + typeof( Samurai.FirstTrialIntroObjective ), + typeof( Samurai.FirstTrialKillObjective ), + typeof( Samurai.FirstTrialReturnObjective ), + typeof( Samurai.SecondTrialIntroObjective ), + typeof( Samurai.SecondTrialAttackObjective ), + typeof( Samurai.SecondTrialReturnObjective ), + typeof( Samurai.ThirdTrialIntroObjective ), + typeof( Samurai.ThirdTrialKillObjective ), + typeof( Samurai.ThirdTrialReturnObjective ), + typeof( Samurai.FourthTrialIntroObjective ), + typeof( Samurai.FourthTrialCatsObjective ), + typeof( Samurai.FourthTrialReturnObjective ), + typeof( Samurai.FifthTrialIntroObjective ), + typeof( Samurai.FifthTrialReturnObjective ), + typeof( Samurai.SixthTrialIntroObjective ), + typeof( Samurai.SixthTrialReturnObjective ), + typeof( Samurai.SeventhTrialIntroObjective ), + typeof( Samurai.SeventhTrialReturnObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // Haochi's Trials + return 1063022; + } + } + + public override object OfferMessage + { + get + { + /* As you enter the courtyard you notice a faded sign. + * It reads:

+ * + * Welcome to your new home, Samurai.

+ * + * Though your skills are only a shadow of what they can be some day, + * you must prove your adherence to the code of the Bushido.

+ * + * Seek Daimyo Haochi for guidance.

+ * + * Will you accept the challenge? + */ + return 1063023; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.MaxValue; } } + public override bool IsTutorial{ get{ return true; } } + + public override int Picture{ get{ return 0x15D7; } } + + public HaochisTrialsQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public HaochisTrialsQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + private bool m_SentRadarConversion; + + public override void Slice() + { + if ( !m_SentRadarConversion && ( From.Map != Map.Malas || From.X < 360 || From.X > 400 || From.Y < 760 || From.Y > 780 ) ) + { + m_SentRadarConversion = true; + AddConversation( new RadarConversation() ); + } + + base.Slice(); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_SentRadarConversion = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (bool) m_SentRadarConversion ); + } + + public static bool HasLostHaochisKatana( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is HaochisTrialsQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FifthTrialReturnObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( HaochisKatana ) ) == null ); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs b/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs new file mode 100644 index 000000000..0f8d222c4 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs @@ -0,0 +1,46 @@ +using System; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + public class HaochisKatana : QuestItem + { + public override int LabelNumber{ get{ return 1063165; } } // Daimyo Haochi's Katana + + [Constructable] + public HaochisKatana() : base( 0x13FF ) + { + Weight = 1.0; + } + + public HaochisKatana( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + HaochisTrialsQuest qs = player.Quest as HaochisTrialsQuest; + + if ( qs == null ) + return true; + + //return !qs.IsObjectiveInProgress( typeof( FifthTrialReturnObjective ) ); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs b/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs new file mode 100644 index 000000000..49362ff95 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisKatanaGenerator.cs @@ -0,0 +1,80 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class HaochisKatanaGenerator : Item + { + [Constructable] + public HaochisKatanaGenerator() : base( 0x1B7B ) + { + Visible = false; + Name = "Haochi's katana generator"; + Movable = false; + } + + public override bool OnMoveOver( Mobile m ) + { + PlayerMobile player = m as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is HaochisTrialsQuest ) + { + if ( HaochisTrialsQuest.HasLostHaochisKatana( player ) ) + { + Item katana = new HaochisKatana(); + + if ( !player.PlaceInBackpack( katana ) ) + { + katana.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + else + { + QuestObjective obj = qs.FindObjective( typeof( FifthTrialIntroObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item katana = new HaochisKatana(); + + if ( player.PlaceInBackpack( katana ) ) + { + obj.Complete(); + } + else + { + katana.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + } + } + } + + return base.OnMoveOver( m ); + } + + public HaochisKatanaGenerator( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs b/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs new file mode 100644 index 000000000..df009244e --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Items/HaochisTreasureChest.cs @@ -0,0 +1,105 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Engines.Quests.Samurai +{ + public class HaochisTreasureChest : WoodenFootLocker + { + [Constructable] + public HaochisTreasureChest() + { + Movable = false; + + GenerateTreasure(); + } + + public HaochisTreasureChest( Serial serial ) : base( serial ) + { + } + + public override bool IsDecoContainer{ get{ return false; } } + + private void GenerateTreasure() + { + for ( int i = this.Items.Count - 1; i >= 0; i-- ) + this.Items[i].Delete(); + + for ( int i = 0; i < 75; i++ ) + { + switch ( Utility.Random( 3 ) ) + { + case 0: DropItem( new GoldBracelet() ); break; + case 1: DropItem( new GoldRing() ); break; + case 2: DropItem( Loot.RandomGem() ); break; + } + } + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + return false; + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + return item == this; + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.Quest is HaochisTrialsQuest ) + { + FifthTrialIntroObjective obj = player.Quest.FindObjective( typeof( FifthTrialIntroObjective ) ) as FifthTrialIntroObjective; + + if ( obj != null ) + { + if ( obj.StolenTreasure ) + from.SendLocalizedMessage( 1063247 ); // The guard is watching you carefully! It would be unwise to remove another item from here. + else + return true; + } + } + + return false; + } + + public override void OnItemLifted( Mobile from, Item item ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.Quest is HaochisTrialsQuest ) + { + FifthTrialIntroObjective obj = player.Quest.FindObjective( typeof( FifthTrialIntroObjective ) ) as FifthTrialIntroObjective; + + if ( obj != null ) + obj.StolenTreasure = true; + } + + Timer.DelayCall( TimeSpan.FromMinutes( 2.0 ), new TimerCallback( GenerateTreasure ) ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( GenerateTreasure ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Items/HonorCandle.cs b/Scripts/Engines/Quests/Haochi's Trials/Items/HonorCandle.cs new file mode 100644 index 000000000..c2303887c --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Items/HonorCandle.cs @@ -0,0 +1,79 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class HonorCandle : CandleLong + { + private static readonly TimeSpan LitDuration = TimeSpan.FromSeconds( 20.0 ); + + public override int LitSound{ get{ return 0; } } + public override int UnlitSound{ get{ return 0; } } + + [Constructable] + public HonorCandle() + { + Movable = false; + Duration = LitDuration; + } + + public HonorCandle( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + bool wasBurning = Burning; + + base.OnDoubleClick( from ); + + if ( !wasBurning && Burning ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player == null ) + return; + + QuestSystem qs = player.Quest; + + if ( qs != null && qs is HaochisTrialsQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SixthTrialIntroObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + + this.SendLocalizedMessageTo( from, 1063251 ); // You light a candle in honor. + } + } + } + + public override void Burn() + { + Douse(); + } + + public override void Douse() + { + base.Douse(); + + Duration = LitDuration; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs new file mode 100644 index 000000000..fe5aee3a1 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/CursedSoul.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + [CorpseName( "a cursed soul corpse" )] + public class CursedSoul : BaseCreature + { + [Constructable] + public CursedSoul() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a cursed soul"; + Body = 3; + BaseSoundID = 471; + + SetStr( 20, 40 ); + SetDex( 40, 60 ); + SetInt( 15, 25 ); + + SetHits( 10, 20 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 8, 12 ); + + SetSkill( SkillName.Wrestling, 35.0, 39.0 ); + SetSkill( SkillName.Tactics, 5.0, 15.0 ); + SetSkill( SkillName.MagicResist, 10.0 ); + + Fame = 200; + Karma = -200; + + switch ( Utility.Random( 10 ) ) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + } + + public CursedSoul( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs new file mode 100644 index 000000000..cd5441293 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/DeadlyImp.cs @@ -0,0 +1,80 @@ +using System; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class DeadlyImp : BaseCreature + { + [Constructable] + public DeadlyImp() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a deadly imp"; + Body = 74; + BaseSoundID = 422; + Hue = 0x66A; + + SetStr( 91, 115 ); + SetDex( 61, 80 ); + SetInt( 86, 105 ); + + SetHits( 1000 ); + + SetDamage( 50, 80 ); + + SetDamageType( ResistanceType.Fire, 100 ); + + SetResistance( ResistanceType.Physical, 95, 98 ); + SetResistance( ResistanceType.Fire, 95, 98 ); + SetResistance( ResistanceType.Cold, 95, 98 ); + SetResistance( ResistanceType.Poison, 95, 98 ); + SetResistance( ResistanceType.Energy, 95, 98 ); + + SetSkill( SkillName.Magery, 120.0 ); + SetSkill( SkillName.Tactics, 120.0 ); + SetSkill( SkillName.Wrestling, 120.0 ); + + Fame = 2500; + Karma = -2500; + + CantWalk = true; + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + PlayerMobile player = aggressor as PlayerMobile; + if ( player != null ) + { + QuestSystem qs = player.Quest; + if ( qs is HaochisTrialsQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SecondTrialAttackObjective ) ); + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + qs.AddObjective( new SecondTrialReturnObjective( false ) ); + } + } + } + } + + public DeadlyImp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs new file mode 100644 index 000000000..9e1ebd250 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/DiseasedCat.cs @@ -0,0 +1,56 @@ +using System; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class DiseasedCat : BaseCreature + { + [Constructable] + public DiseasedCat() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a deseased cat"; + Body = 0xC9; + Hue = Utility.RandomAnimalHue(); + BaseSoundID = 0x69; + + SetStr( 9 ); + SetDex( 35 ); + SetInt( 5 ); + + SetHits( 6 ); + SetMana( 0 ); + + SetDamage( 1 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 4.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + VirtualArmor = 8; + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public DiseasedCat( Serial serial ) : base( serial ) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs new file mode 100644 index 000000000..0fa72a9d4 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/FierceDragon.cs @@ -0,0 +1,102 @@ +using System; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class FierceDragon : BaseCreature + { + [Constructable] + public FierceDragon() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a fierce dragon"; + Body = 103; + BaseSoundID = 362; + + SetStr( 6000, 6020 ); + SetDex( 0 ); + SetInt( 850, 870 ); + + SetDamage( 50, 80 ); + + SetDamageType( ResistanceType.Fire, 100 ); + + SetResistance( ResistanceType.Physical, 95, 98 ); + SetResistance( ResistanceType.Fire, 95, 98 ); + SetResistance( ResistanceType.Cold, 95, 98 ); + SetResistance( ResistanceType.Poison, 95, 98 ); + SetResistance( ResistanceType.Energy, 95, 98 ); + + SetSkill( SkillName.Tactics, 120.0 ); + SetSkill( SkillName.Wrestling, 120.0 ); + SetSkill( SkillName.Magery, 120.0 ); + + Fame = 15000; + Karma = 15000; + + CantWalk = true; + } + + public override int GetIdleSound() + { + return 0x2C4; + } + + public override int GetAttackSound() + { + return 0x2C0; + } + + public override int GetDeathSound() + { + return 0x2C1; + } + + public override int GetAngerSound() + { + return 0x2C4; + } + + public override int GetHurtSound() + { + return 0x2C3; + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + PlayerMobile player = aggressor as PlayerMobile; + if ( player != null ) + { + QuestSystem qs = player.Quest; + if ( qs is HaochisTrialsQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( SecondTrialAttackObjective ) ); + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + qs.AddObjective( new SecondTrialReturnObjective( true ) ); + } + } + } + } + + public FierceDragon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/Haochi.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/Haochi.cs new file mode 100644 index 000000000..6f4b36824 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/Haochi.cs @@ -0,0 +1,182 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + public class Haochi : BaseQuester + { + [Constructable] + public Haochi() : base( "the Honorable Samurai Legend" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x8403; + + Female = false; + Body = 0x190; + Name = "Daimyo Haochi"; + } + + public override void InitOutfit() + { + HairItemID = 0x204A; + HairHue = 0x901; + + AddItem( new SamuraiTabi() ); + AddItem( new JinBaori() ); + + AddItem( new PlateHaidate() ); + AddItem( new StandardPlateKabuto() ); + AddItem( new PlateMempo() ); + AddItem( new PlateDo() ); + AddItem( new PlateHiroSode() ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 2; + } + + public override int TalkNumber{ get { return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is HaochisTrialsQuest ) + { + if ( HaochisTrialsQuest.HasLostHaochisKatana( player ) ) + { + qs.AddConversation( new LostSwordConversation() ); + return; + } + + QuestObjective obj = qs.FindObjective( typeof( FindHaochiObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + return; + } + + obj = qs.FindObjective( typeof( FirstTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + player.AddToBackpack( new LeatherDo() ); + obj.Complete(); + return; + } + + obj = qs.FindObjective( typeof( SecondTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( ((SecondTrialReturnObjective)obj).Dragon ) + player.AddToBackpack( new LeatherSuneate() ); + + obj.Complete(); + return; + } + + obj = qs.FindObjective( typeof( ThirdTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + player.AddToBackpack( new LeatherHiroSode() ); + obj.Complete(); + return; + } + + obj = qs.FindObjective( typeof( FourthTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( !((FourthTrialReturnObjective)obj).KilledCat ) + { + Container cont = GetNewContainer(); + cont.DropItem( new LeatherHiroSode() ); + cont.DropItem( new JinBaori() ); + player.AddToBackpack( cont ); + } + + obj.Complete(); + return; + } + + obj = qs.FindObjective( typeof( FifthTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container pack = player.Backpack; + if ( pack != null ) + { + Item katana = pack.FindItemByType( typeof( HaochisKatana ) ); + if ( katana != null ) + { + katana.Delete(); + obj.Complete(); + + obj = qs.FindObjective( typeof( FifthTrialIntroObjective ) ); + if ( obj != null && ((FifthTrialIntroObjective)obj).StolenTreasure ) + qs.AddConversation( new SixthTrialIntroConversation( true ) ); + else + qs.AddConversation( new SixthTrialIntroConversation( false ) ); + } + } + + return; + } + + obj = qs.FindObjective( typeof( SixthTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + return; + } + + obj = qs.FindObjective( typeof( SeventhTrialReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + BaseWeapon weapon = new Daisho(); + BaseRunicTool.ApplyAttributesTo( weapon, Utility.Random( 1,3 ), 10, 30 ); + player.AddToBackpack( weapon ); + + BaseArmor armor = new LeatherDo(); + BaseRunicTool.ApplyAttributesTo( armor, Utility.Random( 1,3 ), 10, 20 ); + player.AddToBackpack( armor ); + + obj.Complete(); + return; + } + } + } + + public Haochi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/HaochisGuardsman.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/HaochisGuardsman.cs new file mode 100644 index 000000000..62c89fb39 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/HaochisGuardsman.cs @@ -0,0 +1,85 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + public class HaochisGuardsman : BaseQuester + { + [Constructable] + public HaochisGuardsman() : base( "the Guardsman of Daimyo Haochi" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = Race.Human.RandomSkinHue(); + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + public override void InitOutfit() + { + Utility.AssignRandomHair( this ); + + AddItem( new LeatherDo() ); + AddItem( new LeatherHiroSode() ); + AddItem( new SamuraiTabi( Utility.RandomNondyedHue() ) ); + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new StuddedHaidate() ); break; + case 1: AddItem( new PlateSuneate() ); break; + default: AddItem( new LeatherSuneate() ); break; + } + + switch ( Utility.Random( 4 ) ) + { + case 0: AddItem( new DecorativePlateKabuto() ); break; + case 1: AddItem( new ChainHatsuburi() ); break; + case 2: AddItem( new LightPlateJingasa() ); break; + default: AddItem( new LeatherJingasa() ); break; + } + + Item weapon; + switch ( Utility.Random( 3 ) ) + { + case 0: weapon = new NoDachi(); break; + case 1: weapon = new Lajatang(); break; + default: weapon = new Wakizashi(); break; + } + weapon.Movable = false; + AddItem( weapon ); + } + + public override int TalkNumber{ get { return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public HaochisGuardsman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs new file mode 100644 index 000000000..9e6523e2b --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/InjuredWolf.cs @@ -0,0 +1,59 @@ +using System; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + [CorpseName( "an injured wolf corpse" )] + public class InjuredWolf : BaseCreature + { + [Constructable] + public InjuredWolf() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Body = 0xE1; + Name = "an injured wolf"; + BaseSoundID = 0xE5; + + Hue = Utility.RandomAnimalHue(); + + SetStr( 10, 20 ); + SetDex( 45, 65 ); + SetInt( 10, 15 ); + + SetHits( 1 ); + + SetDamage( 1, 3 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + + SetSkill( SkillName.MagicResist, 10.0 ); + SetSkill( SkillName.Tactics, 0.0, 5.0 ); + SetSkill( SkillName.Wrestling, 20.0, 30.0 ); + } + + public override int GetIdleSound() + { + return 0xE9; + } + + public InjuredWolf( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/Relnia.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/Relnia.cs new file mode 100644 index 000000000..ad7914beb --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/Relnia.cs @@ -0,0 +1,96 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + public class Relnia : BaseQuester + { + [Constructable] + public Relnia() : base( "the Gypsy" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83FF; + + Female = true; + Body = 0x191; + Name = "Disheveled Relnia"; + } + + public override void InitOutfit() + { + HairItemID = 0x203C; + HairHue = 0x654; + + AddItem( new ThighBoots( 0x901 ) ); + AddItem( new FancyShirt( 0x5F3 ) ); + AddItem( new SkullCap( 0x6A7 ) ); + AddItem( new Skirt( 0x544 ) ); + } + + public override int TalkNumber{ get { return -1; } } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is HaochisTrialsQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FourthTrialCatsObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Gold gold = dropped as Gold; + + if ( gold != null ) + { + obj.Complete(); + qs.AddObjective( new FourthTrialReturnObjective( false ) ); + + SayTo( from, 1063241 ); // I thank thee. This gold will be a great help to me and mine! + + gold.Consume(); // Intentional difference from OSI: don't take all the gold of poor newbies! + return gold.Deleted; + } + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + + public Relnia( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs new file mode 100644 index 000000000..bf861ec7a --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungNinja.cs @@ -0,0 +1,62 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + [CorpseName( "a young ninja's corpse" )] + public class YoungNinja : BaseCreature + { + [Constructable] + public YoungNinja() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + InitStats( 45, 30, 5 ); + SetHits( 20, 30 ); + + Hue = Utility.RandomSkinHue(); + Body = 0x190; + Name = "a young ninja"; + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this ); + + AddItem( new NinjaTabi() ); + AddItem( new LeatherNinjaPants() ); + AddItem( new LeatherNinjaJacket() ); + AddItem( new LeatherNinjaBelt() ); + + AddItem( new Bandana( Utility.RandomNondyedHue() ) ); + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new Tessen() ); break; + case 1: AddItem( new Kama() ); break; + default: AddItem( new Lajatang() ); break; + } + + SetSkill( SkillName.Swords, 50.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public YoungNinja( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs new file mode 100644 index 000000000..80a7d94ad --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Mobiles/YoungRonin.cs @@ -0,0 +1,68 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Samurai +{ + [CorpseName( "a young ronin's corpse" )] + public class YoungRonin : BaseCreature + { + [Constructable] + public YoungRonin() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + InitStats( 45, 30, 5 ); + SetHits( 10, 20 ); + + Hue = Utility.RandomSkinHue(); + Body = 0x190; + Name = "a young ronin"; + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this ); + + AddItem( new LeatherDo() ); + AddItem( new LeatherHiroSode() ); + AddItem( new SamuraiTabi() ); + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new StuddedHaidate() ); break; + case 1: AddItem( new PlateSuneate() ); break; + default: AddItem( new LeatherSuneate() ); break; + } + + AddItem( new Bandana( Utility.RandomNondyedHue() ) ); + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new NoDachi() ); break; + case 1: AddItem( new Lajatang() ); break; + default: AddItem( new Wakizashi() ); break; + } + + SetSkill( SkillName.Swords, 50.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public YoungRonin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Haochi's Trials/Objectives.cs b/Scripts/Engines/Quests/Haochi's Trials/Objectives.cs new file mode 100644 index 000000000..e30d64db8 --- /dev/null +++ b/Scripts/Engines/Quests/Haochi's Trials/Objectives.cs @@ -0,0 +1,555 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Samurai +{ + public class FindHaochiObjective : QuestObjective + { + public override object Message + { + get + { + // Speak to Daimyo Haochi. + return 1063026; + } + } + + public FindHaochiObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FirstTrialIntroConversation() ); + } + } + + public class FirstTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + // Follow the green path. The guards will now let you through. + return 1063030; + } + } + + public FirstTrialIntroObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FirstTrialKillConversation() ); + } + } + + public class FirstTrialKillObjective : QuestObjective + { + public override object Message + { + get + { + // Kill 3 Young Ronin or 3 Cursed Souls. Return to Daimyo Haochi when you have finished. + return 1063032; + } + } + + public FirstTrialKillObjective() + { + } + + private int m_CursedSoulsKilled; + private int m_YoungRoninKilled; + + public override int MaxProgress{ get{ return 3; } } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is CursedSoul ) + { + if ( m_CursedSoulsKilled == 0 ) + System.AddConversation( new GainKarmaConversation( true ) ); + + m_CursedSoulsKilled++; + + // Cursed Souls killed: ~1_COUNT~ + System.From.SendLocalizedMessage( 1063038, m_CursedSoulsKilled.ToString() ); + } + else if ( creature is YoungRonin ) + { + if ( m_YoungRoninKilled == 0 ) + System.AddConversation( new GainKarmaConversation( false ) ); + + m_YoungRoninKilled++; + + // Young Ronin killed: ~1_COUNT~ + System.From.SendLocalizedMessage( 1063039, m_YoungRoninKilled.ToString() ); + } + + CurProgress = Math.Max( m_CursedSoulsKilled, m_YoungRoninKilled ); + } + + public override void OnComplete() + { + System.AddObjective( new FirstTrialReturnObjective( m_CursedSoulsKilled > m_YoungRoninKilled ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_CursedSoulsKilled = reader.ReadEncodedInt(); + m_YoungRoninKilled = reader.ReadEncodedInt(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( m_CursedSoulsKilled ); + writer.WriteEncodedInt( m_YoungRoninKilled ); + } + } + + public class FirstTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // The first trial is complete. Return to Daimyo Haochi. + return 1063044; + } + } + + bool m_CursedSoul; + + public FirstTrialReturnObjective( bool cursedSoul ) + { + m_CursedSoul = cursedSoul; + } + + public FirstTrialReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new SecondTrialIntroConversation( m_CursedSoul ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_CursedSoul = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_CursedSoul ); + } + } + + public class SecondTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + // Follow the yellow path. The guards will now let you through. + return 1063047; + } + } + + public SecondTrialIntroObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new SecondTrialAttackConversation() ); + } + } + + public class SecondTrialAttackObjective : QuestObjective + { + public override object Message + { + get + { + // Choose your opponent and attack one with all your skill. + return 1063058; + } + } + + public SecondTrialAttackObjective() + { + } + } + + public class SecondTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // The second trial is complete. Return to Daimyo Haochi. + return 1063229; + } + } + + private bool m_Dragon; + + public bool Dragon{ get{ return m_Dragon; } } + + public SecondTrialReturnObjective( bool dragon ) + { + m_Dragon = dragon; + } + + public SecondTrialReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ThirdTrialIntroConversation( m_Dragon ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Dragon = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Dragon ); + } + } + + public class ThirdTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + /* The next trial will test your benevolence. Follow the blue path. + * The guards will now let you through. + */ + return 1063061; + } + } + + public ThirdTrialIntroObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new ThirdTrialKillConversation() ); + } + } + + public class ThirdTrialKillObjective : QuestObjective + { + public override object Message + { + get + { + /* Use your Honorable Execution skill to finish off the wounded wolf. + * Double click the icon in your Book of Bushido to activate the skill. + * When you are done, return to Daimyo Haochi. + */ + return 1063063; + } + } + + public ThirdTrialKillObjective() + { + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is InjuredWolf ) + Complete(); + } + + public override void OnComplete() + { + System.AddObjective( new ThirdTrialReturnObjective() ); + } + } + + public class ThirdTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // Return to Daimyo Haochi. + return 1063064; + } + } + + public ThirdTrialReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FourthTrialIntroConversation() ); + } + } + + public class FourthTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + // Follow the red path and pass through the guards to the entrance of the fourth trial. + return 1063066; + } + } + + public FourthTrialIntroObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FourthTrialCatsConversation() ); + } + } + + public class FourthTrialCatsObjective : QuestObjective + { + public override object Message + { + get + { + /* Give the gypsy gold or hunt one of the cats to eliminate the undue + * need it has placed on the gypsy. + */ + return 1063068; + } + } + + public FourthTrialCatsObjective() + { + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is DiseasedCat ) + { + Complete(); + System.AddObjective( new FourthTrialReturnObjective( true ) ); + } + } + } + + public class FourthTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // You have made your choice. Return now to Daimyo Haochi. + return 1063242; + } + } + + private bool m_KilledCat; + + public bool KilledCat{ get{ return m_KilledCat; } } + + public FourthTrialReturnObjective( bool killedCat ) + { + m_KilledCat = killedCat; + } + + public FourthTrialReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FifthTrialIntroConversation( m_KilledCat ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_KilledCat = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_KilledCat ); + } + } + + public class FifthTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + // Retrieve Daimyo Haochi’s katana from the treasure room. + return 1063072; + } + } + + private bool m_StolenTreasure; + + public bool StolenTreasure{ get{ return m_StolenTreasure; } set{ m_StolenTreasure = value; } } + + public FifthTrialIntroObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new FifthTrialReturnConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_StolenTreasure = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_StolenTreasure ); + } + } + + public class FifthTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // Give the sword to Daimyo Haochi. + return 1063073; + } + } + + public FifthTrialReturnObjective() + { + } + } + + public class SixthTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + // Light one of the candles near the altar and return to Daimyo Haochi. + return 1063078; + } + } + + public SixthTrialIntroObjective() + { + } + + public override void OnComplete() + { + System.AddObjective( new SixthTrialReturnObjective() ); + } + } + + public class SixthTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // You have done as requested. Return to Daimyo Haochi. + return 1063252; + } + } + + public SixthTrialReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new SeventhTrialIntroConversation() ); + } + } + + public class SeventhTrialIntroObjective : QuestObjective + { + public override object Message + { + get + { + /* Three young Ninja must be dealt with. Your job is to kill them. + * When you have done so, return to Daimyo Haochi. + */ + return 1063080; + } + } + + public override int MaxProgress{ get{ return 3; } } + + public SeventhTrialIntroObjective() + { + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is YoungNinja ) + CurProgress++; + } + + public override void OnComplete() + { + System.AddObjective( new SeventhTrialReturnObjective() ); + } + } + + public class SeventhTrialReturnObjective : QuestObjective + { + public override object Message + { + get + { + // The executions are complete. Return to the Daimyo. + return 1063253; + } + } + + public SeventhTrialReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new EndConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Solen Matriarch/Conversations.cs b/Scripts/Engines/Quests/Solen Matriarch/Conversations.cs new file mode 100644 index 000000000..25dde0627 --- /dev/null +++ b/Scripts/Engines/Quests/Solen Matriarch/Conversations.cs @@ -0,0 +1,330 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Matriarch +{ + public class DontOfferConversation : QuestConversation + { + private bool m_Friend; + + public override object Message + { + get + { + if ( m_Friend ) + { + /* The Solen Matriarch smiles as you greet her.

+ * + * It is good to see you again. I would offer to process some zoogi fungus for you, + * but you seem to be busy with another task at the moment. Perhaps you should + * finish whatever is occupying your attention at the moment and return to me once + * you're done. + */ + return 1054081; + } + else + { + /* The Solen Matriarch smiles as she eats the seed you offered.

+ * + * Thank you for that seed. It was quite delicious.

+ * + * I would offer to make you a friend of my colony, but you seem to be busy with + * another task at the moment. Perhaps you should finish whatever is occupying + * your attention at the moment and return to me once you're done. + */ + return 1054079; + } + } + } + + public override bool Logged{ get{ return false; } } + + public DontOfferConversation( bool friend ) + { + m_Friend = friend; + } + + public DontOfferConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Friend = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Friend ); + } + } + + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch looks pleased that you've accepted.

+ * + * Very good. Please start by hunting some infiltrators from the other solen + * colony and eliminating them. Slay 7 of them and then return to me.

+ * + * Farewell for now and good hunting. + */ + return 1054084; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new KillInfiltratorsObjective() ); + } + } + + public class DuringKillInfiltratorsConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch looks up as you approach.

+ * + * You're back, but you have not yet eliminated 7 infiltrators from the enemy + * colony. Return when you have completed this task.

+ * + * Carry on. I'll be waiting for your return. + */ + return 1054089; + } + } + + public override bool Logged{ get{ return false; } } + + public DuringKillInfiltratorsConversation() + { + } + } + + public class GatherWaterConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch nods favorably as you approach her.

+ * + * Marvelous! I'm impressed at your ability to hunt and kill enemies for me. + * My colony is thankful.

+ * + * Now I must ask that you gather some water for me. A standard pitcher of water + * holds approximately one gallon. Please decant 8 gallons of fresh water + * into our water vats.

+ * + * Farewell for now. + */ + return 1054091; + } + } + + public GatherWaterConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GatherWaterObjective() ); + } + } + + public class DuringWaterGatheringConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch looks up as you approach.

+ * + * You're back, but you have not yet gathered 8 gallons of water. Return when + * you have completed this task.

+ * + * Carry on. I'll be waiting for your return. + */ + return 1054094; + } + } + + public override bool Logged{ get{ return false; } } + + public DuringWaterGatheringConversation() + { + } + } + + public class ProcessFungiConversation : QuestConversation + { + private bool m_Friend; + + public override object Message + { + get + { + if ( m_Friend ) + { + /* The Solen Matriarch listens as you report the completion of your + * tasks to her.

+ * + * I give you my thanks for your help, and I will gladly process some zoogi + * fungus into powder of translocation for you. Two of the zoogi fungi are + * required for each measure of the powder. I will process up to 200 zoogi fungi + * into 100 measures of powder of translocation.

+ * + * I will also give you some gold for assisting me and my colony, but first let's + * take care of your zoogi fungus. + */ + return 1054097; + } + else + { + /* The Solen Matriarch listens as you report the completion of your + * tasks to her.

+ * + * I give you my thanks for your help, and I will gladly make you a friend of my + * solen colony. My warriors, workers, and queens will not longer look at you + * as an intruder and attack you when you enter our lair.

+ * + * I will also process some zoogi fungus into powder of translocation for you. + * Two of the zoogi fungi are required for each measure of the powder. I will + * process up to 200 zoogi fungi into 100 measures of powder of translocation.

+ * + * I will also give you some gold for assisting me and my colony, but first let's + * take care of your zoogi fungus. + */ + return 1054096; + } + } + } + + public ProcessFungiConversation( bool friend ) + { + m_Friend = friend; + } + + public override void OnRead() + { + System.AddObjective( new ProcessFungiObjective() ); + } + + public ProcessFungiConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Friend = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Friend ); + } + } + + public class DuringFungiProcessConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch smiles as you greet her.

+ * + * I will gladly process some zoogi fungus into powder of translocation for you. + * Two of the zoogi fungi are required for each measure of the powder. + * I will process up to 200 zoogi fungi into 100 measures of powder of translocation. + */ + return 1054099; + } + } + + public override bool Logged{ get{ return false; } } + + public DuringFungiProcessConversation() + { + } + } + + public class FullBackpackConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch looks at you with a smile.

+ * + * While I'd like to finish conducting our business, it seems that you're a + * bit overloaded with equipment at the moment.

+ * + * Perhaps you should free some room in your backpack before we proceed. + */ + return 1054102; + } + } + + private bool m_Logged; + + public override bool Logged{ get{ return m_Logged; } } + + public FullBackpackConversation( bool logged ) + { + m_Logged = logged; + } + + public FullBackpackConversation() + { + m_Logged = true; + } + + public override void OnRead() + { + if ( m_Logged ) + System.AddObjective( new GetRewardObjective() ); + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* The Solen Matriarch smiles as you greet her.

+ * + * Ah good, you've returned. I will conclude our business by giving you + * gold I owe you for aiding me. + */ + return 1054101; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Solen Matriarch/Mobiles/SolenMatriarch.cs b/Scripts/Engines/Quests/Solen Matriarch/Mobiles/SolenMatriarch.cs new file mode 100644 index 000000000..60dd74d21 --- /dev/null +++ b/Scripts/Engines/Quests/Solen Matriarch/Mobiles/SolenMatriarch.cs @@ -0,0 +1,339 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; +using Server.ContextMenus; +using Server.Engines.Plants; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Matriarch +{ + public abstract class BaseSolenMatriarch : BaseQuester + { + public abstract bool RedSolen{ get; } + + public override bool DisallowAllMoves{ get{ return false; } } + + public BaseSolenMatriarch() + { + Name = "the solen matriarch"; + + Body = 0x328; + + if ( !RedSolen ) + Hue = 0x44E; + + SpeechHue = 0; + } + + public override int GetIdleSound() + { + return 0x10D; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + if ( SolenMatriarchQuest.IsFriend( to, this.RedSolen ) ) + return true; + + SolenMatriarchQuest qs = to.Quest as SolenMatriarchQuest; + + return qs != null && qs.RedSolen == this.RedSolen; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + this.Direction = GetDirectionTo( player ); + + SolenMatriarchQuest qs = player.Quest as SolenMatriarchQuest; + + if ( qs != null && qs.RedSolen == this.RedSolen ) + { + if ( qs.IsObjectiveInProgress( typeof( KillInfiltratorsObjective ) ) ) + { + qs.AddConversation( new DuringKillInfiltratorsConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( ReturnAfterKillsObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( GatherWaterObjective ) ) ) + { + qs.AddConversation( new DuringWaterGatheringConversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnAfterWaterObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( ProcessFungiObjective ) ) ) + { + qs.AddConversation( new DuringFungiProcessConversation() ); + } + else + { + obj = qs.FindObjective( typeof( GetRewardObjective ) ); + + if ( obj != null && !obj.Completed ) + { + if ( SolenMatriarchQuest.GiveRewardTo( player ) ) + { + obj.Complete(); + } + else + { + qs.AddConversation( new FullBackpackConversation( false ) ); + } + } + } + } + } + } + else if ( SolenMatriarchQuest.IsFriend( player, this.RedSolen ) ) + { + QuestSystem newQuest = new SolenMatriarchQuest( player, this.RedSolen ); + + if ( player.Quest == null && QuestSystem.CanOfferQuest( player, typeof( SolenMatriarchQuest ) ) ) + { + newQuest.SendOffer(); + } + else + { + newQuest.AddConversation( new DontOfferConversation( true ) ); + } + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + if ( dropped is Seed ) + { + SolenMatriarchQuest qs = player.Quest as SolenMatriarchQuest; + + if ( qs != null && qs.RedSolen == this.RedSolen ) + { + SayTo( player, 1054080 ); // Thank you for that plant seed. Those have such wonderful flavor. + } + else + { + QuestSystem newQuest = new SolenMatriarchQuest( player, this.RedSolen ); + + if ( player.Quest == null && QuestSystem.CanOfferQuest( player, typeof( SolenMatriarchQuest ) ) ) + { + newQuest.SendOffer(); + } + else + { + newQuest.AddConversation( new DontOfferConversation( SolenMatriarchQuest.IsFriend( player, this.RedSolen ) ) ); + } + } + + dropped.Delete(); + return true; + } + else if ( dropped is ZoogiFungus ) + { + OnGivenFungi( player, (ZoogiFungus)dropped ); + + return dropped.Deleted; + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null ) + { + SolenMatriarchQuest qs = pm.Quest as SolenMatriarchQuest; + + if ( qs != null && qs.RedSolen == this.RedSolen ) + { + if ( qs.IsObjectiveInProgress( typeof( ProcessFungiObjective ) ) ) + { + list.Add( new ProcessZoogiFungusEntry( this, pm ) ); + } + } + } + } + } + + private class ProcessZoogiFungusEntry : ContextMenuEntry + { + private BaseSolenMatriarch m_Matriarch; + private PlayerMobile m_From; + + public ProcessZoogiFungusEntry( BaseSolenMatriarch matriarch, PlayerMobile from ) : base( 6184 ) + { + m_Matriarch = matriarch; + m_From = from; + } + + public override void OnClick() + { + if ( m_From.Alive ) + m_From.Target = new ProcessFungiTarget( m_Matriarch, m_From ); + } + } + + private class ProcessFungiTarget : Target + { + private BaseSolenMatriarch m_Matriarch; + private PlayerMobile m_From; + + public ProcessFungiTarget( BaseSolenMatriarch matriarch, PlayerMobile from ) : base( -1, false, TargetFlags.None ) + { + m_Matriarch = matriarch; + m_From = from; + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + from.SendLocalizedMessage( 1042021, "", 0x59 ); // Cancelled. + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is ZoogiFungus ) + { + ZoogiFungus fungus = (ZoogiFungus)targeted; + + if ( fungus.IsChildOf( m_From.Backpack ) ) + m_Matriarch.OnGivenFungi( m_From, (ZoogiFungus)targeted ); + else + m_From.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + } + + public void OnGivenFungi( PlayerMobile player, ZoogiFungus fungi ) + { + this.Direction = GetDirectionTo( player ); + + SolenMatriarchQuest qs = player.Quest as SolenMatriarchQuest; + + if ( qs != null && qs.RedSolen == this.RedSolen ) + { + QuestObjective obj = qs.FindObjective( typeof( ProcessFungiObjective ) ); + + if ( obj != null && !obj.Completed ) + { + int amount = fungi.Amount / 2; + + if ( amount > 100 ) + amount = 100; + + if ( amount > 0 ) + { + if ( amount * 2 >= fungi.Amount ) + fungi.Delete(); + else + fungi.Amount -= amount * 2; + + PowderOfTranslocation powder = new PowderOfTranslocation( amount ); + player.AddToBackpack( powder ); + + player.SendLocalizedMessage( 1054100 ); // You receive some powder of translocation. + + obj.Complete(); + } + } + } + } + + public BaseSolenMatriarch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class RedSolenMatriarch : BaseSolenMatriarch + { + public override bool RedSolen{ get{ return true; } } + + [Constructable] + public RedSolenMatriarch() + { + } + + public RedSolenMatriarch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class BlackSolenMatriarch : BaseSolenMatriarch + { + public override bool RedSolen{ get{ return false; } } + + [Constructable] + public BlackSolenMatriarch() + { + } + + public BlackSolenMatriarch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Solen Matriarch/Objectives.cs b/Scripts/Engines/Quests/Solen Matriarch/Objectives.cs new file mode 100644 index 000000000..bc337bafd --- /dev/null +++ b/Scripts/Engines/Quests/Solen Matriarch/Objectives.cs @@ -0,0 +1,216 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Matriarch +{ + public class KillInfiltratorsObjective : QuestObjective + { + public override object Message + { + get + { + // Kill 7 black/red solen infiltrators. + return ((SolenMatriarchQuest)System).RedSolen ? 1054086 : 1054085; + } + } + + public override int MaxProgress{ get{ return 7; } } + + public KillInfiltratorsObjective() + { + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Black/Red Solen Infiltrators killed: + gump.AddHtmlLocalized( 70, 260, 270, 100, ((SolenMatriarchQuest)System).RedSolen ? 1054088 : 1054087, BaseQuestGump.Blue, false, false ); + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, MaxProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + if ( Completed ) + return false; + + bool redSolen = ((SolenMatriarchQuest)System).RedSolen; + + if ( redSolen ) + return from is BlackSolenInfiltratorWarrior || from is BlackSolenInfiltratorQueen; + else + return from is RedSolenInfiltratorWarrior || from is RedSolenInfiltratorQueen; + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + bool redSolen = ((SolenMatriarchQuest)System).RedSolen; + + if ( redSolen ) + { + if ( creature is BlackSolenInfiltratorWarrior || creature is BlackSolenInfiltratorQueen ) + CurProgress++; + } + else + { + if ( creature is RedSolenInfiltratorWarrior || creature is RedSolenInfiltratorQueen ) + CurProgress++; + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnAfterKillsObjective() ); + } + } + + public class ReturnAfterKillsObjective : QuestObjective + { + public override object Message + { + get + { + /* You've completed your task of slaying solen infiltrators. Return to the + * Matriarch who gave you this task. + */ + return 1054090; + } + } + + public ReturnAfterKillsObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new GatherWaterConversation() ); + } + } + + public class GatherWaterObjective : QuestObjective + { + public override object Message + { + get + { + // Gather 8 gallons of water for the water vats of the solen ant lair. + return 1054092; + } + } + + public override int MaxProgress{ get{ return 40; } } + + public GatherWaterObjective() + { + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + gump.AddHtmlLocalized( 70, 260, 270, 100, 1054093, BaseQuestGump.Blue, false, false ); // Gallons of Water gathered: + gump.AddLabel( 70, 280, 0x64, (CurProgress / 5).ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, (MaxProgress / 5).ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnAfterWaterObjective() ); + } + } + + public class ReturnAfterWaterObjective : QuestObjective + { + public override object Message + { + get + { + // You've completed your task of gathering water. Return to the Matriarch who gave you this task. + return 1054095; + } + } + + public ReturnAfterWaterObjective() + { + } + + public override void OnComplete() + { + PlayerMobile player = System.From; + bool redSolen = ((SolenMatriarchQuest)System).RedSolen; + + bool friend = SolenMatriarchQuest.IsFriend( player, redSolen ); + + System.AddConversation( new ProcessFungiConversation( friend ) ); + + if ( redSolen ) + player.SolenFriendship = SolenFriendship.Red; + else + player.SolenFriendship = SolenFriendship.Black; + } + } + + public class ProcessFungiObjective : QuestObjective + { + public override object Message + { + get + { + // Give the Solen Matriarch a stack of zoogi fungus to process into powder of translocation. + return 1054098; + } + } + + public ProcessFungiObjective() + { + } + + public override void OnComplete() + { + if ( SolenMatriarchQuest.GiveRewardTo( System.From ) ) + { + System.Complete(); + } + else + { + System.AddConversation( new FullBackpackConversation( true ) ); + } + } + } + + public class GetRewardObjective : QuestObjective + { + public override object Message + { + get + { + // Return to the solen matriarch for your reward. + return 1054149; + } + } + + public GetRewardObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new EndConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Solen Matriarch/SolenMatriarchQuest.cs b/Scripts/Engines/Quests/Solen Matriarch/SolenMatriarchQuest.cs new file mode 100644 index 000000000..d87514658 --- /dev/null +++ b/Scripts/Engines/Quests/Solen Matriarch/SolenMatriarchQuest.cs @@ -0,0 +1,160 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Matriarch +{ + public class SolenMatriarchQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Matriarch.DontOfferConversation ), + typeof( Matriarch.AcceptConversation ), + typeof( Matriarch.DuringKillInfiltratorsConversation ), + typeof( Matriarch.GatherWaterConversation ), + typeof( Matriarch.DuringWaterGatheringConversation ), + typeof( Matriarch.ProcessFungiConversation ), + typeof( Matriarch.DuringFungiProcessConversation ), + typeof( Matriarch.FullBackpackConversation ), + typeof( Matriarch.EndConversation ), + typeof( Matriarch.KillInfiltratorsObjective ), + typeof( Matriarch.ReturnAfterKillsObjective ), + typeof( Matriarch.GatherWaterObjective ), + typeof( Matriarch.ReturnAfterWaterObjective ), + typeof( Matriarch.ProcessFungiObjective ), + typeof( Matriarch.GetRewardObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // Solen Matriarch Quest + return 1054147; + } + } + + public override object OfferMessage + { + get + { + if ( IsFriend( From, RedSolen ) ) + { + /* The Solen Matriarch smiles happily as you greet her.

+ * + * Hello again. It is always good to see a friend of our colony.

+ * + * Would you like me to process some zoogi fungus into powder of translocation + * for you? I would be happy to do so if you will first undertake a couple + * tasks for me.

+ * + * First, I would like for you to eliminate some infiltrators from the other + * solen colony. They are spying on my colony, and I fear for the safety of my + * people. They must be slain.

+ * + * After that, I must ask that you gather some water for me. Our water supplies + * are inadequate, so we must try to supplement our reserve using water vats here + * in our lair.

+ * + * Will you accept my offer? + */ + return 1054083; + } + else + { + /* The Solen Matriarch smiles happily as she eats the seed you offered.

+ * + * I think you for that seed. I was quite delicious. So full of flavor.

+ * + * Hmm... if you would like, I could make you a friend of my colony. This would stop + * the warriors, workers, and queens of my colony from thinking you are an intruder, + * thus they would not attack you. In addition, as a friend of my colony I will process + * zoogi fungus into powder of translocation for you.

+ * + * To become a friend of my colony, I ask that you complete a couple tasks for me. These + * are the same tasks I will ask of you when you wish me to process zoogi fungus, + * by the way.

+ * + * First, I would like for you to eliminate some infiltrators from the other solen colony. + * They are spying on my colony, and I fear for the safety of my people. They must + * be slain.

+ * + * After that, I must ask that you gather some water for me. Our water supplies are + * inadequate, so we must try to supplement our reserve using water vats here in our + * lair.

+ * + * Will you accept my offer? + */ + return 1054082; + } + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.Zero; } } + public override bool IsTutorial{ get{ return false; } } + + public override int Picture{ get{ return 0x15C9; } } + + private bool m_RedSolen; + + public bool RedSolen{ get{ return m_RedSolen; } } + + public SolenMatriarchQuest( PlayerMobile from, bool redSolen ) : base( from ) + { + m_RedSolen = redSolen; + } + + // Serialization + public SolenMatriarchQuest() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_RedSolen = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_RedSolen ); + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + public static bool IsFriend( PlayerMobile player, bool redSolen ) + { + if ( redSolen ) + return player.SolenFriendship == SolenFriendship.Red; + else + return player.SolenFriendship == SolenFriendship.Black; + } + + public static bool GiveRewardTo( PlayerMobile player ) + { + Gold gold = new Gold( Utility.RandomMinMax( 250, 350 ) ); + + if ( player.PlaceInBackpack( gold ) ) + { + player.SendLocalizedMessage( 1054076 ); // You have been given some gold. + return true; + } + else + { + gold.Delete(); + return false; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Study of the Solen Hive/Conversations.cs b/Scripts/Engines/Quests/Study of the Solen Hive/Conversations.cs new file mode 100644 index 000000000..ae569c95d --- /dev/null +++ b/Scripts/Engines/Quests/Study of the Solen Hive/Conversations.cs @@ -0,0 +1,205 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Naturalist +{ + public class DontOfferConversation : QuestConversation + { + public override object Message + { + get + { + /* The Naturalist looks up from his scribbled notes.

+ * + * Greetings!

+ * + * If you're interested in helping out a scholar of some repute, I do have some + * work that I could use some assistance with.

+ * + * You seem a little preoccupied with another task right now, however. Perhaps + * you should finish whatever it is that has your attention at the moment and + * return to me once you're done. + */ + return 1054052; + } + } + + public override bool Logged{ get{ return false; } } + + public DontOfferConversation() + { + } + } + + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Ah! This is splendid news! Each time an assistant travels into the + * Solen Hive to gather information for me, I feel as if I am one step + * closer to some grand discovery. Though I felt the same way when I was + * certain that Terathans had the ability to change their shape to resemble + * various fruits and vegetables - a point on which I am certain further + * study of the beasts will prove correct.

+ * + * In any case, I cannot thank you enough! Please return to me when you have + * studied all the Solen Egg Nests hidden within the Solen Hive. + */ + return 1054043; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new StudyNestsObjective() ); + } + } + + public class NaturalistDuringStudyConversation : QuestConversation + { + public override object Message + { + get + { + /* The Naturalist looks up from his notes with a frustrated look + * on his face.

+ * + * Haven't you finished the task I appointed you yet? Gah! It's so + * difficult to find a good apprentice these days.

+ * + * Remember, you must first find an entrance to the Solen Hive. Once inside, + * I need you to examine the Solen Egg Nests for me. When you have studied + * all four nests, you should have enough information to earn yourself a + * reward.

+ * + * Now go on, away with you! I have piles of notes from other more helpful + * apprentices that I still need to study! + */ + return 1054049; + } + } + + public override bool Logged{ get{ return false; } } + + public NaturalistDuringStudyConversation() + { + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* The Naturalist looks up from his notes with a pleased expression + * on his face.

+ * + * Ah! Thank you, my goodly apprentice! These notes you have gathered will + * no doubt assist me in my understanding of these fascinating Solen creatures.

+ * + * Now, since you've done such a fine job, I feel that I should give you a little + * reward.

+ * + * I have a botanist friend who has discovered a strange mutation in the plants + * she has grown. Through science and sorcery, she has managed to produce a mutant + * strain of colored seeds the like of which no gardener has laid eyes upon.

+ * + * As a reward for your fine efforts, I present you with this strange rare seed. + * Which reminds me, I still need to compile my notes on Solen dietary habits. They + * are voracious seed eaters, those Solen Matriarchs!

+ * + * In any case, I must get back to my notes now. I give you my thanks once more, + * and bid a good day to you my little apprentice! If you wish to help me out again, + * just say the word. + */ + return 1054050; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class SpecialEndConversation : QuestConversation + { + public override object Message + { + get + { + /* The Naturalist looks up from his notes with an ecstatic look upon + * his face.

+ * + * Oh my! These notes you've brought me - they say you have information on the + * Secret Solen Egg Nest? I've heard many tales of this secret store of special + * Solen Eggs, but there are many missing gaps in my notes concerning it.

+ * + * The notes you've made here will most certainly advance my understanding of + * this mysterious breed of creatures!

+ * + * Considering the amazing effort you put into your work, I feel as if I should + * give you something extra special as a bonus. Hrmm...

+ * + * I have a botanist friend who has discovered a strange mutation in the plants + * she has grown. Through science and sorcery, she has managed to produce a rare + * mutant strain of colored seeds the like of which no gardener has laid + * eyes upon.

+ * + * I've given a few of her seeds out to various apprentices - but I usually keep + * her very rare stock all for myself. They're quite amazing looking! However, + * since you've done such a fine job for me, I'll present you with one of + * these rare fire-red seeds.

+ * + * Once again, my thanks to you! Now I really must get back to studying these notes! + * Take care, my fine apprentice, and come back if you wish to help me further! + */ + return 1054051; + } + } + + public SpecialEndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class FullBackpackConversation : QuestConversation + { + public override object Message + { + get + { + /* The Naturalist looks at you with a friendly expression.

+ * + * I see you've returned with information for me. While I'd like to finish + * conducting our business, it seems that you're a bit overloaded with + * equipment at the moment. Perhaps you'd better free up some room before + * we get to discussing your reward. + */ + return 1054053; + } + } + + public override bool Logged{ get{ return false; } } + + public FullBackpackConversation() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Study of the Solen Hive/Mobiles/Naturalist.cs b/Scripts/Engines/Quests/Study of the Solen Hive/Mobiles/Naturalist.cs new file mode 100644 index 000000000..07d556f1a --- /dev/null +++ b/Scripts/Engines/Quests/Study of the Solen Hive/Mobiles/Naturalist.cs @@ -0,0 +1,158 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Plants; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Naturalist +{ + public class Naturalist : BaseQuester + { + [Constructable] + public Naturalist() : base( "the Naturalist" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = Utility.RandomSkinHue(); + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + public override void InitOutfit() + { + AddItem( new Tunic( 0x598 ) ); + AddItem( new LongPants( 0x59B ) ); + AddItem( new Boots() ); + + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, HairHue ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + StudyOfSolenQuest qs = player.Quest as StudyOfSolenQuest; + + if ( qs != null && qs.Naturalist == this ) + { + StudyNestsObjective study = qs.FindObjective( typeof( StudyNestsObjective ) ) as StudyNestsObjective; + + if ( study != null ) + { + if ( !study.Completed ) + { + PlaySound( 0x41F ); + qs.AddConversation( new NaturalistDuringStudyConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( ReturnToNaturalistObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Seed reward; + + PlantType type; + switch ( Utility.Random( 17 ) ) + { + case 0: type = PlantType.CampionFlowers; break; + case 1: type = PlantType.Poppies; break; + case 2: type = PlantType.Snowdrops; break; + case 3: type = PlantType.Bulrushes; break; + case 4: type = PlantType.Lilies; break; + case 5: type = PlantType.PampasGrass; break; + case 6: type = PlantType.Rushes; break; + case 7: type = PlantType.ElephantEarPlant; break; + case 8: type = PlantType.Fern; break; + case 9: type = PlantType.PonytailPalm; break; + case 10: type = PlantType.SmallPalm; break; + case 11: type = PlantType.CenturyPlant; break; + case 12: type = PlantType.WaterPlant; break; + case 13: type = PlantType.SnakePlant; break; + case 14: type = PlantType.PricklyPearCactus; break; + case 15: type = PlantType.BarrelCactus; break; + default: type = PlantType.TribarrelCactus; break; + } + + if ( study.StudiedSpecialNest ) + { + reward = new Seed( type, PlantHue.FireRed, false ); + } + else + { + PlantHue hue; + switch ( Utility.Random( 3 ) ) + { + case 0: hue = PlantHue.Pink; break; + case 1: hue = PlantHue.Magenta; break; + default: hue = PlantHue.Aqua; break; + } + + reward = new Seed( type, hue, false ); + } + + if ( player.PlaceInBackpack( reward ) ) + { + obj.Complete(); + + PlaySound( 0x449 ); + PlaySound( 0x41B ); + + if ( study.StudiedSpecialNest ) + qs.AddConversation( new SpecialEndConversation() ); + else + qs.AddConversation( new EndConversation() ); + } + else + { + reward.Delete(); + + qs.AddConversation( new FullBackpackConversation() ); + } + } + } + } + } + else + { + QuestSystem newQuest = new StudyOfSolenQuest( player, this ); + + if ( player.Quest == null && QuestSystem.CanOfferQuest( player, typeof( StudyOfSolenQuest ) ) ) + { + PlaySound( 0x42F ); + newQuest.SendOffer(); + } + else + { + PlaySound( 0x448 ); + newQuest.AddConversation( new DontOfferConversation() ); + } + } + } + + public Naturalist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Study of the Solen Hive/NestArea.cs b/Scripts/Engines/Quests/Study of the Solen Hive/NestArea.cs new file mode 100644 index 000000000..c53de64bb --- /dev/null +++ b/Scripts/Engines/Quests/Study of the Solen Hive/NestArea.cs @@ -0,0 +1,91 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Naturalist +{ + public class NestArea + { + private static readonly NestArea[] m_Areas = new NestArea[] + { + new NestArea( false, new Rectangle2D( 5861, 1787, 26, 25 ) ), + + new NestArea( false, new Rectangle2D( 5734, 1788, 14, 50 ), + new Rectangle2D( 5748, 1800, 3, 34 ), + new Rectangle2D( 5751, 1808, 2, 20 ) ), + + new NestArea( false, new Rectangle2D( 5907, 1908, 19, 43 ) ), + + new NestArea( false, new Rectangle2D( 5721, 1926, 24, 29 ), + new Rectangle2D( 5745, 1935, 7, 22 ) ), + + new NestArea( true, new Rectangle2D( 5651, 1853, 21, 32 ), + new Rectangle2D( 5672, 1857, 6, 20 ) ) + }; + + public static int NonSpecialCount + { + get + { + int n = 0; + foreach ( NestArea area in m_Areas ) + { + if ( !area.Special ) + n++; + } + return n; + } + } + + public static NestArea Find( IPoint2D p ) + { + foreach ( NestArea area in m_Areas ) + { + if ( area.Contains( p ) ) + return area; + } + return null; + } + + public static NestArea GetByID( int id ) + { + if ( id >= 0 && id < m_Areas.Length ) + return m_Areas[id]; + else + return null; + } + + private bool m_Special; + private Rectangle2D[] m_Rects; + + public bool Special{ get{ return m_Special; } } + + public int ID + { + get + { + for ( int i = 0; i < m_Areas.Length; i++ ) + { + if ( m_Areas[i] == this ) + return i; + } + return 0; + } + } + + private NestArea( bool special, params Rectangle2D[] rects ) + { + m_Special = special; + m_Rects = rects; + } + + public bool Contains( IPoint2D p ) + { + foreach ( Rectangle2D rect in m_Rects ) + { + if ( rect.Contains( p ) ) + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Study of the Solen Hive/Objectives.cs b/Scripts/Engines/Quests/Study of the Solen Hive/Objectives.cs new file mode 100644 index 000000000..ca38b955c --- /dev/null +++ b/Scripts/Engines/Quests/Study of the Solen Hive/Objectives.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Naturalist +{ + public class StudyNestsObjective : QuestObjective + { + public override object Message + { + get + { + /* Find an entrance to the Solen Hive, and search within for the Solen + * Egg Nests. Each Nest must be studied for some time without a break in + * concentration in order to gather useful information.

+ * + * Once you have completed your study of the Nests, return to the Naturalist + * who gave you this task. + */ + return 1054044; + } + } + + public override int MaxProgress{ get{ return NestArea.NonSpecialCount; } } + + private NestArea m_CurrentNest; + private DateTime m_StudyBegin; + + private enum StudyState { Inactive, FirstStep, SecondStep } + private StudyState m_StudyState; + + private ArrayList m_StudiedNests; + private bool m_StudiedSpecialNest; + + public bool StudiedSpecialNest{ get{ return m_StudiedSpecialNest; } } + + public StudyNestsObjective() + { + m_StudiedNests = new ArrayList(); + } + + public override bool GetTimerEvent() + { + return true; + } + + public override void CheckProgress() + { + PlayerMobile from = System.From; + + if ( m_CurrentNest != null ) + { + NestArea nest = m_CurrentNest; + + if ( ( from.Map == Map.Trammel || from.Map == Map.Felucca ) && nest.Contains( from ) ) + { + if ( m_StudyState != StudyState.Inactive ) + { + TimeSpan time = DateTime.Now - m_StudyBegin; + + if ( time > TimeSpan.FromSeconds( 30.0 ) ) + { + m_StudiedNests.Add( nest ); + m_StudyState = StudyState.Inactive; + + if ( m_CurrentNest.Special ) + { + from.SendLocalizedMessage( 1054057 ); // You complete your examination of this bizarre Egg Nest. The Naturalist will undoubtedly be quite interested in these notes! + m_StudiedSpecialNest = true; + } + else + { + from.SendLocalizedMessage( 1054054 ); // You have completed your study of this Solen Egg Nest. You put your notes away. + CurProgress++; + } + } + else if ( m_StudyState == StudyState.FirstStep && time > TimeSpan.FromSeconds( 15.0 ) ) + { + if ( !nest.Special ) + from.SendLocalizedMessage( 1054058 ); // You begin recording your completed notes on a bit of parchment. + + m_StudyState = StudyState.SecondStep; + } + } + } + else + { + if ( m_StudyState != StudyState.Inactive ) + from.SendLocalizedMessage( 1054046 ); // You abandon your study of the Solen Egg Nest without gathering the needed information. + + m_CurrentNest = null; + } + } + else if ( from.Map == Map.Trammel || from.Map == Map.Felucca ) + { + NestArea nest = NestArea.Find( from ); + + if ( nest != null ) + { + m_CurrentNest = nest; + m_StudyBegin = DateTime.Now; + + if ( m_StudiedNests.Contains( nest ) ) + { + m_StudyState = StudyState.Inactive; + + from.SendLocalizedMessage( 1054047 ); // You glance at the Egg Nest, realizing you've already studied this one. + } + else + { + m_StudyState = StudyState.FirstStep; + + if ( nest.Special ) + from.SendLocalizedMessage( 1054056 ); // You notice something very odd about this Solen Egg Nest. You begin taking notes. + else + from.SendLocalizedMessage( 1054045 ); // You begin studying the Solen Egg Nest to gather information. + + if ( from.Female ) + from.PlaySound( 0x30B ); + else + from.PlaySound( 0x419 ); + } + } + } + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + gump.AddHtmlLocalized( 70, 260, 270, 100, 1054055, BaseQuestGump.Blue, false, false ); // Solen Nests Studied : + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, MaxProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnToNaturalistObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + int count = reader.ReadEncodedInt(); + for ( int i = 0; i < count; i++ ) + { + NestArea nest = NestArea.GetByID( reader.ReadEncodedInt() ); + m_StudiedNests.Add( nest ); + } + + m_StudiedSpecialNest = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_StudiedNests.Count ); + foreach ( NestArea nest in m_StudiedNests ) + { + writer.WriteEncodedInt( (int) nest.ID ); + } + + writer.Write( (bool) m_StudiedSpecialNest ); + } + } + + public class ReturnToNaturalistObjective : QuestObjective + { + public override object Message + { + get + { + /* You have studied enough Solen Egg Nests to gather a fair amount of + * useful information. Return to the Naturalist who gave you this task. + */ + return 1054048; + } + } + + public ReturnToNaturalistObjective() + { + } + + public override void RenderProgress( BaseQuestGump gump ) + { + string count = NestArea.NonSpecialCount.ToString(); + + gump.AddHtmlLocalized( 70, 260, 270, 100, 1054055, BaseQuestGump.Blue, false, false ); // Solen Nests Studied : + gump.AddLabel( 70, 280, 0x64, count ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, count ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Study of the Solen Hive/StudyOfSolenQuest.cs b/Scripts/Engines/Quests/Study of the Solen Hive/StudyOfSolenQuest.cs new file mode 100644 index 000000000..f8f3ca00a --- /dev/null +++ b/Scripts/Engines/Quests/Study of the Solen Hive/StudyOfSolenQuest.cs @@ -0,0 +1,111 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Naturalist +{ + public class StudyOfSolenQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( StudyNestsObjective ), + typeof( ReturnToNaturalistObjective ), + typeof( DontOfferConversation ), + typeof( AcceptConversation ), + typeof( NaturalistDuringStudyConversation ), + typeof( EndConversation ), + typeof( SpecialEndConversation ), + typeof( FullBackpackConversation ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // "Study of the Solen Hive" + return 1054041; + } + } + + public override object OfferMessage + { + get + { + /* The Naturalist looks up from his notes, regarding you with a hopeful + * look in his eyes.

+ * + * Um..yes..excuse me. I was wondering if you could offer me a bit of assistance. + * You see, I'm a naturalist of some repute - a gentleman and a scholar if you + * will - primarily interested in the study of insects and arachnids. While I've + * written a few interesting books on the marvelous Terathan race and their bizarre + * culture, now I've heard tales of a truly significant new discovery!

+ * + * Apparently a race of ant-like creatures known as the Solen have appeared in + * our world, scuttling up from some previously hidden home. Can you believe it? + * Truly these are amazing times! To a scholar such as myself this is indeed + * an exciting opportunity.

+ * + * That said, while I may be a genius of some reknown, sharp as a tack and quick + * with the quill, I'm afraid I'm not much of the adventuring type. Though I have + * gained assistance before, I still have many unanswered questions.

+ * + * I am particularly interested in the Solen Egg Nests that are studiously + * protected by the Solen workers. If you would be so kind as to assist me, + * I would ask that you travel into the Solen Hive and inspect each of the + * Solen Egg Nests that reside within. You will have to spend some time examining + * each Nest before you have gathered enough information. Once you are done, + * report back to me and I will reward you as best as I can for your valiant + * efforts!

+ * + * Will you accept my offer? + */ + return 1054042; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.Zero; } } + public override bool IsTutorial{ get{ return false; } } + + public override int Picture{ get{ return 0x15C7; } } + + private Naturalist m_Naturalist; + + public Naturalist Naturalist{ get{ return m_Naturalist; } } + + public StudyOfSolenQuest( PlayerMobile from, Naturalist naturalist ) : base( from ) + { + m_Naturalist = naturalist; + } + + // Serialization + public StudyOfSolenQuest() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Naturalist = (Naturalist) reader.ReadMobile(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Mobile) m_Naturalist ); + } + + public override void Accept() + { + base.Accept(); + + if ( m_Naturalist != null ) + m_Naturalist.PlaySound( 0x431 ); + + AddConversation( new AcceptConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Terrible Hatchlings/Conversations.cs b/Scripts/Engines/Quests/Terrible Hatchlings/Conversations.cs new file mode 100644 index 000000000..2946ae670 --- /dev/null +++ b/Scripts/Engines/Quests/Terrible Hatchlings/Conversations.cs @@ -0,0 +1,111 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Zento +{ + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Important Quest Information

+ * + * During your quest, any important information that a + * NPC gives you, will appear in a window + * such as this one. You can review the information at any time during your + * quest.

+ * + * Getting Help

+ * + * Some of the text you will come across during your quest, + * will be underlined links to the codex of wisdom, + * or online help system. You can click on the text to get detailed information + * on the underlined subject. You may also access the Codex Of Wisdom by + * pressing "F1" or by clicking on the "?" on the toolbar at the top of + * your screen.

Context Menus

+ * + * Context menus can be called up by single left-clicking (or Shift + single + * left-click, if you changed it) on most objects or NPCs in the world. + * Nearly everything, including your own avatar will have context menus available. + * Bringing up your avatar's context menu will give you options to cancel your quest + * and review various quest information.

+ */ + return 1049092; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FirstKillObjective() ); + } + } + + public class DirectionConversation : QuestConversation + { + public override object Message + { + get + { + // The Deathwatch Beetle Hatchlings live in The Waste - the desert close to this city. + return 1063323; + } + } + + public override bool Logged{ get{ return false; } } + + public DirectionConversation() + { + } + } + + public class TakeCareConversation : QuestConversation + { + public override object Message + { + get + { + // I know you can take care of those nasty Deathwatch Beetle Hatchlings! No get to it! + return 1063324; + } + } + + public override bool Logged{ get{ return false; } } + + public TakeCareConversation() + { + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* Thank you for helping me get rid of these vile beasts! + * You have been rewarded for your good deeds. If you wish to + * help me in the future, visit me again.

+ * + * Farewell. + */ + return 1063321; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Terrible Hatchlings/Mobiles/AnsellaGryen.cs b/Scripts/Engines/Quests/Terrible Hatchlings/Mobiles/AnsellaGryen.cs new file mode 100644 index 000000000..1cdb0c16a --- /dev/null +++ b/Scripts/Engines/Quests/Terrible Hatchlings/Mobiles/AnsellaGryen.cs @@ -0,0 +1,138 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Items; + +namespace Server.Engines.Quests.Zento +{ + public class AnsellaGryen : BaseQuester + { + [Constructable] + public AnsellaGryen() + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83EA; + + Female = true; + Body = 0x191; + Name = "Ansella Gryen"; + } + + public override void InitOutfit() + { + HairItemID = 0x203B; + HairHue = 0x1BB; + + AddItem( new SamuraiTabi( 0x8FD ) ); + AddItem( new FemaleKimono( 0x4B6 ) ); + AddItem( new Obi( 0x526 ) ); + + AddItem( new GoldBracelet() ); + } + + public override int GetAutoTalkRange( PlayerMobile m ) + { + if ( m.Quest == null ) + return 3; + else + return -1; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is TerribleHatchlingsQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FirstKillObjective ) ) ) + { + qs.AddConversation( new DirectionConversation() ); + } + else if ( qs.IsObjectiveInProgress( typeof( SecondKillObjective ) ) + || qs.IsObjectiveInProgress( typeof( ThirdKillObjective ) ) ) + { + qs.AddConversation( new TakeCareConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( ReturnObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + cont.DropItem( new Gold( Utility.RandomMinMax( 100, 200 ) ) ); + + if ( Utility.RandomBool() ) + { + BaseWeapon weapon = Loot.RandomWeapon( true ); + BaseRunicTool.ApplyAttributesTo( weapon, 3, 10, 30 ); + + cont.DropItem( weapon ); + } + else + { + BaseArmor armor = Loot.RandomArmor( true ); + BaseRunicTool.ApplyAttributesTo( armor, 1, 10, 20 ); + + cont.DropItem( armor ); + } + + if ( player.PlaceInBackpack( cont ) ) + { + obj.Complete(); + } + else + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + } + } + else + { + TerribleHatchlingsQuest newQuest = new TerribleHatchlingsQuest( player ); + bool inRestartPeriod = false; + + if ( qs != null ) + { + if ( contextMenu ) + SayTo( player, 1063322 ); // Before you can help me with the Terrible Hatchlings, you'll need to finish the quest you've already taken! + } + else if ( QuestSystem.CanOfferQuest( player, typeof( TerribleHatchlingsQuest ), out inRestartPeriod ) ) + { + newQuest.SendOffer(); + } + else if ( inRestartPeriod && contextMenu ) + { + SayTo( player, 1049357 ); // I have nothing more for you at this time. + } + } + } + + public AnsellaGryen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Terrible Hatchlings/Objectives.cs b/Scripts/Engines/Quests/Terrible Hatchlings/Objectives.cs new file mode 100644 index 000000000..adb9c43d5 --- /dev/null +++ b/Scripts/Engines/Quests/Terrible Hatchlings/Objectives.cs @@ -0,0 +1,178 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Zento +{ + public class FirstKillObjective : QuestObjective + { + public override object Message + { + get + { + // Kill 10 Deathwatch Beetle Hatchlings and return to Ansella Gryen. + return 1063316; + } + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Deathwatch Beetle Hatchlings killed: + gump.AddHtmlLocalized( 70, 260, 270, 100, 1063318, 0x12DC6BF, false, false ); + + gump.AddLabel( 70, 280, 0x64, "0" ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, "10" ); + } + else + { + base.RenderProgress( gump ); + } + } + + public FirstKillObjective() + { + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is DeathwatchBeetleHatchling ) + Complete(); + } + + public override void OnComplete() + { + System.AddObjective( new SecondKillObjective() ); + } + } + + public class SecondKillObjective : QuestObjective + { + public override object Message + { + get + { + /* Great job! One less terrible hatchling in the Waste!

+ * + * Once you've killed 10 of the Deathwatch Beetle Hatchlings, + * return to Ansella for your reward! + */ + return 1063320; + } + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Deathwatch Beetle Hatchlings killed: + gump.AddHtmlLocalized( 70, 260, 270, 100, 1063318, 0x12DC6BF, false, false ); + + gump.AddLabel( 70, 280, 0x64, "1" ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, "10" ); + } + else + { + base.RenderProgress( gump ); + } + } + + public SecondKillObjective() + { + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is DeathwatchBeetleHatchling ) + { + Complete(); + System.AddObjective( new ThirdKillObjective( 2 ) ); + } + } + + public override void OnRead() + { + if ( !Completed ) + { + Complete(); + System.AddObjective( new ThirdKillObjective( 1 ) ); + } + } + } + + public class ThirdKillObjective : QuestObjective + { + public override object Message + { + get + { + // Continue killing Deathwatch Beetle Hatchlings. + return 1063319; + } + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + // Deathwatch Beetle Hatchlings killed: + gump.AddHtmlLocalized( 70, 260, 270, 100, 1063318, 0x12DC6BF, false, false ); + + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, "10" ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override int MaxProgress{ get{ return 10; } } + + public ThirdKillObjective( int startingProgress ) + { + CurProgress = startingProgress; + } + + public ThirdKillObjective() + { + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is DeathwatchBeetleHatchling ) + CurProgress++; + } + + public override void OnComplete() + { + System.AddObjective( new ReturnObjective() ); + } + } + + public class ReturnObjective : QuestObjective + { + public override object Message + { + get + { + // Return to Ansella Gryen for your reward. + return 1063313; + } + } + + public ReturnObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new EndConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Terrible Hatchlings/TerribleHatchlingsQuest.cs b/Scripts/Engines/Quests/Terrible Hatchlings/TerribleHatchlingsQuest.cs new file mode 100644 index 000000000..ed818a8ae --- /dev/null +++ b/Scripts/Engines/Quests/Terrible Hatchlings/TerribleHatchlingsQuest.cs @@ -0,0 +1,70 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Zento +{ + public class TerribleHatchlingsQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( AcceptConversation ), + typeof( DirectionConversation ), + typeof( TakeCareConversation ), + typeof( EndConversation ), + typeof( FirstKillObjective ), + typeof( SecondKillObjective ), + typeof( ThirdKillObjective ), + typeof( ReturnObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // Terrible Hatchlings + return 1063314; + } + } + + public override object OfferMessage + { + get + { + /* The Deathwatch Beetle Hatchlings have trampled through my fields + * again, what a nuisance! Please help me get rid of the terrible + * hatchlings. If you kill 10 of them, you will be rewarded. + * The Deathwatch Beetle Hatchlings live in The Waste - + * the desert close to this city.

+ * + * Will you accept this challenge? + */ + return 1063315; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.MaxValue; } } + public override bool IsTutorial{ get{ return true; } } + + public override int Picture{ get{ return 0x15CF; } } + + public TerribleHatchlingsQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public TerribleHatchlingsQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Conversations.cs b/Scripts/Engines/Quests/The Summoning/Conversations.cs new file mode 100644 index 000000000..34ee3ed96 --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Conversations.cs @@ -0,0 +1,93 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Doom +{ + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* You have accepted Victoria's help. She requires 1000 Daemon + * bones to summon the devourer.

+ * + * You may hand Victoria the bones as you collect them and she + * will keep count of how many you have brought her.

+ * + * Daemon bones can be collected via various means throughout + * Dungeon Doom.

+ * + * Good luck. + */ + return 1050027; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new CollectBonesObjective() ); + } + } + + public class VanquishDaemonConversation : QuestConversation + { + public override object Message + { + get + { + /* Well done brave soul. I shall summon the beast to the circle + * of stones just South-East of here. Take great care - the beast + * takes many forms. Now hurry... + */ + return 1050021; + } + } + + public VanquishDaemonConversation() + { + } + + public override void OnRead() + { + Victoria victoria = ((TheSummoningQuest)System).Victoria; + + if ( victoria == null ) + { + System.From.SendMessage( "Internal error: unable to find Victoria. Quest unable to continue." ); + System.Cancel(); + } + else + { + SummoningAltar altar = victoria.Altar; + + if ( altar == null ) + { + System.From.SendMessage( "Internal error: unable to find summoning altar. Quest unable to continue." ); + System.Cancel(); + } + else if ( altar.Daemon == null || !altar.Daemon.Alive ) + { + BoneDemon daemon = new BoneDemon(); + + daemon.MoveToWorld( altar.Location, altar.Map ); + altar.Daemon = daemon; + + System.AddObjective( new VanquishDaemonObjective( daemon ) ); + } + else + { + victoria.SayTo( System.From, "The devourer has already been summoned." ); + + ((TheSummoningQuest)System).WaitForSummon = true; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Items/BellOfTheDead.cs b/Scripts/Engines/Quests/The Summoning/Items/BellOfTheDead.cs new file mode 100644 index 000000000..d51f5efed --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Items/BellOfTheDead.cs @@ -0,0 +1,135 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Engines.Quests.Doom +{ + public class BellOfTheDead : Item + { + public override int LabelNumber{ get{ return 1050018; } } // bell of the dead + + [Constructable] + public BellOfTheDead() : base( 0x91A ) + { + Hue = 0x835; + Movable = false; + } + + private Chyloth m_Chyloth; + private SkeletalDragon m_Dragon; + private bool m_Summoning; + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public Chyloth Chyloth + { + get{ return m_Chyloth; } + set{ m_Chyloth = value; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public SkeletalDragon Dragon + { + get{ return m_Dragon; } + set{ m_Dragon = value; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public bool Summoning + { + get{ return m_Summoning; } + set{ m_Summoning = value; } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 2 ) ) + BeginSummon( from ); + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + + public virtual void BeginSummon( Mobile from ) + { + if ( m_Chyloth != null && !m_Chyloth.Deleted ) + { + from.SendLocalizedMessage( 1050010 ); // The ferry man has already been summoned. There is no need to ring for him again. + } + else if ( m_Dragon != null && !m_Dragon.Deleted ) + { + from.SendLocalizedMessage( 1050017 ); // The ferryman has recently been summoned already. You decide against ringing the bell again so soon. + } + else if ( !m_Summoning ) + { + m_Summoning = true; + + Effects.PlaySound( GetWorldLocation(), Map, 0x100 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 8.0 ), new TimerStateCallback( EndSummon ), from ); + } + } + + public virtual void EndSummon( object state ) + { + Mobile from = (Mobile)state; + + if ( m_Chyloth != null && !m_Chyloth.Deleted ) + { + from.SendLocalizedMessage( 1050010 ); // The ferry man has already been summoned. There is no need to ring for him again. + } + else if ( m_Dragon != null && !m_Dragon.Deleted ) + { + from.SendLocalizedMessage( 1050017 ); // The ferryman has recently been summoned already. You decide against ringing the bell again so soon. + } + else if ( m_Summoning ) + { + m_Summoning = false; + + Point3D loc = GetWorldLocation(); + + loc.Z -= 16; + + Effects.SendLocationParticles( EffectItem.Create( loc, Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 0, 0, 2023, 0 ); + Effects.PlaySound( loc, Map, 0x1FE ); + + m_Chyloth = new Chyloth(); + + m_Chyloth.Direction = (Direction)(7 & (4 + (int)from.GetDirectionTo( loc ))); + m_Chyloth.MoveToWorld( loc, Map ); + + m_Chyloth.Bell = this; + m_Chyloth.AngryAt = from; + m_Chyloth.BeginGiveWarning(); + m_Chyloth.BeginRemove( TimeSpan.FromSeconds( 40.0 ) ); + } + } + + public BellOfTheDead( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Chyloth ); + writer.Write( (Mobile) m_Dragon ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Chyloth = reader.ReadMobile() as Chyloth; + m_Dragon = reader.ReadMobile() as SkeletalDragon; + + if ( m_Chyloth != null ) + m_Chyloth.Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Items/ChylothShroud.cs b/Scripts/Engines/Quests/The Summoning/Items/ChylothShroud.cs new file mode 100644 index 000000000..5fc54c893 --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Items/ChylothShroud.cs @@ -0,0 +1,32 @@ +using System; + +namespace Server.Engines.Quests.Doom +{ + public class ChylothShroud : Item + { + [Constructable] + public ChylothShroud() : base( 0x204E ) + { + Hue = 0x846; + Layer = Layer.OuterTorso; + } + + public ChylothShroud( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Items/ChylothStaff.cs b/Scripts/Engines/Quests/The Summoning/Items/ChylothStaff.cs new file mode 100644 index 000000000..18b687018 --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Items/ChylothStaff.cs @@ -0,0 +1,35 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Engines.Quests.Doom +{ + public class ChylothStaff : BlackStaff + { + public override int LabelNumber{ get{ return 1041111; } } // a magic staff + + [Constructable] + public ChylothStaff() + { + Hue = 0x482; + } + + public ChylothStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Items/GoldenSkull.cs b/Scripts/Engines/Quests/The Summoning/Items/GoldenSkull.cs new file mode 100644 index 000000000..7ea5e3a6c --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Items/GoldenSkull.cs @@ -0,0 +1,35 @@ +using System; + +namespace Server.Engines.Quests.Doom +{ + public class GoldenSkull : Item + { + public override int LabelNumber{ get{ return 1061619; } } // a golden skull + + [Constructable] + public GoldenSkull() : base( Utility.Random( 0x1AE2, 3 ) ) + { + Weight = 1.0; + Hue = 0x8A5; + LootType = LootType.Blessed; + } + + public GoldenSkull( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Items/GrandGrimoire.cs b/Scripts/Engines/Quests/The Summoning/Items/GrandGrimoire.cs new file mode 100644 index 000000000..3d2b54b2c --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Items/GrandGrimoire.cs @@ -0,0 +1,36 @@ +using System; + +namespace Server.Engines.Quests.Doom +{ + public class GrandGrimoire : Item + { + public override int LabelNumber{ get{ return 1060801; } } // The Grand Grimoire + + [Constructable] + public GrandGrimoire() : base( 0xEFA ) + { + Weight = 1.0; + Hue = 0x835; + Layer = Layer.OneHanded; + LootType = LootType.Blessed; + } + + public GrandGrimoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Items/SummoningAltar.cs b/Scripts/Engines/Quests/The Summoning/Items/SummoningAltar.cs new file mode 100644 index 000000000..ddf51f493 --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Items/SummoningAltar.cs @@ -0,0 +1,64 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Doom +{ + public class SummoningAltar : AbbatoirAddon + { + private BoneDemon m_Daemon; + + public BoneDemon Daemon + { + get{ return m_Daemon; } + set + { + m_Daemon = value; + CheckDaemon(); + } + } + + public void CheckDaemon() + { + if ( m_Daemon == null || !m_Daemon.Alive ) + { + m_Daemon = null; + Hue = 0; + } + else + { + Hue = 0x66D; + } + } + + [Constructable] + public SummoningAltar() + { + } + + public SummoningAltar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Daemon ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Daemon = reader.ReadMobile() as BoneDemon; + + CheckDaemon(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Mobiles/Chyloth.cs b/Scripts/Engines/Quests/The Summoning/Mobiles/Chyloth.cs new file mode 100644 index 000000000..7edc0c3dc --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Mobiles/Chyloth.cs @@ -0,0 +1,323 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.ContextMenus; +using Server.Engines.PartySystem; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Doom +{ + public class Chyloth : BaseQuester + { + [Constructable] + public Chyloth() : base( "the Ferryman" ) + { + } + + public Chyloth( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x8455; + Body = 0x190; + + Name = "Chyloth"; + } + + public override void InitOutfit() + { + EquipItem( new ChylothShroud() ); + EquipItem( new ChylothStaff() ); + } + + private Mobile m_AngryAt; + private BellOfTheDead m_Bell; + + public BellOfTheDead Bell + { + get{ return m_Bell; } + set{ m_Bell = value; } + } + + public Mobile AngryAt + { + get{ return m_AngryAt; } + set{ m_AngryAt = value; } + } + + public virtual void BeginGiveWarning() + { + if ( Deleted || m_AngryAt == null ) + return; + + Timer.DelayCall( TimeSpan.FromSeconds( 4.0 ), new TimerCallback( EndGiveWarning ) ); + } + + public virtual void EndGiveWarning() + { + if ( Deleted || m_AngryAt == null ) + return; + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1050013, m_AngryAt.Name ); // You have summoned me in vain ~1_NAME~! Only the dead may cross! + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1050014 ); // Why have you disturbed me, mortal?!? + + BeginSummonDragon(); + } + + public virtual void BeginSummonDragon() + { + if ( Deleted || m_AngryAt == null ) + return; + + Timer.DelayCall( TimeSpan.FromSeconds( 30.0 ), new TimerCallback( EndSummonDragon ) ); + } + + public virtual void BeginRemove( TimeSpan delay ) + { + Timer.DelayCall( delay, new TimerCallback( EndRemove ) ); + } + + public virtual void EndRemove() + { + if ( Deleted ) + return; + + Point3D loc = this.Location; + Map map = this.Map; + + Effects.SendLocationParticles( EffectItem.Create( loc, map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 0, 0, 2023, 0 ); + Effects.PlaySound( loc, map, 0x1FE ); + + Delete(); + } + + private static int[] m_Offsets = new int[] + { + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1 + }; + + public virtual void EndSummonDragon() + { + if ( Deleted || m_AngryAt == null ) + return; + + Map map = m_AngryAt.Map; + + if ( map == null ) + return; + + if ( !m_AngryAt.Region.IsPartOf( "Doom" ) ) + return; + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1050015 ); // Feel the wrath of my legions!!! + PublicOverheadMessage( MessageType.Regular, 0x3B2, false, "MUHAHAHAHA HAHAH HAHA" ); // A wee bit crazy, aren't we? + + SkeletalDragon dragon = new SkeletalDragon(); + + int offset = Utility.Random( 8 ) * 2; + + bool foundLoc = false; + + for ( int i = 0; i < m_Offsets.Length; i += 2 ) + { + int x = m_AngryAt.X + m_Offsets[(offset + i) % m_Offsets.Length]; + int y = m_AngryAt.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length]; + + if ( map.CanSpawnMobile( x, y, m_AngryAt.Z ) ) + { + dragon.MoveToWorld( new Point3D( x, y, m_AngryAt.Z ), map ); + foundLoc = true; + break; + } + else + { + int z = map.GetAverageZ( x, y ); + + if ( map.CanSpawnMobile( x, y, z ) ) + { + dragon.MoveToWorld( new Point3D( x, y, z ), map ); + foundLoc = true; + break; + } + } + } + + if ( !foundLoc ) + dragon.MoveToWorld( m_AngryAt.Location, map ); + + dragon.Combatant = m_AngryAt; + + if ( m_Bell != null ) + m_Bell.Dragon = dragon; + } + + public static void TeleportToFerry( Mobile from ) + { + Point3D loc = new Point3D( 408, 251, 2 ); + Map map = Map.Malas; + + Effects.SendLocationParticles( EffectItem.Create( loc, map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 0, 0, 2023, 0 ); + Effects.PlaySound( loc, map, 0x1FE ); + + BaseCreature.TeleportPets( from, loc, map ); + + from.MoveToWorld( loc, map ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is GoldenSkull ) + { + dropped.Delete(); + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1050046, from.Name ); // Very well, ~1_NAME~, I accept your token. You may cross. + BeginRemove( TimeSpan.FromSeconds( 4.0 ) ); + + Party p = PartySystem.Party.Get( from ); + + if ( p != null ) + { + for ( int i = 0; i < p.Members.Count; ++i ) + { + PartyMemberInfo pmi = (PartyMemberInfo)p.Members[i]; + Mobile member = pmi.Mobile; + + if ( member != from && member.Map == Map.Malas && member.Region.IsPartOf( "Doom" ) ) + { + if ( m_AngryAt == member ) + m_AngryAt = null; + + member.CloseGump( typeof( ChylothPartyGump ) ); + member.SendGump( new ChylothPartyGump( from, member ) ); + } + } + } + + if ( m_AngryAt == from ) + m_AngryAt = null; + + TeleportToFerry( from ); + + return false; + } + + return base.OnDragDrop( from, dropped ); + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return false; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ChylothPartyGump : Gump + { + private Mobile m_Leader; + private Mobile m_Member; + + public ChylothPartyGump( Mobile leader, Mobile member ) : base( 150, 50 ) + { + m_Leader = leader; + m_Member = member; + + Closable = false; + + AddPage( 0 ); + + AddImage( 0, 0, 3600 ); + + AddImageTiled( 0, 14, 15, 200, 3603 ); + AddImageTiled( 380, 14, 14, 200, 3605 ); + AddImage( 0, 201, 3606 ); + AddImageTiled( 15, 201, 370, 16, 3607 ); + AddImageTiled( 15, 0, 370, 16, 3601 ); + AddImage( 380, 0, 3602 ); + AddImage( 380, 201, 3608 ); + AddImageTiled( 15, 15, 365, 190, 2624 ); + + AddRadio( 30, 140, 9727, 9730, true, 1 ); + AddHtmlLocalized( 65, 145, 300, 25, 1050050, 0x7FFF, false, false ); // Yes, let's go! + + AddRadio( 30, 175, 9727, 9730, false, 0 ); + AddHtmlLocalized( 65, 178, 300, 25, 1050049, 0x7FFF, false, false ); // No thanks, I'd rather stay here. + + AddHtmlLocalized( 30, 20, 360, 35, 1050047, 0x7FFF, false, false ); // Another player has paid Chyloth for your passage across lake Mortis: + + AddHtmlLocalized( 30, 105, 345, 40, 1050048, 0x5B2D, false, false ); // Do you wish to accept their invitation at this time? + + AddImage( 65, 72, 5605 ); + + AddImageTiled( 80, 90, 200, 1, 9107 ); + AddImageTiled( 95, 92, 200, 1, 9157 ); + + AddLabel( 90, 70, 1645, leader.Name ); + + AddButton( 290, 175, 247, 248, 2, GumpButtonType.Reply, 0 ); + + AddImageTiled( 15, 14, 365, 1, 9107 ); + AddImageTiled( 380, 14, 1, 190, 9105 ); + AddImageTiled( 15, 205, 365, 1, 9107 ); + AddImageTiled( 15, 14, 1, 190, 9105 ); + AddImageTiled( 0, 0, 395, 1, 9157 ); + AddImageTiled( 394, 0, 1, 217, 9155 ); + AddImageTiled( 0, 216, 395, 1, 9157 ); + AddImageTiled( 0, 0, 1, 217, 9155 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 2 && info.IsSwitched( 1 ) ) + { + if ( m_Member.Region.IsPartOf( "Doom" ) ) + { + m_Leader.SendLocalizedMessage( 1050054, m_Member.Name ); // ~1_NAME~ has accepted your invitation to cross lake Mortis. + + Chyloth.TeleportToFerry( m_Member ); + } + else + { + m_Member.SendLocalizedMessage( 1050051 ); // The invitation has been revoked. + } + } + else + { + m_Member.SendLocalizedMessage( 1050052 ); // You have declined their invitation. + m_Leader.SendLocalizedMessage( 1050053, m_Member.Name ); // ~1_NAME~ has declined your invitation to cross lake Mortis. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Mobiles/Victoria.cs b/Scripts/Engines/Quests/The Summoning/Mobiles/Victoria.cs new file mode 100644 index 000000000..a7b1046a3 --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Mobiles/Victoria.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.ContextMenus; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Engines.Quests.Doom +{ + public class Victoria : BaseQuester + { + public override int TalkNumber{ get{ return 6159; } } // Ask about Chyloth + public override bool ClickTitle{ get{ return true; } } + public override bool IsActiveVendor{ get{ return true; } } + public override bool DisallowAllMoves{ get{ return false; } } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBMage() ); + } + + [Constructable] + public Victoria() : base( "the Sorceress" ) + { + } + + public Victoria( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Female = true; + Hue = 0x8835; + Body = 0x191; + + Name = "Victoria"; + } + + private SummoningAltar m_Altar; + + private const int AltarRange = 24; + + public SummoningAltar Altar + { + get + { + if ( m_Altar == null || m_Altar.Deleted || m_Altar.Map != this.Map || !Utility.InRange( m_Altar.Location, this.Location, AltarRange ) ) + { + foreach ( Item item in GetItemsInRange( AltarRange ) ) + { + if ( item is SummoningAltar ) + { + m_Altar = (SummoningAltar)item; + break; + } + } + } + + return m_Altar; + } + } + + public override void InitOutfit() + { + EquipItem( new GrandGrimoire() ); + + EquipItem( SetHue( new Sandals(), 0x455 ) ); + EquipItem( SetHue( new SkullCap(), 0x455 ) ); + EquipItem( SetHue( new PlainDress(), 0x455 ) ); + + HairItemID = 0x203C; + HairHue = 0x482; + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is TheSummoningQuest ) + { + if ( dropped is DaemonBone ) + { + DaemonBone bones = (DaemonBone)dropped; + + QuestObjective obj = qs.FindObjective( typeof( CollectBonesObjective ) ); + + if ( obj != null && !obj.Completed ) + { + int need = obj.MaxProgress - obj.CurProgress; + + if ( bones.Amount < need ) + { + obj.CurProgress += bones.Amount; + bones.Delete(); + + qs.ShowQuestLogUpdated(); + } + else + { + obj.Complete(); + bones.Consume( need ); + + if ( !bones.Deleted ) + { + // TODO: Accurate? + SayTo( from, 1050038 ); // You have already given me all the Daemon bones necessary to weave the spell. Keep these for a later time. + } + } + } + else + { + // TODO: Accurate? + SayTo( from, 1050038 ); // You have already given me all the Daemon bones necessary to weave the spell. Keep these for a later time. + } + + return false; + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return ( to.Quest == null && QuestSystem.CanOfferQuest( to, typeof( TheSummoningQuest ) ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs == null && QuestSystem.CanOfferQuest( player, typeof( TheSummoningQuest ) ) ) + { + Direction = GetDirectionTo( player ); + new TheSummoningQuest( this, player ).SendOffer(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/Objectives.cs b/Scripts/Engines/Quests/The Summoning/Objectives.cs new file mode 100644 index 000000000..d131e4020 --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/Objectives.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using System.Collections.Generic; + +namespace Server.Engines.Quests.Doom +{ + public class CollectBonesObjective : QuestObjective + { + public override object Message + { + get + { + /* Find 1000 Daemon bones and hand them + * to Victoria as you find them. + */ + return 1050026; + } + } + + public override int MaxProgress{ get{ return 1000; } } + + public CollectBonesObjective() + { + } + + public override void OnComplete() + { + Victoria victoria = ((TheSummoningQuest)System).Victoria; + + if ( victoria == null ) + { + System.From.SendMessage( "Internal error: unable to find Victoria. Quest unable to continue." ); + System.Cancel(); + } + else + { + SummoningAltar altar = victoria.Altar; + + if ( altar == null ) + { + System.From.SendMessage( "Internal error: unable to find summoning altar. Quest unable to continue." ); + System.Cancel(); + } + else if ( altar.Daemon == null || !altar.Daemon.Alive ) + { + System.AddConversation( new VanquishDaemonConversation() ); + } + else + { + victoria.SayTo( System.From, "The devourer has already been summoned. Return when the devourer has been slain and I will summon it for you." ); + ((TheSummoningQuest)System).WaitForSummon = true; + } + } + } + + public override void RenderMessage( BaseQuestGump gump ) + { + if ( CurProgress > 0 && CurProgress < MaxProgress ) + gump.AddHtmlObject( 70, 130, 300, 100, 1050028, BaseQuestGump.Blue, false, false ); // Victoria has accepted the Daemon bones, but the requirement is not yet met. + else + base.RenderMessage( gump ); + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( CurProgress > 0 && CurProgress < MaxProgress ) + { + gump.AddHtmlObject( 70, 260, 270, 100, 1050019, BaseQuestGump.Blue, false, false ); // Number of bones collected: + + gump.AddLabel( 70, 280, 100, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 100, "/" ); + gump.AddLabel( 130, 280, 100, MaxProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + } + + public class VanquishDaemonObjective : QuestObjective + { + private BoneDemon m_Daemon; + private Corpse m_CorpseWithSkull; + + public Corpse CorpseWithSkull + { + get{ return m_CorpseWithSkull; } + set{ m_CorpseWithSkull = value; } + } + + public override object Message + { + get + { + /* Go forth and vanquish the devourer that has been summoned! + */ + return 1050037; + } + } + + public VanquishDaemonObjective( BoneDemon daemon ) + { + m_Daemon = daemon; + } + + // Serialization + public VanquishDaemonObjective() + { + } + + public override void CheckProgress() + { + if ( m_Daemon == null || !m_Daemon.Alive ) + Complete(); + } + + public override void OnComplete() + { + Victoria victoria = ((TheSummoningQuest)System).Victoria; + + if ( victoria != null ) + { + SummoningAltar altar = victoria.Altar; + + if ( altar != null ) + altar.CheckDaemon(); + } + + PlayerMobile from = System.From; + + if ( !from.Alive ) + { + from.SendLocalizedMessage( 1050033 ); // The devourer lies dead, unfortunately so do you. You cannot claim your reward while dead. You will need to face him again. + ((TheSummoningQuest)System).WaitForSummon = true; + } + else + { + bool hasRights = true; + + if ( m_Daemon != null ) + { + List lootingRights = BaseCreature.GetLootingRights( m_Daemon.DamageEntries, m_Daemon.HitsMax ); + + for ( int i = 0; i < lootingRights.Count; ++i ) + { + DamageStore ds = lootingRights[i]; + + if ( ds.m_HasRight && ds.m_Mobile == from ) + { + hasRights = true; + break; + } + } + } + + if ( !hasRights ) + { + from.SendLocalizedMessage( 1050034 ); // The devourer lies dead. Unfortunately you did not sufficiently prove your worth in combating the devourer. Victoria shall summon another incarnation of the devourer to the circle of stones. Try again noble adventurer. + ((TheSummoningQuest)System).WaitForSummon = true; + } + else + { + from.SendLocalizedMessage( 1050035 ); // The devourer lies dead. Search his corpse to claim your prize! + + if ( m_Daemon != null ) + m_CorpseWithSkull = m_Daemon.Corpse as Corpse; + } + } + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Daemon = reader.ReadMobile() as BoneDemon; + m_CorpseWithSkull = reader.ReadItem() as Corpse; + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Mobile) m_Daemon ); + writer.Write( (Item) m_CorpseWithSkull ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/The Summoning/TheSummoningQuest.cs b/Scripts/Engines/Quests/The Summoning/TheSummoningQuest.cs new file mode 100644 index 000000000..d29f610ef --- /dev/null +++ b/Scripts/Engines/Quests/The Summoning/TheSummoningQuest.cs @@ -0,0 +1,166 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Doom +{ + public class TheSummoningQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Doom.AcceptConversation ), + typeof( Doom.CollectBonesObjective ), + typeof( Doom.VanquishDaemonConversation ), + typeof( Doom.VanquishDaemonObjective ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + private Victoria m_Victoria; + private bool m_WaitForSummon; + + public Victoria Victoria + { + get{ return m_Victoria; } + } + + public bool WaitForSummon + { + get{ return m_WaitForSummon; } + set{ m_WaitForSummon = value; } + } + + public override object Name + { + get + { + // The Summoning + return 1050025; + } + } + + public override object OfferMessage + { + get + { + /* Victoria turns to you and smiles...

+ * + * Chyloth, eh? He is the ferry man of lake Mortis, beyond which lies + * the nest of the The Dark Father - the fountainhead of all the evil + * that you see around you here.

+ * + * 800 and some years ago, my sisters and I persuaded the ferry man Chyloth + * to take us across the lake to battle the The Dark Father. + * My party was utterly destroyed, except for me. For my insolence, I was + * cursed by the The Dark Father to wander these halls for eternity, + * unable to die - unable to leave.

+ * + * Chyloth usually only crosses over the souls of the undead, but he can be + * persuaded otherwise...with a token of gold, in the form of a human skull. + * Such a gem can be found only in the belly of the hellspawn known as + * the devourer.

+ * + * I can help you summon the beast from the depths of the abyss, but I require + * 1000 Daemon bones to do so. If you accept my help, I will store the Daemon + * bones for you until you have collected all 1000 of them. Once the bones + * are collected in full, I will summon the beast for you, which you must + * slay to claim your prize.

+ * + * Do you accept? + */ + return 1050020; + } + } + + public override bool IsTutorial{ get{ return false; } } + public override TimeSpan RestartDelay{ get{ return TimeSpan.Zero; } } + public override int Picture{ get{ return 0x15B5; } } + + // NOTE: Quest not entirely OSI-accurate: some changes made to prevent numerous OSI bugs + + public override void Slice() + { + if ( m_WaitForSummon && m_Victoria != null ) + { + SummoningAltar altar = m_Victoria.Altar; + + if ( altar != null && (altar.Daemon == null || !altar.Daemon.Alive) ) + { + if ( From.Map == m_Victoria.Map && From.InRange( m_Victoria, 8 ) ) + { + m_WaitForSummon = false; + + AddConversation( new VanquishDaemonConversation() ); + } + } + } + + base.Slice(); + } + + public static int GetDaemonBonesFor( BaseCreature creature ) + { + if ( creature == null || creature.Controlled || creature.Summoned ) + return 0; + + int fame = creature.Fame; + + if ( fame < 1500 ) + return Utility.Dice( 2, 5, -1 ); + else if ( fame < 20000 ) + return Utility.Dice( 2, 4, 8 ); + else + return 50; + } + + public TheSummoningQuest( Victoria victoria, PlayerMobile from ) : base( from ) + { + m_Victoria = victoria; + } + + public TheSummoningQuest() + { + } + + public override void Cancel() + { + base.Cancel(); + + QuestObjective obj = FindObjective( typeof( CollectBonesObjective ) ); + + if ( obj != null && obj.CurProgress > 0 ) + { + BankBox box = From.BankBox; + + if ( box != null ) + box.DropItem( new DaemonBone( obj.CurProgress ) ); + + From.SendLocalizedMessage( 1050030 ); // The Daemon bones that you have thus far given to Victoria have been returned to you. + } + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Victoria = reader.ReadMobile() as Victoria; + m_WaitForSummon = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Mobile) m_Victoria ); + writer.Write( (bool) m_WaitForSummon ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Conversations.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Conversations.cs new file mode 100644 index 000000000..a62e8cabd --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Conversations.cs @@ -0,0 +1,970 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Haven +{ + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Important Quest Information

+ * + * During your quest, any important information that a + * NPC gives you, will appear + * in a window such as this one. You can review the information + * at any time during your quest.

+ * + * Getting Help

+ * + * Some of the text you will come across during your quest, + * will be underlined links to the codex of wisdom, + * or online help system. You can click on the text to get detailed + * information on the underlined subject. You may also access the + * Codex Of Wisdom by pressing "F1" or by clicking on the "?" on the toolbar + * at the top of your screen.

+ * + * Context Menus

+ * + * Context menus can be called up by single left-clicking + * (or Shift + single left-click, if you changed it) on most objects + * or NPCs in the world. Nearly everything, including your own avatar + * will have context menus available. Bringing up your avatar's + * context menu will give you options to cancel your quest and review + * various quest information.

+ */ + return 1049092; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindUzeraanBeginObjective() ); + } + } + + public class UzeraanTitheConversation : QuestConversation + { + public override object Message + { + get + { + /* Uzeraan greets you as you approach...

+ * + * Greetings traveler!

+ * + * I am Uzeraan, the lord of this house and overseer of this fine city, Haven. + * I know we have just met, but time is short and we need to reinforce + * the troops in the mountain pass, so I will not waste your time + * with pleasantries.

+ * + * We have been trying to fight back the wicked Horde Minions which + * have recently begun attacking our cities - but to no avail. + * We desperately need help!

+ * + * Your first task will be to assess the situation in the mountain pass, + * and help our troops defeat the Horde Minions there.

+ * + * Before I send you into battle however, it is time that I teach you a thing + * or two about the way of the Paladin.

+ * + * The Paladin

+ * + * Paladins are the holy warriors of the realm who have dedicated themselves + * as protectors of the virtues and vanquishers of all that is evil.

+ * + * Paladins have several special abilities that + * are not available to the ordinary warrior. Due to the spiritual nature of + * these abilities, the Paladin requires some amount of mana to activate them. + * In addition to mana, the Paladin is also required to spend a certain amount + * of tithing points each time a special ability + * is used.

+ * + * Tithing points and mana are automatically consumed when a special Paladin + * ability is activated. All Paladin abilities are activated through the + * Book of Chivalry

+ * + * Go now, to the shrine just East of here, just before the doors and + * tithe at least 500 gold.

+ * + * Return here when you are done. + */ + return 1060209; + } + } + + public UzeraanTitheConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new TitheGoldObjective() ); + } + } + + public class UzeraanFirstTaskConversation : QuestConversation + { + public override object Message + { + get + { + if ( System.From.Profession == 1 ) // warrior + { + /* Uzeraan greets you as you approach...

+ * + * Greetings traveler!

+ * + * I am Uzeraan, the lord of this house and overseer of this fine city, Haven. + * I know we have just met, but time is short and we need to reinforce the troops + * in the mountain pass, so I will not waste your time with pleasantries.

+ * + * We have been trying to fight back the wicked Horde Minions which have + * recently begun attacking our cities - but to no avail. We desperately need + * help!

+ * + * Your first task will be to assess the situation in the mountain pass, and + * help our troops defeat the Horde Minions there.

+ * + * Take the road marked with glowing runes, that starts just outside of this + * mansion. Before you go into battle, it would be prudent to + * review combat techniques as well as + * information on healing yourself.

+ * + * To aid you in your fight, you may also wish to + * purchase equipment from Frank the Blacksmith, + * who is standing just South of here.

+ * + * Good luck young warrior. + */ + return 1049088; + } + else if ( System.From.Profession == 2 ) // magician + { + /* Uzeraan greets you as you approach...

+ * + * Greetings traveler!

+ * + * I am Uzeraan, the lord of this house and overseer of this fine city, Haven. + * I know we have just met, but time is short and we need to reinforce + * the troops in the mountain pass, so I will not waste your time with + * pleasantries.

+ * + * We have been trying to fight back the wicked Horde Minions which have + * recently begun attacking our cities - but to no avail. We desperately + * need help!

+ * + * Your first task will be to assess the situation in the mountain pass, + * and help our troops defeat the Horde Minions there.

+ * + * Take the road marked with glowing runes, that starts just outside of this + * mansion. Before you go into battle, it would be prudent to + * review your magic skills as well as + * information on healing yourself.

+ * + * To aid you in your fight, you may also wish to + * purchase equipment from Frank the Blacksmith, + * who is standing just South of here.

+ * + * Good luck young mage. + */ + return 1049386; + } + else + { + /* Uzeraan nods at you with approval and begins to speak...

+ * + * Now that you are ready, let me give you your first task.

+ * + * As I mentioned earlier, we have been trying to fight back the wicked + * Horde Minions which have recently begun attacking our cities + * - but to no avail. Our need is great!

+ * + * Your first task will be to assess the situation in the mountain pass, + * and help our troops defeat the Horde Minions there.

+ * + * Take the road marked with glowing runes, that starts just outside of this mansion. + * Before you go into battle, it would be prudent to + * review combat techniques as well as + * information on healing yourself, + * using your Paladin ability 'Close Wounds'.

+ * + * To aid you in your fight, you may also wish to + * purchase equipment from Frank the Blacksmith, + * who is standing just South of here.

+ * + * Good luck young Paladin! + */ + return 1060388; + } + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1023676, 0xE68 ) // glowing rune + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public UzeraanFirstTaskConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new KillHordeMinionsObjective() ); + } + } + + public class UzeraanReportConversation : QuestConversation + { + public override object Message + { + get + { + if ( System.From.Profession == 2 ) // magician + { + /* You give your report to Uzeraan and after a while, he begins to + * speak...

+ * + * Your report is grim, but all hope is not lost! It has become apparent + * that our swords and spells will not drive the evil from Haven.

+ * + * The head of my order, the High Mage Schmendrick, arrived here shortly after + * you went into battle with the Horde Minions. He has brought with him a + * scroll of great power, that should aid us greatly in our battle.

+ * + * Unfortunately, the entrance to one of our mining caves collapsed recently, + * trapping our miners inside.

+ * + * Schmendrick went to install magical teleporters inside the mines so that + * the miners would have a way out. The miners have since returned, but Schmendrick has not. + * Those who have returned, all seem to have lost their minds to madness; + * mumbling strange things of "the souls of the dead seeking revenge".

+ * + * No matter. We must find Schmendrick.

+ * + * Step onto the teleporter, located against the wall, and seek Schmendrick in the mines.

+ * + * I've given you a bag with some Travel Spells, + * in case you need to make a quick escape. In addition, you may wish to cast + * the Night Sight spell on yourself before going + * into the cave, as it it's pretty dark in there.

+ * + * Now please go. Good luck, friend. + */ + return 1049387; + } + else + { + /* You give your report to Uzeraan and after a while, + * he begins to speak...

+ * + * Your report is grim, but all hope is not lost! It has become apparent + * that our swords and spells will not drive the evil from Haven.

+ * + * The head of my order, the High Mage Schmendrick, arrived here shortly after + * you went into battle with the Horde Minions. He has brought with him a + * scroll of great power, that should aid us greatly in our battle.

+ * + * Unfortunately, the entrance to one of our mining caves collapsed recently, + * trapping our miners inside.

+ * + * Schmendrick went to install magical teleporters inside the mines so that + * the miners would have a way out. The miners have since returned, but Schmendrick has not. + * Those who have returned, all seem to have lost their minds to madness; + * mumbling strange things of "the souls of the dead seeking revenge".

+ * + * No matter. We must find Schmendrick.

+ * + * Step onto the teleporter, located against the wall, and seek Schmendrick in the mines.

+ * + * I've given you a bag with some Night Sight + * and Healing potions + * to help you out along the way. Good luck. + */ + return 1049119; + } + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1026153, 0x1822 ), // teleporter + new QuestItemInfo( 1048032, 0xE76 ) // a bag + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public UzeraanReportConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindSchmendrickObjective() ); + } + } + + public class SchmendrickConversation : QuestConversation + { + public override object Message + { + get + { + if ( System.From.Profession == 5 ) // paladin + { + /* Schmendrick barely pays you any attention as you approach him. + * His mind seems to be occupied with something else. You explain to him that + * you came for the scroll of power and after a long while he begins to speak, + * but apparently still not giving you his full attention...

+ * + * Hmmm.. peculiar indeed. Very strange activity here indeed... I wonder...

+ * + * Hmmm. Oh yes! Scroll, you say? I don't have it, sorry. My apprentice + * was carrying it, and he ran off to somewhere in this cave. Find him and + * you will find the scroll.

+ * + * Be sure to bring the scroll to Uzeraan once you have it. He's the only person + * aside from myself who can read the ancient markings on the scroll. + * I need to figure out what's going on down here before I can leave. + * Strange activity indeed...

+ * + * One more thing...

+ * + * Be careful of the restless souls wandering about. They seem to be in the habit + * of spontaneously attacking people. Perhaps using your paladin ability + * Enemy of One might help you overcome the perils + * of these halls.

+ * + * Schmendrick goes back to his work and you seem to completely fade from his awareness... + */ + return 1060749; + } + else + { + /* Schmendrick barely pays you any attention as you approach him. His + * mind seems to be occupied with something else. You explain to him that + * you came for the scroll of power and after a long while he begins to speak, + * but apparently still not giving you his full attention...

+ * + * Hmmm.. peculiar indeed. Very strange activity here indeed... I wonder...

+ * + * Hmmm. Oh yes! Scroll, you say? I don't have it, sorry. My apprentice was + * carrying it, and he ran off to somewhere in this cave. Find him and you will + * find the scroll.

Be sure to bring the scroll to Uzeraan once you + * have it. He's the only person aside from myself who can read the ancient + * markings on the scroll. I need to figure out what's going on down here before + * I can leave. Strange activity indeed...

+ * + * Schmendrick goes back to his work and you seem to completely fade from his + * awareness... + */ + return 1049322; + } + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1023637, 0xE34 ) // scroll + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public SchmendrickConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindApprenticeObjective() ); + } + } + + public class UzeraanScrollOfPowerConversation : QuestConversation + { + public override object Message + { + get + { + /* Uzeraan carefully unravels the scroll and begins to read... + * after a short while his face lights up with a smile and he speaks to you...

+ * + * This is wonderful, friend! For your troubles I've given you a treasure map + * I had laying about, along with a shovel to dig up the treasure. + * Feel free to find the treasure at your leisure.

+ * + * Now let us get back to the business of this scroll. The only trouble, + * is that this scroll calls for some special ingredients that I do + * not have on hand.

+ * + * Though it may involve some danger, I will ask of you to find + * these reagents for me.

+ * + * There are three reagents I need to complete the spell.

+ * + * The first thing I need is some Fertile Dirt.

+ * + * There lives a Dryad on this island who I know would have such a thing on hand. + * I have recalibrated the teleporter to transport you to the Dryad's grove, + * which lies South-West of this mansion.

+ * + * Tell her Uzeraan sent you, and she should cooperate.

+ * + * Should you get into trouble out there or should you lose your way, do not worry. + * I have also given you a magical horn - a Horn of Retreat. + * Play the horn at any time to open a magical gateway that leads back to this mansion.

+ * + * Should your horn run out of charges, + * simply hand me or any of my mansion guards the horn to have it recharged.

+ * + * Good luck friend. + */ + return 1049325; + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1048030, 0x14EB ), // a Treasure Map + new QuestItemInfo( 1023969, 0xF81 ), // Fertile Dirt + new QuestItemInfo( 1049117, 0xFC4 ) // Horn of Retreat + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public UzeraanScrollOfPowerConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindDryadObjective() ); + } + } + + public class DryadConversation : QuestConversation + { + public override object Message + { + get + { + /* The Dryad watches hungrily as you approach, giving you an + * uneasy feeling in the pit of your stomach. You explain that + * Uzeraan has sent you for a quantity of fertile dirt. With a wide grin + * and in a slightly hoarse voice she replies...

+ * + * Fertile Dirt, eh? Well, I have a few patches here...but what have + * you brought me in return? Came empty-handed did you? That's unfortunate + * indeed... but since you were sent by my dear friend Uzeraan, I supposed + * I could oblige you.

+ * + * The Dryad digs around in the ground and hands you a patch of Fertile Dirt.

+ * + * With a smile she goes back to her work...
+ */ + return 1049326; + } + } + + public DryadConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnFertileDirtObjective() ); + } + } + + public class UzeraanFertileDirtConversation : QuestConversation + { + public override object Message + { + get + { + if ( System.From.Profession == 2 ) // magician + { + /* Uzeraan takes the dirt from you and smiles...

+ * + * Wonderful! I knew I could count on you. As a token of my appreciation + * I've given you a bag with some reagents + * as well as some spell scrolls. They should + * help out a bit.

+ * + * The next item I need is a Vial of Blood. I know it seems strange, + * but that's what the formula asks for. I have some locked away in a chest + * not far from here. It's only a short distance from the mansion. Let me + * give you directions...

Exit the front door to the East. Then follow + * the path to the North. You will pass by several pedestals with lanterns on + * them. Continue on this path until you run into a small hut. Walk up the + * stairs and through the door. Inside you will find a chest. Open it and + * bring me a Vial of Blood from inside the chest. It's very easy to find. + * Just follow the road and you can't miss it.

+ * + * Good luck! + */ + return 1049388; + } + else + { + /* Uzeraan takes the dirt from you and smiles...

+ * + * Wonderful! I knew I could count on you. As a token of my appreciation + * I've given you a bag with some bandages as well as some healing potions. + * They should help out a bit.

+ * + * The next item I need is a Vial of Blood. I know it seems strange, + * but that's what the formula asks for. I have some locked away in a chest + * not far from here. It's only a short distance from the mansion. Let me give + * you directions...

+ * + * Exit the front door to the East. Then follow the path to the North. + * You will pass by several pedestals with lanterns on them. Continue on this + * path until you run into a small hut. Walk up the stairs and through the door. + * Inside you will find a chest. Open it and bring me a Vial of Blood + * from inside the chest. It's very easy to find. Just follow the road and you + * can't miss it.

+ * + * Good luck! + */ + return 1049329; + } + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1023965, 0xF7D ), // Daemon Blood + new QuestItemInfo( 1022581, 0xA22 ), // lantern + }; + + public override QuestItemInfo[] Info{ get{ return m_Info; } } + + public UzeraanFertileDirtConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GetDaemonBloodObjective() ); + } + } + + public class UzeraanDaemonBloodConversation : QuestConversation + { + public override object Message + { + get + { + if ( System.From.Profession == 2 ) // magician + { + //return 1049389; // localized message is bugged + return "You hand Uzeraan the Vial of Blood, which he hastily accepts...
" + + "
" + + "Excellent work! Only one reagent remains and the spell is complete! The final " + + "requirement is a Daemon Bone, which will not be as easily acquired as the " + + "previous two components.
" + + "
" + + "There is a haunted graveyard on this island, which is the home to many undead " + + "creatures. Dispose of the undead as you see fit. Be sure to search their remains " + + "after you have smitten them, to check for a Daemon Bone. I'm quite sure " + + "that you will find what we seek, if you are thorough enough with your " + + "extermination.
" + + "
" + + "Take these explosion spell scrolls and magical wizard's hat to aid you in your " + + "battle. The scrolls should help you make short work of the undead.
" + + "
" + + "Return here when you have found a Daemon Bone."; + } + else + { + /* You hand Uzeraan the Vial of Blood, which he hastily accepts...

+ * + * Excellent work! Only one reagent remains and the spell is complete! + * The final requirement is a Daemon Bone, which will not be as easily + * acquired as the previous two components.

+ * + * There is a haunted graveyard on this island, which is the home to many + * undead creatures. Dispose of the undead as you see fit. Be sure to search + * their remains after you have smitten them, to check for a Daemon Bone. + * I'm quite sure that you will find what we seek, if you are thorough enough + * with your extermination.

+ * + * Take this magical silver sword to aid you in your battle. Silver weapons + * will damage the undead twice as much as your regular weapon.

+ * + * Return here when you have found a Daemon Bone. + */ + return 1049333; + } + } + } + + private static QuestItemInfo[] m_Info = new QuestItemInfo[] + { + new QuestItemInfo( 1017412, 0xF80 ), // Daemon Bone + }; + + private static QuestItemInfo[] m_InfoPaladin = new QuestItemInfo[] + { + new QuestItemInfo( 1017412, 0xF80 ), // Daemon Bone + new QuestItemInfo( 1060577, 0x1F14 ), // Recall Rune + }; + + public override QuestItemInfo[] Info + { + get + { + if ( System.From.Profession == 5 ) // paladin + return m_InfoPaladin; + else + return m_Info; + } + } + + public UzeraanDaemonBloodConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new GetDaemonBoneObjective() ); + } + } + + public class UzeraanDaemonBoneConversation : QuestConversation + { + public override object Message + { + get + { + /* As you hand Uzeraan the final reagent, he nods at you with approval + * and starts searching through the pockets of his robe...

+ * + * After a short while he hands you a small pouch...


+ * + * There you are. Your contract of employment with me has expired and so here + * is your pay. 2000 gold in the form of a check and a magical sextant that + * will help you find Moongates and Banks.

+ * + * Before you can actually spend the money I have given you, however, you must + * cash the check.

+ * + * I have recalibrated the teleporter to take you to the Haven + * Bank. Step onto the teleporter to be taken + * to the bank, which lies South-East of here.

+ * + * Thank you for all your help friend. I hope we shall meet + * each other again in the future.

+ * + * Farewell. + */ + return 1049335; + } + } + + public UzeraanDaemonBoneConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new CashBankCheckObjective() ); + } + } + + public class BankerConversation : QuestConversation + { + public override object Message + { + get + { + /* The banker smiles at you and greets you in a loud and robust voice...

+ * + * Well hello there adventurer! I see you've learned how to cash checks. Wonderful! + * Let me tell you a bit about the banks in this world...

+ * + * Anything that you place into any bank box, can be retrieved from any other + * bank box in the land. For instance, if you place an item into a bank box in + * Britain, it can be retrieved from your bank box in Moonglow or any other city.

+ * + * Bank boxes are very secure. So secure, in fact, that no one can ever get into + * your bank box except for yourself. Security is hard to come by these days, + * but you can trust in the banking system of Britannia! We shall not let you down!

+ * + * I hope to be seeing much more of you as your riches grow! May your bank box overflow + * with the spoils of your adventures.

Farewell adventurer, you are now free to + * explore the world on your own. + */ + return 1060137; + } + } + + public BankerConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class RadarConversation : QuestConversation + { + public override object Message + { + get + { + /* If you are leaving the mansion, you should learn about the Radar Map.

+ * + * The Radar Map (or Overhead View) can be opened by pressing 'ALT-R' on your + * keyboard. It shows your immediate surroundings from a bird's eye view.

+ * + * Pressing ALT-R twice, will enlarge the Radar Map a little. Use the Radar Map + * often as you travel throughout the world to familiarize yourself with your surroundings. + */ + return 1049660; + } + } + + public override bool Logged{ get{ return false; } } + + public RadarConversation() + { + } + } + + public class LostScrollOfPowerConversation : QuestConversation + { + private bool m_FromUzeraan; + + public override object Message + { + get + { + if ( m_FromUzeraan ) + { + /* You return without the scroll???

+ * + * All hope is lost without it, friend. Return to the mines and talk to + * Schmendrick to see if he can help us out of this predicament. + */ + return 1049377; + } + else + { + /* You've lost the scroll? Argh! I will have to try and re-construct + * the scroll from memory. Bring me a blank scroll, which you can + * purchase from the mage shop just + * East of Uzeraan's mansion in Haven.

+ * + * Return the scroll to me and I will try to make another scroll for you.

+ * + * When you return, be sure to hand me the scroll (drag and drop). + */ + return 1049345; + } + } + } + + public override bool Logged{ get{ return false; } } + + public LostScrollOfPowerConversation( bool fromUzeraan ) + { + m_FromUzeraan = fromUzeraan; + } + + public LostScrollOfPowerConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_FromUzeraan = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_FromUzeraan ); + } + } + + public class LostFertileDirtConversation : QuestConversation + { + private bool m_FromUzeraan; + + public override object Message + { + get + { + if ( m_FromUzeraan ) + { + /* You return without Fertile Dirt? It is imperative that we + * get all of the ingredients friend.

+ * + * Seek out the Dryad and ask her to help you again. + */ + return 1049374; + } + else + { + /* You've lost the dirt I gave you?

+ * + * My, my, my... What ever shall we do now?

+ * + * I can try to make you some more, but I will need something + * that I can transform. Bring me an apple, and I shall + * see what I can do.

+ * + * You can buy apples from the + * Provisioner's Shop, which is located a ways East + * of Uzeraan's mansion.

+ * + * Hand me the apple when you have it, and I shall see about transforming + * it for you.

+ * + * Good luck.

+ */ + return 1049359; + } + } + } + + public override bool Logged{ get{ return false; } } + + public LostFertileDirtConversation( bool fromUzeraan ) + { + m_FromUzeraan = fromUzeraan; + } + + public LostFertileDirtConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_FromUzeraan = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_FromUzeraan ); + } + } + + public class DryadAppleConversation : QuestConversation + { + public override object Message + { + get + { + /* The Dryad sticks the apple into the ground and you watch it + * rot before your eyes.

+ * + * She pulls the now fertile dirt out of the ground and hands + * it to you.


+ * + * There you go friend. Try not to lose it again this time, eh? + */ + return 1049360; + } + } + + public override bool Logged{ get{ return false; } } + + public DryadAppleConversation() + { + } + } + + public class LostDaemonBloodConversation : QuestConversation + { + public override object Message + { + get + { + /* You return without a Vial of Blood? It is imperative that we + * get all of the ingredients friend.

+ * + * Go back to the chest and fetch another vial. Please hurry. + */ + return 1049375; + } + } + + public override bool Logged{ get{ return false; } } + + public LostDaemonBloodConversation() + { + } + } + + public class LostDaemonBoneConversation : QuestConversation + { + public override object Message + { + get + { + /* You return without a Daemon Bone? It is imperative that we + * get all of the ingredients friend.

+ * + * Go back to the graveyard and continue hunting the undead until you + * find another one. Please hurry. + */ + return 1049376; + } + } + + public override bool Logged{ get{ return false; } } + + public LostDaemonBoneConversation() + { + } + } + + public class FewReagentsConversation : QuestConversation + { + public override object Message + { + get + { + /* I don't feel comfortable sending you into a potentially dangerous situation + * with as few reagents as you have in your pack.

+ * + * Before going on, please acquire at least 30 of each reagent. You can + * purchase reagents from the Mage shop, which is + * located just East this mansion.

+ * + * Remember that there are eight (8) different reagents: Black Pearl, Mandrake Root, + * Sulfurous Ash, Garlic, Ginseng, Blood Moss, Nightshade and Spider's Silk.

+ * + * Come back here when you are ready to go. + */ + return 1049390; + } + } + + public override bool Logged{ get{ return false; } } + + public FewReagentsConversation() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/Cannon.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/Cannon.cs new file mode 100644 index 000000000..ab01ec876 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/Cannon.cs @@ -0,0 +1,181 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Haven +{ + public enum CannonDirection + { + North, + East, + South, + West + } + + public class Cannon : BaseAddon + { + private CannonDirection m_CannonDirection; + private MilitiaCanoneer m_Canoneer; + + [CommandProperty( AccessLevel.GameMaster )] + public CannonDirection CannonDirection { get { return m_CannonDirection; } } + + [CommandProperty( AccessLevel.GameMaster )] + public MilitiaCanoneer Canoneer { get { return m_Canoneer; } set { m_Canoneer = value; } } + + [Constructable] + public Cannon( CannonDirection direction ) + { + m_CannonDirection = direction; + + switch ( direction ) + { + case CannonDirection.North: + { + AddComponent( new CannonComponent( 0xE8D ), 0, 0, 0 ); + AddComponent( new CannonComponent( 0xE8C ), 0, 1, 0 ); + AddComponent( new CannonComponent( 0xE8B ), 0, 2, 0 ); + + break; + } + case CannonDirection.East: + { + AddComponent( new CannonComponent( 0xE96 ), 0, 0, 0 ); + AddComponent( new CannonComponent( 0xE95 ), -1, 0, 0 ); + AddComponent( new CannonComponent( 0xE94 ), -2, 0, 0 ); + + break; + } + case CannonDirection.South: + { + AddComponent( new CannonComponent( 0xE91 ), 0, 0, 0 ); + AddComponent( new CannonComponent( 0xE92 ), 0, -1, 0 ); + AddComponent( new CannonComponent( 0xE93 ), 0, -2, 0 ); + + break; + } + default: + { + AddComponent( new CannonComponent( 0xE8E ), 0, 0, 0 ); + AddComponent( new CannonComponent( 0xE8F ), 1, 0, 0 ); + AddComponent( new CannonComponent( 0xE90 ), 2, 0, 0 ); + + break; + } + } + } + + public Cannon( Serial serial ) : base( serial ) + { + } + + public void DoFireEffect( IPoint3D target ) + { + Point3D from; + switch ( m_CannonDirection ) + { + case CannonDirection.North: from = new Point3D( X, Y - 1, Z ); break; + case CannonDirection.East: from = new Point3D( X + 1, Y, Z ); break; + case CannonDirection.South: from = new Point3D( X, Y + 1, Z ); break; + default: from = new Point3D( X - 1, Y, Z ); break; + } + + Effects.SendLocationEffect( from, Map, 0x36B0, 16, 1 ); + Effects.PlaySound( from, Map, 0x11D ); + + Effects.SendLocationEffect( target, Map, 0x36B0, 16, 1 ); + Effects.PlaySound( target, Map, 0x11D ); + } + + public void Fire( Mobile from, Mobile target ) + { + DoFireEffect( target ); + + target.Damage( 9999, from ); + } + + public override bool HandlesOnMovement { get { return m_Canoneer != null && !m_Canoneer.Deleted && m_Canoneer.Active; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m_Canoneer == null || m_Canoneer.Deleted || !m_Canoneer.Active ) + return; + + bool canFire; + switch ( m_CannonDirection ) + { + case CannonDirection.North: + canFire = m.X >= X - 7 && m.X <= X + 7 && m.Y == Y - 7 && oldLocation.Y < Y - 7; + break; + case CannonDirection.East: + canFire = m.Y >= Y - 7 && m.Y <= Y + 7 && m.X == X + 7 && oldLocation.X > X + 7; + break; + case CannonDirection.South: + canFire = m.X >= X - 7 && m.X <= X + 7 && m.Y == Y + 7 && oldLocation.Y > Y + 7; + break; + default: + canFire = m.Y >= Y - 7 && m.Y <= Y + 7 && m.X == X - 7 && oldLocation.X < X - 7; + break; + } + + if ( canFire && m_Canoneer.WillFire( this, m ) ) + Fire( m_Canoneer, m ); + } + + public override void Serialize( GenericWriter writer ) + { + if ( m_Canoneer != null && m_Canoneer.Deleted ) + m_Canoneer = null; + + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_CannonDirection ); + writer.Write( (Mobile) m_Canoneer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_CannonDirection = (CannonDirection) reader.ReadEncodedInt(); + m_Canoneer = (MilitiaCanoneer) reader.ReadMobile(); + } + } + + public class CannonComponent : AddonComponent + { + [CommandProperty( AccessLevel.GameMaster )] + public MilitiaCanoneer Canoneer + { + get { return Addon is Cannon ? ((Cannon)Addon).Canoneer : null; } + set { if ( Addon is Cannon ) ((Cannon)Addon).Canoneer = value; } + } + + public CannonComponent( int itemID ) : base( itemID ) + { + } + + public CannonComponent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs new file mode 100644 index 000000000..b3d8fe7ad --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/DaemonBloodChest.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class DaemonBloodChest : MetalChest + { + [Constructable] + public DaemonBloodChest() + { + Movable = false; + } + + public DaemonBloodChest( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.InRange( GetWorldLocation(), 2 ) ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( GetDaemonBloodObjective ) ); + + if ( (obj != null && !obj.Completed) || UzeraanTurmoilQuest.HasLostDaemonBlood( player ) ) + { + Item vial = new QuestDaemonBlood(); + + if ( player.PlaceInBackpack( vial ) ) + { + player.SendLocalizedMessage( 1049331, "", 0x22 ); // You take a vial of blood from the chest and put it in your pack. + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + else + { + player.SendLocalizedMessage( 1049338, "", 0x22 ); // You find a vial of blood, but can't pick it up because your pack is too full. Come back when you have more room in your pack. + vial.Delete(); + } + + return; + } + } + } + + base.OnDoubleClick( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs new file mode 100644 index 000000000..98ea3e533 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class QuestDaemonBlood : QuestItem + { + [Constructable] + public QuestDaemonBlood() : base( 0xF7D ) + { + Weight = 1.0; + } + + public QuestDaemonBlood( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + UzeraanTurmoilQuest qs = player.Quest as UzeraanTurmoilQuest; + + if ( qs == null ) + return true; + + /*return !qs.IsObjectiveInProgress( typeof( ReturnDaemonBloodObjective ) );*/ + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs new file mode 100644 index 000000000..b60b29357 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class QuestDaemonBone : QuestItem + { + [Constructable] + public QuestDaemonBone() : base( 0xF80 ) + { + Weight = 1.0; + } + + public QuestDaemonBone( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + UzeraanTurmoilQuest qs = player.Quest as UzeraanTurmoilQuest; + + if ( qs == null ) + return true; + + //return !qs.IsObjectiveInProgress( typeof( ReturnDaemonBoneObjective ) ); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs new file mode 100644 index 000000000..6e038d82d --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class QuestFertileDirt : QuestItem + { + [Constructable] + public QuestFertileDirt() : base( 0xF81 ) + { + Weight = 1.0; + } + + public QuestFertileDirt( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + UzeraanTurmoilQuest qs = player.Quest as UzeraanTurmoilQuest; + + if ( qs == null ) + return true; + + //return !qs.IsObjectiveInProgress( typeof( ReturnFertileDirtObjective ) ); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs new file mode 100644 index 000000000..bd1a004f6 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickApprenticeCorpse.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class SchmendrickApprenticeCorpse : Corpse + { + private static Mobile GetOwner() + { + Mobile apprentice = new Mobile(); + + apprentice.Hue = Utility.RandomSkinHue(); + apprentice.Female = false; + apprentice.Body = 0x190; + apprentice.Name = NameList.RandomName( "male" ); + + apprentice.Delete(); + + return apprentice; + } + + private static List GetEquipment() + { + List list = new List(); + + list.Add( new Robe( QuestSystem.RandomBrightHue() ) ); + list.Add( new WizardsHat( Utility.RandomNeutralHue() ) ); + list.Add( new Shoes( Utility.RandomNeutralHue() ) ); + + /* + int hairHue = Utility.RandomHairHue(); + + switch ( Utility.Random( 8 ) ) + { + case 0: list.Add( new Afro( hairHue ) ); break; + case 1: list.Add( new KrisnaHair( hairHue ) ); break; + case 2: list.Add( new PageboyHair( hairHue ) ); break; + case 3: list.Add( new PonyTail( hairHue ) ); break; + case 4: list.Add( new ReceedingHair( hairHue ) ); break; + case 5: list.Add( new TwoPigTails( hairHue ) ); break; + case 6: list.Add( new ShortHair( hairHue ) ); break; + case 7: list.Add( new LongHair( hairHue ) ); break; + } + + switch ( Utility.Random( 5 ) ) + { + case 0: list.Add( new LongBeard( hairHue ) ); break; + case 1: list.Add( new MediumLongBeard( hairHue ) ); break; + case 2: list.Add( new Vandyke( hairHue ) ); break; + case 3: list.Add( new Mustache( hairHue ) ); break; + case 4: list.Add( new Goatee( hairHue ) ); break; + } + * */ + + list.Add( new Spellbook() ); + + return list; + } + + private static int m_HairHue; + private static HairInfo GetHair() + { + m_HairHue = Race.Human.RandomHairHue(); + + return new HairInfo( Race.Human.RandomHair( false ), m_HairHue ); + } + + private static FacialHairInfo GetFacialHair() + { + m_HairHue = Race.Human.RandomHairHue(); + + return new FacialHairInfo( Race.Human.RandomFacialHair( false ), m_HairHue ); + } + + private Lantern m_Lantern; + + [Constructable] + public SchmendrickApprenticeCorpse() : base( GetOwner(), GetHair(), GetFacialHair(), GetEquipment() ) + { + Direction = Direction.West; + + foreach ( Item item in EquipItems ) + { + DropItem( item ); + } + + m_Lantern = new Lantern(); + m_Lantern.Movable = false; + m_Lantern.Protected = true; + m_Lantern.Ignite(); + } + + public SchmendrickApprenticeCorpse( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( ItemID == 0x2006 ) // Corpse form + { + list.Add( "a human corpse" ); + list.Add( 1049144, this.Name ); // the remains of ~1_NAME~ the apprentice + } + else + { + list.Add( 1049145 ); // the remains of a wizard's apprentice + } + } + + public override void OnSingleClick( Mobile from ) + { + int hue = Notoriety.GetHue( Server.Misc.NotorietyHandlers.CorpseNotoriety( from, this ) ); + + if ( ItemID == 0x2006 ) // Corpse form + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, 1049144, "", Name ) ); // the remains of ~1_NAME~ the apprentice + else + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, 1049145, "", "" ) ); // the remains of a wizard's apprentice + } + + public override void Open( Mobile from, bool checkSelfLoot ) + { + if ( !from.InRange( this.GetWorldLocation(), 2 ) ) + return; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( FindApprenticeObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item scroll = new SchmendrickScrollOfPower(); + + if ( player.PlaceInBackpack( scroll ) ) + { + player.SendLocalizedMessage( 1049147, "", 0x22 ); // You find the scroll and put it in your pack. + obj.Complete(); + } + else + { + player.SendLocalizedMessage( 1049146, "", 0x22 ); // You find the scroll, but can't pick it up because your pack is too full. Come back when you have more room in your pack. + scroll.Delete(); + } + + return; + } + } + } + + from.SendLocalizedMessage( 1049143, "", 0x22 ); // This is the corpse of a wizard's apprentice. You can't bring yourself to search it without a good reason. + } + + public override void OnLocationChange( Point3D oldLoc ) + { + if ( m_Lantern != null && !m_Lantern.Deleted ) + m_Lantern.Location = new Point3D( this.X, this.Y + 1, this.Z ); + } + + public override void OnMapChange() + { + if ( m_Lantern != null && !m_Lantern.Deleted ) + m_Lantern.Map = this.Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Lantern != null && !m_Lantern.Deleted ) + m_Lantern.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + if ( m_Lantern != null && m_Lantern.Deleted ) + m_Lantern = null; + + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Lantern ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Lantern = (Lantern)reader.ReadItem(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs new file mode 100644 index 000000000..797af2a4c --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs @@ -0,0 +1,46 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class SchmendrickScrollOfPower : QuestItem + { + public override int LabelNumber { get { return 1049118; } } // a scroll with ancient markings + + public SchmendrickScrollOfPower() : base( 0xE34 ) + { + Weight = 1.0; + Hue = 0x34D; + } + + public SchmendrickScrollOfPower( Serial serial ) : base( serial ) + { + } + + public override bool CanDrop( PlayerMobile player ) + { + UzeraanTurmoilQuest qs = player.Quest as UzeraanTurmoilQuest; + + if ( qs == null ) + return true; + + return !qs.IsObjectiveInProgress( typeof( ReturnScrollOfPowerObjective ) ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilHorn.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilHorn.cs new file mode 100644 index 000000000..196c82198 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilHorn.cs @@ -0,0 +1,43 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class UzeraanTurmoilHorn : HornOfRetreat + { + public override bool ValidateUse( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + return ( pm != null && pm.Quest is UzeraanTurmoilQuest ); + } + + [Constructable] + public UzeraanTurmoilHorn() + { + DestLoc = new Point3D( 3597, 2582, 0 ); + DestMap = Map.Trammel; + } + + public UzeraanTurmoilHorn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilTeleporter.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilTeleporter.cs new file mode 100644 index 000000000..7def633e2 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Items/UzeraanTurmoilTeleporter.cs @@ -0,0 +1,74 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class UzeraanTurmoilTeleporter : DynamicTeleporter + { + [Constructable] + public UzeraanTurmoilTeleporter() + { + } + + public override bool GetDestination( PlayerMobile player, ref Point3D loc, ref Map map ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FindSchmendrickObjective ) ) + || qs.IsObjectiveInProgress( typeof( FindApprenticeObjective ) ) + || UzeraanTurmoilQuest.HasLostScrollOfPower( player ) ) + { + loc = new Point3D( 5222, 1858, 0 ); + map = Map.Trammel; + return true; + } + else if ( qs.IsObjectiveInProgress( typeof( FindDryadObjective ) ) + || UzeraanTurmoilQuest.HasLostFertileDirt( player ) ) + { + loc = new Point3D( 3557, 2690, 2 ); + map = Map.Trammel; + return true; + } + else if ( player.Profession != 5 // paladin + && ( qs.IsObjectiveInProgress( typeof( GetDaemonBoneObjective ) ) + || UzeraanTurmoilQuest.HasLostDaemonBone( player ) ) ) + { + loc = new Point3D( 3422, 2653, 48 ); + map = Map.Trammel; + return true; + } + else if ( qs.IsObjectiveInProgress( typeof( CashBankCheckObjective ) ) ) + { + loc = new Point3D( 3624, 2610, 0 ); + map = Map.Trammel; + return true; + } + } + + return false; + } + + public UzeraanTurmoilTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs new file mode 100644 index 000000000..15a5591bc --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Dryad.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.ContextMenus; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class Dryad : BaseQuester + { + public override bool IsActiveVendor{ get{ return true; } } + public override bool DisallowAllMoves{ get{ return false; } } + public override bool ClickTitle{ get { return true; } } + public override bool CanTeach{ get{ return true; } } + + [Constructable] + public Dryad() : base( "the Dryad" ) + { + SetSkill( SkillName.Peacemaking, 80.0, 100.0 ); + SetSkill( SkillName.Cooking, 80.0, 100.0 ); + SetSkill( SkillName.Provocation, 80.0, 100.0 ); + SetSkill( SkillName.Musicianship, 80.0, 100.0 ); + SetSkill( SkillName.Poisoning, 80.0, 100.0 ); + SetSkill( SkillName.Archery, 80.0, 100.0 ); + SetSkill( SkillName.Tailoring, 80.0, 100.0 ); + } + + public Dryad( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x85A7; + + Female = true; + Body = 0x191; + Name = "Anwin Brenna"; + } + + public override void InitOutfit() + { + AddItem( new Kilt( 0x301 ) ); + AddItem( new FancyShirt( 0x300 ) ); + + HairItemID = 0x203D; // Pony Tail + HairHue = 0x22; + + Bow bow = new Bow(); + bow.Movable = false; + AddItem( bow ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBDryad() ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 4; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + UzeraanTurmoilQuest qs = to.Quest as UzeraanTurmoilQuest; + + return ( qs != null && qs.FindObjective( typeof( FindDryadObjective ) ) != null ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( UzeraanTurmoilQuest.HasLostFertileDirt( player ) ) + { + FocusTo( player ); + qs.AddConversation( new LostFertileDirtConversation( false ) ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( FindDryadObjective ) ); + + if ( obj != null && !obj.Completed ) + { + FocusTo( player ); + + Item fertileDirt = new QuestFertileDirt(); + + if ( !player.PlaceInBackpack( fertileDirt ) ) + { + fertileDirt.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + else + { + obj.Complete(); + } + } + else if ( contextMenu ) + { + FocusTo( player ); + SayTo( player, 1049357 ); // I have nothing more for you at this time. + } + } + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + UzeraanTurmoilQuest qs = player.Quest as UzeraanTurmoilQuest; + + if ( qs != null && dropped is Apple && UzeraanTurmoilQuest.HasLostFertileDirt( from ) ) + { + FocusTo( from ); + + Item fertileDirt = new QuestFertileDirt(); + + if ( !player.PlaceInBackpack( fertileDirt ) ) + { + fertileDirt.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + return false; + } + else + { + dropped.Consume(); + qs.AddConversation( new DryadAppleConversation() ); + return dropped.Deleted; + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SBDryad : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBDryad() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bandage ), 5, 20, 0xE21, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 20, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bandage ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Bloodmoss ), 3 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( SpidersSilk ), 2 ); + Add( typeof( MandrakeRoot ), 2 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MansionGuard.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MansionGuard.cs new file mode 100644 index 000000000..3412038d8 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MansionGuard.cs @@ -0,0 +1,80 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class MansionGuard : BaseQuester + { + [Constructable] + public MansionGuard() : base( "the Mansion Guard" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = Utility.RandomSkinHue(); + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + public override void InitOutfit() + { + AddItem( new PlateChest() ); + AddItem( new PlateArms() ); + AddItem( new PlateGloves() ); + AddItem( new PlateLegs() ); + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, HairHue ); + + Bardiche weapon = new Bardiche(); + weapon.Movable = false; + AddItem( weapon ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 3; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return ( to.Quest == null && QuestSystem.CanOfferQuest( to, typeof( UzeraanTurmoilQuest ) ) ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + if ( player.Quest == null && QuestSystem.CanOfferQuest( player, typeof( UzeraanTurmoilQuest ) ) ) + { + Direction = GetDirectionTo( player ); + + new UzeraanTurmoilQuest( player ).SendOffer(); + } + } + + public MansionGuard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaCanoneer.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaCanoneer.cs new file mode 100644 index 000000000..4f647de58 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaCanoneer.cs @@ -0,0 +1,113 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class MilitiaCanoneer : BaseQuester + { + private bool m_Active; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get { return m_Active; } + set { m_Active = value; } + } + + [Constructable] + public MilitiaCanoneer() : base( "the Militia Canoneer" ) + { + m_Active = true; + } + + public override void InitBody() + { + InitStats( 100, 125, 25 ); + + Hue = Utility.RandomSkinHue(); + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + public override void InitOutfit() + { + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, HairHue ); + + AddItem( new PlateChest() ); + AddItem( new PlateArms() ); + AddItem( new PlateGloves() ); + AddItem( new PlateLegs() ); + + Torch torch = new Torch(); + torch.Movable = false; + AddItem( torch ); + torch.Ignite(); + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return false; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player || m is BaseVendor ) + return false; + + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + Mobile master = bc.GetMaster(); + if( master != null ) + return IsEnemy( master ); + } + + return m.Karma < 0; + } + + public bool WillFire( Cannon cannon, Mobile target ) + { + if ( m_Active && IsEnemy( target ) ) + { + Direction = GetDirectionTo( target ); + Say( Utility.RandomList( 500651, 1049098, 1049320, 1043149 ) ); + return true; + } + + return false; + } + + public MilitiaCanoneer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (bool) m_Active ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Active = reader.ReadBool(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs new file mode 100644 index 000000000..c7e0d7f33 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/MilitiaFighter.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Engines.Quests.Haven +{ + public class MilitiaFighter : BaseCreature + { + [Constructable] + public MilitiaFighter() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + InitStats( 40, 30, 5 ); + Title = "the Militia Fighter"; + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + Female = false; + Body = 0x190; + Name = NameList.RandomName( "male" ); + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, HairHue ); + + AddItem( new ThighBoots( 0x1BB ) ); + AddItem( new LeatherChest() ); + AddItem( new LeatherArms() ); + AddItem( new LeatherLegs() ); + AddItem( new LeatherCap() ); + AddItem( new LeatherGloves() ); + AddItem( new LeatherGorget() ); + + Item weapon; + switch ( Utility.Random( 6 ) ) + { + case 0: weapon = new Broadsword(); break; + case 1: weapon = new Cutlass(); break; + case 2: weapon = new Katana(); break; + case 3: weapon = new Longsword(); break; + case 4: weapon = new Scimitar(); break; + default: weapon = new VikingSword(); break; + } + weapon.Movable = false; + AddItem( weapon ); + + Item shield = new BronzeShield(); + shield.Movable = false; + AddItem( shield ); + + SetSkill( SkillName.Swords, 20.0 ); + } + + public override bool ClickTitle { get { return false; } } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player || m is BaseVendor ) + return false; + + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + Mobile master = bc.GetMaster(); + if( master != null ) + return IsEnemy( master ); + } + + return m.Karma < 0; + } + + public MilitiaFighter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MilitiaFighterCorpse : Corpse + { + public MilitiaFighterCorpse( Mobile owner, HairInfo hair, FacialHairInfo facialhair, List equipItems ) : base( owner, hair, facialhair, equipItems ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( ItemID == 0x2006 ) // Corpse form + { + list.Add( "a human corpse" ); + list.Add( 1049318, this.Name ); // the remains of ~1_NAME~ the militia fighter + } + else + { + list.Add( 1049319 ); // the remains of a militia fighter + } + } + + public override void OnSingleClick( Mobile from ) + { + int hue = Notoriety.GetHue( Server.Misc.NotorietyHandlers.CorpseNotoriety( from, this ) ); + + if ( ItemID == 0x2006 ) // Corpse form + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, 1049318, "", Name ) ); // the remains of ~1_NAME~ the militia fighter + else + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, 1049319, "", "" ) ); // the remains of a militia fighter + } + + public override void Open( Mobile from, bool checkSelfLoot ) + { + if ( from.InRange( this.GetWorldLocation(), 2 ) ) + { + from.SendLocalizedMessage( 1049661, "", 0x22 ); // Thinking about his sacrifice, you can't bring yourself to loot the body of this militia fighter. + } + } + + public MilitiaFighterCorpse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Schmendrick.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Schmendrick.cs new file mode 100644 index 000000000..ef2745c34 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Schmendrick.cs @@ -0,0 +1,156 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Gumps; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class Schmendrick : BaseQuester + { + [Constructable] + public Schmendrick() : base( "the High Mage" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83F3; + + Female = false; + Body = 0x190; + Name = "Schmendrick"; + } + + public override void InitOutfit() + { + AddItem( new Robe( 0x4DD ) ); + AddItem( new WizardsHat( 0x482 ) ); + AddItem( new Shoes( 0x482 ) ); + + HairItemID = 0x203C; + HairHue = 0x455; + + FacialHairItemID = 0x203E; + FacialHairHue = 0x455; + + GlacialStaff staff = new GlacialStaff(); + staff.Movable = false; + AddItem( staff ); + + Backpack pack = new Backpack(); + pack.Movable = false; + AddItem( pack ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 7; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + UzeraanTurmoilQuest qs = to.Quest as UzeraanTurmoilQuest; + + return ( qs != null && qs.FindObjective( typeof( FindSchmendrickObjective ) ) != null ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( UzeraanTurmoilQuest.HasLostScrollOfPower( player ) ) + { + FocusTo( player ); + qs.AddConversation( new LostScrollOfPowerConversation( false ) ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( FindSchmendrickObjective ) ); + + if ( obj != null && !obj.Completed ) + { + FocusTo( player ); + obj.Complete(); + } + else if ( contextMenu ) + { + FocusTo( player ); + SayTo( player, 1049357 ); // I have nothing more for you at this time. + } + } + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is BlankScroll && UzeraanTurmoilQuest.HasLostScrollOfPower( from ) ) + { + FocusTo( from ); + + Item scroll = new SchmendrickScrollOfPower(); + + if ( !from.PlaceInBackpack( scroll ) ) + { + scroll.Delete(); + from.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + return false; + } + else + { + dropped.Consume(); + from.SendLocalizedMessage( 1049346 ); // Schmendrick scribbles on the scroll for a few moments and hands you the finished product. + return dropped.Deleted; + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( m is PlayerMobile && !m.Frozen && !m.Alive && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && InLOS( m ) ) + { + if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else + { + Direction = GetDirectionTo( m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.Healer ) ); + } + } + } + + public Schmendrick( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Uzeraan.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Uzeraan.cs new file mode 100644 index 000000000..c84660a30 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Mobiles/Uzeraan.cs @@ -0,0 +1,434 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Gumps; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Haven +{ + public class Uzeraan : BaseQuester + { + [Constructable] + public Uzeraan() : base( "the Conjurer" ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83F3; + + Female = false; + Body = 0x190; + Name = "Uzeraan"; + } + + public override void InitOutfit() + { + AddItem( new Robe( 0x4DD ) ); + AddItem( new WizardsHat( 0x8A5 ) ); + AddItem( new Shoes( 0x8A5 ) ); + + HairItemID = 0x203C; + HairHue = 0x455; + + FacialHairItemID = 0x203E; + FacialHairHue = 0x455; + + BlackStaff staff = new BlackStaff(); + staff.Movable = false; + AddItem( staff ); + } + + public override int GetAutoTalkRange( PlayerMobile pm ) + { + return 3; + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return to.Quest is UzeraanTurmoilQuest; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( UzeraanTurmoilQuest.HasLostScrollOfPower( player ) ) + { + qs.AddConversation( new LostScrollOfPowerConversation( true ) ); + } + else if ( UzeraanTurmoilQuest.HasLostFertileDirt( player ) ) + { + qs.AddConversation( new LostFertileDirtConversation( true ) ); + } + else if ( UzeraanTurmoilQuest.HasLostDaemonBlood( player ) ) + { + qs.AddConversation( new LostDaemonBloodConversation() ); + } + else if ( UzeraanTurmoilQuest.HasLostDaemonBone( player ) ) + { + qs.AddConversation( new LostDaemonBoneConversation() ); + } + else + { + if ( player.Profession == 2 ) // magician + { + Container backpack = player.Backpack; + + if ( backpack == null + || backpack.GetAmount( typeof( BlackPearl ) ) < 30 + || backpack.GetAmount( typeof( Bloodmoss ) ) < 30 + || backpack.GetAmount( typeof( Garlic ) ) < 30 + || backpack.GetAmount( typeof( Ginseng ) ) < 30 + || backpack.GetAmount( typeof( MandrakeRoot ) ) < 30 + || backpack.GetAmount( typeof( Nightshade ) ) < 30 + || backpack.GetAmount( typeof( SulfurousAsh ) ) < 30 + || backpack.GetAmount( typeof( SpidersSilk ) ) < 30 ) + { + qs.AddConversation( new FewReagentsConversation() ); + } + } + + QuestObjective obj = qs.FindObjective( typeof( FindUzeraanBeginObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else + { + obj = qs.FindObjective( typeof( FindUzeraanFirstTaskObjective ) ); + + if ( obj != null && !obj.Completed ) + { + obj.Complete(); + } + else + { + obj = qs.FindObjective( typeof( FindUzeraanAboutReportObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + if ( player.Profession == 2 ) // magician + { + cont.DropItem( new MarkScroll( 5 ) ); + cont.DropItem( new RecallScroll( 5 ) ); + for ( int i = 0; i < 5; i++ ) + { + cont.DropItem( new RecallRune() ); + } + } + else + { + cont.DropItem( new Gold( 300 ) ); + for ( int i = 0; i < 6; i++ ) + { + cont.DropItem( new NightSightPotion() ); + cont.DropItem( new LesserHealPotion() ); + } + } + + if ( !player.PlaceInBackpack( cont ) ) + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + else + { + obj.Complete(); + } + } + else + { + obj = qs.FindObjective( typeof( ReturnScrollOfPowerObjective ) ); + + if ( obj != null && !obj.Completed ) + { + FocusTo( player ); + SayTo( player, 1049378 ); // Hand me the scroll, if you have it. + } + else + { + obj = qs.FindObjective( typeof( ReturnFertileDirtObjective ) ); + + if ( obj != null && !obj.Completed ) + { + FocusTo( player ); + SayTo( player, 1049381 ); // Hand me the Fertile Dirt, if you have it. + } + else + { + obj = qs.FindObjective( typeof( ReturnDaemonBloodObjective ) ); + + if ( obj != null && !obj.Completed ) + { + FocusTo( player ); + SayTo( player, 1049379 ); // Hand me the Vial of Blood, if you have it. + } + else + { + obj = qs.FindObjective( typeof( ReturnDaemonBoneObjective ) ); + + if ( obj != null && !obj.Completed ) + { + FocusTo( player ); + SayTo( player, 1049380 ); // Hand me the Daemon Bone, if you have it. + } + else + { + SayTo( player, 1049357 ); // I have nothing more for you at this time. + } + } + } + } + } + } + } + } + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( dropped is UzeraanTurmoilHorn ) + { + if ( player.Young ) + { + UzeraanTurmoilHorn horn = (UzeraanTurmoilHorn)dropped; + + if ( horn.Charges < 10 ) + { + SayTo( from, 1049384 ); // I have recharged the item for you. + horn.Charges = 10; + } + else + { + SayTo( from, 1049385 ); // That doesn't need recharging yet. + } + } + else + { + player.SendMessage( "You must be young to have this item recharged." ); + } + + return false; + } + + if ( dropped is SchmendrickScrollOfPower ) + { + QuestObjective obj = qs.FindObjective( typeof( ReturnScrollOfPowerObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + cont.DropItem( new TreasureMap( player.Young ? 0 : 1, Map.Trammel ) ); + cont.DropItem( new Shovel() ); + cont.DropItem( new UzeraanTurmoilHorn() ); + + if ( !player.PlaceInBackpack( cont ) ) + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + return false; + } + else + { + dropped.Delete(); + obj.Complete(); + return true; + } + } + } + else if ( dropped is QuestFertileDirt ) + { + QuestObjective obj = qs.FindObjective( typeof( ReturnFertileDirtObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + if ( player.Profession == 2 ) // magician + { + cont.DropItem( new BlackPearl( 20 ) ); + cont.DropItem( new Bloodmoss( 20 ) ); + cont.DropItem( new Garlic( 20 ) ); + cont.DropItem( new Ginseng( 20 ) ); + cont.DropItem( new MandrakeRoot( 20 ) ); + cont.DropItem( new Nightshade( 20 ) ); + cont.DropItem( new SulfurousAsh( 20 ) ); + cont.DropItem( new SpidersSilk( 20 ) ); + + for ( int i = 0; i < 3; i++ ) + cont.DropItem( Loot.RandomScroll( 0, 23, SpellbookType.Regular ) ); + } + else + { + cont.DropItem( new Gold( 300 ) ); + cont.DropItem( new Bandage( 25 ) ); + + for ( int i = 0; i < 5; i++ ) + cont.DropItem( new LesserHealPotion() ); + } + + if ( !player.PlaceInBackpack( cont ) ) + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + return false; + } + else + { + dropped.Delete(); + obj.Complete(); + return true; + } + } + } + else if ( dropped is QuestDaemonBlood ) + { + QuestObjective obj = qs.FindObjective( typeof( ReturnDaemonBloodObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Item reward; + + if ( player.Profession == 2 ) // magician + { + Container cont = GetNewContainer(); + + cont.DropItem( new ExplosionScroll( 4 ) ); + cont.DropItem( new MagicWizardsHat() ); + + reward = cont; + } + else + { + BaseWeapon weapon; + switch ( Utility.Random( 6 ) ) + { + case 0: weapon = new Broadsword(); break; + case 1: weapon = new Cutlass(); break; + case 2: weapon = new Katana(); break; + case 3: weapon = new Longsword(); break; + case 4: weapon = new Scimitar(); break; + default: weapon = new VikingSword(); break; + } + + if ( Core.AOS ) + { + BaseRunicTool.ApplyAttributesTo( weapon, 3, 20, 40 ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)BaseCreature.RandomMinMaxScaled( 2, 4 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)BaseCreature.RandomMinMaxScaled( 2, 4 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)BaseCreature.RandomMinMaxScaled( 2, 4 ); + } + + weapon.Slayer = SlayerName.Silver; + + reward = weapon; + } + + if ( !player.PlaceInBackpack( reward ) ) + { + reward.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + return false; + } + else + { + dropped.Delete(); + obj.Complete(); + return true; + } + } + } + else if ( dropped is QuestDaemonBone ) + { + QuestObjective obj = qs.FindObjective( typeof( ReturnDaemonBoneObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + cont.DropItem( new BankCheck( 2000 ) ); + cont.DropItem( new EnchantedSextant() ); + + if ( !player.PlaceInBackpack( cont ) ) + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + return false; + } + else + { + dropped.Delete(); + obj.Complete(); + return true; + } + } + } + } + } + + return base.OnDragDrop( from, dropped ); + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( m is PlayerMobile && !m.Frozen && !m.Alive && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && InLOS( m ) ) + { + if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else + { + Direction = GetDirectionTo( m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.Healer ) ); + } + } + } + + public Uzeraan( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/Objectives.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/Objectives.cs new file mode 100644 index 000000000..8d2678037 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/Objectives.cs @@ -0,0 +1,638 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; + +namespace Server.Engines.Quests.Haven +{ + public class FindUzeraanBeginObjective : QuestObjective + { + public override object Message + { + get + { + // Find Uzeraan. Uzeraan will explain what you need to do next. + return 1046039; + } + } + + public FindUzeraanBeginObjective() + { + } + + public override void OnComplete() + { + if ( System.From.Profession == 5 ) // paladin + System.AddConversation( new UzeraanTitheConversation() ); + else + System.AddConversation( new UzeraanFirstTaskConversation() ); + } + } + + public class TitheGoldObjective : QuestObjective + { + public override object Message + { + get + { + /* Go to the shrine inside of Uzeraan's Mansion, near the front doors and + * tithe at least 500 gold.

+ * + * Return to Uzeraan when you are done. + */ + return 1060386; + } + } + + private int m_OldTithingPoints; + + public TitheGoldObjective() + { + m_OldTithingPoints = -1; + } + + public override void CheckProgress() + { + PlayerMobile pm = System.From; + int curTithingPoints = pm.TithingPoints; + + if ( curTithingPoints >= 500 ) + Complete(); + else if ( curTithingPoints > m_OldTithingPoints && m_OldTithingPoints >= 0 ) + pm.SendLocalizedMessage( 1060240, "", 0x41 ); // You must have at least 500 tithing points before you can continue in your quest. + + m_OldTithingPoints = curTithingPoints; + } + + public override void OnComplete() + { + System.AddObjective( new FindUzeraanFirstTaskObjective() ); + } + } + + public class FindUzeraanFirstTaskObjective : QuestObjective + { + public override object Message + { + get + { + // Return to Uzeraan, now that you have enough tithing points to continue your quest. + return 1060387; + } + } + + public FindUzeraanFirstTaskObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new UzeraanFirstTaskConversation() ); + } + } + + public enum KillHordeMinionsStep + { + First, + LearnKarma, + Others + } + + public class KillHordeMinionsObjective : QuestObjective + { + private KillHordeMinionsStep m_Step; + + public KillHordeMinionsStep Step { get { return m_Step; } } + + public override object Message + { + get + { + switch ( m_Step ) + { + case KillHordeMinionsStep.First: + /* Find the mountain pass beyond the house which lies at the + * end of the runic road.

+ * + * Assist the city Militia by slaying Horde Minions + */ + return 1049089; + + case KillHordeMinionsStep.LearnKarma: + /* You have just gained some Karma + * for killing the horde minion. Learn + * how this affects your Paladin abilities. + */ + return 1060389; + + default: + // Continue driving back the Horde Minions, as Uzeraan instructed you to do. + return 1060507; + } + } + } + + public override int MaxProgress + { + get + { + if ( System.From.Profession == 5 ) // paladin + { + switch ( m_Step ) + { + case KillHordeMinionsStep.First: return 1; + case KillHordeMinionsStep.LearnKarma: return 2; + default: return 5; + } + } + else + { + return 5; + } + } + } + + public override bool Completed + { + get + { + if ( m_Step == KillHordeMinionsStep.LearnKarma && HasBeenRead ) + return true; + else + return base.Completed; + } + } + + public KillHordeMinionsObjective() + { + } + + public KillHordeMinionsObjective( KillHordeMinionsStep step ) + { + m_Step = step; + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + gump.AddHtmlObject( 70, 260, 270, 100, 1049090, BaseQuestGump.Blue, false, false ); // Horde Minions killed: + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override void OnRead() + { + CheckCompletionStatus(); + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + // This restriction continues until the quest is ended + if ( from is HordeMinion && from.Map == Map.Trammel && from.X >= 3314 && from.X <= 3814 && from.Y >= 2345 && from.Y <= 3095 ) // Haven island + return true; + + return false; + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is HordeMinion && corpse.Map == Map.Trammel && corpse.X >= 3314 && corpse.X <= 3814 && corpse.Y >= 2345 && corpse.Y <= 3095 ) // Haven island + { + if ( CurProgress == 0 ) + System.From.Send( new DisplayHelpTopic( 29, false ) ); // HEALING + + CurProgress++; + } + } + + public override void OnComplete() + { + if ( System.From.Profession == 5 ) + { + switch ( m_Step ) + { + case KillHordeMinionsStep.First: + { + QuestObjective obj = new KillHordeMinionsObjective( KillHordeMinionsStep.LearnKarma ); + System.AddObjective( obj ); + obj.CurProgress = CurProgress; + break; + } + case KillHordeMinionsStep.LearnKarma: + { + QuestObjective obj = new KillHordeMinionsObjective( KillHordeMinionsStep.Others ); + System.AddObjective( obj ); + obj.CurProgress = CurProgress; + break; + } + default: + { + System.AddObjective( new FindUzeraanAboutReportObjective() ); + break; + } + } + } + else + { + System.AddObjective( new FindUzeraanAboutReportObjective() ); + } + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Step = (KillHordeMinionsStep) reader.ReadEncodedInt(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Step ); + } + } + + public class FindUzeraanAboutReportObjective : QuestObjective + { + public override object Message + { + get + { + /* It's no use... The Horde Minions are too many. + * They are appearing out of nowhere.

+ * + * Return to Uzeraan and report your findings. + */ + return 1049091; + } + } + + public FindUzeraanAboutReportObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new UzeraanReportConversation() ); + } + } + + public class FindSchmendrickObjective : QuestObjective + { + public override object Message + { + get + { + /* Prepare for battle and step onto the teleporter, + * located against the wall in the main hall of Uzeraan's mansion.

+ * + * Find Schmendrick within the mines. + */ + return 1049120; + } + } + + public FindSchmendrickObjective() + { + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + // This restriction begins when this objective is completed, and continues until the quest is ended + if ( Completed && from is RestlessSoul && from.Map == Map.Trammel && from.X >= 5199 && from.X <= 5271 && from.Y >= 1812 && from.Y <= 1865 ) // Schmendrick's cave + return true; + + return false; + } + + public override void OnComplete() + { + System.AddConversation( new SchmendrickConversation() ); + } + } + + public class FindApprenticeObjective : QuestObjective + { + public override object Message + { + get + { + /* Find Schmendrick's apprentice who is somewhere in the mining cave. + * The apprentice has the scroll of power needed by Uzeraan. + */ + return 1049323; + } + } + + public FindApprenticeObjective() + { + } + + public override void OnComplete() + { + System.AddObjective( new ReturnScrollOfPowerObjective() ); + } + } + + public class ReturnScrollOfPowerObjective : QuestObjective + { + public override object Message + { + get + { + /* You have obtained the scroll of power! Find your way out of the cave.

+ * + * Hand the scroll to Uzeraan (drag and drop) once you arrive in his mansion. + */ + return 1049324; + } + } + + public ReturnScrollOfPowerObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new UzeraanScrollOfPowerConversation() ); + } + } + + public class FindDryadObjective : QuestObjective + { + public override object Message + { + get + { + /* Find the Dryad in the woods of Haven and get a patch + * of fertile dirt from her.

+ * + * Use Uzeraan's teleporter to get there if necessary. + */ + return 1049358; + } + } + + public FindDryadObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new DryadConversation() ); + } + } + + public class ReturnFertileDirtObjective : QuestObjective + { + public override object Message + { + get + { + /* You have acquired the Fertile Dirt!

+ * + * Return to the mansion (North-East + * of the Dryad's Grove) and hand it to Uzeraan. + */ + return 1049327; + } + } + + public ReturnFertileDirtObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new UzeraanFertileDirtConversation() ); + } + } + + public class GetDaemonBloodObjective : QuestObjective + { + public override object Message + { + get + { + /* Bring back a vial of blood.

+ * + * Follow the road leading north from the mansion and walk into the hut + * to find the chest that contains the vial + */ + return 1049361; + } + } + + private bool m_Ambushed; + + public GetDaemonBloodObjective() + { + } + + public override void CheckProgress() + { + PlayerMobile player = System.From; + + if ( !m_Ambushed && player.Map == Map.Trammel && player.InRange( new Point3D( 3456, 2558, 50 ), 30 ) ) + { + int x = player.X - 1; + int y = player.Y - 2; + int z = Map.Trammel.GetAverageZ( x, y ); + + if ( Map.Trammel.CanSpawnMobile( x, y, z ) ) + { + m_Ambushed = true; + + player.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1049330 ); // You have been ambushed! Fight for your honor!!! + + BaseCreature creature = new HordeMinion(); + creature.MoveToWorld( new Point3D( x, y, z ), Map.Trammel ); + creature.Combatant = player; + } + } + } + + public override void OnComplete() + { + System.AddObjective( new ReturnDaemonBloodObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Ambushed = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Ambushed ); + } + } + + public class ReturnDaemonBloodObjective : QuestObjective + { + public override object Message + { + get + { + /* You have the vial of blood!

+ * + * Return to Uzeraan's mansion and hand him the vial. + */ + return 1049332; + } + } + + public ReturnDaemonBloodObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new UzeraanDaemonBloodConversation() ); + } + } + + public class GetDaemonBoneObjective : QuestObjective + { + private Container m_CorpseWithBone; + + public Container CorpseWithBone + { + get { return m_CorpseWithBone; } + set { m_CorpseWithBone = value; } + } + + public override object Message + { + get + { + if ( System.From.Profession == 5 ) + { + /* Use your Sacred Journey + * ability on the rune to the North + * of Uzeraan to travel to the graveyard. + */ + return 1060755; + } + else + { + /* Use Uzeraan's teleporter to get to the Haunted graveyard.

+ * + * Slay the undead until you find a Daemon Bone. + */ + return 1049362; + } + } + } + + public GetDaemonBoneObjective() + { + } + + public override void OnComplete() + { + System.AddObjective( new ReturnDaemonBoneObjective() ); + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + // This restriction continues until the end of the quest + if ( ( from is Zombie || from is Skeleton ) && from.Map == Map.Trammel && from.X >= 3391 && from.X <= 3424 && from.Y >= 2639 && from.Y <= 2664 ) // Haven graveyard + return true; + + return false; + } + + public override bool GetKillEvent( BaseCreature creature, Container corpse ) + { + if ( base.GetKillEvent( creature, corpse ) ) + return true; + + return UzeraanTurmoilQuest.HasLostDaemonBone( System.From ); + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( ( creature is Zombie || creature is Skeleton ) && corpse.Map == Map.Trammel && corpse.X >= 3391 && corpse.X <= 3424 && corpse.Y >= 2639 && corpse.Y <= 2664 ) // Haven graveyard + { + if ( Utility.RandomDouble() < 0.25 ) + m_CorpseWithBone = corpse; + } + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_CorpseWithBone = (Container) reader.ReadItem(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + if ( m_CorpseWithBone != null && m_CorpseWithBone.Deleted ) + m_CorpseWithBone = null; + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Item) m_CorpseWithBone ); + } + } + + public class ReturnDaemonBoneObjective : QuestObjective + { + public override object Message + { + get + { + /* Head East of here (or use the Horn of Retreat) + * to return to Uzeraan's Mansion and deliver the bone to Uzeraan. + */ + return 1049334; + } + } + + public ReturnDaemonBoneObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new UzeraanDaemonBoneConversation() ); + } + } + + public class CashBankCheckObjective : QuestObjective + { + public override object Message + { + get + { + /* Locate the Haven Bank (use the teleporter in Uzeraan's Mansion + * if necessary), which lies South-East + * of Uzeraan's Mansion. Once there, cash your check. + */ + return 1049336; + } + } + + public CashBankCheckObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new BankerConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Uzeraan Turmoil/UzeraanTurmoilQuest.cs b/Scripts/Engines/Quests/Uzeraan Turmoil/UzeraanTurmoilQuest.cs new file mode 100644 index 000000000..72f43bf09 --- /dev/null +++ b/Scripts/Engines/Quests/Uzeraan Turmoil/UzeraanTurmoilQuest.cs @@ -0,0 +1,232 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Engines.Quests.Haven +{ + public class UzeraanTurmoilQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Haven.AcceptConversation ), + typeof( Haven.UzeraanTitheConversation ), + typeof( Haven.UzeraanFirstTaskConversation ), + typeof( Haven.UzeraanReportConversation ), + typeof( Haven.SchmendrickConversation ), + typeof( Haven.UzeraanScrollOfPowerConversation ), + typeof( Haven.DryadConversation ), + typeof( Haven.UzeraanFertileDirtConversation ), + typeof( Haven.UzeraanDaemonBloodConversation ), + typeof( Haven.UzeraanDaemonBoneConversation ), + typeof( Haven.BankerConversation ), + typeof( Haven.RadarConversation ), + typeof( Haven.LostScrollOfPowerConversation ), + typeof( Haven.LostFertileDirtConversation ), + typeof( Haven.DryadAppleConversation ), + typeof( Haven.LostDaemonBloodConversation ), + typeof( Haven.LostDaemonBoneConversation ), + typeof( Haven.FindUzeraanBeginObjective ), + typeof( Haven.TitheGoldObjective ), + typeof( Haven.FindUzeraanFirstTaskObjective ), + typeof( Haven.KillHordeMinionsObjective ), + typeof( Haven.FindUzeraanAboutReportObjective ), + typeof( Haven.FindSchmendrickObjective ), + typeof( Haven.FindApprenticeObjective ), + typeof( Haven.ReturnScrollOfPowerObjective ), + typeof( Haven.FindDryadObjective ), + typeof( Haven.ReturnFertileDirtObjective ), + typeof( Haven.GetDaemonBloodObjective ), + typeof( Haven.ReturnDaemonBloodObjective ), + typeof( Haven.GetDaemonBoneObjective ), + typeof( Haven.ReturnDaemonBoneObjective ), + typeof( Haven.CashBankCheckObjective ), + typeof( Haven.FewReagentsConversation ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // "Uzeraan's Turmoil" + return 1049007; + } + } + + public override object OfferMessage + { + get + { + /* The guard speaks to you as you come closer...

+ * + * Greetings traveler!

+ * + * Uzeraan, the lord of this house and overseer of this city - + * Haven, has requested an audience with you.

+ * + * Hordes of gruesome hell spawn are beginning to overrun the + * city and terrorize the inhabitants. No one seems to be able + * to stop them.

+ * + * Our fine city militia is falling to the evil creatures + * one battalion after the other.

+ * + * Uzeraan, whom you can find through these doors, is looking to + * hire mercenaries to aid in the battle.

+ * + * Will you assist us? + */ + return 1049008; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.MaxValue; } } + public override bool IsTutorial{ get{ return true; } } + + public override int Picture + { + get + { + switch ( From.Profession ) + { + case 1: return 0x15C9; // warrior + case 2: return 0x15C1; // magician + default: return 0x15D3; // paladin + } + } + } + + private bool m_HasLeftTheMansion; + + public override void Slice() + { + if ( !m_HasLeftTheMansion && ( From.Map != Map.Trammel || From.X < 3573 || From.X > 3611 || From.Y < 2568 || From.Y > 2606 ) ) + { + m_HasLeftTheMansion = true; + AddConversation( new RadarConversation() ); + } + + base.Slice(); + } + + public UzeraanTurmoilQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public UzeraanTurmoilQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_HasLeftTheMansion = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_HasLeftTheMansion ); + } + + public static bool HasLostScrollOfPower( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( ReturnScrollOfPowerObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( SchmendrickScrollOfPower ) ) == null ); + } + } + + return false; + } + + public static bool HasLostFertileDirt( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( ReturnFertileDirtObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( QuestFertileDirt ) ) == null ); + } + } + + return false; + } + + public static bool HasLostDaemonBlood( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( ReturnDaemonBloodObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( QuestDaemonBlood ) ) == null ); + } + } + + return false; + } + + public static bool HasLostDaemonBone( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return false; + + QuestSystem qs = pm.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( ReturnDaemonBoneObjective ) ) ) + { + Container pack = from.Backpack; + + return ( pack == null || pack.FindItemByType( typeof( QuestDaemonBone ) ) == null ); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Conversations.cs b/Scripts/Engines/Quests/Witch Apprentice/Conversations.cs new file mode 100644 index 000000000..12a6fc156 --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Conversations.cs @@ -0,0 +1,732 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Hag +{ + public class DontOfferConversation : QuestConversation + { + public override object Message + { + get + { + /* The ancient, wrinkled hag looks up from her vile-smelling cauldron. + * Her single, unblinking eye attempts to focus in on you, but to + * little avail.

+ * + * What's that? Who's there? What do you want with me? I don't have + * time for the likes of you. I have stews to spice and brews to boil. + * Too many things to complete to be helping out a stranger.

+ * + * Besides, it looks as if you've already got yourself a quest that needs + * doing. Perhaps if you finish the task you're on, you can return to me + * and I'll help you out. But until then, leave an old witch alone to her + * magics! Shoo! Away with ye!

+ * + * The witch rushes you off with a wave of her decrepit hand and returns + * to tending the noxious brew boiling in her cauldron. + */ + return 1055000; + } + } + + public override bool Logged{ get{ return false; } } + + public DontOfferConversation() + { + } + } + + public class AcceptConversation : QuestConversation + { + public override object Message + { + get + { + /* Somewhat out of character for the vile old witch, she actually seems + * delighted that you've accepted her offer.

+ * + * Ah! That's the spirit! You're not a useless bag of bones after all, are ye? + * Well then, best get your hind quarters in gear and head towards the road! + * Remember, my young Apprentice could be anywhere along the road heading towards + * the Yew Graveyard, so be sure to run the whole course of it, and stay + * on track!

+ * + * And for Gashnak's sake, come back here when you've found something! And remember, + * I don't have all day! And watch out for the imp Zeefzorpul! And don't return + * empty handed! And pack a warm sweater! And don't trample my lawn on the + * way out!

+ * + * What are you still doing here? Get to it! Shoo! + */ + return 1055002; + } + } + + public AcceptConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindApprenticeObjective( true ) ); + } + } + + public class HagDuringCorpseSearchConversation : QuestConversation + { + public override object Message + { + get + { + /* The wrinkled hag looks up at you with venom in her eyes.

+ * + * What're you doing back here? I thought I told you to go find my lost + * Apprentice! I don't have time for your laziness, you wretched little worm! + * Shoo! Away with ye! And don't come back until you've found out what's + * happened to my Apprentice! + */ + return 1055003; + } + } + + public override bool Logged{ get{ return false; } } + + public HagDuringCorpseSearchConversation() + { + } + } + + public class ApprenticeCorpseConversation : QuestConversation + { + public override object Message + { + get + { + /* You inspect the charred and bloodied corpse, recognizing it from the + * Hag's description as the lost Apprentice you were tasked to + * bring back.

+ * + * It appears as if he has been scorched by fire and magic, and scratched + * at with vicious claws.

+ * + * You wonder if this horrific act is the work of the vile imp Zeefzorpul + * of which the Hag spoke. You decide you'd best return to the Hag and + * report your findings. + */ + return 1055004; + } + } + + public ApprenticeCorpseConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindGrizeldaAboutMurderObjective() ); + } + } + + public class MurderConversation : QuestConversation + { + public override object Message + { + get + { + /* The wrinkled old Hag looks up from her cauldron of boiling + * innards.

+ * + * Bah! Back already? Can't you see I'm busy with my cooking? You + * wouldn't like to have a little taste of my delicious dragon gizzard soup, + * would you? Haw! I thought as much.

+ * + * Enough of this jibber-jabber then - what news of my Apprentice?

+ * + * What's that? You say that horrible little imp Zeefzorpul was behind his + * disappearance!? What would Zeefzorpul want with my Apprentice? Probably + * just wants to make life more miserable for me than it already is.

+ * + * Wait! Bah! That must be it! Zeefzorpul must have found out that I sent + * my Apprentices out with various Magic Brew Recipes - lists of tasks and + * ingredients that needed completing.

+ * + * That despicable Zeefzorpul knows I need the list of ingredients I gave to + * that Apprentice. I've recipes to mix, stews to boil, magics to cast, and + * fortunes to meddle! I won't let that wretched felchscum spoil my day. + * You then, I need you to go find Zeefzorpul and get that scrap of + * parchment back!

+ * + * I'm not sure where he bides his time, but I'm sure if you go find his imp + * friends and rough them up, they'll squeal on him in no time! They all + * know each others' secret hiding places. Go on! Shoo! Go slay a few imps + * until they cough up their secrets! No mercy for those little nasties! + */ + return 1055005; + } + } + + public MurderConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new KillImpsObjective( true ) ); + } + } + + public class HagDuringImpSearchConversation : QuestConversation + { + public override object Message + { + get + { + /* The sickly old hag looks up from her boiling cauldron.

+ * + * Have you found that vile little Zeefzorpul yet? What!? You've come + * back here without finding out where Zeefzorpul is, and what he's done + * with my Magic Brew Recipe?

+ * + * I told you what needs to be done, you little whelp! Now away with ye! + * And don't you return until you've found my list of ingredients! + */ + return 1055006; + } + } + + public override bool Logged{ get{ return false; } } + + public HagDuringImpSearchConversation() + { + } + } + + public class ImpDeathConversation : QuestConversation + { + private Point3D m_ImpLocation; + + public override object Message + { + get + { + /* The wretched imp cries out for mercy.

+ * + * Forgive me! You master! You great warrior, great hooman, great greatest! + * Forgive! Forgive! I give up Zeef! He no good any way! He always smack me + * head and hurt me good! He say I ugly too, even with me pretty teef!

+ * + * But I knows where he hide! I follow him flapping to his hidey hole. + * He think he so smart but he so wrong! I make scribble drawing of where he + * like to hide! But you need the whistle blower to make him come! He no come + * without it! Make with the whistle at his hidey place, and Zeef must come, + * he cannot resist!

+ * + * The frightened imp hands you a crumpled map and a strange flute.

+ * + * You go to where the picture shows and then you play that whistle! Zeef come, + * me promise! But you make promise that you smack Zeef head good! + * Pweese?

+ * + * With this last request, the miserable little imp falls and breathes no more. + */ + return 1055007; + } + } + + public ImpDeathConversation( Point3D impLocation ) + { + m_ImpLocation = impLocation; + } + + public ImpDeathConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindZeefzorpulObjective( m_ImpLocation ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_ImpLocation = reader.ReadPoint3D(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Point3D) m_ImpLocation ); + } + } + + public class ZeefzorpulConversation : QuestConversation + { + public override object Message + { + get + { + /* In a puff of smoke that smells of brimstone, the imp Zeefzorpul + * appears.

+ * + * Wuh-whut!? How did stupid hooman find mighty Zeefzorpul? This crazy + * many times! This crazy not possible! This big crazy with crazy on top! + * But it happening! How can it be true!?

+ * + * GAH! Even mighty Zeefzorpul can no resist that crazy music! Mighty + * Zeefzorpul do what you want! Have you stupid paper back! Mighty Zeefzorpul + * no want it any way. It dumb. It super dumb. Big dumb like stupid dumb + * tree with dumb things on it! So stupid! So dumb that mighty Zeefzorpul + * not even care! You see me not caring? You better cause it certainly + * happening! Me not caring one bit!

+ * + * The strange little imp tosses the piece of parchment at you. Much + * to your surprise, however, he swoops down in a flash of flapping wings + * and steals the Magic Flute from your grasp.

+ * + * Hah! So stupid like a hooman! Mighty Zeefzorpul has defeated stupid + * hooman and is greatest ever imp in world! You serious stupid, mister + * hooman. Big stupid with stupid on top. Now you no can make trick on me + * again with crazy dance music! Mighty Zeefzorpul fly away to his other + * secret home where you never find him again!

+ * + * Me hope you get eated by a troll!

+ * + * With that, the imp Zeefzorpul disappears in another puff of rancid smoke. + */ + return 1055008; + } + } + + public ZeefzorpulConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new ReturnRecipeObjective() ); + } + } + + public class RecipeConversation : QuestConversation + { + public override object Message + { + get + { + /* The wart-covered witch looks up from pouring fetid scraps of meat + * into her cauldron.

+ * + * You've dealt with that troublesome imp Zeefzorpul? Good for you, little + * one! You're not as useless as you appear, even to a daft old wench such + * as myself!

+ * + * Now then, I see you've recovered my precious Magic Brew Recipe. I suppose + * you expect a reward? Well, you can go on expecting, and I can go on being + * ugly. What good is it to me that I have the list, if I don't have an + * apprentice to go gather the ingredients and perform the tasks + * themselves!

+ * + * If you want your precious little reward, you'll have to complete the task + * I gave to my previous Apprentice. Now away with you! Shoo! Shimmy! Skedattle! + * I've heads to boil and stews to spice! Don't you return until you've completed + * every item on that list! + */ + return 1055009; + } + } + + public RecipeConversation() + { + } + + public override void OnRead() + { + System.AddObjective( new FindIngredientObjective( new Ingredient[0] ) ); + } + } + + public class HagDuringIngredientsConversation : QuestConversation + { + public override object Message + { + get + { + /* The ancient crone looks up from her bubbling brew, staring you down + * with her one good eye.

+ * + * You've returned already have you? And what of your task? Have you gathered + * all the needed ingredients?

+ * + * What's that!? You still haven't finished the simple little task I've set before + * you? Then why come back here and bother me? I can't get a single brew + * concocted if you keep bugging me with your whimpering little diatribes! Why, + * you're worse than my last apprentice - and he was the very king of fools!

+ * + * Go on with ye! Away and begone! I don't want to see hide nor hair of your + * whining little face until you've gathered each and every last one of the ingredients + * on that list!

+ * + * With a disgusting hacking noise, the vile witch spits upon the ground and + * brushes you off with a wave of her wrinkled old hand. + */ + return 1055012; + } + } + + public override bool Logged{ get{ return false; } } + + public HagDuringIngredientsConversation() + { + } + } + + public class BlackheartFirstConversation : QuestConversation + { + public override object Message + { + get + { + /* The bawdy old pirate captain looks up from his bottle of Wild Harpy + * whiskey, as drunk as any man you've ever seen.

+ * + * With an excruciatingly slow movement, he pushes back his tricorne hat + * and stares you down with red-rimmed eyes.


+ * + * Whut tha blazes do ye want, landlubber? Some've Captain Blackheart's + * fine Whiskey? Well ye can drown in the seven seas, ya barnacle-covered + * bilge rat!

+ * + * I've cut down pasty-faced runts like yerself for lesser insults! I've + * sailed the seas've this world fer fifty years, and never seen a more + * milk-soaked pansy lass than ye come in here for a favor. Give ye some + * of my special Whiskey? I'd sooner wrestle a sea serpent naked - and I've + * done that some twenty times!

+ * + * Ye see, ol' Captain Blackheart's Whiskey is only for pirate folk. And ye + * don't look like no pirate I've ever seen. Ye have te have the right cut + * of cloth and the right amount of liquor in yer belly te sail on my crew! + * And without that, ye might as well go home and cry to yer mommy. Cause + * ye ain't ever gonna share no drink with me!

+ * + * Now off with ye!

+ * + * With that, Captain Blackheart goes back to singing his bawdy songs + * and drinking his whiskey. It seems as if you'll have to find some way to + * change his mind about your worthiness. + */ + return 1055010; + } + } + + public BlackheartFirstConversation() + { + } + + public override void OnRead() + { + FindIngredientObjective obj = System.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null ) + System.AddObjective( new FindIngredientObjective( obj.Ingredients, true ) ); + } + } + + public class BlackheartNoPirateConversation : QuestConversation + { + private bool m_Tricorne; + private bool m_Drunken; + + public override object Message + { + get + { + if ( m_Tricorne) + { + if ( m_Drunken ) + { + /* The filthy Captain flashes a pleased grin at you as he looks you up + * and down.

Well that's more like it, me little deck swabber! + * Ye almost look like ye fit in around here, ready te sail the great seas + * of Britannia, sinking boats and slaying sea serpents!

+ * + * But can ye truly handle yerself? Ye might think ye can test me meddle + * with a sip or two of yer dandy wine, but a real pirate walks the decks + * with a belly full of it. Lookit that, yer not even wobblin'!

+ * + * Ye've impressed me a bit, ye wee tyke, but it'll take more'n that te + * join me crew!

Captain Blackheart tips his mug in your direction, + * offering up a jolly laugh, but it seems you still haven't impressed him + * enough. + */ + return 1055059; + } + else + { + /* Captain Blackheart looks up from polishing his cutlass, glaring at + * you with red-rimmed eyes.

+ * + * Well, well. Lookit the wee little deck swabby. Aren't ye a cute lil' + * lassy? Don't ye look just fancy? Ye think yer ready te join me pirate + * crew? Ye think I should offer ye some've me special Blackheart brew?

+ * + * I'll make ye walk the plank, I will! We'll see how sweet n' darlin' ye + * look when the sea serpents get at ye and rip ye te threads! Won't that be + * a pretty picture, eh?

+ * + * Ye don't have the stomach fer the pirate life, that's plain enough te me. Ye + * prance around here like a wee lil' princess, ye do. If ye want to join my + * crew ye can't just look tha part - ye have to have the stomach fer it, filled + * up with rotgut until ye can't see straight. I don't drink with just any ol' + * landlubber! Ye'd best prove yer mettle before ye talk te me again!

+ * + * The drunken pirate captain leans back in his chair, taking another gulp of + * his drink before he starts in on another bawdy pirate song. + */ + return 1055057; + } + } + else + { + if ( m_Drunken ) + { + /* The inebriated pirate looks up at you with a wry grin.

+ * + * Well hello again, me little matey. I see ye have a belly full of rotgut + * in ye. I bet ye think you're a right hero, ready te face the world. But + * as I told ye before, bein' a member of my pirate crew means more'n just + * being able to hold yer drink. Ye have te look the part - and frankly, me + * little barnacle, ye don't have the cut of cloth te fit in with the crowd I + * like te hang around.

+ * + * So scurry off, ye wee sewer rat, and don't come back round these parts all + * liquored up an' three sheets te tha wind, unless yer truly ready te join + * me pirate crew!

+ * + * Captain Blackheart shoves you aside, banging his cutlass against the + * table as he calls to the waitress for another round. + */ + return 1055056; + } + else + { + /* Captain Blackheart looks up from his drink, almost tipping over + * his chair as he looks you up and down.

+ * + * You again? I thought I told ye te get lost? Go on with ye! Ye ain't + * no pirate - yer not even fit te clean the barnacles off me rear end! + * Don't ye come back babbling te me for any of me Blackheart Whiskey until + * ye look and act like a true pirate!

+ * + * Now shove off, sewer rat - I've got drinkin' te do!

+ * + * The inebriated pirate bolts back another mug of ale and brushes you + * off with a wave of his hand. + */ + return 1055058; + } + } + } + } + + public override bool Logged{ get{ return false; } } + + public BlackheartNoPirateConversation( bool tricorne, bool drunken ) + { + m_Tricorne = tricorne; + m_Drunken = drunken; + } + + public BlackheartNoPirateConversation() + { + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Tricorne = reader.ReadBool(); + m_Drunken = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_Tricorne ); + writer.Write( (bool) m_Drunken ); + } + } + + public class BlackheartPirateConversation : QuestConversation + { + private bool m_FirstMet; + + public override object Message + { + get + { + if ( m_FirstMet ) + { + /* The bawdy old pirate captain looks up from his bottle of Wild Harpy + * whiskey, as drunk as any man you've ever seen.

+ * + * Avast ye, ye loveable pirate! Just in from sailin' the glorious sea? Ye + * look right ready te fall down on the spot, ye do!

+ * + * I tell ye what, from the look've ye, ye deserve a belt of better brew than + * the slop ye've been drinking, and I've just the thing.

+ * + * I call it Captain Blackheart's Whiskey, and it'll give ye hairs on yer chest, + * that's for sure. Why, a keg of this stuff once spilled on my ship, and it + * ate a hole right through the deck!

Go on, drink up, or use it to clean + * the rust off your cutlass - it's the best brew, either way!

+ * + * Captain Blackheart hands you a jug of his famous Whiskey. You think it best + * to return it to the Hag, rather than drink any of the noxious swill. + */ + return 1055054; + } + else + { + /* The drunken pirate, Captain Blackheart, looks up from his bottle + * of whiskey with a pleased expression.

+ * + * Well looky here! I didn't think a landlubber like yourself had the pirate + * blood in ye! But look at that! You certainly look the part now! Sure + * you can still keep on your feet? Har!

+ * + * Avast ye, ye loveable pirate! Ye deserve a belt of better brew than the slop + * ye've been drinking, and I've just the thing.

+ * + * I call it Captain Blackheart's Whiskey, and it'll give ye hairs on yer chest, + * that's for sure. Why, a keg of this stuff once spilled on my ship, and it ate + * a hole right through the deck!

+ * + * Go on, drink up, or use it to clean the rust off your cutlass - it's the best + * brew, either way!

+ * + * Captain Blackheart hands you a jug of his famous Whiskey. You think it best + * to return it to the Hag, rather than drink any of the noxious swill. + */ + return 1055011; + } + } + } + + public BlackheartPirateConversation( bool firstMet ) + { + m_FirstMet = firstMet; + } + + public BlackheartPirateConversation() + { + } + + public override void OnRead() + { + FindIngredientObjective obj = System.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null ) + obj.NextStep(); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_FirstMet = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (bool) m_FirstMet ); + } + } + + public class EndConversation : QuestConversation + { + public override object Message + { + get + { + /* The horrible wretch of a witch looks up from her vile experiments + * and focuses her one good eye on you.

+ * + * Eh? What's that? You say you've gathered the ingredients for my delicious + * Magic Brew?

+ * + * Well, well, I don't know exactly what to say. I thought for sure you'd + * end up dead! Haw! Can't blame a lady for wishing, can you? Even if she + * is a bit old and wrinkled.

+ * + * Well, I promised you a reward for your efforts, and I never lie - leastways + * not to someone like you, after the great sacrifices you've made. You know, + * I could use a new Apprentice, in an official capacity as it were. I couldn't + * convince you to stay around and help me out some more could I? There's always + * cauldrons that need cleaning, dung that needs shoveling, newts eye that + * needs a proper chewing, and fires that need stoking.

+ * + * What's that? Not interested? Well, I suppose you have great things ahead of + * you and all that. Feh! Like a puckish little puke like you could ever make + * something of themselves in this cold old world!

+ * + * Nevertheless, I'll give you your blasted reward, and you'd better be happy + * with it because it's all you're getting. Caused me enough trouble as it is. + * Here, take it, and be off with you! It'll be a pleasure to my eye if I + * never have to squint to see you again! And the stench! Smells like you + * washed this very morning! A great fancy folk you are, with your soaps and + * water! Think you're so great...why, I remember when we didn't even have + * soap, and water was made by tiny little fairies and cost a gold piece for + * a thimbleful...I could tell you some stories, I could...

+ * + * Your reward in hand, you decide to leave the old Hag to her mumblings + * before she realizes you're still around and puts you back to work. + */ + return 1055013; + } + } + + public EndConversation() + { + } + + public override void OnRead() + { + System.Complete(); + } + } + + public class RecentlyFinishedConversation : QuestConversation + { + public override object Message + { + get + { + /* The wrinkled old crone stops stirring her noxious stew, looking up at + * you with an annoyed expression on her face.

+ * + * You again? Listen, you little wretch, I'm in no mood for any of your meddlesome + * requests. I've work to do, and no time for your whining.

+ * + * Come back later, and maybe I'll have something for you to do. In the meantime, + * get out of my sight - and don't touch anything on your way out!

+ * + * The vile hag hacks up a gob of phlegm, spitting it on the ground before + * returning to her work. + */ + return 1055064; + } + } + + public RecentlyFinishedConversation() + { + } + + public override bool Logged{ get{ return false; } } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Ingredient.cs b/Scripts/Engines/Quests/Witch Apprentice/Ingredient.cs new file mode 100644 index 000000000..d1ce6ac49 --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Ingredient.cs @@ -0,0 +1,112 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Hag +{ + public enum Ingredient + { + SheepLiver, + RabbitsFoot, + MongbatWing, + ChickenGizzard, + RatTail, + FrogsLeg, + DeerHeart, + LizardTongue, + SlimeOoze, + SpiritEssence, + SwampWater, + RedMushrooms, + Bones, + StarChart, + Whiskey + } + + public class IngredientInfo + { + private static IngredientInfo[] m_Table = new IngredientInfo[] + { + // sheep liver + new IngredientInfo( 1055020, 5, typeof( Sheep ) ), + // rabbit's foot + new IngredientInfo( 1055021, 5, typeof( Rabbit ), typeof( JackRabbit ) ), + // mongbat wing + new IngredientInfo( 1055022, 5, typeof( Mongbat ) ), + // chicken gizzard + new IngredientInfo( 1055023, 5, typeof( Chicken ) ), + // rat tail + new IngredientInfo( 1055024, 5, typeof( Rat ), typeof( GiantRat ), typeof( Sewerrat ) ), + // frog's leg + new IngredientInfo( 1055025, 5, typeof( BullFrog ) ), + // deer heart + new IngredientInfo( 1055026, 5, typeof( Hind ), typeof( GreatHart ) ), + // lizard tongue + new IngredientInfo( 1055027, 5, typeof( LavaLizard ), typeof( Lizardman ) ), + // slime ooze + new IngredientInfo( 1055028, 5, typeof( Slime ) ), + // spirit essence + new IngredientInfo( 1055029, 5, typeof( Ghoul ), typeof( Spectre ), typeof( Shade ), typeof( Wraith ), typeof( Bogle ) ), + // Swamp Water + new IngredientInfo( 1055030, 1 ), + // Freshly Cut Red Mushrooms + new IngredientInfo( 1055031, 1 ), + // Bones Buried In Hallowed Ground + new IngredientInfo( 1055032, 1 ), + // Star Chart + new IngredientInfo( 1055033, 1 ), + // Captain Blackheart's Whiskey + new IngredientInfo( 1055034, 1 ) + }; + + public static IngredientInfo Get( Ingredient ingredient ) + { + int index = (int)ingredient; + + if ( index >= 0 && index < m_Table.Length ) + return m_Table[index]; + else + return m_Table[0]; + } + + public static Ingredient RandomIngredient( Ingredient[] oldIngredients ) + { + int length = m_Table.Length - oldIngredients.Length; + Ingredient[] ingredients = new Ingredient[length]; + + for ( int i = 0, n = 0; i < m_Table.Length && n < ingredients.Length; i++ ) + { + Ingredient currIngredient = (Ingredient)i; + + bool found = false; + for ( int j = 0; !found && j < oldIngredients.Length; j++ ) + { + if ( oldIngredients[j] == currIngredient ) + found = true; + } + + if ( !found ) + ingredients[n++] = currIngredient; + } + + int index = Utility.Random( ingredients.Length ); + + return ingredients[index]; + } + + private int m_Name; + private Type[] m_Creatures; + private int m_Quantity; + + public int Name{ get{ return m_Name; } } + public Type[] Creatures{ get{ return m_Creatures; } } + public int Quantity{ get{ return m_Quantity; } } + + private IngredientInfo( int name, int quantity, params Type[] creatures ) + { + m_Name = name; + m_Creatures = creatures; + m_Quantity = quantity; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Items/Cauldron.cs b/Scripts/Engines/Quests/Witch Apprentice/Items/Cauldron.cs new file mode 100644 index 000000000..d3830a128 --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Items/Cauldron.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Cauldron : Item + { + public override string DefaultName + { + get { return "a cauldron"; } + } + + [Constructable] + public Cauldron() : base( 0x9ED ) + { + Weight = 1.0; + } + + public Cauldron( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs b/Scripts/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs new file mode 100644 index 000000000..a45cc29f8 --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Items/HagApprenticeCorpse.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Misc; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Hag +{ + public class HagApprenticeCorpse : Corpse + { + private static Mobile GetOwner() + { + Mobile apprentice = new Mobile(); + + apprentice.Hue = Utility.RandomSkinHue(); + apprentice.Female = false; + apprentice.Body = 0x190; + + apprentice.Delete(); + + return apprentice; + } + + private static List GetEquipment() + { + return new List(); + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( "a charred corpse" ); + } + + public override void OnSingleClick( Mobile from ) + { + int hue = Notoriety.GetHue( NotorietyHandlers.CorpseNotoriety( from, this ) ); + + from.Send( new AsciiMessage( Serial, ItemID, MessageType.Label, hue, 3, "", "a charred corpse" ) ); + } + + [Constructable] + public HagApprenticeCorpse() : base( GetOwner(), GetEquipment() ) + { + Direction = Direction.South; + + foreach ( Item item in EquipItems ) + { + DropItem( item ); + } + } + + public HagApprenticeCorpse( Serial serial ) : base( serial ) + { + } + + public override void Open( Mobile from, bool checkSelfLoot ) + { + if ( !from.InRange( this.GetWorldLocation(), 2 ) ) + return; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindApprenticeObjective obj = qs.FindObjective( typeof( FindApprenticeObjective ) ) as FindApprenticeObjective; + + if ( obj != null && !obj.Completed ) + { + if ( obj.Corpse == this ) + { + obj.Complete(); + Delete(); + } + else + { + SendLocalizedMessageTo( from, 1055047 ); // You examine the corpse, but it doesn't fit the description of the particular apprentice the Hag tasked you with finding. + } + + return; + } + } + } + + SendLocalizedMessageTo( from, 1055048 ); // You examine the corpse, but find nothing of interest. + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs b/Scripts/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs new file mode 100644 index 000000000..9f01c9c14 --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs @@ -0,0 +1,90 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Hag +{ + public class HangoverCure : Item + { + private int m_Uses; + + public override int LabelNumber{ get{ return 1055060; } } // Grizelda's Extra Strength Hangover Cure + + [CommandProperty( AccessLevel.GameMaster )] + public int Uses + { + get{ return m_Uses; } + set{ m_Uses = value; } + } + + [Constructable] + public HangoverCure() : base( 0xE2B ) + { + Weight = 1.0; + Hue = 0x2D; + + m_Uses = 20; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + SendLocalizedMessageTo( from, 1042038 ); // You must have the object in your backpack to use it. + return; + } + + if ( m_Uses > 0 ) + { + from.PlaySound( 0x2D6 ); + from.SendLocalizedMessage( 501206 ); // An awful taste fills your mouth. + + if ( from.BAC > 0 ) + { + from.BAC = 0; + from.SendLocalizedMessage( 501204 ); // You are now sober! + } + + m_Uses--; + } + else + { + Delete(); + from.SendLocalizedMessage( 501201 ); // There wasn't enough left to have any effect. + } + } + + public HangoverCure( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.WriteEncodedInt( m_Uses ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Uses = reader.ReadEncodedInt(); + break; + } + case 0: + { + m_Uses = 20; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Items/MagicFlute.cs b/Scripts/Engines/Quests/Witch Apprentice/Items/MagicFlute.cs new file mode 100644 index 000000000..0e73f486a --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Items/MagicFlute.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Hag +{ + public class MagicFlute : Item + { + public override int LabelNumber{ get{ return 1055051; } } // magic flute + + [Constructable] + public MagicFlute() : base( 0x1421 ) + { + Hue = 0x8AB; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + SendLocalizedMessageTo( from, 1042292 ); // You must have the object in your backpack to use it. + return; + } + + from.PlaySound( 0x3D ); + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindZeefzorpulObjective obj = qs.FindObjective( typeof( FindZeefzorpulObjective ) ) as FindZeefzorpulObjective; + + if ( obj != null && !obj.Completed ) + { + if ( ( player.Map != Map.Trammel && player.Map != Map.Felucca ) || !player.InRange( obj.ImpLocation, 8 ) ) + { + player.SendLocalizedMessage( 1055053 ); // Nothing happens. Zeefzorpul must not be hiding in this area. + } + else if ( player.InRange( obj.ImpLocation, 4 ) ) + { + Delete(); + + obj.Complete(); + } + else + { + player.SendLocalizedMessage( 1055052 ); // The flute sparkles. Zeefzorpul must be in a good hiding place nearby. + } + } + } + } + } + + public MagicFlute( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs b/Scripts/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs new file mode 100644 index 000000000..58155814e --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Engines.Quests.Hag +{ + public class MoonfireBrew : Item + { + public override int LabelNumber{ get{ return 1055065; } } // a bottle of magical moonfire brew + + [Constructable] + public MoonfireBrew() : base( 0xF04 ) + { + Weight = 1.0; + } + + public MoonfireBrew( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs b/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs new file mode 100644 index 000000000..599e4824f --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Blackheart.cs @@ -0,0 +1,120 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Hag +{ + public class Blackheart : BaseQuester + { + [Constructable] + public Blackheart() : base( "the Drunken Pirate" ) + { + } + + public Blackheart( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83EF; + + Female = false; + Body = 0x190; + Name = "Captain Blackheart"; + } + + public override void InitOutfit() + { + AddItem( new FancyShirt() ); + AddItem( new LongPants( 0x66D ) ); + AddItem( new ThighBoots() ); + AddItem( new TricorneHat( 0x1 ) ); + AddItem( new BodySash( 0x66D ) ); + + LeatherGloves gloves = new LeatherGloves(); + gloves.Hue = 0x66D; + AddItem( gloves ); + + FacialHairItemID = 0x203E; // Long Beard + FacialHairHue = 0x455; + + Item sword = new Cutlass(); + sword.Movable = false; + AddItem( sword ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + Direction = GetDirectionTo( player ); + Animate( 33, 20, 1, true, false, 0 ); + + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindIngredientObjective obj = qs.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null && !obj.Completed && obj.Ingredient == Ingredient.Whiskey ) + { + PlaySound( Utility.RandomBool() ? 0x42E : 0x43F ); + + Item hat = player.FindItemOnLayer( Layer.Helm ); + bool tricorne = hat is TricorneHat; + + if ( tricorne && player.BAC >= 20 ) + { + obj.Complete(); + + if ( obj.BlackheartMet ) + qs.AddConversation( new BlackheartPirateConversation( false ) ); + else + qs.AddConversation( new BlackheartPirateConversation( true ) ); + } + else if ( !obj.BlackheartMet ) + { + obj.Complete(); + + qs.AddConversation( new BlackheartFirstConversation() ); + } + else + { + qs.AddConversation( new BlackheartNoPirateConversation( tricorne, player.BAC > 0 ) ); + } + + return; + } + } + + PlaySound( 0x42C ); + SayTo( player, 1055041 ); // The drunken pirate shakes his fist at you and goes back to drinking. + } + + private void Heave() + { + this.PublicOverheadMessage( Network.MessageType.Regular, 0x3B2, 500849 ); // *hic* + + Timer.DelayCall( TimeSpan.FromSeconds( Utility.RandomMinMax( 60, 180 ) ), new TimerCallback( Heave ) ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Heave(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Grizelda.cs b/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Grizelda.cs new file mode 100644 index 000000000..b711c4861 --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Grizelda.cs @@ -0,0 +1,220 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; + +namespace Server.Engines.Quests.Hag +{ + public class Grizelda : BaseQuester + { + public override bool ClickTitle{ get{ return true; } } + + [Constructable] + public Grizelda() : base( "the Hag" ) + { + } + + public Grizelda( Serial serial ) : base( serial ) + { + } + + public override void InitBody() + { + InitStats( 100, 100, 25 ); + + Hue = 0x83EA; + + Female = true; + Body = 0x191; + Name = "Grizelda"; + } + + public override void InitOutfit() + { + AddItem( new Robe( 0x1 ) ); + AddItem( new Sandals() ); + AddItem( new WizardsHat( 0x1 ) ); + AddItem( new GoldBracelet() ); + + HairItemID = 0x203C; + + Item staff = new GnarledStaff(); + staff.Movable = false; + AddItem( staff ); + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + Direction = GetDirectionTo( player ); + + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + if ( qs.IsObjectiveInProgress( typeof( FindApprenticeObjective ) ) ) + { + PlaySound( 0x259 ); + PlaySound( 0x206 ); + qs.AddConversation( new HagDuringCorpseSearchConversation() ); + } + else + { + QuestObjective obj = qs.FindObjective( typeof( FindGrizeldaAboutMurderObjective ) ); + + if ( obj != null && !obj.Completed ) + { + PlaySound( 0x420 ); + PlaySound( 0x20 ); + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( KillImpsObjective ) ) + || qs.IsObjectiveInProgress( typeof( FindZeefzorpulObjective ) ) ) + { + PlaySound( 0x259 ); + PlaySound( 0x206 ); + qs.AddConversation( new HagDuringImpSearchConversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnRecipeObjective ) ); + + if ( obj != null && !obj.Completed ) + { + PlaySound( 0x258 ); + PlaySound( 0x41B ); + obj.Complete(); + } + else if ( qs.IsObjectiveInProgress( typeof( FindIngredientObjective ) ) ) + { + PlaySound( 0x259 ); + PlaySound( 0x206 ); + qs.AddConversation( new HagDuringIngredientsConversation() ); + } + else + { + obj = qs.FindObjective( typeof( ReturnIngredientsObjective ) ); + + if ( obj != null && !obj.Completed ) + { + Container cont = GetNewContainer(); + + cont.DropItem( new BlackPearl( 30 ) ); + cont.DropItem( new Bloodmoss( 30 ) ); + cont.DropItem( new Garlic( 30 ) ); + cont.DropItem( new Ginseng( 30 ) ); + cont.DropItem( new MandrakeRoot( 30 ) ); + cont.DropItem( new Nightshade( 30 ) ); + cont.DropItem( new SulfurousAsh( 30 ) ); + cont.DropItem( new SpidersSilk( 30 ) ); + + cont.DropItem( new Cauldron() ); + cont.DropItem( new MoonfireBrew() ); + cont.DropItem( new TreasureMap( Utility.RandomMinMax( 1, 4 ), this.Map ) ); + cont.DropItem( new Gold( 2000, 2200 ) ); + + if ( Utility.RandomBool() ) + { + BaseWeapon weapon = Loot.RandomWeapon(); + + if ( Core.AOS ) + { + BaseRunicTool.ApplyAttributesTo( weapon, 2, 20, 30 ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + } + + cont.DropItem( weapon ); + } + else + { + Item item; + + if ( Core.AOS ) + { + item = Loot.RandomArmorOrShieldOrJewelry(); + + if ( item is BaseArmor ) + BaseRunicTool.ApplyAttributesTo( (BaseArmor)item, 2, 20, 30 ); + else if ( item is BaseJewel ) + BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, 2, 20, 30 ); + } + else + { + BaseArmor armor = Loot.RandomArmorOrShield(); + item = armor; + + armor.ProtectionLevel = (ArmorProtectionLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + armor.Durability = (ArmorDurabilityLevel)BaseCreature.RandomMinMaxScaled( 2, 3 ); + } + + cont.DropItem( item ); + } + + if ( player.BAC > 0 ) + cont.DropItem( new HangoverCure() ); + + if ( player.PlaceInBackpack( cont ) ) + { + bool gainedPath = false; + + if ( VirtueHelper.Award( player, VirtueName.Sacrifice, 250, ref gainedPath ) ) // TODO: Check amount on OSI. + player.SendLocalizedMessage( 1054160 ); // You have gained in sacrifice. + + PlaySound( 0x253 ); + PlaySound( 0x20 ); + obj.Complete(); + } + else + { + cont.Delete(); + player.SendLocalizedMessage( 1046260 ); // You need to clear some space in your inventory to continue with the quest. Come back here when you have more space in your inventory. + } + } + } + } + } + } + else + { + QuestSystem newQuest = new WitchApprenticeQuest( player ); + bool inRestartPeriod = false; + + if ( qs != null ) + { + newQuest.AddConversation( new DontOfferConversation() ); + } + else if ( QuestSystem.CanOfferQuest( player, typeof( WitchApprenticeQuest ), out inRestartPeriod ) ) + { + PlaySound( 0x20 ); + PlaySound( 0x206 ); + newQuest.SendOffer(); + } + else if ( inRestartPeriod ) + { + PlaySound( 0x259 ); + PlaySound( 0x206 ); + newQuest.AddConversation( new RecentlyFinishedConversation() ); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs b/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs new file mode 100644 index 000000000..f703abd2b --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Mobiles/Zeefzorpul.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Hag +{ + public class Zeefzorpul : BaseQuester + { + public Zeefzorpul() + { + } + + public override void InitBody() + { + Body = 0x4A; + Name = "Zeefzorpul"; + } + + public Zeefzorpul( Serial serial ) : base( serial ) + { + } + + public override bool CanTalkTo( PlayerMobile to ) + { + return false; + } + + public override void OnTalk( PlayerMobile player, bool contextMenu ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/Objectives.cs b/Scripts/Engines/Quests/Witch Apprentice/Objectives.cs new file mode 100644 index 000000000..1e82ca32d --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/Objectives.cs @@ -0,0 +1,528 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Engines.Quests.Hag +{ + public class FindApprenticeObjective : QuestObjective + { + private static Point3D[] m_CorpseLocations = new Point3D[] + { + new Point3D( 778, 1158, 0 ), + new Point3D( 698, 1443, 0 ), + new Point3D( 785, 1548, 0 ), + new Point3D( 734, 1504, 0 ), + new Point3D( 819, 1266, 0 ) + }; + + private static Point3D RandomCorpseLocation() + { + int index = Utility.Random( m_CorpseLocations.Length ); + + return m_CorpseLocations[index]; + } + + private Corpse m_Corpse; + private Point3D m_CorpseLocation; + + public override object Message + { + get + { + /* To the west of the Hag's house lies the road between Skara Brae + * and Yew. Follow it carefully toward Yew's graveyard, and search for + * any sign of the Hag's apprentice along the road. + */ + return 1055014; + } + } + + public Corpse Corpse{ get{ return m_Corpse; } } + + public FindApprenticeObjective( bool init ) + { + if ( init ) + m_CorpseLocation = RandomCorpseLocation(); + } + + public FindApprenticeObjective() + { + } + + public override void CheckProgress() + { + PlayerMobile player = System.From; + Map map = player.Map; + + if ( ( m_Corpse == null || m_Corpse.Deleted ) && ( map == Map.Trammel || map == Map.Felucca ) && player.InRange( m_CorpseLocation, 8 ) ) + { + m_Corpse = new HagApprenticeCorpse(); + m_Corpse.MoveToWorld( m_CorpseLocation, map ); + + Effects.SendLocationEffect( m_CorpseLocation, map, 0x3728, 10, 10 ); + Effects.PlaySound( m_CorpseLocation, map, 0x1FE ); + + Mobile imp = new Zeefzorpul(); + imp.MoveToWorld( m_CorpseLocation, map ); + + // * You see a strange imp stealing a scrap of paper from the bloodied corpse * + m_Corpse.SendLocalizedMessageTo( player, 1055049 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 3.0 ), new TimerStateCallback( DeleteImp ), imp ); + } + } + + private void DeleteImp( object imp ) + { + Mobile m = imp as Mobile; + + if ( m != null && !m.Deleted ) + { + Effects.SendLocationEffect( m.Location, m.Map, 0x3728, 10, 10 ); + Effects.PlaySound( m.Location, m.Map, 0x1FE ); + + m.Delete(); + } + } + + public override void OnComplete() + { + System.AddConversation( new ApprenticeCorpseConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_CorpseLocation = reader.ReadPoint3D(); + goto case 0; + } + case 0: + { + m_Corpse = (Corpse) reader.ReadItem(); + break; + } + } + + if ( version == 0 ) + m_CorpseLocation = RandomCorpseLocation(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + if ( m_Corpse != null && m_Corpse.Deleted ) + m_Corpse = null; + + writer.WriteEncodedInt( (int) 1 ); // version + + writer.Write( (Point3D) m_CorpseLocation ); + writer.Write( (Item) m_Corpse ); + } + } + + public class FindGrizeldaAboutMurderObjective : QuestObjective + { + public override object Message + { + get + { + /* Return to the Hag to tell her of the vile imp Zeefzorpul's role + * in the murder of her Apprentice, and the subsequent theft of a mysterious + * scrap of parchment from the corpse. + */ + return 1055015; + } + } + + public FindGrizeldaAboutMurderObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new MurderConversation() ); + } + } + + public class KillImpsObjective : QuestObjective + { + private int m_MaxProgress; + + public override object Message + { + get + { + /* Search the realm for any imps you can find, and slash, bash, mash, + * or fry them with magics until one of them gives up the secret hiding + * place of the imp Zeefzorpul. + */ + return 1055016; + } + } + + public override int MaxProgress{ get{ return m_MaxProgress; } } + + public KillImpsObjective( bool init ) + { + if ( init ) + m_MaxProgress = Utility.RandomMinMax( 1, 4 ); + } + + public KillImpsObjective() + { + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + if ( !Completed && from is Imp ) + return true; + + return false; + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + if ( creature is Imp ) + CurProgress++; + } + + public override void OnComplete() + { + PlayerMobile from = System.From; + + Point3D loc = WitchApprenticeQuest.RandomZeefzorpulLocation(); + + MapItem mapItem = new MapItem(); + mapItem.SetDisplay( loc.X - 200, loc.Y - 200, loc.X + 200, loc.Y + 200, 200, 200 ); + mapItem.AddWorldPin( loc.X, loc.Y ); + from.AddToBackpack( mapItem ); + + from.AddToBackpack( new MagicFlute() ); + + from.SendLocalizedMessage( 1055061 ); // You have received a map and a magic flute. + + System.AddConversation( new ImpDeathConversation( loc ) ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_MaxProgress = reader.ReadInt(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (int) m_MaxProgress ); + } + } + + public class FindZeefzorpulObjective : QuestObjective + { + private Point3D m_ImpLocation; + + public override object Message + { + get + { + /* Find the location shown in the map that the imp gave you. When you + * have arrived at the location, play the magic flute he provided, + * and the imp Zeefzorpul will be drawn to your presence. + */ + return 1055017; + } + } + + public Point3D ImpLocation{ get{ return m_ImpLocation; } } + + public FindZeefzorpulObjective( Point3D impLocation ) + { + m_ImpLocation = impLocation; + } + + public FindZeefzorpulObjective() + { + } + + public override void OnComplete() + { + Mobile from = System.From; + Map map = from.Map; + + Effects.SendLocationEffect( m_ImpLocation, map, 0x3728, 10, 10 ); + Effects.PlaySound( m_ImpLocation, map, 0x1FE ); + + Mobile imp = new Zeefzorpul(); + imp.MoveToWorld( m_ImpLocation, map ); + + imp.Direction = imp.GetDirectionTo( from ); + + Timer.DelayCall( TimeSpan.FromSeconds( 3.0 ), new TimerStateCallback( DeleteImp ), imp ); + } + + private void DeleteImp( object imp ) + { + Mobile m = imp as Mobile; + + if ( m != null && !m.Deleted ) + { + Effects.SendLocationEffect( m.Location, m.Map, 0x3728, 10, 10 ); + Effects.PlaySound( m.Location, m.Map, 0x1FE ); + + m.Delete(); + } + + System.From.SendLocalizedMessage( 1055062 ); // You have received the Magic Brew Recipe. + + System.AddConversation( new ZeefzorpulConversation() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_ImpLocation = reader.ReadPoint3D(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.Write( (Point3D) m_ImpLocation ); + } + } + + public class ReturnRecipeObjective : QuestObjective + { + public override object Message + { + get + { + /* Return to the old Hag and tell her you have recovered her Magic + * Brew Recipe from the bizarre imp named Zeefzorpul. + */ + return 1055018; + } + } + + public ReturnRecipeObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new RecipeConversation() ); + } + } + + public class FindIngredientObjective : QuestObjective + { + private Ingredient[] m_Ingredients; + private bool m_BlackheartMet; + + public override object Message + { + get + { + if ( !m_BlackheartMet ) + { + switch ( Step ) + { + case 1: + /* You must gather each ingredient on the Hag's list so that she can cook + * up her vile Magic Brew. The first ingredient is : + */ + return 1055019; + case 2: + /* You must gather each ingredient on the Hag's list so that she can cook + * up her vile Magic Brew. The second ingredient is : + */ + return 1055044; + default: + /* You must gather each ingredient on the Hag's list so that she can cook + * up her vile Magic Brew. The final ingredient is : + */ + return 1055045; + } + } + else + { + /* You are still attempting to obtain a jug of Captain Blackheart's + * Whiskey, but the drunkard Captain refuses to share his unique brew. + * You must prove your worthiness as a pirate to Blackheart before he'll + * offer you a jug. + */ + return 1055055; + } + } + } + + public override int MaxProgress + { + get + { + IngredientInfo info = IngredientInfo.Get( this.Ingredient ); + + return info.Quantity; + } + } + + public Ingredient[] Ingredients{ get{ return m_Ingredients; } } + public Ingredient Ingredient{ get{ return m_Ingredients[m_Ingredients.Length - 1]; } } + public int Step{ get{ return m_Ingredients.Length; } } + public bool BlackheartMet{ get{ return m_BlackheartMet; } } + + public FindIngredientObjective( Ingredient[] oldIngredients ) : this( oldIngredients, false ) + { + } + + public FindIngredientObjective( Ingredient[] oldIngredients, bool blackheartMet ) + { + if ( !blackheartMet ) + { + m_Ingredients = new Ingredient[oldIngredients.Length + 1]; + + for ( int i = 0; i < oldIngredients.Length; i++ ) + m_Ingredients[i] = oldIngredients[i]; + + m_Ingredients[m_Ingredients.Length - 1] = IngredientInfo.RandomIngredient( oldIngredients ); + } + else + { + m_Ingredients = new Ingredient[oldIngredients.Length]; + + for ( int i = 0; i < oldIngredients.Length; i++ ) + m_Ingredients[i] = oldIngredients[i]; + } + + m_BlackheartMet = blackheartMet; + } + + public FindIngredientObjective() + { + } + + public override void RenderProgress( BaseQuestGump gump ) + { + if ( !Completed ) + { + IngredientInfo info = IngredientInfo.Get( this.Ingredient ); + + gump.AddHtmlLocalized( 70, 260, 270, 100, info.Name, BaseQuestGump.Blue, false, false ); + gump.AddLabel( 70, 280, 0x64, CurProgress.ToString() ); + gump.AddLabel( 100, 280, 0x64, "/" ); + gump.AddLabel( 130, 280, 0x64, info.Quantity.ToString() ); + } + else + { + base.RenderProgress( gump ); + } + } + + public override bool IgnoreYoungProtection( Mobile from ) + { + if ( Completed ) + return false; + + IngredientInfo info = IngredientInfo.Get( this.Ingredient ); + Type fromType = from.GetType(); + + for ( int i = 0; i < info.Creatures.Length; i++ ) + { + if ( fromType == info.Creatures[i] ) + return true; + } + + return false; + } + + public override void OnKill( BaseCreature creature, Container corpse ) + { + IngredientInfo info = IngredientInfo.Get( this.Ingredient ); + + for ( int i = 0; i < info.Creatures.Length; i++ ) + { + Type type = info.Creatures[i]; + + if ( creature.GetType() == type ) + { + System.From.SendLocalizedMessage( 1055043, "#" + info.Name ); // You gather a ~1_INGREDIENT_NAME~ from the corpse. + + CurProgress++; + + break; + } + } + } + + public override void OnComplete() + { + if ( this.Ingredient != Ingredient.Whiskey ) + { + NextStep(); + } + } + + public void NextStep() + { + System.From.SendLocalizedMessage( 1055046 ); // You have completed your current task on the Hag's Magic Brew Recipe list. + + if ( Step < 3 ) + System.AddObjective( new FindIngredientObjective( m_Ingredients ) ); + else + System.AddObjective( new ReturnIngredientsObjective() ); + } + + public override void ChildDeserialize( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + m_Ingredients = new Ingredient[reader.ReadEncodedInt()]; + for ( int i = 0; i < m_Ingredients.Length; i++ ) + m_Ingredients[i] = (Ingredient) reader.ReadEncodedInt(); + + m_BlackheartMet = reader.ReadBool(); + } + + public override void ChildSerialize( GenericWriter writer ) + { + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Ingredients.Length ); + for ( int i = 0; i < m_Ingredients.Length; i++ ) + writer.WriteEncodedInt( (int) m_Ingredients[i] ); + + writer.Write( (bool) m_BlackheartMet ); + } + } + + public class ReturnIngredientsObjective : QuestObjective + { + public override object Message + { + get + { + /* You have gathered all the ingredients listed in the Hag's Magic Brew + * Recipe. Return to the Hag and tell her you have completed her task. + */ + return 1055050; + } + } + + public ReturnIngredientsObjective() + { + } + + public override void OnComplete() + { + System.AddConversation( new EndConversation() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Quests/Witch Apprentice/WitchApprenticeQuest.cs b/Scripts/Engines/Quests/Witch Apprentice/WitchApprenticeQuest.cs new file mode 100644 index 000000000..aa697be9f --- /dev/null +++ b/Scripts/Engines/Quests/Witch Apprentice/WitchApprenticeQuest.cs @@ -0,0 +1,141 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Engines.Quests.Hag +{ + public class WitchApprenticeQuest : QuestSystem + { + private static Type[] m_TypeReferenceTable = new Type[] + { + typeof( Hag.FindApprenticeObjective ), + typeof( Hag.FindGrizeldaAboutMurderObjective ), + typeof( Hag.KillImpsObjective ), + typeof( Hag.FindZeefzorpulObjective ), + typeof( Hag.ReturnRecipeObjective ), + typeof( Hag.FindIngredientObjective ), + typeof( Hag.ReturnIngredientsObjective ), + typeof( Hag.DontOfferConversation ), + typeof( Hag.AcceptConversation ), + typeof( Hag.HagDuringCorpseSearchConversation ), + typeof( Hag.ApprenticeCorpseConversation ), + typeof( Hag.MurderConversation ), + typeof( Hag.HagDuringImpSearchConversation ), + typeof( Hag.ImpDeathConversation ), + typeof( Hag.ZeefzorpulConversation ), + typeof( Hag.RecipeConversation ), + typeof( Hag.HagDuringIngredientsConversation ), + typeof( Hag.BlackheartFirstConversation ), + typeof( Hag.BlackheartNoPirateConversation ), + typeof( Hag.BlackheartPirateConversation ), + typeof( Hag.EndConversation ), + typeof( Hag.RecentlyFinishedConversation ) + }; + + public override Type[] TypeReferenceTable{ get{ return m_TypeReferenceTable; } } + + public override object Name + { + get + { + // "The Witch's Apprentice" + return 1055042; + } + } + + public override object OfferMessage + { + get + { + /* The ancient, wrinkled hag looks up from her vile-smelling cauldron. + * Her single, unblinking eye attempts to focus in on you, but to + * little avail.

+ * + * Eh? Who is it? Who's there? Come to trouble an old woman have you?

+ * + * I'll split ye open and swallow yer guts! I'll turn ye into a pile + * o' goo, I will! Bah! As if I didn't have enough to worry about. As if I've + * not enough trouble as it is!

+ * + * Another of my blasted apprentices has gone missing! Foolish children, + * think they know everything. I should turn the lot of them into toads - + * if only they'd return with their task complete! But that's the trouble, innit? + * They never return!

+ * + * But you don't care, do ye? I suppose you're another one of those meddlesome kids, + * come to ask me for something? Eh? Is that it? You want something from me, + * expect me to hand it over? I've enough troubles with my apprentices, and that + * vile imp, Zeefzorpul! Why, I bet it's him who's got the lot of them! And who + * knows what he's done? Vile little thing.

+ * + * If you expect me to help you with your silly little desires, you'll be doing + * something for me first, eh? I expect you to go seek out my apprentice. + * I sent him along the road west of here up towards Yew's graveyard, but he never + * came back. Find him, and bring him back, and I'll give you a little reward that + * I'm sure you'll find pleasant.

+ * + * But I tells ye to watch out for the imp name've Zeefzorpul! He's a despicable + * little beast who likes to fool and fiddle with folk and generally make life + * miserable for everyone. If ye get him on your bad side, you're sure to end up + * ruing the day ye were born. As if you didn't already, with an ugly mug + * like that!

+ * + * Well, you little whelp? Going to help an old hag or not? + */ + return 1055001; + } + } + + public override TimeSpan RestartDelay{ get{ return TimeSpan.FromMinutes( 5.0 ); } } + public override bool IsTutorial{ get{ return false; } } + + public override int Picture{ get{ return 0x15D3; } } + + public WitchApprenticeQuest( PlayerMobile from ) : base( from ) + { + } + + // Serialization + public WitchApprenticeQuest() + { + } + + public override void Accept() + { + base.Accept(); + + AddConversation( new AcceptConversation() ); + } + + private static Point3D[] m_ZeefzorpulLocations = new Point3D[] + { + new Point3D( 1226, 1573, 0 ), + new Point3D( 1929, 1148, 0 ), + new Point3D( 1366, 2723, 0 ), + new Point3D( 1675, 2984, 0 ), + new Point3D( 2177, 3367, 10 ), + new Point3D( 1171, 3594, 0 ), + new Point3D( 1010, 2667, 5 ), + new Point3D( 1591, 2156, 5 ), + new Point3D( 2592, 464, 60 ), + new Point3D( 474, 1654, 0 ), + new Point3D( 897, 2411, 0 ), + new Point3D( 1471, 2505, 5 ), + new Point3D( 1257, 872, 16 ), + new Point3D( 2581, 1118, 0 ), + new Point3D( 2513, 1102, 0 ), + new Point3D( 1608, 3371, 0 ), + new Point3D( 4687, 1179, 0 ), + new Point3D( 3704, 2196, 20 ), + new Point3D( 3346, 572, 0 ), + new Point3D( 569, 1309, 0 ) + }; + + public static Point3D RandomZeefzorpulLocation() + { + int index = Utility.Random( m_ZeefzorpulLocations.Length ); + + return m_ZeefzorpulLocations[index]; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/RemoteAdmin/Network.cs b/Scripts/Engines/RemoteAdmin/Network.cs new file mode 100644 index 000000000..ef08fa107 --- /dev/null +++ b/Scripts/Engines/RemoteAdmin/Network.cs @@ -0,0 +1,254 @@ +using System; +using Server; +using System.Text; +using System.Collections; +using Server.Accounting; +using Server.Network; + +namespace Server.Admin +{ + public class AdminNetwork + { + private static ArrayList m_Auth = new ArrayList(); + private static bool m_NewLine = true; + private static StringBuilder m_ConsoleData = new StringBuilder(); + + private const string DateFormat = "MMMM dd hh:mm:ss.f tt"; + + public static void Configure() + { + PacketHandlers.Register( 0xF1, 0, false, new OnPacketReceive( OnReceive ) ); + + Core.MultiConsoleOut.Add( new EventTextWriter( new EventTextWriter.OnConsoleChar( OnConsoleChar ), new EventTextWriter.OnConsoleLine( OnConsoleLine ), new EventTextWriter.OnConsoleStr( OnConsoleString ) ) ); + Timer.DelayCall( TimeSpan.FromMinutes( 2.5 ), TimeSpan.FromMinutes( 2.5 ), new TimerCallback( CleanUp ) ); + } + + public static void OnConsoleString( string str ) + { + string outStr; + if ( m_NewLine ) + { + outStr = String.Format( "[{0}]: {1}", DateTime.Now.ToString( DateFormat ), str ); + m_NewLine = false; + } + else + { + outStr = str; + } + + m_ConsoleData.Append( outStr ); + if ( m_ConsoleData.Length >= 4096 ) + m_ConsoleData.Remove( 0, 2048 ); + + for (int i=0;i= 4096 ) + m_ConsoleData.Remove( 0, 2048 ); + } + + public static void OnConsoleLine( string line ) + { + string outStr; + if ( m_NewLine ) + outStr = String.Format( "[{0}]: {1}{2}", DateTime.Now.ToString( DateFormat ), line, Console.Out.NewLine ); + else + outStr = String.Format( "{0}{1}", line, Console.Out.NewLine ); + + m_ConsoleData.Append( outStr ); + if ( m_ConsoleData.Length >= 4096 ) + m_ConsoleData.Remove( 0, 2048 ); + + for (int i=0;i 100 && length < 60000 ) + { + byte[] dest = new byte[(int)(length*1.001)+1]; + int destSize = dest.Length; + + ZLibError error = Compression.Pack( dest, ref destSize, source, length, ZLibQuality.Default ); + + if ( error != ZLibError.Okay ) + { + Console.WriteLine( "WARNING: Unable to compress admin packet, zlib error: {0}", error ); + return p; + } + else + { + return new AdminCompressedPacket( dest, destSize, length ); + } + } + else + { + return p; + } + } + } + + public class EventTextWriter : System.IO.TextWriter + { + public delegate void OnConsoleChar( char ch ); + public delegate void OnConsoleLine( string line ); + public delegate void OnConsoleStr( string str ); + + private OnConsoleChar m_OnChar; + private OnConsoleLine m_OnLine; + private OnConsoleStr m_OnStr; + + public EventTextWriter( OnConsoleChar onChar, OnConsoleLine onLine, OnConsoleStr onStr ) + { + m_OnChar = onChar; + m_OnLine = onLine; + m_OnStr = onStr; + } + + public override void Write( char ch ) + { + if ( m_OnChar != null ) + m_OnChar( ch ); + } + + public override void Write( string str ) + { + if ( m_OnStr != null ) + m_OnStr( str ); + } + + public override void WriteLine( string line ) + { + if ( m_OnLine != null ) + m_OnLine( line ); + } + + public override System.Text.Encoding Encoding{ get{ return System.Text.Encoding.ASCII; } } + } +} diff --git a/Scripts/Engines/RemoteAdmin/PacketHandlers.cs b/Scripts/Engines/RemoteAdmin/PacketHandlers.cs new file mode 100644 index 000000000..0e97bb68d --- /dev/null +++ b/Scripts/Engines/RemoteAdmin/PacketHandlers.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Accounting; +using Server.Items; +using Server.Mobiles; + +namespace Server.Admin +{ + public class RemoteAdminHandlers + { + public enum AcctSearchType : byte + { + Username = 0, + IP = 1, + } + + private static OnPacketReceive[] m_Handlers = new OnPacketReceive[256]; + + static RemoteAdminHandlers() + { + //0x02 = login request, handled by AdminNetwork + Register( 0x04, new OnPacketReceive( ServerInfoRequest ) ); + Register( 0x05, new OnPacketReceive( AccountSearch ) ); + Register( 0x06, new OnPacketReceive( RemoveAccount ) ); + Register( 0x07, new OnPacketReceive( UpdateAccount ) ); + } + + public static void Register( byte command, OnPacketReceive handler ) + { + m_Handlers[command] = handler; + } + + public static bool Handle( byte command, NetState state, PacketReader pvSrc ) + { + if ( m_Handlers[command] == null ) + { + Console.WriteLine( "ADMIN: Invalid packet 0x{0:X2} from {1}, disconnecting", command, state ); + return false; + } + else + { + m_Handlers[command]( state, pvSrc ); + return true; + } + } + + private static void ServerInfoRequest( NetState state, PacketReader pvSrc ) + { + state.Send( AdminNetwork.Compress( new ServerInfo() ) ); + } + + private static void AccountSearch( NetState state, PacketReader pvSrc ) + { + AcctSearchType type = (AcctSearchType)pvSrc.ReadByte(); + string term = pvSrc.ReadString(); + + if ( type == AcctSearchType.IP && !Utility.IsValidIP( term ) ) + { + state.Send( new MessageBoxMessage( "Invalid search term.\nThe IP sent was not valid.", "Invalid IP" ) ); + return; + } + else + { + term = term.ToUpper(); + } + + ArrayList list = new ArrayList(); + + foreach ( Account a in Accounts.GetAccounts() ) + { + switch ( type ) + { + case AcctSearchType.Username: + { + if ( a.Username.ToUpper().IndexOf( term ) != -1 ) + list.Add( a ); + break; + } + case AcctSearchType.IP: + { + for( int i=0;i 0 ) + { + if ( list.Count <= 25 ) + state.Send( AdminNetwork.Compress( new AccountSearchResults( list ) ) ); + else + state.Send( new MessageBoxMessage( "There were more than 25 matches to your search.\nNarrow the search parameters and try again.", "Too Many Results" ) ); + } + else + { + state.Send( new MessageBoxMessage( "There were no results to your search.\nPlease try again.", "No Matches" ) ); + } + } + + private static void RemoveAccount( NetState state, PacketReader pvSrc ) + { + IAccount a = Accounts.GetAccount( pvSrc.ReadString() ); + + if ( a == null ) + { + state.Send( new MessageBoxMessage( "The account could not be found (and thus was not deleted).", "Account Not Found" ) ); + } + else if ( a == state.Account ) + { + state.Send( new MessageBoxMessage( "You may not delete your own account.", "Not Allowed" ) ); + } + else + { + a.Delete(); + state.Send( new MessageBoxMessage( "The requested account (and all it's characters) has been deleted.", "Account Deleted" ) ); + } + } + + private static void UpdateAccount( NetState state, PacketReader pvSrc ) + { + string username = pvSrc.ReadString(); + string pass = pvSrc.ReadString(); + + Account a = Accounts.GetAccount( username ) as Account; + + if ( a == null ) + a = new Account( username, pass ); + else if ( pass != "(hidden)" ) + a.SetPassword( pass ); + + if ( a != state.Account ) + { + a.AccessLevel = (AccessLevel)pvSrc.ReadByte(); + a.Banned = pvSrc.ReadBoolean(); + } + else + { + pvSrc.ReadInt16();//skip both + state.Send( new MessageBoxMessage( "Warning: When editing your own account, account status and accesslevel cannot be changed.", "Editing Own Account" ) ); + } + + ArrayList list = new ArrayList(); + ushort length = pvSrc.ReadUInt16(); + bool invalid = false; + for (int i=0;i 0 ) + a.IPRestrictions = (string[])list.ToArray( typeof( string ) ); + else + a.IPRestrictions = new string[0]; + + if ( invalid ) + state.Send( new MessageBoxMessage( "Warning: one or more of the IP Restrictions you specified was not valid.", "Invalid IP Restriction" ) ); + state.Send( new MessageBoxMessage( "Account updated successfully.", "Account Updated" ) ); + } + } +} diff --git a/Scripts/Engines/RemoteAdmin/Packets.cs b/Scripts/Engines/RemoteAdmin/Packets.cs new file mode 100644 index 000000000..f9af4abbe --- /dev/null +++ b/Scripts/Engines/RemoteAdmin/Packets.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Network; +using Server.Accounting; +using Server.Commands; + +namespace Server.Admin +{ + public enum LoginResponse : byte + { + NoUser = 0, + BadIP, + BadPass, + NoAccess, + OK + } + + public sealed class AdminCompressedPacket : Packet + { + public AdminCompressedPacket( byte[] CompData, int CDLen, int unCompSize ) : base( 0x01 ) + { + EnsureCapacity( 1 + 2 + 2 + CDLen ); + m_Stream.Write( (ushort)unCompSize ); + m_Stream.Write( CompData, 0, CDLen ); + } + } + + public sealed class Login : Packet + { + public Login( LoginResponse resp ) : base( 0x02, 2 ) + { + m_Stream.Write( (byte)resp ); + } + } + + public sealed class ConsoleData : Packet + { + public ConsoleData( string str ) : base( 0x03 ) + { + EnsureCapacity( 1 + 2 + 1 + str.Length + 1 ); + m_Stream.Write( (byte) 2 ); + + m_Stream.WriteAsciiNull( str ); + } + + public ConsoleData( char ch ) : base( 0x03 ) + { + EnsureCapacity( 1 + 2 + 1 + 1 ); + m_Stream.Write( (byte) 3 ); + + m_Stream.Write( (byte) ch ); + } + } + + public sealed class ServerInfo : Packet + { + public ServerInfo() : base( 0x04 ) + { + string netVer = Environment.Version.ToString(); + string os = Environment.OSVersion.ToString(); + + EnsureCapacity( 1 + 2 + (10*4) + netVer.Length+1 + os.Length+1 ); + int banned = 0; + int active = 0; + + foreach ( Account acct in Accounts.GetAccounts() ) + { + if ( acct.Banned ) + ++banned; + else + ++active; + } + + m_Stream.Write( (int) active ); + m_Stream.Write( (int) banned ); + m_Stream.Write( (int) Firewall.List.Count ); + m_Stream.Write( (int) NetState.Instances.Count ); + + m_Stream.Write( (int) World.Mobiles.Count ); + m_Stream.Write( (int) Core.ScriptMobiles ); + m_Stream.Write( (int) World.Items.Count ); + m_Stream.Write( (int) Core.ScriptItems ); + + m_Stream.Write( (uint)(DateTime.Now - Clock.ServerStart).TotalSeconds ); + m_Stream.Write( (uint) GC.GetTotalMemory( false ) ); + m_Stream.WriteAsciiNull( netVer ); + m_Stream.WriteAsciiNull( os ); + } + } + + public sealed class AccountSearchResults : Packet + { + public AccountSearchResults( ArrayList results ) : base( 0x05 ) + { + EnsureCapacity( 1 + 2 + 2 ); + + m_Stream.Write( (byte)results.Count ); + + foreach ( Account a in results ) + { + m_Stream.WriteAsciiNull( a.Username ); + + string pwToSend = a.PlainPassword; + + if ( pwToSend == null ) + pwToSend = "(hidden)"; + + m_Stream.WriteAsciiNull( pwToSend ); + m_Stream.Write( (byte)a.AccessLevel ); + m_Stream.Write( a.Banned ); + unchecked { m_Stream.Write( (uint)a.LastLogin.Ticks ); } + + m_Stream.Write( (ushort)a.LoginIPs.Length ); + for (int i=0;i 12 ) + hours = realHours - 12; + else + hours = realHours; + + barGraph.Items.Add( hours + (realHours >= 12 ? " PM" : " AM"), val ); + } + + return barGraph; + } + + public static BarGraph Growth( SnapshotHistory history, string reportName, string valueName ) + { + BarGraph barGraph = new BarGraph( "Growth of " + valueName + " over time", "graphs_" + valueName.ToLower() + "_growth", 10, "Time", valueName, BarGraphRenderMode.Lines ); + + barGraph.FontSize = 6; + barGraph.Interval = 7; + + DateTime startPeriod = history.Snapshots[0].TimeStamp.Date + TimeSpan.FromDays( 1.0 ); + DateTime endPeriod = history.Snapshots[history.Snapshots.Count - 1].TimeStamp.Date; + + ArrayList regions = new ArrayList(); + + DateTime curDate = DateTime.MinValue; + int curPeak = -1; + int curLow = 1000; + int curTotl = 0; + int curCont = 0; + int curValu = 0; + + for ( int i = 0; i < history.Snapshots.Count; ++i ) + { + Snapshot ss = history.Snapshots[i]; + DateTime timeStamp = ss.TimeStamp; + + if ( timeStamp < startPeriod || timeStamp >= endPeriod ) + continue; + + int val = LookupReportValue( ss, reportName, valueName ); + + if ( val == -1 ) + continue; + + DateTime thisDate = timeStamp.Date; + + if ( curDate == DateTime.MinValue ) + curDate = thisDate; + + curCont++; + curTotl += val; + curValu = curTotl / curCont; + + if ( curDate != thisDate && curValu >= 0 ) + { + string mnthName = thisDate.ToString( "MMMM" ); + + if ( regions.Count == 0 ) + { + regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, mnthName ) ); + } + else + { + BarRegion region = (BarRegion)regions[regions.Count - 1]; + + if ( region.m_Name == mnthName ) + region.m_RangeTo = barGraph.Items.Count; + else + regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, mnthName ) ); + } + + barGraph.Items.Add( thisDate.Day.ToString(), curValu ); + + curPeak = val; + curLow = val; + } + else + { + if ( val > curPeak ) + curPeak = val; + + if ( val > 0 && val < curLow ) + curLow = val; + } + + curDate = thisDate; + } + + barGraph.Regions = (BarRegion[])regions.ToArray( typeof( BarRegion ) ); + + return barGraph; + } + + public static BarGraph OverTime( SnapshotHistory history, string reportName, string valueName, int step, int max, int ival ) + { + BarGraph barGraph = new BarGraph( valueName + " over time", "graphs_" + valueName.ToLower() + "_ot", 10, "Time", valueName, BarGraphRenderMode.Lines ); + + TimeSpan ts = TimeSpan.FromHours( (max*step)-0.5 ); + + DateTime mostRecent = history.Snapshots[history.Snapshots.Count - 1].TimeStamp; + DateTime minTime = mostRecent - ts; + + barGraph.FontSize = 6; + barGraph.Interval = ival; + + ArrayList regions = new ArrayList(); + + for ( int i = 0; i < history.Snapshots.Count; ++i ) + { + Snapshot ss = history.Snapshots[i]; + DateTime timeStamp = ss.TimeStamp; + + if ( timeStamp < minTime ) + continue; + + if ( (i % step) != 0 ) + continue; + + int val = LookupReportValue( ss, reportName, valueName ); + + if ( val == -1 ) + continue; + + int realHours = timeStamp.TimeOfDay.Hours; + int hours; + + if ( realHours == 0 ) + hours = 12; + else if ( realHours > 12 ) + hours = realHours - 12; + else + hours = realHours; + + string dayName = timeStamp.DayOfWeek.ToString(); + + if ( regions.Count == 0 ) + { + regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, dayName ) ); + } + else + { + BarRegion region = (BarRegion) regions[regions.Count - 1]; + + if ( region.m_Name == dayName ) + region.m_RangeTo = barGraph.Items.Count; + else + regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, dayName ) ); + } + + barGraph.Items.Add( hours + (realHours >= 12 ? " PM" : " AM"), val ); + } + + barGraph.Regions = (BarRegion[])regions.ToArray( typeof( BarRegion ) ); + + return barGraph; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Charts/Chart.cs b/Scripts/Engines/Reports/Objects/Charts/Chart.cs new file mode 100644 index 000000000..3e9983c95 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Charts/Chart.cs @@ -0,0 +1,44 @@ +using System; + +namespace Server.Engines.Reports +{ + public abstract class Chart : PersistableObject + { + protected string m_Name; + protected string m_FileName; + protected ChartItemCollection m_Items; + + public string Name{ get{ return m_Name; } set{ m_Name = value; } } + public string FileName{ get{ return m_FileName; } set{ m_FileName = value; } } + public ChartItemCollection Items{ get{ return m_Items; } } + + public Chart() + { + m_Items = new ChartItemCollection(); + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetString( "n", m_Name ); + op.SetString( "f", m_FileName ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_Name = Utility.Intern( ip.GetString( "n" ) ); + m_FileName = Utility.Intern( ip.GetString( "f" ) ); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + for ( int i = 0; i < m_Items.Count; ++i ) + m_Items[i].Serialize( op ); + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + while ( ip.HasChild ) + m_Items.Add( ip.GetChild() as ChartItem ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Charts/ChartItem.cs b/Scripts/Engines/Reports/Objects/Charts/ChartItem.cs new file mode 100644 index 000000000..5b5193f08 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Charts/ChartItem.cs @@ -0,0 +1,46 @@ +using System; + +namespace Server.Engines.Reports +{ + public class ChartItem : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "ci", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new ChartItem(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private string m_Name; + private int m_Value; + + public string Name{ get{ return m_Name; } set{ m_Name = value; } } + public int Value{ get{ return m_Value; } set{ m_Value = value; } } + + private ChartItem() + { + } + + public ChartItem( string name, int value ) + { + m_Name = name; + m_Value = value; + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetString( "n", m_Name ); + op.SetInt32( "v", m_Value ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_Name = Utility.Intern( ip.GetString( "n" ) ); + m_Value = ip.GetInt32( "v" ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Charts/ChartItemCollection.cs b/Scripts/Engines/Reports/Objects/Charts/ChartItemCollection.cs new file mode 100644 index 000000000..90dfb2612 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Charts/ChartItemCollection.cs @@ -0,0 +1,205 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.ChartItem. + /// + public class ChartItemCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public ChartItemCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.ChartItem at a specific position in the ChartItemCollection. + /// + public Server.Engines.Reports.ChartItem this[int index] + { + get + { + return ((Server.Engines.Reports.ChartItem)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + public int Add( string name, int value ) + { + return Add( new ChartItem( name, value ) ); + } + + /// + /// Append a Server.Engines.Reports.ChartItem entry to this collection. + /// + /// Server.Engines.Reports.ChartItem instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.ChartItem value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.ChartItem instance is in this collection. + /// + /// Server.Engines.Reports.ChartItem instance to search for. + /// True if the Server.Engines.Reports.ChartItem instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.ChartItem value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.ChartItem instance is in this collection. + /// + /// Server.Engines.Reports.ChartItem instance to find. + /// The zero-based index of the specified Server.Engines.Reports.ChartItem instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.ChartItem value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.ChartItem instance from this collection. + /// + /// The Server.Engines.Reports.ChartItem instance to remove. + public void Remove(Server.Engines.Reports.ChartItem value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.ChartItem instance. + /// + /// An Server.Engines.Reports.ChartItem's enumerator. + public new ChartItemCollectionEnumerator GetEnumerator() + { + return new ChartItemCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.ChartItem instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.ChartItem instance to insert. + public void Insert(int index, Server.Engines.Reports.ChartItem value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.ChartItem. + /// + public class ChartItemCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.ChartItem _currentElement; + + /// + /// Collection to enumerate. + /// + private ChartItemCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal ChartItemCollectionEnumerator(ChartItemCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.ChartItem object in the enumerated ChartItemCollection currently indexed by this instance. + /// + public Server.Engines.Reports.ChartItem Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Charts/PieChart.cs b/Scripts/Engines/Reports/Objects/Charts/PieChart.cs new file mode 100644 index 000000000..9a50e3649 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Charts/PieChart.cs @@ -0,0 +1,47 @@ +using System; + +namespace Server.Engines.Reports +{ + public class PieChart : Chart + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "pc", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new PieChart(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private bool m_ShowPercents; + + public bool ShowPercents{ get{ return m_ShowPercents; } set{ m_ShowPercents = value; } } + + public PieChart( string name, string fileName, bool showPercents ) + { + m_Name = name; + m_FileName = fileName; + m_ShowPercents = showPercents; + } + + private PieChart() + { + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + base.SerializeAttributes( op ); + + op.SetBoolean( "p", m_ShowPercents ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + base.DeserializeAttributes( ip ); + + m_ShowPercents = ip.GetBoolean( "p" ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Reports/ItemValue.cs b/Scripts/Engines/Reports/Objects/Reports/ItemValue.cs new file mode 100644 index 000000000..8bc1d8a82 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/ItemValue.cs @@ -0,0 +1,53 @@ +using System; + +namespace Server.Engines.Reports +{ + public class ItemValue : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "iv", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new ItemValue(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private string m_Value; + private string m_Format; + + public string Value{ get{ return m_Value; } set{ m_Value = value; } } + public string Format{ get{ return m_Format; } set{ m_Format = value; } } + + private ItemValue() + { + } + + public ItemValue( string value ) : this( value, null ) + { + } + + public ItemValue( string value, string format ) + { + m_Value = value; + m_Format = format; + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetString( "v", m_Value ); + op.SetString( "f", m_Format ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_Value = ip.GetString( "v" ); + m_Format = Utility.Intern( ip.GetString( "f" ) ); + + if ( m_Format == null ) + Utility.Intern( ref m_Value ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Reports/ItemValueCollection.cs b/Scripts/Engines/Reports/Objects/Reports/ItemValueCollection.cs new file mode 100644 index 000000000..9fdaea60a --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/ItemValueCollection.cs @@ -0,0 +1,210 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.ItemValue. + /// + public class ItemValueCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public ItemValueCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.ItemValue at a specific position in the ItemValueCollection. + /// + public Server.Engines.Reports.ItemValue this[int index] + { + get + { + return ((Server.Engines.Reports.ItemValue)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + public int Add( string value ) + { + return Add( new ItemValue( value ) ); + } + + public int Add( string value, string format ) + { + return Add( new ItemValue( value, format ) ); + } + + /// + /// Append a Server.Engines.Reports.ItemValue entry to this collection. + /// + /// Server.Engines.Reports.ItemValue instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.ItemValue value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.ItemValue instance is in this collection. + /// + /// Server.Engines.Reports.ItemValue instance to search for. + /// True if the Server.Engines.Reports.ItemValue instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.ItemValue value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.ItemValue instance is in this collection. + /// + /// Server.Engines.Reports.ItemValue instance to find. + /// The zero-based index of the specified Server.Engines.Reports.ItemValue instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.ItemValue value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.ItemValue instance from this collection. + /// + /// The Server.Engines.Reports.ItemValue instance to remove. + public void Remove(Server.Engines.Reports.ItemValue value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.ItemValue instance. + /// + /// An Server.Engines.Reports.ItemValue's enumerator. + public new ItemValueCollectionEnumerator GetEnumerator() + { + return new ItemValueCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.ItemValue instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.ItemValue instance to insert. + public void Insert(int index, Server.Engines.Reports.ItemValue value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.ItemValue. + /// + public class ItemValueCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.ItemValue _currentElement; + + /// + /// Collection to enumerate. + /// + private ItemValueCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal ItemValueCollectionEnumerator(ItemValueCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.ItemValue object in the enumerated ItemValueCollection currently indexed by this instance. + /// + public Server.Engines.Reports.ItemValue Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Reports/Report.cs b/Scripts/Engines/Reports/Objects/Reports/Report.cs new file mode 100644 index 000000000..972e30e15 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/Report.cs @@ -0,0 +1,74 @@ +using System; + +namespace Server.Engines.Reports +{ + public class Report : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "rp", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new Report(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private string m_Name; + private string m_Width; + private ReportColumnCollection m_Columns; + private ReportItemCollection m_Items; + + public string Name{ get{ return m_Name; } set{ m_Name = value; } } + public string Width{ get{ return m_Width; } set{ m_Width = value; } } + public ReportColumnCollection Columns{ get{ return m_Columns; } } + public ReportItemCollection Items{ get{ return m_Items; } } + + private Report() : this( null, null ) + { + } + + public Report( string name, string width ) + { + m_Name = name; + m_Width = width; + m_Columns = new ReportColumnCollection(); + m_Items = new ReportItemCollection(); + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetString( "n", m_Name ); + op.SetString( "w", m_Width ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_Name = Utility.Intern( ip.GetString( "n" ) ); + m_Width = Utility.Intern( ip.GetString( "w" ) ); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + for ( int i = 0; i < m_Columns.Count; ++i ) + m_Columns[i].Serialize( op ); + + for ( int i = 0; i < m_Items.Count; ++i ) + m_Items[i].Serialize( op ); + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + while ( ip.HasChild ) + { + PersistableObject child = ip.GetChild(); + + if ( child is ReportColumn ) + m_Columns.Add( (ReportColumn) child ); + else if ( child is ReportItem ) + m_Items.Add( (ReportItem) child ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Reports/ReportColumn.cs b/Scripts/Engines/Reports/Objects/Reports/ReportColumn.cs new file mode 100644 index 000000000..de054599c --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/ReportColumn.cs @@ -0,0 +1,55 @@ +using System; + +namespace Server.Engines.Reports +{ + public class ReportColumn : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "rc", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new ReportColumn(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private string m_Width; + private string m_Align; + private string m_Name; + + public string Width{ get{ return m_Width; } set{ m_Width = value; } } + public string Align{ get{ return m_Align; } set{ m_Align = value; } } + public string Name{ get{ return m_Name; } set{ m_Name = value; } } + + private ReportColumn() + { + } + + public ReportColumn( string width, string align ) : this( width, align, null ) + { + } + + public ReportColumn( string width, string align, string name ) + { + m_Width = width; + m_Align = align; + m_Name = name; + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetString( "w", m_Width ); + op.SetString( "a", m_Align ); + op.SetString( "n", m_Name ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_Width = Utility.Intern( ip.GetString( "w" ) ); + m_Align = Utility.Intern( ip.GetString( "a" ) ); + m_Name = Utility.Intern( ip.GetString( "n" ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Reports/ReportColumnCollection.cs b/Scripts/Engines/Reports/Objects/Reports/ReportColumnCollection.cs new file mode 100644 index 000000000..7a01fc1b9 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/ReportColumnCollection.cs @@ -0,0 +1,210 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.ReportColumn. + /// + public class ReportColumnCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public ReportColumnCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.ReportColumn at a specific position in the ReportColumnCollection. + /// + public Server.Engines.Reports.ReportColumn this[int index] + { + get + { + return ((Server.Engines.Reports.ReportColumn)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + public int Add( string width, string align ) + { + return Add( new ReportColumn( width, align ) ); + } + + public int Add( string width, string align, string name ) + { + return Add( new ReportColumn( width, align, name ) ); + } + + /// + /// Append a Server.Engines.Reports.ReportColumn entry to this collection. + /// + /// Server.Engines.Reports.ReportColumn instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.ReportColumn value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.ReportColumn instance is in this collection. + /// + /// Server.Engines.Reports.ReportColumn instance to search for. + /// True if the Server.Engines.Reports.ReportColumn instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.ReportColumn value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.ReportColumn instance is in this collection. + /// + /// Server.Engines.Reports.ReportColumn instance to find. + /// The zero-based index of the specified Server.Engines.Reports.ReportColumn instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.ReportColumn value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.ReportColumn instance from this collection. + /// + /// The Server.Engines.Reports.ReportColumn instance to remove. + public void Remove(Server.Engines.Reports.ReportColumn value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.ReportColumn instance. + /// + /// An Server.Engines.Reports.ReportColumn's enumerator. + public new ReportColumnCollectionEnumerator GetEnumerator() + { + return new ReportColumnCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.ReportColumn instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.ReportColumn instance to insert. + public void Insert(int index, Server.Engines.Reports.ReportColumn value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.ReportColumn. + /// + public class ReportColumnCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.ReportColumn _currentElement; + + /// + /// Collection to enumerate. + /// + private ReportColumnCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal ReportColumnCollectionEnumerator(ReportColumnCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.ReportColumn object in the enumerated ReportColumnCollection currently indexed by this instance. + /// + public Server.Engines.Reports.ReportColumn Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Reports/ReportItem.cs b/Scripts/Engines/Reports/Objects/Reports/ReportItem.cs new file mode 100644 index 000000000..09eabe55a --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/ReportItem.cs @@ -0,0 +1,39 @@ +using System; + +namespace Server.Engines.Reports +{ + public class ReportItem : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "ri", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new ReportItem(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private ItemValueCollection m_Values; + + public ItemValueCollection Values{ get{ return m_Values; } } + + public ReportItem() + { + m_Values = new ItemValueCollection(); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + for ( int i = 0; i < m_Values.Count; ++i ) + m_Values[i].Serialize( op ); + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + while ( ip.HasChild ) + m_Values.Add( ip.GetChild() as ItemValue ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Reports/ReportItemCollection.cs b/Scripts/Engines/Reports/Objects/Reports/ReportItemCollection.cs new file mode 100644 index 000000000..283c44829 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Reports/ReportItemCollection.cs @@ -0,0 +1,215 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.ReportItem. + /// + public class ReportItemCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public ReportItemCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.ReportItem at a specific position in the ReportItemCollection. + /// + public Server.Engines.Reports.ReportItem this[int index] + { + get + { + return ((Server.Engines.Reports.ReportItem)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + public int Add( string name, object value ) + { + return Add( name, value, null ); + } + + public int Add( string name, object value, string format ) + { + ReportItem item = new ReportItem(); + + item.Values.Add( name ); + item.Values.Add( value == null ? "" : value.ToString(), format ); + + return Add( item ); + } + + /// + /// Append a Server.Engines.Reports.ReportItem entry to this collection. + /// + /// Server.Engines.Reports.ReportItem instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.ReportItem value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.ReportItem instance is in this collection. + /// + /// Server.Engines.Reports.ReportItem instance to search for. + /// True if the Server.Engines.Reports.ReportItem instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.ReportItem value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.ReportItem instance is in this collection. + /// + /// Server.Engines.Reports.ReportItem instance to find. + /// The zero-based index of the specified Server.Engines.Reports.ReportItem instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.ReportItem value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.ReportItem instance from this collection. + /// + /// The Server.Engines.Reports.ReportItem instance to remove. + public void Remove(Server.Engines.Reports.ReportItem value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.ReportItem instance. + /// + /// An Server.Engines.Reports.ReportItem's enumerator. + public new ReportItemCollectionEnumerator GetEnumerator() + { + return new ReportItemCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.ReportItem instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.ReportItem instance to insert. + public void Insert(int index, Server.Engines.Reports.ReportItem value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.ReportItem. + /// + public class ReportItemCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.ReportItem _currentElement; + + /// + /// Collection to enumerate. + /// + private ReportItemCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal ReportItemCollectionEnumerator(ReportItemCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.ReportItem object in the enumerated ReportItemCollection currently indexed by this instance. + /// + public Server.Engines.Reports.ReportItem Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Snapshots/Snapshot.cs b/Scripts/Engines/Reports/Objects/Snapshots/Snapshot.cs new file mode 100644 index 000000000..10944f700 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Snapshots/Snapshot.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections; + +namespace Server.Engines.Reports +{ + public class Snapshot : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "ss", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new Snapshot(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private DateTime m_TimeStamp; + private ObjectCollection m_Children; + + public DateTime TimeStamp{ get{ return m_TimeStamp; } set{ m_TimeStamp = value; } } + public ObjectCollection Children{ get{ return m_Children; } set{ m_Children = value; } } + + public Snapshot() + { + m_Children = new ObjectCollection(); + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetDateTime( "t", m_TimeStamp ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_TimeStamp = ip.GetDateTime( "t" ); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + for ( int i = 0; i < m_Children.Count; ++i ) + m_Children[i].Serialize( op ); + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + while ( ip.HasChild ) + m_Children.Add( ip.GetChild() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Snapshots/SnapshotCollection.cs b/Scripts/Engines/Reports/Objects/Snapshots/SnapshotCollection.cs new file mode 100644 index 000000000..7ff7bc521 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Snapshots/SnapshotCollection.cs @@ -0,0 +1,200 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.Snapshot. + /// + public class SnapshotCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public SnapshotCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.Snapshot at a specific position in the SnapshotCollection. + /// + public Server.Engines.Reports.Snapshot this[int index] + { + get + { + return ((Server.Engines.Reports.Snapshot)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Engines.Reports.Snapshot entry to this collection. + /// + /// Server.Engines.Reports.Snapshot instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.Snapshot value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.Snapshot instance is in this collection. + /// + /// Server.Engines.Reports.Snapshot instance to search for. + /// True if the Server.Engines.Reports.Snapshot instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.Snapshot value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.Snapshot instance is in this collection. + /// + /// Server.Engines.Reports.Snapshot instance to find. + /// The zero-based index of the specified Server.Engines.Reports.Snapshot instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.Snapshot value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.Snapshot instance from this collection. + /// + /// The Server.Engines.Reports.Snapshot instance to remove. + public void Remove(Server.Engines.Reports.Snapshot value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.Snapshot instance. + /// + /// An Server.Engines.Reports.Snapshot's enumerator. + public new SnapshotCollectionEnumerator GetEnumerator() + { + return new SnapshotCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.Snapshot instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.Snapshot instance to insert. + public void Insert(int index, Server.Engines.Reports.Snapshot value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.Snapshot. + /// + public class SnapshotCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.Snapshot _currentElement; + + /// + /// Collection to enumerate. + /// + private SnapshotCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal SnapshotCollectionEnumerator(SnapshotCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.Snapshot object in the enumerated SnapshotCollection currently indexed by this instance. + /// + public Server.Engines.Reports.Snapshot Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Snapshots/SnapshotHistory.cs b/Scripts/Engines/Reports/Objects/Snapshots/SnapshotHistory.cs new file mode 100644 index 000000000..b1144d156 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Snapshots/SnapshotHistory.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using System.Xml; + +namespace Server.Engines.Reports +{ + public class SnapshotHistory : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "sh", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new SnapshotHistory(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private SnapshotCollection m_Snapshots; + + public SnapshotCollection Snapshots{ get{ return m_Snapshots; } set{ m_Snapshots = value; } } + + public SnapshotHistory() + { + m_Snapshots = new SnapshotCollection(); + } + + public void Save() + { + string path = Path.Combine( Core.BaseDirectory, "reportHistory.xml" ); + PersistanceWriter pw = new XmlPersistanceWriter( path, "Stats" ); + + pw.WriteDocument( this ); + + pw.Close(); + } + + public void Load() + { + string path = Path.Combine( Core.BaseDirectory, "reportHistory.xml" ); + + if ( !File.Exists( path ) ) + return; + + PersistanceReader pr = new XmlPersistanceReader( path, "Stats" ); + + pr.ReadDocument( this ); + + pr.Close(); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + for ( int i = 0; i < m_Snapshots.Count; ++i ) + m_Snapshots[i].Serialize( op ); + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + while ( ip.HasChild ) + m_Snapshots.Add( ip.GetChild() as Snapshot ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Staffing/Info.cs b/Scripts/Engines/Reports/Objects/Staffing/Info.cs new file mode 100644 index 000000000..a56cfa1d6 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/Info.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using Server; +using Server.Accounting; +using Server.Engines; +using Server.Engines.Help; + +namespace Server.Engines.Reports +{ + public abstract class BaseInfo : IComparable + { + private static TimeSpan m_SortRange; + + public static TimeSpan SortRange{ get{ return m_SortRange; } set{ m_SortRange = value; } } + + private string m_Account; + private string m_Display; + private PageInfoCollection m_Pages; + + public string Account{ get{ return m_Account; } set{ m_Account = value; } } + public PageInfoCollection Pages{ get{ return m_Pages; } set{ m_Pages = value; } } + + public string Display + { + get + { + if ( m_Display != null ) + return m_Display; + + if ( m_Account != null ) + { + IAccount acct = Accounts.GetAccount( m_Account ); + + if ( acct != null ) + { + Mobile mob = null; + + for ( int i = 0; i < acct.Length; ++i ) + { + Mobile check = acct[i]; + + if ( check != null && (mob == null || check.AccessLevel > mob.AccessLevel) ) + mob = check; + } + + if ( mob != null && mob.Name != null && mob.Name.Length > 0 ) + return ( m_Display = mob.Name ); + } + } + + return ( m_Display = m_Account ); + } + } + + public int GetPageCount( PageResolution res, DateTime min, DateTime max ) + { + return StaffHistory.GetPageCount( m_Pages, res, min, max ); + } + + public BaseInfo( string account ) + { + m_Account = account; + m_Pages = new PageInfoCollection(); + } + + public void Register( PageInfo page ) + { + m_Pages.Add( page ); + } + + public void Unregister( PageInfo page ) + { + m_Pages.Remove( page ); + } + + public int CompareTo( object obj ) + { + BaseInfo cmp = obj as BaseInfo; + + int v = cmp.GetPageCount( cmp is StaffInfo ? PageResolution.Handled : PageResolution.None, DateTime.Now - m_SortRange, DateTime.Now ) + - this.GetPageCount( this is StaffInfo ? PageResolution.Handled : PageResolution.None, DateTime.Now - m_SortRange, DateTime.Now ); + + if ( v == 0 ) + v = String.Compare( this.Display, cmp.Display ); + + return v; + } + } + + public class StaffInfo : BaseInfo + { + public StaffInfo( string account ) : base( account ) + { + } + } + + public class UserInfo : BaseInfo + { + public UserInfo( string account ) : base( account ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Staffing/PageInfo.cs b/Scripts/Engines/Reports/Objects/Staffing/PageInfo.cs new file mode 100644 index 000000000..734f43c69 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/PageInfo.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections; +using Server; +using Server.Engines; +using Server.Engines.Help; + +namespace Server.Engines.Reports +{ + public enum PageResolution + { + None, + Handled, + Deleted, + Logged, + Canceled + } + + public class PageInfo : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "pi", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new PageInfo(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private StaffHistory m_History; + private StaffInfo m_Resolver; + private UserInfo m_Sender; + + public StaffInfo Resolver + { + get{ return m_Resolver; } + set + { + if ( m_Resolver == value ) + return; + + lock ( StaffHistory.RenderLock ) + { + if ( m_Resolver != null ) + m_Resolver.Unregister( this ); + + m_Resolver = value; + + if ( m_Resolver != null ) + m_Resolver.Register( this ); + } + } + } + + public UserInfo Sender + { + get{ return m_Sender; } + set + { + if ( m_Sender == value ) + return; + + lock ( StaffHistory.RenderLock ) + { + if ( m_Sender != null ) + m_Sender.Unregister( this ); + + m_Sender = value; + + if ( m_Sender != null ) + m_Sender.Register( this ); + } + } + } + + private PageType m_PageType; + private PageResolution m_Resolution; + + private DateTime m_TimeSent; + private DateTime m_TimeResolved; + + private string m_SentBy; + private string m_ResolvedBy; + + private string m_Message; + private ResponseInfoCollection m_Responses; + + public StaffHistory History + { + get{ return m_History; } + set + { + if ( m_History == value ) + return; + + if ( m_History != null ) + { + Sender = null; + Resolver = null; + } + + m_History = value; + + if ( m_History != null ) + { + Sender = m_History.GetUserInfo( m_SentBy ); + UpdateResolver(); + } + } + } + + public PageType PageType{ get{ return m_PageType; } set{ m_PageType = value; } } + public PageResolution Resolution{ get{ return m_Resolution; } } + + public DateTime TimeSent{ get{ return m_TimeSent; } set{ m_TimeSent = value; } } + public DateTime TimeResolved{ get{ return m_TimeResolved; } } + + public string SentBy + { + get{ return m_SentBy; } + set + { + m_SentBy = value; + + if ( m_History != null ) + Sender = m_History.GetUserInfo( m_SentBy ); + } + } + + public string ResolvedBy + { + get{ return m_ResolvedBy; } + } + + public string Message{ get{ return m_Message; } set{ m_Message = value; } } + public ResponseInfoCollection Responses{ get{ return m_Responses; } set{ m_Responses = value; } } + + public void UpdateResolver() + { + string resolvedBy; + DateTime timeResolved; + PageResolution res = GetResolution( out resolvedBy, out timeResolved ); + + if ( m_History != null && IsStaffResolution( res ) ) + Resolver = m_History.GetStaffInfo( resolvedBy ); + else + Resolver = null; + + m_ResolvedBy = resolvedBy; + m_TimeResolved = timeResolved; + m_Resolution = res; + } + + public bool IsStaffResolution( PageResolution res ) + { + return ( res == PageResolution.Handled ); + } + + public static PageResolution ResFromResp( string resp ) + { + switch ( resp ) + { + case "[Handled]": return PageResolution.Handled; + case "[Deleting]": return PageResolution.Deleted; + case "[Logout]": return PageResolution.Logged; + case "[Canceled]": return PageResolution.Canceled; + } + + return PageResolution.None; + } + + public PageResolution GetResolution( out string resolvedBy, out DateTime timeResolved ) + { + for ( int i = m_Responses.Count - 1; i >= 0; --i ) + { + ResponseInfo resp = m_Responses[i]; + PageResolution res = ResFromResp( resp.Message ); + + if ( res != PageResolution.None ) + { + resolvedBy = resp.SentBy; + timeResolved = resp.TimeStamp; + return res; + } + } + + resolvedBy = m_SentBy; + timeResolved = m_TimeSent; + return PageResolution.None; + } + + public static string GetAccount( Mobile mob ) + { + if ( mob == null ) + return null; + + Accounting.Account acct = mob.Account as Accounting.Account; + + if ( acct == null ) + return null; + + return acct.Username; + } + + public PageInfo() + { + m_Responses = new ResponseInfoCollection(); + } + + public PageInfo( PageEntry entry ) + { + m_PageType = entry.Type; + + m_TimeSent = entry.Sent; + m_SentBy = GetAccount( entry.Sender ); + + m_Message = entry.Message; + m_Responses = new ResponseInfoCollection(); + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetInt32( "p", (int)m_PageType ); + + op.SetDateTime( "ts", m_TimeSent ); + op.SetString( "s", m_SentBy ); + + op.SetString( "m", m_Message ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_PageType = (PageType) ip.GetInt32( "p" ); + + m_TimeSent = ip.GetDateTime( "ts" ); + m_SentBy = ip.GetString( "s" ); + + m_Message = ip.GetString( "m" ); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + lock ( this ) + { + for ( int i = 0; i < m_Responses.Count; ++i ) + m_Responses[i].Serialize( op ); + } + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + while ( ip.HasChild ) + m_Responses.Add( ip.GetChild() as ResponseInfo ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Staffing/PageInfoCollection.cs b/Scripts/Engines/Reports/Objects/Staffing/PageInfoCollection.cs new file mode 100644 index 000000000..5bf6b23c5 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/PageInfoCollection.cs @@ -0,0 +1,200 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.PageInfo. + /// + public class PageInfoCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public PageInfoCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.PageInfo at a specific position in the PageInfoCollection. + /// + public Server.Engines.Reports.PageInfo this[int index] + { + get + { + return ((Server.Engines.Reports.PageInfo)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Engines.Reports.PageInfo entry to this collection. + /// + /// Server.Engines.Reports.PageInfo instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.PageInfo value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.PageInfo instance is in this collection. + /// + /// Server.Engines.Reports.PageInfo instance to search for. + /// True if the Server.Engines.Reports.PageInfo instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.PageInfo value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.PageInfo instance is in this collection. + /// + /// Server.Engines.Reports.PageInfo instance to find. + /// The zero-based index of the specified Server.Engines.Reports.PageInfo instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.PageInfo value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.PageInfo instance from this collection. + /// + /// The Server.Engines.Reports.PageInfo instance to remove. + public void Remove(Server.Engines.Reports.PageInfo value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.PageInfo instance. + /// + /// An Server.Engines.Reports.PageInfo's enumerator. + public new PageInfoCollectionEnumerator GetEnumerator() + { + return new PageInfoCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.PageInfo instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.PageInfo instance to insert. + public void Insert(int index, Server.Engines.Reports.PageInfo value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.PageInfo. + /// + public class PageInfoCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.PageInfo _currentElement; + + /// + /// Collection to enumerate. + /// + private PageInfoCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal PageInfoCollectionEnumerator(PageInfoCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.PageInfo object in the enumerated PageInfoCollection currently indexed by this instance. + /// + public Server.Engines.Reports.PageInfo Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Staffing/QueueStatus.cs b/Scripts/Engines/Reports/Objects/Staffing/QueueStatus.cs new file mode 100644 index 000000000..4c46ea7e6 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/QueueStatus.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server; +using Server.Engines; +using Server.Engines.Help; + +namespace Server.Engines.Reports +{ + public class QueueStatus : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "qs", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new QueueStatus(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private DateTime m_TimeStamp; + private int m_Count; + + public DateTime TimeStamp{ get{ return m_TimeStamp; } set{ m_TimeStamp = value; } } + public int Count{ get{ return m_Count; } set{ m_Count = value; } } + + public QueueStatus() + { + } + + public QueueStatus( int count ) + { + m_TimeStamp = DateTime.Now; + m_Count = count; + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetDateTime( "t", m_TimeStamp ); + op.SetInt32( "c", m_Count ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_TimeStamp = ip.GetDateTime( "t" ); + m_Count = ip.GetInt32( "c" ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Staffing/QueueStatusCollection.cs b/Scripts/Engines/Reports/Objects/Staffing/QueueStatusCollection.cs new file mode 100644 index 000000000..c960ffb75 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/QueueStatusCollection.cs @@ -0,0 +1,200 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.QueueStatus. + /// + public class QueueStatusCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public QueueStatusCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.QueueStatus at a specific position in the QueueStatusCollection. + /// + public Server.Engines.Reports.QueueStatus this[int index] + { + get + { + return ((Server.Engines.Reports.QueueStatus)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Engines.Reports.QueueStatus entry to this collection. + /// + /// Server.Engines.Reports.QueueStatus instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.QueueStatus value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.QueueStatus instance is in this collection. + /// + /// Server.Engines.Reports.QueueStatus instance to search for. + /// True if the Server.Engines.Reports.QueueStatus instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.QueueStatus value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.QueueStatus instance is in this collection. + /// + /// Server.Engines.Reports.QueueStatus instance to find. + /// The zero-based index of the specified Server.Engines.Reports.QueueStatus instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.QueueStatus value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.QueueStatus instance from this collection. + /// + /// The Server.Engines.Reports.QueueStatus instance to remove. + public void Remove(Server.Engines.Reports.QueueStatus value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.QueueStatus instance. + /// + /// An Server.Engines.Reports.QueueStatus's enumerator. + public new QueueStatusCollectionEnumerator GetEnumerator() + { + return new QueueStatusCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.QueueStatus instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.QueueStatus instance to insert. + public void Insert(int index, Server.Engines.Reports.QueueStatus value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.QueueStatus. + /// + public class QueueStatusCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.QueueStatus _currentElement; + + /// + /// Collection to enumerate. + /// + private QueueStatusCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal QueueStatusCollectionEnumerator(QueueStatusCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.QueueStatus object in the enumerated QueueStatusCollection currently indexed by this instance. + /// + public Server.Engines.Reports.QueueStatus Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Staffing/ResponseInfo.cs b/Scripts/Engines/Reports/Objects/Staffing/ResponseInfo.cs new file mode 100644 index 000000000..d56d50464 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/ResponseInfo.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using Server; +using Server.Engines; +using Server.Engines.Help; + +namespace Server.Engines.Reports +{ + public class ResponseInfo : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "rs", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new ResponseInfo(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private DateTime m_TimeStamp; + + private string m_SentBy; + private string m_Message; + + public DateTime TimeStamp{ get{ return m_TimeStamp; } set{ m_TimeStamp = value; } } + + public string SentBy{ get{ return m_SentBy; } set{ m_SentBy = value; } } + public string Message{ get{ return m_Message; } set{ m_Message = value; } } + + public ResponseInfo() + { + } + + public ResponseInfo( string sentBy, string message ) + { + m_TimeStamp = DateTime.Now; + m_SentBy = sentBy; + m_Message = message; + } + + public override void SerializeAttributes( PersistanceWriter op ) + { + op.SetDateTime( "t", m_TimeStamp ); + + op.SetString( "s", m_SentBy ); + op.SetString( "m", m_Message ); + } + + public override void DeserializeAttributes( PersistanceReader ip ) + { + m_TimeStamp = ip.GetDateTime( "t" ); + + m_SentBy = ip.GetString( "s" ); + m_Message = ip.GetString( "m" ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Objects/Staffing/ResponseInfoCollection.cs b/Scripts/Engines/Reports/Objects/Staffing/ResponseInfoCollection.cs new file mode 100644 index 000000000..d5227ca5c --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/ResponseInfoCollection.cs @@ -0,0 +1,205 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.ResponseInfo. + /// + public class ResponseInfoCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public ResponseInfoCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.ResponseInfo at a specific position in the ResponseInfoCollection. + /// + public Server.Engines.Reports.ResponseInfo this[int index] + { + get + { + return ((Server.Engines.Reports.ResponseInfo)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + public int Add( string sentBy, string message ) + { + return Add( new ResponseInfo( sentBy, message ) ); + } + + /// + /// Append a Server.Engines.Reports.ResponseInfo entry to this collection. + /// + /// Server.Engines.Reports.ResponseInfo instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.ResponseInfo value) + { + return this.List.Add(value); + } + + /// + /// Determines whether a specified Server.Engines.Reports.ResponseInfo instance is in this collection. + /// + /// Server.Engines.Reports.ResponseInfo instance to search for. + /// True if the Server.Engines.Reports.ResponseInfo instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.ResponseInfo value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.ResponseInfo instance is in this collection. + /// + /// Server.Engines.Reports.ResponseInfo instance to find. + /// The zero-based index of the specified Server.Engines.Reports.ResponseInfo instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.ResponseInfo value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.ResponseInfo instance from this collection. + /// + /// The Server.Engines.Reports.ResponseInfo instance to remove. + public void Remove(Server.Engines.Reports.ResponseInfo value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.ResponseInfo instance. + /// + /// An Server.Engines.Reports.ResponseInfo's enumerator. + public new ResponseInfoCollectionEnumerator GetEnumerator() + { + return new ResponseInfoCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.ResponseInfo instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.ResponseInfo instance to insert. + public void Insert(int index, Server.Engines.Reports.ResponseInfo value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.ResponseInfo. + /// + public class ResponseInfoCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.ResponseInfo _currentElement; + + /// + /// Collection to enumerate. + /// + private ResponseInfoCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal ResponseInfoCollectionEnumerator(ResponseInfoCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.ResponseInfo object in the enumerated ResponseInfoCollection currently indexed by this instance. + /// + public Server.Engines.Reports.ResponseInfo Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Objects/Staffing/StaffHistory.cs b/Scripts/Engines/Reports/Objects/Staffing/StaffHistory.cs new file mode 100644 index 000000000..c9e48d508 --- /dev/null +++ b/Scripts/Engines/Reports/Objects/Staffing/StaffHistory.cs @@ -0,0 +1,406 @@ +using System; +using System.IO; +using System.Xml; +using System.Collections; + +namespace Server.Engines.Reports +{ + public class StaffHistory : PersistableObject + { + #region Type Identification + public static readonly PersistableType ThisTypeID = new PersistableType( "stfhst", new ConstructCallback( Construct ) ); + + private static PersistableObject Construct() + { + return new StaffHistory(); + } + + public override PersistableType TypeID{ get{ return ThisTypeID; } } + #endregion + + private PageInfoCollection m_Pages; + private QueueStatusCollection m_QueueStats; + + private Hashtable m_UserInfo; + private Hashtable m_StaffInfo; + + public PageInfoCollection Pages{ get{ return m_Pages; } set{ m_Pages = value; } } + public QueueStatusCollection QueueStats{ get{ return m_QueueStats; } set{ m_QueueStats = value; } } + + public Hashtable UserInfo{ get{ return m_UserInfo; } set{ m_UserInfo = value; } } + public Hashtable StaffInfo{ get{ return m_StaffInfo; } set{ m_StaffInfo = value; } } + + public void AddPage( PageInfo info ) + { + lock ( SaveLock ) + m_Pages.Add( info ); + + info.History = this; + } + + public StaffHistory() + { + m_Pages = new PageInfoCollection(); + m_QueueStats = new QueueStatusCollection(); + + m_UserInfo = new Hashtable( StringComparer.OrdinalIgnoreCase ); + m_StaffInfo = new Hashtable( StringComparer.OrdinalIgnoreCase ); + } + + public StaffInfo GetStaffInfo( string account ) + { + lock ( RenderLock ) + { + if ( account == null || account.Length == 0 ) + return null; + + StaffInfo info = m_StaffInfo[account] as StaffInfo; + + if ( info == null ) + m_StaffInfo[account] = info = new StaffInfo( account ); + + return info; + } + } + + public UserInfo GetUserInfo( string account ) + { + if ( account == null || account.Length == 0 ) + return null; + + UserInfo info = m_UserInfo[account] as UserInfo; + + if ( info == null ) + m_UserInfo[account] = info = new UserInfo( account ); + + return info; + } + + public static readonly object RenderLock = new object(); + public static readonly object SaveLock = new object(); + + public void Save() + { + lock ( SaveLock ) + { + string path = Path.Combine( Core.BaseDirectory, "staffHistory.xml" ); + PersistanceWriter pw = new XmlPersistanceWriter( path, "Staff" ); + + pw.WriteDocument( this ); + + pw.Close(); + } + } + + public void Load() + { + string path = Path.Combine( Core.BaseDirectory, "staffHistory.xml" ); + + if ( !File.Exists( path ) ) + return; + + PersistanceReader pr = new XmlPersistanceReader( path, "Staff" ); + + pr.ReadDocument( this ); + + pr.Close(); + } + + public override void SerializeChildren( PersistanceWriter op ) + { + for ( int i = 0; i < m_Pages.Count; ++i ) + m_Pages[i].Serialize( op ); + + for ( int i = 0; i < m_QueueStats.Count; ++i ) + m_QueueStats[i].Serialize( op ); + } + + public override void DeserializeChildren( PersistanceReader ip ) + { + DateTime min = DateTime.Now - TimeSpan.FromDays( 8.0 ); + + while ( ip.HasChild ) + { + PersistableObject obj = ip.GetChild(); + + if ( obj is PageInfo ) + { + PageInfo pageInfo = obj as PageInfo; + + pageInfo.UpdateResolver(); + + if ( pageInfo.TimeSent >= min || pageInfo.TimeResolved >= min ) + { + m_Pages.Add( pageInfo ); + pageInfo.History = this; + } + else + { + pageInfo.Sender = null; + pageInfo.Resolver = null; + } + } + else if ( obj is QueueStatus ) + { + QueueStatus queueStatus = obj as QueueStatus; + + if ( queueStatus.TimeStamp >= min ) + m_QueueStats.Add( queueStatus ); + } + } + } + + public StaffInfo[] GetStaff() + { + StaffInfo[] staff = new StaffInfo[m_StaffInfo.Count]; + int index = 0; + + foreach ( StaffInfo staffInfo in m_StaffInfo.Values ) + staff[index++] = staffInfo; + + return staff; + } + + public void Render( ObjectCollection objects ) + { + lock ( RenderLock ) + { + objects.Add( GraphQueueStatus() ); + + StaffInfo[] staff = GetStaff(); + + BaseInfo.SortRange = TimeSpan.FromDays( 7.0 ); + Array.Sort( staff ); + + objects.Add( GraphHourlyPages( m_Pages, PageResolution.None, "New pages by hour", "graph_new_pages_hr" ) ); + objects.Add( GraphHourlyPages( m_Pages, PageResolution.Handled, "Handled pages by hour", "graph_handled_pages_hr" ) ); + objects.Add( GraphHourlyPages( m_Pages, PageResolution.Deleted, "Deleted pages by hour", "graph_deleted_pages_hr" ) ); + objects.Add( GraphHourlyPages( m_Pages, PageResolution.Canceled, "Canceled pages by hour", "graph_canceled_pages_hr" ) ); + objects.Add( GraphHourlyPages( m_Pages, PageResolution.Logged, "Logged-out pages by hour", "graph_logged_pages_hr" ) ); + + BaseInfo.SortRange = TimeSpan.FromDays( 1.0 ); + Array.Sort( staff ); + + objects.Add( ReportTotalPages( staff, TimeSpan.FromDays( 1.0 ), "1 Day" ) ); + objects.AddRange( (PersistableObject[])ChartTotalPages( staff, TimeSpan.FromDays( 1.0 ), "1 Day", "graph_daily_pages" ) ); + + BaseInfo.SortRange = TimeSpan.FromDays( 7.0 ); + Array.Sort( staff ); + + objects.Add( ReportTotalPages( staff, TimeSpan.FromDays( 7.0 ), "1 Week" ) ); + objects.AddRange( (PersistableObject[])ChartTotalPages( staff, TimeSpan.FromDays( 7.0 ), "1 Week", "graph_weekly_pages" ) ); + + BaseInfo.SortRange = TimeSpan.FromDays( 30.0 ); + Array.Sort( staff ); + + objects.Add( ReportTotalPages( staff, TimeSpan.FromDays( 30.0 ), "1 Month" ) ); + objects.AddRange( (PersistableObject[])ChartTotalPages( staff, TimeSpan.FromDays( 30.0 ), "1 Month", "graph_monthly_pages" ) ); + + for ( int i = 0; i < staff.Length; ++i ) + objects.Add( GraphHourlyPages( staff[i] ) ); + } + } + + public static int GetPageCount( StaffInfo staff, DateTime min, DateTime max ) + { + return GetPageCount( staff.Pages, PageResolution.Handled, min, max ); + } + + public static int GetPageCount( PageInfoCollection pages, PageResolution res, DateTime min, DateTime max ) + { + int count = 0; + + for ( int i = 0; i < pages.Count; ++i ) + { + if ( res != PageResolution.None && pages[i].Resolution != res ) + continue; + + DateTime ts = pages[i].TimeResolved; + + if ( ts >= min && ts < max ) + ++count; + } + + return count; + } + + private BarGraph GraphQueueStatus() + { + int[] totals = new int[24]; + int[] counts = new int[24]; + + DateTime max = DateTime.Now; + DateTime min = max - TimeSpan.FromDays( 7.0 ); + + for ( int i = 0; i < m_QueueStats.Count; ++i ) + { + DateTime ts = m_QueueStats[i].TimeStamp; + + if ( ts >= min && ts < max ) + { + DateTime date = ts.Date; + TimeSpan time = ts.TimeOfDay; + + int hour = time.Hours; + + totals[hour] += m_QueueStats[i].Count; + counts[hour]++; + } + } + + BarGraph barGraph = new BarGraph( "Average pages in queue", "graph_pagequeue_avg", 10, "Time", "Pages", BarGraphRenderMode.Lines ); + + barGraph.FontSize = 6; + + for ( int i = 7; i <= totals.Length+7; ++i ) + { + int val; + + if ( counts[i%totals.Length] == 0 ) + val = 0; + else + val = (totals[i%totals.Length] + (counts[i%totals.Length] / 2)) / counts[i%totals.Length]; + + int realHours = i%totals.Length; + int hours; + + if ( realHours == 0 ) + hours = 12; + else if ( realHours > 12 ) + hours = realHours - 12; + else + hours = realHours; + + barGraph.Items.Add( hours + (realHours >= 12 ? " PM" : " AM"), val ); + } + + return barGraph; + } + + private BarGraph GraphHourlyPages( StaffInfo staff ) + { + return GraphHourlyPages( staff.Pages, PageResolution.Handled, "Average pages handled by " + staff.Display, "graphs_" + staff.Account.ToLower() + "_avg" ); + } + + private BarGraph GraphHourlyPages( PageInfoCollection pages, PageResolution res, string title, string fname ) + { + int[] totals = new int[24]; + int[] counts = new int[24]; + + DateTime[] dates = new DateTime[24]; + + DateTime max = DateTime.Now; + DateTime min = max - TimeSpan.FromDays( 7.0 ); + + bool sentStamp = ( res == PageResolution.None ); + + for ( int i = 0; i < pages.Count; ++i ) + { + if ( res != PageResolution.None && pages[i].Resolution != res ) + continue; + + DateTime ts = ( sentStamp ? pages[i].TimeSent : pages[i].TimeResolved ); + + if ( ts >= min && ts < max ) + { + DateTime date = ts.Date; + TimeSpan time = ts.TimeOfDay; + + int hour = time.Hours; + + totals[hour]++; + + if ( dates[hour] != date ) + { + counts[hour]++; + dates[hour] = date; + } + } + } + + BarGraph barGraph = new BarGraph( title, fname, 10, "Time", "Pages", BarGraphRenderMode.Lines ); + + barGraph.FontSize = 6; + + for ( int i = 7; i <= totals.Length+7; ++i ) + { + int val; + + if ( counts[i%totals.Length] == 0 ) + val = 0; + else + val = (totals[i%totals.Length] + (counts[i%totals.Length] / 2)) / counts[i%totals.Length]; + + int realHours = i%totals.Length; + int hours; + + if ( realHours == 0 ) + hours = 12; + else if ( realHours > 12 ) + hours = realHours - 12; + else + hours = realHours; + + barGraph.Items.Add( hours + (realHours >= 12 ? " PM" : " AM"), val ); + } + + return barGraph; + } + + private Report ReportTotalPages( StaffInfo[] staff, TimeSpan ts, string title ) + { + DateTime max = DateTime.Now; + DateTime min = max - ts; + + Report report = new Report( title + " Staff Report", "400" ); + + report.Columns.Add( "65%", "left", "Staff Name" ); + report.Columns.Add( "35%", "center", "Page Count" ); + + for ( int i = 0; i < staff.Length; ++i ) + report.Items.Add( staff[i].Display, GetPageCount( staff[i], min, max ) ); + + return report; + } + + private PieChart[] ChartTotalPages( StaffInfo[] staff, TimeSpan ts, string title, string fname ) + { + DateTime max = DateTime.Now; + DateTime min = max - ts; + + PieChart staffChart = new PieChart( title + " Staff Chart", fname + "_staff", true ); + + int other = 0; + + for ( int i = 0; i < staff.Length; ++i ) + { + int count = GetPageCount( staff[i], min, max ); + + if ( i < 12 && count > 0 ) + staffChart.Items.Add( staff[i].Display, count ); + else + other += count; + } + + if ( other > 0 ) + staffChart.Items.Add( "Other", other ); + + PieChart resChart = new PieChart( title + " Resolutions", fname + "_resol", true ); + + int countTotal = GetPageCount( m_Pages, PageResolution.None, min, max ); + int countHandled = GetPageCount( m_Pages, PageResolution.Handled, min, max ); + int countDeleted = GetPageCount( m_Pages, PageResolution.Deleted, min, max ); + int countCanceled = GetPageCount( m_Pages, PageResolution.Canceled, min, max ); + int countLogged = GetPageCount( m_Pages, PageResolution.Logged, min, max ); + int countUnres = countTotal - ( countHandled + countDeleted + countCanceled + countLogged ); + + resChart.Items.Add( "Handled", countHandled ); + resChart.Items.Add( "Deleted", countDeleted ); + resChart.Items.Add( "Canceled", countCanceled ); + resChart.Items.Add( "Logged Out", countLogged ); + resChart.Items.Add( "Unresolved", countUnres ); + + return new PieChart[]{ staffChart, resChart }; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Persistance/PersistableObject.cs b/Scripts/Engines/Reports/Persistance/PersistableObject.cs new file mode 100644 index 000000000..b6c05ecb5 --- /dev/null +++ b/Scripts/Engines/Reports/Persistance/PersistableObject.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; + +namespace Server.Engines.Reports +{ + public abstract class PersistableObject + { + public abstract PersistableType TypeID{ get; } + + public virtual void SerializeAttributes( PersistanceWriter op ) + { + } + + public virtual void SerializeChildren( PersistanceWriter op ) + { + } + + public void Serialize( PersistanceWriter op ) + { + op.BeginObject( this.TypeID ); + SerializeAttributes( op ); + op.BeginChildren(); + SerializeChildren( op ); + op.FinishChildren(); + op.FinishObject(); + } + + public virtual void DeserializeAttributes( PersistanceReader ip ) + { + } + + public virtual void DeserializeChildren( PersistanceReader ip ) + { + } + + public void Deserialize( PersistanceReader ip ) + { + DeserializeAttributes( ip ); + + if ( ip.BeginChildren() ) + { + DeserializeChildren( ip ); + ip.FinishChildren(); + } + } + + public PersistableObject() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Persistance/PersistableObjectCollection.cs b/Scripts/Engines/Reports/Persistance/PersistableObjectCollection.cs new file mode 100644 index 000000000..5f13695c6 --- /dev/null +++ b/Scripts/Engines/Reports/Persistance/PersistableObjectCollection.cs @@ -0,0 +1,205 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 1.1.4322.573 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Server.Engines.Reports +{ + using System; + using System.Collections; + + + /// + /// Strongly typed collection of Server.Engines.Reports.PersistableObject. + /// + public class ObjectCollection : System.Collections.CollectionBase + { + + /// + /// Default constructor. + /// + public ObjectCollection() : + base() + { + } + + /// + /// Gets or sets the value of the Server.Engines.Reports.PersistableObject at a specific position in the ObjectCollection. + /// + public Server.Engines.Reports.PersistableObject this[int index] + { + get + { + return ((Server.Engines.Reports.PersistableObject)(this.List[index])); + } + set + { + this.List[index] = value; + } + } + + /// + /// Append a Server.Engines.Reports.PersistableObject entry to this collection. + /// + /// Server.Engines.Reports.PersistableObject instance. + /// The position into which the new element was inserted. + public int Add(Server.Engines.Reports.PersistableObject value) + { + return this.List.Add(value); + } + + public void AddRange( PersistableObject[] col ) + { + this.InnerList.AddRange( col ); + } + + /// + /// Determines whether a specified Server.Engines.Reports.PersistableObject instance is in this collection. + /// + /// Server.Engines.Reports.PersistableObject instance to search for. + /// True if the Server.Engines.Reports.PersistableObject instance is in the collection; otherwise false. + public bool Contains(Server.Engines.Reports.PersistableObject value) + { + return this.List.Contains(value); + } + + /// + /// Retrieve the index a specified Server.Engines.Reports.PersistableObject instance is in this collection. + /// + /// Server.Engines.Reports.PersistableObject instance to find. + /// The zero-based index of the specified Server.Engines.Reports.PersistableObject instance. If the object is not found, the return value is -1. + public int IndexOf(Server.Engines.Reports.PersistableObject value) + { + return this.List.IndexOf(value); + } + + /// + /// Removes a specified Server.Engines.Reports.PersistableObject instance from this collection. + /// + /// The Server.Engines.Reports.PersistableObject instance to remove. + public void Remove(Server.Engines.Reports.PersistableObject value) + { + this.List.Remove(value); + } + + /// + /// Returns an enumerator that can iterate through the Server.Engines.Reports.PersistableObject instance. + /// + /// An Server.Engines.Reports.PersistableObject's enumerator. + public new ObjectCollectionEnumerator GetEnumerator() + { + return new ObjectCollectionEnumerator(this); + } + + /// + /// Insert a Server.Engines.Reports.PersistableObject instance into this collection at a specified index. + /// + /// Zero-based index. + /// The Server.Engines.Reports.PersistableObject instance to insert. + public void Insert(int index, Server.Engines.Reports.PersistableObject value) + { + this.List.Insert(index, value); + } + + /// + /// Strongly typed enumerator of Server.Engines.Reports.PersistableObject. + /// + public class ObjectCollectionEnumerator : System.Collections.IEnumerator + { + + /// + /// Current index + /// + private int _index; + + /// + /// Current element pointed to. + /// + private Server.Engines.Reports.PersistableObject _currentElement; + + /// + /// Collection to enumerate. + /// + private ObjectCollection _collection; + + /// + /// Default constructor for enumerator. + /// + /// Instance of the collection to enumerate. + internal ObjectCollectionEnumerator(ObjectCollection collection) + { + _index = -1; + _collection = collection; + } + + /// + /// Gets the Server.Engines.Reports.PersistableObject object in the enumerated ObjectCollection currently indexed by this instance. + /// + public Server.Engines.Reports.PersistableObject Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Gets the current element in the collection. + /// + object IEnumerator.Current + { + get + { + if (((_index == -1) + || (_index >= _collection.Count))) + { + throw new System.IndexOutOfRangeException("Enumerator not started."); + } + else + { + return _currentElement; + } + } + } + + /// + /// Reset the cursor, so it points to the beginning of the enumerator. + /// + public void Reset() + { + _index = -1; + _currentElement = null; + } + + /// + /// Advances the enumerator to the next queue of the enumeration, if one is currently available. + /// + /// true, if the enumerator was succesfully advanced to the next queue; false, if the enumerator has reached the end of the enumeration. + public bool MoveNext() + { + if ((_index + < (_collection.Count - 1))) + { + _index = (_index + 1); + _currentElement = this._collection[_index]; + return true; + } + _index = _collection.Count; + return false; + } + } + } +} diff --git a/Scripts/Engines/Reports/Persistance/PersistableType.cs b/Scripts/Engines/Reports/Persistance/PersistableType.cs new file mode 100644 index 000000000..8ef17faa6 --- /dev/null +++ b/Scripts/Engines/Reports/Persistance/PersistableType.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; + +namespace Server.Engines.Reports +{ + public delegate PersistableObject ConstructCallback(); + + public sealed class PersistableTypeRegistry + { + private static Hashtable m_Table; + + public static PersistableType Find( string name ) + { + return m_Table[name] as PersistableType; + } + + public static void Register( PersistableType type ) + { + if ( type != null ) + m_Table[type.Name] = type; + } + + static PersistableTypeRegistry() + { + m_Table = new Hashtable( StringComparer.OrdinalIgnoreCase ); + + Register( Report.ThisTypeID ); + Register( BarGraph.ThisTypeID ); + Register( PieChart.ThisTypeID ); + Register( Snapshot.ThisTypeID ); + Register( ItemValue.ThisTypeID ); + Register( ChartItem.ThisTypeID ); + Register( ReportItem.ThisTypeID ); + Register( ReportColumn.ThisTypeID ); + Register( SnapshotHistory.ThisTypeID ); + + Register( PageInfo.ThisTypeID ); + Register( QueueStatus.ThisTypeID ); + Register( StaffHistory.ThisTypeID ); + Register( ResponseInfo.ThisTypeID ); + } + } + + public sealed class PersistableType + { + private string m_Name; + private ConstructCallback m_Constructor; + + public string Name{ get{ return m_Name; } } + public ConstructCallback Constructor{ get{ return m_Constructor; } } + + public PersistableType( string name, ConstructCallback constructor ) + { + m_Name = name; + m_Constructor = constructor; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Persistance/PersistanceReader.cs b/Scripts/Engines/Reports/Persistance/PersistanceReader.cs new file mode 100644 index 000000000..be3ba1a48 --- /dev/null +++ b/Scripts/Engines/Reports/Persistance/PersistanceReader.cs @@ -0,0 +1,122 @@ +using System; +using System.IO; +using System.Xml; + +namespace Server.Engines.Reports +{ + public abstract class PersistanceReader + { + public abstract int GetInt32( string key ); + public abstract bool GetBoolean( string key ); + public abstract string GetString( string key ); + public abstract DateTime GetDateTime( string key ); + + public abstract bool BeginChildren(); + public abstract void FinishChildren(); + public abstract bool HasChild{ get; } + public abstract PersistableObject GetChild(); + + public abstract void ReadDocument( PersistableObject root ); + public abstract void Close(); + + public PersistanceReader() + { + } + } + + public class XmlPersistanceReader : PersistanceReader + { + private StreamReader m_Reader; + private XmlTextReader m_Xml; + private string m_Title; + + public XmlPersistanceReader( string filePath, string title ) + { + m_Reader = new StreamReader( filePath ); + m_Xml = new XmlTextReader( m_Reader ); + m_Xml.WhitespaceHandling=WhitespaceHandling.None; + m_Title = title; + } + + public override int GetInt32( string key ) + { + return XmlConvert.ToInt32( m_Xml.GetAttribute( key ) ); + } + + public override bool GetBoolean( string key ) + { + return XmlConvert.ToBoolean( m_Xml.GetAttribute( key ) ); + } + + public override string GetString( string key ) + { + return m_Xml.GetAttribute( key ); + } + + public override DateTime GetDateTime( string key ) + { + string val = m_Xml.GetAttribute( key ); + + if ( val == null ) + return DateTime.MinValue; + + return XmlConvert.ToDateTime( val, XmlDateTimeSerializationMode.Local ); + } + + private bool m_HasChild; + + public override bool HasChild + { + get + { + return m_HasChild; + } + } + + private bool m_WasEmptyElement; + + public override bool BeginChildren() + { + m_HasChild = !m_WasEmptyElement; + + m_Xml.Read(); + + return m_HasChild; + } + + public override void FinishChildren() + { + m_Xml.Read(); + } + + public override PersistableObject GetChild() + { + PersistableType type = PersistableTypeRegistry.Find( m_Xml.Name ); + PersistableObject obj = type.Constructor(); + + m_WasEmptyElement = m_Xml.IsEmptyElement; + + obj.Deserialize( this ); + + m_HasChild = ( m_Xml.NodeType == XmlNodeType.Element ); + + return obj; + } + + public override void ReadDocument( PersistableObject root ) + { + Console.Write( "Reports: {0}: Loading...", m_Title ); + m_Xml.Read(); + m_Xml.Read(); + m_HasChild = !m_Xml.IsEmptyElement; + root.Deserialize( this ); + Console.WriteLine( "done" ); + } + + public override void Close() + { + m_Xml.Close(); + m_Reader.Close(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Persistance/PersistanceWriter.cs b/Scripts/Engines/Reports/Persistance/PersistanceWriter.cs new file mode 100644 index 000000000..441fe3d52 --- /dev/null +++ b/Scripts/Engines/Reports/Persistance/PersistanceWriter.cs @@ -0,0 +1,129 @@ +using System; +using System.IO; +using System.Xml; + +namespace Server.Engines.Reports +{ + public abstract class PersistanceWriter + { + public abstract void SetInt32( string key, int value ); + public abstract void SetBoolean( string key, bool value ); + public abstract void SetString( string key, string value ); + public abstract void SetDateTime( string key, DateTime value ); + + public abstract void BeginObject( PersistableType typeID ); + public abstract void BeginChildren(); + public abstract void FinishChildren(); + public abstract void FinishObject(); + + public abstract void WriteDocument( PersistableObject root ); + public abstract void Close(); + + public PersistanceWriter() + { + } + } + + public sealed class XmlPersistanceWriter : PersistanceWriter + { + private string m_RealFilePath; + private string m_TempFilePath; + + private StreamWriter m_Writer; + private XmlTextWriter m_Xml; + private string m_Title; + + public XmlPersistanceWriter( string filePath, string title ) + { + m_RealFilePath = filePath; + m_TempFilePath = Path.ChangeExtension( filePath, ".tmp" ); + + m_Writer = new StreamWriter( m_TempFilePath ); + m_Xml = new XmlTextWriter( m_Writer ); + + m_Title = title; + } + + public override void SetInt32( string key, int value ) + { + m_Xml.WriteAttributeString( key, XmlConvert.ToString( value ) ); + } + + public override void SetBoolean( string key, bool value ) + { + m_Xml.WriteAttributeString( key, XmlConvert.ToString( value ) ); + } + + public override void SetString( string key, string value ) + { + if ( value != null ) + m_Xml.WriteAttributeString( key, value ); + } + + public override void SetDateTime( string key, DateTime value ) + { + if ( value != DateTime.MinValue ) + m_Xml.WriteAttributeString( key, XmlConvert.ToString( value, XmlDateTimeSerializationMode.Local ) ); + } + + public override void BeginObject( PersistableType typeID ) + { + m_Xml.WriteStartElement( typeID.Name ); + } + + public override void BeginChildren() + { + } + + public override void FinishChildren() + { + } + + public override void FinishObject() + { + m_Xml.WriteEndElement(); + } + + public override void WriteDocument( PersistableObject root ) + { + Console.WriteLine( "Reports: {0}: Save started", m_Title ); + + m_Xml.Formatting = Formatting.Indented; + m_Xml.IndentChar = '\t'; + m_Xml.Indentation = 1; + + m_Xml.WriteStartDocument( true ); + + root.Serialize( this ); + + Console.WriteLine( "Reports: {0}: Save complete", m_Title ); + } + + public override void Close() + { + m_Xml.Close(); + m_Writer.Close(); + + try + { + string renamed = null; + + if ( File.Exists( m_RealFilePath ) ) + { + renamed = Path.ChangeExtension( m_RealFilePath, ".rem" ); + File.Move( m_RealFilePath, renamed ); + File.Move( m_TempFilePath, m_RealFilePath ); + File.Delete( renamed ); + } + else + { + File.Move( m_TempFilePath, m_RealFilePath ); + } + } + catch ( Exception ex ) + { + Console.WriteLine( ex ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Rendering/BarGraphRenderer.cs b/Scripts/Engines/Reports/Rendering/BarGraphRenderer.cs new file mode 100644 index 000000000..5d73584bf --- /dev/null +++ b/Scripts/Engines/Reports/Rendering/BarGraphRenderer.cs @@ -0,0 +1,923 @@ +using System; +using System.Drawing; +using System.Collections; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +namespace Server.Engines.Reports +{ + // Modified from MS sample + + //********************************************************************* + // + // BarGraph Class + // + // This class uses GDI+ to render Bar Chart. + // + //********************************************************************* + + public class BarRegion + { + public int m_RangeFrom, m_RangeTo; + public string m_Name; + + public BarRegion( int rangeFrom, int rangeTo, string name ) + { + m_RangeFrom = rangeFrom; + m_RangeTo = rangeTo; + m_Name = name; + } + } + + public class BarGraphRenderer : ChartRenderer + { + private const float _graphLegendSpacer = 15F; + private const float _labelFontSize = 7f; + private const int _legendFontSize = 9; + private const float _legendRectangleSize = 10F; + private const float _spacer = 5F; + + public BarRegion[] _regions; + + private BarGraphRenderMode _renderMode; + + // Overall related members + private Color _backColor; + private string _fontFamily; + private string _longestTickValue = string.Empty; // Used to calculate max value width + private float _maxTickValueWidth; // Used to calculate left offset of bar graph + private float _totalHeight; + private float _totalWidth; + + // Graph related members + private float _barWidth; + private float _bottomBuffer; // Space from bottom to x axis + private bool _displayBarData; + private Color _fontColor; + private float _graphHeight; + private float _graphWidth; + private float _maxValue = 0.0f; // = final tick value * tick count + private float _scaleFactor; // = _maxValue / _graphHeight + private float _spaceBtwBars; // For now same as _barWidth + private float _topBuffer; // Space from top to the top of y axis + private float _xOrigin; // x position where graph starts drawing + private float _yOrigin; // y position where graph starts drawing + private string _yLabel; + private int _yTickCount; + private float _yTickValue; // Value for each tick = _maxValue/_yTickCount + + // Legend related members + private bool _displayLegend; + private float _legendWidth; + private string _longestLabel = string.Empty; // Used to calculate legend width + private float _maxLabelWidth = 0.0f; + + public string FontFamily + { + get{ return _fontFamily; } + set{ _fontFamily = value; } + } + + public BarGraphRenderMode RenderMode + { + get{ return _renderMode; } + set{ _renderMode = value; } + } + + public Color BackgroundColor + { + set{ _backColor = value; } + } + + public int BottomBuffer + { + set { _bottomBuffer = Convert.ToSingle(value); } + } + + public Color FontColor + { + set{ _fontColor = value; } + } + + public int Height + { + get{ return Convert.ToInt32(_totalHeight); } + set{ _totalHeight = Convert.ToSingle(value); } + } + + public int Width + { + get{ return Convert.ToInt32(_totalWidth); } + set{ _totalWidth = Convert.ToSingle(value); } + } + + public bool ShowLegend + { + get{ return _displayLegend; } + set{ _displayLegend = value; } + } + + public bool ShowData + { + get{ return _displayBarData; } + set{ _displayBarData = value; } + } + public int TopBuffer + { + set { _topBuffer = Convert.ToSingle(value); } + } + + public string VerticalLabel + { + get{ return _yLabel; } + set{ _yLabel = value; } + } + + public int VerticalTickCount + { + get{ return _yTickCount; } + set{ _yTickCount = value; } + } + + private string _xTitle, _yTitle; + + public void SetTitles( string xTitle, string yTitle ) + { + _xTitle = xTitle; + _yTitle = yTitle; + } + + public BarGraphRenderer() + { + AssignDefaultSettings(); + } + + public BarGraphRenderer(Color bgColor) + { + AssignDefaultSettings(); + BackgroundColor = bgColor; + } + + //********************************************************************* + // + // This method collects all data points and calculate all the necessary dimensions + // to draw the bar graph. It is the method called before invoking the Draw() method. + // labels is the x values. + // values is the y values. + // + //********************************************************************* + + public void CollectDataPoints(string[] labels, string[] values) + { + if (labels.Length == values.Length) + { + for(int i=0; i _xOrigin+_graphWidth ) + rc.Width = _xOrigin+_graphWidth-rc.X; + + using ( SolidBrush brsh = new SolidBrush( Color.FromArgb( 48, GetColor( i ) ) ) ) + gfx.FillRectangle( brsh, rc ); + + rc.Offset( (rc.Width - 200.0f) * 0.5f, -16.0f ); + rc.Width = 200.0f; + rc.Height = 20.0f; + + gfx.DrawString( reg.m_Name, font, textBrush, rc, textFormat ); + } + } + } + } + } + } + } + + //********************************************************************* + // + // This method returns a bar graph bitmap to the calling function. It is called after + // all dimensions and data points are calculated. + // + //********************************************************************* + + public override Bitmap Draw() + { + int height = Convert.ToInt32(_totalHeight); + int width = Convert.ToInt32(_totalWidth); + + Bitmap bmp = new Bitmap(width, height); + + using(Graphics graph = Graphics.FromImage(bmp)) + { + graph.CompositingQuality = CompositingQuality.HighQuality; + graph.SmoothingMode = SmoothingMode.AntiAlias; + + using ( SolidBrush brsh = new SolidBrush( _backColor ) ) + graph.FillRectangle(brsh, -1, -1, bmp.Width+1, bmp.Height+1); + + DrawRegions(graph); + DrawVerticalLabelArea(graph); + DrawXLabelBack(graph); + DrawBars(graph); + DrawXLabelArea(graph); + + if (_displayLegend) + DrawLegend(graph); + } + + return bmp; + } + + //********************************************************************* + // + // This method draws all the bars for the graph. + // + //********************************************************************* + + public int _interval; + + private void DrawBars(Graphics graph) + { + SolidBrush brsFont = null; + Font valFont = null; + StringFormat sfFormat = null; + + try + { + brsFont = new SolidBrush(_fontColor); + valFont = new Font(_fontFamily, _labelFontSize); + sfFormat = new StringFormat(); + sfFormat.Alignment = StringAlignment.Center; + int i = 0; + + PointF[] linePoints = null; + + if ( _renderMode == BarGraphRenderMode.Lines ) + linePoints = new PointF[DataPoints.Count]; + + int pointIndex = 0; + + // Draw bars and the value above each bar + using ( Pen pen = new Pen(_fontColor,0.15f) ) + { + using ( SolidBrush whiteBrsh = new SolidBrush( Color.FromArgb( 128, Color.White ) ) ) + { + foreach(DataItem item in DataPoints) + { + using(SolidBrush barBrush = new SolidBrush(item.ItemColor)) + { + float itemY = _yOrigin + _graphHeight - item.SweepSize; + + if ( _renderMode == BarGraphRenderMode.Lines ) + { + linePoints[pointIndex++] = new PointF( _xOrigin + item.StartPos + (_barWidth / 2), itemY ); + } + else if ( _renderMode == BarGraphRenderMode.Bars ) + { + float ox = _xOrigin + item.StartPos; + float oy = itemY; + float ow = _barWidth; + float oh = item.SweepSize; + float of = 9.5f; + + PointF[] pts = new PointF[] + { + new PointF( ox, oy ), + new PointF( ox + ow, oy ), + new PointF( ox + of, oy + of ), + new PointF( ox + of + ow, oy + of ), + new PointF( ox, oy + oh ), + new PointF( ox + of, oy + of + oh ), + new PointF( ox + of + ow, oy + of + oh ) + }; + + graph.FillPolygon( barBrush, new PointF[]{ pts[2], pts[3], pts[6], pts[5] } ); + + using ( SolidBrush ltBrsh = new SolidBrush( System.Windows.Forms.ControlPaint.Light(item.ItemColor,0.1f) ) ) + graph.FillPolygon( ltBrsh, new PointF[]{ pts[0], pts[2], pts[5], pts[4] } ); + + using ( SolidBrush drkBrush = new SolidBrush( System.Windows.Forms.ControlPaint.Dark(item.ItemColor,0.05f) ) ) + graph.FillPolygon( drkBrush, new PointF[]{ pts[0], pts[1], pts[3], pts[2] } ); + + graph.DrawLine( pen, pts[0], pts[1] ); + graph.DrawLine( pen, pts[0], pts[2] ); + graph.DrawLine( pen, pts[1], pts[3] ); + graph.DrawLine( pen, pts[2], pts[3] ); + graph.DrawLine( pen, pts[2], pts[5] ); + graph.DrawLine( pen, pts[0], pts[4] ); + graph.DrawLine( pen, pts[4], pts[5] ); + graph.DrawLine( pen, pts[5], pts[6] ); + graph.DrawLine( pen, pts[3], pts[6] ); + + // Draw data value + if (_displayBarData&&(i%_interval)==0) + { + float sectionWidth = ( _barWidth + _spaceBtwBars ); + float startX = _xOrigin + (i * sectionWidth) + (sectionWidth / 2); // This draws the value on center of the bar + float startY = itemY - 2f - valFont.Height; // Positioned on top of each bar by 2 pixels + RectangleF recVal = new RectangleF(startX-((sectionWidth * _interval) / 2), startY, sectionWidth * _interval, valFont.Height); + SizeF sz = graph.MeasureString(item.Value.ToString("#,###.##"),valFont,recVal.Size,sfFormat); + //using ( SolidBrush brsh = new SolidBrush( Color.FromArgb( 180, 255, 255, 255 ) ) ) + // graph.FillRectangle( brsh, new RectangleF(recVal.X+((recVal.Width-sz.Width)/2),recVal.Y+((recVal.Height-sz.Height)/2),sz.Width+4,sz.Height) ); + + //graph.DrawString(item.Value.ToString("#,###.##"), valFont, brsFont, recVal, sfFormat); + + for ( int box = -1; box <= 1; ++box ) + { + for ( int boy = -1; boy <= 1; ++boy ) + { + if ( box == 0 && boy == 0 ) + continue; + + RectangleF rco = new RectangleF( recVal.X+box, recVal.Y+boy, recVal.Width, recVal.Height ); + graph.DrawString(item.Value.ToString("#,###.##"), valFont, whiteBrsh, rco, sfFormat); + } + } + + graph.DrawString(item.Value.ToString("#,###.##"), valFont, brsFont, recVal, sfFormat); + } + } + + i++; + } + } + + if ( _renderMode == BarGraphRenderMode.Lines ) + { + if ( linePoints.Length >= 2 ) + { + using ( Pen linePen = new Pen( Color.FromArgb(220,Color.Red), 2.5f ) ) + graph.DrawCurve( linePen, linePoints, 0.5f ); + } + + using ( Pen linePen = new Pen( Color.FromArgb(40,_fontColor), 0.8f ) ) + { + for ( int j = 0; j < linePoints.Length; ++j ) + { + graph.DrawLine( linePen, linePoints[j], new PointF( linePoints[j].X, _yOrigin + _graphHeight ) ); + + DataItem item = DataPoints[j]; + float itemY = _yOrigin + _graphHeight - item.SweepSize; + + // Draw data value + if (_displayBarData&&(j%_interval)==0) + { + graph.FillEllipse( brsFont, new RectangleF( linePoints[j].X-2.0f, linePoints[j].Y-2.0f, 4.0f, 4.0f ) ); + + float sectionWidth = ( _barWidth + _spaceBtwBars ); + float startX = _xOrigin + (j * sectionWidth) + (sectionWidth/2); // This draws the value on center of the bar + float startY = itemY - 2f - valFont.Height; // Positioned on top of each bar by 2 pixels + RectangleF recVal = new RectangleF(startX-((sectionWidth * _interval) / 2), startY, sectionWidth * _interval, valFont.Height); + SizeF sz = graph.MeasureString(item.Value.ToString("#,###.##"),valFont,recVal.Size,sfFormat); + //using ( SolidBrush brsh = new SolidBrush( Color.FromArgb( 48, 255, 255, 255 ) ) ) + // graph.FillRectangle( brsh, new RectangleF(recVal.X+((recVal.Width-sz.Width)/2),recVal.Y+((recVal.Height-sz.Height)/2),sz.Width+4,sz.Height) ); + + for ( int box = -1; box <= 1; ++box ) + { + for ( int boy = -1; boy <= 1; ++boy ) + { + if ( box == 0 && boy == 0 ) + continue; + + RectangleF rco = new RectangleF( recVal.X+box, recVal.Y+boy, recVal.Width, recVal.Height ); + graph.DrawString(item.Value.ToString("#,###.##"), valFont, whiteBrsh, rco, sfFormat); + } + } + + graph.DrawString(item.Value.ToString("#,###.##"), valFont, brsFont, recVal, sfFormat); + } + } + } + } + } + } + } + finally + { + if (brsFont != null) brsFont.Dispose(); + if (valFont != null) valFont.Dispose(); + if (sfFormat != null) sfFormat.Dispose(); + } + } + + //********************************************************************* + // + // This method draws the y label, tick marks, tick values, and the y axis. + // + //********************************************************************* + + private void DrawVerticalLabelArea(Graphics graph) + { + Font lblFont = null; + SolidBrush brs = null; + StringFormat lblFormat = null; + Pen pen = null; + StringFormat sfVLabel = null; + + float fo = (_yTitle==null?0.0f:20.0f); + + try + { + brs = new SolidBrush(_fontColor); + lblFormat = new StringFormat(); + pen = new Pen(_fontColor); + + if ( _yTitle != null ) + { + sfVLabel = new StringFormat(); + sfVLabel.Alignment = StringAlignment.Center; + sfVLabel.LineAlignment = StringAlignment.Center; + sfVLabel.FormatFlags=StringFormatFlags.DirectionVertical; + + lblFont = new Font(_fontFamily, _labelFontSize+4.0f); + graph.DrawString(_yTitle,lblFont,brs,new RectangleF( 0.0f, _yOrigin, 20.0f, _graphHeight ), sfVLabel ); + lblFont.Dispose(); + } + + sfVLabel = new StringFormat(); + lblFormat.Alignment = StringAlignment.Far; + lblFormat.FormatFlags|=StringFormatFlags.NoClip; + + // Draw vertical label at the top of y-axis and place it in the middle top of y-axis + lblFont = new Font(_fontFamily, _labelFontSize+2.0f,FontStyle.Bold); + RectangleF recVLabel = new RectangleF(0, _yOrigin-2*_spacer-lblFont.Height, _xOrigin*2, lblFont.Height); + sfVLabel.Alignment = StringAlignment.Center; + sfVLabel.FormatFlags|=StringFormatFlags.NoClip; + //graph.DrawRectangle(Pens.Black,Rectangle.Truncate(recVLabel)); + graph.DrawString(_yLabel, lblFont, brs, recVLabel, sfVLabel); + lblFont.Dispose(); + + lblFont = new Font(_fontFamily, _labelFontSize); + // Draw all tick values and tick marks + using ( Pen smallPen = new Pen(Color.FromArgb(96,_fontColor),0.8f) ) + { + for (int i=0; i<_yTickCount; i++) + { + float currentY = _topBuffer + (i * _yTickValue/_scaleFactor); // Position for tick mark + float labelY = currentY-lblFont.Height/2; // Place label in the middle of tick + RectangleF lblRec = new RectangleF(_spacer+fo-6, labelY, _maxTickValueWidth, lblFont.Height); + + float currentTick = _maxValue - i*_yTickValue; // Calculate tick value from top to bottom + graph.DrawString(currentTick.ToString("#,###.##"), lblFont, brs, lblRec, lblFormat); // Draw tick value + graph.DrawLine(pen, _xOrigin, currentY, _xOrigin - 4.0f, currentY); // Draw tick mark + + graph.DrawLine(smallPen, _xOrigin, currentY, _xOrigin + _graphWidth, currentY ); + } + } + + // Draw y axis + graph.DrawLine(pen, _xOrigin, _yOrigin, _xOrigin, _yOrigin + _graphHeight); + } + finally + { + if (lblFont != null) lblFont.Dispose(); + if (brs != null) brs.Dispose(); + if (lblFormat != null) lblFormat.Dispose(); + if (pen != null) pen.Dispose(); + if (sfVLabel != null) sfVLabel.Dispose(); + } + } + + //********************************************************************* + // + // This method draws x axis and all x labels + // + //********************************************************************* + + private void DrawXLabelBack(Graphics graph) + { + Font lblFont = null; + SolidBrush brs = null; + StringFormat lblFormat = null; + Pen pen = null; + + try + { + lblFont = new Font(_fontFamily, _labelFontSize); + brs = new SolidBrush(_fontColor); + lblFormat = new StringFormat(); + pen = new Pen(_fontColor); + + lblFormat.Alignment = StringAlignment.Center; + + // Draw x axis + graph.DrawLine(pen, _xOrigin, _yOrigin + _graphHeight, _xOrigin + _graphWidth, _yOrigin + _graphHeight ); + } + finally + { + if (lblFont != null) lblFont.Dispose(); + if (brs != null) brs.Dispose(); + if (lblFormat != null) lblFormat.Dispose(); + if (pen != null) pen.Dispose(); + } + } + + private void DrawXLabelArea(Graphics graph) + { + Font lblFont = null; + SolidBrush brs = null; + StringFormat lblFormat = null; + Pen pen = null; + + try + { + brs = new SolidBrush(_fontColor); + pen = new Pen(_fontColor); + + if ( _xTitle != null ) + { + lblFormat = new StringFormat(); + lblFormat.Alignment = StringAlignment.Center; + lblFormat.LineAlignment = StringAlignment.Center; + // sfVLabel.FormatFlags=StringFormatFlags.DirectionVertical; + + lblFont = new Font(_fontFamily, _labelFontSize+2.0f, FontStyle.Bold); + graph.DrawString(_xTitle,lblFont,brs,new RectangleF( _xOrigin, _yOrigin+_graphHeight+14.0f+(_renderMode==BarGraphRenderMode.Bars?10.0f:0.0f)+((DataPoints.Count/_interval)>24?16.0f:0.0f), _graphWidth, 20.0f ), lblFormat ); + } + + lblFont = new Font(_fontFamily, _labelFontSize); + lblFormat = new StringFormat(); + lblFormat.Alignment = StringAlignment.Center; + lblFormat.FormatFlags |= StringFormatFlags.NoClip; + lblFormat.Trimming = StringTrimming.None; + //lblFormat.FormatFlags |= StringFormatFlags.NoWrap; + + float of = 0.0f; + + if ( _renderMode == BarGraphRenderMode.Bars ) + { + of = 10.0f; + + // Draw x axis + graph.DrawLine( pen, _xOrigin + of, _yOrigin + _graphHeight + of, _xOrigin + _graphWidth + of, _yOrigin + _graphHeight + of ); + + graph.DrawLine( pen, _xOrigin, _yOrigin + _graphHeight, _xOrigin + of, _yOrigin + _graphHeight + of ); + graph.DrawLine( pen, _xOrigin + _graphWidth, _yOrigin + _graphHeight, _xOrigin + of + _graphWidth, _yOrigin + _graphHeight + of ); + } + + float currentX; + float currentY = _yOrigin + _graphHeight + 2.0f; // All x labels are drawn 2 pixels below x-axis + float labelWidth = _barWidth + _spaceBtwBars; // Fits exactly below the bar + int i = 0; + + // Draw x labels + foreach(DataItem item in DataPoints) + { + if((i%_interval)==0) + { + currentX = _xOrigin + (i * labelWidth)+of+(labelWidth/2); + RectangleF recLbl = new RectangleF(currentX-((labelWidth*_interval)/2), currentY+of, labelWidth*_interval, lblFont.Height*2); + string lblString = _displayLegend ? item.Label : item.Description; // Decide what to show: short or long + + graph.DrawString(lblString, lblFont, brs, recLbl, lblFormat); + } + i++; + } + } + finally + { + if (lblFont != null) lblFont.Dispose(); + if (brs != null) brs.Dispose(); + if (lblFormat != null) lblFormat.Dispose(); + if (pen != null) pen.Dispose(); + } + } + + //********************************************************************* + // + // This method determines where to place the legend box. + // It draws the legend border, legend description, and legend color code. + // + //********************************************************************* + + private void DrawLegend(Graphics graph) + { + Font lblFont = null; + SolidBrush brs = null; + StringFormat lblFormat = null; + Pen pen = null; + + try + { + lblFont = new Font(_fontFamily, _legendFontSize); + brs = new SolidBrush(_fontColor); + lblFormat = new StringFormat(); + pen = new Pen(_fontColor); + lblFormat.Alignment = StringAlignment.Near; + + // Calculate Legend drawing start point + float startX = _xOrigin + _graphWidth + _graphLegendSpacer; + float startY = _yOrigin; + + float xColorCode = startX + _spacer; + float xLegendText = xColorCode + _legendRectangleSize + _spacer; + float legendHeight = 0.0f; + for(int i=0; i _maxTickValueWidth ) + _maxTickValueWidth = measured; + } + + float leftOffset = _spacer + _maxTickValueWidth + (_yTitle == null ? 0.0f : 20.0f); + float rtOffset = 0.0f; + + if (_displayLegend) + { + _legendWidth = _spacer + _legendRectangleSize + _spacer + _maxLabelWidth + _spacer; + rtOffset = _graphLegendSpacer + _legendWidth + _spacer; + } + else + rtOffset = _spacer; // Make graph in the middle + + if ( _renderMode == BarGraphRenderMode.Bars ) + rtOffset += 10.0f; + + rtOffset+=10.0f; + + _graphHeight = _totalHeight - _topBuffer - _bottomBuffer - (_xTitle == null ? 0.0f : 20.0f); // Buffer spaces are used to print labels + _graphWidth = _totalWidth - leftOffset - rtOffset; + _xOrigin = leftOffset; + _yOrigin = _topBuffer; + + // Once the correct _maxValue is determined, then calculate _scaleFactor + _scaleFactor = _maxValue / _graphHeight; + } + + //********************************************************************* + // + // This method determines the longest tick value from the given data points. + // The result is needed to calculate the correct graph dimension. + // + //********************************************************************* + + private void FindLongestTickValue() + { + float currentTick; + string tickString; + for (int i=0; i<_yTickCount; i++) + { + currentTick = _maxValue - i*_yTickValue; + tickString = currentTick.ToString("#,###.##"); + if (_longestTickValue.Length < tickString.Length) + _longestTickValue = tickString; + } + } + + //********************************************************************* + // + // This method calculates the image width in pixel for a given text + // + //********************************************************************* + + private float CalculateImgFontWidth(string text, float size, string family) + { + Bitmap bmp = null; + Graphics graph = null; + Font font = null; + + try + { + font = new Font(family, size); + + // Calculate the size of the string. + bmp = new Bitmap(1,1,PixelFormat.Format32bppArgb); + graph = Graphics.FromImage(bmp); + SizeF oSize = graph.MeasureString(text, font); + oSize.Width=4+(float)Math.Ceiling(oSize.Width); + + return oSize.Width; + } + finally + { + if (graph != null) graph.Dispose(); + if (bmp != null) bmp.Dispose(); + if (font != null) font.Dispose(); + } + } + + //********************************************************************* + // + // This method creates abbreviation from long description; used for making legend + // + //********************************************************************* + + private string MakeShortLabel(string text) + { + string label = text; + if (text.Length > 2) + { + int midPostition = Convert.ToInt32(Math.Floor(text.Length/2.0)); + label = text.Substring(0,1) + text.Substring(midPostition, 1) + text.Substring(text.Length-1,1); + } + return label; + } + + //********************************************************************* + // + // This method calculates the max value and each tick mark value for the bar graph. + // + //********************************************************************* + + private void CalculateTickAndMax() + { + float tempMax = 0.0f; + + // Give graph some head room first about 10% of current max + _maxValue *= 1.1f; + + if (_maxValue != 0.0f) + { + // Find a rounded value nearest to the current max value + // Calculate this max first to give enough space to draw value on each bar + double exp = Convert.ToDouble(Math.Floor(Math.Log10(_maxValue))); + tempMax = Convert.ToSingle(Math.Ceiling(_maxValue / Math.Pow(10, exp)) * Math.Pow(10, exp)); + } + else + tempMax = 1.0f; + + // Once max value is calculated, tick value can be determined; tick value should be a whole number + _yTickValue = tempMax / _yTickCount; + double expTick = Convert.ToDouble(Math.Floor(Math.Log10(_yTickValue))); + _yTickValue = Convert.ToSingle(Math.Ceiling(_yTickValue / Math.Pow(10, expTick)) * Math.Pow(10, expTick)); + + // Re-calculate the max value with the new tick value + _maxValue = _yTickValue * _yTickCount; + } + + //********************************************************************* + // + // This method calculates the height for each bar in the graph + // + //********************************************************************* + + private void CalculateSweepValues() + { + // Called when all values and scale factor are known + // All values calculated here are relative from (_xOrigin, _yOrigin) + int i = 0; + foreach(DataItem item in DataPoints) + { + // This implementation does not support negative value + if (item.Value >= 0) item.SweepSize = item.Value/_scaleFactor; + + // (_spaceBtwBars/2) makes half white space for the first bar + item.StartPos = (_spaceBtwBars/2) + i * (_barWidth+_spaceBtwBars); + i++; + } + } + + //********************************************************************* + // + // This method calculates the width for each bar in the graph + // + //********************************************************************* + + private void CalculateBarWidth(int dataCount, float barGraphWidth) + { + // White space between each bar is the same as bar width itself + _barWidth = barGraphWidth / (dataCount * 2); // Each bar has 1 white space + //_barWidth =/* (float)Math.Floor(*/_barWidth/*)*/; + _spaceBtwBars = _barWidth; + } + + //********************************************************************* + // + // This method assigns default value to the bar graph properties and is only + // called from BarGraph constructors + // + //********************************************************************* + + private void AssignDefaultSettings() + { + // default values + _totalWidth = 680f; + _totalHeight = 450f; + _fontFamily = "Verdana"; + _backColor = Color.White; + _fontColor = Color.Black; + _topBuffer = 30f; + _bottomBuffer = 30f; + _yTickCount = 2; + _displayLegend = false; + _displayBarData = false; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Rendering/ChartRenderer.cs b/Scripts/Engines/Reports/Rendering/ChartRenderer.cs new file mode 100644 index 000000000..5b4c6f7d6 --- /dev/null +++ b/Scripts/Engines/Reports/Rendering/ChartRenderer.cs @@ -0,0 +1,71 @@ +using System; +using System.Drawing; +using System.Collections; + +namespace Server.Engines.Reports +{ + //********************************************************************* + // + // Chart Class + // + // Base class implementation for BarChart and PieChart + // + //********************************************************************* + + public abstract class ChartRenderer + { + private const int _colorLimit = 9; + + private Color[] _color = + { + Color.Firebrick, + Color.SkyBlue, + Color.MediumSeaGreen, + Color.MediumOrchid, + Color.Chocolate, + Color.SlateBlue, + Color.LightPink, + Color.LightGreen, + Color.Khaki + }; + + // Represent collection of all data points for the chart + private ChartItemsCollection _dataPoints = new ChartItemsCollection(); + + // The implementation of this method is provided by derived classes + public abstract Bitmap Draw(); + + public ChartItemsCollection DataPoints + { + get{ return _dataPoints; } + set{ _dataPoints = value; } + } + + public void SetColor(int index, Color NewColor) + { + if (index < _colorLimit) + { + _color[index] = NewColor; + } + else + { + throw new Exception("Color Limit is " + _colorLimit); + } + } + + public Color GetColor(int index) + { + //return _color[index%_colorLimit]; + + if (index < _colorLimit) + { + return _color[index]; + } + else + { + return _color[(index+2)%_colorLimit]; + //throw new Exception("Color Limit is " + _colorLimit); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Rendering/DataItem.cs b/Scripts/Engines/Reports/Rendering/DataItem.cs new file mode 100644 index 000000000..7ce48aed6 --- /dev/null +++ b/Scripts/Engines/Reports/Rendering/DataItem.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.Drawing; + +namespace Server.Engines.Reports +{ + // Modified from MS sample + + //********************************************************************* + // + // ChartItem Class + // + // This class represents a data point in a chart + // + //********************************************************************* + + public class DataItem + { + private string _label; + private string _description; + private float _value; + private Color _color; + private float _startPos; + private float _sweepSize; + + private DataItem() {} + + public DataItem(string label, string desc, float data, float start, float sweep, Color clr) + { + _label = label; + _description = desc; + _value = data; + _startPos = start; + _sweepSize = sweep; + _color = clr; + } + + public string Label + { + get{ return _label; } + set{ _label = value; } + } + + public string Description + { + get{ return _description; } + set{ _description = value; } + } + + public float Value + { + get{ return _value; } + set{ _value = value; } + } + + public Color ItemColor + { + get{ return _color; } + set{ _color = value; } + } + + public float StartPos + { + get{ return _startPos; } + set{ _startPos = value; } + } + + public float SweepSize + { + get{ return _sweepSize; } + set{ _sweepSize = value; } + } + } + + //********************************************************************* + // + // Custom Collection for ChartItems + // + //********************************************************************* + + public class ChartItemsCollection : CollectionBase + { + public DataItem this[int index] + { + get{ return (DataItem)(List[index]); } + set{ List[index] = value; } + } + + public int Add(DataItem value) + { + return List.Add(value); + } + + public int IndexOf(DataItem value) + { + return List.IndexOf(value); + } + + public bool Contains(DataItem value) + { + return List.Contains(value); + } + + public void Remove(DataItem value) + { + List.Remove(value); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Rendering/HtmlRenderer.cs b/Scripts/Engines/Reports/Rendering/HtmlRenderer.cs new file mode 100644 index 000000000..7bec01f52 --- /dev/null +++ b/Scripts/Engines/Reports/Rendering/HtmlRenderer.cs @@ -0,0 +1,541 @@ +using System; +using System.IO; +using System.Web; +using System.Web.UI; +using System.Net; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.Diagnostics; +using HtmlTag = System.Web.UI.HtmlTextWriterTag; +using HtmlAttr = System.Web.UI.HtmlTextWriterAttribute; + +namespace Server.Engines.Reports +{ + public class HtmlRenderer + { + private string m_Type; + private string m_Title; + private string m_OutputDirectory; + + private DateTime m_TimeStamp; + private ObjectCollection m_Objects; + + private HtmlRenderer( string outputDirectory ) + { + m_Type = outputDirectory; + m_Title = ( m_Type == "staff" ? "Staff" : "Stats" ); + m_OutputDirectory = Path.Combine( Core.BaseDirectory, "output" ); + + if ( !Directory.Exists( m_OutputDirectory ) ) + Directory.CreateDirectory( m_OutputDirectory ); + + m_OutputDirectory = Path.Combine( m_OutputDirectory, outputDirectory ); + + if ( !Directory.Exists( m_OutputDirectory ) ) + Directory.CreateDirectory( m_OutputDirectory ); + } + + public HtmlRenderer( string outputDirectory, Snapshot ss, SnapshotHistory history ) : this( outputDirectory ) + { + m_TimeStamp = ss.TimeStamp; + + m_Objects = new ObjectCollection(); + + for ( int i = 0; i < ss.Children.Count; ++i ) + m_Objects.Add( ss.Children[i] ); + + m_Objects.Add( BarGraph.OverTime( history, "General Stats", "Clients", 1, 100, 6 ) ); + m_Objects.Add( BarGraph.OverTime( history, "General Stats", "Items", 24, 9, 1 ) ); + m_Objects.Add( BarGraph.OverTime( history, "General Stats", "Players", 24, 9, 1 ) ); + m_Objects.Add( BarGraph.OverTime( history, "General Stats", "NPCs", 24, 9, 1 ) ); + m_Objects.Add( BarGraph.DailyAverage( history, "General Stats", "Clients" ) ); + m_Objects.Add( BarGraph.Growth( history, "General Stats", "Clients" ) ); + } + + public HtmlRenderer( string outputDirectory, StaffHistory history ) : this( outputDirectory ) + { + m_TimeStamp = DateTime.Now; + + m_Objects = new ObjectCollection(); + + history.Render( m_Objects ); + } + + public void Render() + { + Console.WriteLine( "Reports: {0}: Render started", m_Title ); + + RenderFull(); + + for ( int i = 0; i < m_Objects.Count; ++i ) + RenderSingle( m_Objects[i] ); + + Console.WriteLine( "Reports: {0}: Render complete", m_Title ); + } + + private static readonly string FtpHost = null; + + private static readonly string FtpUsername = null; + private static readonly string FtpPassword = null; + + private static readonly string FtpStatsDirectory = null; + private static readonly string FtpStaffDirectory = null; + + public void Upload() + { + if ( FtpHost == null ) + return; + + Console.WriteLine( "Reports: {0}: Upload started", m_Title ); + + string filePath = Path.Combine( m_OutputDirectory, "upload.ftp" ); + + using ( StreamWriter op = new StreamWriter( filePath ) ) + { + op.WriteLine( "open \"{0}\"", FtpHost ); + op.WriteLine( FtpUsername ); + op.WriteLine( FtpPassword ); + op.WriteLine( "cd \"{0}\"", ( m_Type == "staff" ? FtpStaffDirectory : FtpStatsDirectory ) ); + op.WriteLine( "mput \"{0}\"", Path.Combine( m_OutputDirectory, "*.html" ) ); + op.WriteLine( "mput \"{0}\"", Path.Combine( m_OutputDirectory, "*.css" ) ); + op.WriteLine( "binary" ); + op.WriteLine( "mput \"{0}\"", Path.Combine( m_OutputDirectory, "*.png" ) ); + op.WriteLine( "disconnect" ); + op.Write( "quit" ); + } + + ProcessStartInfo psi = new ProcessStartInfo(); + + psi.FileName = "ftp"; + psi.Arguments = String.Format( "-i -s:\"{0}\"", filePath ); + + psi.CreateNoWindow = true; + psi.WindowStyle = ProcessWindowStyle.Hidden; + //psi.UseShellExecute = true; + + try + { + Process p = Process.Start( psi ); + + p.WaitForExit(); + } + catch + { + } + + Console.WriteLine( "Reports: {0}: Upload complete", m_Title ); + + try{ File.Delete( filePath ); } + catch{} + } + + public void RenderFull() + { + string filePath = Path.Combine( m_OutputDirectory, "reports.html" ); + + using ( StreamWriter op = new StreamWriter( filePath ) ) + { + using ( HtmlTextWriter html = new HtmlTextWriter( op, "\t" ) ) + RenderFull( html ); + } + + string cssPath = Path.Combine( m_OutputDirectory, "styles.css" ); + + if ( File.Exists( cssPath ) ) + return; + + using ( StreamWriter css = new StreamWriter( cssPath ) ) + { + css.WriteLine( "body { background-color: #FFFFFF; font-family: verdana, arial; font-size: 11px; }" ); + css.WriteLine( "a { color: #28435E; }" ); + css.WriteLine( "a:hover { color: #4878A9; }" ); + css.WriteLine( "td.header { background-color: #9696AA; font-weight: bold; font-size: 12px; }" ); + css.WriteLine( "td.lentry { background-color: #D7D7EB; width: 10%; }" ); + css.WriteLine( "td.rentry { background-color: #FFFFFF; width: 90%; }" ); + css.WriteLine( "td.entry { background-color: #FFFFFF; }" ); + css.WriteLine( "td { font-size: 11px; }" ); + css.Write ( ".tbl-border { background-color: #46465A; }" ); + } + } + + private const string ShardTitle = "Shard"; + + public void RenderFull( HtmlTextWriter html ) + { + html.RenderBeginTag( HtmlTag.Html ); + + html.RenderBeginTag( HtmlTag.Head ); + + html.RenderBeginTag( HtmlTag.Title ); + html.Write( "{0} Statistics", ShardTitle ); + html.RenderEndTag(); + + html.AddAttribute( "rel", "stylesheet" ); + html.AddAttribute( HtmlAttr.Type, "text/css" ); + html.AddAttribute( HtmlAttr.Href, "styles.css" ); + html.RenderBeginTag( HtmlTag.Link ); + html.RenderEndTag(); + + html.RenderEndTag(); + + html.RenderBeginTag( HtmlTag.Body ); + + for ( int i = 0; i < m_Objects.Count; ++i ) + { + RenderDirect( m_Objects[i], html ); + html.Write( "

" ); + } + + html.RenderBeginTag( HtmlTag.Center ); + TimeZone tz = TimeZone.CurrentTimeZone; + bool isDaylight = tz.IsDaylightSavingTime( m_TimeStamp ); + TimeSpan utcOffset = tz.GetUtcOffset( m_TimeStamp ); + + html.Write( "Snapshot taken at {0:d} {0:t}. All times are {1}.", m_TimeStamp, tz.StandardName ); + html.RenderEndTag(); + + html.RenderEndTag(); + + html.RenderEndTag(); + } + + public static string SafeFileName( string name ) + { + return name.ToLower().Replace( ' ', '_' ); + } + + public void RenderSingle( PersistableObject obj ) + { + string filePath = Path.Combine( m_OutputDirectory, SafeFileName( FindNameFrom( obj ) ) + ".html" ); + + using ( StreamWriter op = new StreamWriter( filePath ) ) + { + using ( HtmlTextWriter html = new HtmlTextWriter( op, "\t" ) ) + RenderSingle( obj, html ); + } + } + + private string FindNameFrom( PersistableObject obj ) + { + if ( obj is Report ) + return (obj as Report).Name; + else if ( obj is Chart ) + return (obj as Chart).Name; + + return "Invalid"; + } + + public void RenderSingle( PersistableObject obj, HtmlTextWriter html ) + { + html.RenderBeginTag( HtmlTag.Html ); + + html.RenderBeginTag( HtmlTag.Head ); + + html.RenderBeginTag( HtmlTag.Title ); + html.Write( "{0} Statistics - {1}", ShardTitle, FindNameFrom( obj ) ); + html.RenderEndTag(); + + html.AddAttribute( "rel", "stylesheet" ); + html.AddAttribute( HtmlAttr.Type, "text/css" ); + html.AddAttribute( HtmlAttr.Href, "styles.css" ); + html.RenderBeginTag( HtmlTag.Link ); + html.RenderEndTag(); + + html.RenderEndTag(); + + html.RenderBeginTag( HtmlTag.Body ); + + html.RenderBeginTag( HtmlTag.Center ); + + RenderDirect( obj, html ); + + html.Write( "
" ); + + TimeZone tz = TimeZone.CurrentTimeZone; + bool isDaylight = tz.IsDaylightSavingTime( m_TimeStamp ); + TimeSpan utcOffset = tz.GetUtcOffset( m_TimeStamp ); + + html.Write( "Snapshot taken at {0:d} {0:t}. All times are {1}.", m_TimeStamp, tz.StandardName ); + html.RenderEndTag(); + + html.RenderEndTag(); + + html.RenderEndTag(); + } + + public void RenderDirect( PersistableObject obj, HtmlTextWriter html ) + { + if ( obj is Report ) + RenderReport( obj as Report, html ); + else if ( obj is BarGraph ) + RenderBarGraph( obj as BarGraph, html ); + else if ( obj is PieChart ) + RenderPieChart( obj as PieChart, html ); + } + + private void RenderPieChart( PieChart chart, HtmlTextWriter html ) + { + PieChartRenderer pieChart = new PieChartRenderer( Color.White ); + + pieChart.ShowPercents = chart.ShowPercents; + + string[] labels = new string[chart.Items.Count]; + string[] values = new string[chart.Items.Count]; + + for ( int i = 0; i < chart.Items.Count; ++i ) + { + ChartItem item = chart.Items[i]; + + labels[i] = item.Name; + values[i] = item.Value.ToString(); + } + + pieChart.CollectDataPoints( labels, values ); + + Bitmap bmp = pieChart.Draw(); + + string fileName = chart.FileName + ".png"; + bmp.Save( Path.Combine( m_OutputDirectory, fileName ), ImageFormat.Png ); + + html.Write( "" ); + + html.AddAttribute( HtmlAttr.Cellpadding, "0" ); + html.AddAttribute( HtmlAttr.Cellspacing, "0" ); + html.AddAttribute( HtmlAttr.Border, "0" ); + html.RenderBeginTag( HtmlTag.Table ); + + html.RenderBeginTag( HtmlTag.Tr ); + html.AddAttribute( HtmlAttr.Class, "tbl-border" ); + html.RenderBeginTag( HtmlTag.Td ); + + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Cellpadding, "4" ); + html.AddAttribute( HtmlAttr.Cellspacing, "1" ); + html.RenderBeginTag( HtmlTag.Table ); + + html.RenderBeginTag( HtmlTag.Tr ); + + html.AddAttribute( HtmlAttr.Colspan, "10" ); + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Align, "center" ); + html.AddAttribute( HtmlAttr.Class, "header" ); + html.RenderBeginTag( HtmlTag.Td ); + html.Write( chart.Name ); + html.RenderEndTag(); + html.RenderEndTag(); + + html.RenderBeginTag( HtmlTag.Tr ); + + html.AddAttribute( HtmlAttr.Colspan, "10" ); + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Align, "center" ); + html.AddAttribute( HtmlAttr.Class, "entry" ); + html.RenderBeginTag( HtmlTag.Td ); + + html.AddAttribute( HtmlAttr.Width, bmp.Width.ToString() ); + html.AddAttribute( HtmlAttr.Height, bmp.Height.ToString() ); + html.AddAttribute( HtmlAttr.Src, fileName ); + html.RenderBeginTag( HtmlTag.Img ); + html.RenderEndTag(); + + html.RenderEndTag(); + html.RenderEndTag(); + + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + + bmp.Dispose(); + } + + private void RenderBarGraph( BarGraph graph, HtmlTextWriter html ) + { + BarGraphRenderer barGraph = new BarGraphRenderer( Color.White ); + + barGraph.RenderMode = graph.RenderMode; + + barGraph._regions = graph.Regions; + barGraph.SetTitles( graph.xTitle, null ); + + if ( graph.yTitle != null ) + barGraph.VerticalLabel = graph.yTitle; + + barGraph.FontColor = Color.Black; + barGraph.ShowData = ( graph.Interval == 1 ); + barGraph.VerticalTickCount = graph.Ticks; + + string[] labels = new string[graph.Items.Count]; + string[] values = new string[graph.Items.Count]; + + for ( int i = 0; i < graph.Items.Count; ++i ) + { + ChartItem item = graph.Items[i]; + + labels[i] = item.Name; + values[i] = item.Value.ToString(); + } + + barGraph._interval = graph.Interval; + barGraph.CollectDataPoints( labels, values ); + + Bitmap bmp = barGraph.Draw(); + + string fileName = graph.FileName + ".png"; + bmp.Save( Path.Combine( m_OutputDirectory, fileName ), ImageFormat.Png ); + + html.Write( "" ); + + html.AddAttribute( HtmlAttr.Cellpadding, "0" ); + html.AddAttribute( HtmlAttr.Cellspacing, "0" ); + html.AddAttribute( HtmlAttr.Border, "0" ); + html.RenderBeginTag( HtmlTag.Table ); + + html.RenderBeginTag( HtmlTag.Tr ); + html.AddAttribute( HtmlAttr.Class, "tbl-border" ); + html.RenderBeginTag( HtmlTag.Td ); + + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Cellpadding, "4" ); + html.AddAttribute( HtmlAttr.Cellspacing, "1" ); + html.RenderBeginTag( HtmlTag.Table ); + + html.RenderBeginTag( HtmlTag.Tr ); + + html.AddAttribute( HtmlAttr.Colspan, "10" ); + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Align, "center" ); + html.AddAttribute( HtmlAttr.Class, "header" ); + html.RenderBeginTag( HtmlTag.Td ); + html.Write( graph.Name ); + html.RenderEndTag(); + html.RenderEndTag(); + + html.RenderBeginTag( HtmlTag.Tr ); + + html.AddAttribute( HtmlAttr.Colspan, "10" ); + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Align, "center" ); + html.AddAttribute( HtmlAttr.Class, "entry" ); + html.RenderBeginTag( HtmlTag.Td ); + + html.AddAttribute( HtmlAttr.Width, bmp.Width.ToString() ); + html.AddAttribute( HtmlAttr.Height, bmp.Height.ToString() ); + html.AddAttribute( HtmlAttr.Src, fileName ); + html.RenderBeginTag( HtmlTag.Img ); + html.RenderEndTag(); + + html.RenderEndTag(); + html.RenderEndTag(); + + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + + bmp.Dispose(); + } + + private void RenderReport( Report report, HtmlTextWriter html ) + { + html.AddAttribute( HtmlAttr.Width, report.Width ); + html.AddAttribute( HtmlAttr.Cellpadding, "0" ); + html.AddAttribute( HtmlAttr.Cellspacing, "0" ); + html.AddAttribute( HtmlAttr.Border, "0" ); + html.RenderBeginTag( HtmlTag.Table ); + + html.RenderBeginTag( HtmlTag.Tr ); + html.AddAttribute( HtmlAttr.Class, "tbl-border" ); + html.RenderBeginTag( HtmlTag.Td ); + + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Cellpadding, "4" ); + html.AddAttribute( HtmlAttr.Cellspacing, "1" ); + html.RenderBeginTag( HtmlTag.Table ); + + html.RenderBeginTag( HtmlTag.Tr ); + html.AddAttribute( HtmlAttr.Colspan, "10" ); + html.AddAttribute( HtmlAttr.Width, "100%" ); + html.AddAttribute( HtmlAttr.Align, "center" ); + html.AddAttribute( HtmlAttr.Class, "header" ); + html.RenderBeginTag( HtmlTag.Td ); + html.Write( report.Name ); + html.RenderEndTag(); + html.RenderEndTag(); + + bool isNamed = false; + + for ( int i = 0; i < report.Columns.Count && !isNamed; ++i ) + isNamed = ( report.Columns[i].Name != null ); + + if ( isNamed ) + { + html.RenderBeginTag( HtmlTag.Tr ); + + for ( int i = 0; i < report.Columns.Count; ++i ) + { + ReportColumn column = report.Columns[i]; + + html.AddAttribute( HtmlAttr.Class, "header" ); + html.AddAttribute( HtmlAttr.Width, column.Width ); + html.AddAttribute( HtmlAttr.Align, column.Align ); + html.RenderBeginTag( HtmlTag.Td ); + + html.Write( column.Name ); + + html.RenderEndTag(); + } + + html.RenderEndTag(); + } + + for ( int i = 0; i < report.Items.Count; ++i ) + { + ReportItem item = report.Items[i]; + + html.RenderBeginTag( HtmlTag.Tr ); + + for ( int j = 0; j < item.Values.Count; ++j ) + { + if ( !isNamed && j == 0 ) + html.AddAttribute( HtmlAttr.Width, report.Columns[j].Width ); + + html.AddAttribute( HtmlAttr.Align, report.Columns[j].Align ); + html.AddAttribute( HtmlAttr.Class, "entry" ); + html.RenderBeginTag( HtmlTag.Td ); + + if ( item.Values[j].Format == null ) + html.Write( item.Values[j].Value ); + else + html.Write( int.Parse( item.Values[j].Value ).ToString( item.Values[j].Format ) ); + + html.RenderEndTag(); + } + + html.RenderEndTag(); + } + + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + html.RenderEndTag(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Rendering/PieChartRenderer.cs b/Scripts/Engines/Reports/Rendering/PieChartRenderer.cs new file mode 100644 index 000000000..5cd0e0f34 --- /dev/null +++ b/Scripts/Engines/Reports/Rendering/PieChartRenderer.cs @@ -0,0 +1,252 @@ +using System; +using System.Collections; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +namespace Server.Engines.Reports +{ + // Modified from MS sample + + //********************************************************************* + // + // PieChart Class + // + // This class uses GDI+ to render Pie Chart. + // + //********************************************************************* + + public class PieChartRenderer : ChartRenderer + { + private const int _bufferSpace = 125; + private ArrayList _chartItems; + private int _perimeter; + private Color _backgroundColor; + private Color _borderColor; + private float _total; + private int _legendWidth; + private int _legendHeight; + private int _legendFontHeight; + private string _legendFontStyle; + private float _legendFontSize; + private bool _showPercents; + + public bool ShowPercents{ get{ return _showPercents; } set{ _showPercents = value; } } + + public PieChartRenderer() + { + _chartItems = new ArrayList(); + _perimeter = 250; + _backgroundColor = Color.White; + _borderColor = Color.FromArgb(63,63,63); + _legendFontSize = 8; + _legendFontStyle = "Verdana"; + } + + public PieChartRenderer(Color bgColor) + { + _chartItems = new ArrayList(); + _perimeter = 250; + _backgroundColor = bgColor; + _borderColor = Color.FromArgb(63,63,63); + _legendFontSize = 8; + _legendFontStyle = "Verdana"; + } + + //********************************************************************* + // + // This method collects all data points and calculate all the necessary dimensions + // to draw the chart. It is the first method called before invoking the Draw() method. + // + //********************************************************************* + + public void CollectDataPoints(string[] xValues, string[] yValues) + { + _total = 0.0f; + + for (int i = 0;i < xValues.Length;i++) + { + float ftemp = Convert.ToSingle(yValues[i]); + _chartItems.Add(new DataItem(xValues[i], xValues.ToString(), ftemp, 0, 0, Color.AliceBlue)); + _total += ftemp; + } + + float nextStartPos = 0.0f; + int counter = 0; + foreach (DataItem item in _chartItems) + { + item.StartPos = nextStartPos; + item.SweepSize = item.Value / _total * 360; + nextStartPos = item.StartPos + item.SweepSize; + item.ItemColor = GetColor(counter++); + } + + CalculateLegendWidthHeight(); + } + + //********************************************************************* + // + // This method returns a bitmap to the calling function. This is the method + // that actually draws the pie chart and the legend with it. + // + //********************************************************************* + + public override Bitmap Draw() + { + int perimeter = _perimeter; + Rectangle pieRect = new Rectangle(0, 0, perimeter, perimeter-1); + Bitmap bmp = new Bitmap(perimeter + _legendWidth, perimeter); + Font fnt = null; + Pen pen = null; + Graphics grp = null; + StringFormat sf = null, sfp = null; + + try + { + grp = Graphics.FromImage(bmp); + grp.CompositingQuality = CompositingQuality.HighQuality; + grp.SmoothingMode = SmoothingMode.AntiAlias; + sf = new StringFormat(); + + //Paint Back ground + using ( SolidBrush brsh = new SolidBrush( _backgroundColor ) ) + grp.FillRectangle(brsh, -1, -1, perimeter + _legendWidth+1, perimeter+1); + + //Align text to the right + sf.Alignment = StringAlignment.Far; + + //Draw all wedges and legends + for(int i=0; i<_chartItems.Count; i++) + { + DataItem item = (DataItem) _chartItems[i]; + SolidBrush brs = null; + try + { + brs = new SolidBrush(item.ItemColor); + grp.FillPie(brs, pieRect, item.StartPos, item.SweepSize); + + //grp.DrawPie(new Pen(_borderColor,1.2f),pieRect,item.StartPos,item.SweepSize); + + if ( fnt == null ) + fnt = new Font( _legendFontStyle, _legendFontSize ); + + if ( _showPercents && item.SweepSize > 10 ) + { + if ( sfp == null ) + { + sfp = new StringFormat(); + sfp.Alignment = StringAlignment.Center; + sfp.LineAlignment = StringAlignment.Center; + } + + float perc = (item.SweepSize * 100.0f) / 360.0f; + string percString = String.Format( "{0:F0}%", perc ); + + float px = pieRect.X+(pieRect.Width/2); + float py = pieRect.Y+(pieRect.Height/2); + + double angle = item.StartPos + (item.SweepSize/2); + double rads = (angle/180.0)*Math.PI; + + px += (float)(Math.Cos( rads ) * perimeter / 3); + py += (float)(Math.Sin( rads ) * perimeter / 3); + + grp.DrawString( percString, fnt, Brushes.Gray, + new RectangleF( px - 30 - 1, py - 20, 60, 40 ), sfp ); + + grp.DrawString( percString, fnt, Brushes.Gray, + new RectangleF( px - 30 + 1, py - 20, 60, 40 ), sfp ); + + grp.DrawString( percString, fnt, Brushes.Gray, + new RectangleF( px - 30, py - 20 - 1, 60, 40 ), sfp ); + + grp.DrawString( percString, fnt, Brushes.Gray, + new RectangleF( px - 30, py - 20 + 1, 60, 40 ), sfp ); + + + grp.DrawString( percString, fnt, Brushes.White, + new RectangleF( px - 30, py - 20, 60, 40 ), sfp ); + } + + if ( pen == null ) + pen = new Pen( _borderColor, 0.5f ); + + grp.FillRectangle(brs, perimeter + _bufferSpace, i * _legendFontHeight + 15, 10, 10); + grp.DrawRectangle(pen, perimeter + _bufferSpace, i * _legendFontHeight + 15, 10, 10); + + grp.DrawString(item.Label, fnt, + Brushes.Black, perimeter + _bufferSpace + 20, i * _legendFontHeight + 13); + + grp.DrawString(item.Value.ToString("#,###.##"), fnt, + Brushes.Black, perimeter + _bufferSpace + 200, i * _legendFontHeight + 13,sf); + } + finally + { + if (brs !=null) + brs.Dispose(); + } + } + + for(int i=0; i<_chartItems.Count; i++) + { + DataItem item = (DataItem) _chartItems[i]; + SolidBrush brs = null; + try + { + grp.DrawPie(new Pen(_borderColor,0.5f),pieRect,item.StartPos,item.SweepSize); + } + finally + { + if (brs !=null) + brs.Dispose(); + } + } + + //draws the border around Pie + using ( Pen pen2 = new Pen( _borderColor, 2 ) ) + grp.DrawEllipse(pen2, pieRect); + + //draw border around legend + using ( Pen pen1 = new Pen( _borderColor, 1 ) ) + grp.DrawRectangle(pen1, perimeter + _bufferSpace - 10, 10, 220, _chartItems.Count * _legendFontHeight + 25); + + //Draw Total under legend + using ( Font fntb = new Font( _legendFontStyle, _legendFontSize, FontStyle.Bold ) ) + { + grp.DrawString("Total", fntb, + Brushes.Black, perimeter + _bufferSpace + 30, (_chartItems.Count+1) * _legendFontHeight,sf); + grp.DrawString(_total.ToString("#,###.##"), fntb, + Brushes.Black, perimeter + _bufferSpace + 200, (_chartItems.Count+1) * _legendFontHeight,sf); + } + + grp.SmoothingMode = SmoothingMode.AntiAlias; + } + finally + { + if (sf != null) sf.Dispose(); + if (grp != null) grp.Dispose(); + if (sfp != null) sfp.Dispose(); + if (fnt != null) fnt.Dispose(); + if (pen != null) pen.Dispose(); + } + return bmp; + } + + //********************************************************************* + // + // This method calculates the space required to draw the chart legend. + // + //********************************************************************* + + private void CalculateLegendWidthHeight() + { + Font fontLegend = new Font(_legendFontStyle, _legendFontSize); + _legendFontHeight = fontLegend.Height+3; + _legendHeight = fontLegend.Height * (_chartItems.Count + 1); + if (_legendHeight > _perimeter) _perimeter = _legendHeight; + + _legendWidth = _perimeter + _bufferSpace; + fontLegend.Dispose(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Reports/Reports.cs b/Scripts/Engines/Reports/Reports.cs new file mode 100644 index 000000000..7f9824f1e --- /dev/null +++ b/Scripts/Engines/Reports/Reports.cs @@ -0,0 +1,420 @@ +using System; +using System.Threading; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Factions; +using Server.Accounting; + +namespace Server.Engines.Reports +{ + public class Reports + { + public static void Initialize() + { + m_StatsHistory = new SnapshotHistory(); + m_StatsHistory.Load(); + + m_StaffHistory = new StaffHistory(); + m_StaffHistory.Load(); + + DateTime now = DateTime.Now; + + DateTime date = now.Date; + TimeSpan timeOfDay = now.TimeOfDay; + + m_GenerateTime = date + TimeSpan.FromHours( Math.Ceiling( timeOfDay.TotalHours ) ); + + Timer.DelayCall( TimeSpan.FromMinutes( 0.5 ), TimeSpan.FromMinutes( 0.5 ), new TimerCallback( CheckRegenerate ) ); + } + + private static DateTime m_GenerateTime; + + public static void CheckRegenerate() + { + if ( DateTime.Now < m_GenerateTime ) + return; + + Generate(); + m_GenerateTime += TimeSpan.FromHours( 1.0 ); + } + + private static SnapshotHistory m_StatsHistory; + private static StaffHistory m_StaffHistory; + + public static StaffHistory StaffHistory{ get{ return m_StaffHistory; } } + + public static void Generate() + { + Snapshot ss = new Snapshot(); + + ss.TimeStamp = DateTime.Now; + + FillSnapshot( ss ); + + m_StatsHistory.Snapshots.Add( ss ); + m_StaffHistory.QueueStats.Add( new QueueStatus( Engines.Help.PageQueue.List.Count ) ); + + ThreadPool.QueueUserWorkItem( new WaitCallback( UpdateOutput ), ss ); + } + + private static void UpdateOutput( object state ) + { + m_StatsHistory.Save(); + m_StaffHistory.Save(); + + HtmlRenderer renderer = new HtmlRenderer( "stats", (Snapshot) state, m_StatsHistory ); + renderer.Render(); + renderer.Upload(); + + renderer = new HtmlRenderer( "staff", m_StaffHistory ); + renderer.Render(); + renderer.Upload(); + } + + public static void FillSnapshot( Snapshot ss ) + { + ss.Children.Add( CompileGeneralStats() ); + ss.Children.Add( CompileStatChart() ); + + PersistableObject[] obs = CompileSkillReports(); + + for ( int i = 0; i < obs.Length; ++i ) + ss.Children.Add( obs[i] ); + + obs = CompileFactionReports(); + + for ( int i = 0; i < obs.Length; ++i ) + ss.Children.Add( obs[i] ); + } + + public static Report CompileGeneralStats() + { + Report report = new Report( "General Stats", "200" ); + + report.Columns.Add( "50%", "left" ); + report.Columns.Add( "50%", "left" ); + + int npcs = 0, players = 0; + + foreach ( Mobile mob in World.Mobiles.Values ) + { + if ( mob.Player ) + ++players; + else + ++npcs; + } + + report.Items.Add( "NPCs", npcs, "N0" ); + report.Items.Add( "Players", players, "N0" ); + report.Items.Add( "Clients", NetState.Instances.Count, "N0" ); + report.Items.Add( "Accounts", Accounts.Count, "N0" ); + report.Items.Add( "Items", World.Items.Count, "N0" ); + + return report; + } + + public static Chart CompileStatChart() + { + PieChart chart = new PieChart( "Stat Distribution", "graphs_strdexint_distrib", true ); + + ChartItem strItem = new ChartItem( "Strength", 0 ); + ChartItem dexItem = new ChartItem( "Dexterity", 0 ); + ChartItem intItem = new ChartItem( "Intelligence", 0 ); + + foreach ( Mobile mob in World.Mobiles.Values ) + { + if ( mob.RawStatTotal == mob.StatCap && mob is PlayerMobile ) + { + strItem.Value += mob.RawStr; + dexItem.Value += mob.RawDex; + intItem.Value += mob.RawInt; + } + } + + chart.Items.Add( strItem ); + chart.Items.Add( dexItem ); + chart.Items.Add( intItem ); + + return chart; + } + + public class SkillDistribution : IComparable + { + public SkillInfo m_Skill; + public int m_NumberOfGMs; + + public SkillDistribution( SkillInfo skill ) + { + m_Skill = skill; + } + + public int CompareTo( object obj ) + { + return ( ((SkillDistribution)obj).m_NumberOfGMs - m_NumberOfGMs ); + } + } + + public static SkillDistribution[] GetSkillDistribution() + { + int skip = ( Core.ML ? 0 : Core.SE ? 1 : Core.AOS ? 3 : 6 ); //TODO: Change for ML + + SkillDistribution[] distribs = new SkillDistribution[SkillInfo.Table.Length - skip]; + + for ( int i = 0; i < distribs.Length; ++i ) + distribs[i] = new SkillDistribution( SkillInfo.Table[i] ); + + foreach ( Mobile mob in World.Mobiles.Values ) + { + if ( mob.SkillsTotal >= 1500 && mob.SkillsTotal <= 7200 && mob is PlayerMobile ) + { + Skills skills = mob.Skills; + + for ( int i = 0; i < skills.Length - skip; ++i ) + { + Skill skill = skills[i]; + + if ( skill.BaseFixedPoint >= 1000 ) + distribs[i].m_NumberOfGMs++; + } + } + } + + return distribs; + } + + public static PersistableObject[] CompileSkillReports() + { + SkillDistribution[] distribs = GetSkillDistribution(); + + Array.Sort( distribs ); + + return new PersistableObject[]{ CompileSkillChart( distribs ), CompileSkillReport( distribs ) }; + } + + public static Report CompileSkillReport( SkillDistribution[] distribs ) + { + Report report = new Report( "Skill Report", "300" ); + + report.Columns.Add( "70%", "left", "Name" ); + report.Columns.Add( "30%", "center", "GMs" ); + + for ( int i = 0; i < distribs.Length; ++i ) + report.Items.Add( distribs[i].m_Skill.Name, distribs[i].m_NumberOfGMs, "N0" ); + + return report; + } + + public static Chart CompileSkillChart( SkillDistribution[] distribs ) + { + PieChart chart = new PieChart( "GM Skill Distribution", "graphs_skill_distrib", true ); + + for ( int i = 0; i < 12; ++i ) + chart.Items.Add( distribs[i].m_Skill.Name, distribs[i].m_NumberOfGMs ); + + int rem = 0; + + for ( int i = 12; i < distribs.Length; ++i ) + rem += distribs[i].m_NumberOfGMs; + + chart.Items.Add( "Other", rem ); + + return chart; + } + + public static PersistableObject[] CompileFactionReports() + { + return new PersistableObject[]{ CompileFactionMembershipChart(), CompileFactionReport(), CompileFactionTownReport(), CompileSigilReport(), CompileFactionLeaderboard() }; + } + + public static Chart CompileFactionMembershipChart() + { + PieChart chart = new PieChart( "Faction Membership", "graphs_faction_membership", true ); + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + chart.Items.Add( factions[i].Definition.FriendlyName, factions[i].Members.Count ); + + return chart; + } + + public static Report CompileFactionLeaderboard() + { + Report report = new Report( "Faction Leaderboard", "60%" ); + + report.Columns.Add( "28%", "center", "Name" ); + report.Columns.Add( "28%", "center", "Faction" ); + report.Columns.Add( "28%", "center", "Office" ); + report.Columns.Add( "16%", "center", "Kill Points" ); + + ArrayList list = new ArrayList(); + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction faction = factions[i]; + + list.AddRange( faction.Members ); + } + + list.Sort(); + list.Reverse(); + + for ( int i = 0; i < list.Count && i < 15; ++i ) + { + PlayerState pl = (PlayerState)list[i]; + + string office; + + if ( pl.Faction.Commander == pl.Mobile ) + office = "Commanding Lord"; + else if ( pl.Finance != null ) + office = String.Format( "{0} Finance Minister", pl.Finance.Definition.FriendlyName ); + else if ( pl.Sheriff != null ) + office = String.Format( "{0} Sheriff", pl.Sheriff.Definition.FriendlyName ); + else + office = ""; + + ReportItem item = new ReportItem(); + + item.Values.Add( pl.Mobile.Name ); + item.Values.Add( pl.Faction.Definition.FriendlyName ); + item.Values.Add( office ); + item.Values.Add( pl.KillPoints.ToString(), "N0" ); + + report.Items.Add( item ); + } + + return report; + } + + public static Report CompileFactionReport() + { + Report report = new Report( "Faction Statistics", "80%" ); + + report.Columns.Add( "20%", "center", "Name" ); + report.Columns.Add( "20%", "center", "Commander" ); + report.Columns.Add( "15%", "center", "Members" ); + report.Columns.Add( "15%", "center", "Merchants" ); + report.Columns.Add( "15%", "center", "Kill Points" ); + report.Columns.Add( "15%", "center", "Silver" ); + + FactionCollection factions = Faction.Factions; + + for ( int i = 0; i < factions.Count; ++i ) + { + Faction faction = factions[i]; + PlayerStateCollection members = faction.Members; + + int totalKillPoints = 0; + int totalMerchants = 0; + + for ( int j = 0; j < members.Count; ++j ) + { + totalKillPoints += members[j].KillPoints; + + if ( members[j].MerchantTitle != MerchantTitle.None ) + ++totalMerchants; + } + + ReportItem item = new ReportItem(); + + item.Values.Add( faction.Definition.FriendlyName ); + item.Values.Add( faction.Commander == null ? "" : faction.Commander.Name ); + item.Values.Add( faction.Members.Count.ToString(), "N0" ); + item.Values.Add( totalMerchants.ToString(), "N0" ); + item.Values.Add( totalKillPoints.ToString(), "N0" ); + item.Values.Add( faction.Silver.ToString(), "N0" ); + + report.Items.Add( item ); + } + + return report; + } + + public static Report CompileSigilReport() + { + Report report = new Report( "Faction Town Sigils", "50%" ); + + report.Columns.Add( "35%", "center", "Town" ); + report.Columns.Add( "35%", "center", "Controller" ); + report.Columns.Add( "30%", "center", "Capturable" ); + + SigilCollection sigils = Sigil.Sigils; + + for ( int i = 0; i < sigils.Count; ++i ) + { + Sigil sigil = sigils[i]; + + string controller = "Unknown"; + + Mobile mob = sigil.RootParent as Mobile; + + if ( mob != null ) + { + Faction faction = Faction.Find( mob ); + + if ( faction != null ) + controller = faction.Definition.FriendlyName; + } + else if ( sigil.LastMonolith != null && sigil.LastMonolith.Faction != null ) + { + controller = sigil.LastMonolith.Faction.Definition.FriendlyName; + } + + ReportItem item = new ReportItem(); + + item.Values.Add( sigil.Town == null ? "" : sigil.Town.Definition.FriendlyName ); + item.Values.Add( controller ); + item.Values.Add( sigil.IsPurifying ? "No" : "Yes" ); + + report.Items.Add( item ); + } + + return report; + } + + public static Report CompileFactionTownReport() + { + Report report = new Report( "Faction Towns", "80%" ); + + report.Columns.Add( "20%", "center", "Name" ); + report.Columns.Add( "20%", "center", "Owner" ); + report.Columns.Add( "17%", "center", "Sheriff" ); + report.Columns.Add( "17%", "center", "Finance Minister" ); + report.Columns.Add( "13%", "center", "Silver" ); + report.Columns.Add( "13%", "center", "Prices" ); + + TownCollection towns = Town.Towns; + + for ( int i = 0; i < towns.Count; ++i ) + { + Town town = towns[i]; + + string prices = "Normal"; + + if ( town.Tax < 0 ) + prices = town.Tax.ToString() + "%"; + else if ( town.Tax > 0 ) + prices = "+" + town.Tax.ToString() + "%"; + + ReportItem item = new ReportItem(); + + item.Values.Add( town.Definition.FriendlyName ); + item.Values.Add( town.Owner == null ? "Neutral" : town.Owner.Definition.FriendlyName ); + item.Values.Add( town.Sheriff == null ? "" : town.Sheriff.Name ); + item.Values.Add( town.Finance == null ? "" : town.Finance.Name ); + item.Values.Add( town.Silver.ToString(), "N0" ); + item.Values.Add( prices ); + + report.Items.Add( item ); + } + + return report; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Spawner/Spawner.cs b/Scripts/Engines/Spawner/Spawner.cs new file mode 100644 index 000000000..b7e776aef --- /dev/null +++ b/Scripts/Engines/Spawner/Spawner.cs @@ -0,0 +1,777 @@ +using System; +using System.IO; +using System.Collections; +using Server; +using Server.Items; +using System.Collections.Generic; + +namespace Server.Mobiles +{ + public class Spawner : Item + { + private int m_Team; + private int m_HomeRange; + private int m_WalkingRange = -1; + private int m_Count; + private TimeSpan m_MinDelay; + private TimeSpan m_MaxDelay; + private List m_CreaturesName; + private ArrayList m_Creatures; + private DateTime m_End; + private InternalTimer m_Timer; + private bool m_Running; + private bool m_Group; + private WayPoint m_WayPoint; + + public bool IsFull{ get{ return ( m_Creatures != null && m_Creatures.Count >= m_Count ); } } + + public List CreaturesName + { + get { return m_CreaturesName; } + set + { + m_CreaturesName = value; + if ( m_CreaturesName.Count < 1 ) + Stop(); + + InvalidateProperties(); + } + } + + public override void OnAfterDuped( Item newItem ) + { + Spawner s = newItem as Spawner; + + if ( s == null ) + return; + + s.m_CreaturesName = new List( m_CreaturesName ); + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Count + { + get { return m_Count; } + set { m_Count = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public WayPoint WayPoint + { + get + { + return m_WayPoint; + } + set + { + m_WayPoint = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Running + { + get { return m_Running; } + set + { + if ( value ) + Start(); + else + Stop(); + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int HomeRange + { + get { return m_HomeRange; } + set { m_HomeRange = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int WalkingRange + { + get { return m_WalkingRange; } + set { m_WalkingRange = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Team + { + get { return m_Team; } + set { m_Team = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan MinDelay + { + get { return m_MinDelay; } + set { m_MinDelay = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan MaxDelay + { + get { return m_MaxDelay; } + set { m_MaxDelay = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan NextSpawn + { + get + { + if ( m_Running ) + return m_End - DateTime.Now; + else + return TimeSpan.FromSeconds( 0 ); + } + set + { + Start(); + DoTimer( value ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Group + { + get { return m_Group; } + set { m_Group = value; InvalidateProperties(); } + } + + [Constructable] + public Spawner( int amount, int minDelay, int maxDelay, int team, int homeRange, string creatureName ) : base( 0x1f13 ) + { + List creaturesName = new List(); + creaturesName.Add( creatureName.ToLower() ); + InitSpawn( amount, TimeSpan.FromMinutes( minDelay ), TimeSpan.FromMinutes( maxDelay ), team, homeRange, creaturesName ); + } + + [Constructable] + public Spawner( string creatureName ) : base( 0x1f13 ) + { + List creaturesName = new List(); + creaturesName.Add( creatureName.ToLower() ); + InitSpawn( 1, TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 10 ), 0, 4, creaturesName ); + } + + [Constructable] + public Spawner() : base( 0x1f13 ) + { + List creaturesName = new List(); + InitSpawn( 1, TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 10 ), 0, 4, creaturesName ); + } + + public Spawner( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, List creaturesName ) + : base( 0x1f13 ) + { + InitSpawn( amount, minDelay, maxDelay, team, homeRange, creaturesName ); + } + + public override string DefaultName + { + get { return "Spawner"; } + } + + public void InitSpawn( int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int homeRange, List creaturesName ) + { + Visible = false; + Movable = false; + m_Running = true; + m_Group = false; + m_MinDelay = minDelay; + m_MaxDelay = maxDelay; + m_Count = amount; + m_Team = team; + m_HomeRange = homeRange; + m_CreaturesName = creaturesName; + m_Creatures = new ArrayList(); + DoTimer( TimeSpan.FromSeconds( 1 ) ); + } + + public Spawner( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel < AccessLevel.GameMaster ) + return; + + SpawnerGump g = new SpawnerGump( this ); + from.SendGump( g ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Running ) + { + list.Add( 1060742 ); // active + + list.Add( 1060656, m_Count.ToString() ); // amount to make: ~1_val~ + list.Add( 1061169, m_HomeRange.ToString() ); // range ~1_val~ + list.Add( 1060658, "walking range\t{0}", m_WalkingRange ); // ~1_val~: ~2_val~ + + list.Add( 1060659, "group\t{0}", m_Group ); // ~1_val~: ~2_val~ + list.Add( 1060660, "team\t{0}", m_Team ); // ~1_val~: ~2_val~ + list.Add( 1060661, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay ); // ~1_val~: ~2_val~ + + for ( int i = 0; i < 2 && i < m_CreaturesName.Count; ++i ) + list.Add( 1060662 + i, "{0}\t{1}", m_CreaturesName[i], CountCreatures( (string)m_CreaturesName[i] ) ); + } + else + { + list.Add( 1060743 ); // inactive + } + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( m_Running ) + LabelTo( from, "[Running]" ); + else + LabelTo( from, "[Off]" ); + } + + public void Start() + { + if ( !m_Running ) + { + if ( m_CreaturesName.Count > 0 ) + { + m_Running = true; + DoTimer(); + } + } + } + + public void Stop() + { + if ( m_Running ) + { + m_Timer.Stop(); + m_Running = false; + } + } + + public void Defrag() + { + bool removed = false; + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + object o = m_Creatures[i]; + + if ( o is Item ) + { + Item item = (Item)o; + + if ( item.Deleted || item.Parent != null ) + { + m_Creatures.RemoveAt( i ); + --i; + removed = true; + } + } + else if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + if ( m.Deleted ) + { + m_Creatures.RemoveAt( i ); + --i; + removed = true; + } + else if ( m is BaseCreature ) + { + if ( ((BaseCreature)m).Controlled || ((BaseCreature)m).IsStabled ) + { + m_Creatures.RemoveAt( i ); + --i; + removed = true; + } + } + } + else + { + m_Creatures.RemoveAt( i ); + --i; + removed = true; + } + } + + if ( removed ) + InvalidateProperties(); + } + + public void OnTick() + { + DoTimer(); + + if ( m_Group ) + { + Defrag(); + + if ( m_Creatures.Count == 0 ) + { + Respawn(); + } + else + { + return; + } + } + else + { + Spawn(); + } + } + + public void Respawn() + { + RemoveCreatures(); + + for ( int i = 0; i < m_Count; i++ ) + Spawn(); + } + + public void Spawn() + { + if ( m_CreaturesName.Count > 0 ) + Spawn( Utility.Random( m_CreaturesName.Count ) ); + } + + public void Spawn( string creatureName ) + { + for ( int i = 0; i < m_CreaturesName.Count; i++ ) + { + if ( (string)m_CreaturesName[i] == creatureName ) + { + Spawn( i ); + break; + } + } + } + + public void Spawn( int index ) + { + Map map = Map; + + if ( map == null || map == Map.Internal || m_CreaturesName.Count == 0 || index >= m_CreaturesName.Count || Parent != null ) + return; + + Defrag(); + + if ( m_Creatures.Count >= m_Count ) + return; + + Type type = SpawnerType.GetType( (string)m_CreaturesName[index] ); + + if ( type != null ) + { + try + { + object o = Activator.CreateInstance( type ); + + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + m_Creatures.Add( m ); + + + Point3D loc = ( m is BaseVendor ? this.Location : GetSpawnPosition() ); + + m.OnBeforeSpawn( loc, map ); + InvalidateProperties(); + + + m.MoveToWorld( loc, map ); + + if ( m is BaseCreature ) + { + BaseCreature c = (BaseCreature)m; + + if( m_WalkingRange >= 0 ) + c.RangeHome = m_WalkingRange; + else + c.RangeHome = m_HomeRange; + + c.CurrentWayPoint = m_WayPoint; + + if ( m_Team > 0 ) + c.Team = m_Team; + + c.Home = this.Location; + } + + m.OnAfterSpawn(); + } + else if ( o is Item ) + { + Item item = (Item)o; + + m_Creatures.Add( item ); + + Point3D loc = GetSpawnPosition(); + + item.OnBeforeSpawn( loc, map ); + InvalidateProperties(); + + item.MoveToWorld( loc, map ); + + item.OnAfterSpawn(); + } + } + catch + { + } + } + } + + public Point3D GetSpawnPosition() + { + Map map = Map; + + if ( map == null ) + return Location; + + // Try 10 times to find a Spawnable location. + for ( int i = 0; i < 10; i++ ) + { + int x = Location.X + (Utility.Random( (m_HomeRange * 2) + 1 ) - m_HomeRange); + int y = Location.Y + (Utility.Random( (m_HomeRange * 2) + 1 ) - m_HomeRange); + int z = Map.GetAverageZ( x, y ); + + if ( Map.CanSpawnMobile( new Point2D( x, y ), this.Z ) ) + return new Point3D( x, y, this.Z ); + else if ( Map.CanSpawnMobile( new Point2D( x, y ), z ) ) + return new Point3D( x, y, z ); + } + + return this.Location; + } + + public void DoTimer() + { + if ( !m_Running ) + return; + + int minSeconds = (int)m_MinDelay.TotalSeconds; + int maxSeconds = (int)m_MaxDelay.TotalSeconds; + + TimeSpan delay = TimeSpan.FromSeconds( Utility.RandomMinMax( minSeconds, maxSeconds ) ); + DoTimer( delay ); + } + + public void DoTimer( TimeSpan delay ) + { + if ( !m_Running ) + return; + + m_End = DateTime.Now + delay; + + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = new InternalTimer( this, delay ); + m_Timer.Start(); + } + + private class InternalTimer : Timer + { + private Spawner m_Spawner; + + public InternalTimer( Spawner spawner, TimeSpan delay ) : base( delay ) + { + if ( spawner.IsFull ) + Priority = TimerPriority.FiveSeconds; + else + Priority = TimerPriority.OneSecond; + + m_Spawner = spawner; + } + + protected override void OnTick() + { + if ( m_Spawner != null ) + if ( !m_Spawner.Deleted ) + m_Spawner.OnTick(); + } + } + + public int CountCreatures( string creatureName ) + { + Defrag(); + + int count = 0; + + for ( int i = 0; i < m_Creatures.Count; ++i ) + if ( Insensitive.Equals( creatureName, m_Creatures[i].GetType().Name ) ) + ++count; + + return count; + } + + public void RemoveCreatures( string creatureName ) + { + Defrag(); + + creatureName = creatureName.ToLower(); + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + object o = m_Creatures[i]; + + if ( Insensitive.Equals( creatureName, o.GetType().Name ) ) + { + if ( o is Item ) + ((Item)o).Delete(); + else if ( o is Mobile ) + ((Mobile)o).Delete(); + } + } + + InvalidateProperties(); + } + + public void RemoveCreatures() + { + Defrag(); + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + object o = m_Creatures[i]; + + if ( o is Item ) + ((Item)o).Delete(); + else if ( o is Mobile ) + ((Mobile)o).Delete(); + } + + InvalidateProperties(); + } + + public void BringToHome() + { + Defrag(); + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + object o = m_Creatures[i]; + + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + m.MoveToWorld( Location, Map ); + } + else if ( o is Item ) + { + Item item = (Item)o; + + item.MoveToWorld( Location, Map ); + } + } + } + + public override void OnDelete() + { + base.OnDelete(); + + RemoveCreatures(); + if ( m_Timer != null ) + m_Timer.Stop(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 4 ); // version + writer.Write( m_WalkingRange ); + + writer.Write( m_WayPoint ); + + writer.Write( m_Group ); + + writer.Write( m_MinDelay ); + writer.Write( m_MaxDelay ); + writer.Write( m_Count ); + writer.Write( m_Team ); + writer.Write( m_HomeRange ); + writer.Write( m_Running ); + + if ( m_Running ) + writer.WriteDeltaTime( m_End ); + + writer.Write( m_CreaturesName.Count ); + + for ( int i = 0; i < m_CreaturesName.Count; ++i ) + writer.Write( m_CreaturesName[i] ); + + writer.Write( m_Creatures.Count ); + + for ( int i = 0; i < m_Creatures.Count; ++i ) + { + object o = m_Creatures[i]; + + if ( o is Item ) + writer.Write( (Item)o ); + else if ( o is Mobile ) + writer.Write( (Mobile)o ); + else + writer.Write( Serial.MinusOne ); + } + } + + private static WarnTimer m_WarnTimer; + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 4: + { + m_WalkingRange = reader.ReadInt(); + + goto case 3; + } + case 3: + case 2: + { + m_WayPoint = reader.ReadItem() as WayPoint; + + goto case 1; + } + + case 1: + { + m_Group = reader.ReadBool(); + + goto case 0; + } + + case 0: + { + m_MinDelay = reader.ReadTimeSpan(); + m_MaxDelay = reader.ReadTimeSpan(); + m_Count = reader.ReadInt(); + m_Team = reader.ReadInt(); + m_HomeRange = reader.ReadInt(); + m_Running = reader.ReadBool(); + + TimeSpan ts = TimeSpan.Zero; + + if ( m_Running ) + ts = reader.ReadDeltaTime() - DateTime.Now; + + int size = reader.ReadInt(); + + m_CreaturesName = new List( size ); + + for ( int i = 0; i < size; ++i ) + { + string typeName = reader.ReadString(); + + m_CreaturesName.Add( typeName ); + + if ( SpawnerType.GetType( typeName ) == null ) + { + if ( m_WarnTimer == null ) + m_WarnTimer = new WarnTimer(); + + m_WarnTimer.Add( Location, Map, typeName ); + } + } + + int count = reader.ReadInt(); + + m_Creatures = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + IEntity e = World.FindEntity( reader.ReadInt() ); + + if ( e != null ) + m_Creatures.Add( e ); + } + + if ( m_Running ) + DoTimer( ts ); + + break; + } + } + + if ( version < 3 && Weight == 0 ) + Weight = -1; + } + + private class WarnTimer : Timer + { + private ArrayList m_List; + + private class WarnEntry + { + public Point3D m_Point; + public Map m_Map; + public string m_Name; + + public WarnEntry( Point3D p, Map map, string name ) + { + m_Point = p; + m_Map = map; + m_Name = name; + } + } + + public WarnTimer() : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_List = new ArrayList(); + Start(); + } + + public void Add( Point3D p, Map map, string name ) + { + m_List.Add( new WarnEntry( p, map, name ) ); + } + + protected override void OnTick() + { + try + { + Console.WriteLine( "Warning: {0} bad spawns detected, logged: 'badspawn.log'", m_List.Count ); + + using ( StreamWriter op = new StreamWriter( "badspawn.log", true ) ) + { + op.WriteLine( "# Bad spawns : {0}", DateTime.Now ); + op.WriteLine( "# Format: X Y Z F Name" ); + op.WriteLine(); + + foreach ( WarnEntry e in m_List ) + op.WriteLine( "{0}\t{1}\t{2}\t{3}\t{4}", e.m_Point.X, e.m_Point.Y, e.m_Point.Z, e.m_Map, e.m_Name ); + + op.WriteLine(); + op.WriteLine(); + } + } + catch + { + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Spawner/SpawnerGump.cs b/Scripts/Engines/Spawner/SpawnerGump.cs new file mode 100644 index 000000000..73e19fdf2 --- /dev/null +++ b/Scripts/Engines/Spawner/SpawnerGump.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Gumps; +using System.Collections.Generic; + +namespace Server.Mobiles +{ + public class SpawnerGump : Gump + { + private Spawner m_Spawner; + + public SpawnerGump( Spawner spawner ) : base( 50, 50 ) + { + m_Spawner = spawner; + + AddPage( 0 ); + + AddBackground( 0, 0, 260, 371, 5054 ); + + AddLabel( 95, 1, 0, "Creatures List" ); + + AddButton( 5, 347, 0xFB1, 0xFB3, 0, GumpButtonType.Reply, 0 ); + AddLabel( 38, 347, 0x384, "Cancel" ); + + AddButton( 5, 325, 0xFB7, 0xFB9, 1, GumpButtonType.Reply, 0 ); + AddLabel( 38, 325, 0x384, "Okay" ); + + AddButton( 110, 325, 0xFB4, 0xFB6, 2, GumpButtonType.Reply, 0 ); + AddLabel( 143, 325, 0x384, "Bring to Home" ); + + AddButton( 110, 347, 0xFA8, 0xFAA, 3, GumpButtonType.Reply, 0 ); + AddLabel( 143, 347, 0x384, "Total Respawn" ); + + for ( int i = 0; i < 13; i++ ) + { + AddButton( 5, ( 22 * i ) + 20, 0xFA5, 0xFA7, 4 + (i * 2), GumpButtonType.Reply, 0 ); + AddButton( 38, ( 22 * i ) + 20, 0xFA2, 0xFA4, 5 + (i * 2), GumpButtonType.Reply, 0 ); + + AddImageTiled( 71, ( 22 * i ) + 20, 159, 23, 0xA40 ); + AddImageTiled( 72, ( 22 * i ) + 21, 157, 21, 0xBBC ); + + string str = ""; + + if ( i < spawner.CreaturesName.Count ) + { + str = (string)spawner.CreaturesName[i]; + int count = m_Spawner.CountCreatures( str ); + + AddLabel( 232, ( 22 * i ) + 20, 0, count.ToString() ); + } + + AddTextEntry( 75, ( 22 * i ) + 21, 154, 21, 0, i, str ); + } + } + + public List CreateArray( RelayInfo info, Mobile from ) + { + List creaturesName = new List(); + + for ( int i = 0; i < 13; i++ ) + { + TextRelay te = info.GetTextEntry( i ); + + if ( te != null ) + { + string str = te.Text; + + if ( str.Length > 0 ) + { + str = str.Trim(); + + Type type = SpawnerType.GetType( str ); + + if ( type != null ) + creaturesName.Add( str ); + else + from.SendMessage( "{0} is not a valid type name.", str ); + } + } + } + + return creaturesName; + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( m_Spawner.Deleted ) + return; + + switch ( info.ButtonID ) + { + case 0: // Closed + { + break; + } + case 1: // Okay + { + m_Spawner.CreaturesName = CreateArray( info, state.Mobile ); + + break; + } + case 2: // Bring everything home + { + m_Spawner.BringToHome(); + + break; + } + case 3: // Complete respawn + { + m_Spawner.Respawn(); + + break; + } + default: + { + int buttonID = info.ButtonID - 4; + int index = buttonID / 2; + int type = buttonID % 2; + + TextRelay entry = info.GetTextEntry( index ); + + if ( entry != null && entry.Text.Length > 0 ) + { + if ( type == 0 ) // Spawn creature + m_Spawner.Spawn( entry.Text ); + else // Remove creatures + m_Spawner.RemoveCreatures( entry.Text ); + + m_Spawner.CreaturesName = CreateArray( info, state.Mobile ); + } + + break; + } + } + } + } +} diff --git a/Scripts/Engines/Spawner/SpawnerType.cs b/Scripts/Engines/Spawner/SpawnerType.cs new file mode 100644 index 000000000..2b19d6e34 --- /dev/null +++ b/Scripts/Engines/Spawner/SpawnerType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class SpawnerType + { + public static Type GetType( string name ) + { + return ScriptCompiler.FindTypeByName( name ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Treasures of Tokuno/GreaterArtifacts.cs b/Scripts/Engines/Treasures of Tokuno/GreaterArtifacts.cs new file mode 100644 index 000000000..27ef5b28b --- /dev/null +++ b/Scripts/Engines/Treasures of Tokuno/GreaterArtifacts.cs @@ -0,0 +1,610 @@ +using Server; +using System; +using Server.Misc; +using Server.Mobiles; + +namespace Server.Items +{ + public class DarkenedSky : Kama + { + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + public override int LabelNumber { get { return 1070966; } } // Darkened Sky + + [Constructable] + public DarkenedSky() : base() + { + WeaponAttributes.HitLightning = 60; + Attributes.WeaponSpeed = 25; + Attributes.WeaponDamage = 50; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = pois = 0; + cold = nrgy = 50; + } + + public DarkenedSky( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + public class KasaOfTheRajin : Kasa + { + public override int LabelNumber { get { return 1070969; } } // Kasa of the Raj-in + + public override int BasePhysicalResistance { get { return 12; } } + public override int BaseFireResistance { get { return 17; } } + public override int BaseColdResistance { get { return 21; } } + public override int BasePoisonResistance { get { return 17; } } + public override int BaseEnergyResistance { get { return 17; } } + + public override bool CanBeBlessed { get { return false; } } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + [Constructable] + public KasaOfTheRajin() : base() + { + Attributes.SpellDamage = 12; + } + + public KasaOfTheRajin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)2 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version <= 1 ) + { + MaxHitPoints = 255; + HitPoints = 255; + } + + if( version == 0 ) + LootType = LootType.Regular; + } + + } + + public class RuneBeetleCarapace : PlateDo + { + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + public override int LabelNumber{ get{ return 1070968; } } // Rune Beetle Carapace + + public override int BaseColdResistance { get { return 14; } } + public override int BaseEnergyResistance { get { return 14; } } + + [Constructable] + public RuneBeetleCarapace() : base() + { + Attributes.BonusMana = 10; + Attributes.RegenMana = 3; + Attributes.LowerManaCost = 15; + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + } + + public RuneBeetleCarapace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + public class Stormgrip : LeatherGloves + { + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + public override int LabelNumber{ get{ return 1070970; } } // Stormgrip + + public override int BasePhysicalResistance { get { return 10; } } + public override int BaseColdResistance { get { return 18; } } + public override int BaseEnergyResistance { get { return 18; } } + + [Constructable] + public Stormgrip() : base() + { + Attributes.BonusInt = 8; + Attributes.Luck = 125; + Attributes.WeaponDamage = 25; + } + + public Stormgrip( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + public class SwordOfTheStampede : NoDachi + { + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + public override int LabelNumber { get { return 1070964; } } // Sword of the Stampede + + [Constructable] + public SwordOfTheStampede() : base() + { + WeaponAttributes.HitHarm = 100; + Attributes.AttackChance = 10; + Attributes.WeaponDamage = 60; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = pois = nrgy = 0; + cold = 100; + } + + public SwordOfTheStampede( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + public class SwordsOfProsperity : Daisho + { + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + public override int LabelNumber { get { return 1070963; } } // Swords of Prosperity + + [Constructable] + public SwordsOfProsperity() : base() + { + WeaponAttributes.MageWeapon = 30; + Attributes.SpellChanneling = 1; + Attributes.CastSpeed = 1; + Attributes.Luck = 200; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = cold = pois = nrgy = 0; + fire = 100; + } + + public SwordsOfProsperity( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + public class TheHorselord : Yumi + { + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + public override int LabelNumber { get { return 1070967; } } // The Horselord + + [Constructable] + public TheHorselord() : base() + { + Attributes.BonusDex = 5; + Attributes.RegenMana = 1; + Attributes.Luck = 125; + Attributes.WeaponDamage = 50; + + Slayer = SlayerName.ElementalBan; + Slayer2 = SlayerName.ReptilianDeath; + } + + public TheHorselord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TomeOfLostKnowledge : Spellbook + { + public override int LabelNumber { get { return 1070971; } } // Tome of Lost Knowledge + + [Constructable] + public TomeOfLostKnowledge() : base() + { + LootType = LootType.Regular; + Hue = 0x530; + + SkillBonuses.SetValues( 0, SkillName.Magery, 15.0 ); + Attributes.BonusInt = 8; + Attributes.LowerManaCost = 15; + Attributes.SpellDamage = 15; + } + + public TomeOfLostKnowledge( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WindsEdge : Tessen + { + public override int LabelNumber { get { return 1070965; } } // Wind's Edge + + [Constructable] + public WindsEdge() : base() + { + WeaponAttributes.HitLeechMana = 40; + + Attributes.WeaponDamage = 50; + Attributes.WeaponSpeed = 50; + Attributes.DefendChance = 10; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = 0; + nrgy = 100; + } + + + public WindsEdge( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public enum PigmentType + { + None, + ParagonGold, + VioletCouragePurple, + InvulnerabilityBlue, + LunaWhite, + DryadGreen, + ShadowDancerBlack, + BerserkerRed, + NoxGreen, + RumRed, + FireOrange + } + + public class PigmentsOfTokuno : Item, IUsesRemaining + { + public class PigmentInfo + { + //private PigmentType m_PigmentType; + private int m_Hue; + private TextDefinition m_Label; + + public int Hue { get { return m_Hue; } } + public TextDefinition Label { get { return m_Label; } } + + public PigmentInfo( int hue, TextDefinition label ) + { + m_Hue = hue; + m_Label = label; + } + + private static PigmentInfo[] m_Table = new PigmentInfo[] + { + new PigmentInfo( /*PigmentType.None,*/ 0, -1 ), + new PigmentInfo( /*PigmentType.ParagonGold,*/ 0x501, 1070987 ), + new PigmentInfo( /*PigmentType.VioletCouragePurple,*/ 0x486, 1070988 ), + new PigmentInfo( /*PigmentType.InvulnerabilityBlue,*/ 0x4F2, 1070989 ), + new PigmentInfo( /*PigmentType.LunaWhite,*/ 0x47E, 1070990 ), + new PigmentInfo( /*PigmentType.DryadGreen,*/ 0x48F, 1070991 ), + new PigmentInfo( /*PigmentType.ShadowDancerBlack,*/ 0x455, 1070992 ), + new PigmentInfo( /*PigmentType.BerserkerRed,*/ 0x21, 1070993 ), + new PigmentInfo( /*PigmentType.NoxGreen,*/ 0x58C, 1070994 ), + new PigmentInfo( /*PigmentType.RumRed,*/ 0x66C, 1070995 ), + new PigmentInfo( /*PigmentType.FireOrange,*/ 0x54F, 1070996 ) + }; + + public static PigmentInfo GetInfo( PigmentType type ) + { + int v = (int)type; + + if( v < 0 || v >= m_Table.Length ) + v = 0; + + return m_Table[v]; + } + } + + private PigmentType m_Type; + + [CommandProperty( AccessLevel.GameMaster )] + public PigmentType Type + { + get { return m_Type; } + set + { + m_Type = value; + + PigmentInfo p = PigmentInfo.GetInfo( m_Type ); + Hue = p.Hue; + InvalidateProperties(); + } + } + + private int m_UsesRemaining; + + public override int LabelNumber { get { return 1070933; } } // Pigments of Tokuno + + [Constructable] + public PigmentsOfTokuno() : this( PigmentType.None, 10 ) + { + } + + [Constructable] + public PigmentsOfTokuno( PigmentType type ) : this( type, (type == PigmentType.None)? 10 : 50 ) + { + } + + [Constructable] + public PigmentsOfTokuno( PigmentType type, int uses ) : base( 0xEFF ) + { + Weight = 1.0; + m_UsesRemaining = uses; + Type = type; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if( m_Type != PigmentType.None ) + { + PigmentInfo p = PigmentInfo.GetInfo( m_Type ); + TextDefinition.AddTo( list, p.Label ); + } + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + } + + public override void OnDoubleClick( Mobile from ) + { + if( IsAccessibleTo( from ) && from.InRange( GetWorldLocation(), 3 ) ) + { + from.SendLocalizedMessage( 1070929 ); // Select the artifact or enhanced magic item to dye. + from.BeginTarget( 3, false, Server.Targeting.TargetFlags.None, new TargetStateCallback( InternalCallback ), this ); + } + else + from.SendLocalizedMessage( 502436 ); // That is not accessible. + } + + private void InternalCallback( Mobile from, object targeted, object state ) + { + PigmentsOfTokuno pigment = (PigmentsOfTokuno)state; + + if( pigment.Deleted || pigment.UsesRemaining <= 0 || !from.InRange( pigment.GetWorldLocation(), 3 ) || !pigment.IsAccessibleTo( from )) + return; + + Item i = targeted as Item; + + if( i == null ) + from.SendLocalizedMessage( 1070931 ); // You can only dye artifacts and enhanced magic items with this tub. + else if( !from.InRange( i.GetWorldLocation(), 3 ) || !IsAccessibleTo( from ) ) + from.SendLocalizedMessage( 502436 ); // That is not accessible. + else if( from.Items.Contains( i ) ) + from.SendLocalizedMessage( 1070930 ); // Can't dye artifacts or enhanced magic items that are being worn. + else if( i.IsLockedDown ) + from.SendLocalizedMessage( 1070932 ); // You may not dye artifacts and enhanced magic items which are locked down. + else if( i is PigmentsOfTokuno ) + from.SendLocalizedMessage( 1042417 ); // You cannot dye that. + else if( !IsValidItem( i ) ) + from.SendLocalizedMessage( 1070931 ); // You can only dye artifacts and enhanced magic items with this tub. //Yes, it says tub on OSI. Don't ask me why ;p + else + { + //Notes: on OSI there IS no hue check to see if it's already hued. and no messages on successful hue either + i.Hue = PigmentInfo.GetInfo( pigment.Type ).Hue; + + if( --pigment.UsesRemaining <= 0 ) + pigment.Delete(); + } + } + + public static bool IsValidItem( Item i ) + { + if( i is PigmentsOfTokuno ) + return false; + + Type t = i.GetType(); + + CraftResource resource = CraftResource.None; + + if( i is BaseWeapon ) + resource = ((BaseWeapon)i).Resource; + else if( i is BaseArmor ) + resource = ((BaseArmor)i).Resource; + + if( !CraftResources.IsStandard( resource ) ) + return true; + + return( + IsInTypeList( t, TreasuresOfTokuno.LesserArtifacts ) + || IsInTypeList( t, TreasuresOfTokuno.GreaterArtifacts ) + || IsInTypeList( t, DemonKnight.ArtifactRarity10 ) + || IsInTypeList( t, DemonKnight.ArtifactRarity11 ) + || IsInTypeList( t, DemonKnight.ArtifactRarity10 ) + || IsInTypeList( t, StealableArtifactsSpawner.TypesOfEntires ) + || IsInTypeList( t, Paragon.Artifacts ) + || IsInTypeList( t, Leviathan.Artifacts ) + || IsInTypeList( t, TreasureMapChest.Artifacts ) + ); + } + + private static bool IsInTypeList( Type t, Type[] list ) + { + for( int i = 0; i < list.Length; i++ ) + { + if( list[i] == t ) return true; + } + + return false; + } + + public PigmentsOfTokuno( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + + writer.WriteEncodedInt( (int)m_Type ); + writer.WriteEncodedInt( m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Type = (PigmentType)reader.ReadEncodedInt(); + m_UsesRemaining = reader.ReadEncodedInt(); + } + + #region IUsesRemaining Members + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining + { + get { return true; } + set {} + } + + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Engines/Treasures of Tokuno/LesserArtifacts.cs b/Scripts/Engines/Treasures of Tokuno/LesserArtifacts.cs new file mode 100644 index 000000000..cb67be734 --- /dev/null +++ b/Scripts/Engines/Treasures of Tokuno/LesserArtifacts.cs @@ -0,0 +1,785 @@ +using Server; +using System; + +namespace Server.Items +{ + public class AncientFarmersKasa : Kasa + { + public override int LabelNumber{ get{ return 1070922; } } // Ancient Farmer's Kasa + public override int BaseColdResistance { get { return 19; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get { return 255; } } + + [Constructable] + public AncientFarmersKasa() : base() + { + Attributes.BonusStr = 5; + Attributes.BonusStam = 5; + Attributes.RegenStam = 5; + + SkillBonuses.SetValues( 0, SkillName.AnimalLore, 5.0 ); + } + + public AncientFarmersKasa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version <= 1 ) + { + MaxHitPoints = 255; + HitPoints = 255; + } + + if( version == 0 ) + SkillBonuses.SetValues( 0, SkillName.AnimalLore, 5.0 ); + } + } + + public class AncientSamuraiDo : PlateDo + { + public override int LabelNumber { get { return 1070926; } } // Ancient Samurai Do + + public override int BasePhysicalResistance { get { return 15; } } + public override int BaseFireResistance { get { return 12; } } + public override int BaseColdResistance { get { return 10; } } + public override int BasePoisonResistance { get { return 11; } } + public override int BaseEnergyResistance { get { return 8; } } + + [Constructable] + public AncientSamuraiDo() : base() + { + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + SkillBonuses.SetValues( 0, SkillName.Parry, 10.0 ); + } + + public AncientSamuraiDo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class ArmsOfTacticalExcellence : LeatherHiroSode + { + public override int LabelNumber { get { return 1070921; } } // Arms of Tactical Excellence + + public override int BaseFireResistance { get { return 9; } } + public override int BaseColdResistance { get { return 13; } } + public override int BasePoisonResistance { get { return 8; } } + + [Constructable] + public ArmsOfTacticalExcellence() : base() + { + Attributes.BonusDex = 5; + SkillBonuses.SetValues( 0, SkillName.Tactics, 12.0 ); + } + + public ArmsOfTacticalExcellence( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class BlackLotusHood : ClothNinjaHood + { + public override int LabelNumber { get { return 1070919; } } // Black Lotus Hood + + public override int BasePhysicalResistance { get { return 0; } } + public override int BaseFireResistance { get { return 11; } } + public override int BaseColdResistance { get { return 15; } } + public override int BasePoisonResistance { get { return 11; } } + public override int BaseEnergyResistance { get { return 11; } } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + + [Constructable] + public BlackLotusHood() : base() + { + Attributes.LowerManaCost = 6; + Attributes.AttackChance = 6; + ClothingAttributes.SelfRepair = 5; + } + + public BlackLotusHood( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)1 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 ) + { + MaxHitPoints = 255; + HitPoints = 255; + } + } + } + + public class DaimyosHelm : PlateBattleKabuto + { + public override int LabelNumber { get { return 1070920; } } // Daimyo's Helm + + public override int BaseColdResistance { get { return 10; } } + + [Constructable] + public DaimyosHelm() : base() + { + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + ArmorAttributes.SelfRepair = 3; + Attributes.WeaponSpeed = 10; + } + + public DaimyosHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class DemonForks : Sai + { + public override int LabelNumber{ get{ return 1070917; } } // Demon Forks + + [Constructable] + public DemonForks() : base() + { + WeaponAttributes.ResistFireBonus = 10; + WeaponAttributes.ResistPoisonBonus = 10; + + Attributes.ReflectPhysical = 10; + Attributes.WeaponDamage = 35; + Attributes.DefendChance = 10; + } + + public DemonForks( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class DragonNunchaku : Nunchaku + { + public override int LabelNumber{ get{ return 1070914; } } // Dragon Nunchaku + + [Constructable] + public DragonNunchaku() : base() + { + WeaponAttributes.ResistFireBonus = 5; + WeaponAttributes.SelfRepair = 3; + WeaponAttributes.HitFireball = 50; + + Attributes.WeaponDamage = 40; + Attributes.WeaponSpeed = 20; + } + + public DragonNunchaku( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class Exiler : Tetsubo + { + public override int LabelNumber{ get{ return 1070913; } } // Exiler + + [Constructable] + public Exiler() : base() + { + WeaponAttributes.HitDispel = 33; + Slayer = SlayerName.Exorcism; + + Attributes.WeaponDamage = 40; + Attributes.WeaponSpeed = 20; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = 0; + + nrgy = 100; + } + + + public Exiler( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class GlovesOfTheSun : LeatherNinjaMitts + { + public override int LabelNumber { get { return 1070924; } } // Gloves of the Sun + + public override int BaseFireResistance { get { return 24; } } + + [Constructable] + public GlovesOfTheSun() : base() + { + Attributes.RegenHits = 2; + Attributes.NightSight = 1; + Attributes.LowerManaCost = 5; + Attributes.LowerRegCost = 18; + } + + public GlovesOfTheSun( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class HanzosBow : Yumi + { + public override int LabelNumber { get { return 1070918; } } // Hanzo's Bow + + [Constructable] + public HanzosBow() : base() + { + WeaponAttributes.HitLeechHits = 40; + WeaponAttributes.SelfRepair = 3; + + Attributes.WeaponDamage = 50; + + SkillBonuses.SetValues( 0, SkillName.Ninjitsu, 10 ); + } + + public HanzosBow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class LegsOfStability : PlateSuneate + { + public override int LabelNumber { get { return 1070925; } } // Legs of Stability + + public override int BasePhysicalResistance { get { return 20; } } + public override int BasePoisonResistance { get { return 18; } } + + [Constructable] + public LegsOfStability() : base() + { + Attributes.BonusStam = 5; + + ArmorAttributes.SelfRepair = 3; + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + } + + public LegsOfStability( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class PeasantsBokuto : Bokuto + { + public override int LabelNumber { get { return 1070912; } } // Peasant's Bokuto + + [Constructable] + public PeasantsBokuto() : base() + { + WeaponAttributes.SelfRepair = 3; + WeaponAttributes.HitLowerDefend = 30; + + Attributes.WeaponDamage = 35; + Attributes.WeaponSpeed = 10; + Slayer = SlayerName.SnakesBane; + } + + public PeasantsBokuto( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class PilferedDancerFans : Tessen + { + public override int LabelNumber { get { return 1070916; } } // Pilfered Dancer Fans + + [Constructable] + public PilferedDancerFans() : base() + { + Attributes.WeaponDamage = 20; + Attributes.WeaponSpeed = 20; + Attributes.CastRecovery = 2; + Attributes.DefendChance = 5; + Attributes.SpellChanneling = 1; + } + + public PilferedDancerFans( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class TheDestroyer : NoDachi + { + public override int LabelNumber { get { return 1070915; } } // The Destroyer + + [Constructable] + public TheDestroyer() : base() + { + WeaponAttributes.HitLeechStam = 40; + + Attributes.BonusStr = 6; + Attributes.AttackChance = 10; + Attributes.WeaponDamage = 50; + } + + public TheDestroyer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int InitMinHits { get { return 255; } } + public override int InitMaxHits { get { return 255; } } + } + + public class TomeOfEnlightenment : Spellbook + { + public override int LabelNumber { get { return 1070934; } } // Tome of Enlightenment + + [Constructable] + public TomeOfEnlightenment() : base() + { + LootType = LootType.Regular; + Hue = 0x455; + + Attributes.BonusInt = 5; + Attributes.SpellDamage = 10; + Attributes.CastSpeed = 1; + } + + public TomeOfEnlightenment( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + //Non weapon/armor ones: + + public class AncientUrn : Item + { + private static string[] m_Names = new string[] + { + "Akira", + "Avaniaga", + "Aya", + "Chie", + "Emiko", + "Fumiyo", + "Gennai", + "Gennosuke", + "Genjo", + "Hamato", + "Harumi", + "Ikuyo", + "Juri", + "Kaori", + "Kaoru", + "Kiyomori", + "Mayako", + "Motoki", + "Musashi", + "Nami", + "Nobukazu", + "Roku", + "Romi", + "Ryo", + "Sanzo", + "Sakamae", + "Satoshi", + "Takamori", + "Takuro", + "Teruyo", + "Toshiro", + "Yago", + "Yeijiro", + "Yoshi", + "Zeshin" + }; + + public static string[] Names { get { return m_Names; } } + + private string m_UrnName; + + [CommandProperty( AccessLevel.GameMaster )] + public string UrnName + { + get { return m_UrnName; } + set { m_UrnName = value; } + } + + public override int LabelNumber { get { return 1071014; } } // Ancient Urn + + [Constructable] + public AncientUrn( string urnName ) : base( 0x241D ) + { + m_UrnName = urnName; + Weight = 1.0; + } + + [Constructable] + public AncientUrn() : this( m_Names[Utility.Random( m_Names.Length )] ) + { + } + + public AncientUrn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + writer.Write( m_UrnName ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + m_UrnName = reader.ReadString(); + + Utility.Intern( ref m_UrnName ); + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( 1070935, m_UrnName ); // Ancient Urn of ~1_name~ + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, 1070935, m_UrnName ); // Ancient Urn of ~1_name~ + } + + } + + public class HonorableSwords : Item + { + private string m_SwordsName; + + [CommandProperty( AccessLevel.GameMaster )] + public string SwordsName + { + get { return m_SwordsName; } + set { m_SwordsName = value; } + } + + public override int LabelNumber { get { return 1071015; } } // Honorable Swords + + [Constructable] + public HonorableSwords( string swordsName ) : base( 0x2853 ) + { + m_SwordsName = swordsName; + + Weight = 5.0; + } + + [Constructable] + public HonorableSwords() : this( AncientUrn.Names[Utility.Random( AncientUrn.Names.Length )] ) + { + } + + public HonorableSwords( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + writer.Write( m_SwordsName ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + m_SwordsName = reader.ReadString(); + + Utility.Intern( ref m_SwordsName ); + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( 1070936, m_SwordsName ); // Honorable Swords of ~1_name~ + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, 1070936, m_SwordsName ); // Honorable Swords of ~1_name~ + } + + //TODO: the Chest of Heirlooms + } + + public class FluteOfRenewal : BambooFlute + { + public override int LabelNumber { get { return 1070927; } } // Flute of Renewal + + [Constructable] + public FluteOfRenewal() : base() + { + Slayer = SlayerGroup.Groups[Utility.Random( SlayerGroup.Groups.Length - 1 )].Super.Name; //-1 to exclude Fey slayer. Try to confrim no fey slayer on this on OSI + + ReplenishesCharges = true; + } + + public override int InitMinUses { get { return 300; } } + public override int InitMaxUses { get { return 300; } } + + public FluteOfRenewal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( version == 0 && Slayer == SlayerName.Fey ) + Slayer = SlayerGroup.Groups[Utility.Random( SlayerGroup.Groups.Length - 1 )].Super.Name; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs b/Scripts/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs new file mode 100644 index 000000000..0369798e3 --- /dev/null +++ b/Scripts/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs @@ -0,0 +1,506 @@ +using System; +using Server; +using Server.Network; +using System.Collections; +using Server.Items; +using Server.Gumps; +using Server.Misc; +using Server.Mobiles; + +namespace Server.Misc +{ + public class TreasuresOfTokuno + { + private static bool m_Enabled = (Core.Expansion == Expansion.SE); + public static bool Enabled { get { return m_Enabled; } } + + public const int ItemsPerReward = 10; + + private static Type[] m_LesserArtifacts = new Type[] + { + typeof( AncientFarmersKasa ), typeof( AncientSamuraiDo ), typeof( ArmsOfTacticalExcellence ), typeof( BlackLotusHood ), + typeof( DaimyosHelm ), typeof( DemonForks ), typeof( DragonNunchaku ), typeof( Exiler ), typeof( GlovesOfTheSun ), + typeof( HanzosBow ), typeof( LegsOfStability ), typeof( PeasantsBokuto ), typeof( PilferedDancerFans ), typeof( TheDestroyer ), + typeof( TomeOfEnlightenment ), typeof( AncientUrn ), typeof( HonorableSwords ), typeof( PigmentsOfTokuno ), typeof( FluteOfRenewal ) //TODO: Chest of heirlooms + }; + + public static Type[] LesserArtifacts { get { return m_LesserArtifacts; } } + + private static Type[] m_GreaterArtifacts = null; + + public static Type[] GreaterArtifacts + { + get + { + if( m_GreaterArtifacts == null ) + { + m_GreaterArtifacts = new Type[ToTRedeemGump.NormalRewards.Length]; + for( int i = 0; i < m_GreaterArtifacts.Length; i++ ) + { + m_GreaterArtifacts[i] = ToTRedeemGump.NormalRewards[i].Type; + } + } + + return m_GreaterArtifacts; + } + } + + private static bool CheckLocation( Mobile m ) + { + Region r = m.Region; + + if( r.IsPartOf( typeof( Server.Regions.HouseRegion ) ) || Server.Multis.BaseBoat.FindBoatAt( m, m.Map ) != null ) + return false; + //TODO: a CanReach of something check as opposed to above? + + if( r.IsPartOf( "Yomotsu Mines" ) || r.IsPartOf( "Fan Dancer's Dojo" ) ) + return true; + + return (m.Map == Map.Tokuno); + } + + public static void HandleKill( Mobile victim, Mobile killer ) + { + PlayerMobile pm = killer as PlayerMobile; + BaseCreature bc = victim as BaseCreature; + + if( !Enabled || pm == null || bc == null || !CheckLocation( bc ) || !CheckLocation( pm )|| !killer.InRange( victim, 18 )) + return; + + if( bc.Controlled || bc.Owners.Count > 0 || bc.Fame <= 0 ) + return; + + //25000 for 1/100 chance, 10 hyrus + //1500, 1/1000 chance, 20 lizard men for that chance. + + pm.ToTTotalMonsterFame += (int)(bc.Fame * (1 + Math.Sqrt( pm.Luck ) / 100)); + + //This is the Exponentional regression with only 2 datapoints. + //A log. func would also work, but it didn't make as much sense. + //This function isn't OSI exact beign that I don't know OSI's func they used ;p + int x = pm.ToTTotalMonsterFame; + + //const double A = 8.63316841 * Math.Pow( 10, -4 ); + const double A = 0.000863316841; + //const double B = 4.25531915 * Math.Pow( 10, -6 ); + const double B = 0.00000425531915; + + double chance = A * Math.Pow( 10, B * x ); + + if( chance > Utility.RandomDouble() ) + { + Item i = null; + + try + { + i = Activator.CreateInstance( m_LesserArtifacts[Utility.Random( m_LesserArtifacts.Length )] ) as Item; + } + catch + { } + + if( i != null ) + { + if( pm.AddToBackpack( i ) ) + { + pm.SendLocalizedMessage( 1062317 ); // For your valor in combating the fallen beast, a special artifact has been bestowed on you. + pm.ToTTotalMonsterFame = 0; + } + else + { + //Place in bank possibly? + i.Delete(); + } + } + } + } + } +} + +namespace Server.Mobiles +{ + public class IharaSoko : BaseVendor + { + public override bool IsActiveVendor { get { return false; } } + public override bool IsInvulnerable { get { return true; } } + public override bool DisallowAllMoves { get { return true; } } + public override bool ClickTitle { get { return true; } } + public override bool CanTeach { get { return false; } } + + protected ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos { get { return m_SBInfos; } } + public override void InitSBInfo() + { + } + + + public override void InitOutfit() + { + AddItem( new Waraji( 0x711 ) ); + AddItem( new Backpack() ); + AddItem( new Kamishimo( 0x483 ) ); + + Item item = new LightPlateJingasa(); + item.Hue = 0x711; + + AddItem( item ); + } + + + [Constructable] + public IharaSoko() : base( "the Imperial Minister of Trade" ) + { + Name = "Ihara Soko"; + Female = false; + Body = 0x190; + Hue = 0x8403; + } + + public IharaSoko( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override bool CanBeDamaged() + { + return false; + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + //if( !TreasuresOfTokuno.Enabled ) //He still accepts items even if ToTs are turned off. + // return; + + if( m.Alive && m is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m; + + int range = 3; + + if( m.Alive && Math.Abs( Z - m.Z ) < 16 && InRange( m, range ) && !InRange( oldLocation, range ) ) + { + if( pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward ) + { + SayTo( pm, 1070980 ); // Congratulations! You have turned in enough minor treasures to earn a greater reward. + + pm.CloseGump( typeof( ToTTurnInGump ) ); //Sanity + + if( !pm.HasGump( typeof( ToTRedeemGump ) ) ) + pm.SendGump( new ToTRedeemGump( this, false ) ); + } + else + { + if( pm.ToTItemsTurnedIn == 0 ) + SayTo( pm, 1071013 ); // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item. + else + SayTo( pm, 1070981, String.Format( "{0}\t{1}", pm.ToTItemsTurnedIn, TreasuresOfTokuno.ItemsPerReward ) ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + + ArrayList buttons = ToTTurnInGump.FindRedeemableItems( pm ); + + if( buttons.Count > 0 && !pm.HasGump( typeof( ToTTurnInGump ) ) ) + pm.SendGump( new ToTTurnInGump( this, buttons ) ); + } + } + + int leaveRange = 7; + + if( !InRange( m, leaveRange ) && InRange( oldLocation, leaveRange ) ) + { + pm.CloseGump( typeof( ToTRedeemGump ) ); + pm.CloseGump( typeof( ToTTurnInGump ) ); + } + } + } + + public override void TurnToTokuno(){} + } +} + +namespace Server.Gumps +{ + public class ItemTileButtonInfo : ImageTileButtonInfo + { + private Item m_Item; + + public Item Item + { + get { return m_Item; } + set { m_Item = value; } + } + + public ItemTileButtonInfo( Item i ) : base( i.ItemID, i.Hue, ((i.Name == null || i.Name.Length <= 0)? (TextDefinition)i.LabelNumber : (TextDefinition)i.Name ) ) + { + m_Item = i; + } + } + + public class ToTTurnInGump : BaseImageTileButtonsGump + { + public static ArrayList FindRedeemableItems( Mobile m ) + { + Backpack pack = (Backpack)m.Backpack; + if( pack == null ) + return new ArrayList(); + + ArrayList items = new ArrayList( pack.FindItemsByType( TreasuresOfTokuno.LesserArtifacts ) ); + ArrayList buttons = new ArrayList(); + + for( int i = 0; i < items.Count; i++ ) + { + Item item = (Item)items[i]; + //bool acceptable = true; + + /* TODO: + if( item is ChestOfHeirlooms ) + */ + if( item is PigmentsOfTokuno && ((PigmentsOfTokuno)item).Type != PigmentType.None ) + continue; + + buttons.Add( new ItemTileButtonInfo( item ) ); + } + + return buttons; + } + + Mobile m_Collector; + + public ToTTurnInGump( Mobile collector, ArrayList buttons ) : base( 1071012, buttons ) // Click a minor artifact to give it to Ihara Soko. + { + m_Collector = collector; + } + + public ToTTurnInGump( Mobile collector, ItemTileButtonInfo[] buttons ) : base( 1071012, buttons ) // Click a minor artifact to give it to Ihara Soko. + { + m_Collector = collector; + } + + public override void HandleButtonResponse( NetState sender, int adjustedButton, ImageTileButtonInfo buttonInfo ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + Item item = ((ItemTileButtonInfo)buttonInfo).Item; + + if( !( pm != null && item.IsChildOf( pm.Backpack ) && pm.InRange( m_Collector.Location, 7 )) ) + return; + + item.Delete(); + + if( ++pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward ) + { + m_Collector.SayTo( pm, 1070980 ); // Congratulations! You have turned in enough minor treasures to earn a greater reward. + + pm.CloseGump( typeof( ToTTurnInGump ) ); //SAnity + + if( !pm.HasGump( typeof( ToTRedeemGump ) ) ) + pm.SendGump( new ToTRedeemGump( m_Collector, false ) ); + } + else + { + m_Collector.SayTo( pm, 1070981, String.Format( "{0}\t{1}", pm.ToTItemsTurnedIn, TreasuresOfTokuno.ItemsPerReward ) ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + + ArrayList buttons = FindRedeemableItems( pm ); + + pm.CloseGump( typeof( ToTTurnInGump ) ); //Sanity + + if( buttons.Count > 0 ) + pm.SendGump( new ToTTurnInGump( m_Collector, buttons ) ); + } + } + + public override void HandleCancel( NetState sender ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !pm.InRange( m_Collector.Location, 7 ) ) + return; + + if( pm.ToTItemsTurnedIn == 0 ) + m_Collector.SayTo( pm, 1071013 ); // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item. + else if( pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward ) //This case should ALWAYS be true with this gump, jsut a sanity check + m_Collector.SayTo( pm, 1070981, String.Format( "{0}\t{1}", pm.ToTItemsTurnedIn, TreasuresOfTokuno.ItemsPerReward ) ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + else + m_Collector.SayTo( pm, 1070982 ); // When you wish to choose your reward, you have but to approach me again. + } + + } +} + +namespace Server.Gumps +{ + public class ToTRedeemGump : BaseImageTileButtonsGump + { + public class TypeTileButtonInfo : ImageTileButtonInfo + { + private Type m_Type; + + public Type Type { get { return m_Type; } } + + public TypeTileButtonInfo( Type type, int itemID, int hue, TextDefinition label, int localizedToolTip ) : base( itemID, hue, label, localizedToolTip ) + { + m_Type = type; + } + + public TypeTileButtonInfo( Type type, int itemID, TextDefinition label ) : this( type, itemID, 0, label, -1 ) + { + } + + public TypeTileButtonInfo( Type type, int itemID, TextDefinition label, int localizedToolTip ) : this( type, itemID, 0, label, localizedToolTip ) + { + } + } + + public class PigmentsTileButtonInfo : ImageTileButtonInfo + { + private PigmentType m_Pigment; + + public PigmentType Pigment + { + get + { + return m_Pigment; + } + + set + { + m_Pigment = value; + } + } + + public PigmentsTileButtonInfo( PigmentType p ) : base( 0xEFF, PigmentsOfTokuno.PigmentInfo.GetInfo( p ).Hue, PigmentsOfTokuno.PigmentInfo.GetInfo( p ).Label ) + { + m_Pigment = p; + } + } + + private static TypeTileButtonInfo[] m_NormalRewards = new TypeTileButtonInfo[] + { + new TypeTileButtonInfo( typeof( SwordsOfProsperity ), 0x27A9, 1070963, 1071002 ), + new TypeTileButtonInfo( typeof( SwordOfTheStampede ), 0x27A2, 1070964, 1070978 ), + new TypeTileButtonInfo( typeof( WindsEdge ), 0x27A3, 1070965, 1071003 ), + new TypeTileButtonInfo( typeof( DarkenedSky ), 0x27AD, 1070966, 1071004 ), + new TypeTileButtonInfo( typeof( TheHorselord ), 0x27A5, 1070967, 1071005 ), + new TypeTileButtonInfo( typeof( RuneBeetleCarapace ), 0x277D, 1070968, 1071006 ), + new TypeTileButtonInfo( typeof( KasaOfTheRajin ), 0x2798, 1070969, 1071007 ), + new TypeTileButtonInfo( typeof( Stormgrip ), 0x2792, 1070970, 1071008 ), + new TypeTileButtonInfo( typeof( TomeOfLostKnowledge ), 0xEFA, 0x530, 1070971, 1071009 ), + new TypeTileButtonInfo( typeof( PigmentsOfTokuno ), 0xEFF, 1070933, 1071011 ) + }; + + public static TypeTileButtonInfo[] NormalRewards { get { return m_NormalRewards; } } + + private static PigmentsTileButtonInfo[] m_PigmentRewards = new PigmentsTileButtonInfo[] + { + new PigmentsTileButtonInfo( PigmentType.ParagonGold ), + new PigmentsTileButtonInfo( PigmentType.VioletCouragePurple ), + new PigmentsTileButtonInfo( PigmentType.InvulnerabilityBlue ), + new PigmentsTileButtonInfo( PigmentType.LunaWhite ), + new PigmentsTileButtonInfo( PigmentType.DryadGreen ), + new PigmentsTileButtonInfo( PigmentType.ShadowDancerBlack ), + new PigmentsTileButtonInfo( PigmentType.BerserkerRed ), + new PigmentsTileButtonInfo( PigmentType.NoxGreen ), + new PigmentsTileButtonInfo( PigmentType.RumRed ), + new PigmentsTileButtonInfo( PigmentType.FireOrange ) + }; + + public static PigmentsTileButtonInfo[] PigmentRewards { get { return m_PigmentRewards; } } + + private Mobile m_Collector; + + public ToTRedeemGump( Mobile collector, bool pigments ) : base( pigments ? 1070986 : 1070985, pigments ? (ImageTileButtonInfo[])m_PigmentRewards : (ImageTileButtonInfo[])m_NormalRewards ) + { + m_Collector = collector; + } + + public override void HandleButtonResponse( NetState sender, int adjustedButton, ImageTileButtonInfo buttonInfo ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !pm.InRange( m_Collector.Location, 7 ) || !(pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward) ) + return; + + bool pigments = (buttonInfo is PigmentsTileButtonInfo); + + Item item = null; + + if( pigments ) + { + PigmentsTileButtonInfo p = buttonInfo as PigmentsTileButtonInfo; + + item = new PigmentsOfTokuno( p.Pigment ); + } + else + { + TypeTileButtonInfo t = buttonInfo as TypeTileButtonInfo; + + if( t.Type == typeof( PigmentsOfTokuno ) ) //Special case of course. + { + pm.CloseGump( typeof( ToTTurnInGump ) ); //Sanity + pm.CloseGump( typeof( ToTRedeemGump ) ); + + pm.SendGump( new ToTRedeemGump( m_Collector, true ) ); + + return; + } + + try + { + item = (Item)Activator.CreateInstance( t.Type ); + } + catch { } + } + + if( item == null ) + return; //Sanity + + if( pm.AddToBackpack( item ) ) + { + pm.ToTItemsTurnedIn -= TreasuresOfTokuno.ItemsPerReward; + m_Collector.SayTo( pm, 1070984, (item.Name == null || item.Name.Length <= 0)? String.Format( "#{0}", item.LabelNumber ) : item.Name ); // You have earned the gratitude of the Empire. I have placed the ~1_OBJTYPE~ in your backpack. + } + else + { + item.Delete(); + m_Collector.SayTo( pm, 500722 ); // You don't have enough room in your backpack! + m_Collector.SayTo( pm, 1070982 ); // When you wish to choose your reward, you have but to approach me again. + } + } + + + public override void HandleCancel( NetState sender ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !pm.InRange( m_Collector.Location, 7 ) ) + return; + + if( pm.ToTItemsTurnedIn == 0 ) + m_Collector.SayTo( pm, 1071013 ); // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item. + else if( pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward ) //This and above case should ALWAYS be FALSE with this gump, jsut a sanity check + m_Collector.SayTo( pm, 1070981, String.Format( "{0}\t{1}", pm.ToTItemsTurnedIn, TreasuresOfTokuno.ItemsPerReward ) ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward. + else + m_Collector.SayTo( pm, 1070982 ); // When you wish to choose your reward, you have but to approach me again. + + } + } +} + +/* Notes + +Pigments of tokuno do NOT check for if item is already hued 0; APPARENTLY he still accepts it if it's < 10 charges. + +Chest of Heirlooms don't show if unlocked. + +Chest of heirlooms, locked, HARD to pick at 100 lock picking but not impossible. had 95 health to 0, cause it's trapped >< (explosion i think) +*/ \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardCategory.cs b/Scripts/Engines/VeteranRewards/RewardCategory.cs new file mode 100644 index 000000000..8a975c153 --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardCategory.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections; + +namespace Server.Engines.VeteranRewards +{ + public class RewardCategory + { + private int m_Name; + private string m_NameString; + private ArrayList m_Entries; + + public int Name{ get{ return m_Name; } } + public string NameString{ get{ return m_NameString; } } + public ArrayList Entries{ get{ return m_Entries; } } + + public RewardCategory( int name ) + { + m_Name = name; + m_Entries = new ArrayList(); + } + + public RewardCategory( string name ) + { + m_NameString = name; + m_Entries = new ArrayList(); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardChoiceGump.cs b/Scripts/Engines/VeteranRewards/RewardChoiceGump.cs new file mode 100644 index 000000000..ef7c2166d --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardChoiceGump.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.VeteranRewards +{ + public class RewardChoiceGump : Gump + { + private Mobile m_From; + + private void RenderBackground() + { + AddPage( 0 ); + + AddBackground( 10, 10, 600, 450, 2600 ); + + AddButton( 530, 415, 4017, 4019, 0, GumpButtonType.Reply, 0 ); + + AddButton( 60, 415, 4014, 4016, 0, GumpButtonType.Page, 1 ); + AddHtmlLocalized( 95, 415, 200, 20, 1049755, false, false ); // Main Menu + } + + private void RenderCategories() + { + TimeSpan rewardInterval = RewardSystem.RewardInterval; + + string intervalAsString; + + if ( rewardInterval == TimeSpan.FromDays( 30.0 ) ) + intervalAsString = "month"; + else if ( rewardInterval == TimeSpan.FromDays( 60.0 ) ) + intervalAsString = "two months"; + else if ( rewardInterval == TimeSpan.FromDays( 90.0 ) ) + intervalAsString = "three months"; + else if ( rewardInterval == TimeSpan.FromDays( 365.0 ) ) + intervalAsString = "year"; + else + intervalAsString = String.Format( "{0} day{1}", rewardInterval.TotalDays, rewardInterval.TotalDays == 1 ? "" : "s" ); + + AddPage( 1 ); + + AddHtml( 60, 35, 500, 70, "Ultima Online Rewards Program
" + + "Thank you for being a part of the Ultima Online community for a full " + intervalAsString + ". " + + "As a token of our appreciation, you may select from the following in-game reward items listed below. " + + "The gift items will be attributed to the character you have logged-in with on the shard you are on when you chose the item(s). " + + "The number of rewards you are entitled to are listed below and are for your entire account. " + + "To read more about these rewards before making a selection, feel free to visit the uo.com site at " + + "http://www.uo.com/rewards.", true, true ); + + int cur, max; + + RewardSystem.ComputeRewardInfo( m_From, out cur, out max ); + + AddHtmlLocalized( 60, 105, 300, 35, 1006006, false, false ); // Your current total of rewards to choose: + AddLabel( 370, 107, 50, (max - cur).ToString() ); + + AddHtmlLocalized( 60, 140, 300, 35, 1006007, false, false ); // You have already chosen: + AddLabel( 370, 142, 50, cur.ToString() ); + + RewardCategory[] categories = RewardSystem.Categories; + + for ( int i = 0; i < categories.Length; ++i ) + { + if ( categories[i].Entries.Count == 0 ) + continue; + + if ( !RewardSystem.HasAccess( m_From, (RewardEntry)categories[i].Entries[0] ) ) + continue; + + AddButton( 100, 180 + (i * 40), 4005, 4005, 0, GumpButtonType.Page, 2 + i ); + + if ( categories[i].NameString != null ) + AddHtml( 135, 180 + (i * 40), 300, 20, categories[i].NameString, false, false ); + else + AddHtmlLocalized( 135, 180 + (i * 40), 300, 20, categories[i].Name, false, false ); + } + + for ( int i = 0; i < categories.Length; ++i ) + RenderCategory( categories[i], i ); + } + + private int GetButtonID( int type, int index ) + { + return 2 + (index * 20) + type; + } + + private void RenderCategory( RewardCategory category, int index ) + { + AddPage( 2 + index ); + + ArrayList entries = category.Entries; + + for ( int i = 0; i < entries.Count; ++i ) + { + RewardEntry entry = (RewardEntry)entries[i]; + + if ( !RewardSystem.HasAccess( m_From, entry ) ) + break; + + AddButton( 55 + ((i / 12) * 250), 80 + ((i % 12) * 25), 5540, 5541, GetButtonID( index, i ), GumpButtonType.Reply, 0 ); + + if ( entry.NameString != null ) + AddHtml( 80 + ((i / 12) * 250), 80 + ((i % 12) * 25), 250, 20, entry.NameString, false, false ); + else + AddHtmlLocalized( 80 + ((i / 12) * 250), 80 + ((i % 12) * 25), 250, 20, entry.Name, false, false ); + } + } + + public RewardChoiceGump( Mobile from ) : base( 0, 0 ) + { + m_From = from; + + from.CloseGump( typeof( RewardChoiceGump ) ); + + RenderBackground(); + RenderCategories(); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int buttonID = info.ButtonID - 1; + + if ( buttonID == 0 ) + { + int cur, max; + + RewardSystem.ComputeRewardInfo( m_From, out cur, out max ); + + if ( cur < max ) + m_From.SendGump( new RewardNoticeGump( m_From ) ); + } + else + { + --buttonID; + + int type = (buttonID % 20); + int index = (buttonID / 20); + + RewardCategory[] categories = RewardSystem.Categories; + + if ( type >= 0 && type < categories.Length ) + { + RewardCategory category = categories[type]; + + if ( index >= 0 && index < category.Entries.Count ) + { + RewardEntry entry = (RewardEntry)category.Entries[index]; + + if ( !RewardSystem.HasAccess( m_From, entry ) ) + return; + + m_From.SendGump( new RewardConfirmGump( m_From, entry ) ); + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardConfirmGump.cs b/Scripts/Engines/VeteranRewards/RewardConfirmGump.cs new file mode 100644 index 000000000..095c461e1 --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardConfirmGump.cs @@ -0,0 +1,69 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.VeteranRewards +{ + public class RewardConfirmGump : Gump + { + private Mobile m_From; + private RewardEntry m_Entry; + + public RewardConfirmGump( Mobile from, RewardEntry entry ) : base( 0, 0 ) + { + m_From = from; + m_Entry = entry; + + from.CloseGump( typeof( RewardConfirmGump ) ); + + AddPage( 0 ); + + AddBackground( 10, 10, 500, 300, 2600 ); + + AddHtmlLocalized( 30, 55, 300, 35, 1006000, false, false ); // You have selected: + + if ( entry.NameString != null ) + AddHtml( 335, 55, 150, 35, entry.NameString, false, false ); + else + AddHtmlLocalized( 335, 55, 150, 35, entry.Name, false, false ); + + AddHtmlLocalized( 30, 95, 300, 35, 1006001, false, false ); // This will be assigned to this character: + AddLabel( 335, 95, 0, from.Name ); + + AddHtmlLocalized( 35, 160, 450, 90, 1006002, true, true ); // Are you sure you wish to select this reward for this character? You will not be able to transfer this reward to another character on another shard. Click 'ok' below to confirm your selection or 'cancel' to go back to the selection screen. + + AddButton( 60, 265, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 95, 266, 150, 35, 1006044, false, false ); // Ok + + AddButton( 295, 265, 4017, 4019, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 330, 266, 150, 35, 1006045, false, false ); // Cancel + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + { + if ( !RewardSystem.HasAccess( m_From, m_Entry ) ) + return; + + Item item = m_Entry.Construct(); + + if ( item != null ) + { + if ( RewardSystem.ConsumeRewardPoint( m_From ) ) + m_From.AddToBackpack( item ); + else + item.Delete(); + } + } + + int cur, max; + + RewardSystem.ComputeRewardInfo( m_From, out cur, out max ); + + if ( cur < max ) + m_From.SendGump( new RewardNoticeGump( m_From ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardEntry.cs b/Scripts/Engines/VeteranRewards/RewardEntry.cs new file mode 100644 index 000000000..7a2d5f810 --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardEntry.cs @@ -0,0 +1,57 @@ +using System; + +namespace Server.Engines.VeteranRewards +{ + public class RewardEntry + { + private RewardList m_List; + private RewardCategory m_Category; + private Type m_ItemType; + private int m_Name; + private string m_NameString; + private object[] m_Args; + + public RewardList List{ get{ return m_List; } set{ m_List = value; } } + public RewardCategory Category{ get{ return m_Category; } } + public Type ItemType{ get{ return m_ItemType; } } + public int Name{ get{ return m_Name; } } + public string NameString{ get{ return m_NameString; } } + public object[] Args{ get{ return m_Args; } } + + public Item Construct() + { + try + { + Item item = Activator.CreateInstance( m_ItemType, m_Args ) as Item; + + if ( item is IRewardItem ) + ((IRewardItem)item).IsRewardItem = true; + + return item; + } + catch + { + } + + return null; + } + + public RewardEntry( RewardCategory category, int name, Type itemType, params object[] args ) + { + m_Category = category; + m_ItemType = itemType; + m_Name = name; + m_Args = args; + category.Entries.Add( this ); + } + + public RewardEntry( RewardCategory category, string name, Type itemType, params object[] args ) + { + m_Category = category; + m_ItemType = itemType; + m_NameString = name; + m_Args = args; + category.Entries.Add( this ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardList.cs b/Scripts/Engines/VeteranRewards/RewardList.cs new file mode 100644 index 000000000..c8a6517b1 --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardList.cs @@ -0,0 +1,22 @@ +using System; + +namespace Server.Engines.VeteranRewards +{ + public class RewardList + { + private TimeSpan m_Age; + private RewardEntry[] m_Entries; + + public TimeSpan Age{ get{ return m_Age; } } + public RewardEntry[] Entries{ get{ return m_Entries; } } + + public RewardList( TimeSpan interval, int index, RewardEntry[] entries ) + { + m_Age = TimeSpan.FromDays( interval.TotalDays * index ); + m_Entries = entries; + + for ( int i = 0; i < entries.Length; ++i ) + entries[i].List = this; + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardNoticeGump.cs b/Scripts/Engines/VeteranRewards/RewardNoticeGump.cs new file mode 100644 index 000000000..a978f5e5f --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardNoticeGump.cs @@ -0,0 +1,40 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Engines.VeteranRewards +{ + public class RewardNoticeGump : Gump + { + private Mobile m_From; + + public RewardNoticeGump( Mobile from ) : base( 0, 0 ) + { + m_From = from; + + from.CloseGump( typeof( RewardNoticeGump ) ); + + AddPage( 0 ); + + AddBackground( 10, 10, 500, 135, 2600 ); + + /* You have reward items available. + * Click 'ok' below to get the selection menu or 'cancel' to be prompted upon your next login. + */ + AddHtmlLocalized( 52, 35, 420, 55, 1006046, true, true ); + + AddButton( 60, 95, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 95, 96, 150, 35, 1006044, false, false ); // Ok + + AddButton( 285, 95, 4017, 4019, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 320, 96, 150, 35, 1006045, false, false ); // Cancel + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_From.SendGump( new RewardChoiceGump( m_From ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/VeteranRewards/RewardSystem.cs b/Scripts/Engines/VeteranRewards/RewardSystem.cs new file mode 100644 index 000000000..a5f9f068f --- /dev/null +++ b/Scripts/Engines/VeteranRewards/RewardSystem.cs @@ -0,0 +1,361 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Accounting; + +namespace Server.Engines.VeteranRewards +{ + public class RewardSystem + { + private static RewardCategory[] m_Categories; + private static RewardList[] m_Lists; + + public static RewardCategory[] Categories + { + get + { + if ( m_Categories == null ) + SetupRewardTables(); + + return m_Categories; + } + } + + public static RewardList[] Lists + { + get + { + if ( m_Lists == null ) + SetupRewardTables(); + + return m_Lists; + } + } + + public static bool Enabled = true; // change to true to enable vet rewards + public static bool SkillCapRewards = true; // assuming vet rewards are enabled, should total skill cap bonuses be awarded? (720 skills total at 4th level) + public static TimeSpan RewardInterval = TimeSpan.FromDays( 30.0 ); + + public static bool HasAccess( Mobile mob, RewardEntry entry ) + { + TimeSpan ts; + return HasAccess( mob, entry.List, out ts ); + } + + public static bool HasAccess( Mobile mob, RewardList list, out TimeSpan ts ) + { + if ( list == null ) + { + ts = TimeSpan.Zero; + return false; + } + + Account acct = mob.Account as Account; + + if ( acct == null ) + { + ts = TimeSpan.Zero; + return false; + } + + TimeSpan totalTime = (DateTime.Now - acct.Created); + + ts = ( list.Age - totalTime ); + + if ( ts <= TimeSpan.Zero ) + return true; + + return false; + } + + public static int GetRewardLevel( Mobile mob ) + { + Account acct = mob.Account as Account; + + if ( acct == null ) + return 0; + + return GetRewardLevel( acct ); + } + + public static int GetRewardLevel( Account acct ) + { + TimeSpan totalTime = (DateTime.Now - acct.Created); + + int level = (int)(totalTime.TotalDays / RewardInterval.TotalDays); + + if ( level < 0 ) + level = 0; + + return level; + } + + public static bool ConsumeRewardPoint( Mobile mob ) + { + int cur, max; + + ComputeRewardInfo( mob, out cur, out max ); + + if ( cur >= max ) + return false; + + Account acct = mob.Account as Account; + + if ( acct == null ) + return false; + + //if ( mob.AccessLevel < AccessLevel.GameMaster ) + acct.SetTag( "numRewardsChosen", (cur + 1).ToString() ); + + return true; + } + + public static void ComputeRewardInfo( Mobile mob, out int cur, out int max ) + { + int level; + + ComputeRewardInfo( mob, out cur, out max, out level ); + } + + public static void ComputeRewardInfo( Mobile mob, out int cur, out int max, out int level ) + { + Account acct = mob.Account as Account; + + if ( acct == null ) + { + cur = max = level = 0; + return; + } + + level = GetRewardLevel( acct ); + + if ( level == 0 ) + { + cur = max = 0; + return; + } + + string tag = acct.GetTag( "numRewardsChosen" ); + + if ( tag == null || tag == "" ) + cur = 0; + else + cur = Utility.ToInt32( tag ); + + if ( level >= 6 ) + max = 9 + ((level - 6) * 2); + else + max = 2 + level; + } + + public static bool CheckIsUsableBy( Mobile from, Item item, object[] args ) + { + if ( m_Lists == null ) + SetupRewardTables(); + + bool isRelaxedRules = ( item is DyeTub || item is MonsterStatuette ); + + Type type = item.GetType(); + + for ( int i = 0; i < m_Lists.Length; ++i ) + { + RewardList list = m_Lists[i]; + RewardEntry[] entries = list.Entries; + TimeSpan ts; + + for ( int j = 0; j < entries.Length; ++j ) + { + if ( entries[j].ItemType == type ) + { + if ( args == null && entries[j].Args.Length == 0 ) + { + if ( (!isRelaxedRules || i > 0) && !HasAccess( from, list, out ts ) ) + { + from.SendLocalizedMessage( 1008126, true, Math.Ceiling( ts.TotalDays / 30.0 ).ToString() ); // Your account is not old enough to use this item. Months until you can use this item : + return false; + } + + return true; + } + + if ( args.Length == entries[j].Args.Length ) + { + bool match = true; + + for ( int k = 0; match && k < args.Length; ++k ) + match = ( args[k].Equals( entries[j].Args[k] ) ); + + if ( match ) + { + if ( (!isRelaxedRules || i > 0) && !HasAccess( from, list, out ts ) ) + { + from.SendLocalizedMessage( 1008126, true, Math.Ceiling( ts.TotalDays / 30.0 ).ToString() ); // Your account is not old enough to use this item. Months until you can use this item : + return false; + } + + return true; + } + } + } + } + } + + // no entry? + return true; + } + + public static void SetupRewardTables() + { + RewardCategory monsterStatues = new RewardCategory( 1049750 ); + RewardCategory cloaksAndRobes = new RewardCategory( 1049752 ); + RewardCategory etherealSteeds = new RewardCategory( 1049751 ); + RewardCategory specialDyeTubs = new RewardCategory( 1049753 ); + RewardCategory houseAddOns = new RewardCategory( 1049754 ); + + m_Categories = new RewardCategory[] + { + monsterStatues, + cloaksAndRobes, + etherealSteeds, + specialDyeTubs, + houseAddOns + }; + + const int Bronze = 0x972; + const int Copper = 0x96D; + const int Golden = 0x8A5; + const int Agapite = 0x979; + const int Verite = 0x89F; + const int Valorite = 0x8AB; + const int IceGreen = 0x47F; + const int IceBlue = 0x482; + const int DarkGray = 0x497; + const int Fire = 0x489; + const int IceWhite = 0x47E; + const int JetBlack = 0x001; + + m_Lists = new RewardList[] + { + new RewardList( RewardInterval, 1, new RewardEntry[] + { + new RewardEntry( specialDyeTubs, 1006008, typeof( RewardBlackDyeTub ) ), + new RewardEntry( specialDyeTubs, 1006013, typeof( FurnitureDyeTub ) ), + new RewardEntry( specialDyeTubs, 1006047, typeof( SpecialDyeTub ) ), + new RewardEntry( cloaksAndRobes, 1006009, typeof( RewardCloak ), Bronze, 1041286 ), + new RewardEntry( cloaksAndRobes, 1006010, typeof( RewardRobe ), Bronze, 1041287 ), + new RewardEntry( cloaksAndRobes, 1006011, typeof( RewardCloak ), Copper, 1041288 ), + new RewardEntry( cloaksAndRobes, 1006012, typeof( RewardRobe ), Copper, 1041289 ), + new RewardEntry( monsterStatues, 1006024, typeof( MonsterStatuette ), MonsterStatuetteType.Crocodile ), + new RewardEntry( monsterStatues, 1006025, typeof( MonsterStatuette ), MonsterStatuetteType.Daemon ), + new RewardEntry( monsterStatues, 1006026, typeof( MonsterStatuette ), MonsterStatuetteType.Dragon ), + new RewardEntry( monsterStatues, 1006027, typeof( MonsterStatuette ), MonsterStatuetteType.EarthElemental ), + new RewardEntry( monsterStatues, 1006028, typeof( MonsterStatuette ), MonsterStatuetteType.Ettin ), + new RewardEntry( monsterStatues, 1006029, typeof( MonsterStatuette ), MonsterStatuetteType.Gargoyle ), + new RewardEntry( monsterStatues, 1006030, typeof( MonsterStatuette ), MonsterStatuetteType.Gorilla ), + new RewardEntry( monsterStatues, 1006031, typeof( MonsterStatuette ), MonsterStatuetteType.Lich ), + new RewardEntry( monsterStatues, 1006032, typeof( MonsterStatuette ), MonsterStatuetteType.Lizardman ), + new RewardEntry( monsterStatues, 1006033, typeof( MonsterStatuette ), MonsterStatuetteType.Ogre ), + new RewardEntry( monsterStatues, 1006034, typeof( MonsterStatuette ), MonsterStatuetteType.Orc ), + new RewardEntry( monsterStatues, 1006035, typeof( MonsterStatuette ), MonsterStatuetteType.Ratman ), + new RewardEntry( monsterStatues, 1006036, typeof( MonsterStatuette ), MonsterStatuetteType.Skeleton ), + new RewardEntry( monsterStatues, 1006037, typeof( MonsterStatuette ), MonsterStatuetteType.Troll ) + } ), + new RewardList( RewardInterval, 2, new RewardEntry[] + { + new RewardEntry( specialDyeTubs, 1006052, typeof( LeatherDyeTub ) ), + new RewardEntry( cloaksAndRobes, 1006014, typeof( RewardCloak ), Agapite, 1041290 ), + new RewardEntry( cloaksAndRobes, 1006015, typeof( RewardRobe ), Agapite, 1041291 ), + new RewardEntry( cloaksAndRobes, 1006016, typeof( RewardCloak ), Golden, 1041292 ), + new RewardEntry( cloaksAndRobes, 1006017, typeof( RewardRobe ), Golden, 1041293 ) + /*new RewardEntry( houseAddOns, 0, typeof( BannerDeed ) ), + new RewardEntry( houseAddOns, 0, typeof( FlamingSkullDeed ) )*/ + } ), + new RewardList( RewardInterval, 3, new RewardEntry[] + { + new RewardEntry( cloaksAndRobes, 1006020, typeof( RewardCloak ), Verite, 1041294 ), + new RewardEntry( cloaksAndRobes, 1006021, typeof( RewardRobe ), Verite, 1041295 ), + new RewardEntry( cloaksAndRobes, 1006022, typeof( RewardCloak ), Valorite, 1041296 ), + new RewardEntry( cloaksAndRobes, 1006023, typeof( RewardRobe ), Valorite, 1041297 ), + new RewardEntry( monsterStatues, 1006038, typeof( MonsterStatuette ), MonsterStatuetteType.Cow ), + new RewardEntry( monsterStatues, 1006039, typeof( MonsterStatuette ), MonsterStatuetteType.Zombie ), + new RewardEntry( monsterStatues, 1006040, typeof( MonsterStatuette ), MonsterStatuetteType.Llama ), + new RewardEntry( etherealSteeds, 1006019, typeof( EtherealHorse ) ), + new RewardEntry( etherealSteeds, 1006050, typeof( EtherealOstard ) ), + new RewardEntry( etherealSteeds, 1006051, typeof( EtherealLlama ) ) + } ), + new RewardList( RewardInterval, 4, new RewardEntry[] + { + new RewardEntry( specialDyeTubs, 1049740, typeof( RunebookDyeTub ) ), + new RewardEntry( cloaksAndRobes, 1049725, typeof( RewardCloak ), DarkGray, 1049757 ), + new RewardEntry( cloaksAndRobes, 1049726, typeof( RewardRobe ), DarkGray, 1049756 ), + new RewardEntry( cloaksAndRobes, 1049727, typeof( RewardCloak ), IceGreen, 1049759 ), + new RewardEntry( cloaksAndRobes, 1049728, typeof( RewardRobe ), IceGreen, 1049758 ), + new RewardEntry( cloaksAndRobes, 1049729, typeof( RewardCloak ), IceBlue, 1049761 ), + new RewardEntry( cloaksAndRobes, 1049730, typeof( RewardRobe ), IceBlue, 1049760 ), + new RewardEntry( monsterStatues, 1049742, typeof( MonsterStatuette ), MonsterStatuetteType.Ophidian ), + new RewardEntry( monsterStatues, 1049743, typeof( MonsterStatuette ), MonsterStatuetteType.Reaper ), + new RewardEntry( monsterStatues, 1049744, typeof( MonsterStatuette ), MonsterStatuetteType.Mongbat ), + new RewardEntry( etherealSteeds, 1049746, typeof( EtherealKirin ) ), + new RewardEntry( etherealSteeds, 1049745, typeof( EtherealUnicorn ) ), + new RewardEntry( etherealSteeds, 1049747, typeof( EtherealRidgeback ) ) + /*new RewardEntry( houseAddOns, 0, typeof( DecorativeShieldWallHanging ) ), + new RewardEntry( houseAddOns, 0, typeof( SkeletonWallHanging ) )*/ + } ), + new RewardList( RewardInterval, 5, new RewardEntry[] + { + new RewardEntry( specialDyeTubs, 1049741, typeof( StatuetteDyeTub ) ), + new RewardEntry( cloaksAndRobes, 1049731, typeof( RewardCloak ), JetBlack, 1049763 ), + new RewardEntry( cloaksAndRobes, 1049732, typeof( RewardRobe ), JetBlack, 1049762 ), + new RewardEntry( cloaksAndRobes, 1049733, typeof( RewardCloak ), IceWhite, 1049765 ), + new RewardEntry( cloaksAndRobes, 1049734, typeof( RewardRobe ), IceWhite, 1049764 ), + new RewardEntry( cloaksAndRobes, 1049735, typeof( RewardCloak ), Fire, 1049767 ), + new RewardEntry( cloaksAndRobes, 1049736, typeof( RewardRobe ), Fire, 1049766 ), + new RewardEntry( monsterStatues, 1049768, typeof( MonsterStatuette ), MonsterStatuetteType.Gazer ), + new RewardEntry( monsterStatues, 1049769, typeof( MonsterStatuette ), MonsterStatuetteType.FireElemental ), + new RewardEntry( monsterStatues, 1049770, typeof( MonsterStatuette ), MonsterStatuetteType.Wolf ), + new RewardEntry( etherealSteeds, 1049749, typeof( EtherealSwampDragon ) ), + new RewardEntry( etherealSteeds, 1049748, typeof( EtherealBeetle ) ) + /*new RewardEntry( houseAddOns, 0, typeof( AnkhDecoration ) )*/ + } ), + }; + } + + public static void Initialize() + { + if ( Enabled ) + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static void EventSink_Login( LoginEventArgs e ) + { + if ( !e.Mobile.Alive ) + return; + + int cur, max, level; + + ComputeRewardInfo( e.Mobile, out cur, out max, out level ); + + if ( e.Mobile.SkillsCap == 7000 || e.Mobile.SkillsCap == 7050 || e.Mobile.SkillsCap == 7100 || e.Mobile.SkillsCap == 7150 || e.Mobile.SkillsCap == 7200 ) + { + if ( level > 4 ) + level = 4; + else if ( level < 0 ) + level = 0; + + if ( SkillCapRewards ) + e.Mobile.SkillsCap = 7000 + (level * 50); + else + e.Mobile.SkillsCap = 7000; + } + + if ( cur < max ) + e.Mobile.SendGump( new RewardNoticeGump( e.Mobile ) ); + } + } + + public interface IRewardItem + { + bool IsRewardItem{ get; set; } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/Compassion.cs b/Scripts/Engines/Virtues/Compassion.cs new file mode 100644 index 000000000..aadaa4897 --- /dev/null +++ b/Scripts/Engines/Virtues/Compassion.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Targeting; + +namespace Server +{ + public class CompassionVirtue + { + private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 ); + private const int LossAmount = 500; + + public static void Initialize() + { + VirtueGump.Register( 105, new OnVirtueUsed( OnVirtueUsed ) ); + } + + public static void OnVirtueUsed( Mobile from ) + { + from.SendLocalizedMessage( 1053001 ); // This virtue is not activated through the virtue menu. + } + + public static void CheckAtrophy( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + try + { + if ( (pm.LastCompassionLoss + LossDelay) < DateTime.Now ) + { + VirtueHelper.Atrophy( from, VirtueName.Compassion, LossAmount ); + //OSI has no cliloc message for losing compassion. Weird. + pm.LastCompassionLoss = DateTime.Now; + } + } + catch + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/Honor.cs b/Scripts/Engines/Virtues/Honor.cs new file mode 100644 index 000000000..78f4bf56f --- /dev/null +++ b/Scripts/Engines/Virtues/Honor.cs @@ -0,0 +1,412 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; + +namespace Server +{ + public class HonorVirtue + { + private static readonly TimeSpan LossDelay = TimeSpan.FromDays( 7.0 ); + private static readonly int LossAmount = 500; + + private static readonly TimeSpan UseDelay = TimeSpan.FromMinutes( 5.0 ); + + public static void Initialize() + { + VirtueGump.Register( 107, new OnVirtueUsed( OnVirtueUsed ) ); + } + + public static void CheckAtrophy( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + try + { + if ( (pm.LastHonorLoss + LossDelay) < DateTime.Now ) + { + if ( VirtueHelper.Atrophy( from, VirtueName.Honor, LossAmount ) ) + from.SendLocalizedMessage( 1063227 ); // You have lost some Honor. + + pm.LastHonorLoss = DateTime.Now; + } + } + catch + { + } + } + + private static void OnVirtueUsed( Mobile from ) + { + if ( from.Alive ) + { + from.SendLocalizedMessage( 1063160 ); // Target what you wish to honor. + from.Target = new InternalTarget(); + } + } + + private class InternalTarget : Target + { + public InternalTarget() : base( 12, false, TargetFlags.None ) + { + CheckLOS = true; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + PlayerMobile pm = from as PlayerMobile; + if ( pm == null ) + return; + + if ( targeted == pm ) + EmbraceHonor( pm ); + else if ( targeted is Mobile ) + Honor( pm, (Mobile) targeted ); + } + + protected override void OnTargetOutOfRange( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 1063232 ); // You are too far away to honor your opponent + } + } + + private static int GetHonorDuration( PlayerMobile from ) + { + switch ( VirtueHelper.GetLevel( from, VirtueName.Honor ) ) + { + case VirtueLevel.Seeker: return 30; + case VirtueLevel.Follower: return 90; + case VirtueLevel.Knight: return 300; + + default: return 0 ; + } + } + + private static void EmbraceHonor( PlayerMobile pm ) + { + if ( pm.HonorActive ) + { + pm.SendLocalizedMessage( 1063230 ); // You must wait awhile before you can embrace honor again. + return; + } + + int duration = GetHonorDuration( pm ); + if ( duration == 0 ) + { + pm.SendLocalizedMessage( 1063234 ); // You do not have enough honor to do that + return; + } + + TimeSpan waitTime = DateTime.Now - pm.LastHonorUse; + if ( waitTime < UseDelay ) + { + TimeSpan remainingTime = UseDelay - waitTime; + int remainingMinutes = (int) Math.Ceiling( remainingTime.TotalMinutes ); + + pm.SendLocalizedMessage( 1063240, remainingMinutes.ToString() ); // You must wait ~1_HONOR_WAIT~ minutes before embracing honor again + return; + } + + int usedPoints = pm.Virtues.Honor / 20; + VirtueHelper.Atrophy( pm, VirtueName.Honor, usedPoints ); + + pm.HonorActive = true; + pm.SendLocalizedMessage( 1063235 ); // You embrace your honor + + Timer.DelayCall( TimeSpan.FromSeconds( duration ), + delegate() { + pm.HonorActive = false; + pm.LastHonorUse = DateTime.Now; + pm.SendLocalizedMessage( 1063236 ); // You no longer embrace your honor + } ); + } + + private static void Honor( PlayerMobile source, Mobile target ) + { + IHonorTarget honorTarget = target as IHonorTarget; + + if ( honorTarget == null || !source.CanBeHarmful( target, true ) ) + return; + + if ( honorTarget.ReceivedHonorContext != null ) + { + if ( honorTarget.ReceivedHonorContext.Source == source ) + return; + + if ( honorTarget.ReceivedHonorContext.CheckDistance() ) + { + source.SendLocalizedMessage( 1063233 ); // Somebody else is honoring this opponent + return; + } + } + + if ( target.Hits < target.HitsMax ) + { + source.SendLocalizedMessage( 1063166 ); // You cannot honor this monster because it is too damaged. + return; + } + + if ( source.SentHonorContext != null ) + source.SentHonorContext.Cancel(); + + new HonorContext( source, target ); + + source.Direction = source.GetDirectionTo( target ); + + if ( !source.Mounted ) + source.Animate( 32, 5, 1, true, true, 0 ); + + // OSI apparently removed this message... it's nice though + source.Say( 1063231 ); // I honor you + } + } + + public interface IHonorTarget + { + HonorContext ReceivedHonorContext{ get; set; } + } + + public class HonorContext + { + private PlayerMobile m_Source; + private Mobile m_Target; + + private int m_HonorDamageTwentieths; + private int m_TotalDamage; + + private int m_Perfection; + + private enum FirstHit + { + NotDelivered, + Delivered, + Granted + } + + private FirstHit m_FirstHit; + private bool m_Poisoned; + private Point3D m_InitialLocation; + private Map m_InitialMap; + + private InternalTimer m_Timer; + + public PlayerMobile Source{ get{ return m_Source; } } + public Mobile Target{ get{ return m_Target; } } + + public HonorContext( PlayerMobile source, Mobile target ) + { + m_Source = source; + m_Target = target; + + m_FirstHit = FirstHit.NotDelivered; + m_Poisoned = false; + + m_InitialLocation = source.Location; + m_InitialMap = source.Map; + + source.SentHonorContext = this; + ((IHonorTarget)target).ReceivedHonorContext = this; + + m_Timer = new InternalTimer( this ); + m_Timer.Start(); + } + + public void OnSourceDamaged( Mobile from, int amount ) + { + if ( from != m_Target ) + return; + + if ( m_FirstHit == FirstHit.NotDelivered ) + m_FirstHit = FirstHit.Granted; + } + + public void OnTargetPoisoned() + { + m_Poisoned = true; // Set this flag for OnTargetDamaged which will be called next + } + + public void OnTargetDamaged( Mobile from, int amount ) + { + if ( m_FirstHit == FirstHit.NotDelivered ) + m_FirstHit = FirstHit.Delivered; + + if ( m_Poisoned ) + { + m_TotalDamage += amount * 2; + m_Poisoned = false; // Reset the flag + + return; + } + + m_TotalDamage += amount; + + if ( from == m_Source ) + { + if ( m_Target.CanSee( m_Source ) && m_Target.InLOS( m_Source ) && ( m_Source.InRange( m_Target, 1 ) + || ( m_Source.Location == m_InitialLocation && m_Source.Map == m_InitialMap ) ) ) + { + m_HonorDamageTwentieths += amount * 20; + } + else + { + m_HonorDamageTwentieths += amount * 2; + } + } + else if ( from is BaseCreature && ((BaseCreature)from).GetMaster() == from ) + { + m_HonorDamageTwentieths += amount; + } + } + + public void OnTargetHit( Mobile from ) + { + if ( from != m_Source || m_Perfection == 100 ) + return; + + int bushido = (int) from.Skills.Bushido.Value; + if ( bushido < 50 ) + return; + + m_Perfection += bushido / 10; + + if ( m_Perfection >= 100 ) + { + m_Perfection = 100; + m_Source.SendLocalizedMessage( 1063254 ); // You have Achieved Perfection in inflicting damage to this opponent! + } + else + { + m_Source.SendLocalizedMessage( 1063255 ); // You gain in Perfection as you precisely strike your opponent. + } + } + + public void OnTargetMissed( Mobile from ) + { + if ( from != m_Source || m_Perfection == 0 ) + return; + + m_Perfection -= 25; + + if ( m_Perfection <= 0 ) + { + m_Perfection = 0; + m_Source.SendLocalizedMessage( 1063256 ); // You have lost all Perfection in fighting this opponent. + } + else + { + m_Source.SendLocalizedMessage( 1063257 ); // You have lost some Perfection in fighting this opponent. + } + } + + public void OnSourceBeneficialAction( Mobile to ) + { + if ( to != m_Target ) + return; + + if ( m_Perfection >= 0 ) + { + m_Perfection = 0; + m_Source.SendLocalizedMessage( 1063256 ); // You have lost all Perfection in fighting this opponent. + } + } + + public void OnSourceKilled() + { + Cancel(); + } + + public void OnTargetKilled() + { + Cancel(); + + int targetFame = m_Target.Fame; + + if ( m_Perfection > 0 ) + { + int restore = Math.Min( m_Perfection * ( targetFame + 5000 ) / 25000, 10 ); + + m_Source.Hits += restore; + m_Source.Stam += restore; + m_Source.Mana += restore; + } + + if ( m_Source.Virtues.Honor > targetFame ) + return; + + double dGain = ( targetFame * m_HonorDamageTwentieths ) / (double)( 20 * m_TotalDamage ); + dGain = dGain * dGain / 2000000.0; + + if ( m_FirstHit == FirstHit.Granted ) + dGain *= 1.1; // Is this correct? + + int gain = Math.Min( (int)dGain, 200 ); + + if ( gain <= 0 ) + return; + + if ( VirtueHelper.IsHighestPath( m_Source, VirtueName.Honor ) ) + { + m_Source.SendLocalizedMessage( 1063228 ); // You cannot gain more Honor. + return; + } + + bool gainedPath = false; + if ( VirtueHelper.Award( m_Source, VirtueName.Honor, (int) gain, ref gainedPath ) ) + { + if ( gainedPath ) + m_Source.SendLocalizedMessage( 1063226 ); // You have gained a path in Honor! + else + m_Source.SendLocalizedMessage( 1063225 ); // You have gained in Honor. + } + } + + public void ApplyPerfectionDamageBonus( ref double factor ) + { + if ( m_Perfection > 0 ) + factor *= 1.0 + (m_Perfection / 100.0); + } + + public void ApplyPerfectionLuckBonus( ref int luck ) + { + if ( m_Perfection > 0 ) + luck += (m_Perfection * m_Perfection) / 10; + } + + public bool CheckDistance() + { + if ( m_Source.Map == Map.Internal || m_Source.Map != m_Target.Map || !m_Source.InRange( m_Target, 24 ) ) + { + Cancel(); + return false; + } + + return true; + } + + public void Cancel() + { + m_Source.SentHonorContext = null; + ((IHonorTarget)m_Target).ReceivedHonorContext = null; + + m_Timer.Stop(); + } + + private class InternalTimer : Timer + { + private HonorContext m_Context; + + public InternalTimer( HonorContext context ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Context = context; + } + + protected override void OnTick() + { + m_Context.CheckDistance(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/Justice.cs b/Scripts/Engines/Virtues/Justice.cs new file mode 100644 index 000000000..bdc89de3a --- /dev/null +++ b/Scripts/Engines/Virtues/Justice.cs @@ -0,0 +1,248 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Targeting; + +namespace Server +{ + public class JusticeVirtue + { + private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 ); + private const int LossAmount = 950; + + public static void Initialize() + { + VirtueGump.Register( 109, new OnVirtueUsed( OnVirtueUsed ) ); + } + + public static bool CheckMapRegion( Mobile first, Mobile second ) + { + Map map = first.Map; + + if ( second.Map != map ) + return false; + + return GetMapRegion( map, first.Location ) == GetMapRegion( map, second.Location ); + } + + public static int GetMapRegion( Map map, Point3D loc ) + { + if ( map == null || map.MapID >= 2 ) + return 0; + + if ( loc.X < 5120 ) + return 0; + + if ( loc.Y < 2304 ) + return 1; + + return 2; + } + + public static void OnVirtueUsed( Mobile from ) + { + if ( !from.CheckAlive() ) + return; + + PlayerMobile protector = from as PlayerMobile; + + if ( protector == null ) + return; + + if ( !VirtueHelper.IsSeeker( protector, VirtueName.Justice ) ) + { + protector.SendLocalizedMessage( 1049610 ); // You must reach the first path in this virtue to invoke it. + } + else if ( !protector.CanBeginAction( typeof( JusticeVirtue ) ) ) + { + protector.SendLocalizedMessage( 1049370 ); // You must wait a while before offering your protection again. + } + else if ( protector.JusticeProtectors.Count > 0 ) + { + protector.SendLocalizedMessage( 1049542 ); // You cannot protect someone while being protected. + } + else if ( protector.Map != Map.Felucca ) + { + protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here. + } + else + { + protector.BeginTarget( 14, false, TargetFlags.None, new TargetCallback( OnVirtueTargeted ) ); + protector.SendLocalizedMessage( 1049366 ); // Choose the player you wish to protect. + } + } + + public static void OnVirtueTargeted( Mobile from, object obj ) + { + PlayerMobile protector = from as PlayerMobile; + PlayerMobile pm = obj as PlayerMobile; + + if ( protector == null ) + return; + + if ( !VirtueHelper.IsSeeker( protector, VirtueName.Justice ) ) + protector.SendLocalizedMessage( 1049610 ); // You must reach the first path in this virtue to invoke it. + else if ( !protector.CanBeginAction( typeof( JusticeVirtue ) ) ) + protector.SendLocalizedMessage( 1049370 ); // You must wait a while before offering your protection again. + else if ( protector.JusticeProtectors.Count > 0 ) + protector.SendLocalizedMessage( 1049542 ); // You cannot protect someone while being protected. + else if ( protector.Map != Map.Felucca ) + protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here. + else if ( pm == null ) + protector.SendLocalizedMessage( 1049678 ); // Only players can be protected. + else if ( pm.Map != Map.Felucca ) + protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here. + else if ( pm == protector || pm.Criminal || pm.Kills >= 5 ) + protector.SendLocalizedMessage( 1049436 ); // That player cannot be protected. + else if ( pm.JusticeProtectors.Count > 0 ) + protector.SendLocalizedMessage( 1049369 ); // You cannot protect that player right now. + else + pm.SendGump( new AcceptProtectorGump( protector, pm ) ); + } + + public static void OnVirtueAccepted( PlayerMobile protector, PlayerMobile protectee ) + { + if ( !VirtueHelper.IsSeeker( protector, VirtueName.Justice ) ) + { + protector.SendLocalizedMessage( 1049610 ); // You must reach the first path in this virtue to invoke it. + } + else if ( !protector.CanBeginAction( typeof( JusticeVirtue ) ) ) + { + protector.SendLocalizedMessage( 1049370 ); // You must wait a while before offering your protection again. + } + else if ( protector.JusticeProtectors.Count > 0 ) + { + protector.SendLocalizedMessage( 1049542 ); // You cannot protect someone while being protected. + } + else if ( protector.Map != Map.Felucca ) + { + protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here. + } + else if ( protectee.Map != Map.Felucca ) + { + protector.SendLocalizedMessage( 1049372 ); // You cannot use this ability here. + } + else if ( protectee == protector || protectee.Criminal || protectee.Kills >= 5 ) + { + protector.SendLocalizedMessage( 1049436 ); // That player cannot be protected. + } + else if ( protectee.JusticeProtectors.Count > 0 ) + { + protector.SendLocalizedMessage( 1049369 ); // You cannot protect that player right now. + } + else + { + protectee.JusticeProtectors.Add( protector ); + + string args = String.Format( "{0}\t{1}", protector.Name, protectee.Name ); + + protectee.SendLocalizedMessage( 1049451, args ); // You are now being protected by ~1_NAME~. + protector.SendLocalizedMessage( 1049452, args ); // You are now protecting ~2_NAME~. + } + } + + public static void OnVirtueRejected( PlayerMobile protector, PlayerMobile protectee ) + { + string args = String.Format( "{0}\t{1}", protector.Name, protectee.Name ); + + protectee.SendLocalizedMessage( 1049453, args ); // You have declined protection from ~1_NAME~. + protector.SendLocalizedMessage( 1049454, args ); // ~2_NAME~ has declined your protection. + + if ( protector.BeginAction( typeof( JusticeVirtue ) ) ) + Timer.DelayCall( TimeSpan.FromMinutes( 15.0 ), new TimerStateCallback( RejectDelay_Callback ), protector ); + } + + public static void RejectDelay_Callback( object state ) + { + Mobile m = state as Mobile; + + if ( m != null ) + m.EndAction( typeof( JusticeVirtue ) ); + } + + public static void CheckAtrophy( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + try + { + if ( (pm.LastJusticeLoss + LossDelay) < DateTime.Now ) + { + if ( VirtueHelper.Atrophy( from, VirtueName.Justice, LossAmount ) ) + from.SendLocalizedMessage( 1049373 ); // You have lost some Justice. + + pm.LastJusticeLoss = DateTime.Now; + } + } + catch + { + } + } + } + + public class AcceptProtectorGump : Gump + { + private PlayerMobile m_Protector; + private PlayerMobile m_Protectee; + + public AcceptProtectorGump( PlayerMobile protector, PlayerMobile protectee ) : base( 150, 50 ) + { + m_Protector = protector; + m_Protectee = protectee; + + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, 396, 218, 3600 ); + + AddImageTiled( 15, 15, 365, 190, 2624 ); + AddAlphaRegion( 15, 15, 365, 190 ); + + AddHtmlLocalized( 30, 20, 360, 25, 1049365, 0x7FFF, false, false ); // Another player is offering you their protection: + AddLabel( 90, 55, 1153, protector.Name ); + + AddImage( 50, 45, 9005 ); + AddImageTiled( 80, 80, 200, 1, 9107 ); + AddImageTiled( 95, 82, 200, 1, 9157 ); + + AddRadio( 30, 110, 9727, 9730, true, 1 ); + AddHtmlLocalized( 65, 115, 300, 25, 1049444, 0x7FFF, false, false ); // Yes, I would like their protection. + + AddRadio( 30, 145, 9727, 9730, false, 0 ); + AddHtmlLocalized( 65, 148, 300, 25, 1049445, 0x7FFF, false, false ); // No thanks, I can take care of myself. + + AddButton( 160, 175, 247, 248, 2, GumpButtonType.Reply, 0 ); + + AddImage( 215, 0, 50581 ); + + AddImageTiled( 15, 14, 365, 1, 9107 ); + AddImageTiled( 380, 14, 1, 190, 9105 ); + AddImageTiled( 15, 205, 365, 1, 9107 ); + AddImageTiled( 15, 14, 1, 190, 9105 ); + AddImageTiled( 0, 0, 395, 1, 9157 ); + AddImageTiled( 394, 0, 1, 217, 9155 ); + AddImageTiled( 0, 216, 395, 1, 9157 ); + AddImageTiled( 0, 0, 1, 217, 9155 ); + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 2 ) + { + bool okay = info.IsSwitched( 1 ); + + if ( okay ) + JusticeVirtue.OnVirtueAccepted( m_Protector, m_Protectee ); + else + JusticeVirtue.OnVirtueRejected( m_Protector, m_Protectee ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/Sacrifice.cs b/Scripts/Engines/Virtues/Sacrifice.cs new file mode 100644 index 000000000..5ecc3cce9 --- /dev/null +++ b/Scripts/Engines/Virtues/Sacrifice.cs @@ -0,0 +1,189 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Targeting; +using Server.Network; + +namespace Server +{ + public class SacrificeVirtue + { + private static TimeSpan GainDelay = TimeSpan.FromDays( 1.0 ); + private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 ); + private const int LossAmount = 500; + + public static void Initialize() + { + VirtueGump.Register( 110, new OnVirtueUsed( OnVirtueUsed ) ); + } + + public static void OnVirtueUsed( Mobile from ) + { + if ( from.Alive ) + from.Target = new InternalTarget(); + else + Resurrect( from ); + } + + public static void CheckAtrophy( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + try + { + if ( (pm.LastSacrificeLoss + LossDelay) < DateTime.Now ) + { + if ( VirtueHelper.Atrophy( from, VirtueName.Sacrifice, LossAmount ) ) + from.SendLocalizedMessage( 1052041 ); // You have lost some Sacrifice. + + VirtueLevel level = VirtueHelper.GetLevel( from, VirtueName.Sacrifice ); + + pm.AvailableResurrects = (int)level; + pm.LastSacrificeLoss = DateTime.Now; + } + } + catch + { + } + } + + public static void Resurrect( Mobile from ) + { + if ( from.Alive ) + return; + + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + if ( from.Criminal ) + { + from.SendLocalizedMessage( 1052007 ); // You cannot use this ability while flagged as a criminal. + } + else if ( !VirtueHelper.IsSeeker( from, VirtueName.Sacrifice ) ) + { + from.SendLocalizedMessage( 1052004 ); // You cannot use this ability. + } + else if ( pm.AvailableResurrects <= 0 ) + { + from.SendLocalizedMessage( 1052005 ); // You do not have any resurrections left. + } + else + { + /* + * We need to wait for them to accept the gump or they can just use + * Sacrifice and cancel to have items in their backpack for free. + */ + from.CloseGump( typeof( ResurrectGump ) ); + from.SendGump( new ResurrectGump( from, true ) ); + } + } + + public static void Sacrifice( Mobile from, object targeted ) + { + if ( !from.CheckAlive() ) + return; + + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + Mobile targ = targeted as Mobile; + + if ( targ == null ) + return; + + if ( !ValidateCreature( targ ) ) + { + from.SendLocalizedMessage( 1052014 ); // You cannot sacrifice your fame for that creature. + } + else if ( ((targ.Hits * 100) / Math.Max( targ.HitsMax, 1 )) < 90 ) + { + from.SendLocalizedMessage( 1052013 ); // You cannot sacrifice for this monster because it is too damaged. + } + else if ( from.Hidden ) + { + from.SendLocalizedMessage( 1052015 ); // You cannot do that while hidden. + } + else if ( VirtueHelper.IsHighestPath( from, VirtueName.Sacrifice ) ) + { + from.SendLocalizedMessage( 1052068 ); // You have already attained the highest path in this virtue. + } + else if ( from.Fame < 2500 ) + { + from.SendLocalizedMessage( 1052017 ); // You do not have enough fame to sacrifice. + } + else if ( DateTime.Now < (pm.LastSacrificeGain + GainDelay) ) + { + from.SendLocalizedMessage( 1052016 ); // You must wait approximately one day before sacrificing again. + } + else + { + int toGain; + + if( from.Fame < 5000 ) + toGain = 500; + else if( from.Fame < 10000 ) + toGain = 1000; + else + toGain = 2000; + + from.Fame = 0; + + // I have seen the error of my ways! + targ.PublicOverheadMessage( MessageType.Regular, 0x3B2, 1052009 ); + + from.SendLocalizedMessage( 1052010 ); // You have set the creature free. + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerCallback( targ.Delete ) ); + + pm.LastSacrificeGain = DateTime.Now; + + bool gainedPath = false; + + if ( VirtueHelper.Award( from, VirtueName.Sacrifice, toGain, ref gainedPath ) ) + { + if ( gainedPath ) + { + from.SendLocalizedMessage( 1052008 ); // You have gained a path in Sacrifice! + + if ( pm.AvailableResurrects < 3 ) + ++pm.AvailableResurrects; + } + else + { + from.SendLocalizedMessage( 1054160 ); // You have gained in sacrifice. + } + } + + from.SendLocalizedMessage( 1052016 ); // You must wait approximately one day before sacrificing again. + } + } + + public static bool ValidateCreature( Mobile m ) + { + if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned) ) + return false; + + return ( m is Lich || m is Succubus || m is Daemon || m is EvilMage || m is EnslavedGargoyle || m is GargoyleEnforcer ); + } + + private class InternalTarget : Target + { + public InternalTarget() : base( 8, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + Sacrifice( from, targeted ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/Valor.cs b/Scripts/Engines/Virtues/Valor.cs new file mode 100644 index 000000000..9233c56b0 --- /dev/null +++ b/Scripts/Engines/Virtues/Valor.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Mobiles; +using Server.Targeting; +using Server.Engines.CannedEvil; + +namespace Server +{ + public class ValorVirtue + { + private static TimeSpan LossDelay = TimeSpan.FromDays( 7.0 ); + private const int LossAmount = 250; + + public static void Initialize() + { + VirtueGump.Register( 112, new OnVirtueUsed( OnVirtueUsed ) ); + } + + public static void OnVirtueUsed( Mobile from ) + { + if( from.Alive ) + { + from.SendLocalizedMessage( 1054034 ); // Target the Champion Idol of the Champion you wish to challenge!. + from.Target = new InternalTarget(); + } + } + + public static void CheckAtrophy( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if( pm == null ) + return; + + try + { + if( (pm.LastValorLoss + LossDelay) < DateTime.Now ) + { + if( VirtueHelper.Atrophy( from, VirtueName.Valor, LossAmount ) ) + from.SendLocalizedMessage( 1054040 ); // You have lost some Valor. + + pm.LastValorLoss = DateTime.Now; + } + } + catch + { + } + } + + public static void Valor( Mobile from, object targ ) + { + IdolOfTheChampion idol = targ as IdolOfTheChampion; + + if( idol == null || idol.Deleted || idol.Spawn == null || idol.Spawn.Deleted ) + from.SendLocalizedMessage( 1054035 ); // You must target a Champion Idol to challenge the Champion's spawn! + else if( from.Hidden ) + from.SendLocalizedMessage( 1052015 ); // You cannot do that while hidden. + else if( idol.Spawn.HasBeenAdvanced ) + from.SendLocalizedMessage( 1054038 ); // The Champion of this region has already been challenged! + else + { + VirtueLevel vl = VirtueHelper.GetLevel( from, VirtueName.Valor ); + if( idol.Spawn.Active ) + { + if( idol.Spawn.Champion != null ) //TODO: Message? + return; + + int needed, consumed; + switch( idol.Spawn.GetSubLevel() ) + { + case 0: + { + needed = consumed = 2500; + break; + } + case 1: + { + needed = consumed = 5000; + break; + } + case 2: + { + needed = 10000; + consumed = 7500; + break; + } + default: + { + needed = 20000; + consumed = 10000; + break; + } + } + + if( from.Virtues.GetValue( (int)VirtueName.Valor ) >= needed ) + { + VirtueHelper.Atrophy( from, VirtueName.Valor, consumed ); + from.SendLocalizedMessage( 1054037 ); // Your challenge is heard by the Champion of this region! Beware its wrath! + idol.Spawn.HasBeenAdvanced = true; + idol.Spawn.AdvanceLevel(); + } + else + from.SendLocalizedMessage( 1054039 ); // The Champion of this region ignores your challenge. You must further prove your valor. + } + else + { + if( vl == VirtueLevel.Knight ) + { + VirtueHelper.Atrophy( from, VirtueName.Valor, 11000 ); + from.SendLocalizedMessage( 1054037 ); // Your challenge is heard by the Champion of this region! Beware its wrath! + idol.Spawn.Start(); + idol.Spawn.HasBeenAdvanced = true; + } + else + { + from.SendLocalizedMessage( 1054036 ); // You must be a Knight of Valor to summon the champion's spawn in this manner! + } + } + } + + } + + private class InternalTarget : Target + { + public InternalTarget() : base( 14, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + Valor( from, targeted ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/VirtueGump.cs b/Scripts/Engines/Virtues/VirtueGump.cs new file mode 100644 index 000000000..43901b5e7 --- /dev/null +++ b/Scripts/Engines/Virtues/VirtueGump.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Network; + +namespace Server +{ + public delegate void OnVirtueUsed( Mobile from ); + + public class VirtueGump : Gump + { + private static Hashtable m_Callbacks = new Hashtable(); + + public static void Initialize() + { + EventSink.VirtueGumpRequest += new VirtueGumpRequestEventHandler( EventSink_VirtueGumpRequest ); + EventSink.VirtueItemRequest += new VirtueItemRequestEventHandler( EventSink_VirtueItemRequest ); + EventSink.VirtueMacroRequest += new VirtueMacroRequestEventHandler( EventSink_VirtueMacroRequest ); + } + + public static void Register( int gumpID, OnVirtueUsed callback ) + { + m_Callbacks[gumpID] = callback; + } + + private static void EventSink_VirtueItemRequest( VirtueItemRequestEventArgs e ) + { + if ( e.Beholder != e.Beheld ) + return; + + e.Beholder.CloseGump( typeof( VirtueGump ) ); + + if ( e.Beholder.Kills >= 5 ) + { + e.Beholder.SendLocalizedMessage( 1049609 ); // Murderers cannot invoke this virtue. + return; + } + + OnVirtueUsed callback = (OnVirtueUsed)m_Callbacks[e.GumpID]; + + if ( callback != null ) + callback( e.Beholder ); + else + e.Beholder.SendLocalizedMessage( 1052066 ); // That virtue is not active yet. + } + + + private static void EventSink_VirtueMacroRequest( VirtueMacroRequestEventArgs e ) + { + int virtueID = 0; + + switch ( e.VirtueID ) + { + case 0: // Honor + virtueID = 107; break; + case 1: // Sacrifice + virtueID = 110; break; + case 2: // Valor; + virtueID = 112; break; + } + + EventSink_VirtueItemRequest( new VirtueItemRequestEventArgs( e.Mobile, e.Mobile, virtueID ) ); + } + + private static void EventSink_VirtueGumpRequest( VirtueGumpRequestEventArgs e ) + { + Mobile beholder = e.Beholder; + Mobile beheld = e.Beheld; + + if ( beholder == beheld && beholder.Kills >= 5 ) + { + beholder.SendLocalizedMessage( 1049609 ); // Murderers cannot invoke this virtue. + } + else if ( beholder.Map == beheld.Map && beholder.InRange( beheld, 12 ) ) + { + beholder.CloseGump( typeof( VirtueGump ) ); + beholder.SendGump( new VirtueGump( beholder, beheld ) ); + } + } + + private Mobile m_Beholder, m_Beheld; + + public VirtueGump( Mobile beholder, Mobile beheld ) : base( 0, 0 ) + { + m_Beholder = beholder; + m_Beheld = beheld; + + Serial = beheld.Serial; + + AddPage( 0 ); + + AddImage( 30, 40, 104 ); + + AddPage( 1 ); + + Add( new InternalEntry( 61, 71, 108, GetHueFor( 0 ) ) ); // Humility + Add( new InternalEntry( 123, 46, 112, GetHueFor( 4 ) ) ); // Valor + Add( new InternalEntry( 187, 70, 107, GetHueFor( 5 ) ) ); // Honor + Add( new InternalEntry( 35, 135, 110, GetHueFor( 1 ) ) ); // Sacrifice + Add( new InternalEntry( 211, 133, 105, GetHueFor( 2 ) ) ); // Compassion + Add( new InternalEntry( 61, 195, 111, GetHueFor( 3 ) ) ); // Spiritulaity + Add( new InternalEntry( 186, 195, 109, GetHueFor( 6 ) ) ); // Justice + Add( new InternalEntry( 121, 221, 106, GetHueFor( 7 ) ) ); // Honesty + + if ( m_Beholder == m_Beheld ) + { + AddButton( 57, 269, 2027, 2027, 1, GumpButtonType.Reply, 0 ); + AddButton( 186, 269, 2071, 2071, 2, GumpButtonType.Reply, 0 ); + } + } + + private static int[] m_Table = new int[24] + { + 0x0481, 0x0963, 0x0965, + 0x060A, 0x060F, 0x002A, + 0x08A4, 0x08A7, 0x0034, + 0x0965, 0x08FD, 0x0480, + 0x00EA, 0x0845, 0x0020, + 0x0011, 0x0269, 0x013D, + 0x08A1, 0x08A3, 0x0042, + 0x0543, 0x0547, 0x0061 + }; + + private int GetHueFor( int index ) + { + if ( m_Beheld.Virtues.GetValue( index ) == 0 ) + return 2402; + + int value = m_Beheld.Virtues.GetValue( index ); + + if ( value < 4000 ) + return 2402; + + if( value >= 30000 ) + value = 20000; //Sanity + + /* + int vl; + + vl = (value/10000) + + if( value < 10000 ) + vl = 0; + else if( value > 20000 ) + vl = 2; + else + vl = 1; + */ + + return m_Table[(index * 3) + (int)(value/10000)]; + } + + private class InternalEntry : GumpImage + { + public InternalEntry( int x, int y, int gumpID, int hue ) : base( x, y, gumpID, hue ) + { + } + + public override string Compile() + { + return String.Format( "{{ gumppic {0} {1} {2} hue={3} class=VirtueGumpItem }}", X, Y, GumpID, Hue ); + } + + private static byte[] m_Class = Gump.StringToBuffer( " class=VirtueGumpItem" ); + + public override void AppendTo( IGumpWriter disp ) + { + base.AppendTo( disp ); + + disp.AppendLayout( m_Class ); + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 1 && m_Beholder == m_Beheld ) + m_Beholder.SendGump( new VirtueStatusGump( m_Beholder ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/VirtueHelper.cs b/Scripts/Engines/Virtues/VirtueHelper.cs new file mode 100644 index 000000000..ce73efa05 --- /dev/null +++ b/Scripts/Engines/Virtues/VirtueHelper.cs @@ -0,0 +1,115 @@ +using System; + +namespace Server +{ + public enum VirtueLevel + { + None, + Seeker, + Follower, + Knight + } + + public enum VirtueName + { + Humility, + Sacrifice, + Compassion, + Spirituality, + Valor, + Honor, + Justice, + Honesty + } + + public class VirtueHelper + { + public static bool HasAny( Mobile from, VirtueName virtue ) + { + return ( from.Virtues.GetValue( (int)virtue ) > 0 ); + } + + public static bool IsHighestPath( Mobile from, VirtueName virtue ) + { + return ( from.Virtues.GetValue( (int)virtue ) >= GetMaxAmount( virtue ) ); + } + + public static VirtueLevel GetLevel( Mobile from, VirtueName virtue ) + { + int v = from.Virtues.GetValue( (int)virtue ); + int vl; + + if ( v < 4000 ) + vl = 0; + else if ( v >= 20000 ) + vl = 3; + else + vl = ( v + 9999 ) / 10000; + + return (VirtueLevel)vl; + } + + public static int GetMaxAmount( VirtueName virtue ) + { + if( virtue == VirtueName.Honor ) + return 20000; + + if( virtue == VirtueName.Sacrifice ) + return 22000; + + return 21000; + } + + public static bool Award( Mobile from, VirtueName virtue, int amount, ref bool gainedPath ) + { + int current = from.Virtues.GetValue( (int)virtue ); + + int maxAmount = GetMaxAmount( virtue ); + + if ( current >= maxAmount ) + return false; + + if( (current + amount) >= maxAmount ) + amount = maxAmount - current; + + VirtueLevel oldLevel = GetLevel( from, virtue ); + + from.Virtues.SetValue( (int)virtue, current + amount ); + + gainedPath = ( GetLevel( from, virtue ) != oldLevel ); + + return true; + } + + public static bool Atrophy( Mobile from, VirtueName virtue ) + { + return Atrophy( from, virtue, 1 ); + } + public static bool Atrophy( Mobile from, VirtueName virtue, int amount ) + { + int current = from.Virtues.GetValue( (int)virtue ); + + if( (current - amount) >= 0 ) + from.Virtues.SetValue( (int)virtue, current - amount ); + else + from.Virtues.SetValue( (int)virtue, 0 ); + + return ( current > 0 ); + } + + public static bool IsSeeker( Mobile from, VirtueName virtue ) + { + return ( GetLevel( from, virtue ) >= VirtueLevel.Seeker ); + } + + public static bool IsFollower( Mobile from, VirtueName virtue ) + { + return ( GetLevel( from, virtue ) >= VirtueLevel.Follower ); + } + + public static bool IsKnight( Mobile from, VirtueName virtue ) + { + return ( GetLevel( from, virtue ) >= VirtueLevel.Knight ); + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/VirtueInfoGump.cs b/Scripts/Engines/Virtues/VirtueInfoGump.cs new file mode 100644 index 000000000..ae2af8c42 --- /dev/null +++ b/Scripts/Engines/Virtues/VirtueInfoGump.cs @@ -0,0 +1,104 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server +{ + public class VirtueInfoGump : Gump + { + private Mobile m_Beholder; + private int m_Desc; + private string m_Page; + private VirtueName m_Virtue; + + public VirtueInfoGump( Mobile beholder, VirtueName virtue, int description ) : this( beholder, virtue, description, null ) + { + } + + public VirtueInfoGump( Mobile beholder, VirtueName virtue, int description, string webPage ) : base( 0, 0 ) + { + m_Beholder = beholder; + m_Virtue = virtue; + m_Desc = description; + m_Page = webPage; + + int value = beholder.Virtues.GetValue( (int)virtue ); + + AddPage( 0 ); + + AddImage( 30, 40, 2080 ); + AddImage( 47, 77, 2081 ); + AddImage( 47, 147, 2081 ); + AddImage( 47, 217, 2081 ); + AddImage( 47, 267, 2083 ); + AddImage( 70, 213, 2091 ); + + AddPage( 1 ); + + int maxValue = VirtueHelper.GetMaxAmount( m_Virtue ); + + int valueDesc; + + if( value < 1 ) + valueDesc = 1052044; // You have not started on the path of this Virtue. + else if( value < maxValue/6 ) + valueDesc = 1052045; // You have barely begun your journey through the path of this Virtue. + else if( value < maxValue/3 ) + valueDesc = 1052046; // You have progressed in this Virtue, but still have much to do. + else if( value < maxValue/2 ) + valueDesc = 1052047; // Your journey through the path of this Virtue is going well. + else if( value < 2*maxValue/3 ) + valueDesc = 1052048; // You feel very close to achieving your next path in this Virtue. + else if( value < 5*maxValue/6 ) + valueDesc = 1052049; // You have achieved a path in this Virtue. + else + valueDesc = 1052050; // You have achieved the highest path in this Virtue. + + + AddHtmlLocalized( 157, 73, 200, 40, 1051000 + (int)virtue, false, false ); + AddHtmlLocalized( 75, 95, 220, 140, description, false, false ); + AddHtmlLocalized( 70, 224, 229, 60, valueDesc, false, false ); + + AddButton( 65, 277, 1209, 1209, 1, GumpButtonType.Reply, 0 ); + + AddButton( 280, 43, 4014, 4014, 2, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 83, 275, 400, 40, (webPage == null) ? 1052055 : 1052052, false, false ); // This virtue is not yet defined. OR -click to learn more (opens webpage) + + int dots; + + if( value < 4000 ) + dots = value / 400; + else if( value < 10000 ) + dots = (value - 4000) / 600; + else if( value < maxValue ) + dots = (value - 10000) / ((maxValue-10000)/10); + else + dots = 10; + + for ( int i = 0; i < 10; ++i ) + AddImage( 95 + (i * 17), 50, i < dots ? 2362 : 2360 ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 1: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, m_Virtue, m_Desc, m_Page ) ); + + if( m_Page != null ) + state.Send( new LaunchBrowser( m_Page ) ); //No message about web browser starting on OSI + break; + } + case 2: + { + m_Beholder.SendGump( new VirtueStatusGump( m_Beholder ) ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Engines/Virtues/VirtueStatusGump.cs b/Scripts/Engines/Virtues/VirtueStatusGump.cs new file mode 100644 index 000000000..763224b4d --- /dev/null +++ b/Scripts/Engines/Virtues/VirtueStatusGump.cs @@ -0,0 +1,104 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server +{ + public class VirtueStatusGump : Gump + { + private Mobile m_Beholder; + + public VirtueStatusGump( Mobile beholder ) : base( 0, 0 ) + { + m_Beholder = beholder; + + AddPage( 0 ); + + AddImage( 30, 40, 2080 ); + AddImage( 47, 77, 2081 ); + AddImage( 47, 147, 2081 ); + AddImage( 47, 217, 2081 ); + AddImage( 47, 267, 2083 ); + AddImage( 70, 213, 2091 ); + + AddPage( 1 ); + + AddHtml( 140, 73, 200, 20, "The Virtues", false, false ); + + AddHtmlLocalized( 80, 100, 100, 40, 1051000, false, false ); // Humility + AddHtmlLocalized( 80, 129, 100, 40, 1051001, false, false ); // Sacrifice + AddHtmlLocalized( 80, 159, 100, 40, 1051002, false, false ); // Compassion + AddHtmlLocalized( 80, 189, 100, 40, 1051003, false, false ); // Spirituality + AddHtmlLocalized( 200, 100, 200, 40, 1051004, false, false ); // Valor + AddHtmlLocalized( 200, 129, 200, 40, 1051005, false, false ); // Honor + AddHtmlLocalized( 200, 159, 200, 40, 1051006, false, false ); // Justice + AddHtmlLocalized( 200, 189, 200, 40, 1051007, false, false ); // Honesty + + AddHtmlLocalized( 75, 224, 220, 60, 1052062, false, false ); // Click on a blue gem to view your status in that virtue. + + AddButton( 60, 100, 1210, 1210, 1, GumpButtonType.Reply, 0 ); + AddButton( 60, 129, 1210, 1210, 2, GumpButtonType.Reply, 0 ); + AddButton( 60, 159, 1210, 1210, 3, GumpButtonType.Reply, 0 ); + AddButton( 60, 189, 1210, 1210, 4, GumpButtonType.Reply, 0 ); + AddButton( 180, 100, 1210, 1210, 5, GumpButtonType.Reply, 0 ); + AddButton( 180, 129, 1210, 1210, 6, GumpButtonType.Reply, 0 ); + AddButton( 180, 159, 1210, 1210, 7, GumpButtonType.Reply, 0 ); + AddButton( 180, 189, 1210, 1210, 8, GumpButtonType.Reply, 0 ); + + AddButton( 280, 43, 4014, 4014, 9, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 1: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Humility, 1052051 ) ); + break; + } + case 2: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Sacrifice, 1052053, @"http://update.uo.com/design_389.html" ) ); + break; + } + case 3: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Compassion, 1053000, @"http://update.uo.com/design_412.html" ) ); + break; + } + case 4: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Spirituality, 1052056 ) ); + break; + } + case 5: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Valor, 1054033, @"http://update.uo.com/design_427.html" ) ); + break; + } + case 6: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Honor, 1052058, @"http://guide.uo.com/virtues_2.html" ) ); + break; + } + case 7: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Justice, 1052059, @"http://update.uo.com/design_413.html" ) ); + break; + } + case 8: + { + m_Beholder.SendGump( new VirtueInfoGump( m_Beholder, VirtueName.Honesty, 1052060 ) ); + break; + } + case 9: + { + m_Beholder.SendGump( new VirtueGump( m_Beholder, m_Beholder ) ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/AddDoorGump.cs b/Scripts/Gumps/AddDoorGump.cs new file mode 100644 index 000000000..c76fdfa85 --- /dev/null +++ b/Scripts/Gumps/AddDoorGump.cs @@ -0,0 +1,125 @@ +using System; +using Server; +using Server.Items; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class AddDoorGump : Gump + { + private int m_Type; + + public AddDoorGump() : this( -1 ) + { + } + + public void AddBlueBack( int width, int height ) + { + AddBackground ( 0, 0, width-00, height-00, 0xE10 ); + AddBackground ( 8, 5, width-16, height-11, 0x053 ); + AddImageTiled ( 15, 14, width-29, height-29, 0xE14 ); + AddAlphaRegion( 15, 14, width-29, height-29 ); + } + + public AddDoorGump( int type ) : base( 50, 40 ) + { + m_Type = type; + + AddPage( 0 ); + + if ( m_Type >= 0 && m_Type < m_Types.Length ) + { + AddBlueBack( 155, 174 ); + + int baseID = m_Types[m_Type].m_BaseID; + + AddItem( 25, 24, baseID ); + AddButton( 26, 37, 0x5782, 0x5782, 1, GumpButtonType.Reply, 0 ); + + AddItem( 47, 45, baseID + 2 ); + AddButton( 43, 57, 0x5783, 0x5783, 2, GumpButtonType.Reply, 0 ); + + AddItem( 87, 22, baseID + 10 ); + AddButton( 116, 35, 0x5785, 0x5785, 6, GumpButtonType.Reply, 0 ); + + AddItem( 65, 45, baseID + 8 ); + AddButton( 96, 55, 0x5784, 0x5784, 5, GumpButtonType.Reply, 0 ); + + AddButton( 73, 36, 0x2716, 0x2716, 9, GumpButtonType.Reply, 0 ); + } + else + { + AddBlueBack( 265, 145 ); + + for ( int i = 0; i < m_Types.Length; ++i ) + { + AddButton( 30 + (i * 49), 13, 0x2624, 0x2625, i + 1, GumpButtonType.Reply, 0 ); + AddItem( 22 + (i * 49), 20, m_Types[i].m_BaseID ); + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + int button = info.ButtonID - 1; + + if ( m_Type == -1 ) + { + if ( button >= 0 && button < m_Types.Length ) + from.SendGump( new AddDoorGump( button ) ); + } + else + { + if ( button >= 0 && button < 8 ) + { + from.SendGump( new AddDoorGump( m_Type ) ); + CommandSystem.Handle( from, String.Format( "{0}Add {1} {2}", CommandSystem.Prefix, m_Types[m_Type].m_Type.Name, (DoorFacing) button ) ); + } + else if ( button == 8 ) + { + from.SendGump( new AddDoorGump( m_Type ) ); + CommandSystem.Handle( from, String.Format( "{0}Link", CommandSystem.Prefix ) ); + } + else + { + from.SendGump( new AddDoorGump() ); + } + } + } + + public static void Initialize() + { + CommandSystem.Register( "AddDoor", AccessLevel.GameMaster, new CommandEventHandler( AddDoor_OnCommand ) ); + } + + [Usage( "AddDoor" )] + [Description( "Displays a menu from which you can interactively add doors." )] + public static void AddDoor_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendGump( new AddDoorGump() ); + } + + public static DoorInfo[] m_Types = new DoorInfo[] + { + new DoorInfo( typeof( MetalDoor ), 0x675 ), + new DoorInfo( typeof( RattanDoor ), 0x695 ), + new DoorInfo( typeof( DarkWoodDoor ), 0x6A5 ), + new DoorInfo( typeof( LightWoodDoor ), 0x6D5 ), + new DoorInfo( typeof( StrongWoodDoor ), 0x6E5 ) + }; + } + + public class DoorInfo + { + public Type m_Type; + public int m_BaseID; + + public DoorInfo( Type type, int baseID ) + { + m_Type = type; + m_BaseID = baseID; + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/AddGump.cs b/Scripts/Gumps/AddGump.cs new file mode 100644 index 000000000..c13e91cc6 --- /dev/null +++ b/Scripts/Gumps/AddGump.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Server; +using Server.Commands; +using Server.Network; +using Server.Targeting; + +namespace Server.Gumps +{ + public class AddGump : Gump + { + private string m_SearchString; + private Type[] m_SearchResults; + private int m_Page; + + public static void Initialize() + { + CommandSystem.Register( "AddMenu", AccessLevel.GameMaster, new CommandEventHandler( AddMenu_OnCommand ) ); + } + + [Usage( "AddMenu [searchString]" )] + [Description( "Opens an add menu, with an optional initial search string. This menu allows you to search for Items or Mobiles and add them interactively." )] + private static void AddMenu_OnCommand( CommandEventArgs e ) + { + string val = e.ArgString.Trim(); + Type[] types; + bool explicitSearch = false; + + if ( val.Length == 0 ) + { + types = Type.EmptyTypes; + } + else if ( val.Length < 3 ) + { + e.Mobile.SendMessage( "Invalid search string." ); + types = Type.EmptyTypes; + } + else + { + types = Match( val ).ToArray(); + explicitSearch = true; + } + + e.Mobile.SendGump( new AddGump( e.Mobile, val, 0, types, explicitSearch ) ); + } + + public AddGump( Mobile from, string searchString, int page, Type[] searchResults, bool explicitSearch ) : base( 50, 50 ) + { + m_SearchString = searchString; + m_SearchResults = searchResults; + m_Page = page; + + from.CloseGump( typeof( AddGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 280, 5054 ); + + AddImageTiled( 10, 10, 400, 20, 2624 ); + AddAlphaRegion( 10, 10, 400, 20 ); + AddImageTiled( 41, 11, 184, 18, 0xBBC ); + AddImageTiled( 42, 12, 182, 16, 2624 ); + AddAlphaRegion( 42, 12, 182, 16 ); + + AddButton( 10, 9, 4011, 4013, 1, GumpButtonType.Reply, 0 ); + AddTextEntry( 44, 10, 180, 20, 0x480, 0, searchString ); + + AddHtmlLocalized( 230, 10, 100, 20, 3010005, 0x7FFF, false, false ); + + AddImageTiled( 10, 40, 400, 200, 2624 ); + AddAlphaRegion( 10, 40, 400, 200 ); + + if ( searchResults.Length > 0 ) + { + for ( int i = (page * 10); i < ((page + 1) * 10) && i < searchResults.Length; ++i ) + { + int index = i % 10; + + AddLabel( 44, 39 + (index * 20), 0x480, searchResults[i].Name ); + AddButton( 10, 39 + (index * 20), 4023, 4025, 4 + i, GumpButtonType.Reply, 0 ); + } + } + else + { + AddLabel( 15, 44, 0x480, explicitSearch ? "Nothing matched your search terms." : "No results to display." ); + } + + AddImageTiled( 10, 250, 400, 20, 2624 ); + AddAlphaRegion( 10, 250, 400, 20 ); + + if ( m_Page > 0 ) + AddButton( 10, 249, 4014, 4016, 2, GumpButtonType.Reply, 0 ); + else + AddImage( 10, 249, 4014 ); + + AddHtmlLocalized( 44, 250, 170, 20, 1061028, m_Page > 0 ? 0x7FFF : 0x5EF7, false, false ); // Previous page + + if ( ((m_Page + 1) * 10) < searchResults.Length ) + AddButton( 210, 249, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + else + AddImage( 210, 249, 4005 ); + + AddHtmlLocalized( 244, 250, 170, 20, 1061027, ((m_Page + 1) * 10) < searchResults.Length ? 0x7FFF : 0x5EF7, false, false ); // Next page + } + + private static Type typeofItem = typeof( Item ), typeofMobile = typeof( Mobile ); + + private static void Match( string match, Type[] types, List results ) + { + if ( match.Length == 0 ) + return; + + match = match.ToLower(); + + for ( int i = 0; i < types.Length; ++i ) + { + Type t = types[i]; + + if ( (typeofMobile.IsAssignableFrom( t ) || typeofItem.IsAssignableFrom( t )) && t.Name.ToLower().IndexOf( match ) >= 0 && !results.Contains( t ) ) + { + ConstructorInfo[] ctors = t.GetConstructors(); + + for ( int j = 0; j < ctors.Length; ++j ) + { + if ( ctors[j].GetParameters().Length == 0 && ctors[j].IsDefined( typeof( ConstructableAttribute ), false ) ) + { + results.Add( t ); + break; + } + } + } + } + } + + public static List Match( string match ) + { + List results = new List(); + Type[] types; + + Assembly[] asms = ScriptCompiler.Assemblies; + + for ( int i = 0; i < asms.Length; ++i ) + { + types = ScriptCompiler.GetTypeCache( asms[i] ).Types; + Match( match, types, results ); + } + + types = ScriptCompiler.GetTypeCache( Core.Assembly ).Types; + Match( match, types, results ); + + results.Sort( new TypeNameComparer() ); + + return results; + } + + private class TypeNameComparer : IComparer + { + public int Compare( Type x, Type y ) + { + return x.Name.CompareTo( y.Name ); + } + } + + public class InternalTarget : Target + { + private Type m_Type; + private Type[] m_SearchResults; + private string m_SearchString; + private int m_Page; + + public InternalTarget( Type type, Type[] searchResults, string searchString, int page ) : base( -1, true, TargetFlags.None ) + { + m_Type = type; + m_SearchResults = searchResults; + m_SearchString = searchString; + m_Page = page; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + { + if ( p is Item ) + p = ((Item)p).GetWorldTop(); + else if ( p is Mobile ) + p = ((Mobile)p).Location; + + Server.Commands.Add.Invoke( from, new Point3D( p ), new Point3D( p ), new string[]{ m_Type.Name } ); + + from.Target = new InternalTarget( m_Type, m_SearchResults, m_SearchString, m_Page ); + } + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + if ( cancelType == TargetCancelType.Canceled ) + from.SendGump( new AddGump( from, m_SearchString, m_Page, m_SearchResults, true ) ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + switch ( info.ButtonID ) + { + case 1: // Search + { + TextRelay te = info.GetTextEntry( 0 ); + string match = ( te == null ? "" : te.Text.Trim() ); + + if ( match.Length < 3 ) + { + from.SendMessage( "Invalid search string." ); + from.SendGump( new AddGump( from, match, m_Page, m_SearchResults, false ) ); + } + else + { + from.SendGump( new AddGump( from, match, 0, Match( match ).ToArray(), true ) ); + } + + break; + } + case 2: // Previous page + { + if ( m_Page > 0 ) + from.SendGump( new AddGump( from, m_SearchString, m_Page - 1, m_SearchResults, true ) ); + + break; + } + case 3: // Next page + { + if ( (m_Page + 1) * 10 < m_SearchResults.Length ) + from.SendGump( new AddGump( from, m_SearchString, m_Page + 1, m_SearchResults, true ) ); + + break; + } + default: + { + int index = info.ButtonID - 4; + + if ( index >= 0 && index < m_SearchResults.Length ) + { + from.SendMessage( "Where do you wish to place this object? to cancel." ); + from.Target = new InternalTarget( m_SearchResults[index], m_SearchResults, m_SearchString, m_Page ); + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/AdminGump.cs b/Scripts/Gumps/AdminGump.cs new file mode 100644 index 000000000..7271da238 --- /dev/null +++ b/Scripts/Gumps/AdminGump.cs @@ -0,0 +1,2933 @@ +using System; +using System.Net; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using Server; +using Server.Items; +using Server.Prompts; +using Server.Network; +using Server.Accounting; +using Server.Commands; + +namespace Server.Gumps +{ + public enum AdminGumpPage + { + Information_General, + Information_Perf, + Administer, + Clients, + Accounts, + Accounts_Shared, + Firewall, + Administer_WorldBuilding, + Administer_Server, + Administer_Access, + Administer_Access_Lockdown, + Administer_Commands, + ClientInfo, + AccountDetails, + AccountDetails_Information, + AccountDetails_Characters, + AccountDetails_Access, + AccountDetails_Access_ClientIPs, + AccountDetails_Access_Restrictions, + AccountDetails_Comments, + AccountDetails_Tags, + AccountDetails_ChangePassword, + AccountDetails_ChangeAccess, + FirewallInfo + } + + public class AdminGump : Gump + { + private Mobile m_From; + private AdminGumpPage m_PageType; + private ArrayList m_List; + private int m_ListPage; + private object m_State; + + private const int LabelColor = 0x7FFF; + private const int SelectedColor = 0x421F; + private const int DisabledColor = 0x4210; + + private const int LabelColor32 = 0xFFFFFF; + private const int SelectedColor32 = 0x8080FF; + private const int DisabledColor32 = 0x808080; + + private const int LabelHue = 0x480; + private const int GreenHue = 0x40; + private const int RedHue = 0x20; + + public void AddPageButton( int x, int y, int buttonID, string text, AdminGumpPage page, params AdminGumpPage[] subPages ) + { + bool isSelection = ( m_PageType == page ); + + for ( int i = 0; !isSelection && i < subPages.Length; ++i ) + isSelection = ( m_PageType == subPages[i] ); + + AddSelectedButton( x, y, buttonID, text, isSelection ); + } + + public void AddSelectedButton( int x, int y, int buttonID, string text, bool isSelection ) + { + AddButton( x, y - 1, isSelection ? 4006 : 4005, 4007, buttonID, GumpButtonType.Reply, 0 ); + AddHtml( x + 35, y, 200, 20, Color( text, isSelection ? SelectedColor32 : LabelColor32 ), false, false ); + } + + public void AddButtonLabeled( int x, int y, int buttonID, string text ) + { + AddButton( x, y - 1, 4005, 4007, buttonID, GumpButtonType.Reply, 0 ); + AddHtml( x + 35, y, 240, 20, Color( text, LabelColor32 ), false, false ); + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public void AddBlackAlpha( int x, int y, int width, int height ) + { + AddImageTiled( x, y, width, height, 2624 ); + AddAlphaRegion( x, y, width, height ); + } + + public int GetButtonID( int type, int index ) + { + return 1 + (index * 10) + type; + } + + public static string FormatTimeSpan( TimeSpan ts ) + { + return String.Format( "{0:D2}:{1:D2}:{2:D2}:{3:D2}", ts.Days, ts.Hours % 24, ts.Minutes % 60, ts.Seconds % 60 ); + } + + public static string FormatByteAmount( long totalBytes ) + { + if ( totalBytes > 1000000000 ) + return String.Format( "{0:F1} GB", (double)totalBytes / 1073741824 ); + + if ( totalBytes > 1000000 ) + return String.Format( "{0:F1} MB", (double)totalBytes / 1048576 ); + + if ( totalBytes > 1000 ) + return String.Format( "{0:F1} KB", (double)totalBytes / 1024 ); + + return String.Format( "{0} Bytes", totalBytes ); + } + + public static void Initialize() + { + CommandSystem.Register( "Admin", AccessLevel.Administrator, new CommandEventHandler( Admin_OnCommand ) ); + } + + [Usage( "Admin" )] + [Description( "Opens an interface providing server information and administration features including client, account, and firewall management." )] + public static void Admin_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendGump( new AdminGump( e.Mobile, AdminGumpPage.Clients, 0, null, null, null ) ); + } + + public static int GetHueFor( Mobile m ) + { + if ( m == null ) + return LabelHue; + + switch ( m.AccessLevel ) + { + case AccessLevel.Owner: + case AccessLevel.Developer: + case AccessLevel.Administrator: return 0x516; + case AccessLevel.Seer: return 0x144; + case AccessLevel.GameMaster: return 0x21; + case AccessLevel.Counselor: return 0x2; + case AccessLevel.Player: default: + { + if ( m.Kills >= 5 ) + return 0x21; + else if ( m.Criminal ) + return 0x3B1; + + return 0x58; + } + } + } + + private static string[] m_AccessLevelStrings = new string[] + { + "Player", + "Counselor", + "Game Master", + "Seer", + "Administrator", + "Developer", + "Owner" + }; + + public static string FormatAccessLevel( AccessLevel level ) + { + int v = (int)level; + + if ( v >= 0 && v < m_AccessLevelStrings.Length ) + return m_AccessLevelStrings[v]; + + return "Unknown"; + } + + public AdminGump( Mobile from, AdminGumpPage pageType, int listPage, ArrayList list, string notice, object state ) : base( 50, 40 ) + { + from.CloseGump( typeof( AdminGump ) ); + + m_From = from; + m_PageType = pageType; + m_ListPage = listPage; + m_State = state; + m_List = list; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 440, 5054 ); + + AddBlackAlpha( 10, 10, 170, 100 ); + AddBlackAlpha( 190, 10, 220, 100 ); + AddBlackAlpha( 10, 120, 400, 260 ); + AddBlackAlpha( 10, 390, 400, 40 ); + + AddPageButton( 10, 10, GetButtonID( 0, 0 ), "INFORMATION", AdminGumpPage.Information_General, AdminGumpPage.Information_Perf ); + AddPageButton( 10, 30, GetButtonID( 0, 1 ), "ADMINISTER", AdminGumpPage.Administer, AdminGumpPage.Administer_Access, AdminGumpPage.Administer_Commands, AdminGumpPage.Administer_Server, AdminGumpPage.Administer_WorldBuilding, AdminGumpPage.Administer_Access_Lockdown ); + AddPageButton( 10, 50, GetButtonID( 0, 2 ), "CLIENT LIST", AdminGumpPage.Clients, AdminGumpPage.ClientInfo ); + AddPageButton( 10, 70, GetButtonID( 0, 3 ), "ACCOUNT LIST", AdminGumpPage.Accounts, AdminGumpPage.Accounts_Shared, AdminGumpPage.AccountDetails, AdminGumpPage.AccountDetails_Information, AdminGumpPage.AccountDetails_Characters, AdminGumpPage.AccountDetails_Access, AdminGumpPage.AccountDetails_Access_ClientIPs, AdminGumpPage.AccountDetails_Access_Restrictions, AdminGumpPage.AccountDetails_Comments, AdminGumpPage.AccountDetails_Tags, AdminGumpPage.AccountDetails_ChangeAccess, AdminGumpPage.AccountDetails_ChangePassword ); + AddPageButton( 10, 90, GetButtonID( 0, 4 ), "FIREWALL", AdminGumpPage.Firewall, AdminGumpPage.FirewallInfo ); + + if ( notice != null ) + AddHtml( 12, 392, 396, 36, Color( notice, LabelColor32 ), false, false ); + + switch ( pageType ) + { + case AdminGumpPage.Information_General: + { + int banned = 0; + int active = 0; + + foreach ( Account acct in Accounts.GetAccounts() ) + { + if ( acct.Banned ) + ++banned; + else + ++active; + } + + AddLabel( 20, 130, LabelHue, "Active Accounts:" ); + AddLabel( 150, 130, LabelHue, active.ToString() ); + + AddLabel( 20, 150, LabelHue, "Banned Accounts:" ); + AddLabel( 150, 150, LabelHue, banned.ToString() ); + + AddLabel( 20, 170, LabelHue, "Firewalled:" ); + AddLabel( 150, 170, LabelHue, Firewall.List.Count.ToString() ); + + AddLabel( 20, 190, LabelHue, "Clients:" ); + AddLabel( 150, 190, LabelHue, NetState.Instances.Count.ToString() ); + + AddLabel( 20, 210, LabelHue, "Mobiles:" ); + AddLabel( 150, 210, LabelHue, World.Mobiles.Count.ToString() ); + + AddLabel( 20, 230, LabelHue, "Mobile Scripts:" ); + AddLabel( 150, 230, LabelHue, Core.ScriptMobiles.ToString() ); + + AddLabel( 20, 250, LabelHue, "Items:" ); + AddLabel( 150, 250, LabelHue, World.Items.Count.ToString() ); + + AddLabel( 20, 270, LabelHue, "Item Scripts:" ); + AddLabel( 150, 270, LabelHue, Core.ScriptItems.ToString() ); + + AddLabel( 20, 290, LabelHue, "Uptime:" ); + AddLabel( 150, 290, LabelHue, FormatTimeSpan( DateTime.Now - Clock.ServerStart ) ); + + AddLabel( 20, 310, LabelHue, "Memory:" ); + AddLabel( 150, 310, LabelHue, FormatByteAmount( GC.GetTotalMemory( false ) ) ); + + AddLabel( 20, 330, LabelHue, "Framework:" ); + AddLabel( 150, 330, LabelHue, Environment.Version.ToString() ); + + AddLabel( 20, 350, LabelHue, "Operating System: " ); + string os = Environment.OSVersion.ToString(); + + os = os.Replace( "Microsoft", "MSFT" ); + os = os.Replace( "Service Pack", "SP" ); + + AddLabel( 150, 350, LabelHue, os ); + + /*string str; + + try{ str = FormatTimeSpan( Core.Process.TotalProcessorTime ); } + catch{ str = "(unable to retrieve)"; } + + AddLabel( 20, 330, LabelHue, "Process Time:" ); + AddLabel( 250, 330, LabelHue, str );*/ + + /*try{ str = Core.Process.PriorityClass.ToString(); } + catch{ str = "(unable to retrieve)"; } + + AddLabel( 20, 350, LabelHue, "Process Priority:" ); + AddLabel( 250, 350, LabelHue, str );*/ + + AddPageButton( 200, 20, GetButtonID( 0, 0 ), "General", AdminGumpPage.Information_General ); + AddPageButton( 200, 40, GetButtonID( 0, 5 ), "Performance", AdminGumpPage.Information_Perf ); + + break; + } + case AdminGumpPage.Information_Perf: + { + AddLabel( 20, 130, LabelHue, "Cycles Per Second:" ); + AddLabel( 40, 150, LabelHue, "Current: " + Core.CyclesPerSecond.ToString( "N2" ) ); + AddLabel( 40, 170, LabelHue, "Average: " + Core.AverageCPS.ToString( "N2" ) ); + + StringBuilder sb = new StringBuilder(); + + int curUser, maxUser; + int curIOCP, maxIOCP; + + System.Threading.ThreadPool.GetAvailableThreads( out curUser, out curIOCP ); + System.Threading.ThreadPool.GetMaxThreads( out maxUser, out maxIOCP ); + + sb.Append( "Worker Threads:
Capacity: " ); + sb.Append( maxUser ); + sb.Append( "
Available: " ); + sb.Append( curUser ); + sb.Append( "
Usage: " ); + sb.Append( ((maxUser - curUser) * 100) / maxUser ); + sb.Append( "%

IOCP Threads:
Capacity: " ); + sb.Append( maxIOCP ); + sb.Append( "
Available: " ); + sb.Append( curIOCP ); + sb.Append( "
Usage: " ); + sb.Append( ((maxIOCP - curIOCP) * 100) / maxIOCP ); + sb.Append( "%" ); + + List pools = BufferPool.Pools; + + lock ( pools ) + { + for ( int i = 0; i < pools.Count; ++i ) + { + BufferPool pool = pools[i]; + string name; + int freeCount; + int initialCapacity; + int currentCapacity; + int bufferSize; + int misses; + + pool.GetInfo( out name, out freeCount, out initialCapacity, out currentCapacity, out bufferSize, out misses ); + + if ( sb.Length > 0 ) + sb.Append( "

" ); + + sb.Append( name ); + sb.Append( "
Size: " ); + sb.Append( FormatByteAmount( bufferSize ) ); + sb.Append( "
Capacity: " ); + sb.Append( currentCapacity ); + sb.Append( " (" ); + sb.Append( misses ); + sb.Append( " misses)
Available: " ); + sb.Append( freeCount ); + sb.Append( "
Usage: " ); + sb.Append( ((currentCapacity - freeCount) * 100) / currentCapacity ); + sb.Append( "% : " ); + sb.Append( FormatByteAmount( (currentCapacity - freeCount) * bufferSize ) ); + sb.Append( " of " ); + sb.Append( FormatByteAmount( currentCapacity * bufferSize ) ); + } + } + + + AddLabel( 20, 200, LabelHue, "Pooling:" ); + AddHtml( 20, 220, 380, 150, sb.ToString(), true, true ); + + AddPageButton( 200, 20, GetButtonID( 0, 0 ), "General", AdminGumpPage.Information_General ); + AddPageButton( 200, 40, GetButtonID( 0, 5 ), "Performance", AdminGumpPage.Information_Perf ); + + break; + } + case AdminGumpPage.Administer_WorldBuilding: + { + AddHtml( 10, 125, 400, 20, Color( Center( "Generating" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 150, GetButtonID( 3, 100 ), "Documentation" ); + AddButtonLabeled( 220, 150, GetButtonID( 3, 107 ), "Rebuild Categorization" ); + + AddButtonLabeled( 20, 175, GetButtonID( 3, 101 ), "Teleporters" ); + AddButtonLabeled( 220, 175, GetButtonID( 3, 102 ), "Moongates" ); + + AddButtonLabeled( 20, 200, GetButtonID( 3, 103 ), "Vendors" ); + AddButtonLabeled( 220, 200, GetButtonID( 3, 106 ), "Decoration" ); + + AddButtonLabeled( 20, 225, GetButtonID( 3, 104 ), "Doors" ); + AddButtonLabeled( 220, 225, GetButtonID( 3, 105 ), "Signs" ); + + AddHtml( 20, 275, 400, 30, Color( Center( "Statics" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 300, GetButtonID( 3, 110 ), "Freeze (Target)" ); + AddButtonLabeled( 20, 325, GetButtonID( 3, 111 ), "Freeze (World)" ); + AddButtonLabeled( 20, 350, GetButtonID( 3, 112 ), "Freeze (Map)" ); + + AddButtonLabeled( 220, 300, GetButtonID( 3, 120 ), "Unfreeze (Target)" ); + AddButtonLabeled( 220, 325, GetButtonID( 3, 121 ), "Unfreeze (World)" ); + AddButtonLabeled( 220, 350, GetButtonID( 3, 122 ), "Unfreeze (Map)" ); + + goto case AdminGumpPage.Administer; + } + case AdminGumpPage.Administer_Server: + { + AddHtml( 10, 125, 400, 20, Color( Center( "Server" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 150, GetButtonID( 3, 200 ), "Save" ); + + if ( !Core.Service ) + { + AddButtonLabeled( 20, 180, GetButtonID( 3, 201 ), "Shutdown (With Save)" ); + AddButtonLabeled( 20, 200, GetButtonID( 3, 202 ), "Shutdown (Without Save)" ); + + AddButtonLabeled( 20, 230, GetButtonID( 3, 203 ), "Shutdown & Restart (With Save)" ); + AddButtonLabeled( 20, 250, GetButtonID( 3, 204 ), "Shutdown & Restart (Without Save)" ); + } + else + { + AddLabel( 20, 215, LabelHue, "Shutdown/Restart not available." ); + } + + AddHtml( 10, 295, 400, 20, Color( Center( "Broadcast" ), LabelColor32 ), false, false ); + + AddTextField( 20, 320, 380, 20, 0 ); + AddButtonLabeled( 20, 350, GetButtonID( 3, 210 ), "To Everyone" ); + AddButtonLabeled( 220, 350, GetButtonID( 3, 211 ), "To Staff" ); + + goto case AdminGumpPage.Administer; + } + case AdminGumpPage.Administer_Access_Lockdown: + { + AddHtml( 10, 125, 400, 20, Color( Center( "Server Lockdown" ), LabelColor32 ), false, false ); + + AddHtml( 20, 150, 380, 80, Color( "When enabled, only clients with an access level equal to or greater than the specified lockdown level may access the server. After setting a lockdown level, use the Purge Invalid Clients button to disconnect those clients without access.", LabelColor32 ), false, false ); + + AccessLevel level = Misc.AccountHandler.LockdownLevel; + bool isLockedDown = ( level > AccessLevel.Player ); + + AddSelectedButton( 20, 230, GetButtonID( 3, 500 ), "Not Locked Down", !isLockedDown ); + AddSelectedButton( 20, 260, GetButtonID( 3, 504 ), "Administrators", ( isLockedDown && level <= AccessLevel.Administrator ) ); + AddSelectedButton( 20, 280, GetButtonID( 3, 503 ), "Seers", ( isLockedDown && level <= AccessLevel.Seer ) ); + AddSelectedButton( 20, 300, GetButtonID( 3, 502 ), "Game Masters", ( isLockedDown && level <= AccessLevel.GameMaster ) ); + AddSelectedButton( 20, 320, GetButtonID( 3, 501 ), "Counselors", ( isLockedDown && level <= AccessLevel.Counselor ) ); + + AddButtonLabeled( 20, 350, GetButtonID( 3, 510 ), "Purge Invalid Clients" ); + + goto case AdminGumpPage.Administer; + } + case AdminGumpPage.Administer_Access: + { + AddHtml( 10, 125, 400, 20, Color( Center( "Access" ), LabelColor32 ), false, false ); + + AddHtml( 10, 155, 400, 20, Color( Center( "Connectivity" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 180, GetButtonID( 3, 300 ), "Kick" ); + AddButtonLabeled( 220, 180, GetButtonID( 3, 301 ), "Ban" ); + + AddButtonLabeled( 20, 210, GetButtonID( 3, 302 ), "Firewall" ); + AddButtonLabeled( 220, 210, GetButtonID( 3, 303 ), "Lockdown" ); + + AddHtml( 10, 245, 400, 20, Color( Center( "Staff" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 270, GetButtonID( 3, 310 ), "Make Player" ); + AddButtonLabeled( 20, 290, GetButtonID( 3, 311 ), "Make Counselor" ); + AddButtonLabeled( 20, 310, GetButtonID( 3, 312 ), "Make Game Master" ); + AddButtonLabeled( 20, 330, GetButtonID( 3, 313 ), "Make Seer" ); + + if ( from.AccessLevel > AccessLevel.Administrator ) + { + AddButtonLabeled( 220, 270, GetButtonID( 3, 314 ), "Make Administrator" ); + + if ( from.AccessLevel > AccessLevel.Developer ) + { + AddButtonLabeled( 220, 290, GetButtonID( 3, 315 ), "Make Developer" ); + + if ( from.AccessLevel >= AccessLevel.Owner ) + AddButtonLabeled( 220, 310, GetButtonID( 3, 316 ), "Make Owner" ); + } + } + + goto case AdminGumpPage.Administer; + } + case AdminGumpPage.Administer_Commands: + { + AddHtml( 10, 125, 400, 20, Color( Center( "Commands" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 150, GetButtonID( 3, 400 ), "Add" ); + AddButtonLabeled( 220, 150, GetButtonID( 3, 401 ), "Remove" ); + + AddButtonLabeled( 20, 170, GetButtonID( 3, 402 ), "Dupe" ); + AddButtonLabeled( 220, 170, GetButtonID( 3, 403 ), "Dupe in bag" ); + + AddButtonLabeled( 20, 200, GetButtonID( 3, 404 ), "Properties" ); + AddButtonLabeled( 220, 200, GetButtonID( 3, 405 ), "Skills" ); + + AddButtonLabeled( 20, 230, GetButtonID( 3, 406 ), "Mortal" ); + AddButtonLabeled( 220, 230, GetButtonID( 3, 407 ), "Immortal" ); + + AddButtonLabeled( 20, 250, GetButtonID( 3, 408 ), "Squelch" ); + AddButtonLabeled( 220, 250, GetButtonID( 3, 409 ), "Unsquelch" ); + + AddButtonLabeled( 20, 270, GetButtonID( 3, 410 ), "Freeze" ); + AddButtonLabeled( 220, 270, GetButtonID( 3, 411 ), "Unfreeze" ); + + AddButtonLabeled( 20, 290, GetButtonID( 3, 412 ), "Hide" ); + AddButtonLabeled( 220, 290, GetButtonID( 3, 413 ), "Unhide" ); + + AddButtonLabeled( 20, 310, GetButtonID( 3, 414 ), "Kill" ); + AddButtonLabeled( 220, 310, GetButtonID( 3, 415 ), "Resurrect" ); + + AddButtonLabeled( 20, 330, GetButtonID( 3, 416 ), "Move" ); + AddButtonLabeled( 220, 330, GetButtonID( 3, 417 ), "Wipe" ); + + AddButtonLabeled( 20, 350, GetButtonID( 3, 418 ), "Teleport" ); + AddButtonLabeled( 220, 350, GetButtonID( 3, 419 ), "Teleport (Multiple)" ); + + goto case AdminGumpPage.Administer; + } + case AdminGumpPage.Administer: + { + AddPageButton( 200, 20, GetButtonID( 3, 0 ), "World Building", AdminGumpPage.Administer_WorldBuilding ); + AddPageButton( 200, 40, GetButtonID( 3, 1 ), "Server", AdminGumpPage.Administer_Server ); + AddPageButton( 200, 60, GetButtonID( 3, 2 ), "Access", AdminGumpPage.Administer_Access, AdminGumpPage.Administer_Access_Lockdown ); + AddPageButton( 200, 80, GetButtonID( 3, 3 ), "Commands", AdminGumpPage.Administer_Commands ); + + break; + } + case AdminGumpPage.Clients: + { + if ( m_List == null ) + { + m_List = new ArrayList( (ICollection)NetState.Instances ); + m_List.Sort( NetStateComparer.Instance ); + } + + AddClientHeader(); + + AddLabelCropped( 12, 120, 81, 20, LabelHue, "Name" ); + AddLabelCropped( 95, 120, 81, 20, LabelHue, "Account" ); + AddLabelCropped( 178, 120, 81, 20, LabelHue, "Access Level" ); + AddLabelCropped( 273, 120,109, 20, LabelHue, "IP Address" ); + + if ( listPage > 0 ) + AddButton( 375, 122, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 375, 122, 0x25EA ); + + if ( (listPage + 1) * 12 < m_List.Count ) + AddButton( 392, 122, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 392, 122, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddLabel( 12, 140, LabelHue, "There are no clients to display." ); + + for ( int i = 0, index = (listPage * 12); i < 12 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + NetState ns = m_List[index] as NetState; + + if ( ns == null ) + continue; + + Mobile m = ns.Mobile; + Account a = ns.Account as Account; + int offset = 140 + (i * 20); + + if ( m == null ) + { + if ( Admin.AdminNetwork.IsAuth( ns ) ) + AddLabelCropped( 12, offset, 81, 20, LabelHue, "(remote admin)" ); + else + AddLabelCropped( 12, offset, 81, 20, LabelHue, "(logging in)" ); + } + else + { + AddLabelCropped( 12, offset, 81, 20, GetHueFor( m ), m.Name ); + } + AddLabelCropped( 95, offset, 81, 20, LabelHue, a == null ? "(no account)" : a.Username ); + AddLabelCropped( 178, offset, 81, 20, LabelHue, m == null ? (a != null ? FormatAccessLevel( a.AccessLevel ) : "") : FormatAccessLevel( m.AccessLevel ) ); + AddLabelCropped( 273, offset,109, 20, LabelHue, ns.ToString() ); + + if ( a != null || m != null ) + AddButton( 380, offset - 1, 0xFA5, 0xFA7, GetButtonID( 4, index + 2 ), GumpButtonType.Reply, 0 ); + } + + break; + } + case AdminGumpPage.ClientInfo: + { + Mobile m = state as Mobile; + + if ( m == null ) + break; + + AddClientHeader(); + + AddHtml( 10, 125, 400, 20, Color( Center( "Information" ), LabelColor32 ), false, false ); + + int y = 146; + + AddLabel( 20, y, LabelHue, "Name:" ); + AddLabel( 200, y, GetHueFor( m ), m.Name ); + y += 20; + + Account a = m.Account as Account; + + AddLabel( 20, y, LabelHue, "Account:" ); + AddLabel( 200, y, (a != null && a.Banned) ? RedHue : LabelHue, a == null ? "(no account)" : a.Username ); + AddButton( 380, y, 0xFA5, 0xFA7, GetButtonID( 7, 14 ), GumpButtonType.Reply, 0 ); + y += 20; + + NetState ns = m.NetState; + + if ( ns == null ) + { + AddLabel( 20, y, LabelHue, "Address:" ); + AddLabel( 200, y, RedHue, "Offline" ); + y += 20; + + AddLabel( 20, y, LabelHue, "Location:" ); + AddLabel( 200, y, LabelHue, String.Format( "{0} [{1}]", m.Location, m.Map ) ); + y += 44; + } + else + { + AddLabel( 20, y, LabelHue, "Address:" ); + AddLabel( 200, y, GreenHue, ns.ToString() ); + y += 20; + + ClientVersion v = ns.Version; + + AddLabel( 20, y, LabelHue, "Version:" ); + AddLabel( 200, y, LabelHue, v == null ? "(null)" : v.ToString() ); + y += 20; + + AddLabel( 20, y, LabelHue, "Location:" ); + AddLabel( 200, y, LabelHue, String.Format( "{0} [{1}]", m.Location, m.Map ) ); + y += 24; + } + + AddButtonLabeled( 20, y, GetButtonID( 7, 0 ), "Go to" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 1 ), "Get" ); + y += 20; + + AddButtonLabeled( 20, y, GetButtonID( 7, 2 ), "Kick" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 3 ), "Ban" ); + y += 20; + + AddButtonLabeled( 20, y, GetButtonID( 7, 4 ), "Properties" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 5 ), "Skills" ); + y += 20; + + AddButtonLabeled( 20, y, GetButtonID( 7, 6 ), "Mortal" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 7 ), "Immortal" ); + y += 20; + + AddButtonLabeled( 20, y, GetButtonID( 7, 8 ), "Squelch" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 9 ), "Unsquelch" ); + y += 20; + + /*AddButtonLabeled( 20, y, GetButtonID( 7, 10 ), "Hide" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 11 ), "Unhide" ); + y += 20;*/ + + AddButtonLabeled( 20, y, GetButtonID( 7, 12 ), "Kill" ); + AddButtonLabeled( 200, y, GetButtonID( 7, 13 ), "Resurrect" ); + y += 20; + + break; + } + case AdminGumpPage.Accounts_Shared: + { + if ( m_List == null ) + m_List = GetAllSharedAccounts(); + + AddLabelCropped( 12, 120, 60, 20, LabelHue, "Count" ); + AddLabelCropped( 72, 120, 120, 20, LabelHue, "Address" ); + AddLabelCropped( 192, 120, 180, 20, LabelHue, "Accounts" ); + + if ( listPage > 0 ) + AddButton( 375, 122, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 375, 122, 0x25EA ); + + if ( (listPage + 1) * 12 < m_List.Count ) + AddButton( 392, 122, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 392, 122, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddLabel( 12, 140, LabelHue, "There are no accounts to display." ); + + StringBuilder sb = new StringBuilder(); + + for ( int i = 0, index = (listPage * 12); i < 12 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + DictionaryEntry de = (DictionaryEntry)m_List[index]; + + IPAddress ipAddr = (IPAddress)de.Key; + ArrayList accts = (ArrayList)de.Value; + + int offset = 140 + (i * 20); + + AddLabelCropped( 12, offset, 60, 20, LabelHue, accts.Count.ToString() ); + AddLabelCropped( 72, offset, 120, 20, LabelHue, ipAddr.ToString() ); + + if ( sb.Length > 0 ) + sb.Length = 0; + + for ( int j = 0; j < accts.Count; ++j ) + { + if ( j > 0 ) + sb.Append( ", " ); + + if ( j < 4 ) + { + Account acct = (Account)accts[j]; + + sb.Append( acct.Username ); + } + else + { + sb.Append( "..." ); + break; + } + } + + AddLabelCropped( 192, offset, 180, 20, LabelHue, sb.ToString() ); + + AddButton( 380, offset - 1, 0xFA5, 0xFA7, GetButtonID( 5, index + 56 ), GumpButtonType.Reply, 0 ); + } + + break; + } + case AdminGumpPage.Accounts: + { + if ( m_List == null ) + { + m_List = new ArrayList( (ICollection)Accounts.GetAccounts() ); + m_List.Sort( AccountComparer.Instance ); + } + + ArrayList rads = ( state as ArrayList ); + + AddAccountHeader(); + + if ( rads == null ) + AddLabelCropped( 12, 120, 120, 20, LabelHue, "Name" ); + else + AddLabelCropped( 32, 120, 100, 20, LabelHue, "Name" ); + + AddLabelCropped( 132, 120, 120, 20, LabelHue, "Access Level" ); + AddLabelCropped( 252, 120, 120, 20, LabelHue, "Status" ); + + if ( listPage > 0 ) + AddButton( 375, 122, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 375, 122, 0x25EA ); + + if ( (listPage + 1) * 12 < m_List.Count ) + AddButton( 392, 122, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 392, 122, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddLabel( 12, 140, LabelHue, "There are no accounts to display." ); + + if ( rads != null && notice == null ) + { + AddButtonLabeled( 10, 390, GetButtonID( 5, 27 ), "Ban marked" ); + AddButtonLabeled( 10, 410, GetButtonID( 5, 28 ), "Delete marked" ); + + AddButtonLabeled( 210, 400, GetButtonID( 5, 29 ), "Mark all" ); + } + + for ( int i = 0, index = (listPage * 12); i < 12 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + Account a = m_List[index] as Account; + + if ( a == null ) + continue; + + int offset = 140 + (i * 20); + + AccessLevel accessLevel; + bool online; + + GetAccountInfo( a, out accessLevel, out online ); + + if ( rads == null ) + { + AddLabelCropped( 12, offset, 120, 20, LabelHue, a.Username ); + } + else + { + AddCheck( 10, offset, 0xD2, 0xD3, rads.Contains( a ), index ); + AddLabelCropped( 32, offset, 100, 20, LabelHue, a.Username ); + } + + AddLabelCropped( 132, offset, 120, 20, LabelHue, FormatAccessLevel( accessLevel ) ); + + if ( online ) + AddLabelCropped( 252, offset, 120, 20, GreenHue, "Online" ); + else if ( a.Banned ) + AddLabelCropped( 252, offset, 120, 20, RedHue, "Banned" ); + else + AddLabelCropped( 252, offset, 120, 20, RedHue, "Offline" ); + + AddButton( 380, offset - 1, 0xFA5, 0xFA7, GetButtonID( 5, index + 56 ), GumpButtonType.Reply, 0 ); + } + + break; + } + case AdminGumpPage.AccountDetails: + { + AddPageButton( 190, 10, GetButtonID( 5, 0 ), "Information", AdminGumpPage.AccountDetails_Information, AdminGumpPage.AccountDetails_ChangeAccess, AdminGumpPage.AccountDetails_ChangePassword ); + AddPageButton( 190, 30, GetButtonID( 5, 1 ), "Characters", AdminGumpPage.AccountDetails_Characters ); + AddPageButton( 190, 50, GetButtonID( 5, 13 ), "Access", AdminGumpPage.AccountDetails_Access, AdminGumpPage.AccountDetails_Access_ClientIPs, AdminGumpPage.AccountDetails_Access_Restrictions ); + AddPageButton( 190, 70, GetButtonID( 5, 2 ), "Comments", AdminGumpPage.AccountDetails_Comments ); + AddPageButton( 190, 90, GetButtonID( 5, 3 ), "Tags", AdminGumpPage.AccountDetails_Tags ); + break; + } + case AdminGumpPage.AccountDetails_ChangePassword: + { + Account a = state as Account; + + if ( a == null ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( "Change Password" ), LabelColor32 ), false, false ); + + AddLabel( 20, 150, LabelHue, "Username:" ); + AddLabel( 200, 150, LabelHue, a.Username ); + + AddLabel( 20, 180, LabelHue, "Password:" ); + AddTextField( 200, 180, 160, 20, 0 ); + + AddLabel( 20, 210, LabelHue, "Confirm:" ); + AddTextField( 200, 210, 160, 20, 1 ); + + AddButtonLabeled( 20, 240, GetButtonID( 5, 12 ), "Submit Change" ); + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.AccountDetails_ChangeAccess: + { + Account a = state as Account; + + if ( a == null ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( "Change Access Level" ), LabelColor32 ), false, false ); + + AddLabel( 20, 150, LabelHue, "Username:" ); + AddLabel( 200, 150, LabelHue, a.Username ); + + AddLabel( 20, 170, LabelHue, "Current Level:" ); + AddLabel( 200, 170, LabelHue, FormatAccessLevel( a.AccessLevel ) ); + + AddButtonLabeled( 20, 200, GetButtonID( 5, 20 ), "Player" ); + AddButtonLabeled( 20, 220, GetButtonID( 5, 21 ), "Counselor" ); + AddButtonLabeled( 20, 240, GetButtonID( 5, 22 ), "Game Master" ); + AddButtonLabeled( 20, 260, GetButtonID( 5, 23 ), "Seer" ); + + if ( from.AccessLevel > AccessLevel.Administrator ) + { + AddButtonLabeled( 20, 280, GetButtonID( 5, 24 ), "Administrator" ); + + if ( from.AccessLevel > AccessLevel.Developer ) + { + AddButtonLabeled( 20, 300, GetButtonID( 5, 33 ), "Developer" ); + + if ( from.AccessLevel >= AccessLevel.Owner ) + AddButtonLabeled( 20, 320, GetButtonID( 5, 34 ), "Owner" ); + } + } + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.AccountDetails_Information: + { + Account a = state as Account; + + if ( a == null ) + break; + + int charCount = 0; + + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] != null ) + ++charCount; + } + + AddHtml( 10, 125, 400, 20, Color( Center( "Information" ), LabelColor32 ), false, false ); + + AddLabel( 20, 150, LabelHue, "Username:" ); + AddLabel( 200, 150, LabelHue, a.Username ); + + AddLabel( 20, 170, LabelHue, "Access Level:" ); + AddLabel( 200, 170, LabelHue, FormatAccessLevel( a.AccessLevel ) ); + + AddLabel( 20, 190, LabelHue, "Status:" ); + AddLabel( 200, 190, a.Banned ? RedHue : GreenHue, a.Banned ? "Banned" : "Active" ); + + DateTime banTime; + TimeSpan banDuration; + + if ( a.Banned && a.GetBanTags( out banTime, out banDuration ) ) + { + if ( banDuration == TimeSpan.MaxValue ) + { + AddLabel( 250, 190, LabelHue, "(Infinite)" ); + } + else if ( banDuration == TimeSpan.Zero ) + { + AddLabel( 250, 190, LabelHue, "(Zero)" ); + } + else + { + TimeSpan remaining = (DateTime.Now - banTime); + + if ( remaining < TimeSpan.Zero ) + remaining = TimeSpan.Zero; + else if ( remaining > banDuration ) + remaining = banDuration; + + double remMinutes = remaining.TotalMinutes; + double totMinutes = banDuration.TotalMinutes; + + double perc = remMinutes / totMinutes; + + AddLabel( 250, 190, LabelHue, String.Format( "{0} [{1:F0}%]", FormatTimeSpan( banDuration ), perc*100 ) ); + } + } + else if ( a.Banned ) + { + AddLabel( 250, 190, LabelHue, "(Unspecified)" ); + } + + AddLabel( 20, 210, LabelHue, "Created:" ); + AddLabel( 200, 210, LabelHue, a.Created.ToString() ); + + AddLabel( 20, 230, LabelHue, "Last Login:" ); + AddLabel( 200, 230, LabelHue, a.LastLogin.ToString() ); + + AddLabel( 20, 250, LabelHue, "Character Count:" ); + AddLabel( 200, 250, LabelHue, charCount.ToString() ); + + AddLabel( 20, 270, LabelHue, "Comment Count:" ); + AddLabel( 200, 270, LabelHue, a.Comments.Count.ToString() ); + + AddLabel( 20, 290, LabelHue, "Tag Count:" ); + AddLabel( 200, 290, LabelHue, a.Tags.Count.ToString() ); + + AddButtonLabeled( 20, 320, GetButtonID( 5, 8 ), "Change Password" ); + AddButtonLabeled( 200, 320, GetButtonID( 5, 9 ), "Change Access Level" ); + + if ( !a.Banned ) + AddButtonLabeled( 20, 350, GetButtonID( 5, 10 ), "Ban Account" ); + else + AddButtonLabeled( 20, 350, GetButtonID( 5, 11 ), "Unban Account" ); + + AddButtonLabeled( 200, 350, GetButtonID( 5, 25 ), "Delete Account" ); + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.AccountDetails_Access: + { + Account a = state as Account; + + if ( a == null ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( "Access" ), LabelColor32 ), false, false ); + + AddPageButton( 20, 150, GetButtonID( 5, 14 ), "View client addresses", AdminGumpPage.AccountDetails_Access_ClientIPs ); + AddPageButton( 20, 170, GetButtonID( 5, 15 ), "Manage restrictions", AdminGumpPage.AccountDetails_Access_Restrictions ); + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.AccountDetails_Access_ClientIPs: + { + Account a = state as Account; + + if ( a == null ) + break; + + if ( m_List == null ) + m_List = new ArrayList( a.LoginIPs ); + + AddHtml( 10, 195, 400, 20, Color( Center( "Client Addresses" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 200, 225, GetButtonID( 5, 16 ), "View all shared accounts" ); + AddButtonLabeled( 200, 245, GetButtonID( 5, 17 ), "Ban all shared accounts" ); + AddButtonLabeled( 200, 265, GetButtonID( 5, 18 ), "Firewall all addresses" ); + + AddHtml( 195, 295, 210, 80, Color( "List of IP addresses which have accessed this account.", LabelColor32 ), false, false ); + + AddImageTiled( 15, 219, 176, 156, 0xBBC ); + AddBlackAlpha( 16, 220, 174, 154 ); + + AddHtml( 18, 221, 114, 20, Color( "IP Address", LabelColor32 ), false, false ); + + if ( listPage > 0 ) + AddButton( 154, 223, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 154, 223, 0x25EA ); + + if ( (listPage + 1) * 6 < m_List.Count ) + AddButton( 171, 223, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 171, 223, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddHtml( 18, 243, 170, 60, Color( "This account has not yet been accessed.", LabelColor32 ), false, false ); + + for ( int i = 0, index = (listPage * 6); i < 6 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + AddHtml( 18, 243 + (i * 22), 114, 20, Color( m_List[index].ToString(), LabelColor32 ), false, false ); + AddButton( 130, 242 + (i * 22), 0xFA2, 0xFA4, GetButtonID( 8, index ), GumpButtonType.Reply, 0 ); + AddButton( 160, 242 + (i * 22), 0xFA8, 0xFAA, GetButtonID( 9, index ), GumpButtonType.Reply, 0 ); + } + + goto case AdminGumpPage.AccountDetails_Access; + } + case AdminGumpPage.AccountDetails_Access_Restrictions: + { + Account a = state as Account; + + if ( a == null ) + break; + + if ( m_List == null ) + m_List = new ArrayList( a.IPRestrictions ); + + AddHtml( 10, 195, 400, 20, Color( Center( "Address Restrictions" ), LabelColor32 ), false, false ); + + AddTextField( 200, 225, 120, 20, 0 ); + + AddButtonLabeled( 330, 225, GetButtonID( 5, 19 ), "Add" ); + + AddHtml( 195, 255, 210, 120, Color( "Any clients connecting from an address not in this list will be rejected. Or, if the list is empty, any client may connect.", LabelColor32 ), false, false ); + + AddImageTiled( 15, 219, 176, 156, 0xBBC ); + AddBlackAlpha( 16, 220, 174, 154 ); + + AddHtml( 18, 221, 114, 20, Color( "IP Address", LabelColor32 ), false, false ); + + if ( listPage > 0 ) + AddButton( 154, 223, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 154, 223, 0x25EA ); + + if ( (listPage + 1) * 6 < m_List.Count ) + AddButton( 171, 223, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 171, 223, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddHtml( 18, 243, 170, 60, Color( "There are no addresses in this list.", LabelColor32 ), false, false ); + + for ( int i = 0, index = (listPage * 6); i < 6 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + AddHtml( 18, 243 + (i * 22), 114, 20, Color( m_List[index].ToString(), LabelColor32 ), false, false ); + AddButton( 160, 242 + (i * 22), 0xFB1, 0xFB3, GetButtonID( 8, index ), GumpButtonType.Reply, 0 ); + } + + goto case AdminGumpPage.AccountDetails_Access; + } + case AdminGumpPage.AccountDetails_Characters: + { + Account a = state as Account; + + if ( a == null ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( "Characters" ), LabelColor32 ), false, false ); + + AddLabelCropped( 12, 150, 120, 20, LabelHue, "Name" ); + AddLabelCropped( 132, 150, 120, 20, LabelHue, "Access Level" ); + AddLabelCropped( 252, 150, 120, 20, LabelHue, "Status" ); + + int index = 0; + + for ( int i = 0; i < a.Length; ++i ) + { + Mobile m = a[i]; + + if ( m == null ) + continue; + + int offset = 170 + (index * 20); + + AddLabelCropped( 12, offset, 120, 20, GetHueFor( m ), m.Name ); + AddLabelCropped( 132, offset, 120, 20, LabelHue, FormatAccessLevel( m.AccessLevel ) ); + + if ( m.NetState != null ) + AddLabelCropped( 252, offset, 120, 20, GreenHue, "Online" ); + else + AddLabelCropped( 252, offset, 120, 20, RedHue, "Offline" ); + + AddButton( 380, offset - 1, 0xFA5, 0xFA7, GetButtonID( 5, i + 50 ), GumpButtonType.Reply, 0 ); + + ++index; + } + + if ( index == 0 ) + AddLabel( 12, 170, LabelHue, "The character list is empty." ); + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.AccountDetails_Comments: + { + Account a = state as Account; + + if ( a == null ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( "Comments" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 150, GetButtonID( 5, 4 ), "Add Comment" ); + + StringBuilder sb = new StringBuilder(); + + if ( a.Comments.Count == 0 ) + sb.Append( "There are no comments for this account." ); + + for ( int i = 0; i < a.Comments.Count; ++i ) + { + if ( i > 0 ) + sb.Append( "

" ); + + AccountComment c = a.Comments[i]; + + sb.AppendFormat( "[{0}]
{1}", c.AddedBy, c.Content ); + } + + AddHtml( 20, 180, 380, 190, sb.ToString(), true, true ); + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.AccountDetails_Tags: + { + Account a = state as Account; + + if ( a == null ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( "Tags" ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 150, GetButtonID( 5, 5 ), "Add Tag" ); + + StringBuilder sb = new StringBuilder(); + + if ( a.Tags.Count == 0 ) + sb.Append( "There are no tags for this account." ); + + for ( int i = 0; i < a.Tags.Count; ++i ) + { + if ( i > 0 ) + sb.Append( "
" ); + + AccountTag tag = a.Tags[i]; + + sb.AppendFormat( "{0} = {1}", tag.Name, tag.Value ); + } + + AddHtml( 20, 180, 380, 190, sb.ToString(), true, true ); + + goto case AdminGumpPage.AccountDetails; + } + case AdminGumpPage.Firewall: + { + AddFirewallHeader(); + + if ( m_List == null ) + m_List = new ArrayList( Firewall.List ); + + AddLabelCropped( 12, 120, 358, 20, LabelHue, "IP Address" ); + + if ( listPage > 0 ) + AddButton( 375, 122, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 375, 122, 0x25EA ); + + if ( (listPage + 1) * 12 < m_List.Count ) + AddButton( 392, 122, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 392, 122, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddLabel( 12, 140, LabelHue, "The firewall list is empty." ); + + for ( int i = 0, index = (listPage * 12); i < 12 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + object obj = m_List[index]; + + if ( !(obj is IPAddress) && !(obj is String) ) + break; + + int offset = 140 + (i * 20); + + AddLabelCropped( 12, offset, 358, 20, LabelHue, obj.ToString() ); + AddButton( 380, offset - 1, 0xFA5, 0xFA7, GetButtonID( 6, index + 4 ), GumpButtonType.Reply, 0 ); + } + + break; + } + case AdminGumpPage.FirewallInfo: + { + AddFirewallHeader(); + + if ( !(state is IPAddress) && !(state is String) ) + break; + + AddHtml( 10, 125, 400, 20, Color( Center( state.ToString() ), LabelColor32 ), false, false ); + + AddButtonLabeled( 20, 150, GetButtonID( 6, 3 ), "Remove" ); + + AddHtml( 10, 175, 400, 20, Color( Center( "Potentially Effected Accounts" ), LabelColor32 ), false, false ); + + if ( m_List == null ) + { + m_List = new ArrayList(); + + string pattern = state as String; + IPAddress addr = ( state is IPAddress ? (IPAddress)state : IPAddress.Any ); + + foreach ( Account acct in Accounts.GetAccounts() ) + { + IPAddress[] loginList = acct.LoginIPs; + + bool contains = false; + + for( int i = 0; !contains && i < loginList.Length; ++i ) + { + if( pattern == null ) //if is IP Address + { + contains = loginList[i].Equals( addr ); + break; + } + + contains = Utility.IPMatchCIDR( pattern, loginList[i] ); + + if( !contains ) + contains = Utility.IPMatch( pattern, loginList[i] ); + } + + + + if ( contains ) + m_List.Add( acct ); + } + + m_List.Sort( AccountComparer.Instance ); + } + + if ( listPage > 0 ) + AddButton( 375, 177, 0x15E3, 0x15E7, GetButtonID( 1, 0 ), GumpButtonType.Reply, 0 ); + else + AddImage( 375, 177, 0x25EA ); + + if ( (listPage + 1) * 12 < m_List.Count ) + AddButton( 392, 177, 0x15E1, 0x15E5, GetButtonID( 1, 1 ), GumpButtonType.Reply, 0 ); + else + AddImage( 392, 177, 0x25E6 ); + + if ( m_List.Count == 0 ) + AddLabelCropped( 12, 200, 398, 20, LabelHue, "No accounts found." ); + + for ( int i = 0, index = (listPage * 9); i < 9 && index >= 0 && index < m_List.Count; ++i, ++index ) + { + Account a = m_List[index] as Account; + + if ( a == null ) + continue; + + int offset = 200 + (i * 20); + + AccessLevel accessLevel; + bool online; + + GetAccountInfo( a, out accessLevel, out online ); + + AddLabelCropped( 12, offset, 120, 20, LabelHue, a.Username ); + AddLabelCropped( 132, offset, 120, 20, LabelHue, FormatAccessLevel( accessLevel ) ); + + if ( online ) + AddLabelCropped( 252, offset, 120, 20, GreenHue, "Online" ); + else + AddLabelCropped( 252, offset, 120, 20, RedHue, "Offline" ); + + AddButton( 380, offset - 1, 0xFA5, 0xFA7, GetButtonID( 5, index + 56 ), GumpButtonType.Reply, 0 ); + } + + break; + } + } + } + + public void AddTextField( int x, int y, int width, int height, int index ) + { + AddBackground( x - 2, y - 2, width + 4, height + 4, 0x2486 ); + AddTextEntry( x + 2, y + 2, width - 4, height - 4, 0, index, "" ); + } + + public void AddClientHeader() + { + AddTextField( 200, 20, 200, 20, 0 ); + AddButtonLabeled( 200, 50, GetButtonID( 4, 0 ), "Search For Name" ); + AddButtonLabeled( 200, 80, GetButtonID( 4, 1 ), "Search For IP Address" ); + } + + public void AddAccountHeader() + { + AddPage( 1 ); + + AddLabel( 200, 20, LabelHue, "Name:" ); + AddTextField( 250, 20, 150, 20, 0 ); + + AddLabel( 200, 50, LabelHue, "Pass:" ); + AddTextField( 250, 50, 150, 20, 1 ); + + AddButtonLabeled( 200, 80, GetButtonID( 5, 6 ), "Add" ); + AddButtonLabeled( 290, 80, GetButtonID( 5, 7 ), "Search" ); + + AddButton( 384, 84, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 2 ); + + AddPage( 2 ); + + AddButtonLabeled( 200, 20, GetButtonID( 5, 31 ), "View All: Inactive" ); + AddButtonLabeled( 200, 40, GetButtonID( 5, 32 ), "View All: Banned" ); + AddButtonLabeled( 200, 60, GetButtonID( 5, 26 ), "View All: Shared" ); + AddButtonLabeled( 200, 80, GetButtonID( 5, 30 ), "View All: Empty" ); + + AddButton( 384, 84, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 1 ); + + AddPage( 0 ); + } + + public void AddFirewallHeader() + { + AddTextField( 200, 20, 200, 20, 0 ); + AddButtonLabeled( 320, 50, GetButtonID( 6, 0 ), "Search" ); + AddButtonLabeled( 200, 50, GetButtonID( 6, 1 ), "Add (Input)" ); + AddButtonLabeled( 200, 80, GetButtonID( 6, 2 ), "Add (Target)" ); + } + + private static ArrayList GetAllSharedAccounts() + { + Hashtable table = new Hashtable(); + ArrayList list; + + foreach ( Account acct in Accounts.GetAccounts() ) + { + IPAddress[] theirAddresses = acct.LoginIPs; + + for ( int i = 0; i < theirAddresses.Length; ++i ) + { + list = (ArrayList)table[theirAddresses[i]]; + + if ( list == null ) + table[theirAddresses[i]] = list = new ArrayList(); + + list.Add( acct ); + } + } + + list = new ArrayList( table ); + + for ( int i = 0; i < list.Count; ++i ) + { + DictionaryEntry de = (DictionaryEntry)list[i]; + ArrayList accts = (ArrayList)de.Value; + + if ( accts.Count == 1 ) + list.RemoveAt( i-- ); + else + accts.Sort( AccountComparer.Instance ); + } + + list.Sort( SharedAccountComparer.Instance ); + + return list; + } + + private class SharedAccountComparer : IComparer + { + public static readonly IComparer Instance = new SharedAccountComparer(); + + public SharedAccountComparer() + { + } + + public int Compare( object x, object y ) + { + DictionaryEntry a = (DictionaryEntry)x; + DictionaryEntry b = (DictionaryEntry)y; + + ArrayList aList = (ArrayList)a.Value; + ArrayList bList = (ArrayList)b.Value; + + return bList.Count - aList.Count; + } + } + + + private static ArrayList GetSharedAccounts( IPAddress ipAddress ) + { + ArrayList list = new ArrayList(); + + foreach ( Account acct in Accounts.GetAccounts() ) + { + IPAddress[] theirAddresses = acct.LoginIPs; + bool contains = false; + + for ( int i = 0; !contains && i < theirAddresses.Length; ++i ) + contains = ipAddress.Equals( theirAddresses[i] ); + + if ( contains ) + list.Add( acct ); + } + + list.Sort( AccountComparer.Instance ); + return list; + } + + private static ArrayList GetSharedAccounts( IPAddress[] ipAddresses ) + { + ArrayList list = new ArrayList(); + + foreach ( Account acct in Accounts.GetAccounts() ) + { + IPAddress[] theirAddresses = acct.LoginIPs; + bool contains = false; + + for ( int i = 0; !contains && i < theirAddresses.Length; ++i ) + { + IPAddress check = theirAddresses[i]; + + for ( int j = 0; !contains && j < ipAddresses.Length; ++j ) + contains = check.Equals( ipAddresses[j] ); + } + + if ( contains ) + list.Add( acct ); + } + + list.Sort( AccountComparer.Instance ); + return list; + } + + public static void BanShared_Callback( Mobile from, bool okay, object state ) + { + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + string notice; + ArrayList list = null; + + if ( okay ) + { + Account a = (Account)state; + list = GetSharedAccounts( a.LoginIPs ); + + for ( int i = 0; i < list.Count; ++i ) + { + ((Account)list[i]).SetUnspecifiedBan( from ); + ((Account)list[i]).Banned = true; + } + + notice = "All addresses in the list have been banned."; + } + else + { + notice = "You have chosen not to ban all shared accounts."; + } + + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, state ) ); + + if ( okay ) + from.SendGump( new BanDurationGump( list ) ); + } + + public static void AccountDelete_Callback( Mobile from, bool okay, object state ) + { + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + if ( okay ) + { + Account a = (Account)state; + + CommandLogging.WriteLine( from, "{0} {1} deleting account {2}", from.AccessLevel, CommandLogging.Format( from ), a.Username ); + a.Delete(); + + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, null, String.Format( "{0} : The account has been deleted.", a.Username ), null ) ); + } + else + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "You have chosen not to delete the account.", state ) ); + } + } + + public static void ResendGump_Callback( Mobile from, object state ) + { + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + object[] states = (object[])state; + ArrayList list = (ArrayList)states[0]; + ArrayList rads = (ArrayList)states[1]; + int page = (int)states[2]; + + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, page, list, null, rads ) ); + } + + public static void Marked_Callback( Mobile from, bool okay, object state ) + { + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + object[] states = (object[])state; + bool ban = (bool)states[0]; + ArrayList list = (ArrayList)states[1]; + ArrayList rads = (ArrayList)states[2]; + int page = (int)states[3]; + + if ( okay ) + { + if ( !ban ) + NetState.Pause(); + + for ( int i = 0; i < rads.Count; ++i ) + { + Account acct = (Account)rads[i]; + + if ( ban ) + { + CommandLogging.WriteLine( from, "{0} {1} banning account {2}", from.AccessLevel, CommandLogging.Format( from ), acct.Username ); + acct.SetUnspecifiedBan( from ); + acct.Banned = true; + } + else + { + CommandLogging.WriteLine( from, "{0} {1} deleting account {2}", from.AccessLevel, CommandLogging.Format( from ), acct.Username ); + acct.Delete(); + rads.RemoveAt( i-- ); + list.Remove( acct ); + } + } + + if ( !ban ) + NetState.Resume(); + + from.SendGump( new NoticeGump( 1060637, 30720, String.Format( "You have {0} the account{1}.", ban ? "banned" : "deleted", rads.Count == 1 ? "" : "s" ), 0xFFC000, 420, 280, new NoticeGumpCallback( ResendGump_Callback ), new object[]{ list, rads, ban ? page : 0 } ) ); + + if ( ban ) + from.SendGump( new BanDurationGump( list ) ); + } + else + { + from.SendGump( new NoticeGump( 1060637, 30720, String.Format( "You have chosen not to {0} the account{1}.", ban ? "ban" : "delete", rads.Count == 1 ? "" : "s" ), 0xFFC000, 420, 280, new NoticeGumpCallback( ResendGump_Callback ), new object[]{ list, rads, page } ) ); + } + } + + public static void FirewallShared_Callback( Mobile from, bool okay, object state ) + { + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + string notice; + + if ( okay ) + { + Account a = (Account)state; + + for ( int i = 0; i < a.LoginIPs.Length; ++i ) + Firewall.Add( a.LoginIPs[i] ); + + notice = "All addresses in the list have been firewalled."; + } + else + { + notice = "You have chosen not to firewall all addresses."; + } + + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, state ) ); + } + + public static void Firewall_Callback( Mobile from, bool okay, object state ) + { + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + object[] states = (object[])state; + + Account a = (Account)states[0]; + object toFirewall = states[1]; + + string notice; + + if ( okay ) + { + Firewall.Add( toFirewall ); + + notice = String.Format( "{0} : Added to firewall.", toFirewall ); + } + else + { + notice = "You have chosen not to firewall the address."; + } + + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, notice, a ) ); + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + int val = info.ButtonID - 1; + + if ( val < 0 ) + return; + + Mobile from = m_From; + + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + if ( m_PageType == AdminGumpPage.Accounts ) + { + ArrayList list = m_List; + ArrayList rads = m_State as ArrayList; + + if ( list != null && rads != null ) + { + for ( int i = 0, v = m_ListPage*12; i < 12 && v < list.Count; ++i, ++v ) + { + object obj = list[v]; + + if ( info.IsSwitched( v ) ) + { + if ( !rads.Contains( obj ) ) + rads.Add( obj ); + } + else if ( rads.Contains( obj ) ) + { + rads.Remove( obj ); + } + } + } + } + + int type = val % 10; + int index = val / 10; + + switch ( type ) + { + case 0: + { + AdminGumpPage page; + + switch ( index ) + { + case 0: page = AdminGumpPage.Information_General; break; + case 1: page = AdminGumpPage.Administer; break; + case 2: page = AdminGumpPage.Clients; break; + case 3: page = AdminGumpPage.Accounts; break; + case 4: page = AdminGumpPage.Firewall; break; + case 5: page = AdminGumpPage.Information_Perf; break; + default: return; + } + + from.SendGump( new AdminGump( from, page, 0, null, null, null ) ); + break; + } + case 1: + { + switch ( index ) + { + case 0: + { + if ( m_List != null && m_ListPage > 0 ) + from.SendGump( new AdminGump( from, m_PageType, m_ListPage - 1, m_List, null, m_State ) ); + + break; + } + case 1: + { + if ( m_List != null /*&& (m_ListPage + 1) * 12 < m_List.Count*/ ) + from.SendGump( new AdminGump( from, m_PageType, m_ListPage + 1, m_List, null, m_State ) ); + + break; + } + } + + break; + } + case 3: + { + string notice = null; + AdminGumpPage page = AdminGumpPage.Administer; + + if ( index >= 500 ) + page = AdminGumpPage.Administer_Access_Lockdown; + else if ( index >= 400 ) + page = AdminGumpPage.Administer_Commands; + else if ( index >= 300 ) + page = AdminGumpPage.Administer_Access; + else if ( index >= 200 ) + page = AdminGumpPage.Administer_Server; + else if ( index >= 100 ) + page = AdminGumpPage.Administer_WorldBuilding; + + switch ( index ) + { + case 0: page = AdminGumpPage.Administer_WorldBuilding; break; + case 1: page = AdminGumpPage.Administer_Server; break; + case 2: page = AdminGumpPage.Administer_Access; break; + case 3: page = AdminGumpPage.Administer_Commands; break; + + case 100: InvokeCommand( "DocGen" ); notice = "Documentation has been generated."; break; + case 101: InvokeCommand( "TelGen" ); notice = "Teleporters have been generated."; break; + case 102: InvokeCommand( "MoonGen" ); notice = "Moongates have been generated."; break; + case 103: InvokeCommand( "UOAMVendors" ); notice = "Vendor spawners have been generated."; break; + case 104: InvokeCommand( "DoorGen" ); notice = "Doors have been generated."; break; + case 105: InvokeCommand( "SignGen" ); notice = "Signs have been generated."; break; + case 106: InvokeCommand( "Decorate" ); notice = "Decoration has been generated."; break; + case 107: InvokeCommand( "RebuildCategorization" ); notice = "Categorization menu has been regenerated. The server should be restarted."; break; + + case 110: InvokeCommand( "Freeze" ); notice = "Target bounding points."; break; + case 120: InvokeCommand( "Unfreeze" ); notice = "Target bounding points."; break; + + case 200: InvokeCommand( "Save" ); notice = "The world has been saved."; break; + case 201: Shutdown( false, true ); break; + case 202: Shutdown( false, false ); break; + case 203: Shutdown( true, true ); break; + case 204: Shutdown( true, false ); break; + case 210: + case 211: + { + TextRelay relay = info.GetTextEntry( 0 ); + string text = ( relay == null ? null : relay.Text.Trim() ); + + if ( text == null || text.Length == 0 ) + { + notice = "You must enter text to broadcast it."; + } + else + { + notice = "Your message has been broadcasted."; + InvokeCommand( String.Format( "{0} {1}", index == 210 ? "BC" : "SM", text ) ); + } + + break; + } + + case 300: InvokeCommand( "Kick" ); notice = "Target the player to kick."; break; + case 301: InvokeCommand( "Ban" ); notice = "Target the player to ban."; break; + case 302: InvokeCommand( "Firewall" ); notice = "Target the player to firewall."; break; + + case 303: page = AdminGumpPage.Administer_Access_Lockdown; break; + + case 310: InvokeCommand( "Set AccessLevel Player" ); notice = "Target the player to change their access level. (Player)"; break; + case 311: InvokeCommand( "Set AccessLevel Counselor" ); notice = "Target the player to change their access level. (Counselor)"; break; + case 312: InvokeCommand( "Set AccessLevel GameMaster" ); notice = "Target the player to change their access level. (Game Master)"; break; + case 313: InvokeCommand( "Set AccessLevel Seer" ); notice = "Target the player to change their access level. (Seer)"; break; + + case 314: + { + if ( from.AccessLevel > AccessLevel.Administrator ) + { + InvokeCommand( "Set AccessLevel Administrator" ); + notice = "Target the player to change their access level. (Administrator)"; + } + + break; + } + + case 315: + { + if ( from.AccessLevel > AccessLevel.Developer ) + { + InvokeCommand( "Set AccessLevel Developer" ); + notice = "Target the player to change their access level. (Developer)"; + } + + break; + } + + case 316: + { + if ( from.AccessLevel >= AccessLevel.Owner ) + { + InvokeCommand( "Set AccessLevel Owner" ); + notice = "Target the player to change their access level. (Owner)"; + } + + break; + } + + case 400: notice = "Enter search terms to add objects."; break; + case 401: InvokeCommand( "Remove" ); notice = "Target the item or mobile to remove."; break; + case 402: InvokeCommand( "Dupe" ); notice = "Target the item to dupe."; break; + case 403: InvokeCommand( "DupeInBag" ); notice = "Target the item to dupe. The item will be duped at it's current location."; break; + case 404: InvokeCommand( "Props" ); notice = "Target the item or mobile to inspect."; break; + case 405: InvokeCommand( "Skills" ); notice = "Target a mobile to view their skills."; break; + case 406: InvokeCommand( "Set Blessed False" ); notice = "Target the mobile to make mortal."; break; + case 407: InvokeCommand( "Set Blessed True" ); notice = "Target the mobile to make immortal."; break; + case 408: InvokeCommand( "Set Squelched True" ); notice = "Target the mobile to squelch."; break; + case 409: InvokeCommand( "Set Squelched False" ); notice = "Target the mobile to unsquelch."; break; + case 410: InvokeCommand( "Set Frozen True" ); notice = "Target the mobile to freeze."; break; + case 411: InvokeCommand( "Set Frozen False" ); notice = "Target the mobile to unfreeze."; break; + case 412: InvokeCommand( "Set Hidden True" ); notice = "Target the mobile to hide."; break; + case 413: InvokeCommand( "Set Hidden False" ); notice = "Target the mobile to unhide."; break; + case 414: InvokeCommand( "Kill" ); notice = "Target the mobile to kill."; break; + case 415: InvokeCommand( "Resurrect" ); notice = "Target the mobile to resurrect."; break; + case 416: InvokeCommand( "Move" ); notice = "Target the item or mobile to move."; break; + case 417: InvokeCommand( "Wipe" ); notice = "Target bounding points."; break; + case 418: InvokeCommand( "Tele" ); notice = "Choose your destination."; break; + case 419: InvokeCommand( "Multi Tele" ); notice = "Choose your destination."; break; + + case 500: + case 501: + case 502: + case 503: + case 504: + { + Misc.AccountHandler.LockdownLevel = (AccessLevel)(index - 500); + + if ( Misc.AccountHandler.LockdownLevel > AccessLevel.Player ) + notice = "The lockdown level has been changed."; + else + notice = "The server is now accessible to everyone."; + + break; + } + + case 510: + { + AccessLevel level = Misc.AccountHandler.LockdownLevel; + + if ( level > AccessLevel.Player ) + { + List clients = NetState.Instances; + int count = 0; + + for ( int i = 0; i < clients.Count; ++i ) + { + NetState ns = clients[i]; + IAccount a = ns.Account; + + if ( a == null ) + continue; + + bool hasAccess = false; + + if ( a.AccessLevel >= level ) + { + hasAccess = true; + } + else + { + for ( int j = 0; !hasAccess && j < a.Length; ++j ) + { + Mobile m = a[j]; + + if ( m != null && m.AccessLevel >= level ) + hasAccess = true; + } + } + + if ( !hasAccess ) + { + ns.Dispose(); + ++count; + } + } + + if ( count == 0 ) + notice = "Nobody without access was found to disconnect."; + else + notice = String.Format( "Number of players disconnected: {0}", count ); + } + else + { + notice = "The server is not currently locked down."; + } + + break; + } + } + + from.SendGump( new AdminGump( from, page, 0, null, notice, null ) ); + + switch ( index ) + { + case 400: InvokeCommand( "Add" ); break; + case 111: InvokeCommand( "FreezeWorld" ); break; + case 112: InvokeCommand( "FreezeMap" ); break; + case 121: InvokeCommand( "UnfreezeWorld" ); break; + case 122: InvokeCommand( "UnfreezeMap" ); break; + } + + break; + } + case 4: + { + switch ( index ) + { + case 0: + case 1: + { + bool forName = ( index == 0 ); + + ArrayList results = new ArrayList(); + + TextRelay matchEntry = info.GetTextEntry( 0 ); + string match = ( matchEntry == null ? null : matchEntry.Text.Trim().ToLower() ); + string notice = null; + + if ( match == null || match.Length == 0 ) + { + notice = String.Format( "You must enter {0} to search.", forName ? "a name" : "an ip address" ); + } + else + { + List instances = NetState.Instances; + + for ( int i = 0; i < instances.Count; ++i ) + { + NetState ns = instances[i]; + + bool isMatch; + + if ( forName ) + { + Mobile m = ns.Mobile; + IAccount a = ns.Account; + + isMatch = ( m != null && m.Name.ToLower().IndexOf( match ) >= 0 ) + || ( a != null && a.Username.ToLower().IndexOf( match ) >= 0 ); + } + else + { + isMatch = ( ns.ToString().IndexOf( match ) >= 0 ); + } + + if ( isMatch ) + results.Add( ns ); + } + + results.Sort( NetStateComparer.Instance ); + } + + if ( results.Count == 1 ) + { + NetState ns = (NetState)results[0]; + object state = ns.Mobile; + + if ( state == null ) + state = ns.Account; + + if ( state is Mobile ) + from.SendGump( new AdminGump( from, AdminGumpPage.ClientInfo, 0, null, "One match found.", state ) ); + else if ( state is Account ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "One match found.", state ) ); + else + from.SendGump( new AdminGump( from, AdminGumpPage.Clients, 0, results, "One match found.", null ) ); + } + else + { + from.SendGump( new AdminGump( from, AdminGumpPage.Clients, 0, results, notice == null ? (results.Count == 0 ? "Nothing matched your search terms." : null) : notice, null ) ); + } + + break; + } + default: + { + index -= 2; + + if ( m_List != null && index >= 0 && index < m_List.Count ) + { + NetState ns = m_List[index] as NetState; + + if ( ns == null ) + break; + + Mobile m = ns.Mobile; + Account a = ns.Account as Account; + + if ( m != null ) + from.SendGump( new AdminGump( from, AdminGumpPage.ClientInfo, 0, null, null, m ) ); + else if ( a != null ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, null, a ) ); + } + + break; + } + } + + break; + } + case 5: + { + switch ( index ) + { + case 0: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, null, m_State ) ); break; + case 1: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Characters, 0, null, null, m_State ) ); break; + case 2: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Comments, 0, null, null, m_State ) ); break; + case 3: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Tags, 0, null, null, m_State ) ); break; + case 13: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access, 0, null, null, m_State ) ); break; + case 14: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, null, m_State ) ); break; + case 15: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_Restrictions, 0, null, null, m_State ) ); break; + case 4: from.Prompt = new AddCommentPrompt( m_State as Account ); from.SendMessage( "Enter the new account comment." ); break; + case 5: from.Prompt = new AddTagNamePrompt( m_State as Account ); from.SendMessage( "Enter the new tag name." ); break; + case 6: + { + TextRelay unEntry = info.GetTextEntry( 0 ); + TextRelay pwEntry = info.GetTextEntry( 1 ); + + string un = ( unEntry == null ? null : unEntry.Text.Trim() ); + string pw = ( pwEntry == null ? null : pwEntry.Text.Trim() ); + + Account dispAccount = null; + string notice; + + if ( un == null || un.Length == 0 ) + { + notice = "You must enter a username to add an account."; + } + else if ( pw == null || pw.Length == 0 ) + { + notice = "You must enter a password to add an account."; + } + else + { + IAccount account = Accounts.GetAccount( un ); + + if ( account != null ) + { + notice = "There is already an account with that username."; + } + else + { + dispAccount = new Account( un, pw ); + notice = String.Format( "{0} : Account added.", un ); + CommandLogging.WriteLine( from, "{0} {1} adding new account: {2}", from.AccessLevel, CommandLogging.Format( from ), un ); + } + } + + from.SendGump( new AdminGump( from, dispAccount != null ? AdminGumpPage.AccountDetails_Information : m_PageType, m_ListPage, m_List, notice, dispAccount != null ? dispAccount : m_State ) ); + break; + } + case 7: + { + ArrayList results = new ArrayList(); + + TextRelay matchEntry = info.GetTextEntry( 0 ); + string match = ( matchEntry == null ? null : matchEntry.Text.Trim().ToLower() ); + string notice = null; + + if ( match == null || match.Length == 0 ) + { + notice = "You must enter a username to search."; + } + else + { + foreach ( Account check in Accounts.GetAccounts() ) + { + if ( check.Username.ToLower().IndexOf( match ) >= 0 ) + results.Add( check ); + } + + results.Sort( AccountComparer.Instance ); + } + + if ( results.Count == 1 ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "One match found.", results[0] ) ); + else + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, results, notice == null ? (results.Count == 0 ? "Nothing matched your search terms." : null) : notice, new ArrayList() ) ); + + break; + } + case 8: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_ChangePassword, 0, null, null, m_State ) ); break; + case 9: from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_ChangeAccess, 0, null, null, m_State ) ); break; + case 10: case 11: + { + Account a = m_State as Account; + + if ( a == null ) + break; + + a.SetUnspecifiedBan( from ); + a.Banned = ( index == 10 ); + CommandLogging.WriteLine( from, "{0} {1} {3} account {2}", from.AccessLevel, CommandLogging.Format( from ), a.Username, a.Banned ? "banning" : "unbanning" ); + from.SendGump( new AdminGump( from, m_PageType, m_ListPage, m_List, String.Format( "The account has been {0}.", a.Banned ? "banned" : "unbanned" ), m_State ) ); + + if ( index == 10 ) + from.SendGump( new BanDurationGump( a ) ); + + break; + } + case 12: + { + Account a = m_State as Account; + + if ( a == null ) + break; + + TextRelay passwordEntry = info.GetTextEntry( 0 ); + TextRelay confirmEntry = info.GetTextEntry( 1 ); + + string password = ( passwordEntry == null ? null : passwordEntry.Text.Trim() ); + string confirm = ( confirmEntry == null ? null : confirmEntry.Text.Trim() ); + + string notice; + AdminGumpPage page = AdminGumpPage.AccountDetails_ChangePassword; + + if ( password == null || password.Length == 0 ) + { + notice = "You must enter the password."; + } + else if ( confirm != password ) + { + notice = "You must confirm the password. That field must precisely match the password field."; + } + else + { + notice = "The password has been changed."; + a.SetPassword( password ); + page = AdminGumpPage.AccountDetails_Information; + CommandLogging.WriteLine( from, "{0} {1} changing password of account {2}", from.AccessLevel, CommandLogging.Format( from ), a.Username ); + } + + from.SendGump( new AdminGump( from, page, 0, null, notice, m_State ) ); + + break; + } + case 16: // view shared + { + Account a = m_State as Account; + + if ( a == null ) + break; + + ArrayList list = GetSharedAccounts( a.LoginIPs ); + + if ( list.Count > 1 || (list.Count == 1 && !list.Contains( a )) ) + { + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, list, null, new ArrayList() ) ); + } + else if ( a.LoginIPs.Length > 0 ) + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, "There are no other accounts which share an address with this one.", m_State ) ); + } + else + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, "This account has not yet been accessed.", m_State ) ); + } + + break; + } + case 17: // ban shared + { + Account a = m_State as Account; + + if ( a == null ) + break; + + ArrayList list = GetSharedAccounts( a.LoginIPs ); + + if ( list.Count > 0 ) + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat( "You are about to ban {0} account{1}. Do you wish to continue?", list.Count, list.Count != 1 ? "s" : "" ); + + for ( int i = 0; i < list.Count; ++i ) + sb.AppendFormat( "
- {0}", ((Account)list[i]).Username ); + + from.SendGump( new WarningGump( 1060635, 30720, sb.ToString(), 0xFFC000, 420, 400, new WarningGumpCallback( BanShared_Callback ), a ) ); + } + else if ( a.LoginIPs.Length > 0 ) + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, "There are no accounts which share an address with this one.", m_State ) ); + } + else + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, "This account has not yet been accessed.", m_State ) ); + } + + break; + } + case 18: // firewall all + { + Account a = m_State as Account; + + if ( a == null ) + break; + + if ( a.LoginIPs.Length > 0 ) + { + from.SendGump( new WarningGump( 1060635, 30720, String.Format( "You are about to firewall {0} address{1}. Do you wish to continue?", a.LoginIPs.Length, a.LoginIPs.Length != 1 ? "s" : "" ), 0xFFC000, 420, 400, new WarningGumpCallback( FirewallShared_Callback ), a ) ); + } + else + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, "This account has not yet been accessed.", m_State ) ); + } + + break; + } + case 19: // add + { + Account a = m_State as Account; + + if ( a == null ) + break; + + TextRelay entry = info.GetTextEntry( 0 ); + string ip = ( entry == null ? null : entry.Text.Trim() ); + + string notice; + + if ( ip == null || ip.Length == 0 ) + { + notice = "You must enter an address to add."; + } + else + { + string[] list = a.IPRestrictions; + + bool contains = false; + for ( int i = 0; !contains && i < list.Length; ++i ) + contains = ( list[i] == ip ); + + if ( contains ) + { + notice = "That address is already contained in the list."; + } + else + { + string[] newList = new string[list.Length + 1]; + + for ( int i = 0; i < list.Length; ++i ) + newList[i] = list[i]; + + newList[list.Length] = ip; + + a.IPRestrictions = newList; + + notice = String.Format( "{0} : Added to restriction list.", ip ); + } + } + + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_Restrictions, 0, null, notice, m_State ) ); + + break; + } + case 20: + case 21: + case 22: + case 23: + case 24: + { + Account a = m_State as Account; + + if ( a == null ) + break; + + AccessLevel newLevel; + + switch ( index ) + { + default: + case 20: newLevel = AccessLevel.Player; break; + case 21: newLevel = AccessLevel.Counselor; break; + case 22: newLevel = AccessLevel.GameMaster; break; + case 23: newLevel = AccessLevel.Seer; break; + case 24: newLevel = AccessLevel.Administrator; break; + case 33: newLevel = AccessLevel.Developer; break; + case 34: newLevel = AccessLevel.Owner; break; + } + + if ( newLevel < from.AccessLevel ) + { + a.AccessLevel = newLevel; + + CommandLogging.WriteLine( from, "{0} {1} changing access level of account {2} to {3}", from.AccessLevel, CommandLogging.Format( from ), a.Username, a.AccessLevel ); + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "The access level has been changed.", m_State ) ); + } + + break; + } + case 25: + { + Account a = m_State as Account; + + if ( a == null ) + break; + + from.SendGump( new WarningGump( 1060635, 30720, String.Format( "
Account of {0}

You are about to permanently delete the account. Likewise, all characters on the account will be deleted, including equiped, inventory, and banked items. Any houses tied to the account will be demolished.

Do you wish to continue?", a.Username ), 0xFFC000, 420, 280, new WarningGumpCallback( AccountDelete_Callback ), m_State ) ); + break; + } + case 26: // View all shared accounts + { + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts_Shared, 0, null, null, null ) ); + break; + } + case 27: // Ban marked + { + ArrayList list = m_List; + ArrayList rads = m_State as ArrayList; + + if ( list == null || rads == null ) + break; + + if ( rads.Count > 0 ) + from.SendGump( new WarningGump( 1060635, 30720, String.Format( "You are about to ban {0} marked account{1}. Be cautioned, the only way to reverse this is by hand--manually unbanning each account.

Do you wish to continue?", rads.Count, rads.Count == 1 ? "" : "s" ), 0xFFC000, 420, 280, new WarningGumpCallback( Marked_Callback ), new object[]{ true, list, rads, m_ListPage } ) ); + else + from.SendGump( new NoticeGump( 1060637, 30720, "You have not yet marked any accounts. Place a check mark next to the accounts you wish to ban and then try again.", 0xFFC000, 420, 280, new NoticeGumpCallback( ResendGump_Callback ), new object[]{ list, rads, m_ListPage } ) ); + + break; + } + case 28: // Delete marked + { + ArrayList list = m_List; + ArrayList rads = m_State as ArrayList; + + if ( list == null || rads == null ) + break; + + if ( rads.Count > 0 ) + from.SendGump( new WarningGump( 1060635, 30720, String.Format( "You are about to permanently delete {0} marked account{1}. Likewise, all characters on the account{1} will be deleted, including equiped, inventory, and banked items. Any houses tied to the account{1} will be demolished.

Do you wish to continue?", rads.Count, rads.Count == 1 ? "" : "s" ), 0xFFC000, 420, 280, new WarningGumpCallback( Marked_Callback ), new object[]{ false, list, rads, m_ListPage } ) ); + else + from.SendGump( new NoticeGump( 1060637, 30720, "You have not yet marked any accounts. Place a check mark next to the accounts you wish to ban and then try again.", 0xFFC000, 420, 280, new NoticeGumpCallback( ResendGump_Callback ), new object[]{ list, rads, m_ListPage } ) ); + + break; + } + case 29: // Mark all + { + ArrayList list = m_List; + ArrayList rads = m_State as ArrayList; + + if ( list == null || rads == null ) + break; + + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, m_ListPage, m_List, null, new ArrayList( list ) ) ); + + break; + } + case 30: // View all empty accounts + { + ArrayList results = new ArrayList(); + + foreach ( Account acct in Accounts.GetAccounts() ) + { + bool empty = true; + + for ( int i = 0; empty && i < acct.Length; ++i ) + empty = ( acct[i] == null ); + + if ( empty ) + results.Add( acct ); + } + + if ( results.Count == 1 ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "One match found.", results[0] ) ); + else + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, results, (results.Count == 0 ? "Nothing matched your search terms." : null), new ArrayList() ) ); + + break; + } + case 31: // View all inactive accounts + { + ArrayList results = new ArrayList(); + + DateTime minTime = DateTime.Now - TimeSpan.FromDays( 90.0 ); + + foreach ( Account acct in Accounts.GetAccounts() ) + { + if ( acct.LastLogin <= minTime ) + results.Add( acct ); + } + + if ( results.Count == 1 ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "One match found.", results[0] ) ); + else + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, results, (results.Count == 0 ? "Nothing matched your search terms." : null), new ArrayList() ) ); + + break; + } + case 32: // View all banned accounts + { + ArrayList results = new ArrayList(); + + foreach ( Account acct in Accounts.GetAccounts() ) + { + if ( acct.Banned ) + results.Add( acct ); + } + + if ( results.Count == 1 ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, "One match found.", results[0] ) ); + else + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, results, (results.Count == 0 ? "Nothing matched your search terms." : null), new ArrayList() ) ); + + break; + } + default: + { + index -= 50; + + Account a = m_State as Account; + + if ( a != null && index >= 0 && index < a.Length ) + { + Mobile m = a[index]; + + if ( m != null ) + from.SendGump( new AdminGump( from, AdminGumpPage.ClientInfo, 0, null, null, m ) ); + } + else + { + index -= 6; + + if ( m_List != null && index >= 0 && index < m_List.Count ) + { + if ( m_List[index] is Account ) + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, null, m_List[index] ) ); + else if ( m_List[index] is DictionaryEntry ) + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, (ArrayList)(((DictionaryEntry)m_List[index]).Value), null, new ArrayList() ) ); + } + } + + break; + } + } + + break; + } + case 6: + { + switch ( index ) + { + case 0: + { + TextRelay matchEntry = info.GetTextEntry( 0 ); + string match = ( matchEntry == null ? null : matchEntry.Text.Trim() ); + + string notice = null; + ArrayList results = new ArrayList(); + + if ( match == null || match.Length == 0 ) + { + notice = "You must enter a username to search."; + } + else + { + for ( int i = 0; i < Firewall.List.Count; ++i ) + { + string check = Firewall.List[i].ToString(); + + if ( check.IndexOf( match ) >= 0 ) + results.Add( Firewall.List[i] ); + } + } + + if ( results.Count == 1 ) + from.SendGump( new AdminGump( from, AdminGumpPage.FirewallInfo, 0, null, "One match found.", results[0] ) ); + else if ( results.Count > 1 ) + from.SendGump( new AdminGump( from, AdminGumpPage.Firewall, 0, results, String.Format( "Search results for : {0}", match ), m_State ) ); + else + from.SendGump( new AdminGump( from, m_PageType, m_ListPage, m_List, notice == null ? "Nothing matched your search terms." : notice, m_State ) ); + + break; + } + case 1: + { + TextRelay relay = info.GetTextEntry( 0 ); + string text = ( relay == null ? null : relay.Text.Trim() ); + + if ( text == null || text.Length == 0 ) + { + from.SendGump( new AdminGump( from, m_PageType, m_ListPage, m_List, "You must enter an address or pattern to add.", m_State ) ); + } + else if ( !Utility.IsValidIP( text ) ) + { + from.SendGump( new AdminGump( from, m_PageType, m_ListPage, m_List, "That is not a valid address or pattern.", m_State ) ); + } + else + { + object toAdd; + + try{ toAdd = IPAddress.Parse( text ); } + catch{ toAdd = text; } + + CommandLogging.WriteLine( from, "{0} {1} firewalling {2}", from.AccessLevel, CommandLogging.Format( from ), toAdd ); + + Firewall.Add( toAdd ); + from.SendGump( new AdminGump( from, AdminGumpPage.FirewallInfo, 0, null, String.Format( "{0} : Added to firewall.", toAdd ), toAdd ) ); + } + + break; + } + case 2: + { + InvokeCommand( "Firewall" ); + from.SendGump( new AdminGump( from, m_PageType, m_ListPage, m_List, "Target the player to firewall.", m_State ) ); + break; + } + case 3: + { + if ( m_State is IPAddress || m_State is String ) + { + CommandLogging.WriteLine( from, "{0} {1} removing {2} from firewall list", from.AccessLevel, CommandLogging.Format( from ), m_State ); + + Firewall.List.Remove( m_State ); + Firewall.Save(); + from.SendGump( new AdminGump( from, AdminGumpPage.Firewall, 0, null, String.Format( "{0} : Removed from firewall.", m_State ), null ) ); + } + + break; + } + default: + { + index -= 4; + + if ( m_List != null && index >= 0 && index < m_List.Count ) + from.SendGump( new AdminGump( from, AdminGumpPage.FirewallInfo, 0, null, null, m_List[index] ) ); + + break; + } + } + + break; + } + case 7: + { + Mobile m = m_State as Mobile; + + if ( m == null ) + break; + + string notice = null; + bool sendGump = true; + + switch ( index ) + { + case 0: + { + Map map = m.Map; + Point3D loc = m.Location; + + if ( map == null || map == Map.Internal ) + { + map = m.LogoutMap; + loc = m.LogoutLocation; + } + + if ( map != null && map != Map.Internal ) + { + from.MoveToWorld( loc, map ); + notice = "You have been teleported to their location."; + } + + break; + } + case 1: + { + m.MoveToWorld( from.Location, from.Map ); + notice = "They have been teleported to your location."; + break; + } + case 2: + { + NetState ns = m.NetState; + + if ( ns != null ) + { + CommandLogging.WriteLine( from, "{0} {1} {2} {3}", from.AccessLevel, CommandLogging.Format( from ), "kicking", CommandLogging.Format( m ) ); + ns.Dispose(); + notice = "They have been kicked."; + } + else + { + notice = "They are already disconnected."; + } + + break; + } + case 3: + { + Account a = m.Account as Account; + + if ( a != null ) + { + CommandLogging.WriteLine( from, "{0} {1} {2} {3}", from.AccessLevel, CommandLogging.Format( from ), "banning", CommandLogging.Format( m ) ); + a.Banned = true; + + NetState ns = m.NetState; + + if ( ns != null ) + ns.Dispose(); + + notice = "They have been banned."; + } + + break; + } + case 6: + { + Properties.SetValue( from, m, "Blessed", "False" ); + notice = "They are now mortal."; + break; + } + case 7: + { + Properties.SetValue( from, m, "Blessed", "True" ); + notice = "They are now immortal."; + break; + } + case 8: + { + Properties.SetValue( from, m, "Squelched", "True" ); + notice = "They are now squelched."; + break; + } + case 9: + { + Properties.SetValue( from, m, "Squelched", "False" ); + notice = "They are now unsquelched."; + break; + } + case 10: + { + Properties.SetValue( from, m, "Hidden", "True" ); + notice = "They are now hidden."; + break; + } + case 11: + { + Properties.SetValue( from, m, "Hidden", "False" ); + notice = "They are now unhidden."; + break; + } + case 12: + { + CommandLogging.WriteLine( from, "{0} {1} killing {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( m ) ); + m.Kill(); + notice = "They have been killed."; + break; + } + case 13: + { + CommandLogging.WriteLine( from, "{0} {1} resurrecting {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( m ) ); + m.Resurrect(); + notice = "They have been resurrected."; + break; + } + case 14: + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Information, 0, null, null, m.Account ) ); + sendGump = false; + break; + } + } + + if ( sendGump ) + from.SendGump( new AdminGump( from, AdminGumpPage.ClientInfo, 0, null, notice, m_State ) ); + + switch ( index ) + { + case 3: + { + Account a = m.Account as Account; + + if ( a != null ) + from.SendGump( new BanDurationGump( a ) ); + + break; + } + case 4: + { + from.SendGump( new PropertiesGump( from, m ) ); + break; + } + case 5: + { + from.SendGump( new SkillsGump( from, m ) ); + break; + } + } + + break; + } + case 8: + { + if ( m_List != null && index >= 0 && index < m_List.Count ) + { + Account a = m_State as Account; + + if ( a == null ) + break; + + if ( m_PageType == AdminGumpPage.AccountDetails_Access_ClientIPs ) + { + from.SendGump( new WarningGump( 1060635, 30720, String.Format( "You are about to firewall {0}. All connection attempts from a matching IP will be refused. Are you sure?", m_List[index] ), 0xFFC000, 420, 280, new WarningGumpCallback( Firewall_Callback ), new object[]{ a, m_List[index] } ) ); + } + else if ( m_PageType == AdminGumpPage.AccountDetails_Access_Restrictions ) + { + ArrayList list = new ArrayList( a.IPRestrictions ); + + list.Remove( m_List[index] ); + + a.IPRestrictions = (string[])list.ToArray( typeof( string ) ); + + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_Restrictions, 0, null, String.Format( "{0} : Removed from list.", m_List[index] ), a ) ); + } + } + + break; + } + case 9: + { + if ( m_List != null && index >= 0 && index < m_List.Count ) + { + if ( m_PageType == AdminGumpPage.AccountDetails_Access_ClientIPs ) + { + object obj = m_List[index]; + + if ( !(obj is IPAddress) ) + break; + + Account a = m_State as Account; + + if ( a == null ) + break; + + ArrayList list = GetSharedAccounts( (IPAddress)obj ); + + if ( list.Count > 1 || (list.Count == 1 && !list.Contains( a )) ) + from.SendGump( new AdminGump( from, AdminGumpPage.Accounts, 0, list, null, new ArrayList() ) ); + else + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Access_ClientIPs, 0, null, "There are no other accounts which share that address.", m_State ) ); + } + } + + break; + } + } + } + + private void Shutdown( bool restart, bool save ) + { + CommandLogging.WriteLine( m_From, "{0} {1} shuting down server (Restart: {2}) (Save: {3})", m_From.AccessLevel, CommandLogging.Format( m_From ), restart, save ); + + if ( save ) + InvokeCommand( "Save" ); + + if ( restart ) + Process.Start( Core.ExePath ); + + Core.Process.Kill(); + } + + private void InvokeCommand( string ip ) + { + CommandSystem.Handle( m_From, String.Format( "{0}{1}", CommandSystem.Prefix, ip ) ); + } + + public static void GetAccountInfo( Account a, out AccessLevel accessLevel, out bool online ) + { + accessLevel = a.AccessLevel; + online = false; + + for ( int j = 0; j < a.Length; ++j ) + { + Mobile check = a[j]; + + if ( check == null ) + continue; + + if ( check.AccessLevel > accessLevel ) + accessLevel = check.AccessLevel; + + if ( check.NetState != null ) + online = true; + } + } + + private class AddCommentPrompt : Prompt + { + private Account m_Account; + + public AddCommentPrompt( Account acct ) + { + m_Account = acct; + } + + public override void OnCancel( Mobile from ) + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Comments, 0, null, "Request to add comment was canceled.", m_Account ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Account != null ) + { + m_Account.Comments.Add( new AccountComment( from.Name, text ) ); + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Comments, 0, null, "Comment added.", m_Account ) ); + } + } + } + + private class AddTagNamePrompt : Prompt + { + private Account m_Account; + + public AddTagNamePrompt( Account acct ) + { + m_Account = acct; + } + + public override void OnCancel( Mobile from ) + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Tags, 0, null, "Request to add tag was canceled.", m_Account ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + from.Prompt = new AddTagValuePrompt( m_Account, text ); + from.SendMessage( "Enter the new tag value." ); + } + } + + private class AddTagValuePrompt : Prompt + { + private Account m_Account; + private string m_Name; + + public AddTagValuePrompt( Account acct, string name ) + { + m_Account = acct; + m_Name = name; + } + + public override void OnCancel( Mobile from ) + { + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Tags, 0, null, "Request to add tag was canceled.", m_Account ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Account != null ) + { + m_Account.AddTag( m_Name, text ); + from.SendGump( new AdminGump( from, AdminGumpPage.AccountDetails_Tags, 0, null, "Tag added.", m_Account ) ); + } + } + } + + private class NetStateComparer : IComparer + { + public static readonly IComparer Instance = new NetStateComparer(); + + public NetStateComparer() + { + } + + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + NetState a = x as NetState; + NetState b = y as NetState; + + if ( a == null || b == null ) + throw new ArgumentException(); + + Mobile aMob = a.Mobile; + Mobile bMob = b.Mobile; + + if ( aMob == null && bMob == null ) + return 0; + else if ( aMob == null ) + return 1; + else if ( bMob == null ) + return -1; + + if ( aMob.AccessLevel > bMob.AccessLevel ) + return -1; + else if ( aMob.AccessLevel < bMob.AccessLevel ) + return 1; + else + return Insensitive.Compare( aMob.Name, bMob.Name ); + } + } + + private class AccountComparer : IComparer + { + public static readonly IComparer Instance = new AccountComparer(); + + public AccountComparer() + { + } + + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + Account a = x as Account; + Account b = y as Account; + + if ( a == null || b == null ) + throw new ArgumentException(); + + AccessLevel aLevel, bLevel; + bool aOnline, bOnline; + + GetAccountInfo( a, out aLevel, out aOnline ); + GetAccountInfo( b, out bLevel, out bOnline ); + + if ( aOnline && !bOnline ) + return -1; + else if ( bOnline && !aOnline ) + return 1; + else if ( aLevel > bLevel ) + return -1; + else if ( aLevel < bLevel ) + return 1; + else + return Insensitive.Compare( a.Username, b.Username ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/BanDurationGump.cs b/Scripts/Gumps/BanDurationGump.cs new file mode 100644 index 000000000..10d83b870 --- /dev/null +++ b/Scripts/Gumps/BanDurationGump.cs @@ -0,0 +1,244 @@ +using System; +using System.Net; +using System.Text; +using System.Collections; +using System.Diagnostics; +using Server; +using Server.Items; +using Server.Prompts; +using Server.Network; +using Server.Accounting; +using Server.Commands; + +namespace Server.Gumps +{ + public class BanDurationGump : Gump + { + private ArrayList m_List; + + public void AddButtonLabeled( int x, int y, int buttonID, string text ) + { + AddButton( x, y - 1, 4005, 4007, buttonID, GumpButtonType.Reply, 0 ); + AddHtml( x + 35, y, 240, 20, text, false, false ); + } + + public void AddTextField( int x, int y, int width, int height, int index ) + { + AddBackground( x - 2, y - 2, width + 4, height + 4, 0x2486 ); + AddTextEntry( x + 2, y + 2, width - 4, height - 4, 0, index, "" ); + } + + public static ArrayList MakeList( object obj ) + { + ArrayList list = new ArrayList( 1 ); + list.Add( obj ); + return list; + } + + public BanDurationGump( Account a ) : this( MakeList( a ) ) + { + } + + public BanDurationGump( ArrayList list ) : base( (640 - 170) / 2, (480 - 305) / 2 ) + { + m_List = list; + + int width = 170; + int height = 305; + + AddPage( 0 ); + + AddBackground( 0, 0, width, height, 5054 ); + + //AddImageTiled( 10, 10, width - 20, 20, 2624 ); + //AddAlphaRegion( 10, 10, width - 20, 20 ); + AddHtml( 10, 10, width - 20, 20, "
Ban Duration
", false, false ); + + //AddImageTiled( 10, 40, width - 20, height - 50, 2624 ); + //AddAlphaRegion( 10, 40, width - 20, height - 50 ); + + AddButtonLabeled( 15, 45, 1, "Infinite" ); + AddButtonLabeled( 15, 65, 2, "From D:H:M:S" ); + + AddInput( 3, 0, "Days" ); + AddInput( 4, 1, "Hours" ); + AddInput( 5, 2, "Minutes" ); + AddInput( 6, 3, "Seconds" ); + } + + public void AddInput( int bid, int idx, string name ) + { + int x = 15; + int y = 95 + (idx * 50); + + AddButtonLabeled( x, y, bid, name ); + AddTextField( x + 35, y + 20, 100, 20, idx ); + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( from.AccessLevel < AccessLevel.Administrator ) + return; + + TextRelay d = info.GetTextEntry( 0 ); + TextRelay h = info.GetTextEntry( 1 ); + TextRelay m = info.GetTextEntry( 2 ); + TextRelay s = info.GetTextEntry( 3 ); + + TimeSpan duration; + bool shouldSet; + + string fromString = from.ToString(); + + switch ( info.ButtonID ) + { + case 0: + { + for ( int i = 0; i < m_List.Count; ++i ) + { + Account a = (Account)m_List[i]; + + a.SetUnspecifiedBan( from ); + } + + from.SendMessage( "Duration unspecified." ); + return; + } + case 1: // infinite + { + duration = TimeSpan.MaxValue; + shouldSet = true; + break; + } + case 2: // From D:H:M:S + { + if ( d != null && h != null && m != null && s != null ) + { + try + { + duration = new TimeSpan( Utility.ToInt32( d.Text ), Utility.ToInt32( h.Text ), Utility.ToInt32( m.Text ), Utility.ToInt32( s.Text ) ); + shouldSet = true; + + break; + } + catch + { + } + } + + duration = TimeSpan.Zero; + shouldSet = false; + + break; + } + case 3: // From D + { + if ( d != null ) + { + try + { + duration = TimeSpan.FromDays( Utility.ToDouble( d.Text ) ); + shouldSet = true; + + break; + } + catch + { + } + } + + duration = TimeSpan.Zero; + shouldSet = false; + + break; + } + case 4: // From H + { + if ( h != null ) + { + try + { + duration = TimeSpan.FromHours( Utility.ToDouble( h.Text ) ); + shouldSet = true; + + break; + } + catch + { + } + } + + duration = TimeSpan.Zero; + shouldSet = false; + + break; + } + case 5: // From M + { + if ( m != null ) + { + try + { + duration = TimeSpan.FromMinutes( Utility.ToDouble( m.Text ) ); + shouldSet = true; + + break; + } + catch + { + } + } + + duration = TimeSpan.Zero; + shouldSet = false; + + break; + } + case 6: // From S + { + if ( s != null ) + { + try + { + duration = TimeSpan.FromSeconds( Utility.ToDouble( s.Text ) ); + shouldSet = true; + + break; + } + catch + { + } + } + + duration = TimeSpan.Zero; + shouldSet = false; + + break; + } + default: return; + } + + if ( shouldSet ) + { + for ( int i = 0; i < m_List.Count; ++i ) + { + Account a = (Account)m_List[i]; + + a.SetBanTags( from, DateTime.Now, duration ); + } + + if ( duration == TimeSpan.MaxValue ) + from.SendMessage( "Duration is infinite." ); + else + from.SendMessage( "Duration is {0}.", duration ); + } + else + { + from.SendMessage( "Values improperly formatted." ); + from.SendGump( new BanDurationGump( m_List ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/BaseGridGump.cs b/Scripts/Gumps/BaseGridGump.cs new file mode 100644 index 000000000..102f9d8f7 --- /dev/null +++ b/Scripts/Gumps/BaseGridGump.cs @@ -0,0 +1,197 @@ +using System; + +namespace Server.Gumps +{ + public abstract class BaseGridGump : Gump + { + private int m_CurrentX, m_CurrentY; + private int m_CurrentPage; + + protected GumpBackground m_Background; + protected GumpImageTiled m_Offset; + + public int CurrentPage + { + get{ return m_CurrentPage; } + } + + public int CurrentX + { + get{ return m_CurrentX; } + } + + public int CurrentY + { + get{ return m_CurrentY; } + } + + public BaseGridGump( int x, int y ) : base( x, y ) + { + } + + public virtual int BorderSize{ get{ return 10; } } + public virtual int OffsetSize{ get{ return 1; } } + + public virtual int EntryHeight{ get{ return 20; } } + + public virtual int OffsetGumpID{ get{ return 0x0A40; } } + public virtual int HeaderGumpID{ get{ return 0x0E14; } } + public virtual int EntryGumpID{ get{ return 0x0BBC; } } + public virtual int BackGumpID{ get{ return 0x13BE; } } + + public virtual int TextHue{ get{ return 0; } } + public virtual int TextOffsetX{ get{ return 2; } } + + public const int ArrowLeftID1 = 0x15E3; + public const int ArrowLeftID2 = 0x15E7; + public const int ArrowLeftWidth = 16; + public const int ArrowLeftHeight = 16; + + public const int ArrowRightID1 = 0x15E1; + public const int ArrowRightID2 = 0x15E5; + public const int ArrowRightWidth = 16; + public const int ArrowRightHeight = 16; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public int GetButtonID( int typeCount, int type, int index ) + { + return 1 + (index * typeCount) + type; + } + + public bool SplitButtonID( int buttonID, int typeCount, out int type, out int index ) + { + if ( buttonID < 1 ) + { + type = 0; + index = 0; + return false; + } + + buttonID -= 1; + + type = buttonID % typeCount; + index = buttonID / typeCount; + + return true; + } + + public void FinishPage() + { + if ( m_Background != null ) + m_Background.Height = m_CurrentY + EntryHeight + OffsetSize + BorderSize; + + if ( m_Offset != null ) + m_Offset.Height = m_CurrentY + EntryHeight + OffsetSize - BorderSize; + } + + public void AddNewPage() + { + FinishPage(); + + m_CurrentX = BorderSize + OffsetSize; + m_CurrentY = BorderSize + OffsetSize; + + AddPage( ++m_CurrentPage ); + + m_Background = new GumpBackground( 0, 0, 100, 100, BackGumpID ); + Add( m_Background ); + + m_Offset = new GumpImageTiled( BorderSize, BorderSize, 100, 100, OffsetGumpID ); + Add( m_Offset ); + } + + public void AddNewLine() + { + m_CurrentY += EntryHeight + OffsetSize; + m_CurrentX = BorderSize + OffsetSize; + } + + public void IncreaseX( int width ) + { + m_CurrentX += width + OffsetSize; + + width = m_CurrentX + BorderSize; + + if ( m_Background != null && width > m_Background.Width ) + m_Background.Width = width; + + width = m_CurrentX - BorderSize; + + if ( m_Offset != null && width > m_Offset.Width ) + m_Offset.Width = width; + } + + public void AddEntryLabel( int width, string text ) + { + AddImageTiled( m_CurrentX, m_CurrentY, width, EntryHeight, EntryGumpID ); + AddLabelCropped( m_CurrentX + TextOffsetX, m_CurrentY, width - TextOffsetX, EntryHeight, TextHue, text ); + + IncreaseX( width ); + } + + public void AddEntryHtml( int width, string text ) + { + AddImageTiled( m_CurrentX, m_CurrentY, width, EntryHeight, EntryGumpID ); + AddHtml( m_CurrentX + TextOffsetX, m_CurrentY, width - TextOffsetX, EntryHeight, text, false, false ); + + IncreaseX( width ); + } + + public void AddEntryHeader( int width ) + { + AddEntryHeader( width, 1 ); + } + + public void AddEntryHeader( int width, int spannedEntries ) + { + AddImageTiled( m_CurrentX, m_CurrentY, width, (EntryHeight * spannedEntries) + (OffsetSize * (spannedEntries - 1)), HeaderGumpID ); + IncreaseX( width ); + } + + public void AddBlankLine() + { + if ( m_Offset != null ) + AddImageTiled( m_Offset.X, m_CurrentY, m_Offset.Width, EntryHeight, BackGumpID + 4 ); + + AddNewLine(); + } + + public void AddEntryButton( int width, int normalID, int pressedID, int buttonID, int buttonWidth, int buttonHeight ) + { + AddEntryButton( width, normalID, pressedID, buttonID, buttonWidth, buttonHeight, 1 ); + } + + public void AddEntryButton( int width, int normalID, int pressedID, int buttonID, int buttonWidth, int buttonHeight, int spannedEntries ) + { + AddImageTiled( m_CurrentX, m_CurrentY, width, (EntryHeight * spannedEntries) + (OffsetSize * (spannedEntries - 1)), HeaderGumpID ); + AddButton( m_CurrentX + ((width - buttonWidth) / 2), m_CurrentY + (((EntryHeight * spannedEntries) + (OffsetSize * (spannedEntries - 1)) - buttonHeight) / 2), normalID, pressedID, buttonID, GumpButtonType.Reply, 0 ); + + IncreaseX( width ); + } + + public void AddEntryPageButton( int width, int normalID, int pressedID, int page, int buttonWidth, int buttonHeight ) + { + AddImageTiled( m_CurrentX, m_CurrentY, width, EntryHeight, HeaderGumpID ); + AddButton( m_CurrentX + ((width - buttonWidth) / 2), m_CurrentY + ((EntryHeight - buttonHeight) / 2), normalID, pressedID, 0, GumpButtonType.Page, page ); + + IncreaseX( width ); + } + + public void AddEntryText( int width, int entryID, string initialText ) + { + AddImageTiled( m_CurrentX, m_CurrentY, width, EntryHeight, EntryGumpID ); + AddTextEntry( m_CurrentX + TextOffsetX, m_CurrentY, width - TextOffsetX, EntryHeight, TextHue, entryID, initialText ); + + IncreaseX( width ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/BaseImageTileButtonsGump.cs b/Scripts/Gumps/BaseImageTileButtonsGump.cs new file mode 100644 index 000000000..7c3fa0b88 --- /dev/null +++ b/Scripts/Gumps/BaseImageTileButtonsGump.cs @@ -0,0 +1,131 @@ +using System; +using Server; +using Server.Network; +using System.Collections; + +namespace Server.Gumps +{ + public class ImageTileButtonInfo + { + private int m_ItemID; + private int m_Hue; + private int m_LocalizedTooltip; + + private TextDefinition m_Label; + + public virtual int ItemID + { + get{ return m_ItemID; } + set{ m_ItemID = value; } + } + public virtual int Hue + { + get{ return m_Hue; } + set{ m_Hue = value; } + } + public virtual int LocalizedTooltip + { + get{ return m_LocalizedTooltip; } + set{ m_LocalizedTooltip = value; } + } + + public virtual TextDefinition Label + { + get{ return m_Label; } + set{ m_Label = value; } + } + + public ImageTileButtonInfo( int itemID, int hue, TextDefinition label, int localizedTooltip ) + { + m_Hue = hue; + m_ItemID = itemID; + m_Label = label; + m_LocalizedTooltip = localizedTooltip; + } + public ImageTileButtonInfo( int itemID, int hue, TextDefinition label ) : this( itemID, hue, label, -1 ) + { + } + } + + public class BaseImageTileButtonsGump : Gump + { + private ImageTileButtonInfo[] m_Buttons; + + protected ImageTileButtonInfo[] Buttons { get { return m_Buttons; } } + + protected virtual int XItems{ get{ return 2; } } + protected virtual int YItems{ get { return 5; } } + + public BaseImageTileButtonsGump( TextDefinition header, ArrayList buttons ) : this( header, (ImageTileButtonInfo[])buttons.ToArray( typeof( ImageTileButtonInfo ) ) ) + { + } + public BaseImageTileButtonsGump( TextDefinition header, ImageTileButtonInfo[] buttons ) : base( 10, 10 ) //Coords are 0, o on OSI, intentional difference + { + m_Buttons = buttons; + AddPage( 0 ); + + int x = XItems * 250; + int y = YItems * 64; + + AddBackground( 0, 0, x+20, y+84, 0x13BE ); + AddImageTiled( 10, 10, x, 20, 0xA40 ); + AddImageTiled( 10, 40, x, y+4, 0xA40 ); + AddImageTiled( 10, y+54, x, 20, 0xA40 ); + AddAlphaRegion( 10, 10, x, y+64 ); + + AddButton( 10, y+54, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); //Cancel Button + AddHtmlLocalized( 45, y+56, x-50, 20, 1060051, 0x7FFF, false, false ); // CANCEL + TextDefinition.AddHtmlText( this, 14, 12, x, 20, header, false, false, 0x7FFF, 0xFFFFFF ); + + AddPage( 1 ); + + int itemsPerPage = XItems * YItems; + + for( int i = 0; i < buttons.Length; i++ ) + { + int position = i % itemsPerPage; + + int innerX = (position % XItems) * 250 + 14; + int innerY = (position / XItems) * 64 + 44; + + int pageNum = i / itemsPerPage + 1; + + if( position == 0 && i != 0 ) + { + AddButton( x-100, y+54, 0xFA5, 0xFA7, 0, GumpButtonType.Page, pageNum ); + AddHtmlLocalized( x-60, y+56, 60, 20, 1043353, 0x7FFF, false, false ); // Next + + AddPage( pageNum ); + + AddButton( x-200, y+54, 0xFAE, 0xFB0, 0, GumpButtonType.Page, pageNum - 1 ); + AddHtmlLocalized( x-160, y+56, 60, 20, 1011393, 0x7FFF, false, false ); // Back + + } + + ImageTileButtonInfo b = buttons[i]; + + AddImageTiledButton( innerX, innerY, 0x918, 0x919, 100 + i, GumpButtonType.Reply, 0, b.ItemID, b.Hue, 15, 10, b.LocalizedTooltip ); + TextDefinition.AddHtmlText( this, innerX + 84, innerY, 250, 60, b.Label, false, false, 0x7FFF, 0xFFFFFF ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int adjustedID = info.ButtonID - 100; + + if( adjustedID >= 0 && adjustedID < Buttons.Length ) + HandleButtonResponse( sender, adjustedID, Buttons[adjustedID] ); + else + HandleCancel( sender ); + + } + + public virtual void HandleButtonResponse( NetState sender, int adjustedButton, ImageTileButtonInfo buttonInfo ) + { + } + + public virtual void HandleCancel( NetState sender ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/CategorizedAddGump.cs b/Scripts/Gumps/CategorizedAddGump.cs new file mode 100644 index 000000000..a7884e491 --- /dev/null +++ b/Scripts/Gumps/CategorizedAddGump.cs @@ -0,0 +1,383 @@ +using System; +using System.IO; +using System.Xml; +using System.Collections; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public abstract class CAGNode + { + public abstract string Caption{ get; } + public abstract void OnClick( Mobile from, int page ); + } + + public class CAGObject : CAGNode + { + private Type m_Type; + private int m_ItemID; + private int m_Hue; + private CAGCategory m_Parent; + + public Type Type{ get{ return m_Type; } } + public int ItemID{ get{ return m_ItemID; } } + public int Hue{ get{ return m_Hue; } } + public CAGCategory Parent{ get{ return m_Parent; } } + + public override string Caption{ get{ return ( m_Type == null ? "bad type" : m_Type.Name ); } } + + public override void OnClick( Mobile from, int page ) + { + if ( m_Type == null ) + { + from.SendMessage( "That is an invalid type name." ); + } + else + { + CommandSystem.Handle( from, String.Format( "{0}Add {1}", CommandSystem.Prefix, m_Type.Name ) ); + + from.SendGump( new CategorizedAddGump( from, m_Parent, page ) ); + } + } + + public CAGObject( CAGCategory parent, XmlTextReader xml ) + { + m_Parent = parent; + + if ( xml.MoveToAttribute( "type" ) ) + m_Type = ScriptCompiler.FindTypeByFullName( xml.Value, false ); + + if ( xml.MoveToAttribute( "gfx" ) ) + m_ItemID = XmlConvert.ToInt32( xml.Value ); + + if ( xml.MoveToAttribute( "hue" ) ) + m_Hue = XmlConvert.ToInt32( xml.Value ); + } + } + + public class CAGCategory : CAGNode + { + private string m_Title; + private CAGNode[] m_Nodes; + private CAGCategory m_Parent; + + public string Title{ get{ return m_Title; } } + public CAGNode[] Nodes{ get{ return m_Nodes; } } + public CAGCategory Parent{ get{ return m_Parent; } } + + public override string Caption{ get{ return m_Title; } } + + public override void OnClick( Mobile from, int page ) + { + from.SendGump( new CategorizedAddGump( from, this, 0 ) ); + } + + private CAGCategory() + { + m_Title = "no data"; + m_Nodes = new CAGNode[0]; + } + + public CAGCategory( CAGCategory parent, XmlTextReader xml ) + { + m_Parent = parent; + + if ( xml.MoveToAttribute( "title" ) ) + m_Title = xml.Value; + else + m_Title = "empty"; + + if ( m_Title == "Docked" ) + m_Title = "Docked 2"; + + if ( xml.IsEmptyElement ) + { + m_Nodes = new CAGNode[0]; + } + else + { + ArrayList nodes = new ArrayList(); + + while ( xml.Read() && xml.NodeType != XmlNodeType.EndElement ) + { + if ( xml.NodeType == XmlNodeType.Element && xml.Name == "object" ) + nodes.Add( new CAGObject( this, xml ) ); + else if ( xml.NodeType == XmlNodeType.Element && xml.Name == "category" ) + nodes.Add( new CAGCategory( this, xml ) ); + else + xml.Skip(); + } + + m_Nodes = (CAGNode[])nodes.ToArray( typeof( CAGNode ) ); + } + } + + private static CAGCategory m_Root; + + public static CAGCategory Root + { + get + { + if ( m_Root == null ) + m_Root = Load( "Data/objects.xml" ); + + return m_Root; + } + } + + public static CAGCategory Load( string path ) + { + if ( File.Exists( path ) ) + { + XmlTextReader xml = new XmlTextReader( path ); + + xml.WhitespaceHandling = WhitespaceHandling.None; + + while ( xml.Read() ) + { + if ( xml.Name == "category" && xml.NodeType == XmlNodeType.Element ) + { + CAGCategory cat = new CAGCategory( null, xml ); + + xml.Close(); + + return cat; + } + } + } + + return new CAGCategory(); + } + } + + public class CategorizedAddGump : Gump + { + public static bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY + (((EntryHeight - 20) / 2) / 2); + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY + (((EntryHeight - 20) / 2) / 2); + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY + (((EntryHeight - 20) / 2) / 2); + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = 24;//PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static bool PrevLabel = false, NextLabel = false; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + private static readonly int EntryWidth = 180; + private static readonly int EntryCount = 15; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (EntryCount + 1)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + private Mobile m_Owner; + private CAGCategory m_Category; + private int m_Page; + + public CategorizedAddGump( Mobile owner ) : this( owner, CAGCategory.Root, 0 ) + { + } + + public CategorizedAddGump( Mobile owner, CAGCategory category, int page ) : base( GumpOffsetX, GumpOffsetY ) + { + owner.CloseGump( typeof( WhoGump ) ); + + m_Owner = owner; + m_Category = category; + + Initialize( page ); + } + + public void Initialize( int page ) + { + m_Page = page; + + CAGNode[] nodes = m_Category.Nodes; + + int count = nodes.Length - (page * EntryCount); + + if ( count < 0 ) + count = 0; + else if ( count > EntryCount ) + count = EntryCount; + + int totalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (count + 1)); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( m_Category.Parent != null ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1, GumpButtonType.Reply, 0 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + int emptyWidth = TotalWidth - (PrevWidth * 2) - NextWidth - (OffsetSize * 5) - (OldStyle ? SetWidth + OffsetSize : 0); + + if ( !OldStyle ) + AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, EntryGumpID ); + + AddHtml( x + TextOffsetX, y + ((EntryHeight - 20) / 2), emptyWidth - TextOffsetX, EntryHeight, String.Format( "
{0}
", m_Category.Caption ), false, false ); + + x += emptyWidth + OffsetSize; + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( page > 0 ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 2, GumpButtonType.Reply, 0 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x, y, NextWidth, EntryHeight, HeaderGumpID ); + + if ( (page + 1) * EntryCount < nodes.Length ) + { + AddButton( x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 3, GumpButtonType.Reply, 1 ); + + if ( NextLabel ) + AddLabel( x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next" ); + } + + for ( int i = 0, index = page * EntryCount; i < EntryCount && index < nodes.Length; ++i, ++index ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + CAGNode node = nodes[index]; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y + ((EntryHeight - 20) / 2), EntryWidth - TextOffsetX, EntryHeight, TextHue, node.Caption ); + + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 4, GumpButtonType.Reply, 0 ); + + if ( node is CAGObject ) + { + CAGObject obj = (CAGObject)node; + int itemID = obj.ItemID; + + Rectangle2D bounds = ItemBounds.Table[itemID]; + + if ( itemID != 1 && bounds.Height < (EntryHeight * 2) ) + { + if ( bounds.Height < EntryHeight ) + AddItem( x - OffsetSize - 22 - ((i % 2) * 44) - (bounds.Width / 2) - bounds.X, y + (EntryHeight / 2) - (bounds.Height / 2) - bounds.Y, itemID ); + else + AddItem( x - OffsetSize - 22 - ((i % 2) * 44) - (bounds.Width / 2) - bounds.X, y + EntryHeight - 1 - bounds.Height - bounds.Y, itemID ); + } + } + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = m_Owner; + + switch ( info.ButtonID ) + { + case 0: // Closed + { + return; + } + case 1: // Up + { + if ( m_Category.Parent != null ) + { + int index = Array.IndexOf( m_Category.Parent.Nodes, m_Category ) / EntryCount; + + if ( index < 0 ) + index = 0; + + from.SendGump( new CategorizedAddGump( from, m_Category.Parent, index ) ); + } + + break; + } + case 2: // Previous + { + if ( m_Page > 0 ) + from.SendGump( new CategorizedAddGump( from, m_Category, m_Page - 1 ) ); + + break; + } + case 3: // Next + { + if ( (m_Page + 1) * EntryCount < m_Category.Nodes.Length ) + from.SendGump( new CategorizedAddGump( from, m_Category, m_Page + 1 ) ); + + break; + } + default: + { + int index = (m_Page * EntryCount) + (info.ButtonID - 4); + + if ( index >= 0 && index < m_Category.Nodes.Length ) + m_Category.Nodes[index].OnClick( from, m_Page ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/ClientGump.cs b/Scripts/Gumps/ClientGump.cs new file mode 100644 index 000000000..d30e7b92a --- /dev/null +++ b/Scripts/Gumps/ClientGump.cs @@ -0,0 +1,302 @@ +using System; +using System.Net; +using Server; +using Server.Accounting; +using Server.Network; +using Server.Targets; +using Server.Gumps; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Gumps +{ + public class ClientGump : Gump + { + private NetState m_State; + + private void Resend( Mobile to, RelayInfo info ) + { + TextRelay te = info.GetTextEntry( 0 ); + + to.SendGump( new ClientGump( to, m_State, te == null ? "" : te.Text ) ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( m_State == null ) + return; + + Mobile focus = m_State.Mobile; + Mobile from = state.Mobile; + + if ( focus == null ) + { + from.SendMessage( "That character is no longer online." ); + return; + } + else if ( focus.Deleted ) + { + from.SendMessage( "That character no longer exists." ); + return; + } + else if ( from != focus && focus.Hidden && from.AccessLevel < focus.AccessLevel ) + { + from.SendMessage( "That character is no longer visible." ); + return; + } + + switch ( info.ButtonID ) + { + case 1: // Tell + { + TextRelay text = info.GetTextEntry( 0 ); + + if ( text != null ) + { + focus.SendMessage( 0x482, "{0} tells you:", from.Name ); + focus.SendMessage( 0x482, text.Text ); + + CommandLogging.WriteLine( from, "{0} {1} telling {2} \"{3}\" ", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ), text.Text ); + } + + from.SendGump( new ClientGump( from, m_State ) ); + + break; + } + case 4: // Props + { + Resend( from, info ); + + if ( !BaseCommand.IsAccessible( from, focus ) ) + from.SendMessage( "That is not accessible." ); + else + { + from.SendGump( new PropertiesGump( from, focus ) ); + CommandLogging.WriteLine( from, "{0} {1} opening properties gump of {2} ", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ) ); + } + + break; + } + case 5: // Go to + { + if ( focus.Map == null || focus.Map == Map.Internal ) + { + from.SendMessage( "That character is not in the world." ); + } + else + { + from.MoveToWorld( focus.Location, focus.Map ); + Resend( from, info ); + + CommandLogging.WriteLine( from, "{0} {1} going to {2}, Location {3}, Map {4}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ), focus.Location, focus.Map ); + } + + break; + } + case 6: // Get + { + if ( from.Map == null || from.Map == Map.Internal ) + { + from.SendMessage( "You cannot bring that person here." ); + } + else + { + focus.MoveToWorld( from.Location, from.Map ); + Resend( from, info ); + + CommandLogging.WriteLine( from, "{0} {1} bringing {2} to Location {3}, Map {4}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ), from.Location, from.Map ); + } + + break; + } + case 7: // Move + { + from.Target = new MoveTarget( focus ); + Resend( from, info ); + + break; + } + case 8: // Kick + { + if ( from.AccessLevel >= AccessLevel.GameMaster && from.AccessLevel > focus.AccessLevel ) + { + focus.Say( "I've been kicked!" ); + + m_State.Dispose(); + + CommandLogging.WriteLine( from, "{0} {1} kicking {2} ", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ) ); + } + + break; + } + case 9: // Kill + { + if ( from.AccessLevel >= AccessLevel.GameMaster && from.AccessLevel > focus.AccessLevel ) + { + focus.Kill(); + CommandLogging.WriteLine( from, "{0} {1} killing {2} ", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ) ); + } + + Resend( from, info ); + + break; + } + case 10: //Res + { + if ( from.AccessLevel >= AccessLevel.GameMaster && from.AccessLevel > focus.AccessLevel ) + { + focus.PlaySound( 0x214 ); + focus.FixedEffect( 0x376A, 10, 16 ); + + focus.Resurrect(); + + CommandLogging.WriteLine( from, "{0} {1} resurrecting {2} ", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ) ); + } + + Resend( from, info ); + + break; + } + case 11: // Skills + { + Resend( from, info ); + + if ( from.AccessLevel > focus.AccessLevel ) + { + from.SendGump( new SkillsGump( from, (Mobile)focus ) ); + CommandLogging.WriteLine( from, "{0} {1} Opening Skills gump of {2} ", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( focus ) ); + } + + break; + } + } + } + + public ClientGump( Mobile from, NetState state ) : this( from, state, "" ) + { + } + + private const int LabelColor32 = 0xFFFFFF; + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public ClientGump( Mobile from, NetState state, string initialText ) : base( 30, 20 ) + { + if ( state == null ) + return; + + m_State = state; + + AddPage( 0 ); + + AddBackground( 0, 0, 400, 274, 5054 ); + + AddImageTiled( 10, 10, 380, 19, 0xA40 ); + AddAlphaRegion( 10, 10, 380, 19 ); + + AddImageTiled( 10, 32, 380, 232, 0xA40 ); + AddAlphaRegion( 10, 32, 380, 232 ); + + AddHtml( 10, 10, 380, 20, Color( Center( "User Information" ), LabelColor32 ), false, false ); + + int line = 0; + + AddHtml( 14, 36 + (line * 20), 200, 20, Color( "Address:", LabelColor32 ), false, false ); + AddHtml( 70, 36 + (line++ * 20), 200, 20, Color( state.ToString(), LabelColor32 ), false, false ); + + AddHtml( 14, 36 + (line * 20), 200, 20, Color( "Client:", LabelColor32 ), false, false ); + AddHtml( 70, 36 + (line++ * 20), 200, 20, Color( state.Version == null ? "(null)" : state.Version.ToString(), LabelColor32 ), false, false ); + + AddHtml( 14, 36 + (line * 20), 200, 20, Color( "Version:", LabelColor32 ), false, false ); + + ExpansionInfo info = state.ExpansionInfo; + string expansionName = info.Name; + + if ( info.ID == (int)Expansion.None ) + expansionName = (((state.Flags & 0x04) != 0) ? "Blackthorn's Revenge" : ((state.Flags & 0x02) != 0) ? "Third Dawn" : ((state.Flags & 0x01) != 0) ? "Renaissance" : "The Second Age" ); + + AddHtml( 70, 36 + (line++ * 20), 200, 20, Color( expansionName, LabelColor32 ), false, false ); + + Account a = state.Account as Account; + Mobile m = state.Mobile; + + if ( from.AccessLevel >= AccessLevel.GameMaster && a != null ) + { + AddHtml( 14, 36 + (line * 20), 200, 20, Color( "Account:", LabelColor32 ), false, false ); + AddHtml( 70, 36 + (line++ * 20), 200, 20, Color( a.Username, LabelColor32 ), false, false ); + } + + if ( m != null ) + { + AddHtml( 14, 36 + (line * 20), 200, 20, Color( "Mobile:", LabelColor32 ), false, false ); + AddHtml( 70, 36 + (line++ * 20), 200, 20, Color( String.Format( "{0} (0x{1:X})", m.Name, m.Serial.Value ), LabelColor32 ), false, false ); + + AddHtml( 14, 36 + (line * 20), 200, 20, Color( "Location:", LabelColor32 ), false, false ); + AddHtml( 70, 36 + (line++ * 20), 200, 20, Color( String.Format( "{0} [{1}]", m.Location, m.Map ), LabelColor32 ), false, false ); + + AddButton( 13, 157, 0xFAB, 0xFAD, 1, GumpButtonType.Reply, 0 ); + AddHtml( 48, 158, 200, 20, Color( "Send Message", LabelColor32 ), false, false ); + + AddImageTiled( 12, 182, 376, 80, 0xA40 ); + AddImageTiled( 13, 183, 374, 78, 0xBBC ); + AddTextEntry( 15, 183, 372, 78, 0x480, 0, "" ); + + AddImageTiled( 245, 35, 142, 144, 5058 ); + + AddImageTiled( 246, 36, 140, 142, 0xA40 ); + AddAlphaRegion( 246, 36, 140, 142 ); + + line = 0; + + if ( BaseCommand.IsAccessible( from, m ) ) + { + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 4, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Properties", LabelColor32 ), false, false ); + } + + if ( from != m ) + { + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 5, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Go to them", LabelColor32 ), false, false ); + + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 6, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Bring them here", LabelColor32 ), false, false ); + } + + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 7, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Move to target", LabelColor32 ), false, false ); + + if ( from.AccessLevel >= AccessLevel.GameMaster && from.AccessLevel > m.AccessLevel ) + { + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 8, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Disconnect", LabelColor32 ), false, false ); + + if ( m.Alive ) + { + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 9, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Kill", LabelColor32 ), false, false ); + } + else + { + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 10, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Resurrect", LabelColor32 ), false, false ); + } + } + + if ( from.AccessLevel >= AccessLevel.Counselor && from.AccessLevel > m.AccessLevel ) + { + AddButton( 246, 36 + (line * 20), 0xFA5, 0xFA7, 11, GumpButtonType.Reply, 0 ); + AddHtml( 280, 38 + (line++ * 20), 100, 20, Color( "Skills browser", LabelColor32 ), false, false ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/ConfirmReleaseGump.cs b/Scripts/Gumps/ConfirmReleaseGump.cs new file mode 100644 index 000000000..4d64f7f32 --- /dev/null +++ b/Scripts/Gumps/ConfirmReleaseGump.cs @@ -0,0 +1,48 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Gumps +{ + public class ConfirmReleaseGump : Gump + { + private Mobile m_From; + private BaseCreature m_Pet; + + public ConfirmReleaseGump( Mobile from, BaseCreature pet ) : base( 50, 50 ) + { + m_From = from; + m_Pet = pet; + + m_From.CloseGump( typeof( ConfirmReleaseGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 270, 120, 5054 ); + AddBackground( 10, 10, 250, 100, 3000 ); + + AddHtmlLocalized( 20, 15, 230, 60, 1046257, true, true ); // Are you sure you want to release your pet? + + AddButton( 20, 80, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 80, 75, 20, 1011011, false, false ); // CONTINUE + + AddButton( 135, 80, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 170, 80, 75, 20, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 2 ) + { + if ( !m_Pet.Deleted && m_Pet.Controlled && m_From == m_Pet.ControlMaster && m_From.CheckAlive() /*&& m_Pet.CheckControlChance( m_From )*/ ) + { + if ( m_Pet.Map == m_From.Map && m_Pet.InRange( m_From, 14 ) ) + { + m_Pet.ControlTarget = null; + m_Pet.ControlOrder = OrderType.Release; + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Go/ChildNode.cs b/Scripts/Gumps/Go/ChildNode.cs new file mode 100644 index 000000000..aa72693f1 --- /dev/null +++ b/Scripts/Gumps/Go/ChildNode.cs @@ -0,0 +1,66 @@ +using System; +using System.Xml; +using Server; + +namespace Server.Gumps +{ + public class ChildNode + { + private ParentNode m_Parent; + + private string m_Name; + private Point3D m_Location; + + public ChildNode( XmlTextReader xml, ParentNode parent ) + { + m_Parent = parent; + + Parse( xml ); + } + + private void Parse( XmlTextReader xml ) + { + if ( xml.MoveToAttribute( "name" ) ) + m_Name = xml.Value; + else + m_Name = "empty"; + + int x = 0, y = 0, z = 0; + + if ( xml.MoveToAttribute( "x" ) ) + x = Utility.ToInt32( xml.Value ); + + if ( xml.MoveToAttribute( "y" ) ) + y = Utility.ToInt32( xml.Value ); + + if ( xml.MoveToAttribute( "z" ) ) + z = Utility.ToInt32( xml.Value ); + + m_Location = new Point3D( x, y, z ); + } + + public ParentNode Parent + { + get + { + return m_Parent; + } + } + + public string Name + { + get + { + return m_Name; + } + } + + public Point3D Location + { + get + { + return m_Location; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Go/GoGump.cs b/Scripts/Gumps/Go/GoGump.cs new file mode 100644 index 000000000..26a5ef34d --- /dev/null +++ b/Scripts/Gumps/Go/GoGump.cs @@ -0,0 +1,358 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Gumps; + +namespace Server.Gumps +{ + public class GoGump : Gump + { + public static readonly LocationTree Felucca = new LocationTree( "felucca.xml", Map.Felucca ); + public static readonly LocationTree Trammel = new LocationTree( "trammel.xml", Map.Trammel ); + public static readonly LocationTree Ilshenar = new LocationTree( "ilshenar.xml", Map.Ilshenar ); + public static readonly LocationTree Malas = new LocationTree( "malas.xml", Map.Malas ); + public static readonly LocationTree Tokuno = new LocationTree( "tokuno.xml", Map.Tokuno ); + + public static bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static bool PrevLabel = false, NextLabel = false; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + private static readonly int EntryWidth = 180; + private static readonly int EntryCount = 15; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (EntryCount + 1)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public static void DisplayTo( Mobile from ) + { + LocationTree tree; + + if ( from.Map == Map.Ilshenar ) + tree = Ilshenar; + else if ( from.Map == Map.Felucca ) + tree = Felucca; + else if ( from.Map == Map.Trammel ) + tree = Trammel; + else if ( from.Map == Map.Malas ) + tree = Malas; + else + tree = Tokuno; + + ParentNode branch = (ParentNode)tree.LastBranch[from]; + + if ( branch == null ) + branch = tree.Root; + + if ( branch != null ) + from.SendGump( new GoGump( 0, from, tree, branch ) ); + } + + private LocationTree m_Tree; + private ParentNode m_Node; + private int m_Page; + + private GoGump( int page, Mobile from, LocationTree tree, ParentNode node ) : base( 50, 50 ) + { + from.CloseGump( typeof( GoGump ) ); + + tree.LastBranch[from] = node; + + m_Page = page; + m_Tree = tree; + m_Node = node; + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + int count = node.Children.Length - (page * EntryCount); + + if ( count < 0 ) + count = 0; + else if ( count > EntryCount ) + count = EntryCount; + + int totalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (count + 1)); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID ); + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( node.Parent != null ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1, GumpButtonType.Reply, 0 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + int emptyWidth = TotalWidth - (PrevWidth * 2) - NextWidth - (OffsetSize * 5) - (OldStyle ? SetWidth + OffsetSize : 0); + + if ( !OldStyle ) + AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, EntryGumpID ); + + AddHtml( x + TextOffsetX, y, emptyWidth - TextOffsetX, EntryHeight, String.Format( "
{0}
", node.Name ), false, false ); + + x += emptyWidth + OffsetSize; + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( page > 0 ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 2, GumpButtonType.Reply, 0 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x, y, NextWidth, EntryHeight, HeaderGumpID ); + + if ( (page + 1) * EntryCount < node.Children.Length ) + { + AddButton( x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 3, GumpButtonType.Reply, 1 ); + + if ( NextLabel ) + AddLabel( x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next" ); + } + + for ( int i = 0, index = page * EntryCount; i < EntryCount && index < node.Children.Length; ++i, ++index ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + object child = node.Children[index]; + string name = ""; + + if ( child is ParentNode ) + name = ((ParentNode)child).Name; + else if ( child is ChildNode ) + name = ((ChildNode)child).Name; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, name ); + + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, index + 4, GumpButtonType.Reply, 0 ); + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + switch ( info.ButtonID ) + { + case 1: + { + if ( m_Node.Parent != null ) + from.SendGump( new GoGump( 0, from, m_Tree, m_Node.Parent ) ); + + break; + } + case 2: + { + if ( m_Page > 0 ) + from.SendGump( new GoGump( m_Page - 1, from, m_Tree, m_Node ) ); + + break; + } + case 3: + { + if ( (m_Page + 1) * EntryCount < m_Node.Children.Length ) + from.SendGump( new GoGump( m_Page + 1, from, m_Tree, m_Node ) ); + + break; + } + default: + { + int index = info.ButtonID - 4; + + if ( index >= 0 && index < m_Node.Children.Length ) + { + object o = m_Node.Children[index]; + + if ( o is ParentNode ) + { + from.SendGump( new GoGump( 0, from, m_Tree, (ParentNode)o ) ); + } + else + { + ChildNode n = (ChildNode)o; + + from.MoveToWorld( n.Location, m_Tree.Map ); + } + } + + break; + } + } + } + + public static bool MoveToLocation( Mobile m, string text ) + { + string[] args = text.Split( '.' ); + + if ( args.Length == 0 ) + return false; + + for ( int i = 0; i < args.Length; ++i ) + args[i].Trim(); + + ArrayList trees = new ArrayList(); + + // The mobile's map is to be checked first. + if ( m.Map == Map.Ilshenar ) + trees.Add( Ilshenar ); + else if ( m.Map == Map.Felucca ) + trees.Add( Felucca ); + else if ( m.Map == Map.Trammel ) + trees.Add( Trammel ); + else if ( m.Map == Map.Malas ) + trees.Add( Malas ); + else + trees.Add( Tokuno ); + + // The other maps follow. + if ( !trees.Contains( Trammel ) ) + trees.Add( Trammel ); + + if ( !trees.Contains( Felucca ) ) + trees.Add( Felucca ); + + if ( !trees.Contains( Ilshenar ) ) + trees.Add( Ilshenar ); + + if ( !trees.Contains( Malas ) ) + trees.Add( Malas ); + + if ( !trees.Contains( Tokuno ) ) + trees.Add( Tokuno ); + + LocationTree tree; + + for ( int i = 0; i < trees.Count; ++i ) + { + tree = (LocationTree)trees[i]; + + // If a specific tree was requested, it's the only one we need to parse. + if ( Insensitive.Equals( args[0], tree.Map.Name ) ) + { + if ( args.Length < 2 ) + return false; + + return ParseNode( tree.Root, args, 1, m, tree.Map ); + } + } + + for ( int i = 0; i < trees.Count; ++i ) + { + tree = (LocationTree)trees[i]; + + // Parse all trees. + if ( ParseNode( tree.Root, args, 0, m, tree.Map ) ) + return true; + } + + return false; + } + + public static bool ParseNode( ParentNode node, string[] args, int argsIndex, Mobile m, Map map ) + { + if ( args[argsIndex].Length == 0 ) + return false; + + for ( int i = 0; i < node.Children.Length; ++i ) + { + object child = node.Children[i]; + + if ( child is ParentNode ) + { + if ( Insensitive.Equals( ((ParentNode)child).Name, args[argsIndex] ) ) + { + if ( (argsIndex + 1) >= args.Length ) + return false; + + if ( args[argsIndex + 1].Length == 0 ) + return false; + + return ParseNode( (ParentNode)child, args, argsIndex + 1, m, map ); + } + else + { + if ( ParseNode( (ParentNode)child, args, argsIndex, m, map ) ) + return true; + } + } + else if ( child is ChildNode ) + { + if ( Insensitive.Equals( ((ChildNode)child).Name, args[argsIndex] ) ) + { + m.MoveToWorld( ((ChildNode)child).Location, map ); + return true; + } + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Go/LocationTree.cs b/Scripts/Gumps/Go/LocationTree.cs new file mode 100644 index 000000000..e5140e058 --- /dev/null +++ b/Scripts/Gumps/Go/LocationTree.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Xml; +using System.Collections; +using Server; + +namespace Server.Gumps +{ + public class LocationTree + { + private Map m_Map; + private ParentNode m_Root; + private Hashtable m_LastBranch; + + public LocationTree( string fileName, Map map ) + { + m_LastBranch = new Hashtable(); + m_Map = map; + + string path = Path.Combine( "Data/Locations/", fileName ); + + if ( File.Exists( path ) ) + { + XmlTextReader xml = new XmlTextReader( new StreamReader( path ) ); + + xml.WhitespaceHandling = WhitespaceHandling.None; + + m_Root = Parse( xml ); + + xml.Close(); + } + } + + public Hashtable LastBranch + { + get + { + return m_LastBranch; + } + } + + public Map Map + { + get + { + return m_Map; + } + } + + public ParentNode Root + { + get + { + return m_Root; + } + } + + private ParentNode Parse( XmlTextReader xml ) + { + xml.Read(); + xml.Read(); + xml.Read(); + + return new ParentNode( xml, null ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Go/ParentNode.cs b/Scripts/Gumps/Go/ParentNode.cs new file mode 100644 index 000000000..31c771023 --- /dev/null +++ b/Scripts/Gumps/Go/ParentNode.cs @@ -0,0 +1,79 @@ +using System; +using System.Xml; +using System.Collections; +using Server; + +namespace Server.Gumps +{ + public class ParentNode + { + private ParentNode m_Parent; + private object[] m_Children; + + private string m_Name; + + public ParentNode( XmlTextReader xml, ParentNode parent ) + { + m_Parent = parent; + + Parse( xml ); + } + + private void Parse( XmlTextReader xml ) + { + if ( xml.MoveToAttribute( "name" ) ) + m_Name = xml.Value; + else + m_Name = "empty"; + + if ( xml.IsEmptyElement ) + { + m_Children = new object[0]; + } + else + { + ArrayList children = new ArrayList(); + + while ( xml.Read() && xml.NodeType == XmlNodeType.Element ) + { + if ( xml.Name == "child" ) + { + ChildNode n = new ChildNode( xml, this ); + + children.Add( n ); + } + else + { + children.Add( new ParentNode( xml, this ) ); + } + } + + m_Children = children.ToArray(); + } + } + + public ParentNode Parent + { + get + { + return m_Parent; + } + } + + public object[] Children + { + get + { + return m_Children; + } + } + + public string Name + { + get + { + return m_Name; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/DeclareFealtyGump.cs b/Scripts/Gumps/Guilds/DeclareFealtyGump.cs new file mode 100644 index 000000000..33c3ab71b --- /dev/null +++ b/Scripts/Gumps/Guilds/DeclareFealtyGump.cs @@ -0,0 +1,54 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class DeclareFealtyGump : GuildMobileListGump + { + public DeclareFealtyGump( Mobile from, Guild guild ) : base( from, guild, true, guild.Members ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011097, false, false ); // Declare your fealty + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 250, 35, 1011098, false, false ); // I have selected my new lord. + + AddButton( 300, 400, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Mobile m = (Mobile)m_List[index]; + + if ( m != null && !m.Deleted ) + { + state.Mobile.GuildFealty = m; + } + } + } + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + } + } +} diff --git a/Scripts/Gumps/Guilds/GrantGuildTitleGump.cs b/Scripts/Gumps/Guilds/GrantGuildTitleGump.cs new file mode 100644 index 000000000..3ae8f7092 --- /dev/null +++ b/Scripts/Gumps/Guilds/GrantGuildTitleGump.cs @@ -0,0 +1,57 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GrantGuildTitleGump : GuildMobileListGump + { + public GrantGuildTitleGump( Mobile from, Guild guild ) : base( from, guild, true, guild.Members ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011118, false, false ); // Grant a title to another member. + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011127, false, false ); // I dub thee... + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Mobile m = (Mobile)m_List[index]; + + if ( m != null && !m.Deleted ) + { + m_Mobile.SendLocalizedMessage( 1013074 ); // New title (20 characters max): + m_Mobile.Prompt = new GuildTitlePrompt( m_Mobile, m, m_Guild ); + } + } + } + } + else if ( info.ButtonID == 2 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildAbbrvPrompt.cs b/Scripts/Gumps/Guilds/GuildAbbrvPrompt.cs new file mode 100644 index 000000000..570359730 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildAbbrvPrompt.cs @@ -0,0 +1,55 @@ +using System; +using Server; +using Server.Guilds; +using Server.Prompts; + +namespace Server.Gumps +{ + public class GuildAbbrvPrompt : Prompt + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildAbbrvPrompt( Mobile m, Guild g ) + { + m_Mobile = m; + m_Guild = g; + } + + public override void OnCancel( Mobile from ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + text = text.Trim(); + + if ( text.Length > 3 ) + text = text.Substring( 0, 3 ); + + if ( text.Length > 0 ) + { + if ( Guild.FindByAbbrev( text ) != null ) + { + m_Mobile.SendMessage( "{0} conflicts with the abbreviation of an existing guild.", text ); + } + else + { + m_Guild.Abbreviation = text; + m_Guild.GuildMessage( 1018025, true, text ); // Your guild abbreviation has changed: + } + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildAcceptWarGump.cs b/Scripts/Gumps/Guilds/GuildAcceptWarGump.cs new file mode 100644 index 000000000..132f78ace --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildAcceptWarGump.cs @@ -0,0 +1,67 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildAcceptWarGump : GuildListGump + { + public GuildAcceptWarGump( Mobile from, Guild guild ) : base( from, guild, true, guild.WarInvitations ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011147, false, false ); // Select the guild to accept the invitations: + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011100, false, false ); // Accept war invitations. + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Guild g = (Guild)m_List[index]; + + if ( g != null ) + { + m_Guild.WarInvitations.Remove( g ); + g.WarDeclarations.Remove( m_Guild ); + + m_Guild.AddEnemy( g ); + m_Guild.GuildMessage( 1018020, true, "{0} ({1})", g.Name, g.Abbreviation ); + + GuildGump.EnsureClosed( m_Mobile ); + + if ( m_Guild.WarInvitations.Count > 0 ) + m_Mobile.SendGump( new GuildAcceptWarGump( m_Mobile, m_Guild ) ); + else + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + } + else if ( info.ButtonID == 2 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildAdminCandidatesGump.cs b/Scripts/Gumps/Guilds/GuildAdminCandidatesGump.cs new file mode 100644 index 000000000..d9694dfe3 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildAdminCandidatesGump.cs @@ -0,0 +1,126 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; +using Server.Factions; + +namespace Server.Gumps +{ + public class GuildAdminCandidatesGump : GuildMobileListGump + { + public GuildAdminCandidatesGump( Mobile from, Guild guild ) : base( from, guild, true, guild.Candidates ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1013075, false, false ); // Accept or Refuse candidates for membership + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1013076, false, false ); // Accept + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1013077, false, false ); // Refuse + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + switch ( info.ButtonID ) + { + case 0: + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + + break; + } + case 1: // Accept + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Mobile m = (Mobile)m_List[index]; + + if ( m != null && !m.Deleted ) + { + #region Factions + PlayerState guildState = PlayerState.Find( m_Guild.Leader ); + PlayerState targetState = PlayerState.Find( m ); + + Faction guildFaction = ( guildState == null ? null : guildState.Faction ); + Faction targetFaction = ( targetState == null ? null : targetState.Faction ); + + if ( guildFaction != targetFaction ) + { + if ( guildFaction == null ) + m_Mobile.SendLocalizedMessage( 1013027 ); // That player cannot join a non-faction guild. + else if ( targetFaction == null ) + m_Mobile.SendLocalizedMessage( 1013026 ); // That player must be in a faction before joining this guild. + else + m_Mobile.SendLocalizedMessage( 1013028 ); // That person has a different faction affiliation. + + break; + } + else if ( targetState != null && targetState.IsLeaving ) + { + // OSI does this quite strangely, so we'll just do it this way + m_Mobile.SendMessage( "That person is quitting their faction and so you may not recruit them." ); + break; + } + #endregion + + m_Guild.Candidates.Remove( m ); + m_Guild.Accepted.Add( m ); + + GuildGump.EnsureClosed( m_Mobile ); + + if ( m_Guild.Candidates.Count > 0 ) + m_Mobile.SendGump( new GuildAdminCandidatesGump( m_Mobile, m_Guild ) ); + else + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + + break; + } + case 2: // Refuse + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Mobile m = (Mobile)m_List[index]; + + if ( m != null && !m.Deleted ) + { + m_Guild.Candidates.Remove( m ); + + GuildGump.EnsureClosed( m_Mobile ); + + if ( m_Guild.Candidates.Count > 0 ) + m_Mobile.SendGump( new GuildAdminCandidatesGump( m_Mobile, m_Guild ) ); + else + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildCandidatesGump.cs b/Scripts/Gumps/Guilds/GuildCandidatesGump.cs new file mode 100644 index 000000000..a441ff74e --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildCandidatesGump.cs @@ -0,0 +1,34 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildCandidatesGump : GuildMobileListGump + { + public GuildCandidatesGump( Mobile from, Guild guild ) : base( from, guild, false, guild.Candidates ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 500, 35, 1013030, false, false ); //
Candidates
+ + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 300, 35, 1011120, false, false ); // Return to the main menu. + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildChangeTypeGump.cs b/Scripts/Gumps/Guilds/GuildChangeTypeGump.cs new file mode 100644 index 000000000..46a3ed525 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildChangeTypeGump.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Network; +using Server.Factions; + +namespace Server.Gumps +{ + public class GuildChangeTypeGump : Gump + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildChangeTypeGump( Mobile from, Guild guild ) : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 400, 5054 ); + AddBackground( 10, 10, 530, 380, 3000 ); + + AddHtmlLocalized( 20, 15, 510, 30, 1013062, false, false ); //
Change Guild Type Menu
+ + AddHtmlLocalized( 50, 50, 450, 30, 1013066, false, false ); // Please select the type of guild you would like to change to + + AddButton( 20, 100, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 85, 100, 300, 30, 1013063, false, false ); // Standard guild + + AddButton( 20, 150, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddItem( 50, 143, 7109 ); + AddHtmlLocalized( 85, 150, 300, 300, 1013064, false, false ); // Order guild + + AddButton( 20, 200, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddItem( 45, 200, 7107 ); + AddHtmlLocalized( 85, 200, 300, 300, 1013065, false, false ); // Chaos guild + + AddButton( 300, 360, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 360, 150, 30, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + PlayerState pl = PlayerState.Find( m_Mobile ); + + if ( pl != null ) + { + m_Mobile.SendLocalizedMessage( 1010405 ); // You cannot change guild types while in a Faction! + } + else if ( m_Guild.TypeLastChange.AddDays( 7 ) > DateTime.Now ) + { + m_Mobile.SendLocalizedMessage( 1005292 ); // Your guild type will be changed in one week. + } + else + { + + GuildType newType; + + switch ( info.ButtonID ) + { + default: return; // Close + case 1: newType = GuildType.Regular; break; + case 2: newType = GuildType.Order; break; + case 3: newType = GuildType.Chaos; break; + } + + if ( m_Guild.Type == newType ) + return; + + m_Guild.Type = newType; + m_Guild.GuildMessage( 1018022, true, newType.ToString() ); // Guild Message: Your guild type has changed: + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } +} diff --git a/Scripts/Gumps/Guilds/GuildCharterGump.cs b/Scripts/Gumps/Guilds/GuildCharterGump.cs new file mode 100644 index 000000000..4cee9af9d --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildCharterGump.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildCharterGump : Gump + { + private Mobile m_Mobile; + private Guild m_Guild; + + private const string DefaultWebsite = "http://www.runuo.com/"; + + public GuildCharterGump( Mobile from, Guild guild ) : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 400, 5054 ); + AddBackground( 10, 10, 530, 380, 3000 ); + + AddButton( 20, 360, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 360, 300, 35, 1011120, false, false ); // Return to the main menu. + + string charter; + + if ( (charter = guild.Charter) == null || (charter = charter.Trim()).Length <= 0 ) + AddHtmlLocalized( 20, 20, 400, 35, 1013032, false, false ); // No charter has been defined. + else + AddHtml( 20, 20, 510, 75, charter, true, true ); + + AddButton( 20, 200, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 200, 300, 20, 1011122, false, false ); // Visit the guild website : + + string website; + + if ( (website = guild.Website) == null || (website = website.Trim()).Length <= 0 ) + website = DefaultWebsite; + + AddHtml( 55, 220, 300, 20, website, false, false ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + switch ( info.ButtonID ) + { + case 0: return; // Close + case 1: break; // Return to main menu + case 2: + { + string website; + + if ( (website = m_Guild.Website) == null || (website = website.Trim()).Length <= 0 ) + website = DefaultWebsite; + + m_Mobile.LaunchBrowser( website ); + break; + } + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildCharterPrompt.cs b/Scripts/Gumps/Guilds/GuildCharterPrompt.cs new file mode 100644 index 000000000..e56661a66 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildCharterPrompt.cs @@ -0,0 +1,48 @@ +using System; +using Server; +using Server.Guilds; +using Server.Prompts; + +namespace Server.Gumps +{ + public class GuildCharterPrompt : Prompt + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildCharterPrompt( Mobile m, Guild g ) + { + m_Mobile = m; + m_Guild = g; + } + + public override void OnCancel( Mobile from ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + text = text.Trim(); + + if ( text.Length > 50 ) + text = text.Substring( 0, 50 ); + + if ( text.Length > 0 ) + m_Guild.Charter = text; + + m_Mobile.SendLocalizedMessage( 1013072 ); // Enter the new website for the guild (50 characters max): + m_Mobile.Prompt = new GuildWebsitePrompt( m_Mobile, m_Guild ); + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildDeclarePeaceGump.cs b/Scripts/Gumps/Guilds/GuildDeclarePeaceGump.cs new file mode 100644 index 000000000..0481268f5 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildDeclarePeaceGump.cs @@ -0,0 +1,64 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildDeclarePeaceGump : GuildListGump + { + public GuildDeclarePeaceGump( Mobile from, Guild guild ) : base( from, guild, true, guild.Enemies ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011137, false, false ); // Select the guild you wish to declare peace with. + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011138, false, false ); // Send the olive branch. + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Guild g = (Guild)m_List[index]; + + if ( g != null ) + { + m_Guild.RemoveEnemy( g ); + m_Guild.GuildMessage( 1018018, true, "{0} ({1})", g.Name, g.Abbreviation ); // Guild Message: You are now at peace with this guild: + + GuildGump.EnsureClosed( m_Mobile ); + + if ( m_Guild.Enemies.Count > 0 ) + m_Mobile.SendGump( new GuildDeclarePeaceGump( m_Mobile, m_Guild ) ); + else + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + } + else if ( info.ButtonID == 2 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildDeclareWarGump.cs b/Scripts/Gumps/Guilds/GuildDeclareWarGump.cs new file mode 100644 index 000000000..846b8ad8c --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildDeclareWarGump.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Network; +using Server.Factions; +using System.Collections.Generic; + +namespace Server.Gumps +{ + public class GuildDeclareWarGump : GuildListGump + { + public GuildDeclareWarGump( Mobile from, Guild guild, List list ) + : base( from, guild, true, list ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011065, false, false ); // Select the guild you wish to declare war on. + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011068, false, false ); // Send the challenge! + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Guild g = m_List[index]; + + if ( g != null ) + { + if ( g == m_Guild ) + { + m_Mobile.SendLocalizedMessage( 501184 ); // You cannot declare war against yourself! + } + else if ( (g.WarInvitations.Contains( m_Guild ) && m_Guild.WarDeclarations.Contains( g )) || m_Guild.IsWar( g ) ) + { + m_Mobile.SendLocalizedMessage( 501183 ); // You are already at war with that guild. + } + else if( Faction.Find( m_Guild.Leader ) != null ) + { + m_Mobile.SendLocalizedMessage( 1005288 ); // You cannot declare war while you are in a faction + } + else + { + if ( !m_Guild.WarDeclarations.Contains( g ) ) + { + m_Guild.WarDeclarations.Add( g ); + m_Guild.GuildMessage( 1018019, true, "{0} ({1})", g.Name, g.Abbreviation ); // Guild Message: Your guild has sent an invitation for war: + } + + if ( !g.WarInvitations.Contains( m_Guild ) ) + { + g.WarInvitations.Add( m_Guild ); + g.GuildMessage( 1018021, true, "{0} ({1})", m_Guild.Name, m_Guild.Abbreviation ); // Guild Message: Your guild has received an invitation to war: + } + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildWarAdminGump( m_Mobile, m_Guild ) ); + } + } + } + } + else if ( info.ButtonID == 2 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildDeclareWarPrompt.cs b/Scripts/Gumps/Guilds/GuildDeclareWarPrompt.cs new file mode 100644 index 000000000..a47275137 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildDeclareWarPrompt.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Prompts; +using System.Collections.Generic; + +namespace Server.Gumps +{ + public class GuildDeclareWarPrompt : Prompt + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildDeclareWarPrompt( Mobile m, Guild g ) + { + m_Mobile = m; + m_Guild = g; + } + + public override void OnCancel( Mobile from ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildWarAdminGump( m_Mobile, m_Guild ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + text = text.Trim(); + + if ( text.Length >= 3 ) + { + List guilds = Utility.CastConvertList( Guild.Search( text ) ); + + GuildGump.EnsureClosed( m_Mobile ); + + if ( guilds.Count > 0 ) + { + m_Mobile.SendGump( new GuildDeclareWarGump( m_Mobile, m_Guild, guilds ) ); + } + else + { + m_Mobile.SendGump( new GuildWarAdminGump( m_Mobile, m_Guild ) ); + m_Mobile.SendLocalizedMessage( 1018003 ); // No guilds found matching - try another name in the search + } + } + else + { + m_Mobile.SendMessage( "Search string must be at least three letters in length." ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildDismissGump.cs b/Scripts/Gumps/Guilds/GuildDismissGump.cs new file mode 100644 index 000000000..31e1e3f4b --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildDismissGump.cs @@ -0,0 +1,62 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildDismissGump : GuildMobileListGump + { + public GuildDismissGump ( Mobile from, Guild guild ) : base( from, guild, true, guild.Members ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011124, false, false ); // Whom do you wish to dismiss? + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011125, false, false ); // Kick them out! + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Mobile m = (Mobile)m_List[index]; + + if ( m != null && !m.Deleted ) + { + m_Guild.RemoveMember( m ); + + if ( m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Mobile == m_Guild.Leader ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + } + } + else if ( info.ButtonID == 2 && (m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Mobile == m_Guild.Leader) ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildGump.cs b/Scripts/Gumps/Guilds/GuildGump.cs new file mode 100644 index 000000000..2905274c0 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildGump.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Network; +using Server.Prompts; +using Server.Targeting; + +namespace Server.Gumps +{ + public class GuildGump : Gump + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildGump( Mobile beholder, Guild guild ) : base( 20, 30 ) + { + m_Mobile = beholder; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 400, 5054 ); + AddBackground( 10, 10, 530, 380, 3000 ); + + AddHtml( 20, 15, 200, 35, guild.Name, false, false ); + + Mobile leader = guild.Leader; + + if ( leader != null ) + { + string leadTitle; + + if ( (leadTitle = leader.GuildTitle) != null && (leadTitle = leadTitle.Trim()).Length > 0 ) + leadTitle += ": "; + else + leadTitle = ""; + + string leadName; + + if ( (leadName = leader.Name) == null || (leadName = leadName.Trim()).Length <= 0 ) + leadName = "(empty)"; + + AddHtml( 220, 15, 250, 35, leadTitle + leadName, false, false ); + } + + AddButton( 20, 50, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 50, 100, 20, 1013022, false, false ); // Loyal to + + Mobile fealty = beholder.GuildFealty; + + if ( fealty == null || !guild.IsMember( fealty ) ) + fealty = leader; + + if ( fealty == null ) + fealty = beholder; + + string fealtyName; + + if ( fealty == null || (fealtyName = fealty.Name) == null || (fealtyName = fealtyName.Trim()).Length <= 0 ) + fealtyName = "(empty)"; + + if ( beholder == fealty ) + AddHtmlLocalized( 55, 70, 470, 20, 1018002, false, false ); // yourself + else + AddHtml( 55, 70, 470, 20, fealtyName, false, false ); + + AddButton( 215, 50, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 250, 50, 170, 20, 1013023, false, false ); // Display guild abbreviation + AddHtmlLocalized( 250, 70, 50, 20, beholder.DisplayGuildTitle ? 1011262 : 1011263, false, false ); // on/off + + AddButton( 20, 100, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 100, 470, 30, 1011086, false, false ); // View the current roster. + + AddButton( 20, 130, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 130, 470, 30, 1011085, false, false ); // Recruit someone into the guild. + + if ( guild.Candidates.Count > 0 ) + { + AddButton( 20, 160, 4005, 4007, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 160, 470, 30, 1011093, false, false ); // View list of candidates who have been sponsored to the guild. + } + else + { + AddImage( 20, 160, 4020 ); + AddHtmlLocalized( 55, 160, 470, 30, 1013031, false, false ); // There are currently no candidates for membership. + } + + AddButton( 20, 220, 4005, 4007, 6, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 220, 470, 30, 1011087, false, false ); // View the guild's charter. + + AddButton( 20, 250, 4005, 4007, 7, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 250, 470, 30, 1011092, false, false ); // Resign from the guild. + + AddButton( 20, 280, 4005, 4007, 8, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 280, 470, 30, 1011095, false, false ); // View list of guilds you are at war with. + + if ( beholder.AccessLevel >= AccessLevel.GameMaster || beholder == leader ) + { + AddButton( 20, 310, 4005, 4007, 9, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 310, 470, 30, 1011094, false, false ); // Access guildmaster functions. + } + else + { + AddImage( 20, 310, 4020 ); + AddHtmlLocalized( 55, 310, 470, 30, 1018013, false, false ); // Reserved for guildmaster + } + + AddButton( 20, 360, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 360, 470, 30, 1011441, false, false ); // EXIT + } + + public static void EnsureClosed( Mobile m ) + { + m.CloseGump( typeof( DeclareFealtyGump ) ); + m.CloseGump( typeof( GrantGuildTitleGump ) ); + m.CloseGump( typeof( GuildAdminCandidatesGump ) ); + m.CloseGump( typeof( GuildCandidatesGump ) ); + m.CloseGump( typeof( GuildChangeTypeGump ) ); + m.CloseGump( typeof( GuildCharterGump ) ); + m.CloseGump( typeof( GuildDismissGump ) ); + m.CloseGump( typeof( GuildGump ) ); + m.CloseGump( typeof( GuildmasterGump ) ); + m.CloseGump( typeof( GuildRosterGump ) ); + m.CloseGump( typeof( GuildWarGump ) ); + } + + public static bool BadLeader( Mobile m, Guild g ) + { + if ( m.Deleted || g.Disbanded || (m.AccessLevel < AccessLevel.GameMaster && g.Leader != m) ) + return true; + + Item stone = g.Guildstone; + + return ( stone == null || stone.Deleted || !m.InRange( stone.GetWorldLocation(), 2 ) ); + } + + public static bool BadMember( Mobile m, Guild g ) + { + if ( m.Deleted || g.Disbanded || (m.AccessLevel < AccessLevel.GameMaster && !g.IsMember( m )) ) + return true; + + Item stone = g.Guildstone; + + return ( stone == null || stone.Deleted || !m.InRange( stone.GetWorldLocation(), 2 ) ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( BadMember( m_Mobile, m_Guild ) ) + return; + + switch ( info.ButtonID ) + { + case 1: // Loyalty + { + EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new DeclareFealtyGump( m_Mobile, m_Guild ) ); + + break; + } + case 2: // Toggle display abbreviation + { + m_Mobile.DisplayGuildTitle = !m_Mobile.DisplayGuildTitle; + + EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + + break; + } + case 3: // View the current roster + { + EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildRosterGump( m_Mobile, m_Guild ) ); + + break; + } + case 4: // Recruit + { + m_Mobile.Target = new GuildRecruitTarget( m_Mobile, m_Guild ); + + break; + } + case 5: // Membership candidates + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildCandidatesGump( m_Mobile, m_Guild ) ); + + break; + } + case 6: // View charter + { + EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildCharterGump( m_Mobile, m_Guild ) ); + + break; + } + case 7: // Resign + { + m_Guild.RemoveMember( m_Mobile ); + + break; + } + case 8: // View wars + { + EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildWarGump( m_Mobile, m_Guild ) ); + + break; + } + case 9: // Guildmaster functions + { + if ( m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Guild.Leader == m_Mobile ) + { + EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildListGump.cs b/Scripts/Gumps/Guilds/GuildListGump.cs new file mode 100644 index 000000000..04e888e34 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildListGump.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using System.Collections.Generic; + +namespace Server.Gumps +{ + public abstract class GuildListGump : Gump + { + protected Mobile m_Mobile; + protected Guild m_Guild; + protected List m_List; + + public GuildListGump( Mobile from, Guild guild, bool radio, List list ) : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 440, 5054 ); + AddBackground( 10, 10, 530, 420, 3000 ); + + Design(); + + m_List = new List( list ); + + for ( int i = 0; i < m_List.Count; ++i ) + { + if ( (i % 11) == 0 ) + { + if ( i != 0 ) + { + AddButton( 300, 370, 4005, 4007, 0, GumpButtonType.Page, (i / 11) + 1 ); + AddHtmlLocalized( 335, 370, 300, 35, 1011066, false, false ); // Next page + } + + AddPage( (i / 11) + 1 ); + + if ( i != 0 ) + { + AddButton( 20, 370, 4014, 4016, 0, GumpButtonType.Page, (i / 11) ); + AddHtmlLocalized( 55, 370, 300, 35, 1011067, false, false ); // Previous page + } + } + + if ( radio ) + AddRadio( 20, 35 + ((i % 11) * 30), 208, 209, false, i ); + + Guild g = m_List[i]; + + string name; + + if ( (name = g.Name) != null && (name = name.Trim()).Length <= 0 ) + name = "(empty)"; + + AddLabel( (radio ? 55 : 20), 35 + ((i % 11) * 30), 0, name ); + } + } + + protected virtual void Design() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildMobileListGump.cs b/Scripts/Gumps/Guilds/GuildMobileListGump.cs new file mode 100644 index 000000000..b749d20bd --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildMobileListGump.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using System.Collections.Generic; + +namespace Server.Gumps +{ + public abstract class GuildMobileListGump : Gump + { + protected Mobile m_Mobile; + protected Guild m_Guild; + protected List m_List; + + public GuildMobileListGump( Mobile from, Guild guild, bool radio, List list ) + : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 440, 5054 ); + AddBackground( 10, 10, 530, 420, 3000 ); + + Design(); + + m_List = new List( list ); + + for ( int i = 0; i < m_List.Count; ++i ) + { + if ( (i % 11) == 0 ) + { + if ( i != 0 ) + { + AddButton( 300, 370, 4005, 4007, 0, GumpButtonType.Page, (i / 11) + 1 ); + AddHtmlLocalized( 335, 370, 300, 35, 1011066, false, false ); // Next page + } + + AddPage( (i / 11) + 1 ); + + if ( i != 0 ) + { + AddButton( 20, 370, 4014, 4016, 0, GumpButtonType.Page, (i / 11) ); + AddHtmlLocalized( 55, 370, 300, 35, 1011067, false, false ); // Previous page + } + } + + if ( radio ) + AddRadio( 20, 35 + ((i % 11) * 30), 208, 209, false, i ); + + Mobile m = m_List[i]; + + string name; + + if ( (name = m.Name) != null && (name = name.Trim()).Length <= 0 ) + name = "(empty)"; + + AddLabel( (radio ? 55 : 20), 35 + ((i % 11) * 30), 0, name ); + } + } + + protected virtual void Design() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildNamePrompt.cs b/Scripts/Gumps/Guilds/GuildNamePrompt.cs new file mode 100644 index 000000000..d81762874 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildNamePrompt.cs @@ -0,0 +1,55 @@ +using System; +using Server; +using Server.Guilds; +using Server.Prompts; + +namespace Server.Gumps +{ + public class GuildNamePrompt : Prompt + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildNamePrompt( Mobile m, Guild g ) + { + m_Mobile = m; + m_Guild = g; + } + + public override void OnCancel( Mobile from ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + text = text.Trim(); + + if ( text.Length > 40 ) + text = text.Substring( 0, 40 ); + + if ( text.Length > 0 ) + { + if ( Guild.FindByName( text ) != null ) + { + m_Mobile.SendMessage( "{0} conflicts with the name of an existing guild.", text ); + } + else + { + m_Guild.Name = text; + m_Guild.GuildMessage( 1018024, true, text ); // The name of your guild has changed: + } + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildRejectWarGump.cs b/Scripts/Gumps/Guilds/GuildRejectWarGump.cs new file mode 100644 index 000000000..4215dcb4e --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildRejectWarGump.cs @@ -0,0 +1,64 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildRejectWarGump : GuildListGump + { + public GuildRejectWarGump( Mobile from, Guild guild ) : base( from, guild, true, guild.WarInvitations ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011148, false, false ); // Select the guild to reject their invitations: + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011101, false, false ); // Reject war invitations. + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Guild g = (Guild)m_List[index]; + + if ( g != null ) + { + m_Guild.WarInvitations.Remove( g ); + g.WarDeclarations.Remove( m_Guild ); + + GuildGump.EnsureClosed( m_Mobile ); + + if ( m_Guild.WarInvitations.Count > 0 ) + m_Mobile.SendGump( new GuildRejectWarGump( m_Mobile, m_Guild ) ); + else + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + } + else if ( info.ButtonID == 2 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildRescindDeclarationGump.cs b/Scripts/Gumps/Guilds/GuildRescindDeclarationGump.cs new file mode 100644 index 000000000..5a3139e39 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildRescindDeclarationGump.cs @@ -0,0 +1,64 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildRescindDeclarationGump : GuildListGump + { + public GuildRescindDeclarationGump( Mobile from, Guild guild ) : base( from, guild, true, guild.WarDeclarations ) + { + } + + protected override void Design() + { + AddHtmlLocalized( 20, 10, 400, 35, 1011150, false, false ); // Select the guild to rescind our invitations: + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 245, 30, 1011102, false, false ); // Rescind your war declarations. + + AddButton( 300, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 400, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_List.Count ) + { + Guild g = (Guild)m_List[index]; + + if ( g != null ) + { + m_Guild.WarDeclarations.Remove( g ); + g.WarInvitations.Remove( m_Guild ); + + GuildGump.EnsureClosed( m_Mobile ); + + if ( m_Guild.WarDeclarations.Count > 0 ) + m_Mobile.SendGump( new GuildRescindDeclarationGump( m_Mobile, m_Guild ) ); + else + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } + } + else if ( info.ButtonID == 2 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildRosterGump.cs b/Scripts/Gumps/Guilds/GuildRosterGump.cs new file mode 100644 index 000000000..7e7662d71 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildRosterGump.cs @@ -0,0 +1,34 @@ +using System; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildRosterGump : GuildMobileListGump + { + public GuildRosterGump( Mobile from, Guild guild ) : base( from, guild, false, guild.Members ) + { + } + + protected override void Design() + { + AddHtml( 20, 10, 500, 35, String.Format( "
{0}
", m_Guild.Name ), false, false ); + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 300, 35, 1011120, false, false ); // Return to the main menu. + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildTitlePrompt.cs b/Scripts/Gumps/Guilds/GuildTitlePrompt.cs new file mode 100644 index 000000000..777d4e559 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildTitlePrompt.cs @@ -0,0 +1,50 @@ +using System; +using Server; +using Server.Guilds; +using Server.Prompts; + +namespace Server.Gumps +{ + public class GuildTitlePrompt : Prompt + { + private Mobile m_Leader, m_Target; + private Guild m_Guild; + + public GuildTitlePrompt( Mobile leader, Mobile target, Guild g ) + { + m_Leader = leader; + m_Target = target; + m_Guild = g; + } + + public override void OnCancel( Mobile from ) + { + if ( GuildGump.BadLeader( m_Leader, m_Guild ) ) + return; + else if ( m_Target.Deleted || !m_Guild.IsMember( m_Target ) ) + return; + + GuildGump.EnsureClosed( m_Leader ); + m_Leader.SendGump( new GuildmasterGump( m_Leader, m_Guild ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( GuildGump.BadLeader( m_Leader, m_Guild ) ) + return; + else if ( m_Target.Deleted || !m_Guild.IsMember( m_Target ) ) + return; + + text = text.Trim(); + + if ( text.Length > 20 ) + text = text.Substring( 0, 20 ); + + if ( text.Length > 0 ) + m_Target.GuildTitle = text; + + GuildGump.EnsureClosed( m_Leader ); + m_Leader.SendGump( new GuildmasterGump( m_Leader, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildWarAdminGump.cs b/Scripts/Gumps/Guilds/GuildWarAdminGump.cs new file mode 100644 index 000000000..fd389b15a --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildWarAdminGump.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildWarAdminGump : Gump + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildWarAdminGump( Mobile from, Guild guild ) : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 440, 5054 ); + AddBackground( 10, 10, 530, 420, 3000 ); + + AddHtmlLocalized( 20, 10, 510, 35, 1011105, false, false ); //
WAR FUNCTIONS
+ + AddButton( 20, 40, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 40, 400, 30, 1011099, false, false ); // Declare war through guild name search. + + int count = 0; + + if ( guild.Enemies.Count > 0 ) + { + AddButton( 20, 160 + (count * 30), 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 160 + (count++ * 30), 400, 30, 1011103, false, false ); // Declare peace. + } + else + { + AddHtmlLocalized( 20, 160 + (count++ * 30), 400, 30, 1013033, false, false ); // No current wars + } + + if ( guild.WarInvitations.Count > 0 ) + { + AddButton( 20, 160 + (count * 30), 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 160 + (count++ * 30), 400, 30, 1011100, false, false ); // Accept war invitations. + + AddButton( 20, 160 + (count * 30), 4005, 4007, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 160 + (count++ * 30), 400, 30, 1011101, false, false ); // Reject war invitations. + } + else + { + AddHtmlLocalized( 20, 160 + (count++ * 30), 400, 30, 1018012, false, false ); // No current invitations received for war. + } + + if ( guild.WarDeclarations.Count > 0 ) + { + AddButton( 20, 160 + (count * 30), 4005, 4007, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 160 + (count++ * 30), 400, 30, 1011102, false, false ); // Rescind your war declarations. + } + else + { + AddHtmlLocalized( 20, 160 + (count++ * 30), 400, 30, 1013055, false, false ); // No current war declarations + } + + AddButton( 20, 400, 4005, 4007, 6, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 400, 35, 1011104, false, false ); // Return to the previous menu. + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + switch ( info.ButtonID ) + { + case 1: // Declare war + { + m_Mobile.SendLocalizedMessage( 1018001 ); // Declare war through search - Enter Guild Name: + m_Mobile.Prompt = new GuildDeclareWarPrompt( m_Mobile, m_Guild ); + + break; + } + case 2: // Declare peace + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildDeclarePeaceGump( m_Mobile, m_Guild ) ); + + break; + } + case 3: // Accept war + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildAcceptWarGump( m_Mobile, m_Guild ) ); + + break; + } + case 4: // Reject war + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildRejectWarGump( m_Mobile, m_Guild ) ); + + break; + } + case 5: // Rescind declarations + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildRescindDeclarationGump( m_Mobile, m_Guild ) ); + + break; + } + case 6: // Return + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildWarGump.cs b/Scripts/Gumps/Guilds/GuildWarGump.cs new file mode 100644 index 000000000..b777034c1 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildWarGump.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Network; +using System.Collections.Generic; + +namespace Server.Gumps +{ + public class GuildWarGump : Gump + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildWarGump( Mobile from, Guild guild ) : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 440, 5054 ); + AddBackground( 10, 10, 530, 420, 3000 ); + + AddHtmlLocalized( 20, 10, 500, 35, 1011133, false, false ); //
WARFARE STATUS
+ + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 300, 35, 1011120, false, false ); // Return to the main menu. + + AddPage( 1 ); + + AddButton( 375, 375, 5224, 5224, 0, GumpButtonType.Page, 2 ); + AddHtmlLocalized( 410, 373, 100, 25, 1011066, false, false ); // Next page + + AddHtmlLocalized( 20, 45, 400, 20, 1011134, false, false ); // We are at war with: + + List enemies = guild.Enemies; + + if ( enemies.Count == 0 ) + { + AddHtmlLocalized( 20, 65, 400, 20, 1013033, false, false ); // No current wars + } + else + { + for ( int i = 0; i < enemies.Count; ++i ) + { + Guild g = enemies[i]; + + AddHtml( 20, 65 + (i * 20), 300, 20, g.Name, false, false ); + } + } + + AddPage( 2 ); + + AddButton( 375, 375, 5224, 5224, 0, GumpButtonType.Page, 3 ); + AddHtmlLocalized( 410, 373, 100, 25, 1011066, false, false ); // Next page + + AddButton( 30, 375, 5223, 5223, 0, GumpButtonType.Page, 1 ); + AddHtmlLocalized( 65, 373, 150, 25, 1011067, false, false ); // Previous page + + AddHtmlLocalized( 20, 45, 400, 20, 1011136, false, false ); // Guilds that we have declared war on: + + List declared = guild.WarDeclarations; + + if ( declared.Count == 0 ) + { + AddHtmlLocalized( 20, 65, 400, 20, 1018012, false, false ); // No current invitations received for war. + } + else + { + for ( int i = 0; i < declared.Count; ++i ) + { + Guild g = (Guild)declared[i]; + + AddHtml( 20, 65 + (i * 20), 300, 20, g.Name, false, false ); + } + } + + AddPage( 3 ); + + AddButton( 30, 375, 5223, 5223, 0, GumpButtonType.Page, 2 ); + AddHtmlLocalized( 65, 373, 150, 25, 1011067, false, false ); // Previous page + + AddHtmlLocalized( 20, 45, 400, 20, 1011135, false, false ); // Guilds that have declared war on us: + + List invites = guild.WarInvitations; + + if ( invites.Count == 0 ) + { + AddHtmlLocalized( 20, 65, 400, 20, 1013055, false, false ); // No current war declarations + } + else + { + for ( int i = 0; i < invites.Count; ++i ) + { + Guild g = invites[i]; + + AddHtml( 20, 65 + (i * 20), 300, 20, g.Name, false, false ); + } + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + if ( info.ButtonID == 1 ) + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildWebsitePrompt.cs b/Scripts/Gumps/Guilds/GuildWebsitePrompt.cs new file mode 100644 index 000000000..164c0a01c --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildWebsitePrompt.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Guilds; +using Server.Prompts; + +namespace Server.Gumps +{ + public class GuildWebsitePrompt : Prompt + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildWebsitePrompt( Mobile m, Guild g ) + { + m_Mobile = m; + m_Guild = g; + } + + public override void OnCancel( Mobile from ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + text = text.Trim(); + + if ( text.Length > 50 ) + text = text.Substring( 0, 50 ); + + if ( text.Length > 0 ) + m_Guild.Website = text; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/GuildmasterGump.cs b/Scripts/Gumps/Guilds/GuildmasterGump.cs new file mode 100644 index 000000000..3616c60b0 --- /dev/null +++ b/Scripts/Gumps/Guilds/GuildmasterGump.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Guilds; +using Server.Network; + +namespace Server.Gumps +{ + public class GuildmasterGump : Gump + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildmasterGump( Mobile from, Guild guild ) : base( 20, 30 ) + { + m_Mobile = from; + m_Guild = guild; + + Dragable = false; + + AddPage( 0 ); + AddBackground( 0, 0, 550, 400, 5054 ); + AddBackground( 10, 10, 530, 380, 3000 ); + + AddHtmlLocalized( 20, 15, 510, 35, 1011121, false, false ); //
GUILDMASTER FUNCTIONS
+ + AddButton( 20, 40, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 40, 470, 30, 1011107, false, false ); // Set the guild name. + + AddButton( 20, 70, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 70, 470, 30, 1011109, false, false ); // Set the guild's abbreviation. + + AddButton( 20, 100, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + switch ( m_Guild.Type ) + { + case GuildType.Regular: + AddHtmlLocalized( 55, 100, 470, 30, 1013059, false, false ); // Change guild type: Currently Standard + break; + case GuildType.Order: + AddHtmlLocalized( 55, 100, 470, 30, 1013057, false, false ); // Change guild type: Currently Order + break; + case GuildType.Chaos: + AddHtmlLocalized( 55, 100, 470, 30, 1013058, false, false ); // Change guild type: Currently Chaos + break; + } + + AddButton( 20, 130, 4005, 4007, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 130, 470, 30, 1011112, false, false ); // Set the guild's charter. + + AddButton( 20, 160, 4005, 4007, 6, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 160, 470, 30, 1011113, false, false ); // Dismiss a member. + + AddButton( 20, 190, 4005, 4007, 7, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 190, 470, 30, 1011114, false, false ); // Go to the WAR menu. + + if ( m_Guild.Candidates.Count > 0 ) + { + AddButton( 20, 220, 4005, 4007, 8, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 220, 470, 30, 1013056, false, false ); // Administer the list of candidates + } + else + { + AddImage( 20, 220, 4020 ); + AddHtmlLocalized( 55, 220, 470, 30, 1013031, false, false ); // There are currently no candidates for membership. + } + + AddButton( 20, 250, 4005, 4007, 9, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 250, 470, 30, 1011117, false, false ); // Set the guildmaster's title. + + AddButton( 20, 280, 4005, 4007, 10, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 280, 470, 30, 1011118, false, false ); // Grant a title to another member. + + AddButton( 20, 310, 4005, 4007, 11, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 310, 470, 30, 1011119, false, false ); // Move this guildstone. + + AddButton( 20, 360, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 360, 245, 30, 1011120, false, false ); // Return to the main menu. + + AddButton( 300, 360, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 335, 360, 100, 30, 1011441, false, false ); // EXIT + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( GuildGump.BadLeader( m_Mobile, m_Guild ) ) + return; + + switch ( info.ButtonID ) + { + case 1: // Main menu + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + + break; + } + case 2: // Set guild name + { + m_Mobile.SendLocalizedMessage( 1013060 ); // Enter new guild name (40 characters max): + m_Mobile.Prompt = new GuildNamePrompt( m_Mobile, m_Guild ); + + break; + } + case 3: // Set guild abbreviation + { + m_Mobile.SendLocalizedMessage( 1013061 ); // Enter new guild abbreviation (3 characters max): + m_Mobile.Prompt = new GuildAbbrvPrompt( m_Mobile, m_Guild ); + + break; + } + case 4: // Change guild type + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildChangeTypeGump( m_Mobile, m_Guild ) ); + + break; + } + case 5: // Set charter + { + m_Mobile.SendLocalizedMessage( 1013071 ); // Enter the new guild charter (50 characters max): + m_Mobile.Prompt = new GuildCharterPrompt( m_Mobile, m_Guild ); + + break; + } + case 6: // Dismiss member + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildDismissGump( m_Mobile, m_Guild ) ); + + break; + } + case 7: // War menu + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildWarAdminGump( m_Mobile, m_Guild ) ); + + break; + } + case 8: // Administer candidates + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildAdminCandidatesGump( m_Mobile, m_Guild ) ); + + break; + } + case 9: // Set guildmaster's title + { + m_Mobile.SendLocalizedMessage( 1013073 ); // Enter new guildmaster title (20 characters max): + m_Mobile.Prompt = new GuildTitlePrompt( m_Mobile, m_Mobile, m_Guild ); + + break; + } + case 10: // Grant title + { + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GrantGuildTitleGump( m_Mobile, m_Guild ) ); + + break; + } + case 11: // Move guildstone + { + if ( m_Guild.Guildstone != null ) + { + GuildTeleporter item = new GuildTeleporter( m_Guild.Guildstone ); + + if ( m_Guild.Teleporter != null ) + m_Guild.Teleporter.Delete(); + + m_Mobile.SendLocalizedMessage( 501133 ); // Use the teleporting object placed in your backpack to move this guildstone. + + m_Mobile.AddToBackpack( item ); + m_Guild.Teleporter = item; + } + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildmasterGump( m_Mobile, m_Guild ) ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/AdvancedSearch.cs b/Scripts/Gumps/Guilds/New Guild System/AdvancedSearch.cs new file mode 100644 index 000000000..3e4df3cc3 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/AdvancedSearch.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Guilds +{ + public delegate void SearchSelectionCallback( GuildDisplayType display ); + + public class GuildAdvancedSearchGump : BaseGuildGump + { + private GuildDisplayType m_Display; + private SearchSelectionCallback m_Callback; + + public GuildAdvancedSearchGump( PlayerMobile pm, Guild g, GuildDisplayType display, SearchSelectionCallback callback ) : base( pm, g ) + { + m_Callback = callback; + m_Display = display; + PopulateGump(); + } + + public override void PopulateGump() + { + base.PopulateGump(); + + AddHtmlLocalized( 431, 43, 110, 26, 1062978, 0xF, false, false ); // Diplomacy + + AddHtmlLocalized( 65, 80, 480, 26, 1063124, 0xF, true, false ); // Advanced Search Options + + AddHtmlLocalized( 65, 110, 480, 26, 1063136 + (int)m_Display, 0xF, false, false ); // Showing All Guilds/w/Relation/Waiting Relation + + AddGroup( 1 ); + AddRadio( 75, 140, 0xD2, 0xD3, false, 2 ); + AddHtmlLocalized( 105, 140, 200, 26, 1063006, 0x0, false, false ); // Show Guilds with Relationship + AddRadio( 75, 170, 0xD2, 0xD3, false, 1 ); + AddHtmlLocalized( 105, 170, 200, 26, 1063005, 0x0, false, false ); // Show Guilds Awaiting Action + AddRadio( 75, 200, 0xD2, 0xD3, false, 0 ); + AddHtmlLocalized( 105, 200, 200, 26, 1063007, 0x0, false, false ); // Show All Guilds + + AddBackground( 450, 370, 100, 26, 0x2486 ); + AddButton( 455, 375, 0x845, 0x846, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 480, 373, 60, 26, 1006044, 0x0, false, false ); // OK + AddBackground( 340, 370, 100, 26, 0x2486 ); + AddButton( 345, 375, 0x845, 0x846, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 370, 373, 60, 26, 1006045, 0x0, false, false ); // Cancel + } + + + public override void OnResponse( NetState sender, RelayInfo info ) + { + base.OnResponse( sender, info ); + + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !IsMember( pm, guild ) ) + return; + + GuildDisplayType display = m_Display; + + if( info.ButtonID == 5 ) + { + for( int i = 0; i < 3; i++ ) + { + if( info.IsSwitched( i ) ) + { + display = (GuildDisplayType)i; + m_Callback( display ); + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/BaseGuildGump.cs b/Scripts/Gumps/Guilds/New Guild System/BaseGuildGump.cs new file mode 100644 index 000000000..9329e96da --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/BaseGuildGump.cs @@ -0,0 +1,144 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Network; +using Server.Factions; +using Server.Misc; + +namespace Server.Guilds +{ + public abstract class BaseGuildGump : Gump + { + private Guild m_Guild; + private PlayerMobile m_Player; + + protected Guild guild{ get{ return m_Guild; } } + protected PlayerMobile player{ get{ return m_Player; } } + + public BaseGuildGump( PlayerMobile pm, Guild g ) : this( pm, g, 10, 10 ) + { + } + + public BaseGuildGump( PlayerMobile pm, Guild g, int x, int y ) : base( x, y ) + { + m_Guild = g; + m_Player = pm; + + pm.CloseGump( typeof( BaseGuildGump ) ); + } + + //There's prolly a way to have all the vars set of inherited classes before something is called in the Ctor... but... I can't think of it right now, and I can't use Timer.DelayCall here :< + + public virtual void PopulateGump() + { + AddPage( 0 ); + + AddBackground( 0, 0, 600, 440, 0x24AE ); + AddBackground( 66, 40, 150, 26, 0x2486 ); + AddButton( 71, 45, 0x845, 0x846, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 96, 43, 110, 26, 1063014, 0x0, false, false ); // My Guild + AddBackground( 236, 40, 150, 26, 0x2486 ); + AddButton( 241, 45, 0x845, 0x846, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 266, 43, 110, 26, 1062974, 0x0, false, false ); // Guild Roster + AddBackground( 401, 40, 150, 26, 0x2486 ); + AddButton( 406, 45, 0x845, 0x846, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 431, 43, 110, 26, 1062978, 0x0, false, false ); // Diplomacy + AddPage( 1 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( !IsMember( pm, guild ) ) + return; + + switch( info.ButtonID ) + { + case 1: + { + pm.SendGump( new GuildInfoGump( pm, guild ) ); + break; + } + case 2: + { + pm.SendGump( new GuildRosterGump( pm, guild ) ); + break; + } + case 3: + { + pm.SendGump( new GuildDiplomacyGump( pm, guild ) ); + break; + } + } + } + + public static bool IsLeader( Mobile m, Guild g ) + { + return !( m.Deleted || g.Disbanded || !( m is PlayerMobile ) || (m.AccessLevel < AccessLevel.GameMaster && g.Leader != m) ); + } + + public static bool IsMember( Mobile m, Guild g ) + { + return !( m.Deleted || g.Disbanded || !( m is PlayerMobile ) || (m.AccessLevel < AccessLevel.GameMaster && !g.IsMember( m )) ); + } + + public static bool CheckProfanity( string s ) + { + return CheckProfanity( s, 50 ); + } + public static bool CheckProfanity( string s, int maxLength ) + { + //return NameVerification.Validate( s, 1, 50, true, true, false, int.MaxValue, ProfanityProtection.Exceptions, ProfanityProtection.Disallowed, ProfanityProtection.StartDisallowed ); //What am I doing wrong, this still allows chars like the <3 symbol... 3 AM. someone change this to use this + + //With testing on OSI, Guild stuff seems to follow a 'simpler' method of profanity protection + if( s.Length < 1 || s.Length > maxLength ) + return false; + + char[] exceptions = ProfanityProtection.Exceptions; + + s = s.ToLower(); + + for ( int i = 0; i < s.Length; ++i ) + { + char c = s[i]; + + if ( (c < 'a' || c > 'z') && (c < '0' || c > '9')) + { + bool except = false; + + for( int j = 0; !except && j < exceptions.Length; j++ ) + if( c == exceptions[j] ) + except = true; + + if( !except ) + return false; + } + } + + string[] disallowed = ProfanityProtection.Disallowed; + + for( int i = 0; i < disallowed.Length; i++ ) + { + if ( s.IndexOf( disallowed[i] ) != -1 ) + return false; + } + + return true; + } + + public void AddHtmlText( int x, int y, int width, int height, TextDefinition text, bool back, bool scroll ) + { + if ( text != null && text.Number > 0 ) + AddHtmlLocalized( x, y, width, height, text.Number, back, scroll ); + else if ( text != null && text.String != null ) + AddHtml( x, y, width, height, text.String, back, scroll ); + } + + public static string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/BaseGuildListGump.cs b/Scripts/Gumps/Guilds/New Guild System/BaseGuildListGump.cs new file mode 100644 index 000000000..6dc230714 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/BaseGuildListGump.cs @@ -0,0 +1,209 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using System.Collections; +using System.Collections.Generic; + +namespace Server.Guilds +{ + public abstract class BaseGuildListGump : BaseGuildGump + { + List m_List; + IComparer m_Comparer; + InfoField[] m_Fields; + bool m_Ascending; + string m_Filter; + int m_StartNumber; + + private const int itemsPerPage = 8; + + public BaseGuildListGump( PlayerMobile pm, Guild g, List list, IComparer currentComparer, bool ascending, string filter, int startNumber, InfoField[] fields ) + : base( pm, g ) + { + m_Filter = filter.Trim(); + + m_Comparer = currentComparer; + m_Fields = fields; + m_Ascending = ascending; + m_StartNumber = startNumber; + m_List = list; + } + + public virtual bool WillFilter{ get{ return (m_Filter.Length >= 0); } } + + public override void PopulateGump() + { + base.PopulateGump(); + + List list = m_List; + if( WillFilter ) + { + m_List = new List(); + for( int i = 0; i < list.Count; i++ ) + { + if( !IsFiltered( list[i], m_Filter ) ) + m_List.Add( list[i] ); + } + } + else + { + m_List = new List( list ); + } + + m_List.Sort( m_Comparer ); + m_StartNumber = Math.Max( Math.Min( m_StartNumber, m_List.Count - 1 ), 0 ); + + + + AddBackground( 130, 75, 385, 30, 0xBB8 ); + AddTextEntry( 135, 80, 375, 30, 0x481, 1, m_Filter ); + AddButton( 520, 75, 0x867, 0x868, 5, GumpButtonType.Reply, 0 ); //Filter Button + + int width = 0; + for( int i = 0; i < m_Fields.Length; i++ ) + { + InfoField f = m_Fields[i]; + + AddImageTiled( 65 + width, 110, f.Width + 10, 26, 0xA40 ); + AddImageTiled( 67 + width, 112, f.Width + 6, 22, 0xBBC ); + AddHtmlText( 70 + width, 113, f.Width, 20, f.Name, false, false ); + + bool isComparer = ( m_Fields[i].Comparer.GetType() == m_Comparer.GetType() ); + + int ButtonID = ( isComparer ) ? ( m_Ascending ? 0x983 : 0x985 ) : 0x2716; + + AddButton( 59 + width + f.Width, 117, ButtonID, ButtonID + (isComparer ? 1 : 0) , 100 + i, GumpButtonType.Reply, 0 ); + + width += (f.Width + 12); + } + + if( m_StartNumber <= 0 ) + AddButton( 65, 80, 0x15E3, 0x15E7, 0, GumpButtonType.Page, 0 ); + else + AddButton( 65, 80, 0x15E3, 0x15E7, 6, GumpButtonType.Reply, 0 ); // Back + + if( m_StartNumber + itemsPerPage > m_List.Count ) + AddButton( 95, 80, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 0 ); + else + AddButton( 95, 80, 0x15E1, 0x15E5, 7, GumpButtonType.Reply, 0 ); // Forward + + + + int itemNumber = 0; + + if( m_Ascending ) + for( int i = m_StartNumber; i < m_StartNumber + itemsPerPage && i < m_List.Count; i++ ) + DrawEntry( m_List[i], i, itemNumber++ ); + else //descending, go from bottom of list to the top + for( int i = m_List.Count - 1 - m_StartNumber; i >= 0 && i >= (m_List.Count - itemsPerPage - m_StartNumber); i-- ) + DrawEntry( m_List[i], i, itemNumber++ ); + + DrawEndingEntry( itemNumber ); + } + + public virtual void DrawEndingEntry( int itemNumber ) + { + } + + public virtual bool HasRelationship( T o ) + { + return false; + } + + public virtual void DrawEntry( T o, int index, int itemNumber ) + { + int width = 0; + for( int j = 0; j < m_Fields.Length; j++ ) + { + InfoField f = m_Fields[j]; + + AddImageTiled( 65 + width, 138 + itemNumber * 28, f.Width + 10, 26, 0xA40 ); + AddImageTiled( 67 + width, 140 + itemNumber * 28, f.Width + 6, 22, 0xBBC ); + AddHtmlText( 70 + width, 141 + itemNumber * 28, f.Width, 20, GetValuesFor( o, m_Fields.Length )[j], false, false ); + + width += (f.Width + 12); + } + + if( HasRelationship( o ) ) + AddButton( 40, 143 + itemNumber * 28, 0x8AF, 0x8AF, 200 + index, GumpButtonType.Reply, 0 ); //Info Button + else + AddButton( 40, 143 + itemNumber * 28, 0x4B9, 0x4BA, 200 + index, GumpButtonType.Reply, 0 ); //Info Button + } + + protected abstract TextDefinition[] GetValuesFor( T o, int aryLength ); + protected abstract bool IsFiltered( T o, string filter ); + + public override void OnResponse( NetState sender, RelayInfo info ) + { + base.OnResponse( sender, info ); + + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !IsMember( pm, guild ) ) + return; + + int id = info.ButtonID; + + switch( id ) + { + case 5: //Filter + { + TextRelay t = info.GetTextEntry( 1 ); + pm.SendGump( GetResentGump( player, guild, m_Comparer, m_Ascending, ( t == null ) ? "" : t.Text, 0 ) ); + break; + } + case 6: //Back + { + pm.SendGump( GetResentGump( player, guild, m_Comparer, m_Ascending, m_Filter, m_StartNumber - itemsPerPage ) ); + break; + } + case 7: //Forward + { + pm.SendGump( GetResentGump( player, guild, m_Comparer, m_Ascending, m_Filter, m_StartNumber + itemsPerPage ) ); + break; + } + } + + if( id >= 100 && id < (100 + m_Fields.Length) ) + { + IComparer comparer = m_Fields[id-100].Comparer; + + if( m_Comparer.GetType() == comparer.GetType() ) + m_Ascending = !m_Ascending; + + pm.SendGump( GetResentGump( player, guild, comparer, m_Ascending, m_Filter, 0 ) ); + } + else if( id >= 200 && id < ( 200 + m_List.Count ) ) + { + pm.SendGump( GetObjectInfoGump( player, guild, m_List[id - 200] ) ); + } + } + + + public abstract Gump GetResentGump( PlayerMobile pm, Guild g, IComparer comparer, bool ascending, string filter, int startNumber ); + public abstract Gump GetObjectInfoGump( PlayerMobile pm, Guild g, T o ); + + public void ResendGump() + { + player.SendGump( GetResentGump( player, guild, m_Comparer, m_Ascending, m_Filter, m_StartNumber ) ); + } + } + public struct InfoField + { + private TextDefinition m_Name; + private int m_Width; + private IComparer m_Comparer; + + public TextDefinition Name{ get{ return m_Name; } } + public int Width{ get{ return m_Width; } } + public IComparer Comparer { get { return m_Comparer; } } + public InfoField( TextDefinition name, int width, IComparer comparer ) + { + m_Name = name; + m_Width = width; + m_Comparer = comparer; + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/Create Guild Gump.cs b/Scripts/Gumps/Guilds/New Guild System/Create Guild Gump.cs new file mode 100644 index 000000000..9d491f2d0 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/Create Guild Gump.cs @@ -0,0 +1,101 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Guilds +{ + public class CreateGuildGump : Gump + { + public CreateGuildGump( PlayerMobile pm ) : this( pm, "Guild Name", "" ) + { + } + + public CreateGuildGump( PlayerMobile pm, string guildName, string guildAbbrev ) : base( 10, 10 ) + { + pm.CloseGump( typeof( CreateGuildGump ) ); + pm.CloseGump( typeof( BaseGuildGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 500, 300, 0x2422 ); + AddHtmlLocalized( 25, 20, 450, 25, 1062939, 0x0, true, false ); //
GUILD MENU
+ AddHtmlLocalized( 25, 60, 450, 60, 1062940, 0x0, false, false ); // As you are not a member of any guild, you can create your own by providing a unique guild name and paying the standard guild registration fee. + AddHtmlLocalized( 25, 135, 120, 25, 1062941, 0x0, false, false ); // Registration Fee: + AddLabel( 155, 135, 0x481, Guild.RegistrationFee.ToString() ); + AddHtmlLocalized( 25, 165, 120, 25, 1011140, 0x0, false, false ); // Enter Guild Name: + AddBackground( 155, 160, 320, 26, 0xBB8 ); + AddTextEntry( 160, 163, 315, 21, 0x481, 5, guildName ); + AddHtmlLocalized( 25, 191, 120, 26, 1063035, 0x0, false, false ); // Abbreviation: + AddBackground( 155, 186, 320, 26, 0xBB8 ); + AddTextEntry( 160, 189, 315, 21, 0x481, 6, guildAbbrev ); + AddButton( 415, 217, 0xF7, 0xF8, 1, GumpButtonType.Reply, 0 ); + AddButton( 345, 217, 0xF2, 0xF1, 0, GumpButtonType.Reply, 0 ); + + if( pm.AcceptGuildInvites ) + AddButton( 20, 260, 0xD2, 0xD3, 2, GumpButtonType.Reply, 0 ); + else + AddButton( 20, 260, 0xD3, 0xD2, 2, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 45, 260, 200, 30, 1062943, 0x0, false, false ); // Ignore Guild Invites + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || pm.Guild != null ) + return; //Sanity + + switch( info.ButtonID ) + { + case 1: + { + TextRelay tName = info.GetTextEntry( 5 ); + TextRelay tAbbrev = info.GetTextEntry( 6 ); + + string guildName = (tName == null) ? "" : tName.Text; + string guildAbbrev = (tAbbrev == null) ? "" : tAbbrev.Text; + + guildName = Utility.FixHtml( guildName.Trim() ); + guildAbbrev = Utility.FixHtml( guildAbbrev.Trim() ); + + if( guildName.Length <= 0 ) + pm.SendLocalizedMessage( 1070884 ); // Guild name cannot be blank. + else if( guildAbbrev.Length <= 0 ) + pm.SendLocalizedMessage( 1070885 ); // You must provide a guild abbreviation. + else if( guildName.Length > Guild.NameLimit ) + pm.SendLocalizedMessage( 1063036, Guild.NameLimit.ToString() ); // A guild name cannot be more than ~1_val~ characters in length. + else if( guildAbbrev.Length > Guild.AbbrevLimit ) + pm.SendLocalizedMessage( 1063037, Guild.AbbrevLimit.ToString() ); // An abbreviation cannot exceed ~1_val~ characters in length. + else if( Guild.FindByAbbrev( guildAbbrev ) != null || !BaseGuildGump.CheckProfanity( guildAbbrev ) ) + pm.SendLocalizedMessage( 501153 ); // That abbreviation is not available. + else if( Guild.FindByName( guildName ) != null || !BaseGuildGump.CheckProfanity( guildName ) ) + pm.SendLocalizedMessage( 1063000 ); // That guild name is not available. + else if( !Banker.Withdraw( pm, Guild.RegistrationFee ) ) + pm.SendLocalizedMessage( 1063001, Guild.RegistrationFee.ToString() ); // You do not possess the ~1_val~ gold piece fee required to create a guild. + else + { + pm.SendLocalizedMessage( 1060398, Guild.RegistrationFee.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + pm.SendLocalizedMessage( 1063238 ); // Your new guild has been founded. + pm.Guild = new Guild( pm, guildName, guildAbbrev ); + } + + break; + } + case 2: + { + pm.AcceptGuildInvites = !pm.AcceptGuildInvites; + + if( pm.AcceptGuildInvites ) + pm.SendLocalizedMessage( 1070699 ); // You are now accepting guild invitations. + else + pm.SendLocalizedMessage( 1070698 ); // You are now ignoring guild invitations. + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/DiplomacyGump.cs b/Scripts/Gumps/Guilds/New Guild System/DiplomacyGump.cs new file mode 100644 index 000000000..7d4ddc92b --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/DiplomacyGump.cs @@ -0,0 +1,280 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using System.Collections; +using System.Collections.Generic; + +namespace Server.Guilds +{ + public enum GuildDisplayType + { + All, + AwaitingAction, + Relations + } + + public class GuildDiplomacyGump : BaseGuildListGump + { + protected virtual bool AllowAdvancedSearch{ get{ return true; } } + #region Comparers + private class NameComparer : IComparer + { + public static readonly IComparer Instance = new NameComparer(); + + public NameComparer() + { + } + + public int Compare( Guild x, Guild y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + return Insensitive.Compare( x.Name, y.Name ); + } + } + + private class StatusComparer : IComparer + { + private enum GuildCompareStatus + { + Peace, + Ally, + War + } + private Guild m_Guild; + public StatusComparer( Guild g ) + { + m_Guild = g; + } + + public int Compare( Guild x, Guild y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + GuildCompareStatus aStatus = GuildCompareStatus.Peace; + GuildCompareStatus bStatus = GuildCompareStatus.Peace; + + if( m_Guild.IsAlly( x ) ) + aStatus = GuildCompareStatus.Ally; + else if( m_Guild.IsWar( x ) ) + aStatus = GuildCompareStatus.War; + + + if( m_Guild.IsAlly( y ) ) + bStatus = GuildCompareStatus.Ally; + else if( m_Guild.IsWar( y ) ) + bStatus = GuildCompareStatus.War; + + return ((int)aStatus).CompareTo( (int)bStatus ); + } + } + private class AbbrevComparer : IComparer + { + public static readonly IComparer Instance = new AbbrevComparer(); + + public AbbrevComparer() + { + } + + public int Compare( Guild x, Guild y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + return Insensitive.Compare( x.Abbreviation, y.Abbreviation ); + } + } + + #endregion + + GuildDisplayType m_Display; + TextDefinition m_LowerText; + + public GuildDiplomacyGump( PlayerMobile pm, Guild g ) + : this( pm, g, GuildDiplomacyGump.NameComparer.Instance, true, "", 0, GuildDisplayType.All, Utility.CastConvertList( new List( Guild.List.Values ) ), (1063136 + (int)GuildDisplayType.All) ) + { + } + + public GuildDiplomacyGump( PlayerMobile pm, Guild g, IComparer currentComparer, bool ascending, string filter, int startNumber, GuildDisplayType display ) + : this( pm, g, currentComparer, ascending, filter, startNumber, display, Utility.CastConvertList( new List( Guild.List.Values ) ), (1063136 + (int)display) ) + { + } + + public GuildDiplomacyGump( PlayerMobile pm, Guild g, IComparer currentComparer, bool ascending, string filter, int startNumber, List list, TextDefinition lowerText ) + : this( pm, g, currentComparer, ascending, filter, startNumber, GuildDisplayType.All, list, lowerText ) + { + } + + public GuildDiplomacyGump( PlayerMobile pm, Guild g, bool ascending, string filter, int startNumber, List list, TextDefinition lowerText ) + : this( pm, g, GuildDiplomacyGump.NameComparer.Instance, ascending, filter, startNumber, GuildDisplayType.All, list, lowerText ) + { + } + + public GuildDiplomacyGump( PlayerMobile pm, Guild g, IComparer currentComparer, bool ascending, string filter, int startNumber, GuildDisplayType display, List list, TextDefinition lowerText ) + : base( pm, g, list, currentComparer, ascending, filter, startNumber, + new InfoField[] + { + new InfoField( 1062954, 280, GuildDiplomacyGump.NameComparer.Instance ), //Guild Name + new InfoField( 1062957, 50, GuildDiplomacyGump.AbbrevComparer.Instance ), //Abbrev + new InfoField( 1062958, 120, new GuildDiplomacyGump.StatusComparer( g ) ) //Guild Title + }) + { + + m_Display = display; + m_LowerText = lowerText; + PopulateGump(); + } + + public override void PopulateGump() + { + base.PopulateGump(); + + AddHtmlLocalized( 431, 43, 110, 26, 1062978, 0xF, false, false ); // Diplomacy + } + + protected override TextDefinition[] GetValuesFor( Guild g, int aryLength ) + { + TextDefinition[] defs = new TextDefinition[aryLength]; + + defs[0] = ( g == guild ) ? Color( g.Name, 0x006600 ) : g.Name; + defs[1] = g.Abbreviation; + + defs[2] = 3000085; //Peace + + + if( guild.IsAlly( g ) ) + { + if( guild.Alliance.Leader == g ) + defs[2] = 1063237; // Alliance Leader + else + defs[2] = 1062964; // Ally + } + else if( guild.IsWar( g ) ) + { + defs[2] = 3000086; // War + } + + return defs; + } + + public override bool HasRelationship( Guild g ) + { + if( g == guild ) + return false; + + if( guild.FindPendingWar( g ) != null ) + return true; + + AllianceInfo alliance = guild.Alliance; + if( alliance != null && alliance.IsPendingMember( g ) ) + return true; + + return false; + } + + public override void DrawEndingEntry( int itemNumber ) + { + //AddHtmlLocalized( 66, 153 + itemNumber * 28, 280, 26, 1063136 + (int)m_Display, 0xF, false, false ); // Showing All Guilds/Awaiting Action/ w/Relation Ship + //AddHtmlText( 66, 153 + itemNumber * 28, 280, 26, m_LowerText, false, false ); + + if ( m_LowerText != null && m_LowerText.Number > 0 ) + AddHtmlLocalized( 66, 153 + itemNumber * 28, 280, 26, m_LowerText.Number, 0xF, false, false ); + else if ( m_LowerText != null && m_LowerText.String != null ) + AddHtml( 66, 153 + itemNumber * 28, 280, 26, Color( m_LowerText.String, 0x99 ), false, false ); + + if( AllowAdvancedSearch ) + { + AddBackground( 350, 148 + itemNumber * 28, 200, 26, 0x2486 ); + AddButton( 355, 153 + itemNumber * 28, 0x845, 0x846, 8, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 380, 151 + itemNumber * 28, 160, 26, 1063083, 0x0, false, false ); // Advanced Search + } + } + + + protected override bool IsFiltered( Guild g, string filter ) + { + if( g == null ) + return true; + + switch( m_Display ) + { + case GuildDisplayType.Relations: + { + //if( !( guild.IsWar( g ) || guild.IsAlly( g ) ) ) + + if( !( guild.FindActiveWar( g ) != null || guild.IsAlly( g ) ) ) //As per OSI, only the guild leader wars show up under the sorting by relation + return true; + + return false; + } + case GuildDisplayType.AwaitingAction: + { + return !HasRelationship( g ); + } + } + + return !( Insensitive.Contains( g.Name, filter ) || Insensitive.Contains( g.Abbreviation, filter ) ); + } + + public override bool WillFilter + { + get + { + if( m_Display == GuildDisplayType.All ) + return base.WillFilter; + + return true; + } + } + + + public override Gump GetResentGump( PlayerMobile pm, Guild g, IComparer comparer, bool ascending, string filter, int startNumber ) + { + return new GuildDiplomacyGump( pm, g, comparer, ascending, filter, startNumber, m_Display ); + } + + public override Gump GetObjectInfoGump( PlayerMobile pm, Guild g, Guild o ) + { + if( guild == o ) + return new GuildInfoGump( pm, g ); + + return new OtherGuildInfo( pm, g, (Guild)o ) ; + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + base.OnResponse( sender, info ); + + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !IsMember( pm, guild ) ) + return; + + if( AllowAdvancedSearch && info.ButtonID == 8 ) + pm.SendGump( new GuildAdvancedSearchGump( pm, guild, m_Display, new SearchSelectionCallback( AdvancedSearch_Callback ) )); + + } + + public void AdvancedSearch_Callback( GuildDisplayType display ) + { + m_Display = display; + ResendGump(); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/GuildInfoGump.cs b/Scripts/Gumps/Guilds/New Guild System/GuildInfoGump.cs new file mode 100644 index 000000000..d4c44bdc5 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/GuildInfoGump.cs @@ -0,0 +1,175 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Network; +using Server.Factions; +using Server.Prompts; + +namespace Server.Guilds +{ + public class GuildInfoGump : BaseGuildGump + { + private bool m_IsResigning; + + public GuildInfoGump( PlayerMobile pm, Guild g ) : this( pm, g, false ) + { + } + public GuildInfoGump( PlayerMobile pm, Guild g, bool isResigning ) : base( pm, g ) + { + m_IsResigning = isResigning; + PopulateGump(); + } + + public override void PopulateGump() + { + bool isLeader = IsLeader( player, guild ); + base.PopulateGump(); + + AddHtmlLocalized( 96, 43, 110, 26, 1063014, 0xF, false, false ); // My Guild + + AddImageTiled( 65, 80, 160, 26, 0xA40 ); + AddImageTiled( 67, 82, 156, 22, 0xBBC ); + AddHtmlLocalized( 70, 83, 150, 20, 1062954, 0x0, false, false ); // Guild Name + AddHtml( 233, 84, 320, 26, guild.Name, false, false ); + + AddImageTiled( 65, 114, 160, 26, 0xA40 ); + AddImageTiled( 67, 116, 156, 22, 0xBBC ); + AddHtmlLocalized( 70, 117, 150, 20, 1063025, 0x0, false, false ); // Alliance + + if( guild.Alliance != null && guild.Alliance.IsMember( guild ) ) + { + AddHtml( 233, 118, 320, 26, guild.Alliance.Name, false, false ); + AddButton( 40, 120, 0x4B9, 0x4BA, 6, GumpButtonType.Reply, 0 ); //Alliance Roster + } + + AddImageTiled( 65, 148, 160, 26, 0xA40 ); + AddImageTiled( 67, 150, 156, 22, 0xBBC ); + AddHtmlLocalized( 70, 151, 150, 20, 1063084, 0x0, false, false ); // Guild Faction + + Faction f = Faction.Find( guild.Leader ); + if( f != null ) + AddHtml( 233, 152, 320, 26, f.ToString(), false, false ); + + AddImageTiled( 65, 196, 480, 4, 0x238D ); + + + string s = guild.Charter; + if( s == null || s == "" ) + s = "The guild leader has not yet set the guild charter."; + + AddHtml( 65, 216, 480, 80, s, true, true ); + if( isLeader ) + AddButton( 40, 251, 0x4B9, 0x4BA, 4, GumpButtonType.Reply, 0 ); //Charter Edit button + + s = guild.Website; + if( s == null || s == "" ) + s = "Guild website not yet set."; + AddHtml( 65, 306, 480, 30, s, true, false ); + if( isLeader ) + AddButton( 40, 313, 0x4B9, 0x4BA, 5, GumpButtonType.Reply, 0 ); //Website Edit button + + AddCheck( 65, 370, 0xD2, 0xD3, player.DisplayGuildTitle, 0 ); + AddHtmlLocalized( 95, 370, 150, 26, 1063085, 0x0, false, false ); // Show Guild Title + AddBackground( 450, 370, 100, 26, 0x2486 ); + + AddButton( 455, 375, 0x845, 0x846, 7, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 480, 373, 60, 26, 3006115, (m_IsResigning) ? 0x5000 : 0, false, false ); // Resign + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + base.OnResponse( sender, info ); + + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( !IsMember( pm, guild ) ) + return; + + + pm.DisplayGuildTitle = info.IsSwitched( 0 ); + + switch( info.ButtonID ) + { + //1-3 handled by base.OnResponse + case 4: + { + if( IsLeader( pm, guild ) ) + { + pm.SendLocalizedMessage( 1013071 ); // Enter the new guild charter (50 characters max): + + pm.BeginPrompt( new PromptCallback( SetCharter_Callback ), true ); //Have the same callback handle both canceling and deletion cause the 2nd callback would just get a text of "" + } + break; + } + case 5: + { + if( IsLeader( pm, guild ) ) + { + pm.SendLocalizedMessage( 1013072 ); // Enter the new website for the guild (50 characters max): + pm.BeginPrompt( new PromptCallback( SetWebsite_Callback ), true ); //Have the same callback handle both canceling and deletion cause the 2nd callback would just get a text of "" + } + break; + } + case 6: + { + //Alliance Roster + if( guild.Alliance != null && guild.Alliance.IsMember( guild ) ) + pm.SendGump( new AllianceInfo.AllianceRosterGump( pm, guild, guild.Alliance ) ); + + break; + } + case 7: + { + //Resign + if( !m_IsResigning ) + { + pm.SendLocalizedMessage( 1063332 ); // Are you sure you wish to resign from your guild? + pm.SendGump( new GuildInfoGump( pm, guild, true ) ); + } + else + { + guild.RemoveMember( pm, 1063411 ); // You resign from your guild. + } + break; + } + } + } + + public void SetCharter_Callback( Mobile from, string text ) + { + if( !IsLeader( from, guild ) ) + return; + + string charter = Utility.FixHtml( text.Trim() ); + + if( charter.Length > 50 ) + { + from.SendLocalizedMessage( 1070774, "50" ); // Your guild charter cannot exceed ~1_val~ characters. + } + else + { + guild.Charter = charter; + from.SendLocalizedMessage( 1070775 ); // You submit a new guild charter. + return; + } + } + + public void SetWebsite_Callback( Mobile from, string text ) + { + if( !IsLeader( from, guild ) ) + return; + + string site = Utility.FixHtml( text.Trim() ); + + if( site.Length > 50 ) + from.SendLocalizedMessage( 1070777, "50" ); // Your guild website cannot exceed ~1_val~ characters. + else + { + guild.Website = site; + from.SendLocalizedMessage( 1070778 ); // You submit a new guild website. + return; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs b/Scripts/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs new file mode 100644 index 000000000..6653f2093 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/GuildInvitationRequest.cs @@ -0,0 +1,63 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Guilds +{ + public class GuildInvitationRequest : BaseGuildGump + { + PlayerMobile m_Inviter; + public GuildInvitationRequest( PlayerMobile pm, Guild g, PlayerMobile inviter ) : base( pm, g ) + { + m_Inviter = inviter; + + PopulateGump(); + } + + public override void PopulateGump() + { + AddPage( 0 ); + + AddBackground( 0, 0, 350, 170, 0x2422 ); + AddHtmlLocalized( 25, 20, 300, 45, 1062946, 0x0, true, false ); //
You have been invited to join a guild! (Warning: Accepting will make you attackable!)
+ AddHtml( 25, 75, 300, 25, String.Format( "
{0}
", guild.Name ), true, false ); + AddButton( 265, 130, 0xF7, 0xF8, 1, GumpButtonType.Reply, 0 ); + AddButton( 195, 130, 0xF2, 0xF1, 0, GumpButtonType.Reply, 0 ); + AddButton( 20, 130, 0xD2, 0xD3, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 130, 150, 30, 1062943, 0x0, false, false ); // Ignore Guild Invites + } + + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if( guild.Disbanded || player.Guild != null ) + return; + + switch( info.ButtonID ) + { + case 0: + { + m_Inviter.SendLocalizedMessage( 1063250, String.Format( "{0}\t{1}", player.Name, guild.Name ) ); // ~1_val~ has declined your invitation to join ~2_val~. + break; + } + case 1: + { + guild.AddMember( player ); + player.SendLocalizedMessage( 1063056, guild.Name ); // You have joined ~1_val~. + m_Inviter.SendLocalizedMessage( 1063249, String.Format( "{0}\t{1}", player.Name, guild.Name ) ); // ~1_val~ has accepted your invitation to join ~2_val~. + + break; + } + case 2: + { + player.AcceptGuildInvites = false; + player.SendLocalizedMessage( 1070698 ); // You are now ignoring guild invitations. + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs b/Scripts/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs new file mode 100644 index 000000000..b5f21676d --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/GuildMemberInfoGump.cs @@ -0,0 +1,231 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using Server.Targeting; +using Server.Prompts; + +namespace Server.Guilds +{ + public class GuildMemberInfoGump : BaseGuildGump + { + PlayerMobile m_Member; + bool m_ToLeader, m_toKick; + + public GuildMemberInfoGump( PlayerMobile pm, Guild g, PlayerMobile member, bool toKick, bool toPromoteToLeader ) : base( pm, g, 10, 40 ) + { + m_ToLeader = toPromoteToLeader; + m_toKick = toKick; + m_Member = (PlayerMobile)member; + PopulateGump(); + } + + public override void PopulateGump() + { + AddPage( 0 ); + + AddBackground( 0, 0, 350, 255, 0x242C ); + AddHtmlLocalized( 20, 15, 310, 26, 1063018, 0x0, false, false ); //
Guild Member Information
+ AddImageTiled( 20, 40, 310, 2, 0x2711 ); + + AddHtmlLocalized( 20, 50, 150, 26, 1062955, 0x0, true, false ); // Name + AddHtml( 180, 53, 150, 26, m_Member.Name, false, false ); + + AddHtmlLocalized( 20, 80, 150, 26, 1062956, 0x0, true, false ); // Rank + AddHtmlLocalized( 180, 83, 150, 26, m_Member.GuildRank.Name, 0x0, false, false ); + + AddHtmlLocalized( 20, 110, 150, 26, 1062953, 0x0, true, false ); // Guild Title + AddHtml( 180, 113, 150, 26, m_Member.GuildTitle, false, false ); + AddImageTiled( 20, 142, 310, 2, 0x2711 ); + + AddBackground( 20, 150, 310, 26, 0x2486 ); + AddButton( 25, 155, 0x845, 0x846, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 153, 270, 26, (m_Member == player.GuildFealty && guild.Leader != m_Member) ? 1063082 : 1062996, 0x0, false, false ); // Clear/Cast Vote For This Member + + AddBackground( 20, 180, 150, 26, 0x2486 ); + AddButton( 25, 185, 0x845, 0x846, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 183, 110, 26, 1062993, (m_ToLeader)? 0x990000 : 0, false, false ); // Promote + + AddBackground( 180, 180, 150, 26, 0x2486 ); + AddButton( 185, 185, 0x845, 0x846, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 210, 183, 110, 26, 1062995, 0x0, false, false ); // Set Guild Title + + AddBackground( 20, 210, 150, 26, 0x2486 ); + AddButton( 25, 215, 0x845, 0x846, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 213, 110, 26, 1062994, 0x0, false, false ); // Demote + + AddBackground( 180, 210, 150, 26, 0x2486 ); + AddButton( 185, 215, 0x845, 0x846, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 210, 213, 110, 26, 1062997, (m_toKick)? 0x5000 : 0, false, false ); // Kick + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !IsMember( pm, guild ) || !IsMember( m_Member, guild ) ) + return; + + RankDefinition playerRank = pm.GuildRank; + RankDefinition targetRank = m_Member.GuildRank; + + switch( info.ButtonID ) + { + case 1: //Promote + { + if( playerRank.GetFlag( RankFlags.CanPromoteDemote ) && ((playerRank.Rank -1 ) > targetRank.Rank || ( playerRank == RankDefinition.Leader && playerRank.Rank > targetRank.Rank )) ) + { + targetRank = RankDefinition.Ranks[targetRank.Rank + 1]; + + if( targetRank == RankDefinition.Leader ) + { + if( m_ToLeader ) + { + m_Member.GuildRank = targetRank; + pm.SendLocalizedMessage( 1063156, m_Member.Name ); // The guild information for ~1_val~ has been updated. + pm.SendLocalizedMessage( 1063156, pm.Name ); // The guild information for ~1_val~ has been updated. + guild.Leader = m_Member; + } + else + { + pm.SendLocalizedMessage( 1063144 ); // Are you sure you wish to make this member the new guild leader? + pm.SendGump( new GuildMemberInfoGump( player, guild, m_Member, false, true ) ); + } + } + else + { + m_Member.GuildRank = targetRank; + pm.SendLocalizedMessage( 1063156, m_Member.Name ); // The guild information for ~1_val~ has been updated. + } + } + else + pm.SendLocalizedMessage( 1063143 ); // You don't have permission to promote this member. + + break; + } + case 2: //Demote + { + if( playerRank.GetFlag( RankFlags.CanPromoteDemote ) && playerRank.Rank > targetRank.Rank ) + { + if( targetRank == RankDefinition.Lowest ) + { + if( RankDefinition.Lowest.Name.Number == 1062963 ) + pm.SendLocalizedMessage( 1063333 ); // You can't demote a ronin. + else + pm.SendMessage( "You can't demote a {0}.", RankDefinition.Lowest.Name ); + } + else + { + m_Member.GuildRank = RankDefinition.Ranks[targetRank.Rank - 1]; + pm.SendLocalizedMessage( 1063156, m_Member.Name ); // The guild information for ~1_val~ has been updated. + } + } + else + pm.SendLocalizedMessage( 1063146 ); // You don't have permission to demote this member. + + + break; + } + case 3: //Set Guild title + { + if( playerRank.GetFlag( RankFlags.CanSetGuildTitle ) && ( playerRank.Rank > targetRank.Rank || m_Member == player)) + { + pm.SendLocalizedMessage( 1011128 ); // Enter the new title for this guild member or 'none' to remove a title: + + pm.BeginPrompt( new PromptCallback( SetTitle_Callback ) ); + } + else if( m_Member.GuildTitle == null || m_Member.GuildTitle.Length <= 0 ) + { + pm.SendLocalizedMessage( 1070746 ); // You don't have the permission to set that member's guild title. + } + else + { + pm.SendLocalizedMessage( 1063148 ); // You don't have permission to change this member's guild title. + } + + break; + } + case 4: //Vote + { + if( m_Member == pm.GuildFealty && guild.Leader != m_Member ) + pm.SendLocalizedMessage( 1063158 ); // You have cleared your vote for guild leader. + else if( playerRank.GetFlag( RankFlags.CanVote ) ) + { + if( m_Member == guild.Leader ) + pm.SendLocalizedMessage( 1063424 ); // You can't vote for the current guild leader. + else if( guild.CanBeVotedFor( m_Member ) ) + pm.SendLocalizedMessage( 1063425 ); // You can't vote for an inactive guild member. + else + { + pm.GuildFealty = m_Member; + pm.SendLocalizedMessage( 1063159, m_Member.Name ); // You cast your vote for ~1_val~ for guild leader. + } + } + else + pm.SendLocalizedMessage( 1063149 ); // You don't have permission to vote. + + break; + } + case 5: //Kick + { + if( ( playerRank.GetFlag( RankFlags.RemovePlayers ) && playerRank.Rank > targetRank.Rank ) || ( playerRank.GetFlag( RankFlags.RemoveLowestRank ) && targetRank == RankDefinition.Lowest ) ) + { + if( m_toKick ) + { + guild.RemoveMember( m_Member ); + pm.SendLocalizedMessage( 1063157 ); // The member has been removed from your guild. + } + else + { + pm.SendLocalizedMessage( 1063152 ); // Are you sure you wish to kick this member from the guild? + pm.SendGump( new GuildMemberInfoGump( player, guild, m_Member, true, false ) ); + } + } + else + pm.SendLocalizedMessage( 1063151 ); // You don't have permission to remove this member. + + break; + } + } + } + + public void SetTitle_Callback( Mobile from, string text ) + { + PlayerMobile pm = from as PlayerMobile; + PlayerMobile targ = m_Member; + + if( pm == null || targ == null ) + return; + + Guild g = targ.Guild as Guild; + + if( g == null || !IsMember( pm, g ) || !(pm.GuildRank.GetFlag( RankFlags.CanSetGuildTitle ) && (pm.GuildRank.Rank > targ.GuildRank.Rank || pm == targ)) ) + { + if( m_Member.GuildTitle == null || m_Member.GuildTitle.Length <= 0 ) + pm.SendLocalizedMessage( 1070746 ); // You don't have the permission to set that member's guild title. + else + pm.SendLocalizedMessage( 1063148 ); // You don't have permission to change this member's guild title. + + return; + } + + + string title = Utility.FixHtml( text.Trim() ); + + if( title.Length > 20 ) + from.SendLocalizedMessage( 501178 ); // That title is too long. + else if( !BaseGuildGump.CheckProfanity( title ) ) + from.SendLocalizedMessage( 501179 ); // That title is disallowed. + else + { + if( Insensitive.Equals( title, "none" ) ) + targ.GuildTitle = null; + else + targ.GuildTitle = title; + + pm.SendLocalizedMessage( 1063156, targ.Name ); // The guild information for ~1_val~ has been updated. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/GuildRosterGump.cs b/Scripts/Gumps/Guilds/New Guild System/GuildRosterGump.cs new file mode 100644 index 000000000..53a80f340 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/GuildRosterGump.cs @@ -0,0 +1,266 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using System.Collections; +using Server.Targets; +using Server.Factions; +using System.Collections.Generic; + +namespace Server.Guilds +{ + public class GuildRosterGump : BaseGuildListGump //TODO: Convert to + { + #region Comparers + private class NameComparer : IComparer + { + public static readonly IComparer Instance = new NameComparer(); + + public NameComparer() + { + } + + public int Compare( PlayerMobile x, PlayerMobile y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + return Insensitive.Compare( x.Name, y.Name ); + } + } + + private class LastOnComparer : IComparer + { + public static readonly IComparer Instance = new LastOnComparer(); + + public LastOnComparer() + { + } + + public int Compare( PlayerMobile x, PlayerMobile y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + NetState aState = x.NetState; + NetState bState = y.NetState; + + if ( aState == null && bState == null ) + return x.LastOnline.CompareTo( y.LastOnline ); + else if ( aState == null ) + return 1; + else if ( bState == null ) + return -1; + else + return 0; + } + } + private class TitleComparer : IComparer + { + public static readonly IComparer Instance = new TitleComparer(); + + public TitleComparer() + { + } + + public int Compare( PlayerMobile x, PlayerMobile y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + return Insensitive.Compare( x.GuildTitle, y.GuildTitle ); + } + } + + private class RankComparer : IComparer + { + public static readonly IComparer Instance = new RankComparer(); + + public RankComparer() + { + } + + public int Compare( PlayerMobile x, PlayerMobile y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + return x.GuildRank.Rank.CompareTo( y.GuildRank.Rank ); + } + } + + #endregion + + private static InfoField[] m_Fields = + new InfoField[] + { + new InfoField( 1062955, 130, GuildRosterGump.NameComparer.Instance ), //Name + new InfoField( 1062956, 80, GuildRosterGump.RankComparer.Instance ), //Rank + new InfoField( 1062952, 80, GuildRosterGump.LastOnComparer.Instance), //Last On + new InfoField( 1062953, 150, GuildRosterGump.TitleComparer.Instance ) //Guild Title + }; + + public GuildRosterGump( PlayerMobile pm, Guild g ) : this( pm, g, GuildRosterGump.LastOnComparer.Instance, true, "", 0 ) + { + } + + public GuildRosterGump( PlayerMobile pm, Guild g, IComparer currentComparer, bool ascending, string filter, int startNumber ) + : base( pm, g, Utility.SafeConvertList( g.Members ), currentComparer, ascending, filter, startNumber, m_Fields ) + { + PopulateGump(); + } + + public override void PopulateGump() + { + base.PopulateGump(); + + AddHtmlLocalized( 266, 43, 110, 26, 1062974, 0xF, false, false ); // Guild Roster + } + + public override void DrawEndingEntry( int itemNumber ) + { + AddBackground( 225, 148 + itemNumber * 28, 150, 26, 0x2486 ); + AddButton( 230, 153 + itemNumber * 28, 0x845, 0x846, 8, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 255, 151 + itemNumber * 28, 110, 26, 1062992, 0x0, false, false ); // Invite Player + } + + protected override TextDefinition[] GetValuesFor( PlayerMobile pm, int aryLength ) + { + TextDefinition[] defs = new TextDefinition[aryLength]; + + string name = String.Format( "{0}{1}", pm.Name, ( player.GuildFealty == pm && player.GuildFealty != guild.Leader ) ? " *" : "" ); + + if( pm == player ) + name = Color( name, 0x006600 ); + else if( pm.NetState != null ) + name = Color( name, 0x000066 ); + + defs[0] = name; + defs[1] = pm.GuildRank.Name; + defs[2] = (pm.NetState != null) ? new TextDefinition( 1063015 ): new TextDefinition( pm.LastOnline.ToString( "yyyy-MM-dd" ) ); + defs[3] = (pm.GuildTitle == null) ? "" : pm.GuildTitle; + + return defs; + } + + protected override bool IsFiltered( PlayerMobile pm, string filter ) + { + if( pm == null ) + return true; + + return !Insensitive.Contains( pm.Name, filter ); + } + + public override Gump GetResentGump( PlayerMobile pm, Guild g, IComparer comparer, bool ascending, string filter, int startNumber ) + { + return new GuildRosterGump( pm, g, comparer, ascending, filter, startNumber ); + } + + public override Gump GetObjectInfoGump( PlayerMobile pm, Guild g, PlayerMobile o ) + { + return new GuildMemberInfoGump( pm, g, o, false, false ) ; + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + base.OnResponse( sender, info ); + + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( pm == null || !IsMember( pm, guild ) ) + return; + + if( info.ButtonID == 8 ) + { + if( pm.GuildRank.GetFlag( RankFlags.CanInvitePlayer ) ) + { + pm.SendLocalizedMessage( 1063048 ); // Whom do you wish to invite into your guild? + pm.BeginTarget( -1, false, Targeting.TargetFlags.None, new TargetStateCallback( InvitePlayer_Callback ), guild ); + } + else + pm.SendLocalizedMessage( 503301 ); // You don't have permission to do that. + } + } + + public void InvitePlayer_Callback( Mobile from, object targeted, object state ) + { + PlayerMobile pm = from as PlayerMobile; + PlayerMobile targ = targeted as PlayerMobile; + + Guild g = state as Guild; + + PlayerState guildState = PlayerState.Find( g.Leader ); + PlayerState targetState = PlayerState.Find( targ ); + + Faction guildFaction = ( guildState == null ? null : guildState.Faction ); + Faction targetFaction = ( targetState == null ? null : targetState.Faction ); + + if( pm == null || !IsMember( pm, guild ) || !pm.GuildRank.GetFlag( RankFlags.CanInvitePlayer ) ) + { + pm.SendLocalizedMessage( 503301 ); // You don't have permission to do that. + } + else if( targ == null ) + { + pm.SendLocalizedMessage( 1063334 ); // That isn't a valid player. + } + else if( !targ.AcceptGuildInvites ) + { + pm.SendLocalizedMessage( 1063049, targ.Name ); // ~1_val~ is not accepting guild invitations. + } + else if( g.IsMember( targ ) ) + { + pm.SendLocalizedMessage( 1063050, targ.Name ); // ~1_val~ is already a member of your guild! + } + else if( targ.Guild != null ) + { + pm.SendLocalizedMessage( 1063051, targ.Name ); // ~1_val~ is already a member of a guild. + } + else if( targ.HasGump( typeof( BaseGuildGump ) ) || targ.HasGump( typeof( CreateGuildGump ) )) //TODO: Check message if CreateGuildGump Open + { + pm.SendLocalizedMessage( 1063052, targ.Name ); // ~1_val~ is currently considering another guild invitation. + } + #region Factions + else if( targ.Young && guildFaction != null ) + { + pm.SendLocalizedMessage( 1070766 ); // You cannot invite a young player to your faction-aligned guild. + } + else if ( guildFaction != targetFaction ) + { + if ( guildFaction == null ) + pm.SendLocalizedMessage( 1013027 ); // That player cannot join a non-faction guild. + else if ( targetFaction == null ) + pm.SendLocalizedMessage( 1013026 ); // That player must be in a faction before joining this guild. + else + pm.SendLocalizedMessage( 1013028 ); // That person has a different faction affiliation. + } + else if ( targetState != null && targetState.IsLeaving ) + { + // OSI does this quite strangely, so we'll just do it this way + pm.SendMessage( "That person is quitting their faction and so you may not recruit them." ); + } + #endregion + else + { + pm.SendLocalizedMessage( 1063053, targ.Name ); // You invite ~1_val~ to join your guild. + targ.SendGump( new GuildInvitationRequest( targ, guild, pm ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/OtherGuildInfo.cs b/Scripts/Gumps/Guilds/New Guild System/OtherGuildInfo.cs new file mode 100644 index 000000000..06397efe5 --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/OtherGuildInfo.cs @@ -0,0 +1,602 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Gumps; +using Server.Network; +using Server.Factions; +using Server.Prompts; + +namespace Server.Guilds +{ + public class OtherGuildInfo : BaseGuildGump + { + private Guild m_Other; + public OtherGuildInfo( PlayerMobile pm, Guild g, Guild otherGuild ) : base( pm, g, 10, 40 ) + { + m_Other = otherGuild; + + g.CheckExpiredWars(); + + PopulateGump(); + } + + public void AddButtonAndBackground( int x, int y, int buttonID, int locNum ) + { + AddBackground( x, y, 225, 26, 0x2486 ); + AddButton( x+5, y+5, 0x845, 0x846, buttonID, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( x+30, y+3, 185, 26, locNum, 0x0, false, false ); + } + + public override void PopulateGump() + { + WarDeclaration war = guild.FindPendingWar( m_Other ); + WarDeclaration activeWar = guild.FindActiveWar( m_Other ); + + AllianceInfo alliance = guild.Alliance; + AllianceInfo otherAlliance = m_Other.Alliance; + //NOTE TO SELF: Only only alliance leader can see pending guild alliance statuses + + bool PendingWar = (war != null); + bool ActiveWar = (activeWar != null); + AddPage( 0 ); + + AddBackground( 0, 0, 520, 335, 0x242C ); + AddHtmlLocalized( 20, 15, 480, 26, 1062975, 0x0, false, false ); //
Guild Relationship
+ AddImageTiled( 20, 40, 480, 2, 0x2711 ); + AddHtmlLocalized( 20, 50, 120, 26, 1062954, 0x0, true, false ); // Guild Name + AddHtml( 150, 53, 360, 26, m_Other.Name, false, false ); + + AddHtmlLocalized( 20, 80, 120, 26, 1063025, 0x0, true, false ); // Alliance + + if( otherAlliance != null ) + { + if( otherAlliance.IsMember( m_Other )) + { + AddHtml( 150, 83, 360, 26, otherAlliance.Name, false, false ); + } + //else if( otherAlliance.Leader == guild && ( otherAlliance.IsPendingMember( m_Other ) || otherAlliance.IsPendingMember( guild ) ) ) + /* else if( (otherAlliance.Leader == guild && otherAlliance.IsPendingMember( m_Other ) ) || ( otherAlliance.Leader == m_Other && otherAlliance.IsPendingMember( guild ) ) ) + { + AddHtml( 150, 83, 360, 26, Color( alliance.Name, 0xF), false, false ); + } + //AddHtml( 150, 83, 360, 26, ( alliance.PendingMembers.Contains( guild ) || alliance.PendingMembers.Contains( m_Other ) ) ? String.Format( "{0}", alliance.Name ) : alliance.Name, false, false ); + //AddHtml( 150, 83, 360, 26, ( otherAlliance == alliance && otherAlliance.PendingMembers.Contains( guild ) || otherAlliance.PendingMembers.Contains( m_Other ) ) ? String.Format( "{0}", otherAlliance.Name ) : otherAlliance.Name, false, false ); + */ + } + + AddHtmlLocalized( 20, 110, 120, 26, 1063139, 0x0, true, false ); // Abbreviation + AddHtml( 150, 113, 120, 26, m_Other.Abbreviation, false, false ); + + + string kills = "0/0"; + string time = "00:00"; + string otherKills = "0/0"; + + WarDeclaration otherWar; + + if( ActiveWar ) + { + kills = String.Format( "{0}/{1}", activeWar.Kills, activeWar.MaxKills ); + + TimeSpan timeRemaining = TimeSpan.Zero; + + if( activeWar.WarLength != TimeSpan.Zero && (activeWar.WarBeginning + activeWar.WarLength) > DateTime.Now ) + timeRemaining = (activeWar.WarBeginning + activeWar.WarLength) - DateTime.Now; + + //time = String.Format( "{0:D2}:{1:D2}", timeRemaining.Hours.ToString(), timeRemaining.Subtract( TimeSpan.FromHours( timeRemaining.Hours ) ).Minutes ); //Is there a formatter for htis? it's 2AM and I'm tired and can't find it + time = String.Format( "{0:D2}:{1:mm}", timeRemaining.Hours, DateTime.MinValue + timeRemaining ); + + otherWar = m_Other.FindActiveWar( guild ); + if( otherWar != null ) + otherKills = String.Format( "{0}/{1}", otherWar.Kills, otherWar.MaxKills ); + } + else if( PendingWar ) + { + kills = Color( String.Format( "{0}/{1}", war.Kills, war.MaxKills ), 0x990000 ); + //time = Color( String.Format( "{0}:{1}", war.WarLength.Hours, ((TimeSpan)(war.WarLength - TimeSpan.FromHours( war.WarLength.Hours ))).Minutes ), 0xFF0000 ); + time = Color( String.Format( "{0:D2}:{1:mm}", war.WarLength.Hours, DateTime.MinValue + war.WarLength ), 0x990000 ); + + otherWar = m_Other.FindPendingWar( guild ); + if( otherWar != null ) + otherKills = Color( String.Format( "{0}/{1}", otherWar.Kills, otherWar.MaxKills ), 0x990000 ); + } + + AddHtmlLocalized( 280, 110, 120, 26, 1062966, 0x0, true, false ); // Your Kills + AddHtml( 410, 113, 120, 26, kills , false, false ); + + AddHtmlLocalized( 20, 140, 120, 26, 1062968, 0x0, true, false ); // Time Remaining + AddHtml( 150, 143, 120, 26, time, false, false ); + + AddHtmlLocalized( 280, 140, 120, 26, 1062967, 0x0, true, false ); // Their Kills + AddHtml( 410, 143, 120, 26, otherKills, false, false ); + + AddImageTiled( 20, 172, 480, 2, 0x2711 ); + + int number = 1062973;//
You are at peace with this guild.
+ + + if( PendingWar ) + { + if( war.WarRequester ) + { + number = 1063027; //
You have challenged this guild to war!
+ } + else + { + number = 1062969; //
This guild has challenged you to war!
+ + AddButtonAndBackground( 20, 260, 5, 1062981 ); // Accept Challenge + AddButtonAndBackground( 275, 260, 6, 1062983 ); //Modify Terms + } + + AddButtonAndBackground( 20, 290, 7, 1062982 ); // Dismiss Challenge + } + else if( ActiveWar ) + { + number = 1062965; //
You are at war with this guild!
+ AddButtonAndBackground( 20, 290, 8, 1062980 ); // Surrender + } + else if ( alliance != null && alliance == otherAlliance ) //alliance, Same Alliance + { + if( alliance.IsMember( guild ) && alliance.IsMember( m_Other ) ) //Both in Same alliance, full members + { + number = 1062970; //
You are allied with this guild.
+ + if( alliance.Leader == guild ) + { + AddButtonAndBackground( 20, 260, 12, 1062984 ); // Remove Guild from Alliance + AddButtonAndBackground( 275, 260, 13, 1063433 ); // Promote to Alliance Leader //Note: No 'confirmation' like the other leader guild promotion things + //Remove guild from alliance //Promote to Alliance Leader + } + + //Show roster, Centered, up + AddButtonAndBackground( 148, 215, 10, 1063164 ); //Show Alliance Roster + //Leave Alliance + AddButtonAndBackground( 20, 290, 11, 1062985 ); // Leave Alliance + } + else if( alliance.Leader == guild && alliance.IsPendingMember( m_Other ) ) + { + number = 1062971; //
You have requested an alliance with this guild.
+ + //Show Alliance Roster, Centered, down. + AddButtonAndBackground( 148, 245, 10, 1063164 ); //Show Alliance Roster + //Withdraw Request + AddButtonAndBackground( 20, 290, 14, 1062986 ); // Withdraw Request + + AddHtml( 150, 83, 360, 26, Color( alliance.Name, 0x99 ), false, false ); + } + else if( alliance.Leader == m_Other && alliance.IsPendingMember( guild ) ) + { + number = 1062972; //
This guild has requested an alliance.
+ + //Show alliance Roster, top + AddButtonAndBackground( 148, 215, 10, 1063164 ); //Show Alliance Roster + //Deny Request + //Accept Request + AddButtonAndBackground( 20, 260, 15, 1062988 ); // Deny Request + AddButtonAndBackground( 20, 290, 16, 1062987 ); // Accept Request + + AddHtml( 150, 83, 360, 26, Color( alliance.Name, 0x99 ), false, false ); + } + } + else + { + AddButtonAndBackground( 20, 260, 2, 1062990 ); // Request Alliance + AddButtonAndBackground( 20, 290, 1, 1062989 ); // Declare War! + } + + AddButtonAndBackground( 275, 290, 0, 3000091 ); //Cancel + + AddHtmlLocalized( 20, 180, 480, 30, number, 0x0, true, false ); + AddImageTiled( 20, 245, 480, 2, 0x2711 ); + } + + + public override void OnResponse( NetState sender, RelayInfo info ) + { + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( !IsMember( pm, guild ) ) + return; + + RankDefinition playerRank = pm.GuildRank; + + WarDeclaration war = guild.FindPendingWar( m_Other ); + WarDeclaration activeWar = guild.FindActiveWar( m_Other ); + WarDeclaration otherWar = m_Other.FindPendingWar( guild ); + + AllianceInfo alliance = guild.Alliance; + AllianceInfo otherAlliance = m_Other.Alliance; + + switch( info.ButtonID ) + { + #region War + case 5: //Accept the war + { + if( war != null && !war.WarRequester && activeWar == null ) + { + if( !playerRank.GetFlag( RankFlags.ControlWarStatus ) ) + { + pm.SendLocalizedMessage( 1063440 ); // You don't have permission to negotiate wars. + } + else + { + //Accept the war + guild.PendingWars.Remove( war ); + war.WarBeginning = DateTime.Now; + guild.AcceptedWars.Add( war ); + + if( guild.Alliance != null && guild.Alliance.IsMember( guild ) ) + { + guild.Alliance.AllianceMessage( 1070769, ((m_Other.Alliance != null) ? m_Other.Alliance.Name : m_Other.Name) ); // Guild Message: Your guild is now at war with ~1_GUILDNAME~ + guild.Alliance.InvalidateMemberProperties(); + } + else + { + guild.GuildMessage( 1070769, ((m_Other.Alliance != null) ? m_Other.Alliance.Name : m_Other.Name) ); // Guild Message: Your guild is now at war with ~1_GUILDNAME~ + guild.InvalidateMemberProperties(); + } + //Technically SHOULD say Your guild is now at war w/out any info, intentional diff. + + m_Other.PendingWars.Remove( otherWar ); + otherWar.WarBeginning = DateTime.Now; + m_Other.AcceptedWars.Add( otherWar ); + + if( m_Other.Alliance != null && m_Other.Alliance.IsMember( m_Other ) ) + { + m_Other.Alliance.AllianceMessage( 1070769, ((guild.Alliance != null) ? guild.Alliance.Name : guild.Name) ); // Guild Message: Your guild is now at war with ~1_GUILDNAME~ + m_Other.Alliance.InvalidateMemberProperties(); + } + else + { + m_Other.GuildMessage( 1070769, ((guild.Alliance != null) ? guild.Alliance.Name : guild.Name) ); // Guild Message: Your guild is now at war with ~1_GUILDNAME~ + m_Other.InvalidateMemberProperties(); + } + } + } + + break; + } + case 6: //Modify war terms + { + if( war != null && !war.WarRequester && activeWar == null ) + { + if( !playerRank.GetFlag( RankFlags.ControlWarStatus ) ) + { + pm.SendLocalizedMessage( 1063440 ); // You don't have permission to negotiate wars. + } + else + { + pm.SendGump( new WarDeclarationGump( pm, guild, m_Other ) ); + } + } + break; + } + case 7: //Dismiss war + { + if( war != null ) + { + if( !playerRank.GetFlag( RankFlags.ControlWarStatus ) ) + { + pm.SendLocalizedMessage( 1063440 ); // You don't have permission to negotiate wars. + } + else + { + //Dismiss the war + guild.PendingWars.Remove( war ); + m_Other.PendingWars.Remove( otherWar ); + pm.SendLocalizedMessage( 1070752 ); // The proposal has been updated. + //Messages to opposing guild? (Testing on OSI says no) + } + } + break; + } + case 8: //Surrender + { + if( !playerRank.GetFlag( RankFlags.ControlWarStatus ) ) + { + pm.SendLocalizedMessage( 1063440 ); // You don't have permission to negotiate wars. + } + else + { + if( activeWar != null ) + { + if( guild.Alliance != null && guild.Alliance.IsMember( guild ) ) + { + guild.Alliance.AllianceMessage( 1070740, ((m_Other.Alliance != null) ? m_Other.Alliance.Name : m_Other.Name) );// You have lost the war with ~1_val~. + guild.Alliance.InvalidateMemberProperties(); + } + else + { + guild.GuildMessage( 1070740, ((m_Other.Alliance != null) ? m_Other.Alliance.Name : m_Other.Name) );// You have lost the war with ~1_val~. + guild.InvalidateMemberProperties(); + } + + guild.AcceptedWars.Remove( activeWar ); + + if( m_Other.Alliance != null && m_Other.Alliance.IsMember( m_Other ) ) + { + m_Other.Alliance.AllianceMessage( 1070739, ((guild.Alliance != null) ? guild.Alliance.Name : guild.Name) );// You have won the war against ~1_val~! + m_Other.Alliance.InvalidateMemberProperties(); + } + else + { + m_Other.GuildMessage( 1070739, ((guild.Alliance != null) ? guild.Alliance.Name : guild.Name) );// You have won the war against ~1_val~! + m_Other.InvalidateMemberProperties(); + } + + m_Other.AcceptedWars.Remove( m_Other.FindActiveWar( guild ) ); + } + } + break; + } + case 1: //Declare War + { + if( war == null && activeWar == null ) + { + if( !playerRank.GetFlag( RankFlags.ControlWarStatus ) ) + { + pm.SendLocalizedMessage( 1063440 ); // You don't have permission to negotiate wars. + } + else if( alliance != null && alliance.Leader != guild ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", guild.Name, alliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + pm.SendLocalizedMessage( 1070707, alliance.Leader.Name ); // You need to negotiate via ~1_val~ instead. + } + else if( otherAlliance != null && otherAlliance.Leader != m_Other ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", m_Other.Name, otherAlliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + pm.SendLocalizedMessage( 1070707, otherAlliance.Leader.Name ); // You need to negotiate via ~1_val~ instead. + } + else + { + pm.SendGump( new WarDeclarationGump( pm, guild, m_Other ) ); + } + } + break; + } + #endregion + case 2: //Request Alliance + { + #region New alliance + if( alliance == null ) + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1070747 ); // You don't have permission to create an alliance. + } + else if( Faction.Find( guild.Leader ) != Faction.Find( m_Other.Leader ) ) + { + pm.SendLocalizedMessage( 1070758 ); // You cannot propose an alliance to a guild with a different faction allegiance. + } + else if( otherAlliance != null ) + { + if( otherAlliance.IsPendingMember( m_Other ) ) + pm.SendLocalizedMessage( 1063416, m_Other.Name ); // ~1_val~ is currently considering another alliance proposal. + else + pm.SendLocalizedMessage( 1063426, m_Other.Name ); // ~1_val~ already belongs to an alliance. + } + else if( m_Other.AcceptedWars.Count > 0 || m_Other.PendingWars.Count > 0 ) + { + pm.SendLocalizedMessage( 1063427, m_Other.Name ); // ~1_val~ is currently involved in a guild war. + } + else if( guild.AcceptedWars.Count > 0 || guild.PendingWars.Count > 0 ) + { + pm.SendLocalizedMessage( 1063427, guild.Name ); // ~1_val~ is currently involved in a guild war. + } + else + { + pm.SendLocalizedMessage( 1063439 ); // Enter a name for the new alliance: + pm.BeginPrompt( new PromptCallback( CreateAlliance_Callback ) ); + } + } + #endregion + #region Existing Alliance + else + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( alliance.Leader != guild ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", guild.Name, alliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + } + else if( otherAlliance != null ) + { + if( otherAlliance.IsPendingMember( m_Other ) ) + pm.SendLocalizedMessage( 1063416, m_Other.Name ); // ~1_val~ is currently considering another alliance proposal. + else + pm.SendLocalizedMessage( 1063426, m_Other.Name ); // ~1_val~ already belongs to an alliance. + } + else if( alliance.IsPendingMember( guild ) ) + { + pm.SendLocalizedMessage( 1063416, guild.Name ); // ~1_val~ is currently considering another alliance proposal. + } + else if( m_Other.AcceptedWars.Count > 0 || m_Other.PendingWars.Count > 0 ) + { + pm.SendLocalizedMessage( 1063427, m_Other.Name ); // ~1_val~ is currently involved in a guild war. + } + else if( guild.AcceptedWars.Count > 0 || guild.PendingWars.Count > 0 ) + { + pm.SendLocalizedMessage( 1063427, guild.Name ); // ~1_val~ is currently involved in a guild war. + } + else if( Faction.Find( guild.Leader ) != Faction.Find( m_Other.Leader ) ) + { + pm.SendLocalizedMessage( 1070758 ); // You cannot propose an alliance to a guild with a different faction allegiance. + } + else + { + pm.SendLocalizedMessage( 1070750, m_Other.Name ); // An invitation to join your alliance has been sent to ~1_val~. + + m_Other.GuildMessage( 1070780, guild.Name ); // ~1_val~ has proposed an alliance. + + m_Other.Alliance = alliance; //Calls addPendingGuild + //alliance.AddPendingGuild( m_Other ); + } + } + #endregion + break; + } + case 10: //Show Alliance Roster + { + if( alliance != null && alliance == otherAlliance ) + pm.SendGump( new AllianceInfo.AllianceRosterGump( pm, guild, alliance ) ); + + break; + } + case 11: //Leave Alliance + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( alliance != null && alliance.IsMember( guild ) ) + { + guild.Alliance = null; //Calls alliance.Removeguild +// alliance.RemoveGuild( guild ); + } + break; + } + case 12: //Remove Guild from alliance + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( alliance != null && alliance.Leader != guild ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", guild.Name, alliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + } + else if( alliance != null && alliance.IsMember( guild ) && alliance.IsMember( m_Other ) ) + { + m_Other.Alliance = null; + } + break; + } + case 13: //Promote to Alliance leader + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( alliance != null && alliance.Leader != guild ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", guild.Name, alliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + } + else if( alliance != null && alliance.IsMember( guild ) && alliance.IsMember( m_Other ) ) + { + pm.SendLocalizedMessage( 1063434, String.Format( "{0}\t{1}", m_Other.Name, alliance.Name ) ); // ~1_val~ is now the leader of ~2_val~. + + alliance.Leader = m_Other; + } + break; + } + case 14: //Withdraw Request + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( alliance != null && alliance.Leader == guild && alliance.IsPendingMember( m_Other ) ) + { + m_Other.Alliance = null; + pm.SendLocalizedMessage( 1070752 ); // The proposal has been updated. + } + break; + } + case 15: //Deny Alliance Request + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( alliance != null && otherAlliance != null && alliance.Leader == m_Other && otherAlliance.IsPendingMember( guild ) ) + { + pm.SendLocalizedMessage( 1070752 ); // The proposal has been updated. + //m_Other.GuildMessage( 1070782 ); // ~1_val~ has responded to your proposal. //Per OSI commented out. + + guild.Alliance = null; + } + break; + } + case 16: //Accept Alliance Request + { + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1063436 ); // You don't have permission to negotiate an alliance. + } + else if( otherAlliance != null && otherAlliance.Leader == m_Other && otherAlliance.IsPendingMember( guild ) ) + { + pm.SendLocalizedMessage( 1070752 ); // The proposal has been updated. + + otherAlliance.TurnToMember( m_Other ); //No need to verify it's in the guild or already a member, the function does this + + otherAlliance.TurnToMember( guild ); + } + break; + } + } + } + + public void CreateAlliance_Callback( Mobile from, string text ) + { + PlayerMobile pm = from as PlayerMobile; + + + AllianceInfo alliance = guild.Alliance; + AllianceInfo otherAlliance = m_Other.Alliance; + + if( !IsMember( from, guild ) || alliance != null ) + return; + + + RankDefinition playerRank = pm.GuildRank; + + + if( !playerRank.GetFlag( RankFlags.AllianceControl ) ) + { + pm.SendLocalizedMessage( 1070747 ); // You don't have permission to create an alliance. + } + else if( Faction.Find( guild.Leader ) != Faction.Find( m_Other.Leader ) ) + { + //Notes about this: OSI only cares/checks when proposing, you can change your faction all you want later. + pm.SendLocalizedMessage( 1070758 ); // You cannot propose an alliance to a guild with a different faction allegiance. + } + else if( otherAlliance != null ) + { + if( otherAlliance.IsPendingMember( m_Other ) ) + pm.SendLocalizedMessage( 1063416, m_Other.Name ); // ~1_val~ is currently considering another alliance proposal. + else + pm.SendLocalizedMessage( 1063426, m_Other.Name ); // ~1_val~ already belongs to an alliance. + } + else if( m_Other.AcceptedWars.Count > 0 || m_Other.PendingWars.Count > 0 ) + { + pm.SendLocalizedMessage( 1063427, m_Other.Name ); // ~1_val~ is currently involved in a guild war. + } + else if( guild.AcceptedWars.Count > 0 || guild.PendingWars.Count > 0 ) + { + pm.SendLocalizedMessage( 1063427, guild.Name ); // ~1_val~ is currently involved in a guild war. + } + else + { + string name = Utility.FixHtml( text.Trim() ); + + if( !BaseGuildGump.CheckProfanity( name ) ) + pm.SendLocalizedMessage( 1070886 ); // That alliance name is not allowed. + else if( name.Length > Guild.NameLimit ) + pm.SendLocalizedMessage( 1070887, Guild.NameLimit.ToString() ); // An alliance name cannot exceed ~1_val~ characters in length. + else if( AllianceInfo.Alliances.Contains( name.ToLower() ) ) + pm.SendLocalizedMessage( 1063428 ); // That alliance name is not available. + else + { + pm.SendLocalizedMessage( 1070750, m_Other.Name ); // An invitation to join your alliance has been sent to ~1_val~. + + m_Other.GuildMessage( 1070780, guild.Name ); // ~1_val~ has proposed an alliance. + + new AllianceInfo( guild, name, m_Other ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/New Guild System/War Declaration gump.cs b/Scripts/Gumps/Guilds/New Guild System/War Declaration gump.cs new file mode 100644 index 000000000..d08deae4f --- /dev/null +++ b/Scripts/Gumps/Guilds/New Guild System/War Declaration gump.cs @@ -0,0 +1,130 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Guilds +{ + public class WarDeclarationGump : BaseGuildGump + { + private Guild m_Other; + + public WarDeclarationGump( PlayerMobile pm, Guild g, Guild otherGuild ) : base( pm, g ) + { + m_Other = otherGuild; + WarDeclaration war = g.FindPendingWar( otherGuild ); + + AddPage( 0 ); + + AddBackground( 0, 0, 500, 340, 0x24AE ); + AddBackground( 65, 50, 370, 30, 0x2486 ); + AddHtmlLocalized( 75, 55, 370, 26, 1062979, 0x3C00, false, false ); //
Declaration of War
+ AddImage( 410, 45, 0x232C ); + AddHtmlLocalized( 65, 95, 200, 20, 1063009, 0x14AF, false, false ); // Duration of War + AddHtmlLocalized( 65, 120, 400, 20, 1063010, 0x0, false, false ); // Enter the number of hours the war will last. + AddBackground( 65, 150, 40, 30, 0x2486 ); + AddTextEntry( 70, 154, 50, 30, 0x481, 10, (war != null) ? war.WarLength.Hours.ToString() : "0" ); + AddHtmlLocalized( 65, 195, 200, 20, 1063011, 0x14AF, false, false ); // Victory Condition + AddHtmlLocalized( 65, 220, 400, 20, 1063012, 0x0, false, false ); // Enter the winning number of kills. + AddBackground( 65, 250, 40, 30, 0x2486 ); + AddTextEntry( 70, 254, 50, 30, 0x481, 11, (war != null) ? war.MaxKills.ToString() : "0" ); + AddBackground( 190, 270, 130, 26, 0x2486 ); + AddButton( 195, 275, 0x845, 0x846, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 220, 273, 90, 26, 1006045, 0x0, false, false ); // Cancel + AddBackground( 330, 270, 130, 26, 0x2486 ); + AddButton( 335, 275, 0x845, 0x846, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 360, 273, 90, 26, 1062989, 0x5000, false, false ); // Declare War! + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + + PlayerMobile pm = sender.Mobile as PlayerMobile; + + if( !IsMember( pm, guild ) ) + return; + + RankDefinition playerRank = pm.GuildRank; + + switch( info.ButtonID ) + { + case 1: + { + AllianceInfo alliance = guild.Alliance; + AllianceInfo otherAlliance = m_Other.Alliance; + + if( !playerRank.GetFlag( RankFlags.ControlWarStatus ) ) + { + pm.SendLocalizedMessage( 1063440 ); // You don't have permission to negotiate wars. + } + else if( alliance != null && alliance.Leader != guild ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", guild.Name, alliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + pm.SendLocalizedMessage( 1070707, alliance.Leader.Name ); // You need to negotiate via ~1_val~ instead. + } + else if( otherAlliance != null && otherAlliance.Leader != m_Other ) + { + pm.SendLocalizedMessage( 1063239, String.Format( "{0}\t{1}", m_Other.Name, otherAlliance.Name ) ); // ~1_val~ is not the leader of the ~2_val~ alliance. + pm.SendLocalizedMessage( 1070707, otherAlliance.Leader.Name ); // You need to negotiate via ~1_val~ instead. + } + else + { + WarDeclaration activeWar = guild.FindActiveWar( m_Other ); + + if( activeWar == null ) + { + WarDeclaration war = guild.FindPendingWar( m_Other ); + WarDeclaration otherWar = m_Other.FindPendingWar( guild ); + + //Note: OSI differs from what it says on website. unlimited war = 0 kills/ 0 hrs. Not > 999. (sidenote: they both cap at 65535, 7.5 years, but, still.) + TextRelay tKills = info.GetTextEntry( 11 ); + TextRelay tWarLength = info.GetTextEntry( 10 ); + + int maxKills = (tKills == null)? 0 : Math.Max( Math.Min( Utility.ToInt32( info.GetTextEntry( 11 ).Text ), 0xFFFF ), 0 ); + TimeSpan warLength = TimeSpan.FromHours( (tWarLength == null) ? 0 : Math.Max( Math.Min( Utility.ToInt32( info.GetTextEntry( 10 ).Text ), 0xFFFF ), 0 ) ); + + if( war != null ) + { + war.MaxKills = maxKills; + war.WarLength = warLength; + war.WarRequester = true; + } + else + { + guild.PendingWars.Add( new WarDeclaration( guild, m_Other, maxKills, warLength, true ) ); + } + + if( otherWar != null ) + { + otherWar.MaxKills = maxKills; + otherWar.WarLength = warLength; + otherWar.WarRequester = false; + } + else + { + m_Other.PendingWars.Add( new WarDeclaration( m_Other, guild, maxKills, warLength, false ) ); + } + + if( war != null ) + { + pm.SendLocalizedMessage( 1070752 ); // The proposal has been updated. + //m_Other.GuildMessage( 1070782 ); // ~1_val~ has responded to your proposal. + } + else + m_Other.GuildMessage( 1070781, ((guild.Alliance != null ) ? guild.Alliance.Name : guild.Name ) ); // ~1_val~ has proposed a war. + + pm.SendLocalizedMessage( 1070751, ((m_Other.Alliance != null ) ? m_Other.Alliance.Name : m_Other.Name ) ); // War proposal has been sent to ~1_val~. + } + } + break; + } + default: + { + pm.SendGump( new OtherGuildInfo( pm, guild, m_Other ) ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Guilds/RecruitTarget.cs b/Scripts/Gumps/Guilds/RecruitTarget.cs new file mode 100644 index 000000000..12b69db2b --- /dev/null +++ b/Scripts/Gumps/Guilds/RecruitTarget.cs @@ -0,0 +1,95 @@ +using System; +using Server; +using Server.Guilds; +using Server.Targeting; +using Server.Factions; + +namespace Server.Gumps +{ + public class GuildRecruitTarget : Target + { + private Mobile m_Mobile; + private Guild m_Guild; + + public GuildRecruitTarget( Mobile m, Guild guild ) : base( 10, false, TargetFlags.None ) + { + m_Mobile = m; + m_Guild = guild; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + if ( targeted is Mobile ) + { + Mobile m = (Mobile)targeted; + + PlayerState guildState = PlayerState.Find( m_Guild.Leader ); + PlayerState targetState = PlayerState.Find( m ); + + Faction guildFaction = ( guildState == null ? null : guildState.Faction ); + Faction targetFaction = ( targetState == null ? null : targetState.Faction ); + + if ( !m.Player ) + { + m_Mobile.SendLocalizedMessage( 501161 ); // You may only recruit players into the guild. + } + else if ( !m.Alive ) + { + m_Mobile.SendLocalizedMessage( 501162 ); // Only the living may be recruited. + } + else if ( m_Guild.IsMember( m ) ) + { + m_Mobile.SendLocalizedMessage( 501163 ); // They are already a guildmember! + } + else if ( m_Guild.Candidates.Contains( m ) ) + { + m_Mobile.SendLocalizedMessage( 501164 ); // They are already a candidate. + } + else if ( m_Guild.Accepted.Contains( m ) ) + { + m_Mobile.SendLocalizedMessage( 501165 ); // They have already been accepted for membership, and merely need to use the Guildstone to gain full membership. + } + else if ( m.Guild != null ) + { + m_Mobile.SendLocalizedMessage( 501166 ); // You can only recruit candidates who are not already in a guild. + } + #region Factions + else if ( guildFaction != targetFaction ) + { + if ( guildFaction == null ) + m_Mobile.SendLocalizedMessage( 1013027 ); // That player cannot join a non-faction guild. + else if ( targetFaction == null ) + m_Mobile.SendLocalizedMessage( 1013026 ); // That player must be in a faction before joining this guild. + else + m_Mobile.SendLocalizedMessage( 1013028 ); // That person has a different faction affiliation. + } + else if ( targetState != null && targetState.IsLeaving ) + { + // OSI does this quite strangely, so we'll just do it this way + m_Mobile.SendMessage( "That person is quitting their faction and so you may not recruit them." ); + } + #endregion + else if ( m_Mobile.AccessLevel >= AccessLevel.GameMaster || m_Guild.Leader == m_Mobile ) + { + m_Guild.Accepted.Add( m ); + } + else + { + m_Guild.Candidates.Add( m ); + } + } + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( GuildGump.BadMember( m_Mobile, m_Guild ) ) + return; + + GuildGump.EnsureClosed( m_Mobile ); + m_Mobile.SendGump( new GuildGump( m_Mobile, m_Guild ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/HouseDemolishGump.cs b/Scripts/Gumps/HouseDemolishGump.cs new file mode 100644 index 000000000..7d8bde4ac --- /dev/null +++ b/Scripts/Gumps/HouseDemolishGump.cs @@ -0,0 +1,138 @@ +using System; +using Server; +using Server.Items; +using Server.Multis; +using Server.Multis.Deeds; +using Server.Network; +using Server.Mobiles; + +namespace Server.Gumps +{ + public class HouseDemolishGump : Gump + { + private Mobile m_Mobile; + private BaseHouse m_House; + + public HouseDemolishGump( Mobile mobile, BaseHouse house ) : base( 110, 100 ) + { + m_Mobile = mobile; + m_House = house; + + mobile.CloseGump( typeof( HouseDemolishGump ) ); + + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 280, 5054 ); + + AddImageTiled( 10, 10, 400, 20, 2624 ); + AddAlphaRegion( 10, 10, 400, 20 ); + + AddHtmlLocalized( 10, 10, 400, 20, 1060635, 30720, false, false ); //
WARNING
+ + AddImageTiled( 10, 40, 400, 200, 2624 ); + AddAlphaRegion( 10, 40, 400, 200 ); + + AddHtmlLocalized( 10, 40, 400, 200, 1061795, 32512, false, true ); /* You are about to demolish your house. + * You will be refunded the house's value directly to your bank box. + * All items in the house will remain behind and can be freely picked up by anyone. + * Once the house is demolished, anyone can attempt to place a new house on the vacant land. + * This action will not un-condemn any other houses on your account, nor will it end your 7-day waiting period (if it applies to you). + * Are you sure you wish to continue? + */ + + AddImageTiled( 10, 250, 400, 20, 2624 ); + AddAlphaRegion( 10, 250, 400, 20 ); + + AddButton( 10, 250, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40, 250, 170, 20, 1011036, 32767, false, false ); // OKAY + + AddButton( 210, 250, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 240, 250, 170, 20, 1011012, 32767, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 1 && !m_House.Deleted ) + { + if ( m_House.IsOwner( m_Mobile ) ) + { + if ( m_House.MovingCrate != null || m_House.InternalizedVendors.Count > 0 ) + { + return; + } + else if( !Guilds.Guild.NewGuildSystem && m_House.FindGuildstone() != null ) + { + m_Mobile.SendLocalizedMessage( 501389 ); // You cannot redeed a house with a guildstone inside. + return; + } + /*else if ( m_House.PlayerVendors.Count > 0 ) + { + m_Mobile.SendLocalizedMessage( 503236 ); // You need to collect your vendor's belongings before moving. + return; + }*/ + else if ( m_House.HasRentedVendors && m_House.VendorInventories.Count > 0 ) + { + m_Mobile.SendLocalizedMessage( 1062679 ); // You cannot do that that while you still have contract vendors or unclaimed contract vendor inventory in your house. + return; + } + else if ( m_House.HasRentedVendors ) + { + m_Mobile.SendLocalizedMessage( 1062680 ); // You cannot do that that while you still have contract vendors in your house. + return; + } + else if ( m_House.VendorInventories.Count > 0 ) + { + m_Mobile.SendLocalizedMessage( 1062681 ); // You cannot do that that while you still have unclaimed contract vendor inventory in your house. + return; + } + + Item toGive = null; + + if ( m_House.IsAosRules ) + { + if ( m_House.Price > 0 ) + toGive = new BankCheck( m_House.Price ); + else + toGive = m_House.GetDeed(); + } + else + { + toGive = m_House.GetDeed(); + + if ( toGive == null && m_House.Price > 0 ) + toGive = new BankCheck( m_House.Price ); + } + + if ( toGive != null ) + { + BankBox box = m_Mobile.BankBox; + + if ( box != null && box.TryDropItem( m_Mobile, toGive, false ) ) + { + if ( toGive is BankCheck ) + m_Mobile.SendLocalizedMessage( 1060397, ((BankCheck)toGive).Worth.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + + m_House.RemoveKeys( m_Mobile ); + m_House.Delete(); + } + else + { + toGive.Delete(); + m_Mobile.SendLocalizedMessage( 500390 ); // Your bank box is full. + } + } + else + { + m_Mobile.SendMessage( "Unable to refund house." ); + } + } + else + { + m_Mobile.SendLocalizedMessage( 501320 ); // Only the house owner may do this. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/HouseGump.cs b/Scripts/Gumps/HouseGump.cs new file mode 100644 index 000000000..a1fa4f30e --- /dev/null +++ b/Scripts/Gumps/HouseGump.cs @@ -0,0 +1,755 @@ +using System; +using System.Reflection; +using System.Collections; +using Server.Network; +using Server.Prompts; +using Server.Multis; +using Server.Multis.Deeds; +using Server.Items; + +namespace Server.Gumps +{ + public class HouseListGump : Gump + { + private BaseHouse m_House; + + public HouseListGump( int number, ArrayList list, BaseHouse house, bool accountOf ) : base( 20, 30 ) + { + if ( house.Deleted ) + return; + + m_House = house; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 430, 5054 ); + AddBackground( 10, 10, 400, 410, 3000 ); + + AddButton( 20, 388, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 388, 300, 20, 1011104, false, false ); // Return to previous menu + + AddHtmlLocalized( 20, 20, 350, 20, number, false, false ); + + if ( list != null ) + { + for ( int i = 0; i < list.Count; ++i ) + { + if ( (i % 16) == 0 ) + { + if ( i != 0 ) + { + // Next button + AddButton( 370, 20, 4005, 4007, 0, GumpButtonType.Page, (i / 16) + 1 ); + } + + AddPage( (i / 16) + 1 ); + + if ( i != 0 ) + { + // Previous button + AddButton( 340, 20, 4014, 4016, 0, GumpButtonType.Page, i / 16 ); + } + } + + Mobile m = (Mobile)list[i]; + + string name; + + if ( m == null || (name = m.Name) == null || (name = name.Trim()).Length <= 0 ) + continue; + + AddLabel( 55, 55 + ((i % 16) * 20), 0, accountOf && m.Player && m.Account != null ? String.Format( "Account of {0}", name ) : name ); + } + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( m_House.Deleted ) + return; + + Mobile from = state.Mobile; + + from.SendGump( new HouseGump( from, m_House ) ); + } + } + + public class HouseRemoveGump : Gump + { + private BaseHouse m_House; + private ArrayList m_List, m_Copy; + private int m_Number; + private bool m_AccountOf; + + public HouseRemoveGump( int number, ArrayList list, BaseHouse house, bool accountOf ) : base( 20, 30 ) + { + if ( house.Deleted ) + return; + + m_House = house; + m_List = list; + m_Number = number; + m_AccountOf = accountOf; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 430, 5054 ); + AddBackground( 10, 10, 400, 410, 3000 ); + + AddButton( 20, 388, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 388, 300, 20, 1011104, false, false ); // Return to previous menu + + AddButton( 20, 365, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 365, 300, 20, 1011270, false, false ); // Remove now! + + AddHtmlLocalized( 20, 20, 350, 20, number, false, false ); + + if ( list != null ) + { + m_Copy = new ArrayList( list ); + + for ( int i = 0; i < list.Count; ++i ) + { + if ( (i % 15) == 0 ) + { + if ( i != 0 ) + { + // Next button + AddButton( 370, 20, 4005, 4007, 0, GumpButtonType.Page, (i / 15) + 1 ); + } + + AddPage( (i / 15) + 1 ); + + if ( i != 0 ) + { + // Previous button + AddButton( 340, 20, 4014, 4016, 0, GumpButtonType.Page, i / 15 ); + } + } + + Mobile m = (Mobile)list[i]; + + string name; + + if ( m == null || (name = m.Name) == null || (name = name.Trim()).Length <= 0 ) + continue; + + AddCheck( 34, 52 + ((i % 15) * 20), 0xD2, 0xD3, false, i ); + AddLabel( 55, 52 + ((i % 15) * 20), 0, accountOf && m.Player && m.Account != null ? String.Format( "Account of {0}", name ) : name ); + } + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( m_House.Deleted ) + return; + + Mobile from = state.Mobile; + + if ( m_List != null && info.ButtonID == 1 ) // Remove now + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + for ( int i = 0; i < switches.Length; ++i ) + { + int index = switches[i]; + + if ( index >= 0 && index < m_Copy.Count ) + m_List.Remove( m_Copy[index] ); + } + + if ( m_List.Count > 0 ) + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseRemoveGump( m_Number, m_List, m_House, m_AccountOf ) ); + return; + } + } + } + + from.SendGump( new HouseGump( from, m_House ) ); + } + } + + public class HouseGump : Gump + { + private BaseHouse m_House; + + private ArrayList Wrap( string value ) + { + if ( value == null || (value = value.Trim()).Length <= 0 ) + return null; + + string[] values = value.Split( ' ' ); + ArrayList list = new ArrayList(); + string current = ""; + + for ( int i = 0; i < values.Length; ++i ) + { + string val = values[i]; + + string v = current.Length == 0 ? val : current + ' ' + val; + + if ( v.Length < 10 ) + { + current = v; + } + else if ( v.Length == 10 ) + { + list.Add( v ); + + if ( list.Count == 6 ) + return list; + + current = ""; + } + else if ( val.Length <= 10 ) + { + list.Add( current ); + + if ( list.Count == 6 ) + return list; + + current = val; + } + else + { + while ( v.Length >= 10 ) + { + list.Add( v.Substring( 0, 10 ) ); + + if ( list.Count == 6 ) + return list; + + v = v.Substring( 10 ); + } + + current = v; + } + } + + if ( current.Length > 0 ) + list.Add( current ); + + return list; + } + + public HouseGump( Mobile from, BaseHouse house ) : base( 20, 30 ) + { + if ( house.Deleted ) + return; + + m_House = house; + + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + + bool isCombatRestricted = house.IsCombatRestricted( from ); + + bool isOwner = m_House.IsOwner( from ); + bool isCoOwner = isOwner || m_House.IsCoOwner( from ); + bool isFriend = isCoOwner || m_House.IsFriend( from ); + + if ( isCombatRestricted ) + isFriend = isCoOwner = isOwner = false; + + AddPage( 0 ); + + if ( isFriend ) + { + AddBackground( 0, 0, 420, 430, 5054 ); + AddBackground( 10, 10, 400,410, 3000 ); + } + + AddImage( 130, 0, 100 ); + + if ( m_House.Sign != null ) + { + ArrayList lines = Wrap( m_House.Sign.GetName() ); + + if ( lines != null ) + { + for ( int i = 0, y = (101 - (lines.Count * 14)) / 2; i < lines.Count; ++i, y += 14 ) + { + string s = (string)lines[i]; + + AddLabel( 130 + ((143 - (s.Length * 8)) / 2), y, 0, s ); + } + } + } + + if ( !isFriend ) + return; + + AddHtmlLocalized( 55, 103, 75, 20, 1011233, false, false ); // INFO + AddButton( 20, 103, 4005, 4007, 0, GumpButtonType.Page, 1 ); + + AddHtmlLocalized( 170, 103, 75, 20, 1011234, false, false ); // FRIENDS + AddButton( 135, 103, 4005, 4007, 0, GumpButtonType.Page, 2 ); + + AddHtmlLocalized( 295, 103, 75, 20, 1011235, false, false ); // OPTIONS + AddButton( 260, 103, 4005, 4007, 0, GumpButtonType.Page, 3 ); + + AddHtmlLocalized( 295, 390, 75, 20, 1011441, false, false ); // EXIT + AddButton( 260, 390, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 390, 200, 20, 1011236, false, false ); // Change this house's name! + AddButton( 20, 390, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + // Info page + AddPage( 1 ); + + AddHtmlLocalized( 20, 135, 100, 20, 1011242, false, false ); // Owned by: + AddHtml( 120, 135, 100, 20, GetOwnerName(), false, false ); + + AddHtmlLocalized( 20, 170, 275, 20, 1011237, false, false ); // Number of locked down items: + AddHtml( 320, 170, 50, 20, m_House.LockDownCount.ToString(), false, false ); + + AddHtmlLocalized( 20, 190, 275, 20, 1011238, false, false ); // Maximum locked down items: + AddHtml( 320, 190, 50, 20, m_House.MaxLockDowns.ToString(), false, false ); + + AddHtmlLocalized( 20, 210, 275, 20, 1011239, false, false ); // Number of secure containers: + AddHtml( 320, 210, 50, 20, m_House.SecureCount.ToString(), false, false ); + + AddHtmlLocalized( 20, 230, 275, 20, 1011240, false, false ); // Maximum number of secure containers: + AddHtml( 320, 230, 50, 20, m_House.MaxSecures.ToString(), false, false ); + + AddHtmlLocalized( 20, 260, 400, 20, 1018032, false, false ); // This house is properly placed. + AddHtmlLocalized( 20, 280, 400, 20, 1018035, false, false ); // This house is of modern design. + + if ( m_House.Public ) + { + // TODO: Validate exact placement + AddHtmlLocalized( 20, 305, 275, 20, 1011241, false, false ); // Number of visits this building has had + AddHtml( 320, 305, 50, 20, m_House.Visits.ToString(), false, false ); + } + + // Friends page + AddPage( 2 ); + + AddHtmlLocalized( 45, 130, 150, 20, 1011266, false, false ); // List of co-owners + AddButton( 20, 130, 2714, 2715, 2, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 45, 150, 150, 20, 1011267, false, false ); // Add a co-owner + AddButton( 20, 150, 2714, 2715, 3, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 45, 170, 150, 20, 1018036, false, false ); // Remove a co-owner + AddButton( 20, 170, 2714, 2715, 4, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 45, 190, 150, 20, 1011268, false, false ); // Clear co-owner list + AddButton( 20, 190, 2714, 2715, 5, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 225, 130, 155, 20, 1011243, false, false ); // List of Friends + AddButton( 200, 130, 2714, 2715, 6, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 225, 150, 155, 20, 1011244, false, false ); // Add a Friend + AddButton( 200, 150, 2714, 2715, 7, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 225, 170, 155, 20, 1018037, false, false ); // Remove a Friend + AddButton( 200, 170, 2714, 2715, 8, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 225, 190, 155, 20, 1011245, false, false ); // Clear Friends list + AddButton( 200, 190, 2714, 2715, 9, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 120, 215, 280, 20, 1011258, false, false ); // Ban someone from the house + AddButton( 95, 215, 2714, 2715, 10, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 120, 235, 280, 20, 1011259, false, false ); // Eject someone from the house + AddButton( 95, 235, 2714, 2715, 11, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 120, 255, 280, 20, 1011260, false, false ); // View a list of banned people + AddButton( 95, 255, 2714, 2715, 12, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 120, 275, 280, 20, 1011261, false, false ); // Lift a ban + AddButton( 95, 275, 2714, 2715, 13, GumpButtonType.Reply, 0 ); + + // Options page + AddPage( 3 ); + + AddHtmlLocalized( 45, 150, 355, 30, 1011248, false, false ); // Transfer ownership of the house + AddButton( 20, 150, 2714, 2715, 14, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 45, 180, 355, 30, 1011249, false, false ); // Demolish house and get deed back + AddButton( 20, 180, 2714, 2715, 15, GumpButtonType.Reply, 0 ); + + if ( !m_House.Public ) + { + AddHtmlLocalized( 45, 210, 355, 30, 1011247, false, false ); // Change the house locks + AddButton( 20, 210, 2714, 2715, 16, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 45, 240, 350, 90, 1011253, false, false ); // Declare this building to be public. This will make your front door unlockable. + AddButton( 20, 240, 2714, 2715, 17, GumpButtonType.Reply, 0 ); + } + else + { + //AddHtmlLocalized( 45, 280, 350, 30, 1011250, false, false ); // Change the sign type + AddHtmlLocalized( 45, 210, 350, 30, 1011250, false, false ); // Change the sign type + AddButton( 20, 210, 2714, 2715, 0, GumpButtonType.Page, 4 ); + + AddHtmlLocalized( 45, 240, 350, 30, 1011252, false, false ); // Declare this building to be private. + AddButton( 20, 240, 2714, 2715, 17, GumpButtonType.Reply, 0 ); + + // Change the sign type + AddPage( 4 ); + + for ( int i = 0; i < 24; ++i ) + { + AddRadio( 53 + ((i / 4) * 50), 137 + ((i % 4) * 35), 210, 211, false, i + 1 ); + AddItem( 60 + ((i / 4) * 50), 130 + ((i % 4) * 35), 2980 + (i * 2) ); + } + + AddHtmlLocalized( 200, 305, 129, 20, 1011254, false, false ); // Guild sign choices + AddButton( 350, 305, 252, 253, 0, GumpButtonType.Page, 5 ); + + AddHtmlLocalized( 200, 340, 355, 30, 1011277, false, false ); // Okay that is fine. + AddButton( 350, 340, 4005, 4007, 18, GumpButtonType.Reply, 0 ); + + AddPage( 5 ); + + for ( int i = 0; i < 29; ++i ) + { + AddRadio( 53 + ((i / 5) * 50), 137 + ((i % 5) * 35), 210, 211, false, i + 25 ); + AddItem( 60 + ((i / 5) * 50), 130 + ((i % 5) * 35), 3028 + (i * 2) ); + } + + AddHtmlLocalized( 200, 305, 129, 20, 1011255, false, false ); // Shop sign choices + AddButton( 350, 305, 250, 251, 0, GumpButtonType.Page, 4 ); + + AddHtmlLocalized( 200, 340, 355, 30, 1011277, false, false ); // Okay that is fine. + AddButton( 350, 340, 4005, 4007, 18, GumpButtonType.Reply, 0 ); + } + } + + private string GetOwnerName() + { + Mobile m = m_House.Owner; + + if ( m == null ) + return "(unowned)"; + + string name; + + if ( (name = m.Name) == null || (name = name.Trim()).Length <= 0 ) + name = "(no name)"; + + return name; + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_House.Deleted ) + return; + + Mobile from = sender.Mobile; + + bool isCombatRestricted = m_House.IsCombatRestricted( from ); + + bool isOwner = m_House.IsOwner( from ); + bool isCoOwner = isOwner || m_House.IsCoOwner( from ); + bool isFriend = isCoOwner || m_House.IsFriend( from ); + + if ( isCombatRestricted ) + isFriend = isCoOwner = isOwner = false; + + if ( !isFriend || !from.Alive ) + return; + + Item sign = m_House.Sign; + + if ( sign == null || from.Map != sign.Map || !from.InRange( sign.GetWorldLocation(), 18 ) ) + return; + + switch ( info.ButtonID ) + { + case 1: // Rename sign + { + from.Prompt = new RenamePrompt( m_House ); + from.SendLocalizedMessage( 501302 ); // What dost thou wish the sign to say? + + break; + } + case 2: // List of co-owners + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseListGump( 1011275, m_House.CoOwners, m_House, false ) ); + + break; + } + case 3: // Add co-owner + { + if ( isOwner ) + { + from.SendLocalizedMessage( 501328 ); // Target the person you wish to name a co-owner of your household. + from.Target = new CoOwnerTarget( true, m_House ); + } + else + { + from.SendLocalizedMessage( 501327 ); // Only the house owner may add Co-owners. + } + + break; + } + case 4: // Remove co-owner + { + if ( isOwner ) + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseRemoveGump( 1011274, m_House.CoOwners, m_House, false ) ); + } + else + { + from.SendLocalizedMessage( 501329 ); // Only the house owner may remove co-owners. + } + + break; + } + case 5: // Clear co-owners + { + if ( isOwner ) + { + if ( m_House.CoOwners != null ) + m_House.CoOwners.Clear(); + + from.SendLocalizedMessage( 501333 ); // All co-owners have been removed from this house. + } + else + { + from.SendLocalizedMessage( 501330 ); // Only the house owner may remove co-owners. + } + + break; + } + case 6: // List friends + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseListGump( 1011273, m_House.Friends, m_House, false ) ); + + break; + } + case 7: // Add friend + { + if ( isCoOwner ) + { + from.SendLocalizedMessage( 501317 ); // Target the person you wish to name a friend of your household. + from.Target = new HouseFriendTarget( true, m_House ); + } + else + { + from.SendLocalizedMessage( 501316 ); // Only the house owner may add friends. + } + + break; + } + case 8: // Remove friend + { + if ( isCoOwner ) + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseRemoveGump( 1011272, m_House.Friends, m_House, false ) ); + } + else + { + from.SendLocalizedMessage( 501318 ); // Only the house owner may remove friends. + } + + break; + } + case 9: // Clear friends + { + if ( isCoOwner ) + { + if ( m_House.Friends != null ) + m_House.Friends.Clear(); + + from.SendLocalizedMessage( 501332 ); // All friends have been removed from this house. + } + else + { + from.SendLocalizedMessage( 501319 ); // Only the house owner may remove friends. + } + + break; + } + case 10: // Ban + { + from.SendLocalizedMessage( 501325 ); // Target the individual to ban from this house. + from.Target = new HouseBanTarget( true, m_House ); + + break; + } + case 11: // Eject + { + from.SendLocalizedMessage( 501326 ); // Target the individual to eject from this house. + from.Target = new HouseKickTarget( m_House ); + + break; + } + case 12: // List bans + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseListGump( 1011271, m_House.Bans, m_House, true ) ); + + break; + } + case 13: // Remove ban + { + from.CloseGump( typeof( HouseGump ) ); + from.CloseGump( typeof( HouseListGump ) ); + from.CloseGump( typeof( HouseRemoveGump ) ); + from.SendGump( new HouseRemoveGump( 1011269, m_House.Bans, m_House, true ) ); + + break; + } + case 14: // Transfer ownership + { + if ( isOwner ) + { + from.SendLocalizedMessage( 501309 ); // Target the person to whom you wish to give this house. + from.Target = new HouseOwnerTarget( m_House ); + } + else + { + from.SendLocalizedMessage( 501310 ); // Only the house owner may do this. + } + + break; + } + case 15: // Demolish house + { + if ( isOwner ) + { + if ( !Guilds.Guild.NewGuildSystem && m_House.FindGuildstone() != null ) + { + from.SendLocalizedMessage( 501389 ); // You cannot redeed a house with a guildstone inside. + } + else + { + from.CloseGump( typeof( HouseDemolishGump ) ); + from.SendGump( new HouseDemolishGump( from, m_House ) ); + } + } + else + { + from.SendLocalizedMessage( 501320 ); // Only the house owner may do this. + } + + break; + } + case 16: // Change locks + { + if ( m_House.Public ) + { + from.SendLocalizedMessage( 501669 );// Public houses are always unlocked. + } + else + { + if ( isOwner ) + { + m_House.RemoveKeys( from ); + m_House.ChangeLocks( from ); + + from.SendLocalizedMessage( 501306 ); // The locks on your front door have been changed, and new master keys have been placed in your bank and your backpack. + } + else + { + from.SendLocalizedMessage( 501303 ); // Only the house owner may change the house locks. + } + } + + break; + } + case 17: // Declare public/private + { + if ( isOwner ) + { + if ( m_House.Public && m_House.PlayerVendors.Count > 0 ) + { + from.SendLocalizedMessage( 501887 ); // You have vendors working out of this building. It cannot be declared private until there are no vendors in place. + break; + } + + m_House.Public = !m_House.Public; + if ( !m_House.Public ) + { + m_House.ChangeLocks( from ); + + from.SendLocalizedMessage( 501888 ); // This house is now private. + from.SendLocalizedMessage( 501306 ); // The locks on your front door have been changed, and new master keys have been placed in your bank and your backpack. + } + else + { + m_House.RemoveKeys( from ); + m_House.RemoveLocks(); + from.SendLocalizedMessage( 501886 );//This house is now public. Friends of the house my now have vendors working out of this building. + } + } + else + { + from.SendLocalizedMessage( 501307 ); // Only the house owner may do this. + } + + break; + } + case 18: // Change type + { + if ( isOwner ) + { + if ( m_House.Public && info.Switches.Length > 0 ) + { + int index = info.Switches[0] - 1; + + if ( index >= 0 && index < 53 ) + m_House.ChangeSignType( 2980 + (index * 2) ); + } + } + else + { + from.SendLocalizedMessage( 501307 ); // Only the house owner may do this. + } + + break; + } + } + } + } +} + +namespace Server.Prompts +{ + public class RenamePrompt : Prompt + { + private BaseHouse m_House; + + public RenamePrompt( BaseHouse house ) + { + m_House = house; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_House.IsFriend( from ) ) + { + if ( m_House.Sign != null ) + m_House.Sign.Name = text; + + from.SendMessage( "Sign changed." ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/HouseGumpAOS.cs b/Scripts/Gumps/HouseGumpAOS.cs new file mode 100644 index 000000000..0e13921b1 --- /dev/null +++ b/Scripts/Gumps/HouseGumpAOS.cs @@ -0,0 +1,1431 @@ +using System; +using System.Reflection; +using System.Collections; +using Server.Network; +using Server.Prompts; +using Server.Mobiles; +using Server.Multis; +using Server.Multis.Deeds; +using Server.Items; +using System.Collections.Generic; + +namespace Server.Gumps +{ + public enum HouseGumpPageAOS + { + Information, + Security, + Storage, + Customize, + Ownership, + ChangeHanger, + ChangeFoundation, + ChangeSign, + RemoveCoOwner, + ListCoOwner, + RemoveFriend, + ListFriend, + RemoveBan, + ListBan, + RemoveAccess, + ListAccess, + ChangePost, + Vendors + } + + public class HouseGumpAOS : Gump + { + private BaseHouse m_House; + private HouseGumpPageAOS m_Page; + + private const int LabelColor = 0x7FFF; + private const int SelectedColor = 0x421F; + private const int DisabledColor = 0x4210; + private const int WarningColor = 0x7E10; + + private const int LabelHue = 0x481; + private const int HighlightedLabelHue = 0x64; + + private ArrayList m_List; + + private string GetOwnerName() + { + Mobile m = m_House.Owner; + + if ( m == null || m.Deleted ) + return "(unowned)"; + + string name; + + if ( (name = m.Name) == null || (name = name.Trim()).Length <= 0 ) + name = "(no name)"; + + return name; + } + + private string GetDateTime( DateTime val ) + { + if ( val == DateTime.MinValue ) + return ""; + + return val.ToString( "yyyy'-'MM'-'dd HH':'mm':'ss" ); + } + + public void AddPageButton( int x, int y, int buttonID, int number, HouseGumpPageAOS page ) + { + bool isSelection = ( m_Page == page ); + + AddButton( x, y, isSelection ? 4006 : 4005, 4007, buttonID, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( x + 45, y, 200, 20, number, isSelection ? SelectedColor : LabelColor, false, false ); + } + + public void AddButtonLabeled( int x, int y, int buttonID, int number ) + { + AddButtonLabeled( x, y, buttonID, number, true ); + } + + public void AddButtonLabeled( int x, int y, int buttonID, int number, bool enabled ) + { + if ( enabled ) + AddButton( x, y, 4005, 4007, buttonID, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( x + 35, y, 240, 20, number, enabled ? LabelColor : DisabledColor, false, false ); + } + + public void AddList( ArrayList list, int button, bool accountOf, bool leadingStar, Mobile from ) + { + if ( list == null ) + return; + + m_List = new ArrayList( list ); + + int lastPage = 0; + int index = 0; + + for ( int i = 0; i < list.Count; ++i ) + { + int offset = index % 10; + int page = 1 + (index / 10); + + if ( page != lastPage ) + { + if ( lastPage != 0 ) + AddButton( 40, 360, 4005, 4007, 0, GumpButtonType.Page, page ); + + AddPage( page ); + + if ( lastPage != 0 ) + AddButton( 10, 360, 4014, 4016, 0, GumpButtonType.Page, lastPage ); + + lastPage = page; + } + + + Mobile m = (Mobile)list[i]; + + string name; + int labelHue = LabelHue; + + if ( m is PlayerVendor ) + { + PlayerVendor vendor = (PlayerVendor) m; + + name = vendor.ShopName; + + if ( vendor.IsOwner( from ) ) + labelHue = HighlightedLabelHue; + } + else if ( m != null ) + { + name = m.Name; + } + else + { + continue; + } + + if ( (name = name.Trim()).Length <= 0 ) + continue; + + if ( button != -1 ) + AddButton( 10, 150 + (offset * 20), 4005, 4007, GetButtonID( button, i ), GumpButtonType.Reply, 0 ); + + if ( accountOf && m.Player && m.Account != null ) + name = "Account of " + name; + + if ( leadingStar ) + name = "* " + name; + + AddLabel( button > 0 ? 45 : 10, 150 + (offset * 20), labelHue, name ); + ++index; + } + } + + public int GetButtonID( int type, int index ) + { + return 1 + (index * 15) + type; + } + + private static int[] m_HangerNumbers = new int[] + { + 2968, 2970, 2972, + 2974, 2976, 2978 + }; + + private static int[] m_FoundationNumbers = (Core.ML ? new int[] + { + 20, 189, 765, 65, 101, 0x2DF7, 0x2DFB + }: + new int[] + { + 20, 189, 765, 65, 101 + }); + + private static int[] m_PostNumbers = new int[] + { + 9, 29, 54, 90, 147, 169, + 177, 204, 251, 257, 263, + 298, 347, 424, 441, 466, + 514, 600, 601, 602, 603, + 660, 666, 672, 898, 970, + 974, 982 + }; + + public HouseGumpAOS( HouseGumpPageAOS page, Mobile from, BaseHouse house ) : base( 50, 40 ) + { + m_House = house; + m_Page = page; + + from.CloseGump( typeof( HouseGumpAOS ) ); + //from.CloseGump( typeof( HouseListGump ) ); + //from.CloseGump( typeof( HouseRemoveGump ) ); + + bool isCombatRestricted = house.IsCombatRestricted( from ); + + bool isOwner = house.IsOwner( from ); + bool isCoOwner = isOwner || house.IsCoOwner( from ); + bool isFriend = isCoOwner || house.IsFriend( from ); + + if ( isCombatRestricted ) + isFriend = isCoOwner = isOwner = false; + + AddPage( 0 ); + + if ( isFriend || page == HouseGumpPageAOS.Vendors ) + { + AddBackground( 0, 0, 420, page != HouseGumpPageAOS.Vendors ? 440 : 420, 5054 ); + + AddImageTiled( 10, 10, 400, 100, 2624 ); + AddAlphaRegion( 10, 10, 400, 100 ); + + AddImageTiled( 10, 120, 400, 260, 2624 ); + AddAlphaRegion( 10, 120, 400, 260 ); + + AddImageTiled( 10, 390, 400, page != HouseGumpPageAOS.Vendors ? 40 : 20, 2624 ); + AddAlphaRegion( 10, 390, 400, page != HouseGumpPageAOS.Vendors ? 40 : 20 ); + + AddButtonLabeled( 250, page != HouseGumpPageAOS.Vendors ? 410 : 390, 0, 1060675 ); // CLOSE + } + + AddImage( 10, 10, 100 ); + + if ( m_House.Sign != null ) + { + ArrayList lines = Wrap( m_House.Sign.GetName() ); + + if ( lines != null ) + { + for ( int i = 0, y = (114 - (lines.Count * 14)) / 2; i < lines.Count; ++i, y += 14 ) + { + string s = (string)lines[i]; + + AddLabel( 10 + ((160 - (s.Length * 8)) / 2), y, 0, s ); + } + } + } + + if ( page == HouseGumpPageAOS.Vendors ) + { + AddHtmlLocalized( 10, 120, 400, 20, 1062428, LabelColor, false, false ); //
SHOPS
+ + AddList( house.AvailableVendorsFor( from ), 1, false, false, from ); + return; + } + + if ( !isFriend ) + return; + + if ( house.Public ) + { + AddButtonLabeled( 10, 390, GetButtonID( 0, 0 ), 1060674 ); // Banish + AddButtonLabeled( 10, 410, GetButtonID( 0, 1 ), 1011261 ); // Lift a Ban + } + else + { + AddButtonLabeled( 10, 390, GetButtonID( 0, 2 ), 1060676 ); // Grant Access + AddButtonLabeled( 10, 410, GetButtonID( 0, 3 ), 1060677 ); // Revoke Access + } + + AddPageButton( 150, 10, GetButtonID( 1, 0 ), 1060668, HouseGumpPageAOS.Information ); + AddPageButton( 150, 30, GetButtonID( 1, 1 ), 1060669, HouseGumpPageAOS.Security ); + AddPageButton( 150, 50, GetButtonID( 1, 2 ), 1060670, HouseGumpPageAOS.Storage ); + AddPageButton( 150, 70, GetButtonID( 1, 3 ), 1060671, HouseGumpPageAOS.Customize ); + AddPageButton( 150, 90, GetButtonID( 1, 4 ), 1060672, HouseGumpPageAOS.Ownership ); + + switch ( page ) + { + case HouseGumpPageAOS.Information: + { + AddHtmlLocalized( 20, 130, 200, 20, 1011242, LabelColor, false, false ); // Owned By: + AddLabel( 210, 130, LabelHue, GetOwnerName() ); + + AddHtmlLocalized( 20, 170, 380, 20, 1018032, SelectedColor, false, false ); // This house is properly placed. + AddHtmlLocalized( 20, 190, 380, 20, 1018035, SelectedColor, false, false ); // This house is of modern design. + AddHtmlLocalized( 20, 210, 380, 20, (house is HouseFoundation) ? 1060681 : 1060680, SelectedColor, false, false ); // This is a (pre | custom)-built house. + AddHtmlLocalized( 20, 230, 380, 20, house.Public ? 1060678 : 1060679, SelectedColor, false, false ); // This house is (private | open to the public). + + switch ( house.DecayType ) + { + case DecayType.Ageless: + case DecayType.AutoRefresh: + { + AddHtmlLocalized( 20, 250, 380, 20, 1062209, SelectedColor, false, false ); // This house is Automatically refreshed. + break; + } + case DecayType.ManualRefresh: + { + AddHtmlLocalized( 20, 250, 380, 20, 1062208, SelectedColor, false, false ); // This house is Grandfathered. + break; + } + case DecayType.Condemned: + { + AddHtmlLocalized( 20, 250, 380, 20, 1062207, WarningColor, false, false ); // This house is Condemned. + break; + } + } + + AddHtmlLocalized( 20, 290, 200, 20, 1060692, SelectedColor, false, false ); // Built On: + AddLabel( 250, 290, LabelHue, GetDateTime( house.BuiltOn ) ); + + AddHtmlLocalized( 20, 310, 200, 20, 1060693, SelectedColor, false, false ); // Last Traded: + AddLabel( 250, 310, LabelHue, GetDateTime( house.LastTraded ) ); + + AddHtmlLocalized( 20, 330, 200, 20, 1061793, SelectedColor, false, false ); // House Value + AddLabel( 250, 330, LabelHue, house.Price.ToString() ); + + AddHtmlLocalized( 20, 360, 300, 20, 1011241, SelectedColor, false, false ); // Number of visits this building has had: + AddLabel( 350, 360, LabelHue, house.Visits.ToString() ); + + break; + } + case HouseGumpPageAOS.Security: + { + AddButtonLabeled( 10, 130, GetButtonID( 3, 0 ), 1011266, isCoOwner ); // View Co-Owner List + AddButtonLabeled( 10, 150, GetButtonID( 3, 1 ), 1011267, isOwner ); // Add a Co-Owner + AddButtonLabeled( 10, 170, GetButtonID( 3, 2 ), 1018036, isOwner ); // Remove a Co-Owner + AddButtonLabeled( 10, 190, GetButtonID( 3, 3 ), 1011268, isOwner ); // Clear Co-Owner List + + AddButtonLabeled( 10, 220, GetButtonID( 3, 4 ), 1011243 ); // View Friends List + AddButtonLabeled( 10, 240, GetButtonID( 3, 5 ), 1011244, isCoOwner ); // Add a Friend + AddButtonLabeled( 10, 260, GetButtonID( 3, 6 ), 1018037, isCoOwner ); // Remove a Friend + AddButtonLabeled( 10, 280, GetButtonID( 3, 7 ), 1011245, isCoOwner ); // Clear Friend List + + if ( house.Public ) + { + AddButtonLabeled( 10, 310, GetButtonID( 3, 8 ), 1011260 ); // View Ban List + AddButtonLabeled( 10, 330, GetButtonID( 3, 9 ), 1060698 ); // Clear Ban List + + AddButtonLabeled( 210, 130, GetButtonID( 3, 12 ), 1060695, isOwner ); // Change to Private + + AddHtmlLocalized( 245, 150, 240, 20, 1060694, SelectedColor, false, false ); // Change to Public + } + else + { + AddButtonLabeled( 10, 310, GetButtonID( 3, 10 ), 1060699 ); // View Access List + AddButtonLabeled( 10, 330, GetButtonID( 3, 11 ), 1060700 ); // Clear Access List + + AddHtmlLocalized( 245, 130, 240, 20, 1060695, SelectedColor, false, false ); // Change to Private + + AddButtonLabeled( 210, 150, GetButtonID( 3, 13 ), 1060694, isOwner ); // Change to Public + } + + break; + } + case HouseGumpPageAOS.Storage: + { + AddHtmlLocalized( 10, 130, 400, 20, 1060682, LabelColor, false, false ); //
HOUSE STORAGE SUMMARY
+ + // This is not as OSI; storage changes not yet implemented + + /*AddHtmlLocalized( 10, 170, 275, 20, 1011237, LabelColor, false, false ); // Number of locked down items: + AddLabel( 310, 170, LabelHue, m_House.LockDownCount.ToString() ); + + AddHtmlLocalized( 10, 190, 275, 20, 1011238, LabelColor, false, false ); // Maximum locked down items: + AddLabel( 310, 190, LabelHue, m_House.MaxLockDowns.ToString() ); + + AddHtmlLocalized( 10, 210, 275, 20, 1011239, LabelColor, false, false ); // Number of secure containers: + AddLabel( 310, 210, LabelHue, m_House.SecureCount.ToString() ); + + AddHtmlLocalized( 10, 230, 275, 20, 1011240, LabelColor, false, false ); // Maximum number of secure containers: + AddLabel( 310, 230, LabelHue, m_House.MaxSecures.ToString() );*/ + + int fromSecures, fromVendors, fromLockdowns, fromMovingCrate; + + int maxSecures = house.GetAosMaxSecures(); + int curSecures = house.GetAosCurSecures( out fromSecures, out fromVendors, out fromLockdowns, out fromMovingCrate ); + + int maxLockdowns = house.GetAosMaxLockdowns(); + int curLockdowns = house.GetAosCurLockdowns(); + + int bonusStorage = (int)((house.BonusStorageScalar - 1) * 100); + + if( bonusStorage > 0 ) + { + AddHtmlLocalized( 10, 150, 300, 20, 1072519, LabelColor, false, false ); // Increased Storage + AddLabel( 310, 150, LabelHue, String.Format( "{0}%", bonusStorage ) ); + } + + AddHtmlLocalized( 10, 170, 300, 20, 1060683, LabelColor, false, false ); // Maximum Secure Storage + AddLabel( 310, 170, LabelHue, maxSecures.ToString() ); + + AddHtmlLocalized( 10, 190, 300, 20, 1060685, LabelColor, false, false ); // Used by Moving Crate + AddLabel( 310, 190, LabelHue, fromMovingCrate.ToString() ); + + AddHtmlLocalized( 10, 210, 300, 20, 1060686, LabelColor, false, false ); // Used by Lockdowns + AddLabel( 310, 210, LabelHue, fromLockdowns.ToString() ); + + if ( BaseHouse.NewVendorSystem ) + { + AddHtmlLocalized( 10, 230, 300, 20, 1060688, LabelColor, false, false ); // Used by Secure Containers + AddLabel( 310, 230, LabelHue, fromSecures.ToString() ); + + AddHtmlLocalized( 10, 250, 300, 20, 1060689, LabelColor, false, false ); // Available Storage + AddLabel( 310, 250, LabelHue, Math.Max( maxSecures - curSecures, 0 ).ToString() ); + + AddHtmlLocalized( 10, 290, 300, 20, 1060690, LabelColor, false, false ); // Maximum Lockdowns + AddLabel( 310, 290, LabelHue, maxLockdowns.ToString() ); + + AddHtmlLocalized( 10, 310, 300, 20, 1060691, LabelColor, false, false ); // Available Lockdowns + AddLabel( 310, 310, LabelHue, Math.Max( maxLockdowns - curLockdowns, 0 ).ToString() ); + + int maxVendors = house.GetNewVendorSystemMaxVendors(); + int vendors = house.PlayerVendors.Count + house.VendorRentalContracts.Count; + + AddHtmlLocalized( 10, 350, 300, 20, 1062391, LabelColor, false, false ); // Vendor Count + AddLabel( 310, 350, LabelHue, vendors.ToString() + " / " + maxVendors.ToString() ); + } + else + { + AddHtmlLocalized( 10, 230, 300, 20, 1060687, LabelColor, false, false ); // Used by Vendors + AddLabel( 310, 230, LabelHue, fromVendors.ToString() ); + + AddHtmlLocalized( 10, 250, 300, 20, 1060688, LabelColor, false, false ); // Used by Secure Containers + AddLabel( 310, 250, LabelHue, fromSecures.ToString() ); + + AddHtmlLocalized( 10, 270, 300, 20, 1060689, LabelColor, false, false ); // Available Storage + AddLabel( 310, 270, LabelHue, Math.Max( maxSecures - curSecures, 0 ).ToString() ); + + AddHtmlLocalized( 10, 330, 300, 20, 1060690, LabelColor, false, false ); // Maximum Lockdowns + AddLabel( 310, 330, LabelHue, maxLockdowns.ToString() ); + + AddHtmlLocalized( 10, 350, 300, 20, 1060691, LabelColor, false, false ); // Available Lockdowns + AddLabel( 310, 350, LabelHue, Math.Max( maxLockdowns - curLockdowns, 0 ).ToString() ); + } + + break; + } + case HouseGumpPageAOS.Customize: + { + bool isCustomizable = isOwner && ( house is HouseFoundation ); + + AddButtonLabeled( 10, 120, GetButtonID( 5, 0 ), 1060759, isOwner && !isCustomizable && ( house.ConvertEntry != null ) ); // Convert Into Customizable House + AddButtonLabeled( 10, 160, GetButtonID( 5, 1 ), 1060765, isOwner && isCustomizable ); // Customize This House + AddButtonLabeled( 10, 180, GetButtonID( 5, 2 ), 1060760, isOwner && house.MovingCrate != null ); // Relocate Moving Crate + AddButtonLabeled( 10, 210, GetButtonID( 5, 3 ), 1060761, isOwner && house.Public ); // Change House Sign + AddButtonLabeled( 10, 230, GetButtonID( 5, 4 ), 1060762, isOwner && isCustomizable ); // Change House Sign Hanger + AddButtonLabeled( 10, 250, GetButtonID( 5, 5 ), 1060763, isOwner && isCustomizable && ( ((HouseFoundation)house).Signpost != null ) ); // Change Signpost + AddButtonLabeled( 10, 280, GetButtonID( 5, 6 ), 1062004, isOwner && isCustomizable ); // Change Foundation Style + AddButtonLabeled( 10, 310, GetButtonID( 5, 7 ), 1060764, isCoOwner ); // Rename House + + break; + } + case HouseGumpPageAOS.Ownership: + { + AddButtonLabeled( 10, 130, GetButtonID( 6, 0 ), 1061794, isOwner && house.MovingCrate == null && house.InternalizedVendors.Count == 0 ); // Demolish House + AddButtonLabeled( 10, 150, GetButtonID( 6, 1 ), 1061797, isOwner ); // Trade House + AddButtonLabeled( 10, 190, GetButtonID( 6, 2 ), 1061798, false ); // Make Primary + + break; + } + case HouseGumpPageAOS.ChangeHanger: + { + for ( int i = 0; i < m_HangerNumbers.Length; ++i ) + { + int x = 50 + ((i % 3) * 100); + int y = 180 + ((i / 3) * 80); + + AddButton( x, y, 4005, 4007, GetButtonID( 7, i ), GumpButtonType.Reply, 0 ); + AddItem( x + 20, y, m_HangerNumbers[i] ); + } + + break; + } + case HouseGumpPageAOS.ChangeFoundation: + { + for ( int i = 0; i < m_FoundationNumbers.Length; ++i ) + { + int x = 15 + ((i % 5) * 80); + int y = 180 + ((i / 5) * 100); + + AddButton( x, y, 4005, 4007, GetButtonID( 8, i ), GumpButtonType.Reply, 0 ); + AddItem( x + 25, y, m_FoundationNumbers[i] ); + } + + break; + } + case HouseGumpPageAOS.ChangeSign: + { + int index = 0; + + for ( int i = 0; i < 3; ++i ) + { + AddPage( i + 1 ); + + AddButton( 10, 360, 4005, 4007, 0, GumpButtonType.Page, ((i + 1) % 3) + 1 ); + + for ( int j = 0; j < 18; ++j ) + { + int x = 30 + ((j % 6) * 60); + int y = 130 + ((j / 6) * 60); + + AddButton( x, y, 4005, 4007, GetButtonID( 9, index ), GumpButtonType.Reply, 0 ); + AddItem( x + 20, y, 2980 + (index++ * 2) ); + } + } + + break; + } + case HouseGumpPageAOS.RemoveCoOwner: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060730, LabelColor, false, false ); //
CO-OWNER LIST
+ AddList( house.CoOwners, 10, false, true, from ); + break; + } + case HouseGumpPageAOS.ListCoOwner: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060730, LabelColor, false, false ); //
CO-OWNER LIST
+ AddList( house.CoOwners, -1, false, true, from ); + break; + } + case HouseGumpPageAOS.RemoveFriend: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060731, LabelColor, false, false ); //
FRIENDS LIST
+ AddList( house.Friends, 11, false, true, from ); + break; + } + case HouseGumpPageAOS.ListFriend: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060731, LabelColor, false, false ); //
FRIENDS LIST
+ AddList( house.Friends, -1, false, true, from ); + break; + } + case HouseGumpPageAOS.RemoveBan: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060733, LabelColor, false, false ); //
BAN LIST
+ AddList( house.Bans, 12, true, true, from ); + break; + } + case HouseGumpPageAOS.ListBan: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060733, LabelColor, false, false ); //
BAN LIST
+ AddList( house.Bans, -1, true, true, from ); + break; + } + case HouseGumpPageAOS.RemoveAccess: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060732, LabelColor, false, false ); //
ACCESS LIST
+ AddList( house.Access, 13, false, true, from ); + break; + } + case HouseGumpPageAOS.ListAccess: + { + AddHtmlLocalized( 10, 120, 400, 20, 1060732, LabelColor, false, false ); //
ACCESS LIST
+ AddList( house.Access, -1, false, true, from ); + break; + } + case HouseGumpPageAOS.ChangePost: + { + int index = 0; + + for ( int i = 0; i < 2; ++i ) + { + AddPage( i + 1 ); + + AddButton( 10, 360, 4005, 4007, 0, GumpButtonType.Page, ((i + 1) % 2) + 1 ); + + for ( int j = 0; j < 16 && index < m_PostNumbers.Length; ++j ) + { + int x = 15 + ((j % 8) * 50); + int y = 130 + ((j / 8) * 110); + + AddButton( x, y, 4005, 4007, GetButtonID( 14, index ), GumpButtonType.Reply, 0 ); + AddItem( x + 10, y, m_PostNumbers[index++] ); + } + } + + break; + } + } + } + + public static void PublicPrivateNotice_Callback( Mobile from, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( !house.Deleted ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, house ) ); + } + + public static void CustomizeNotice_Callback( Mobile from, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( !house.Deleted ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Customize, from, house ) ); + } + + public static void ClearCoOwners_Callback( Mobile from, bool okay, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( house.Deleted ) + return; + + if ( okay && house.IsOwner( from ) ) + { + if ( house.CoOwners != null ) + house.CoOwners.Clear(); + + from.SendLocalizedMessage( 501333 ); // All co-owners have been removed from this house. + } + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, house ) ); + } + + public static void ClearFriends_Callback( Mobile from, bool okay, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( house.Deleted ) + return; + + if ( okay && house.IsCoOwner( from ) ) + { + if ( house.Friends != null ) + house.Friends.Clear(); + + from.SendLocalizedMessage( 501332 ); // All friends have been removed from this house. + } + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, house ) ); + } + + public static void ClearBans_Callback( Mobile from, bool okay, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( house.Deleted ) + return; + + if ( okay && house.IsFriend( from ) ) + { + if ( house.Bans != null ) + house.Bans.Clear(); + + from.SendLocalizedMessage( 1060754 ); // All bans for this house have been lifted. + } + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, house ) ); + } + + public static void ClearAccess_Callback( Mobile from, bool okay, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( house.Deleted ) + return; + + if ( okay && house.IsFriend( from ) ) + { + ArrayList list = new ArrayList( house.Access ); + + if ( house.Access != null ) + house.Access.Clear(); + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + if ( !house.HasAccess( m ) && house.IsInside( m ) ) + { + m.Location = house.BanLocation; + m.SendLocalizedMessage( 1060734 ); // Your access to this house has been revoked. + } + } + + from.SendLocalizedMessage( 1061843 ); // This house's Access List has been cleared. + } + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, house ) ); + } + + public static void ConvertHouse_Callback( Mobile from, bool okay, object state ) + { + BaseHouse house = (BaseHouse) state; + + if ( house.Deleted ) + return; + + if ( okay && house.IsOwner( from ) && !house.HasRentedVendors ) + { + HousePlacementEntry e = house.ConvertEntry; + + if ( e != null ) + { + int cost = e.Cost - house.Price; + + if ( cost > 0 ) + { + if ( Banker.Withdraw( from, cost ) ) + { + from.SendLocalizedMessage( 1060398, cost.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + } + else + { + from.SendLocalizedMessage( 1061624 ); // You do not have enough funds in your bank to cover the difference between your old house and your new one. + return; + } + } + else if ( cost < 0 ) + { + if ( Banker.Deposit( from, -cost ) ) + from.SendLocalizedMessage( 1060397, (-cost).ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + else + return; + } + + BaseHouse newHouse = e.ConstructHouse( from ); + + if ( newHouse != null ) + { + newHouse.Price = e.Cost; + + house.MoveAllToCrate(); + + newHouse.Friends = new ArrayList( house.Friends ); + newHouse.CoOwners = new ArrayList( house.CoOwners ); + newHouse.Bans = new ArrayList( house.Bans ); + newHouse.Access = new ArrayList( house.Access ); + newHouse.BuiltOn = house.BuiltOn; + newHouse.LastTraded = house.LastTraded; + newHouse.Public = house.Public; + + newHouse.VendorInventories.AddRange( house.VendorInventories ); + house.VendorInventories.Clear(); + + foreach ( VendorInventory inventory in newHouse.VendorInventories ) + { + inventory.House = newHouse; + } + + newHouse.InternalizedVendors.AddRange( house.InternalizedVendors ); + house.InternalizedVendors.Clear(); + + foreach ( Mobile mobile in newHouse.InternalizedVendors ) + { + if ( mobile is PlayerVendor ) + ((PlayerVendor)mobile).House = newHouse; + else if ( mobile is PlayerBarkeeper ) + ((PlayerBarkeeper)mobile).House = newHouse; + } + + if( house.MovingCrate != null ) + { + newHouse.MovingCrate = house.MovingCrate; + newHouse.MovingCrate.House = newHouse; + house.MovingCrate = null; + } + + ArrayList items = house.GetItems(); + ArrayList mobiles = house.GetMobiles(); + + newHouse.MoveToWorld( new Point3D( house.X + house.ConvertOffsetX, house.Y + house.ConvertOffsetY, house.Z + house.ConvertOffsetZ ), house.Map ); + house.Delete(); + + foreach ( Item item in items ) + { + item.Location = newHouse.BanLocation; + } + + foreach ( Mobile mobile in mobiles ) + { + mobile.Location = newHouse.BanLocation; + } + + /* You have successfully replaced your original house with a new house. + * The value of the replaced house has been deposited into your bank box. + * All of the items in your original house have been relocated to a Moving Crate in the new house. + * Any deed-based house add-ons have been converted back into deeds. + * Vendors and barkeeps in the house, if any, have been stored in the Moving Crate as well. + * Use the Get Vendor context-sensitive menu option on your character to retrieve them. + * These containers can be used to re-create the vendor in a new location. + * Any barkeepers have been converted into deeds. + */ + from.SendGump( new NoticeGump( 1060637, 30720, 1060012, 32512, 420, 280, null, null ) ); + return; + } + } + } + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, house ) ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_House.Deleted ) + return; + + Mobile from = sender.Mobile; + + bool isCombatRestricted = m_House.IsCombatRestricted( from ); + + bool isOwner = m_House.IsOwner( from ); + bool isCoOwner = isOwner || m_House.IsCoOwner( from ); + bool isFriend = isCoOwner || m_House.IsFriend( from ); + + if ( isCombatRestricted ) + isFriend = isCoOwner = isFriend = false; + + if ( !from.CheckAlive() ) + return; + + Item sign = m_House.Sign; + + if ( sign == null || from.Map != sign.Map || !from.InRange( sign.GetWorldLocation(), 18 ) ) + return; + + HouseFoundation foundation = m_House as HouseFoundation; + bool isCustomizable = ( foundation != null ); + + int val = info.ButtonID - 1; + + if ( val < 0 ) + return; + + int type = val % 15; + int index = val / 15; + + if ( m_Page == HouseGumpPageAOS.Vendors ) + { + if ( index >= 0 && index < m_List.Count ) + { + PlayerVendor vendor = (PlayerVendor) m_List[index]; + + if ( !vendor.CanInteractWith( from, false ) ) + return; + + if ( from.Map != sign.Map || !from.InRange( sign, 5 ) ) + { + from.SendLocalizedMessage( 1062429 ); // You must be within five paces of the house sign to use this option. + } + else if ( vendor.IsOwner( from ) ) + { + vendor.SendOwnerGump( from ); + } + else + { + vendor.OpenBackpack( from ); + } + } + + return; + } + + if ( !isFriend ) + return; + + switch ( type ) + { + case 0: + { + switch ( index ) + { + case 0: // Banish + { + if ( m_House.Public ) + { + from.SendLocalizedMessage( 501325 ); // Target the individual to ban from this house. + from.Target = new HouseBanTarget( true, m_House ); + } + + break; + } + case 1: // Lift Ban + { + if ( m_House.Public ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveBan, from, m_House ) ); + + break; + } + case 2: // Grant Access + { + if ( !m_House.Public ) + { + from.SendLocalizedMessage( 1060711 ); // Target the person you would like to grant access to. + from.Target = new HouseAccessTarget( m_House ); + } + + break; + } + case 3: // Revoke Access + { + if ( !m_House.Public ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveAccess, from, m_House ) ); + + break; + } + } + + break; + } + case 1: + { + HouseGumpPageAOS page; + + switch ( index ) + { + case 0: page = HouseGumpPageAOS.Information; break; + case 1: page = HouseGumpPageAOS.Security; break; + case 2: page = HouseGumpPageAOS.Storage; break; + case 3: page = HouseGumpPageAOS.Customize; break; + case 4: page = HouseGumpPageAOS.Ownership; break; + default: return; + } + + from.SendGump( new HouseGumpAOS( page, from, m_House ) ); + break; + } + case 3: + { + switch ( index ) + { + case 0: // View Co-Owner List + { + if ( isCoOwner ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ListCoOwner, from, m_House ) ); + + break; + } + case 1: // Add a Co-Owner + { + if ( isOwner ) + { + from.SendLocalizedMessage( 501328 ); // Target the person you wish to name a co-owner of your household. + from.Target = new CoOwnerTarget( true, m_House ); + } + + break; + } + case 2: // Remove a Co-Owner + { + if ( isOwner ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveCoOwner, from, m_House ) ); + + break; + } + case 3: // Clear Co-Owner List + { + if ( isOwner ) + from.SendGump( new WarningGump( 1060635, 30720, 1060736, 32512, 420, 280, new WarningGumpCallback( ClearCoOwners_Callback ), m_House ) ); + + break; + } + case 4: // View Friends List + { + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ListFriend, from, m_House ) ); + + break; + } + case 5: // Add a Friend + { + if ( isCoOwner ) + { + from.SendLocalizedMessage( 501317 ); // Target the person you wish to name a friend of your household. + from.Target = new HouseFriendTarget( true, m_House ); + } + + break; + } + case 6: // Remove a Friend + { + if ( isCoOwner ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveFriend, from, m_House ) ); + + break; + } + case 7: // Clear Friend List + { + if ( isCoOwner ) + from.SendGump( new WarningGump( 1060635, 30720, 1018039, 32512, 420, 280, new WarningGumpCallback( ClearFriends_Callback ), m_House ) ); + + break; + } + case 8: // View Ban List + { + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ListBan, from, m_House ) ); + + break; + } + case 9: // Clear Ban List + { + from.SendGump( new WarningGump( 1060635, 30720, 1060753, 32512, 420, 280, new WarningGumpCallback( ClearBans_Callback ), m_House ) ); + + break; + } + case 10: // View Access List + { + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ListAccess, from, m_House ) ); + + break; + } + case 11: // Clear Access List + { + from.SendGump( new WarningGump( 1060635, 30720, 1061842, 32512, 420, 280, new WarningGumpCallback( ClearAccess_Callback ), m_House ) ); + + break; + } + case 12: // Make Private + { + if ( isOwner ) + { + if ( m_House.PlayerVendors.Count > 0 ) + { + // You have vendors working out of this building. It cannot be declared private until there are no vendors in place. + from.SendGump( new NoticeGump( 1060637, 30720, 501887, 32512, 320, 180, new NoticeGumpCallback( PublicPrivateNotice_Callback ), m_House ) ); + break; + } + + if ( m_House.VendorRentalContracts.Count > 0 ) + { + // You cannot currently take this action because you have vendor contracts locked down in your home. You must remove them first. + from.SendGump( new NoticeGump( 1060637, 30720, 1062351, 32512, 320, 180, new NoticeGumpCallback( PublicPrivateNotice_Callback ), m_House ) ); + break; + } + + m_House.Public = false; + + m_House.ChangeLocks( from ); + + // This house is now private. + from.SendGump( new NoticeGump( 1060637, 30720, 501888, 32512, 320, 180, new NoticeGumpCallback( PublicPrivateNotice_Callback ), m_House ) ); + + Region r = m_House.Region; + List list = r.GetMobiles(); + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + if ( !m_House.HasAccess( m ) && m_House.IsInside( m ) ) + m.Location = m_House.BanLocation; + } + } + + break; + } + case 13: // Make Public + { + if ( isOwner ) + { + m_House.Public = true; + + m_House.RemoveKeys( from ); + m_House.RemoveLocks(); + + if ( BaseHouse.NewVendorSystem ) + { + // This house is now public. The owner may now place vendors and vendor rental contracts. + from.SendGump( new NoticeGump( 1060637, 30720, 501886, 32512, 320, 180, new NoticeGumpCallback( PublicPrivateNotice_Callback ), m_House ) ); + } + else + { + from.SendGump( new NoticeGump( 1060637, 30720, "This house is now public. Friends of the house may now have vendors working out of this building.", 0xF8C000, 320, 180, new NoticeGumpCallback( PublicPrivateNotice_Callback ), m_House ) ); + } + + Region r = m_House.Region; + List list = r.GetMobiles(); + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + if ( m_House.IsBanned( m ) && m_House.IsInside( m ) ) + m.Location = m_House.BanLocation; + } + } + + break; + } + } + + break; + } + case 5: + { + switch ( index ) + { + case 0: // Convert Into Customizable House + { + if ( isOwner && !isCustomizable ) + { + if ( m_House.HasRentedVendors ) + { + // You cannot perform this action while you still have vendors rented out in this house. + from.SendGump( new NoticeGump( 1060637, 30720, 1062395, 32512, 320, 180, new NoticeGumpCallback( CustomizeNotice_Callback ), m_House ) ); + } + else + { + HousePlacementEntry e = m_House.ConvertEntry; + + if ( e != null ) + { + /* You are about to turn your house into a customizable house. + * You will be refunded the value of this house, and then be charged the cost of the equivalent customizable dirt lot. + * All of your possessions in the house will be transported to a Moving Crate. + * Deed-based house add-ons will be converted back into deeds. + * Vendors and barkeeps will also be stored in the Moving Crate. + * Your house will be leveled to its foundation, and you will be able to build new walls, windows, doors, and stairs. + * Are you sure you wish to continue? + */ + from.SendGump( new WarningGump( 1060635, 30720, 1060013, 32512, 420, 280, new WarningGumpCallback( ConvertHouse_Callback ), m_House ) ); + } + } + } + + break; + } + case 1: // Customize This House + { + if ( isOwner && isCustomizable ) + { + if ( m_House.HasRentedVendors ) + { + // You cannot perform this action while you still have vendors rented out in this house. + from.SendGump( new NoticeGump( 1060637, 30720, 1062395, 32512, 320, 180, new NoticeGumpCallback( CustomizeNotice_Callback ), m_House ) ); + } + else + { + foundation.BeginCustomize( from ); + } + } + + break; + } + case 2: // Relocate Moving Crate + { + MovingCrate crate = m_House.MovingCrate; + + if ( isOwner && crate != null ) + { + if ( !m_House.IsInside( from ) ) + { + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + } + else + { + crate.MoveToWorld( from.Location, from.Map ); + crate.RestartTimer(); + } + } + + break; + } + case 3: // Change House Sign + { + if ( isOwner && m_House.Public ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ChangeSign, from, m_House ) ); + + break; + } + case 4: // Change House Sign Hanger + { + if ( isOwner && isCustomizable ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ChangeHanger, from, m_House ) ); + + break; + } + case 5: // Change Signpost + { + if ( isOwner && isCustomizable && foundation.Signpost != null ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ChangePost, from, m_House ) ); + + break; + } + case 6: // Change Foundation Style + { + if ( isOwner && isCustomizable ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.ChangeFoundation, from, m_House ) ); + + break; + } + case 7: // Rename House + { + if ( isCoOwner ) + { + from.Prompt = new RenamePrompt( m_House ); + from.SendLocalizedMessage( 501302 ); // What dost thou wish the sign to say? + } + + break; + } + } + + break; + } + case 6: + { + switch ( index ) + { + case 0: // Demolish + { + if ( isOwner && m_House.MovingCrate == null && m_House.InternalizedVendors.Count == 0 ) + { + if( !Guilds.Guild.NewGuildSystem && m_House.FindGuildstone() != null ) + from.SendLocalizedMessage( 501389 ); // You cannot redeed a house with a guildstone inside. + else + from.SendGump( new HouseDemolishGump( from, m_House ) ); + } + + break; + } + case 1: // Trade House + { + if ( isOwner ) + { + if ( BaseHouse.NewVendorSystem && m_House.HasPersonalVendors ) + { + from.SendLocalizedMessage( 1062467 ); // You cannot trade this house while you still have personal vendors inside. + } + else if ( m_House.DecayLevel == DecayLevel.DemolitionPending ) + { + from.SendLocalizedMessage( 1005321 ); // This house has been marked for demolition, and it cannot be transferred. + } + else + { + from.SendLocalizedMessage( 501309 ); // Target the person to whom you wish to give this house. + from.Target = new HouseOwnerTarget( m_House ); + } + } + + break; + } + case 2: // Make Primary + break; + } + + break; + } + case 7: + { + if ( isOwner && isCustomizable && index >= 0 && index < m_HangerNumbers.Length ) + { + Item hanger = foundation.SignHanger; + + if ( hanger != null ) + hanger.ItemID = m_HangerNumbers[index]; + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Customize, from, m_House ) ); + } + + break; + } + case 8: + { + if ( isOwner && isCustomizable ) + { + FoundationType newType; + + switch ( index ) + { + case 0: newType = FoundationType.DarkWood; break; + case 1: newType = FoundationType.LightWood; break; + case 2: newType = FoundationType.Dungeon; break; + case 3: newType = FoundationType.Brick; break; + case 4: newType = FoundationType.Stone; break; + case 5: if( Core.ML ) newType = FoundationType.ElvenGrey; else return; break; + case 6: if( Core.ML ) newType = FoundationType.ElvenNatural; else return; break; + default: return; + } + + foundation.Type = newType; + + DesignState state = foundation.BackupState; + HouseFoundation.ApplyFoundation( newType, state.Components ); + state.OnRevised(); + + state = foundation.DesignState; + HouseFoundation.ApplyFoundation( newType, state.Components ); + state.OnRevised(); + + state = foundation.CurrentState; + HouseFoundation.ApplyFoundation( newType, state.Components ); + state.OnRevised(); + + foundation.Delta( ItemDelta.Update ); + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Customize, from, m_House ) ); + } + + break; + } + case 9: + { + if ( isOwner && m_House.Public && index >= 0 && index < 54 ) + { + m_House.ChangeSignType( 2980 + (index * 2) ); + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Customize, from, m_House ) ); + } + + break; + } + case 10: + { + if ( isOwner && m_List != null && index >= 0 && index < m_List.Count ) + { + m_House.RemoveCoOwner( from, (Mobile)m_List[index] ); + + if ( m_House.CoOwners.Count > 0 ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveCoOwner, from, m_House ) ); + else + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, m_House ) ); + } + + break; + } + case 11: + { + if ( isCoOwner && m_List != null && index >= 0 && index < m_List.Count ) + { + m_House.RemoveFriend( from, (Mobile)m_List[index] ); + + if ( m_House.Friends.Count > 0 ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveFriend, from, m_House ) ); + else + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, m_House ) ); + } + + break; + } + case 12: + { + if ( m_List != null && index >= 0 && index < m_List.Count ) + { + m_House.RemoveBan( from, (Mobile)m_List[index] ); + + if ( m_House.Bans.Count > 0 ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveBan, from, m_House ) ); + else + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, m_House ) ); + } + + break; + } + case 13: + { + if ( m_List != null && index >= 0 && index < m_List.Count ) + { + m_House.RemoveAccess( from, (Mobile)m_List[index] ); + + if ( m_House.Access.Count > 0 ) + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.RemoveAccess, from, m_House ) ); + else + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Security, from, m_House ) ); + } + + break; + } + case 14: + { + if ( isOwner && isCustomizable && index >= 0 && index < m_PostNumbers.Length ) + { + foundation.SignpostGraphic = m_PostNumbers[index]; + foundation.CheckSignpost(); + + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Customize, from, m_House ) ); + } + + break; + } + } + } + + private ArrayList Wrap( string value ) + { + if ( value == null || (value = value.Trim()).Length <= 0 ) + return null; + + string[] values = value.Split( ' ' ); + ArrayList list = new ArrayList(); + string current = ""; + + for ( int i = 0; i < values.Length; ++i ) + { + string val = values[i]; + + string v = current.Length == 0 ? val : current + ' ' + val; + + if ( v.Length < 10 ) + { + current = v; + } + else if ( v.Length == 10 ) + { + list.Add( v ); + + if ( list.Count == 6 ) + return list; + + current = ""; + } + else if ( val.Length <= 10 ) + { + list.Add( current ); + + if ( list.Count == 6 ) + return list; + + current = val; + } + else + { + while ( v.Length >= 10 ) + { + list.Add( v.Substring( 0, 10 ) ); + + if ( list.Count == 6 ) + return list; + + v = v.Substring( 10 ); + } + + current = v; + } + } + + if ( current.Length > 0 ) + list.Add( current ); + + return list; + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/HouseTransferGump.cs b/Scripts/Gumps/HouseTransferGump.cs new file mode 100644 index 000000000..b6788e092 --- /dev/null +++ b/Scripts/Gumps/HouseTransferGump.cs @@ -0,0 +1,66 @@ +using System; +using Server; +using Server.Items; +using Server.Multis; +using Server.Multis.Deeds; +using Server.Network; + +namespace Server.Gumps +{ + public class HouseTransferGump : Gump + { + private Mobile m_From, m_To; + private BaseHouse m_House; + + public HouseTransferGump( Mobile from, Mobile to, BaseHouse house ) : base( 110, 100 ) + { + m_From = from; + m_To = to; + m_House = house; + + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 280, 5054 ); + + AddImageTiled( 10, 10, 400, 20, 2624 ); + AddAlphaRegion( 10, 10, 400, 20 ); + + AddHtmlLocalized( 10, 10, 400, 20, 1060635, 30720, false, false ); //
WARNING
+ + AddImageTiled( 10, 40, 400, 200, 2624 ); + AddAlphaRegion( 10, 40, 400, 200 ); + + /* Another player is attempting to initiate a house trade with you. + * In order for you to see this window, both you and the other person are standing within two paces of the house to be traded. + * If you click OKAY below, a house trade scroll will appear in your trade window and you can complete the transaction. + * This scroll is a distinctive blue color and will show the name of the house, the name of the owner of that house, and the sextant coordinates of the center of the house when you hover your mouse over it. + * In order for the transaction to be successful, you both must accept the trade and you both must remain within two paces of the house sign. + *

Accepting this house in trade will condemn any and all of your other houses that you may have. + * All of your houses on all shards will be affected. + *

In addition, you will not be able to place another house or have one transferred to you for one (1) real-life week.

+ * Once you accept these terms, these effects cannot be reversed. + * Re-deeding or transferring your new house will not uncondemn your other house(s) nor will the one week timer be removed.

+ * If you are absolutely certain you wish to proceed, click the button next to OKAY below. + * If you do not wish to trade for this house, click CANCEL. + */ + AddHtmlLocalized( 10, 40, 400, 200, 1062086, 32512, false, true ); + + AddImageTiled( 10, 250, 400, 20, 2624 ); + AddAlphaRegion( 10, 250, 400, 20 ); + + AddButton( 10, 250, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40, 250, 170, 20, 1011036, 32767, false, false ); // OKAY + + AddButton( 210, 250, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 240, 250, 170, 20, 1011012, 32767, false, false ); // CANCEL + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 1 && !m_House.Deleted ) + m_House.EndConfirmTransfer( m_From, m_To ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/NoticeGump.cs b/Scripts/Gumps/NoticeGump.cs new file mode 100644 index 000000000..5d863b232 --- /dev/null +++ b/Scripts/Gumps/NoticeGump.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Gumps +{ + public delegate void NoticeGumpCallback( Mobile from, object state ); + + public class NoticeGump : Gump + { + private NoticeGumpCallback m_Callback; + private object m_State; + + public NoticeGump( int header, int headerColor, object content, int contentColor, int width, int height, NoticeGumpCallback callback, object state ) : base( (640 - width) / 2, (480 - height) / 2 ) + { + m_Callback = callback; + m_State = state; + + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, width, height, 5054 ); + + AddImageTiled( 10, 10, width - 20, 20, 2624 ); + AddAlphaRegion( 10, 10, width - 20, 20 ); + AddHtmlLocalized( 10, 10, width - 20, 20, header, headerColor, false, false ); + + AddImageTiled( 10, 40, width - 20, height - 80, 2624 ); + AddAlphaRegion( 10, 40, width - 20, height - 80 ); + + if ( content is int ) + AddHtmlLocalized( 10, 40, width - 20, height - 80, (int)content, contentColor, false, true ); + else if ( content is string ) + AddHtml( 10, 40, width - 20, height - 80, String.Format( "{1}", contentColor, content ), false, true ); + + AddImageTiled( 10, height - 30, width - 20, 20, 2624 ); + AddAlphaRegion( 10, height - 30, width - 20, 20 ); + AddButton( 10, height - 30, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40, height - 30, 120, 20, 1011036, 32767, false, false ); // OKAY + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 && m_Callback != null ) + m_Callback( sender.Mobile, m_State ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/PetResurrectGump.cs b/Scripts/Gumps/PetResurrectGump.cs new file mode 100644 index 000000000..bede19155 --- /dev/null +++ b/Scripts/Gumps/PetResurrectGump.cs @@ -0,0 +1,69 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Gumps; + +namespace Server.Gumps +{ + public class PetResurrectGump : Gump + { + private BaseCreature m_Pet; + + public PetResurrectGump( Mobile from, BaseCreature pet ) : base( 50, 50 ) + { + from.CloseGump( typeof( PetResurrectGump ) ); + + m_Pet = pet; + + AddPage( 0 ); + + AddBackground( 10, 10, 265, 140, 0x242C ); + + AddItem( 205, 40, 0x4 ); + AddItem( 227, 40, 0x5 ); + + AddItem( 180, 78, 0xCAE ); + AddItem( 195, 90, 0xCAD ); + AddItem( 218, 95, 0xCB0 ); + + AddHtmlLocalized( 30, 30, 150, 75, 1049665, false, false ); //
Wilt thou sanctify the resurrection of:
+ AddHtml( 30, 70, 150, 25, String.Format( "
{0}
", pet.Name ), true, false ); + + AddButton( 40, 105, 0x81A, 0x81B, 0x1, GumpButtonType.Reply, 0 ); // Okay + AddButton( 110, 105, 0x819, 0x818, 0x2, GumpButtonType.Reply, 0 ); // Cancel + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( m_Pet.Deleted || !m_Pet.IsBonded || !m_Pet.IsDeadPet ) + return; + + Mobile from = state.Mobile; + + if ( info.ButtonID == 1 ) + { + if ( m_Pet.Map == null || !m_Pet.Map.CanFit( m_Pet.Location, 16, false, false ) ) + { + from.SendLocalizedMessage( 503256 ); // You fail to resurrect the creature. + return; + } + + m_Pet.PlaySound( 0x214 ); + m_Pet.FixedEffect( 0x376A, 10, 16 ); + m_Pet.ResurrectPet(); + + double decreaseAmount; + + if( from == m_Pet.ControlMaster ) + decreaseAmount = 0.1; + else + decreaseAmount = 0.2; + + for ( int i = 0; i < m_Pet.Skills.Length; ++i ) //Decrease all skills on pet. + m_Pet.Skills[i].Base -= decreaseAmount; + } + + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/PlayerVendorGumps.cs b/Scripts/Gumps/PlayerVendorGumps.cs new file mode 100644 index 000000000..706f34cf3 --- /dev/null +++ b/Scripts/Gumps/PlayerVendorGumps.cs @@ -0,0 +1,960 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; +using Server.HuePickers; +using Server.Multis; + +namespace Server.Gumps +{ + public class PlayerVendorBuyGump : Gump + { + private PlayerVendor m_Vendor; + private VendorItem m_VI; + + public PlayerVendorBuyGump( PlayerVendor vendor, VendorItem vi ) : base( 100, 200 ) + { + m_Vendor = vendor; + m_VI = vi; + + AddBackground( 100, 10, 300, 150, 5054 ); + + AddHtmlLocalized( 125, 20, 250, 24, 1019070, false, false ); // You have agreed to purchase: + + if ( vi.Description != null && vi.Description != "" ) + AddLabel( 125, 45, 0, vi.Description ); + else + AddHtmlLocalized( 125, 45, 250, 24, 1019072, false, false ); // an item without a description + + AddHtmlLocalized( 125, 70, 250, 24, 1019071, false, false ); // for the amount of: + AddLabel( 125, 95, 0, vi.Price.ToString() ); + + AddButton( 250, 130, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 282, 130, 100, 24, 1011012, false, false ); // CANCEL + + AddButton( 120, 130, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 152, 130, 100, 24, 1011036, false, false ); // OKAY + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + if ( !m_Vendor.CanInteractWith( from, false ) ) + return; + + if ( m_Vendor.IsOwner( from ) ) + { + m_Vendor.SayTo( from, 503212 ); // You own this shop, just take what you want. + return; + } + + if ( info.ButtonID == 1 ) + { + if ( !m_VI.Valid ) + { + m_Vendor.SayTo( from, 503216 ); // You can't buy that. + return; + } + + int totalGold = 0; + + if ( from.Backpack != null ) + totalGold += from.Backpack.GetAmount( typeof( Gold ) ); + + if ( from.BankBox != null ) + totalGold += from.BankBox.GetAmount( typeof( Gold ) ); + + if ( totalGold < m_VI.Price ) + { + m_Vendor.SayTo( from, 503205 ); // You cannot afford this item. + } + else if ( !from.PlaceInBackpack( m_VI.Item ) ) + { + m_Vendor.SayTo( from, 503204 ); // You do not have room in your backpack for this. + } + else + { + int leftPrice = m_VI.Price; + + if ( from.Backpack != null ) + leftPrice -= from.Backpack.ConsumeUpTo( typeof( Gold ), leftPrice ); + + if ( leftPrice > 0 && from.BankBox != null ) + from.BankBox.ConsumeUpTo( typeof( Gold ), leftPrice ); + + m_Vendor.HoldGold += m_VI.Price; + + from.SendLocalizedMessage( 503201 ); // You take the item. + } + } + else + { + from.SendLocalizedMessage( 503207 ); // Cancelled purchase. + } + } + } + + public class PlayerVendorOwnerGump : Gump + { + private PlayerVendor m_Vendor; + + public PlayerVendorOwnerGump( PlayerVendor vendor ) : base( 50, 200 ) + { + m_Vendor = vendor; + + int perDay = m_Vendor.ChargePerDay; + + AddPage( 0 ); + AddBackground( 25, 10, 530, 140, 5054 ); + + AddHtmlLocalized( 425, 25, 120, 20, 1019068, false, false ); // See goods + AddButton( 390, 25, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 425, 48, 120, 20, 1019069, false, false ); // Customize + AddButton( 390, 48, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 425, 72, 120, 20, 1011012, false, false ); // CANCEL + AddButton( 390, 71, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 40, 72, 260, 20, 1038321, false, false ); // Gold held for you: + AddLabel( 300, 72, 0, m_Vendor.HoldGold.ToString() ); + AddHtmlLocalized( 40, 96, 260, 20, 1038322, false, false ); // Gold held in my account: + AddLabel( 300, 96, 0, m_Vendor.BankAccount.ToString() ); + + //AddHtmlLocalized( 40, 120, 260, 20, 1038324, false, false ); // My charge per day is: + // Localization has changed, we must use a string here + AddHtml( 40, 120, 260, 20, "My charge per day is:", false, false ); + AddLabel( 300, 120, 0, perDay.ToString() ); + + double days = (m_Vendor.HoldGold + m_Vendor.BankAccount) / ((double)perDay); + + AddHtmlLocalized( 40, 25, 260, 20, 1038318, false, false ); // Amount of days I can work: + AddLabel( 300, 25, 0, ((int)days).ToString() ); + AddHtmlLocalized( 40, 48, 260, 20, 1038319, false, false ); // Earth days: + AddLabel( 300, 48, 0, ((int)(days / 12.0)).ToString() ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + switch ( info.ButtonID ) + { + case 1: + { + m_Vendor.OpenBackpack( from ); + + break; + } + case 2: + { + from.SendGump( new PlayerVendorCustomizeGump( m_Vendor, from ) ); + + break; + } + } + } + } + + public class NewPlayerVendorOwnerGump : Gump + { + private PlayerVendor m_Vendor; + + public NewPlayerVendorOwnerGump( PlayerVendor vendor ) : base( 50, 200 ) + { + m_Vendor = vendor; + + int perRealWorldDay = vendor.ChargePerRealWorldDay; + int goldHeld = vendor.HoldGold; + + AddBackground( 25, 10, 530, 180, 0x13BE ); + + AddImageTiled( 35, 20, 510, 160, 0xA40 ); + AddAlphaRegion( 35, 20, 510, 160 ); + + AddImage( 10, 0, 0x28DC ); + AddImage( 537, 175, 0x28DC ); + AddImage( 10, 175, 0x28DC ); + AddImage( 537, 0, 0x28DC ); + + if ( goldHeld < perRealWorldDay ) + { + int goldNeeded = perRealWorldDay - goldHeld; + + AddHtmlLocalized( 40, 35, 260, 20, 1038320, 0x7FFF, false, false ); // Gold needed for 1 day of vendor salary: + AddLabel( 300, 35, 0x1F, goldNeeded.ToString() ); + } + else + { + int days = goldHeld / perRealWorldDay; + + AddHtmlLocalized( 40, 35, 260, 20, 1038318, 0x7FFF, false, false ); // # of days Vendor salary is paid for: + AddLabel( 300, 35, 0x480, days.ToString() ); + } + + AddHtmlLocalized( 40, 58, 260, 20, 1038324, 0x7FFF, false, false ); // My charge per real world day is: + AddLabel( 300, 58, 0x480, perRealWorldDay.ToString() ); + + AddHtmlLocalized( 40, 82, 260, 20, 1038322, 0x7FFF, false, false ); // Gold held in my account: + AddLabel( 300, 82, 0x480, goldHeld.ToString() ); + + AddHtmlLocalized( 40, 108, 260, 20, 1062509, 0x7FFF, false, false ); // Shop Name: + AddLabel( 140, 106, 0x66D, vendor.ShopName ); + + if ( vendor is RentedVendor ) + { + int days, hours; + ((RentedVendor)vendor).ComputeRentalExpireDelay( out days, out hours ); + + AddLabel( 38, 132, 0x480, String.Format( "Location rental will expire in {0} day{1} and {2} hour{3}.", days, days != 1 ? "s" : "", hours, hours != 1 ? "s" : "" ) ); + } + + AddButton( 390, 24, 0x15E1, 0x15E5, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 408, 21, 120, 20, 1019068, 0x7FFF, false, false ); // See goods + + AddButton( 390, 44, 0x15E1, 0x15E5, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 408, 41, 120, 20, 1019069, 0x7FFF, false, false ); // Customize + + AddButton( 390, 64, 0x15E1, 0x15E5, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 408, 61, 120, 20, 1062434, 0x7FFF, false, false ); // Rename Shop + + AddButton( 390, 84, 0x15E1, 0x15E5, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 408, 81, 120, 20, 3006217, 0x7FFF, false, false ); // Rename Vendor + + AddButton( 390, 104, 0x15E1, 0x15E5, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 408, 101, 120, 20, 3006123, 0x7FFF, false, false ); // Open Paperdoll + + AddButton( 390, 124, 0x15E1, 0x15E5, 6, GumpButtonType.Reply, 0 ); + AddLabel( 408, 121, 0x480, "Collect Gold" ); + + AddButton( 390, 144, 0x15E1, 0x15E5, 7, GumpButtonType.Reply, 0 ); + AddLabel( 408, 141, 0x480, "Dismiss Vendor" ); + + AddButton( 390, 162, 0x15E1, 0x15E5, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 408, 161, 120, 20, 1011012, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 1 || info.ButtonID == 2 ) // See goods or Customize + m_Vendor.CheckTeleport( from ); + + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + switch ( info.ButtonID ) + { + case 1: // See goods + { + m_Vendor.OpenBackpack( from ); + + break; + } + case 2: // Customize + { + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + + break; + } + case 3: // Rename Shop + { + m_Vendor.RenameShop( from ); + + break; + } + case 4: // Rename Vendor + { + m_Vendor.Rename( from ); + + break; + } + case 5: // Open Paperdoll + { + m_Vendor.DisplayPaperdollTo( from ); + + break; + } + case 6: // Collect Gold + { + m_Vendor.CollectGold( from ); + + break; + } + case 7: // Dismiss Vendor + { + m_Vendor.Dismiss( from ); + + break; + } + } + } + } + + public class PlayerVendorCustomizeGump : Gump + { + private Mobile m_Vendor; + + private class CustomItem + { + private Type m_Type; + private int m_ItemID; + private int m_LocNum; + private int m_ArtNum; + private bool m_LongText; + + public CustomItem( int itemID, int loc ) : this( null, itemID, loc, 0, false ) + { + } + + public CustomItem( int itemID, int loc, bool longText ) : this( null, itemID, loc, 0, longText ) + { + } + + public CustomItem( Type type, int loc ) : this( type, loc, 0 ) + { + } + + public CustomItem( Type type, int loc, int art ) : this( type, 0, loc, art, false ) + { + } + + public CustomItem( Type type, int itemID, int loc, int art, bool longText ) + { + m_Type = type; + m_ItemID = itemID; + m_LocNum = loc; + m_ArtNum = art; + m_LongText = longText; + } + + public Item Create() + { + if ( m_Type == null ) + return null; + + Item i = null; + + try + { + ConstructorInfo ctor = m_Type.GetConstructor( new Type[0] ); + if ( ctor != null ) + i = ctor.Invoke( null ) as Item; + } + catch + { + } + + return i; + } + + public Type Type{ get{ return m_Type; } } + public int ItemID{ get{ return m_ItemID; } } + public int LocNumber{ get{ return m_LocNum; } } + public int ArtNumber{ get{ return m_ArtNum; } } + public bool LongText{ get{ return m_LongText; } } + } + + private class CustomCategory + { + private CustomItem[] m_Entries; + private Layer m_Layer; + private bool m_CanDye; + private int m_LocNum; + + public CustomCategory( Layer layer, int loc, bool canDye, CustomItem[] items ) + { + m_Entries = items; + m_CanDye = canDye; + m_Layer = layer; + m_LocNum = loc; + } + + public bool CanDye{ get{ return m_CanDye; } } + public CustomItem[] Entries{ get{ return m_Entries; } } + public Layer Layer{ get{ return m_Layer; } } + public int LocNumber{ get{ return m_LocNum; } } + } + + private static CustomCategory[] Categories = new CustomCategory[]{ + new CustomCategory( Layer.InnerTorso, 1011357, true, new CustomItem[]{// Upper Torso + new CustomItem( typeof( Shirt ), 1011359, 5399 ), + new CustomItem( typeof( FancyShirt ), 1011360, 7933 ), + new CustomItem( typeof( PlainDress ), 1011363, 7937 ), + new CustomItem( typeof( FancyDress ), 1011364, 7935 ), + new CustomItem( typeof( Robe ), 1011365, 7939 ) + } ), + + new CustomCategory( Layer.MiddleTorso, 1011371, true, new CustomItem[]{//Over chest + new CustomItem( typeof( Doublet ), 1011358, 8059 ), + new CustomItem( typeof( Tunic ), 1011361, 8097 ), + new CustomItem( typeof( JesterSuit ), 1011366, 8095 ), + new CustomItem( typeof( BodySash ), 1011372, 5441 ), + new CustomItem( typeof( Surcoat ), 1011362, 8189 ), + new CustomItem( typeof( HalfApron ), 1011373, 5435 ), + new CustomItem( typeof( FullApron ), 1011374, 5437 ), + } ), + + new CustomCategory( Layer.Shoes, 1011388, true, new CustomItem[]{//Footwear + new CustomItem( typeof( Sandals ), 1011389, 5901 ), + new CustomItem( typeof( Shoes ), 1011390, 5904 ), + new CustomItem( typeof( Boots ), 1011391, 5899 ), + new CustomItem( typeof( ThighBoots ), 1011392, 5906 ), + } ), + + new CustomCategory( Layer.Helm, 1011375, true, new CustomItem[]{//Hats + new CustomItem( typeof( SkullCap ), 1011376, 5444 ), + new CustomItem( typeof( Bandana ), 1011377, 5440 ), + new CustomItem( typeof( FloppyHat ), 1011378, 5907 ), + new CustomItem( typeof( WideBrimHat ), 1011379, 5908 ), + new CustomItem( typeof( Cap ), 1011380, 5909 ), + new CustomItem( typeof( TallStrawHat ), 1011382, 5910 ) + } ), + + new CustomCategory( Layer.Helm, 1015319, true, new CustomItem[]{//More Hats + new CustomItem( typeof( StrawHat ), 1011382, 5911 ), + new CustomItem( typeof( WizardsHat ), 1011383, 5912 ), + new CustomItem( typeof( Bonnet ), 1011384, 5913 ), + new CustomItem( typeof( FeatheredHat ), 1011385, 5914 ), + new CustomItem( typeof( TricorneHat ), 1011386, 5915 ), + new CustomItem( typeof( JesterHat ), 1011387, 5916 ) + } ), + + new CustomCategory( Layer.Pants, 1011367, true, new CustomItem[]{ //Lower Torso + new CustomItem( typeof( LongPants ), 1011368, 5433 ), + new CustomItem( typeof( Kilt ), 1011369, 5431 ), + new CustomItem( typeof( Skirt ), 1011370, 5398 ), + } ), + + new CustomCategory( Layer.Cloak, 1011393, true, new CustomItem[]{ // Back + new CustomItem( typeof( Cloak ), 1011394, 5397 ) + } ), + + new CustomCategory( Layer.Hair, 1011395, true, new CustomItem[]{ // Hair + new CustomItem( 0x203B, 1011052 ), + new CustomItem( 0x203C, 1011053 ), + new CustomItem( 0x203D, 1011054 ), + new CustomItem( 0x2044, 1011055 ), + new CustomItem( 0x2045, 1011047 ), + new CustomItem( 0x204A, 1011050 ), + new CustomItem( 0x2047, 1011396 ), + new CustomItem( 0x2048, 1011048 ), + new CustomItem( 0x2049, 1011049 ), + } ), + + new CustomCategory( Layer.FacialHair, 1015320, true, new CustomItem[]{//Facial Hair + new CustomItem( 0x2041, 1011062 ), + new CustomItem( 0x203F, 1011060 ), + new CustomItem( 0x204B, 1015321, true ), + new CustomItem( 0x203E, 1011061 ), + new CustomItem( 0x204C, 1015322, true ), + new CustomItem( 0x2040, 1015323 ), + new CustomItem( 0x204D, 1011401 ), + } ), + + new CustomCategory( Layer.FirstValid, 1011397, false, new CustomItem[]{//Held items + new CustomItem( typeof( FishingPole ), 1011406, 3520 ), + new CustomItem( typeof( Pickaxe ), 1011407, 3717 ), + new CustomItem( typeof( Pitchfork ), 1011408, 3720 ), + new CustomItem( typeof( Cleaver ), 1015324, 3778 ), + new CustomItem( typeof( Mace ), 1011409, 3933 ), + new CustomItem( typeof( Torch ), 1011410, 3940 ), + new CustomItem( typeof( Hammer ), 1011411, 4020 ), + new CustomItem( typeof( Longsword ), 1011412, 3936 ), + new CustomItem( typeof( GnarledStaff ), 1011413, 5113 ) + } ), + + new CustomCategory( Layer.FirstValid, 1015325, false, new CustomItem[]{//More held items + new CustomItem( typeof( Crossbow ), 1011414, 3920 ), + new CustomItem( typeof( WarMace ), 1011415, 5126 ), + new CustomItem( typeof( TwoHandedAxe ), 1011416, 5186 ), + new CustomItem( typeof( Spear ), 1011417, 3939 ), + new CustomItem( typeof( Katana ), 1011418, 5118 ), + new CustomItem( typeof( Spellbook ), 1011419, 3834 ) + } ) + }; + + public PlayerVendorCustomizeGump( Mobile v, Mobile from ) : base( 30, 40 ) + { + m_Vendor = v; + int x,y; + + from.CloseGump( typeof( PlayerVendorCustomizeGump ) ); + + AddPage( 0 ); + AddBackground( 0, 0, 585, 393, 5054 ); + AddBackground( 195, 36, 387, 275, 3000 ); + AddHtmlLocalized( 10, 10, 565, 18, 1011356, false, false ); //
VENDOR CUSTOMIZATION MENU
+ AddHtmlLocalized( 60, 355, 150, 18, 1011036, false, false ); // OKAY + AddButton( 25, 355, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 320, 355, 150, 18, 1011012, false, false ); // CANCEL + AddButton( 285, 355, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + + y = 35; + for ( int i=0;i 0 ) + { + int cnum = info.Switches[0]; + int cat = cnum%256; + int ent = cnum>>8; + + if ( cat < Categories.Length && cat >= 0 ) + { + if ( ent < Categories[cat].Entries.Length && ent >= 0 ) + { + Item item = m_Vendor.FindItemOnLayer( Categories[cat].Layer ); + + if ( item != null ) + item.Delete(); + + List items = m_Vendor.Items; + + for ( int i = 0; item == null && i < items.Count; ++i ) + { + Item checkitem = items[i]; + Type type = checkitem.GetType(); + + for ( int j = 0; item == null && j < Categories[cat].Entries.Length; ++j ) + { + if ( type == Categories[cat].Entries[j].Type ) + item = checkitem; + } + } + + if ( item != null ) + item.Delete(); + + if ( Categories[cat].Layer == Layer.FacialHair ) + { + if ( m_Vendor.Female ) + from.SendLocalizedMessage( 1010639 ); // You cannot place facial hair on a woman! + else + m_Vendor.FacialHairItemID = Categories[cat].Entries[ent].ItemID; + } + else if ( Categories[cat].Layer == Layer.Hair ) + m_Vendor.HairItemID = Categories[cat].Entries[ent].ItemID; + + else + { + item = Categories[cat].Entries[ent].Create(); + + if ( item != null ) + { + item.Layer = Categories[cat].Layer; + + if ( !m_Vendor.EquipItem( item ) ) + item.Delete(); + } + } + + from.SendGump( new PlayerVendorCustomizeGump( m_Vendor, from ) ); + } + } + else + { + cat -= 100; + + if ( cat < 100 ) + { + if ( cat < Categories.Length && cat >= 0 ) + { + Item item = null; + + List items = m_Vendor.Items; + + for ( int i = 0; item == null && i < items.Count; ++i ) + { + Item checkitem = items[i]; + Type type = checkitem.GetType(); + + for ( int j = 0; item == null && j < Categories[cat].Entries.Length; ++j ) + { + if ( type == Categories[cat].Entries[j].Type ) + item = checkitem; + } + } + + if ( item != null ) + new PVHuePicker( item, m_Vendor, from ).SendTo( state ); + } + } + else + { + cat -= 100; + + if ( cat < Categories.Length && cat >= 0 ) + { + Item item = null; + + List items = m_Vendor.Items; + + for ( int i = 0; item == null && i < items.Count; ++i ) + { + Item checkitem = items[i]; + Type type = checkitem.GetType(); + + for ( int j = 0; item == null && j < Categories[cat].Entries.Length; ++j ) + { + if ( type == Categories[cat].Entries[j].Type ) + item = checkitem; + } + } + + if ( item != null ) + item.Delete(); + + from.SendGump( new PlayerVendorCustomizeGump( m_Vendor, from ) ); + } + } + } + } + } + + private class PVHuePicker : HuePicker + { + private Item m_Item; + private Mobile m_Vendor; + private Mobile m_Mob; + + public PVHuePicker( Item item, Mobile v, Mobile from ) : base( (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair) ? 0xFAB : item.ItemID ) + { + m_Vendor = v; + m_Item = item; + m_Mob = from; + } + + public override void OnResponse( int hue ) + { + if ( m_Item.Deleted ) + return; + + if ( m_Vendor is PlayerVendor && !((PlayerVendor)m_Vendor).CanInteractWith( m_Mob, true ) ) + return; + + if ( m_Vendor is PlayerBarkeeper && !((PlayerBarkeeper)m_Vendor).IsOwner( m_Mob ) ) + return; + + m_Item.Hue = hue; + m_Mob.SendGump( new PlayerVendorCustomizeGump( m_Vendor, m_Mob ) ); + } + } + } + + public class NewPlayerVendorCustomizeGump : Gump + { + private PlayerVendor m_Vendor; + + private class HairOrBeard + { + private int m_ItemID; + private int m_Name; + + public int ItemID{ get{ return m_ItemID; } } + public int Name{ get{ return m_Name; } } + + public HairOrBeard( int itemID, int name ) + { + m_ItemID = itemID; + m_Name = name; + } + } + + private static HairOrBeard[] m_HairStyles = new HairOrBeard[] + { + new HairOrBeard( 0x203B, 1011052 ), // Short + new HairOrBeard( 0x203C, 1011053 ), // Long + new HairOrBeard( 0x203D, 1011054 ), // Ponytail + new HairOrBeard( 0x2044, 1011055 ), // Mohawk + new HairOrBeard( 0x2045, 1011047 ), // Pageboy + new HairOrBeard( 0x204A, 1011050 ), // Topknot + new HairOrBeard( 0x2047, 1011396 ), // Curly + new HairOrBeard( 0x2048, 1011048 ), // Receding + new HairOrBeard( 0x2049, 1011049 ) // 2-tails + }; + + private static HairOrBeard[] m_BeardStyles = new HairOrBeard[] + { + new HairOrBeard( 0x2041, 1011062 ), // Mustache + new HairOrBeard( 0x203F, 1011060 ), // Short beard + new HairOrBeard( 0x204B, 1015321 ), // Short Beard & Moustache + new HairOrBeard( 0x203E, 1011061 ), // Long beard + new HairOrBeard( 0x204C, 1015322 ), // Long Beard & Moustache + new HairOrBeard( 0x2040, 1015323 ), // Goatee + new HairOrBeard( 0x204D, 1011401 ) // Vandyke + }; + + public NewPlayerVendorCustomizeGump( PlayerVendor vendor ) : base( 50, 50 ) + { + m_Vendor = vendor; + + AddBackground( 0, 0, 370, 370, 0x13BE ); + + AddImageTiled( 10, 10, 350, 20, 0xA40 ); + AddImageTiled( 10, 40, 350, 20, 0xA40 ); + AddImageTiled( 10, 70, 350, 260, 0xA40 ); + AddImageTiled( 10, 340, 350, 20, 0xA40 ); + + AddAlphaRegion( 10, 10, 350, 350 ); + + AddHtmlLocalized( 10, 12, 350, 18, 1011356, 0x7FFF, false, false ); //
VENDOR CUSTOMIZATION MENU
+ + AddHtmlLocalized( 10, 42, 150, 18, 1062459, 0x421F, false, false ); //
HAIR
+ + for ( int i = 0; i < m_HairStyles.Length; i++ ) + { + HairOrBeard hair = m_HairStyles[i]; + + AddButton( 10, 70 + i * 20, 0xFA5, 0xFA7, 0x100 | i, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 72 + i * 20, 110, 18, hair.Name, 0x7FFF, false, false ); + } + + AddButton( 10, 70 + m_HairStyles.Length * 20, 0xFB1, 0xFB3, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 72 + m_HairStyles.Length * 20, 110, 18, 1011403, 0x7FFF, false, false ); // Remove + + AddButton( 10, 70 + (m_HairStyles.Length + 1) * 20, 0xFA5, 0xFA7, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 72 + (m_HairStyles.Length + 1) * 20, 110, 18, 1011402, 0x7FFF, false, false ); // Color + + if ( vendor.Female ) + { + AddButton( 160, 290, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 195, 292, 160, 18, 1015327, 0x7FFF, false, false ); // Male + + AddHtmlLocalized( 195, 312, 160, 18, 1015328, 0x421F, false, false ); // Female + } + else + { + AddHtmlLocalized( 160, 42, 210, 18, 1062460, 0x421F, false, false ); //
BEARD
+ + for ( int i = 0; i < m_BeardStyles.Length; i++ ) + { + HairOrBeard beard = m_BeardStyles[i]; + + AddButton( 160, 70 + i * 20, 0xFA5, 0xFA7, 0x200 | i, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 195, 72 + i * 20, 160, 18, beard.Name, 0x7FFF, false, false ); + } + + AddButton( 160, 70 + m_BeardStyles.Length * 20, 0xFB1, 0xFB3, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 195, 72 + m_BeardStyles.Length * 20, 160, 18, 1011403, 0x7FFF, false, false ); // Remove + + AddButton( 160, 70 + (m_BeardStyles.Length + 1) * 20, 0xFA5, 0xFA7, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 195, 72 + (m_BeardStyles.Length + 1) * 20, 160, 18, 1011402, 0x7FFF, false, false ); // Color + + AddHtmlLocalized( 195, 292, 160, 18, 1015327, 0x421F, false, false ); // Male + + AddButton( 160, 310, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 195, 312, 160, 18, 1015328, 0x7FFF, false, false ); // Female + } + + AddButton( 10, 340, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 342, 305, 18, 1060675, 0x7FFF, false, false ); // CLOSE + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + switch ( info.ButtonID ) + { + case 0: // CLOSE + { + m_Vendor.Direction = m_Vendor.GetDirectionTo( from ); + m_Vendor.Animate( 32, 5, 1, true, false, 0 ); // bow + m_Vendor.SayTo( from, 1043310 + Utility.Random( 12 ) ); // a little random speech + + break; + } + case 1: // Female/Male + { + if ( m_Vendor.Female ) + { + m_Vendor.BodyValue = 400; + m_Vendor.Female = false; + } + else + { + m_Vendor.BodyValue = 401; + m_Vendor.Female = true; + + m_Vendor.FacialHairItemID = 0; + } + + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + + break; + } + case 2: // Remove hair + { + m_Vendor.HairItemID = 0; + + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + + break; + } + case 3: // Color hair + { + if ( m_Vendor.HairItemID > 0 ) + { + new PVHuePicker( m_Vendor, false, from ).SendTo( from.NetState ); + } + else + { + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + } + + break; + } + case 4: // Remove beard + { + m_Vendor.FacialHairItemID = 0; + + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + + break; + } + case 5: // Color beard + { + if ( m_Vendor.FacialHairItemID > 0 ) + { + new PVHuePicker( m_Vendor, true, from ).SendTo( from.NetState ); + } + else + { + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + } + + break; + } + default: + { + if ( (info.ButtonID & 0x100) != 0 ) // Hair style selected + { + int index = info.ButtonID & 0xFF; + + if ( index >= m_HairStyles.Length ) + return; + + HairOrBeard hairStyle = m_HairStyles[index]; + + m_Vendor.HairItemID = hairStyle.ItemID; + + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + } + else if ( (info.ButtonID & 0x200) != 0 ) // Beard style selected + { + if ( m_Vendor.Female ) + return; + + int index = info.ButtonID & 0xFF; + + if ( index >= m_BeardStyles.Length ) + return; + + HairOrBeard beardStyle = m_BeardStyles[index]; + + m_Vendor.FacialHairItemID = beardStyle.ItemID; + + from.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + } + + break; + } + } + } + + private class PVHuePicker : HuePicker + { + private PlayerVendor m_Vendor; + private bool m_FacialHair; + private Mobile m_From; + + public PVHuePicker( PlayerVendor vendor, bool facialHair, Mobile from ) : base( 0xFAB ) + { + m_Vendor = vendor; + m_FacialHair = facialHair; + m_From = from; + } + + public override void OnResponse( int hue ) + { + if ( !m_Vendor.CanInteractWith( m_From, true ) ) + return; + + if ( m_FacialHair ) + m_Vendor.FacialHairHue = hue; + else + m_Vendor.HairHue = hue; + + m_From.SendGump( new NewPlayerVendorCustomizeGump( m_Vendor ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/PolymorphGump.cs b/Scripts/Gumps/PolymorphGump.cs new file mode 100644 index 000000000..c3b9e1eeb --- /dev/null +++ b/Scripts/Gumps/PolymorphGump.cs @@ -0,0 +1,249 @@ +using System; +using Server; +using Server.Network; +using Server.Targets; +using Server.Spells; +using Server.Spells.Seventh; + +namespace Server.Gumps +{ + public class PolymorphEntry + { + public static readonly PolymorphEntry Chicken = new PolymorphEntry( 8401, 0xD0, 1015236, 15, 10 ); + public static readonly PolymorphEntry Dog = new PolymorphEntry( 8405, 0xD9, 1015237, 17, 10 ); + public static readonly PolymorphEntry Wolf = new PolymorphEntry( 8426, 0xE1, 1015238, 18, 10 ); + public static readonly PolymorphEntry Panther = new PolymorphEntry( 8473, 0xD6, 1015239, 20, 14 ); + public static readonly PolymorphEntry Gorilla = new PolymorphEntry( 8437, 0x1D, 1015240, 23, 10 ); + public static readonly PolymorphEntry BlackBear = new PolymorphEntry( 8399, 0xD3, 1015241, 22, 10 ); + public static readonly PolymorphEntry GrizzlyBear = new PolymorphEntry( 8411, 0xD4, 1015242, 22, 12 ); + public static readonly PolymorphEntry PolarBear = new PolymorphEntry( 8417, 0xD5, 1015243, 26, 10 ); + public static readonly PolymorphEntry HumanMale = new PolymorphEntry( 8397, 0x190, 1015244, 29, 8 ); + public static readonly PolymorphEntry HumanFemale = new PolymorphEntry( 8398, 0x191, 1015254, 29, 10 ); + public static readonly PolymorphEntry Slime = new PolymorphEntry( 8424, 0x33, 1015246, 5, 10 ); + public static readonly PolymorphEntry Orc = new PolymorphEntry( 8416, 0x11, 1015247, 29, 10 ); + public static readonly PolymorphEntry LizardMan = new PolymorphEntry( 8414, 0x21, 1015248, 26, 10 ); + public static readonly PolymorphEntry Gargoyle = new PolymorphEntry( 8409, 0x04, 1015249, 22, 10 ); + public static readonly PolymorphEntry Ogre = new PolymorphEntry( 8415, 0x01, 1015250, 24, 9 ); + public static readonly PolymorphEntry Troll = new PolymorphEntry( 8425, 0x36, 1015251, 25, 9 ); + public static readonly PolymorphEntry Ettin = new PolymorphEntry( 8408, 0x02, 1015252, 25, 8 ); + public static readonly PolymorphEntry Daemon = new PolymorphEntry( 8403, 0x09, 1015253, 25, 8 ); + + + private int m_Art, m_Body, m_Num, m_X, m_Y; + + private PolymorphEntry( int Art, int Body, int LocNum, int X, int Y ) + { + m_Art = Art; + m_Body = Body; + m_Num = LocNum; + m_X = X; + m_Y = Y; + } + + public int ArtID { get { return m_Art; } } + public int BodyID { get { return m_Body; } } + public int LocNumber{ get { return m_Num; } } + public int X{ get{ return m_X; } } + public int Y{ get{ return m_Y; } } + } + + + public class PolymorphGump : Gump + { + private class PolymorphCategory + { + private int m_Num; + private PolymorphEntry[] m_Entries; + + public PolymorphCategory( int num, params PolymorphEntry[] entries ) + { + m_Num = num; + m_Entries = entries; + } + + public PolymorphEntry[] Entries{ get { return m_Entries; } } + public int LocNumber{ get { return m_Num; } } + } + + private static PolymorphCategory[] Categories = new PolymorphCategory[] + { + new PolymorphCategory( 1015235, // Animals + PolymorphEntry.Chicken, + PolymorphEntry.Dog, + PolymorphEntry.Wolf, + PolymorphEntry.Panther, + PolymorphEntry.Gorilla, + PolymorphEntry.BlackBear, + PolymorphEntry.GrizzlyBear, + PolymorphEntry.PolarBear, + PolymorphEntry.HumanMale ), + + new PolymorphCategory( 1015245, // Monsters + PolymorphEntry.Slime, + PolymorphEntry.Orc, + PolymorphEntry.LizardMan, + PolymorphEntry.Gargoyle, + PolymorphEntry.Ogre, + PolymorphEntry.Troll, + PolymorphEntry.Ettin, + PolymorphEntry.Daemon, + PolymorphEntry.HumanFemale ) + }; + + + private Mobile m_Caster; + private Item m_Scroll; + + public PolymorphGump( Mobile caster, Item scroll ) : base( 50, 50 ) + { + m_Caster = caster; + m_Scroll = scroll; + + int x,y; + AddPage( 0 ); + AddBackground( 0, 0, 585, 393, 5054 ); + AddBackground( 195, 36, 387, 275, 3000 ); + AddHtmlLocalized( 0, 0, 510, 18, 1015234, false, false ); //
Polymorph Selection Menu
+ AddHtmlLocalized( 60, 355, 150, 18, 1011036, false, false ); // OKAY + AddButton( 25, 355, 4005, 4007, 1, GumpButtonType.Reply, 1 ); + AddHtmlLocalized( 320, 355, 150, 18, 1011012, false, false ); // CANCEL + AddButton( 285, 355, 4005, 4007, 0, GumpButtonType.Reply, 2 ); + + y = 35; + for ( int i=0;i 0 ) + { + int cnum = info.Switches[0]; + int cat = cnum%256; + int ent = cnum>>8; + + if ( cat >= 0 && cat < Categories.Length ) + { + if ( ent >= 0 && ent < Categories[cat].Entries.Length ) + { + Spell spell = new PolymorphSpell( m_Caster, m_Scroll, Categories[cat].Entries[ent].BodyID ); + spell.Cast(); + } + } + } + } + } + + + public class NewPolymorphGump : Gump + { + private static readonly PolymorphEntry[] m_Entries = new PolymorphEntry[] + { + PolymorphEntry.Chicken, + PolymorphEntry.Dog, + PolymorphEntry.Wolf, + PolymorphEntry.Panther, + PolymorphEntry.Gorilla, + PolymorphEntry.BlackBear, + PolymorphEntry.GrizzlyBear, + PolymorphEntry.PolarBear, + PolymorphEntry.HumanMale, + PolymorphEntry.HumanFemale, + PolymorphEntry.Slime, + PolymorphEntry.Orc, + PolymorphEntry.LizardMan, + PolymorphEntry.Gargoyle, + PolymorphEntry.Ogre, + PolymorphEntry.Troll, + PolymorphEntry.Ettin, + PolymorphEntry.Daemon + }; + + + private Mobile m_Caster; + private Item m_Scroll; + + public NewPolymorphGump( Mobile caster, Item scroll ) : base( 0, 0 ) + { + m_Caster = caster; + m_Scroll = scroll; + + AddPage( 0 ); + + AddBackground( 0, 0, 520, 404, 0x13BE ); + AddImageTiled( 10, 10, 500, 20, 0xA40 ); + AddImageTiled( 10, 40, 500, 324, 0xA40 ); + AddImageTiled( 10, 374, 500, 20, 0xA40 ); + AddAlphaRegion( 10, 10, 500, 384 ); + + AddHtmlLocalized( 14, 12, 500, 20, 1015234, 0x7FFF, false, false ); //
Polymorph Selection Menu
+ + AddButton( 10, 374, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 376, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + + for ( int i = 0; i < m_Entries.Length; i++ ) + { + PolymorphEntry entry = m_Entries[i]; + + int page = i / 10 + 1; + int pos = i % 10; + + if ( pos == 0 ) + { + if ( page > 1 ) + { + AddButton( 400, 374, 0xFA5, 0xFA7, 0, GumpButtonType.Page, page ); + AddHtmlLocalized( 440, 376, 60, 20, 1043353, 0x7FFF, false, false ); // Next + } + + AddPage( page ); + + if ( page > 1 ) + { + AddButton( 300, 374, 0xFAE, 0xFB0, 0, GumpButtonType.Page, 1 ); + AddHtmlLocalized( 340, 376, 60, 20, 1011393, 0x7FFF, false, false ); // Back + } + } + + int x = ( pos % 2 == 0 ) ? 14 : 264; + int y = ( pos / 2 ) * 64 + 44; + + AddImageTiledButton( x, y, 0x918, 0x919, i + 1, GumpButtonType.Reply, 0, entry.ArtID, 0x0, entry.X, entry.Y ); + AddHtmlLocalized( x + 84, y, 250, 60, entry.LocNumber, 0x7FFF, false, false ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int idx = info.ButtonID - 1; + + if ( idx < 0 || idx >= m_Entries.Length ) + return; + + Spell spell = new PolymorphSpell( m_Caster, m_Scroll, m_Entries[idx].BodyID ); + spell.Cast(); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/PropsConfig.cs b/Scripts/Gumps/Properties/PropsConfig.cs new file mode 100644 index 000000000..1750d1f26 --- /dev/null +++ b/Scripts/Gumps/Properties/PropsConfig.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Gumps +{ + public class PropsConfig + { + public static readonly bool OldStyle = false; + + public static readonly int GumpOffsetX = 30; + public static readonly int GumpOffsetY = 30; + + public static readonly int TextHue = 0; + public static readonly int TextOffsetX = 2; + + public static readonly int OffsetGumpID = 0x0A40; // Pure black + public static readonly int HeaderGumpID = OldStyle ? 0x0BBC : 0x0E14; // Light offwhite, textured : Dark navy blue, textured + public static readonly int EntryGumpID = 0x0BBC; // Light offwhite, textured + public static readonly int BackGumpID = 0x13BE; // Gray slate/stoney + public static readonly int SetGumpID = OldStyle ? 0x0000 : 0x0E14; // Empty : Dark navy blue, textured + + public static readonly int SetWidth = 20; + public static readonly int SetOffsetX = OldStyle ? 4 : 2, SetOffsetY = 2; + public static readonly int SetButtonID1 = 0x15E1; // Arrow pointing right + public static readonly int SetButtonID2 = 0x15E5; // " pressed + + public static readonly int PrevWidth = 20; + public static readonly int PrevOffsetX = 2, PrevOffsetY = 2; + public static readonly int PrevButtonID1 = 0x15E3; // Arrow pointing left + public static readonly int PrevButtonID2 = 0x15E7; // " pressed + + public static readonly int NextWidth = 20; + public static readonly int NextOffsetX = 2, NextOffsetY = 2; + public static readonly int NextButtonID1 = 0x15E1; // Arrow pointing right + public static readonly int NextButtonID2 = 0x15E5; // " pressed + + public static readonly int OffsetSize = 1; + + public static readonly int EntryHeight = 20; + public static readonly int BorderSize = 10; + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/PropsGump.cs b/Scripts/Gumps/Properties/PropsGump.cs new file mode 100644 index 000000000..98ef8e725 --- /dev/null +++ b/Scripts/Gumps/Properties/PropsGump.cs @@ -0,0 +1,758 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.Menus; +using Server.Menus.Questions; +using Server.Targeting; +using CPA = Server.CommandPropertyAttribute; + +namespace Server.Gumps +{ + public class PropertiesGump : Gump + { + private ArrayList m_List; + private int m_Page; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static bool PrevLabel = OldStyle, NextLabel = OldStyle; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + private static readonly int NameWidth = 107; + private static readonly int ValueWidth = 128; + + private static readonly int EntryCount = 15; + + private static readonly int TypeWidth = NameWidth + OffsetSize + ValueWidth; + + private static readonly int TotalWidth = OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (EntryCount + 1)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public PropertiesGump( Mobile mobile, object o ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Mobile = mobile; + m_Object = o; + m_List = BuildList(); + + Initialize( 0 ); + } + + public PropertiesGump( Mobile mobile, object o, Stack stack, StackEntry parent ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_List = BuildList(); + + if ( parent != null ) + { + if ( m_Stack == null ) + m_Stack = new Stack(); + + m_Stack.Push( parent ); + } + + Initialize( 0 ); + } + + public PropertiesGump( Mobile mobile, object o, Stack stack, ArrayList list, int page ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Mobile = mobile; + m_Object = o; + m_List = list; + m_Stack = stack; + + Initialize( page ); + } + + private void Initialize( int page ) + { + m_Page = page; + + int count = m_List.Count - (page * EntryCount); + + if ( count < 0 ) + count = 0; + else if ( count > EntryCount ) + count = EntryCount; + + int lastIndex = (page * EntryCount) + count - 1; + + if ( lastIndex >= 0 && lastIndex < m_List.Count && m_List[lastIndex] == null ) + --count; + + int totalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (count + 1)); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + int emptyWidth = TotalWidth - PrevWidth - NextWidth - (OffsetSize * 4) - (OldStyle ? SetWidth + OffsetSize : 0); + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( page > 0 ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1, GumpButtonType.Reply, 0 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, HeaderGumpID ); + + x += emptyWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x, y, NextWidth, EntryHeight, HeaderGumpID ); + + if ( (page + 1) * EntryCount < m_List.Count ) + { + AddButton( x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 2, GumpButtonType.Reply, 1 ); + + if ( NextLabel ) + AddLabel( x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next" ); + } + + for ( int i = 0, index = page * EntryCount; i < count && index < m_List.Count; ++i, ++index ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + object o = m_List[index]; + + if ( o == null ) + { + AddImageTiled( x - OffsetSize, y, TotalWidth, EntryHeight, BackGumpID + 4 ); + } + else if ( o is Type ) + { + Type type = (Type)o; + + AddImageTiled( x, y, TypeWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, TypeWidth - TextOffsetX, EntryHeight, TextHue, type.Name ); + x += TypeWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + } + else if ( o is PropertyInfo ) + { + PropertyInfo prop = (PropertyInfo)o; + + AddImageTiled( x, y, NameWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, NameWidth - TextOffsetX, EntryHeight, TextHue, prop.Name ); + x += NameWidth + OffsetSize; + AddImageTiled( x, y, ValueWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, ValueWidth - TextOffsetX, EntryHeight, TextHue, ValueToString( prop ) ); + x += ValueWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + CPA cpa = GetCPA( prop ); + + if ( prop.CanWrite && cpa != null && m_Mobile.AccessLevel >= cpa.WriteLevel ) + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 3, GumpButtonType.Reply, 0 ); + } + } + } + + public static string[] m_BoolNames = new string[]{ "True", "False" }; + public static object[] m_BoolValues = new object[]{ true, false }; + + public static string[] m_PoisonNames = new string[]{ "None", "Lesser", "Regular", "Greater", "Deadly", "Lethal" }; + public static object[] m_PoisonValues = new object[]{ null, Poison.Lesser, Poison.Regular, Poison.Greater, Poison.Deadly, Poison.Lethal }; + + public class StackEntry + { + public object m_Object; + public PropertyInfo m_Property; + + public StackEntry( object obj, PropertyInfo prop ) + { + m_Object = obj; + m_Property = prop; + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + if ( !Server.Commands.Generic.BaseCommand.IsAccessible( from, m_Object ) ) + { + from.SendMessage( "You may no longer access their properties." ); + return; + } + + switch ( info.ButtonID ) + { + case 0: // Closed + { + if ( m_Stack != null && m_Stack.Count > 0 ) + { + StackEntry entry = (StackEntry)m_Stack.Pop(); + + from.SendGump( new PropertiesGump( from, entry.m_Object, m_Stack, null ) ); + } + + break; + } + case 1: // Previous + { + if ( m_Page > 0 ) + from.SendGump( new PropertiesGump( from, m_Object, m_Stack, m_List, m_Page - 1 ) ); + + break; + } + case 2: // Next + { + if ( (m_Page + 1) * EntryCount < m_List.Count ) + from.SendGump( new PropertiesGump( from, m_Object, m_Stack, m_List, m_Page + 1 ) ); + + break; + } + default: + { + int index = (m_Page * EntryCount) + (info.ButtonID - 3); + + if ( index >= 0 && index < m_List.Count ) + { + PropertyInfo prop = m_List[index] as PropertyInfo; + + if ( prop == null ) + return; + + CPA attr = GetCPA( prop ); + + if ( !prop.CanWrite || attr == null || from.AccessLevel < attr.WriteLevel ) + return; + + Type type = prop.PropertyType; + + if ( IsType( type, typeofMobile ) || IsType( type, typeofItem ) ) + from.SendGump( new SetObjectGump( prop, from, m_Object, m_Stack, type, m_Page, m_List ) ); + else if ( IsType( type, typeofType ) ) + from.Target = new SetObjectTarget( prop, from, m_Object, m_Stack, type, m_Page, m_List ); + else if ( IsType( type, typeofPoint3D ) ) + from.SendGump( new SetPoint3DGump( prop, from, m_Object, m_Stack, m_Page, m_List ) ); + else if ( IsType( type, typeofPoint2D ) ) + from.SendGump( new SetPoint2DGump( prop, from, m_Object, m_Stack, m_Page, m_List ) ); + else if ( IsType( type, typeofTimeSpan ) ) + from.SendGump( new SetTimeSpanGump( prop, from, m_Object, m_Stack, m_Page, m_List ) ); + else if ( IsCustomEnum( type ) ) + from.SendGump( new SetCustomEnumGump( prop, from, m_Object, m_Stack, m_Page, m_List, GetCustomEnumNames( type ) ) ); + else if ( IsType( type, typeofEnum ) ) + from.SendGump( new SetListOptionGump( prop, from, m_Object, m_Stack, m_Page, m_List, Enum.GetNames( type ), GetObjects( Enum.GetValues( type ) ) ) ); + else if ( IsType( type, typeofBool ) ) + from.SendGump( new SetListOptionGump( prop, from, m_Object, m_Stack, m_Page, m_List, m_BoolNames, m_BoolValues ) ); + else if ( IsType( type, typeofString ) || IsType( type, typeofReal ) || IsType( type, typeofNumeric ) ) + from.SendGump( new SetGump( prop, from, m_Object, m_Stack, m_Page, m_List ) ); + else if ( IsType( type, typeofPoison ) ) + from.SendGump( new SetListOptionGump( prop, from, m_Object, m_Stack, m_Page, m_List, m_PoisonNames, m_PoisonValues ) ); + else if ( IsType( type, typeofMap ) ) + from.SendGump( new SetListOptionGump( prop, from, m_Object, m_Stack, m_Page, m_List, Map.GetMapNames(), Map.GetMapValues() ) ); + else if ( IsType( type, typeofSkills ) && m_Object is Mobile ) + { + from.SendGump( new PropertiesGump( from, m_Object, m_Stack, m_List, m_Page ) ); + from.SendGump( new SkillsGump( from, (Mobile)m_Object ) ); + } + else if( HasAttribute( type, typeofPropertyObject, true ) ) + { + object o = prop.GetValue( m_Object, null ); + + if( o != null ) + from.SendGump( new PropertiesGump( from, o, m_Stack, new StackEntry( m_Object, prop ) ) ); + } + } + + break; + } + } + } + + private static object[] GetObjects( Array a ) + { + object[] list = new object[a.Length]; + + for ( int i = 0; i < list.Length; ++i ) + list[i] = a.GetValue( i ); + + return list; + } + + private static bool IsCustomEnum( Type type ) + { + return type.IsDefined( typeofCustomEnum, false ); + } + + public static void OnValueChanged( object obj, PropertyInfo prop, Stack stack ) + { + if ( stack == null || stack.Count == 0 ) + return; + + if ( !prop.PropertyType.IsValueType ) + return; + + StackEntry peek = (StackEntry)stack.Peek(); + + if ( peek.m_Property.CanWrite ) + peek.m_Property.SetValue( peek.m_Object, obj, null ); + } + + private static string[] GetCustomEnumNames( Type type ) + { + object[] attrs = type.GetCustomAttributes( typeofCustomEnum, false ); + + if ( attrs.Length == 0 ) + return new string[0]; + + CustomEnumAttribute ce = attrs[0] as CustomEnumAttribute; + + if ( ce == null ) + return new string[0]; + + return ce.Names; + } + + private static bool HasAttribute( Type type, Type check, bool inherit ) + { + object[] objs = type.GetCustomAttributes( check, inherit ); + + return ( objs != null && objs.Length > 0 ); + } + + private static bool IsType( Type type, Type check ) + { + return type == check || type.IsSubclassOf( check ); + } + + private static bool IsType( Type type, Type[] check ) + { + for ( int i = 0; i < check.Length; ++i ) + if ( IsType( type, check[i] ) ) + return true; + + return false; + } + + private static Type typeofMobile = typeof( Mobile ); + private static Type typeofItem = typeof( Item ); + private static Type typeofType = typeof( Type ); + private static Type typeofPoint3D = typeof( Point3D ); + private static Type typeofPoint2D = typeof( Point2D ); + private static Type typeofTimeSpan = typeof( TimeSpan ); + private static Type typeofCustomEnum = typeof( CustomEnumAttribute ); + private static Type typeofEnum = typeof( Enum ); + private static Type typeofBool = typeof( Boolean ); + private static Type typeofString = typeof( String ); + private static Type typeofPoison = typeof( Poison ); + private static Type typeofMap = typeof( Map ); + private static Type typeofSkills = typeof( Skills ); + private static Type typeofPropertyObject = typeof( PropertyObjectAttribute ); + private static Type typeofNoSort = typeof( NoSortAttribute ); + + private static Type[] typeofReal = new Type[] + { + typeof( Single ), + typeof( Double ) + }; + + private static Type[] typeofNumeric = new Type[] + { + typeof( Byte ), + typeof( Int16 ), + typeof( Int32 ), + typeof( Int64 ), + typeof( SByte ), + typeof( UInt16 ), + typeof( UInt32 ), + typeof( UInt64 ) + }; + + private string ValueToString( PropertyInfo prop ) + { + return ValueToString( m_Object, prop ); + } + + public static string ValueToString( object obj, PropertyInfo prop ) + { + try + { + return ValueToString( prop.GetValue( obj, null ) ); + } + catch ( Exception e ) + { + return String.Format( "!{0}!", e.GetType() ); + } + } + + public static string ValueToString( object o ) + { + if ( o == null ) + { + return "-null-"; + } + else if ( o is string ) + { + return String.Format( "\"{0}\"", (string)o ); + } + else if ( o is bool ) + { + return o.ToString(); + } + else if ( o is char ) + { + return String.Format( "0x{0:X} '{1}'", (int)(char)o, (char)o ); + } + else if ( o is Serial ) + { + Serial s = (Serial)o; + + if ( s.IsValid ) + { + if ( s.IsItem ) + { + return String.Format( "(I) 0x{0:X}", s.Value ); + } + else if ( s.IsMobile ) + { + return String.Format( "(M) 0x{0:X}", s.Value ); + } + } + + return String.Format( "(?) 0x{0:X}", s.Value ); + } + else if ( o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong ) + { + return String.Format( "{0} (0x{0:X})", o ); + } + else if ( o is Mobile ) + { + return String.Format( "(M) 0x{0:X} \"{1}\"", ((Mobile)o).Serial.Value, ((Mobile)o).Name ); + } + else if ( o is Item ) + { + return String.Format( "(I) 0x{0:X}", ((Item)o).Serial.Value ); + } + else if ( o is Type ) + { + return ((Type)o).Name; + } + else + { + return o.ToString(); + } + } + + private ArrayList BuildList() + { + Type type = m_Object.GetType(); + + PropertyInfo[] props = type.GetProperties( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public ); + + ArrayList groups = GetGroups( type, props ); + ArrayList list = new ArrayList(); + + for ( int i = 0; i < groups.Count; ++i ) + { + DictionaryEntry de = (DictionaryEntry)groups[i]; + ArrayList groupList = (ArrayList)de.Value; + + if ( !HasAttribute( (Type)de.Key, typeofNoSort, false ) ) + groupList.Sort( PropertySorter.Instance ); + + if ( i != 0 ) + list.Add( null ); + + list.Add( de.Key ); + list.AddRange( groupList ); + } + + return list; + } + + private static Type typeofCPA = typeof( CPA ); + private static Type typeofObject = typeof( object ); + + private static CPA GetCPA( PropertyInfo prop ) + { + object[] attrs = prop.GetCustomAttributes( typeofCPA, false ); + + if ( attrs.Length > 0 ) + return attrs[0] as CPA; + else + return null; + } + + private ArrayList GetGroups( Type objectType, PropertyInfo[] props ) + { + Hashtable groups = new Hashtable(); + + for ( int i = 0; i < props.Length; ++i ) + { + PropertyInfo prop = props[i]; + + if ( prop.CanRead ) + { + CPA attr = GetCPA( prop ); + + if ( attr != null && m_Mobile.AccessLevel >= attr.ReadLevel ) + { + Type type = prop.DeclaringType; + + while ( true ) + { + Type baseType = type.BaseType; + + if ( baseType == null || baseType == typeofObject ) + break; + + if ( baseType.GetProperty( prop.Name, prop.PropertyType ) != null ) + type = baseType; + else + break; + } + + ArrayList list = (ArrayList)groups[type]; + + if ( list == null ) + groups[type] = list = new ArrayList(); + + list.Add( prop ); + } + } + } + + ArrayList sorted = new ArrayList( groups ); + + sorted.Sort( new GroupComparer( objectType ) ); + + return sorted; + } + + public static object GetObjectFromString( Type t, string s ) + { + if ( t == typeof( string ) ) + { + return s; + } + else if ( t == typeof( byte ) || t == typeof( sbyte ) || t == typeof( short ) || t == typeof( ushort ) || t == typeof( int ) || t == typeof( uint ) || t == typeof( long ) || t == typeof( ulong ) ) + { + if ( s.StartsWith( "0x" ) ) + { + if ( t == typeof( ulong ) || t == typeof( uint ) || t == typeof( ushort ) || t == typeof( byte ) ) + { + return Convert.ChangeType( Convert.ToUInt64( s.Substring( 2 ), 16 ), t ); + } + else + { + return Convert.ChangeType( Convert.ToInt64( s.Substring( 2 ), 16 ), t ); + } + } + else + { + return Convert.ChangeType( s, t ); + } + } + else if ( t == typeof( double ) || t == typeof( float ) ) + { + return Convert.ChangeType( s, t ); + } + else if ( t.IsDefined( typeof( ParsableAttribute ), false ) ) + { + MethodInfo parseMethod = t.GetMethod( "Parse", new Type[]{ typeof( string ) } ); + + return parseMethod.Invoke( null, new object[]{ s } ); + } + + throw new Exception( "bad" ); + } + + private static string GetStringFromObject( object o ) + { + if ( o == null ) + { + return "-null-"; + } + else if ( o is string ) + { + return String.Format( "\"{0}\"", (string)o ); + } + else if ( o is bool ) + { + return o.ToString(); + } + else if ( o is char ) + { + return String.Format( "0x{0:X} '{1}'", (int)(char)o, (char)o ); + } + else if ( o is Serial ) + { + Serial s = (Serial)o; + + if ( s.IsValid ) + { + if ( s.IsItem ) + { + return String.Format( "(I) 0x{0:X}", s.Value ); + } + else if ( s.IsMobile ) + { + return String.Format( "(M) 0x{0:X}", s.Value ); + } + } + + return String.Format( "(?) 0x{0:X}", s.Value ); + } + else if ( o is byte || o is sbyte || o is short || o is ushort || o is int || o is uint || o is long || o is ulong ) + { + return String.Format( "{0} (0x{0:X})", o ); + } + else if ( o is Mobile ) + { + return String.Format( "(M) 0x{0:X} \"{1}\"", ((Mobile)o).Serial.Value, ((Mobile)o).Name ); + } + else if ( o is Item ) + { + return String.Format( "(I) 0x{0:X}", ((Item)o).Serial.Value ); + } + else if ( o is Type ) + { + return ((Type)o).Name; + } + else + { + return o.ToString(); + } + } + + private class PropertySorter : IComparer + { + public static readonly PropertySorter Instance = new PropertySorter(); + + private PropertySorter() + { + } + + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + PropertyInfo a = x as PropertyInfo; + PropertyInfo b = y as PropertyInfo; + + if ( a == null || b == null ) + throw new ArgumentException(); + + return a.Name.CompareTo( b.Name ); + } + } + + private class GroupComparer : IComparer + { + private Type m_Start; + + public GroupComparer( Type start ) + { + m_Start = start; + } + + private static Type typeofObject = typeof( Object ); + + private int GetDistance( Type type ) + { + Type current = m_Start; + + int dist; + + for ( dist = 0; current != null && current != typeofObject && current != type; ++dist ) + current = current.BaseType; + + return dist; + } + + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + if ( !(x is DictionaryEntry) || !(y is DictionaryEntry) ) + throw new ArgumentException(); + + DictionaryEntry de1 = (DictionaryEntry)x; + DictionaryEntry de2 = (DictionaryEntry)y; + + Type a = (Type)de1.Key; + Type b = (Type)de2.Key; + + return GetDistance( a ).CompareTo( GetDistance( b ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetBodyGump.cs b/Scripts/Gumps/Properties/SetBodyGump.cs new file mode 100644 index 000000000..f21d3b2e0 --- /dev/null +++ b/Scripts/Gumps/Properties/SetBodyGump.cs @@ -0,0 +1,292 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.HuePickers; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetBodyGump : Gump + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + private int m_OurPage; + private ArrayList m_OurList; + private ModelBodyType m_OurType; + + private const int LabelColor32 = 0xFFFFFF; + private const int SelectedColor32 = 0x8080FF; + private const int TextColor32 = 0xFFFFFF; + + public SetBodyGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : this( prop, mobile, o, stack, page, list, 0, null, ModelBodyType.Invalid ) + { + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public void AddTypeButton( int x, int y, int buttonID, string text, ModelBodyType type ) + { + bool isSelection = ( m_OurType == type ); + + AddButton( x, y - 1, isSelection ? 4006 : 4005, 4007, buttonID, GumpButtonType.Reply, 0 ); + AddHtml( x + 35, y, 200, 20, Color( text, isSelection ? SelectedColor32 : LabelColor32 ), false, false ); + } + + public SetBodyGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list, int ourPage, ArrayList ourList, ModelBodyType ourType ) : base( 20, 30 ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + m_OurPage = ourPage; + m_OurList = ourList; + m_OurType = ourType; + + AddPage( 0 ); + + AddBackground( 0, 0, 525, 328, 5054 ); + + AddImageTiled( 10, 10, 505, 20, 0xA40 ); + AddAlphaRegion( 10, 10, 505, 20 ); + + AddImageTiled( 10, 35, 505, 283, 0xA40 ); + AddAlphaRegion( 10, 35, 505, 283 ); + + AddTypeButton( 10, 10, 1, "Monster", ModelBodyType.Monsters ); + AddTypeButton( 130, 10, 2, "Animal", ModelBodyType.Animals ); + AddTypeButton( 250, 10, 3, "Marine", ModelBodyType.Sea ); + AddTypeButton( 370, 10, 4, "Human", ModelBodyType.Human ); + + AddImage( 480, 12, 0x25EA ); + AddImage( 497, 12, 0x25E6 ); + + if ( ourList == null ) + { + AddLabel( 15, 40, 0x480, "Choose a body type above." ); + } + else if ( ourList.Count == 0 ) + { + AddLabel( 15, 40, 0x480, "The server must have UO:3D installed to use this feature." ); + } + else + { + for ( int i = 0, index = (ourPage * 12); i < 12 && index >= 0 && index < ourList.Count; ++i, ++index ) + { + InternalEntry entry = (InternalEntry)ourList[index]; + int itemID = entry.ItemID; + + Rectangle2D bounds = ItemBounds.Table[itemID & 0x3FFF]; + + int x = 15 + ((i % 4) * 125); + int y = 40 + ((i / 4) * 93); + + AddItem( x + ((120 - bounds.Width) / 2) - bounds.X, y + ((69 - bounds.Height) / 2) - bounds.Y, itemID ); + AddButton( x + 6, y + 66, 0x98D, 0x98D, 7 + index, GumpButtonType.Reply, 0 ); + + x += 6; + y += 67; + + AddHtml( x + 0, y - 1, 108, 21, Center( entry.DisplayName ), false, false ); + AddHtml( x + 0, y + 1, 108, 21, Center( entry.DisplayName ), false, false ); + AddHtml( x - 1, y + 0, 108, 21, Center( entry.DisplayName ), false, false ); + AddHtml( x + 1, y + 0, 108, 21, Center( entry.DisplayName ), false, false ); + AddHtml( x + 0, y + 0, 108, 21, Color( Center( entry.DisplayName ), TextColor32 ), false, false ); + } + + if ( ourPage > 0 ) + AddButton( 480, 12, 0x15E3, 0x15E7, 5, GumpButtonType.Reply, 0 ); + + if ( (ourPage + 1) * 12 < ourList.Count ) + AddButton( 497, 12, 0x15E1, 0x15E5, 6, GumpButtonType.Reply, 0 ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int index = info.ButtonID - 1; + + if ( index == -1 ) + { + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + else if ( index >= 0 && index < 4 ) + { + if ( m_Monster == null ) + LoadLists(); + + ModelBodyType type; + ArrayList list; + + switch ( index ) + { + default: + case 0: type = ModelBodyType.Monsters; list = m_Monster; break; + case 1: type = ModelBodyType.Animals; list = m_Animal; break; + case 2: type = ModelBodyType.Sea; list = m_Sea; break; + case 3: type = ModelBodyType.Human; list = m_Human; break; + } + + m_Mobile.SendGump( new SetBodyGump( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List, 0, list, type ) ); + } + else if ( m_OurList != null ) + { + index -= 4; + + if ( index == 0 && m_OurPage > 0 ) + { + m_Mobile.SendGump( new SetBodyGump( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List, m_OurPage - 1, m_OurList, m_OurType ) ); + } + else if ( index == 1 && ((m_OurPage + 1) * 12) < m_OurList.Count ) + { + m_Mobile.SendGump( new SetBodyGump( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List, m_OurPage + 1, m_OurList, m_OurType ) ); + } + else + { + index -= 2; + + if ( index >= 0 && index < m_OurList.Count ) + { + try + { + InternalEntry entry = (InternalEntry)m_OurList[index]; + + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, entry.Body.ToString() ); + m_Property.SetValue( m_Object, entry.Body, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + + m_Mobile.SendGump( new SetBodyGump( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List, m_OurPage, m_OurList, m_OurType ) ); + } + } + } + } + + private static ArrayList m_Monster, m_Animal, m_Sea, m_Human; + + private static void LoadLists() + { + m_Monster = new ArrayList(); + m_Animal = new ArrayList(); + m_Sea = new ArrayList(); + m_Human = new ArrayList(); + + ArrayList entries = Docs.LoadBodies(); + + for ( int i = 0; i < entries.Count; ++i ) + { + BodyEntry oldEntry = (BodyEntry)entries[i]; + int bodyID = oldEntry.Body.BodyID; + + if ( ((Body)bodyID).IsEmpty ) + continue; + + ArrayList list = null; + + switch ( oldEntry.BodyType ) + { + case ModelBodyType.Monsters: list = m_Monster; break; + case ModelBodyType.Animals: list = m_Animal; break; + case ModelBodyType.Sea: list = m_Sea; break; + case ModelBodyType.Human: list = m_Human; break; + } + + if ( list == null ) + continue; + + int itemID = ShrinkTable.Lookup( bodyID, -1 ); + + if ( itemID != -1 ) + list.Add( new InternalEntry( bodyID, itemID, oldEntry.Name ) ); + } + + m_Monster.Sort(); + m_Animal.Sort(); + m_Sea.Sort(); + m_Human.Sort(); + } + + private class InternalEntry : IComparable + { + private int m_Body; + private int m_ItemID; + private string m_Name; + private string m_DisplayName; + + public int Body{ get{ return m_Body; } } + public int ItemID{ get{ return m_ItemID; } } + public string Name{ get{ return m_Name; } } + public string DisplayName{ get{ return m_DisplayName; } } + + private static string[] m_GroupNames = new string[] + { + "ogres_", "ettins_", "walking_dead_", "gargoyles_", + "orcs_", "flails_", "daemons_", "arachnids_", + "dragons_", "elementals_", "serpents_", "gazers_", + "liche_", "spirits_", "harpies_", "headless_", + "lizard_race_", "mongbat_", "rat_race_", "scorpions_", + "trolls_", "slimes_", "skeletons_", "ethereals_", + "terathan_", "imps_", "cyclops_", "krakens_", + "frogs_", "ophidians_", "centaurs_", "mages_", + "fey_race_", "genies_", "paladins_", "shadowlords_", + "succubi_", "lizards_", "rodents_", "birds_", + "bovines_", "bruins_", "canines_", "deer_", + "equines_", "felines_", "fowl_", "gorillas_", + "kirin_", "llamas_", "ostards_", "porcines_", + "ruminants_", "walrus_", "dolphins_", "sea_horse_", + "sea_serpents_", "character_", "h_", "titans_" + }; + + public InternalEntry( int body, int itemID, string name ) + { + m_Body = body; + m_ItemID = itemID; + m_Name = name; + + m_DisplayName = name.ToLower(); + + for ( int i = 0; i < m_GroupNames.Length; ++i ) + { + if ( m_DisplayName.StartsWith( m_GroupNames[i] ) ) + { + m_DisplayName = m_DisplayName.Substring( m_GroupNames[i].Length ); + break; + } + } + + m_DisplayName = m_DisplayName.Replace( '_', ' ' ); + } + + public int CompareTo( object obj ) + { + InternalEntry comp = (InternalEntry)obj; + + int v = m_Name.CompareTo( comp.m_Name ); + + if ( v == 0 ) + m_Body.CompareTo( comp.m_Body ); + + return v; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetCustomEnumGump.cs b/Scripts/Gumps/Properties/SetCustomEnumGump.cs new file mode 100644 index 000000000..34b923f31 --- /dev/null +++ b/Scripts/Gumps/Properties/SetCustomEnumGump.cs @@ -0,0 +1,50 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetCustomEnumGump : SetListOptionGump + { + private string[] m_Names; + + public SetCustomEnumGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int propspage, ArrayList list, string[] names ) : base( prop, mobile, o, stack, propspage, list, names, null ) + { + m_Names = names; + } + + public override void OnResponse( NetState sender, RelayInfo relayInfo ) + { + int index = relayInfo.ButtonID - 1; + + if ( index >= 0 && index < m_Names.Length ) + { + try + { + MethodInfo info = m_Property.PropertyType.GetMethod( "Parse", new Type[]{ typeof( string ) } ); + + string result = ""; + + if ( info != null ) + result = Properties.SetDirect( m_Mobile, m_Object, m_Object, m_Property, m_Property.Name, info.Invoke( null, new object[] { m_Names[index] } ), true ); + else if ( m_Property.PropertyType == typeof( Enum ) || m_Property.PropertyType.IsSubclassOf( typeof( Enum ) ) ) + result = Properties.SetDirect( m_Mobile, m_Object, m_Object, m_Property, m_Property.Name, Enum.Parse( m_Property.PropertyType, m_Names[index], false ), true ); + + m_Mobile.SendMessage( result ); + + if ( result == "Property has been set." ) + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetGump.cs b/Scripts/Gumps/Properties/SetGump.cs new file mode 100644 index 000000000..552a8dfa8 --- /dev/null +++ b/Scripts/Gumps/Properties/SetGump.cs @@ -0,0 +1,279 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.HuePickers; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetGump : Gump + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int EntryWidth = 212; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + (2 * (EntryHeight + OffsetSize)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public SetGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + + bool canNull = !prop.PropertyType.IsValueType; + bool canDye = prop.IsDefined( typeof( HueAttribute ), false ); + bool isBody = prop.IsDefined( typeof( BodyAttribute ), false ); + + object val = prop.GetValue( m_Object, null ); + string initialText; + + if ( val == null ) + initialText = ""; + else + initialText = val.ToString(); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BackHeight + (canNull ? (EntryHeight + OffsetSize) : 0) + (canDye ? (EntryHeight + OffsetSize) : 0) + (isBody ? (EntryHeight + OffsetSize) : 0), BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight + (canNull ? (EntryHeight + OffsetSize) : 0) + (canDye ? (EntryHeight + OffsetSize) : 0) + (isBody ? (EntryHeight + OffsetSize) : 0), OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddTextEntry( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, 0, initialText ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1, GumpButtonType.Reply, 0 ); + + if ( canNull ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Null" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2, GumpButtonType.Reply, 0 ); + } + + if ( canDye ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Hue Picker" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3, GumpButtonType.Reply, 0 ); + } + + if ( isBody ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Body Picker" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 4, GumpButtonType.Reply, 0 ); + } + } + + private class InternalPicker : HuePicker + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public InternalPicker( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( ((IHued)o).HuedItemID ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + } + + public override void OnResponse( int hue ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, hue.ToString() ); + m_Property.SetValue( m_Object, hue, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + object toSet; + bool shouldSet, shouldSend = true; + + switch ( info.ButtonID ) + { + case 1: + { + TextRelay text = info.GetTextEntry( 0 ); + + if ( text != null ) + { + try + { + toSet = PropertiesGump.GetObjectFromString( m_Property.PropertyType, text.Text ); + shouldSet = true; + } + catch + { + toSet = null; + shouldSet = false; + m_Mobile.SendMessage( "Bad format" ); + } + } + else + { + toSet = null; + shouldSet = false; + } + + break; + } + case 2: // Null + { + toSet = null; + shouldSet = true; + + break; + } + case 3: // Hue Picker + { + toSet = null; + shouldSet = false; + shouldSend = false; + + m_Mobile.SendHuePicker( new InternalPicker( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List ) ); + + break; + } + case 4: // Body Picker + { + toSet = null; + shouldSet = false; + shouldSend = false; + + m_Mobile.SendGump( new SetBodyGump( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List ) ); + + break; + } + default: + { + toSet = null; + shouldSet = false; + + break; + } + } + + if ( shouldSet ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, toSet==null?"(null)":toSet.ToString() ); + m_Property.SetValue( m_Object, toSet, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + if ( shouldSend ) + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetListOptionGump.cs b/Scripts/Gumps/Properties/SetListOptionGump.cs new file mode 100644 index 000000000..a976906a0 --- /dev/null +++ b/Scripts/Gumps/Properties/SetListOptionGump.cs @@ -0,0 +1,187 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetListOptionGump : Gump + { + protected PropertyInfo m_Property; + protected Mobile m_Mobile; + protected object m_Object; + protected Stack m_Stack; + protected int m_Page; + protected ArrayList m_List; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int EntryWidth = 212; + private static readonly int EntryCount = 13; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + + private static bool PrevLabel = OldStyle, NextLabel = OldStyle; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + protected object[] m_Values; + + public SetListOptionGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int propspage, ArrayList list, string[] names, object[] values ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = propspage; + m_List = list; + + m_Values = values; + + int pages = (names.Length + EntryCount - 1) / EntryCount; + int index = 0; + + for ( int page = 1; page <= pages; ++page ) + { + AddPage( page ); + + int start = (page - 1) * EntryCount; + int count = names.Length - start; + + if ( count > EntryCount ) + count = EntryCount; + + int totalHeight = OffsetSize + ((count + 2) * (EntryHeight + OffsetSize)); + int backHeight = BorderSize + totalHeight + BorderSize; + + AddBackground( 0, 0, BackWidth, backHeight, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID ); + + + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + int emptyWidth = TotalWidth - PrevWidth - NextWidth - (OffsetSize * 4) - (OldStyle ? SetWidth + OffsetSize : 0); + + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( page > 1 ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 0, GumpButtonType.Page, page - 1 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, HeaderGumpID ); + + x += emptyWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x, y, NextWidth, EntryHeight, HeaderGumpID ); + + if ( page < pages ) + { + AddButton( x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 0, GumpButtonType.Page, page + 1 ); + + if ( NextLabel ) + AddLabel( x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next" ); + } + + + + AddRect( 0, prop.Name, 0 ); + + for ( int i = 0; i < count; ++i ) + AddRect( i + 1, names[index], ++index ); + } + } + + private void AddRect( int index, string str, int button ) + { + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize + ((index + 1) * (EntryHeight + OffsetSize)); + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, str ); + + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + if ( button != 0 ) + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, button, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_Values.Length ) + { + try + { + object toSet = m_Values[index]; + + string result = Properties.SetDirect( m_Mobile, m_Object, m_Object, m_Property, m_Property.Name, toSet, true ); + + m_Mobile.SendMessage( result ); + + if ( result == "Property has been set." ) + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetObjectGump.cs b/Scripts/Gumps/Properties/SetObjectGump.cs new file mode 100644 index 000000000..10406aa08 --- /dev/null +++ b/Scripts/Gumps/Properties/SetObjectGump.cs @@ -0,0 +1,307 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.Prompts; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetObjectGump : Gump + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private Type m_Type; + private int m_Page; + private ArrayList m_List; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int EntryWidth = 212; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + (5 * (EntryHeight + OffsetSize)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public SetObjectGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, Type type, int page, ArrayList list ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Type = type; + m_Page = page; + m_List = list; + + string initialText = PropertiesGump.ValueToString( o, prop ); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BackHeight, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, initialText ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Change by Serial" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Nullify" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "View Properties" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 4, GumpButtonType.Reply, 0 ); + } + + private class InternalPrompt : Prompt + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private Type m_Type; + private int m_Page; + private ArrayList m_List; + + public InternalPrompt( PropertyInfo prop, Mobile mobile, object o, Stack stack, Type type, int page, ArrayList list ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Type = type; + m_Page = page; + m_List = list; + } + + public override void OnCancel( Mobile from ) + { + m_Mobile.SendGump( new SetObjectGump( m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + object toSet; + bool shouldSet; + + try + { + int serial = Utility.ToInt32( text ); + + toSet = World.FindEntity( serial ); + + if ( toSet == null ) + { + shouldSet = false; + m_Mobile.SendMessage( "No object with that serial was found." ); + } + else if ( !m_Type.IsAssignableFrom( toSet.GetType() ) ) + { + toSet = null; + shouldSet = false; + m_Mobile.SendMessage( "The object with that serial could not be assigned to a property of type : {0}", m_Type.Name ); + } + else + { + shouldSet = true; + } + } + catch + { + toSet = null; + shouldSet = false; + m_Mobile.SendMessage( "Bad format" ); + } + + if ( shouldSet ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, toSet==null?"(null)":toSet.ToString() ); + m_Property.SetValue( m_Object, toSet, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + m_Mobile.SendGump( new SetObjectGump( m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List ) ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + object toSet; + bool shouldSet, shouldSend = true; + object viewProps = null; + + switch ( info.ButtonID ) + { + case 0: // closed + { + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + + toSet = null; + shouldSet = false; + shouldSend = false; + + break; + } + case 1: // Change by Target + { + m_Mobile.Target = new SetObjectTarget( m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List ); + toSet = null; + shouldSet = false; + shouldSend = false; + break; + } + case 2: // Change by Serial + { + toSet = null; + shouldSet = false; + shouldSend = false; + + m_Mobile.SendMessage( "Enter the serial you wish to find:" ); + m_Mobile.Prompt = new InternalPrompt( m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List ); + + break; + } + case 3: // Nullify + { + toSet = null; + shouldSet = true; + + break; + } + case 4: // View Properties + { + toSet = null; + shouldSet = false; + + object obj = m_Property.GetValue( m_Object, null ); + + if ( obj == null ) + m_Mobile.SendMessage( "The property is null and so you cannot view its properties." ); + else if ( !Server.Commands.Generic.BaseCommand.IsAccessible( m_Mobile, obj ) ) + m_Mobile.SendMessage( "You may not view their properties." ); + else + viewProps = obj; + + break; + } + default: + { + toSet = null; + shouldSet = false; + + break; + } + } + + if ( shouldSet ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, toSet==null?"(null)":toSet.ToString() ); + m_Property.SetValue( m_Object, toSet, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + if ( shouldSend ) + m_Mobile.SendGump( new SetObjectGump( m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List ) ); + + if ( viewProps != null ) + m_Mobile.SendGump( new PropertiesGump( m_Mobile, viewProps ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetObjectTarget.cs b/Scripts/Gumps/Properties/SetObjectTarget.cs new file mode 100644 index 000000000..5dc8193bc --- /dev/null +++ b/Scripts/Gumps/Properties/SetObjectTarget.cs @@ -0,0 +1,66 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Items; +using Server.Targeting; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetObjectTarget : Target + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private Type m_Type; + private int m_Page; + private ArrayList m_List; + + public SetObjectTarget( PropertyInfo prop, Mobile mobile, object o, Stack stack, Type type, int page, ArrayList list ) : base( -1, false, TargetFlags.None ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Type = type; + m_Page = page; + m_List = list; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + try + { + if ( m_Type == typeof( Type ) ) + targeted = targeted.GetType(); + else if ( (m_Type == typeof( BaseAddon ) || m_Type.IsAssignableFrom( typeof( BaseAddon ) )) && targeted is AddonComponent ) + targeted = ((AddonComponent)targeted).Addon; + + if ( m_Type.IsAssignableFrom( targeted.GetType() ) ) + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, targeted.ToString() ); + m_Property.SetValue( m_Object, targeted, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + else + { + m_Mobile.SendMessage( "That cannot be assigned to a property of type : {0}", m_Type.Name ); + } + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( m_Type == typeof( Type ) ) + from.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + else + from.SendGump( new SetObjectGump( m_Property, m_Mobile, m_Object, m_Stack, m_Type, m_Page, m_List ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetPoint2DGump.cs b/Scripts/Gumps/Properties/SetPoint2DGump.cs new file mode 100644 index 000000000..4c20703cc --- /dev/null +++ b/Scripts/Gumps/Properties/SetPoint2DGump.cs @@ -0,0 +1,237 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetPoint2DGump : Gump + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int CoordWidth = 105; + private static readonly int EntryWidth = CoordWidth + OffsetSize + CoordWidth; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + (4 * (EntryHeight + OffsetSize)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public SetPoint2DGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + + Point2D p = (Point2D)prop.GetValue( o, null ); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BackHeight, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Use your location" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Target a location" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, CoordWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "X:" ); + AddTextEntry( x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 0, p.X.ToString() ); + x += CoordWidth + OffsetSize; + + AddImageTiled( x, y, CoordWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "Y:" ); + AddTextEntry( x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 1, p.Y.ToString() ); + x += CoordWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3, GumpButtonType.Reply, 0 ); + } + + private class InternalTarget : Target + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public InternalTarget( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( -1, true, TargetFlags.None ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + IPoint3D p = targeted as IPoint3D; + + if ( p != null ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, new Point2D( p ).ToString() ); + m_Property.SetValue( m_Object, new Point2D( p ), null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Point2D toSet; + bool shouldSet, shouldSend; + + switch ( info.ButtonID ) + { + case 1: // Current location + { + toSet = new Point2D( m_Mobile.Location ); + shouldSet = true; + shouldSend = true; + + break; + } + case 2: // Pick location + { + m_Mobile.Target = new InternalTarget( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List ); + + toSet = Point2D.Zero; + shouldSet = false; + shouldSend = false; + + break; + } + case 3: // Use values + { + TextRelay x = info.GetTextEntry( 0 ); + TextRelay y = info.GetTextEntry( 1 ); + + toSet = new Point2D( x == null ? 0 : Utility.ToInt32( x.Text ), y == null ? 0 : Utility.ToInt32( y.Text ) ); + shouldSet = true; + shouldSend = true; + + break; + } + default: + { + toSet = Point2D.Zero; + shouldSet = false; + shouldSend = true; + + break; + } + } + + if ( shouldSet ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, toSet.ToString() ); + m_Property.SetValue( m_Object, toSet, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + if ( shouldSend ) + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetPoint3DGump.cs b/Scripts/Gumps/Properties/SetPoint3DGump.cs new file mode 100644 index 000000000..c38b01035 --- /dev/null +++ b/Scripts/Gumps/Properties/SetPoint3DGump.cs @@ -0,0 +1,243 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetPoint3DGump : Gump + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int CoordWidth = 70; + private static readonly int EntryWidth = CoordWidth + OffsetSize + CoordWidth + OffsetSize + CoordWidth; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + (4 * (EntryHeight + OffsetSize)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public SetPoint3DGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + + Point3D p = (Point3D)prop.GetValue( o, null ); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BackHeight, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, prop.Name ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Use your location" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, "Target a location" ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 2, GumpButtonType.Reply, 0 ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, CoordWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "X:" ); + AddTextEntry( x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 0, p.X.ToString() ); + x += CoordWidth + OffsetSize; + + AddImageTiled( x, y, CoordWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "Y:" ); + AddTextEntry( x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 1, p.Y.ToString() ); + x += CoordWidth + OffsetSize; + + AddImageTiled( x, y, CoordWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, CoordWidth - TextOffsetX, EntryHeight, TextHue, "Z:" ); + AddTextEntry( x + 16, y, CoordWidth - 16, EntryHeight, TextHue, 2, p.Z.ToString() ); + x += CoordWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 3, GumpButtonType.Reply, 0 ); + } + + private class InternalTarget : Target + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public InternalTarget( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( -1, true, TargetFlags.None ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + IPoint3D p = targeted as IPoint3D; + + if ( p != null ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, new Point3D( p ).ToString() ); + m_Property.SetValue( m_Object, new Point3D( p ), null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Point3D toSet; + bool shouldSet, shouldSend; + + switch ( info.ButtonID ) + { + case 1: // Current location + { + toSet = m_Mobile.Location; + shouldSet = true; + shouldSend = true; + + break; + } + case 2: // Pick location + { + m_Mobile.Target = new InternalTarget( m_Property, m_Mobile, m_Object, m_Stack, m_Page, m_List ); + + toSet = Point3D.Zero; + shouldSet = false; + shouldSend = false; + + break; + } + case 3: // Use values + { + TextRelay x = info.GetTextEntry( 0 ); + TextRelay y = info.GetTextEntry( 1 ); + TextRelay z = info.GetTextEntry( 2 ); + + toSet = new Point3D( x == null ? 0 : Utility.ToInt32( x.Text ), y == null ? 0 : Utility.ToInt32( y.Text ), z == null ? 0 : Utility.ToInt32( z.Text ) ); + shouldSet = true; + shouldSend = true; + + break; + } + default: + { + toSet = Point3D.Zero; + shouldSet = false; + shouldSend = true; + + break; + } + } + + if ( shouldSet ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, toSet.ToString() ); + m_Property.SetValue( m_Object, toSet, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + if ( shouldSend ) + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/Properties/SetTimeSpanGump.cs b/Scripts/Gumps/Properties/SetTimeSpanGump.cs new file mode 100644 index 000000000..a208de1ed --- /dev/null +++ b/Scripts/Gumps/Properties/SetTimeSpanGump.cs @@ -0,0 +1,239 @@ +using System; +using System.Reflection; +using System.Collections; +using Server; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class SetTimeSpanGump : Gump + { + private PropertyInfo m_Property; + private Mobile m_Mobile; + private object m_Object; + private Stack m_Stack; + private int m_Page; + private ArrayList m_List; + + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int EntryWidth = 212; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + (7 * (EntryHeight + OffsetSize)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + public SetTimeSpanGump( PropertyInfo prop, Mobile mobile, object o, Stack stack, int page, ArrayList list ) : base( GumpOffsetX, GumpOffsetY ) + { + m_Property = prop; + m_Mobile = mobile; + m_Object = o; + m_Stack = stack; + m_Page = page; + m_List = list; + + TimeSpan ts = (TimeSpan)prop.GetValue( o, null ); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BackHeight, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID ); + + AddRect( 0, prop.Name, 0, -1 ); + AddRect( 1, ts.ToString(), 0, -1 ); + AddRect( 2, "Zero", 1, -1 ); + AddRect( 3, "From H:M:S", 2, -1 ); + AddRect( 4, "H:", 3, 0 ); + AddRect( 5, "M:", 4, 1 ); + AddRect( 6, "S:", 5, 2 ); + } + + private void AddRect( int index, string str, int button, int text ) + { + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize + (index * (EntryHeight + OffsetSize)); + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, str ); + + if ( text != -1 ) + AddTextEntry( x + 16 + TextOffsetX, y, EntryWidth - TextOffsetX - 16, EntryHeight, TextHue, text, "" ); + + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + if ( button != 0 ) + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, button, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + TimeSpan toSet; + bool shouldSet, shouldSend; + + TextRelay h = info.GetTextEntry( 0 ); + TextRelay m = info.GetTextEntry( 1 ); + TextRelay s = info.GetTextEntry( 2 ); + + switch ( info.ButtonID ) + { + case 1: // Zero + { + toSet = TimeSpan.Zero; + shouldSet = true; + shouldSend = true; + + break; + } + case 2: // From H:M:S + { + bool successfulParse = false; + if( h != null && m != null && s != null ) + { + successfulParse = TimeSpan.TryParse( h.Text + ":" + m.Text + ":" + s.Text, out toSet ); + } + else + { + toSet = TimeSpan.Zero; + } + + + shouldSet = shouldSend = successfulParse; + + break; + } + case 3: // From H + { + if ( h != null ) + { + try + { + toSet = TimeSpan.FromHours( Utility.ToDouble( h.Text ) ); + shouldSet = true; + shouldSend = true; + + break; + } + catch + { + } + } + + toSet = TimeSpan.Zero; + shouldSet = false; + shouldSend = false; + + break; + } + case 4: // From M + { + if ( m != null ) + { + try + { + toSet = TimeSpan.FromMinutes( Utility.ToDouble( m.Text ) ); + shouldSet = true; + shouldSend = true; + + break; + } + catch + { + } + } + + toSet = TimeSpan.Zero; + shouldSet = false; + shouldSend = false; + + break; + } + case 5: // From S + { + if ( s != null ) + { + try + { + toSet = TimeSpan.FromSeconds( Utility.ToDouble( s.Text ) ); + shouldSet = true; + shouldSend = true; + + break; + } + catch + { + } + } + + toSet = TimeSpan.Zero; + shouldSet = false; + shouldSend = false; + + break; + } + default: + { + toSet = TimeSpan.Zero; + shouldSet = false; + shouldSend = true; + + break; + } + } + + if ( shouldSet ) + { + try + { + CommandLogging.LogChangeProperty( m_Mobile, m_Object, m_Property.Name, toSet.ToString() ); + m_Property.SetValue( m_Object, toSet, null ); + PropertiesGump.OnValueChanged( m_Object, m_Property, m_Stack ); + } + catch + { + m_Mobile.SendMessage( "An exception was caught. The property may not have changed." ); + } + } + + if ( shouldSend ) + m_Mobile.SendGump( new PropertiesGump( m_Mobile, m_Object, m_Stack, m_List, m_Page ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/ReclaimVendorGump.cs b/Scripts/Gumps/ReclaimVendorGump.cs new file mode 100644 index 000000000..caecf0239 --- /dev/null +++ b/Scripts/Gumps/ReclaimVendorGump.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Multis; + +namespace Server.Gumps +{ + public class ReclaimVendorGump : Gump + { + private BaseHouse m_House; + private ArrayList m_Vendors; + + public ReclaimVendorGump( BaseHouse house ) : base( 50, 50 ) + { + m_House = house; + m_Vendors = new ArrayList( house.InternalizedVendors ); + + AddBackground( 0, 0, 170, 50 + m_Vendors.Count * 20, 0x13BE ); + + AddImageTiled( 10, 10, 150, 20, 0xA40 ); + AddHtmlLocalized( 10, 10, 150, 20, 1061827, 0x7FFF, false, false ); //
Reclaim Vendor
+ + AddImageTiled( 10, 40, 150, m_Vendors.Count * 20, 0xA40 ); + + for ( int i = 0; i < m_Vendors.Count; i++ ) + { + Mobile m = (Mobile) m_Vendors[i]; + + int y = 40 + i * 20; + + AddButton( 10, y, 0xFA5, 0xFA7, i + 1, GumpButtonType.Reply, 0 ); + AddLabel( 45, y, 0x481, m.Name ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 0 || !m_House.IsActive || !m_House.IsInside( from ) || !m_House.IsOwner( from ) || !from.CheckAlive() ) + return; + + int index = info.ButtonID - 1; + + if ( index < 0 || index >= m_Vendors.Count ) + return; + + Mobile mob = (Mobile) m_Vendors[index]; + + if ( !m_House.InternalizedVendors.Contains( mob ) ) + return; + + if ( mob.Deleted ) + { + m_House.InternalizedVendors.Remove( mob ); + } + else + { + bool vendor, contract; + BaseHouse.IsThereVendor( from.Location, from.Map, out vendor, out contract ); + + if ( vendor ) + { + from.SendLocalizedMessage( 1062677 ); // You cannot place a vendor or barkeep at this location. + } + else if ( contract ) + { + from.SendLocalizedMessage( 1062678 ); // You cannot place a vendor or barkeep on top of a rental contract! + } + else + { + m_House.InternalizedVendors.Remove( mob ); + mob.MoveToWorld( from.Location, from.Map ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/ReportMurderer.cs b/Scripts/Gumps/ReportMurderer.cs new file mode 100644 index 000000000..99c03dbe8 --- /dev/null +++ b/Scripts/Gumps/ReportMurderer.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Gumps +{ + public class ReportMurdererGump : Gump + { + private int m_Idx; + private ArrayList m_Killers; + private Mobile m_Victum; + + public static void Initialize() + { + EventSink.PlayerDeath += new PlayerDeathEventHandler( EventSink_PlayerDeath ); + } + + public static void EventSink_PlayerDeath( PlayerDeathEventArgs e ) + { + Mobile m = e.Mobile; + + ArrayList killers = new ArrayList(); + ArrayList toGive = new ArrayList(); + + foreach ( AggressorInfo ai in m.Aggressors ) + { + if ( ai.Attacker.Player && ai.CanReportMurder && !ai.Reported ) + { + killers.Add( ai.Attacker ); + ai.Reported = true; + } + + if ( ai.Attacker.Player && (DateTime.Now - ai.LastCombatTime) < TimeSpan.FromSeconds( 30.0 ) && !toGive.Contains( ai.Attacker ) ) + toGive.Add( ai.Attacker ); + } + + foreach ( AggressorInfo ai in m.Aggressed ) + { + if ( ai.Defender.Player && (DateTime.Now - ai.LastCombatTime) < TimeSpan.FromSeconds( 30.0 ) && !toGive.Contains( ai.Defender ) ) + toGive.Add( ai.Defender ); + } + + foreach ( Mobile g in toGive ) + { + int n = Notoriety.Compute( g, m ); + + int theirKarma = m.Karma, ourKarma = g.Karma; + bool innocent = ( n == Notoriety.Innocent ); + bool criminal = ( n == Notoriety.Criminal || n == Notoriety.Murderer ); + + int fameAward = m.Fame / 200; + int karmaAward = 0; + + if ( innocent ) + karmaAward = ( ourKarma > -2500 ? -850 : -110 - (m.Karma / 100) ); + else if ( criminal ) + karmaAward = 50; + + Titles.AwardFame( g, fameAward, false ); + Titles.AwardKarma( g, karmaAward, true ); + } + + if ( m is PlayerMobile && ((PlayerMobile)m).NpcGuild == NpcGuild.ThievesGuild ) + return; + + if ( killers.Count > 0 ) + new GumpTimer( m, killers ).Start(); + } + + private class GumpTimer : Timer + { + private Mobile m_Victim; + private ArrayList m_Killers; + + public GumpTimer( Mobile victim, ArrayList killers ) : base( TimeSpan.FromSeconds( 4.0 ) ) + { + m_Victim = victim; + m_Killers = killers; + } + + protected override void OnTick() + { + m_Victim.SendGump( new ReportMurdererGump( m_Victim, m_Killers ) ); + } + } + + public ReportMurdererGump( Mobile victum, ArrayList killers ) : this( victum, killers, 0 ) + { + } + + private ReportMurdererGump( Mobile victum, ArrayList killers, int idx ) : base( 0, 0 ) + { + m_Killers = killers; + m_Victum = victum; + m_Idx = idx; + BuildGump(); + } + + private void BuildGump() + { + AddBackground( 265, 205, 320, 290, 5054 ); + Closable = false; + Resizable = false; + + AddPage( 0 ); + + AddImageTiled( 225, 175, 50, 45, 0xCE ); //Top left corner + AddImageTiled( 267, 175, 315, 44, 0xC9 ); //Top bar + AddImageTiled( 582, 175, 43, 45, 0xCF ); //Top right corner + AddImageTiled( 225, 219, 44, 270, 0xCA ); //Left side + AddImageTiled( 582, 219, 44, 270, 0xCB ); //Right side + AddImageTiled( 225, 489, 44, 43, 0xCC ); //Lower left corner + AddImageTiled( 267, 489, 315, 43, 0xE9 ); //Lower Bar + AddImageTiled( 582, 489, 43, 43, 0xCD ); //Lower right corner + + AddPage( 1 ); + + AddHtml( 260, 234, 300, 140, ((Mobile)m_Killers[m_Idx]).Name, false, false ); // Player's Name + AddHtmlLocalized( 260, 254, 300, 140, 1049066, false, false ); // Would you like to report... + + AddButton( 260, 300, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 300, 300, 300, 50, 1046362, false, false ); // Yes + + AddButton( 360, 300, 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 400, 300, 300, 50, 1046363, false, false ); // No + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + switch ( info.ButtonID ) + { + case 1: + { + Mobile killer = (Mobile)m_Killers[m_Idx]; + if ( killer != null && !killer.Deleted ) + { + killer.Kills++; + killer.ShortTermMurders++; + + if ( killer is PlayerMobile ) + ((PlayerMobile)killer).ResetKillTime(); + + killer.SendLocalizedMessage( 1049067 );//You have been reported for murder! + + if ( killer.Kills == 5 ) + killer.SendLocalizedMessage( 502134 );//You are now known as a murderer! + else if ( SkillHandlers.Stealing.SuspendOnMurder && killer.Kills == 1 && killer is PlayerMobile && ((PlayerMobile)killer).NpcGuild == NpcGuild.ThievesGuild ) + killer.SendLocalizedMessage( 501562 ); // You have been suspended by the Thieves Guild. + } + break; + } + case 2: + { + break; + } + } + + m_Idx++; + if ( m_Idx < m_Killers.Count ) + from.SendGump( new ReportMurdererGump( from, m_Killers, m_Idx ) ); + } + } +} diff --git a/Scripts/Gumps/ResurrectGump.cs b/Scripts/Gumps/ResurrectGump.cs new file mode 100644 index 000000000..4e10c7a7c --- /dev/null +++ b/Scripts/Gumps/ResurrectGump.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; + +namespace Server.Gumps +{ + public enum ResurrectMessage + { + ChaosShrine = 0, + VirtueShrine = 1, + Healer = 2, + Generic = 3, + } + + public class ResurrectGump : Gump + { + private Mobile m_Healer; + private int m_Price; + private bool m_FromSacrifice; + + public ResurrectGump( Mobile owner ) : this( owner, owner, ResurrectMessage.Generic, false ) + { + } + + public ResurrectGump( Mobile owner, bool fromSacrifice ) : this( owner, owner, ResurrectMessage.Generic, fromSacrifice ) + { + } + + public ResurrectGump( Mobile owner, Mobile healer ) : this( owner, healer, ResurrectMessage.Generic, false ) + { + } + + public ResurrectGump( Mobile owner, ResurrectMessage msg ) : this( owner, owner, msg, false ) + { + } + + public ResurrectGump( Mobile owner, Mobile healer, ResurrectMessage msg, bool fromSacrifice ) : base( 100, 0 ) + { + m_Healer = healer; + + m_FromSacrifice = fromSacrifice; + + AddPage( 0 ); + + AddBackground( 0, 0, 400, 350, 2600 ); + + AddHtmlLocalized( 0, 20, 400, 35, 1011022, false, false ); //
Resurrection
+ + AddHtmlLocalized( 50, 55, 300, 140, 1011023 + (int)msg, true, true ); /* It is possible for you to be resurrected here by this healer. Do you wish to try?
+ * CONTINUE - You chose to try to come back to life now.
+ * CANCEL - You prefer to remain a ghost for now. + */ + + AddButton( 200, 227, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 235, 230, 110, 35, 1011012, false, false ); // CANCEL + + AddButton( 65, 227, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 100, 230, 110, 35, 1011011, false, false ); // CONTINUE + } + + public ResurrectGump( Mobile owner, Mobile healer, int price ) : base( 150, 50 ) + { + m_Healer = healer; + m_Price = price; + + Closable = false; + + AddPage( 0 ); + + AddImage( 0, 0, 3600 ); + + AddImageTiled( 0, 14, 15, 200, 3603 ); + AddImageTiled( 380, 14, 14, 200, 3605 ); + + AddImage( 0, 201, 3606 ); + + AddImageTiled( 15, 201, 370, 16, 3607 ); + AddImageTiled( 15, 0, 370, 16, 3601 ); + + AddImage( 380, 0, 3602 ); + + AddImage( 380, 201, 3608 ); + + AddImageTiled( 15, 15, 365, 190, 2624 ); + + AddRadio( 30, 140, 9727, 9730, true, 1 ); + AddHtmlLocalized( 65, 145, 300, 25, 1060015, 0x7FFF, false, false ); // Grudgingly pay the money + + AddRadio( 30, 175, 9727, 9730, false, 0 ); + AddHtmlLocalized( 65, 178, 300, 25, 1060016, 0x7FFF, false, false ); // I'd rather stay dead, you scoundrel!!! + + AddHtmlLocalized( 30, 20, 360, 35, 1060017, 0x7FFF, false, false ); // Wishing to rejoin the living, are you? I can restore your body... for a price of course... + + AddHtmlLocalized( 30, 105, 345, 40, 1060018, 0x5B2D, false, false ); // Do you accept the fee, which will be withdrawn from your bank? + + AddImage( 65, 72, 5605 ); + + AddImageTiled( 80, 90, 200, 1, 9107 ); + AddImageTiled( 95, 92, 200, 1, 9157 ); + + AddLabel( 90, 70, 1645, price.ToString() ); + AddHtmlLocalized( 140, 70, 100, 25, 1023823, 0x7FFF, false, false ); // gold coins + + AddButton( 290, 175, 247, 248, 2, GumpButtonType.Reply, 0 ); + + AddImageTiled( 15, 14, 365, 1, 9107 ); + AddImageTiled( 380, 14, 1, 190, 9105 ); + AddImageTiled( 15, 205, 365, 1, 9107 ); + AddImageTiled( 15, 14, 1, 190, 9105 ); + AddImageTiled( 0, 0, 395, 1, 9157 ); + AddImageTiled( 394, 0, 1, 217, 9155 ); + AddImageTiled( 0, 216, 395, 1, 9157 ); + AddImageTiled( 0, 0, 1, 217, 9155 ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + if ( info.ButtonID == 1 || info.ButtonID == 2 ) + { + if ( from.Map == null || !from.Map.CanFit( from.Location, 16, false, false ) ) + { + from.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + return; + } + + if ( m_Price > 0 ) + { + if ( info.IsSwitched( 1 ) ) + { + if ( Banker.Withdraw( from, m_Price ) ) + { + from.SendLocalizedMessage( 1060398, m_Price.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + from.SendLocalizedMessage( 1060022, Banker.GetBalance( from ).ToString() ); // You have ~1_AMOUNT~ gold in cash remaining in your bank box. + } + else + { + from.SendLocalizedMessage( 1060020 ); // Unfortunately, you do not have enough cash in your bank to cover the cost of the healing. + return; + } + } + else + { + from.SendLocalizedMessage( 1060019 ); // You decide against paying the healer, and thus remain dead. + return; + } + } + + from.PlaySound( 0x214 ); + from.FixedEffect( 0x376A, 10, 16 ); + + from.Resurrect(); + + if ( m_Healer != null && from != m_Healer ) + { + VirtueLevel level = VirtueHelper.GetLevel( m_Healer, VirtueName.Compassion ); + + switch ( level ) + { + case VirtueLevel.Seeker: from.Hits = AOS.Scale( from.HitsMax, 20 ); break; + case VirtueLevel.Follower: from.Hits = AOS.Scale( from.HitsMax, 40 ); break; + case VirtueLevel.Knight: from.Hits = AOS.Scale( from.HitsMax, 80 ); break; + } + } + + if ( m_FromSacrifice && !from.Criminal && from is PlayerMobile ) + { + ((PlayerMobile)from).AvailableResurrects -= 1; + + Container pack = from.Backpack; + Container corpse = from.Corpse; + + if ( pack != null && corpse != null ) + { + List items = new List( corpse.Items ); + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + if ( item.Layer != Layer.Hair && item.Layer != Layer.FacialHair && item.Movable ) + pack.DropItem( item ); + } + } + } + + if ( from.Fame > 0 ) + { + int amount = from.Fame / 10; + + Misc.Titles.AwardFame( from, -amount, true ); + } + + if ( !Core.AOS && from.ShortTermMurders >= 5 ) + { + double loss = (100.0 - ( 4.0 + (from.ShortTermMurders / 5.0))) / 100.0; // 5 to 15% loss + + if ( loss < 0.85 ) + loss = 0.85; + else if ( loss > 0.95 ) + loss = 0.95; + + if ( from.RawStr * loss > 10 ) + from.RawStr = (int)(from.RawStr * loss); + if ( from.RawInt * loss > 10 ) + from.RawInt = (int)(from.RawInt * loss); + if ( from.RawDex * loss > 10 ) + from.RawDex = (int)(from.RawDex * loss); + + for ( int s = 0; s < from.Skills.Length; s++ ) + { + if ( from.Skills[s].Base * loss > 35 ) + from.Skills[s].Base *= loss; + } + } + } + } + } +} diff --git a/Scripts/Gumps/RunebookGump.cs b/Scripts/Gumps/RunebookGump.cs new file mode 100644 index 000000000..cdc1d838b --- /dev/null +++ b/Scripts/Gumps/RunebookGump.cs @@ -0,0 +1,433 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Network; +using Server.Spells; +using Server.Spells.Fourth; +using Server.Spells.Seventh; +using Server.Spells.Chivalry; +using Server.Prompts; + +namespace Server.Gumps +{ + public class RunebookGump : Gump + { + private Runebook m_Book; + + public Runebook Book{ get{ return m_Book; } } + + public int GetMapHue( Map map ) + { + if ( map == Map.Trammel ) + return 10; + else if ( map == Map.Felucca ) + return 81; + else if ( map == Map.Ilshenar ) + return 1102; + else if ( map == Map.Malas ) + return 1102; + else if ( map == Map.Tokuno ) + return 1154; + + return 0; + } + + public string GetName( string name ) + { + if ( name == null || (name = name.Trim()).Length <= 0 ) + return "(indescript)"; + + return name; + } + + private void AddBackground() + { + AddPage( 0 ); + + // Background image + AddImage( 100, 10, 2200 ); + + // Two seperators + for ( int i = 0; i < 2; ++i ) + { + int xOffset = 125 + (i * 165); + + AddImage( xOffset, 50, 57 ); + xOffset += 20; + + for ( int j = 0; j < 6; ++j, xOffset += 15 ) + AddImage( xOffset, 50, 58 ); + + AddImage( xOffset - 5, 50, 59 ); + } + + // First four page buttons + for ( int i = 0, xOffset = 130, gumpID = 2225; i < 4; ++i, xOffset += 35, ++gumpID ) + AddButton( xOffset, 187, gumpID, gumpID, 0, GumpButtonType.Page, 2 + i ); + + // Next four page buttons + for ( int i = 0, xOffset = 300, gumpID = 2229; i < 4; ++i, xOffset += 35, ++gumpID ) + AddButton( xOffset, 187, gumpID, gumpID, 0, GumpButtonType.Page, 6 + i ); + + // Charges + AddHtmlLocalized( 140, 40, 80, 18, 1011296, false, false ); // Charges: + AddHtml( 220, 40, 30, 18, m_Book.CurCharges.ToString(), false, false ); + + // Max charges + AddHtmlLocalized( 300, 40, 100, 18, 1011297, false, false ); // Max Charges: + AddHtml( 400, 40, 30, 18, m_Book.MaxCharges.ToString(), false, false ); + } + + private void AddIndex() + { + // Index + AddPage( 1 ); + + // Rename button + AddButton( 125, 15, 2472, 2473, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 158, 22, 100, 18, 1011299, false, false ); // Rename book + + // List of entries + ArrayList entries = m_Book.Entries; + + for ( int i = 0; i < 16; ++i ) + { + string desc; + int hue; + + if ( i < entries.Count ) + { + desc = GetName( ((RunebookEntry)entries[i]).Description ); + hue = GetMapHue( ((RunebookEntry)entries[i]).Map ); + } + else + { + desc = "Empty"; + hue = 0; + } + + // Use charge button + AddButton( 130 + ((i / 8) * 160), 65 + ((i % 8) * 15), 2103, 2104, 2 + (i * 6) + 0, GumpButtonType.Reply, 0 ); + + // Description label + AddLabelCropped( 145 + ((i / 8) * 160), 60 + ((i % 8) * 15), 115, 17, hue, desc ); + } + + // Turn page button + AddButton( 393, 14, 2206, 2206, 0, GumpButtonType.Page, 2 ); + } + + private void AddDetails( int index, int half ) + { + // Use charge button + AddButton( 130 + (half * 160), 65, 2103, 2104, 2 + (index * 6) + 0, GumpButtonType.Reply, 0 ); + + string desc; + int hue; + + if ( index < m_Book.Entries.Count ) + { + RunebookEntry e = (RunebookEntry)m_Book.Entries[index]; + + desc = GetName( e.Description ); + hue = GetMapHue( e.Map ); + + // Location labels + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + if ( Sextant.Format( e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + { + AddLabel( 135 + (half * 160), 80, 0, String.Format( "{0}° {1}'{2}", yLat, yMins, ySouth ? "S" : "N" ) ); + AddLabel( 135 + (half * 160), 95, 0, String.Format( "{0}° {1}'{2}", xLong, xMins, xEast ? "E" : "W" ) ); + } + + // Drop rune button + AddButton( 135 + (half * 160), 115, 2437, 2438, 2 + (index * 6) + 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 150 + (half * 160), 115, 100, 18, 1011298, false, false ); // Drop rune + + if ( e != m_Book.Default ) + { + // Set as default button + AddButton( 160 + (half * 140), 20, 2361, 2361, 2 + (index * 6) + 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 175 + (half * 140), 15, 100, 18, 1011300, false, false ); // Set default + } + + if ( Core.AOS ) + { + AddButton( 135 + (half * 160), 140, 2103, 2104, 2 + (index * 6) + 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 150 + (half * 160), 136, 110, 20, 1062722, false, false ); // Recall + + AddButton( 135 + (half * 160), 158, 2103, 2104, 2 + (index * 6) + 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 150 + (half * 160), 154, 110, 20, 1062723, false, false ); // Gate Travel + + AddButton( 135 + (half * 160), 176, 2103, 2104, 2 + (index * 6) + 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 150 + (half * 160), 172, 110, 20, 1062724, false, false ); // Sacred Journey + } + else + { + // Recall button + AddButton( 135 + (half * 160), 140, 2271, 2271, 2 + (index * 6) + 3, GumpButtonType.Reply, 0 ); + + // Gate button + AddButton( 205 + (half * 160), 140, 2291, 2291, 2 + (index * 6) + 4, GumpButtonType.Reply, 0 ); + } + } + else + { + desc = "Empty"; + hue = 0; + } + + // Description label + AddLabelCropped( 145 + (half * 160), 60, 115, 17, hue, desc ); + } + + public RunebookGump( Mobile from, Runebook book ) : base( 150, 200 ) + { + m_Book = book; + + AddBackground(); + AddIndex(); + + for ( int page = 0; page < 8; ++page ) + { + AddPage( 2 + page ); + + AddButton( 125, 14, 2205, 2205, 0, GumpButtonType.Page, 1 + page ); + + if ( page < 7 ) + AddButton( 393, 14, 2206, 2206, 0, GumpButtonType.Page, 3 + page ); + + for ( int half = 0; half < 2; ++half ) + AddDetails( (page * 2) + half, half ); + } + } + + public static bool HasSpell( Mobile from, int spellID ) + { + Spellbook book = Spellbook.Find( from, spellID ); + + return ( book != null && book.HasSpell( spellID ) ); + } + + private class InternalPrompt : Prompt + { + private Runebook m_Book; + + public InternalPrompt( Runebook book ) + { + m_Book = book; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Book.Deleted || !from.InRange( m_Book.GetWorldLocation(), 1 ) ) + return; + + if ( m_Book.CheckAccess( from ) ) + { + m_Book.Description = Utility.FixHtml( text.Trim() ); + + from.CloseGump( typeof( RunebookGump ) ); + from.SendGump( new RunebookGump( from, m_Book ) ); + + from.SendMessage( "The book's title has been changed." ); + } + else + { + from.SendLocalizedMessage( 502416 ); // That cannot be done while the book is locked down. + } + } + + public override void OnCancel( Mobile from ) + { + from.SendLocalizedMessage( 502415 ); // Request cancelled. + + if ( !m_Book.Deleted && from.InRange( m_Book.GetWorldLocation(), 1 ) ) + { + from.CloseGump( typeof( RunebookGump ) ); + from.SendGump( new RunebookGump( from, m_Book ) ); + } + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + if ( m_Book.Deleted || !from.InRange( m_Book.GetWorldLocation(), 1 ) || !Multis.DesignContext.Check( from ) ) + return; + + int buttonID = info.ButtonID; + + if ( buttonID == 1 ) // Rename book + { + if ( m_Book.CheckAccess( from ) ) + { + from.SendLocalizedMessage( 502414 ); // Please enter a title for the runebook: + from.Prompt = new InternalPrompt( m_Book ); + } + else + { + from.SendLocalizedMessage( 502413 ); // That cannot be done while the book is locked down. + } + } + else + { + buttonID -= 2; + + int index = buttonID / 6; + int type = buttonID % 6; + + if ( index >= 0 && index < m_Book.Entries.Count ) + { + RunebookEntry e = (RunebookEntry)m_Book.Entries[index]; + + switch ( type ) + { + case 0: // Use charges + { + if ( m_Book.CurCharges <= 0 ) + { + from.CloseGump( typeof( RunebookGump ) ); + from.SendGump( new RunebookGump( from, m_Book ) ); + + from.SendLocalizedMessage( 502412 ); // There are no charges left on that item. + } + else + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + if ( Sextant.Format( e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + { + string location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + from.SendMessage( location ); + } + + m_Book.OnTravel(); + + new RecallSpell( from, m_Book, e, m_Book ).Cast(); + } + + break; + } + case 1: // Drop rune + { + if ( m_Book.CheckAccess( from ) ) + { + m_Book.DropRune( from, e, index ); + + from.CloseGump( typeof( RunebookGump ) ); + from.SendGump( new RunebookGump( from, m_Book ) ); + } + else + { + from.SendLocalizedMessage( 502413 ); // That cannot be done while the book is locked down. + } + + break; + } + case 2: // Set default + { + if ( m_Book.CheckAccess( from ) ) + { + m_Book.Default = e; + + from.CloseGump( typeof( RunebookGump ) ); + from.SendGump( new RunebookGump( from, m_Book ) ); + + from.SendLocalizedMessage( 502417 ); // New default location set. + } + else + { + from.SendLocalizedMessage( 502413 ); // That cannot be done while the book is locked down. + } + + break; + } + case 3: // Recall + { + if ( HasSpell( from, 31 ) ) + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + if ( Sextant.Format( e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + { + string location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + from.SendMessage( location ); + } + + m_Book.OnTravel(); + new RecallSpell( from, null, e, null ).Cast(); + } + else + { + from.SendLocalizedMessage( 500015 ); // You do not have that spell! + } + + break; + } + case 4: // Gate + { + if ( HasSpell( from, 51 ) ) + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + if ( Sextant.Format( e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + { + string location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + from.SendMessage( location ); + } + + m_Book.OnTravel(); + new GateTravelSpell( from, null, e ).Cast(); + } + else + { + from.SendLocalizedMessage( 500015 ); // You do not have that spell! + } + + break; + } + case 5: // Sacred Journey + { + if ( Core.AOS ) + { + if ( HasSpell( from, 209 ) ) + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + if ( Sextant.Format( e.Location, e.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + { + string location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + from.SendMessage( location ); + } + + m_Book.OnTravel(); + new SacredJourneySpell( from, null, e, null ).Cast(); + } + else + { + from.SendLocalizedMessage( 500015 ); // You do not have that spell! + } + } + + break; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/SetSecureLevelGump.cs b/Scripts/Gumps/SetSecureLevelGump.cs new file mode 100644 index 000000000..779d6e700 --- /dev/null +++ b/Scripts/Gumps/SetSecureLevelGump.cs @@ -0,0 +1,93 @@ +using System; +using Server; +using Server.Multis; +using Server.Network; +using Server.Guilds; + +namespace Server.Gumps +{ + public interface ISecurable + { + SecureLevel Level{ get; set; } + } + + public class SetSecureLevelGump : Gump + { + private ISecurable m_Info; + + public SetSecureLevelGump( Mobile owner, ISecurable info, BaseHouse house ) : base( 50, 50 ) + { + m_Info = info; + + AddPage( 0 ); + + int offset = ( Guild.NewGuildSystem )? 20 : 0; + + AddBackground( 0, 0, 220, 160 + offset, 5054 ); + + AddImageTiled( 10, 10, 200, 20, 5124 ); + AddImageTiled( 10, 40, 200, 20, 5124 ); + AddImageTiled( 10, 70, 200, 80 + offset, 5124 ); + + AddAlphaRegion( 10, 10, 200, 140 ); + + AddHtmlLocalized( 10, 10, 200, 20, 1061276, 32767, false, false ); //
SET ACCESS
+ AddHtmlLocalized( 10, 40, 100, 20, 1041474, 32767, false, false ); // Owner: + + AddLabel( 110, 40, 1152, owner == null ? "" : owner.Name ); + + AddButton( 10, 70, GetFirstID( SecureLevel.Owner ), 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 70, 150, 20, 1061277, GetColor( SecureLevel.Owner ), false, false ); // Owner Only + + AddButton( 10, 90, GetFirstID( SecureLevel.CoOwners ), 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 90, 150, 20, 1061278, GetColor( SecureLevel.CoOwners ), false, false ); // Co-Owners + + AddButton( 10, 110, GetFirstID( SecureLevel.Friends ), 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 110, 150, 20, 1061279, GetColor( SecureLevel.Friends ), false, false ); // Friends + + Mobile houseOwner = house.Owner; + if( Guild.NewGuildSystem && house != null && houseOwner != null && houseOwner.Guild != null && ((Guild)houseOwner.Guild).Leader == houseOwner ) //Only the actual House owner AND guild master can set guild secures + { + AddButton( 10, 130, GetFirstID( SecureLevel.Guild ), 4007, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 130, 150, 20, 1063455, GetColor( SecureLevel.Guild ), false, false ); // Guild Members + } + + AddButton( 10, 130 + offset, GetFirstID( SecureLevel.Anyone ), 4007, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 130 + offset, 150, 20, 1061626, GetColor( SecureLevel.Anyone ), false, false ); // Anyone + } + + public int GetColor( SecureLevel level ) + { + return ( m_Info.Level == level ) ? 0x7F18 : 0x7FFF; + } + + public int GetFirstID( SecureLevel level ) + { + return ( m_Info.Level == level ) ? 4006 : 4005; + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + SecureLevel level = m_Info.Level; + + switch ( info.ButtonID ) + { + case 1: level = SecureLevel.Owner; break; + case 2: level = SecureLevel.CoOwners; break; + case 3: level = SecureLevel.Friends; break; + case 4: level = SecureLevel.Anyone; break; + case 5: level = SecureLevel.Guild; break; + } + + if ( m_Info.Level == level ) + { + state.Mobile.SendLocalizedMessage( 1061281 ); // Access level unchanged. + } + else + { + m_Info.Level = level; + state.Mobile.SendLocalizedMessage( 1061280 ); // New access level set. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/SkillsGump.cs b/Scripts/Gumps/SkillsGump.cs new file mode 100644 index 000000000..a8a8e3209 --- /dev/null +++ b/Scripts/Gumps/SkillsGump.cs @@ -0,0 +1,546 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Commands; + +namespace Server.Gumps +{ + public class EditSkillGump : Gump + { + public static readonly bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static readonly int EntryWidth = 160; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + (2 * (EntryHeight + OffsetSize)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + private Mobile m_From; + private Mobile m_Target; + private Skill m_Skill; + + private SkillsGumpGroup m_Selected; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + { + try + { + if ( m_From.AccessLevel >= AccessLevel.GameMaster ) + { + TextRelay text = info.GetTextEntry( 0 ); + + if ( text != null ) + { + m_Skill.Base = Convert.ToDouble( text.Text ); + CommandLogging.LogChangeProperty( m_From, m_Target, String.Format( "{0}.Base", m_Skill ), m_Skill.Base.ToString() ); + } + } + else + { + m_From.SendMessage( "You may not change that." ); + } + + m_From.SendGump( new SkillsGump( m_From, m_Target, m_Selected ) ); + } + catch + { + m_From.SendMessage( "Bad format. ###.# expected." ); + m_From.SendGump( new EditSkillGump( m_From, m_Target, m_Skill, m_Selected ) ); + } + } + else + { + m_From.SendGump( new SkillsGump( m_From, m_Target, m_Selected ) ); + } + } + + public EditSkillGump( Mobile from, Mobile target, Skill skill, SkillsGumpGroup selected ) : base( GumpOffsetX, GumpOffsetY ) + { + m_From = from; + m_Target = target; + m_Skill = skill; + m_Selected = selected; + + string initialText = m_Skill.Base.ToString( "F1" ); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BackHeight, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), TotalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, skill.Name ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddTextEntry( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, TextHue, 0, initialText ); + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, 1, GumpButtonType.Reply, 0 ); + } + } + + public class SkillsGump : Gump + { + public static bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + /* + private static bool PrevLabel = OldStyle, NextLabel = OldStyle; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + * */ + + private static readonly int NameWidth = 107; + private static readonly int ValueWidth = 128; + + private static readonly int EntryCount = 15; + + private static readonly int TypeWidth = NameWidth + OffsetSize + ValueWidth; + + private static readonly int TotalWidth = OffsetSize + NameWidth + OffsetSize + ValueWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (EntryCount + 1)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + private static readonly int IndentWidth = 12; + + private Mobile m_From; + private Mobile m_Target; + + private SkillsGumpGroup[] m_Groups; + private SkillsGumpGroup m_Selected; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int buttonID = info.ButtonID - 1; + + int index = buttonID / 3; + int type = buttonID % 3; + + switch ( type ) + { + case 0: + { + if ( index >= 0 && index < m_Groups.Length ) + { + SkillsGumpGroup newSelection = m_Groups[index]; + + if ( m_Selected != newSelection ) + m_From.SendGump( new SkillsGump( m_From, m_Target, newSelection ) ); + else + m_From.SendGump( new SkillsGump( m_From, m_Target, null ) ); + } + + break; + } + case 1: + { + if ( m_Selected != null && index >= 0 && index < m_Selected.Skills.Length ) + { + Skill sk = m_Target.Skills[m_Selected.Skills[index]]; + + if ( sk != null ) + { + if ( m_From.AccessLevel >= AccessLevel.GameMaster ) + { + m_From.SendGump( new EditSkillGump( m_From, m_Target, sk, m_Selected ) ); + } + else + { + m_From.SendMessage( "You may not change that." ); + m_From.SendGump( new SkillsGump( m_From, m_Target, m_Selected ) ); + } + } + else + { + m_From.SendGump( new SkillsGump( m_From, m_Target, m_Selected ) ); + } + } + + break; + } + case 2: + { + if ( m_Selected != null && index >= 0 && index < m_Selected.Skills.Length ) + { + Skill sk = m_Target.Skills[m_Selected.Skills[index]]; + + if ( sk != null ) + { + if ( m_From.AccessLevel >= AccessLevel.GameMaster ) + { + switch ( sk.Lock ) + { + case SkillLock.Up: sk.SetLockNoRelay( SkillLock.Down ); sk.Update(); break; + case SkillLock.Down: sk.SetLockNoRelay( SkillLock.Locked ); sk.Update(); break; + case SkillLock.Locked: sk.SetLockNoRelay( SkillLock.Up ); sk.Update(); break; + } + } + else + { + m_From.SendMessage( "You may not change that." ); + } + + m_From.SendGump( new SkillsGump( m_From, m_Target, m_Selected ) ); + } + } + + break; + } + } + } + + public int GetButtonID( int type, int index ) + { + return 1 + (index * 3) + type; + } + + public SkillsGump( Mobile from, Mobile target ) : this( from, target, null ) + { + } + + public SkillsGump( Mobile from, Mobile target, SkillsGumpGroup selected ) : base( GumpOffsetX, GumpOffsetY ) + { + m_From = from; + m_Target = target; + + m_Groups = SkillsGumpGroup.Groups; + m_Selected = selected; + + int count = m_Groups.Length; + + if ( selected != null ) + count += selected.Skills.Length; + + int totalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (count + 1)); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + int emptyWidth = TotalWidth - PrevWidth - NextWidth - (OffsetSize * 4) - (OldStyle ? SetWidth + OffsetSize : 0); + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + x += PrevWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, HeaderGumpID ); + + x += emptyWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x, y, NextWidth, EntryHeight, HeaderGumpID ); + + for ( int i = 0; i < m_Groups.Length; ++i ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + SkillsGumpGroup group = m_Groups[i]; + + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( group == selected ) + AddButton( x + PrevOffsetX, y + PrevOffsetY, 0x15E2, 0x15E6, GetButtonID( 0, i ), GumpButtonType.Reply, 0 ); + else + AddButton( x + PrevOffsetX, y + PrevOffsetY, 0x15E1, 0x15E5, GetButtonID( 0, i ), GumpButtonType.Reply, 0 ); + + x += PrevWidth + OffsetSize; + + x -= (OldStyle ? OffsetSize : 0); + + AddImageTiled( x, y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, EntryGumpID ); + AddLabel( x + TextOffsetX, y, TextHue, group.Name ); + + x += emptyWidth + (OldStyle ? OffsetSize * 2 : 0); + x += OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + if ( group == selected ) + { + int indentMaskX = BorderSize; + int indentMaskY = y + EntryHeight + OffsetSize; + + for ( int j = 0; j < group.Skills.Length; ++j ) + { + Skill sk = target.Skills[group.Skills[j]]; + + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + x += OffsetSize; + x += IndentWidth; + + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + AddButton( x + PrevOffsetX, y + PrevOffsetY, 0x15E1, 0x15E5, GetButtonID( 1, j ), GumpButtonType.Reply, 0 ); + + x += PrevWidth + OffsetSize; + + x -= (OldStyle ? OffsetSize : 0); + + AddImageTiled( x, y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0) - OffsetSize - IndentWidth, EntryHeight, EntryGumpID ); + AddLabel( x + TextOffsetX, y, TextHue, sk == null ? "(null)" : sk.Name ); + + x += emptyWidth + (OldStyle ? OffsetSize * 2 : 0) - OffsetSize - IndentWidth; + x += OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + if ( sk != null ) + { + int buttonID1, buttonID2; + int xOffset, yOffset; + + switch ( sk.Lock ) + { + default: + case SkillLock.Up: buttonID1 = 0x983; buttonID2 = 0x983; xOffset = 6; yOffset = 4; break; + case SkillLock.Down: buttonID1 = 0x985; buttonID2 = 0x985; xOffset = 6; yOffset = 4; break; + case SkillLock.Locked: buttonID1 = 0x82C; buttonID2 = 0x82C; xOffset = 5; yOffset = 2; break; + } + + AddButton( x + xOffset, y + yOffset, buttonID1, buttonID2, GetButtonID( 2, j ), GumpButtonType.Reply, 0 ); + + y += 1; + x -= OffsetSize; + x -= 1; + x -= 50; + + AddImageTiled( x, y, 50, EntryHeight - 2, OffsetGumpID ); + + x += 1; + y += 1; + + AddImageTiled( x, y, 48, EntryHeight - 4, EntryGumpID ); + + AddLabelCropped( x + TextOffsetX, y - 1, 48 - TextOffsetX, EntryHeight - 3, TextHue, sk.Base.ToString( "F1" ) ); + + y -= 2; + } + } + + AddImageTiled( indentMaskX, indentMaskY, IndentWidth + OffsetSize, (group.Skills.Length * (EntryHeight + OffsetSize)) - (i < (m_Groups.Length - 1) ? OffsetSize : 0), BackGumpID + 4 ); + } + } + } + } + + public class SkillsGumpGroup + { + private string m_Name; + private SkillName[] m_Skills; + + public string Name{ get{ return m_Name; } } + public SkillName[] Skills{ get{ return m_Skills; } } + + public SkillsGumpGroup( string name, SkillName[] skills ) + { + m_Name = name; + m_Skills = skills; + + Array.Sort( m_Skills, new SkillNameComparer() ); + } + + private class SkillNameComparer : IComparer + { + public SkillNameComparer() + { + } + + public int Compare( object x, object y ) + { + SkillName a = (SkillName)x; + SkillName b = (SkillName)y; + + string aName = SkillInfo.Table[(int)a].Name; + string bName = SkillInfo.Table[(int)b].Name; + + return aName.CompareTo( bName ); + } + } + + private static SkillsGumpGroup[] m_Groups = new SkillsGumpGroup[] + { + new SkillsGumpGroup( "Crafting", new SkillName[] + { + SkillName.Alchemy, + SkillName.Blacksmith, + SkillName.Cartography, + SkillName.Carpentry, + SkillName.Cooking, + SkillName.Fletching, + SkillName.Inscribe, + SkillName.Tailoring, + SkillName.Tinkering + } ), + new SkillsGumpGroup( "Bardic", new SkillName[] + { + SkillName.Discordance, + SkillName.Musicianship, + SkillName.Peacemaking, + SkillName.Provocation + } ), + new SkillsGumpGroup( "Magical", new SkillName[] + { + SkillName.Chivalry, + SkillName.EvalInt, + SkillName.Magery, + SkillName.MagicResist, + SkillName.Meditation, + SkillName.Necromancy, + SkillName.SpiritSpeak, + SkillName.Ninjitsu, + SkillName.Bushido + } ), + new SkillsGumpGroup( "Miscellaneous", new SkillName[] + { + SkillName.Camping, + SkillName.Fishing, + SkillName.Focus, + SkillName.Healing, + SkillName.Herding, + SkillName.Lockpicking, + SkillName.Lumberjacking, + SkillName.Mining, + SkillName.Snooping, + SkillName.Veterinary + } ), + new SkillsGumpGroup( "Combat Ratings", new SkillName[] + { + SkillName.Archery, + SkillName.Fencing, + SkillName.Macing, + SkillName.Parry, + SkillName.Swords, + SkillName.Tactics, + SkillName.Wrestling + } ), + new SkillsGumpGroup( "Actions", new SkillName[] + { + SkillName.AnimalTaming, + SkillName.Begging, + SkillName.DetectHidden, + SkillName.Hiding, + SkillName.RemoveTrap, + SkillName.Poisoning, + SkillName.Stealing, + SkillName.Stealth, + SkillName.Tracking + } ), + new SkillsGumpGroup( "Lore & Knowledge", new SkillName[] + { + SkillName.Anatomy, + SkillName.AnimalLore, + SkillName.ArmsLore, + SkillName.Forensics, + SkillName.ItemID, + SkillName.TasteID + } ) + }; + + public static SkillsGumpGroup[] Groups + { + get{ return m_Groups; } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/TithingGump.cs b/Scripts/Gumps/TithingGump.cs new file mode 100644 index 000000000..16f69b2fc --- /dev/null +++ b/Scripts/Gumps/TithingGump.cs @@ -0,0 +1,125 @@ +using System; +using Server; +using Server.Items; +using Server.Network; + +namespace Server.Gumps +{ + public class TithingGump : Gump + { + private Mobile m_From; + private int m_Offer; + + public TithingGump( Mobile from, int offer ) : base( 160, 40 ) + { + int totalGold = from.TotalGold; + + if ( offer > totalGold ) + offer = totalGold; + else if ( offer < 0 ) + offer = 0; + + m_From = from; + m_Offer = offer; + + AddPage( 0 ); + + AddImage( 30, 30, 102 ); + + AddHtmlLocalized( 95, 100, 120, 100, 1060198, 0, false, false ); // May your wealth bring blessings to those in need, if tithed upon this most sacred site. + + AddLabel( 57, 274, 0, "Gold:" ); + AddLabel( 87, 274, 53, (totalGold - offer).ToString() ); + + AddLabel( 137, 274, 0, "Tithe:" ); + AddLabel( 172, 274, 53, offer.ToString() ); + + AddButton( 105, 230, 5220, 5220, 2, GumpButtonType.Reply, 0 ); + AddButton( 113, 230, 5222, 5222, 2, GumpButtonType.Reply, 0 ); + AddLabel( 108, 228, 0, "<" ); + AddLabel( 112, 228, 0, "<" ); + + AddButton( 127, 230, 5223, 5223, 1, GumpButtonType.Reply, 0 ); + AddLabel( 131, 228, 0, "<" ); + + AddButton( 147, 230, 5224, 5224, 3, GumpButtonType.Reply, 0 ); + AddLabel( 153, 228, 0, ">" ); + + AddButton( 168, 230, 5220, 5220, 4, GumpButtonType.Reply, 0 ); + AddButton( 176, 230, 5222, 5222, 4, GumpButtonType.Reply, 0 ); + AddLabel( 172, 228, 0, ">" ); + AddLabel( 176, 228, 0, ">" ); + + AddButton( 217, 272, 4023, 4024, 5, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 0: + { + // You have decided to tithe no gold to the shrine. + m_From.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1060193 ); + break; + } + case 1: + case 2: + case 3: + case 4: + { + int offer = 0; + + switch ( info.ButtonID ) + { + case 1: offer = m_Offer - 100; break; + case 2: offer = 0; break; + case 3: offer = m_Offer + 100; break; + case 4: offer = m_From.TotalGold; break; + } + + m_From.SendGump( new TithingGump( m_From, offer ) ); + break; + } + case 5: + { + int totalGold = m_From.TotalGold; + + if ( m_Offer > totalGold ) + m_Offer = totalGold; + else if ( m_Offer < 0 ) + m_Offer = 0; + + if ( (m_From.TithingPoints + m_Offer) > 100000 ) // TODO: What's the maximum? + m_Offer = (100000 - m_From.TithingPoints); + + if ( m_Offer <= 0 ) + { + // You have decided to tithe no gold to the shrine. + m_From.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1060193 ); + break; + } + + Container pack = m_From.Backpack; + + if ( pack != null && pack.ConsumeTotal( typeof( Gold ), m_Offer ) ) + { + // You tithe gold to the shrine as a sign of devotion. + m_From.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1060195 ); + m_From.TithingPoints += m_Offer; + + m_From.PlaySound( 0x243 ); + m_From.PlaySound( 0x2E6 ); + } + else + { + // You do not have enough gold to tithe that amount! + m_From.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1060194 ); + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/VendorInventoryGump.cs b/Scripts/Gumps/VendorInventoryGump.cs new file mode 100644 index 000000000..6db321db3 --- /dev/null +++ b/Scripts/Gumps/VendorInventoryGump.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Multis; +using Server.Mobiles; + +namespace Server.Gumps +{ + public class VendorInventoryGump : Gump + { + private BaseHouse m_House; + private ArrayList m_Inventories; + + public VendorInventoryGump( BaseHouse house, Mobile from ) : base( 50, 50 ) + { + m_House = house; + m_Inventories = new ArrayList( house.VendorInventories ); + + AddBackground( 0, 0, 420, 50 + 20 * m_Inventories.Count, 0x13BE ); + + AddImageTiled( 10, 10, 400, 20, 0xA40 ); + AddHtmlLocalized( 15, 10, 200, 20, 1062435, 0x7FFF, false, false ); // Reclaim Vendor Inventory + AddHtmlLocalized( 330, 10, 50, 20, 1062465, 0x7FFF, false, false ); // Expires + + AddImageTiled( 10, 40, 400, 20 * m_Inventories.Count, 0xA40 ); + + for ( int i = 0; i < m_Inventories.Count; i++ ) + { + VendorInventory inventory = (VendorInventory) m_Inventories[i]; + + int y = 40 + 20 * i; + + if ( inventory.Owner == from ) + AddButton( 10, y, 0xFA5, 0xFA7, i + 1, GumpButtonType.Reply, 0 ); + + AddLabel( 45, y, 0x481, String.Format( "{0} ({1})", inventory.ShopName, inventory.VendorName ) ); + + TimeSpan expire = inventory.ExpireTime - DateTime.Now; + int hours = (int) expire.TotalHours; + + AddLabel( 320, y, 0x481, hours.ToString() ); + AddHtmlLocalized( 350, y, 50, 20, 1062466, 0x7FFF, false, false ); // hour(s) + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 ) + return; + + Mobile from = sender.Mobile; + HouseSign sign = m_House.Sign; + + if ( m_House.Deleted || sign == null || sign.Deleted || !from.CheckAlive() ) + return; + + if ( from.Map != sign.Map || !from.InRange( sign, 5 ) ) + { + from.SendLocalizedMessage( 1062429 ); // You must be within five paces of the house sign to use this option. + return; + } + + int index = info.ButtonID - 1; + if ( index < 0 || index >= m_Inventories.Count ) + return; + + VendorInventory inventory = (VendorInventory) m_Inventories[index]; + + if ( inventory.Owner != from || !m_House.VendorInventories.Contains( inventory ) ) + return; + + int totalItems = 0; + int givenToBackpack = 0; + int givenToBankBox = 0; + for ( int i = inventory.Items.Count - 1; i >= 0; i-- ) + { + Item item = (Item) inventory.Items[i]; + + if ( item.Deleted ) + { + inventory.Items.RemoveAt( i ); + continue; + } + + totalItems += 1 + item.TotalItems; + + if ( from.PlaceInBackpack( item ) ) + { + inventory.Items.RemoveAt( i ); + givenToBackpack += 1 + item.TotalItems; + } + else if ( from.BankBox != null && from.BankBox.TryDropItem( from, item, false ) ) + { + inventory.Items.RemoveAt( i ); + givenToBankBox += 1 + item.TotalItems; + } + } + + from.SendLocalizedMessage( 1062436, totalItems.ToString() + "\t" + inventory.Gold.ToString() ); // The vendor you selected had ~1_COUNT~ items in its inventory, and ~2_AMOUNT~ gold in its account. + + int givenGold = Banker.DepositUpTo( from, inventory.Gold ); + inventory.Gold -= givenGold; + + from.SendLocalizedMessage( 1060397, givenGold.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + from.SendLocalizedMessage( 1062437, givenToBackpack.ToString() + "\t" + givenToBankBox.ToString() ); // ~1_COUNT~ items have been removed from the shop inventory and placed in your backpack. ~2_BANKCOUNT~ items were removed from the shop inventory and placed in your bank box. + + if ( inventory.Gold > 0 || inventory.Items.Count > 0 ) + { + from.SendLocalizedMessage( 1062440 ); // Some of the shop inventory would not fit in your backpack or bank box. Please free up some room and try again. + } + else + { + inventory.Delete(); + from.SendLocalizedMessage( 1062438 ); // The shop is now empty of inventory and funds, so it has been deleted. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/VendorRentalGumps.cs b/Scripts/Gumps/VendorRentalGumps.cs new file mode 100644 index 000000000..eedb07efb --- /dev/null +++ b/Scripts/Gumps/VendorRentalGumps.cs @@ -0,0 +1,628 @@ +using System; +using Server; +using Server.Items; +using Server.Network; +using Server.Prompts; +using Server.Mobiles; +using Server.Targeting; +using Server.Multis; + +namespace Server.Gumps +{ + public abstract class BaseVendorRentalGump : Gump + { + protected enum GumpType + { + UnlockedContract, + LockedContract, + Offer, + VendorLandlord, + VendorRenter + } + + protected BaseVendorRentalGump( GumpType type, VendorRentalDuration duration, int price, int renewalPrice, + Mobile landlord, Mobile renter, bool landlordRenew, bool renterRenew, bool renew ) : base( 100, 100 ) + { + if ( type == GumpType.Offer ) + Closable = false; + + AddPage( 0 ); + + AddImage( 0, 0, 0x1F40 ); + AddImageTiled( 20, 37, 300, 308, 0x1F42 ); + AddImage( 20, 325, 0x1F43 ); + + AddImage( 35, 8, 0x39 ); + AddImageTiled( 65, 8, 257, 10, 0x3A ); + AddImage( 290, 8, 0x3B ); + + AddImageTiled( 70, 55, 230, 2, 0x23C5 ); + + AddImage( 32, 33, 0x2635 ); + AddHtmlLocalized( 70, 35, 270, 20, 1062353, 0x1, false, false ); // Vendor Rental Contract + + + AddPage( 1 ); + + if ( type != GumpType.UnlockedContract ) + { + AddImage( 65, 60, 0x827 ); + AddHtmlLocalized( 79, 58, 270, 20, 1062370, 0x1, false, false ); // Landlord: + AddLabel( 150, 58, 0x64, landlord != null ? landlord.Name : "" ); + + AddImageTiled( 70, 80, 230, 2, 0x23C5 ); + } + + if ( type == GumpType.UnlockedContract || type == GumpType.LockedContract ) + AddButton( 30, 96, 0x15E1, 0x15E5, 0, GumpButtonType.Page, 2 ); + AddHtmlLocalized( 50, 95, 150, 20, 1062354, 0x1, false, false ); // Contract Length + AddHtmlLocalized( 230, 95, 270, 20, duration.Name, 0x1, false, false ); + + if ( type == GumpType.UnlockedContract || type == GumpType.LockedContract ) + AddButton( 30, 116, 0x15E1, 0x15E5, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 115, 150, 20, 1062356, 0x1, false, false ); // Price Per Rental + AddLabel( 230, 115, 0x64, price > 0 ? price.ToString() : "FREE" ); + + AddImageTiled( 50, 160, 250, 2, 0x23BF ); + + if ( type == GumpType.Offer ) + { + AddButton( 67, 180, 0x482, 0x483, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 100, 180, 270, 20, 1049011, 0x28, false, false ); // I accept! + + AddButton( 67, 210, 0x47F, 0x480, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 100, 210, 270, 20, 1049012, 0x28, false, false ); // No thanks, I decline. + } + else + { + AddImage( 49, 170, 0x61 ); + AddHtmlLocalized( 60, 170, 250, 20, 1062355, 0x1, false, false ); // Renew On Expiration? + + if ( type == GumpType.LockedContract || type == GumpType.UnlockedContract || type == GumpType.VendorLandlord ) + AddButton( 30, 192, 0x15E1, 0x15E5, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 85, 190, 250, 20, 1062359, 0x1, false, false ); // Landlord: + AddHtmlLocalized( 230, 190, 270, 20, landlordRenew ? 1049717 : 1049718, 0x1, false, false ); // YES / NO + + if ( type == GumpType.VendorRenter ) + AddButton( 30, 212, 0x15E1, 0x15E5, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 85, 210, 250, 20, 1062360, 0x1, false, false ); // Renter: + AddHtmlLocalized( 230, 210, 270, 20, renterRenew ? 1049717 : 1049718, 0x1, false, false ); // YES / NO + + if ( renew ) + { + AddImage( 49, 233, 0x939 ); + AddHtmlLocalized( 70, 230, 250, 20, 1062482, 0x1, false, false ); // Contract WILL renew + } + else + { + AddImage( 49, 233, 0x938 ); + AddHtmlLocalized( 70, 230, 250, 20, 1062483, 0x1, false, false ); // Contract WILL NOT renew + } + } + + AddImageTiled( 30, 283, 257, 30, 0x5D ); + AddImage( 285, 283, 0x5E ); + AddImage( 20, 288, 0x232C ); + + if ( type == GumpType.LockedContract ) + { + AddButton( 67, 295, 0x15E1, 0x15E5, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 85, 294, 270, 20, 1062358, 0x28, false, false ); // Offer Contract To Someone + } + else if ( type == GumpType.VendorLandlord || type == GumpType.VendorRenter ) + { + if ( type == GumpType.VendorLandlord ) + AddButton( 30, 250, 0x15E1, 0x15E1, 6, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 85, 250, 250, 20, 1062499, 0x1, false, false ); // Renewal Price + AddLabel( 230, 250, 0x64, renewalPrice.ToString() ); + + AddHtmlLocalized( 60, 294, 270, 20, 1062369, 0x1, false, false ); // Renter: + AddLabel( 120, 293, 0x64, renter != null ? renter.Name : "" ); + } + + + if ( type == GumpType.UnlockedContract || type == GumpType.LockedContract ) + { + AddPage( 2 ); + + for ( int i = 0; i < VendorRentalDuration.Instances.Length; i++ ) + { + VendorRentalDuration durationItem = VendorRentalDuration.Instances[i]; + + AddButton( 30, 76 + i * 20, 0x15E1, 0x15E5, 0x10 | i, GumpButtonType.Reply, 1 ); + AddHtmlLocalized( 50, 75 + i * 20, 150, 20, durationItem.Name, 0x1, false, false ); + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( !IsValidResponse( from ) ) + return; + + if ( (info.ButtonID & 0x10) != 0 ) // Contract duration + { + int index = info.ButtonID & 0xF; + + if ( index < VendorRentalDuration.Instances.Length ) + { + SetContractDuration( from, VendorRentalDuration.Instances[index] ); + } + } + else + { + switch ( info.ButtonID ) + { + case 1: // Price Per Rental + SetPricePerRental( from ); + break; + + case 2: // Accept offer + AcceptOffer( from ); + break; + + case 3: // Renew on expiration - landlord + LandlordRenewOnExpiration( from ); + break; + + case 4: // Renew on expiration - renter + RenterRenewOnExpiration( from ); + break; + + case 5: // Offer Contract To Someone + OfferContract( from ); + break; + + case 6: // Renewal price + SetRenewalPrice( from ); + break; + + default: + Cancel( from ); + break; + } + } + } + + protected abstract bool IsValidResponse( Mobile from ); + + protected virtual void SetContractDuration( Mobile from, VendorRentalDuration duration ) + { + } + + protected virtual void SetPricePerRental( Mobile from ) + { + } + + protected virtual void AcceptOffer( Mobile from ) + { + } + + protected virtual void LandlordRenewOnExpiration( Mobile from ) + { + } + + protected virtual void RenterRenewOnExpiration( Mobile from ) + { + } + + protected virtual void OfferContract( Mobile from ) + { + } + + protected virtual void SetRenewalPrice( Mobile from ) + { + } + + protected virtual void Cancel( Mobile from ) + { + } + } + + public class VendorRentalContractGump : BaseVendorRentalGump + { + private VendorRentalContract m_Contract; + + public VendorRentalContractGump( VendorRentalContract contract, Mobile from ) : base( + contract.IsLockedDown ? GumpType.LockedContract : GumpType.UnlockedContract, contract.Duration, + contract.Price, contract.Price, from, null, contract.LandlordRenew, false, false ) + { + m_Contract = contract; + } + + protected override bool IsValidResponse( Mobile from ) + { + return m_Contract.IsUsableBy( from, true, true, true, true ); + } + + protected override void SetContractDuration( Mobile from, VendorRentalDuration duration ) + { + m_Contract.Duration = duration; + + from.SendGump( new VendorRentalContractGump( m_Contract, from ) ); + } + + protected override void SetPricePerRental( Mobile from ) + { + from.SendLocalizedMessage( 1062365 ); // Please enter the amount of gold that should be charged for this contract (ESC to cancel): + from.Prompt = new PricePerRentalPrompt( m_Contract ); + } + + protected override void LandlordRenewOnExpiration( Mobile from ) + { + m_Contract.LandlordRenew = !m_Contract.LandlordRenew; + + from.SendGump( new VendorRentalContractGump( m_Contract, from ) ); + } + + protected override void OfferContract( Mobile from ) + { + if ( m_Contract.IsLandlord( from ) ) + { + from.SendLocalizedMessage( 1062371 ); // Please target the person you wish to offer this contract to. + from.Target = new OfferContractTarget( m_Contract ); + } + } + + private class PricePerRentalPrompt : Prompt + { + private VendorRentalContract m_Contract; + + public PricePerRentalPrompt( VendorRentalContract contract ) + { + m_Contract = contract; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_Contract.IsUsableBy( from, true, true, true, true ) ) + return; + + text = text.Trim(); + + int price; + + try + { + price = Convert.ToInt32( text ); + } + catch + { + price = -1; + } + + if ( price < 0 ) + { + from.SendLocalizedMessage( 1062485 ); // Invalid entry. Rental fee set to 0. + m_Contract.Price = 0; + } + else if ( price > 5000000 ) + { + m_Contract.Price = 5000000; + } + else + { + m_Contract.Price = price; + } + + from.SendGump( new VendorRentalContractGump( m_Contract, from ) ); + } + + public override void OnCancel( Mobile from ) + { + if ( m_Contract.IsUsableBy( from, true, true, true, true ) ) + from.SendGump( new VendorRentalContractGump( m_Contract, from ) ); + } + } + + private class OfferContractTarget : Target + { + private VendorRentalContract m_Contract; + + public OfferContractTarget( VendorRentalContract contract ) : base( -1, false, TargetFlags.None ) + { + m_Contract = contract; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Contract.IsUsableBy( from, true, false, true, true ) ) + return; + + Mobile mob = targeted as Mobile; + + if ( mob == null || !mob.Player || !mob.Alive || mob == from ) + { + from.SendMessage( "That is not a valid target for a rental contract!" ); + } + else if ( !mob.InRange( m_Contract, 5 ) ) + { + from.SendLocalizedMessage( 501853 ); // Target is too far away. + } + else + { + from.SendLocalizedMessage( 1062372 ); // Please wait while that person considers your offer. + + mob.SendLocalizedMessage( 1062373, from.Name ); // ~1_NAME~ is offering you a vendor rental. If you choose to accept this offer, you have 30 seconds to do so. + mob.SendGump( new VendorRentalOfferGump( m_Contract, from ) ); + + m_Contract.Offeree = mob; + } + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + from.SendLocalizedMessage( 1062380 ); // You decide against offering the contract to anyone. + } + } + } + + public class VendorRentalOfferGump : BaseVendorRentalGump + { + private VendorRentalContract m_Contract; + private Mobile m_Landlord; + + public VendorRentalOfferGump( VendorRentalContract contract, Mobile landlord ) : base( + GumpType.Offer, contract.Duration, contract.Price, contract.Price, + landlord, null, contract.LandlordRenew, false, false ) + { + m_Contract = contract; + m_Landlord = landlord; + } + + protected override bool IsValidResponse( Mobile from ) + { + return m_Contract.IsUsableBy( m_Landlord, true, false, false, false ) && from.CheckAlive() && m_Contract.Offeree == from; + } + + protected override void AcceptOffer( Mobile from ) + { + m_Contract.Offeree = null; + + if ( !m_Contract.Map.CanFit( m_Contract.Location, 16, false, false ) ) + { + m_Landlord.SendLocalizedMessage( 1062486 ); // A vendor cannot exist at that location. Please try again. + return; + } + + BaseHouse house = BaseHouse.FindHouseAt( m_Contract ); + if ( house == null ) + return; + + int price = m_Contract.Price; + int goldToGive; + + if ( price > 0 ) + { + if ( Banker.Withdraw( from, price ) ) + { + from.SendLocalizedMessage( 1060398, price.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + + int depositedGold = Banker.DepositUpTo( m_Landlord, price ); + goldToGive = price - depositedGold; + + if ( depositedGold > 0 ) + m_Landlord.SendLocalizedMessage( 1060397, price.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + + if ( goldToGive > 0 ) + m_Landlord.SendLocalizedMessage( 500390 ); // Your bank box is full. + } + else + { + from.SendLocalizedMessage( 1062378 ); // You do not have enough gold in your bank account to cover the cost of the contract. + m_Landlord.SendLocalizedMessage( 1062374, from.Name ); // ~1_NAME~ has declined your vendor rental offer. + + return; + } + } + else + { + goldToGive = 0; + } + + PlayerVendor vendor = new RentedVendor( from, house, m_Contract.Duration, price, m_Contract.LandlordRenew, goldToGive ); + vendor.MoveToWorld( m_Contract.Location, m_Contract.Map ); + + m_Contract.Delete(); + + from.SendLocalizedMessage( 1062377 ); // You have accepted the offer and now own a vendor in this house. Rental contract options and details may be viewed on this vendor via the 'Contract Options' context menu. + m_Landlord.SendLocalizedMessage( 1062376, from.Name ); // ~1_NAME~ has accepted your vendor rental offer. Rental contract details and options may be viewed on this vendor via the 'Contract Options' context menu. + } + + protected override void Cancel( Mobile from ) + { + m_Contract.Offeree = null; + + from.SendLocalizedMessage( 1062375 ); // You decline the offer for a vendor space rental. + m_Landlord.SendLocalizedMessage( 1062374, from.Name ); // ~1_NAME~ has declined your vendor rental offer. + } + } + + public class RenterVendorRentalGump : BaseVendorRentalGump + { + private RentedVendor m_Vendor; + + public RenterVendorRentalGump( RentedVendor vendor ) : base( + GumpType.VendorRenter, vendor.RentalDuration, vendor.RentalPrice, vendor.RenewalPrice, + vendor.Landlord, vendor.Owner, vendor.LandlordRenew, vendor.RenterRenew, vendor.Renew ) + { + m_Vendor = vendor; + } + + protected override bool IsValidResponse( Mobile from ) + { + return m_Vendor.CanInteractWith( from, true ); + } + + protected override void RenterRenewOnExpiration( Mobile from ) + { + m_Vendor.RenterRenew = !m_Vendor.RenterRenew; + + from.SendGump( new RenterVendorRentalGump( m_Vendor ) ); + } + } + + public class LandlordVendorRentalGump : BaseVendorRentalGump + { + private RentedVendor m_Vendor; + + public LandlordVendorRentalGump( RentedVendor vendor ) : base( + GumpType.VendorLandlord, vendor.RentalDuration, vendor.RentalPrice, vendor.RenewalPrice, + vendor.Landlord, vendor.Owner, vendor.LandlordRenew, vendor.RenterRenew, vendor.Renew ) + { + m_Vendor = vendor; + } + + protected override bool IsValidResponse( Mobile from ) + { + return m_Vendor.CanInteractWith( from, false ) && m_Vendor.IsLandlord( from ); + } + + protected override void LandlordRenewOnExpiration( Mobile from ) + { + m_Vendor.LandlordRenew = !m_Vendor.LandlordRenew; + + from.SendGump( new LandlordVendorRentalGump( m_Vendor ) ); + } + + protected override void SetRenewalPrice( Mobile from ) + { + from.SendLocalizedMessage( 1062500 ); // Enter contract renewal price: + + from.Prompt = new ContractRenewalPricePrompt( m_Vendor ); + } + + private class ContractRenewalPricePrompt : Prompt + { + private RentedVendor m_Vendor; + + public ContractRenewalPricePrompt( RentedVendor vendor ) + { + m_Vendor = vendor; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_Vendor.CanInteractWith( from, false ) || !m_Vendor.IsLandlord( from ) ) + return; + + text = text.Trim(); + + int price; + + try + { + price = Convert.ToInt32( text ); + } + catch + { + price = -1; + } + + if ( price < 0 ) + { + from.SendLocalizedMessage( 1062485 ); // Invalid entry. Rental fee set to 0. + m_Vendor.RenewalPrice = 0; + } + else if ( price > 5000000 ) + { + m_Vendor.RenewalPrice = 5000000; + } + else + { + m_Vendor.RenewalPrice = price; + } + + m_Vendor.RenterRenew = false; + + from.SendGump( new LandlordVendorRentalGump( m_Vendor ) ); + } + + public override void OnCancel( Mobile from ) + { + if ( m_Vendor.CanInteractWith( from, false ) && m_Vendor.IsLandlord( from ) ) + from.SendGump( new LandlordVendorRentalGump( m_Vendor ) ); + } + } + } + + public class VendorRentalRefundGump : Gump + { + private RentedVendor m_Vendor; + private Mobile m_Landlord; + private int m_RefundAmount; + + public VendorRentalRefundGump( RentedVendor vendor, Mobile landlord, int refundAmount ) : base( 50, 50 ) + { + m_Vendor = vendor; + m_Landlord = landlord; + m_RefundAmount = refundAmount; + + AddBackground( 0, 0, 420, 320, 0x13BE ); + + AddImageTiled( 10, 10, 400, 300, 0xA40 ); + AddAlphaRegion( 10, 10, 400, 300 ); + + /* The landlord for this vendor is offering you a partial refund of your rental fee + * in exchange for immediate termination of your rental contract.

+ * + * If you accept this offer, the vendor will be immediately dismissed. You will then + * be able to claim the inventory and any funds the vendor may be holding for you via + * a context menu on the house sign for this house. + */ + AddHtmlLocalized( 10, 10, 400, 150, 1062501, 0x7FFF, false, true ); + + AddHtmlLocalized( 10, 180, 150, 20, 1062508, 0x7FFF, false, false ); // Vendor Name: + AddLabel( 160, 180, 0x480, vendor.Name ); + + AddHtmlLocalized( 10, 200, 150, 20, 1062509, 0x7FFF, false, false ); // Shop Name: + AddLabel( 160, 200, 0x480, vendor.ShopName ); + + AddHtmlLocalized( 10, 220, 150, 20, 1062510, 0x7FFF, false, false ); // Refund Amount: + AddLabel( 160, 220, 0x480, refundAmount.ToString() ); + + AddButton( 10, 268, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 268, 350, 20, 1062511, 0x7FFF, false, false ); // Agree, and dismiss vendor + + AddButton( 10, 288, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 288, 350, 20, 1062512, 0x7FFF, false, false ); // No, I want to keep my vendor + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( !m_Vendor.CanInteractWith( from, true ) || !m_Vendor.CanInteractWith( m_Landlord, false ) || !m_Vendor.IsLandlord( m_Landlord ) ) + return; + + if ( info.ButtonID == 1 ) + { + if ( Banker.Withdraw( m_Landlord, m_RefundAmount ) ) + { + m_Landlord.SendLocalizedMessage( 1060398, m_RefundAmount.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + + int depositedGold = Banker.DepositUpTo( from, m_RefundAmount ); + + if ( depositedGold > 0 ) + from.SendLocalizedMessage( 1060397, depositedGold.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + + m_Vendor.HoldGold += m_RefundAmount - depositedGold; + + m_Vendor.Destroy( false ); + + from.SendMessage( "Remember to claim your vendor's belongings from the house sign!" ); + } + else + { + m_Landlord.SendLocalizedMessage( 1062507 ); // You do not have that much money in your bank account. + } + } + else + { + m_Landlord.SendLocalizedMessage( 1062513 ); // The renter declined your offer. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/ViewHousesGump.cs b/Scripts/Gumps/ViewHousesGump.cs new file mode 100644 index 000000000..99ee0b438 --- /dev/null +++ b/Scripts/Gumps/ViewHousesGump.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Multis; +using Server.Targeting; +using Server.Accounting; +using Server.Commands; + +namespace Server.Gumps +{ + public class ViewHousesGump : Gump + { + public static void Initialize() + { + CommandSystem.Register( "ViewHouses", AccessLevel.GameMaster, new CommandEventHandler( ViewHouses_OnCommand ) ); + } + + [Usage( "ViewHouses" )] + [Description( "Displays a menu listing all houses of a targeted player. The menu also contains specific house details, and options to: go to house, open house menu, and demolish house." )] + public static void ViewHouses_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ViewHouses_OnTarget ) ); + } + + public static void ViewHouses_OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + from.SendGump( new ViewHousesGump( from, GetHouses( (Mobile)targeted ), null ) ); + } + + private class HouseComparer : IComparer + { + public static readonly IComparer Instance = new HouseComparer(); + + public int Compare( object x, object y ) + { + return ((BaseHouse)x).BuiltOn.CompareTo( ((BaseHouse)y).BuiltOn ); + } + } + + public static ArrayList GetHouses( Mobile owner ) + { + ArrayList list = new ArrayList(); + + Account acct = owner.Account as Account; + + if ( acct == null ) + { + list.AddRange( BaseHouse.GetHouses( owner ) ); + } + else + { + for ( int i = 0; i < acct.Length; ++i ) + { + Mobile mob = acct[i]; + + if ( mob != null ) + list.AddRange( BaseHouse.GetHouses( mob ) ); + } + } + + list.Sort( HouseComparer.Instance ); + + return list; + } + + private Mobile m_From; + private ArrayList m_List; + private BaseHouse m_Selection; + + public ViewHousesGump( Mobile from, ArrayList list, BaseHouse sel ) : base( 50, 40 ) + { + m_From = from; + m_List = list; + m_Selection = sel; + + from.CloseGump( typeof( ViewHousesGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 240, 360, 5054 ); + AddBlackAlpha( 10, 10, 220, 340 ); + + if ( sel == null || sel.Deleted ) + { + m_Selection = null; + + AddHtml( 35, 15, 120, 20, Color( "House Type", White ), false, false ); + + if ( list.Count == 0 ) + AddHtml( 35, 40, 160, 40, Color( "There were no houses found for that player.", White ), false, false ); + + AddImage( 190, 17, 0x25EA ); + AddImage( 207, 17, 0x25E6 ); + + int page = 0; + + for ( int i = 0; i < list.Count; ++i ) + { + if ( (i % 15) == 0 ) + { + if ( page > 0 ) + AddButton( 207, 17, 0x15E1, 0x15E5, 0, GumpButtonType.Page, page+1 ); + + AddPage( ++page ); + + if ( page > 1 ) + AddButton( 190, 17, 0x15E3, 0x15E7, 0, GumpButtonType.Page, page-1 ); + } + + object name = FindHouseName( (BaseHouse)list[i] ); + + AddHtml( 15, 40 + ((i % 15) * 20), 20, 20, Color( String.Format( "{0}.", i+1 ), White ), false, false ); + + if ( name is int ) + AddHtmlLocalized( 35, 40 + ((i % 15) * 20), 160, 20, (int)name, White16, false, false ); + else if ( name is string ) + AddHtml( 35, 40 + ((i % 15) * 20), 160, 20, Color( (string)name, White ), false, false ); + + AddButton( 198, 39 + ((i % 15) * 20), 4005, 4007, i+1, GumpButtonType.Reply, 0 ); + } + } + else + { + string houseName, owner, location; + Map map = sel.Map; + + houseName = (sel.Sign == null) ? "An Unnamed House" : sel.Sign.GetName(); + owner = (sel.Owner == null) ? "nobody" : sel.Owner.Name; + + int xLong = 0, yLat = 0, xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + bool valid = Sextant.Format( sel.Location, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ); + + if ( valid ) + location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + else + location = "unknown"; + + AddHtml( 10, 15, 220, 20, Color( Center( "House Properties" ), White ), false, false ); + + AddHtml( 15, 40, 210, 20, Color( "Facet:", White ), false, false ); + AddHtml( 15, 40, 210, 20, Color( Right( map == null ? "(null)" : map.Name ), White ), false, false ); + + AddHtml( 15, 60, 210, 20, Color( "Location:", White ), false, false ); + AddHtml( 15, 60, 210, 20, Color( Right( sel.Location.ToString() ), White ), false, false ); + + AddHtml( 15, 80, 210, 20, Color( "Sextant:", White ), false, false ); + AddHtml( 15, 80, 210, 20, Color( Right( location ), White ), false, false ); + + AddHtml( 15, 100, 210, 20, Color( "Owner:", White ), false, false ); + AddHtml( 15, 100, 210, 20, Color( Right( owner ), White ), false, false ); + + AddHtml( 15, 120, 210, 20, Color( "Name:", White ), false, false ); + AddHtml( 15, 120, 210, 20, Color( Right( houseName ), White ), false, false ); + + AddHtml( 15, 140, 210, 20, Color( "Friends:", White ), false, false ); + AddHtml( 15, 140, 210, 20, Color( Right( sel.Friends.Count.ToString() ), White ), false, false ); + + AddHtml( 15, 160, 210, 20, Color( "Co-Owners:", White ), false, false ); + AddHtml( 15, 160, 210, 20, Color( Right( sel.CoOwners.Count.ToString() ), White ), false, false ); + + AddHtml( 15, 180, 210, 20, Color( "Bans:", White ), false, false ); + AddHtml( 15, 180, 210, 20, Color( Right( sel.Bans.Count.ToString() ), White ), false, false ); + + AddHtml( 15, 200, 210, 20, Color( "Decays:", White ), false, false ); + AddHtml( 15, 200, 210, 20, Color( Right( sel.CanDecay ? "Yes" : "No" ), White ), false, false ); + + AddHtml( 15, 220, 210, 20, Color( "Decay Level:", White ), false, false ); + AddHtml( 15, 220, 210, 20, Color( Right( sel.DecayLevel.ToString() ), White ), false, false ); + + AddButton( 15, 245, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtml( 50, 245, 120, 20, Color( "Go to house", White ), false, false ); + + AddButton( 15, 265, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtml( 50, 265, 120, 20, Color( "Open house menu", White ), false, false ); + + AddButton( 15, 285, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtml( 50, 285, 120, 20, Color( "Demolish house", White ), false, false ); + + AddButton( 15, 305, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + AddHtml( 50, 305, 120, 20, Color( "Refresh house", White ), false, false ); + } + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( m_Selection == null ) + { + int v = info.ButtonID - 1; + + if ( v >= 0 && v < m_List.Count ) + m_From.SendGump( new ViewHousesGump( m_From, m_List, (BaseHouse)m_List[v] ) ); + } + else if ( !m_Selection.Deleted ) + { + switch ( info.ButtonID ) + { + case 0: + { + m_From.SendGump( new ViewHousesGump( m_From, m_List, null ) ); + break; + } + case 1: + { + Map map = m_Selection.Map; + + if ( map != null && map != Map.Internal ) + m_From.MoveToWorld( m_Selection.BanLocation, map ); + + m_From.SendGump( new ViewHousesGump( m_From, m_List, m_Selection ) ); + + break; + } + case 2: + { + m_From.SendGump( new ViewHousesGump( m_From, m_List, m_Selection ) ); + + HouseSign sign = m_Selection.Sign; + + if ( sign != null && !sign.Deleted ) + sign.OnDoubleClick( m_From ); + + break; + } + case 3: + { + m_From.SendGump( new ViewHousesGump( m_From, m_List, m_Selection ) ); + m_From.SendGump( new HouseDemolishGump( m_From, m_Selection ) ); + + break; + } + case 4: + { + m_Selection.RefreshDecay(); + m_From.SendGump( new ViewHousesGump( m_From, m_List, m_Selection ) ); + + break; + } + } + } + } + + public object FindHouseName( BaseHouse house ) + { + int multiID = house.ItemID & 0x3FFF; + HousePlacementEntry[] entries; + + entries = HousePlacementEntry.ClassicHouses; + + for ( int i = 0; i < entries.Length; ++i ) + { + if ( entries[i].MultiID == multiID ) + return entries[i].Description; + } + + entries = HousePlacementEntry.TwoStoryFoundations; + + for ( int i = 0; i < entries.Length; ++i ) + { + if ( entries[i].MultiID == multiID ) + return entries[i].Description; + } + + entries = HousePlacementEntry.ThreeStoryFoundations; + + for ( int i = 0; i < entries.Length; ++i ) + { + if ( entries[i].MultiID == multiID ) + return entries[i].Description; + } + + return house.GetType().Name; + } + + private const int White16 = 0x7FFF; + private const int White = 0xFFFFFF; + + public string Right( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + public void AddBlackAlpha( int x, int y, int width, int height ) + { + AddImageTiled( x, y, width, height, 2624 ); + AddAlphaRegion( x, y, width, height ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/WarningGump.cs b/Scripts/Gumps/WarningGump.cs new file mode 100644 index 000000000..bba00a492 --- /dev/null +++ b/Scripts/Gumps/WarningGump.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Gumps +{ + public delegate void WarningGumpCallback( Mobile from, bool okay, object state ); + + public class WarningGump : Gump + { + private WarningGumpCallback m_Callback; + private object m_State; + + public WarningGump( int header, int headerColor, object content, int contentColor, int width, int height, WarningGumpCallback callback, object state ) : base( (640 - width) / 2, (480 - height) / 2 ) + { + m_Callback = callback; + m_State = state; + + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, width, height, 5054 ); + + AddImageTiled( 10, 10, width - 20, 20, 2624 ); + AddAlphaRegion( 10, 10, width - 20, 20 ); + AddHtmlLocalized( 10, 10, width - 20, 20, header, headerColor, false, false ); + + AddImageTiled( 10, 40, width - 20, height - 80, 2624 ); + AddAlphaRegion( 10, 40, width - 20, height - 80 ); + + if ( content is int ) + AddHtmlLocalized( 10, 40, width - 20, height - 80, (int)content, contentColor, false, true ); + else if ( content is string ) + AddHtml( 10, 40, width - 20, height - 80, String.Format( "{1}", contentColor, content ), false, true ); + + AddImageTiled( 10, height - 30, width - 20, 20, 2624 ); + AddAlphaRegion( 10, height - 30, width - 20, 20 ); + + AddButton( 10, height - 30, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40, height - 30, 170, 20, 1011036, 32767, false, false ); // OKAY + + AddButton( 10 + ((width - 20) / 2), height - 30, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40 + ((width - 20) / 2), height - 30, 170, 20, 1011012, 32767, false, false ); // CANCEL + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 && m_Callback != null ) + m_Callback( sender.Mobile, true, m_State ); + else if ( m_Callback != null ) + m_Callback( sender.Mobile, false, m_State ); + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/WhoGump.cs b/Scripts/Gumps/WhoGump.cs new file mode 100644 index 000000000..62635bd0a --- /dev/null +++ b/Scripts/Gumps/WhoGump.cs @@ -0,0 +1,311 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Commands; +using Server.Mobiles; +using Server.Network; + +namespace Server.Gumps +{ + public class WhoGump : Gump + { + public static void Initialize() + { + CommandSystem.Register( "Who", AccessLevel.Counselor, new CommandEventHandler( WhoList_OnCommand ) ); + CommandSystem.Register( "WhoList", AccessLevel.Counselor, new CommandEventHandler( WhoList_OnCommand ) ); + } + + [Usage( "WhoList [filter]" )] + [Aliases( "Who" )] + [Description( "Lists all connected clients. Optionally filters results by name." )] + private static void WhoList_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendGump( new WhoGump( e.Mobile, e.ArgString ) ); + } + + public static bool OldStyle = PropsConfig.OldStyle; + + public static readonly int GumpOffsetX = PropsConfig.GumpOffsetX; + public static readonly int GumpOffsetY = PropsConfig.GumpOffsetY; + + public static readonly int TextHue = PropsConfig.TextHue; + public static readonly int TextOffsetX = PropsConfig.TextOffsetX; + + public static readonly int OffsetGumpID = PropsConfig.OffsetGumpID; + public static readonly int HeaderGumpID = PropsConfig.HeaderGumpID; + public static readonly int EntryGumpID = PropsConfig.EntryGumpID; + public static readonly int BackGumpID = PropsConfig.BackGumpID; + public static readonly int SetGumpID = PropsConfig.SetGumpID; + + public static readonly int SetWidth = PropsConfig.SetWidth; + public static readonly int SetOffsetX = PropsConfig.SetOffsetX, SetOffsetY = PropsConfig.SetOffsetY; + public static readonly int SetButtonID1 = PropsConfig.SetButtonID1; + public static readonly int SetButtonID2 = PropsConfig.SetButtonID2; + + public static readonly int PrevWidth = PropsConfig.PrevWidth; + public static readonly int PrevOffsetX = PropsConfig.PrevOffsetX, PrevOffsetY = PropsConfig.PrevOffsetY; + public static readonly int PrevButtonID1 = PropsConfig.PrevButtonID1; + public static readonly int PrevButtonID2 = PropsConfig.PrevButtonID2; + + public static readonly int NextWidth = PropsConfig.NextWidth; + public static readonly int NextOffsetX = PropsConfig.NextOffsetX, NextOffsetY = PropsConfig.NextOffsetY; + public static readonly int NextButtonID1 = PropsConfig.NextButtonID1; + public static readonly int NextButtonID2 = PropsConfig.NextButtonID2; + + public static readonly int OffsetSize = PropsConfig.OffsetSize; + + public static readonly int EntryHeight = PropsConfig.EntryHeight; + public static readonly int BorderSize = PropsConfig.BorderSize; + + private static bool PrevLabel = false, NextLabel = false; + + private static readonly int PrevLabelOffsetX = PrevWidth + 1; + private static readonly int PrevLabelOffsetY = 0; + + private static readonly int NextLabelOffsetX = -29; + private static readonly int NextLabelOffsetY = 0; + + private static readonly int EntryWidth = 180; + private static readonly int EntryCount = 15; + + private static readonly int TotalWidth = OffsetSize + EntryWidth + OffsetSize + SetWidth + OffsetSize; + private static readonly int TotalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (EntryCount + 1)); + + private static readonly int BackWidth = BorderSize + TotalWidth + BorderSize; + private static readonly int BackHeight = BorderSize + TotalHeight + BorderSize; + + private Mobile m_Owner; + private ArrayList m_Mobiles; + private int m_Page; + + private class InternalComparer : IComparer + { + public static readonly IComparer Instance = new InternalComparer(); + + public InternalComparer() + { + } + + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + Mobile a = x as Mobile; + Mobile b = y as Mobile; + + if ( a == null || b == null ) + throw new ArgumentException(); + + if ( a.AccessLevel > b.AccessLevel ) + return -1; + else if ( a.AccessLevel < b.AccessLevel ) + return 1; + else + return Insensitive.Compare( a.Name, b.Name ); + } + } + + public WhoGump( Mobile owner, string filter ) : this( owner, BuildList( owner, filter ), 0 ) + { + } + + public WhoGump( Mobile owner, ArrayList list, int page ) : base( GumpOffsetX, GumpOffsetY ) + { + owner.CloseGump( typeof( WhoGump ) ); + + m_Owner = owner; + m_Mobiles = list; + + Initialize( page ); + } + + public static ArrayList BuildList( Mobile owner, string filter ) + { + if ( filter != null && (filter = filter.Trim()).Length == 0 ) + filter = null; + else + filter = filter.ToLower(); + + ArrayList list = new ArrayList(); + List states = NetState.Instances; + + for ( int i = 0; i < states.Count; ++i ) + { + Mobile m = states[i].Mobile; + + if ( m != null && (m == owner ||!m.Hidden || owner.AccessLevel >= m.AccessLevel || (m is PlayerMobile && ((PlayerMobile)m).VisibilityList.Contains( owner ) ) ) ) + { + if ( filter != null && ( m.Name == null || m.Name.ToLower().IndexOf( filter ) < 0 ) ) + continue; + + list.Add( m ); + } + } + + list.Sort( InternalComparer.Instance ); + + return list; + } + + public void Initialize( int page ) + { + m_Page = page; + + int count = m_Mobiles.Count - (page * EntryCount); + + if ( count < 0 ) + count = 0; + else if ( count > EntryCount ) + count = EntryCount; + + int totalHeight = OffsetSize + ((EntryHeight + OffsetSize) * (count + 1)); + + AddPage( 0 ); + + AddBackground( 0, 0, BackWidth, BorderSize + totalHeight + BorderSize, BackGumpID ); + AddImageTiled( BorderSize, BorderSize, TotalWidth - (OldStyle ? SetWidth + OffsetSize : 0), totalHeight, OffsetGumpID ); + + int x = BorderSize + OffsetSize; + int y = BorderSize + OffsetSize; + + int emptyWidth = TotalWidth - PrevWidth - NextWidth - (OffsetSize * 4) - (OldStyle ? SetWidth + OffsetSize : 0); + + if ( !OldStyle ) + AddImageTiled( x - (OldStyle ? OffsetSize : 0), y, emptyWidth + (OldStyle ? OffsetSize * 2 : 0), EntryHeight, EntryGumpID ); + + AddLabel( x + TextOffsetX, y, TextHue, String.Format( "Page {0} of {1} ({2})", page+1, (m_Mobiles.Count + EntryCount - 1) / EntryCount, m_Mobiles.Count ) ); + + x += emptyWidth + OffsetSize; + + if ( OldStyle ) + AddImageTiled( x, y, TotalWidth - (OffsetSize * 3) - SetWidth, EntryHeight, HeaderGumpID ); + else + AddImageTiled( x, y, PrevWidth, EntryHeight, HeaderGumpID ); + + if ( page > 0 ) + { + AddButton( x + PrevOffsetX, y + PrevOffsetY, PrevButtonID1, PrevButtonID2, 1, GumpButtonType.Reply, 0 ); + + if ( PrevLabel ) + AddLabel( x + PrevLabelOffsetX, y + PrevLabelOffsetY, TextHue, "Previous" ); + } + + x += PrevWidth + OffsetSize; + + if ( !OldStyle ) + AddImageTiled( x, y, NextWidth, EntryHeight, HeaderGumpID ); + + if ( (page + 1) * EntryCount < m_Mobiles.Count ) + { + AddButton( x + NextOffsetX, y + NextOffsetY, NextButtonID1, NextButtonID2, 2, GumpButtonType.Reply, 1 ); + + if ( NextLabel ) + AddLabel( x + NextLabelOffsetX, y + NextLabelOffsetY, TextHue, "Next" ); + } + + for ( int i = 0, index = page * EntryCount; i < EntryCount && index < m_Mobiles.Count; ++i, ++index ) + { + x = BorderSize + OffsetSize; + y += EntryHeight + OffsetSize; + + Mobile m = (Mobile)m_Mobiles[index]; + + AddImageTiled( x, y, EntryWidth, EntryHeight, EntryGumpID ); + AddLabelCropped( x + TextOffsetX, y, EntryWidth - TextOffsetX, EntryHeight, GetHueFor( m ), m.Deleted ? "(deleted)" : m.Name ); + + x += EntryWidth + OffsetSize; + + if ( SetGumpID != 0 ) + AddImageTiled( x, y, SetWidth, EntryHeight, SetGumpID ); + + if ( m.NetState != null && !m.Deleted ) + AddButton( x + SetOffsetX, y + SetOffsetY, SetButtonID1, SetButtonID2, i + 3, GumpButtonType.Reply, 0 ); + } + } + + private static int GetHueFor( Mobile m ) + { + switch ( m.AccessLevel ) + { + case AccessLevel.Owner: + case AccessLevel.Developer: + case AccessLevel.Administrator: return 0x516; + case AccessLevel.Seer: return 0x144; + case AccessLevel.GameMaster: return 0x21; + case AccessLevel.Counselor: return 0x2; + case AccessLevel.Player: default: + { + if ( m.Kills >= 5 ) + return 0x21; + else if ( m.Criminal ) + return 0x3B1; + + return 0x58; + } + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + Mobile from = state.Mobile; + + switch ( info.ButtonID ) + { + case 0: // Closed + { + return; + } + case 1: // Previous + { + if ( m_Page > 0 ) + from.SendGump( new WhoGump( from, m_Mobiles, m_Page - 1 ) ); + + break; + } + case 2: // Next + { + if ( (m_Page + 1) * EntryCount < m_Mobiles.Count ) + from.SendGump( new WhoGump( from, m_Mobiles, m_Page + 1 ) ); + + break; + } + default: + { + int index = (m_Page * EntryCount) + (info.ButtonID - 3); + + if ( index >= 0 && index < m_Mobiles.Count ) + { + Mobile m = (Mobile)m_Mobiles[index]; + + if ( m.Deleted ) + { + from.SendMessage( "That player has deleted their character." ); + from.SendGump( new WhoGump( from, m_Mobiles, m_Page ) ); + } + else if ( m.NetState == null ) + { + from.SendMessage( "That player is no longer online." ); + from.SendGump( new WhoGump( from, m_Mobiles, m_Page ) ); + } + else if ( m == from || !m.Hidden || from.AccessLevel >= m.AccessLevel || (m is PlayerMobile && ((PlayerMobile)m).VisibilityList.Contains( from ))) + { + from.SendGump( new ClientGump( from, m.NetState ) ); + } + else + { + from.SendMessage( "You cannot see them." ); + from.SendGump( new WhoGump( from, m_Mobiles, m_Page ) ); + } + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Gumps/YoungGumps.cs b/Scripts/Gumps/YoungGumps.cs new file mode 100644 index 000000000..64effb158 --- /dev/null +++ b/Scripts/Gumps/YoungGumps.cs @@ -0,0 +1,98 @@ +using System; +using Server; +using Server.Network; +using Server.Mobiles; +using Server.Accounting; + +namespace Server.Gumps +{ + public class YoungDungeonWarning : Gump + { + public YoungDungeonWarning() : base( 150, 200 ) + { + AddBackground( 0, 0, 250, 170, 0xA28 ); + + AddHtmlLocalized( 20, 43, 215, 70, 1018030, true, true ); // Warning: monsters may attack you on site down here in the dungeons! + + AddButton( 70, 123, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 105, 125, 100, 35, 1011036, false, false ); // OKAY + } + } + + public class YoungDeathNotice : Gump + { + public YoungDeathNotice() : base( 100, 15 ) + { + Closable = false; + + AddBackground( 25, 10, 425, 444, 0x13BE ); + + AddImageTiled( 33, 20, 407, 425, 0xA40 ); + AddAlphaRegion( 33, 20, 407, 425 ); + + AddHtmlLocalized( 190, 24, 120, 20, 1046287, 0x7D00, false, false ); // You have died. + + // As a ghost you cannot interact with the world. You cannot touch items nor can you use them. + AddHtmlLocalized( 50, 50, 380, 40, 1046288, 0xFFFFFF, false, false ); + // You can pass through doors as though they do not exist. However, you cannot pass through walls. + AddHtmlLocalized( 50, 100, 380, 45, 1046289, 0xFFFFFF, false, false ); + // Since you are a new player, any items you had on your person at the time of your death will be in your backpack upon resurrection. + AddHtmlLocalized( 50, 140, 380, 60, 1046291, 0xFFFFFF, false, false ); + // To be resurrected you must find a healer in town or wandering in the wilderness. Some powerful players may also be able to resurrect you. + AddHtmlLocalized( 50, 204, 380, 65, 1046292, 0xFFFFFF, false, false ); + // While you are still in young status, you will be transported to the nearest healer (along with your items) at the time of your death. + AddHtmlLocalized( 50, 269, 380, 65, 1046293, 0xFFFFFF, false, false ); + // To rejoin the world of the living simply walk near one of the NPC healers, and they will resurrect you as long as you are not marked as a criminal. + AddHtmlLocalized( 50, 334, 380, 70, 1046294, 0xFFFFFF, false, false ); + + AddButton( 195, 410, 0xF8, 0xF9, 0, GumpButtonType.Reply, 0 ); + } + } + + public class RenounceYoungGump : Gump + { + public RenounceYoungGump() : base( 150, 50 ) + { + AddBackground( 0, 0, 450, 400, 0xA28 ); + + AddHtmlLocalized( 0, 30, 450, 35, 1013004, false, false ); //
Renouncing 'Young Player' Status
+ + /* As a 'Young' player, you are currently under a system of protection that prevents + * you from being attacked by other players and certain monsters.

+ * + * If you choose to renounce your status as a 'Young' player, you will lose this protection. + * You will become vulnerable to other players, and many monsters that had only glared + * at you menacingly before will now attack you on sight!

+ * + * Select OKAY now if you wish to renounce your status as a 'Young' player, otherwise + * press CANCEL. + */ + AddHtmlLocalized( 30, 70, 390, 210, 1013005, true, true ); + + AddButton( 45, 298, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 78, 300, 100, 35, 1011036, false, false ); // OKAY + + AddButton( 178, 298, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 211, 300, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 1 ) + { + Account acc = from.Account as Account; + + if ( acc != null ) + { + acc.RemoveYoungStatus( 502085 ); // You have chosen to renounce your `Young' player status. + } + } + else + { + from.SendLocalizedMessage( 502086 ); // You have chosen not to renounce your `Young' player status. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/AbbatoirAddon.cs b/Scripts/Items/Addons/AbbatoirAddon.cs new file mode 100644 index 000000000..392f1d078 --- /dev/null +++ b/Scripts/Items/Addons/AbbatoirAddon.cs @@ -0,0 +1,71 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AbbatoirAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new AbbatoirDeed(); } } + + [Constructable] + public AbbatoirAddon() + { + AddComponent( new AddonComponent( 0x120E ), -1, -1, 0 ); + AddComponent( new AddonComponent( 0x120F ), 0, -1, 0 ); + AddComponent( new AddonComponent( 0x1210 ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0x1215 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x1216 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1211 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1214 ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0x1213 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1212 ), 1, 1, 0 ); + } + + public AbbatoirAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AbbatoirDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new AbbatoirAddon(); } } + public override int LabelNumber{ get{ return 1044329; } } // abbatoir + + [Constructable] + public AbbatoirDeed() + { + } + + public AbbatoirDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/AddonComponent.cs b/Scripts/Items/Addons/AddonComponent.cs new file mode 100644 index 000000000..f593e7194 --- /dev/null +++ b/Scripts/Items/Addons/AddonComponent.cs @@ -0,0 +1,285 @@ +using System; +using Server; + +namespace Server.Items +{ + [Server.Engines.Craft.Anvil] + public class AnvilComponent : AddonComponent + { + [Constructable] + public AnvilComponent( int itemID ) : base( itemID ) + { + } + + public AnvilComponent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Server.Engines.Craft.Forge] + public class ForgeComponent : AddonComponent + { + [Constructable] + public ForgeComponent( int itemID ) : base( itemID ) + { + } + + public ForgeComponent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LocalizedAddonComponent : AddonComponent + { + private int m_LabelNumber; + + [CommandProperty( AccessLevel.GameMaster )] + public int Number + { + get{ return m_LabelNumber; } + set{ m_LabelNumber = value; InvalidateProperties(); } + } + + public override int LabelNumber{ get{ return m_LabelNumber; } } + + [Constructable] + public LocalizedAddonComponent( int itemID, int labelNumber ) : base( itemID ) + { + m_LabelNumber = labelNumber; + } + + public LocalizedAddonComponent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_LabelNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_LabelNumber = reader.ReadInt(); + break; + } + } + } + } + + public class AddonComponent : Item, IChopable + { + private Point3D m_Offset; + private BaseAddon m_Addon; + + [CommandProperty( AccessLevel.GameMaster )] + public BaseAddon Addon + { + get + { + return m_Addon; + } + set + { + m_Addon = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Offset + { + get + { + return m_Offset; + } + set + { + m_Offset = value; + } + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get + { + return base.Hue; + } + set + { + base.Hue = value; + + if ( m_Addon != null && m_Addon.ShareHue ) + m_Addon.Hue = value; + } + } + + public virtual bool NeedsWall{ get{ return false; } } + public virtual Point3D WallPosition{ get{ return Point3D.Zero; } } + + [Constructable] + public AddonComponent( int itemID ) : base( itemID ) + { + Movable = false; + ApplyLightTo( this ); + } + + public AddonComponent( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Addon != null ) + m_Addon.OnComponentUsed( this, from ); + } + + public void OnChop( Mobile from ) + { + if ( m_Addon != null && from.InRange( GetWorldLocation(), 3 ) ) + m_Addon.OnChop( from ); + else + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + + public override void OnLocationChange( Point3D old ) + { + if ( m_Addon != null ) + m_Addon.Location = new Point3D( X - m_Offset.X, Y - m_Offset.Y, Z - m_Offset.Z ); + } + + public override void OnMapChange() + { + if ( m_Addon != null ) + m_Addon.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Addon != null ) + m_Addon.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_Addon ); + writer.Write( m_Offset ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_Addon = reader.ReadItem() as BaseAddon; + m_Offset = reader.ReadPoint3D(); + + if ( m_Addon != null ) + m_Addon.OnComponentLoaded( this ); + + ApplyLightTo( this ); + + break; + } + } + + if ( version < 1 && Weight == 0 ) + Weight = -1; + } + + public static void ApplyLightTo( Item item ) + { + if ( (item.ItemData.Flags & TileFlag.LightSource) == 0 ) + return; // not a light source + + int itemID = item.ItemID; + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + LightEntry entry = m_Entries[i]; + int[] toMatch = entry.m_ItemIDs; + bool contains = false; + + for ( int j = 0; !contains && j < toMatch.Length; ++j ) + contains = ( itemID == toMatch[j] ); + + if ( contains ) + { + item.Light = entry.m_Light; + return; + } + } + } + + private static LightEntry[] m_Entries = new LightEntry[] + { + new LightEntry( LightType.WestSmall, 1122, 1123, 1124, 1141, 1142, 1143, 1144, 1145, 1146, 2347, 2359, 2360, 2361, 2362, 2363, 2364, 2387, 2388, 2389, 2390, 2391, 2392 ), + new LightEntry( LightType.NorthSmall, 1131, 1133, 1134, 1147, 1148, 1149, 1150, 1151, 1152, 2352, 2373, 2374, 2375, 2376, 2377, 2378, 2401, 2402, 2403, 2404, 2405, 2406 ), + new LightEntry( LightType.Circle300, 6526, 6538, 6571 ), + new LightEntry( LightType.Circle150, 5703, 6587 ) + }; + + private class LightEntry + { + public LightType m_Light; + public int[] m_ItemIDs; + + public LightEntry( LightType light, params int[] itemIDs ) + { + m_Light = light; + m_ItemIDs = itemIDs; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/AlchemistTableEastAddon.cs b/Scripts/Items/Addons/AlchemistTableEastAddon.cs new file mode 100644 index 000000000..19577e023 --- /dev/null +++ b/Scripts/Items/Addons/AlchemistTableEastAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AlchemistTableEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new AlchemistTableEastDeed(); } } + + [Constructable] + public AlchemistTableEastAddon() + { + AddComponent( new AddonComponent( 0x2DD3 ), 0, 0, 0 ); + } + + public AlchemistTableEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class AlchemistTableEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new AlchemistTableEastAddon(); } } + public override int LabelNumber{ get{ return 1073397; } } // alchemist table (east) + + [Constructable] + public AlchemistTableEastDeed() + { + } + + public AlchemistTableEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/AlchemistTableSouthAddon.cs b/Scripts/Items/Addons/AlchemistTableSouthAddon.cs new file mode 100644 index 000000000..1302da0d9 --- /dev/null +++ b/Scripts/Items/Addons/AlchemistTableSouthAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AlchemistTableSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new AlchemistTableSouthDeed(); } } + + [Constructable] + public AlchemistTableSouthAddon() + { + AddComponent( new AddonComponent( 0x2DD4 ), 0, 0, 0 ); + } + + public AlchemistTableSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class AlchemistTableSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new AlchemistTableSouthAddon(); } } + public override int LabelNumber{ get{ return 1073396; } } // alchemist table (south) + + [Constructable] + public AlchemistTableSouthDeed() + { + } + + public AlchemistTableSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/AnvilEastAddon.cs b/Scripts/Items/Addons/AnvilEastAddon.cs new file mode 100644 index 000000000..2df2183ff --- /dev/null +++ b/Scripts/Items/Addons/AnvilEastAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AnvilEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new AnvilEastDeed(); } } + + [Constructable] + public AnvilEastAddon() + { + AddComponent( new AnvilComponent( 0xFAF ), 0, 0, 0 ); + } + + public AnvilEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AnvilEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new AnvilEastAddon(); } } + public override int LabelNumber{ get{ return 1044333; } } // anvil (east) + + [Constructable] + public AnvilEastDeed() + { + } + + public AnvilEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/AnvilSouthAddon.cs b/Scripts/Items/Addons/AnvilSouthAddon.cs new file mode 100644 index 000000000..e635c91ac --- /dev/null +++ b/Scripts/Items/Addons/AnvilSouthAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AnvilSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new AnvilSouthDeed(); } } + + [Constructable] + public AnvilSouthAddon() + { + AddComponent( new AnvilComponent( 0xFB0 ), 0, 0, 0 ); + } + + public AnvilSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AnvilSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new AnvilSouthAddon(); } } + public override int LabelNumber{ get{ return 1044334; } } // anvil (south) + + [Constructable] + public AnvilSouthDeed() + { + } + + public AnvilSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ArcaneBookshelfEastAddon.cs b/Scripts/Items/Addons/ArcaneBookshelfEastAddon.cs new file mode 100644 index 000000000..673d197d9 --- /dev/null +++ b/Scripts/Items/Addons/ArcaneBookshelfEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcaneBookshelfEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ArcaneBookshelfEastDeed(); } } + + [Constructable] + public ArcaneBookshelfEastAddon() + { + AddComponent( new AddonComponent( 0x3084 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3085 ), -1, 0, 0 ); + } + + public ArcaneBookshelfEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ArcaneBookshelfEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ArcaneBookshelfEastAddon(); } } + public override int LabelNumber{ get{ return 1073371; } } // arcane bookshelf (east) + + [Constructable] + public ArcaneBookshelfEastDeed() + { + } + + public ArcaneBookshelfEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ArcaneBookshelfSouthAddon.cs b/Scripts/Items/Addons/ArcaneBookshelfSouthAddon.cs new file mode 100644 index 000000000..f7e6499fc --- /dev/null +++ b/Scripts/Items/Addons/ArcaneBookshelfSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcaneBookshelfSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ArcaneBookshelfSouthDeed(); } } + + [Constructable] + public ArcaneBookshelfSouthAddon() + { + AddComponent( new AddonComponent( 0x3087 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3086 ), 0, 1, 0 ); + } + + public ArcaneBookshelfSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ArcaneBookshelfSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ArcaneBookshelfSouthAddon(); } } + public override int LabelNumber{ get{ return 1072871; } } // arcane bookshelf (south) + + [Constructable] + public ArcaneBookshelfSouthDeed() + { + } + + public ArcaneBookshelfSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ArcaneCircleAddon.cs b/Scripts/Items/Addons/ArcaneCircleAddon.cs new file mode 100644 index 000000000..2905c5eed --- /dev/null +++ b/Scripts/Items/Addons/ArcaneCircleAddon.cs @@ -0,0 +1,71 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcaneCircleAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ArcaneCircleDeed(); } } + + [Constructable] + public ArcaneCircleAddon() + { + AddComponent( new AddonComponent( 0x3080 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x3082 ), 0, -1, 0 ); + AddComponent( new AddonComponent( 0x3081 ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0x307D ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0x307F ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x307E ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x307C ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x307B ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x3083 ), 1, 1, 0 ); + } + + public ArcaneCircleAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ArcaneCircleDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ArcaneCircleAddon(); } } + public override int LabelNumber{ get{ return 1072703; } } // arcane circle + + [Constructable] + public ArcaneCircleDeed() + { + } + + public ArcaneCircleDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ArcanistStatueEastAddon.cs b/Scripts/Items/Addons/ArcanistStatueEastAddon.cs new file mode 100644 index 000000000..1f13f1851 --- /dev/null +++ b/Scripts/Items/Addons/ArcanistStatueEastAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcanistStatueEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ArcanistStatueEastDeed(); } } + + [Constructable] + public ArcanistStatueEastAddon() + { + AddComponent( new AddonComponent( 0x2D0E ), 0, 0, 0 ); + } + + public ArcanistStatueEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ArcanistStatueEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ArcanistStatueEastAddon(); } } + public override int LabelNumber{ get{ return 1072886; } } // arcanist statue (east) + + [Constructable] + public ArcanistStatueEastDeed() + { + } + + public ArcanistStatueEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ArcanistStatueSouthAddon.cs b/Scripts/Items/Addons/ArcanistStatueSouthAddon.cs new file mode 100644 index 000000000..1f055dec4 --- /dev/null +++ b/Scripts/Items/Addons/ArcanistStatueSouthAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcanistStatueSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ArcanistStatueSouthDeed(); } } + + [Constructable] + public ArcanistStatueSouthAddon() + { + AddComponent( new AddonComponent( 0x2D0F ), 0, 0, 0 ); + } + + public ArcanistStatueSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ArcanistStatueSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ArcanistStatueSouthAddon(); } } + public override int LabelNumber{ get{ return 1072885; } } // arcanist statue (south) + + [Constructable] + public ArcanistStatueSouthDeed() + { + } + + public ArcanistStatueSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ArcheryButteAddon.cs b/Scripts/Items/Addons/ArcheryButteAddon.cs new file mode 100644 index 000000000..828328ada --- /dev/null +++ b/Scripts/Items/Addons/ArcheryButteAddon.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections; +using Server; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x100A/*East*/, 0x100B/*South*/ )] + public class ArcheryButte : AddonComponent + { + private double m_MinSkill; + private double m_MaxSkill; + + private int m_Arrows, m_Bolts; + + private DateTime m_LastUse; + + [CommandProperty( AccessLevel.GameMaster )] + public double MinSkill + { + get{ return m_MinSkill; } + set{ m_MinSkill = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double MaxSkill + { + get{ return m_MaxSkill; } + set{ m_MaxSkill = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastUse + { + get{ return m_LastUse; } + set{ m_LastUse = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool FacingEast + { + get{ return ( ItemID == 0x100A ); } + set{ ItemID = value ? 0x100A : 0x100B; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Arrows + { + get{ return m_Arrows; } + set{ m_Arrows = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Bolts + { + get{ return m_Bolts; } + set{ m_Bolts = value; } + } + + [Constructable] + public ArcheryButte() : this( 0x100A ) + { + } + + public ArcheryButte( int itemID ) : base( itemID ) + { + m_MinSkill = -25.0; + m_MaxSkill = +25.0; + } + + public ArcheryButte( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( (m_Arrows > 0 || m_Bolts > 0) && from.InRange( GetWorldLocation(), 1 ) ) + Gather( from ); + else + Fire( from ); + } + + public void Gather( Mobile from ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500592 ); // You gather the arrows and bolts. + + if ( m_Arrows > 0 ) + from.AddToBackpack( new Arrow( m_Arrows ) ); + + if ( m_Bolts > 0 ) + from.AddToBackpack( new Bolt( m_Bolts ) ); + + m_Arrows = 0; + m_Bolts = 0; + + m_Entries = null; + } + + private static TimeSpan UseDelay = TimeSpan.FromSeconds( 2.0 ); + + private class ScoreEntry + { + private int m_Total; + private int m_Count; + + public int Total{ get{ return m_Total; } set{ m_Total = value; } } + public int Count{ get{ return m_Count; } set{ m_Count = value; } } + + public void Record( int score ) + { + m_Total += score; + m_Count += 1; + } + + public ScoreEntry() + { + } + } + + private Hashtable m_Entries; + + private ScoreEntry GetEntryFor( Mobile from ) + { + if ( m_Entries == null ) + m_Entries = new Hashtable(); + + ScoreEntry e = (ScoreEntry)m_Entries[from]; + + if ( e == null ) + m_Entries[from] = e = new ScoreEntry(); + + return e; + } + + public void Fire( Mobile from ) + { + BaseRanged bow = from.Weapon as BaseRanged; + + if ( bow == null ) + { + SendLocalizedMessageTo( from, 500593 ); // You must practice with ranged weapons on this. + return; + } + + if ( DateTime.Now < (m_LastUse + UseDelay) ) + return; + + Point3D worldLoc = GetWorldLocation(); + + if ( FacingEast ? from.X <= worldLoc.X : from.Y <= worldLoc.Y ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500596 ); // You would do better to stand in front of the archery butte. + return; + } + + if ( FacingEast ? from.Y != worldLoc.Y : from.X != worldLoc.X ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500597 ); // You aren't properly lined up with the archery butte to get an accurate shot. + return; + } + + if ( !from.InRange( worldLoc, 6 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500598 ); // You are too far away from the archery butte to get an accurate shot. + return; + } + else if ( from.InRange( worldLoc, 4 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500599 ); // You are too close to the target. + return; + } + + Container pack = from.Backpack; + Type ammoType = bow.AmmoType; + + bool isArrow = ( ammoType == typeof( Arrow ) ); + bool isBolt = ( ammoType == typeof( Bolt ) ); + bool isKnown = ( isArrow || isBolt ); + + if ( pack == null || !pack.ConsumeTotal( ammoType, 1 ) ) + { + if ( isArrow ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500594 ); // You do not have any arrows with which to practice. + else if ( isBolt ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500595 ); // You do not have any crossbow bolts with which to practice. + else + SendLocalizedMessageTo( from, 500593 ); // You must practice with ranged weapons on this. + + return; + } + + m_LastUse = DateTime.Now; + + from.Direction = from.GetDirectionTo( GetWorldLocation() ); + bow.PlaySwingAnimation( from ); + from.MovingEffect( this, bow.EffectID, 18, 1, false, false ); + + ScoreEntry se = GetEntryFor( from ); + + if ( !from.CheckSkill( bow.Skill, m_MinSkill, m_MaxSkill ) ) + { + from.PlaySound( bow.MissSound ); + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500604, from.Name ); // You miss the target altogether. + + se.Record( 0 ); + + if ( se.Count == 1 ) + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1062719, se.Total.ToString() ); + else + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1042683, String.Format( "{0}\t{1}", se.Total, se.Count ) ); + + return; + } + + Effects.PlaySound( Location, Map, 0x2B1 ); + + double rand = Utility.RandomDouble(); + + int area, score, splitScore; + + if ( 0.10 > rand ) + { + area = 0; // bullseye + score = 50; + splitScore = 100; + } + else if ( 0.25 > rand ) + { + area = 1; // inner ring + score = 10; + splitScore = 20; + } + else if ( 0.50 > rand ) + { + area = 2; // middle ring + score = 5; + splitScore = 15; + } + else + { + area = 3; // outer ring + score = 2; + splitScore = 5; + } + + bool split = ( isKnown && ((m_Arrows + m_Bolts) * 0.02) > Utility.RandomDouble() ); + + if ( split ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1010027 + (isArrow ? 0 : 4) + area, from.Name ); + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1010035 + area, from.Name ); + + if ( isArrow ) + ++m_Arrows; + else if ( isBolt ) + ++m_Bolts; + } + + se.Record( split ? splitScore : score ); + + if ( se.Count == 1 ) + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1062719, se.Total.ToString() ); + else + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1042683, String.Format( "{0}\t{1}", se.Total, se.Count ) ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_MinSkill ); + writer.Write( m_MaxSkill ); + writer.Write( m_Arrows ); + writer.Write( m_Bolts ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_MinSkill = reader.ReadDouble(); + m_MaxSkill = reader.ReadDouble(); + m_Arrows = reader.ReadInt(); + m_Bolts = reader.ReadInt(); + + if ( m_MinSkill == 0.0 && m_MaxSkill == 30.0 ) + { + m_MinSkill = -25.0; + m_MaxSkill = +25.0; + } + + break; + } + } + } + } + + public class ArcheryButteAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ArcheryButteDeed(); } } + + [Constructable] + public ArcheryButteAddon() + { + AddComponent( new ArcheryButte( 0x100A ), 0, 0, 0 ); + } + + public ArcheryButteAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ArcheryButteDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ArcheryButteAddon(); } } + public override int LabelNumber{ get{ return 1024106; } } // archery butte + + [Constructable] + public ArcheryButteDeed() + { + } + + public ArcheryButteDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/BallotBox.cs b/Scripts/Items/Addons/BallotBox.cs new file mode 100644 index 000000000..4237edf95 --- /dev/null +++ b/Scripts/Items/Addons/BallotBox.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections; +using Server; +using Server.Multis; +using Server.Gumps; +using Server.Network; +using Server.Prompts; + +namespace Server.Items +{ + public class BallotBox : AddonComponent + { + public static readonly int MaxTopicLines = 6; + + public override int LabelNumber{ get{ return 1041006; } } // a ballot box + + private string[] m_Topic; + private ArrayList m_Yes; + private ArrayList m_No; + + public string[] Topic + { + get{ return m_Topic; } + } + + public ArrayList Yes + { + get{ return m_Yes; } + } + + public ArrayList No + { + get{ return m_No; } + } + + [Constructable] + public BallotBox() : base( 0x9A8 ) + { + m_Topic = new string[0]; + m_Yes = new ArrayList(); + m_No = new ArrayList(); + } + + public BallotBox( Serial serial ) : base( serial ) + { + } + + public void ClearTopic() + { + m_Topic = new string[0]; + + ClearVotes(); + } + + public void AddLineToTopic( string line ) + { + if ( m_Topic.Length >= MaxTopicLines ) + return; + + string[] newTopic = new string[m_Topic.Length + 1]; + m_Topic.CopyTo( newTopic, 0 ); + newTopic[m_Topic.Length] = line; + + m_Topic = newTopic; + + ClearVotes(); + } + + public void ClearVotes() + { + Yes.Clear(); + No.Clear(); + } + + public bool IsOwner( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + return ( house != null && house.IsOwner( from ) ); + } + + public bool HasVoted( Mobile from ) + { + return ( Yes.Contains( from ) || No.Contains( from ) ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + SendLocalizedMessageTo( from, 500369 ); // I'm a ballot box, not a container! + return false; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( this.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + else + { + bool isOwner = IsOwner( from ); + from.SendGump( new InternalGump( this, isOwner ) ); + } + } + + private class InternalGump : Gump + { + private BallotBox m_Box; + + public InternalGump( BallotBox box, bool isOwner ) : base( 110, 70 ) + { + m_Box = box; + + AddBackground( 0, 0, 400, 350, 0xA28 ); + + if ( isOwner ) + AddHtmlLocalized( 0, 15, 400, 35, 1011000, false, false ); //
Ballot Box Owner's Menu
+ else + AddHtmlLocalized( 0, 15, 400, 35, 1011001, false, false ); //
Ballot Box -- Vote Here!
+ + AddHtmlLocalized( 0, 50, 400, 35, 1011002, false, false ); //
Topic
+ + int lineCount = box.Topic.Length; + AddBackground( 25, 90, 350, Math.Max( 20 * lineCount, 20 ), 0x1400 ); + + for ( int i = 0; i < lineCount; i++ ) + { + string line = box.Topic[i]; + + if ( line != null && line != "" ) + AddLabelCropped( 30, 90 + i * 20, 340, 20, 0x3E3, line ); + } + + int yesCount = box.Yes.Count; + int noCount = box.No.Count; + int totalVotes = yesCount + noCount; + + AddHtmlLocalized( 0, 215, 400, 35, 1011003, false, false ); //
votes
+ + if ( !isOwner ) + AddButton( 20, 240, 0xFA5, 0xFA7, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 242, 25, 35, 1011004, false, false ); // aye: + AddLabel( 78, 242, 0x0, String.Format( "[{0}]", yesCount ) ); + + if ( !isOwner ) + AddButton( 20, 275, 0xFA5, 0xFA7, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 277, 25, 35, 1011005, false, false ); // nay: + AddLabel( 78, 277, 0x0, String.Format( "[{0}]", noCount ) ); + + if ( totalVotes > 0 ) + { + AddImageTiled( 130, 242, ( yesCount * 225 ) / totalVotes, 10, 0xD6 ); + AddImageTiled( 130, 277, ( noCount * 225 ) / totalVotes, 10, 0xD6 ); + } + + AddButton( 45, 305, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 308, 40, 35, 1011008, false, false ); // done + + if ( isOwner ) + { + AddButton( 120, 305, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 155, 308, 100, 35, 1011006, false, false ); // change topic + + AddButton( 240, 305, 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 275, 308, 300, 100, 1011007, false, false ); // reset votes + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Box.Deleted || info.ButtonID == 0 ) + return; + + Mobile from = sender.Mobile; + + if ( from.Map != m_Box.Map || !from.InRange( m_Box.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + bool isOwner = m_Box.IsOwner( from ); + + switch ( info.ButtonID ) + { + case 1: // change topic + { + if ( isOwner ) + { + m_Box.ClearTopic(); + + from.SendLocalizedMessage( 500370, "", 0x35 ); // Enter a line of text for your ballot, and hit ENTER. Hit ESC after the last line is entered. + from.Prompt = new TopicPrompt( m_Box ); + } + + break; + } + case 2: // reset votes + { + if ( isOwner ) + { + m_Box.ClearVotes(); + from.SendLocalizedMessage( 500371 ); // Votes zeroed out. + } + + goto default; + } + case 3: // aye + { + if ( !isOwner ) + { + if ( m_Box.HasVoted( from ) ) + { + from.SendLocalizedMessage( 500374 ); // You have already voted on this ballot. + } + else + { + m_Box.Yes.Add( from ); + from.SendLocalizedMessage( 500373 ); // Your vote has been registered. + } + } + + goto default; + } + case 4: // nay + { + if ( !isOwner ) + { + if ( m_Box.HasVoted( from ) ) + { + from.SendLocalizedMessage( 500374 ); // You have already voted on this ballot. + } + else + { + m_Box.No.Add( from ); + from.SendLocalizedMessage( 500373 ); // Your vote has been registered. + } + } + + goto default; + } + default: + { + from.SendGump( new InternalGump( m_Box, isOwner ) ); + break; + } + } + } + } + + private class TopicPrompt : Prompt + { + private BallotBox m_Box; + + public TopicPrompt( BallotBox box ) + { + m_Box = box; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Box.Deleted || !m_Box.IsOwner( from ) ) + return; + + if ( from.Map != m_Box.Map || !from.InRange( m_Box.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + m_Box.AddLineToTopic( text.TrimEnd() ); + + if ( m_Box.Topic.Length < MaxTopicLines ) + { + from.SendLocalizedMessage( 500377, "", 0x35 ); // Next line or ESC to finish: + from.Prompt = new TopicPrompt( m_Box ); + } + else + { + from.SendLocalizedMessage( 500376, "", 0x35 ); // Ballot entry complete. + from.SendGump( new InternalGump( m_Box, true ) ); + } + } + + public override void OnCancel( Mobile from ) + { + if ( m_Box.Deleted || !m_Box.IsOwner( from ) ) + return; + + if ( from.Map != m_Box.Map || !from.InRange( m_Box.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + from.SendLocalizedMessage( 500376, "", 0x35 ); // Ballot entry complete. + from.SendGump( new InternalGump( m_Box, true ) ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.WriteEncodedInt( m_Topic.Length ); + + for ( int i = 0; i < m_Topic.Length; i++ ) + writer.Write( (string) m_Topic[i] ); + + writer.WriteMobileList( m_Yes, true ); + writer.WriteMobileList( m_No, true ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Topic = new string[reader.ReadEncodedInt()]; + + for ( int i = 0; i < m_Topic.Length; i++ ) + m_Topic[i] = reader.ReadString(); + + m_Yes = reader.ReadMobileList(); + m_No = reader.ReadMobileList(); + } + } + + public class BallotBoxAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BallotBoxDeed(); } } + + public BallotBoxAddon() + { + AddComponent( new BallotBox(), 0, 0, 0 ); + } + + public BallotBoxAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class BallotBoxDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BallotBoxAddon(); } } + + public override int LabelNumber{ get{ return 1044327; } } // ballot box + + [Constructable] + public BallotBoxDeed() + { + } + + public BallotBoxDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/BaseAddon.cs b/Scripts/Items/Addons/BaseAddon.cs new file mode 100644 index 000000000..9e5f1e589 --- /dev/null +++ b/Scripts/Items/Addons/BaseAddon.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Multis; +using Server.Regions; + +namespace Server.Items +{ + public enum AddonFitResult + { + Valid, + Blocked, + NotInHouse, + DoorsNotClosed, + DoorTooClose, + NoWall + } + + public interface IAddon + { + Item Deed{ get; } + + bool CouldFit( IPoint3D p, Map map ); + } + + public abstract class BaseAddon : Item, IChopable, IAddon + { + private List m_Components; + + public void AddComponent( AddonComponent c, int x, int y, int z ) + { + if ( Deleted ) + return; + + m_Components.Add( c ); + + c.Addon = this; + c.Offset = new Point3D( x, y, z ); + c.MoveToWorld( new Point3D( X + x, Y + y, Z + z ), Map ); + } + + public BaseAddon() : base( 1 ) + { + Movable = false; + Visible = false; + + m_Components = new List(); + } + + public virtual bool RetainDeedHue{ get{ return false; } } + + public void OnChop( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsOwner( from ) && house.Addons.Contains( this ) ) + { + Effects.PlaySound( GetWorldLocation(), Map, 0x3B3 ); + from.SendLocalizedMessage( 500461 ); // You destroy the item. + + int hue = 0; + + if ( RetainDeedHue ) + { + for ( int i = 0; hue == 0 && i < m_Components.Count; ++i ) + { + AddonComponent c = (AddonComponent)m_Components[i]; + + if ( c.Hue != 0 ) + hue = c.Hue; + } + } + + Delete(); + + house.Addons.Remove( this ); + + BaseAddonDeed deed = Deed; + + if ( deed != null ) + { + if ( RetainDeedHue ) + deed.Hue = hue; + + from.AddToBackpack( deed ); + } + } + } + + public virtual BaseAddonDeed Deed{ get{ return null; } } + + Item IAddon.Deed + { + get{ return this.Deed; } + } + + public List Components + { + get + { + return m_Components; + } + } + + public BaseAddon( Serial serial ) : base( serial ) + { + } + + public bool CouldFit( IPoint3D p, Map map ) + { + ArrayList houses = null; + + return ( CouldFit( p, map, null, ref houses ) == AddonFitResult.Valid ); + } + + public AddonFitResult CouldFit( IPoint3D p, Map map, Mobile from, ref ArrayList houseList ) + { + if ( Deleted ) + return AddonFitResult.Blocked; + + ArrayList houses = new ArrayList(); + + foreach ( AddonComponent c in m_Components ) + { + Point3D p3D = new Point3D( p.X + c.Offset.X, p.Y + c.Offset.Y, p.Z + c.Offset.Z ); + + if ( !map.CanFit( p3D.X, p3D.Y, p3D.Z, c.ItemData.Height, false, true, ( c.Z == 0 ) ) ) + return AddonFitResult.Blocked; + else if ( !CheckHouse( from, p3D, map, c.ItemData.Height, houses ) ) + return AddonFitResult.NotInHouse; + + if ( c.NeedsWall ) + { + Point3D wall = c.WallPosition; + + if ( !IsWall( p3D.X + wall.X, p3D.Y + wall.Y, p3D.Z + wall.Z, map ) ) + return AddonFitResult.NoWall; + } + } + + foreach ( BaseHouse house in houses ) + { + ArrayList doors = house.Doors; + + for ( int i = 0; i < doors.Count; ++i ) + { + BaseDoor door = doors[i] as BaseDoor; + + if ( door != null && door.Open ) + return AddonFitResult.DoorsNotClosed; + + Point3D doorLoc = door.GetWorldLocation(); + int doorHeight = door.ItemData.CalcHeight; + + foreach ( AddonComponent c in m_Components ) + { + Point3D addonLoc = new Point3D( p.X + c.Offset.X, p.Y + c.Offset.Y, p.Z + c.Offset.Z ); + int addonHeight = c.ItemData.CalcHeight; + + if ( Utility.InRange( doorLoc, addonLoc, 1 ) && (addonLoc.Z == doorLoc.Z || ((addonLoc.Z + addonHeight) > doorLoc.Z && (doorLoc.Z + doorHeight) > addonLoc.Z)) ) + return AddonFitResult.DoorTooClose; + } + } + } + + houseList = houses; + return AddonFitResult.Valid; + } + + public static bool CheckHouse( Mobile from, Point3D p, Map map, int height, ArrayList list ) + { + if ( from == null || from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + BaseHouse house = BaseHouse.FindHouseAt( p, map, height ); + + if ( house == null || !house.IsOwner( from ) ) + return false; + + if ( !list.Contains( house ) ) + list.Add( house ); + + return true; + } + + public static bool IsWall( int x, int y, int z, Map map ) + { + if ( map == null ) + return false; + + Tile[] tiles = map.Tiles.GetStaticTiles( x, y, true ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile t = tiles[i]; + ItemData id = TileData.ItemTable[t.ID & 0x3FFF]; + + if ( (id.Flags & TileFlag.Wall) != 0 && (z + 16) > t.Z && (t.Z + t.Height) > z ) + return true; + } + + return false; + } + + public virtual void OnComponentLoaded( AddonComponent c ) + { + } + + public virtual void OnComponentUsed( AddonComponent c, Mobile from ) + { + } + + public override void OnLocationChange( Point3D oldLoc ) + { + if ( Deleted ) + return; + + foreach ( AddonComponent c in m_Components ) + c.Location = new Point3D( X + c.Offset.X, Y + c.Offset.Y, Z + c.Offset.Z ); + } + + public override void OnMapChange() + { + if ( Deleted ) + return; + + foreach ( AddonComponent c in m_Components ) + c.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + foreach ( AddonComponent c in m_Components ) + c.Delete(); + } + + public virtual bool ShareHue{ get{ return true; } } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get + { + return base.Hue; + } + set + { + if ( base.Hue != value ) + { + base.Hue = value; + + if ( !Deleted && this.ShareHue && m_Components != null ) + { + foreach ( AddonComponent c in m_Components ) + c.Hue = value; + } + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.WriteItemList( m_Components ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_Components = reader.ReadStrongItemList(); + break; + } + } + + if ( version < 1 && Weight == 0 ) + Weight = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/BaseAddonDeed.cs b/Scripts/Items/Addons/BaseAddonDeed.cs new file mode 100644 index 000000000..14ec46fc3 --- /dev/null +++ b/Scripts/Items/Addons/BaseAddonDeed.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Items +{ + [Flipable( 0x14F0, 0x14EF )] + public abstract class BaseAddonDeed : Item + { + public abstract BaseAddon Addon{ get; } + + public BaseAddonDeed() : base( 0x14F0 ) + { + Weight = 1.0; + + if ( !Core.AOS ) + LootType = LootType.Newbied; + } + + public BaseAddonDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 0.0 ) + Weight = 1.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + from.Target = new InternalTarget( this ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + private class InternalTarget : Target + { + private BaseAddonDeed m_Deed; + + public InternalTarget( BaseAddonDeed deed ) : base( -1, true, TargetFlags.None ) + { + m_Deed = deed; + + CheckLOS = false; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + IPoint3D p = targeted as IPoint3D; + Map map = from.Map; + + if ( p == null || map == null || m_Deed.Deleted ) + return; + + if ( m_Deed.IsChildOf( from.Backpack ) ) + { + BaseAddon addon = m_Deed.Addon; + + Server.Spells.SpellHelper.GetSurfaceTop( ref p ); + + ArrayList houses = null; + + AddonFitResult res = addon.CouldFit( p, map, from, ref houses ); + + if ( res == AddonFitResult.Valid ) + addon.MoveToWorld( new Point3D( p ), map ); + else if ( res == AddonFitResult.Blocked ) + from.SendLocalizedMessage( 500269 ); // You cannot build that there. + else if ( res == AddonFitResult.NotInHouse ) + from.SendLocalizedMessage( 500274 ); // You can only place this in a house that you own! + else if ( res == AddonFitResult.DoorsNotClosed ) + from.SendMessage( "You must close all house doors before placing this." ); + else if ( res == AddonFitResult.DoorTooClose ) + from.SendLocalizedMessage( 500271 ); // You cannot build near the door. + else if ( res == AddonFitResult.NoWall ) + from.SendLocalizedMessage( 500268 ); // This object needs to be mounted on something. + + if ( res == AddonFitResult.Valid ) + { + m_Deed.Delete(); + + if ( houses != null ) + { + foreach ( Server.Multis.BaseHouse h in houses ) + h.Addons.Add( addon ); + } + } + else + { + addon.Delete(); + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/BearRugs.cs b/Scripts/Items/Addons/BearRugs.cs new file mode 100644 index 000000000..e170100d6 --- /dev/null +++ b/Scripts/Items/Addons/BearRugs.cs @@ -0,0 +1,269 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BrownBearRugEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BrownBearRugEastDeed(); } } + + [Constructable] + public BrownBearRugEastAddon() + { + AddComponent( new AddonComponent( 0x1E40 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E41 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E42 ), 1,-1, 0 ); + AddComponent( new AddonComponent( 0x1E43 ), 0,-1, 0 ); + AddComponent( new AddonComponent( 0x1E44 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1E45 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1E46 ),-1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E47 ),-1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E48 ),-1,-1, 0 ); + } + + public BrownBearRugEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrownBearRugEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BrownBearRugEastAddon(); } } + public override int LabelNumber{ get{ return 1049397; } } // a brown bear rug deed facing east + + [Constructable] + public BrownBearRugEastDeed() + { + } + + public BrownBearRugEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrownBearRugSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BrownBearRugSouthDeed(); } } + + [Constructable] + public BrownBearRugSouthAddon() + { + AddComponent( new AddonComponent( 0x1E36 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E37 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1E38 ),-1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E39 ),-1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E3A ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1E3B ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E3C ), 1,-1, 0 ); + AddComponent( new AddonComponent( 0x1E3D ), 0,-1, 0 ); + AddComponent( new AddonComponent( 0x1E3E ),-1,-1, 0 ); + } + + public BrownBearRugSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrownBearRugSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BrownBearRugSouthAddon(); } } + public override int LabelNumber{ get{ return 1049398; } } // a brown bear rug deed facing south + + [Constructable] + public BrownBearRugSouthDeed() + { + } + + public BrownBearRugSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PolarBearRugEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new PolarBearRugEastDeed(); } } + + [Constructable] + public PolarBearRugEastAddon() + { + AddComponent( new AddonComponent( 0x1E53 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E54 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E55 ), 1,-1, 0 ); + AddComponent( new AddonComponent( 0x1E56 ), 0,-1, 0 ); + AddComponent( new AddonComponent( 0x1E57 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1E58 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1E59 ),-1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E5A ),-1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E5B ),-1,-1, 0 ); + } + + public PolarBearRugEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PolarBearRugEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new PolarBearRugEastAddon(); } } + public override int LabelNumber{ get{ return 1049399; } } // a polar bear rug deed facing east + + [Constructable] + public PolarBearRugEastDeed() + { + } + + public PolarBearRugEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PolarBearRugSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new PolarBearRugSouthDeed(); } } + + [Constructable] + public PolarBearRugSouthAddon() + { + AddComponent( new AddonComponent( 0x1E49 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E4A ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1E4B ),-1, 1, 0 ); + AddComponent( new AddonComponent( 0x1E4C ),-1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E4D ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1E4E ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1E4F ), 1,-1, 0 ); + AddComponent( new AddonComponent( 0x1E50 ), 0,-1, 0 ); + AddComponent( new AddonComponent( 0x1E51 ),-1,-1, 0 ); + } + + public PolarBearRugSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PolarBearRugSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new PolarBearRugSouthAddon(); } } + public override int LabelNumber{ get{ return 1049400; } } // a polar bear rug deed facing south + + [Constructable] + public PolarBearRugSouthDeed() + { + } + + public PolarBearRugSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/BloodPentagram.cs b/Scripts/Items/Addons/BloodPentagram.cs new file mode 100644 index 000000000..628de00fe --- /dev/null +++ b/Scripts/Items/Addons/BloodPentagram.cs @@ -0,0 +1,70 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BloodPentagram : BaseAddon + { + [Constructable] + public BloodPentagram () + { + AddComponent( new AddonComponent( 0x1CF9 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1CF8 ), 0, 2, 0 ); + AddComponent( new AddonComponent( 0x1CF7 ), 0, 3, 0 ); + AddComponent( new AddonComponent( 0x1CF6 ), 0, 4, 0 ); + AddComponent( new AddonComponent( 0x1CF5 ), 0, 5, 0 ); + + AddComponent( new AddonComponent( 0x1CFB ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1CFA ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1D09 ), 1, 2, 0 ); + AddComponent( new AddonComponent( 0x1D08 ), 1, 3, 0 ); + AddComponent( new AddonComponent( 0x1D07 ), 1, 4, 0 ); + AddComponent( new AddonComponent( 0x1CF4 ), 1, 5, 0 ); + + AddComponent( new AddonComponent( 0x1CFC ), 2, 0, 0 ); + AddComponent( new AddonComponent( 0x1D0A ), 2, 1, 0 ); + AddComponent( new AddonComponent( 0x1D11 ), 2, 2, 0 ); + AddComponent( new AddonComponent( 0x1D10 ), 2, 3, 0 ); + AddComponent( new AddonComponent( 0x1D06 ), 2, 4, 0 ); + AddComponent( new AddonComponent( 0x1CF3 ), 2, 5, 0 ); + + AddComponent( new AddonComponent( 0x1CFD ), 3, 0, 0 ); + AddComponent( new AddonComponent( 0x1D0B ), 3, 1, 0 ); + AddComponent( new AddonComponent( 0x1D12 ), 3, 2, 0 ); + AddComponent( new AddonComponent( 0x1D0F ), 3, 3, 0 ); + AddComponent( new AddonComponent( 0x1D05 ), 3, 4, 0 ); + AddComponent( new AddonComponent( 0x1CF2 ), 3, 5, 0 ); + + AddComponent( new AddonComponent( 0x1CFE ), 4, 0, 0 ); + AddComponent( new AddonComponent( 0x1D0C ), 4, 1, 0 ); + AddComponent( new AddonComponent( 0x1D0D ), 4, 2, 0 ); + AddComponent( new AddonComponent( 0x1D0E ), 4, 3, 0 ); + AddComponent( new AddonComponent( 0x1D04 ), 4, 4, 0 ); + AddComponent( new AddonComponent( 0x1CF1 ), 4, 5, 0 ); + + AddComponent( new AddonComponent( 0x1CFF ), 5, 0, 0 ); + AddComponent( new AddonComponent( 0x1D00 ), 5, 1, 0 ); + AddComponent( new AddonComponent( 0x1D01 ), 5, 2, 0 ); + AddComponent( new AddonComponent( 0x1D02 ), 5, 3, 0 ); + AddComponent( new AddonComponent( 0x1D03 ), 5, 4, 0 ); + } + + public BloodPentagram( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/DartBoard.cs b/Scripts/Items/Addons/DartBoard.cs new file mode 100644 index 000000000..1744382a1 --- /dev/null +++ b/Scripts/Items/Addons/DartBoard.cs @@ -0,0 +1,218 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class DartBoard : AddonComponent + { + public override bool NeedsWall{ get{ return true; } } + public override Point3D WallPosition{ get{ return this.East ? new Point3D( -1, 0, 0 ) : new Point3D( 0, -1, 0 ); } } + + public bool East{ get{ return this.ItemID == 0x1E2F; } } + + [Constructable] + public DartBoard() : this( true ) + { + } + + [Constructable] + public DartBoard( bool east ) : base( east ? 0x1E2F : 0x1E2E ) + { + } + + public DartBoard( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + Direction dir; + if ( from.Location != this.Location ) + dir = from.GetDirectionTo( this ); + else if ( this.East ) + dir = Direction.West; + else + dir = Direction.North; + + from.Direction = dir; + + bool canThrow = true; + + if ( !from.InRange( this, 4 ) || !from.InLOS( this ) ) + canThrow = false; + else if ( this.East ) + canThrow = ( dir == Direction.Left || dir == Direction.West || dir == Direction.Up ); + else + canThrow = ( dir == Direction.Up || dir == Direction.North || dir == Direction.Right ); + + if ( canThrow ) + Throw( from ); + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + + public void Throw( Mobile from ) + { + BaseKnife knife = from.Weapon as BaseKnife; + + if ( knife == null ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500751 ); // Try holding a knife... + return; + } + + from.Animate( from.Mounted ? 26 : 9, 7, 1, true, false, 0 ); + from.MovingEffect( this, knife.ItemID, 7, 1, false, false ); + from.PlaySound( 0x238 ); + + double rand = Utility.RandomDouble(); + + int message; + if ( rand < 0.05 ) + message = 500752; // BULLSEYE! 50 Points! + else if ( rand < 0.20 ) + message = 500753; // Just missed the center! 20 points. + else if ( rand < 0.45 ) + message = 500754; // 10 point shot. + else if ( rand < 0.70 ) + message = 500755; // 5 pointer. + else if ( rand < 0.85 ) + message = 500756; // 1 point. Bad throw. + else + message = 500757; // Missed. + + PublicOverheadMessage( MessageType.Regular, 0x3B2, message ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DartBoardEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new DartBoardEastDeed(); } } + + public DartBoardEastAddon() + { + AddComponent( new DartBoard( true ), 0, 0, 0 ); + } + + public DartBoardEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DartBoardEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new DartBoardEastAddon(); } } + + public override int LabelNumber{ get{ return 1044326; } } // dartboard (east) + + [Constructable] + public DartBoardEastDeed() + { + } + + public DartBoardEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DartBoardSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new DartBoardSouthDeed(); } } + + public DartBoardSouthAddon() + { + AddComponent( new DartBoard( false ), 0, 0, 0 ); + } + + public DartBoardSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DartBoardSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new DartBoardSouthAddon(); } } + + public override int LabelNumber{ get{ return 1044325; } } // dartboard (south) + + [Constructable] + public DartBoardSouthDeed() + { + } + + public DartBoardSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenBedEastAddon.cs b/Scripts/Items/Addons/ElvenBedEastAddon.cs new file mode 100644 index 000000000..75b168e4f --- /dev/null +++ b/Scripts/Items/Addons/ElvenBedEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenBedEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenBedEastDeed(); } } + + [Constructable] + public ElvenBedEastAddon() + { + AddComponent( new AddonComponent( 0x304D ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x304C ), 1, 0, 0 ); + } + + public ElvenBedEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenBedEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenBedEastAddon(); } } + public override int LabelNumber{ get{ return 1072861; } } // elven bed (east) + + [Constructable] + public ElvenBedEastDeed() + { + } + + public ElvenBedEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenBedSouthAddon.cs b/Scripts/Items/Addons/ElvenBedSouthAddon.cs new file mode 100644 index 000000000..8c76245db --- /dev/null +++ b/Scripts/Items/Addons/ElvenBedSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenBedSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenBedSouthDeed(); } } + + [Constructable] + public ElvenBedSouthAddon() + { + AddComponent( new AddonComponent( 0x3050 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3051 ), 0, -1, 0 ); + } + + public ElvenBedSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenBedSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenBedSouthAddon(); } } + public override int LabelNumber{ get{ return 1072860; } } // elven bed (south) + + [Constructable] + public ElvenBedSouthDeed() + { + } + + public ElvenBedSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenDresserEastAddon.cs b/Scripts/Items/Addons/ElvenDresserEastAddon.cs new file mode 100644 index 000000000..f5f2bef60 --- /dev/null +++ b/Scripts/Items/Addons/ElvenDresserEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenDresserEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenDresserEastDeed(); } } + + [Constructable] + public ElvenDresserEastAddon() + { + AddComponent( new AddonComponent( 0x30E4 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x30E3 ), 0, -1, 0 ); + } + + public ElvenDresserEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenDresserEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenDresserEastAddon(); } } + public override int LabelNumber{ get{ return 1073388; } } // elven dresser (east) + + [Constructable] + public ElvenDresserEastDeed() + { + } + + public ElvenDresserEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenDresserSouthAddon.cs b/Scripts/Items/Addons/ElvenDresserSouthAddon.cs new file mode 100644 index 000000000..4db58cee8 --- /dev/null +++ b/Scripts/Items/Addons/ElvenDresserSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenDresserSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenDresserSouthDeed(); } } + + [Constructable] + public ElvenDresserSouthAddon() + { + AddComponent( new AddonComponent( 0x30E5 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x30E6 ), 1, 0, 0 ); + } + + public ElvenDresserSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenDresserSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenDresserSouthAddon(); } } + public override int LabelNumber{ get{ return 1072864; } } // elven dresser (south) + + [Constructable] + public ElvenDresserSouthDeed() + { + } + + public ElvenDresserSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenForgeAddon.cs b/Scripts/Items/Addons/ElvenForgeAddon.cs new file mode 100644 index 000000000..2db9e1682 --- /dev/null +++ b/Scripts/Items/Addons/ElvenForgeAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenForgeAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenForgeDeed(); } } + + [Constructable] + public ElvenForgeAddon() + { + AddComponent( new AddonComponent( 0x2DD8 ), 0, 0, 0 ); + } + + public ElvenForgeAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenForgeDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenForgeAddon(); } } + public override int LabelNumber{ get{ return 1072875; } } // squirrel statue (east) + + [Constructable] + public ElvenForgeDeed() + { + } + + public ElvenForgeDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenLoveseatEastAddon.cs b/Scripts/Items/Addons/ElvenLoveseatEastAddon.cs new file mode 100644 index 000000000..706e93b97 --- /dev/null +++ b/Scripts/Items/Addons/ElvenLoveseatEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenLoveseatEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenLoveseatEastDeed(); } } + + [Constructable] + public ElvenLoveseatEastAddon() + { + AddComponent( new AddonComponent( 0x3089 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3088 ), 1, 0, 0 ); + } + + public ElvenLoveseatEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenLoveseatEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenLoveseatEastAddon(); } } + public override int LabelNumber{ get{ return 1073372; } } // elven loveseat (east) + + [Constructable] + public ElvenLoveseatEastDeed() + { + } + + public ElvenLoveseatEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenLoveseatSouthAddon.cs b/Scripts/Items/Addons/ElvenLoveseatSouthAddon.cs new file mode 100644 index 000000000..071e7c59b --- /dev/null +++ b/Scripts/Items/Addons/ElvenLoveseatSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenLoveseatSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenLoveseatSouthDeed(); } } + + [Constructable] + public ElvenLoveseatSouthAddon() + { + AddComponent( new AddonComponent( 0x308A ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x308B ), 0, -1, 0 ); + } + + public ElvenLoveseatSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenLoveseatSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenLoveseatSouthAddon(); } } + public override int LabelNumber{ get{ return 1072867; } } // elven loveseat (south) + + [Constructable] + public ElvenLoveseatSouthDeed() + { + } + + public ElvenLoveseatSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenSpinningwheelEastAddon.cs b/Scripts/Items/Addons/ElvenSpinningwheelEastAddon.cs new file mode 100644 index 000000000..06aea8891 --- /dev/null +++ b/Scripts/Items/Addons/ElvenSpinningwheelEastAddon.cs @@ -0,0 +1,137 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenSpinningwheelEastAddon : BaseAddon, ISpinningWheel + { + public override BaseAddonDeed Deed{ get{ return new ElvenSpinningwheelEastDeed(); } } + + [Constructable] + public ElvenSpinningwheelEastAddon() + { + AddComponent( new AddonComponent( 0x2DD9 ), 0, 0, 0 ); + } + + public ElvenSpinningwheelEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + + private Timer m_Timer; + + public override void OnComponentLoaded( AddonComponent c ) + { + switch ( c.ItemID ) + { + case 0x2E3D: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + public bool Spinning{ get{ return m_Timer != null; } } + + public void BeginSpin( SpinCallback callback, Mobile from, int hue ) + { + m_Timer = new SpinTimer( this, callback, from, hue ); + m_Timer.Start(); + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x2DD9: + case 0x101C: + case 0x10A4: ++c.ItemID; break; + } + } + } + + public void EndSpin( SpinCallback callback, Mobile from, int hue ) + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + if ( callback != null ) + callback( this, from, hue ); + } + + private class SpinTimer : Timer + { + private ElvenSpinningwheelEastAddon m_Wheel; + private SpinCallback m_Callback; + private Mobile m_From; + private int m_Hue; + + public SpinTimer( ElvenSpinningwheelEastAddon wheel, SpinCallback callback, Mobile from, int hue ) : base( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Wheel = wheel; + m_Callback = callback; + m_From = from; + m_Hue = hue; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Wheel.EndSpin( m_Callback, m_From, m_Hue ); + } + } + } + + public class ElvenSpinningwheelEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenSpinningwheelEastAddon(); } } + public override int LabelNumber{ get{ return 1073393; } } // elven spinning wheel (east) + + [Constructable] + public ElvenSpinningwheelEastDeed() + { + } + + public ElvenSpinningwheelEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenSpinningwheelSouthAddon.cs b/Scripts/Items/Addons/ElvenSpinningwheelSouthAddon.cs new file mode 100644 index 000000000..0df8698ea --- /dev/null +++ b/Scripts/Items/Addons/ElvenSpinningwheelSouthAddon.cs @@ -0,0 +1,139 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenSpinningwheelSouthAddon : BaseAddon, ISpinningWheel + { + public override BaseAddonDeed Deed{ get{ return new ElvenSpinningwheelSouthDeed(); } } + + [Constructable] + public ElvenSpinningwheelSouthAddon() + { + AddComponent( new AddonComponent( 0x2DDA ), 0, 0, 0 ); + } + + public ElvenSpinningwheelSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + + private Timer m_Timer; + + public override void OnComponentLoaded( AddonComponent c ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + public bool Spinning{ get{ return m_Timer != null; } } + + public void BeginSpin( SpinCallback callback, Mobile from, int hue ) + { + m_Timer = new SpinTimer( this, callback, from, hue ); + m_Timer.Start(); + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1015: + case 0x1019: + case 0x101C: + case 0x10A4: ++c.ItemID; break; + } + } + } + + public void EndSpin( SpinCallback callback, Mobile from, int hue ) + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + if ( callback != null ) + callback( this, from, hue ); + } + + private class SpinTimer : Timer + { + private ElvenSpinningwheelSouthAddon m_Wheel; + private SpinCallback m_Callback; + private Mobile m_From; + private int m_Hue; + + public SpinTimer( ElvenSpinningwheelSouthAddon wheel, SpinCallback callback, Mobile from, int hue ) : base( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Wheel = wheel; + m_Callback = callback; + m_From = from; + m_Hue = hue; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Wheel.EndSpin( m_Callback, m_From, m_Hue ); + } + } + } + + public class ElvenSpinningwheelSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenSpinningwheelSouthAddon(); } } + public override int LabelNumber{ get{ return 1072878; } } // spinning wheel (south) + + [Constructable] + public ElvenSpinningwheelSouthDeed() + { + } + + public ElvenSpinningwheelSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenStoveEastAddon.cs b/Scripts/Items/Addons/ElvenStoveEastAddon.cs new file mode 100644 index 000000000..952a28914 --- /dev/null +++ b/Scripts/Items/Addons/ElvenStoveEastAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenStoveEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenStoveEastDeed(); } } + + [Constructable] + public ElvenStoveEastAddon() + { + AddComponent( new AddonComponent( 0x2DDB ), 0, 0, 0 ); + } + + public ElvenStoveEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenStoveEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenStoveEastAddon(); } } + public override int LabelNumber{ get{ return 1073395; } } // elven oven (east) + + [Constructable] + public ElvenStoveEastDeed() + { + } + + public ElvenStoveEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenStoveSouthAddon.cs b/Scripts/Items/Addons/ElvenStoveSouthAddon.cs new file mode 100644 index 000000000..2a4aa4b29 --- /dev/null +++ b/Scripts/Items/Addons/ElvenStoveSouthAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenStoveSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenStoveSouthDeed(); } } + + [Constructable] + public ElvenStoveSouthAddon() + { + AddComponent( new AddonComponent( 0x2DDC ), 0, 0, 0 ); + } + + public ElvenStoveSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenStoveSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenStoveSouthAddon(); } } + public override int LabelNumber{ get{ return 1073394; } } // elven oven (south) + + [Constructable] + public ElvenStoveSouthDeed() + { + } + + public ElvenStoveSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenWashbasinEastAddon.cs b/Scripts/Items/Addons/ElvenWashbasinEastAddon.cs new file mode 100644 index 000000000..abb8e6194 --- /dev/null +++ b/Scripts/Items/Addons/ElvenWashbasinEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenWashBasinEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenWashBasinEastDeed(); } } + + [Constructable] + public ElvenWashBasinEastAddon() + { + AddComponent( new AddonComponent( 0x30DF ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x30E0 ), 0, -1, 0 ); + } + + public ElvenWashBasinEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenWashBasinEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenWashBasinEastAddon(); } } + public override int LabelNumber{ get{ return 1073387; } } // elven wash basin (east) + + [Constructable] + public ElvenWashBasinEastDeed() + { + } + + public ElvenWashBasinEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ElvenWashbasinSouthAddon.cs b/Scripts/Items/Addons/ElvenWashbasinSouthAddon.cs new file mode 100644 index 000000000..6a32fc416 --- /dev/null +++ b/Scripts/Items/Addons/ElvenWashbasinSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ElvenWashBasinSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ElvenWashBasinSouthDeed(); } } + + [Constructable] + public ElvenWashBasinSouthAddon() + { + AddComponent( new AddonComponent( 0x30E1 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x30E2 ), 1, 0, 0 ); + } + + public ElvenWashBasinSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenWashBasinSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ElvenWashBasinSouthAddon(); } } + public override int LabelNumber{ get{ return 1072865; } } // elven wash basin (south) + + [Constructable] + public ElvenWashBasinSouthDeed() + { + } + + public ElvenWashBasinSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/FancyElvenTableEastAddon.cs b/Scripts/Items/Addons/FancyElvenTableEastAddon.cs new file mode 100644 index 000000000..ebd4f6414 --- /dev/null +++ b/Scripts/Items/Addons/FancyElvenTableEastAddon.cs @@ -0,0 +1,65 @@ +using System; +using Server; + +namespace Server.Items +{ + public class FancyElvenTableEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new FancyElvenTableEastDeed(); } } + + [Constructable] + public FancyElvenTableEastAddon() + { + AddComponent( new AddonComponent( 0x3094 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x3093 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3092 ), 1, 0, 0 ); + } + + public FancyElvenTableEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class FancyElvenTableEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new FancyElvenTableEastAddon(); } } + public override int LabelNumber{ get{ return 1073386; } } // hardwood table (east) + + [Constructable] + public FancyElvenTableEastDeed() + { + } + + public FancyElvenTableEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/FancyElvenTableSouthAddon.cs b/Scripts/Items/Addons/FancyElvenTableSouthAddon.cs new file mode 100644 index 000000000..b59b27481 --- /dev/null +++ b/Scripts/Items/Addons/FancyElvenTableSouthAddon.cs @@ -0,0 +1,65 @@ +using System; +using Server; + +namespace Server.Items +{ + public class FancyElvenTableSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new FancyElvenTableSouthDeed(); } } + + [Constructable] + public FancyElvenTableSouthAddon() + { + AddComponent( new AddonComponent( 0x3095 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x3096 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3097 ), 0, -1, 0 ); + } + + public FancyElvenTableSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class FancyElvenTableSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new FancyElvenTableSouthAddon(); } } + public override int LabelNumber{ get{ return 1073385; } } // hardwood table (south) + + [Constructable] + public FancyElvenTableSouthDeed() + { + } + + public FancyElvenTableSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/FireColumnAddon.cs b/Scripts/Items/Addons/FireColumnAddon.cs new file mode 100644 index 000000000..7693f7a45 --- /dev/null +++ b/Scripts/Items/Addons/FireColumnAddon.cs @@ -0,0 +1,67 @@ +using System; +using System.Text; +using System.Net; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class FireColumnAddon : BaseAddon + { + public override bool ShareHue + { + get { return false; } + } + + [Constructable] + public FireColumnAddon() + : this( false ) + { + } + + [Constructable] + public FireColumnAddon( bool bloody ) + { + AddComponent( new AddonComponent( 0x3A5 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3A5 ), 0, 0, 5 ); + AddComponent( new AddonComponent( 0x3A5 ), 0, 0, 10 ); + AddComponent( new AddonComponent( 0x3A5 ), 0, 0, 15 ); + + AddComponent( new AddonComponent( 0x19BB ), 0, 0, 21 ); + AddComponent( new AddonComponent( 0x19AB ), 0, 0, 23 ); + + if ( bloody ) + { + AddComponent( new AddonComponent( 0x122B ), -2, 0, 0 ); + AddComponent( new AddonComponent( 0x122E ), 0, -2, 0 ); + AddComponent( new AddonComponent( 0x122D ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0x122F ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0x122D ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x122A ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x122B ), 2, -1, 0 ); + AddComponent( new AddonComponent( 0x122B ), 0, 2, 0 ); + AddComponent( new AddonComponent( 0x122E ), 1, 1, 0 ); + } + } + + public FireColumnAddon( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/FlourMillEastAddon.cs b/Scripts/Items/Addons/FlourMillEastAddon.cs new file mode 100644 index 000000000..c8eecd59d --- /dev/null +++ b/Scripts/Items/Addons/FlourMillEastAddon.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using System.Collections.Generic; + +namespace Server.Items +{ + public interface IFlourMill + { + int MaxFlour{ get; } + int CurFlour{ get; set; } + } + + public enum FlourMillStage + { + Empty, + Filled, + Working + } + + public class FlourMillEastAddon : BaseAddon, IFlourMill + { + public override BaseAddonDeed Deed{ get{ return new FlourMillEastDeed(); } } + + private int m_Flour; + private Timer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxFlour + { + get{ return 2; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurFlour + { + get{ return m_Flour; } + set{ m_Flour = Math.Max( 0, Math.Min( value, MaxFlour ) ); UpdateStage(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool HasFlour + { + get{ return ( m_Flour > 0 ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsFull + { + get{ return ( m_Flour >= MaxFlour ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsWorking + { + get{ return ( m_Timer != null ); } + } + + public void StartWorking( Mobile from ) + { + if ( IsWorking ) + return; + + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( FinishWorking_Callback ), from ); + UpdateStage(); + } + + private void FinishWorking_Callback( object state ) + { + if ( m_Timer != null ) + { + m_Timer.Stop(); + m_Timer = null; + } + + Mobile from = state as Mobile; + + if ( from != null && !from.Deleted && !this.Deleted && IsFull ) + { + SackFlour flour = new SackFlour(); + + flour.ItemID = ( Utility.RandomBool() ? 4153 : 4165 ); + + if ( from.PlaceInBackpack( flour ) ) + { + m_Flour = 0; + } + else + { + flour.Delete(); + from.SendLocalizedMessage( 500998 ); // There is not enough room in your backpack! You stop grinding. + } + } + + UpdateStage(); + } + + private static int[][] m_StageTable = new int[][] + { + new int[]{ 0x1920, 0x1921, 0x1925 }, + new int[]{ 0x1922, 0x1923, 0x1926 }, + new int[]{ 0x1924, 0x1924, 0x1928 } + }; + + private int[] FindItemTable( int itemID ) + { + for ( int i = 0; i < m_StageTable.Length; ++i ) + { + int[] itemTable = m_StageTable[i]; + + for ( int j = 0; j < itemTable.Length; ++j ) + { + if ( itemTable[j] == itemID ) + return itemTable; + } + } + + return null; + } + + public void UpdateStage() + { + if ( IsWorking ) + UpdateStage( FlourMillStage.Working ); + else if ( HasFlour ) + UpdateStage( FlourMillStage.Filled ); + else + UpdateStage( FlourMillStage.Empty ); + } + + public void UpdateStage( FlourMillStage stage ) + { + List components = this.Components; + + int[][] stageTable = m_StageTable; + + for ( int i = 0; i < components.Count; ++i ) + { + AddonComponent component = components[i] as AddonComponent; + + if ( component == null ) + continue; + + int[] itemTable = FindItemTable( component.ItemID ); + + if ( itemTable != null ) + component.ItemID = itemTable[(int)stage]; + } + } + + public override void OnComponentUsed( AddonComponent c, Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 4 ) || !from.InLOS( this ) ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else if ( !IsFull ) + from.SendLocalizedMessage( 500997 ); // You need more wheat to make a sack of flour. + else + StartWorking( from ); + } + + [Constructable] + public FlourMillEastAddon() + { + AddComponent( new AddonComponent( 0x1920 ),-1, 0, 0 ); + AddComponent( new AddonComponent( 0x1922 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1924 ), 1, 0, 0 ); + } + + public FlourMillEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Flour ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Flour = reader.ReadInt(); + break; + } + } + + UpdateStage(); + } + } + + public class FlourMillEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new FlourMillEastAddon(); } } + public override int LabelNumber{ get{ return 1044347; } } // flour mill (east) + + [Constructable] + public FlourMillEastDeed() + { + } + + public FlourMillEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/FlourMillSouthAddon.cs b/Scripts/Items/Addons/FlourMillSouthAddon.cs new file mode 100644 index 000000000..82d3b59d8 --- /dev/null +++ b/Scripts/Items/Addons/FlourMillSouthAddon.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using System.Collections.Generic; + +namespace Server.Items +{ + public class FlourMillSouthAddon : BaseAddon, IFlourMill + { + public override BaseAddonDeed Deed{ get{ return new FlourMillSouthDeed(); } } + private int m_Flour; + private Timer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxFlour + { + get{ return 2; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurFlour + { + get{ return m_Flour; } + set{ m_Flour = Math.Max( 0, Math.Min( value, MaxFlour ) ); UpdateStage(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool HasFlour + { + get{ return ( m_Flour > 0 ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsFull + { + get{ return ( m_Flour >= MaxFlour ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsWorking + { + get{ return ( m_Timer != null ); } + } + + public void StartWorking( Mobile from ) + { + if ( IsWorking ) + return; + + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( FinishWorking_Callback ), from ); + UpdateStage(); + } + + private void FinishWorking_Callback( object state ) + { + if ( m_Timer != null ) + { + m_Timer.Stop(); + m_Timer = null; + } + + Mobile from = state as Mobile; + + if ( from != null && !from.Deleted && !this.Deleted && IsFull ) + { + SackFlour flour = new SackFlour(); + + flour.ItemID = ( Utility.RandomBool() ? 4153 : 4165 ); + + if ( from.PlaceInBackpack( flour ) ) + { + m_Flour = 0; + } + else + { + flour.Delete(); + from.SendLocalizedMessage( 500998 ); // There is not enough room in your backpack! You stop grinding. + } + } + + UpdateStage(); + } + + private static int[][] m_StageTable = new int[][] + { + new int[]{ 0x192C, 0x192D, 0x1931 }, + new int[]{ 0x192E, 0x192F, 0x1932 }, + new int[]{ 0x1930, 0x1930, 0x1934 } + }; + + private int[] FindItemTable( int itemID ) + { + for ( int i = 0; i < m_StageTable.Length; ++i ) + { + int[] itemTable = m_StageTable[i]; + + for ( int j = 0; j < itemTable.Length; ++j ) + { + if ( itemTable[j] == itemID ) + return itemTable; + } + } + + return null; + } + + public void UpdateStage() + { + if ( IsWorking ) + UpdateStage( FlourMillStage.Working ); + else if ( HasFlour ) + UpdateStage( FlourMillStage.Filled ); + else + UpdateStage( FlourMillStage.Empty ); + } + + public void UpdateStage( FlourMillStage stage ) + { + List components = this.Components; + + int[][] stageTable = m_StageTable; + + for ( int i = 0; i < components.Count; ++i ) + { + AddonComponent component = components[i] as AddonComponent; + + if ( component == null ) + continue; + + int[] itemTable = FindItemTable( component.ItemID ); + + if ( itemTable != null ) + component.ItemID = itemTable[(int)stage]; + } + } + + public override void OnComponentUsed( AddonComponent c, Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 4 ) || !from.InLOS( this ) ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else if ( !IsFull ) + from.SendLocalizedMessage( 500997 ); // You need more wheat to make a sack of flour. + else + StartWorking( from ); + } + + [Constructable] + public FlourMillSouthAddon() + { + AddComponent( new AddonComponent( 0x192C ), 0,-1, 0 ); + AddComponent( new AddonComponent( 0x192E ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1930 ), 0, 1, 0 ); + } + + public FlourMillSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Flour ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Flour = reader.ReadInt(); + break; + } + } + + UpdateStage(); + } + } + + public class FlourMillSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new FlourMillSouthAddon(); } } + public override int LabelNumber{ get{ return 1044348; } } // flour mill (south) + + [Constructable] + public FlourMillSouthDeed() + { + } + + public FlourMillSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/FlowerTapestries.cs b/Scripts/Items/Addons/FlowerTapestries.cs new file mode 100644 index 000000000..e1a77563b --- /dev/null +++ b/Scripts/Items/Addons/FlowerTapestries.cs @@ -0,0 +1,241 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LightFlowerTapestryEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LightFlowerTapestryEastDeed(); } } + + [Constructable] + public LightFlowerTapestryEastAddon() + { + AddComponent( new AddonComponent( 0xFDC ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xFDB ), 0, 1, 0 ); + } + + public LightFlowerTapestryEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightFlowerTapestryEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LightFlowerTapestryEastAddon(); } } + public override int LabelNumber{ get{ return 1049393; } } // a flower tapestry deed facing east + + [Constructable] + public LightFlowerTapestryEastDeed() + { + } + + public LightFlowerTapestryEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightFlowerTapestrySouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LightFlowerTapestrySouthDeed(); } } + + [Constructable] + public LightFlowerTapestrySouthAddon() + { + AddComponent( new AddonComponent( 0xFD9 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xFDA ), 1, 0, 0 ); + } + + public LightFlowerTapestrySouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightFlowerTapestrySouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LightFlowerTapestrySouthAddon(); } } + public override int LabelNumber{ get{ return 1049394; } } // a flower tapestry deed facing south + + [Constructable] + public LightFlowerTapestrySouthDeed() + { + } + + public LightFlowerTapestrySouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkFlowerTapestryEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new DarkFlowerTapestryEastDeed(); } } + + [Constructable] + public DarkFlowerTapestryEastAddon() + { + AddComponent( new AddonComponent( 0xFE0 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xFDF ), 0, 1, 0 ); + } + + public DarkFlowerTapestryEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkFlowerTapestryEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new DarkFlowerTapestryEastAddon(); } } + public override int LabelNumber{ get{ return 1049395; } } // a dark flower tapestry deed facing east + + [Constructable] + public DarkFlowerTapestryEastDeed() + { + } + + public DarkFlowerTapestryEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkFlowerTapestrySouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new DarkFlowerTapestrySouthDeed(); } } + + [Constructable] + public DarkFlowerTapestrySouthAddon() + { + AddComponent( new AddonComponent( 0xFDD ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xFDE ), 1, 0, 0 ); + } + + public DarkFlowerTapestrySouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkFlowerTapestrySouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new DarkFlowerTapestrySouthAddon(); } } + public override int LabelNumber{ get{ return 1049396; } } // a dark flower tapestry deed facing south + + [Constructable] + public DarkFlowerTapestrySouthDeed() + { + } + + public DarkFlowerTapestrySouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/GiantWebs.cs b/Scripts/Items/Addons/GiantWebs.cs new file mode 100644 index 000000000..bf93d2634 --- /dev/null +++ b/Scripts/Items/Addons/GiantWebs.cs @@ -0,0 +1,207 @@ +using System; +using System.Text; +using System.Net; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class GiantWeb1 : BaseAddon + { + [Constructable] + public GiantWeb1() + { + int itemID = 4280; + int count = 5; + bool leftToRight = false; + + for ( int i = 0; i < count; ++i ) + AddComponent( new AddonComponent( itemID++ ), leftToRight ? i : count - 1 - i, -( leftToRight ? i : count - 1 - i ), 0 ); + } + + public GiantWeb1( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } + + public class GiantWeb2 : BaseAddon + { + [Constructable] + public GiantWeb2() + { + int itemID = 4285; + int count = 5; + bool leftToRight = true; + + for ( int i = 0; i < count; ++i ) + AddComponent( new AddonComponent( itemID++ ), leftToRight ? i : count - 1 - i, -( leftToRight ? i : count - 1 - i ), 0 ); + } + + public GiantWeb2( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } + + public class GiantWeb3 : BaseAddon + { + [Constructable] + public GiantWeb3() + { + int itemID = 4290; + int count = 4; + bool leftToRight = true; + + for ( int i = 0; i < count; ++i ) + AddComponent( new AddonComponent( itemID++ ), leftToRight ? i : count - 1 - i, -( leftToRight ? i : count - 1 - i ), 0 ); + } + + public GiantWeb3( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } + + public class GiantWeb4 : BaseAddon + { + [Constructable] + public GiantWeb4() + { + int itemID = 4294; + int count = 4; + bool leftToRight = false; + + for ( int i = 0; i < count; ++i ) + AddComponent( new AddonComponent( itemID++ ), leftToRight ? i : count - 1 - i, -( leftToRight ? i : count - 1 - i ), 0 ); + } + + public GiantWeb4( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } + + public class GiantWeb5 : BaseAddon + { + [Constructable] + public GiantWeb5() + { + int itemID = 4298; + int count = 4; + bool leftToRight = true; + + for ( int i = 0; i < count; ++i ) + AddComponent( new AddonComponent( itemID++ ), leftToRight ? i : count - 1 - i, -( leftToRight ? i : count - 1 - i ), 0 ); + } + + public GiantWeb5( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } + + public class GiantWeb6 : BaseAddon + { + [Constructable] + public GiantWeb6() + { + int itemID = 4302; + int count = 4; + bool leftToRight = false; + + for ( int i = 0; i < count; ++i ) + AddComponent( new AddonComponent( itemID++ ), leftToRight ? i : count - 1 - i, -( leftToRight ? i : count - 1 - i ), 0 ); + } + + public GiantWeb6( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/GozaMats.cs b/Scripts/Items/Addons/GozaMats.cs new file mode 100644 index 000000000..e9eb2f276 --- /dev/null +++ b/Scripts/Items/Addons/GozaMats.cs @@ -0,0 +1,474 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GozaMatEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new GozaMatEastDeed(); } } + + [Constructable] + public GozaMatEastAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28a4, 1030688 ), 1, 0, 0 ); + AddComponent( new LocalizedAddonComponent( 0x28a5, 1030688 ), 0, 0, 0 ); + } + + public GozaMatEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GozaMatEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new GozaMatEastAddon(); } } + public override int LabelNumber{ get{ return 1030404; } } // goza (east) + + [Constructable] + public GozaMatEastDeed() + { + } + + public GozaMatEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GozaMatSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new GozaMatSouthDeed(); } } + + [Constructable] + public GozaMatSouthAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28a6, 1030688 ), 0, 1, 0 ); + AddComponent( new LocalizedAddonComponent( 0x28a7, 1030688 ), 0, 0, 0 ); + } + + public GozaMatSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GozaMatSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new GozaMatSouthAddon(); } } + public override int LabelNumber{ get{ return 1030405; } } // goza (south) + + [Constructable] + public GozaMatSouthDeed() + { + } + + public GozaMatSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SquareGozaMatEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SquareGozaMatEastDeed(); } } + public override int LabelNumber{ get{ return 1030688; } } // goza mat + + [Constructable] + public SquareGozaMatEastAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28a8, 1030688 ), 0, 0, 0 ); + } + + public SquareGozaMatEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SquareGozaMatEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SquareGozaMatEastAddon(); } } + public override int LabelNumber{ get{ return 1030407; } } // square goza (east) + + [Constructable] + public SquareGozaMatEastDeed() + { + } + + public SquareGozaMatEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SquareGozaMatSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SquareGozaMatSouthDeed(); } } + + [Constructable] + public SquareGozaMatSouthAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28a9, 1030688 ), 0, 0, 0 ); + } + + public SquareGozaMatSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SquareGozaMatSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SquareGozaMatSouthAddon(); } } + public override int LabelNumber{ get{ return 1030406; } } // square goza (south) + + + [Constructable] + public SquareGozaMatSouthDeed() + { + } + + public SquareGozaMatSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrocadeGozaMatEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BrocadeGozaMatEastDeed(); } } + + [Constructable] + public BrocadeGozaMatEastAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28AB, 1030688 ), 0, 0, 0 ); + AddComponent( new LocalizedAddonComponent( 0x28AA, 1030688 ), 1, 0, 0 ); + } + + public BrocadeGozaMatEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrocadeGozaMatEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BrocadeGozaMatEastAddon(); } } + public override int LabelNumber{ get{ return 1030408; } } // brocade goza (east) + + [Constructable] + public BrocadeGozaMatEastDeed() + { + } + + public BrocadeGozaMatEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + public class BrocadeGozaMatSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BrocadeGozaMatSouthDeed(); } } + + [Constructable] + public BrocadeGozaMatSouthAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28AD, 1030688 ), 0, 0, 0 ); + AddComponent( new LocalizedAddonComponent( 0x28AC, 1030688 ), 0, 1, 0 ); + } + + public BrocadeGozaMatSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrocadeGozaMatSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BrocadeGozaMatSouthAddon(); } } + public override int LabelNumber{ get{ return 1030409; } } // brocade goza (south) + + [Constructable] + public BrocadeGozaMatSouthDeed() + { + } + + public BrocadeGozaMatSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + public class BrocadeSquareGozaMatEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BrocadeSquareGozaMatEastDeed(); } } + + [Constructable] + public BrocadeSquareGozaMatEastAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28AE, 1030688 ), 0, 0, 0 ); + } + + public BrocadeSquareGozaMatEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrocadeSquareGozaMatEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BrocadeSquareGozaMatEastAddon(); } } + public override int LabelNumber{ get{ return 1030411; } } // brocade square goza (east) + + [Constructable] + public BrocadeSquareGozaMatEastDeed() + { + } + + public BrocadeSquareGozaMatEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrocadeSquareGozaMatSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new BrocadeSquareGozaMatSouthDeed(); } } + + [Constructable] + public BrocadeSquareGozaMatSouthAddon() + { + AddComponent( new LocalizedAddonComponent( 0x28AF, 1030688 ), 0, 0, 0 ); + } + + public BrocadeSquareGozaMatSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrocadeSquareGozaMatSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new BrocadeSquareGozaMatSouthAddon(); } } + public override int LabelNumber{ get{ return 1030410; } } // brocade square goza (south) + + + [Constructable] + public BrocadeSquareGozaMatSouthDeed() + { + } + + public BrocadeSquareGozaMatSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/GrayBrickFireplaceEastAddon.cs b/Scripts/Items/Addons/GrayBrickFireplaceEastAddon.cs new file mode 100644 index 000000000..b206693ca --- /dev/null +++ b/Scripts/Items/Addons/GrayBrickFireplaceEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GrayBrickFireplaceEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new GrayBrickFireplaceEastDeed(); } } + + [Constructable] + public GrayBrickFireplaceEastAddon() + { + AddComponent( new AddonComponent( 0x93D ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x937 ), 0, 1, 0 ); + } + + public GrayBrickFireplaceEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GrayBrickFireplaceEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new GrayBrickFireplaceEastAddon(); } } + public override int LabelNumber{ get{ return 1061846; } } // grey brick fireplace (east) + + [Constructable] + public GrayBrickFireplaceEastDeed() + { + } + + public GrayBrickFireplaceEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/GrayBrickFireplaceSouthAddon.cs b/Scripts/Items/Addons/GrayBrickFireplaceSouthAddon.cs new file mode 100644 index 000000000..9e3e86d91 --- /dev/null +++ b/Scripts/Items/Addons/GrayBrickFireplaceSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GrayBrickFireplaceSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new GrayBrickFireplaceSouthDeed(); } } + + [Constructable] + public GrayBrickFireplaceSouthAddon() + { + AddComponent( new AddonComponent( 0x94B ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x945 ), 0, 0, 0 ); + } + + public GrayBrickFireplaceSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GrayBrickFireplaceSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new GrayBrickFireplaceSouthAddon(); } } + public override int LabelNumber{ get{ return 1061847; } } // grey brick fireplace (south) + + [Constructable] + public GrayBrickFireplaceSouthDeed() + { + } + + public GrayBrickFireplaceSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/JackOLantern.cs b/Scripts/Items/Addons/JackOLantern.cs new file mode 100644 index 000000000..a0144cb95 --- /dev/null +++ b/Scripts/Items/Addons/JackOLantern.cs @@ -0,0 +1,88 @@ +using System; +using System.Text; +using System.Net; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class JackOLantern : BaseAddon + { + public override bool ShareHue + { + get { return false; } + } + + private AddonComponent GetComponent( int itemID, int hue ) + { + AddonComponent ac = new AddonComponent( itemID ); + + ac.Hue = hue; + ac.Name = "jack-o-latern"; + + return ac; + } + + [Constructable] + public JackOLantern() + : this( 1 > Utility.Random( 2 ) ) + { + } + + [Constructable] + public JackOLantern( bool south ) + { + AddComponent( new AddonComponent( 5703 ), 0, 0, +0 ); + + int hue = 1161; + //( 1 > Utility.Random( 5 ) ? 2118 : 1161 ); + + if ( !south ) + { + AddComponent( GetComponent( 3178, 0000 ), 0, 0, -1 ); + AddComponent( GetComponent( 3883, hue ), 0, 0, +1 ); + AddComponent( GetComponent( 3862, hue ), 0, 0, +0 ); + } + else + { + AddComponent( GetComponent( 3179, 0000 ), 0, 0, +0 ); + AddComponent( GetComponent( 3885, hue ), 0, 0, -1 ); + AddComponent( GetComponent( 3871, hue ), 0, 0, +0 ); + } + } + + public JackOLantern( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + + if ( version == 0 ) + { + Timer.DelayCall( TimeSpan.Zero, delegate() + { + for ( int i = 0; i < Components.Count; ++i ) + { + AddonComponent ac = Components[i] as AddonComponent; + + if ( ac != null && ac.Hue == 2118 ) + ac.Hue = 1161; + } + } ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LargeBedEastAddon.cs b/Scripts/Items/Addons/LargeBedEastAddon.cs new file mode 100644 index 000000000..ac77ea22b --- /dev/null +++ b/Scripts/Items/Addons/LargeBedEastAddon.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LargeBedEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LargeBedEastDeed(); } } + + [Constructable] + public LargeBedEastAddon() + { + AddComponent( new AddonComponent( 0xA7D ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xA7C ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0xA79 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0xA78 ), 1, 1, 0 ); + } + + public LargeBedEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeBedEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LargeBedEastAddon(); } } + public override int LabelNumber{ get{ return 1044324; } } // large bed (east) + + [Constructable] + public LargeBedEastDeed() + { + } + + public LargeBedEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LargeBedSouthAddon.cs b/Scripts/Items/Addons/LargeBedSouthAddon.cs new file mode 100644 index 000000000..29be75761 --- /dev/null +++ b/Scripts/Items/Addons/LargeBedSouthAddon.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LargeBedSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LargeBedSouthDeed(); } } + + [Constructable] + public LargeBedSouthAddon() + { + AddComponent( new AddonComponent( 0xA83 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xA7F ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0xA82 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0xA7E ), 1, 1, 0 ); + } + + public LargeBedSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeBedSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LargeBedSouthAddon(); } } + public override int LabelNumber{ get{ return 1044323; } } // large bed (south) + + [Constructable] + public LargeBedSouthDeed() + { + } + + public LargeBedSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LargeForgeEastAddon.cs b/Scripts/Items/Addons/LargeForgeEastAddon.cs new file mode 100644 index 000000000..d98bea26c --- /dev/null +++ b/Scripts/Items/Addons/LargeForgeEastAddon.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LargeForgeEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LargeForgeEastDeed(); } } + + [Constructable] + public LargeForgeEastAddon() + { + AddComponent( new ForgeComponent( 0x1986 ), 0, 0, 0 ); + AddComponent( new ForgeComponent( 0x198A ), 0, 1, 0 ); + AddComponent( new ForgeComponent( 0x1996 ), 0, 2, 0 ); + AddComponent( new ForgeComponent( 0x1992 ), 0, 3, 0 ); + } + + public LargeForgeEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeForgeEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LargeForgeEastAddon(); } } + public override int LabelNumber{ get{ return 1044331; } } // large forge (east) + + [Constructable] + public LargeForgeEastDeed() + { + } + + public LargeForgeEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LargeForgeSouthAddon.cs b/Scripts/Items/Addons/LargeForgeSouthAddon.cs new file mode 100644 index 000000000..a51061c45 --- /dev/null +++ b/Scripts/Items/Addons/LargeForgeSouthAddon.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LargeForgeSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LargeForgeSouthDeed(); } } + + [Constructable] + public LargeForgeSouthAddon() + { + AddComponent( new ForgeComponent( 0x197A ), 0, 0, 0 ); + AddComponent( new ForgeComponent( 0x197E ), 1, 0, 0 ); + AddComponent( new ForgeComponent( 0x19A2 ), 2, 0, 0 ); + AddComponent( new ForgeComponent( 0x199E ), 3, 0, 0 ); + } + + public LargeForgeSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeForgeSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LargeForgeSouthAddon(); } } + public override int LabelNumber{ get{ return 1044332; } } // large forge (south) + + [Constructable] + public LargeForgeSouthDeed() + { + } + + public LargeForgeSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LargeStoneTableEastAddon.cs b/Scripts/Items/Addons/LargeStoneTableEastAddon.cs new file mode 100644 index 000000000..86012406e --- /dev/null +++ b/Scripts/Items/Addons/LargeStoneTableEastAddon.cs @@ -0,0 +1,73 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LargeStoneTableEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LargeStoneTableEastDeed(); } } + + public override bool RetainDeedHue{ get{ return true; } } + + [Constructable] + public LargeStoneTableEastAddon() : this( 0 ) + { + } + + [Constructable] + public LargeStoneTableEastAddon( int hue ) + { + AddComponent( new AddonComponent( 0x1202 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1203 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x1201 ), 0, 2, 0 ); + Hue = hue; + } + + public LargeStoneTableEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeStoneTableEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LargeStoneTableEastAddon( this.Hue ); } } + public override int LabelNumber{ get{ return 1044511; } } // large stone table (east) + + [Constructable] + public LargeStoneTableEastDeed() + { + } + + public LargeStoneTableEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LargeStoneTableSouthAddon.cs b/Scripts/Items/Addons/LargeStoneTableSouthAddon.cs new file mode 100644 index 000000000..576326fd5 --- /dev/null +++ b/Scripts/Items/Addons/LargeStoneTableSouthAddon.cs @@ -0,0 +1,73 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LargeStoneTableSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new LargeStoneTableSouthDeed(); } } + + public override bool RetainDeedHue{ get{ return true; } } + + [Constructable] + public LargeStoneTableSouthAddon() : this( 0 ) + { + } + + [Constructable] + public LargeStoneTableSouthAddon( int hue ) + { + AddComponent( new AddonComponent( 0x1205 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1206 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1204 ), 2, 0, 0 ); + Hue = hue; + } + + public LargeStoneTableSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeStoneTableSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LargeStoneTableSouthAddon( this.Hue ); } } + public override int LabelNumber{ get{ return 1044512; } } // large stone table (South) + + [Constructable] + public LargeStoneTableSouthDeed() + { + } + + public LargeStoneTableSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LoomEastAddon.cs b/Scripts/Items/Addons/LoomEastAddon.cs new file mode 100644 index 000000000..6f49f4e4e --- /dev/null +++ b/Scripts/Items/Addons/LoomEastAddon.cs @@ -0,0 +1,84 @@ +using System; +using Server; + +namespace Server.Items +{ + public interface ILoom + { + int Phase{ get; set; } + } + + public class LoomEastAddon : BaseAddon, ILoom + { + public override BaseAddonDeed Deed{ get{ return new LoomEastDeed(); } } + + private int m_Phase; + + public int Phase{ get{ return m_Phase; } set{ m_Phase = value; } } + + [Constructable] + public LoomEastAddon() + { + AddComponent( new AddonComponent( 0x1060 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x105F ), 0, 1, 0 ); + } + + public LoomEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Phase ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Phase = reader.ReadInt(); + break; + } + } + } + } + + public class LoomEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LoomEastAddon(); } } + public override int LabelNumber{ get{ return 1044343; } } // loom (east) + + [Constructable] + public LoomEastDeed() + { + } + + public LoomEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/LoomSouthAddon.cs b/Scripts/Items/Addons/LoomSouthAddon.cs new file mode 100644 index 000000000..ff0b5e24b --- /dev/null +++ b/Scripts/Items/Addons/LoomSouthAddon.cs @@ -0,0 +1,79 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LoomSouthAddon : BaseAddon, ILoom + { + public override BaseAddonDeed Deed{ get{ return new LoomSouthDeed(); } } + + private int m_Phase; + + public int Phase{ get{ return m_Phase; } set{ m_Phase = value; } } + + [Constructable] + public LoomSouthAddon() + { + AddComponent( new AddonComponent( 0x1061 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1062 ), 1, 0, 0 ); + } + + public LoomSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Phase ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Phase = reader.ReadInt(); + break; + } + } + } + } + + public class LoomSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new LoomSouthAddon(); } } + public override int LabelNumber{ get{ return 1044344; } } // loom (south) + + [Constructable] + public LoomSouthDeed() + { + } + + public LoomSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/MediumStoneTableEastAddon.cs b/Scripts/Items/Addons/MediumStoneTableEastAddon.cs new file mode 100644 index 000000000..70c9f1b1f --- /dev/null +++ b/Scripts/Items/Addons/MediumStoneTableEastAddon.cs @@ -0,0 +1,72 @@ +using System; +using Server; + +namespace Server.Items +{ + public class MediumStoneTableEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new MediumStoneTableEastDeed(); } } + + public override bool RetainDeedHue{ get{ return true; } } + + [Constructable] + public MediumStoneTableEastAddon() : this( 0 ) + { + } + + [Constructable] + public MediumStoneTableEastAddon( int hue ) + { + AddComponent( new AddonComponent( 0x1202 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1201 ), 0, 1, 0 ); + Hue = hue; + } + + public MediumStoneTableEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumStoneTableEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new MediumStoneTableEastAddon( this.Hue ); } } + public override int LabelNumber{ get{ return 1044508; } } // stone table (east) + + [Constructable] + public MediumStoneTableEastDeed() + { + } + + public MediumStoneTableEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/MediumStoneTableSouthAddon.cs b/Scripts/Items/Addons/MediumStoneTableSouthAddon.cs new file mode 100644 index 000000000..3b4c17381 --- /dev/null +++ b/Scripts/Items/Addons/MediumStoneTableSouthAddon.cs @@ -0,0 +1,72 @@ +using System; +using Server; + +namespace Server.Items +{ + public class MediumStoneTableSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new MediumStoneTableSouthDeed(); } } + + public override bool RetainDeedHue{ get{ return true; } } + + [Constructable] + public MediumStoneTableSouthAddon() : this( 0 ) + { + } + + [Constructable] + public MediumStoneTableSouthAddon( int hue ) + { + AddComponent( new AddonComponent( 0x1205 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x1204 ), 1, 0, 0 ); + Hue = hue; + } + + public MediumStoneTableSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumStoneTableSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new MediumStoneTableSouthAddon( Hue ); } } + public override int LabelNumber{ get{ return 1044509; } } // stone table (South) + + [Constructable] + public MediumStoneTableSouthDeed() + { + } + + public MediumStoneTableSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/OrnateElvenTableEastAddon.cs b/Scripts/Items/Addons/OrnateElvenTableEastAddon.cs new file mode 100644 index 000000000..222c03fc7 --- /dev/null +++ b/Scripts/Items/Addons/OrnateElvenTableEastAddon.cs @@ -0,0 +1,65 @@ +using System; +using Server; + +namespace Server.Items +{ + public class OrnateElvenTableEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new OrnateElvenTableEastDeed(); } } + + [Constructable] + public OrnateElvenTableEastAddon() + { + AddComponent( new AddonComponent( 0x308E ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x308D ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x308C ), 1, 0, 0 ); + } + + public OrnateElvenTableEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrnateElvenTableEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new OrnateElvenTableEastAddon(); } } + public override int LabelNumber{ get{ return 1073384; } } // ornate table (east) + + [Constructable] + public OrnateElvenTableEastDeed() + { + } + + public OrnateElvenTableEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/OrnateElvenTableSouthAddon.cs b/Scripts/Items/Addons/OrnateElvenTableSouthAddon.cs new file mode 100644 index 000000000..7f00e92da --- /dev/null +++ b/Scripts/Items/Addons/OrnateElvenTableSouthAddon.cs @@ -0,0 +1,65 @@ +using System; +using Server; + +namespace Server.Items +{ + public class OrnateElvenTableSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new OrnateElvenTableSouthDeed(); } } + + [Constructable] + public OrnateElvenTableSouthAddon() + { + AddComponent( new AddonComponent( 0x308F ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x3090 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3091 ), 0, -1, 0 ); + } + + public OrnateElvenTableSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrnateElvenTableSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new OrnateElvenTableSouthAddon(); } } + public override int LabelNumber{ get{ return 1072869; } } // ornate table (south) + + [Constructable] + public OrnateElvenTableSouthDeed() + { + } + + public OrnateElvenTableSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ParrotPerchAddon.cs b/Scripts/Items/Addons/ParrotPerchAddon.cs new file mode 100644 index 000000000..9e16cf1ff --- /dev/null +++ b/Scripts/Items/Addons/ParrotPerchAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ParrotPerchAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new ParrotPerchDeed(); } } + + [Constructable] + public ParrotPerchAddon() + { + AddComponent( new AddonComponent( 0x2FF4 ), 0, 0, 0 ); + } + + public ParrotPerchAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ParrotPerchDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new ParrotPerchAddon(); } } + public override int LabelNumber{ get{ return 1072617; } } // parrot perch + + [Constructable] + public ParrotPerchDeed() + { + } + + public ParrotPerchDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/PentagramAddon.cs b/Scripts/Items/Addons/PentagramAddon.cs new file mode 100644 index 000000000..379a9209e --- /dev/null +++ b/Scripts/Items/Addons/PentagramAddon.cs @@ -0,0 +1,71 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PentagramAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new PentagramDeed(); } } + + [Constructable] + public PentagramAddon() + { + AddComponent( new AddonComponent( 0xFE7 ), -1, -1, 0 ); + AddComponent( new AddonComponent( 0xFE8 ), 0, -1, 0 ); + AddComponent( new AddonComponent( 0xFEB ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0xFE6 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0xFEA ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xFEE ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0xFE9 ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0xFEC ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0xFED ), 1, 1, 0 ); + } + + public PentagramAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PentagramDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new PentagramAddon(); } } + public override int LabelNumber{ get{ return 1044328; } } // pentagram + + [Constructable] + public PentagramDeed() + { + } + + public PentagramDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/PickpocketDips.cs b/Scripts/Items/Addons/PickpocketDips.cs new file mode 100644 index 000000000..7a20aacc9 --- /dev/null +++ b/Scripts/Items/Addons/PickpocketDips.cs @@ -0,0 +1,274 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1EC0, 0x1EC3 )] + public class PickpocketDip : AddonComponent + { + private double m_MinSkill; + private double m_MaxSkill; + + private Timer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public double MinSkill + { + get{ return m_MinSkill; } + set{ m_MinSkill = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double MaxSkill + { + get{ return m_MaxSkill; } + set{ m_MaxSkill = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Swinging + { + get{ return ( m_Timer != null ); } + } + + public PickpocketDip( int itemID ) : base( itemID ) + { + m_MinSkill = -25.0; + m_MaxSkill = +25.0; + } + + public void UpdateItemID() + { + int baseItemID = 0x1EC0 + (((ItemID - 0x1EC0) / 3) * 3); + + ItemID = baseItemID + (Swinging ? 1 : 0); + } + + public void BeginSwing() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = new InternalTimer( this ); + m_Timer.Start(); + + UpdateItemID(); + } + + public void EndSwing() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + UpdateItemID(); + } + + public void Use( Mobile from ) + { + from.Direction = from.GetDirectionTo( GetWorldLocation() ); + + Effects.PlaySound( GetWorldLocation(), Map, 0x4F ); + + if ( from.CheckSkill( SkillName.Stealing, m_MinSkill, m_MaxSkill ) ) + { + SendLocalizedMessageTo( from, 501834 ); // You successfully avoid disturbing the dip while searching it. + } + else + { + Effects.PlaySound( GetWorldLocation(), Map, 0x390 ); + + BeginSwing(); + ProcessDelta(); + SendLocalizedMessageTo( from, 501831 ); // You carelessly bump the dip and start it swinging. + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 1 ) ) + SendLocalizedMessageTo( from, 501816 ); // You are too far away to do that. + else if ( Swinging ) + SendLocalizedMessageTo( from, 501815 ); // You have to wait until it stops swinging. + else if ( from.Skills[SkillName.Stealing].Base >= m_MaxSkill ) + SendLocalizedMessageTo( from, 501830 ); // Your ability to steal cannot improve any further by simply practicing on a dummy. + else if ( from.Mounted ) + SendLocalizedMessageTo( from, 501829 ); // You can't practice on this while on a mount. + else + Use( from ); + } + + public PickpocketDip( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_MinSkill ); + writer.Write( m_MaxSkill ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_MinSkill = reader.ReadDouble(); + m_MaxSkill = reader.ReadDouble(); + + if ( m_MinSkill == 0.0 && m_MaxSkill == 30.0 ) + { + m_MinSkill = -25.0; + m_MaxSkill = +25.0; + } + + break; + } + } + + UpdateItemID(); + } + + private class InternalTimer : Timer + { + private PickpocketDip m_Dip; + + public InternalTimer( PickpocketDip dip ) : base( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Dip = dip; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + m_Dip.EndSwing(); + } + } + } + + public class PickpocketDipEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new PickpocketDipEastDeed(); } } + + [Constructable] + public PickpocketDipEastAddon() + { + AddComponent( new PickpocketDip( 0x1EC3 ), 0, 0, 0 ); + } + + public PickpocketDipEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PickpocketDipEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new PickpocketDipEastAddon(); } } + public override int LabelNumber{ get{ return 1044337; } } // pickpocket dip (east) + + [Constructable] + public PickpocketDipEastDeed() + { + } + + public PickpocketDipEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PickpocketDipSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new PickpocketDipSouthDeed(); } } + + [Constructable] + public PickpocketDipSouthAddon() + { + AddComponent( new PickpocketDip( 0x1EC0 ), 0, 0, 0 ); + } + + public PickpocketDipSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PickpocketDipSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new PickpocketDipSouthAddon(); } } + public override int LabelNumber{ get{ return 1044338; } } // pickpocket dip (south) + + [Constructable] + public PickpocketDipSouthDeed() + { + } + + public PickpocketDipSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/PyramidAddon.cs b/Scripts/Items/Addons/PyramidAddon.cs new file mode 100644 index 000000000..e60fc2c53 --- /dev/null +++ b/Scripts/Items/Addons/PyramidAddon.cs @@ -0,0 +1,63 @@ +using System; +using System.Text; +using System.Net; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class PyramidAddon : BaseAddon + { + public override bool ShareHue + { + get { return false; } + } + + [Constructable] + public PyramidAddon() + { + AddComponent( new AddonComponent( 1006 ), 0, 0, 5 ); + + for ( int o = 1; o <= 2; ++o ) + { + AddComponent( new AddonComponent( 1011 ), -o, -o, ( 2 - o ) * 5 ); + AddComponent( new AddonComponent( 1012 ), +o, +o, ( 2 - o ) * 5 ); + AddComponent( new AddonComponent( 1013 ), +o, -o, ( 2 - o ) * 5 ); + AddComponent( new AddonComponent( 1014 ), -o, +o, ( 2 - o ) * 5 ); + } + + for ( int o = -1; o <= 1; ++o ) + { + AddComponent( new AddonComponent( 1007 ), o, 2, 0 ); + AddComponent( new AddonComponent( 1008 ), 2, o, 0 ); + AddComponent( new AddonComponent( 1009 ), o, -2, 0 ); + AddComponent( new AddonComponent( 1010 ), -2, o, 0 ); + } + + AddComponent( new AddonComponent( 1007 ), 0, 1, 5 ); + AddComponent( new AddonComponent( 1008 ), 1, 0, 5 ); + AddComponent( new AddonComponent( 1009 ), 0, -1, 5 ); + AddComponent( new AddonComponent( 1010 ), -1, 0, 5 ); + } + + public PyramidAddon( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/RejuvinationAnkhs.cs b/Scripts/Items/Addons/RejuvinationAnkhs.cs new file mode 100644 index 000000000..6142e6f0b --- /dev/null +++ b/Scripts/Items/Addons/RejuvinationAnkhs.cs @@ -0,0 +1,180 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class RejuvinationAddonComponent : AddonComponent + { + public RejuvinationAddonComponent( int itemID ) : base( itemID ) + { + } + + public RejuvinationAddonComponent( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.BeginAction( typeof( RejuvinationAddonComponent ) ) ) + { + from.FixedEffect( 0x373A, 1, 16 ); + + int random = Utility.Random( 1, 4 ); + + if ( random == 1 || random == 4 ) + { + from.Hits = from.HitsMax; + SendLocalizedMessageTo( from, 500801 ); // A sense of warmth fills your body! + } + + if ( random == 2 || random == 4 ) + { + from.Mana = from.ManaMax; + SendLocalizedMessageTo( from, 500802 ); // A feeling of power surges through your veins! + } + + if ( random == 3 || random == 4 ) + { + from.Stam = from.StamMax; + SendLocalizedMessageTo( from, 500803 ); // You feel as though you've slept for days! + } + + Timer.DelayCall( TimeSpan.FromHours( 2.0 ), new TimerStateCallback( ReleaseUseLock_Callback ), new object[]{ from, random } ); + } + } + + public virtual void ReleaseUseLock_Callback( object state ) + { + object[] states = (object[])state; + + Mobile from = (Mobile)states[0]; + int random = (int)states[1]; + + from.EndAction( typeof( RejuvinationAddonComponent ) ); + + if ( random == 4 ) + { + from.Hits = from.HitsMax; + from.Mana = from.ManaMax; + from.Stam = from.StamMax; + SendLocalizedMessageTo( from, 500807 ); // You feel completely rejuvinated! + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public abstract class BaseRejuvinationAnkh : BaseAddon + { + public BaseRejuvinationAnkh() + { + } + + public override bool HandlesOnMovement{ get{ return true; } } + + private DateTime m_NextMessage; + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( m.Player && Utility.InRange( Location, m.Location, 3 ) && !Utility.InRange( Location, oldLocation, 3 ) ) + { + if ( DateTime.Now >= m_NextMessage ) + { + if ( Components.Count > 0 ) + ((AddonComponent)Components[0]).SendLocalizedMessageTo( m, 1010061 ); // An overwhelming sense of peace fills you. + + m_NextMessage = DateTime.Now + TimeSpan.FromSeconds( 25.0 ); + } + } + } + + public BaseRejuvinationAnkh( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RejuvinationAnkhWest : BaseRejuvinationAnkh + { + [Constructable] + public RejuvinationAnkhWest() + { + AddComponent( new RejuvinationAddonComponent( 0x3 ), 0, 0, 0 ); + AddComponent( new RejuvinationAddonComponent( 0x2 ), 0, 1, 0 ); + } + + public RejuvinationAnkhWest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RejuvinationAnkhNorth : BaseRejuvinationAnkh + { + [Constructable] + public RejuvinationAnkhNorth() + { + AddComponent( new RejuvinationAddonComponent( 0x4 ), 0, 0, 0 ); + AddComponent( new RejuvinationAddonComponent( 0x5 ), 1, 0, 0 ); + } + + public RejuvinationAnkhNorth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SHTeleporter.cs b/Scripts/Items/Addons/SHTeleporter.cs new file mode 100644 index 000000000..af596d317 --- /dev/null +++ b/Scripts/Items/Addons/SHTeleporter.cs @@ -0,0 +1,453 @@ +using System; +using Server.Commands; + +namespace Server.Items +{ + public class SHTeleComponent : AddonComponent + { + private bool m_Active; + private SHTeleComponent m_TeleDest; + private Point3D m_TeleOffset; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get { return m_Active; } + set + { + m_Active = value; + + SHTeleporter sourceAddon = Addon as SHTeleporter; + + if ( sourceAddon != null ) + sourceAddon.ChangeActive( value ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D TeleOffset + { + get { return m_TeleOffset; } + set { m_TeleOffset = value; } + } + + [CommandProperty( AccessLevel.Counselor )] + public Point3D TelePoint + { + get { return new Point3D( Location.X + TeleOffset.X, Location.Y + TeleOffset.Y, Location.Z + TeleOffset.Z ); } + set { m_TeleOffset = new Point3D( value.X - Location.X, value.Y - Location.Y, value.Z - Location.Z ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SHTeleComponent TeleDest + { + get { return m_TeleDest; } + set + { + m_TeleDest = value; + + SHTeleporter sourceAddon = Addon as SHTeleporter; + + if ( sourceAddon != null ) + sourceAddon.ChangeDest( value ); + } + } + + public override string DefaultName + { + get { return "a hole"; } + } + + [Constructable] + public SHTeleComponent() : this( 0x1775 ) + { + } + + [Constructable] + public SHTeleComponent( int itemID ) : this( itemID, new Point3D( 0, 0, 0 ) ) + { + } + + [Constructable] + public SHTeleComponent( int itemID, Point3D offset ) : base( itemID ) + { + Movable = false; + Hue = 1; + + m_Active = true; + m_TeleOffset = offset; + } + + public SHTeleComponent( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile m ) + { + if ( !m_Active || m_TeleDest == null || m_TeleDest.Deleted || m_TeleDest.Map == Map.Internal ) + return; + + if ( m.InRange( this, 3 ) ) + { + Map map = m_TeleDest.Map; + Point3D p = m_TeleDest.TelePoint; + + Server.Mobiles.BaseCreature.TeleportPets( m, p, map ); + + m.MoveToWorld( p, map ); + } + else + { + m.SendLocalizedMessage( 1019045 ); // I can't reach that. + } + } + + public override void OnDoubleClickDead( Mobile m ) + { + OnDoubleClick( m ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( m_Active ); + writer.Write( m_TeleDest ); + writer.Write( m_TeleOffset ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Active = reader.ReadBool(); + m_TeleDest = reader.ReadItem() as SHTeleComponent; + m_TeleOffset = reader.ReadPoint3D(); + } + } + + public class SHTeleporter : BaseAddon + { + public static void Initialize() + { + CommandSystem.Register( "SHTelGen", AccessLevel.Administrator, new CommandEventHandler( SHTelGen_OnCommand ) ); + } + + [Usage( "SHTelGen" )] + [Description( "Generates solen hives teleporters." )] + public static void SHTelGen_OnCommand( CommandEventArgs e ) + { + World.Broadcast( 0x35, true, "Solen hives teleporters are being generated, please wait." ); + + DateTime startTime = DateTime.Now; + + int count = new SHTeleporterCreator().CreateSHTeleporters(); + + DateTime endTime = DateTime.Now; + + World.Broadcast( 0x35, true, "{0} solen hives teleporters have been created. The entire process took {1:F1} seconds.", count, (endTime - startTime).TotalSeconds ); + } + + public class SHTeleporterCreator + { + private int m_Count; + + public SHTeleporterCreator() + { + m_Count = 0; + } + + public static SHTeleporter FindSHTeleporter( Map map, Point3D p ) + { + IPooledEnumerable eable = map.GetItemsInRange( p, 0 ); + + foreach ( Item item in eable ) + { + if ( item is SHTeleporter && item.Z == p.Z ) + { + eable.Free(); + return (SHTeleporter)item; + } + } + + eable.Free(); + return null; + } + + public SHTeleporter AddSHT( Map map, bool ext, int x, int y, int z ) + { + Point3D p = new Point3D( x, y, z ); + SHTeleporter tele = FindSHTeleporter( map, p ); + + if ( tele == null ) + { + tele = new SHTeleporter( ext ); + tele.MoveToWorld( p, map ); + + m_Count++; + } + + return tele; + } + + public static void Link( SHTeleporter tele1, SHTeleporter tele2 ) + { + tele1.ChangeDest( tele2 ); + tele2.ChangeDest( tele1 ); + } + + public void AddSHTCouple( Map map, bool ext1, int x1, int y1, int z1, bool ext2, int x2, int y2, int z2 ) + { + SHTeleporter tele1 = AddSHT( map, ext1, x1, y1, z1 ); + SHTeleporter tele2 = AddSHT( map, ext2, x2, y2, z2 ); + + Link( tele1, tele2 ); + } + + public void AddSHTCouple( bool ext1, int x1, int y1, int z1, bool ext2, int x2, int y2, int z2 ) + { + AddSHTCouple( Map.Trammel, ext1, x1, y1, z1, ext2, x2, y2, z2 ); + AddSHTCouple( Map.Felucca, ext1, x1, y1, z1, ext2, x2, y2, z2 ); + } + + public int CreateSHTeleporters() + { + SHTeleporter tele1, tele2; + + AddSHTCouple( true, 2608, 763, 0, false, 5918, 1794, 0 ); + AddSHTCouple( false, 5897, 1877, 0, false, 5871, 1867, 0 ); + AddSHTCouple( false, 5852, 1848, 0, false, 5771, 1867, 0 ); + + tele1 = AddSHT( Map.Trammel, false, 5747, 1895, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Trammel, false, 5658, 1898, 0 ); + Link( tele1, tele2 ); + + tele1 = AddSHT( Map.Felucca, false, 5747, 1895, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Felucca, false, 5658, 1898, 0 ); + Link( tele1, tele2 ); + + AddSHTCouple( false, 5727, 1894, 0, false, 5756, 1794, 0 ); + AddSHTCouple( false, 5784, 1929, 0, false, 5700, 1929, 0 ); + + tele1 = AddSHT( Map.Trammel, false, 5711, 1952, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Trammel, false, 5657, 1954, 0 ); + Link( tele1, tele2 ); + + tele1 = AddSHT( Map.Felucca, false, 5711, 1952, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Felucca, false, 5657, 1954, 0 ); + Link( tele1, tele2 ); + + tele1 = AddSHT( Map.Trammel, false, 5655, 2018, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Trammel, true, 1690, 2789, 0 ); + Link( tele1, tele2 ); + + tele1 = AddSHT( Map.Felucca, false, 5655, 2018, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Felucca, true, 1690, 2789, 0 ); + Link( tele1, tele2 ); + + AddSHTCouple( false, 5809, 1905, 0, false, 5876, 1891, 0 ); + + tele1 = AddSHT( Map.Trammel, false, 5814, 2015, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Trammel, false, 5913, 1893, 0 ); + Link( tele1, tele2 ); + + tele1 = AddSHT( Map.Felucca, false, 5814, 2015, 0 ); + tele1.LeftTele.TeleOffset = new Point3D( -1, 3, 0 ); + tele2 = AddSHT( Map.Felucca, false, 5913, 1893, 0 ); + Link( tele1, tele2 ); + + AddSHTCouple( false, 5919, 2021, 0, true, 1724, 814, 0 ); + + tele1 = AddSHT( Map.Trammel, false, 5654, 1791, 0 ); + tele2 = AddSHT( Map.Trammel, true, 730, 1451, 0 ); + Link( tele1, tele2 ); + AddSHT( Map.Trammel, false, 5734, 1859, 0 ).ChangeDest( tele2 ); + + tele1 = AddSHT( Map.Felucca, false, 5654, 1791, 0 ); + tele2 = AddSHT( Map.Felucca, true, 730, 1451, 0 ); + Link( tele1, tele2 ); + AddSHT( Map.Felucca, false, 5734, 1859, 0 ).ChangeDest( tele2 ); + + return m_Count; + } + } + + private bool m_External; + + private SHTeleComponent m_UpTele; + private SHTeleComponent m_RightTele; + private SHTeleComponent m_DownTele; + private SHTeleComponent m_LeftTele; + + private bool m_Changing; + + [CommandProperty( AccessLevel.GameMaster )] + public bool External { get { return m_External; } } + + public SHTeleComponent UpTele { get { return m_UpTele; } } + public SHTeleComponent RightTele { get { return m_RightTele; } } + public SHTeleComponent DownTele { get { return m_DownTele; } } + public SHTeleComponent LeftTele { get { return m_LeftTele; } } + + [Constructable] + public SHTeleporter() : this( true ) + { + } + + [Constructable] + public SHTeleporter( bool external ) + { + m_Changing = false; + m_External = external; + + if ( external ) + { + AddComponent( new AddonComponent( 0x549 ), -1, -1, 0 ); + AddComponent( new AddonComponent( 0x54D ), 0, -1, 0 ); + AddComponent( new AddonComponent( 0x54E ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0x548 ), 2, -1, 0 ); + AddComponent( new AddonComponent( 0x54B ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x53B ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x53B ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x544 ), 2, 0, 0 ); + AddComponent( new AddonComponent( 0x54C ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0x53B ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x53B ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x545 ), 2, 1, 0 ); + AddComponent( new AddonComponent( 0x547 ), -1, 2, 0 ); + AddComponent( new AddonComponent( 0x541 ), 0, 2, 0 ); + AddComponent( new AddonComponent( 0x543 ), 1, 2, 0 ); + AddComponent( new AddonComponent( 0x540 ), 2, 2, 0 ); + } + + Point3D upOS = external ? new Point3D( -1, 0, 0 ) : new Point3D( -2, -1, 0 ); + m_UpTele = new SHTeleComponent( external ? 0x1775 : 0x495, upOS ); + AddComponent( m_UpTele, 0, 0, 0 ); + + Point3D rightOS = external ? new Point3D( -2, 0, 0 ) : new Point3D( 2, -1, 0 ); + m_RightTele = new SHTeleComponent( external ? 0x1775 : 0x495, rightOS ); + AddComponent( m_RightTele, 1, 0, 0 ); + + Point3D downOS = external ? new Point3D( -2, -1, 0 ) : new Point3D( 2, 2, 0 ); + m_DownTele = new SHTeleComponent( external ? 0x1776 : 0x495, downOS ); + AddComponent( m_DownTele, 1, 1, 0 ); + + Point3D leftOS = external ? new Point3D( -1, -1, 0 ) : new Point3D( -1, 2, 0 ); + m_LeftTele = new SHTeleComponent( external ? 0x1775 : 0x495, leftOS ); + AddComponent( m_LeftTele, 0, 1, 0 ); + } + + public SHTeleporter( Serial serial ) : base( serial ) + { + m_Changing = false; + } + + public override bool ShareHue{ get { return false; } } + + public void ChangeActive( bool active ) + { + if ( m_Changing ) + return; + + m_Changing = true; + + m_UpTele.Active = active; + m_RightTele.Active = active; + m_DownTele.Active = active; + m_LeftTele.Active = active; + + m_Changing = false; + } + + public void ChangeDest( SHTeleComponent dest ) + { + if ( m_Changing ) + return; + + m_Changing = true; + + if ( dest == null || !(dest.Addon is SHTeleporter) ) + { + m_UpTele.TeleDest = dest; + m_RightTele.TeleDest = dest; + m_DownTele.TeleDest = dest; + m_LeftTele.TeleDest = dest; + } + else + { + SHTeleporter destAddon = (SHTeleporter)dest.Addon; + + m_UpTele.TeleDest = destAddon.UpTele; + m_RightTele.TeleDest = destAddon.RightTele; + m_DownTele.TeleDest = destAddon.DownTele; + m_LeftTele.TeleDest = destAddon.LeftTele; + } + + m_Changing = false; + } + + public void ChangeDest( SHTeleporter destAddon ) + { + if ( m_Changing ) + return; + + m_Changing = true; + + if ( destAddon != null ) + { + m_UpTele.TeleDest = destAddon.UpTele; + m_RightTele.TeleDest = destAddon.RightTele; + m_DownTele.TeleDest = destAddon.DownTele; + m_LeftTele.TeleDest = destAddon.LeftTele; + } + else + { + m_UpTele.TeleDest = null; + m_RightTele.TeleDest = null; + m_DownTele.TeleDest = null; + m_LeftTele.TeleDest = null; + } + + m_Changing = false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( m_External ); + + writer.Write( m_UpTele ); + writer.Write( m_RightTele ); + writer.Write( m_DownTele ); + writer.Write( m_LeftTele ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_External = reader.ReadBool(); + + m_UpTele = (SHTeleComponent)reader.ReadItem(); + m_RightTele = (SHTeleComponent)reader.ReadItem(); + m_DownTele = (SHTeleComponent)reader.ReadItem(); + m_LeftTele = (SHTeleComponent)reader.ReadItem(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SandstoneFireplaceEastAddon.cs b/Scripts/Items/Addons/SandstoneFireplaceEastAddon.cs new file mode 100644 index 000000000..cd7b5c524 --- /dev/null +++ b/Scripts/Items/Addons/SandstoneFireplaceEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SandstoneFireplaceEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SandstoneFireplaceEastDeed(); } } + + [Constructable] + public SandstoneFireplaceEastAddon() + { + AddComponent( new AddonComponent( 0x489 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x475 ), 0, 1, 0 ); + } + + public SandstoneFireplaceEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SandstoneFireplaceEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SandstoneFireplaceEastAddon(); } } + public override int LabelNumber{ get{ return 1061844; } } // sandstone fireplace (east) + + [Constructable] + public SandstoneFireplaceEastDeed() + { + } + + public SandstoneFireplaceEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SandstoneFireplaceSouthAddon.cs b/Scripts/Items/Addons/SandstoneFireplaceSouthAddon.cs new file mode 100644 index 000000000..e1bb0a4ad --- /dev/null +++ b/Scripts/Items/Addons/SandstoneFireplaceSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SandstoneFireplaceSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SandstoneFireplaceSouthDeed(); } } + + [Constructable] + public SandstoneFireplaceSouthAddon() + { + AddComponent( new AddonComponent( 0x482 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x47B ), 0, 0, 0 ); + } + + public SandstoneFireplaceSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SandstoneFireplaceSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SandstoneFireplaceSouthAddon(); } } + public override int LabelNumber{ get{ return 1061845; } } // sandstone fireplace (south) + + [Constructable] + public SandstoneFireplaceSouthDeed() + { + } + + public SandstoneFireplaceSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SandstoneFountainAddon.cs b/Scripts/Items/Addons/SandstoneFountainAddon.cs new file mode 100644 index 000000000..df3acb524 --- /dev/null +++ b/Scripts/Items/Addons/SandstoneFountainAddon.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SandstoneFountainAddon : BaseAddon + { + [Constructable] + public SandstoneFountainAddon() + { + int itemID = 0x19C3; + + AddComponent( new AddonComponent( itemID++ ), -2, +1, 0 ); + AddComponent( new AddonComponent( itemID++ ), -1, +1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +0, +1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +1, +1, 0 ); + + AddComponent( new AddonComponent( itemID++ ), +1, +0, 0 ); + AddComponent( new AddonComponent( itemID++ ), +1, -1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +1, -2, 0 ); + + AddComponent( new AddonComponent( itemID++ ), +0, -2, 0 ); + AddComponent( new AddonComponent( itemID++ ), +0, -1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +0, +0, 0 ); + + AddComponent( new AddonComponent( itemID++ ), -1, +0, 0 ); + AddComponent( new AddonComponent( itemID++ ), -2, +0, 0 ); + + AddComponent( new AddonComponent( itemID++ ), -2, -1, 0 ); + AddComponent( new AddonComponent( itemID++ ), -1, -1, 0 ); + + AddComponent( new AddonComponent( itemID++ ), -1, -2, 0 ); + AddComponent( new AddonComponent( ++itemID ), -2, -2, 0 ); + } + + public SandstoneFountainAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SerpentPillarAddon.cs b/Scripts/Items/Addons/SerpentPillarAddon.cs new file mode 100644 index 000000000..51f75cb0c --- /dev/null +++ b/Scripts/Items/Addons/SerpentPillarAddon.cs @@ -0,0 +1,54 @@ +using System; +using System.Text; +using System.Net; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class SerpentPillarAddon : BaseAddon + { + [Constructable] + public SerpentPillarAddon() + { + AddComponent( new AddonComponent( 9020 ), -2, +1, 0 ); + AddComponent( new AddonComponent( 9021 ), -2, +0, 0 ); + AddComponent( new AddonComponent( 9022 ), -2, -1, 0 ); + AddComponent( new AddonComponent( 9023 ), -2, -2, 0 ); + AddComponent( new AddonComponent( 9024 ), -1, -2, 0 ); + AddComponent( new AddonComponent( 9025 ), +0, -2, 0 ); + AddComponent( new AddonComponent( 9026 ), +1, -2, 0 ); + + AddComponent( new AddonComponent( 9027 ), -1, +1, 0 ); + AddComponent( new AddonComponent( 9028 ), -1, +0, 0 ); + AddComponent( new AddonComponent( 9029 ), -1, -1, 0 ); + + AddComponent( new AddonComponent( 9030 ), +0, +1, 0 ); + AddComponent( new AddonComponent( 9031 ), +0, +0, 0 ); + AddComponent( new AddonComponent( 9032 ), +0, -1, 0 ); + + AddComponent( new AddonComponent( 9033 ), +1, +0, 0 ); + AddComponent( new AddonComponent( 9034 ), +1, -1, 0 ); + } + + public SerpentPillarAddon( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/ShrineOfWisdomAddon.cs b/Scripts/Items/Addons/ShrineOfWisdomAddon.cs new file mode 100644 index 000000000..3592d2985 --- /dev/null +++ b/Scripts/Items/Addons/ShrineOfWisdomAddon.cs @@ -0,0 +1,65 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ShrineOfWisdomAddon : BaseAddon + { + [Constructable] + public ShrineOfWisdomAddon() + { + AddComponent( new ShrineOfWisdomComponent( 0x14C3 ), 0, 0, 0 ); + AddComponent( new ShrineOfWisdomComponent( 0x14C6 ), 1, 0, 0 ); + AddComponent( new ShrineOfWisdomComponent( 0x14D4 ), 0, 1, 0 ); + AddComponent( new ShrineOfWisdomComponent( 0x14D5 ), 1, 1, 0 ); + Hue = 0x47E; + } + + public ShrineOfWisdomAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Server.Engines.Craft.Forge] + public class ShrineOfWisdomComponent : AddonComponent + { + public override int LabelNumber{ get{ return 1062046; } } // Shrine of Wisdom + + [Constructable] + public ShrineOfWisdomComponent( int itemID ) : base( itemID ) + { + } + + public ShrineOfWisdomComponent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SkullPileAddon.cs b/Scripts/Items/Addons/SkullPileAddon.cs new file mode 100644 index 000000000..72f634ad2 --- /dev/null +++ b/Scripts/Items/Addons/SkullPileAddon.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; +using System.Net; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class SkullPileAddon : BaseAddon + { + [Constructable] + public SkullPileAddon() + { + AddComponent( new AddonComponent( 6872 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 6873 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 6874 ), -1, 1, 0 ); + AddComponent( new AddonComponent( 6875 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 6876 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 6877 ), 1, -1, 0 ); + AddComponent( new AddonComponent( 6878 ), 2, -1, 0 ); + AddComponent( new AddonComponent( 6879 ), 2, 0, 0 ); + } + + public SkullPileAddon( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SmallBedEastAddon.cs b/Scripts/Items/Addons/SmallBedEastAddon.cs new file mode 100644 index 000000000..bc223c201 --- /dev/null +++ b/Scripts/Items/Addons/SmallBedEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SmallBedEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SmallBedEastDeed(); } } + + [Constructable] + public SmallBedEastAddon() + { + AddComponent( new AddonComponent( 0xA5D ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xA62 ), 1, 0, 0 ); + } + + public SmallBedEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallBedEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SmallBedEastAddon(); } } + public override int LabelNumber{ get{ return 1044322; } } // small bed (east) + + [Constructable] + public SmallBedEastDeed() + { + } + + public SmallBedEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SmallBedSouthAddon.cs b/Scripts/Items/Addons/SmallBedSouthAddon.cs new file mode 100644 index 000000000..678de43a0 --- /dev/null +++ b/Scripts/Items/Addons/SmallBedSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SmallBedSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SmallBedSouthDeed(); } } + + [Constructable] + public SmallBedSouthAddon() + { + AddComponent( new AddonComponent( 0xA63 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xA5C ), 0, 1, 0 ); + } + + public SmallBedSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallBedSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SmallBedSouthAddon(); } } + public override int LabelNumber{ get{ return 1044321; } } // small bed (south) + + [Constructable] + public SmallBedSouthDeed() + { + } + + public SmallBedSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SmallForgeAddon.cs b/Scripts/Items/Addons/SmallForgeAddon.cs new file mode 100644 index 000000000..5a461fb9e --- /dev/null +++ b/Scripts/Items/Addons/SmallForgeAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SmallForgeAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SmallForgeDeed(); } } + + [Constructable] + public SmallForgeAddon() + { + AddComponent( new ForgeComponent( 0xFB1 ), 0, 0, 0 ); + } + + public SmallForgeAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallForgeDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SmallForgeAddon(); } } + public override int LabelNumber{ get{ return 1044330; } } // small forge + + [Constructable] + public SmallForgeDeed() + { + } + + public SmallForgeDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SpinningwheelEastAddon.cs b/Scripts/Items/Addons/SpinningwheelEastAddon.cs new file mode 100644 index 000000000..6d07f86c1 --- /dev/null +++ b/Scripts/Items/Addons/SpinningwheelEastAddon.cs @@ -0,0 +1,147 @@ +using System; +using Server; + +namespace Server.Items +{ + public delegate void SpinCallback( ISpinningWheel sender, Mobile from, int hue ); + + public interface ISpinningWheel + { + bool Spinning{ get; } + void BeginSpin( SpinCallback callback, Mobile from, int hue ); + } + + public class SpinningwheelEastAddon : BaseAddon, ISpinningWheel + { + public override BaseAddonDeed Deed{ get{ return new SpinningwheelEastDeed(); } } + + [Constructable] + public SpinningwheelEastAddon() + { + AddComponent( new AddonComponent( 0x1019 ), 0, 0, 0 ); + } + + public SpinningwheelEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private Timer m_Timer; + + public override void OnComponentLoaded( AddonComponent c ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + public bool Spinning{ get{ return m_Timer != null; } } + + public void BeginSpin( SpinCallback callback, Mobile from, int hue ) + { + m_Timer = new SpinTimer( this, callback, from, hue ); + m_Timer.Start(); + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1015: + case 0x1019: + case 0x101C: + case 0x10A4: ++c.ItemID; break; + } + } + } + + public void EndSpin( SpinCallback callback, Mobile from, int hue ) + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + if ( callback != null ) + callback( this, from, hue ); + } + + private class SpinTimer : Timer + { + private SpinningwheelEastAddon m_Wheel; + private SpinCallback m_Callback; + private Mobile m_From; + private int m_Hue; + + public SpinTimer( SpinningwheelEastAddon wheel, SpinCallback callback, Mobile from, int hue ) : base( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Wheel = wheel; + m_Callback = callback; + m_From = from; + m_Hue = hue; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Wheel.EndSpin( m_Callback, m_From, m_Hue ); + } + } + } + + public class SpinningwheelEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SpinningwheelEastAddon(); } } + public override int LabelNumber{ get{ return 1044341; } } // spining wheel (east) + + [Constructable] + public SpinningwheelEastDeed() + { + } + + public SpinningwheelEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SpinningwheelSouthAddon.cs b/Scripts/Items/Addons/SpinningwheelSouthAddon.cs new file mode 100644 index 000000000..a2cd565f3 --- /dev/null +++ b/Scripts/Items/Addons/SpinningwheelSouthAddon.cs @@ -0,0 +1,139 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SpinningwheelSouthAddon : BaseAddon, ISpinningWheel + { + public override BaseAddonDeed Deed{ get{ return new SpinningwheelSouthDeed(); } } + + [Constructable] + public SpinningwheelSouthAddon() + { + AddComponent( new AddonComponent( 0x1015 ), 0, 0, 0 ); + } + + public SpinningwheelSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private Timer m_Timer; + + public override void OnComponentLoaded( AddonComponent c ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + public bool Spinning{ get{ return m_Timer != null; } } + + public void BeginSpin( SpinCallback callback, Mobile from, int hue ) + { + m_Timer = new SpinTimer( this, callback, from, hue ); + m_Timer.Start(); + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1015: + case 0x1019: + case 0x101C: + case 0x10A4: ++c.ItemID; break; + } + } + } + + public void EndSpin( SpinCallback callback, Mobile from, int hue ) + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + foreach ( AddonComponent c in Components ) + { + switch ( c.ItemID ) + { + case 0x1016: + case 0x101A: + case 0x101D: + case 0x10A5: --c.ItemID; break; + } + } + + if ( callback != null ) + callback( this, from, hue ); + } + + private class SpinTimer : Timer + { + private SpinningwheelSouthAddon m_Wheel; + private SpinCallback m_Callback; + private Mobile m_From; + private int m_Hue; + + public SpinTimer( SpinningwheelSouthAddon wheel, SpinCallback callback, Mobile from, int hue ) : base( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Wheel = wheel; + m_Callback = callback; + m_From = from; + m_Hue = hue; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Wheel.EndSpin( m_Callback, m_From, m_Hue ); + } + } + } + + public class SpinningwheelSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SpinningwheelSouthAddon(); } } + public override int LabelNumber{ get{ return 1044342; } } // spining wheel (south) + + [Constructable] + public SpinningwheelSouthDeed() + { + } + + public SpinningwheelSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SquirrelStatueEastAddon.cs b/Scripts/Items/Addons/SquirrelStatueEastAddon.cs new file mode 100644 index 000000000..6c7427ef5 --- /dev/null +++ b/Scripts/Items/Addons/SquirrelStatueEastAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SquirrelStatueEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SquirrelStatueEastDeed(); } } + + [Constructable] + public SquirrelStatueEastAddon() + { + AddComponent( new AddonComponent( 0x2D10 ), 0, 0, 0 ); + } + + public SquirrelStatueEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class SquirrelStatueEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SquirrelStatueEastAddon(); } } + public override int LabelNumber{ get{ return 1073398; } } // squirrel statue (east) + + [Constructable] + public SquirrelStatueEastDeed() + { + } + + public SquirrelStatueEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/SquirrelStatueSouthAddon.cs b/Scripts/Items/Addons/SquirrelStatueSouthAddon.cs new file mode 100644 index 000000000..a4269ee25 --- /dev/null +++ b/Scripts/Items/Addons/SquirrelStatueSouthAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SquirrelStatueSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SquirrelStatueSouthDeed(); } } + + [Constructable] + public SquirrelStatueSouthAddon() + { + AddComponent( new AddonComponent( 0x2D11 ), 0, 0, 0 ); + } + + public SquirrelStatueSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class SquirrelStatueSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SquirrelStatueSouthAddon(); } } + public override int LabelNumber{ get{ return 1072884; } } // squirrel statue (south) + + [Constructable] + public SquirrelStatueSouthDeed() + { + } + + public SquirrelStatueSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/StoneFireplaceEastAddon.cs b/Scripts/Items/Addons/StoneFireplaceEastAddon.cs new file mode 100644 index 000000000..0144bf310 --- /dev/null +++ b/Scripts/Items/Addons/StoneFireplaceEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StoneFireplaceEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new StoneFireplaceEastDeed(); } } + + [Constructable] + public StoneFireplaceEastAddon() + { + AddComponent( new AddonComponent( 0x959 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x953 ), 0, 1, 0 ); + } + + public StoneFireplaceEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StoneFireplaceEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new StoneFireplaceEastAddon(); } } + public override int LabelNumber{ get{ return 1061848; } } // stone fireplace (east) + + [Constructable] + public StoneFireplaceEastDeed() + { + } + + public StoneFireplaceEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/StoneFireplaceSouthAddon.cs b/Scripts/Items/Addons/StoneFireplaceSouthAddon.cs new file mode 100644 index 000000000..945c04bb3 --- /dev/null +++ b/Scripts/Items/Addons/StoneFireplaceSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StoneFireplaceSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new StoneFireplaceSouthDeed(); } } + + [Constructable] + public StoneFireplaceSouthAddon() + { + AddComponent( new AddonComponent( 0x967 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x961 ), 0, 0, 0 ); + } + + public StoneFireplaceSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StoneFireplaceSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new StoneFireplaceSouthAddon(); } } + public override int LabelNumber{ get{ return 1061849; } } // stone fireplace (south) + + [Constructable] + public StoneFireplaceSouthDeed() + { + } + + public StoneFireplaceSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/StoneFountainAddon.cs b/Scripts/Items/Addons/StoneFountainAddon.cs new file mode 100644 index 000000000..a0132e687 --- /dev/null +++ b/Scripts/Items/Addons/StoneFountainAddon.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StoneFountainAddon : BaseAddon + { + [Constructable] + public StoneFountainAddon() + { + int itemID = 0x1731; + + AddComponent( new AddonComponent( itemID++ ), -2, +1, 0 ); + AddComponent( new AddonComponent( itemID++ ), -1, +1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +0, +1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +1, +1, 0 ); + + AddComponent( new AddonComponent( itemID++ ), +1, +0, 0 ); + AddComponent( new AddonComponent( itemID++ ), +1, -1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +1, -2, 0 ); + + AddComponent( new AddonComponent( itemID++ ), +0, -2, 0 ); + AddComponent( new AddonComponent( itemID++ ), +0, -1, 0 ); + AddComponent( new AddonComponent( itemID++ ), +0, +0, 0 ); + + AddComponent( new AddonComponent( itemID++ ), -1, +0, 0 ); + AddComponent( new AddonComponent( itemID++ ), -2, +0, 0 ); + + AddComponent( new AddonComponent( itemID++ ), -2, -1, 0 ); + AddComponent( new AddonComponent( itemID++ ), -1, -1, 0 ); + + AddComponent( new AddonComponent( itemID++ ), -1, -2, 0 ); + AddComponent( new AddonComponent( ++itemID ), -2, -2, 0 ); + } + + public StoneFountainAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/StoneOvenEastAddon.cs b/Scripts/Items/Addons/StoneOvenEastAddon.cs new file mode 100644 index 000000000..59811cba7 --- /dev/null +++ b/Scripts/Items/Addons/StoneOvenEastAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StoneOvenEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new StoneOvenEastDeed(); } } + + [Constructable] + public StoneOvenEastAddon() + { + AddComponent( new AddonComponent( 0x92C ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x92B ), 0, 1, 0 ); + } + + public StoneOvenEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StoneOvenEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new StoneOvenEastAddon(); } } + public override int LabelNumber{ get{ return 1044345; } } // stone oven (east) + + [Constructable] + public StoneOvenEastDeed() + { + } + + public StoneOvenEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/StoneOvenSouthAddon.cs b/Scripts/Items/Addons/StoneOvenSouthAddon.cs new file mode 100644 index 000000000..380a55449 --- /dev/null +++ b/Scripts/Items/Addons/StoneOvenSouthAddon.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StoneOvenSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new StoneOvenSouthDeed(); } } + + [Constructable] + public StoneOvenSouthAddon() + { + AddComponent( new AddonComponent( 0x931 ), -1, 0, 0 ); + AddComponent( new AddonComponent( 0x930 ), 0, 0, 0 ); + } + + public StoneOvenSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StoneOvenSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new StoneOvenSouthAddon(); } } + public override int LabelNumber{ get{ return 1044346; } } // stone oven (south) + + [Constructable] + public StoneOvenSouthDeed() + { + } + + public StoneOvenSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/StretchedHides.cs b/Scripts/Items/Addons/StretchedHides.cs new file mode 100644 index 000000000..7874bab98 --- /dev/null +++ b/Scripts/Items/Addons/StretchedHides.cs @@ -0,0 +1,237 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SmallStretchedHideEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SmallStretchedHideEastDeed(); } } + + [Constructable] + public SmallStretchedHideEastAddon() + { + AddComponent( new AddonComponent( 0x1069 ), 0, 0, 0 ); + } + + public SmallStretchedHideEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallStretchedHideEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SmallStretchedHideEastAddon(); } } + public override int LabelNumber{ get{ return 1049401; } } // a small stretched hide deed facing east + + [Constructable] + public SmallStretchedHideEastDeed() + { + } + + public SmallStretchedHideEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallStretchedHideSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new SmallStretchedHideSouthDeed(); } } + + [Constructable] + public SmallStretchedHideSouthAddon() + { + AddComponent( new AddonComponent( 0x107A ), 0, 0, 0 ); + } + + public SmallStretchedHideSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallStretchedHideSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new SmallStretchedHideSouthAddon(); } } + public override int LabelNumber{ get{ return 1049402; } } // a small stretched hide deed facing south + + [Constructable] + public SmallStretchedHideSouthDeed() + { + } + + public SmallStretchedHideSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumStretchedHideEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new MediumStretchedHideEastDeed(); } } + + [Constructable] + public MediumStretchedHideEastAddon() + { + AddComponent( new AddonComponent( 0x106B ), 0, 0, 0 ); + } + + public MediumStretchedHideEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumStretchedHideEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new MediumStretchedHideEastAddon(); } } + public override int LabelNumber{ get{ return 1049403; } } // a medium stretched hide deed facing east + + [Constructable] + public MediumStretchedHideEastDeed() + { + } + + public MediumStretchedHideEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumStretchedHideSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new MediumStretchedHideSouthDeed(); } } + + [Constructable] + public MediumStretchedHideSouthAddon() + { + AddComponent( new AddonComponent( 0x107C ), 0, 0, 0 ); + } + + public MediumStretchedHideSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumStretchedHideSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new MediumStretchedHideSouthAddon(); } } + public override int LabelNumber{ get{ return 1049404; } } // a medium stretched hide deed facing south + + [Constructable] + public MediumStretchedHideSouthDeed() + { + } + + public MediumStretchedHideSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/TallElvenBedEastAddon.cs b/Scripts/Items/Addons/TallElvenBedEastAddon.cs new file mode 100644 index 000000000..48fb16680 --- /dev/null +++ b/Scripts/Items/Addons/TallElvenBedEastAddon.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TallElvenBedEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new TallElvenBedEastDeed(); } } + + [Constructable] + public TallElvenBedEastAddon() + { + AddComponent( new AddonComponent( 0x3054 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x3053 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x3055 ), 2, -1, 0 ); + AddComponent( new AddonComponent( 0x3052 ), 2, 0, 0 ); + } + + public TallElvenBedEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TallElvenBedEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new TallElvenBedEastAddon(); } } + public override int LabelNumber{ get{ return 1072859; } } // tall elven bed (east) + + [Constructable] + public TallElvenBedEastDeed() + { + } + + public TallElvenBedEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/TallElvenBedSouthAddon.cs b/Scripts/Items/Addons/TallElvenBedSouthAddon.cs new file mode 100644 index 000000000..c981f4d09 --- /dev/null +++ b/Scripts/Items/Addons/TallElvenBedSouthAddon.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TallElvenBedSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new TallElvenBedSouthDeed(); } } + + [Constructable] + public TallElvenBedSouthAddon() + { + AddComponent( new AddonComponent( 0x3058 ), 0, 0, 0 ); // angolo alto sx + AddComponent( new AddonComponent( 0x3057 ), -1, 1, 0 ); // angolo basso sx + AddComponent( new AddonComponent( 0x3059 ), 0, -1, 0 ); // angolo alto dx + AddComponent( new AddonComponent( 0x3056 ), 0, 1, 0 ); // angolo basso dx + } + + public TallElvenBedSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class TallElvenBedSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new TallElvenBedSouthAddon(); } } + public override int LabelNumber{ get{ return 1072858; } } // tall elven bed (south) + + [Constructable] + public TallElvenBedSouthDeed() + { + } + + public TallElvenBedSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/Telescope.cs b/Scripts/Items/Addons/Telescope.cs new file mode 100644 index 000000000..cb592afd1 --- /dev/null +++ b/Scripts/Items/Addons/Telescope.cs @@ -0,0 +1,106 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Telescope : BaseAddon + { + [Constructable] + public Telescope() + { + AddComponent( new AddonComponent( 0x1494 ), 0, 5, 0 ); + AddComponent( new AddonComponent( 0x145B ), 0, 6, 0 ); + AddComponent( new AddonComponent( 0x145A ), 0, 7, 0 ); + + AddComponent( new AddonComponent( 0x1495 ), 1, 4, 0 ); + AddComponent( new AddonComponent( 0x145C ), 1, 7, 0 ); + AddComponent( new AddonComponent( 0x145D ), 1, 8, 0 ); + + AddComponent( new AddonComponent( 0x1496 ), 2, 3, 0 ); + AddComponent( new AddonComponent( 0x1499 ), 2, 4, 0 ); + AddComponent( new AddonComponent( 0x148E ), 2, 6, 0 ); + AddComponent( new AddonComponent( 0x1493 ), 2, 7, 0 ); + AddComponent( new AddonComponent( 0x1492 ), 2, 8, 0 ); + AddComponent( new AddonComponent( 0x145E ), 2, 9, 0 ); + AddComponent( new AddonComponent( 0x1459 ), 2,10, 0 ); + + AddComponent( new AddonComponent( 0x1497 ), 3, 2, 0 ); + AddComponent( new AddonComponent( 0x145F ), 3, 9, 0 ); + AddComponent( new AddonComponent( 0x1461 ), 3,10, 0 ); + + AddComponent( new AddonComponent( 0x149A ), 4, 1, 0 ); + AddComponent( new AddonComponent( 0x1498 ), 4, 2, 0 ); + AddComponent( new AddonComponent( 0x148F ), 4, 4, 0 ); + AddComponent( new AddonComponent( 0x148D ), 4, 6, 0 ); + AddComponent( new AddonComponent( 0x1488 ), 4, 8, 0 ); + AddComponent( new AddonComponent( 0x1460 ), 4, 9, 0 ); + AddComponent( new AddonComponent( 0x1462 ), 4,10, 0 ); + + AddComponent( new AddonComponent( 0x147D ), 5, 0, 0 ); + AddComponent( new AddonComponent( 0x1490 ), 5, 4, 0 ); + AddComponent( new AddonComponent( 0x148B ), 5, 5, 0 ); + AddComponent( new AddonComponent( 0x148A ), 5, 6, 0 ); + AddComponent( new AddonComponent( 0x1486 ), 5, 7, 0 ); + AddComponent( new AddonComponent( 0x1485 ), 5, 8, 0 ); + + AddComponent( new AddonComponent( 0x147C ), 6, 0, 0 ); + AddComponent( new AddonComponent( 0x1491 ), 6, 4, 0 ); + AddComponent( new AddonComponent( 0x148C ), 6, 5, 0 ); + AddComponent( new AddonComponent( 0x1489 ), 6, 6, 0 ); + AddComponent( new AddonComponent( 0x1487 ), 6, 7, 0 ); + AddComponent( new AddonComponent( 0x1484 ), 6, 8, 0 ); + AddComponent( new AddonComponent( 0x1463 ), 6,10, 0 ); + + AddComponent( new AddonComponent( 0x147B ), 7, 0, 0 ); + AddComponent( new AddonComponent( 0x147F ), 7, 3, 0 ); + AddComponent( new AddonComponent( 0x1480 ), 7, 4, 0 ); + AddComponent( new AddonComponent( 0x1482 ), 7, 5, 0 ); + AddComponent( new AddonComponent( 0x1469 ), 7, 6, 0 ); + AddComponent( new AddonComponent( 0x1468 ), 7, 7, 0 ); + AddComponent( new AddonComponent( 0x1465 ), 7, 8, 0 ); + AddComponent( new AddonComponent( 0x1464 ), 7, 9, 0 ); + + AddComponent( new AddonComponent( 0x147A ), 8, 0, 0 ); + AddComponent( new AddonComponent( 0x1479 ), 8, 1, 0 ); + AddComponent( new AddonComponent( 0x1477 ), 8, 2, 0 ); + AddComponent( new AddonComponent( 0x147E ), 8, 3, 0 ); + AddComponent( new AddonComponent( 0x1481 ), 8, 4, 0 ); + AddComponent( new AddonComponent( 0x1483 ), 8, 5, 0 ); + AddComponent( new AddonComponent( 0x146A ), 8, 6, 0 ); + AddComponent( new AddonComponent( 0x1467 ), 8, 7, 0 ); + AddComponent( new AddonComponent( 0x1466 ), 8, 8, 0 ); + + AddComponent( new AddonComponent( 0x1478 ), 9, 1, 0 ); + AddComponent( new AddonComponent( 0x1475 ), 9, 2, 0 ); + AddComponent( new AddonComponent( 0x1474 ), 9, 3, 0 ); + AddComponent( new AddonComponent( 0x146F ), 9, 4, 0 ); + AddComponent( new AddonComponent( 0x146E ), 9, 5, 0 ); + AddComponent( new AddonComponent( 0x146D ), 9, 6, 0 ); + AddComponent( new AddonComponent( 0x146B ), 9, 7, 0 ); + + AddComponent( new AddonComponent( 0x1476 ),10, 2, 0 ); + AddComponent( new AddonComponent( 0x1473 ),10, 3, 0 ); + AddComponent( new AddonComponent( 0x1470 ),10, 4, 0 ); + AddComponent( new AddonComponent( 0x1471 ),10, 5, 0 ); + AddComponent( new AddonComponent( 0x1472 ),10, 6, 0 ); + } + + public Telescope( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/TrainingDummies.cs b/Scripts/Items/Addons/TrainingDummies.cs new file mode 100644 index 000000000..d77feb36b --- /dev/null +++ b/Scripts/Items/Addons/TrainingDummies.cs @@ -0,0 +1,284 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1070, 0x1074 )] + public class TrainingDummy : AddonComponent + { + private double m_MinSkill; + private double m_MaxSkill; + + private Timer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public double MinSkill + { + get{ return m_MinSkill; } + set{ m_MinSkill = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double MaxSkill + { + get{ return m_MaxSkill; } + set{ m_MaxSkill = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Swinging + { + get{ return ( m_Timer != null ); } + } + + [Constructable] + public TrainingDummy() : this( 0x1074 ) + { + } + + [Constructable] + public TrainingDummy( int itemID ) : base( itemID ) + { + m_MinSkill = -25.0; + m_MaxSkill = +25.0; + } + + public void UpdateItemID() + { + int baseItemID = (ItemID / 2) * 2; + + ItemID = baseItemID + (Swinging ? 1 : 0); + } + + public void BeginSwing() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = new InternalTimer( this ); + m_Timer.Start(); + } + + public void EndSwing() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + UpdateItemID(); + } + + public void OnHit() + { + UpdateItemID(); + Effects.PlaySound( GetWorldLocation(), Map, Utility.RandomList( 0x3A4, 0x3A6, 0x3A9, 0x3AE, 0x3B4, 0x3B6 ) ); + } + + public void Use( Mobile from, BaseWeapon weapon ) + { + BeginSwing(); + + from.Direction = from.GetDirectionTo( GetWorldLocation() ); + weapon.PlaySwingAnimation( from ); + + from.CheckSkill( weapon.Skill, m_MinSkill, m_MaxSkill ); + } + + public override void OnDoubleClick( Mobile from ) + { + BaseWeapon weapon = from.Weapon as BaseWeapon; + + if ( weapon is BaseRanged ) + SendLocalizedMessageTo( from, 501822 ); // You can't practice ranged weapons on this. + else if ( weapon == null || !from.InRange( GetWorldLocation(), weapon.MaxRange ) ) + SendLocalizedMessageTo( from, 501816 ); // You are too far away to do that. + else if ( Swinging ) + SendLocalizedMessageTo( from, 501815 ); // You have to wait until it stops swinging. + else if ( from.Skills[weapon.Skill].Base >= m_MaxSkill ) + SendLocalizedMessageTo( from, 501828 ); // Your skill cannot improve any further by simply practicing with a dummy. + else if ( from.Mounted ) + SendLocalizedMessageTo( from, 501829 ); // You can't practice on this while on a mount. + else + Use( from, weapon ); + } + + public TrainingDummy( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_MinSkill ); + writer.Write( m_MaxSkill ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_MinSkill = reader.ReadDouble(); + m_MaxSkill = reader.ReadDouble(); + + if ( m_MinSkill == 0.0 && m_MaxSkill == 30.0 ) + { + m_MinSkill = -25.0; + m_MaxSkill = +25.0; + } + + break; + } + } + + UpdateItemID(); + } + + private class InternalTimer : Timer + { + private TrainingDummy m_Dummy; + private bool m_Delay = true; + + public InternalTimer( TrainingDummy dummy ) : base( TimeSpan.FromSeconds( 0.25 ), TimeSpan.FromSeconds( 2.75 ) ) + { + m_Dummy = dummy; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Delay ) + m_Dummy.OnHit(); + else + m_Dummy.EndSwing(); + + m_Delay = !m_Delay; + } + } + } + + public class TrainingDummyEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new TrainingDummyEastDeed(); } } + + [Constructable] + public TrainingDummyEastAddon() + { + AddComponent( new TrainingDummy( 0x1074 ), 0, 0, 0 ); + } + + public TrainingDummyEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TrainingDummyEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new TrainingDummyEastAddon(); } } + public override int LabelNumber{ get{ return 1044335; } } // training dummy (east) + + [Constructable] + public TrainingDummyEastDeed() + { + } + + public TrainingDummyEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TrainingDummySouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new TrainingDummySouthDeed(); } } + + [Constructable] + public TrainingDummySouthAddon() + { + AddComponent( new TrainingDummy( 0x1070 ), 0, 0, 0 ); + } + + public TrainingDummySouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TrainingDummySouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new TrainingDummySouthAddon(); } } + public override int LabelNumber{ get{ return 1044336; } } // training dummy (south) + + [Constructable] + public TrainingDummySouthDeed() + { + } + + public TrainingDummySouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/WarriorStatueEastAddon.cs b/Scripts/Items/Addons/WarriorStatueEastAddon.cs new file mode 100644 index 000000000..8e87608b6 --- /dev/null +++ b/Scripts/Items/Addons/WarriorStatueEastAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WarriorStatueEastAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new WarriorStatueEastDeed(); } } + + [Constructable] + public WarriorStatueEastAddon() + { + AddComponent( new AddonComponent( 0x2D12 ), 0, 0, 0 ); + } + + public WarriorStatueEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class WarriorStatueEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new WarriorStatueEastAddon(); } } + public override int LabelNumber{ get{ return 1072888; } } // warrior statue (east) + + [Constructable] + public WarriorStatueEastDeed() + { + } + + public WarriorStatueEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/WarriorStatueSouthAddon.cs b/Scripts/Items/Addons/WarriorStatueSouthAddon.cs new file mode 100644 index 000000000..0369aef53 --- /dev/null +++ b/Scripts/Items/Addons/WarriorStatueSouthAddon.cs @@ -0,0 +1,63 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WarriorStatueSouthAddon : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new WarriorStatueSouthDeed(); } } + + [Constructable] + public WarriorStatueSouthAddon() + { + AddComponent( new AddonComponent( 0x2D13 ), 0, 0, 0 ); + } + + public WarriorStatueSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class WarriorStatueSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new WarriorStatueSouthAddon(); } } + public override int LabelNumber{ get{ return 1072887; } } // warrior statue (south) + + [Constructable] + public WarriorStatueSouthDeed() + { + } + + public WarriorStatueSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/WaterTroughEastAddon.cs b/Scripts/Items/Addons/WaterTroughEastAddon.cs new file mode 100644 index 000000000..2c317df6a --- /dev/null +++ b/Scripts/Items/Addons/WaterTroughEastAddon.cs @@ -0,0 +1,70 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WaterTroughEastAddon : BaseAddon, IWaterSource + { + public override BaseAddonDeed Deed{ get{ return new WaterTroughEastDeed(); } } + + [Constructable] + public WaterTroughEastAddon() + { + AddComponent( new AddonComponent( 0xB41 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xB42 ), 0, 1, 0 ); + } + + public WaterTroughEastAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public int Quantity + { + get{ return 500; } + set{} + } + } + + public class WaterTroughEastDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new WaterTroughEastAddon(); } } + public override int LabelNumber{ get{ return 1044349; } } // water trough (east) + + [Constructable] + public WaterTroughEastDeed() + { + } + + public WaterTroughEastDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/WaterTroughSouthAddon.cs b/Scripts/Items/Addons/WaterTroughSouthAddon.cs new file mode 100644 index 000000000..ffe6577b7 --- /dev/null +++ b/Scripts/Items/Addons/WaterTroughSouthAddon.cs @@ -0,0 +1,70 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WaterTroughSouthAddon : BaseAddon, IWaterSource + { + public override BaseAddonDeed Deed{ get{ return new WaterTroughSouthDeed(); } } + + [Constructable] + public WaterTroughSouthAddon() + { + AddComponent( new AddonComponent( 0xB43 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0xB44 ), 1, 0, 0 ); + } + + public WaterTroughSouthAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public int Quantity + { + get{ return 500; } + set{} + } + } + + public class WaterTroughSouthDeed : BaseAddonDeed + { + public override BaseAddon Addon{ get{ return new WaterTroughSouthAddon(); } } + public override int LabelNumber{ get{ return 1044350; } } // water trough (south) + + [Constructable] + public WaterTroughSouthDeed() + { + } + + public WaterTroughSouthDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Addons/WaterVat.cs b/Scripts/Items/Addons/WaterVat.cs new file mode 100644 index 000000000..5ae73fc0f --- /dev/null +++ b/Scripts/Items/Addons/WaterVat.cs @@ -0,0 +1,87 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WaterVatEast : BaseAddon + { + [Constructable] + public WaterVatEast() + { + AddComponent( new AddonComponent( 0x1558 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x14DE ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0x1552 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x14DF ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0x1554 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1559 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1550 ), 1, 3, 0 ); + AddComponent( new AddonComponent( 0x1555 ), 3, 1, 0 ); + AddComponent( new AddonComponent( 0x14D7 ), 2, 2, 0 ); + + // Blockers + AddComponent( new AddonComponent( 0x21A4 ), 2, -1, 0 ); + AddComponent( new AddonComponent( 0x21A4 ), 3, 0, 0 ); + AddComponent( new AddonComponent( 0x21A4 ), -1, 2, 0 ); + AddComponent( new AddonComponent( 0x21A4 ), 0, 3, 0 ); + } + + public WaterVatEast( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class WaterVatSouth : BaseAddon + { + [Constructable] + public WaterVatSouth() + { + AddComponent( new AddonComponent( 0x1558 ), 0, 0, 0 ); + AddComponent( new AddonComponent( 0x14DE ), -1, 1, 0 ); + AddComponent( new AddonComponent( 0x1552 ), 0, 1, 0 ); + AddComponent( new AddonComponent( 0x14DF ), 1, -1, 0 ); + AddComponent( new AddonComponent( 0x1554 ), 1, 0, 0 ); + AddComponent( new AddonComponent( 0x1559 ), 1, 1, 0 ); + AddComponent( new AddonComponent( 0x1551 ), 1, 3, 0 ); + AddComponent( new AddonComponent( 0x1556 ), 3, 1, 0 ); + AddComponent( new AddonComponent( 0x14D7 ), 2, 2, 0 ); + + // Blockers + AddComponent( new AddonComponent( 0x21A4 ), 2, -1, 0 ); + AddComponent( new AddonComponent( 0x21A4 ), 3, 0, 0 ); + AddComponent( new AddonComponent( 0x21A4 ), -1, 2, 0 ); + AddComponent( new AddonComponent( 0x21A4 ), 0, 3, 0 ); + } + + public WaterVatSouth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/ArmorEnums.cs b/Scripts/Items/Armor/ArmorEnums.cs new file mode 100644 index 000000000..6658979a7 --- /dev/null +++ b/Scripts/Items/Armor/ArmorEnums.cs @@ -0,0 +1,64 @@ +using System; + +namespace Server.Items +{ + public enum ArmorQuality + { + Low, + Regular, + Exceptional + } + + public enum ArmorDurabilityLevel + { + Regular, + Durable, + Substantial, + Massive, + Fortified, + Indestructible + } + + public enum ArmorProtectionLevel + { + Regular, + Defense, + Guarding, + Hardening, + Fortification, + Invulnerability, + } + + public enum ArmorBodyType + { + Gorget, + Gloves, + Helmet, + Arms, + Legs, + Chest, + Shield + } + + public enum ArmorMaterialType + { + Cloth, + Leather, + Studded, + Bone, + Spined, + Horned, + Barbed, + Ringmail, + Chainmail, + Plate, + Dragon // On OSI, Dragon is seen and considered its own type. + } + + public enum ArmorMeditationAllowance + { + All, + Half, + None + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/ArmorOfFortune.cs b/Scripts/Items/Armor/Artifacts/ArmorOfFortune.cs new file mode 100644 index 000000000..d96d7eb0a --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/ArmorOfFortune.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArmorOfFortune : StuddedChest + { + public override int LabelNumber{ get{ return 1061098; } } // Armor of Fortune + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ArmorOfFortune() + { + Hue = 0x501; + Attributes.Luck = 200; + Attributes.DefendChance = 15; + Attributes.LowerRegCost = 40; + ArmorAttributes.MageArmor = 1; + } + + public ArmorOfFortune( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/Craftable/BrambleCoat.cs b/Scripts/Items/Armor/Artifacts/Craftable/BrambleCoat.cs new file mode 100644 index 000000000..efab368e8 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/Craftable/BrambleCoat.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class BrambleCoat : WoodlandChest + { + public override int LabelNumber{ get{ return 1072925; } } // Bramble Coat + + public override int BasePhysicalResistance{ get{ return 10; } } + public override int BaseFireResistance{ get{ return 8; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 8; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + [Constructable] + public BrambleCoat() + { + Hue = 0x1; + + ArmorAttributes.SelfRepair = 3; + Attributes.BonusHits = 4; + Attributes.Luck = 150; + Attributes.ReflectPhysical = 25; + Attributes.DefendChance = 15; + } + + public BrambleCoat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs b/Scripts/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs new file mode 100644 index 000000000..1a4450dbc --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/Craftable/IronwoodCrown.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class IronwoodCrown : RavenHelm + { + public override int LabelNumber{ get{ return 1072924; } } // Ironwood Crown + + public override int BasePhysicalResistance{ get{ return 10; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 7; } } + public override int BaseEnergyResistance{ get{ return 10; } } + + [Constructable] + public IronwoodCrown() + { + Hue = 0x1; + + ArmorAttributes.SelfRepair = 3; + + Attributes.BonusStr = 5; + Attributes.BonusDex = 5; + Attributes.BonusInt = 5; + } + + public IronwoodCrown( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs b/Scripts/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs new file mode 100644 index 000000000..cf715ed80 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/Craftable/SongWovenMantle.cs @@ -0,0 +1,43 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class SongWovenMantle : LeafArms + { + public override int LabelNumber{ get{ return 1072931; } } // Song Woven Mantle + + public override int BasePhysicalResistance{ get{ return 14; } } + public override int BaseColdResistance{ get{ return 14; } } + public override int BaseEnergyResistance{ get{ return 16; } } + + [Constructable] + public SongWovenMantle() + { + Hue = 0x493; + + SkillBonuses.SetValues( 0, SkillName.Musicianship, 10.0 ); + + Attributes.Luck = 100; + Attributes.DefendChance = 5; + } + + public SongWovenMantle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs b/Scripts/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs new file mode 100644 index 000000000..490ecc114 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/Craftable/SpellWovenBritches.cs @@ -0,0 +1,43 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class SpellWovenBritches : LeafLegs + { + public override int LabelNumber{ get{ return 1072929; } } // Spell Woven Britches + + public override int BaseFireResistance{ get{ return 15; } } + public override int BasePoisonResistance{ get{ return 16; } } + + [Constructable] + public SpellWovenBritches() + { + Hue = 0x487; + + SkillBonuses.SetValues( 0, SkillName.Meditation, 10.0 ); + + Attributes.BonusInt = 8; + Attributes.SpellDamage = 10; + Attributes.LowerManaCost = 10; + } + + public SpellWovenBritches( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/Craftable/StitchersMittens.cs b/Scripts/Items/Armor/Artifacts/Craftable/StitchersMittens.cs new file mode 100644 index 000000000..8862ff805 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/Craftable/StitchersMittens.cs @@ -0,0 +1,43 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StitchersMittens : LeafGloves + { + public override int LabelNumber{ get{ return 1072932; } } // Stitcher's Mittens + + public override int BasePhysicalResistance{ get{ return 20; } } + public override int BaseColdResistance{ get{ return 20; } } + + [Constructable] + public StitchersMittens() + { + Hue = 0x481; + + SkillBonuses.SetValues( 0, SkillName.Healing, 10.0 ); + + Attributes.BonusDex = 5; + Attributes.LowerRegCost = 30; + } + + public StitchersMittens( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/GauntletsOfNobility.cs b/Scripts/Items/Armor/Artifacts/GauntletsOfNobility.cs new file mode 100644 index 000000000..4b67431e4 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/GauntletsOfNobility.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GauntletsOfNobility : RingmailGloves + { + public override int LabelNumber{ get{ return 1061092; } } // Gauntlets of Nobility + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 18; } } + public override int BasePoisonResistance{ get{ return 20; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public GauntletsOfNobility() + { + Hue = 0x4FE; + Attributes.BonusStr = 8; + Attributes.Luck = 100; + Attributes.WeaponDamage = 20; + } + + public GauntletsOfNobility( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( Hue == 0x562 ) + Hue = 0x4FE; + + PhysicalBonus = 0; + PoisonBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/HelmOfInsight.cs b/Scripts/Items/Armor/Artifacts/HelmOfInsight.cs new file mode 100644 index 000000000..51073d60d --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/HelmOfInsight.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HelmOfInsight : PlateHelm + { + public override int LabelNumber{ get{ return 1061096; } } // Helm of Insight + public override int ArtifactRarity{ get{ return 11; } } + + public override int BaseEnergyResistance{ get{ return 17; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public HelmOfInsight() + { + Hue = 0x554; + Attributes.BonusInt = 8; + Attributes.BonusMana = 15; + Attributes.RegenMana = 2; + Attributes.LowerManaCost = 8; + } + + public HelmOfInsight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + EnergyBonus = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/HolyKnightsBreastplate.cs b/Scripts/Items/Armor/Artifacts/HolyKnightsBreastplate.cs new file mode 100644 index 000000000..e0c358217 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/HolyKnightsBreastplate.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HolyKnightsBreastplate : PlateChest + { + public override int LabelNumber{ get{ return 1061097; } } // Holy Knight's Breastplate + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 35; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public HolyKnightsBreastplate() + { + Hue = 0x47E; + Attributes.BonusHits = 10; + Attributes.ReflectPhysical = 15; + } + + public HolyKnightsBreastplate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + PhysicalBonus = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/InquisitorsResolution.cs b/Scripts/Items/Armor/Artifacts/InquisitorsResolution.cs new file mode 100644 index 000000000..5ed710ca5 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/InquisitorsResolution.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class InquisitorsResolution : PlateGloves + { + public override int LabelNumber{ get{ return 1060206; } } // The Inquisitor's Resolution + public override int ArtifactRarity{ get{ return 10; } } + + public override int BaseColdResistance{ get{ return 22; } } + public override int BaseEnergyResistance{ get{ return 17; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public InquisitorsResolution() + { + Hue = 0x4F2; + Attributes.CastRecovery = 3; + Attributes.LowerManaCost = 8; + ArmorAttributes.MageArmor = 1; + } + + public InquisitorsResolution( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + ColdBonus = 0; + EnergyBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/JackalsCollar.cs b/Scripts/Items/Armor/Artifacts/JackalsCollar.cs new file mode 100644 index 000000000..ff8ef1c18 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/JackalsCollar.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Items +{ + public class JackalsCollar : PlateGorget + { + public override int LabelNumber{ get{ return 1061594; } } // Jackal's Collar + public override int ArtifactRarity{ get{ return 11; } } + + public override int BaseFireResistance{ get{ return 23; } } + public override int BaseColdResistance{ get{ return 17; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public JackalsCollar() + { + Hue = 0x6D1; + Attributes.BonusDex = 15; + Attributes.RegenHits = 2; + } + + public JackalsCollar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( Hue == 0x54B ) + Hue = 0x6D1; + + FireBonus = 0; + ColdBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/LeggingsOfBane.cs b/Scripts/Items/Armor/Artifacts/LeggingsOfBane.cs new file mode 100644 index 000000000..f4b0d3f5e --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/LeggingsOfBane.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LeggingsOfBane : ChainLegs + { + public override int LabelNumber{ get{ return 1061100; } } // Leggings of Bane + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePoisonResistance{ get{ return 36; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public LeggingsOfBane() + { + Hue = 0x4F5; + ArmorAttributes.DurabilityBonus = 100; + Attributes.BonusStam = 8; + Attributes.AttackChance = 20; + } + + public LeggingsOfBane( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( Hue == 0x559 ) + Hue = 0x4F5; + + if ( ArmorAttributes.DurabilityBonus == 0 ) + ArmorAttributes.DurabilityBonus = 100; + + PoisonBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/MidnightBracers.cs b/Scripts/Items/Armor/Artifacts/MidnightBracers.cs new file mode 100644 index 000000000..dcdecd8fe --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/MidnightBracers.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class MidnightBracers : BoneArms + { + public override int LabelNumber{ get{ return 1061093; } } // Midnight Bracers + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 23; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public MidnightBracers() + { + Hue = 0x455; + SkillBonuses.SetValues( 0, SkillName.Necromancy, 20.0 ); + Attributes.SpellDamage = 10; + ArmorAttributes.MageArmor = 1; + } + + public MidnightBracers( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + PhysicalBonus = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs b/Scripts/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs new file mode 100644 index 000000000..cbc1e3de4 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/OrnateCrownOfTheHarrower.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + public class OrnateCrownOfTheHarrower : BoneHelm + { + public override int LabelNumber{ get{ return 1061095; } } // Ornate Crown of the Harrower + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePoisonResistance{ get{ return 17; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public OrnateCrownOfTheHarrower() + { + Hue = 0x4F6; + Attributes.RegenHits = 2; + Attributes.RegenStam = 3; + Attributes.WeaponDamage = 25; + } + + public OrnateCrownOfTheHarrower( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( Hue == 0x55A ) + Hue = 0x4F6; + + PoisonBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/ShadowDancerLeggings.cs b/Scripts/Items/Armor/Artifacts/ShadowDancerLeggings.cs new file mode 100644 index 000000000..05dbeeafd --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/ShadowDancerLeggings.cs @@ -0,0 +1,55 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ShadowDancerLeggings : LeatherLegs + { + public override int LabelNumber{ get{ return 1061598; } } // Shadow Dancer Leggings + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 17; } } + public override int BasePoisonResistance{ get{ return 18; } } + public override int BaseEnergyResistance{ get{ return 18; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ShadowDancerLeggings() + { + ItemID = 0x13D2; + Hue = 0x455; + SkillBonuses.SetValues( 0, SkillName.Stealth, 20.0 ); + SkillBonuses.SetValues( 1, SkillName.Stealing, 20.0 ); + } + + public ShadowDancerLeggings( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( ItemID == 0x13CB ) + ItemID = 0x13D2; + + PhysicalBonus = 0; + PoisonBonus = 0; + EnergyBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/TunicOfFire.cs b/Scripts/Items/Armor/Artifacts/TunicOfFire.cs new file mode 100644 index 000000000..b74167a04 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/TunicOfFire.cs @@ -0,0 +1,56 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TunicOfFire : ChainChest + { + public override int LabelNumber{ get{ return 1061099; } } // Tunic of Fire + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 24; } } + public override int BaseFireResistance{ get{ return 34; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TunicOfFire() + { + Hue = 0x54F; + ArmorAttributes.SelfRepair = 5; + Attributes.NightSight = 1; + Attributes.ReflectPhysical = 15; + } + + public TunicOfFire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( Hue == 0x54E ) + Hue = 0x54F; + + if ( Attributes.NightSight == 0 ) + Attributes.NightSight = 1; + + PhysicalBonus = 0; + FireBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs b/Scripts/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs new file mode 100644 index 000000000..bae6413f2 --- /dev/null +++ b/Scripts/Items/Armor/Artifacts/VoiceOfTheFallenKing.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + public class VoiceOfTheFallenKing : LeatherGorget + { + public override int LabelNumber{ get{ return 1061094; } } // Voice of the Fallen King + public override int ArtifactRarity{ get{ return 11; } } + + public override int BaseColdResistance{ get{ return 18; } } + public override int BaseEnergyResistance{ get{ return 18; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public VoiceOfTheFallenKing() + { + Hue = 0x76D; + Attributes.BonusStr = 8; + Attributes.RegenHits = 5; + Attributes.RegenStam = 3; + } + + public VoiceOfTheFallenKing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( Hue == 0x551 ) + Hue = 0x76D; + + ColdBonus = 0; + EnergyBonus = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/BaseArmor.cs b/Scripts/Items/Armor/BaseArmor.cs new file mode 100644 index 000000000..b92758a19 --- /dev/null +++ b/Scripts/Items/Armor/BaseArmor.cs @@ -0,0 +1,1606 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Engines.Craft; +using Server.Factions; +using AMA = Server.Items.ArmorMeditationAllowance; +using AMT = Server.Items.ArmorMaterialType; +using ABT = Server.Items.ArmorBodyType; + +namespace Server.Items +{ + public abstract class BaseArmor : Item, IScissorable, IFactionItem, ICraftable, IWearableDurability + { + #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 + + + + /* Armor internals work differently now (Jun 19 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: + * - ArmorBase + * - StrBonus + * - DexBonus + * - IntBonus + * - StrReq + * - DexReq + * - IntReq + * - MeditationAllowance + */ + + // Instance values. These values must are unique to each armor piece. + private int m_MaxHitPoints; + private int m_HitPoints; + private Mobile m_Crafter; + private ArmorQuality m_Quality; + private ArmorDurabilityLevel m_Durability; + private ArmorProtectionLevel m_Protection; + private CraftResource m_Resource; + private bool m_Identified, m_PlayerConstructed; + private int m_PhysicalBonus, m_FireBonus, m_ColdBonus, m_PoisonBonus, m_EnergyBonus; + + private AosAttributes m_AosAttributes; + private AosArmorAttributes m_AosArmorAttributes; + private AosSkillBonuses m_AosSkillBonuses; + + // Overridable values. These values are provided to override the defaults which get defined in the individual armor scripts. + private int m_ArmorBase = -1; + private int m_StrBonus = -1, m_DexBonus = -1, m_IntBonus = -1; + private int m_StrReq = -1, m_DexReq = -1, m_IntReq = -1; + private AMA m_Meditate = (AMA)(-1); + + + public virtual bool AllowMaleWearer{ get{ return true; } } + public virtual bool AllowFemaleWearer{ get{ return true; } } + + public abstract AMT MaterialType{ get; } + + public virtual int RevertArmorBase{ get{ return ArmorBase; } } + public virtual int ArmorBase{ get{ return 0; } } + + public virtual AMA DefMedAllowance{ get{ return AMA.None; } } + public virtual AMA AosMedAllowance{ get{ return DefMedAllowance; } } + public virtual AMA OldMedAllowance{ get{ return DefMedAllowance; } } + + + public virtual int AosStrBonus{ get{ return 0; } } + public virtual int AosDexBonus{ get{ return 0; } } + public virtual int AosIntBonus{ get{ return 0; } } + public virtual int AosStrReq{ get{ return 0; } } + public virtual int AosDexReq{ get{ return 0; } } + public virtual int AosIntReq{ get{ return 0; } } + + + public virtual int OldStrBonus{ get{ return 0; } } + public virtual int OldDexBonus{ get{ return 0; } } + public virtual int OldIntBonus{ get{ return 0; } } + public virtual int OldStrReq{ get{ return 0; } } + public virtual int OldDexReq{ get{ return 0; } } + public virtual int OldIntReq{ get{ return 0; } } + + [CommandProperty( AccessLevel.GameMaster )] + public AMA MeditationAllowance + { + get{ return ( m_Meditate == (AMA)(-1) ? Core.AOS ? AosMedAllowance : OldMedAllowance : m_Meditate ); } + set{ m_Meditate = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int BaseArmorRating + { + get + { + if ( m_ArmorBase == -1 ) + return ArmorBase; + else + return m_ArmorBase; + } + set + { + m_ArmorBase = value; Invalidate(); + } + } + + public double BaseArmorRatingScaled + { + get + { + return ( BaseArmorRating * ArmorScalar ); + } + } + + public virtual double ArmorRating + { + get + { + int ar = BaseArmorRating; + + if ( m_Protection != ArmorProtectionLevel.Regular ) + ar += 10 + (5 * (int)m_Protection); + + switch ( m_Resource ) + { + case CraftResource.DullCopper: ar += 2; break; + case CraftResource.ShadowIron: ar += 4; break; + case CraftResource.Copper: ar += 6; break; + case CraftResource.Bronze: ar += 8; break; + case CraftResource.Gold: ar += 10; break; + case CraftResource.Agapite: ar += 12; break; + case CraftResource.Verite: ar += 14; break; + case CraftResource.Valorite: ar += 16; break; + case CraftResource.SpinedLeather: ar += 10; break; + case CraftResource.HornedLeather: ar += 13; break; + case CraftResource.BarbedLeather: ar += 16; break; + } + + ar += -8 + (8 * (int)m_Quality); + return ScaleArmorByDurability( ar ); + } + } + + public double ArmorRatingScaled + { + get + { + return ( ArmorRating * ArmorScalar ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int StrBonus + { + get{ return ( m_StrBonus == -1 ? Core.AOS ? AosStrBonus : OldStrBonus : m_StrBonus ); } + set{ m_StrBonus = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int DexBonus + { + get{ return ( m_DexBonus == -1 ? Core.AOS ? AosDexBonus : OldDexBonus : m_DexBonus ); } + set{ m_DexBonus = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int IntBonus + { + get{ return ( m_IntBonus == -1 ? Core.AOS ? AosIntBonus : OldIntBonus : m_IntBonus ); } + set{ m_IntBonus = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int StrRequirement + { + get{ return ( m_StrReq == -1 ? Core.AOS ? AosStrReq : OldStrReq : m_StrReq ); } + set{ m_StrReq = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int DexRequirement + { + get{ return ( m_DexReq == -1 ? Core.AOS ? AosDexReq : OldDexReq : m_DexReq ); } + set{ m_DexReq = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int IntRequirement + { + get{ return ( m_IntReq == -1 ? Core.AOS ? AosIntReq : OldIntReq : m_IntReq ); } + set{ m_IntReq = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Identified + { + get{ return m_Identified; } + set{ m_Identified = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool PlayerConstructed + { + get{ return m_PlayerConstructed; } + set{ m_PlayerConstructed = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get + { + return m_Resource; + } + set + { + if ( m_Resource != value ) + { + UnscaleDurability(); + + m_Resource = value; + Hue = CraftResources.GetHue( m_Resource ); + + Invalidate(); + InvalidateProperties(); + + if ( Parent is Mobile ) + ((Mobile)Parent).UpdateResistances(); + + ScaleDurability(); + } + } + } + + public virtual double ArmorScalar + { + get + { + int pos = (int)BodyPosition; + + if ( pos >= 0 && pos < m_ArmorScalars.Length ) + return m_ArmorScalars[pos]; + + return 1.0; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxHitPoints + { + get{ return m_MaxHitPoints; } + set{ m_MaxHitPoints = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitPoints + { + get + { + return m_HitPoints; + } + set + { + if ( value != m_HitPoints && MaxHitPoints > 0 ) + { + m_HitPoints = value; + + if ( m_HitPoints < 0 ) + Delete(); + else if ( m_HitPoints > MaxHitPoints ) + m_HitPoints = MaxHitPoints; + + InvalidateProperties(); + } + } + } + + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + + [CommandProperty( AccessLevel.GameMaster )] + public ArmorQuality Quality + { + get{ return m_Quality; } + set{ UnscaleDurability(); m_Quality = value; Invalidate(); InvalidateProperties(); ScaleDurability(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ArmorDurabilityLevel Durability + { + get{ return m_Durability; } + set{ UnscaleDurability(); m_Durability = value; ScaleDurability(); InvalidateProperties(); } + } + + public virtual int ArtifactRarity + { + get{ return 0; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ArmorProtectionLevel ProtectionLevel + { + get + { + return m_Protection; + } + set + { + if ( m_Protection != value ) + { + m_Protection = value; + + Invalidate(); + InvalidateProperties(); + + if ( Parent is Mobile ) + ((Mobile)Parent).UpdateResistances(); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosAttributes Attributes + { + get{ return m_AosAttributes; } + set{} + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosArmorAttributes ArmorAttributes + { + get{ return m_AosArmorAttributes; } + set{} + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosSkillBonuses SkillBonuses + { + get{ return m_AosSkillBonuses; } + set{} + } + + public int ComputeStatReq( StatType type ) + { + int v; + + if ( type == StatType.Str ) + v = StrRequirement; + else if ( type == StatType.Dex ) + v = DexRequirement; + else + v = IntRequirement; + + return AOS.Scale( v, 100 - GetLowerStatReq() ); + } + + public int ComputeStatBonus( StatType type ) + { + if ( type == StatType.Str ) + return StrBonus + Attributes.BonusStr; + else if ( type == StatType.Dex ) + return DexBonus + Attributes.BonusDex; + else + return IntBonus + Attributes.BonusInt; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int PhysicalBonus{ get{ return m_PhysicalBonus; } set{ m_PhysicalBonus = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int FireBonus{ get{ return m_FireBonus; } set{ m_FireBonus = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ColdBonus{ get{ return m_ColdBonus; } set{ m_ColdBonus = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int PoisonBonus{ get{ return m_PoisonBonus; } set{ m_PoisonBonus = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int EnergyBonus{ get{ return m_EnergyBonus; } set{ m_EnergyBonus = value; InvalidateProperties(); } } + + public virtual int BasePhysicalResistance{ get{ return 0; } } + public virtual int BaseFireResistance{ get{ return 0; } } + public virtual int BaseColdResistance{ get{ return 0; } } + public virtual int BasePoisonResistance{ get{ return 0; } } + public virtual int BaseEnergyResistance{ get{ return 0; } } + + public override int PhysicalResistance{ get{ return BasePhysicalResistance + GetProtOffset() + GetResourceAttrs().ArmorPhysicalResist + m_PhysicalBonus; } } + public override int FireResistance{ get{ return BaseFireResistance + GetProtOffset() + GetResourceAttrs().ArmorFireResist + m_FireBonus; } } + public override int ColdResistance{ get{ return BaseColdResistance + GetProtOffset() + GetResourceAttrs().ArmorColdResist + m_ColdBonus; } } + public override int PoisonResistance{ get{ return BasePoisonResistance + GetProtOffset() + GetResourceAttrs().ArmorPoisonResist + m_PoisonBonus; } } + public override int EnergyResistance{ get{ return BaseEnergyResistance + GetProtOffset() + GetResourceAttrs().ArmorEnergyResist + m_EnergyBonus; } } + + public virtual int InitMinHits{ get{ return 0; } } + public virtual int InitMaxHits{ get{ return 0; } } + + [CommandProperty( AccessLevel.GameMaster )] + public ArmorBodyType BodyPosition + { + get + { + switch ( this.Layer ) + { + default: + case Layer.Neck: return ArmorBodyType.Gorget; + case Layer.TwoHanded: return ArmorBodyType.Shield; + case Layer.Gloves: return ArmorBodyType.Gloves; + case Layer.Helm: return ArmorBodyType.Helmet; + case Layer.Arms: return ArmorBodyType.Arms; + + case Layer.InnerLegs: + case Layer.OuterLegs: + case Layer.Pants: return ArmorBodyType.Legs; + + case Layer.InnerTorso: + case Layer.OuterTorso: + case Layer.Shirt: return ArmorBodyType.Chest; + } + } + } + + public void DistributeBonuses( int amount ) + { + for ( int i = 0; i < amount; ++i ) + { + switch ( Utility.Random( 5 ) ) + { + case 0: ++m_PhysicalBonus; break; + case 1: ++m_FireBonus; break; + case 2: ++m_ColdBonus; break; + case 3: ++m_PoisonBonus; break; + case 4: ++m_EnergyBonus; break; + } + } + + InvalidateProperties(); + } + + public CraftAttributeInfo GetResourceAttrs() + { + CraftResourceInfo info = CraftResources.GetInfo( m_Resource ); + + if ( info == null ) + return CraftAttributeInfo.Blank; + + return info.AttributeInfo; + } + + public int GetProtOffset() + { + switch ( m_Protection ) + { + case ArmorProtectionLevel.Guarding: return 1; + case ArmorProtectionLevel.Hardening: return 2; + case ArmorProtectionLevel.Fortification: return 3; + case ArmorProtectionLevel.Invulnerability: return 4; + } + + return 0; + } + + public void UnscaleDurability() + { + int scale = 100 + GetDurabilityBonus(); + + m_HitPoints = ((m_HitPoints * 100) + (scale - 1)) / scale; + m_MaxHitPoints = ((m_MaxHitPoints * 100) + (scale - 1)) / scale; + InvalidateProperties(); + } + + public void ScaleDurability() + { + int scale = 100 + GetDurabilityBonus(); + + m_HitPoints = ((m_HitPoints * scale) + 99) / 100; + m_MaxHitPoints = ((m_MaxHitPoints * scale) + 99) / 100; + InvalidateProperties(); + } + + public int GetDurabilityBonus() + { + int bonus = 0; + + if ( m_Quality == ArmorQuality.Exceptional ) + bonus += 20; + + switch ( m_Durability ) + { + case ArmorDurabilityLevel.Durable: bonus += 20; break; + case ArmorDurabilityLevel.Substantial: bonus += 50; break; + case ArmorDurabilityLevel.Massive: bonus += 70; break; + case ArmorDurabilityLevel.Fortified: bonus += 100; break; + case ArmorDurabilityLevel.Indestructible: bonus += 120; break; + } + + if ( Core.AOS ) + { + bonus += m_AosArmorAttributes.DurabilityBonus; + + CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); + CraftAttributeInfo attrInfo = null; + + if ( resInfo != null ) + attrInfo = resInfo.AttributeInfo; + + if ( attrInfo != null ) + bonus += attrInfo.ArmorDurability; + } + + return bonus; + } + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 502437 ); // Items you wish to cut must be in your backpack. + return false; + } + + CraftSystem system = DefTailoring.CraftSystem; + + CraftItem item = system.CraftItems.SearchFor( GetType() ); + + if ( item != null && item.Ressources.Count == 1 && item.Ressources.GetAt( 0 ).Amount >= 2 ) + { + try + { + Item res = (Item)Activator.CreateInstance( CraftResources.GetInfo( m_Resource ).ResourceTypes[0] ); + + ScissorHelper( from, res, m_PlayerConstructed ? (item.Ressources.GetAt( 0 ).Amount / 2) : 1 ); + return true; + } + catch + { + } + } + + from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. + return false; + } + + private static double[] m_ArmorScalars = { 0.07, 0.07, 0.14, 0.15, 0.22, 0.35 }; + + public static double[] ArmorScalars + { + get + { + return m_ArmorScalars; + } + set + { + m_ArmorScalars = value; + } + } + + public static void ValidateMobile( Mobile m ) + { + for ( int i = m.Items.Count - 1; i >= 0; --i ) + { + if ( i >= m.Items.Count ) + continue; + + Item item = m.Items[i]; + + if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if( armor.RequiredRace != null && m.Race != armor.RequiredRace ) + { + if( armor.RequiredRace == Race.Elf ) + m.SendLocalizedMessage( 1072203 ); // Only Elves may use this. + else + m.SendMessage( "Only {0} may use this.", armor.RequiredRace.PluralName ); + + m.AddToBackpack( armor ); + } + else if ( !armor.AllowMaleWearer && !m.Female && m.AccessLevel < AccessLevel.GameMaster ) + { + if ( armor.AllowFemaleWearer ) + m.SendLocalizedMessage( 1010388 ); // Only females can wear this. + else + m.SendMessage( "You may not wear this." ); + + m.AddToBackpack( armor ); + } + else if ( !armor.AllowFemaleWearer && m.Female && m.AccessLevel < AccessLevel.GameMaster ) + { + if ( armor.AllowMaleWearer ) + m.SendLocalizedMessage( 1063343 ); // Only males can wear this. + else + m.SendMessage( "You may not wear this." ); + + m.AddToBackpack( armor ); + } + } + } + } + + public int GetLowerStatReq() + { + if ( !Core.AOS ) + return 0; + + int v = m_AosArmorAttributes.LowerStatReq; + + CraftResourceInfo info = CraftResources.GetInfo( m_Resource ); + + if ( info != null ) + { + CraftAttributeInfo attrInfo = info.AttributeInfo; + + if ( attrInfo != null ) + v += attrInfo.ArmorLowerRequirements; + } + + if ( v > 100 ) + v = 100; + + return v; + } + + public override void OnAdded( object parent ) + { + if ( parent is Mobile ) + { + Mobile from = (Mobile)parent; + + if ( Core.AOS ) + m_AosSkillBonuses.AddTo( from ); + + from.Delta( MobileDelta.Armor ); // Tell them armor rating has changed + } + } + + public virtual double ScaleArmorByDurability( double armor ) + { + int scale = 100; + + if ( m_MaxHitPoints > 0 && m_HitPoints < m_MaxHitPoints ) + scale = 50 + ((50 * m_HitPoints) / m_MaxHitPoints); + + return ( armor * scale ) / 100; + } + + protected void Invalidate() + { + if ( Parent is Mobile ) + ((Mobile)Parent).Delta( MobileDelta.Armor ); // Tell them armor rating has changed + } + + public BaseArmor( Serial serial ) : base( serial ) + { + } + + 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 ); + } + + [Flags] + private enum SaveFlag + { + None = 0x00000000, + Attributes = 0x00000001, + ArmorAttributes = 0x00000002, + PhysicalBonus = 0x00000004, + FireBonus = 0x00000008, + ColdBonus = 0x00000010, + PoisonBonus = 0x00000020, + EnergyBonus = 0x00000040, + Identified = 0x00000080, + MaxHitPoints = 0x00000100, + HitPoints = 0x00000200, + Crafter = 0x00000400, + Quality = 0x00000800, + Durability = 0x00001000, + Protection = 0x00002000, + Resource = 0x00004000, + BaseArmor = 0x00008000, + StrBonus = 0x00010000, + DexBonus = 0x00020000, + IntBonus = 0x00040000, + StrReq = 0x00080000, + DexReq = 0x00100000, + IntReq = 0x00200000, + MedAllowance = 0x00400000, + SkillBonuses = 0x00800000, + PlayerConstructed = 0x01000000 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 7 ); // version + + SaveFlag flags = SaveFlag.None; + + SetSaveFlag( ref flags, SaveFlag.Attributes, !m_AosAttributes.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.ArmorAttributes, !m_AosArmorAttributes.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.PhysicalBonus, m_PhysicalBonus != 0 ); + SetSaveFlag( ref flags, SaveFlag.FireBonus, m_FireBonus != 0 ); + SetSaveFlag( ref flags, SaveFlag.ColdBonus, m_ColdBonus != 0 ); + SetSaveFlag( ref flags, SaveFlag.PoisonBonus, m_PoisonBonus != 0 ); + SetSaveFlag( ref flags, SaveFlag.EnergyBonus, m_EnergyBonus != 0 ); + SetSaveFlag( ref flags, SaveFlag.Identified, m_Identified != false ); + SetSaveFlag( ref flags, SaveFlag.MaxHitPoints, m_MaxHitPoints != 0 ); + SetSaveFlag( ref flags, SaveFlag.HitPoints, m_HitPoints != 0 ); + SetSaveFlag( ref flags, SaveFlag.Crafter, m_Crafter != null ); + SetSaveFlag( ref flags, SaveFlag.Quality, m_Quality != ArmorQuality.Regular ); + SetSaveFlag( ref flags, SaveFlag.Durability, m_Durability != ArmorDurabilityLevel.Regular ); + SetSaveFlag( ref flags, SaveFlag.Protection, m_Protection != ArmorProtectionLevel.Regular ); + SetSaveFlag( ref flags, SaveFlag.Resource, m_Resource != DefaultResource ); + SetSaveFlag( ref flags, SaveFlag.BaseArmor, m_ArmorBase != -1 ); + SetSaveFlag( ref flags, SaveFlag.StrBonus, m_StrBonus != -1 ); + SetSaveFlag( ref flags, SaveFlag.DexBonus, m_DexBonus != -1 ); + SetSaveFlag( ref flags, SaveFlag.IntBonus, m_IntBonus != -1 ); + 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.MedAllowance, m_Meditate != (AMA)(-1) ); + SetSaveFlag( ref flags, SaveFlag.SkillBonuses, !m_AosSkillBonuses.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.PlayerConstructed, m_PlayerConstructed != false ); + + writer.WriteEncodedInt( (int) flags ); + + if ( GetSaveFlag( flags, SaveFlag.Attributes ) ) + m_AosAttributes.Serialize( writer ); + + if ( GetSaveFlag( flags, SaveFlag.ArmorAttributes ) ) + m_AosArmorAttributes.Serialize( writer ); + + if ( GetSaveFlag( flags, SaveFlag.PhysicalBonus ) ) + writer.WriteEncodedInt( (int) m_PhysicalBonus ); + + if ( GetSaveFlag( flags, SaveFlag.FireBonus ) ) + writer.WriteEncodedInt( (int) m_FireBonus ); + + if ( GetSaveFlag( flags, SaveFlag.ColdBonus ) ) + writer.WriteEncodedInt( (int) m_ColdBonus ); + + if ( GetSaveFlag( flags, SaveFlag.PoisonBonus ) ) + writer.WriteEncodedInt( (int) m_PoisonBonus ); + + if ( GetSaveFlag( flags, SaveFlag.EnergyBonus ) ) + writer.WriteEncodedInt( (int) m_EnergyBonus ); + + if ( GetSaveFlag( flags, SaveFlag.MaxHitPoints ) ) + writer.WriteEncodedInt( (int) m_MaxHitPoints ); + + if ( GetSaveFlag( flags, SaveFlag.HitPoints ) ) + writer.WriteEncodedInt( (int) m_HitPoints ); + + if ( GetSaveFlag( flags, SaveFlag.Crafter ) ) + writer.Write( (Mobile) m_Crafter ); + + if ( GetSaveFlag( flags, SaveFlag.Quality ) ) + writer.WriteEncodedInt( (int) m_Quality ); + + if ( GetSaveFlag( flags, SaveFlag.Durability ) ) + writer.WriteEncodedInt( (int) m_Durability ); + + if ( GetSaveFlag( flags, SaveFlag.Protection ) ) + writer.WriteEncodedInt( (int) m_Protection ); + + if ( GetSaveFlag( flags, SaveFlag.Resource ) ) + writer.WriteEncodedInt( (int) m_Resource ); + + if ( GetSaveFlag( flags, SaveFlag.BaseArmor ) ) + writer.WriteEncodedInt( (int) m_ArmorBase ); + + if ( GetSaveFlag( flags, SaveFlag.StrBonus ) ) + writer.WriteEncodedInt( (int) m_StrBonus ); + + if ( GetSaveFlag( flags, SaveFlag.DexBonus ) ) + writer.WriteEncodedInt( (int) m_DexBonus ); + + if ( GetSaveFlag( flags, SaveFlag.IntBonus ) ) + writer.WriteEncodedInt( (int) m_IntBonus ); + + if ( GetSaveFlag( flags, SaveFlag.StrReq ) ) + writer.WriteEncodedInt( (int) m_StrReq ); + + if ( GetSaveFlag( flags, SaveFlag.DexReq ) ) + writer.WriteEncodedInt( (int) m_DexReq ); + + if ( GetSaveFlag( flags, SaveFlag.IntReq ) ) + writer.WriteEncodedInt( (int) m_IntReq ); + + if ( GetSaveFlag( flags, SaveFlag.MedAllowance ) ) + writer.WriteEncodedInt( (int) m_Meditate ); + + if ( GetSaveFlag( flags, SaveFlag.SkillBonuses ) ) + m_AosSkillBonuses.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 7: + case 6: + case 5: + { + SaveFlag flags = (SaveFlag)reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Attributes ) ) + m_AosAttributes = new AosAttributes( this, reader ); + else + m_AosAttributes = new AosAttributes( this ); + + if ( GetSaveFlag( flags, SaveFlag.ArmorAttributes ) ) + m_AosArmorAttributes = new AosArmorAttributes( this, reader ); + else + m_AosArmorAttributes = new AosArmorAttributes( this ); + + if ( GetSaveFlag( flags, SaveFlag.PhysicalBonus ) ) + m_PhysicalBonus = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.FireBonus ) ) + m_FireBonus = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.ColdBonus ) ) + m_ColdBonus = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.PoisonBonus ) ) + m_PoisonBonus = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.EnergyBonus ) ) + m_EnergyBonus = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Identified ) ) + m_Identified = ( version >= 7 || reader.ReadBool() ); + + if ( GetSaveFlag( flags, SaveFlag.MaxHitPoints ) ) + m_MaxHitPoints = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.HitPoints ) ) + m_HitPoints = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Crafter ) ) + m_Crafter = reader.ReadMobile(); + + if ( GetSaveFlag( flags, SaveFlag.Quality ) ) + m_Quality = (ArmorQuality)reader.ReadEncodedInt(); + else + m_Quality = ArmorQuality.Regular; + + if ( version == 5 && m_Quality == ArmorQuality.Low ) + m_Quality = ArmorQuality.Regular; + + if ( GetSaveFlag( flags, SaveFlag.Durability ) ) + { + m_Durability = (ArmorDurabilityLevel)reader.ReadEncodedInt(); + + if ( m_Durability > ArmorDurabilityLevel.Indestructible ) + m_Durability = ArmorDurabilityLevel.Durable; + } + + if ( GetSaveFlag( flags, SaveFlag.Protection ) ) + { + m_Protection = (ArmorProtectionLevel)reader.ReadEncodedInt(); + + if ( m_Protection > ArmorProtectionLevel.Invulnerability ) + m_Protection = ArmorProtectionLevel.Defense; + } + + if ( GetSaveFlag( flags, SaveFlag.Resource ) ) + m_Resource = (CraftResource)reader.ReadEncodedInt(); + else + m_Resource = DefaultResource; + + if ( m_Resource == CraftResource.None ) + m_Resource = DefaultResource; + + if ( GetSaveFlag( flags, SaveFlag.BaseArmor ) ) + m_ArmorBase = reader.ReadEncodedInt(); + else + m_ArmorBase = -1; + + if ( GetSaveFlag( flags, SaveFlag.StrBonus ) ) + m_StrBonus = reader.ReadEncodedInt(); + else + m_StrBonus = -1; + + if ( GetSaveFlag( flags, SaveFlag.DexBonus ) ) + m_DexBonus = reader.ReadEncodedInt(); + else + m_DexBonus = -1; + + if ( GetSaveFlag( flags, SaveFlag.IntBonus ) ) + m_IntBonus = reader.ReadEncodedInt(); + else + m_IntBonus = -1; + + if ( GetSaveFlag( flags, SaveFlag.StrReq ) ) + m_StrReq = reader.ReadEncodedInt(); + else + m_StrReq = -1; + + if ( GetSaveFlag( flags, SaveFlag.DexReq ) ) + m_DexReq = reader.ReadEncodedInt(); + else + m_DexReq = -1; + + if ( GetSaveFlag( flags, SaveFlag.IntReq ) ) + m_IntReq = reader.ReadEncodedInt(); + else + m_IntReq = -1; + + if ( GetSaveFlag( flags, SaveFlag.MedAllowance ) ) + m_Meditate = (AMA)reader.ReadEncodedInt(); + else + m_Meditate = (AMA)(-1); + + if ( GetSaveFlag( flags, SaveFlag.SkillBonuses ) ) + m_AosSkillBonuses = new AosSkillBonuses( this, reader ); + + if ( GetSaveFlag( flags, SaveFlag.PlayerConstructed ) ) + m_PlayerConstructed = true; + + break; + } + case 4: + { + m_AosAttributes = new AosAttributes( this, reader ); + m_AosArmorAttributes = new AosArmorAttributes( this, reader ); + goto case 3; + } + case 3: + { + m_PhysicalBonus = reader.ReadInt(); + m_FireBonus = reader.ReadInt(); + m_ColdBonus = reader.ReadInt(); + m_PoisonBonus = reader.ReadInt(); + m_EnergyBonus = reader.ReadInt(); + goto case 2; + } + case 2: + case 1: + { + m_Identified = reader.ReadBool(); + goto case 0; + } + case 0: + { + m_ArmorBase = reader.ReadInt(); + m_MaxHitPoints = reader.ReadInt(); + m_HitPoints = reader.ReadInt(); + m_Crafter = reader.ReadMobile(); + m_Quality = (ArmorQuality)reader.ReadInt(); + m_Durability = (ArmorDurabilityLevel)reader.ReadInt(); + m_Protection = (ArmorProtectionLevel)reader.ReadInt(); + + AMT mat = (AMT)reader.ReadInt(); + + if ( m_ArmorBase == RevertArmorBase ) + m_ArmorBase = -1; + + /*m_BodyPos = (ArmorBodyType)*/reader.ReadInt(); + + if ( version < 4 ) + { + m_AosAttributes = new AosAttributes( this ); + m_AosArmorAttributes = new AosArmorAttributes( this ); + } + + if ( version < 3 && m_Quality == ArmorQuality.Exceptional ) + DistributeBonuses( 6 ); + + if ( version >= 2 ) + { + m_Resource = (CraftResource)reader.ReadInt(); + } + else + { + OreInfo info; + + switch ( reader.ReadInt() ) + { + default: + case 0: info = OreInfo.Iron; break; + case 1: info = OreInfo.DullCopper; break; + case 2: info = OreInfo.ShadowIron; break; + case 3: info = OreInfo.Copper; break; + case 4: info = OreInfo.Bronze; break; + case 5: info = OreInfo.Gold; break; + case 6: info = OreInfo.Agapite; break; + case 7: info = OreInfo.Verite; break; + case 8: info = OreInfo.Valorite; break; + } + + m_Resource = CraftResources.GetFromOreInfo( info, mat ); + } + + m_StrBonus = reader.ReadInt(); + m_DexBonus = reader.ReadInt(); + m_IntBonus = reader.ReadInt(); + m_StrReq = reader.ReadInt(); + m_DexReq = reader.ReadInt(); + m_IntReq = reader.ReadInt(); + + if ( m_StrBonus == OldStrBonus ) + m_StrBonus = -1; + + if ( m_DexBonus == OldDexBonus ) + m_DexBonus = -1; + + if ( m_IntBonus == OldIntBonus ) + m_IntBonus = -1; + + if ( m_StrReq == OldStrReq ) + m_StrReq = -1; + + if ( m_DexReq == OldDexReq ) + m_DexReq = -1; + + if ( m_IntReq == OldIntReq ) + m_IntReq = -1; + + m_Meditate = (AMA)reader.ReadInt(); + + if ( m_Meditate == OldMedAllowance ) + m_Meditate = (AMA)(-1); + + if ( m_Resource == CraftResource.None ) + { + if ( mat == ArmorMaterialType.Studded || mat == ArmorMaterialType.Leather ) + m_Resource = CraftResource.RegularLeather; + else if ( mat == ArmorMaterialType.Spined ) + m_Resource = CraftResource.SpinedLeather; + else if ( mat == ArmorMaterialType.Horned ) + m_Resource = CraftResource.HornedLeather; + else if ( mat == ArmorMaterialType.Barbed ) + m_Resource = CraftResource.BarbedLeather; + else + m_Resource = CraftResource.Iron; + } + + if ( m_MaxHitPoints == 0 && m_HitPoints == 0 ) + m_HitPoints = m_MaxHitPoints = Utility.RandomMinMax( InitMinHits, InitMaxHits ); + + break; + } + } + + if ( m_AosSkillBonuses == null ) + m_AosSkillBonuses = new AosSkillBonuses( this ); + + if ( Core.AOS && Parent is Mobile ) + m_AosSkillBonuses.AddTo( (Mobile)Parent ); + + int strBonus = ComputeStatBonus( StatType.Str ); + int dexBonus = ComputeStatBonus( StatType.Dex ); + int intBonus = ComputeStatBonus( StatType.Int ); + + 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(); + + if ( version < 7 ) + m_PlayerConstructed = true; // we don't know, so, assume it's crafted + } + + public virtual CraftResource DefaultResource{ get{ return CraftResource.Iron; } } + + public BaseArmor( int itemID ) : base( itemID ) + { + m_Quality = ArmorQuality.Regular; + m_Durability = ArmorDurabilityLevel.Regular; + m_Crafter = null; + + m_Resource = DefaultResource; + Hue = CraftResources.GetHue( m_Resource ); + + m_HitPoints = m_MaxHitPoints = Utility.RandomMinMax( InitMinHits, InitMaxHits ); + + this.Layer = (Layer)ItemData.Quality; + + m_AosAttributes = new AosAttributes( this ); + m_AosArmorAttributes = new AosArmorAttributes( this ); + m_AosSkillBonuses = new AosSkillBonuses( this ); + } + + 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; } } + + public override bool CanEquip( Mobile from ) + { + if( !Ethics.Ethic.CheckEquip( from, this ) ) + return false; + + if( from.AccessLevel < AccessLevel.GameMaster ) + { + 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( !AllowMaleWearer && !from.Female ) + { + if( AllowFemaleWearer ) + from.SendLocalizedMessage( 1010388 ); // Only females can wear this. + else + from.SendMessage( "You may not wear this." ); + + return false; + } + else if( !AllowFemaleWearer && from.Female ) + { + if( AllowMaleWearer ) + from.SendLocalizedMessage( 1063343 ); // Only males can wear this. + else + from.SendMessage( "You may not wear this." ); + + return false; + } + else + { + int strBonus = ComputeStatBonus( StatType.Str ), strReq = ComputeStatReq( StatType.Str ); + int dexBonus = ComputeStatBonus( StatType.Dex ), dexReq = ComputeStatReq( StatType.Dex ); + int intBonus = ComputeStatBonus( StatType.Int ), intReq = ComputeStatReq( StatType.Int ); + + if( from.Dex < dexReq || (from.Dex + dexBonus) < 1 ) + { + from.SendLocalizedMessage( 502077 ); // You do not have enough dexterity to equip this item. + return false; + } + else if( from.Str < strReq || (from.Str + strBonus) < 1 ) + { + from.SendLocalizedMessage( 500213 ); // You are not strong enough to equip that. + return false; + } + else if( from.Int < intReq || (from.Int + intBonus) < 1 ) + { + from.SendMessage( "You are not smart enough to equip that." ); + return false; + } + } + } + + return base.CanEquip( from ); + } + + public override bool CheckPropertyConfliction( Mobile m ) + { + if ( base.CheckPropertyConfliction( m ) ) + return true; + + if ( Layer == Layer.Pants ) + return ( m.FindItemOnLayer( Layer.InnerLegs ) != null ); + + if ( Layer == Layer.Shirt ) + return ( m.FindItemOnLayer( Layer.InnerTorso ) != null ); + + return false; + } + + public override bool OnEquip( Mobile from ) + { + from.CheckStatTimers(); + + int strBonus = ComputeStatBonus( StatType.Str ); + int dexBonus = ComputeStatBonus( StatType.Dex ); + int intBonus = ComputeStatBonus( StatType.Int ); + + 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 ) ); + } + + return base.OnEquip( from ); + } + + public override void OnRemoved( object parent ) + { + if ( parent is Mobile ) + { + Mobile m = (Mobile)parent; + string modName = this.Serial.ToString(); + + m.RemoveStatMod( modName + "Str" ); + m.RemoveStatMod( modName + "Dex" ); + m.RemoveStatMod( modName + "Int" ); + + if ( Core.AOS ) + m_AosSkillBonuses.Remove(); + + ((Mobile)parent).Delta( MobileDelta.Armor ); // Tell them armor rating has changed + m.CheckStatTimers(); + } + + base.OnRemoved( parent ); + } + + public virtual int OnHit( BaseWeapon weapon, int damageTaken ) + { + double HalfAr = ArmorRating / 2.0; + int Absorbed = (int)(HalfAr + HalfAr*Utility.RandomDouble()); + + damageTaken -= Absorbed; + if ( damageTaken < 0 ) + damageTaken = 0; + + if ( Absorbed < 2 ) + Absorbed = 2; + + if ( 25 > Utility.Random( 100 ) ) // 25% chance to lower durability + { + if ( Core.AOS && m_AosArmorAttributes.SelfRepair > Utility.Random( 10 ) ) + { + HitPoints += 2; + } + else + { + int wear; + + if ( weapon.Type == WeaponType.Bashing ) + wear = Absorbed / 2; + else + wear = Utility.Random( 2 ); + + if ( wear > 0 && m_MaxHitPoints > 0 ) + { + if ( m_HitPoints >= wear ) + { + HitPoints -= wear; + wear = 0; + } + else + { + wear -= HitPoints; + HitPoints = 0; + } + + if ( wear > 0 ) + { + if ( m_MaxHitPoints > wear ) + { + MaxHitPoints -= wear; + + if ( Parent is Mobile ) + ((Mobile)Parent).LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061121 ); // Your equipment is severely damaged. + } + else + { + Delete(); + } + } + } + } + } + + return damageTaken; + } + + 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 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 ( m_Quality == ArmorQuality.Exceptional ) + { + if ( oreType != 0 ) + list.Add( 1053100, "#{0}\t{1}", oreType, GetNameString() ); // exceptional ~1_oretype~ ~2_armortype~ + else + list.Add( 1050040, GetNameString() ); // exceptional ~1_ITEMNAME~ + } + else + { + 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 GetLuckBonus() + { + CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); + + if ( resInfo == null ) + return 0; + + CraftAttributeInfo attrInfo = resInfo.AttributeInfo; + + if ( attrInfo == null ) + return 0; + + return attrInfo.ArmorLuck; + } + + 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( RequiredRace == Race.Elf ) + list.Add( 1075086 ); // Elves Only + + 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 = 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_AosArmorAttributes.MageArmor) != 0 ) + list.Add( 1060437 ); // mage armor + + 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_AosArmorAttributes.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~% + + base.AddResistanceProperties( list ); + + if ( (prop = GetDurabilityBonus()) > 0 ) + list.Add( 1060410, prop.ToString() ); // durability ~1_val~% + + if ( (prop = ComputeStatReq( StatType.Str )) > 0 ) + list.Add( 1061170, prop.ToString() ); // strength requirement ~1_val~ + + if ( m_HitPoints >= 0 && m_MaxHitPoints > 0 ) + list.Add( 1060639, "{0}\t{1}", m_HitPoints, m_MaxHitPoints ); // durability ~1_val~ / ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + ArrayList attrs = new ArrayList(); + + 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 == ArmorQuality.Exceptional ) + attrs.Add( new EquipInfoAttribute( 1018305 - (int)m_Quality ) ); + + if ( m_Identified ) + { + if ( m_Durability != ArmorDurabilityLevel.Regular ) + attrs.Add( new EquipInfoAttribute( 1038000 + (int)m_Durability ) ); + + if ( m_Protection > ArmorProtectionLevel.Regular && m_Protection <= ArmorProtectionLevel.Invulnerability ) + attrs.Add( new EquipInfoAttribute( 1038005 + (int)m_Protection ) ); + } + else if ( m_Durability != ArmorDurabilityLevel.Regular || (m_Protection > ArmorProtectionLevel.Regular && m_Protection <= ArmorProtectionLevel.Invulnerability) ) + { + attrs.Add( new EquipInfoAttribute( 1038000 ) ); // Unidentified + } + + 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, (EquipInfoAttribute[])attrs.ToArray( typeof( EquipInfoAttribute ) ) ); + + from.Send( new DisplayEquipmentInfo( this, eqInfo ) ); + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Quality = (ArmorQuality)quality; + + if ( makersMark ) + Crafter = from; + + Type resourceType = typeRes; + + if ( resourceType == null ) + resourceType = craftItem.Ressources.GetAt( 0 ).ItemType; + + Resource = CraftResources.GetFromType( resourceType ); + PlayerConstructed = true; + + CraftContext context = craftSystem.GetContext( from ); + + if ( context != null && context.DoNotColor ) + Hue = 0; + + if ( quality == 2 ) + DistributeBonuses( (tool is BaseRunicTool ? 6 : Core.SE ? 15 : 14) ); // Not sure since when, but right now 15 points are added, not 14. + + if ( Core.AOS && tool is BaseRunicTool ) + ((BaseRunicTool)tool).ApplyAttributesTo( this ); + + return quality; + } + + #endregion + } +} diff --git a/Scripts/Items/Armor/Bone/BoneArms.cs b/Scripts/Items/Armor/Bone/BoneArms.cs new file mode 100644 index 000000000..06f45e825 --- /dev/null +++ b/Scripts/Items/Armor/Bone/BoneArms.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x144e, 0x1453 )] + public class BoneArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 30; } } + public override int RevertArmorBase{ get{ return 4; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public BoneArms() : base( 0x144E ) + { + Weight = 2.0; + } + + public BoneArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Bone/BoneChest.cs b/Scripts/Items/Armor/Bone/BoneChest.cs new file mode 100644 index 000000000..669c2ce3f --- /dev/null +++ b/Scripts/Items/Armor/Bone/BoneChest.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x144f, 0x1454 )] + public class BoneChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 60; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -6; } } + + public override int ArmorBase{ get{ return 30; } } + public override int RevertArmorBase{ get{ return 11; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public BoneChest() : base( 0x144F ) + { + Weight = 6.0; + } + + public BoneChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + + if ( Weight == 1.0 ) + Weight = 6.0; + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Bone/BoneGloves.cs b/Scripts/Items/Armor/Bone/BoneGloves.cs new file mode 100644 index 000000000..dc0739f00 --- /dev/null +++ b/Scripts/Items/Armor/Bone/BoneGloves.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1450, 0x1455 )] + public class BoneGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 30; } } + public override int RevertArmorBase{ get{ return 2; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public BoneGloves() : base( 0x1450 ) + { + Weight = 2.0; + } + + public BoneGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Bone/BoneLegs.cs b/Scripts/Items/Armor/Bone/BoneLegs.cs new file mode 100644 index 000000000..c647b3454 --- /dev/null +++ b/Scripts/Items/Armor/Bone/BoneLegs.cs @@ -0,0 +1,51 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1452, 0x1457 )] + public class BoneLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -4; } } + + public override int ArmorBase{ get{ return 30; } } + public override int RevertArmorBase{ get{ return 7; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public BoneLegs() : base( 0x1452 ) + { + Weight = 3.0; + } + + public BoneLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Chain/ChainChest.cs b/Scripts/Items/Armor/Chain/ChainChest.cs new file mode 100644 index 000000000..7815f1aa8 --- /dev/null +++ b/Scripts/Items/Armor/Chain/ChainChest.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13bf, 0x13c4 )] + public class ChainChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 4; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 1; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 60; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -5; } } + + public override int ArmorBase{ get{ return 28; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Chainmail; } } + + [Constructable] + public ChainChest() : base( 0x13BF ) + { + Weight = 7.0; + } + + public ChainChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Chain/ChainHatsuburi.cs b/Scripts/Items/Armor/Chain/ChainHatsuburi.cs new file mode 100644 index 000000000..734bee2fe --- /dev/null +++ b/Scripts/Items/Armor/Chain/ChainHatsuburi.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class ChainHatsuburi : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 50; } } + public override int OldStrReq{ get{ return 50; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Chainmail; } } + + [Constructable] + public ChainHatsuburi() : base( 0x2774 ) + { + Weight = 7.0; + } + + public ChainHatsuburi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Chain/ChainLegs.cs b/Scripts/Items/Armor/Chain/ChainLegs.cs new file mode 100644 index 000000000..9602aecd3 --- /dev/null +++ b/Scripts/Items/Armor/Chain/ChainLegs.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13be, 0x13c3 )] + public class ChainLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 4; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 1; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 60; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -3; } } + + public override int ArmorBase{ get{ return 28; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Chainmail; } } + + [Constructable] + public ChainLegs() : base( 0x13BE ) + { + Weight = 7.0; + } + + public ChainLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/DaemonBone/DaemonArms.cs b/Scripts/Items/Armor/DaemonBone/DaemonArms.cs new file mode 100644 index 000000000..b164cc041 --- /dev/null +++ b/Scripts/Items/Armor/DaemonBone/DaemonArms.cs @@ -0,0 +1,63 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x144e, 0x1453 )] + public class DaemonArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 46; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041371; } } // daemon bone arms + + [Constructable] + public DaemonArms() : base( 0x144E ) + { + Weight = 2.0; + Hue = 0x648; + + ArmorAttributes.SelfRepair = 1; + } + + public DaemonArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( ArmorAttributes.SelfRepair == 0 ) + ArmorAttributes.SelfRepair = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/DaemonBone/DaemonChest.cs b/Scripts/Items/Armor/DaemonBone/DaemonChest.cs new file mode 100644 index 000000000..ad87f3212 --- /dev/null +++ b/Scripts/Items/Armor/DaemonBone/DaemonChest.cs @@ -0,0 +1,63 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x144f, 0x1454 )] + public class DaemonChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + public override int AosStrReq{ get{ return 60; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -6; } } + + public override int ArmorBase{ get{ return 46; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041372; } } // daemon bone armor + + [Constructable] + public DaemonChest() : base( 0x144F ) + { + Weight = 6.0; + Hue = 0x648; + + ArmorAttributes.SelfRepair = 1; + } + + public DaemonChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 6.0; + + if ( ArmorAttributes.SelfRepair == 0 ) + ArmorAttributes.SelfRepair = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/DaemonBone/DaemonGloves.cs b/Scripts/Items/Armor/DaemonBone/DaemonGloves.cs new file mode 100644 index 000000000..fbfd5b9fe --- /dev/null +++ b/Scripts/Items/Armor/DaemonBone/DaemonGloves.cs @@ -0,0 +1,63 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1450, 0x1455 )] + public class DaemonGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 46; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041373; } } // daemon bone gloves + + [Constructable] + public DaemonGloves() : base( 0x1450 ) + { + Weight = 2.0; + Hue = 0x648; + + ArmorAttributes.SelfRepair = 1; + } + + public DaemonGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + + if ( ArmorAttributes.SelfRepair == 0 ) + ArmorAttributes.SelfRepair = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/DaemonBone/DaemonLegs.cs b/Scripts/Items/Armor/DaemonBone/DaemonLegs.cs new file mode 100644 index 000000000..6e3b1f13b --- /dev/null +++ b/Scripts/Items/Armor/DaemonBone/DaemonLegs.cs @@ -0,0 +1,58 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1452, 0x1457 )] + public class DaemonLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -4; } } + + public override int ArmorBase{ get{ return 46; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041375; } } // daemon bone leggings + + [Constructable] + public DaemonLegs() : base( 0x1452 ) + { + Weight = 3.0; + Hue = 0x648; + + ArmorAttributes.SelfRepair = 1; + } + + public DaemonLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( ArmorAttributes.SelfRepair == 0 ) + ArmorAttributes.SelfRepair = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Dragon/DragonArms.cs b/Scripts/Items/Armor/Dragon/DragonArms.cs new file mode 100644 index 000000000..81fc97afb --- /dev/null +++ b/Scripts/Items/Armor/Dragon/DragonArms.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2657, 0x2658 )] + public class DragonArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 75; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Dragon; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RedScales; } } + + [Constructable] + public DragonArms() : base( 0x2657 ) + { + Weight = 5.0; + } + + public DragonArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 15.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Dragon/DragonChest.cs b/Scripts/Items/Armor/Dragon/DragonChest.cs new file mode 100644 index 000000000..07540ca44 --- /dev/null +++ b/Scripts/Items/Armor/Dragon/DragonChest.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2641, 0x2642 )] + public class DragonChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 75; } } + public override int OldStrReq{ get{ return 60; } } + + public override int OldDexBonus{ get{ return -8; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Dragon; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RedScales; } } + + [Constructable] + public DragonChest() : base( 0x2641 ) + { + Weight = 10.0; + } + + public DragonChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 15.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Dragon/DragonGloves.cs b/Scripts/Items/Armor/Dragon/DragonGloves.cs new file mode 100644 index 000000000..47cbf738f --- /dev/null +++ b/Scripts/Items/Armor/Dragon/DragonGloves.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2643, 0x2644 )] + public class DragonGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 75; } } + public override int OldStrReq{ get{ return 30; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Dragon; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RedScales; } } + + [Constructable] + public DragonGloves() : base( 0x2643 ) + { + Weight = 2.0; + } + + public DragonGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Dragon/DragonHelm.cs b/Scripts/Items/Armor/Dragon/DragonHelm.cs new file mode 100644 index 000000000..093a29ef0 --- /dev/null +++ b/Scripts/Items/Armor/Dragon/DragonHelm.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [Flipable( 0x2645, 0x2646 )] + public class DragonHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 75; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Dragon; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RedScales; } } + + [Constructable] + public DragonHelm() : base( 0x2645 ) + { + Weight = 5.0; + } + + public DragonHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Dragon/DragonLegs.cs b/Scripts/Items/Armor/Dragon/DragonLegs.cs new file mode 100644 index 000000000..db5410df8 --- /dev/null +++ b/Scripts/Items/Armor/Dragon/DragonLegs.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2647, 0x2648 )] + public class DragonLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 75; } } + public override int OldStrReq{ get{ return 60; } } + + public override int OldDexBonus{ get{ return -6; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Dragon; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RedScales; } } + + [Constructable] + public DragonLegs() : base( 0x2647 ) + { + Weight = 6.0; + } + + public DragonLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/Bascinet.cs b/Scripts/Items/Armor/Helmets/Bascinet.cs new file mode 100644 index 000000000..e2c88337c --- /dev/null +++ b/Scripts/Items/Armor/Helmets/Bascinet.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Bascinet : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 7; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 18; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public Bascinet() : base( 0x140C ) + { + Weight = 5.0; + } + + public Bascinet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/BoneHelm.cs b/Scripts/Items/Armor/Helmets/BoneHelm.cs new file mode 100644 index 000000000..430ce326f --- /dev/null +++ b/Scripts/Items/Armor/Helmets/BoneHelm.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x1451, 0x1456 )] + public class BoneHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 40; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public BoneHelm() : base( 0x1451 ) + { + Weight = 3.0; + } + + public BoneHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + + if ( Weight == 1.0 ) + Weight = 3.0; + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/ChainCoif.cs b/Scripts/Items/Armor/Helmets/ChainCoif.cs new file mode 100644 index 000000000..3292b3e90 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/ChainCoif.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x13BB, 0x13C0 )] + public class ChainCoif : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 4; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 1; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 60; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 28; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Chainmail; } } + + [Constructable] + public ChainCoif() : base( 0x13BB ) + { + Weight = 1.0; + } + + public ChainCoif( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/Circlet.cs b/Scripts/Items/Armor/Helmets/Circlet.cs new file mode 100644 index 000000000..52ce0c060 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/Circlet.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B6E, 0x3165 )] + public class Circlet : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 1; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override Race RequiredRace{ get { return Race.Elf; } } + + [Constructable] + public Circlet() : base( 0x2B6E ) + { + Weight = 2.0; + } + + public Circlet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/CloseHelm.cs b/Scripts/Items/Armor/Helmets/CloseHelm.cs new file mode 100644 index 000000000..e17e7f72b --- /dev/null +++ b/Scripts/Items/Armor/Helmets/CloseHelm.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CloseHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public CloseHelm() : base( 0x1408 ) + { + Weight = 5.0; + } + + public CloseHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/DaemonHelm.cs b/Scripts/Items/Armor/Helmets/DaemonHelm.cs new file mode 100644 index 000000000..11d29d54b --- /dev/null +++ b/Scripts/Items/Armor/Helmets/DaemonHelm.cs @@ -0,0 +1,61 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x1451, 0x1456 )] + public class DaemonHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 40; } } + + public override int ArmorBase{ get{ return 46; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Bone; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041374; } } // daemon bone helmet + + [Constructable] + public DaemonHelm() : base( 0x1451 ) + { + Hue = 0x648; + Weight = 3.0; + + ArmorAttributes.SelfRepair = 1; + } + + public DaemonHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 3.0; + + if ( ArmorAttributes.SelfRepair == 0 ) + ArmorAttributes.SelfRepair = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/GemmedCirclet.cs b/Scripts/Items/Armor/Helmets/GemmedCirclet.cs new file mode 100644 index 000000000..1abfdaea9 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/GemmedCirclet.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B70, 0x3167 )] + public class GemmedCirclet : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 1; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 35; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public GemmedCirclet() : base( 0x2B70 ) + { + Weight = 2.0; + } + + public GemmedCirclet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/Helmet.cs b/Scripts/Items/Armor/Helmets/Helmet.cs new file mode 100644 index 000000000..24ea97819 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/Helmet.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Helmet : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 45; } } + public override int OldStrReq{ get{ return 40; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public Helmet() : base( 0x140A ) + { + Weight = 5.0; + } + + public Helmet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/LeafTonlet.cs b/Scripts/Items/Armor/Helmets/LeafTonlet.cs new file mode 100644 index 000000000..09cc5957d --- /dev/null +++ b/Scripts/Items/Armor/Helmets/LeafTonlet.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2FCA, 0x3180 )] + public class LeafTonlet : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeafTonlet() : base( 0x2FCA ) + { + Weight = 2.0; + } + + public LeafTonlet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/LeatherCap.cs b/Scripts/Items/Armor/Helmets/LeatherCap.cs new file mode 100644 index 000000000..13d4599f2 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/LeatherCap.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x1db9, 0x1dba )] + public class LeatherCap : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherCap() : base( 0x1DB9 ) + { + Weight = 2.0; + } + + public LeatherCap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/NorseHelm.cs b/Scripts/Items/Armor/Helmets/NorseHelm.cs new file mode 100644 index 000000000..45b831fdf --- /dev/null +++ b/Scripts/Items/Armor/Helmets/NorseHelm.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Items +{ + public class NorseHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 4; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 40; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public NorseHelm() : base( 0x140E ) + { + Weight = 5.0; + } + + public NorseHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/OrcHelm.cs b/Scripts/Items/Armor/Helmets/OrcHelm.cs new file mode 100644 index 000000000..078896f58 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/OrcHelm.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Items +{ + public class OrcHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 20; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public OrcHelm() : base( 0x1F0B ) + { + Weight = 1; + } + + public OrcHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/PlateHelm.cs b/Scripts/Items/Armor/Helmets/PlateHelm.cs new file mode 100644 index 000000000..b4bc1122d --- /dev/null +++ b/Scripts/Items/Armor/Helmets/PlateHelm.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PlateHelm : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 80; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateHelm() : base( 0x1412 ) + { + Weight = 5.0; + } + + public PlateHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/RavenHelm.cs b/Scripts/Items/Armor/Helmets/RavenHelm.cs new file mode 100644 index 000000000..727923010 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/RavenHelm.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B71, 0x3168 )] + public class RavenHelm : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public RavenHelm() : base( 0x2B71 ) + { + Weight = 5.0; + } + + public RavenHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/RoyalCirclet.cs b/Scripts/Items/Armor/Helmets/RoyalCirclet.cs new file mode 100644 index 000000000..d968b59c0 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/RoyalCirclet.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B6F, 0x3166 )] + public class RoyalCirclet : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 1; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 35; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public RoyalCirclet() : base( 0x2B6F ) + { + Weight = 2.0; + } + + public RoyalCirclet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/VultureHelm.cs b/Scripts/Items/Armor/Helmets/VultureHelm.cs new file mode 100644 index 000000000..f6c211e13 --- /dev/null +++ b/Scripts/Items/Armor/Helmets/VultureHelm.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B72, 0x3169 )] + public class VultureHelm : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public VultureHelm() : base( 0x2B72 ) + { + Weight = 5.0; + } + + public VultureHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Helmets/WingedHelm.cs b/Scripts/Items/Armor/Helmets/WingedHelm.cs new file mode 100644 index 000000000..464f55caa --- /dev/null +++ b/Scripts/Items/Armor/Helmets/WingedHelm.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B73, 0x316A )] + public class WingedHelm : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 55; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public WingedHelm() : base( 0x2B73 ) + { + Weight = 5.0; + } + + public WingedHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/FemaleLeafChest.cs b/Scripts/Items/Armor/Leather/FemaleLeafChest.cs new file mode 100644 index 000000000..1fa6ef81c --- /dev/null +++ b/Scripts/Items/Armor/Leather/FemaleLeafChest.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2FCB, 0x3181 )] + public class FemaleLeafChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public FemaleLeafChest() : base( 0x2FCB ) + { + Weight = 2.0; + } + + public FemaleLeafChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/FemaleLeatherChest.cs b/Scripts/Items/Armor/Leather/FemaleLeatherChest.cs new file mode 100644 index 000000000..33eed6c3d --- /dev/null +++ b/Scripts/Items/Armor/Leather/FemaleLeatherChest.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c06, 0x1c07 )] + public class FemaleLeatherChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public FemaleLeatherChest() : base( 0x1C06 ) + { + Weight = 1.0; + } + + public FemaleLeatherChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeafArms.cs b/Scripts/Items/Armor/Leather/LeafArms.cs new file mode 100644 index 000000000..32716ff9c --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeafArms.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2FC8, 0x317E )] + public class LeafArms : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 15; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeafArms() : base( 0x2FC8 ) + { + Weight = 2.0; + } + + public LeafArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeafChest.cs b/Scripts/Items/Armor/Leather/LeafChest.cs new file mode 100644 index 000000000..e9aded197 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeafChest.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2FC5, 0x317B )] + public class LeafChest : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeafChest() : base( 0x2FC5 ) + { + Weight = 2.0; + } + + public LeafChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeafGloves.cs b/Scripts/Items/Armor/Leather/LeafGloves.cs new file mode 100644 index 000000000..5ffa9254a --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeafGloves.cs @@ -0,0 +1,140 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [Flipable] + public class LeafGloves : BaseArmor, IArcaneEquip + { + public override Race RequiredRace { get { return Race.Elf; } } + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeafGloves() : base( 0x2FC6 ) + { + Weight = 2.0; + } + + public LeafGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + if ( IsArcane ) + { + writer.Write( true ); + writer.Write( (int) m_CurArcaneCharges ); + writer.Write( (int) m_MaxArcaneCharges ); + } + else + { + writer.Write( false ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + if ( reader.ReadBool() ) + { + m_CurArcaneCharges = reader.ReadInt(); + m_MaxArcaneCharges = reader.ReadInt(); + + if ( Hue == 2118 ) + Hue = ArcaneGem.DefaultArcaneHue; + } + + break; + } + } + } + + #region Arcane Impl + private int m_MaxArcaneCharges, m_CurArcaneCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxArcaneCharges + { + get{ return m_MaxArcaneCharges; } + set{ m_MaxArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurArcaneCharges + { + get{ return m_CurArcaneCharges; } + set{ m_CurArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsArcane + { + get{ return ( m_MaxArcaneCharges > 0 && m_CurArcaneCharges >= 0 ); } + } + + public void Update() + { + if ( IsArcane ) + ItemID = 0x26B0; // TODO: Check + else if ( ItemID == 0x26B0 ) + ItemID = 0x2FC6; + + if ( IsArcane && CurArcaneCharges == 0 ) + Hue = 0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( IsArcane ) + list.Add( 1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ); // arcane charges: ~1_val~ / ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( IsArcane ) + LabelTo( from, 1061837, String.Format( "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ) ); + } + + public void Flip() + { + if ( ItemID == 0x2FC6 ) + ItemID = 0x317C; + else if ( ItemID == 0x317C ) + ItemID = 0x2FC6; + } + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeafGorget.cs b/Scripts/Items/Armor/Leather/LeafGorget.cs new file mode 100644 index 000000000..6f40f5bf8 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeafGorget.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeafGorget : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeafGorget() : base( 0x2FC7 ) + { + Weight = 2.0; + } + + public LeafGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeafLegs.cs b/Scripts/Items/Armor/Leather/LeafLegs.cs new file mode 100644 index 000000000..86918f1fc --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeafLegs.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2FC9, 0x317F )] + public class LeafLegs : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeafLegs() : base( 0x2FC9 ) + { + Weight = 2.0; + } + + public LeafLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherArms.cs b/Scripts/Items/Armor/Leather/LeatherArms.cs new file mode 100644 index 000000000..b7c7438cb --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherArms.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13cd, 0x13c5 )] + public class LeatherArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherArms() : base( 0x13CD ) + { + Weight = 2.0; + } + + public LeatherArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherBustierArms.cs b/Scripts/Items/Armor/Leather/LeatherBustierArms.cs new file mode 100644 index 000000000..4e63ba65f --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherBustierArms.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c0a, 0x1c0b )] + public class LeatherBustierArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public LeatherBustierArms() : base( 0x1C0A ) + { + Weight = 1.0; + } + + public LeatherBustierArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherChest.cs b/Scripts/Items/Armor/Leather/LeatherChest.cs new file mode 100644 index 000000000..1fb39cda6 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherChest.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13cc, 0x13d3 )] + public class LeatherChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherChest() : base( 0x13CC ) + { + Weight = 6.0; + } + + public LeatherChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 6.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherDo.cs b/Scripts/Items/Armor/Leather/LeatherDo.cs new file mode 100644 index 000000000..cc4e3113a --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherDo.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherDo : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 40; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherDo() : base( 0x27C6 ) + { + Weight = 6.0; + } + + public LeatherDo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherGloves.cs b/Scripts/Items/Armor/Leather/LeatherGloves.cs new file mode 100644 index 000000000..3c1d7e6a0 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherGloves.cs @@ -0,0 +1,139 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [Flipable] + public class LeatherGloves : BaseArmor, IArcaneEquip + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherGloves() : base( 0x13C6 ) + { + Weight = 1.0; + } + + public LeatherGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + if ( IsArcane ) + { + writer.Write( true ); + writer.Write( (int) m_CurArcaneCharges ); + writer.Write( (int) m_MaxArcaneCharges ); + } + else + { + writer.Write( false ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + if ( reader.ReadBool() ) + { + m_CurArcaneCharges = reader.ReadInt(); + m_MaxArcaneCharges = reader.ReadInt(); + + if ( Hue == 2118 ) + Hue = ArcaneGem.DefaultArcaneHue; + } + + break; + } + } + } + + #region Arcane Impl + private int m_MaxArcaneCharges, m_CurArcaneCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxArcaneCharges + { + get{ return m_MaxArcaneCharges; } + set{ m_MaxArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurArcaneCharges + { + get{ return m_CurArcaneCharges; } + set{ m_CurArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsArcane + { + get{ return ( m_MaxArcaneCharges > 0 && m_CurArcaneCharges >= 0 ); } + } + + public void Update() + { + if ( IsArcane ) + ItemID = 0x26B0; + else if ( ItemID == 0x26B0 ) + ItemID = 0x13C6; + + if ( IsArcane && CurArcaneCharges == 0 ) + Hue = 0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( IsArcane ) + list.Add( 1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ); // arcane charges: ~1_val~ / ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( IsArcane ) + LabelTo( from, 1061837, String.Format( "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ) ); + } + + public void Flip() + { + if ( ItemID == 0x13C6 ) + ItemID = 0x13CE; + else if ( ItemID == 0x13CE ) + ItemID = 0x13C6; + } + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherGorget.cs b/Scripts/Items/Armor/Leather/LeatherGorget.cs new file mode 100644 index 000000000..c9d4a7a1a --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherGorget.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherGorget : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherGorget() : base( 0x13C7 ) + { + Weight = 1.0; + } + + public LeatherGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherHaidate.cs b/Scripts/Items/Armor/Leather/LeatherHaidate.cs new file mode 100644 index 000000000..dbe853a53 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherHaidate.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherHaidate : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType { get { return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherHaidate() : base( 0x278A ) + { + Weight = 4.0; + } + + public LeatherHaidate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherHiroSode.cs b/Scripts/Items/Armor/Leather/LeatherHiroSode.cs new file mode 100644 index 000000000..9759a3e13 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherHiroSode.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherHiroSode : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherHiroSode() : base( 0x277E ) + { + Weight = 1.0; + } + + public LeatherHiroSode( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherJingasa.cs b/Scripts/Items/Armor/Leather/LeatherJingasa.cs new file mode 100644 index 000000000..a08b295e7 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherJingasa.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherJingasa : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 4; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherJingasa() : base( 0x2776 ) + { + Weight = 3.0; + } + + public LeatherJingasa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherLegs.cs b/Scripts/Items/Armor/Leather/LeatherLegs.cs new file mode 100644 index 000000000..82b2145f1 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherLegs.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13cb, 0x13d2 )] + public class LeatherLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherLegs() : base( 0x13CB ) + { + Weight = 4.0; + } + + public LeatherLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherMempo.cs b/Scripts/Items/Armor/Leather/LeatherMempo.cs new file mode 100644 index 000000000..067a31cd7 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherMempo.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherMempo : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 30; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherMempo() : base( 0x277A ) + { + Weight = 2.0; + } + + public LeatherMempo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherNinjaHood.cs b/Scripts/Items/Armor/Leather/LeatherNinjaHood.cs new file mode 100644 index 000000000..0caa5bdf1 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherNinjaHood.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherNinjaHood : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherNinjaHood() : base( 0x278E ) + { + Weight = 2.0; + } + + public LeatherNinjaHood( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherNinjaJacket.cs b/Scripts/Items/Armor/Leather/LeatherNinjaJacket.cs new file mode 100644 index 000000000..222bb99dd --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherNinjaJacket.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherNinjaJacket : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherNinjaJacket() : base( 0x2793 ) + { + Weight = 5.0; + } + + public LeatherNinjaJacket( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherNinjaMitts.cs b/Scripts/Items/Armor/Leather/LeatherNinjaMitts.cs new file mode 100644 index 000000000..eb3ae3795 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherNinjaMitts.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherNinjaMitts : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 25; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherNinjaMitts() : base( 0x2792 ) + { + Weight = 2.0; + } + + public LeatherNinjaMitts( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherNinjaPants.cs b/Scripts/Items/Armor/Leather/LeatherNinjaPants.cs new file mode 100644 index 000000000..ab8da69e9 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherNinjaPants.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherNinjaPants : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 10; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherNinjaPants() : base( 0x2791 ) + { + Weight = 3.0; + } + + public LeatherNinjaPants( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherShorts.cs b/Scripts/Items/Armor/Leather/LeatherShorts.cs new file mode 100644 index 000000000..e062d975f --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherShorts.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c00, 0x1c01 )] + public class LeatherShorts : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public LeatherShorts() : base( 0x1C00 ) + { + Weight = 3.0; + } + + public LeatherShorts( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherSkirt.cs b/Scripts/Items/Armor/Leather/LeatherSkirt.cs new file mode 100644 index 000000000..bebd13e59 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherSkirt.cs @@ -0,0 +1,55 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c08, 0x1c09 )] + public class LeatherSkirt : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public LeatherSkirt() : base( 0x1C08 ) + { + Weight = 1.0; + } + + public LeatherSkirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + + if ( Weight == 3.0 ) + Weight = 1.0; + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Leather/LeatherSuneate.cs b/Scripts/Items/Armor/Leather/LeatherSuneate.cs new file mode 100644 index 000000000..d34f927d2 --- /dev/null +++ b/Scripts/Items/Armor/Leather/LeatherSuneate.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LeatherSuneate : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource { get { return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + [Constructable] + public LeatherSuneate() : base( 0x2786 ) + { + Weight = 4.0; + } + + public LeatherSuneate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/DecorativePlateKabuto.cs b/Scripts/Items/Armor/Plate/DecorativePlateKabuto.cs new file mode 100644 index 000000000..85e1d4844 --- /dev/null +++ b/Scripts/Items/Armor/Plate/DecorativePlateKabuto.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class DecorativePlateKabuto : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 70; } } + public override int OldStrReq{ get{ return 70; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public DecorativePlateKabuto() : base( 0x2778 ) + { + Weight = 6.0; + } + + public DecorativePlateKabuto( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/FemalePlateChest.cs b/Scripts/Items/Armor/Plate/FemalePlateChest.cs new file mode 100644 index 000000000..08d88337e --- /dev/null +++ b/Scripts/Items/Armor/Plate/FemalePlateChest.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c04, 0x1c05 )] + public class FemalePlateChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 95; } } + public override int OldStrReq{ get{ return 45; } } + + public override int OldDexBonus{ get{ return -5; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public FemalePlateChest() : base( 0x1C04 ) + { + Weight = 4.0; + } + + public FemalePlateChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 4.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/FemaleWoodlandChest.cs b/Scripts/Items/Armor/Plate/FemaleWoodlandChest.cs new file mode 100644 index 000000000..70e57827c --- /dev/null +++ b/Scripts/Items/Armor/Plate/FemaleWoodlandChest.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + //Is this a filler-type item? the clilocs don't match up and at a glacnce I can't find direct reference of it + [FlipableAttribute( 0x2B6D, 0x3164 )] + public class FemaleElvenPlateChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 95; } } + public override int OldStrReq{ get{ return 95; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + public override int ArmorBase{ get{ return 30; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public FemaleElvenPlateChest() : base( 0x2B6D ) + { + Weight = 8.0; + } + + public FemaleElvenPlateChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/HeavyPlateJingasa.cs b/Scripts/Items/Armor/Plate/HeavyPlateJingasa.cs new file mode 100644 index 000000000..b76a955e5 --- /dev/null +++ b/Scripts/Items/Armor/Plate/HeavyPlateJingasa.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class HeavyPlateJingasa : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 7; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 70; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 55; } } + + public override int ArmorBase{ get{ return 4; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public HeavyPlateJingasa() : base( 0x2777 ) + { + Weight = 5.0; + } + + public HeavyPlateJingasa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/LightPlateJingasa.cs b/Scripts/Items/Armor/Plate/LightPlateJingasa.cs new file mode 100644 index 000000000..1087dfd14 --- /dev/null +++ b/Scripts/Items/Armor/Plate/LightPlateJingasa.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class LightPlateJingasa : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 7; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 55; } } + + public override int ArmorBase{ get{ return 4; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public LightPlateJingasa() : base( 0x2781 ) + { + Weight = 5.0; + } + + public LightPlateJingasa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateArms.cs b/Scripts/Items/Armor/Plate/PlateArms.cs new file mode 100644 index 000000000..a5f59aef4 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateArms.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1410, 0x1417 )] + public class PlateArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 80; } } + public override int OldStrReq{ get{ return 40; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateArms() : base( 0x1410 ) + { + Weight = 5.0; + } + + public PlateArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateBattleKabuto.cs b/Scripts/Items/Armor/Plate/PlateBattleKabuto.cs new file mode 100644 index 000000000..4acb465ee --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateBattleKabuto.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateBattleKabuto : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 60; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 70; } } + public override int OldStrReq{ get{ return 70; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateBattleKabuto() : base( 0x2785 ) + { + Weight = 6.0; + } + + public PlateBattleKabuto( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateChest.cs b/Scripts/Items/Armor/Plate/PlateChest.cs new file mode 100644 index 000000000..648d731cf --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateChest.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1415, 0x1416 )] + public class PlateChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 95; } } + public override int OldStrReq{ get{ return 60; } } + + public override int OldDexBonus{ get{ return -8; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateChest() : base( 0x1415 ) + { + Weight = 10.0; + } + + public PlateChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 10.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateDo.cs b/Scripts/Items/Armor/Plate/PlateDo.cs new file mode 100644 index 000000000..ca5cfb2ff --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateDo.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateDo : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 60; } } + public override int InitMaxHits{ get{ return 70; } } + + public override int AosStrReq{ get{ return 85; } } + public override int OldStrReq{ get{ return 85; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateDo() : base( 0x277D ) + { + Weight = 10.0; + } + + public PlateDo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateGloves.cs b/Scripts/Items/Armor/Plate/PlateGloves.cs new file mode 100644 index 000000000..66b7243b6 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateGloves.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1414, 0x1418 )] + public class PlateGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 70; } } + public override int OldStrReq{ get{ return 30; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateGloves() : base( 0x1414 ) + { + Weight = 2.0; + } + + public PlateGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateGorget.cs b/Scripts/Items/Armor/Plate/PlateGorget.cs new file mode 100644 index 000000000..ed8ee7fb3 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateGorget.cs @@ -0,0 +1,48 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateGorget : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 45; } } + public override int OldStrReq{ get{ return 30; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateGorget() : base( 0x1413 ) + { + Weight = 2.0; + } + + public PlateGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateHaidate.cs b/Scripts/Items/Armor/Plate/PlateHaidate.cs new file mode 100644 index 000000000..eb666c361 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateHaidate.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateHaidate : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 80; } } + public override int OldStrReq{ get{ return 80; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateHaidate() : base( 0x278D ) + { + Weight = 7.0; + } + + public PlateHaidate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateHatsuburi.cs b/Scripts/Items/Armor/Plate/PlateHatsuburi.cs new file mode 100644 index 000000000..a34f66512 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateHatsuburi.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateHatsuburi : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 65; } } + public override int OldStrReq{ get{ return 65; } } + + public override int ArmorBase{ get{ return 4; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateHatsuburi() : base( 0x2775 ) + { + Weight = 5.0; + } + + public PlateHatsuburi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateHiroSode.cs b/Scripts/Items/Armor/Plate/PlateHiroSode.cs new file mode 100644 index 000000000..f67981cd4 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateHiroSode.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateHiroSode : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 75; } } + + public override int AosStrReq{ get{ return 75; } } + public override int OldStrReq{ get{ return 75; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateHiroSode() : base( 0x2780 ) + { + Weight = 3.0; + } + + public PlateHiroSode( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateLegs.cs b/Scripts/Items/Armor/Plate/PlateLegs.cs new file mode 100644 index 000000000..0f68e6fe6 --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateLegs.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1411, 0x141a )] + public class PlateLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 90; } } + + public override int OldStrReq{ get{ return 60; } } + public override int OldDexBonus{ get{ return -6; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateLegs() : base( 0x1411 ) + { + Weight = 7.0; + } + + public PlateLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateMempo.cs b/Scripts/Items/Armor/Plate/PlateMempo.cs new file mode 100644 index 000000000..a7b9cc34c --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateMempo.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateMempo : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 60; } } + public override int InitMaxHits{ get{ return 70; } } + + public override int AosStrReq{ get{ return 50; } } + public override int OldStrReq{ get{ return 50; } } + + public override int ArmorBase{ get{ return 4; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateMempo() : base( 0x2779 ) + { + Weight = 3.0; + } + + public PlateMempo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/PlateSuneate.cs b/Scripts/Items/Armor/Plate/PlateSuneate.cs new file mode 100644 index 000000000..0e2bdd7ee --- /dev/null +++ b/Scripts/Items/Armor/Plate/PlateSuneate.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class PlateSuneate : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 80; } } + public override int OldStrReq{ get{ return 80; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public PlateSuneate() : base( 0x2788 ) + { + Weight = 7.0; + } + + public PlateSuneate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/SmallPlateJingasa.cs b/Scripts/Items/Armor/Plate/SmallPlateJingasa.cs new file mode 100644 index 000000000..8cf5a63fe --- /dev/null +++ b/Scripts/Items/Armor/Plate/SmallPlateJingasa.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class SmallPlateJingasa : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 7; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 55; } } + + public override int ArmorBase{ get{ return 4; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public SmallPlateJingasa() : base( 0x2784 ) + { + Weight = 5.0; + } + + public SmallPlateJingasa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/StandardPlateKabuto.cs b/Scripts/Items/Armor/Plate/StandardPlateKabuto.cs new file mode 100644 index 000000000..38626d9b1 --- /dev/null +++ b/Scripts/Items/Armor/Plate/StandardPlateKabuto.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StandardPlateKabuto : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 2; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 2; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 60; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 70; } } + public override int OldStrReq{ get{ return 70; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public StandardPlateKabuto() : base( 0x2789 ) + { + Weight = 6.0; + } + + public StandardPlateKabuto( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/WoodlandArms.cs b/Scripts/Items/Armor/Plate/WoodlandArms.cs new file mode 100644 index 000000000..cde2ecae3 --- /dev/null +++ b/Scripts/Items/Armor/Plate/WoodlandArms.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B6C, 0x3163 )] + public class WoodlandArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 80; } } + public override int OldStrReq{ get{ return 80; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public WoodlandArms() : base( 0x2B6C ) + { + Weight = 5.0; + } + + public WoodlandArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/WoodlandChest.cs b/Scripts/Items/Armor/Plate/WoodlandChest.cs new file mode 100644 index 000000000..140ba1aa7 --- /dev/null +++ b/Scripts/Items/Armor/Plate/WoodlandChest.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B67, 0x315E )] + public class WoodlandChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 95; } } + public override int OldStrReq{ get{ return 95; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public WoodlandChest() : base( 0x2B67 ) + { + Weight = 8.0; + } + + public WoodlandChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/WoodlandGloves.cs b/Scripts/Items/Armor/Plate/WoodlandGloves.cs new file mode 100644 index 000000000..f52b7deaa --- /dev/null +++ b/Scripts/Items/Armor/Plate/WoodlandGloves.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B6A, 0x3161 )] + public class WoodlandGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 70; } } + public override int OldStrReq{ get{ return 70; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public WoodlandGloves() : base( 0x2B6A ) + { + Weight = 2.0; + } + + public WoodlandGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/WoodlandGorget.cs b/Scripts/Items/Armor/Plate/WoodlandGorget.cs new file mode 100644 index 000000000..f58e06ba5 --- /dev/null +++ b/Scripts/Items/Armor/Plate/WoodlandGorget.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B69, 0x3160 )] + public class WoodlandGorget : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 45; } } + public override int OldStrReq{ get{ return 45; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public WoodlandGorget() : base( 0x2B69 ) + { + Weight = 2.0; + } + + public WoodlandGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Plate/WoodlandLegs.cs b/Scripts/Items/Armor/Plate/WoodlandLegs.cs new file mode 100644 index 000000000..e3ad1c567 --- /dev/null +++ b/Scripts/Items/Armor/Plate/WoodlandLegs.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B6B, 0x3162 )] + public class WoodlandLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 2; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 90; } } + public override int OldStrReq{ get{ return 90; } } + + public override int ArmorBase{ get{ return 40; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public WoodlandLegs() : base( 0x2B6B ) + { + Weight = 8.0; + } + + public WoodlandLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ranger/RangerArms.cs b/Scripts/Items/Armor/Ranger/RangerArms.cs new file mode 100644 index 000000000..17c6381c2 --- /dev/null +++ b/Scripts/Items/Armor/Ranger/RangerArms.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13dc, 0x13d4 )] + public class RangerArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041493; } } // studded sleeves, ranger armor + + [Constructable] + public RangerArms() : base( 0x13DC ) + { + Weight = 4.0; + Hue = 0x59C; + } + + public RangerArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 4.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ranger/RangerChest.cs b/Scripts/Items/Armor/Ranger/RangerChest.cs new file mode 100644 index 000000000..3114a071c --- /dev/null +++ b/Scripts/Items/Armor/Ranger/RangerChest.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13db, 0x13e2 )] + public class RangerChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 35; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041497; } } // studded tunic, ranger armor + + [Constructable] + public RangerChest() : base( 0x13DB ) + { + Weight = 8.0; + Hue = 0x59C; + } + + public RangerChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 8.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ranger/RangerGloves.cs b/Scripts/Items/Armor/Ranger/RangerGloves.cs new file mode 100644 index 000000000..5e9a8cfa0 --- /dev/null +++ b/Scripts/Items/Armor/Ranger/RangerGloves.cs @@ -0,0 +1,51 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13d5, 0x13dd )] + public class RangerGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041494; } } // studded gloves, ranger armor + + [Constructable] + public RangerGloves() : base( 0x13D5 ) + { + Weight = 1.0; + Hue = 0x59C; + } + + public RangerGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ranger/RangerGorget.cs b/Scripts/Items/Armor/Ranger/RangerGorget.cs new file mode 100644 index 000000000..5166be751 --- /dev/null +++ b/Scripts/Items/Armor/Ranger/RangerGorget.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class RangerGorget : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041495; } } // studded gorget, ranger armor + + [Constructable] + public RangerGorget() : base( 0x13D6 ) + { + Weight = 1.0; + Hue = 0x59C; + } + + public RangerGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ranger/RangerLegs.cs b/Scripts/Items/Armor/Ranger/RangerLegs.cs new file mode 100644 index 000000000..57ecdb7ab --- /dev/null +++ b/Scripts/Items/Armor/Ranger/RangerLegs.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13da, 0x13e1 )] + public class RangerLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1041496; } } // studded leggings, ranger armor + + [Constructable] + public RangerLegs() : base( 0x13DA ) + { + Weight = 3.0; + Hue = 0x59C; + } + + public RangerLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ring/RingmailArms.cs b/Scripts/Items/Armor/Ring/RingmailArms.cs new file mode 100644 index 000000000..b1a899326 --- /dev/null +++ b/Scripts/Items/Armor/Ring/RingmailArms.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13ee, 0x13ef )] + public class RingmailArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 1; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 22; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Ringmail; } } + + [Constructable] + public RingmailArms() : base( 0x13EE ) + { + Weight = 15.0; + } + + public RingmailArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 15.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ring/RingmailChest.cs b/Scripts/Items/Armor/Ring/RingmailChest.cs new file mode 100644 index 000000000..c7843814e --- /dev/null +++ b/Scripts/Items/Armor/Ring/RingmailChest.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13ec, 0x13ed )] + public class RingmailChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 1; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -2; } } + + public override int ArmorBase{ get{ return 22; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Ringmail; } } + + [Constructable] + public RingmailChest() : base( 0x13EC ) + { + Weight = 15.0; + } + + public RingmailChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 15.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ring/RingmailGloves.cs b/Scripts/Items/Armor/Ring/RingmailGloves.cs new file mode 100644 index 000000000..0ac4b6106 --- /dev/null +++ b/Scripts/Items/Armor/Ring/RingmailGloves.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13eb, 0x13f2 )] + public class RingmailGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 1; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 22; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Ringmail; } } + + [Constructable] + public RingmailGloves() : base( 0x13EB ) + { + Weight = 2.0; + } + + public RingmailGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Ring/RingmailLegs.cs b/Scripts/Items/Armor/Ring/RingmailLegs.cs new file mode 100644 index 000000000..59f9b9573 --- /dev/null +++ b/Scripts/Items/Armor/Ring/RingmailLegs.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13f0, 0x13f1 )] + public class RingmailLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 1; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 22; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Ringmail; } } + + [Constructable] + public RingmailLegs() : base( 0x13F0 ) + { + Weight = 15.0; + } + + public RingmailLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/FemaleStuddedChest.cs b/Scripts/Items/Armor/Studded/FemaleStuddedChest.cs new file mode 100644 index 000000000..7c5fc5189 --- /dev/null +++ b/Scripts/Items/Armor/Studded/FemaleStuddedChest.cs @@ -0,0 +1,55 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c02, 0x1c03 )] + public class FemaleStuddedChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 35; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public FemaleStuddedChest() : base( 0x1C02 ) + { + Weight = 6.0; + } + + public FemaleStuddedChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 6.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/HideChest.cs b/Scripts/Items/Armor/Studded/HideChest.cs new file mode 100644 index 000000000..98561a393 --- /dev/null +++ b/Scripts/Items/Armor/Studded/HideChest.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B74, 0x316B )] + public class HideChest : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public HideChest() : base( 0x2B74 ) + { + Weight = 6.0; + } + + public HideChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/HideFemaleChest.cs b/Scripts/Items/Armor/Studded/HideFemaleChest.cs new file mode 100644 index 000000000..56ad99306 --- /dev/null +++ b/Scripts/Items/Armor/Studded/HideFemaleChest.cs @@ -0,0 +1,56 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B79, 0x3170 )] + public class HideFemaleChest : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 35; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public HideFemaleChest() : base( 0x2B79 ) + { + Weight = 6.0; + } + + public HideFemaleChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/HideGloves.cs b/Scripts/Items/Armor/Studded/HideGloves.cs new file mode 100644 index 000000000..e5d5afcf8 --- /dev/null +++ b/Scripts/Items/Armor/Studded/HideGloves.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B75, 0x316C )] + public class HideGloves : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 15; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public HideGloves() : base( 0x2B75 ) + { + Weight = 2.0; + } + + public HideGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/HideGorget.cs b/Scripts/Items/Armor/Studded/HideGorget.cs new file mode 100644 index 000000000..6b944c582 --- /dev/null +++ b/Scripts/Items/Armor/Studded/HideGorget.cs @@ -0,0 +1,55 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B76, 0x316D )] + public class HideGorget : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 15; } } + public override int OldStrReq{ get{ return 15; } } + + public override int ArmorBase{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public HideGorget() : base( 0x2B76 ) + { + Weight = 3.0; + } + + public HideGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/HidePants.cs b/Scripts/Items/Armor/Studded/HidePants.cs new file mode 100644 index 000000000..d11fa2412 --- /dev/null +++ b/Scripts/Items/Armor/Studded/HidePants.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B78, 0x316F )] + public class HidePants : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public HidePants() : base( 0x2B78 ) + { + Weight = 5.0; + } + + public HidePants( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/HidePauldrons.cs b/Scripts/Items/Armor/Studded/HidePauldrons.cs new file mode 100644 index 000000000..f1f06a503 --- /dev/null +++ b/Scripts/Items/Armor/Studded/HidePauldrons.cs @@ -0,0 +1,54 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2B77, 0x316E )] + public class HidePauldrons : BaseArmor + { + public override Race RequiredRace { get { return Race.Elf; } } + + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 4; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 2; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public HidePauldrons() : base( 0x2B77 ) + { + Weight = 4.0; + } + + public HidePauldrons( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedArms.cs b/Scripts/Items/Armor/Studded/StuddedArms.cs new file mode 100644 index 000000000..ef0d72286 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedArms.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13dc, 0x13d4 )] + public class StuddedArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public StuddedArms() : base( 0x13DC ) + { + Weight = 4.0; + } + + public StuddedArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 4.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedBustierArms.cs b/Scripts/Items/Armor/Studded/StuddedBustierArms.cs new file mode 100644 index 000000000..499db375f --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedBustierArms.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1c0c, 0x1c0d )] + public class StuddedBustierArms : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 35; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + public override bool AllowMaleWearer{ get{ return false; } } + + [Constructable] + public StuddedBustierArms() : base( 0x1C0C ) + { + Weight = 1.0; + } + + public StuddedBustierArms( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedChest.cs b/Scripts/Items/Armor/Studded/StuddedChest.cs new file mode 100644 index 000000000..868212069 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedChest.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13db, 0x13e2 )] + public class StuddedChest : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 35; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public StuddedChest() : base( 0x13DB ) + { + Weight = 8.0; + } + + public StuddedChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 8.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedDo.cs b/Scripts/Items/Armor/Studded/StuddedDo.cs new file mode 100644 index 000000000..6f55002c4 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedDo.cs @@ -0,0 +1,47 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StuddedDo : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 55; } } + public override int OldStrReq{ get{ return 55; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public StuddedDo() : base( 0x27C7 ) + { + Weight = 8.0; + } + + public StuddedDo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedGloves.cs b/Scripts/Items/Armor/Studded/StuddedGloves.cs new file mode 100644 index 000000000..caed71ea8 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedGloves.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13d5, 0x13dd )] + public class StuddedGloves : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public StuddedGloves() : base( 0x13D5 ) + { + Weight = 1.0; + } + + public StuddedGloves( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedGorget.cs b/Scripts/Items/Armor/Studded/StuddedGorget.cs new file mode 100644 index 000000000..8608a2bcf --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedGorget.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StuddedGorget : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public StuddedGorget() : base( 0x13D6 ) + { + Weight = 1.0; + } + + public StuddedGorget( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedHaidate.cs b/Scripts/Items/Armor/Studded/StuddedHaidate.cs new file mode 100644 index 000000000..2472b7fa0 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedHaidate.cs @@ -0,0 +1,47 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StuddedHaidate : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 30; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public StuddedHaidate() : base( 0x278B ) + { + Weight = 5.0; + } + + public StuddedHaidate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedHiroSode.cs b/Scripts/Items/Armor/Studded/StuddedHiroSode.cs new file mode 100644 index 000000000..25adbc0b2 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedHiroSode.cs @@ -0,0 +1,47 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StuddedHiroSode : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 55; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 30; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public StuddedHiroSode() : base( 0x277F ) + { + Weight = 1.0; + } + + public StuddedHiroSode( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedLegs.cs b/Scripts/Items/Armor/Studded/StuddedLegs.cs new file mode 100644 index 000000000..340552016 --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedLegs.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13da, 0x13e1 )] + public class StuddedLegs : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.Half; } } + + [Constructable] + public StuddedLegs() : base( 0x13DA ) + { + Weight = 5.0; + } + + public StuddedLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedMempo.cs b/Scripts/Items/Armor/Studded/StuddedMempo.cs new file mode 100644 index 000000000..afc66fe1a --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedMempo.cs @@ -0,0 +1,47 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StuddedMempo : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 30; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public StuddedMempo() : base( 0x279D ) + { + Weight = 2.0; + } + + public StuddedMempo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Armor/Studded/StuddedSuneate.cs b/Scripts/Items/Armor/Studded/StuddedSuneate.cs new file mode 100644 index 000000000..0885969df --- /dev/null +++ b/Scripts/Items/Armor/Studded/StuddedSuneate.cs @@ -0,0 +1,47 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class StuddedSuneate : BaseArmor + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 30; } } + public override int OldStrReq{ get{ return 30; } } + + public override int ArmorBase{ get{ return 3; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public StuddedSuneate() : base( 0x27D2 ) + { + Weight = 5.0; + } + + public StuddedSuneate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/BonePile.cs b/Scripts/Items/Body Parts/BonePile.cs new file mode 100644 index 000000000..63b60764b --- /dev/null +++ b/Scripts/Items/Body Parts/BonePile.cs @@ -0,0 +1,45 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x1B09, 0x1B10 )] + public class BonePile : Item, IScissorable + { + [Constructable] + public BonePile( ) : base( 0x1B09 + Utility.Random( 8 ) ) + { + Stackable = false; + Weight = 10.0; + } + + public BonePile( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) + return false; + + base.ScissorHelper( from, new Bone(), Utility.RandomMinMax( 10, 15 ) ); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/Head.cs b/Scripts/Items/Body Parts/Head.cs new file mode 100644 index 000000000..d79875ae9 --- /dev/null +++ b/Scripts/Items/Body Parts/Head.cs @@ -0,0 +1,128 @@ +using System; +using Server; + +namespace Server.Items +{ + public enum HeadType + { + Regular, + Duel, + Tournament + } + + public class Head : Item + { + private string m_PlayerName; + private HeadType m_HeadType; + + [CommandProperty( AccessLevel.GameMaster )] + public string PlayerName + { + get { return m_PlayerName; } + set { m_PlayerName = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public HeadType HeadType + { + get { return m_HeadType; } + set { m_HeadType = value; } + } + + public override string DefaultName + { + get + { + if ( m_PlayerName == null ) + return base.DefaultName; + + switch ( m_HeadType ) + { + default: + return String.Format( "the head of {0}", m_PlayerName ); + + case HeadType.Duel: + return String.Format( "the head of {0}, taken in a duel", m_PlayerName ); + + case HeadType.Tournament: + return String.Format( "the head of {0}, taken in a tournament", m_PlayerName ); + } + } + } + + [Constructable] + public Head() + : this( null ) + { + } + + [Constructable] + public Head( string playerName ) + : this( HeadType.Regular, playerName ) + { + } + + [Constructable] + public Head( HeadType headType, string playerName ) + : base( 0x1DA0 ) + { + m_HeadType = headType; + m_PlayerName = playerName; + } + + public Head( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (string) m_PlayerName ); + writer.WriteEncodedInt( (int) m_HeadType ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + m_PlayerName = reader.ReadString(); + m_HeadType = (HeadType) reader.ReadEncodedInt(); + break; + + case 0: + string format = this.Name; + + if ( format != null ) + { + if ( format.StartsWith( "the head of " ) ) + format = format.Substring( "the head of ".Length ); + + if ( format.EndsWith( ", taken in a duel" ) ) + { + format = format.Substring( 0, format.Length - ", taken in a duel".Length ); + m_HeadType = HeadType.Duel; + } + else if ( format.EndsWith( ", taken in a tournament" ) ) + { + format = format.Substring( 0, format.Length - ", taken in a tournament".Length ); + m_HeadType = HeadType.Tournament; + } + } + + m_PlayerName = format; + this.Name = null; + + break; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/LeftArm.cs b/Scripts/Items/Body Parts/LeftArm.cs new file mode 100644 index 000000000..873bfda17 --- /dev/null +++ b/Scripts/Items/Body Parts/LeftArm.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LeftArm : Item + { + [Constructable] + public LeftArm() : base( 0x1DA1 ) + { + } + + public LeftArm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/LeftLeg.cs b/Scripts/Items/Body Parts/LeftLeg.cs new file mode 100644 index 000000000..06d33b4ad --- /dev/null +++ b/Scripts/Items/Body Parts/LeftLeg.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LeftLeg : Item + { + [Constructable] + public LeftLeg() : base( 0x1DA3 ) + { + } + + public LeftLeg( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/RibCage.cs b/Scripts/Items/Body Parts/RibCage.cs new file mode 100644 index 000000000..af86afbb3 --- /dev/null +++ b/Scripts/Items/Body Parts/RibCage.cs @@ -0,0 +1,45 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x1B17, 0x1B18 )] + public class RibCage : Item, IScissorable + { + [Constructable] + public RibCage() : base( 0x1B17 + Utility.Random( 2 ) ) + { + Stackable = false; + Weight = 5.0; + } + + public RibCage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) + return false; + + base.ScissorHelper( from, new Bone(), Utility.RandomMinMax( 3, 5 ) ); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/RightArm.cs b/Scripts/Items/Body Parts/RightArm.cs new file mode 100644 index 000000000..3d0778240 --- /dev/null +++ b/Scripts/Items/Body Parts/RightArm.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RightArm : Item + { + [Constructable] + public RightArm() : base( 0x1DA2 ) + { + } + + public RightArm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/RightLeg.cs b/Scripts/Items/Body Parts/RightLeg.cs new file mode 100644 index 000000000..7de41e98c --- /dev/null +++ b/Scripts/Items/Body Parts/RightLeg.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RightLeg : Item + { + [Constructable] + public RightLeg() : base( 0x1DA4 ) + { + } + + public RightLeg( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Body Parts/Torso.cs b/Scripts/Items/Body Parts/Torso.cs new file mode 100644 index 000000000..7ff9c7351 --- /dev/null +++ b/Scripts/Items/Body Parts/Torso.cs @@ -0,0 +1,32 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Torso : Item + { + [Constructable] + public Torso() : base( 0x1D9F ) + { + Weight = 2.0; + } + + public Torso( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/BaseBook.cs b/Scripts/Items/Books/BaseBook.cs new file mode 100644 index 000000000..6ff616815 --- /dev/null +++ b/Scripts/Items/Books/BaseBook.cs @@ -0,0 +1,466 @@ +using System; +using System.IO; +using System.Text; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class BookPageInfo + { + private string[] m_Lines; + + public string[] Lines + { + get + { + return m_Lines; + } + set + { + m_Lines = value; + } + } + + public BookPageInfo() + { + m_Lines = new string[0]; + } + + public BookPageInfo( params string[] lines ) + { + m_Lines = lines; + } + + public BookPageInfo( GenericReader reader ) + { + int length = reader.ReadInt(); + + m_Lines = new string[length]; + + for ( int i = 0; i < m_Lines.Length; ++i ) + m_Lines[i] = Utility.Intern( reader.ReadString() ); + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( m_Lines.Length ); + + for ( int i = 0; i < m_Lines.Length; ++i ) + writer.Write( m_Lines[i] ); + } + } + + public class BaseBook : Item + { + private string m_Title; + private string m_Author; + private BookPageInfo[] m_Pages; + private bool m_Writable; + + [CommandProperty( AccessLevel.GameMaster )] + public string Title + { + get { return m_Title; } + set { m_Title = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Author + { + get { return m_Author; } + set { m_Author = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Writable + { + get { return m_Writable; } + set { m_Writable = value; } + } + + [CommandProperty(AccessLevel.GameMaster)] + public int PagesCount + { + get { return m_Pages.Length; } + } + + public BookPageInfo[] Pages + { + get { return m_Pages; } + } + + [Constructable] + public BaseBook( int itemID ) : this( itemID, 20, true ) + { + } + + [Constructable] + public BaseBook( int itemID, int pageCount, bool writable ) : this( itemID, null, null, pageCount, writable ) + { + } + + [Constructable] + public BaseBook( int itemID, string title, string author, int pageCount, bool writable ) : base( itemID ) + { + m_Title = title; + m_Author = author; + m_Writable = writable; + + BookContent content = this.DefaultContent; + + if ( content == null ) + { + m_Pages = new BookPageInfo[pageCount]; + + for ( int i = 0; i < m_Pages.Length; ++i ) + m_Pages[i] = new BookPageInfo(); + } + else + { + m_Pages = content.Copy(); + } + } + + // Intended for defined books only + public BaseBook( int itemID, bool writable ) : base( itemID ) + { + m_Writable = writable; + + BookContent content = this.DefaultContent; + + if ( content == null ) + { + m_Pages = new BookPageInfo[0]; + } + else + { + m_Title = content.Title; + m_Author = content.Author; + m_Pages = content.Copy(); + } + } + + public virtual BookContent DefaultContent{ get{ return null; } } + + public BaseBook( Serial serial ) : base( serial ) + { + } + + [Flags] + private enum SaveFlags + { + None = 0x00, + Title = 0x01, + Author = 0x02, + Writable = 0x04, + Content = 0x08 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + BookContent content = this.DefaultContent; + + SaveFlags flags = SaveFlags.None; + + if ( m_Title != ( content == null ? null : content.Title ) ) + flags |= SaveFlags.Title; + + if ( m_Author != ( content == null ? null : content.Author ) ) + flags |= SaveFlags.Author; + + if ( m_Writable ) + flags |= SaveFlags.Writable; + + if ( content == null || !content.IsMatch( m_Pages ) ) + flags |= SaveFlags.Content; + + writer.Write( (int) 3 ); // version + + writer.Write( (byte) flags ); + + if ( (flags & SaveFlags.Title) != 0 ) + writer.Write( m_Title ); + + if ( (flags & SaveFlags.Author) != 0 ) + writer.Write( m_Author ); + + if ( (flags & SaveFlags.Content) != 0 ) + { + writer.WriteEncodedInt( m_Pages.Length ); + + for ( int i = 0; i < m_Pages.Length; ++i ) + m_Pages[i].Serialize( writer ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + case 2: + { + BookContent content = this.DefaultContent; + + SaveFlags flags = (SaveFlags) reader.ReadByte(); + + if ( (flags & SaveFlags.Title) != 0 ) + m_Title = Utility.Intern( reader.ReadString() ); + else if ( content != null ) + m_Title = content.Title; + + if ( (flags & SaveFlags.Author) != 0 ) + m_Author = reader.ReadString(); + else if ( content != null ) + m_Author = content.Author; + + m_Writable = ( flags & SaveFlags.Writable ) != 0; + + if ( (flags & SaveFlags.Content) != 0 ) + { + m_Pages = new BookPageInfo[reader.ReadEncodedInt()]; + + for ( int i = 0; i < m_Pages.Length; ++i ) + m_Pages[i] = new BookPageInfo( reader ); + } + else + { + if ( content != null ) + m_Pages = content.Copy(); + else + m_Pages = new BookPageInfo[0]; + } + + break; + } + case 1: + case 0: + { + m_Title = reader.ReadString(); + m_Author = reader.ReadString(); + m_Writable = reader.ReadBool(); + + if ( version == 0 || reader.ReadBool() ) + { + m_Pages = new BookPageInfo[reader.ReadInt()]; + + for ( int i = 0; i < m_Pages.Length; ++i ) + m_Pages[i] = new BookPageInfo( reader ); + } + else + { + BookContent content = this.DefaultContent; + + if ( content != null ) + m_Pages = content.Copy(); + else + m_Pages = new BookPageInfo[0]; + } + + break; + } + } + + if ( version < 3 && ( Weight == 1 || Weight == 2 ) ) + Weight = -1; + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( m_Title != null && m_Title.Length > 0 ) + list.Add( m_Title ); + else + base.AddNameProperty( list ); + } + + /*public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Title != null && m_Title.Length > 0 ) + list.Add( 1060658, "Title\t{0}", m_Title ); // ~1_val~: ~2_val~ + + if ( m_Author != null && m_Author.Length > 0 ) + list.Add( 1060659, "Author\t{0}", m_Author ); // ~1_val~: ~2_val~ + + if ( m_Pages != null && m_Pages.Length > 0 ) + list.Add( 1060660, "Pages\t{0}", m_Pages.Length ); // ~1_val~: ~2_val~ + }*/ + + public override void OnSingleClick ( Mobile from ) + { + LabelTo( from, "{0} by {1}", m_Title, m_Author ); + LabelTo( from, "[{0} pages]", m_Pages.Length ); + } + + public override void OnDoubleClick ( Mobile from ) + { + if ( m_Title == null && m_Author == null && m_Writable == true ) + { + Title = "a book"; + Author = from.Name; + } + + from.Send( new BookHeader( from, this ) ); + from.Send( new BookPageDetails( this ) ); + } + + public static void Initialize() + { + PacketHandlers.Register( 0xD4, 0, true, new OnPacketReceive( HeaderChange ) ); + PacketHandlers.Register( 0x66, 0, true, new OnPacketReceive( ContentChange ) ); + PacketHandlers.Register( 0x93, 99, true, new OnPacketReceive( OldHeaderChange ) ); + } + + public static void OldHeaderChange( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + BaseBook book = World.FindItem( pvSrc.ReadInt32() ) as BaseBook; + + if ( book == null || !book.Writable || !from.InRange( book.GetWorldLocation(), 1 ) ) + return; + + pvSrc.Seek( 4, SeekOrigin.Current ); // Skip flags and page count + + string title = pvSrc.ReadStringSafe( 60 ); + string author = pvSrc.ReadStringSafe( 30 ); + + book.Title = Utility.FixHtml( title ); + book.Author = Utility.FixHtml( author ); + } + + public static void HeaderChange( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + BaseBook book = World.FindItem( pvSrc.ReadInt32() ) as BaseBook; + + if ( book == null || !book.Writable || !from.InRange( book.GetWorldLocation(), 1 ) ) + return; + + pvSrc.Seek( 4, SeekOrigin.Current ); // Skip flags and page count + + int titleLength = pvSrc.ReadUInt16(); + + if ( titleLength > 60 ) + return; + + string title = pvSrc.ReadUTF8StringSafe( titleLength ); + + int authorLength = pvSrc.ReadUInt16(); + + if ( authorLength > 30 ) + return; + + string author = pvSrc.ReadUTF8StringSafe( authorLength ); + + book.Title = Utility.FixHtml( title ); + book.Author = Utility.FixHtml( author ); + } + + public static void ContentChange( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + BaseBook book = World.FindItem( pvSrc.ReadInt32() ) as BaseBook; + + if ( book == null || !book.Writable || !from.InRange( book.GetWorldLocation(), 1 ) ) + return; + + int pageCount = pvSrc.ReadUInt16(); + + if ( pageCount > book.PagesCount ) + return; + + for ( int i = 0; i < pageCount; ++i ) + { + int index = pvSrc.ReadUInt16(); + + if ( index >= 1 && index <= book.PagesCount ) + { + --index; + + int lineCount = pvSrc.ReadUInt16(); + + if ( lineCount <= 8 ) + { + string[] lines = new string[lineCount]; + + for ( int j = 0; j < lineCount; ++j ) + if ( (lines[j] = pvSrc.ReadUTF8StringSafe()).Length >= 80 ) + return; + + book.Pages[index].Lines = lines; + } + else + { + return; + } + } + else + { + return; + } + } + } + } + + public sealed class BookPageDetails : Packet + { + public BookPageDetails( BaseBook book ) : base( 0x66 ) + { + EnsureCapacity( 256 ); + + m_Stream.Write( (int) book.Serial ); + m_Stream.Write( (ushort) book.PagesCount ); + + for ( int i = 0; i < book.PagesCount; ++i ) + { + BookPageInfo page = book.Pages[i]; + + m_Stream.Write( (ushort) (i + 1) ); + m_Stream.Write( (ushort) page.Lines.Length ); + + for ( int j = 0; j < page.Lines.Length; ++j ) + { + byte[] buffer = Utility.UTF8.GetBytes( page.Lines[j] ); + + m_Stream.Write( buffer, 0, buffer.Length ); + m_Stream.Write( (byte) 0 ); + } + } + } + } + + public sealed class BookHeader : Packet + { + public BookHeader( Mobile from, BaseBook book ) : base ( 0xD4 ) + { + string title = book.Title == null ? "" : book.Title; + string author = book.Author == null ? "" : book.Author; + + byte[] titleBuffer = Utility.UTF8.GetBytes( title ); + byte[] authorBuffer = Utility.UTF8.GetBytes( author ); + + EnsureCapacity( 15 + titleBuffer.Length + authorBuffer.Length ); + + m_Stream.Write( (int) book.Serial ); + m_Stream.Write( (bool) true ); + m_Stream.Write( (bool) book.Writable && from.InRange( book.GetWorldLocation(), 1 ) ); + m_Stream.Write( (ushort) book.PagesCount ); + + m_Stream.Write( (ushort) (titleBuffer.Length + 1) ); + m_Stream.Write( titleBuffer, 0, titleBuffer.Length ); + m_Stream.Write( (byte) 0 ); // terminate + + m_Stream.Write( (ushort) (authorBuffer.Length + 1) ); + m_Stream.Write( authorBuffer, 0, authorBuffer.Length ); + m_Stream.Write( (byte) 0 ); // terminate + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/BlueBook.cs b/Scripts/Items/Books/BlueBook.cs new file mode 100644 index 000000000..6814d72b6 --- /dev/null +++ b/Scripts/Items/Books/BlueBook.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BlueBook : BaseBook + { + [Constructable] + public BlueBook() : base( 0xFF2 ) + { + } + + [Constructable] + public BlueBook( int pageCount, bool writable ) : base( 0xFF2, pageCount, writable ) + { + } + + [Constructable] + public BlueBook( string title, string author, int pageCount, bool writable ) : base( 0xFF2, title, author, pageCount, writable ) + { + } + + // Intended for defined books only + public BlueBook( bool writable ) : base( 0xFF2, writable ) + { + } + + public BlueBook( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/BrownBook.cs b/Scripts/Items/Books/BrownBook.cs new file mode 100644 index 000000000..b8b43ada2 --- /dev/null +++ b/Scripts/Items/Books/BrownBook.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BrownBook : BaseBook + { + [Constructable] + public BrownBook() : base( 0xFEF ) + { + } + + [Constructable] + public BrownBook( int pageCount, bool writable ) : base( 0xFEF, pageCount, writable ) + { + } + + [Constructable] + public BrownBook( string title, string author, int pageCount, bool writable ) : base( 0xFEF, title, author, pageCount, writable ) + { + } + + // Intended for defined books only + public BrownBook( bool writable ) : base( 0xFEF, writable ) + { + } + + public BrownBook( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/BlackthornWelcomeBook.cs b/Scripts/Items/Books/Defined/BlackthornWelcomeBook.cs new file mode 100644 index 000000000..f4e8ba2a0 --- /dev/null +++ b/Scripts/Items/Books/Defined/BlackthornWelcomeBook.cs @@ -0,0 +1,297 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BlackthornWelcomeBook : RedBook + { + public static readonly BookContent Content = new BookContent + ( + "A Welcome", "Lord Blackthorn", + new BookPageInfo + ( + " Greetings to you,", + "new member of the", + "Trusted.", + " You now read these", + "words because you", + "have been deemed", + "worthy to join the", + "ranks of" + ), + new BookPageInfo + ( + "Britannia's defenders.", + "Some will call you a", + "betrayer of mankind, I", + "say they are", + "misguided. I call you", + "a defender for Sosaria", + "needs saving from", + "itself." + ), + new BookPageInfo + ( + " The forces of order", + "once ruled our world.", + "Like a great", + "darkness over our", + "lives we lived under", + "the oppressive watch", + "of a king who", + "dictated our actions" + ), + new BookPageInfo + ( + "and passed judgement", + "on our character. He", + "suppressed our way of", + "life by denying our", + "freedom and the", + "ability to determine", + "who we are and what", + "we stand for. Even" + ), + new BookPageInfo + ( + "today in the absense", + "of this man we still", + "see the symbol of his", + "tyranny, we watch his", + "personal guards patrol", + "the cities to", + "intimidate us, and we", + "feel his laws like a" + ), + new BookPageInfo + ( + "vice on our lives.", + " You are here", + "because you choose to", + "be free. Like many", + "Britannians you have", + "felt the opression of", + "one man's ideas", + "weighing down upon" + ), + new BookPageInfo + ( + "you like chains. You", + "have felt the embrace", + "of fear, wondering if", + "you face consequence", + "for simply having", + "ideas not in harmony", + "with those forced upon", + "you. You have seen" + ), + new BookPageInfo + ( + "men fight and die for", + "the principles of a", + "zealot and wondered,", + "'Who will fight for", + "my principles should", + "they be opposed?'", + "You tire of living", + "under the shadow of" + ), + new BookPageInfo + ( + "dreams that do not", + "belong to you. And", + "most of all, you have", + "wondered what you", + "can do to live free.", + " Your journey to", + "freedom begins here.", + " I, like you, once" + ), + new BookPageInfo + ( + "desired my freedom", + "from the limits placed", + "on me. I watched in", + "disgust as this world", + "became engrossed with", + "the preaching of", + "virtue and none of the", + "practice. I held my" + ), + new BookPageInfo + ( + "convictions in check,", + "fearful of the reaction", + "of men blinded by", + "belief. I forced", + "myself to bury the", + "very idealogy that", + "made me an individual.", + "I was fortunate that" + ), + new BookPageInfo + ( + "a being of unique", + "power and", + "unimaginable", + "intelligence saw", + "through to the true", + "person I was, the", + "person I was meant to", + "be. Exodus found" + ), + new BookPageInfo + ( + "within me a man of", + "free will,", + "determination, and", + "incomprehension for", + "the plight of", + "oppression forced on so", + "many Britannians.", + " Exodus has also" + ), + new BookPageInfo + ( + "chosen you because of", + "the strength of your", + "character.", + " Many of the men", + "who were once my", + "peers look upon me", + "and see a betrayer of", + "humanity. They" + ), + new BookPageInfo + ( + "claim my newfound", + "form is unnatural and", + "wrong. Because they", + "see the unknown in", + "me, they show fear.", + "They disapprove of", + "my choices and in", + "their ignorance see" + ), + new BookPageInfo + ( + "evil. Yet I hide my", + "true self no longer", + "from these men. My", + "thoughts and my", + "personal morality have", + "been liberated in the", + "face of the oppression", + "that once consumed me." + ), + new BookPageInfo + ( + "Where they see a", + "man no longer human.", + "I see a man that has", + "not betrayed his", + "humanity but has been", + "freed from it. This", + "is the power that has", + "been granted to me by" + ), + new BookPageInfo + ( + "Exodus. I have been", + "given my freedom. I", + "have been released", + "from my fears.", + " Exodus will give", + "you the power to", + "conquer your fears as", + "well." + ), + new BookPageInfo + ( + " When your fear", + "of this world is gone,", + "then the world truly", + "belongs to you in a", + "way it never has", + "before. You, trusted", + "one, will soon be given", + "a gift. Your body," + ), + new BookPageInfo + ( + "like mine, will be", + "enlightened and raised", + "to a level no mortal", + "can know. The power", + "to control your own", + "destiny will belong to", + "you for the first time", + "in your life." + ), + new BookPageInfo + ( + " You will, at long", + "last, be cleansed of", + "fear.", + " Together, with the", + "power of Exodus", + "behind us, we shall", + "finally wage war on", + "the oppression that" + ), + new BookPageInfo + ( + "once held us back", + "from our full", + "potential. We shall", + "claim this world, and", + "reshape it in an image", + "of freedom for all of", + "us. Those who once", + "told you who you are" + ), + new BookPageInfo + ( + "and how you should", + "live will no longer be", + "able to stand in the", + "way of your free will.", + "Many say you will", + "be abandoning your", + "humanity but in truth,", + "you will be more than" + ), + new BookPageInfo + ( + "human.", + " You will be freed." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public BlackthornWelcomeBook() : base( false ) + { + Hue = 0x89B; + } + + public BlackthornWelcomeBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/BookContent.cs b/Scripts/Items/Books/Defined/BookContent.cs new file mode 100644 index 000000000..f06b785e1 --- /dev/null +++ b/Scripts/Items/Books/Defined/BookContent.cs @@ -0,0 +1,61 @@ +using System; + +namespace Server.Items +{ + public class BookContent + { + private string m_Title; + private string m_Author; + + private BookPageInfo[] m_Pages; + + public string Title{ get{ return m_Title; } } + public string Author{ get{ return m_Author; } } + + public BookPageInfo[] Pages{ get{ return m_Pages; } } + + public BookContent( string title, string author, params BookPageInfo[] pages ) + { + m_Title = title; + m_Author = author; + m_Pages = pages; + } + + public BookPageInfo[] Copy() + { + BookPageInfo[] copy = new BookPageInfo[m_Pages.Length]; + + for ( int i = 0; i < copy.Length; ++i ) + copy[i] = new BookPageInfo( m_Pages[i].Lines ); + + return copy; + } + + public bool IsMatch( BookPageInfo[] cmp ) + { + if ( cmp.Length != m_Pages.Length ) + return false; + + for ( int i = 0; i < cmp.Length; ++i ) + { + string[] a = m_Pages[i].Lines; + string[] b = cmp[i].Lines; + + if ( a.Length != b.Length ) + { + return false; + } + else if ( a != b ) + { + for ( int j = 0; j < a.Length; ++j ) + { + if ( a[j] != b[j] ) + return false; + } + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/DrakovsJournal.cs b/Scripts/Items/Books/Defined/DrakovsJournal.cs new file mode 100644 index 000000000..46cf1b6ad --- /dev/null +++ b/Scripts/Items/Books/Defined/DrakovsJournal.cs @@ -0,0 +1,129 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DrakovsJournal : BlueBook + { + public static readonly BookContent Content = new BookContent + ( + "Drakov's Journal", "Drakov", + new BookPageInfo + ( + "My Master", + "", + "This journal was", + "found on one of", + "our controllers. It", + "seems he has lost", + "faith in you. Know", + "that he has been" + ), + new BookPageInfo + ( + "dealth with and will", + "never again speak", + "ill of you or our", + "cause.", + " -Galzon" + ), + new BookPageInfo + ( + "We have completted", + "construction of the", + "devices needed to", + "build the clockwork", + "overseers and minions", + "as per the request of", + "the Master. The", + "gargoyles have been" + ), + new BookPageInfo + ( + "most useful and their", + "knowledge of the", + "techniques for the", + "construction of these", + "creatures will serve", + "us well.", + " -----", + "I am not one to" + ), + new BookPageInfo + ( + "criticize the Master,", + "but I believe he may", + "have erred in his", + "decision to destroy", + "the wingless ones.", + "Already our forces", + "are weakened by the", + "constant attacks of" + ), + new BookPageInfo + ( + "the humans Their", + "strength and", + "unquestioning", + "compliance would", + "have made them very", + "useful in the fight", + "against the humans.", + "But the Master felt" + ), + new BookPageInfo + ( + "their presence to be", + "an annoyance and", + "a distraction to the", + "winged ones. It was", + "not difficult at all", + "to remove them from", + "this world. But now", + "I fear without more" + ), + new BookPageInfo + ( + "allies, willing or", + "not, we stand", + "little chance of", + "defeating the foul", + "humans from our", + "lands. Perhaps if", + "the Master had", + "shown a little" + ), + new BookPageInfo + ( + "mercy and forsight", + "we would not be", + "in such dire peril." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public DrakovsJournal() : base( false ) + { + } + + public DrakovsJournal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/FropozJournal.cs b/Scripts/Items/Books/Defined/FropozJournal.cs new file mode 100644 index 000000000..c2ef0600d --- /dev/null +++ b/Scripts/Items/Books/Defined/FropozJournal.cs @@ -0,0 +1,182 @@ +using System; +using Server; + +namespace Server.Items +{ + public class FropozJournal : RedBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal", "Fropoz", + new BookPageInfo + ( + "I have done as my", + "Master has", + "instructed me.", + "", + "The painted humans", + "have been driven into", + "Britannia and are even", + "now wreaking havoc" + ), + new BookPageInfo + ( + "across the land,", + "providing us with the", + "distraction my Master", + "requested. We", + "have provided them", + "with the masks", + "necessary to defeat", + "the orcs, thus" + ), + new BookPageInfo + ( + "causing even more", + "distress for the people", + "of Britannia. The", + "unsuspecting fools", + "are too busy dealing", + "with the orc hordes to", + "continue their", + "exploration of our" + ), + new BookPageInfo + ( + "lands. We are", + "safe...for now.", + " ----", + "The attacks", + "continue exactly as", + "planned. My Master", + "is pleased with my", + "work and we are" + ), + new BookPageInfo + ( + "closer to our goals than", + "ever before. The", + "gargoyles have proven", + "to be more troublesome", + "than we first", + "anticipated, but I", + "believe we can", + "subjugate them fully" + ), + new BookPageInfo + ( + "given enough time. It's", + "unfortunate that we", + "did not discover their", + "knowledge sooner.", + "Even now they", + "prepare our armies", + "for battle, but not", + "without resistance." + ), + new BookPageInfo + ( + "Now that some of", + "them know of the", + "other lands and of", + "humans, they will", + "double their efforts to", + "seek help. This", + "cannot be allowed.", + " -----" + ), + new BookPageInfo + ( + "Damn them!! The", + "humans proved", + "more resourcefull than", + "we thought them", + "capable of. Already", + "their homes are free", + "of orcs and savages", + "and they once again" + ), + new BookPageInfo + ( + "are treading in our", + "lands. We may have to", + "move sooner than we", + "thought. I will", + "prepar my brethern", + "and our golems.", + "Hopefully, we can", + "buy our Master some" + ), + new BookPageInfo + ( + "more time before the", + "humans discover us.", + " -----", + "It's too late. The", + "gargoyles whom have", + "evaded our capture", + "have opened the doors", + "to our land." + ), + new BookPageInfo + ( + "They pray the", + "humans will help", + "them, despite the", + "actions of their", + "cousins in Britannia. I", + "fear they are right.", + "I must go to warn", + "the MastKai Hohiro," + ), + new BookPageInfo + ( + ), + new BookPageInfo + ( + "10.11.2001", + "first one to be here", + "", + "Congrats. I didn't really", + "care to log on earlier,", + "nor did I come straight", + "here. 2pm, Magus" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public FropozJournal() : base( false ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( "Fropoz's Journal" ); + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, "Fropoz's Journal" ); + } + + public FropozJournal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/KaburJournal.cs b/Scripts/Items/Books/Defined/KaburJournal.cs new file mode 100644 index 000000000..ee7e9acdd --- /dev/null +++ b/Scripts/Items/Books/Defined/KaburJournal.cs @@ -0,0 +1,267 @@ +using System; +using Server; + +namespace Server.Items +{ + public class KaburJournal : RedBook + { + public static readonly BookContent Content = new BookContent + ( + "Journal", "Kabur", + new BookPageInfo + ( + "The campaign to slaughter", + "the Meer goes well.", + "Although they seem to", + "oppose the forces of", + "ours at every turn, we", + "still defeat them in", + "combat. Spies of the", + "Meer have been found and" + ), + new BookPageInfo + ( + "slain outside of the", + "fortress of ours. The", + "fools underestimate us.", + "We have the power of", + "Lord Exodus behind us.", + "Soon they will learn to", + "serve the Juka and I", + "shall carry the head of" + ), + new BookPageInfo + ( + "the wench, Dasha, on a", + "spike for all the warriors", + "of ours to share triumph", + "under.", + "", + "One of the warriors of", + "the Juka died today.", + "During the training" + ), + new BookPageInfo + ( + "exercises of ours he", + "spoke out in favor of", + "the warriors of the", + "Meer, saying that they", + "were indeed powerful and", + "would provide a challenge", + "to the Juka. A Juka in", + "fear is no Juka. I gave" + ), + new BookPageInfo + ( + "him the death of a", + "coward, outside of battle.", + "", + "More spies of the Meer", + "have been found around", + "the fortress of ours.", + "Many have been seen and", + "escaped the wrath of the" + ), + new BookPageInfo + ( + "warriors of ours. Those", + "who have been captured", + "and tortured have", + "revealed nothing to us,", + "even when subjected to", + "the spells of the females.", + " I know the Meer must", + "have plans against us if" + ), + new BookPageInfo + ( + "they send so many spies.", + " I may send the troops", + "of the Juka to invade", + "the camps of theirs as a", + "warning.", + "", + "I have met Dasha in", + "battle this day. The" + ), + new BookPageInfo + ( + "efforts of hers to draw", + "me into a Black Duel", + "were foolish. Had we", + "not been interrupted in", + "the cave I would have", + "ended the life of hers", + "but I will have to wait", + "for another battle. Lord" + ), + new BookPageInfo + ( + "Exodus has ordered more", + "patrols around the", + "fortress of ours. If", + "Dasha is any indication,", + "the Meer will strike soon.", + "", + "More Meer stand outside", + "of the fortress of ours" + ), + new BookPageInfo + ( + "than I have ever seen at", + "once. They must seek", + "vengeance for the", + "destruction of their", + "forest. Many Juka stand", + "ready at the base of the", + "mountain to face the", + "forces of theirs but" + ), + new BookPageInfo + ( + "today may be the final", + "battle. Exodus has", + "summoned me, I must", + "prepare.", + "", + "Dusk has passed and the", + "Juka now live in a new", + "age, a later time. I have" + ), + new BookPageInfo + ( + "just returned from", + "exploring the new world", + "that surrounds the", + "fortress of the Juka.", + "During the attack of the", + "Meer the madman", + "Adranath tried to destroy", + "the fortress of ours" + ), + new BookPageInfo + ( + "with great magic. At", + "once he was still and", + "light surrounded the", + "fortress. Everything", + "faded from view. When I", + "regained the senses of", + "mine I saw no sign of", + "the Meer but Dasha." + ), + new BookPageInfo + ( + "She has not been found", + "since this new being,", + "Blackthorn, blasted her", + "from the top of the", + "fortress.", + "The forest was gone, now", + "replaced by grasslands.", + "In the far distance I" + ), + new BookPageInfo + ( + "could see humans that", + "had covered the bodies of", + "theirs in marks. Even", + "Gargoyles populate this", + "place. Exodus has", + "explained to me that the", + "Juka and the fortress of", + "ours have been pulled" + ), + new BookPageInfo + ( + "forward in time. The", + "world we knew is now", + "thousands of years in the", + "past. Lord Exodus say", + "he has has saved the", + "Juka from extinction. I", + "do not want to believe", + "him. I asked this" + ), + new BookPageInfo + ( + "stranger about the Meer,", + "but he tells me a new", + "enemy remains to be", + "destroyed. It seems the", + "enemies of ours have", + "passed away to dust like", + "the forest." + ), + new BookPageInfo + ( + "I have spoken with other", + "Juka and I suspect I have", + "been told the truth. All", + "the Juka had powerful", + "dreams. In the dreams", + "of ours the Meer invaded", + "the fortress of ours and", + "a great battle took place." + ), + new BookPageInfo + ( + " All the Juka and all the", + "Meer perished and the", + "fortress was destroyed", + "from Adranath's spells. I", + "would not like to believe", + "that the Meer could ever", + "destroy us, but now it", + "seems we have seen a" + ), + new BookPageInfo + ( + "vision of the fate of", + "ours now lost in time. I", + "must now wonder if the", + "Meer did not die in the", + "battle with the Juka, how", + "did they die? And more", + "importantly, where is", + "Dasha?" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public KaburJournal() : base( false ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( "Khabur's Journal" ); + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, "Khabur's Journal" ); + } + + public KaburJournal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/LibraryBooks.cs b/Scripts/Items/Books/Defined/LibraryBooks.cs new file mode 100644 index 000000000..2dc92701e --- /dev/null +++ b/Scripts/Items/Books/Defined/LibraryBooks.cs @@ -0,0 +1,6211 @@ +using System; +using Server; + +namespace Server.Items +{ + #region A Grammar of Orcish + public class GrammarOfOrcish : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "A Grammar of Orcish", "Yorick of Yew", + new BookPageInfo + ( + "This volume, and", + "others in the series,", + "are sponsored by", + "donations from Lord", + "Blackthorn, ever a", + "supporter of", + "understanding the", + "other sentient races" + ), + new BookPageInfo + ( + "of Britannia.", + "-", + "", + " The Orcish tongue", + "may fall unpleasingly", + "'pon the ear, yet it", + "has within it a", + "complex grammar oft" + ), + new BookPageInfo + ( + "misunderstood by", + "those who merely", + "hear the few broken", + "words of English our", + "orcish brothers", + "manage without", + "education.", + " These are the basic" + ), + new BookPageInfo + ( + "rules of orcish:", + " Orcish has five", + "tenses: present, past,", + "future imperfect,", + "present interjectional,", + "and prehensile.", + " Examples: gugroflu,", + "gugrofloog, gugrobo," + ), + new BookPageInfo + ( + "gugroglu!, gugrogug.", + " All transitive verbs", + "in the prehensile", + "tense end in \"ug.\"", + " Examples:", + "urgleighug,", + "biggugdaghgug,", + "curdakalmug." + ), + new BookPageInfo + ( + " All present", + "interjectional", + "conjugations start", + "with the letter G", + "unless the contain the", + "third declensive", + "accent of the letter U.", + " Examples:" + ), + new BookPageInfo + ( + "ghothudunglug, but not", + "azhbuugub.", + " The past tense can", + "only refer to events", + "since the last meal,", + "but the prehensile", + "tense can refer to", + "any event within" + ), + new BookPageInfo + ( + "reach.", + " The present tense", + "is conjugated like the", + "future imperfect", + "tense, when the", + "interrogative mode is", + "used by pitching the", + "sound a quarter-tone" + ), + new BookPageInfo + ( + "higher.", + "Orcish hath no", + "concept of person, as", + "in first person, third", + "person, I, we, etc.", + " Orcish grammar", + "relies upon the three", + "cardinal rules of" + ), + new BookPageInfo + ( + "accretion, prefixing,", + "and agglutination, in", + "addition to pitch. In", + "the former, phonemes", + "combine into larger", + "words which may", + "contain full phrasal", + "significance. In the" + ), + new BookPageInfo + ( + "second, prefixing", + "specific phonetic", + "sounds changes the", + "subject of the", + "sentence into object,", + "interrogative,", + "addressed individual,", + "or dinner." + ), + new BookPageInfo + ( + " Agglutination occurs", + "whenever four of the", + "same letter are", + "present in a word, in", + "which case, any two", + "of them may be", + "removed or slurred.", + " Pitch changes the" + ), + new BookPageInfo + ( + "phoneme value of", + "individual syllables,", + "thus completely", + "altering what a word", + "may mean. The", + "classic example is", + "\"Aktgluthugrot", + "bigglogubuu" + ), + new BookPageInfo + ( + "dargilgaglug lublublub\"", + "which can mean \"You", + "are such a pretty", + "girl,\" \"My mother ate", + "your primroses,\" or", + "\"Jellyfish nose paints", + "alms potato,\"", + "depending on pitch." + ), + new BookPageInfo + ( + " Orcish poetry often", + "relies upon repeating", + "the same phrase in", + "multiple pitches, even", + "changing pitch", + "midword. None of", + "this great art is", + "translatable." + ), + new BookPageInfo + ( + " The orcish language", + "uses the following", + "vowels: ab, ad, ag, akt,", + "at, augh, auh, azh, e,", + "i, o, oo, u, uu. The", + "vowel sound a is not", + "recognized as a vowel", + "and does not exist in" + ), + new BookPageInfo + ( + "their alphabet.", + "The orcish alphabet is", + "best learned using the", + "classic rhyme", + "repeated at 23", + "different pitches:", + " Lugnog ghu blat", + "suggaroglug," + ), + new BookPageInfo + ( + "Gaghbuu dakdar ab", + "highugbo,", + " Gothnogbuim ad", + "gilgubbugbuilug", + "Bilgeaugh thurggulg", + "stuiggro!", + "", + "A translation of the" + ), + new BookPageInfo + ( + "first pitch:", + "Eat food, the first", + "letter is ab,", + "Kill people, next letter", + "is ad,", + "I forget the rest", + "But augh is in there", + "somewhere!" + ), + new BookPageInfo + ( + "", + " What follows is a", + "complete phonetic", + "library of the orcish", + "language:", + "ab, ad, ag, akt, alm,", + "at, augh, auh, azh,", + "ba, ba, bag, bar, baz," + ), + new BookPageInfo + ( + "bid, bilge, bo, bog, bog,", + "brui, bu, buad, bug,", + "bug, buil, buim, bum,", + "buo, buor, buu, ca,", + "car, clog, cro, cuk,", + "cur, da, dagh, dagh,", + "dak, dar, deak, der,", + "dil, dit, dor, dre, dri," + ), + new BookPageInfo + ( + "dru, du, dud, duf,", + "dug, dug, duh, dun,", + "eag, eg, egg, eichel,", + "ek, ep, ewk, faugh,", + "fid, flu, fog, foo,", + "foz, fruk, fu, fub,", + "fud, fun, fup, fur,", + "gaa, gag, gagh, gan," + ), + new BookPageInfo + ( + "gar, gh, gha, ghat,", + "ghed, ghig, gho, ghu,", + "gig, gil, gka, glu, glu,", + "glug, gna, gno, gnu,", + "gol, gom, goth, grunt,", + "grut, gu, gub, gub,", + "gug, gug, gugh, guk,", + "guk," + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GrammarOfOrcish() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public GrammarOfOrcish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region A Politic Call to Anarchy + public class CallToAnarchy : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "A Politic Call to Anarchy", "Lord Blackthorn", + new BookPageInfo + ( + " Let it never be said", + "that I have aught as", + "quarrel with my liege", + "Lord British, for", + "indeed we be of the", + "best of friends,", + "sharing amicable", + "games of chess 'pon a" + ), + new BookPageInfo + ( + "winter's night, and", + "talking at length into", + "the wee hours of the", + "issues that affect the", + "realm of Britannia.", + " Yet true friendship", + "doth not prevent true", + "philosophical" + ), + new BookPageInfo + ( + "disagreement either.", + "While I view with", + "approval my lord's", + "affection for his", + "carefully crafted", + "philosophy of the", + "Eight Virtues,", + "wherein moral" + ), + new BookPageInfo + ( + "behavior is", + "encouraged in the", + "populace, I view with", + "less approval the", + "expenditure of public", + "funds upon the", + "construction of", + "\"shrines\" to said" + ), + new BookPageInfo + ( + "ideals.", + " The issue is not one", + "of funds, however,", + "but a disagreement", + "most intellectual over", + "the proper way of", + "humankind in an", + "ethical sense. Surely" + ), + new BookPageInfo + ( + "freedom of decision", + "must be regarded as", + "paramount in any", + "such moral decision?", + "Though none fail to", + "censure the", + "murderer, a subtler", + "question arises when" + ), + new BookPageInfo + ( + "we ask if his", + "behavior would be", + "ethical if he were", + "forced to it.", + " I say to thee, the", + "reader, quite flatly,", + "that no ethical system", + "shall have sway over" + ), + new BookPageInfo + ( + "me unless it", + "convinceth me, for", + "that freely made", + "choice is to me the", + "sign that the system", + "hath validity.", + " Whereas the system", + "of \"Virtues\" that my" + ), + new BookPageInfo + ( + "liege espouses is", + "indeed a compilation", + "of commonly approved", + "virtues, I approve of", + "it. Where it seeks to", + "control the populace", + "and restrict their", + "diversity and their" + ), + new BookPageInfo + ( + "range of behaviors, I", + "quarrel with it. And", + "thus do I issue this", + "politic call to anarchy,", + "whilst humbly", + "begging forgiveness", + "of Lord British for", + "my impertinence:" + ), + new BookPageInfo + ( + " Celebrate thy", + "differences. Take", + "thy actions according", + "to thy own lights.", + "Question from what", + "source a law, a rule,", + "a judge, and a virtue", + "may arise. 'Twere" + ), + new BookPageInfo + ( + "possible (though I", + "suggest it not", + "seriously) that a", + "daemon planted the", + "seed of these", + "\"Virtues\" in my Lord", + "British's mind; 'twere", + "possible that the" + ), + new BookPageInfo + ( + "Shrines were but a", + "plan to destroy this", + "world. Thou canst not", + "know unless thou", + "questioneth, doubteth,", + "and in the end,", + "unless thou relyest", + "upon THYSELF and" + ), + new BookPageInfo + ( + "thy judgement.", + " I offer these words", + "as mere philosophical", + "musings for those", + "who seek", + "enlightenment, for", + "'tis the issue that", + "hath occupied mine" + ), + new BookPageInfo + ( + "interest and that of", + "Lord British for", + "some time now." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public CallToAnarchy() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public CallToAnarchy( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region A Primer on Arms and Weapons + public class ArmsAndWeaponsPrimer : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "A Primer on Arms and Weapons", "Martin", + new BookPageInfo + ( + " These are the", + "basic elements to", + "consider in assessing", + "a weapon, of which", + "all warriors who", + "regard themselves as", + "more than mere", + "mercenaries should be" + ), + new BookPageInfo + ( + "aware.", + " First and most", + "obvious is the amount", + "of damage that the", + "weapon may do", + "against unprotected", + "flesh. While 'tis this", + "which first attracts" + ), + new BookPageInfo + ( + "the attention of the", + "novice, 'tis a deadly", + "mistake to regard it", + "as the sole value of a", + "weapon. While it may", + "prove devastating", + "indeed as a means of", + "causing damage, a" + ), + new BookPageInfo + ( + "weapon must also", + "serve as stout shield", + "when engaged in", + "combat.", + " Hence the second", + "issue to which to pay", + "attention is the", + "amount of protection" + ), + new BookPageInfo + ( + "that a weapon may", + "offer. Pay close", + "attention to the guard", + "on it, if it be a blade,", + "or the stoutness of", + "its wood if it is a pole", + "arm.", + " Oft related to this" + ), + new BookPageInfo + ( + "is the weight of the", + "weapon, for a heavy", + "weapon is more", + "difficult to maneuver", + "to block with, though", + "it may do more", + "damage to thy", + "opponent." + ), + new BookPageInfo + ( + " If a weapon is too", + "heavy for the wielder", + "to move it freely,", + "they should choose", + "another and not", + "attempt to prove their", + "prowess by the size", + "of their sword." + ), + new BookPageInfo + ( + " The reach of a", + "weapon both increases", + "its defensive ability,", + "and renders it more", + "useful in open spaces", + "as it allows attack", + "against the opponent", + "without the need to" + ), + new BookPageInfo + ( + "close. But be aware of", + "the limitations of thy", + "weapon! For a", + "weapon with great", + "reach may be useless", + "in close quarters, for", + "lack of space to", + "maneuver it. Should" + ), + new BookPageInfo + ( + "that dagger-wielding", + "enemy close on thee", + "and thy halberd, 'tis", + "best to flee.", + " Lastly, a factor", + "that must always be", + "considered is the", + "condition of the" + ), + new BookPageInfo + ( + "weapon. It might be a", + "wondrous magical", + "blade of surpassing", + "sharpness and it may", + "leap to block blows", + "with a mind of its", + "own. It also might be", + "of such flimsy" + ), + new BookPageInfo + ( + "construction, or", + "damaged to such an", + "extent, that the first", + "time it clangs against", + "steel, 'twill shatter", + "into useless shards.", + " Seek ye a good", + "blacksmith should thy" + ), + new BookPageInfo + ( + "weapon become", + "damaged, but be", + "aware that their", + "ministrations may", + "simply make the", + "matter worse.", + " While mages of", + "some ability oft create" + ), + new BookPageInfo + ( + "magical weapons", + "which enhance skill,", + "are preternaturally", + "sharp, or incinerate", + "the enemy as they", + "fall, to my mind the", + "greatest gift that they", + "can grant a stout" + ), + new BookPageInfo + ( + "sword is to make it", + "resistant to damage,", + "for thy own skill can", + "make up the", + "difference. Except", + "for the fireball, but", + "if the corpse is", + "charred, then so will" + ), + new BookPageInfo + ( + "be the possessions,", + "which maketh looting", + "difficult!" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public ArmsAndWeaponsPrimer() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public ArmsAndWeaponsPrimer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region A Song of Samlethe + public class SongOfSamlethe : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "A Song of Samlethe", "Sandra", + new BookPageInfo + ( + "The first bear did", + "swim by day,", + "And it did sleep by", + "night.", + "It kept itself within", + "its cave", + "and ate by starry", + "light." + ), + new BookPageInfo + ( + "", + "The second bear it did", + "cavort", + "'Neath canopies of", + "trees,", + "And danced its", + "strange bearish sort", + "Of joy for all to see." + ), + new BookPageInfo + ( + "", + "The first bear, well,", + "'twas hunted,", + "And today adorns a", + "floor.", + "Its ruggish face has", + "been dented", + "By footfalls and the" + ), + new BookPageInfo + ( + "door.", + "", + "The second bear did", + "step once", + "Into a mushroom ring,", + "And now does dance", + "the dunce", + "For wisps and" + ), + new BookPageInfo + ( + "unseen things.", + "", + "So do not dance, and", + "do not sleep,", + "Or else be led astray!", + "For bears all end up", + "six feet deep", + "At the end of" + ), + new BookPageInfo + ( + "Samlethe's day." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public SongOfSamlethe() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public SongOfSamlethe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region A Tale of Three Tribes + public class TaleOfThreeTribes : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "A Tale of Three Tribes", "Janet, Scribe", + new BookPageInfo + ( + " The dungeon known", + "as Despise is in fact", + "not a dungeon as", + "such, but rather a", + "large natural cave.", + "Inhospitable and", + "unfriendly to", + "visitors, it is filled" + ), + new BookPageInfo + ( + "with damp spots", + "where the deadly", + "Exploding Red Spotted", + "Toadstool grows in", + "abundance.", + " According to the", + "oldest of historical", + "texts, in days gone" + ), + new BookPageInfo + ( + "by the cave was once", + "the home of three", + "separate tribes who", + "had come to an", + "accommodation with", + "each other. Oddly", + "enough, the three", + "tribes were of" + ), + new BookPageInfo + ( + "dragons, lizard men,", + "and rat men. While", + "today few except", + "extremists associated", + "with Lord Blackthorn", + "regard these latter", + "two as being", + "intelligent beings," + ), + new BookPageInfo + ( + "apparently they have", + "indeed fallen from a", + "more evolved state", + "over the years.", + " 'Tis said that these", + "three races did dwell", + "in relative harmony", + "within the vast cave," + ), + new BookPageInfo + ( + "building when they", + "required it, and", + "trading amongst", + "themselves if needed.", + " But over time,", + "something happened,", + "and they were forced", + "to withdraw from" + ), + new BookPageInfo + ( + "their society, until", + "today thou mayst", + "find individuals of", + "each species within", + "the dungeon, but", + "never again as a", + "civilization.", + " 'Tis also said that" + ), + new BookPageInfo + ( + "someday the three", + "tribes may return to", + "Despise, to once again", + "inhabit it together.", + " Until then, nothing", + "remains as token of", + "this save an oddly", + "intelligent skeleton," + ), + new BookPageInfo + ( + "magically enchanted,", + "that doth speak when", + "questions are asked,", + "and from whom I", + "obtained these tales", + "one day, when I was", + "pursued by evil", + "monsters and fled" + ), + new BookPageInfo + ( + "into his skeletal arms.", + " Fortunately, I", + "escaped and lived to", + "write it all down!" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TaleOfThreeTribes() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public TaleOfThreeTribes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Beltran's Guide to Guilds + public class GuideToGuilds : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Beltran's Guide to Guilds", "Beltran", + new BookPageInfo + ( + " This reference", + "work is intended", + "merely to serve as", + "resource for those", + "curious as to the full", + "range of trades and", + "societies extant in", + "Britannia and nearby" + ), + new BookPageInfo + ( + "nations. For each", + "trade or guild, their", + "blazon is given.", + "", + " Armourer's Guild.", + "Gold bar above black", + "bar." + ), + new BookPageInfo + ( + " Association of", + "Warriors. Blue cross", + "on a red field.", + "", + " Barters' Guild.", + "Green and white", + "stripes, diagonal." + ), + new BookPageInfo + ( + " Blacksmith's Guild.", + "Gold alongside black.", + "", + " Federation of", + "Rogues and Beggars.", + "Red above black.", + "", + " Fighters and" + ), + new BookPageInfo + ( + "Footmen. Blue", + "horzontal bar on red", + "field.", + "", + " Guild of Archers.", + "A gold swath parting", + "red and blue." + ), + new BookPageInfo + ( + " Guild of", + "Armaments. Swath of", + "gold on black field,", + "gold accents.", + "", + " Guild of Assassins.", + "Black and red", + "quartered." + ), + new BookPageInfo + ( + "", + " Guild of Barbers.", + "Red and white", + "stripes.", + "", + " Guild of Cavalry and", + "Horse. Vertical blue", + "on a red field." + ), + new BookPageInfo + ( + "", + " Guild of", + "Fishermen. Blue and", + "white, quartered.", + "", + " Guild of Mages.", + "Purple and blue, in a", + "crossed pennant" + ), + new BookPageInfo + ( + "pattern.", + "", + " Guild of", + "Provisioners. White", + "bar above green bar.", + "", + " Guild of Sorcery. A", + "field divided" + ), + new BookPageInfo + ( + "diagonally in blue and", + "purple.", + "", + " Healers Guild. Gold", + "swath dividing green", + "from purple, gold", + "accents." + ), + new BookPageInfo + ( + " Lord British's", + "Healers of Virtue.", + "Golden ankh on dark", + "green.", + "", + " Masters of Illusion.", + "Blue and purple", + "checkers." + ), + new BookPageInfo + ( + "", + " Merchants' Guild.", + "Gold coins on green", + "field.", + "", + " Mining Cooperative.", + "A gold cross,", + "quartering blue and" + ), + new BookPageInfo + ( + "black.", + "", + " Order of Engineers.", + "Purple, gold, and blue", + "vertical.", + "", + " Sailors' Maritime", + "Association. A white" + ), + new BookPageInfo + ( + "bar centered on a blue", + "field.", + "", + " Seamen's Chapter.", + "Blue and white in a", + "crossed pennant", + "pattern." + ), + new BookPageInfo + ( + " Society of Cooks and", + "Chefs. White and red", + "diagonal fields", + "checker on green", + "field.", + "", + " Society of", + "Shipwrights. White" + ), + new BookPageInfo + ( + "diagonal above blue.", + "", + " Society of Thieves.", + "Black and red diagonal", + "stripes.", + "", + " Society of", + "Weaponsmakers. Gold" + ), + new BookPageInfo + ( + "diagonal above black.", + "", + " Tailor's Hall. Purple", + "above gold above red.", + "", + " The Bardic", + "Collegium. Purple and", + "red checkers on gold" + ), + new BookPageInfo + ( + "field.", + "", + " Traders' Guild.", + "White bar centered", + "down green field." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public GuideToGuilds() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public GuideToGuilds( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Birds of Britannia + public class BirdsOfBritannia : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Birds of Britannia", "Thom the Heathen", + new BookPageInfo + ( + " The WREN is a", + "tiny insect-eating", + "bird with a loud voice.", + " The cheerful trills", + "of Wrens are", + "extraordinarily", + "varied and melodious.", + " The SWALLOW" + ), + new BookPageInfo + ( + "is easily recognized", + "by its forked tail.", + "Swallows catch", + "insects in flight, and", + "have squeaky,", + "twittering songs.", + " The WARBLER is", + "an exceptional singer," + ), + new BookPageInfo + ( + "whose extensive", + "songs combine the", + "best qualities of", + "Wrens and Swallows.", + " The NUTHATCH", + "climbs down trees", + "head first, searching", + "for insects in the" + ), + new BookPageInfo + ( + "bark. It sings a", + "repetitive series of", + "notes with a nasal", + "tone quality.", + " The agile", + "CHICKADEE has a", + "buzzy", + "\"chick-a-dee-dee\"" + ), + new BookPageInfo + ( + "call, from which its", + "name is derived. Its", + "song is a series of", + "whistled notes.", + " The THRUSH is a", + "brown bird with a", + "spotted breast, which", + "eats worms and" + ), + new BookPageInfo + ( + "snails, and has a", + "beautiful singing", + "voice. Thrushes use", + "a stone as an anvil to", + "smash the shells of", + "snails.", + " The little", + "NIGHTINGALE is" + ), + new BookPageInfo + ( + "also known for its", + "beautiful song, which", + "it sings even at night.", + " The STARLING", + "is a small dark bird", + "with a yellow bill and", + "a squeaky,", + "high-pitched song." + ), + new BookPageInfo + ( + "Starlings can mimic", + "the sounds of other", + "birds.", + " The SKYLARK", + "sings a series of", + "high-pitched", + "melodious trills in", + "flight." + ), + new BookPageInfo + ( + " The FINCH is a", + "small seed-eating bird", + "with a conical beak", + "and a musical,", + "warbling song.", + " The CROSSBILL", + "is a kind of Finch", + "with a strange" + ), + new BookPageInfo + ( + "crossed bill, which it", + "uses to extract seeds", + "from pine cones.", + " The CANARY is a", + "kind of Finch that is", + "often kept as a pet.", + "Miners would often", + "take Canaries" + ), + new BookPageInfo + ( + "underground with", + "them, to warn them", + "of the presence of", + "hazardous vapors in", + "the air.", + " The SPARROW", + "weaves a nest of", + "grass, and has an" + ), + new BookPageInfo + ( + "unmusical chirp for a", + "voice.", + " The TOWHEE is a", + "kind of Sparrow that", + "continually reminds", + "listeners to drink", + "their tea.", + " The SHRIKE is a" + ), + new BookPageInfo + ( + "gray bird with a", + "hooked bill. Shrikes", + "have the habit of", + "impaling their prey", + "on thorns.", + " The", + "WOODPECKER has a", + "pointed beak that is" + ), + new BookPageInfo + ( + "suitable for pecking at", + "wood to get at the", + "insects inside.", + " The", + "KINGFISHER dives", + "for fish, which it", + "catches with its long,", + "pointed beak." + ), + new BookPageInfo + ( + " The TERN", + "migrates over great", + "distances, from one", + "end of Britannia to", + "the other each year.", + "Terns dive from the", + "air to catch fish.", + " The PLOVER is a" + ), + new BookPageInfo + ( + "bird that distracts", + "predators by", + "pretending to have a", + "broken wing.", + " The LAPWING is", + "a kind of Plover that", + "has a long black crest.", + " The HAWK is a" + ), + new BookPageInfo + ( + "predator that feeds on", + "small birds, mice,", + "squirrels, and other", + "small animals. Small", + "hawks are known as", + "Kites.", + " The DOVE is a", + "seed-eating bird with" + ), + new BookPageInfo + ( + "a peaceful reputation.", + " Doves have a", + "low-pitched cooing", + "song.", + " The PARROT is a", + "brightly colored bird", + "with a hooked bill,", + "favored as a" + ), + new BookPageInfo + ( + "companion by pirates.", + " Parrots can be", + "taught to imitate the", + "human voice.", + " The CUCKOO is a", + "devious bird that lays", + "eggs in the nests of", + "Warblers and other" + ), + new BookPageInfo + ( + "small birds. Cuckoos", + "have the uncanny", + "ability to keep track", + "of time, singing once", + "at the beginning of", + "each hour.", + " The", + "ROADRUNNER is" + ), + new BookPageInfo + ( + "an unusual bird with", + "a long tail, which", + "runs swiftly along", + "the ground hunting", + "for lizards and", + "snakes.", + " The SWIFT is a", + "very agile bird that" + ), + new BookPageInfo + ( + "spends nearly its", + "entire life in the air.", + "With their mouths", + "wide open, Swifts", + "capture insects in", + "mid-flight.", + " The", + "HUMMINGBIRD is a" + ), + new BookPageInfo + ( + "cross between a", + "Swift and a Fairy.", + "These tiny, brightly", + "colored birds hover", + "magically near", + "flowers, and live on", + "the nectar they", + "provide." + ), + new BookPageInfo + ( + " The OWL is a", + "reputedly wise bird", + "that is active at night,", + "unlike most birds.", + "Owls have excellent", + "night vision and", + "low-pitched hooting", + "calls. Their wings" + ), + new BookPageInfo + ( + "are silent in flight.", + " The", + "GOATSUCKER is a", + "strange owl-like bird", + "that is thought to live", + "on the milk of goats.", + "These mysterious", + "birds make jarring" + ), + new BookPageInfo + ( + "sounds at night, for", + "which reason they", + "are also called", + "Nightjars.", + " The DUCK is a", + "bird that swims more", + "often than it flies,", + "and has a nasal voice" + ), + new BookPageInfo + ( + "that is described as a", + "\"quack\".", + " The SWAN is a", + "kind of long-necked", + "Duck that is all white.", + " Swans are usually", + "voiceless, but they", + "are said to have an" + ), + new BookPageInfo + ( + "extraordinarily", + "beautiful song." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public BirdsOfBritannia() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public BirdsOfBritannia( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Britannian Flora: A Casual Guide + public class BritannianFlora : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Britannian Flora: A Casual Guide", "Herbert the Lost", + new BookPageInfo + ( + " Oft 'pon rambling", + "through the woods", + "avoiding bears have I", + "spotted some plant", + "whose like I have", + "never seen before,", + "and concluded that I", + "was a blithering idiot" + ), + new BookPageInfo + ( + "for failing to notice it", + "in the past. Equally", + "as oft have I", + "concluded that I was a", + "worse idiot for not", + "running faster from", + "the bear.", + " While not all my" + ), + new BookPageInfo + ( + "readers may share", + "my proclivities for", + "tree-climbing, it", + "occurred to me that", + "mayhap mine", + "information might", + "serve some humble", + "purpose." + ), + new BookPageInfo + ( + " The two most", + "unique flowering", + "plants in the", + "Britannian", + "countryside are the", + "orfleur and the", + "whiteflower, also", + "called white horns." + ), + new BookPageInfo + ( + " The orfleur is", + "notable for its", + "massive orange-red", + "blossoms, which", + "dwarf marigolds like", + "the sun dwarfs your", + "common fireball spell.", + "The odor of said" + ), + new BookPageInfo + ( + "blooms is best", + "described as", + "peppermint-apple,", + "with a dash of garlic.", + "'Tis a popular potted", + "plant despite, or", + "perhaps because of,", + "its exotic nature." + ), + new BookPageInfo + ( + " Whiteflowers exude", + "a subtle fragrance not", + "unlike that of freshly", + "shaven wood mixed", + "with cool lemon ice.", + "Their tall stands", + "always droop with the", + "heavy weight of the" + ), + new BookPageInfo + ( + "massive blooms, oft", + "as large as a child's", + "head.", + " The flowers are so", + "large that one may", + "scoop out the pollen in", + "handfuls, and during", + "the spring season" + ), + new BookPageInfo + ( + "many a prank hath", + "been played by idle", + "boys 'pon their", + "sisters by dumping", + "said pollen into their", + "clothing drawers,", + "causing sneezes for", + "days." + ), + new BookPageInfo + ( + " The most", + "interesting native tree", + "to Britannia is the", + "spider tree. The", + "reason for its naming", + "is obscure, but may", + "have to do with the", + "twisted gray stalks" + ), + new BookPageInfo + ( + "from which the", + "spherical canopy", + "sprouts. 'Tis", + "something of a", + "misnomer to term", + "these \"trunks\" as", + "they are spindly and", + "flexible. Spider trees" + ), + new BookPageInfo + ( + "provide a fresh,", + "piney smell to a room", + "and are therefore", + "often potted.", + " In jungle climes,", + "one finds the blade", + "plant, whose sharp", + "leaves oft collect" + ), + new BookPageInfo + ( + "water for the thirsty", + "traveler, yet can", + "draw blood easily.", + " The deadliest plant,", + "if you can call a", + "fungus such, is the", + "Exploding Red Spotted", + "Toadstool. No pattern" + ), + new BookPageInfo + ( + "can be discerned to", + "its habitats save", + "malice, for merely", + "approaching results in", + "the cap exploding", + "with powder, noxious", + "gas, and tiny painful", + "pellets flying in all" + ), + new BookPageInfo + ( + "directions.", + "Unfortunately, 'tis", + "impossible to tell it", + "apart from the", + "Ordinary Red Spotted", + "Toadstool save through", + "experimentation.", + " Truly odd among the" + ), + new BookPageInfo + ( + "varied flora of", + "Britannia, however,", + "are those which bear", + "names clearly alien to", + "our tongue. Among", + "these I name the", + "Tuscany pine (for I", + "have never seen a" + ), + new BookPageInfo + ( + "region of this world", + "named Tuscany), the", + "o'hii tree, whose very", + "name sounds like", + "some tropical isle, and", + "the welsh poppy,", + "which while", + "different from the" + ), + new BookPageInfo + ( + "ordinary poppy in", + "color and appearance,", + "is prefaced with the", + "odd word \"welsh,\"", + "which as far as I", + "know means to forgo", + "paying a debt." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public BritannianFlora() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public BritannianFlora( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Classic Children's Tales, Volume 2 + public class ChildrenTalesVol2 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Classic Children's Tales, Volume 2", "Guilhem, Editor", + new BookPageInfo + ( + "Clarke's Printery", + "is Honored to", + "Present Tales from", + "Ages Past!", + " Guilhem the", + "Scholar Shall End", + "EachVolume with", + "Staid Commentary." + ), + new BookPageInfo + ( + "", + "THE RHYME", + "Dance in the Star", + "Chamber", + "And Dance in the Pit", + "And Eat of your", + "Entrees", + "In the Glass House" + ), + new BookPageInfo + ( + "you Sit", + "", + "COMMENTARY", + " A common feeding", + "rhyme for little", + "babies, 'tis thought", + "that this little ditty is", + "part of the corpus of" + ), + new BookPageInfo + ( + "legendary tales", + "regarding the world", + "before Sosaria (see", + "the wonderful fables", + "of Fabio the Poor for", + "fictionalized versions", + "of these stories, also", + "available from this" + ), + new BookPageInfo + ( + "same publisher).", + " According to these", + "old tales, which", + "survive mostly in the", + "hills and remote", + "villages where Lord", + "British is as yet a", + "distant and mythical" + ), + new BookPageInfo + ( + "ruler, the gods of old", + "(a fanciful notion!)", + "met to discuss the", + "progress of creating", + "the world in mystical", + "rooms. A simple", + "analysis reveals these", + "rooms to be mere" + ), + new BookPageInfo + ( + "mythological", + "generalizations.", + " \"The Star", + "Chamber\" is clearly a", + "reference to the sky.", + "\"The Pit\" is certainly", + "an Underworld", + "analogous to the" + ), + new BookPageInfo + ( + "Snakehills of other", + "tales, and \"the Glass", + "House\" is no doubt the", + "vantage point from", + "which the gods", + "observed their", + "creation. All is simple", + "when seen from this" + ), + new BookPageInfo + ( + "perspective, leaving", + "only the mysterious", + "reference to dinners.", + "Oddly enough, the", + "rhyme is universally", + "used only for", + "midnight feedings,", + "never during the day." + ), + new BookPageInfo + ( + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public ChildrenTalesVol2() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public ChildrenTalesVol2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Classic Tales of Vesper, Volume 1 + public class TalesOfVesperVol1 : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Classic Tales of Vesper, Volume 1", "Clarke's Printery", + new BookPageInfo + ( + "'Tis an Honor to", + "present to Thee these", + "Tales collected from", + "Ages Past. In this", + "Inaugural Volume, we", + "present this Verse", + "oft Recited as a", + "Lullabye for sleepy" + ), + new BookPageInfo + ( + "Children.", + "", + "Preface", + "by Guilhem the", + "Scholar", + "", + " The meaning of this", + "verse has oft been" + ), + new BookPageInfo + ( + "discussed in halls of", + "scholarly sorts, for", + "its mysterious", + "singsongy melody is", + "oddly disturbing to", + "adult ears, though", + "children seem to find", + "it restful as they" + ), + new BookPageInfo + ( + "sleep. Perhaps it is", + "but the remnant of a", + "longer ballad once", + "extant, for there are", + "internal indications", + "that it once told a", + "longer story about", + "ill-fated lovers, and a" + ), + new BookPageInfo + ( + "magical experiment", + "gone awry. However,", + "poetic license and the", + "folk process has", + "distorted the words", + "until now the locale of", + "the tale is no more", + "than \"in the wind,\"" + ), + new BookPageInfo + ( + "which while it serves", + "a pleasingly", + "metaphorical purpose,", + "fails to inform the", + "listener as to any real", + "locale!", + " Another possibility", + "is that this is some" + ), + new BookPageInfo + ( + "form of creation", + "myth explaining the", + "genesis of the various", + "humanoid creatures", + "that roam the lands of", + "Britannia. It does not", + "take a stretch of the", + "imagination to name" + ), + new BookPageInfo + ( + "the middle verse's", + "\"girl becomes tree\" as", + "a possible explanation", + "for the reaper, for in", + "the area surrounding", + "Minoc, reapers are", + "oft referred to among", + "the lumberjacking" + ), + new BookPageInfo + ( + "community as", + "\"widowmakers.\" That", + "these creatures are", + "of arcane origin is", + "assumed, but the", + "verse seems to imply", + "a long ago creator, and", + "uses the antique" + ), + new BookPageInfo + ( + "magickal terminology", + "of \"plaiting strands", + "of ether\" that is so", + "often found in", + "ancient texts. In", + "addition, the", + "reference to", + "\"snakehills\" may" + ), + new BookPageInfo + ( + "profitably be regarded", + "as a reference to an", + "actual location, such", + "as perhaps a local", + "term for the", + "Serpent's Spine.", + " A commoner", + "interpretation is that" + ), + new BookPageInfo + ( + "like many nursery", + "rhymes, it is a", + "simple explanation", + "for death, wherein", + "the wind snatches up", + "boys and girls and", + "when they sleep in", + "order to keep the" + ), + new BookPageInfo + ( + "balance of the world.", + "Notable tales have", + "been written for", + "children of", + "adventures in \"the", + "Snakehills,\" which", + "are presumed to be an", + "Afterworld whence" + ), + new BookPageInfo + ( + "the spirit lives on. A", + "grim lullabye, to be", + "sure, but no worse", + "than \"lest I die before", + "I wake\" surely.", + " In either case, 'tis", + "an old favorite,", + "herein printed for" + ), + new BookPageInfo + ( + "the first time for", + "thy enjoyment and", + "perusal!", + "", + "In the Wind where", + "the Balance", + "Is Whispered in", + "Hallways" + ), + new BookPageInfo + ( + "In the Wind where", + "the Magic", + "Flows All through the", + "Night", + "There live Mages and", + "Mages", + "With Robes made of", + "Whole Days" + ), + new BookPageInfo + ( + "Reading Books full of", + "Doings", + "Printed on Light", + "", + "In the Wind where", + "the Lovers", + "Are Crossed under", + "Shadows" + ), + new BookPageInfo + ( + "Where they Meet and", + "are Parted", + "By the Orders of", + "Fate", + "The Girl becomes", + "Tree,", + "And thus becomes", + "Widow" + ), + new BookPageInfo + ( + "The Boy becomes", + "Earth", + "And Wanders Till", + "Late", + "", + "In the Wind are the", + "Monsters", + "First Born First" + ), + new BookPageInfo + ( + "Created", + "When Chanting and", + "Ether", + "Mix Meddling and", + "Nigh", + "Fear going to Wind,", + "Fear Finding its", + "Plaitings," + ), + new BookPageInfo + ( + "Go Not to the", + "Snakehills", + "Lest You Care to Die" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TalesOfVesperVol1() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public TalesOfVesperVol1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Deceit: A Dungeon of Horrors + public class DeceitDungeonOfHorror : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Deceit: A Dungeon of Horrors", "Mercenary Justin", + new BookPageInfo + ( + " My employers have", + "oft taken me into this", + "den of hideous", + "creatures, and I", + "thought that it", + "behooved me to write", + "down what I know of", + "it, now that I am" + ), + new BookPageInfo + ( + "retired from the life", + "of an adventurer for", + "hire.", + " Deceit was once a", + "temple to forgotten", + "powers of old. It was", + "taken over by mages", + "who eventually were" + ), + new BookPageInfo + ( + "driven out by the", + "depredations of their", + "own evil lackeys.", + "However, many of", + "the magical traps and", + "devices that they", + "placed for their", + "defenses remain," + ), + new BookPageInfo + ( + "particularly those the", + "wizards used to", + "protect their", + "treasures.", + " The dungeon is", + "mystically linked by", + "crystal balls placed in", + "different locations." + ), + new BookPageInfo + ( + "These magical orbs do", + "transmit speech, and", + "even have memory of", + "things that have been", + "said near them. No", + "doubt they once", + "served as a warning", + "system" + ), + new BookPageInfo + ( + " Be wary of a", + "brazier that giveth", + "warning when", + "approached; thou canst", + "use it to summon", + "deadly creatures.", + " There be a", + "tantalizing chest," + ), + new BookPageInfo + ( + "undoubtedly full of", + "treasure, that cannot", + "be reached save past a", + "complex set of", + "pressure plates that", + "trigger deadly spikes.", + "As I never had", + "sufficient folk with" + ), + new BookPageInfo + ( + "me to unlock the", + "puzzle, I never", + "obtained the riches", + "that awaited there.", + " Do not investigate", + "iron maidens too", + "closely, for they may", + "suck you within" + ), + new BookPageInfo + ( + "them!", + " There is one place", + "where a deadly trap", + "can only be disarmed", + "by making use of a", + "statue that cleverly", + "conceals a lever.", + " Oft one encounters" + ), + new BookPageInfo + ( + "the deadly exploding", + "toadstool; the ones in", + "Deceit are deadlier", + "than most, as they", + "explode continually.", + "Likewise, the very", + "pools of water and", + "slime on the floor" + ), + new BookPageInfo + ( + "may poison thee.", + " The most magical", + "device in the dungeon", + "is a mystical bridge", + "that can only be", + "triggered by a level", + "embedded in the floor.", + "Be wary however," + ), + new BookPageInfo + ( + "for the bridge thus", + "created doth burst", + "into flame when one", + "passeth across it!" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public DeceitDungeonOfHorror() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public DeceitDungeonOfHorror( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Dimensional Travel, a Monograph + public class DimensionalTravel : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Dimensional Travel, a Monograph", "Dryus Doost, Mage", + new BookPageInfo + ( + " 'Tis beyond the", + "scope of this small", + "monograph to discuss", + "the details of", + "moongates, and the", + "manners in which", + "they distort the", + "fabric of reality in" + ), + new BookPageInfo + ( + "such a manner as to", + "permit the passage of", + "living flesh from", + "place to place, world to", + "world, or indeed from", + "dimension to", + "dimension.", + " Instead, allow me to" + ), + new BookPageInfo + ( + "bring thy attention,", + "Gentle Reader, to the", + "curious", + "characteristics that", + "are shared by certain", + "individuals within", + "our realm.", + " Long has it been" + ), + new BookPageInfo + ( + "known that the blue", + "moongate permits", + "travel from place to", + "place, and none have", + "trouble in taking this", + "path. Yet 'tis also", + "known, albeit only to a", + "few, that certain" + ), + new BookPageInfo + ( + "individuals are unable", + "to traverse the black", + "moongates that permit", + "travel from one", + "dimension to another.", + " The noted mage and", + "peer of our realm,", + "Lord Blackthorn, once" + ), + new BookPageInfo + ( + "told me in", + "conversation that his", + "arcane research had", + "indicated that the", + "issue was one of", + "conversation of ether.", + "To wit, given the", + "postulate that matter" + ), + new BookPageInfo + ( + "within a given", + "dimension may be but", + "a cross-section of", + "ethereal matter that", + "exists in multiple", + "dimensions, it", + "becomes obvious that", + "said ethereal" + ), + new BookPageInfo + ( + "structure cannot", + "enter dimensions in", + "which it is already", + "present.", + " Imagine an", + "individual (and the", + "Lord Blackthorn", + "hinted that he was" + ), + new BookPageInfo + ( + "one such) who exists", + "already in some form", + "in multiple", + "dimensions; said", + "individual would not", + "be able to cross into", + "another dimension", + "because HE IS" + ), + new BookPageInfo + ( + "ALREADY THERE.", + " The implications of", + "this are staggering,", + "and merit further", + "study. 'Tis well", + "known by theorists in", + "the field that", + "divisions in the" + ), + new BookPageInfo + ( + "ethereal structure of", + "an individual are", + "already implicit at the", + "temporal level, as", + "causality forces", + "divisions upon the", + "ether. This is the", + "basic operating" + ), + new BookPageInfo + ( + "mechanism by which", + "white moongates", + "function, permitting", + "time travel.", + " As time travel is", + "not barred by the", + "presence of an earlier", + "self (though" + ), + new BookPageInfo + ( + "encountering said", + "earlier self can prove", + "arcanely perilous),", + "there must be some", + "rigidity to the", + "ethereal structure", + "that bars multiple", + "instantiations of" + ), + new BookPageInfo + ( + "structures from", + "manifesting within", + "the same context.", + " If one regards time", + "and causal bifurcation", + "as a web, perhaps the", + "appropriate analogy", + "for dimensional" + ), + new BookPageInfo + ( + "matrices is that of a", + "crystalline structure,", + "with rigid linkages.", + "The only way in", + "which an individual", + "such as Lord", + "Blackthorn, who", + "exists in multiple" + ), + new BookPageInfo + ( + "dimensional matrices,", + "can cross worlds via", + "a black moongate,", + "would be for the", + "entire crystalline", + "structure of the", + "dimension to", + "perfectly match the" + ), + new BookPageInfo + ( + "ethereal resonance of", + "the destination", + "dimension.", + " The problem of why", + "certain individuals", + "are already replicated", + "in multiple crystalline", + "matrices is one that I" + ), + new BookPageInfo + ( + "fail to provide any", + "schema for in these", + "poor theories. It is", + "my fondest hope that", + "someday someone", + "shall conquer that", + "thorny problem and", + "enlighten the world." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public DimensionalTravel() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public DimensionalTravel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Ethical Hedonism: An Introduction + public class EthicalHedonism : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Ethical Hedonism: An Introduction", "Richard Garriott", + new BookPageInfo + ( + " Societies oft have", + "common codes of", + "conduct which it", + "expects all its people", + "to abide by. Now,", + "while 'tis true that", + "this can offer some", + "advantages, most of" + ), + new BookPageInfo + ( + "the codes I see today", + "around Britannia have", + "fatal flaws. Let us", + "examine them.", + " First, there is", + "Blackthorn's code of", + "Chaos or basically", + "Anarchy. Whereas" + ), + new BookPageInfo + ( + "this affords the", + "individual maximum", + "opportunity for", + "individuality and even", + "pursuit of personal", + "happiness, it does not", + "offer even basic", + "interpersonal conduct" + ), + new BookPageInfo + ( + "codes to prevent", + "people from killing", + "each other.", + " Without such basic", + "tenets, all the people", + "will need to spend a", + "significant portion of", + "their time and effort" + ), + new BookPageInfo + ( + "towards personal", + "protection and thus", + "less time towards", + "other more beneficial", + "pursuits.", + " Then there are the", + "moral codes that are", + "so popular today." + ), + new BookPageInfo + ( + "These codes are built", + "largely on historical", + "tradition rather than", + "current logic and thus", + "are also antiquated.", + "For example many", + "moral codes we see", + "today include" + ), + new BookPageInfo + ( + "statements about not", + "eating certain foods", + "that once were often", + "poisonous, but today", + "can be prepared", + "safely.", + " Many forbid contact", + "between young people" + ), + new BookPageInfo + ( + "of the opposite", + "gender, which can in", + "fact be hazardous; but", + "the codes often have", + "lost the context as to", + "why this is done,", + "instead merely calling", + "it amoral. In this day" + ), + new BookPageInfo + ( + "and age to call that a", + "necessary moral", + "would need a new", + "reasoning. I put forth", + "that tradition is not", + "enough", + " Then there are", + "Lord British's" + ), + new BookPageInfo + ( + "Virtues. It strikes me", + "that while a system", + "of virtues is", + "wonderful as a", + "touchstone to guide a", + "society to good", + "behavior, these are", + "but shades of the" + ), + new BookPageInfo + ( + "underlying truth as to", + "why one may wish to", + "live a life according to", + "certain rules of", + "conduct.", + " On the other hand,", + "clearly the Virtues", + "that I have heard" + ), + new BookPageInfo + ( + "Lord British speak of", + "are clearly positive", + "codes of conduct, far", + "better than the world", + "of anarchy that Lord", + "Blackthorn suggests.", + "Yet, are not these", + "Virtues still derived" + ), + new BookPageInfo + ( + "from a set of", + "principles which", + "though they sound", + "good, are difficult to", + "pin down as actual,", + "undeniable, rational", + "truths?", + " Worse yet though" + ), + new BookPageInfo + ( + "imagine a society", + "who's code of", + "conduct was based on", + "pure survival of the", + "strongest. While this", + "society may function", + "and even accomplish", + "much, it can be" + ), + new BookPageInfo + ( + "fairly argued that", + "personal happiness", + "would suffer greatly,", + "except for those at", + "the top. To rule that", + "out, however, we", + "must first believe", + "that people have a" + ), + new BookPageInfo + ( + "right to pursue", + "happiness.", + " I hope is a safe", + "assumption that all", + "beings wish to be", + "happy; I will broadly", + "describe this as", + "Hedonism. Yet, if all" + ), + new BookPageInfo + ( + "people did is live a", + "life of hedonism,", + "their hedonism might", + "be in conflict with", + "those near them, so I", + "will use the term", + "Ethics to describe", + "limits one might put" + ), + new BookPageInfo + ( + "on one's hedonistic", + "tendencies to allow", + "others to pursue their", + "happiness as well.", + " Allow me to give", + "this example: If one", + "were to live alone on a", + "desert isle, one could" + ), + new BookPageInfo + ( + "live a life of pure", + "hedonism, for no", + "action one might take", + "could interfere with", + "another's right to", + "pursue their", + "happiness. Poison the", + "lake if you like, there" + ), + new BookPageInfo + ( + "is no one to blame but", + "yourself!", + " Now suppose two", + "of you live on that", + "island. Thou dost not", + "want thy neighbor to", + "feel free to poison the", + "lake. Would it not be" + ), + new BookPageInfo + ( + "better to consider it", + "unethical to poison the", + "lake without first", + "thinking of those", + "whose pursuit of", + "happiness might be", + "affected by this", + "action?" + ), + new BookPageInfo + ( + " I put forth that it is", + "the fact that we as a", + "people choose to live in", + "groups known as a", + "society that causes us", + "to compromise our", + "pure hedonism with", + "logical ethics." + ), + new BookPageInfo + ( + "Likewise we accept", + "not being able to kill", + "others without", + "reason, because our", + "own pursuit of", + "happiness would be", + "greatly interfered", + "with if we feared" + ), + new BookPageInfo + ( + "others would do the", + "same to us. From", + "this basis of logic can", + "be formed the Tenets", + "of Ethical Hedonism.", + " For more on this", + "subject, see The", + "Tenants of Ethical" + ), + new BookPageInfo + ( + "Hedonism, by", + "Richard Garriott and", + "Herman Miller." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public EthicalHedonism() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public EthicalHedonism( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region My Story + public class MyStory : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "My Story", "Sherry the Mouse", + new BookPageInfo + ( + " 'Twas on a chill", + "night, when the moon", + "shone pasty-faced", + "above the horizon,", + "balanced on the", + "towers of Lord", + "British's castle, that", + "the events I am about" + ), + new BookPageInfo + ( + "to relate took place,", + "some years ago now. I", + "witnessed them all", + "from my tiny", + "mousehole.", + " Milords British and", + "Blackthorn are", + "accustomed to a game" + ), + new BookPageInfo + ( + "of chess 'pon an", + "evening, over which", + "they argue the issues", + "that affect the course", + "of the realm. Lord", + "Blackthorn was on his", + "way to Lord British's", + "chambers, and Lord" + ), + new BookPageInfo + ( + "British stood by a", + "window casement,", + "just having finished", + "setting the pieces", + "upon the board.", + " Suddenly the", + "shutters blew open,", + "and Lord British fell" + ), + new BookPageInfo + ( + "to the ground, one", + "hand shielding his", + "eyes. A chill wind", + "entered the room, and", + "it seemed a gash was", + "torn in the very air.", + "Through the gash I", + "could see stars and" + ), + new BookPageInfo + ( + "swirling clouds of", + "stellar dust, and a", + "coldness sucked all", + "the warmth from the", + "air. A terrible wind", + "tossed books and", + "blankets across the", + "room, and furniture" + ), + new BookPageInfo + ( + "toppled.", + " From within this", + "gash issued a great", + "voice, unlike any I", + "have ever heard. And", + "these are the words it", + "spoke (for I", + "memorized them most" + ), + new BookPageInfo + ( + "carefully):", + " \"Greetings, Lord", + "British. I am the", + "Time Lord, a being", + "from beyond your", + "dimension, as thou", + "art from a world", + "other than Sosaria. I" + ), + new BookPageInfo + ( + "am here to bring thee", + "warning. Dost thou", + "recall how long ago a", + "mysterious Stranger", + "came to Sosaria and", + "saved the world from", + "the evil wizard", + "Mondain? He" + ), + new BookPageInfo + ( + "shattered the Gem of", + "Immortality, within", + "which dwelled a", + "perfect likeness of", + "this world.\"", + " Lord British slowly", + "stood and faced the", + "hole in the air. \"I" + ), + new BookPageInfo + ( + "remember,\" he said.", + "\"Oft have I wished", + "that stranger would", + "return.\"", + " \"He hath returned,\"", + "spoke the voice. \"But", + "not to here. When the", + "Gem was shattered, a" + ), + new BookPageInfo + ( + "thousand shards were", + "scattered across the", + "dimensions, and in", + "each shard there is a", + "perfect likeness of", + "this world. And thou", + "dost live upon one", + "such shard, for thou" + ), + new BookPageInfo + ( + "art not of the true", + "world-thou art", + "merely a reflection.\"", + " Lord British looked", + "shaken by this, and I", + "did not know what to", + "think! Was I merely a", + "shadow of the real" + ), + new BookPageInfo + ( + "me, which lives still", + "somewhere else", + "across uncounted", + "universes?", + " \"My task is to heal", + "this shattered world,", + "Lord British,\" said", + "the voice. \"And I seek" + ), + new BookPageInfo + ( + "to enlist thee in my", + "cause. Be warned that", + "in this case, healing", + "carries with it a", + "terrible price.\"", + " Concern warred", + "with curiosity on my", + "liege's face, but ever" + ), + new BookPageInfo + ( + "one to shoulder a", + "burden, he", + "straightened and", + "faced the gash in the", + "air bravely. \"Name", + "thy price.\"", + " \"A shard of a", + "universe is a" + ), + new BookPageInfo + ( + "powerful thing, and a", + "universe shattered is", + "always in danger", + "from the powers of", + "darkness. Already", + "three shards were", + "turned to evil, and", + "sent to plague the" + ), + new BookPageInfo + ( + "original universe in", + "the form of", + "Shadowlords. Many", + "times have I brought", + "the Stranger back to", + "Britannia, to preserve", + "it from its own folly", + "or from outside" + ), + new BookPageInfo + ( + "dangers. Yet as long", + "as the world", + "remaineth in pieces,", + "it remaineth", + "vulnerable. We must", + "bring the shards into", + "harmony, so that they", + "resonate in such a" + ), + new BookPageInfo + ( + "manner that matches", + "the original universe.", + "Then the two", + "universes shall", + "merge, and be again", + "as one.\"", + " \"But if we are only", + "shadows...\" Lord" + ), + new BookPageInfo + ( + "British said", + "wonderingly.", + " The light from the", + "stars within the hole", + "seemed to dim.", + "\"Indeed, the", + "reflections shall", + "become one with the" + ), + new BookPageInfo + ( + "original. Thou wouldst", + "cease to be as thou", + "art, and become part", + "of the larger you.", + "Thou shalt not die;", + "however, uncounted", + "generations have", + "passed and borne" + ), + new BookPageInfo + ( + "children since that", + "day, and they have no", + "counterparts. They", + "would perish utterly.\"", + " Lord British sagged", + "in shock, realizing", + "the terrible price that", + "would be paid to heal" + ), + new BookPageInfo + ( + "the universe. \"All of", + "my people,\" he", + "breathed.", + " \"'Tis for the greater", + "good.\"", + " Lord British bowed", + "his head.", + " 'Twas then I saw" + ), + new BookPageInfo + ( + "the movement by the", + "door, half-hid by the", + "heavy red curtains.", + "Lord Blackthorn stood", + "there, concealed from", + "the rest of the room,", + "his face white. How", + "long had he been" + ), + new BookPageInfo + ( + "listening? I cannot", + "say, yet I suspect", + "that he had heard all", + "that the mysterious", + "voice had to say.", + " \"How then, shall I", + "aid thee?\" Lord", + "British said," + ), + new BookPageInfo + ( + "weariness in his", + "voice.", + " \"Aid the nobilty that", + "resideth in the", + "human heart. Protect", + "the Virtues that so", + "recently came to thee", + "in thought late at" + ), + new BookPageInfo + ( + "night. They are the", + "Virtues of life, as", + "your counterpart", + "understands them to", + "be. For when thy", + "populace doth live and", + "breathe these Virtues,", + "shall it match the" + ), + new BookPageInfo + ( + "true Britannia, and", + "thy shard shall", + "rejoin with it.\"", + " The gash in the air", + "began to close, and", + "with it warmth stole", + "back into the room.", + " \"I was going to" + ), + new BookPageInfo + ( + "discuss my idea with", + "Blackthorn tonight,\"", + "Lord British", + "breathed. \"Have I no", + "thoughts that are my", + "own? Is my life but", + "a reflection of", + "another me?\"" + ), + new BookPageInfo + ( + " \"Nay,\" said the", + "voice, smaller through", + "the diminished", + "opening. \"Say, rather,", + "that you are parallel,", + "for there is no", + "guarantee that thou", + "shalt accomplish what" + ), + new BookPageInfo + ( + "I have set thee to. I", + "speak tonight to a", + "thousand of thee, and", + "ask the same of all.", + "Perhaps not all shall", + "seek to aid me.\" And", + "with that, the gash", + "closed, and the voice" + ), + new BookPageInfo + ( + "was gone, leaving a", + "room that appeare", + "tossed by a mighty", + "storm.", + " \"Destroy the world", + "to save the universe,\"", + "Lord British said", + "bitterly. \"I do not" + ), + new BookPageInfo + ( + "wonder that some", + "may balk.\"", + " Lord Blackthorn", + "collected himself, and", + "strode into the room,", + "a decent mimicry of", + "surprise on his face.", + "\"My liege! What has" + ), + new BookPageInfo + ( + "happened here?\" he", + "exclaimed, feigning", + "dismay well. But not", + "well enough to fool", + "his old friend, whose", + "eyes narrowed at", + "seeing him there.", + " \"How much didst" + ), + new BookPageInfo + ( + "thou hear?\" demanded", + "Lord British.", + " \"Why, nothing,\"", + "managed Blackthorn,", + "his head ducked away", + "from his friend, as", + "he bent to retrieve the", + "fallen chess pieces. \"I" + ), + new BookPageInfo + ( + "merely came for our", + "game of chess.\"", + " Together they", + "righted the pedestal", + "table, and set the", + "pieces upon the black", + "and white squares.", + "\"Such simplicity to" + ), + new BookPageInfo + ( + "the game, Blackthorn,\"", + "mused Lord British,", + "idly brushing one", + "finger against the", + "board. \"Black and", + "white, each to its own", + "color, as if life were", + "so simple. What think" + ), + new BookPageInfo + ( + "you?\"", + " Blackthorn sat", + "heavily on a hassock", + "beside the chess table.", + "\"I think that matters", + "are never so simple,", + "my liege. And that I", + "would regret it deeply" + ), + new BookPageInfo + ( + "if someone, such as a", + "friend, saw it thus.\"", + " Lord British's eyes", + "met his. \"Yet", + "sometimes one must", + "sacrifice a pawn to", + "save a king.\"", + " Lord Blackthorn met" + ), + new BookPageInfo + ( + "his gaze squarely.", + "\"Even pawns have", + "lives and loves at", + "home, my lord.\" Then", + "he reached out for a", + "pawn, and firmly", + "moved it forward two", + "squares. \"Shall we" + ), + new BookPageInfo + ( + "play a game?\" he", + "asked.", + " The chess game that", + "night was a draw,", + "and they played", + "grimly.", + " And the next day,", + "Lord British gathered" + ), + new BookPageInfo + ( + "the nobles to proclaim", + "the idea of a new", + "system of Virtues,", + "and declared that", + "shrines should be", + "built across the land.", + " Lord Blackthorn", + "opposed it bitterly," + ), + new BookPageInfo + ( + "and many thought", + "him strange for doing", + "so, for ever had he", + "been a noble and", + "upright man, and", + "ever had he and Lord", + "British been in", + "accord. Declaring that" + ), + new BookPageInfo + ( + "he should start his", + "own shrine, he", + "departed the castle", + "that day to live in a", + "tower in a lake on the", + "north side of the", + "city.", + " They are still the" + ), + new BookPageInfo + ( + "best of friends, yet a", + "sadness hangs", + "between them, as if", + "they were forced into", + "making choices that", + "appealed not to them.", + "And at night, when I", + "creep softly from one" + ), + new BookPageInfo + ( + "corner of my liege's", + "bedchamber to", + "another, I sometimes", + "see him take a pawn", + "from his night table,", + "and hold it in his", + "hand, and quietly", + "weep." + ), + new BookPageInfo + ( + " But I am but a", + "mouse, and none hear", + "me. This tale goes", + "unknown, save for", + "my writing these", + "enormous letters with", + "mine ink-stained tiny", + "paws for thee to" + ), + new BookPageInfo + ( + "read, for I fear", + "indeed for our world", + "and for our people in", + "these perilous times." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public MyStory() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public MyStory( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region On the Diversity of Our Land + public class DiversityOfOurLand : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "On the Diversity of Our Land", "Lord Blackthorn", + new BookPageInfo + ( + " While I deplore the", + "depredations of the", + "misguided and", + "belligerent races with", + "which we share our", + "fair Britannia, and", + "alongside the populace,", + "do mourn the needless" + ), + new BookPageInfo + ( + "deaths that their", + "raids cause, I cannot", + "countenance the policy", + "of wholesale slaughter", + "of these races that", + "seems to be the habit", + "of our soldierly", + "element." + ), + new BookPageInfo + ( + " Can we not regard", + "the ratmen, lizard", + "men, and orcs are", + "fellow intelligent", + "beings with whom we", + "share a planet? Why", + "must we slay them", + "on sight, rather than" + ), + new BookPageInfo + ( + "attempt to engage", + "them in dialogue?", + "There is no policy of", + "shooting at wisps", + "when they grace us", + "with their presence", + "(not that an arrow", + "could do much to" + ), + new BookPageInfo + ( + "pierce them!).", + " To view these", + "creatures as vermin", + "denies their obvious", + "intelligence, and we", + "cannot underestimate", + "the repercussions", + "that their slaughter" + ), + new BookPageInfo + ( + "may have. If we", + "regard the slaying of", + "fellow humans as a", + "crime, so must we", + "regard the killing of", + "an orc.", + " At the same time,", + "should a lizardman" + ), + new BookPageInfo + ( + "slay a human, should", + "we not forgive their", + "ignorance and", + "foolishness? Let us", + "not surrender the", + "high moral ground by", + "descending to", + "bestiality." + ), + new BookPageInfo + ( + " Now, I say not that", + "we should fail to", + "defend ourselves in", + "case of attack, for", + "even amongst humans", + "we see war, we see", + "famine, and we see", + "assault (though we" + ), + new BookPageInfo + ( + "owe a debt of", + "gratitude to our Lord", + "British for", + "preserving us from", + "the worst of these!).", + "However, incursions", + "such as the recent", + "tragedy which cost us" + ), + new BookPageInfo + ( + "the life of Japheth,", + "Guildmaster of", + "Trinsic's Paladins,", + "are folly.", + " I had met Japheth,", + "and like all paladins,", + "he burned with an", + "inner fire. Yet" + ), + new BookPageInfo + ( + "though I had the", + "utmost respect for", + "him, none could deny", + "the hatred that", + "flashed in his eyes at", + "the mere mention of", + "orcs. And thus he", + "carried his battle to" + ), + new BookPageInfo + ( + "the orc camps, and", + "died there, unable to", + "rise above his own", + "childhood experiences", + "depicted in his book,", + "\"The Burning of", + "Trinsic.\" 'Tis a", + "shame that even our" + ), + new BookPageInfo + ( + "mightiest men fall", + "prey to this", + "ignorance!", + " Are there not", + "legends of orcs", + "adopting human", + "children to raise as", + "their own? Tales of" + ), + new BookPageInfo + ( + "complex societies built", + "underground by races", + "we regard as bestial?", + " Let us not repeat", + "the mistake of", + "Japheth of the", + "Paladins, and let us", + "cease to persecute the" + ), + new BookPageInfo + ( + "nonhuman races,", + "before we discover", + "that we are harming", + "ourselves in the", + "process." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public DiversityOfOurLand() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public DiversityOfOurLand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Quest of the Virtues + public class QuestOfVirtues : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Quest of the Virtues", "Autenil", + new BookPageInfo + ( + "Volume 1", + "Chapter 1: Starting out", + "I begin my Quest in the", + "fine bank of Skara Brae.", + "My Quest is to travel by", + "foot to the eight shrines", + "of Britannia. Although", + "this may sound easy, it" + ), + new BookPageInfo + ( + "is hampered because I", + "have forsaken my abilities", + "and worldly possessions.", + "Instead, all that I have", + "to live by are a set of", + "plain clothes, a ship to", + "sail the seas and my", + "diary in which to record" + ), + new BookPageInfo + ( + "my adventures. Onward", + "Ho towards the Shrine of", + "Spirituality!", + "", + "Chapter 2: The Road to", + "Spirituality", + "", + "Since I have forsaken" + ), + new BookPageInfo + ( + "magic as a form of", + "travel, I must find a way", + "to the mainland from this", + "island town. Fortunately,", + "I was able to barter a", + "ride from the ferryman.", + "Upon reaching the", + "mainland, I followed the" + ), + new BookPageInfo + ( + "road until it turned", + "North, yet I must", + "continue Eastward. I", + "paused to listen to the", + "pleasant sounds of the", + "birds chirping and enjoyed", + "the peace away from the", + "busy life. Upon reaching" + ), + new BookPageInfo + ( + "the Hedge Maze I recalled", + "a story about the mage", + "Relvinian in the days of", + "old and turned South to", + "go around it. Possessing", + "no weapons and having", + "forsaken my training, I", + "heeded the sign that said," + ), + new BookPageInfo + ( + "\"Enter and Become One", + "Among Ghosts.\" The River", + "forced me to turn South", + "and then continue East.", + "", + "Chapter 3: Spirituality", + "", + "Spirituality is the leader" + ), + new BookPageInfo + ( + "of the Virtues. It is the", + "meditation and", + "understanding of all other", + "Virtues. Without", + "Spirituality, one cannot", + "completely follow the", + "Virtues, for It is the", + "dedication and adherance" + ), + new BookPageInfo + ( + "to them.", + "", + "Chapter 4: Finding Honor", + "", + "My next visit will be to", + "the Shrine of Honor,", + "which lies a fair distance", + "to the South. There is a" + ), + new BookPageInfo + ( + "road to my East that I", + "will follow to the town", + "of Trinsic. I found a", + "warm room at the", + "Traveller's Inn, and woke", + "refreshed in the morning", + "to hear the sounds of", + "nature as I prepared to" + ), + new BookPageInfo + ( + "continue my journeys. I", + "thanked the kind innkeeper", + "and headed out of Trinsic", + "and South. I conversed", + "briefly with one lucky", + "enough to own a house in", + "the beautiful country and", + "he bade me good fortune" + ), + new BookPageInfo + ( + "on my travels. I used my", + "rusty blade to cut", + "through dense jungles", + "past ruins of a forgotten", + "realm, now inhabited only", + "by the Undead. Many a", + "mongbat did hamper my", + "journey, but finally I" + ), + new BookPageInfo + ( + "arrived at the Shrine of", + "Honor.", + "", + "Chapter 5: Honor", + "", + "Many link Honor and", + "battle, but it can be", + "used with any aspect of" + ), + new BookPageInfo + ( + "Life. Honor is to abide", + "by the rules, dishonor is", + "to cheat; to seek the", + "unfair advantage. I vowed", + "to always live life with", + "Honor.", + "", + "Chapter 6: Seeking Valor" + ), + new BookPageInfo + ( + "I must now embark on my", + "trusty small ship, the", + "Hollandia, to the South", + "and East, to a small", + "island where few have", + "travelled. I know not yet", + "what I will encounter at", + "sea, so I bid the Virtues" + ), + new BookPageInfo + ( + "grant me safety. So", + "begins my voyage. I", + "managed to sail unnoticed", + "past some water", + "elementals which took a", + "fair bit of navigation", + "from my tillerman.", + "However, I arrived without" + ), + new BookPageInfo + ( + "incident.", + "", + "Chapter 7: Valor", + "", + "The Shrine of Valor is", + "protected by many a", + "beast far too poisonous", + "and foul for myself to" + ), + new BookPageInfo + ( + "vanquish. One mush show", + "Valor to approach the", + "Shrine! Valor is often", + "shown in one's willingness", + "to fight what maybe a", + "losing battle upon which", + "he believes. It takes", + "Valor to stand your" + ), + new BookPageInfo + ( + "ground against the many", + "murderers and lawbreakers", + "in our lands. You may", + "lose, but you show Valor", + "in that you fight that", + "which must be opposed.", + "Fight the fights you", + "believe in, not just the" + ), + new BookPageInfo + ( + "fights you think you can", + "win.", + "", + "Chapter 8: The Voyage to", + "Humility", + "", + "My journey will continue", + "to the East towards the" + ), + new BookPageInfo + ( + "Shrine of Humility. I", + "launch my boat from the", + "West side of the Island", + "of Valor, where I made", + "my daring escape from", + "the many Giant Serpents", + "chasing me with their", + "poisonous venom and" + ), + new BookPageInfo + ( + "hissing tongues. Beautiful", + "blue waves washed over", + "the bow of the boat as", + "dolphins played, merrily", + "leading me on. The voyage", + "is long and the water", + "turbulent but finally land", + "was struck. Quickly I ran," + ), + new BookPageInfo + ( + "eager to find my final", + "destination for the day.", + "", + "Chapter 9: Humility", + "", + "The Shrine of Humility is", + "surprisingly spartan; it is", + "merely a grove of stone" + ), + new BookPageInfo + ( + "pillars with an ankh and", + "the Humility stone at its", + "center. I would", + "characterize Humility as", + "this Quest; returning to", + "my roots in this world. I", + "have rejected my", + "possessions and my wealth" + ), + new BookPageInfo + ( + "in order to rely only", + "upon my cunning and", + "instincts. No longer have", + "I that which makes me", + "Glorious to others, but I", + "have only that which I", + "need to survive. I may no", + "longer rely upon myself, I" + ), + new BookPageInfo + ( + "must rely upon others", + "for my survival. My", + "journey to Humility has", + "only made me realize even", + "more how Blessed I have", + "been." + ), + new BookPageInfo + ( + "Chapter 10: Onward to", + "Honesty", + "", + "Now the journey will turn", + "South towards the Island", + "of Ice. However, my", + "voyages and excursions", + "into the jungle have made" + ), + new BookPageInfo + ( + "me quite tired, so I will", + "camp here beside a river", + "near the Shrine of", + "Humility for the night. I", + "awake the next morning", + "refreshed and invigorated,", + "but also under attack! A", + "Headless One has noticed" + ), + new BookPageInfo + ( + "my rise from slumber and", + "attacks viciously. My", + "trusty cleaver was in my", + "hand instantly, but my", + "lack of skill with the", + "weapon delayed the death", + "of the creature. I", + "launched the Hollandia and" + ), + new BookPageInfo + ( + "set sail for the Island of", + "Ice, seeking the Shrine of", + "Honesty. As I sail the", + "vast oceans, I find myself", + "desiring the company of", + "my fellow man. Hopefully I", + "shall meet some kind of", + "traveller with whom I may" + ), + new BookPageInfo + ( + "exchange a few words.", + "Shortly I was landing my", + "boat on the North end of", + "the Island of Ice. I", + "quickly made my way", + "through snow across the", + "frigid tundra while trying", + "to keep warm. The Shrine" + ), + new BookPageInfo + ( + "of Honesty bid me", + "welcome as I felth the", + "warmth radiate throughout", + "me.", + "", + "Chapter 11: Honesty", + "", + "Honesty is to uphold and" + ), + new BookPageInfo + ( + "defend the truth at all", + "times. Furthermore,", + "Honesty requires us to", + "be fair and true to our", + "fellow man; not taking", + "undue advantage. Honesty", + "is a Virtue often lacking", + "in today's world. It seems" + ), + new BookPageInfo + ( + "as though people are out", + "to gain wealth with no", + "regard to Honesty", + "towards other people.", + "Honesty is the foundation", + "upon which trust is built.", + "If the foundation", + "crumbles, everything built" + ), + new BookPageInfo + ( + "upon it must fall. The", + "cold environment which", + "houses the Shrine of", + "Honesty is a testament", + "to its value in today's", + "society. The symbolic cold", + "and secluded location", + "shows us that only the" + ), + new BookPageInfo + ( + "most dedicated to", + "pursuing Honesty will", + "achieve it. May we all be", + "Honest with our fellow", + "man and remember to", + "treat them how we would", + "like to be treated." + ), + new BookPageInfo + ( + "Chapter 12: The Path to", + "Sacrifice.", + "", + "I made my way to the", + "West side of the Island", + "of Ice. From there I", + "launch the Hollandia and", + "sail slightly to the North" + ), + new BookPageInfo + ( + "and West. I land my boat", + "just East of the Shrine", + "of Sacrifice, so my road", + "is West. Although I enjoy", + "the sea, I am happy to", + "be back on the mainland", + "for the final three", + "Shrines." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public QuestOfVirtues() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public QuestOfVirtues( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Regarding Llamas + public class RegardingLlamas : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Regarding Llamas", "Simon", + new BookPageInfo + ( + " Llamas are curious", + "beasts, shaggy and", + "sought after for their", + "wool, yet of a", + "curiously arrogant", + "disposition reflected", + "in their eyes. They", + "live in mountainous" + ), + new BookPageInfo + ( + "areas, though who", + "may have first tamed", + "them is lost in the", + "mists of history.", + " 'Tis a well-known", + "fact that llamas can", + "indeed be tamed, and", + "used as grazing" + ), + new BookPageInfo + ( + "animals, for their", + "meat, and of course", + "for their wool. Yet", + "'tis lesser known that", + "their ornery", + "disposition and", + "tendency to spit at", + "those they dislike" + ), + new BookPageInfo + ( + "makes them appealing", + "guard creatures as", + "well, though they", + "have little sound with", + "which to sound an", + "alarum." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public RegardingLlamas() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public RegardingLlamas( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Talking to Wisps + public class TalkingToWisps : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Talking to Wisps", "Yorick ofMoonglow", + new BookPageInfo + ( + "This volume was", + "sponsored by", + "donations from Lord", + "Blackthorn, ever a", + "supporter of", + "understanding the", + "other sentient races", + "of Britannia." + ), + new BookPageInfo + ( + "-", + " Wisps are the most", + "intelligent of the", + "nonhuman races", + "inhabiting Britannia.", + "'Tis claimed by the", + "great sages that", + "someday we shall be" + ), + new BookPageInfo + ( + "able to converse with", + "them openly in our", + "native", + "tongue--indeed, we", + "must hope that wisps", + "learn our language,", + "for it is not possible", + "for humans to" + ), + new BookPageInfo + ( + "pronounce wispish!", + " The wispish", + "language seems to", + "only contain one", + "vowel, the letter Y.", + "However, the letters", + "W, C, M, and L seem", + "to be treated" + ), + new BookPageInfo + ( + "grammatically as", + "vowels, and in", + "addition every letter", + "is followed by what", + "sounds to the human", + "ear like a glottal stop.", + "It is possible that the", + "glottal stop is" + ), + new BookPageInfo + ( + "considered a vowel as", + "well.", + " Wisps do make use", + "of what sound to us", + "like pitch and", + "emphasis shifts", + "similar to", + "exclamations and" + ), + new BookPageInfo + ( + "questions.", + " The average word is", + "wispish seems to", + "consist of three", + "phonemes and three", + "glottal stops, plus", + "possibly a pitch shift.", + "It often sounds like a" + ), + new BookPageInfo + ( + "fire burning or", + "crackling. Some have", + "speculated that what", + "we are analyzing is", + "in fact nothing more", + "than the very air", + "crackling near the", + "wisp's glow, and not" + ), + new BookPageInfo + ( + "language, but this is", + "of course unlikely." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TalkingToWisps() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public TalkingToWisps( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Taming Dragons + public class TamingDragons : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Taming Dragons", "Wyrd Beastmaster", + new BookPageInfo + ( + " I have not much to", + "tell about dragons. The", + "sole time I approached", + "one with an eye", + "towards taming it,", + "my initial attempts at", + "calming it met with", + "failure. It fixed a" + ), + new BookPageInfo + ( + "massive beady eye", + "upon me, and began", + "its slithering", + "approach, intending no", + "doubt to insert me", + "into its maw and bear", + "down with its teeth.", + " However, as I was" + ), + new BookPageInfo + ( + "engaged in what", + "remains to this day", + "the most terrifying", + "combat of my life,", + "the dragon suddenly", + "whirled as if in a", + "panic, ran a short", + "distance, took off into" + ), + new BookPageInfo + ( + "the air, then", + "transformed into a", + "whirlwind. Lastly, it", + "exploded, showering", + "gouts of black blood", + "and heaving, stinking", + "flesh upon miles of", + "countryside. The" + ), + new BookPageInfo + ( + "fireball was massive,", + "enough to light a city,", + "I should surmise.", + " I never did discover", + "the exact cause of", + "this strange behavior,", + "except to assume that", + "it was not typical for" + ), + new BookPageInfo + ( + "this reptilian species.", + "My best guesses", + "revolve around a", + "magical fracture in", + "the nature of reality,", + "which is far too", + "esoteric a territory", + "for one of my limited" + ), + new BookPageInfo + ( + "scholarship.", + " Hence my basic", + "advice to those who", + "seek to tame a", + "dragon-be sure that", + "thou hast mastered", + "the twin skills of", + "taming animals, and" + ), + new BookPageInfo + ( + "running away very", + "very fast." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TamingDragons() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public TamingDragons( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Bold Stranger + public class BoldStranger : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Bold Stranger", "Old Fabio the Poor", + new BookPageInfo + ( + " In a time before", + "time, the Gods that Be", + "assembled a group of", + "artisans, craftsmen", + "and lore masters", + "(for, yes, even in", + "those days, art", + "existed) to create the" + ), + new BookPageInfo + ( + "world of Sosaria. To", + "this group, the gods", + "gave a tiny world,", + "Rytabul, in which to", + "test their works, to", + "see if they were of", + "the quality desired", + "for the true world in" + ), + new BookPageInfo + ( + "which they would be", + "placed. And though", + "the gods were tight", + "fisted with their gold,", + "this small crew", + "worked hard and long,", + "and were happy in", + "their tasks." + ), + new BookPageInfo + ( + " A small corner of", + "Rytabul had been", + "claimed by the artisan", + "Selrahc the Slow.", + "Though he was not", + "the fastest of the", + "assembled workers,", + "the gods smiled upon" + ), + new BookPageInfo + ( + "his work, even", + "presenting him with", + "a mystic talisman", + "proclaiming his work", + "the best among the", + "newer artisans. And", + "so Selrahc went about", + "his business, creating" + ), + new BookPageInfo + ( + "hundreds of designs", + "which would one day", + "add color and variety", + "to Sosaria.", + " One day a", + "stranger appeared to", + "Selrahc. His chest", + "was bare and he wore" + ), + new BookPageInfo + ( + "trousers of the", + "brightest green, and", + "wherever he went,", + "plants grew in his", + "footsteps. This", + "caused Selrahc no end", + "of trouble, the", + "stranger always" + ), + new BookPageInfo + ( + "looking over his", + "shoulder, and the", + "plants sprouting in", + "places Selrahc", + "required to ply his", + "art. And so Selrahc", + "approached the", + "stranger and bade" + ), + new BookPageInfo + ( + "him speak. But this", + "man in green", + "remained silent.", + "Selrahc pleaded with", + "the stranger to give", + "his name, and would", + "he please leave", + "Selrahc to his work." + ), + new BookPageInfo + ( + "But this mysterious", + "stranger remained", + "mute.", + " This angered", + "Selrahc mightily. Who", + "was this silent man,", + "interfering with", + "tasks the gods" + ), + new BookPageInfo + ( + "themselves had", + "entrusted to Selrahc?", + "In an attempt to", + "embarrass this", + "interloper, Selrahc", + "stole his green", + "trousers, leaving him", + "naked and open to" + ), + new BookPageInfo + ( + "comments about his", + "very manhood, and", + "still the stranger", + "would not speak,", + "would not leave this", + "tiny corner of", + "Rytabul.", + " Vexed to his very" + ), + new BookPageInfo + ( + "limits, Selrahc took", + "his war axe and", + "smote the silent one", + "mightily, again and", + "again, until the silent", + "stranger ran away,", + "having never said a", + "word, and never" + ), + new BookPageInfo + ( + "showed himself in", + "Rytabul again.", + " Thus endeth the", + "tale of the bold", + "stranger." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public BoldStranger() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public BoldStranger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Burning of Trinsic + public class BurningOfTrinsic : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Burning of Trinsic", "Japheth of Trinsic", + new BookPageInfo + ( + " 'Twas a sight to", + "see, the sunlight", + "falling lightly on the", + "sandstone walls of", + "Trinsic 'pon a", + "morning in spring.", + " Children ran along", + "the parapets and" + ), + new BookPageInfo + ( + "walkways, their", + "laughter and running", + "providing music to the", + "daybreak, despite", + "their oft-ragged", + "clothing.", + " And I was one of", + "those young ones," + ), + new BookPageInfo + ( + "letting my joy rise", + "up to the skies.", + " Little did we all", + "know of the darker", + "days that would lie", + "ahead, for we were", + "too young.", + " Had we but gained" + ), + new BookPageInfo + ( + "access to the quiet", + "councils held in the", + "Paladin tower as it", + "faced the sea,", + "councils lit by", + "candlelight and", + "worry, we would", + "have learned more of" + ), + new BookPageInfo + ( + "the fears of", + "imminent attack from", + "the forest, where", + "foul creatures born", + "of dank caves and", + "darkness were", + "marauding ever more", + "often into the lands" + ), + new BookPageInfo + ( + "around Trinsic's", + "moat.", + " But we were", + "children! The", + "parapets and the moat", + "were places to play,", + "not stout defenses,", + "and we gave no" + ), + new BookPageInfo + ( + "thought to the", + "necessities that must", + "have required their", + "construction.", + " We used to reach", + "the sheltered", + "orchards on the lee", + "side of the parapet" + ), + new BookPageInfo + ( + "walls, where the", + "southern river cut", + "through the city, by", + "swimming across the", + "water.", + " The rich folk who", + "lived in the great", + "manses there would" + ), + new BookPageInfo + ( + "shout from their", + "windows and shake", + "their fists, for we", + "would run through", + "their gardens and", + "tear up the delicate", + "foxgloves and", + "orfleurs with our" + ), + new BookPageInfo + ( + "unshod dirty feet.", + "Then we would dive", + "into the water and", + "splash merrily to the", + "fruit trees.", + " The southern", + "river lazily slid", + "under the an ungated" + ), + new BookPageInfo + ( + "arch in the mighty", + "wall, and we would", + "lay on the grassy", + "bank and watch it", + "gurgle by the lily", + "pads.", + " That spring that", + "pleasant spot became" + ), + new BookPageInfo + ( + "the doorway through", + "which our city of", + "Trinsic let in the", + "monstrous deformed", + "humanoids that", + "savaged us. I lay upon", + "that grassy bank and", + "watched them wade" + ), + new BookPageInfo + ( + "in, their coarse hair", + "wet and matted, algae", + "and muck festooning", + "their wild brows.", + " They caught sight", + "of a quicksilver girl", + "with bright blond hair", + "and lively eyes. Her" + ), + new BookPageInfo + ( + "name was Leyla, and", + "that spring I had held", + "fond dreams of", + "holding her hand and", + "sharing flavored ice", + "while dangling our", + "feet off the small", + "bridge by Smugglers" + ), + new BookPageInfo + ( + "Gate.", + " And I said nothing", + "when they caught", + "her, and did not cry", + "out when they", + "dragged her off", + "through that breach in", + "our wall, and did not" + ), + new BookPageInfo + ( + "warn the city when I", + "saw the helmeted orc", + "captains call the", + "charge upon the", + "mansions.", + " Blame me not, for", + "I was but a child, and", + "one who hid in the" + ), + new BookPageInfo + ( + "branches of the peach", + "trees, all a-tremble", + "whilst I watched the", + "smoke rise from Sean", + "the tailor's, and fire", + "lash out at the roof of", + "witchy Eleanor's", + "tavern." + ), + new BookPageInfo + ( + " To this day I have", + "had no word of", + "Leyla, and to this", + "day the smell of", + "burning wood can", + "conjure terrible", + "dreams. Yet with the", + "eyes of adulthood, 'tis" + ), + new BookPageInfo + ( + "possible to examine", + "the flaws in the", + "defense of Trinsic on", + "that fateful day, and", + "the reasons why our", + "walls are now", + "double-thick, and", + "why our buildings" + ), + new BookPageInfo + ( + "are now built as", + "fortresses within a", + "somber fortified city.", + " While I can look", + "out from the top of", + "the new Paladin", + "tower, and spy the", + "mighty white sails" + ), + new BookPageInfo + ( + "across the barrier", + "island, and can", + "descry the small", + "hollow south of the", + "city where gypsies", + "are wont to camp, I", + "can also envision the", + "city as it might be" + ), + new BookPageInfo + ( + "burning, and I bless", + "the bargain we made:", + "space for safety,", + "grace for sturdiness,", + "and wood for stone.", + " Whilst I live, I", + "shall not see Trinsic", + "burn, and no more" + ), + new BookPageInfo + ( + "cries of little girls", + "will haunt the sleep", + "of our fair citizens.", + " - Japheth, Paladin", + "Guildmaster of the", + "City of Trinsic" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public BurningOfTrinsic() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public BurningOfTrinsic( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Fight + public class TheFight : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Fight", "M. de la Garza", + new BookPageInfo + ( + " A cold autumn's", + "morning with misty", + "fog secures a dozen", + "brave knights,", + "supplying hidden", + "shelter from prying", + "eyes deep in the", + "foothills of the" + ), + new BookPageInfo + ( + "vibrant valley.", + "Dragons soar like", + "fierce warriors,", + "circling around and", + "around, then roaring", + "like thunder, rallying", + "all that listen. The", + "dragons land swiftly" + ), + new BookPageInfo + ( + "beside the proud", + "warriors, bending", + "necks and extending", + "wings, lifting black", + "claws and allowing", + "valiant fighters to", + "ride forth and win an", + "arisen battle. The" + ), + new BookPageInfo + ( + "increasing winds", + "silence the sounds of", + "combat, and they", + "fight, standing their", + "ground like mothers", + "protecting their", + "childern, bright", + "armor flashing as" + ), + new BookPageInfo + ( + "each one falls.", + " A cold autumn's", + "evening with misty", + "fog cradles a dozen", + "battered corpses of", + "knights, creasing", + "them in currents of", + "winds that run deep" + ), + new BookPageInfo + ( + "in the foothills of the", + "desolate valley.", + "Dragons glide like", + "silent angels, circling", + "around and around,", + "then calling like", + "banshees; keening", + "cries of mourning." + ), + new BookPageInfo + ( + "The dragons land", + "heavily beside the", + "peaceful bodies,", + "bending necks and", + "extending wings,", + "lifting black claws", + "and allowing valiant", + "fighters to ride forth" + ), + new BookPageInfo + ( + "and win an arisen", + "battle. The increasing", + "winds silence the", + "sounds of combat, and", + "they fight, standing", + "their ground like", + "mothers protecting", + "their childern, bright" + ), + new BookPageInfo + ( + "armor flashing as", + "each one falls.", + " A cold autumn's", + "evening with misty", + "fog cradles a dozen", + "battered corpses of", + "knights, creasing", + "them in currents of" + ), + new BookPageInfo + ( + "winds that run deep", + "in the foothills of the", + "desolate valley.", + "Dragons glide like", + "silent angels, circling", + "around and around,", + "then calling like", + "banshees; keening" + ), + new BookPageInfo + ( + "cries of mourning.", + "The dragons land", + "heavily beside the", + "peaceful bodies,", + "bending necks and", + "extending wings,", + "lifting black claws", + "and pinching the" + ), + new BookPageInfo + ( + "sacred ground and", + "new eternal home.", + "The dying winds", + "whistle among the", + "dead in somber", + "procession, and they", + "lie, grasping weapons", + "to protect themselves" + ), + new BookPageInfo + ( + "like knights still in", + "battle, shattered", + "armor shining like", + "newly born stars." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TheFight() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public TheFight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Life of a Travelling Minstrel + public class LifeOfATravellingMinstrel : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Life of a Travelling Minstrel", "Sarah of Yew", + new BookPageInfo + ( + " While 'tis true that", + "the musician who", + "seeketh only to make", + "sweet music for", + "herself and for", + "others needs little", + "more than some", + "talent, and stern" + ), + new BookPageInfo + ( + "practice at the chosen", + "instrument, those of", + "us who seek the open", + "road shall find indeed", + "that a greater skill is", + "required. Herein", + "discover those secrets", + "which I have learned" + ), + new BookPageInfo + ( + "over the years as an", + "itinerant performer...", + " Once I was in", + "Jhelom, and", + "accidentally angered a", + "bravo of some local", + "repute, whose blade", + "flickered all too" + ), + new BookPageInfo + ( + "eagerly near my", + "slender neck (for I", + "was young then).", + "After various threats", + "to \"ruin my pretty", + "face\" this bravo", + "grabbed my arm in a", + "most unseemly" + ), + new BookPageInfo + ( + "fashion and tossed", + "me into a barbaric", + "enclosure locally", + "entitled a dueling pit.", + "My plaintive cries", + "for help went", + "unheeded by the", + "guards, for the" + ), + new BookPageInfo + ( + "inhabitants of Jhelom", + "are eager indeed to", + "measure fighting", + "prowess at any time!", + " What saved me was", + "the ability to", + "improvise a melody", + "and tune that" + ), + new BookPageInfo + ( + "satirized the", + "proceedings, and", + "sufficiently angered", + "an onlooker to prod", + "him to coming to my", + "defense. Once that", + "fight was underway,", + "I was able to make" + ), + new BookPageInfo + ( + "good my escape.", + "Hence, I regard the", + "ability to incite fights", + "as indispensable to", + "the prudent bard.", + " Upon another", + "occasion, 'twas the", + "obverse side of that" + ), + new BookPageInfo + ( + "coin which saved me,", + "for I was being held", + "prisoner by a", + "particularly nasty", + "band of ruffians who", + "had seized me", + "unawares from the", + "road to Vesper." + ), + new BookPageInfo + ( + " They had worked", + "themselves into a", + "frenzy and were", + "ready to attack and I", + "fear, tear me limb", + "from limb, when I", + "began to sing", + "frantically, tapping" + ), + new BookPageInfo + ( + "my falled drum with", + "my tied up feet. The", + "melody developed into", + "a soothing one, and", + "the brigands slowly", + "calmed down to the", + "extent of apologizing,", + "and they let me go!" + ), + new BookPageInfo + ( + " A final example I", + "would pray you grant", + "your attention: once I", + "was lost upon a large", + "isle far to the east of", + "the mainland, well", + "beyond Serpent's", + "Hold, where lava" + ), + new BookPageInfo + ( + "made its sluggish", + "way across the", + "surface landscape.", + "And this accursed", + "land was filled with", + "vile beasts and", + "cunning dragons.", + " I was being pursued" + ), + new BookPageInfo + ( + "by one of said fell", + "dragons when I found", + "myself trapped. I", + "quickly skirted a", + "bubbling pool of molten", + "rock and attempted to", + "hide.", + " The dragon scented" + ), + new BookPageInfo + ( + "me and was", + "preparing to skirt the", + "pool, when I began to", + "play a lusty tune", + "upon my lute that", + "attracted its attention.", + "Mesmerized and", + "enticed by the" + ), + new BookPageInfo + ( + "melody, it stepped", + "directly toward sme,", + "and into the", + "lava-where its foot", + "was so burned that it", + "quickly hopped away,", + "undignified and", + "annoyed." + ), + new BookPageInfo + ( + " 'Tis my fond hope", + "that other travelling", + "minstrels shall learn", + "from my experiences", + "and apply themselves", + "to practicing these", + "skills in order to", + "preserve life and" + ), + new BookPageInfo + ( + "limb." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public LifeOfATravellingMinstrel() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public LifeOfATravellingMinstrel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Major Trade Associations + public class MajorTradeAssociation : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Major Trade Associations", "Pieter of Vesper", + new BookPageInfo + ( + " There are ten major", + "trade associations that", + "operate legitimately in", + "the lands of Britannia", + "and among its trading", + "partners. Many of", + "these guilds are", + "divided into local or" + ), + new BookPageInfo + ( + "specialty subguilds,", + "who use the same", + "colors but vary the", + "heraldic pattern.", + " There are many", + "lesser trade", + "associations that have", + "closed membership," + ), + new BookPageInfo + ( + "and one can join them", + "only by invitation.", + "Beltran's Guide to", + "Guilds is the", + "definitive text on the", + "full range of guilds", + "and other associations", + "in Britannia, and I" + ), + new BookPageInfo + ( + "heartily recommend", + "it.", + " In what follows I", + "have attempted to", + "bring together the", + "known information", + "regarding these", + "guilds. I offer thee" + ), + new BookPageInfo + ( + "the name, typical", + "membership, heraldic", + "colors, known", + "specialty", + "organizations within", + "the larger guild, and", + "any known", + "affiliations to other" + ), + new BookPageInfo + ( + "guilds, which often", + "occur because of", + "trade reasons.", + "", + "The Guild of Arcane", + "Arts", + "Members: alchemists", + "and wizards" + ), + new BookPageInfo + ( + "Colors: blue and purple", + "Subguilds: Illusionists,", + "Mages, Wizards", + "Affiliations: Healer's", + "Guild", + "", + "The Warrior's Guild", + "Members:" + ), + new BookPageInfo + ( + "mercenaries,", + "soldiery, guardsmen,", + "weapons masters,", + "paladins.", + "Colors: Blue and red", + "Subguilds: Cavalry,", + "Fighters, Warriors", + "Affiliations: League" + ), + new BookPageInfo + ( + "of Rangers", + "", + "League of Rangers", + "Members: rangers,", + "bowyers, animal", + "trainers", + "Colors: Red, gold and", + "blue" + ), + new BookPageInfo + ( + "", + "Guild of Healers", + "Members: healers", + "Colors: Green, gold,", + "and purple", + "Affiliations: Guild of", + "Arcane Arts" + ), + new BookPageInfo + ( + "Mining Cooperative", + "Members: miners", + "Colors: blue and black", + "checkers, with a gold", + "cross", + "Affiliations: Order of", + "Engineers" + ), + new BookPageInfo + ( + "Merchants'", + "Association", + "Members:", + "innkeepers,", + "tavernkeepers,", + "jewelers,", + "provisioners", + "Colors: gold coins on a" + ), + new BookPageInfo + ( + "green field for", + "Merchants. White", + "and green for the", + "others.", + "Subguilds: Barters,", + "Provisioners,", + "Traders, Merchants" + ), + new BookPageInfo + ( + "Order of Engineers", + "Members: tinkers and", + "engineers", + "Colors: Blue, gold, and", + "purple vertical bars", + "Affiliations: Mining", + "Cooperative" + ), + new BookPageInfo + ( + "Society of Clothiers", + "Members: tailors and", + "weavers", + "Colors: Purple, gold,", + "and red horizontal", + "bars", + "", + "Maritime Guild" + ), + new BookPageInfo + ( + "Members: fishermen,", + "sailors, mapmakers,", + "shipwrights", + "Colors: blue and white", + "Subguilds:", + "Fishermen, Sailors,", + "Shipwrights" + ), + new BookPageInfo + ( + "Bardic Collegium", + "Members: bards,", + "musicians,", + "storytellers, and other", + "performers", + "Colors: Purple, red", + "and gold checkerboard" + ), + new BookPageInfo + ( + "Society of Thieves", + "Members: beggars,", + "cutpurses, assassins,", + "and brigands", + "Colors: red and black", + "Subguilds: Rogues", + "(beggars), Assassins,", + "Thieves" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public MajorTradeAssociation() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public MajorTradeAssociation( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Rankings of Trades + public class RankingsOfTrades : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Rankings of Trades", "Lord Higginbotham", + new BookPageInfo + ( + " Whilst 'tis true that", + "within each trade, one", + "finds differing titles", + "and accolades granted", + "to the members of a", + "given guild,", + "nonetheless for the", + "betterment of trade" + ), + new BookPageInfo + ( + "and understanding,", + "we must have a", + "commonality of", + "titling.", + " For those who may", + "find themselves", + "ignorant of the finer", + "distinctions between a" + ), + new BookPageInfo + ( + "three-knot member of", + "the Sailors' Maritime", + "Association and a", + "second thaumaturge,", + "this book shall serve", + "as a simple", + "introduction to the", + "common cant used" + ), + new BookPageInfo + ( + "when members of", + "differing guilds and", + "trade organizations", + "must trade with each", + "other and must", + "establish relative", + "credentials.", + " Neophyte" + ), + new BookPageInfo + ( + "Has shown interest", + "in learning the craft", + "and some meager", + "talent.", + " Novice", + "Is practicing basic", + "skills but has not been", + "admitted to full" + ), + new BookPageInfo + ( + "standing.", + " Apprentice", + "A student of the", + "discipline.", + " Journeyman", + "Warranted to practice", + "the discipline under", + "the eyes of a tutor." + ), + new BookPageInfo + ( + " Expert", + "A full member of the", + "guild.", + " Adept", + "A member of the", + "guild qualified to", + "teach others.", + " Master" + ), + new BookPageInfo + ( + "Acknowledged as", + "qualified to lead a hall", + "or business.", + " Grandmaster", + "Rarely a permanent", + "title, granted in", + "common parlance to", + "those who have" + ), + new BookPageInfo + ( + "shown extreme", + "mastery of their", + "craft recently." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public RankingsOfTrades() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public RankingsOfTrades( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region The Wild Girl of the Forest + public class WildGirlOfTheForest : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "The Wild Girl of the Forest", "Horace the Trader", + new BookPageInfo + ( + " Her name was", + "Leyla, she said, and", + "her hair was braided", + "wild with creepers", + "and thorns. I", + "marveled that they", + "did not hurt her, but", + "when I asked, she but" + ), + new BookPageInfo + ( + "shrugged and let her", + "eyes roam once more", + "across the woods.", + "Though I had my", + "hands securely", + "fastened by her", + "ropes, I itched to", + "reach out and comb" + ), + new BookPageInfo + ( + "that unruly golden", + "mane, dirtied and", + "leaf-ridden.", + " Her provenance,", + "she told me over", + "nights illumined by", + "campfires, was once", + "the city of Trinsic." + ), + new BookPageInfo + ( + "She claimed to have", + "been kidnapped and", + "raised by orcs, which", + "I judged an unlikely", + "tale, for all know orcs", + "delight in eating the", + "meat of honest folk.", + "When I told her this," + ), + new BookPageInfo + ( + "she laughed a fey", + "laugh, and gaily", + "admitted that honest", + "she was not, for oft", + "had she stolen folk", + "away from caravans", + "to loot their", + "possessions from an" + ), + new BookPageInfo + ( + "unconscious body!", + " At this, I began to", + "fear for my life, and", + "her smile seemed full", + "of teeth sharper than", + "a human ought to", + "have, for the tale of", + "orcish raising had" + ), + new BookPageInfo + ( + "struck fear into the", + "marrow of my bones.", + "\"Wilt thou eat me?\" I", + "asked, a-tremble,", + "fearing the answer.", + " And she cocked", + "her head at me, like a", + "wild animal facing a" + ), + new BookPageInfo + ( + "word that it dost not", + "understand, and the", + "fixity in her eyes", + "was a glimpse into", + "the deeper reaches of", + "the Abyss. But she", + "finally grunted, and", + "said, \"Nay,\" in a" + ), + new BookPageInfo + ( + "voice that recalled to", + "me a child. \"Nay,\"", + "she said, \"for thou", + "dost remind me of a", + "boy I knew once,", + "when I was a girl", + "who played in a city", + "of great sandstone" + ), + new BookPageInfo + ( + "walls, before I was", + "taken. He had sandy", + "hair like thee, and I", + "dreamt as a child of", + "holding his hand and", + "sharing flavored ice.", + "His name was", + "Japheth.\"" + ), + new BookPageInfo + ( + " The next morning", + "she let me go,", + "stripped of my pouch", + "and clothes, and bade", + "me run through the", + "woods, and to fear", + "recapture, for surely", + "her heart would not" + ), + new BookPageInfo + ( + "soften again. 'Twas a", + "fearful run, and I", + "came to the road to", + "Yew with welts and", + "scratches run", + "rampant crost my", + "skin, but I did not see", + "her again." + ), + new BookPageInfo + ( + " Oft have I", + "wondered of the boy", + "named Japheth, and", + "whether he", + "remembers a girl who", + "lived in sandstone", + "walls. The only", + "Japheth I know is the" + ), + new BookPageInfo + ( + "Guildmaster of", + "Paladins who died", + "last year warring", + "amidst the orcs, and", + "though he had indeed", + "sandy hair, I cannot", + "picture him side by", + "side with a feral girl" + ), + new BookPageInfo + ( + "whose tongue has", + "tasted of human", + "flesh.", + " Yet the paths of", + "fate are strange", + "indeed, and I suppose", + "'tis possible that this", + "paladin died" + ), + new BookPageInfo + ( + "defending his", + "remembered lady's", + "honor, unknowingly", + "struck down by the", + "orc that she called", + "father." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public WildGirlOfTheForest() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public WildGirlOfTheForest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Treatise on Alchemy + public class TreatiseOnAlchemy : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Treatise on Alchemy", "Felicia Hierophant", + new BookPageInfo + ( + " The alchemical", + "arts are notable for", + "their deceptive", + "simplicity. 'Tis true", + "that to our best", + "knowledge currently,", + "there are but eight", + "valid potions that can" + ), + new BookPageInfo + ( + "be made (though I", + "emphasize that new", + "discoveries may", + "always await).", + "However, the delicate", + "balance of confecting", + "the potions is", + "difficult indeed, and" + ), + new BookPageInfo + ( + "requires great skill.", + " To give thee an", + "example of the", + "simpler potions that", + "can be created by", + "those well-versed in", + "the subtleties of", + "alchemy:" + ), + new BookPageInfo + ( + " Black pearl, that", + "rare substance that is", + "oft found lying", + "unannounced upon the", + "surface of the", + "ground, when", + "properly crushed", + "with mortar and" + ), + new BookPageInfo + ( + "pestle, can yield a", + "fine powder. Said", + "powder in the proper", + "proportions when", + "mixed via the", + "alchemical arts can", + "yield a wonderfully", + "refreshing drink." + ), + new BookPageInfo + ( + " The revolting blood", + "moss so gingerly", + "scraped off of", + "windowsills by", + "fastidious housewives", + "is but a tiny cousin to", + "the wilder version,", + "which when properly" + ), + new BookPageInfo + ( + "prepared yields a", + "magical liquid that for", + "a time can make the", + "imbiber a more agile", + "and dextrous", + "individual.", + " However, beware", + "of the deadly" + ), + new BookPageInfo + ( + "nightshade, for it", + "yields a deceptively", + "sweet-tasting poison", + "that can prove highly", + "fatal to the drinker,", + "and in fact is also", + "used by assassins to", + "coat their blades." + ), + new BookPageInfo + ( + "Fortunately, this", + "latter art of poisoning", + "is little known!", + " There is much to", + "reward the student of", + "alchemy, indeed. The", + "rumours of longtime", + "alchemists losing" + ), + new BookPageInfo + ( + "their hair and", + "acquiring an", + "unhealthy pallor, not", + "to mention unsightly", + "blotches upon their", + "once-fair skin, are", + "unhappily, true. Yet", + "the joys of the mind" + ), + new BookPageInfo + ( + "make up for the", + "complete loss of", + "interest that others", + "may have in thee as", + "an object of", + "courtship, and I have", + "never regretted that", + "choice. Honestly," + ), + new BookPageInfo + ( + "truly. Not once." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TreatiseOnAlchemy() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public TreatiseOnAlchemy( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Virtue + public class VirtueBook : BaseBook + { + public static readonly BookContent Content = new BookContent + ( + "Virtue", "Lord British", + new BookPageInfo + ( + " Within this world", + "live people with many", + "different ideals, and", + "this is good. Yet what", + "is it within the people", + "of our land that sorts", + "out the good from the", + "evil, the cherished" + ), + new BookPageInfo + ( + "form the disdained?", + "Virtue, I say it is,", + "and virtue is the", + "logical outcome of a", + "people who wish to", + "live together in a", + "bonded society.", + " For without Virtues" + ), + new BookPageInfo + ( + "as a code of conduct", + "which people maintain", + "in their relations", + "with each other, the", + "fabric of that society", + "will become weakened.", + "For a society to grow", + "and prosper for all," + ), + new BookPageInfo + ( + "each must grant the", + "others a common base", + "of consideration.", + " I call this base the", + "Virtues. For though", + "one person might gain", + "personal advantage by", + "breaching such a" + ), + new BookPageInfo + ( + "code, the society as a", + "whole would suffer.", + " There are three", + "Principle Virtues that", + "should guide people to", + "enlightenment. These", + "are: Truth, Love and", + "Courage. From all the" + ), + new BookPageInfo + ( + "infinite reasons one", + "may have to found an", + "action, such as greed", + "or charity, envy or", + "pity, the three", + "Principle Virtues", + "stand out.", + " In fact all other" + ), + new BookPageInfo + ( + "virtues and vices can", + "be show to be built", + "from these principles", + "and their opposite", + "corruption's of", + "Falsehood, Hatred and", + "Cowardice. These", + "three Principles can" + ), + new BookPageInfo + ( + "be combined in eight", + "ways, which I will", + "call the eight virtues.", + "The eight virtues", + "which we should", + "build our society upon", + "follow.", + " Truth alone becomes" + ), + new BookPageInfo + ( + "Honesty, for without", + "honesty between our", + "people, how can we", + "build the trust which", + "is needed to", + "maximize our", + "successes.", + " Love alone becomes" + ), + new BookPageInfo + ( + "compassion, for at", + "some time or another", + "all of us will need the", + "compassion of others,", + "and most likely", + "compassion will be", + "shown to those who", + "have shown it." + ), + new BookPageInfo + ( + " Courage alone", + "becomes Valor,", + "without valor our", + "people will never", + "reach into the", + "unknown or to the", + "risky and will never", + "achieve." + ), + new BookPageInfo + ( + " Truth tempered by", + "Love give us Justice,", + "for only in a loving", + "search for the truth", + "can one dispense fair", + "Justice, rather than", + "create a cold and", + "callous people." + ), + new BookPageInfo + ( + " Love and Courage", + "give us Sacrifice, for", + "a people who love each", + "other will be willing", + "to make personal", + "sacrifices to help", + "other in need, which", + "one day, may be" + ), + new BookPageInfo + ( + "needed in return.", + " Courage and Truth", + "give us Honor, great", + "knights know this", + "well, that chivalric", + "honor can be found", + "by adhering to this", + "code of conduct." + ), + new BookPageInfo + ( + " Combining Truth,", + "Love and Courage", + "suggest the virtue of", + "Spirituality the virtue", + "that causes one to be", + "introspective, to", + "wonder about ones", + "place in this world" + ), + new BookPageInfo + ( + "and whether one's", + "deeds will be recorded", + "as a gift to the world", + "or a plague.", + " The final Virtue is", + "more complicated. For", + "the eighth combination", + "is that devoid of" + ), + new BookPageInfo + ( + "Truth, Love or", + "Courage which can", + "only exist in a state", + "of great Pride, which", + "of course is not a", + "virtue at all. Perhaps", + "this trick of fate is a", + "test to see if one can" + ), + new BookPageInfo + ( + "realize that the true", + "virtue is that of", + "Humility. I feel that", + "the people of", + "Magincia fail to see", + "this to such a degree", + "that I would not be", + "surprised if some ill" + ), + new BookPageInfo + ( + "fate awaited their", + "future.", + " Thus from the", + "infinite possibilities", + "which spawned the", + "Three Principles of", + "Truth, Love and", + "Courage, come the" + ), + new BookPageInfo + ( + "Eight Virtues of", + "Honesty, Compassion,", + "Valor, Justice,", + "Sacrifice, Honor,", + "Spirituality, and", + "Humility." + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public VirtueBook() : base( Utility.Random( 0xFEF, 2 ), false ) + { + } + + public VirtueBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion +} \ No newline at end of file diff --git a/Scripts/Items/Books/Defined/TranslatedGargoyleJournal.cs b/Scripts/Items/Books/Defined/TranslatedGargoyleJournal.cs new file mode 100644 index 000000000..d6f4b01a3 --- /dev/null +++ b/Scripts/Items/Books/Defined/TranslatedGargoyleJournal.cs @@ -0,0 +1,164 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TranslatedGargoyleJournal : BlueBook + { + public static readonly BookContent Content = new BookContent + ( + "Translated Journal", "Velis", + new BookPageInfo + ( + "This text has been", + "translated from a", + "gargoyle's journal", + "following his capture", + "and subsequent", + "reeducation.", + "", + " -Velis" + ), + new BookPageInfo + ( + "I write this in the", + "hopes that someday a", + "soul of pure heart and", + "mind will read it. We", + "are not the evil beings", + "that our cousin", + "gargoyles have made", + "us out to be. We" + ), + new BookPageInfo + ( + "consider them", + "uncivilized and they", + "have no concept of the", + "Principles. To you", + "who reads this, I beg", + "for your help in", + "saving my brethern", + "and preserving my" + ), + new BookPageInfo + ( + "race. We stand at the", + "edge of destruction as", + "does the rest of the", + "world. Once it was", + "written law that we", + "would not allow the", + "knowledge of our", + "civilization to spread" + ), + new BookPageInfo + ( + "into the world, no we", + "are left with little", + "choice...contact the", + "outside world in the hopes", + "of finding help to save", + "it or becoming the", + "unwilling bringers of", + "its damnation." + ), + new BookPageInfo + ( + " I fear my capture is", + "certain, the", + "controllers grow ever", + "closer to my hiding", + "place and I know if", + "they discover me, my", + "fate will be as that of", + "my brothers." + ), + new BookPageInfo + ( + "Although we resisted", + "with all our strength", + "it is now clear that we", + "must have assistance", + "or our people will be", + "gone. And if our", + "oppressor achieves", + "his goals our race will" + ), + new BookPageInfo + ( + "surely be joined buy", + "others.", + " Those of us who", + "have not yet been", + "taken hope to open a", + "path from the outside", + "world into the city.", + "We believe we have" + ), + new BookPageInfo + ( + "found weak areas in", + "the mountains that we", + "can successfully", + "knock through with", + "our limited supplies.", + "We will have to work", + "quickly and the risk", + "of being discovered is" + ), + new BookPageInfo + ( + "great, but no choice", + "remains..." + ), + new BookPageInfo + ( + ), + new BookPageInfo + ( + ), + new BookPageInfo + ( + "Kai Hohiro, 12pm.", + "10.11.2001", + "first one to be here" + ) + ); + + public override BookContent DefaultContent{ get{ return Content; } } + + [Constructable] + public TranslatedGargoyleJournal() : base( false ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( "Translated Gargoyle Journal" ); + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, "Translated Gargoyle Journal" ); + } + + public TranslatedGargoyleJournal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/RedBook.cs b/Scripts/Items/Books/RedBook.cs new file mode 100644 index 000000000..8f98a1ee4 --- /dev/null +++ b/Scripts/Items/Books/RedBook.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RedBook : BaseBook + { + [Constructable] + public RedBook() : base( 0xFF1 ) + { + } + + [Constructable] + public RedBook( int pageCount, bool writable ) : base( 0xFF1, pageCount, writable ) + { + } + + [Constructable] + public RedBook( string title, string author, int pageCount, bool writable ) : base( 0xFF1, title, author, pageCount, writable ) + { + } + + // Intended for defined books only + public RedBook( bool writable ) : base( 0xFF1, writable ) + { + } + + public RedBook( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Books/TanBook.cs b/Scripts/Items/Books/TanBook.cs new file mode 100644 index 000000000..50f1c556e --- /dev/null +++ b/Scripts/Items/Books/TanBook.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TanBook : BaseBook + { + [Constructable] + public TanBook() : base( 0xFF0 ) + { + } + + [Constructable] + public TanBook( int pageCount, bool writable ) : base( 0xFF0, pageCount, writable ) + { + } + + [Constructable] + public TanBook( string title, string author, int pageCount, bool writable ) : base( 0xFF0, title, author, pageCount, writable ) + { + } + + // Intended for defined books only + public TanBook( bool writable ) : base( 0xFF0, writable ) + { + } + + public TanBook( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Artifacts/DivineCountenance.cs b/Scripts/Items/Clothing/Artifacts/DivineCountenance.cs new file mode 100644 index 000000000..d067e91cb --- /dev/null +++ b/Scripts/Items/Clothing/Artifacts/DivineCountenance.cs @@ -0,0 +1,61 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DivineCountenance : HornedTribalMask + { + public override int LabelNumber{ get{ return 1061289; } } // Divine Countenance + + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 8; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BaseEnergyResistance{ get{ return 25; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public DivineCountenance() + { + Hue = 0x482; + + Attributes.BonusInt = 8; + Attributes.RegenMana = 2; + Attributes.ReflectPhysical = 15; + Attributes.LowerManaCost = 8; + } + + public DivineCountenance( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Resistances.Physical = 0; + Resistances.Fire = 0; + Resistances.Cold = 0; + Resistances.Energy = 0; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Artifacts/HatOfTheMagi.cs b/Scripts/Items/Clothing/Artifacts/HatOfTheMagi.cs new file mode 100644 index 000000000..8757af72b --- /dev/null +++ b/Scripts/Items/Clothing/Artifacts/HatOfTheMagi.cs @@ -0,0 +1,56 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HatOfTheMagi : WizardsHat + { + public override int LabelNumber{ get{ return 1061597; } } // Hat of the Magi + + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePoisonResistance{ get{ return 20; } } + public override int BaseEnergyResistance{ get{ return 20; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public HatOfTheMagi() + { + Hue = 0x481; + + Attributes.BonusInt = 8; + Attributes.RegenMana = 4; + Attributes.SpellDamage = 10; + } + + public HatOfTheMagi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Resistances.Poison = 0; + Resistances.Energy = 0; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Artifacts/HuntersHeaddress.cs b/Scripts/Items/Clothing/Artifacts/HuntersHeaddress.cs new file mode 100644 index 000000000..bba6634e1 --- /dev/null +++ b/Scripts/Items/Clothing/Artifacts/HuntersHeaddress.cs @@ -0,0 +1,56 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HuntersHeaddress : DeerMask + { + public override int LabelNumber{ get{ return 1061595; } } // Hunter's Headdress + + public override int ArtifactRarity{ get{ return 11; } } + + public override int BaseColdResistance{ get{ return 23; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public HuntersHeaddress() + { + Hue = 0x594; + + SkillBonuses.SetValues( 0, SkillName.Archery, 20 ); + + Attributes.BonusDex = 8; + Attributes.NightSight = 1; + Attributes.AttackChance = 15; + + } + + public HuntersHeaddress( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + switch ( version ) + { + case 0: + { + Resistances.Cold = 0; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Artifacts/SpiritOfTheTotem.cs b/Scripts/Items/Clothing/Artifacts/SpiritOfTheTotem.cs new file mode 100644 index 000000000..00fa98c2f --- /dev/null +++ b/Scripts/Items/Clothing/Artifacts/SpiritOfTheTotem.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SpiritOfTheTotem : BearMask + { + public override int LabelNumber{ get{ return 1061599; } } // Spirit of the Totem + + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 20; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public SpiritOfTheTotem() + { + Hue = 0x455; + + Attributes.BonusStr = 20; + Attributes.ReflectPhysical = 15; + Attributes.AttackChance = 15; + } + + public SpiritOfTheTotem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Resistances.Physical = 0; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/BaseClothing.cs b/Scripts/Items/Clothing/BaseClothing.cs new file mode 100644 index 000000000..df8bfc872 --- /dev/null +++ b/Scripts/Items/Clothing/BaseClothing.cs @@ -0,0 +1,998 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Engines.Craft; +using Server.Factions; + +namespace Server.Items +{ + public enum ClothingQuality + { + Low, + Regular, + Exceptional + } + + public interface IArcaneEquip + { + bool IsArcane{ get; } + int CurArcaneCharges{ get; set; } + int MaxArcaneCharges{ get; set; } + } + + public abstract class BaseClothing : Item, IDyable, IScissorable, IFactionItem, ICraftable, IWearableDurability + { + #region Factions + private FactionItem m_FactionState; + + public FactionItem FactionItemState + { + get{ return m_FactionState; } + set + { + m_FactionState = value; + + if ( m_FactionState == null ) + Hue = 0; + + LootType = ( m_FactionState == null ? LootType.Regular : LootType.Blessed ); + } + } + #endregion + + + private int m_MaxHitPoints; + private int m_HitPoints; + private Mobile m_Crafter; + private ClothingQuality m_Quality; + private bool m_PlayerConstructed; + protected CraftResource m_Resource; + private int m_StrReq = -1; + + private AosAttributes m_AosAttributes; + private AosArmorAttributes m_AosClothingAttributes; + private AosSkillBonuses m_AosSkillBonuses; + private AosElementAttributes m_AosResistances; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxHitPoints + { + get{ return m_MaxHitPoints; } + set{ m_MaxHitPoints = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitPoints + { + get + { + return m_HitPoints; + } + set + { + if ( value != m_HitPoints && MaxHitPoints > 0 ) + { + m_HitPoints = value; + + if ( m_HitPoints < 0 ) + Delete(); + else if ( m_HitPoints > MaxHitPoints ) + m_HitPoints = MaxHitPoints; + + InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int StrRequirement + { + get{ return ( m_StrReq == -1 ? (Core.AOS ? AosStrReq : OldStrReq) : m_StrReq ); } + set{ m_StrReq = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ClothingQuality Quality + { + get{ return m_Quality; } + set{ m_Quality = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool PlayerConstructed + { + get{ return m_PlayerConstructed; } + set{ m_PlayerConstructed = value; } + } + + public virtual CraftResource DefaultResource{ get{ return CraftResource.None; } } + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; Hue = CraftResources.GetHue( m_Resource ); InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosAttributes Attributes + { + get{ return m_AosAttributes; } + set{} + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosArmorAttributes ClothingAttributes + { + get{ return m_AosClothingAttributes; } + set{} + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosSkillBonuses SkillBonuses + { + get{ return m_AosSkillBonuses; } + set{} + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosElementAttributes Resistances + { + get{ return m_AosResistances; } + set{} + } + + public virtual int BasePhysicalResistance{ get{ return 0; } } + public virtual int BaseFireResistance{ get{ return 0; } } + public virtual int BaseColdResistance{ get{ return 0; } } + public virtual int BasePoisonResistance{ get{ return 0; } } + public virtual int BaseEnergyResistance{ get{ return 0; } } + + public override int PhysicalResistance{ get{ return BasePhysicalResistance + m_AosResistances.Physical; } } + public override int FireResistance{ get{ return BaseFireResistance + m_AosResistances.Fire; } } + public override int ColdResistance{ get{ return BaseColdResistance + m_AosResistances.Cold; } } + public override int PoisonResistance{ get{ return BasePoisonResistance + m_AosResistances.Poison; } } + public override int EnergyResistance{ get{ return BaseEnergyResistance + m_AosResistances.Energy; } } + + public virtual int ArtifactRarity{ get{ return 0; } } + + public virtual int BaseStrBonus{ get{ return 0; } } + public virtual int BaseDexBonus{ get{ return 0; } } + public virtual int BaseIntBonus { get { return 0; } } + + 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; } } + + public override bool CanEquip( Mobile from ) + { + if ( !Ethics.Ethic.CheckEquip( from, this ) ) + return false; + + if( from.AccessLevel < AccessLevel.GameMaster ) + { + 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( !AllowMaleWearer && !from.Female ) + { + if( AllowFemaleWearer ) + from.SendLocalizedMessage( 1010388 ); // Only females can wear this. + else + from.SendMessage( "You may not wear this." ); + + return false; + } + else if( !AllowFemaleWearer && from.Female ) + { + if( AllowMaleWearer ) + from.SendLocalizedMessage( 1063343 ); // Only males can wear this. + else + from.SendMessage( "You may not wear this." ); + + return false; + } + else + { + int strBonus = ComputeStatBonus( StatType.Str ); + int strReq = ComputeStatReq( StatType.Str ); + + if( from.Str < strReq || (from.Str + strBonus) < 1 ) + { + from.SendLocalizedMessage( 500213 ); // You are not strong enough to equip that. + return false; + } + } + } + + return base.CanEquip( from ); + } + + public virtual int AosStrReq{ get{ return 10; } } + public virtual int OldStrReq{ get{ return 0; } } + + public virtual int InitMinHits{ get{ return 0; } } + public virtual int InitMaxHits{ get{ return 0; } } + + public virtual bool AllowMaleWearer{ get{ return true; } } + public virtual bool AllowFemaleWearer{ get{ return true; } } + public virtual bool CanBeBlessed{ get{ return true; } } + + public int ComputeStatReq( StatType type ) + { + int v; + + //if ( type == StatType.Str ) + v = StrRequirement; + + return AOS.Scale( v, 100 - GetLowerStatReq() ); + } + + public int ComputeStatBonus( StatType type ) + { + if ( type == StatType.Str ) + return BaseStrBonus + Attributes.BonusStr; + else if ( type == StatType.Dex ) + return BaseDexBonus + Attributes.BonusDex; + else + return BaseIntBonus + Attributes.BonusInt; + } + + public virtual void AddStatBonuses( Mobile parent ) + { + if ( parent == null ) + return; + + int strBonus = ComputeStatBonus( StatType.Str ); + int dexBonus = ComputeStatBonus( StatType.Dex ); + int intBonus = ComputeStatBonus( StatType.Int ); + + if ( strBonus == 0 && dexBonus == 0 && intBonus == 0 ) + return; + + string modName = this.Serial.ToString(); + + if ( strBonus != 0 ) + parent.AddStatMod( new StatMod( StatType.Str, modName + "Str", strBonus, TimeSpan.Zero ) ); + + if ( dexBonus != 0 ) + parent.AddStatMod( new StatMod( StatType.Dex, modName + "Dex", dexBonus, TimeSpan.Zero ) ); + + if ( intBonus != 0 ) + parent.AddStatMod( new StatMod( StatType.Int, modName + "Int", intBonus, TimeSpan.Zero ) ); + } + + public static void ValidateMobile( Mobile m ) + { + for ( int i = m.Items.Count - 1; i >= 0; --i ) + { + if ( i >= m.Items.Count ) + continue; + + Item item = m.Items[i]; + + if ( item is BaseClothing ) + { + BaseClothing clothing = (BaseClothing)item; + + if( clothing.RequiredRace != null && m.Race != clothing.RequiredRace ) + { + if( clothing.RequiredRace == Race.Elf ) + m.SendLocalizedMessage( 1072203 ); // Only Elves may use this. + else + m.SendMessage( "Only {0} may use this.", clothing.RequiredRace.PluralName ); + + m.AddToBackpack( clothing ); + } + else if ( !clothing.AllowMaleWearer && !m.Female && m.AccessLevel < AccessLevel.GameMaster ) + { + if ( clothing.AllowFemaleWearer ) + m.SendLocalizedMessage( 1010388 ); // Only females can wear this. + else + m.SendMessage( "You may not wear this." ); + + m.AddToBackpack( clothing ); + } + else if ( !clothing.AllowFemaleWearer && m.Female && m.AccessLevel < AccessLevel.GameMaster ) + { + if ( clothing.AllowMaleWearer ) + m.SendLocalizedMessage( 1063343 ); // Only males can wear this. + else + m.SendMessage( "You may not wear this." ); + + m.AddToBackpack( clothing ); + } + } + } + } + + public int GetLowerStatReq() + { + if ( !Core.AOS ) + return 0; + + return m_AosClothingAttributes.LowerStatReq; + } + + public override void OnAdded( object parent ) + { + Mobile mob = parent as Mobile; + + if ( mob != null ) + { + if ( Core.AOS ) + m_AosSkillBonuses.AddTo( mob ); + + AddStatBonuses( mob ); + mob.CheckStatTimers(); + } + + base.OnAdded( parent ); + } + + public override void OnRemoved( object parent ) + { + Mobile mob = parent as Mobile; + + if ( mob != null ) + { + if ( Core.AOS ) + m_AosSkillBonuses.Remove(); + + string modName = this.Serial.ToString(); + + mob.RemoveStatMod( modName + "Str" ); + mob.RemoveStatMod( modName + "Dex" ); + mob.RemoveStatMod( modName + "Int" ); + + mob.CheckStatTimers(); + } + + base.OnRemoved( parent ); + } + + public virtual int OnHit( BaseWeapon weapon, int damageTaken ) + { + int Absorbed = Utility.RandomMinMax( 1, 4 ); + + damageTaken -= Absorbed; + + if ( damageTaken < 0 ) + damageTaken = 0; + + if ( 25 > Utility.Random( 100 ) ) // 25% chance to lower durability + { + if ( Core.AOS && m_AosClothingAttributes.SelfRepair > Utility.Random( 10 ) ) + { + HitPoints += 2; + } + else + { + int wear; + + if ( weapon.Type == WeaponType.Bashing ) + wear = Absorbed / 2; + else + wear = Utility.Random( 2 ); + + if ( wear > 0 && m_MaxHitPoints > 0 ) + { + if ( m_HitPoints >= wear ) + { + HitPoints -= wear; + wear = 0; + } + else + { + wear -= HitPoints; + HitPoints = 0; + } + + if ( wear > 0 ) + { + if ( m_MaxHitPoints > wear ) + { + MaxHitPoints -= wear; + + if ( Parent is Mobile ) + ((Mobile)Parent).LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061121 ); // Your equipment is severely damaged. + } + else + { + Delete(); + } + } + } + } + } + + return damageTaken; + } + + public BaseClothing( int itemID, Layer layer ) : this( itemID, layer, 0 ) + { + } + + public BaseClothing( int itemID, Layer layer, int hue ) : base( itemID ) + { + Layer = layer; + Hue = hue; + + m_Resource = DefaultResource; + m_Quality = ClothingQuality.Regular; + + m_HitPoints = m_MaxHitPoints = Utility.RandomMinMax( InitMinHits, InitMaxHits ); + + m_AosAttributes = new AosAttributes( this ); + m_AosClothingAttributes = new AosArmorAttributes( this ); + m_AosSkillBonuses = new AosSkillBonuses( this ); + m_AosResistances = new AosElementAttributes( this ); + } + + public BaseClothing( Serial serial ) : base( serial ) + { + } + + public override bool AllowEquipedCast( Mobile from ) + { + if ( base.AllowEquipedCast( from ) ) + return true; + + return ( m_AosAttributes.SpellChanneling != 0 ); + } + + public override bool CheckPropertyConfliction( Mobile m ) + { + if ( base.CheckPropertyConfliction( m ) ) + return true; + + if ( Layer == Layer.Pants ) + return ( m.FindItemOnLayer( Layer.InnerLegs ) != null ); + + if ( Layer == Layer.Shirt ) + return ( m.FindItemOnLayer( Layer.InnerTorso ) != null ); + + return false; + } + + private string GetNameString() + { + string name = this.Name; + + if ( name == null ) + name = String.Format( "#{0}", LabelNumber ); + + return name; + } + + 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 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_Quality == ClothingQuality.Exceptional ) + list.Add( 1060636 ); // exceptional + + if( RequiredRace == Race.Elf ) + list.Add( 1075086 ); // Elves Only + + if ( m_AosSkillBonuses != null ) + 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_AosClothingAttributes.LowerStatReq) != 0 ) + list.Add( 1060435, prop.ToString() ); // lower requirements ~1_val~% + + if ( (prop = m_AosAttributes.Luck) != 0 ) + list.Add( 1060436, prop.ToString() ); // luck ~1_val~ + + if ( (prop = m_AosClothingAttributes.MageArmor) != 0 ) + list.Add( 1060437 ); // mage armor + + 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_AosClothingAttributes.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~% + + base.AddResistanceProperties( list ); + + if ( (prop = m_AosClothingAttributes.DurabilityBonus) > 0 ) + list.Add( 1060410, prop.ToString() ); // durability ~1_val~% + + if ( (prop = ComputeStatReq( StatType.Str )) > 0 ) + list.Add( 1061170, prop.ToString() ); // strength requirement ~1_val~ + + if ( m_HitPoints >= 0 && m_MaxHitPoints > 0 ) + list.Add( 1060639, "{0}\t{1}", m_HitPoints, m_MaxHitPoints ); // durability ~1_val~ / ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + ArrayList attrs = new ArrayList(); + + 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 == ClothingQuality.Exceptional ) + attrs.Add( new EquipInfoAttribute( 1018305 - (int)m_Quality ) ); + + 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, (EquipInfoAttribute[])attrs.ToArray( typeof( EquipInfoAttribute ) ) ); + + from.Send( new DisplayEquipmentInfo( this, eqInfo ) ); + } + + #region Serialization + 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 ); + } + + [Flags] + private enum SaveFlag + { + None = 0x00000000, + Resource = 0x00000001, + Attributes = 0x00000002, + ClothingAttributes = 0x00000004, + SkillBonuses = 0x00000008, + Resistances = 0x00000010, + MaxHitPoints = 0x00000020, + HitPoints = 0x00000040, + PlayerConstructed = 0x00000080, + Crafter = 0x00000100, + Quality = 0x00000200, + StrReq = 0x00000400 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 5 ); // version + + SaveFlag flags = SaveFlag.None; + + SetSaveFlag( ref flags, SaveFlag.Resource, m_Resource != DefaultResource ); + SetSaveFlag( ref flags, SaveFlag.Attributes, !m_AosAttributes.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.ClothingAttributes,!m_AosClothingAttributes.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.SkillBonuses, !m_AosSkillBonuses.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.Resistances, !m_AosResistances.IsEmpty ); + SetSaveFlag( ref flags, SaveFlag.MaxHitPoints, m_MaxHitPoints != 0 ); + SetSaveFlag( ref flags, SaveFlag.HitPoints, m_HitPoints != 0 ); + SetSaveFlag( ref flags, SaveFlag.PlayerConstructed, m_PlayerConstructed != false ); + SetSaveFlag( ref flags, SaveFlag.Crafter, m_Crafter != null ); + SetSaveFlag( ref flags, SaveFlag.Quality, m_Quality != ClothingQuality.Regular ); + SetSaveFlag( ref flags, SaveFlag.StrReq, m_StrReq != -1 ); + + writer.WriteEncodedInt( (int) flags ); + + if ( GetSaveFlag( flags, SaveFlag.Resource ) ) + writer.WriteEncodedInt( (int) m_Resource ); + + if ( GetSaveFlag( flags, SaveFlag.Attributes ) ) + m_AosAttributes.Serialize( writer ); + + if ( GetSaveFlag( flags, SaveFlag.ClothingAttributes ) ) + m_AosClothingAttributes.Serialize( writer ); + + if ( GetSaveFlag( flags, SaveFlag.SkillBonuses ) ) + m_AosSkillBonuses.Serialize( writer ); + + if ( GetSaveFlag( flags, SaveFlag.Resistances ) ) + m_AosResistances.Serialize( writer ); + + if ( GetSaveFlag( flags, SaveFlag.MaxHitPoints ) ) + writer.WriteEncodedInt( (int) m_MaxHitPoints ); + + if ( GetSaveFlag( flags, SaveFlag.HitPoints ) ) + writer.WriteEncodedInt( (int) m_HitPoints ); + + if ( GetSaveFlag( flags, SaveFlag.Crafter ) ) + writer.Write( (Mobile) m_Crafter ); + + if ( GetSaveFlag( flags, SaveFlag.Quality ) ) + writer.WriteEncodedInt( (int) m_Quality ); + + if ( GetSaveFlag( flags, SaveFlag.StrReq ) ) + writer.WriteEncodedInt( (int) m_StrReq ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 5: + { + SaveFlag flags = (SaveFlag)reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Resource ) ) + m_Resource = (CraftResource)reader.ReadEncodedInt(); + else + m_Resource = DefaultResource; + + if ( GetSaveFlag( flags, SaveFlag.Attributes ) ) + m_AosAttributes = new AosAttributes( this, reader ); + else + m_AosAttributes = new AosAttributes( this ); + + if ( GetSaveFlag( flags, SaveFlag.ClothingAttributes ) ) + m_AosClothingAttributes = new AosArmorAttributes( this, reader ); + else + m_AosClothingAttributes = new AosArmorAttributes( this ); + + if ( GetSaveFlag( flags, SaveFlag.SkillBonuses ) ) + m_AosSkillBonuses = new AosSkillBonuses( this, reader ); + else + m_AosSkillBonuses = new AosSkillBonuses( this ); + + if ( GetSaveFlag( flags, SaveFlag.Resistances ) ) + m_AosResistances = new AosElementAttributes( this, reader ); + else + m_AosResistances = new AosElementAttributes( this ); + + if ( GetSaveFlag( flags, SaveFlag.MaxHitPoints ) ) + m_MaxHitPoints = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.HitPoints ) ) + m_HitPoints = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Crafter ) ) + m_Crafter = reader.ReadMobile(); + + if ( GetSaveFlag( flags, SaveFlag.Quality ) ) + m_Quality = (ClothingQuality)reader.ReadEncodedInt(); + else + m_Quality = ClothingQuality.Regular; + + if ( GetSaveFlag( flags, SaveFlag.StrReq ) ) + m_StrReq = reader.ReadEncodedInt(); + else + m_StrReq = -1; + + if ( GetSaveFlag( flags, SaveFlag.PlayerConstructed ) ) + m_PlayerConstructed = true; + + break; + } + case 4: + { + m_Resource = (CraftResource)reader.ReadInt(); + + goto case 3; + } + case 3: + { + m_AosAttributes = new AosAttributes( this, reader ); + m_AosClothingAttributes = new AosArmorAttributes( this, reader ); + m_AosSkillBonuses = new AosSkillBonuses( this, reader ); + m_AosResistances = new AosElementAttributes( this, reader ); + + goto case 2; + } + case 2: + { + m_PlayerConstructed = reader.ReadBool(); + goto case 1; + } + case 1: + { + m_Crafter = reader.ReadMobile(); + m_Quality = (ClothingQuality)reader.ReadInt(); + break; + } + case 0: + { + m_Crafter = null; + m_Quality = ClothingQuality.Regular; + break; + } + } + + if ( version < 2 ) + m_PlayerConstructed = true; // we don't know, so, assume it's crafted + + if ( version < 3 ) + { + m_AosAttributes = new AosAttributes( this ); + m_AosClothingAttributes = new AosArmorAttributes( this ); + m_AosSkillBonuses = new AosSkillBonuses( this ); + m_AosResistances = new AosElementAttributes( this ); + } + + if ( version < 4 ) + m_Resource = DefaultResource; + + if ( m_MaxHitPoints == 0 && m_HitPoints == 0 ) + m_HitPoints = m_MaxHitPoints = Utility.RandomMinMax( InitMinHits, InitMaxHits ); + + Mobile parent = Parent as Mobile; + + if ( parent != null ) + { + if ( Core.AOS ) + m_AosSkillBonuses.AddTo( parent ); + + AddStatBonuses( parent ); + parent.CheckStatTimers(); + } + } + #endregion + + public virtual bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + else if ( RootParent is Mobile && from != RootParent ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 502437 ); // Items you wish to cut must be in your backpack. + return false; + } + + if ( Ethics.Ethic.IsImbued( this ) ) + { + from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. + return false; + } + + CraftSystem system = DefTailoring.CraftSystem; + + CraftItem item = system.CraftItems.SearchFor( GetType() ); + + if ( item != null && item.Ressources.Count == 1 && item.Ressources.GetAt( 0 ).Amount >= 2 ) + { + try + { + Type resourceType = null; + + CraftResourceInfo info = CraftResources.GetInfo( m_Resource ); + + if ( info != null && info.ResourceTypes.Length > 0 ) + resourceType = info.ResourceTypes[0]; + + if ( resourceType == null ) + resourceType = item.Ressources.GetAt( 0 ).ItemType; + + Item res = (Item)Activator.CreateInstance( resourceType ); + + ScissorHelper( from, res, m_PlayerConstructed ? (item.Ressources.GetAt( 0 ).Amount / 2) : 1 ); + + res.LootType = LootType.Regular; + + return true; + } + catch + { + } + } + + from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. + return false; + } + + public void DistributeBonuses( int amount ) + { + for ( int i = 0; i < amount; ++i ) + { + switch ( Utility.Random( 5 ) ) + { + case 0: ++m_AosResistances.Physical; break; + case 1: ++m_AosResistances.Fire; break; + case 2: ++m_AosResistances.Cold; break; + case 3: ++m_AosResistances.Poison; break; + case 4: ++m_AosResistances.Energy; break; + } + } + + InvalidateProperties(); + } + + #region ICraftable Members + + public virtual int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Quality = (ClothingQuality)quality; + + if ( makersMark ) + Crafter = from; + + if ( DefaultResource != CraftResource.None ) + { + Type resourceType = typeRes; + + if ( resourceType == null ) + resourceType = craftItem.Ressources.GetAt( 0 ).ItemType; + + Resource = CraftResources.GetFromType( resourceType ); + } + else + { + Hue = resHue; + } + + PlayerConstructed = true; + + CraftContext context = craftSystem.GetContext( from ); + + if ( context != null && context.DoNotColor ) + Hue = 0; + + return quality; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Cloaks.cs b/Scripts/Items/Clothing/Cloaks.cs new file mode 100644 index 000000000..1311c059b --- /dev/null +++ b/Scripts/Items/Clothing/Cloaks.cs @@ -0,0 +1,306 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseCloak : BaseClothing + { + public BaseCloak( int itemID ) : this( itemID, 0 ) + { + } + + public BaseCloak( int itemID, int hue ) : base( itemID, Layer.Cloak, hue ) + { + } + + public BaseCloak( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable] + public class Cloak : BaseCloak, IArcaneEquip + { + #region Arcane Impl + private int m_MaxArcaneCharges, m_CurArcaneCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxArcaneCharges + { + get{ return m_MaxArcaneCharges; } + set{ m_MaxArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurArcaneCharges + { + get{ return m_CurArcaneCharges; } + set{ m_CurArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsArcane + { + get{ return ( m_MaxArcaneCharges > 0 && m_CurArcaneCharges >= 0 ); } + } + + public void Update() + { + if ( IsArcane ) + ItemID = 0x26AD; + else if ( ItemID == 0x26AD ) + ItemID = 0x1515; + + if ( IsArcane && CurArcaneCharges == 0 ) + Hue = 0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( IsArcane ) + list.Add( 1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ); // arcane charges: ~1_val~ / ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( IsArcane ) + LabelTo( from, 1061837, String.Format( "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ) ); + } + + public void Flip() + { + if ( ItemID == 0x1515 ) + ItemID = 0x1530; + else if ( ItemID == 0x1530 ) + ItemID = 0x1515; + } + #endregion + + [Constructable] + public Cloak() : this( 0 ) + { + } + + [Constructable] + public Cloak( int hue ) : base( 0x1515, hue ) + { + Weight = 5.0; + } + + public Cloak( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + if ( IsArcane ) + { + writer.Write( true ); + writer.Write( (int) m_CurArcaneCharges ); + writer.Write( (int) m_MaxArcaneCharges ); + } + else + { + writer.Write( false ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + if ( reader.ReadBool() ) + { + m_CurArcaneCharges = reader.ReadInt(); + m_MaxArcaneCharges = reader.ReadInt(); + + if ( Hue == 2118 ) + Hue = ArcaneGem.DefaultArcaneHue; + } + + break; + } + } + + if ( Weight == 4.0 ) + Weight = 5.0; + } + } + + [Flipable] + public class RewardCloak : BaseCloak, Engines.VeteranRewards.IRewardItem + { + private int m_LabelNumber; + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Number + { + get{ return m_LabelNumber; } + set{ m_LabelNumber = value; InvalidateProperties(); } + } + + public override int LabelNumber + { + get + { + if ( m_LabelNumber > 0 ) + return m_LabelNumber; + + return base.LabelNumber; + } + } + + public override int BasePhysicalResistance{ get{ return 3; } } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( parent is Mobile ) + ((Mobile)parent).VirtualArmorMod += 2; + } + + public override void OnRemoved(object parent) + { + base.OnRemoved( parent ); + + if ( parent is Mobile ) + ((Mobile)parent).VirtualArmorMod -= 2; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public override bool CanEquip( Mobile m ) + { + if ( !base.CanEquip( m ) ) + return false; + + return !m_IsRewardItem || Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( m, this, new object[]{ Hue, m_LabelNumber } ); + } + + [Constructable] + public RewardCloak() : this( 0 ) + { + } + + [Constructable] + public RewardCloak( int hue ) : this( hue, 0 ) + { + } + + [Constructable] + public RewardCloak( int hue, int labelNumber ) : base( 0x1515, hue ) + { + Weight = 5.0; + LootType = LootType.Blessed; + + m_LabelNumber = labelNumber; + } + + public RewardCloak( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_LabelNumber ); + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_LabelNumber = reader.ReadInt(); + m_IsRewardItem = reader.ReadBool(); + break; + } + } + + if ( Parent is Mobile ) + ((Mobile)Parent).VirtualArmorMod += 2; + } + } + + [Flipable( 0x230A, 0x2309 )] + public class FurCape : BaseCloak + { + [Constructable] + public FurCape() : this( 0 ) + { + } + + [Constructable] + public FurCape( int hue ) : base( 0x230A, hue ) + { + Weight = 4.0; + } + + public FurCape( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Hats.cs b/Scripts/Items/Clothing/Hats.cs new file mode 100644 index 000000000..70329f445 --- /dev/null +++ b/Scripts/Items/Clothing/Hats.cs @@ -0,0 +1,1034 @@ +using System; +using Server.Engines.Craft; + +namespace Server.Items +{ + public abstract class BaseHat : BaseClothing + { + public BaseHat( int itemID ) : this( itemID, 0 ) + { + } + + public BaseHat( int itemID, int hue ) : base( itemID, Layer.Helm, hue ) + { + } + + public BaseHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Quality = (ClothingQuality)quality; + + if( Quality == ClothingQuality.Exceptional ) + DistributeBonuses( (tool is BaseRunicTool ? 6 : (Core.SE ? 15 : 14)) ); //BLAME OSI. (We can't confirm it's an OSI bug yet.) + + return base.OnCraft( quality, makersMark, from, craftSystem, typeRes, tool, craftItem, resHue ); + } + + } + [Flipable( 0x2798, 0x27E3 )] + public class Kasa : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public Kasa() : this( 0 ) + { + } + + [Constructable] + public Kasa( int hue ) : base( 0x2798, hue ) + { + Weight = 3.0; + } + + public Kasa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x278F, 0x27DA )] + public class ClothNinjaHood : BaseHat + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 6; } } + public override int BasePoisonResistance{ get{ return 9; } } + public override int BaseEnergyResistance{ get{ return 9; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public ClothNinjaHood() : this( 0 ) + { + } + + [Constructable] + public ClothNinjaHood( int hue ) : base( 0x278F, hue ) + { + Weight = 2.0; + } + + public ClothNinjaHood( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2306, 0x2305 )] + public class FlowerGarland : BaseHat + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 6; } } + public override int BasePoisonResistance{ get{ return 9; } } + public override int BaseEnergyResistance{ get{ return 9; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public FlowerGarland() : this( 0 ) + { + } + + [Constructable] + public FlowerGarland( int hue ) : base( 0x2306, hue ) + { + Weight = 1.0; + } + + public FlowerGarland( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FloppyHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public FloppyHat() : this( 0 ) + { + } + + [Constructable] + public FloppyHat( int hue ) : base( 0x1713, hue ) + { + Weight = 1.0; + } + + public FloppyHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WideBrimHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public WideBrimHat() : this( 0 ) + { + } + + [Constructable] + public WideBrimHat( int hue ) : base( 0x1714, hue ) + { + Weight = 1.0; + } + + public WideBrimHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Cap : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public Cap() : this( 0 ) + { + } + + [Constructable] + public Cap( int hue ) : base( 0x1715, hue ) + { + Weight = 1.0; + } + + public Cap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SkullCap : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 5; } } + public override int BasePoisonResistance{ get{ return 8; } } + public override int BaseEnergyResistance{ get{ return 8; } } + + public override int InitMinHits{ get{ return 7; } } + public override int InitMaxHits{ get{ return 12; } } + + [Constructable] + public SkullCap() : this( 0 ) + { + } + + [Constructable] + public SkullCap( int hue ) : base( 0x1544, hue ) + { + Weight = 1.0; + } + + public SkullCap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Bandana : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 5; } } + public override int BasePoisonResistance{ get{ return 8; } } + public override int BaseEnergyResistance{ get{ return 8; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public Bandana() : this( 0 ) + { + } + + [Constructable] + public Bandana( int hue ) : base( 0x1540, hue ) + { + Weight = 1.0; + } + + public Bandana( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BearMask : BaseHat + { + public override int BasePhysicalResistance{ get{ return 5; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 8; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public BearMask() : this( 0 ) + { + } + + [Constructable] + public BearMask( int hue ) : base( 0x1545, hue ) + { + Weight = 5.0; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public BearMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DeerMask : BaseHat + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 6; } } + public override int BaseColdResistance{ get{ return 8; } } + public override int BasePoisonResistance{ get{ return 1; } } + public override int BaseEnergyResistance{ get{ return 7; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public DeerMask() : this( 0 ) + { + } + + [Constructable] + public DeerMask( int hue ) : base( 0x1547, hue ) + { + Weight = 4.0; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public DeerMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class HornedTribalMask : BaseHat + { + public override int BasePhysicalResistance{ get{ return 6; } } + public override int BaseFireResistance{ get{ return 9; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public HornedTribalMask() : this( 0 ) + { + } + + [Constructable] + public HornedTribalMask( int hue ) : base( 0x1549, hue ) + { + Weight = 2.0; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public HornedTribalMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TribalMask : BaseHat + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 6; } } + public override int BasePoisonResistance{ get{ return 10; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public TribalMask() : this( 0 ) + { + } + + [Constructable] + public TribalMask( int hue ) : base( 0x154B, hue ) + { + Weight = 2.0; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public TribalMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TallStrawHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public TallStrawHat() : this( 0 ) + { + } + + [Constructable] + public TallStrawHat( int hue ) : base( 0x1716, hue ) + { + Weight = 1.0; + } + + public TallStrawHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StrawHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public StrawHat() : this( 0 ) + { + } + + [Constructable] + public StrawHat( int hue ) : base( 0x1717, hue ) + { + Weight = 1.0; + } + + public StrawHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class OrcishKinMask : BaseHat + { + public override int BasePhysicalResistance{ get{ return 1; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 7; } } + public override int BaseEnergyResistance{ get{ return 8; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public override string DefaultName + { + get { return "a mask of orcish kin"; } + } + + [Constructable] + public OrcishKinMask() : this( 0x8A4 ) + { + } + + [Constructable] + public OrcishKinMask( int hue ) : base( 0x141B, hue ) + { + Weight = 2.0; + } + + public override bool CanEquip( Mobile m ) + { + if ( !base.CanEquip( m ) ) + return false; + + if ( m.BodyMod == 183 || m.BodyMod == 184 ) + { + m.SendLocalizedMessage( 1061629 ); // You can't do that while wearing savage kin paint. + return false; + } + + return true; + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( parent is Mobile ) + Misc.Titles.AwardKarma( (Mobile)parent, -20, true ); + } + + public OrcishKinMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue != 0x8A4 ) + Hue = 0x8A4; + } + } + + public class SavageMask : BaseHat + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 6; } } + public override int BasePoisonResistance{ get{ return 10; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + public static int GetRandomHue() + { + int v = Utility.RandomBirdHue(); + + if ( v == 2101 ) + v = 0; + + return v; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + [Constructable] + public SavageMask() : this( GetRandomHue() ) + { + } + + [Constructable] + public SavageMask( int hue ) : base( 0x154B, hue ) + { + Weight = 2.0; + } + + public SavageMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue != 0 && (Hue < 2101 || Hue > 2130) ) + Hue = GetRandomHue(); + } + } + + public class WizardsHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public WizardsHat() : this( 0 ) + { + } + + [Constructable] + public WizardsHat( int hue ) : base( 0x1718, hue ) + { + Weight = 1.0; + } + + public WizardsHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MagicWizardsHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int LabelNumber{ get{ return 1041072; } } // a magical wizard's hat + + public override int BaseStrBonus{ get{ return -5; } } + public override int BaseDexBonus{ get{ return -5; } } + public override int BaseIntBonus{ get{ return +5; } } + + [Constructable] + public MagicWizardsHat() : this( 0 ) + { + } + + [Constructable] + public MagicWizardsHat( int hue ) : base( 0x1718, hue ) + { + Weight = 1.0; + } + + public MagicWizardsHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Bonnet : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public Bonnet() : this( 0 ) + { + } + + [Constructable] + public Bonnet( int hue ) : base( 0x1719, hue ) + { + Weight = 1.0; + } + + public Bonnet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FeatheredHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public FeatheredHat() : this( 0 ) + { + } + + [Constructable] + public FeatheredHat( int hue ) : base( 0x171A, hue ) + { + Weight = 1.0; + } + + public FeatheredHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TricorneHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public TricorneHat() : this( 0 ) + { + } + + [Constructable] + public TricorneHat( int hue ) : base( 0x171B, hue ) + { + Weight = 1.0; + } + + public TricorneHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class JesterHat : BaseHat + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 9; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 30; } } + + [Constructable] + public JesterHat() : this( 0 ) + { + } + + [Constructable] + public JesterHat( int hue ) : base( 0x171C, hue ) + { + Weight = 1.0; + } + + public JesterHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/MiddleTorso.cs b/Scripts/Items/Clothing/MiddleTorso.cs new file mode 100644 index 000000000..dfcbbb572 --- /dev/null +++ b/Scripts/Items/Clothing/MiddleTorso.cs @@ -0,0 +1,303 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseMiddleTorso : BaseClothing + { + public BaseMiddleTorso( int itemID ) : this( itemID, 0 ) + { + } + + public BaseMiddleTorso( int itemID, int hue ) : base( itemID, Layer.MiddleTorso, hue ) + { + } + + public BaseMiddleTorso( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1541, 0x1542 )] + public class BodySash : BaseMiddleTorso + { + [Constructable] + public BodySash() : this( 0 ) + { + } + + [Constructable] + public BodySash( int hue ) : base( 0x1541, hue ) + { + Weight = 1.0; + } + + public BodySash( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x153d, 0x153e )] + public class FullApron : BaseMiddleTorso + { + [Constructable] + public FullApron() : this( 0 ) + { + } + + [Constructable] + public FullApron( int hue ) : base( 0x153d, hue ) + { + Weight = 4.0; + } + + public FullApron( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1f7b, 0x1f7c )] + public class Doublet : BaseMiddleTorso + { + [Constructable] + public Doublet() : this( 0 ) + { + } + + [Constructable] + public Doublet( int hue ) : base( 0x1F7B, hue ) + { + Weight = 2.0; + } + + public Doublet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1ffd, 0x1ffe )] + public class Surcoat : BaseMiddleTorso + { + [Constructable] + public Surcoat() : this( 0 ) + { + } + + [Constructable] + public Surcoat( int hue ) : base( 0x1FFD, hue ) + { + Weight = 6.0; + } + + public Surcoat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 6.0; + } + } + + [Flipable( 0x1fa1, 0x1fa2 )] + public class Tunic : BaseMiddleTorso + { + [Constructable] + public Tunic() : this( 0 ) + { + } + + [Constructable] + public Tunic( int hue ) : base( 0x1FA1, hue ) + { + Weight = 5.0; + } + + public Tunic( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2310, 0x230F )] + public class FormalShirt : BaseMiddleTorso + { + [Constructable] + public FormalShirt() : this( 0 ) + { + } + + [Constructable] + public FormalShirt( int hue ) : base( 0x2310, hue ) + { + Weight = 1.0; + } + + public FormalShirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + if ( Weight == 2.0 ) + Weight = 1.0; + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1f9f, 0x1fa0 )] + public class JesterSuit : BaseMiddleTorso + { + [Constructable] + public JesterSuit() : this( 0 ) + { + } + + [Constructable] + public JesterSuit( int hue ) : base( 0x1F9F, hue ) + { + Weight = 4.0; + } + + public JesterSuit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x27A1, 0x27EC )] + public class JinBaori : BaseMiddleTorso + { + [Constructable] + public JinBaori() : this( 0 ) + { + } + + [Constructable] + public JinBaori( int hue ) : base( 0x27A1, hue ) + { + Weight = 3.0; + } + + public JinBaori( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/OuterLegs.cs b/Scripts/Items/Clothing/OuterLegs.cs new file mode 100644 index 000000000..947105f8f --- /dev/null +++ b/Scripts/Items/Clothing/OuterLegs.cs @@ -0,0 +1,168 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseOuterLegs : BaseClothing + { + public BaseOuterLegs( int itemID ) : this( itemID, 0 ) + { + } + + public BaseOuterLegs( int itemID, int hue ) : base( itemID, Layer.OuterLegs, hue ) + { + } + + public BaseOuterLegs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x230C, 0x230B )] + public class FurSarong : BaseOuterLegs + { + [Constructable] + public FurSarong() : this( 0 ) + { + } + + [Constructable] + public FurSarong( int hue ) : base( 0x230C, hue ) + { + Weight = 3.0; + } + + public FurSarong( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 4.0 ) + Weight = 3.0; + } + } + + [Flipable( 0x1516, 0x1531 )] + public class Skirt : BaseOuterLegs + { + [Constructable] + public Skirt() : this( 0 ) + { + } + + [Constructable] + public Skirt( int hue ) : base( 0x1516, hue ) + { + Weight = 4.0; + } + + public Skirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1537, 0x1538 )] + public class Kilt : BaseOuterLegs + { + [Constructable] + public Kilt() : this( 0 ) + { + } + + [Constructable] + public Kilt( int hue ) : base( 0x1537, hue ) + { + Weight = 2.0; + } + + public Kilt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x279A, 0x27E5 )] + public class Hakama : BaseOuterLegs + { + [Constructable] + public Hakama() : this( 0 ) + { + } + + [Constructable] + public Hakama( int hue ) : base( 0x279A, hue ) + { + Weight = 2.0; + } + + public Hakama( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/OuterTorso.cs b/Scripts/Items/Clothing/OuterTorso.cs new file mode 100644 index 000000000..abea21309 --- /dev/null +++ b/Scripts/Items/Clothing/OuterTorso.cs @@ -0,0 +1,665 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseOuterTorso : BaseClothing + { + public BaseOuterTorso( int itemID ) : this( itemID, 0 ) + { + } + + public BaseOuterTorso( int itemID, int hue ) : base( itemID, Layer.OuterTorso, hue ) + { + } + + public BaseOuterTorso( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x230E, 0x230D )] + public class GildedDress : BaseOuterTorso + { + [Constructable] + public GildedDress() : this( 0 ) + { + } + + [Constructable] + public GildedDress( int hue ) : base( 0x230E, hue ) + { + Weight = 3.0; + } + + public GildedDress( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1F00, 0x1EFF )] + public class FancyDress : BaseOuterTorso + { + [Constructable] + public FancyDress() : this( 0 ) + { + } + + [Constructable] + public FancyDress( int hue ) : base( 0x1F00, hue ) + { + Weight = 3.0; + } + + public FancyDress( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DeathRobe : Robe + { + public override bool DisplayLootType + { + get{ return false; } + } + + [Constructable] + public DeathRobe() + { + LootType = LootType.Newbied; + Hue = 2301; + } + + public new bool Scissor( Mobile from, Scissors scissors ) + { + from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. + return false; + } + + public DeathRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 && Hue == 0 ) + Hue = 2301; + } + } + + [Flipable] + public class RewardRobe : BaseOuterTorso, Engines.VeteranRewards.IRewardItem + { + private int m_LabelNumber; + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Number + { + get{ return m_LabelNumber; } + set{ m_LabelNumber = value; InvalidateProperties(); } + } + + public override int LabelNumber + { + get + { + if ( m_LabelNumber > 0 ) + return m_LabelNumber; + + return base.LabelNumber; + } + } + + public override int BasePhysicalResistance{ get{ return 3; } } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( parent is Mobile ) + ((Mobile)parent).VirtualArmorMod += 2; + } + + public override void OnRemoved(object parent) + { + base.OnRemoved( parent ); + + if ( parent is Mobile ) + ((Mobile)parent).VirtualArmorMod -= 2; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public override bool CanEquip( Mobile m ) + { + if ( !base.CanEquip( m ) ) + return false; + + return !m_IsRewardItem || Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( m, this, new object[]{ Hue, m_LabelNumber } ); + } + + [Constructable] + public RewardRobe() : this( 0 ) + { + } + + [Constructable] + public RewardRobe( int hue ) : this( hue, 0 ) + { + } + + [Constructable] + public RewardRobe( int hue, int labelNumber ) : base( 0x1F03, hue ) + { + Weight = 3.0; + LootType = LootType.Blessed; + + m_LabelNumber = labelNumber; + } + + public RewardRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_LabelNumber ); + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_LabelNumber = reader.ReadInt(); + m_IsRewardItem = reader.ReadBool(); + break; + } + } + + if ( Parent is Mobile ) + ((Mobile)Parent).VirtualArmorMod += 2; + } + } + + [Flipable] + public class Robe : BaseOuterTorso, IArcaneEquip + { + #region Arcane Impl + private int m_MaxArcaneCharges, m_CurArcaneCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxArcaneCharges + { + get{ return m_MaxArcaneCharges; } + set{ m_MaxArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurArcaneCharges + { + get{ return m_CurArcaneCharges; } + set{ m_CurArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsArcane + { + get{ return ( m_MaxArcaneCharges > 0 && m_CurArcaneCharges >= 0 ); } + } + + public void Update() + { + if ( IsArcane ) + ItemID = 0x26AE; + else if ( ItemID == 0x26AE ) + ItemID = 0x1F04; + + if ( IsArcane && CurArcaneCharges == 0 ) + Hue = 0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( IsArcane ) + list.Add( 1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ); // arcane charges: ~1_val~ / ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( IsArcane ) + LabelTo( from, 1061837, String.Format( "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ) ); + } + + public void Flip() + { + if ( ItemID == 0x1F03 ) + ItemID = 0x1F04; + else if ( ItemID == 0x1F04 ) + ItemID = 0x1F03; + } + #endregion + + [Constructable] + public Robe() : this( 0 ) + { + } + + [Constructable] + public Robe( int hue ) : base( 0x1F03, hue ) + { + Weight = 3.0; + } + + public Robe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + if ( IsArcane ) + { + writer.Write( true ); + writer.Write( (int) m_CurArcaneCharges ); + writer.Write( (int) m_MaxArcaneCharges ); + } + else + { + writer.Write( false ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + if ( reader.ReadBool() ) + { + m_CurArcaneCharges = reader.ReadInt(); + m_MaxArcaneCharges = reader.ReadInt(); + + if ( Hue == 2118 ) + Hue = ArcaneGem.DefaultArcaneHue; + } + + break; + } + } + } + } + + [Flipable( 0x2684, 0x2683 )] + public class HoodedShroudOfShadows : BaseOuterTorso + { + [Constructable] + public HoodedShroudOfShadows() : this( 0x455 ) + { + } + + [Constructable] + public HoodedShroudOfShadows( int hue ) : base( 0x2684, hue ) + { + LootType = LootType.Blessed; + Weight = 3.0; + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public HoodedShroudOfShadows( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1f01, 0x1f02 )] + public class PlainDress : BaseOuterTorso + { + [Constructable] + public PlainDress() : this( 0 ) + { + } + + [Constructable] + public PlainDress( int hue ) : base( 0x1F01, hue ) + { + Weight = 2.0; + } + + public PlainDress( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 2.0; + } + } + + [Flipable( 0x2799, 0x27E4 )] + public class Kamishimo : BaseOuterTorso + { + [Constructable] + public Kamishimo() : this( 0 ) + { + } + + [Constructable] + public Kamishimo( int hue ) : base( 0x2799, hue ) + { + Weight = 3.0; + } + + public Kamishimo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x279C, 0x27E7 )] + public class HakamaShita : BaseOuterTorso + { + [Constructable] + public HakamaShita() : this( 0 ) + { + } + + [Constructable] + public HakamaShita( int hue ) : base( 0x279C, hue ) + { + Weight = 3.0; + } + + public HakamaShita( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2782, 0x27CD )] + public class MaleKimono : BaseOuterTorso + { + [Constructable] + public MaleKimono() : this( 0 ) + { + } + + [Constructable] + public MaleKimono( int hue ) : base( 0x2782, hue ) + { + Weight = 3.0; + } + + public override bool AllowFemaleWearer{ get{ return false; } } + + public MaleKimono( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2783, 0x27CE )] + public class FemaleKimono : BaseOuterTorso + { + [Constructable] + public FemaleKimono() : this( 0 ) + { + } + + [Constructable] + public FemaleKimono( int hue ) : base( 0x2783, hue ) + { + Weight = 3.0; + } + + public override bool AllowMaleWearer{ get{ return false; } } + + public FemaleKimono( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2FB9, 0x3173 )] + public class MaleElvenRobe : BaseOuterTorso + { + // TODO: Supports arcane? + public override Race RequiredRace { get { return Race.Elf; } } + + [Constructable] + public MaleElvenRobe() : this( 0 ) + { + } + + [Constructable] + public MaleElvenRobe( int hue ) : base( 0x2FB9, hue ) + { + Weight = 2.0; + } + + public override bool AllowFemaleWearer{ get{ return false; } } + + public MaleElvenRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + [Flipable( 0x2FBA, 0x3174 )] + public class FemaleElvenRobe : BaseOuterTorso + { + // TODO: Supports arcane? + public override Race RequiredRace { get { return Race.Elf; } } + [Constructable] + public FemaleElvenRobe() : this( 0 ) + { + } + + [Constructable] + public FemaleElvenRobe( int hue ) : base( 0x2FBA, hue ) + { + Weight = 2.0; + } + + public override bool AllowMaleWearer{ get{ return false; } } + + public FemaleElvenRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Pants.cs b/Scripts/Items/Clothing/Pants.cs new file mode 100644 index 000000000..c4c6a50a9 --- /dev/null +++ b/Scripts/Items/Clothing/Pants.cs @@ -0,0 +1,167 @@ +using System; + +namespace Server.Items +{ + public abstract class BasePants : BaseClothing + { + public BasePants( int itemID ) : this( itemID, 0 ) + { + } + + public BasePants( int itemID, int hue ) : base( itemID, Layer.Pants, hue ) + { + } + + public BasePants( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x152e, 0x152f )] + public class ShortPants : BasePants + { + [Constructable] + public ShortPants() : this( 0 ) + { + } + + [Constructable] + public ShortPants( int hue ) : base( 0x152E, hue ) + { + Weight = 2.0; + } + + public ShortPants( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1539, 0x153a )] + public class LongPants : BasePants + { + [Constructable] + public LongPants() : this( 0 ) + { + } + + [Constructable] + public LongPants( int hue ) : base( 0x1539, hue ) + { + Weight = 2.0; + } + + public LongPants( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x279B, 0x27E6 )] + public class TattsukeHakama : BasePants + { + [Constructable] + public TattsukeHakama() : this( 0 ) + { + } + + [Constructable] + public TattsukeHakama( int hue ) : base( 0x279B, hue ) + { + Weight = 2.0; + } + + public TattsukeHakama( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x2FC3, 0x3179 )] + public class ElvenPants : BasePants + { + public override Race RequiredRace { get { return Race.Elf; } } + + [Constructable] + public ElvenPants() : this( 0 ) + { + } + + [Constructable] + public ElvenPants( int hue ) : base( 0x2FC3, hue ) + { + Weight = 2.0; + } + + public ElvenPants( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Shirts.cs b/Scripts/Items/Clothing/Shirts.cs new file mode 100644 index 000000000..af7733bf1 --- /dev/null +++ b/Scripts/Items/Clothing/Shirts.cs @@ -0,0 +1,204 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseShirt : BaseClothing + { + public BaseShirt( int itemID ) : this( itemID, 0 ) + { + } + + public BaseShirt( int itemID, int hue ) : base( itemID, Layer.Shirt, hue ) + { + } + + public BaseShirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1efd, 0x1efe )] + public class FancyShirt : BaseShirt + { + [Constructable] + public FancyShirt() : this( 0 ) + { + } + + [Constructable] + public FancyShirt( int hue ) : base( 0x1EFD, hue ) + { + Weight = 2.0; + } + + public FancyShirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1517, 0x1518 )] + public class Shirt : BaseShirt + { + [Constructable] + public Shirt() : this( 0 ) + { + } + + [Constructable] + public Shirt( int hue ) : base( 0x1517, hue ) + { + Weight = 1.0; + } + + public Shirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } + + [Flipable( 0x2794, 0x27DF )] + public class ClothNinjaJacket : BaseShirt + { + [Constructable] + public ClothNinjaJacket() : this( 0 ) + { + } + + [Constructable] + public ClothNinjaJacket( int hue ) : base( 0x2794, hue ) + { + Weight = 5.0; + Layer = Layer.InnerTorso; + } + + public ClothNinjaJacket( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ElvenShirt : BaseShirt + { + public override Race RequiredRace { get { return Race.Elf; } } + + [Constructable] + public ElvenShirt() : this( 0 ) + { + } + + [Constructable] + public ElvenShirt( int hue ) : base( 0x3175, hue ) + { + Weight = 2.0; + } + + public ElvenShirt(Serial serial) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ElvenDarkShirt : BaseShirt + { + public override Race RequiredRace { get { return Race.Elf; } } + [Constructable] + public ElvenDarkShirt() : this( 0 ) + { + } + + [Constructable] + public ElvenDarkShirt( int hue ) : base( 0x3176, hue ) + { + Weight = 2.0; + } + + public ElvenDarkShirt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Clothing/Shoes.cs b/Scripts/Items/Clothing/Shoes.cs new file mode 100644 index 000000000..5b77f15ae --- /dev/null +++ b/Scripts/Items/Clothing/Shoes.cs @@ -0,0 +1,450 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseShoes : BaseClothing + { + public BaseShoes( int itemID ) : this( itemID, 0 ) + { + } + + public BaseShoes( int itemID, int hue ) : base( itemID, Layer.Shoes, hue ) + { + } + + public BaseShoes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: break; // empty, resource removed + case 1: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + case 0: + { + m_Resource = DefaultResource; + break; + } + } + } + } + + [Flipable( 0x2307, 0x2308 )] + public class FurBoots : BaseShoes + { + [Constructable] + public FurBoots() : this( 0 ) + { + } + + [Constructable] + public FurBoots( int hue ) : base( 0x2307, hue ) + { + Weight = 3.0; + } + + public FurBoots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x170b, 0x170c )] + public class Boots : BaseShoes + { + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public Boots() : this( 0 ) + { + } + + [Constructable] + public Boots( int hue ) : base( 0x170B, hue ) + { + Weight = 3.0; + } + + public Boots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable] + public class ThighBoots : BaseShoes, IArcaneEquip + { + #region Arcane Impl + private int m_MaxArcaneCharges, m_CurArcaneCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxArcaneCharges + { + get{ return m_MaxArcaneCharges; } + set{ m_MaxArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurArcaneCharges + { + get{ return m_CurArcaneCharges; } + set{ m_CurArcaneCharges = value; InvalidateProperties(); Update(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsArcane + { + get{ return ( m_MaxArcaneCharges > 0 && m_CurArcaneCharges >= 0 ); } + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( IsArcane ) + LabelTo( from, 1061837, String.Format( "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ) ); + } + + public void Update() + { + if ( IsArcane ) + ItemID = 0x26AF; + else if ( ItemID == 0x26AF ) + ItemID = 0x1711; + + if ( IsArcane && CurArcaneCharges == 0 ) + Hue = 0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( IsArcane ) + list.Add( 1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges ); // arcane charges: ~1_val~ / ~2_val~ + } + + public void Flip() + { + if ( ItemID == 0x1711 ) + ItemID = 0x1712; + else if ( ItemID == 0x1712 ) + ItemID = 0x1711; + } + #endregion + + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public ThighBoots() : this( 0 ) + { + } + + [Constructable] + public ThighBoots( int hue ) : base( 0x1711, hue ) + { + Weight = 4.0; + } + + public ThighBoots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + if ( IsArcane ) + { + writer.Write( true ); + writer.Write( (int) m_CurArcaneCharges ); + writer.Write( (int) m_MaxArcaneCharges ); + } + else + { + writer.Write( false ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + if ( reader.ReadBool() ) + { + m_CurArcaneCharges = reader.ReadInt(); + m_MaxArcaneCharges = reader.ReadInt(); + + if ( Hue == 2118 ) + Hue = ArcaneGem.DefaultArcaneHue; + } + + break; + } + } + } + } + + [FlipableAttribute( 0x170f, 0x1710 )] + public class Shoes : BaseShoes + { + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public Shoes() : this( 0 ) + { + } + + [Constructable] + public Shoes( int hue ) : base( 0x170F, hue ) + { + Weight = 2.0; + } + + public Shoes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x170d, 0x170e )] + public class Sandals : BaseShoes + { + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + [Constructable] + public Sandals() : this( 0 ) + { + } + + [Constructable] + public Sandals( int hue ) : base( 0x170D, hue ) + { + Weight = 1.0; + } + + public Sandals( Serial serial ) : base( serial ) + { + } + + public override bool Dye( Mobile from, DyeTub sender ) + { + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2797, 0x27E2 )] + public class NinjaTabi : BaseShoes + { + [Constructable] + public NinjaTabi() : this( 0 ) + { + } + + [Constructable] + public NinjaTabi( int hue ) : base( 0x2797, hue ) + { + Weight = 2.0; + } + + public NinjaTabi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2796, 0x27E1 )] + public class SamuraiTabi : BaseShoes + { + [Constructable] + public SamuraiTabi() : this( 0 ) + { + } + + [Constructable] + public SamuraiTabi( int hue ) : base( 0x2796, hue ) + { + Weight = 2.0; + } + + public SamuraiTabi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x2796, 0x27E1 )] + public class Waraji : BaseShoes + { + [Constructable] + public Waraji() : this( 0 ) + { + } + + [Constructable] + public Waraji( int hue ) : base( 0x2796, hue ) + { + Weight = 2.0; + } + + public Waraji( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x2FC4, 0x317A )] + public class ElvenBoots : BaseShoes + { + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override Race RequiredRace { get { return Race.Elf; } } + + [Constructable] + public ElvenBoots() : this( 0 ) + { + } + + [Constructable] + public ElvenBoots( int hue ) : base( 0x2FC4, hue ) + { + Weight = 2.0; + } + + public ElvenBoots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Items/Clothing/Waist.cs b/Scripts/Items/Clothing/Waist.cs new file mode 100644 index 000000000..34db6c45b --- /dev/null +++ b/Scripts/Items/Clothing/Waist.cs @@ -0,0 +1,133 @@ +using System; + +namespace Server.Items +{ + + public abstract class BaseWaist : BaseClothing + { + public BaseWaist( int itemID ) : this( itemID, 0 ) + { + } + + public BaseWaist( int itemID, int hue ) : base( itemID, Layer.Waist, hue ) + { + } + + public BaseWaist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x153b, 0x153c )] + public class HalfApron : BaseWaist + { + [Constructable] + public HalfApron() : this( 0 ) + { + } + + [Constructable] + public HalfApron( int hue ) : base( 0x153b, hue ) + { + Weight = 2.0; + } + + public HalfApron( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x27A0, 0x27EB )] + public class Obi : BaseWaist + { + [Constructable] + public Obi() : this( 0 ) + { + } + + [Constructable] + public Obi( int hue ) : base( 0x27A0, hue ) + { + Weight = 1.0; + } + + public Obi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x2B68, 0x315F )] + public class WoodlandBelt : BaseWaist + { + [Constructable] + public WoodlandBelt() : this( 0 ) + { + } + + [Constructable] + public WoodlandBelt( int hue ) : base( 0x2B68, hue ) + { + Weight = 4.0; + } + + public WoodlandBelt( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Items/Construction/Ankhs.cs b/Scripts/Items/Construction/Ankhs.cs new file mode 100644 index 000000000..ceb3e1897 --- /dev/null +++ b/Scripts/Items/Construction/Ankhs.cs @@ -0,0 +1,433 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.ContextMenus; + +namespace Server.Items +{ + public class Ankhs + { + public const int ResurrectRange = 2; + public const int TitheRange = 2; + public const int LockRange = 2; + + public static void GetContextMenuEntries( Mobile from, Item item, List list ) + { + if ( from is PlayerMobile ) + list.Add( new LockKarmaEntry( (PlayerMobile)from ) ); + + list.Add( new ResurrectEntry( from, item ) ); + + if ( Core.AOS ) + list.Add( new TitheEntry( from ) ); + } + + public static void Resurrect( Mobile m, Item item ) + { + if ( m.Alive ) + return; + + if ( !m.InRange( item.GetWorldLocation(), ResurrectRange ) ) + m.SendLocalizedMessage( 500446 ); // That is too far away. + else if( m.Map != null && m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.VirtueShrine ) ); + } + else + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + + private class ResurrectEntry : ContextMenuEntry + { + private Mobile m_Mobile; + private Item m_Item; + + public ResurrectEntry( Mobile mobile, Item item ) : base( 6195, ResurrectRange ) + { + m_Mobile = mobile; + m_Item = item; + + Enabled = !m_Mobile.Alive; + } + + public override void OnClick() + { + Resurrect( m_Mobile, m_Item ); + } + } + + private class LockKarmaEntry : ContextMenuEntry + { + private PlayerMobile m_Mobile; + + public LockKarmaEntry( PlayerMobile mobile ) : base( mobile.KarmaLocked ? 6197 : 6196, LockRange ) + { + m_Mobile = mobile; + } + + public override void OnClick() + { + m_Mobile.KarmaLocked = !m_Mobile.KarmaLocked; + + if ( m_Mobile.KarmaLocked ) + m_Mobile.SendLocalizedMessage( 1060192 ); // Your karma has been locked. Your karma can no longer be raised. + else + m_Mobile.SendLocalizedMessage( 1060191 ); // Your karma has been unlocked. Your karma can be raised again. + } + } + + private class TitheEntry : ContextMenuEntry + { + private Mobile m_Mobile; + + public TitheEntry( Mobile mobile ) : base( 6198, TitheRange ) + { + m_Mobile = mobile; + + Enabled = m_Mobile.Alive; + } + + public override void OnClick() + { + if ( m_Mobile.CheckAlive() ) + m_Mobile.SendGump( new TithingGump( m_Mobile, 0 ) ); + } + } + } + + public class AnkhWest : Item + { + private InternalItem m_Item; + + [Constructable] + public AnkhWest() : this( false ) + { + } + + [Constructable] + public AnkhWest( bool bloodied ) : base( bloodied ? 0x1D98 : 0x3 ) + { + Movable = false; + + m_Item = new InternalItem( bloodied, this ); + } + + public AnkhWest( Serial serial ) : base( serial ) + { + } + + public override bool HandlesOnMovement{ get{ return true; } } // Tell the core that we implement OnMovement + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( Parent == null && Utility.InRange( Location, m.Location, 1 ) && !Utility.InRange( Location, oldLocation, 1 ) ) + Ankhs.Resurrect( m, this ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + Ankhs.GetContextMenuEntries( from, this, list ); + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get{ return base.Hue; } + set{ base.Hue = value; if ( m_Item.Hue != value ) m_Item.Hue = value; } + } + + public override void OnDoubleClickDead( Mobile m ) + { + Ankhs.Resurrect( m, this ); + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X, Y + 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + + private class InternalItem : Item + { + private AnkhWest m_Item; + + public InternalItem( bool bloodied, AnkhWest item ) : base( bloodied ? 0x1D97 : 0x2 ) + { + Movable = false; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X, Y - 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override bool HandlesOnMovement{ get{ return true; } } // Tell the core that we implement OnMovement + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( Parent == null && Utility.InRange( Location, m.Location, 1 ) && !Utility.InRange( Location, oldLocation, 1 ) ) + Ankhs.Resurrect( m, this ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + Ankhs.GetContextMenuEntries( from, this, list ); + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get{ return base.Hue; } + set{ base.Hue = value; if ( m_Item.Hue != value ) m_Item.Hue = value; } + } + + public override void OnDoubleClickDead( Mobile m ) + { + Ankhs.Resurrect( m, this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as AnkhWest; + } + } + } + + public class AnkhEast : Item + { + private InternalItem m_Item; + + [Constructable] + public AnkhEast() : this( false ) + { + } + + [Constructable] + public AnkhEast( bool bloodied ) : base( bloodied ? 0x1E5D : 0x4 ) + { + Movable = false; + + m_Item = new InternalItem( bloodied, this ); + } + + public AnkhEast( Serial serial ) : base( serial ) + { + } + + public override bool HandlesOnMovement{ get{ return true; } } // Tell the core that we implement OnMovement + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( Parent == null && Utility.InRange( Location, m.Location, 1 ) && !Utility.InRange( Location, oldLocation, 1 ) ) + Ankhs.Resurrect( m, this ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + Ankhs.GetContextMenuEntries( from, this, list ); + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get{ return base.Hue; } + set{ base.Hue = value; if ( m_Item.Hue != value ) m_Item.Hue = value; } + } + + public override void OnDoubleClickDead( Mobile m ) + { + Ankhs.Resurrect( m, this ); + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + + private class InternalItem : Item + { + private AnkhEast m_Item; + + public InternalItem( bool bloodied, AnkhEast item ) : base( bloodied ? 0x1E5C : 0x5 ) + { + Movable = false; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override bool HandlesOnMovement{ get{ return true; } } // Tell the core that we implement OnMovement + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( Parent == null && Utility.InRange( Location, m.Location, 1 ) && !Utility.InRange( Location, oldLocation, 1 ) ) + Ankhs.Resurrect( m, this ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + Ankhs.GetContextMenuEntries( from, this, list ); + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get{ return base.Hue; } + set{ base.Hue = value; if ( m_Item.Hue != value ) m_Item.Hue = value; } + } + + public override void OnDoubleClickDead( Mobile m ) + { + Ankhs.Resurrect( m, this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as AnkhEast; + } + } + } +} diff --git a/Scripts/Items/Construction/Chairs/Benchs.cs b/Scripts/Items/Construction/Chairs/Benchs.cs new file mode 100644 index 000000000..4c067d168 --- /dev/null +++ b/Scripts/Items/Construction/Chairs/Benchs.cs @@ -0,0 +1,33 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable( 0xB2D, 0xB2C )] + public class WoodenBench : Item + { + [Constructable] + public WoodenBench() : base( 0xB2D ) + { + Weight = 6; + } + + public WoodenBench(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Chairs/Chairs.cs b/Scripts/Items/Construction/Chairs/Chairs.cs new file mode 100644 index 000000000..ff1ac6c38 --- /dev/null +++ b/Scripts/Items/Construction/Chairs/Chairs.cs @@ -0,0 +1,246 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable( 0xB4F, 0xB4E, 0xB50, 0xB51 )] + public class FancyWoodenChairCushion : Item + { + [Constructable] + public FancyWoodenChairCushion() : base(0xB4F) + { + Weight = 20.0; + } + + public FancyWoodenChairCushion(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 20.0; + } + } + + [Furniture] + [Flipable( 0xB53, 0xB52, 0xB54, 0xB55 )] + public class WoodenChairCushion : Item + { + [Constructable] + public WoodenChairCushion() : base(0xB53) + { + Weight = 20.0; + } + + public WoodenChairCushion(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 20.0; + } + } + + [Furniture] + [Flipable( 0xB57, 0xB56, 0xB59, 0xB58 )] + public class WoodenChair : Item + { + [Constructable] + public WoodenChair() : base(0xB57) + { + Weight = 20.0; + } + + public WoodenChair(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 20.0; + } + } + + [Furniture] + [Flipable( 0xB5B, 0xB5A, 0xB5C, 0xB5D )] + public class BambooChair : Item + { + [Constructable] + public BambooChair() : base(0xB5B) + { + Weight = 20.0; + } + + public BambooChair(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 20.0; + } + } + + [DynamicFliping] + [Flipable(0x1218, 0x1219, 0x121A, 0x121B)] + public class StoneChair : Item + { + [Constructable] + public StoneChair() : base(0x1218) + { + Weight = 20; + } + + public StoneChair(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + [DynamicFliping] + [Flipable( 0x2DE3, 0x2DE4, 0x2DE5, 0x2DE6 )] + public class OrnateElvenChair : Item + { + [Constructable] + public OrnateElvenChair() : base( 0x2DE3 ) + { + Weight = 1.0; + } + + public OrnateElvenChair( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + [DynamicFliping] + [Flipable( 0x2DEB, 0x2DEC, 0x2DED, 0x2DEE )] + public class BigElvenChair : Item + { + [Constructable] + public BigElvenChair() : base( 0x2DEB ) + { + } + + public BigElvenChair( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + [DynamicFliping] + [Flipable( 0x2DF5, 0x2DF6 )] + public class ElvenReadingChair : Item + { + [Constructable] + public ElvenReadingChair() : base( 0x2DF5 ) + { + } + + public ElvenReadingChair( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Chairs/Stools.cs b/Scripts/Items/Construction/Chairs/Stools.cs new file mode 100644 index 000000000..00ea2a27b --- /dev/null +++ b/Scripts/Items/Construction/Chairs/Stools.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Items +{ + [Furniture] + public class Stool : Item + { + [Constructable] + public Stool() : base( 0xA2A ) + { + Weight = 10.0; + } + + public Stool(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 10.0; + } + } + + [Furniture] + public class FootStool : Item + { + [Constructable] + public FootStool() : base( 0xB5E ) + { + Weight = 6.0; + } + + public FootStool(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 10.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Chairs/Thrones.cs b/Scripts/Items/Construction/Chairs/Thrones.cs new file mode 100644 index 000000000..0024f4418 --- /dev/null +++ b/Scripts/Items/Construction/Chairs/Thrones.cs @@ -0,0 +1,68 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable(0xB32, 0xB33)] + public class Throne : Item + { + [Constructable] + public Throne() : base(0xB33) + { + Weight = 1.0; + } + + public Throne(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 1.0; + } + } + + [Furniture] + [Flipable( 0xB2E, 0xB2F, 0xB31, 0xB30 )] + public class WoodenThrone : Item + { + [Constructable] + public WoodenThrone() : base(0xB2E) + { + Weight = 15.0; + } + + public WoodenThrone(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 15.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Decorative/DecorativeShield.cs b/Scripts/Items/Construction/Decorative/DecorativeShield.cs new file mode 100644 index 000000000..fc2d4e6c9 --- /dev/null +++ b/Scripts/Items/Construction/Decorative/DecorativeShield.cs @@ -0,0 +1,426 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + + [FlipableAttribute( 0x156C, 0x156D )] + public class DecorativeShield1 : Item + { + [Constructable] + public DecorativeShield1() : base( 0x156C ) + { + Movable = false; + } + + public DecorativeShield1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + [FlipableAttribute( 0x156E, 0x156F )] + public class DecorativeShield2 : Item + { + [Constructable] + public DecorativeShield2() : base( 0x156E ) + { + Movable = false; + } + + public DecorativeShield2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1570, 0x1571 )] + public class DecorativeShield3 : Item + { + [Constructable] + public DecorativeShield3() : base( 0x1570 ) + { + Movable = false; + } + + public DecorativeShield3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1572, 0x1573 )] + public class DecorativeShield4 : Item + { + [Constructable] + public DecorativeShield4() : base( 0x1572 ) + { + Movable = false; + } + + public DecorativeShield4( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1574, 0x1575 )] + public class DecorativeShield5 : Item + { + [Constructable] + public DecorativeShield5() : base( 0x1574 ) + { + Movable = false; + } + + public DecorativeShield5( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1576, 0x1577 )] + public class DecorativeShield6 : Item + { + [Constructable] + public DecorativeShield6() : base( 0x1576 ) + { + Movable = false; + } + + public DecorativeShield6( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1578, 0x1579 )] + public class DecorativeShield7 : Item + { + [Constructable] + public DecorativeShield7() : base( 0x1578 ) + { + Movable = false; + } + + public DecorativeShield7( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x157A, 0x157B )] + public class DecorativeShield8 : Item + { + [Constructable] + public DecorativeShield8() : base( 0x157A ) + { + Movable = false; + } + + public DecorativeShield8( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x157C, 0x157D )] + public class DecorativeShield9 : Item + { + [Constructable] + public DecorativeShield9() : base( 0x157C ) + { + Movable = false; + } + + public DecorativeShield9( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x157E, 0x157F )] + public class DecorativeShield10 : Item + { + [Constructable] + public DecorativeShield10() : base( 0x157E ) + { + Movable = false; + } + + public DecorativeShield10( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1580, 0x1581 )] + public class DecorativeShield11 : Item + { + [Constructable] + public DecorativeShield11() : base( 0x1580 ) + { + Movable = false; + } + + public DecorativeShield11( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1582, 0x1583, 0x1634, 0x1635 )] + public class DecorativeShieldSword1North : Item + { + [Constructable] + public DecorativeShieldSword1North() : base( Utility.Random( 0x1582, 2 ) ) + { + Movable = false; + } + + public DecorativeShieldSword1North( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1634, 0x1635, 0x1582, 0x1583 )] + public class DecorativeShieldSword1West : Item + { + [Constructable] + public DecorativeShieldSword1West() : base( Utility.Random( 0x1634, 2 ) ) + { + Movable = false; + } + + public DecorativeShieldSword1West( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1584, 0x1585, 0x1636, 0x1637 )] + public class DecorativeShieldSword2North : Item + { + [Constructable] + public DecorativeShieldSword2North() : base( Utility.Random( 0x1584, 2 ) ) + { + Movable = false; + } + + public DecorativeShieldSword2North( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1636, 0x1637, 0x1584, 0x1585 )] + public class DecorativeShieldSword2West : Item + { + [Constructable] + public DecorativeShieldSword2West() : base( Utility.Random( 0x1636, 2 ) ) + { + Movable = false; + } + + public DecorativeShieldSword2West( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Decorative/DecorativeWeapon.cs b/Scripts/Items/Construction/Decorative/DecorativeWeapon.cs new file mode 100644 index 000000000..b93fe40d7 --- /dev/null +++ b/Scripts/Items/Construction/Decorative/DecorativeWeapon.cs @@ -0,0 +1,546 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x155E, 0x155F, 0x155C, 0x155D )] + public class DecorativeBowWest : Item + { + [Constructable] + public DecorativeBowWest() : base( Utility.Random( 0x155E, 2 ) ) + { + Movable = false; + } + + public DecorativeBowWest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x155C, 0x155D, 0x155E, 0x155F )] + public class DecorativeBowNorth : Item + { + [Constructable] + public DecorativeBowNorth() : base( Utility.Random( 0x155C, 2 ) ) + { + Movable = false; + } + + public DecorativeBowNorth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1560, 0x1561, 0x1562, 0x1563 )] + public class DecorativeAxeNorth : Item + { + [Constructable] + public DecorativeAxeNorth() : base( Utility.Random( 0x1560, 2 ) ) + { + Movable = false; + } + + public DecorativeAxeNorth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1562, 0x1563, 0x1560, 0x1561 )] + public class DecorativeAxeWest : Item + { + [Constructable] + public DecorativeAxeWest() : base( Utility.Random( 0x1562, 2 ) ) + { + Movable = false; + } + + public DecorativeAxeWest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DecorativeSwordNorth : Item + { + private InternalItem m_Item; + + [Constructable] + public DecorativeSwordNorth() : base( 0x1565 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public DecorativeSwordNorth( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private DecorativeSwordNorth m_Item; + + public InternalItem( DecorativeSwordNorth item ) : base( 0x1564 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as DecorativeSwordNorth; + } + } + } + public class DecorativeSwordWest : Item + { + private InternalItem m_Item; + + [Constructable] + public DecorativeSwordWest() : base( 0x1566 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public DecorativeSwordWest( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 1 , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private DecorativeSwordWest m_Item; + + public InternalItem( DecorativeSwordWest item ) : base( 0x1567 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as DecorativeSwordWest; + } + } + } + public class DecorativeDAxeNorth : Item + { + private InternalItem m_Item; + + [Constructable] + public DecorativeDAxeNorth() : base( 0x1569 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public DecorativeDAxeNorth( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private DecorativeDAxeNorth m_Item; + + public InternalItem( DecorativeDAxeNorth item ) : base( 0x1568 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as DecorativeDAxeNorth; + } + } + } + public class DecorativeDAxeWest : Item + { + private InternalItem m_Item; + + [Constructable] + public DecorativeDAxeWest() : base( 0x156A ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public DecorativeDAxeWest( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 1 , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private DecorativeDAxeWest m_Item; + + public InternalItem( DecorativeDAxeWest item ) : base( 0x156B ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as DecorativeDAxeWest; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Decorative/PaintingPortraits.cs b/Scripts/Items/Construction/Decorative/PaintingPortraits.cs new file mode 100644 index 000000000..eaa06bc0e --- /dev/null +++ b/Scripts/Items/Construction/Decorative/PaintingPortraits.cs @@ -0,0 +1,200 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + public class LargePainting : Item + { + [Constructable] + public LargePainting() : base( 0x0EA0 ) + { + Movable = false; + } + + public LargePainting( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0E9F, 0x0EC8 )] + public class WomanPortrait1 : Item + { + [Constructable] + public WomanPortrait1() : base( 0x0E9F ) + { + Movable = false; + } + + public WomanPortrait1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0EE7, 0x0EC9 )] + public class WomanPortrait2 : Item + { + [Constructable] + public WomanPortrait2() : base( 0x0EE7 ) + { + Movable = false; + } + + public WomanPortrait2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0EA2, 0x0EA1 )] + public class ManPortrait1 : Item + { + [Constructable] + public ManPortrait1() : base( 0x0EA2 ) + { + Movable = false; + } + + public ManPortrait1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0EA3, 0x0EA4 )] + public class ManPortrait2 : Item + { + [Constructable] + public ManPortrait2() : base( 0x0EA3 ) + { + Movable = false; + } + + public ManPortrait2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0EA6, 0x0EA5 )] + public class LadyPortrait1 : Item + { + [Constructable] + public LadyPortrait1() : base( 0x0EA6 ) + { + Movable = false; + } + + public LadyPortrait1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0EA7, 0x0EA8 )] + public class LadyPortrait2 : Item + { + [Constructable] + public LadyPortrait2() : base( 0x0EA7 ) + { + Movable = false; + } + + public LadyPortrait2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Decorative/Tapestry.cs b/Scripts/Items/Construction/Decorative/Tapestry.cs new file mode 100644 index 000000000..76055d1b3 --- /dev/null +++ b/Scripts/Items/Construction/Decorative/Tapestry.cs @@ -0,0 +1,1192 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + public class Tapestry1N : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry1N() : base( 0xEAA ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry1N( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry1N m_Item; + + public InternalItem( Tapestry1N item ) : base( 0xEAB ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry1N; + } + } + } + + public class Tapestry2N : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry2N() : base( 0xEAC ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry2N( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry2N m_Item; + + public InternalItem( Tapestry2N item ) : base( 0xEAD ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry2N; + } + } + } + public class Tapestry2W : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry2W() : base( 0xEAE ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry2W( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 1 , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry2W m_Item; + + public InternalItem( Tapestry2W item ) : base( 0xEAF ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 1 , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry2W; + } + } + } + + public class Tapestry3N : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry3N() : base( 0xFD6 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry3N( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 2, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry3N m_Item; + + public InternalItem( Tapestry3N item ) : base( 0xFD5 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 2, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry3N; + } + } + } + + public class Tapestry3W : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry3W() : base( 0xFD7 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry3W( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 2 , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry3W m_Item; + + public InternalItem( Tapestry3W item ) : base( 0xFD8 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 2 , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry3W; + } + } + } + + public class Tapestry4N : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry4N() : base( 0xFDA ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry4N( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry4N m_Item; + + public InternalItem( Tapestry4N item ) : base( 0xFD9 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry4N; + } + } + } + public class Tapestry4W : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry4W() : base( 0xFDB ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry4W( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + + private class InternalItem : Item + { + private Tapestry4W m_Item; + + public InternalItem( Tapestry4W item ) : base( 0xFDC ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry4W; + } + } + } + + public class Tapestry5N : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry5N() : base( 0xFDE ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry5N( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry5N m_Item; + + public InternalItem( Tapestry5N item ) : base( 0xFDD ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry5N; + } + } + } + public class Tapestry5W : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry5W() : base( 0xFDF ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry5W( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + + private class InternalItem : Item + { + private Tapestry5W m_Item; + + public InternalItem( Tapestry5W item ) : base( 0xFE0 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry5W; + } + } + } + + public class Tapestry6N : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry6N() : base( 0xFE2 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry6N( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + private class InternalItem : Item + { + private Tapestry6N m_Item; + + public InternalItem( Tapestry6N item ) : base( 0xFE1 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y , Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry6N; + } + } + } + public class Tapestry6W : Item + { + private InternalItem m_Item; + + [Constructable] + public Tapestry6W() : base( 0xFE3 ) + { + Movable = false; + + m_Item = new InternalItem( this ); + } + + public Tapestry6W( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y - 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + } + + private class InternalItem : Item + { + private Tapestry6W m_Item; + + public InternalItem( Tapestry6W item ) : base( 0xFE4 ) + { + Movable = true; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X , Y + 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as Tapestry6W; + } + } + } +} diff --git a/Scripts/Items/Construction/Doors/BaseDoor.cs b/Scripts/Items/Construction/Doors/BaseDoor.cs new file mode 100644 index 000000000..a4356f9e9 --- /dev/null +++ b/Scripts/Items/Construction/Doors/BaseDoor.cs @@ -0,0 +1,590 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Commands; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public abstract class BaseDoor : Item, ILockable, ITelekinesisable + { + private bool m_Open, m_Locked; + private int m_OpenedID, m_OpenedSound; + private int m_ClosedID, m_ClosedSound; + private Point3D m_Offset; + private BaseDoor m_Link; + private uint m_KeyValue; + + private Timer m_Timer; + + private static Point3D[] m_Offsets = new Point3D[] + { + new Point3D(-1, 1, 0 ), + new Point3D( 1, 1, 0 ), + new Point3D(-1, 0, 0 ), + new Point3D( 1,-1, 0 ), + new Point3D( 1, 1, 0 ), + new Point3D( 1,-1, 0 ), + new Point3D( 0, 0, 0 ), + new Point3D( 0,-1, 0 ), + + new Point3D( 0, 0, 0 ), + new Point3D( 0, 0, 0 ), + new Point3D( 0, 0, 0 ), + new Point3D( 0, 0, 0 ) + }; + + // Called by RunUO + public static void Initialize() + { + EventSink.OpenDoorMacroUsed += new OpenDoorMacroEventHandler( EventSink_OpenDoorMacroUsed ); + + CommandSystem.Register( "Link", AccessLevel.GameMaster, new CommandEventHandler( Link_OnCommand ) ); + CommandSystem.Register( "ChainLink", AccessLevel.GameMaster, new CommandEventHandler( ChainLink_OnCommand ) ); + } + + [Usage( "Link" )] + [Description( "Links two targeted doors together." )] + private static void Link_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( Link_OnFirstTarget ) ); + e.Mobile.SendMessage( "Target the first door to link." ); + } + + private static void Link_OnFirstTarget( Mobile from, object targeted ) + { + BaseDoor door = targeted as BaseDoor; + + if ( door == null ) + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( Link_OnFirstTarget ) ); + from.SendMessage( "That is not a door. Try again." ); + } + else + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( Link_OnSecondTarget ), door ); + from.SendMessage( "Target the second door to link." ); + } + } + + private static void Link_OnSecondTarget( Mobile from, object targeted, object state ) + { + BaseDoor first = (BaseDoor)state; + BaseDoor second = targeted as BaseDoor; + + if ( second == null ) + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( Link_OnSecondTarget ), first ); + from.SendMessage( "That is not a door. Try again." ); + } + else + { + first.Link = second; + second.Link = first; + from.SendMessage( "The doors have been linked." ); + } + } + + [Usage( "ChainLink" )] + [Description( "Chain-links two or more targeted doors together." )] + private static void ChainLink_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( ChainLink_OnTarget ), new ArrayList() ); + e.Mobile.SendMessage( "Target the first of a sequence of doors to link." ); + } + + private static void ChainLink_OnTarget( Mobile from, object targeted, object state ) + { + BaseDoor door = targeted as BaseDoor; + + if ( door == null ) + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( ChainLink_OnTarget ), state ); + from.SendMessage( "That is not a door. Try again." ); + } + else + { + ArrayList list = (ArrayList)state; + + if ( list.Count > 0 && list[0] == door ) + { + if ( list.Count >= 2 ) + { + for ( int i = 0; i < list.Count; ++i ) + ((BaseDoor)list[i]).Link = ((BaseDoor)list[(i + 1) % list.Count]); + + from.SendMessage( "The chain of doors have been linked." ); + } + else + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( ChainLink_OnTarget ), state ); + from.SendMessage( "You have not yet targeted two unique doors. Target the second door to link." ); + } + } + else if ( list.Contains( door ) ) + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( ChainLink_OnTarget ), state ); + from.SendMessage( "You have already targeted that door. Target another door, or retarget the first door to complete the chain." ); + } + else + { + list.Add( door ); + + from.BeginTarget( -1, false, TargetFlags.None, new TargetStateCallback( ChainLink_OnTarget ), state ); + + if ( list.Count == 1 ) + from.SendMessage( "Target the second door to link." ); + else + from.SendMessage( "Target another door to link. To complete the chain, retarget the first door." ); + } + } + } + + private static void EventSink_OpenDoorMacroUsed( OpenDoorMacroEventArgs args ) + { + Mobile m = args.Mobile; + + if ( m.Map != null ) + { + int x = m.X, y = m.Y; + + switch ( m.Direction & Direction.Mask ) + { + case Direction.North: --y; break; + case Direction.Right: ++x; --y; break; + case Direction.East: ++x; break; + case Direction.Down: ++x; ++y; break; + case Direction.South: ++y; break; + case Direction.Left: --x; ++y; break; + case Direction.West: --x; break; + case Direction.Up: --x; --y; break; + } + + Sector sector = m.Map.GetSector( x, y ); + + foreach ( Item item in sector.Items ) + { + if ( item.Location.X == x && item.Location.Y == y && (item.Z + item.ItemData.Height) > m.Z && (m.Z + 16) > item.Z && item is BaseDoor && m.CanSee( item ) && m.InLOS( item ) ) + { + if ( m.CheckAlive() ) + { + m.SendLocalizedMessage( 500024 ); // Opening door... + item.OnDoubleClick( m ); + } + + break; + } + } + } + } + + public static Point3D GetOffset( DoorFacing facing ) + { + return m_Offsets[(int)facing]; + } + + private class InternalTimer : Timer + { + private BaseDoor m_Door; + + public InternalTimer( BaseDoor door ) : base( TimeSpan.FromSeconds( 20.0 ), TimeSpan.FromSeconds( 10.0 ) ) + { + Priority = TimerPriority.OneSecond; + m_Door = door; + } + + protected override void OnTick() + { + if ( m_Door.Open && m_Door.IsFreeToClose() ) + m_Door.Open = false; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Locked + { + get + { + return m_Locked; + } + set + { + m_Locked = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public uint KeyValue + { + get + { + return m_KeyValue; + } + set + { + m_KeyValue = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Open + { + get + { + return m_Open; + } + set + { + if ( m_Open != value ) + { + m_Open = value; + + ItemID = m_Open ? m_OpenedID : m_ClosedID; + + if ( m_Open ) + Location = new Point3D( X + m_Offset.X, Y + m_Offset.Y, Z + m_Offset.Z ); + else + Location = new Point3D( X - m_Offset.X, Y - m_Offset.Y, Z - m_Offset.Z ); + + Effects.PlaySound( this, Map, m_Open ? m_OpenedSound : m_ClosedSound ); + + if ( m_Open ) + m_Timer.Start(); + else + m_Timer.Stop(); + } + } + } + + public bool CanClose() + { + if ( !m_Open ) + return true; + + Map map = Map; + + if ( map == null ) + return false; + + Point3D p = new Point3D( X - m_Offset.X, Y - m_Offset.Y, Z - m_Offset.Z ); + + return CheckFit( map, p, 16 ); + } + + private bool CheckFit( Map map, Point3D p, int height ) + { + if ( map == Map.Internal ) + return false; + + int x = p.X; + int y = p.Y; + int z = p.Z; + + Sector sector = map.GetSector( x, y ); + List items = sector.Items; + List mobs = sector.Mobiles; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + if ( item.ItemID < 0x4000 && item.AtWorldPoint( x, y ) && !(item is BaseDoor) ) + { + ItemData id = item.ItemData; + bool surface = id.Surface; + bool impassable = id.Impassable; + + if ( (surface || impassable) && (item.Z + id.CalcHeight) > z && (z + height) > item.Z ) + return false; + } + } + + for ( int i = 0; i < mobs.Count; ++i ) + { + Mobile m = mobs[i]; + + if ( m.Location.X == x && m.Location.Y == y ) + { + if ( m.Hidden && m.AccessLevel > AccessLevel.Player ) + continue; + + if ( !m.Alive ) + continue; + + if ( (m.Z + 16) > z && (z + height) > m.Z ) + return false; + } + } + + return true; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int OpenedID + { + get + { + return m_OpenedID; + } + set + { + m_OpenedID = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ClosedID + { + get + { + return m_ClosedID; + } + set + { + m_ClosedID = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int OpenedSound + { + get + { + return m_OpenedSound; + } + set + { + m_OpenedSound = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ClosedSound + { + get + { + return m_ClosedSound; + } + set + { + m_ClosedSound = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Offset + { + get + { + return m_Offset; + } + set + { + m_Offset = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BaseDoor Link + { + get + { + if ( m_Link != null && m_Link.Deleted ) + m_Link = null; + + return m_Link; + } + set + { + m_Link = value; + } + } + + public virtual bool UseChainedFunctionality{ get{ return false; } } + + public ArrayList GetChain() + { + ArrayList list = new ArrayList(); + BaseDoor c = this; + + do + { + list.Add( c ); + c = c.Link; + } while ( c != null && !list.Contains( c ) ); + + return list; + } + + public bool IsFreeToClose() + { + if ( !UseChainedFunctionality ) + return CanClose(); + + ArrayList list = GetChain(); + + bool freeToClose = true; + + for ( int i = 0; freeToClose && i < list.Count; ++i ) + freeToClose = ((BaseDoor)list[i]).CanClose(); + + return freeToClose; + } + + public void OnTelekinesis( Mobile from ) + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x376A, 9, 32, 5022 ); + Effects.PlaySound( Location, Map, 0x1F5 ); + + Use( from ); + } + + public virtual bool IsInside( Mobile from ) + { + return false; + } + + public virtual bool UseLocks() + { + return true; + } + + public virtual void Use( Mobile from ) + { + if ( m_Locked && !m_Open && UseLocks() ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502502 ); // That is locked, but you open it with your godly powers. + //from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, 502502, "", "" ) ); // That is locked, but you open it with your godly powers. + } + else if ( Key.ContainsKey( from.Backpack, this.KeyValue ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501282 ); // You quickly unlock, open, and relock the door + } + else if ( IsInside( from ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501280 ); // That is locked, but is usable from the inside. + } + else + { + if ( Hue == 0x44E && Map == Map.Malas ) // doom door into healer room in doom + this.SendLocalizedMessageTo( from, 1060014 ); // Only the dead may pass. + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502503 ); // That is locked. + + return; + } + } + + if ( m_Open && !IsFreeToClose() ) + return; + + if ( m_Open ) + OnClosed( from ); + else + OnOpened( from ); + + if ( UseChainedFunctionality ) + { + bool open = !m_Open; + + ArrayList list = GetChain(); + + for ( int i = 0; i < list.Count; ++i ) + ((BaseDoor)list[i]).Open = open; + } + else + { + Open = !m_Open; + + BaseDoor link = this.Link; + + if ( m_Open && link != null && !link.Open ) + link.Open = true; + } + } + + public virtual void OnOpened( Mobile from ) + { + } + + public virtual void OnClosed( Mobile from ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel == AccessLevel.Player && (/*!from.InLOS( this ) || */!from.InRange( GetWorldLocation(), 2 )) ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else + Use( from ); + } + + public BaseDoor( int closedID, int openedID, int openedSound, int closedSound, Point3D offset ) : base( closedID ) + { + m_OpenedID = openedID; + m_ClosedID = closedID; + m_OpenedSound = openedSound; + m_ClosedSound = closedSound; + m_Offset = offset; + + m_Timer = new InternalTimer( this ); + + Movable = false; + } + + public BaseDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_KeyValue ); + + writer.Write( m_Open ); + writer.Write( m_Locked ); + writer.Write( m_OpenedID ); + writer.Write( m_ClosedID ); + writer.Write( m_OpenedSound ); + writer.Write( m_ClosedSound ); + writer.Write( m_Offset ); + writer.Write( m_Link ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_KeyValue = reader.ReadUInt(); + m_Open = reader.ReadBool(); + m_Locked = reader.ReadBool(); + m_OpenedID = reader.ReadInt(); + m_ClosedID = reader.ReadInt(); + m_OpenedSound = reader.ReadInt(); + m_ClosedSound = reader.ReadInt(); + m_Offset = reader.ReadPoint3D(); + m_Link = reader.ReadItem() as BaseDoor; + + m_Timer = new InternalTimer( this ); + + if ( m_Open ) + m_Timer.Start(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Doors/Doors.cs b/Scripts/Items/Construction/Doors/Doors.cs new file mode 100644 index 000000000..8ecab9345 --- /dev/null +++ b/Scripts/Items/Construction/Doors/Doors.cs @@ -0,0 +1,359 @@ +using System; + +namespace Server.Items +{ + public enum DoorFacing + { + WestCW, + EastCCW, + WestCCW, + EastCW, + SouthCW, + NorthCCW, + SouthCCW, + NorthCW, + //Sliding Doors + SouthSW, + SouthSE, + WestSS, + WestSN + } + + public class IronGateShort : BaseDoor + { + [Constructable] + public IronGateShort( DoorFacing facing ) : base( 0x84c + (2 * (int)facing), 0x84d + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public IronGateShort( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class IronGate : BaseDoor + { + [Constructable] + public IronGate( DoorFacing facing ) : base( 0x824 + (2 * (int)facing), 0x825 + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public IronGate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightWoodGate : BaseDoor + { + [Constructable] + public LightWoodGate( DoorFacing facing ) : base( 0x839 + (2 * (int)facing), 0x83A + (2 * (int)facing), 0xEB, 0xF2, BaseDoor.GetOffset( facing ) ) + { + } + + public LightWoodGate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkWoodGate : BaseDoor + { + [Constructable] + public DarkWoodGate( DoorFacing facing ) : base( 0x866 + (2 * (int)facing), 0x867 + (2 * (int)facing), 0xEB, 0xF2, BaseDoor.GetOffset( facing ) ) + { + } + + public DarkWoodGate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MetalDoor : BaseDoor + { + [Constructable] + public MetalDoor( DoorFacing facing ) : base( 0x675 + (2 * (int)facing), 0x676 + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public MetalDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BarredMetalDoor : BaseDoor + { + [Constructable] + public BarredMetalDoor( DoorFacing facing ) : base( 0x685 + (2 * (int)facing), 0x686 + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public BarredMetalDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BarredMetalDoor2 : BaseDoor + { + [Constructable] + public BarredMetalDoor2( DoorFacing facing ) : base( 0x1FED + (2 * (int)facing), 0x1FEE + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public BarredMetalDoor2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RattanDoor : BaseDoor + { + [Constructable] + public RattanDoor( DoorFacing facing ) : base( 0x695 + (2 * (int)facing), 0x696 + (2 * (int)facing), 0xEB, 0xF2, BaseDoor.GetOffset( facing ) ) + { + } + + public RattanDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkWoodDoor : BaseDoor + { + [Constructable] + public DarkWoodDoor( DoorFacing facing ) : base( 0x6A5 + (2 * (int)facing), 0x6A6 + (2 * (int)facing), 0xEA, 0xF1, BaseDoor.GetOffset( facing ) ) + { + } + + public DarkWoodDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumWoodDoor : BaseDoor + { + [Constructable] + public MediumWoodDoor( DoorFacing facing ) : base( 0x6B5 + (2 * (int)facing), 0x6B6 + (2 * (int)facing), 0xEA, 0xF1, BaseDoor.GetOffset( facing ) ) + { + } + + public MediumWoodDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MetalDoor2 : BaseDoor + { + [Constructable] + public MetalDoor2( DoorFacing facing ) : base( 0x6C5 + (2 * (int)facing), 0x6C6 + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public MetalDoor2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightWoodDoor : BaseDoor + { + [Constructable] + public LightWoodDoor( DoorFacing facing ) : base( 0x6D5 + (2 * (int)facing), 0x6D6 + (2 * (int)facing), 0xEA, 0xF1, BaseDoor.GetOffset( facing ) ) + { + } + + public LightWoodDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StrongWoodDoor : BaseDoor + { + [Constructable] + public StrongWoodDoor( DoorFacing facing ) : base( 0x6E5 + (2 * (int)facing), 0x6E6 + (2 * (int)facing), 0xEA, 0xF1, BaseDoor.GetOffset( facing ) ) + { + } + + public StrongWoodDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Doors/HouseDoors.cs b/Scripts/Items/Construction/Doors/HouseDoors.cs new file mode 100644 index 000000000..766bb2ca8 --- /dev/null +++ b/Scripts/Items/Construction/Doors/HouseDoors.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections; +using Server; +using Server.Multis; +using Server.Gumps; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + public class MetalHouseDoor : BaseHouseDoor + { + [Constructable] + public MetalHouseDoor( DoorFacing facing ) : base( facing, 0x675 + (2 * (int)facing), 0x676 + (2 * (int)facing), 0xEC, 0xF3, BaseDoor.GetOffset( facing ) ) + { + } + + public MetalHouseDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkWoodHouseDoor : BaseHouseDoor + { + [Constructable] + public DarkWoodHouseDoor( DoorFacing facing ) : base( facing, 0x6A5 + (2 * (int)facing), 0x6A6 + (2 * (int)facing), 0xEA, 0xF1, BaseDoor.GetOffset( facing ) ) + { + } + + public DarkWoodHouseDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GenericHouseDoor : BaseHouseDoor + { + [Constructable] + public GenericHouseDoor( DoorFacing facing, int baseItemID, int openedSound, int closedSound ) : this( facing, baseItemID, openedSound, closedSound, true ) + { + } + + [Constructable] + public GenericHouseDoor( DoorFacing facing, int baseItemID, int openedSound, int closedSound, bool autoAdjust ) + : base( facing, baseItemID + (autoAdjust ? (2 * (int)facing) : 0), baseItemID + 1 + (autoAdjust ? (2 * (int)facing) : 0), openedSound, closedSound, BaseDoor.GetOffset( facing ) ) + { + } + + public GenericHouseDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public abstract class BaseHouseDoor : BaseDoor, ISecurable + { + private DoorFacing m_Facing; + private SecureLevel m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public DoorFacing Facing + { + get{ return m_Facing; } + set{ m_Facing = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public BaseHouseDoor( DoorFacing facing, int closedID, int openedID, int openedSound, int closedSound, Point3D offset ) : base( closedID, openedID, openedSound, closedSound, offset ) + { + m_Facing = facing; + m_Level = SecureLevel.Anyone; + } + + public BaseHouse FindHouse() + { + Point3D loc; + + if ( Open ) + loc = new Point3D( X - Offset.X, Y - Offset.Y, Z - Offset.Z ); + else + loc = this.Location; + + return BaseHouse.FindHouseAt( loc, Map, 20 ); + } + + public bool CheckAccess( Mobile m ) + { + BaseHouse house = FindHouse(); + + if ( house == null ) + return false; + + if ( !house.IsAosRules ) + return true; + + if ( house.Public ? house.IsBanned( m ) : !house.HasAccess( m ) ) + return false; + + return house.HasSecureAccess( m, m_Level ); + } + + public override void OnOpened( Mobile from ) + { + BaseHouse house = FindHouse(); + + if ( house != null && house.IsFriend( from ) && from.AccessLevel == AccessLevel.Player && house.RefreshDecay() ) + from.SendLocalizedMessage( 1043293 ); // Your house's age and contents have been refreshed. + + if ( house != null && house.Public && !house.IsFriend( from ) ) + house.Visits++; + } + + public override bool UseLocks() + { + BaseHouse house = FindHouse(); + + return ( house == null || !house.IsAosRules ); + } + + public override void Use( Mobile from ) + { + if ( !CheckAccess( from ) ) + from.SendLocalizedMessage( 1061637 ); // You are not allowed to access this. + else + base.Use( from ); + } + + public BaseHouseDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Level ); + + writer.Write( (int) m_Facing ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + goto case 0; + } + case 0: + { + if ( version < 1 ) + m_Level = SecureLevel.Anyone; + + m_Facing = (DoorFacing)reader.ReadInt(); + break; + } + } + } + + public override bool IsInside( Mobile from ) + { + int x,y,w,h; + + const int r = 2; + const int bs = r*2+1; + const int ss = r+1; + + switch ( m_Facing ) + { + case DoorFacing.WestCW: + case DoorFacing.EastCCW: x = -r; y = -r; w = bs; h = ss; break; + + case DoorFacing.EastCW: + case DoorFacing.WestCCW: x = -r; y = 0; w = bs; h = ss; break; + + case DoorFacing.SouthCW: + case DoorFacing.NorthCCW: x = -r; y = -r; w = ss; h = bs; break; + + case DoorFacing.NorthCW: + case DoorFacing.SouthCCW: x = 0; y = -r; w = ss; h = bs; break; + + //No way to test the 'insideness' of SE Sliding doors on OSI, so leaving them default to false until furthur information gained + + default: return false; + } + + int rx = from.X - X; + int ry = from.Y - Y; + int az = Math.Abs( from.Z - Z ); + + return ( rx >= x && rx < (x+w) && ry >= y && ry < (y+h) && az <= 4 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Doors/Portcullis.cs b/Scripts/Items/Construction/Doors/Portcullis.cs new file mode 100644 index 000000000..129547fde --- /dev/null +++ b/Scripts/Items/Construction/Doors/Portcullis.cs @@ -0,0 +1,60 @@ +using System; + +namespace Server.Items +{ + public class PortcullisNS : BaseDoor + { + public override bool UseChainedFunctionality{ get{ return true; } } + + [Constructable] + public PortcullisNS() : base( 0x6F5, 0x6F5, 0xF0, 0xEF, new Point3D( 0, 0, 20 ) ) + { + } + + public PortcullisNS( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PortcullisEW : BaseDoor + { + public override bool UseChainedFunctionality{ get{ return true; } } + + [Constructable] + public PortcullisEW() : base( 0x6F6, 0x6F6, 0xF0, 0xEF, new Point3D( 0, 0, 20 ) ) + { + } + + public PortcullisEW( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Doors/SecretDoors.cs b/Scripts/Items/Construction/Doors/SecretDoors.cs new file mode 100644 index 000000000..d43cc51d1 --- /dev/null +++ b/Scripts/Items/Construction/Doors/SecretDoors.cs @@ -0,0 +1,160 @@ +using System; + +namespace Server.Items +{ + public class SecretStoneDoor1 : BaseDoor + { + [Constructable] + public SecretStoneDoor1( DoorFacing facing ) : base( 0xE8 + (2 * (int)facing), 0xE9 + (2 * (int)facing), 0xED, 0xF4, BaseDoor.GetOffset( facing ) ) + { + } + + public SecretStoneDoor1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SecretDungeonDoor : BaseDoor + { + [Constructable] + public SecretDungeonDoor( DoorFacing facing ) : base( 0x314 + (2 * (int)facing), 0x315 + (2 * (int)facing), 0xED, 0xF4, BaseDoor.GetOffset( facing ) ) + { + } + + public SecretDungeonDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SecretStoneDoor2 : BaseDoor + { + [Constructable] + public SecretStoneDoor2( DoorFacing facing ) : base( 0x324 + (2 * (int)facing), 0x325 + (2 * (int)facing), 0xED, 0xF4, BaseDoor.GetOffset( facing ) ) + { + } + + public SecretStoneDoor2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SecretWoodenDoor : BaseDoor + { + [Constructable] + public SecretWoodenDoor( DoorFacing facing ) : base( 0x334 + (2 * (int)facing), 0x335 + (2 * (int)facing), 0xED, 0xF4, BaseDoor.GetOffset( facing ) ) + { + } + + public SecretWoodenDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SecretLightWoodDoor : BaseDoor + { + [Constructable] + public SecretLightWoodDoor( DoorFacing facing ) : base( 0x344 + (2 * (int)facing), 0x345 + (2 * (int)facing), 0xED, 0xF4, BaseDoor.GetOffset( facing ) ) + { + } + + public SecretLightWoodDoor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SecretStoneDoor3 : BaseDoor + { + [Constructable] + public SecretStoneDoor3( DoorFacing facing ) : base( 0x354 + (2 * (int)facing), 0x355 + (2 * (int)facing), 0xED, 0xF4, BaseDoor.GetOffset( facing ) ) + { + } + + public SecretStoneDoor3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) // Default Serialize method + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) // Default Deserialize method + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Floors/Floors.cs b/Scripts/Items/Construction/Floors/Floors.cs new file mode 100644 index 000000000..cdb577d42 --- /dev/null +++ b/Scripts/Items/Construction/Floors/Floors.cs @@ -0,0 +1,628 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseFloor : Item + { + public BaseFloor( int itemID, int count ) : base( Utility.Random( itemID, count ) ) + { + Movable = false; + } + + public BaseFloor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StonePaversLight : BaseFloor + { + [Constructable] + public StonePaversLight() : base( 0x519, 4 ) + { + } + + public StonePaversLight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StonePaversMedium : BaseFloor + { + [Constructable] + public StonePaversMedium() : base( 0x51D, 4 ) + { + } + + public StonePaversMedium( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StonePaversDark : BaseFloor + { + [Constructable] + public StonePaversDark() : base( 0x521, 4 ) + { + } + + public StonePaversDark( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreyFlagstones : BaseFloor + { + [Constructable] + public GreyFlagstones() : base( 0x4FC, 4 ) + { + } + + public GreyFlagstones( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SandFlagstones : BaseFloor + { + [Constructable] + public SandFlagstones() : base( 0x500, 4 ) + { + } + + public SandFlagstones( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MarbleFloor : BaseFloor + { + [Constructable] + public MarbleFloor() : base( 0x50D, 2 ) + { + } + + public MarbleFloor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenMarbleFloor : BaseFloor + { + [Constructable] + public GreenMarbleFloor() : base( 0x50F, 2 ) + { + } + + public GreenMarbleFloor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreyMarbleFloor : BaseFloor + { + [Constructable] + public GreyMarbleFloor() : base( 0x511, 4 ) + { + } + + public GreyMarbleFloor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CobblestonesFloor : BaseFloor + { + [Constructable] + public CobblestonesFloor() : base( 0x515, 4 ) + { + } + + public CobblestonesFloor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SandstoneFloorN : BaseFloor + { + [Constructable] + public SandstoneFloorN() : base( 0x525, 4 ) + { + } + + public SandstoneFloorN( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SandstoneFloorW : BaseFloor + { + [Constructable] + public SandstoneFloorW() : base( 0x529, 4 ) + { + } + + public SandstoneFloorW( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkSandstoneFloorN : BaseFloor + { + [Constructable] + public DarkSandstoneFloorN() : base( 0x52F, 4 ) + { + } + + public DarkSandstoneFloorN( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkSandstoneFloorW : BaseFloor + { + [Constructable] + public DarkSandstoneFloorW() : base( 0x533, 4 ) + { + } + + public DarkSandstoneFloorW( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BricksFloor1 : BaseFloor + { + [Constructable] + public BricksFloor1() : base( 0x4E2, 8 ) + { + } + + public BricksFloor1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BricksFloor2 : BaseFloor + { + [Constructable] + public BricksFloor2() : base( 0x537, 4 ) + { + } + + public BricksFloor2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CaveFloorCenter : BaseFloor + { + [Constructable] + public CaveFloorCenter() : base( 0x53B, 4 ) + { + } + + public CaveFloorCenter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CaveFloorSouth : BaseFloor + { + [Constructable] + public CaveFloorSouth() : base( 0x541, 3 ) + { + } + + public CaveFloorSouth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CaveFloorEast : BaseFloor + { + [Constructable] + public CaveFloorEast() : base( 0x544, 3 ) + { + } + + public CaveFloorEast( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CaveFloorWest : BaseFloor + { + [Constructable] + public CaveFloorWest() : base( 0x54A, 3 ) + { + } + + public CaveFloorWest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CaveFloorNorth : BaseFloor + { + [Constructable] + public CaveFloorNorth() : base( 0x54D, 3 ) + { + } + + public CaveFloorNorth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MarblePavers : BaseFloor + { + [Constructable] + public MarblePavers() : base( 0x495, 4 ) + { + } + + public MarblePavers( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BlueSlateFloorCenter : BaseFloor + { + [Constructable] + public BlueSlateFloorCenter() : base( 0x49B, 1 ) + { + } + + public BlueSlateFloorCenter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreySlateFloor : BaseFloor + { + [Constructable] + public GreySlateFloor() : base( 0x49C, 1 ) + { + } + + public GreySlateFloor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/BarrelParts.cs b/Scripts/Items/Construction/Misc/BarrelParts.cs new file mode 100644 index 000000000..823a0b16d --- /dev/null +++ b/Scripts/Items/Construction/Misc/BarrelParts.cs @@ -0,0 +1,113 @@ +using System; + +namespace Server.Items +{ + public class BarrelLid : Item + { + [Constructable] + public BarrelLid() : base(0x1DB8) + { + Weight = 2; + } + + public BarrelLid(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute(0x1EB1, 0x1EB2, 0x1EB3, 0x1EB4)] + public class BarrelStaves : Item + { + [Constructable] + public BarrelStaves() : base(0x1EB1) + { + Weight = 1; + } + + public BarrelStaves(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class BarrelHoops : Item + { + [Constructable] + public BarrelHoops() : base(0x1DB7) + { + Weight = 5; + } + + public BarrelHoops(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class BarrelTap : Item + { + [Constructable] + public BarrelTap() : base(0x1004) + { + Weight = 1; + } + + public BarrelTap(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/Easle.cs b/Scripts/Items/Construction/Misc/Easle.cs new file mode 100644 index 000000000..d3a180fcc --- /dev/null +++ b/Scripts/Items/Construction/Misc/Easle.cs @@ -0,0 +1,36 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable(0xF65, 0xF67, 0xF69)] + public class Easle : Item + { + [Constructable] + public Easle() : base(0xF65) + { + Weight = 25.0; + } + + public Easle(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 10.0 ) + Weight = 25.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/MeltedWax.cs b/Scripts/Items/Construction/Misc/MeltedWax.cs new file mode 100644 index 000000000..ea67d3d20 --- /dev/null +++ b/Scripts/Items/Construction/Misc/MeltedWax.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class MeltedWax : Item + { + public override int LabelNumber{ get{ return 1016492; } } // melted wax + + [Constructable] + public MeltedWax() : base( 0x122A ) + { + Movable = false; + Hue = 0x835; + } + + public MeltedWax(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/MusicStand.cs b/Scripts/Items/Construction/Misc/MusicStand.cs new file mode 100644 index 000000000..ce76b1b1b --- /dev/null +++ b/Scripts/Items/Construction/Misc/MusicStand.cs @@ -0,0 +1,68 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable(0xEBB, 0xEBC)] + public class TallMusicStand : Item + { + [Constructable] + public TallMusicStand() : base(0xEBB) + { + Weight = 10.0; + } + + public TallMusicStand(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 8.0 ) + Weight = 10.0; + } + } + + [Furniture] + [Flipable(0xEB6,0xEB8)] + public class ShortMusicStand : Item + { + [Constructable] + public ShortMusicStand() : base(0xEB6) + { + Weight = 10.0; + } + + public ShortMusicStand(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 10.0; + } + } +} diff --git a/Scripts/Items/Construction/Misc/Obelisk.cs b/Scripts/Items/Construction/Misc/Obelisk.cs new file mode 100644 index 000000000..d48e550d3 --- /dev/null +++ b/Scripts/Items/Construction/Misc/Obelisk.cs @@ -0,0 +1,33 @@ +using System; + +namespace Server.Items +{ + public class Obelisk : Item + { + public override int LabelNumber{ get{ return 1016474; } } // an obelisk + + [Constructable] + public Obelisk() : base(0x1184) + { + Movable = false; + } + + public Obelisk(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/Screens.cs b/Scripts/Items/Construction/Misc/Screens.cs new file mode 100644 index 000000000..c538d4e22 --- /dev/null +++ b/Scripts/Items/Construction/Misc/Screens.cs @@ -0,0 +1,65 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable( 0x24D0, 0x24D1, 0x24D2, 0x24D3, 0x24D4 )] + public class BambooScreen : Item + { + [Constructable] + public BambooScreen() : base(0x24D0) + { + Weight = 20.0; + } + + public BambooScreen(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + } + } + + [Furniture] + [Flipable( 0x24CB, 0x24CC, 0x24CD, 0x24CE, 0x24CF )] + public class ShojiScreen : Item + { + [Constructable] + public ShojiScreen() : base(0x24CB) + { + Weight = 20.0; + } + + public ShojiScreen(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + } + } + +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/Statues.cs b/Scripts/Items/Construction/Misc/Statues.cs new file mode 100644 index 000000000..22aedeeb5 --- /dev/null +++ b/Scripts/Items/Construction/Misc/Statues.cs @@ -0,0 +1,350 @@ +using System; + +namespace Server.Items +{ + public class StatueSouth : Item + { + [Constructable] + public StatueSouth() : base(0x139A) + { + Weight = 10; + } + + public StatueSouth(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatueSouth2 : Item + { + [Constructable] + public StatueSouth2() : base(0x1227) + { + Weight = 10; + } + + public StatueSouth2(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatueNorth : Item + { + [Constructable] + public StatueNorth() : base(0x139B) + { + Weight = 10; + } + + public StatueNorth(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatueWest : Item + { + [Constructable] + public StatueWest() : base(0x1226) + { + Weight = 10; + } + + public StatueWest(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatueEast : Item + { + [Constructable] + public StatueEast() : base(0x139C) + { + Weight = 10; + } + + public StatueEast(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatueEast2 : Item + { + [Constructable] + public StatueEast2() : base(0x1224) + { + Weight = 10; + } + + public StatueEast2(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatueSouthEast : Item + { + [Constructable] + public StatueSouthEast() : base(0x1225) + { + Weight = 10; + } + + public StatueSouthEast(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class BustSouth : Item + { + [Constructable] + public BustSouth() : base(0x12CB) + { + Weight = 10; + } + + public BustSouth(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class BustEast : Item + { + [Constructable] + public BustEast() : base(0x12CA) + { + Weight = 10; + } + + public BustEast(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatuePegasus : Item + { + [Constructable] + public StatuePegasus() : base(0x139D) + { + Weight = 10; + } + + public StatuePegasus(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class StatuePegasus2 : Item + { + [Constructable] + public StatuePegasus2() : base(0x1228) + { + Weight = 10; + } + + public StatuePegasus2(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class SmallTowerSculpture : Item + { + [Constructable] + public SmallTowerSculpture() : base(0x241A) + { + Weight = 20.0; + } + + public SmallTowerSculpture(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/Vase.cs b/Scripts/Items/Construction/Misc/Vase.cs new file mode 100644 index 000000000..87778301b --- /dev/null +++ b/Scripts/Items/Construction/Misc/Vase.cs @@ -0,0 +1,85 @@ +using System; + +namespace Server.Items +{ + public class Vase : Item + { + [Constructable] + public Vase() : base( 0xB46 ) + { + Weight = 10; + } + + public Vase( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeVase : Item + { + [Constructable] + public LargeVase() : base( 0xB45 ) + { + Weight = 15; + } + + public LargeVase( Serial serial ) : base(serial) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallUrn : Item + { + [Constructable] + public SmallUrn() : base( 0x241C ) + { + Weight = 20.0; + } + + public SmallUrn(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write( (int)0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Misc/Vines.cs b/Scripts/Items/Construction/Misc/Vines.cs new file mode 100644 index 000000000..8de661052 --- /dev/null +++ b/Scripts/Items/Construction/Misc/Vines.cs @@ -0,0 +1,42 @@ +using System; + +namespace Server.Items +{ + public class Vines : Item + { + [Constructable] + public Vines() : this( Utility.Random( 8 ) ) + { + } + + [Constructable] + public Vines( int v ) : base( 0xCEB ) + { + if ( v < 0 || v > 7 ) + v = 0; + + ItemID += v; + Weight = 1.0; + } + + public Vines(Serial serial) : base(serial) + { + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Ruined/RuinedItemSingle.cs b/Scripts/Items/Construction/Ruined/RuinedItemSingle.cs new file mode 100644 index 000000000..6c37b87eb --- /dev/null +++ b/Scripts/Items/Construction/Ruined/RuinedItemSingle.cs @@ -0,0 +1,315 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xC10, 0xC11 )] + public class RuinedFallenChairA : Item + { + [Constructable] + public RuinedFallenChairA() : base( 0xC10 ) + { + Movable = false; + } + + public RuinedFallenChairA( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + [FlipableAttribute( 0xC13, 0xC12 )] + public class RuinedArmoire : Item + { + [Constructable] + public RuinedArmoire() : base( 0xC13 ) + { + Movable = false; + } + + public RuinedArmoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + [FlipableAttribute( 0xC14, 0xC15 )] + public class RuinedBookcase : Item + { + [Constructable] + public RuinedBookcase() : base( 0xC14 ) + { + Movable = false; + } + + public RuinedBookcase( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + public class RuinedBooks : Item + { + [Constructable] + public RuinedBooks() : base( 0xC16 ) + { + Movable = false; + } + + public RuinedBooks( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC17, 0xC18 )] + public class CoveredChair : Item + { + [Constructable] + public CoveredChair() : base( 0xC17 ) + { + Movable = false; + } + + public CoveredChair( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + [FlipableAttribute( 0xC19, 0xC1A )] + public class RuinedFallenChairB : Item + { + [Constructable] + public RuinedFallenChairB() : base( 0xC19 ) + { + Movable = false; + } + + public RuinedFallenChairB( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } + + [FlipableAttribute( 0xC1B, 0xC1C, 0xC1E, 0xC1D )] + public class RuinedChair : Item + { + [Constructable] + public RuinedChair() : base( 0xC1B ) + { + Movable = false; + } + + public RuinedChair( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RuinedClock : Item + { + [Constructable] + public RuinedClock() : base( 0xC1F ) + { + Movable = false; + } + + public RuinedClock( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC24, 0xC25 )] + public class RuinedDrawers : Item + { + [Constructable] + public RuinedDrawers() : base( 0xC24 ) + { + Movable = false; + } + + public RuinedDrawers( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RuinedPainting : Item + { + [Constructable] + public RuinedPainting() : base( 0xC2C ) + { + Movable = false; + } + + public RuinedPainting( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC2D, 0xC2F, 0xC2E, 0xC30 )] + public class WoodDebris : Item + { + [Constructable] + public WoodDebris() : base( 0xC2D ) + { + Movable = false; + } + + public WoodDebris( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Signs/BaseSign.cs b/Scripts/Items/Construction/Signs/BaseSign.cs new file mode 100644 index 000000000..841081e7a --- /dev/null +++ b/Scripts/Items/Construction/Signs/BaseSign.cs @@ -0,0 +1,31 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseSign : Item + { + public BaseSign( int dispID ) : base( dispID ) + { + Movable = false; + } + + public BaseSign( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Signs/LocalizedSign.cs b/Scripts/Items/Construction/Signs/LocalizedSign.cs new file mode 100644 index 000000000..b2c0fbc43 --- /dev/null +++ b/Scripts/Items/Construction/Signs/LocalizedSign.cs @@ -0,0 +1,56 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LocalizedSign : Sign + { + private int m_LabelNumber; + + public override int LabelNumber{ get{ return m_LabelNumber; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Number{ get{ return m_LabelNumber; } set{ m_LabelNumber = value; InvalidateProperties(); } } + + [Constructable] + public LocalizedSign( SignType type, SignFacing facing, int labelNumber ) : base( ( 0xB95 + (2 * (int)type) ) + (int)facing ) + { + m_LabelNumber = labelNumber; + } + + [Constructable] + public LocalizedSign( int itemID, int labelNumber ) : base( itemID ) + { + m_LabelNumber = labelNumber; + } + + public LocalizedSign( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_LabelNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_LabelNumber = reader.ReadInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Signs/Sign.cs b/Scripts/Items/Construction/Signs/Sign.cs new file mode 100644 index 000000000..5fd80833c --- /dev/null +++ b/Scripts/Items/Construction/Signs/Sign.cs @@ -0,0 +1,107 @@ +using System; +using Server; + +namespace Server.Items +{ + public enum SignFacing + { + North, + West + } + + public enum SignType + { + Library, + DarkWoodenPost, + LightWoodenPost, + MetalPostC, + MetalPostB, + MetalPostA, + MetalPost, + Bakery, + Tailor, + Tinker, + Butcher, + Healer, + Mage, + Woodworker, + Customs, + Inn, + Shipwright, + Stables, + BarberShop, + Bard, + Fletcher, + Armourer, + Jeweler, + Tavern, + ReagentShop, + Blacksmith, + Painter, + Provisioner, + Bowyer, + WoodenSign, + BrassSign, + ArmamentsGuild, + ArmourersGuild, + BlacksmithsGuild, + WeaponsGuild, + BardicGuild, + BartersGuild, + ProvisionersGuild, + TradersGuild, + CooksGuild, + HealersGuild, + MagesGuild, + SorcerersGuild, + IllusionistGuild, + MinersGuild, + ArchersGuild, + SeamensGuild, + FishermensGuild, + SailorsGuild, + ShipwrightsGuild, + TailorsGuild, + ThievesGuild, + RoguesGuild, + AssassinsGuild, + TinkersGuild, + WarriorsGuild, + CavalryGuild, + FightersGuild, + MerchantsGuild, + Bank, + Theatre + } + + public class Sign : BaseSign + { + [Constructable] + public Sign( SignType type, SignFacing facing ) : base( ( 0xB95 + (2 * (int)type) ) + (int)facing ) + { + } + + [Constructable] + public Sign( int itemID ) : base( itemID ) + { + } + + public Sign( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Tables/Tables.cs b/Scripts/Items/Construction/Tables/Tables.cs new file mode 100644 index 000000000..22078e896 --- /dev/null +++ b/Scripts/Items/Construction/Tables/Tables.cs @@ -0,0 +1,159 @@ +using System; + +namespace Server.Items +{ + + [Furniture] + public class ElegantLowTable : Item + { + [Constructable] + public ElegantLowTable() : base(0x2819) + { + Weight = 1.0; + } + + public ElegantLowTable(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + } + } + + [Furniture] + public class PlainLowTable : Item + { + [Constructable] + public PlainLowTable() : base(0x281A) + { + Weight = 1.0; + } + + public PlainLowTable(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + } + } + + [Furniture] + [Flipable(0xB90,0xB7D)] + public class LargeTable : Item + { + [Constructable] + public LargeTable() : base(0xB90) + { + Weight = 1.0; + } + + public LargeTable(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 4.0 ) + Weight = 1.0; + } + } + + [Furniture] + [Flipable(0xB35,0xB34)] + public class Nightstand : Item + { + [Constructable] + public Nightstand() : base(0xB35) + { + Weight = 1.0; + } + + public Nightstand(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 4.0 ) + Weight = 1.0; + } + } + + [Furniture] + [Flipable(0xB8F,0xB7C)] + public class YewWoodTable : Item + { + [Constructable] + public YewWoodTable() : base(0xB8F) + { + Weight = 1.0; + } + + public YewWoodTable(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 4.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Tables/WritingTable.cs b/Scripts/Items/Construction/Tables/WritingTable.cs new file mode 100644 index 000000000..3ece21dab --- /dev/null +++ b/Scripts/Items/Construction/Tables/WritingTable.cs @@ -0,0 +1,36 @@ +using System; + +namespace Server.Items +{ + [Furniture] + [Flipable(0xB4A,0xB49, 0xB4B, 0xB4C)] + public class WritingTable : Item + { + [Constructable] + public WritingTable() : base(0xB4A) + { + Weight = 1.0; + } + + public WritingTable(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( Weight == 4.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Walls/BaseWall.cs b/Scripts/Items/Construction/Walls/BaseWall.cs new file mode 100644 index 000000000..d59c65435 --- /dev/null +++ b/Scripts/Items/Construction/Walls/BaseWall.cs @@ -0,0 +1,30 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseWall : Item + { + public BaseWall( int itemID ) : base( itemID ) + { + Movable = false; + } + + public BaseWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Walls/DarkWoodWall.cs b/Scripts/Items/Construction/Walls/DarkWoodWall.cs new file mode 100644 index 000000000..32a69e014 --- /dev/null +++ b/Scripts/Items/Construction/Walls/DarkWoodWall.cs @@ -0,0 +1,54 @@ +using System; + +namespace Server.Items +{ + public enum DarkWoodWallTypes + { + Corner, + SouthWall, + EastWall, + CornerPost, + EastDoorFrame, + SouthDoorFrame, + WestDoorFrame, + NorthDoorFrame, + SouthWindow, + EastWindow, + CornerMedium, + EastWallMedium, + SouthWallMedium, + CornerPostMedium, + CornerShort, + EastWallShort, + SouthWallShort, + CornerPostShort, + SouthWallVShort, + EastWallVShort + } + + public class DarkWoodWall : BaseWall + { + [Constructable] + public DarkWoodWall( DarkWoodWallTypes type ) : base( 0x0006 + (int)type ) + { + } + + public DarkWoodWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Walls/ThickGrayStoneWall.cs b/Scripts/Items/Construction/Walls/ThickGrayStoneWall.cs new file mode 100644 index 000000000..9de353d6f --- /dev/null +++ b/Scripts/Items/Construction/Walls/ThickGrayStoneWall.cs @@ -0,0 +1,64 @@ +/**************************************** + * NAME : Thick Gray Stone Wall * + * SCRIPT : ThickGrayStoneWall.cs * + * VERSION : v1.00 * + * CREATOR : Mans Sjoberg (Allmight) * + * CREATED : 10-07.2002 * + * **************************************/ + +using System; + +namespace Server.Items +{ + public enum ThickGrayStoneWallTypes + { + WestArch, + NorthArch, + SouthArchTop, + EastArchTop, + EastArch, + SouthArch, + Wall1, + Wall2, + Wall3, + SouthWindow, + Wall4, + EastWindow, + WestArch2, + NorthArch2, + SouthArchTop2, + EastArchTop2, + EastArch2, + SouthArch2, + SWArchEdge2, + SouthWindow2, + NEArchEdge2, + EastWindow2 + } + + public class ThickGrayStoneWall : BaseWall + { + [Constructable] + public ThickGrayStoneWall( ThickGrayStoneWallTypes type) : base( 0x007A + (int)type ) + { + } + + public ThickGrayStoneWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Walls/ThinBrickWall.cs b/Scripts/Items/Construction/Walls/ThinBrickWall.cs new file mode 100644 index 000000000..4f3a9a349 --- /dev/null +++ b/Scripts/Items/Construction/Walls/ThinBrickWall.cs @@ -0,0 +1,70 @@ +using System; + +namespace Server.Items +{ + public enum ThinBrickWallTypes + { + Corner, + SouthWall, + EastWall, + CornerPost, + EastDoorFrame, + SouthDoorFrame, + WestDoorFrame, + NorthDoorFrame, + SouthWindow, + EastWindow, + CornerMedium, + SouthWallMedium, + EastWallMedium, + CornerPostMedium, + CornerShort, + SouthWallShort, + EastWallShort, + CornerPostShort, + CornerArch, + SouthArch, + WestArch, + EastArch, + NorthArch, + SouthCenterArchTall, + EastCenterArchTall, + EastCornerArchTall, + SouthCornerArchTall, + SouthCornerArch, + EastCornerArch, + SouthCenterArch, + EastCenterArch, + CornerVVShort, + SouthWallVVShort, + EastWallVVShort, + SouthWallVShort, + EastWallVShort + }; + + public class ThinBrickWall : BaseWall + { + [Constructable] + public ThinBrickWall( ThinBrickWallTypes type ) : base( 0x0033 + (int)type ) + { + } + + public ThinBrickWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Walls/ThinStoneWall.cs b/Scripts/Items/Construction/Walls/ThinStoneWall.cs new file mode 100644 index 000000000..10b8c7afc --- /dev/null +++ b/Scripts/Items/Construction/Walls/ThinStoneWall.cs @@ -0,0 +1,59 @@ +using System; + +namespace Server.Items +{ + public enum ThinStoneWallTypes + { + Corner, + EastWall, + SouthWall, + CornerPost, + EastDoorFrame, + SouthDoorFrame, + NorthDoorFrame, + WestDoorFrame, + SouthWindow, + EastWindow, + CornerMedium, + SouthWallMedium, + EastWallMedium, + CornerPostMedium, + CornerArch, + EastArch, + SouthArch, + NorthArch, + WestArch, + CornerShort, + EastWallShort, + SouthWallShort, + CornerPostShort, + SouthWallShort2, + EastWallShort2 + } + + public class ThinStoneWall : BaseWall + { + [Constructable] + public ThinStoneWall( ThinStoneWallTypes type ) : base( 0x001A + (int)type ) + { + } + + public ThinStoneWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Construction/Walls/WhiteStoneWall.cs b/Scripts/Items/Construction/Walls/WhiteStoneWall.cs new file mode 100644 index 000000000..c0ee6a389 --- /dev/null +++ b/Scripts/Items/Construction/Walls/WhiteStoneWall.cs @@ -0,0 +1,77 @@ +/**************************************** + * NAME : White Stone Wall * + * SCRIPT : WhiteStoneWall.cs * + * VERSION : v1.00 * + * CREATOR : Mans Sjoberg (Allmight) * + * CREATED : 10-07.2002 * + * **************************************/ + +using System; + +namespace Server.Items +{ + public enum WhiteStoneWallTypes + { + EastWall, + SouthWall, + SECorner, + NWCornerPost, + EastArrowLoop, + SouthArrowLoop, + EastWindow, + SouthWindow, + SouthWallMedium, + EastWallMedium, + SECornerMedium, + NWCornerPostMedium, + SouthWallShort, + EastWallShort, + SECornerShort, + NWCornerPostShort, + NECornerPostShort, + SWCornerPostShort, + SouthWallVShort, + EastWallVShort, + SECornerVShort, + NWCornerPostVShort, + SECornerArch, + SouthArch, + WestArch, + EastArch, + NorthArch, + EastBattlement, + SECornerBattlement, + SouthBattlement, + NECornerBattlement, + SWCornerBattlement, + Column, + SouthWallVVShort, + EastWallVVShort + } + + public class WhiteStoneWall : BaseWall + { + [Constructable] + public WhiteStoneWall( WhiteStoneWallTypes type) : base( 0x0057 + (int)type ) + { + } + + public WhiteStoneWall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/BaseTreasureChest.cs b/Scripts/Items/Containers/BaseTreasureChest.cs new file mode 100644 index 000000000..10d9affc3 --- /dev/null +++ b/Scripts/Items/Containers/BaseTreasureChest.cs @@ -0,0 +1,249 @@ +using Server; +using Server.Items; +using Server.Network; +using System; +using System.Collections; + +namespace Server.Items +{ + public class BaseTreasureChest : LockableContainer + { + private TreasureLevel m_TreasureLevel; + private short m_MaxSpawnTime = 60; + private short m_MinSpawnTime = 10; + private TreasureResetTimer m_ResetTimer; + + [CommandProperty( AccessLevel.GameMaster )] + public TreasureLevel Level + { + get + { + return m_TreasureLevel; + } + set + { + m_TreasureLevel = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public short MaxSpawnTime + { + get + { + return m_MaxSpawnTime; + } + set + { + m_MaxSpawnTime = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public short MinSpawnTime + { + get + { + return m_MinSpawnTime; + } + set + { + m_MinSpawnTime = value; + } + } + + public override bool IsDecoContainer + { + get{ return false; } + } + + public BaseTreasureChest( int itemID ) : this( itemID, TreasureLevel.Level2 ) + { + } + + public BaseTreasureChest( int itemID, TreasureLevel level ) : base( itemID ) + { + m_TreasureLevel = level; + Locked = true; + Movable = false; + + SetLockLevel(); + GenerateTreasure(); + } + + public BaseTreasureChest( Serial serial ) : base( serial ) + { + } + + public override string DefaultName + { + get + { + if ( this.Locked ) + return "a locked treasure chest"; + + return "a treasure chest"; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + writer.Write( (byte) m_TreasureLevel ); + writer.Write( m_MinSpawnTime ); + writer.Write( m_MaxSpawnTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_TreasureLevel = (TreasureLevel)reader.ReadByte(); + m_MinSpawnTime = reader.ReadShort(); + m_MaxSpawnTime = reader.ReadShort(); + + if( !Locked ) + StartResetTimer(); + } + + protected virtual void SetLockLevel() + { + switch( m_TreasureLevel ) + { + case TreasureLevel.Level1: + this.RequiredSkill = this.LockLevel = 5; + break; + + case TreasureLevel.Level2: + this.RequiredSkill = this.LockLevel = 20; + break; + + case TreasureLevel.Level3: + this.RequiredSkill = this.LockLevel = 50; + break; + + case TreasureLevel.Level4: + this.RequiredSkill = this.LockLevel = 70; + break; + + case TreasureLevel.Level5: + this.RequiredSkill = this.LockLevel = 90; + break; + + case TreasureLevel.Level6: + this.RequiredSkill = this.LockLevel = 100; + break; + } + } + + private void StartResetTimer() + { + if( m_ResetTimer == null ) + m_ResetTimer = new TreasureResetTimer( this ); + else + m_ResetTimer.Delay = TimeSpan.FromMinutes( Utility.Random( m_MinSpawnTime, m_MaxSpawnTime )); + + m_ResetTimer.Start(); + } + + protected virtual void GenerateTreasure() + { + int MinGold = 1; + int MaxGold = 2; + + switch( m_TreasureLevel ) + { + case TreasureLevel.Level1: + MinGold = 100; + MaxGold = 300; + break; + + case TreasureLevel.Level2: + MinGold = 300; + MaxGold = 600; + break; + + case TreasureLevel.Level3: + MinGold = 600; + MaxGold = 900; + break; + + case TreasureLevel.Level4: + MinGold = 900; + MaxGold = 1200; + break; + + case TreasureLevel.Level5: + MinGold = 1200; + MaxGold = 5000; + break; + + case TreasureLevel.Level6: + MinGold = 5000; + MaxGold = 9000; + break; + } + + DropItem( new Gold( MinGold, MaxGold ) ); + } + + public override void LockPick( Mobile from ) + { + base.LockPick( from ); + + StartResetTimer(); + } + + public void ClearContents() + { + for ( int i = Items.Count - 1; i >= 0; --i ) + { + if ( i < Items.Count ) + Items[i].Delete(); + } + } + + public void Reset() + { + if( m_ResetTimer != null ) + { + if( m_ResetTimer.Running ) + m_ResetTimer.Stop(); + } + + Locked = true; + ClearContents(); + GenerateTreasure(); + } + + public enum TreasureLevel + { + Level1, + Level2, + Level3, + Level4, + Level5, + Level6, + }; + + private class TreasureResetTimer : Timer + { + private BaseTreasureChest m_Chest; + + public TreasureResetTimer( BaseTreasureChest chest ) : base ( TimeSpan.FromMinutes( Utility.Random( chest.MinSpawnTime, chest.MaxSpawnTime ) ) ) + { + m_Chest = chest; + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + m_Chest.Reset(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/Container.cs b/Scripts/Items/Containers/Container.cs new file mode 100644 index 000000000..f7a179dc9 --- /dev/null +++ b/Scripts/Items/Containers/Container.cs @@ -0,0 +1,891 @@ +using System; +using System.Collections; +using Server.Multis; +using Server.Mobiles; +using Server.Network; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + public abstract class BaseContainer : Container + { + public override int DefaultMaxWeight + { + get + { + if ( IsSecure ) + return 0; + + return base.DefaultMaxWeight; + } + } + + public BaseContainer( int itemID ) : base( itemID ) + { + } + + public override bool IsAccessibleTo( Mobile m ) + { + if ( !BaseHouse.CheckAccessible( m, this ) ) + return false; + + return base.IsAccessibleTo( m ); + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + if ( this.IsSecure && !BaseHouse.CheckHold( m, this, item, message, checkItems, plusItems, plusWeight ) ) + return false; + + return base.CheckHold( m, item, message, checkItems, plusItems, plusWeight ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public override bool TryDropItem( Mobile from, Item dropped, bool sendFullMessage ) + { + if ( !CheckHold( from, dropped, sendFullMessage, true ) ) + return false; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsLockedDown( this ) ) + { + if ( dropped is VendorRentalContract || ( dropped is Container && ((Container)dropped).FindItemByType( typeof( VendorRentalContract ) ) != null ) ) + { + from.SendLocalizedMessage( 1062492 ); // You cannot place a rental contract in a locked down container. + return false; + } + + if ( !house.LockDown( from, dropped, false ) ) + return false; + } + + List list = this.Items; + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = list[i]; + + if ( !(item is Container) && item.StackWith( from, dropped, false ) ) + return true; + } + + DropItem( dropped ); + + return true; + } + + public override bool OnDragDropInto( Mobile from, Item item, Point3D p ) + { + if ( !CheckHold( from, item, true, true ) ) + return false; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsLockedDown( this ) ) + { + if ( item is VendorRentalContract || ( item is Container && ((Container)item).FindItemByType( typeof( VendorRentalContract ) ) != null ) ) + { + from.SendLocalizedMessage( 1062492 ); // You cannot place a rental contract in a locked down container. + return false; + } + + if ( !house.LockDown( from, item, false ) ) + return false; + } + + item.Location = new Point3D( p.X, p.Y, 0 ); + AddItem( item ); + + from.SendSound( GetDroppedSound( item ), GetWorldLocation() ); + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel > AccessLevel.Player || from.InRange( this.GetWorldLocation(), 2 ) || this.RootParent is PlayerVendor ) + Open( from ); + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + + public virtual void Open( Mobile from ) + { + DisplayTo( from ); + } + + public BaseContainer( Serial serial ) : base( serial ) + { + } + + /* Note: base class insertion; we cannot serialize anything here */ + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + } + } + + public class StrongBackpack : Backpack //Used on Pack animals + { + [Constructable] + public StrongBackpack() + { + Layer = Layer.Backpack; + Weight = 3.0; + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + return base.CheckHold( m, item, false, checkItems, plusItems, plusWeight ); + } + + public override int DefaultMaxWeight{ get{ return 1600; } } + + public override bool CheckContentDisplay( Mobile from ) + { + object root = this.RootParent; + + if ( root is BaseCreature && ((BaseCreature)root).Controlled && ((BaseCreature)root).ControlMaster == from ) + return true; + + return base.CheckContentDisplay( from ); + } + + public StrongBackpack( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Backpack : BaseContainer, IDyable + { + [Constructable] + public Backpack() : base( 0xE75 ) + { + Layer = Layer.Backpack; + Weight = 3.0; + } + + public Backpack( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && ItemID == 0x9B2 ) + ItemID = 0xE75; + } + } + + public class Pouch : TrapableContainer + { + [Constructable] + public Pouch() : base( 0xE79 ) + { + Weight = 1.0; + } + + public Pouch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public abstract class BaseBagBall : BaseContainer, IDyable + { + public BaseBagBall( int itemID ) : base( itemID ) + { + Weight = 1.0; + } + + public BaseBagBall( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallBagBall : BaseBagBall + { + [Constructable] + public SmallBagBall() : base( 0x2256 ) + { + } + + public SmallBagBall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeBagBall : BaseBagBall + { + [Constructable] + public LargeBagBall() : base( 0x2257 ) + { + } + + public LargeBagBall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Bag : BaseContainer, IDyable + { + [Constructable] + public Bag() : base( 0xE76 ) + { + Weight = 2.0; + } + + public Bag( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Barrel : BaseContainer + { + [Constructable] + public Barrel() : base( 0xE77 ) + { + Weight = 25.0; + } + + public Barrel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 0.0 ) + Weight = 25.0; + } + } + + public class Keg : BaseContainer + { + [Constructable] + public Keg() : base( 0xE7F ) + { + Weight = 15.0; + } + + public Keg( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PicnicBasket : BaseContainer + { + [Constructable] + public PicnicBasket() : base( 0xE7A ) + { + Weight = 2.0; // Stratics doesn't know weight + } + + public PicnicBasket( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Basket : BaseContainer + { + [Constructable] + public Basket() : base( 0x990 ) + { + Weight = 1.0; // Stratics doesn't know weight + } + + public Basket( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0x9AA, 0xE7D )] + public class WoodenBox : LockableContainer + { + [Constructable] + public WoodenBox() : base( 0x9AA ) + { + Weight = 4.0; + } + + public WoodenBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0x9A9, 0xE7E )] + public class SmallCrate : LockableContainer + { + [Constructable] + public SmallCrate() : base( 0x9A9 ) + { + Weight = 2.0; + } + + public SmallCrate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 4.0 ) + Weight = 2.0; + } + } + + [Furniture] + [Flipable( 0xE3F, 0xE3E )] + public class MediumCrate : LockableContainer + { + [Constructable] + public MediumCrate() : base( 0xE3F ) + { + Weight = 2.0; + } + + public MediumCrate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 2.0; + } + } + + [Furniture] + [Flipable( 0xE3D, 0xE3C )] + public class LargeCrate : LockableContainer + { + [Constructable] + public LargeCrate() : base( 0xE3D ) + { + Weight = 1.0; + } + + public LargeCrate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 8.0 ) + Weight = 1.0; + } + } + + [DynamicFliping] + [Flipable( 0x9A8, 0xE80 )] + public class MetalBox : LockableContainer + { + [Constructable] + public MetalBox() : base( 0x9A8 ) + { + } + + public MetalBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 3 ) + Weight = -1; + } + } + + [DynamicFliping] + [Flipable( 0x9AB, 0xE7C )] + public class MetalChest : LockableContainer + { + [Constructable] + public MetalChest() : base( 0x9AB ) + { + } + + public MetalChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 25 ) + Weight = -1; + } + } + + [DynamicFliping] + [Flipable( 0xE41, 0xE40 )] + public class MetalGoldenChest : LockableContainer + { + [Constructable] + public MetalGoldenChest() : base( 0xE41 ) + { + } + + public MetalGoldenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 25 ) + Weight = -1; + } + } + + [Furniture] + [Flipable( 0xe43, 0xe42 )] + public class WoodenChest : LockableContainer + { + [Constructable] + public WoodenChest() : base( 0xe43 ) + { + Weight = 2.0; + } + + public WoodenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 15.0 ) + Weight = 2.0; + } + } + + [Furniture] + [Flipable( 0x280B, 0x280C )] + public class PlainWoodenChest : LockableContainer + { + [Constructable] + public PlainWoodenChest() : base( 0x280B ) + { + } + + public PlainWoodenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 15 ) + Weight = -1; + } + } + + [Furniture] + [Flipable( 0x280D, 0x280E )] + public class OrnateWoodenChest : LockableContainer + { + [Constructable] + public OrnateWoodenChest() : base( 0x280D ) + { + } + + public OrnateWoodenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 15 ) + Weight = -1; + } + } + + [Furniture] + [Flipable( 0x280F, 0x2810 )] + public class GildedWoodenChest : LockableContainer + { + [Constructable] + public GildedWoodenChest() : base( 0x280F ) + { + } + + public GildedWoodenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 15 ) + Weight = -1; + } + } + + [Furniture] + [Flipable( 0x2811, 0x2812 )] + public class WoodenFootLocker : LockableContainer + { + [Constructable] + public WoodenFootLocker() : base( 0x2811 ) + { + } + + public WoodenFootLocker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 15 ) + Weight = -1; + } + } + + [Furniture] + [Flipable( 0x2813, 0x2814 )] + public class FinishedWoodenChest : LockableContainer + { + [Constructable] + public FinishedWoodenChest() : base( 0x2813 ) + { + } + + public FinishedWoodenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 15 ) + Weight = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/FillableContainers.cs b/Scripts/Items/Containers/FillableContainers.cs new file mode 100644 index 000000000..b3bbd29fd --- /dev/null +++ b/Scripts/Items/Containers/FillableContainers.cs @@ -0,0 +1,1502 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Mobiles; + +namespace Server.Items +{ + public abstract class FillableContainer : LockableContainer + { + public virtual int MinRespawnMinutes{ get{ return 60; } } + public virtual int MaxRespawnMinutes{ get{ return 90; } } + + public virtual bool IsLockable{ get{ return true; } } + public virtual bool IsTrapable{ get{ return IsLockable; } } + + public virtual int SpawnThreshold{ get{ return 2; } } + + protected FillableContent m_Content; + + protected DateTime m_NextRespawnTime; + protected Timer m_RespawnTimer; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NextRespawnTime{ get{ return m_NextRespawnTime; } } + + [CommandProperty( AccessLevel.GameMaster )] + public FillableContentType ContentType + { + get{ return FillableContent.Lookup( m_Content ); } + set{ Content = FillableContent.Lookup( value ); } + } + + public FillableContent Content + { + get{ return m_Content; } + set + { + if ( m_Content == value ) + return; + + m_Content = value; + + for ( int i = Items.Count - 1; i >= 0; --i ) + { + if ( i < Items.Count ) + Items[i].Delete(); + } + + Respawn(); + } + } + + public FillableContainer( int itemID ) : base( itemID ) + { + Movable = false; + } + + public override void OnMapChange() + { + base.OnMapChange(); + AcquireContent(); + } + + public override void OnLocationChange( Point3D oldLocation ) + { + base.OnLocationChange( oldLocation ); + AcquireContent(); + } + + public virtual void AcquireContent() + { + if ( m_Content != null ) + return; + + m_Content = FillableContent.Acquire( this.GetWorldLocation(), this.Map ); + + if ( m_Content != null ) + Respawn(); + } + + public override void OnItemRemoved( Item item ) + { + CheckRespawn(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_RespawnTimer != null ) + { + m_RespawnTimer.Stop(); + m_RespawnTimer = null; + } + } + + public int GetItemsCount() + { + int count = 0; + + foreach ( Item item in this.Items ) + { + count += item.Amount; + } + + return count; + } + + public void CheckRespawn() + { + bool canSpawn = ( m_Content != null && !Deleted && GetItemsCount() <= SpawnThreshold && !Movable && Parent == null && !IsLockedDown && !IsSecure ); + + if ( canSpawn ) + { + if ( m_RespawnTimer == null ) + { + int mins = Utility.RandomMinMax( this.MinRespawnMinutes, this.MaxRespawnMinutes ); + TimeSpan delay = TimeSpan.FromMinutes( mins ); + + m_NextRespawnTime = DateTime.Now + delay; + m_RespawnTimer = Timer.DelayCall( delay, new TimerCallback( Respawn ) ); + } + } + else if ( m_RespawnTimer != null ) + { + m_RespawnTimer.Stop(); + m_RespawnTimer = null; + } + } + + public void Respawn() + { + if ( m_RespawnTimer != null ) + { + m_RespawnTimer.Stop(); + m_RespawnTimer = null; + } + + if ( m_Content == null || Deleted ) + return; + + GenerateContent(); + + if ( IsLockable ) + { + Locked = true; + + int difficulty = (m_Content.Level - 1) * 30; + + LockLevel = difficulty - 10; + MaxLockLevel = difficulty + 30; + RequiredSkill = difficulty; + } + + if ( IsTrapable && (m_Content.Level > 1 || 4 > Utility.Random( 5 )) ) + { + if ( m_Content.Level > Utility.Random( 5 ) ) + TrapType = TrapType.PoisonTrap; + else + TrapType = TrapType.ExplosionTrap; + + TrapPower = m_Content.Level * Utility.RandomMinMax( 10, 30 ); + TrapLevel = m_Content.Level; + } + else + { + TrapType = TrapType.None; + TrapPower = 0; + TrapLevel = 0; + } + + CheckRespawn(); + } + + protected virtual int GetSpawnCount() + { + int itemsCount = GetItemsCount(); + + if ( itemsCount > SpawnThreshold ) + return 0; + + int maxSpawnCount = ( 1 + SpawnThreshold - itemsCount ) * 2; + + return Utility.RandomMinMax( 0, maxSpawnCount ); + } + + public virtual void GenerateContent() + { + if ( m_Content == null || Deleted ) + return; + + int toSpawn = GetSpawnCount(); + + for ( int i = 0; i < toSpawn; ++i ) + { + Item item = m_Content.Construct(); + + if ( item != null ) + { + List list = this.Items; + + for ( int j = 0; j < list.Count; ++j ) + { + Item subItem = list[j]; + + if ( !(subItem is Container) && subItem.StackWith( null, item, false ) ) + break; + } + + if ( item != null && !item.Deleted ) + DropItem( item ); + } + } + } + + public FillableContainer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); // version + + writer.Write( (int) ContentType ); + + if ( m_RespawnTimer != null ) + { + writer.Write( true ); + writer.WriteDeltaTime( (DateTime) m_NextRespawnTime ); + } + else + { + writer.Write( false ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Content = FillableContent.Lookup( (FillableContentType) reader.ReadInt() ); + goto case 0; + } + case 0: + { + if ( reader.ReadBool() ) + { + m_NextRespawnTime = reader.ReadDeltaTime(); + + TimeSpan delay = m_NextRespawnTime - DateTime.Now; + m_RespawnTimer = Timer.DelayCall( delay > TimeSpan.Zero ? delay : TimeSpan.Zero, new TimerCallback( Respawn ) ); + } + else + { + CheckRespawn(); + } + + break; + } + } + } + } + + [Flipable( 0xA97, 0xA99, 0xA98, 0xA9A, 0xA9B, 0xA9C )] + public class LibraryBookcase : FillableContainer + { + public override bool IsLockable{ get{ return false; } } + public override int SpawnThreshold{ get{ return 5; } } + + protected override int GetSpawnCount() + { + return ( 5 - GetItemsCount() ); + } + + public override void AcquireContent() + { + if ( m_Content != null ) + return; + + m_Content = FillableContent.Library; + + if ( m_Content != null ) + Respawn(); + } + + [Constructable] + public LibraryBookcase() : base( 0xA97 ) + { + Weight = 1.0; + } + + public LibraryBookcase( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + if ( version == 0 && m_Content == null ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( AcquireContent ) ); + } + } + + [Flipable( 0xE3D, 0xE3C )] + public class FillableLargeCrate : FillableContainer + { + [Constructable] + public FillableLargeCrate() : base( 0xE3D ) + { + Weight = 1.0; + } + + public FillableLargeCrate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + [Flipable( 0x9A9, 0xE7E )] + public class FillableSmallCrate : FillableContainer + { + [Constructable] + public FillableSmallCrate() : base( 0x9A9 ) + { + Weight = 1.0; + } + + public FillableSmallCrate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + [Flipable( 0x9AA, 0xE7D )] + public class FillableWoodenBox : FillableContainer + { + [Constructable] + public FillableWoodenBox() : base( 0x9AA ) + { + Weight = 4.0; + } + + public FillableWoodenBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x9A8, 0xE80 )] + public class FillableMetalBox : FillableContainer + { + [Constructable] + public FillableMetalBox() : base( 0x9A8 ) + { + } + + public FillableMetalBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + if ( version == 0 && Weight == 3 ) + Weight = -1; + } + } + + public class FillableBarrel : FillableContainer + { + public override bool IsLockable{ get{ return false; } } + + [Constructable] + public FillableBarrel() : base( 0xE77 ) + { + } + + public FillableBarrel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + if ( version == 0 && Weight == 25 ) + Weight = -1; + } + } + + [Flipable( 0x9AB, 0xE7C )] + public class FillableMetalChest : FillableContainer + { + [Constructable] + public FillableMetalChest() : base( 0x9AB ) + { + } + + public FillableMetalChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 25 ) + Weight = -1; + } + } + + [Flipable( 0xE41, 0xE40 )] + public class FillableMetalGoldenChest : FillableContainer + { + [Constructable] + public FillableMetalGoldenChest() : base( 0xE41 ) + { + } + + public FillableMetalGoldenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 25 ) + Weight = -1; + } + } + + [Flipable( 0xE43, 0xE42 )] + public class FillableWoodenChest : FillableContainer + { + [Constructable] + public FillableWoodenChest() : base( 0xE43 ) + { + } + + public FillableWoodenChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 2 ) + Weight = -1; + } + } + + public class FillableEntry + { + protected Type[] m_Types; + protected int m_Weight; + + public Type[] Types{ get{ return m_Types; } } + public int Weight{ get{ return m_Weight; } } + + public FillableEntry( Type type ) : this( 1, new Type[]{ type } ) + { + } + + public FillableEntry( int weight, Type type ) : this( weight, new Type[]{ type } ) + { + } + + public FillableEntry( Type[] types ) : this( 1, types ) + { + } + + public FillableEntry( int weight, Type[] types ) + { + m_Weight = weight; + m_Types = types; + } + + public FillableEntry( int weight, Type[] types, int offset, int count ) + { + m_Weight = weight; + m_Types = new Type[count]; + + for ( int i = 0; i < m_Types.Length; ++i ) + m_Types[i] = types[offset + i]; + } + + public virtual Item Construct() + { + Item item = Loot.Construct( m_Types ); + + if ( item is Key ) + ((Key)item).ItemID = Utility.RandomList( (int)KeyType.Copper, (int)KeyType.Gold, (int)KeyType.Iron, (int)KeyType.Rusty ); + else if ( item is Arrow || item is Bolt ) + item.Amount = Utility.RandomMinMax( 2, 6 ); + else if ( item is Bandage || item is Lockpick ) + item.Amount = Utility.RandomMinMax( 1, 3 ); + + return item; + } + } + + public class FillableBvrge : FillableEntry + { + private BeverageType m_Content; + + public BeverageType Content{ get{ return m_Content; } } + + public FillableBvrge( Type type, BeverageType content ) : this( 1, type, content ) + { + } + + public FillableBvrge( int weight, Type type, BeverageType content ) : base( weight, type ) + { + m_Content = content; + } + + public override Item Construct() + { + Item item; + + int index = Utility.Random( m_Types.Length ); + + if ( m_Types[index] == typeof( BeverageBottle ) ) + { + item = new BeverageBottle( m_Content ); + } + else if ( m_Types[index] == typeof( Jug ) ) + { + item = new Jug( m_Content ); + } + else + { + item = base.Construct(); + + if ( item is BaseBeverage ) + { + BaseBeverage bev = (BaseBeverage) item; + + bev.Content = m_Content; + bev.Quantity = bev.MaxQuantity; + } + } + + return item; + } + } + + public enum FillableContentType + { + None = -1, + Weaponsmith, Provisioner, Mage, + Alchemist, Armorer, ArtisanGuild, + Baker, Bard, Blacksmith, + Bowyer, Butcher, Carpenter, + Clothier, Cobbler, Docks, + Farm, FighterGuild, Guard, + Healer, Herbalist, Inn, + Jeweler, Library, Merchant, + Mill, Mine, Observatory, + Painter, Ranger, Stables, + Tanner, Tavern, ThiefGuild, + Tinker, Veterinarian + } + + public class FillableContent + { + private int m_Level; + private Type[] m_Vendors; + + private FillableEntry[] m_Entries; + private int m_Weight; + + public int Level{ get{ return m_Level; } } + public Type[] Vendors{ get{ return m_Vendors; } } + + public FillableContentType TypeID{ get{ return Lookup( this ); } } + + public FillableContent( int level, Type[] vendors, FillableEntry[] entries ) + { + m_Level = level; + m_Vendors = vendors; + m_Entries = entries; + + for ( int i = 0; i < entries.Length; ++i ) + m_Weight += entries[i].Weight; + } + + public virtual Item Construct() + { + int index = Utility.Random( m_Weight ); + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + FillableEntry entry = m_Entries[i]; + + if ( index < entry.Weight ) + return entry.Construct(); + + index -= entry.Weight; + } + + return null; + } + + public static FillableContent Alchemist = new FillableContent( + 1, + new Type[] + { + typeof( Mobiles.Alchemist ) + }, + new FillableEntry[] + { + new FillableEntry( typeof( NightSightPotion ) ), + new FillableEntry( typeof( LesserCurePotion ) ), + new FillableEntry( typeof( AgilityPotion ) ), + new FillableEntry( typeof( StrengthPotion ) ), + new FillableEntry( typeof( LesserPoisonPotion ) ), + new FillableEntry( typeof( RefreshPotion ) ), + new FillableEntry( typeof( LesserHealPotion ) ), + new FillableEntry( typeof( LesserExplosionPotion ) ), + new FillableEntry( typeof( MortarPestle ) ) + } ); + + public static FillableContent Armorer = new FillableContent( + 2, + new Type[] + { + typeof( Armorer ) + }, + new FillableEntry[] + { + new FillableEntry( 2, typeof( ChainCoif ) ), + new FillableEntry( 1, typeof( PlateGorget ) ), + new FillableEntry( 1, typeof( BronzeShield ) ), + new FillableEntry( 1, typeof( Buckler ) ), + new FillableEntry( 2, typeof( MetalKiteShield ) ), + new FillableEntry( 2, typeof( HeaterShield ) ), + new FillableEntry( 1, typeof( WoodenShield ) ), + new FillableEntry( 1, typeof( MetalShield ) ) + } ); + + public static FillableContent ArtisanGuild = new FillableContent( + 1, + new Type[] + { + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( PaintsAndBrush ) ), + new FillableEntry( 1, typeof( SledgeHammer ) ), + new FillableEntry( 2, typeof( SmithHammer ) ), + new FillableEntry( 2, typeof( Tongs ) ), + new FillableEntry( 4, typeof( Lockpick ) ), + new FillableEntry( 4, typeof( TinkerTools ) ), + new FillableEntry( 1, typeof( MalletAndChisel ) ), + new FillableEntry( 1, typeof( StatueEast2 ) ), + new FillableEntry( 1, typeof( StatueSouth ) ), + new FillableEntry( 1, typeof( StatueSouthEast ) ), + new FillableEntry( 1, typeof( StatueWest ) ), + new FillableEntry( 1, typeof( StatueNorth ) ), + new FillableEntry( 1, typeof( StatueEast ) ), + new FillableEntry( 1, typeof( BustEast ) ), + new FillableEntry( 1, typeof( BustSouth ) ), + new FillableEntry( 1, typeof( BearMask ) ), + new FillableEntry( 1, typeof( DeerMask ) ), + new FillableEntry( 4, typeof( OrcHelm ) ), + new FillableEntry( 1, typeof( TribalMask ) ), + new FillableEntry( 1, typeof( HornedTribalMask ) ) + } ); + + public static FillableContent Baker = new FillableContent( + 1, + new Type[] + { + typeof( Baker ), + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( RollingPin ) ), + new FillableEntry( 2, typeof( SackFlour ) ), + new FillableEntry( 2, typeof( BreadLoaf ) ), + new FillableEntry( 1, typeof( FrenchBread ) ) + } ); + + public static FillableContent Bard = new FillableContent( + 1, + new Type[] + { + typeof( Bard ), + typeof( BardGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( LapHarp ) ), + new FillableEntry( 2, typeof( Lute ) ), + new FillableEntry( 1, typeof( Drums ) ), + new FillableEntry( 1, typeof( Tambourine ) ), + new FillableEntry( 1, typeof( TambourineTassel ) ) + } ); + + public static FillableContent Blacksmith = new FillableContent( + 2, + new Type[] + { + typeof( Blacksmith ), + typeof( BlacksmithGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 8, typeof( SmithHammer ) ), + new FillableEntry( 8, typeof( Tongs ) ), + new FillableEntry( 8, typeof( SledgeHammer ) ), + //new FillableEntry( 8, typeof( IronOre ) ), TODO: Smaller ore + new FillableEntry( 8, typeof( IronIngot ) ), + new FillableEntry( 1, typeof( IronWire ) ), + new FillableEntry( 1, typeof( SilverWire ) ), + new FillableEntry( 1, typeof( GoldWire ) ), + new FillableEntry( 1, typeof( CopperWire ) ), + new FillableEntry( 1, typeof( HorseShoes ) ), + new FillableEntry( 1, typeof( ForgedMetal ) ) + } ); + + public static FillableContent Bowyer = new FillableContent( + 2, + new Type[] + { + typeof( Bowyer ) + }, + new FillableEntry[] + { + new FillableEntry( 2, typeof( Bow ) ), + new FillableEntry( 2, typeof( Crossbow ) ), + new FillableEntry( 1, typeof( Arrow ) ) + } ); + + public static FillableContent Butcher = new FillableContent( + 1, + new Type[] + { + typeof( Butcher ), + }, + new FillableEntry[] + { + new FillableEntry( 2, typeof( Cleaver ) ), + new FillableEntry( 2, typeof( SlabOfBacon ) ), + new FillableEntry( 2, typeof( Bacon ) ), + new FillableEntry( 1, typeof( RawFishSteak ) ), + new FillableEntry( 1, typeof( FishSteak ) ), + new FillableEntry( 2, typeof( CookedBird ) ), + new FillableEntry( 2, typeof( RawBird ) ), + new FillableEntry( 2, typeof( Ham ) ), + new FillableEntry( 1, typeof( RawLambLeg ) ), + new FillableEntry( 1, typeof( LambLeg ) ), + new FillableEntry( 1, typeof( Ribs ) ), + new FillableEntry( 1, typeof( RawRibs ) ), + new FillableEntry( 2, typeof( Sausage ) ), + new FillableEntry( 1, typeof( RawChickenLeg ) ), + new FillableEntry( 1, typeof( ChickenLeg ) ) + } ); + + public static FillableContent Carpenter = new FillableContent( + 1, + new Type[] + { + typeof( Carpenter ), + typeof( Architect ), + typeof( RealEstateBroker ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( ChiselsNorth ) ), + new FillableEntry( 1, typeof( ChiselsWest ) ), + new FillableEntry( 2, typeof( DovetailSaw ) ), + new FillableEntry( 2, typeof( Hammer ) ), + new FillableEntry( 2, typeof( MouldingPlane ) ), + new FillableEntry( 2, typeof( Nails ) ), + new FillableEntry( 2, typeof( JointingPlane ) ), + new FillableEntry( 2, typeof( SmoothingPlane ) ), + new FillableEntry( 2, typeof( Saw ) ), + new FillableEntry( 2, typeof( DrawKnife ) ), + new FillableEntry( 1, typeof( Log ) ), + new FillableEntry( 1, typeof( Froe ) ), + new FillableEntry( 1, typeof( Inshave ) ), + new FillableEntry( 1, typeof( Scorp ) ) + } ); + + public static FillableContent Clothier = new FillableContent( + 1, + new Type[] + { + typeof( Tailor ), + typeof( Weaver ), + typeof( TailorGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Cotton ) ), + new FillableEntry( 1, typeof( Wool ) ), + new FillableEntry( 1, typeof( DarkYarn ) ), + new FillableEntry( 1, typeof( LightYarn ) ), + new FillableEntry( 1, typeof( LightYarnUnraveled ) ), + new FillableEntry( 1, typeof( SpoolOfThread ) ), + // Four different types + //new FillableEntry( 1, typeof( FoldedCloth ) ), + //new FillableEntry( 1, typeof( FoldedCloth ) ), + //new FillableEntry( 1, typeof( FoldedCloth ) ), + //new FillableEntry( 1, typeof( FoldedCloth ) ), + new FillableEntry( 1, typeof( Dyes ) ), + new FillableEntry( 2, typeof( Leather ) ) + } ); + + public static FillableContent Cobbler = new FillableContent( + 1, + new Type[] + { + typeof( Cobbler ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Boots ) ), + new FillableEntry( 2, typeof( Shoes ) ), + new FillableEntry( 2, typeof( Sandals ) ), + new FillableEntry( 1, typeof( ThighBoots ) ) + } ); + + public static FillableContent Docks = new FillableContent( + 1, + new Type[] + { + typeof( Fisherman ), + typeof( FisherGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( FishingPole ) ), + // Two different types + //new FillableEntry( 1, typeof( SmallFish ) ), + //new FillableEntry( 1, typeof( SmallFish ) ), + new FillableEntry( 4, typeof( Fish ) ) + } ); + + public static FillableContent Farm = new FillableContent( + 1, + new Type[] + { + typeof( Farmer ), + typeof( Rancher ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Shirt ) ), + new FillableEntry( 1, typeof( ShortPants ) ), + new FillableEntry( 1, typeof( Skirt ) ), + new FillableEntry( 1, typeof( PlainDress ) ), + new FillableEntry( 1, typeof( Cap ) ), + new FillableEntry( 2, typeof( Sandals ) ), + new FillableEntry( 2, typeof( GnarledStaff ) ), + new FillableEntry( 2, typeof( Pitchfork ) ), + new FillableEntry( 1, typeof( Bag ) ), + new FillableEntry( 1, typeof( Kindling ) ), + new FillableEntry( 1, typeof( Lettuce ) ), + new FillableEntry( 1, typeof( Onion ) ), + new FillableEntry( 1, typeof( Turnip ) ), + new FillableEntry( 1, typeof( Ham ) ), + new FillableEntry( 1, typeof( Bacon ) ), + new FillableEntry( 1, typeof( RawLambLeg ) ), + new FillableEntry( 1, typeof( SheafOfHay ) ), + new FillableBvrge( 1, typeof( Pitcher ), BeverageType.Milk ) + } ); + + public static FillableContent FighterGuild = new FillableContent( + 3, + new Type[] + { + typeof( WarriorGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 12, Loot.ArmorTypes ), + new FillableEntry( 8, Loot.WeaponTypes ), + new FillableEntry( 3, Loot.ShieldTypes ), + new FillableEntry( 1, typeof( Arrow ) ) + } ); + + public static FillableContent Guard = new FillableContent( + 3, + new Type[] + { + }, + new FillableEntry[] + { + new FillableEntry( 12, Loot.ArmorTypes ), + new FillableEntry( 8, Loot.WeaponTypes ), + new FillableEntry( 3, Loot.ShieldTypes ), + new FillableEntry( 1, typeof( Arrow ) ) + } ); + + public static FillableContent Healer = new FillableContent( + 1, + new Type[] + { + typeof( Healer ), + typeof( HealerGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Bandage ) ), + new FillableEntry( 1, typeof( MortarPestle ) ), + new FillableEntry( 1, typeof( LesserHealPotion ) ) + } ); + + public static FillableContent Herbalist = new FillableContent( + 1, + new Type[] + { + typeof( Herbalist ) + }, + new FillableEntry[] + { + new FillableEntry( 10, typeof( Garlic ) ), + new FillableEntry( 10, typeof( Ginseng ) ), + new FillableEntry( 10, typeof( MandrakeRoot ) ), + new FillableEntry( 1, typeof( DeadWood ) ), + new FillableEntry( 1, typeof( WhiteDriedFlowers ) ), + new FillableEntry( 1, typeof( GreenDriedFlowers ) ), + new FillableEntry( 1, typeof( DriedOnions ) ), + new FillableEntry( 1, typeof( DriedHerbs ) ) + } ); + + public static FillableContent Inn = new FillableContent( + 1, + new Type[] + { + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Candle ) ), + new FillableEntry( 1, typeof( Torch ) ), + new FillableEntry( 1, typeof( Lantern ) ) + } ); + + public static FillableContent Jeweler = new FillableContent( + 2, + new Type[] + { + typeof( Jeweler ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( GoldRing ) ), + new FillableEntry( 1, typeof( GoldBracelet ) ), + new FillableEntry( 1, typeof( GoldEarrings ) ), + new FillableEntry( 1, typeof( GoldNecklace ) ), + new FillableEntry( 1, typeof( GoldBeadNecklace ) ), + new FillableEntry( 1, typeof( Necklace ) ), + new FillableEntry( 1, typeof( Beads ) ), + new FillableEntry( 9, Loot.GemTypes ) + } ); + + public static FillableContent Library = new FillableContent( + 1, + new Type[] + { + typeof( Scribe ) + }, + new FillableEntry[] + { + new FillableEntry( 8, Loot.LibraryBookTypes ), + new FillableEntry( 1, typeof( RedBook ) ), + new FillableEntry( 1, typeof( BlueBook ) ) + } ); + + public static FillableContent Mage = new FillableContent( + 2, + new Type[] + { + typeof( Mage ), + typeof( HolyMage ), + typeof( MageGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 16, typeof( BlankScroll ) ), + new FillableEntry( 14, typeof( Spellbook ) ), + new FillableEntry( 12, Loot.RegularScrollTypes, 0, 8 ), + new FillableEntry( 11, Loot.RegularScrollTypes, 8, 8 ), + new FillableEntry( 10, Loot.RegularScrollTypes, 16, 8 ), + new FillableEntry( 9, Loot.RegularScrollTypes, 24, 8 ), + new FillableEntry( 8, Loot.RegularScrollTypes, 32, 8 ), + new FillableEntry( 7, Loot.RegularScrollTypes, 40, 8 ), + new FillableEntry( 6, Loot.RegularScrollTypes, 48, 8 ), + new FillableEntry( 5, Loot.RegularScrollTypes, 56, 8 ) + } ); + + public static FillableContent Merchant = new FillableContent( + 1, + new Type[] + { + typeof( MerchantGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( CheeseWheel ) ), + new FillableEntry( 1, typeof( CheeseWedge ) ), + new FillableEntry( 1, typeof( CheeseSlice ) ), + new FillableEntry( 1, typeof( Eggs ) ), + new FillableEntry( 4, typeof( Fish ) ), + new FillableEntry( 2, typeof( RawFishSteak ) ), + new FillableEntry( 2, typeof( FishSteak ) ), + new FillableEntry( 1, typeof( Apple ) ), + new FillableEntry( 2, typeof( Banana ) ), + new FillableEntry( 2, typeof( Bananas ) ), + new FillableEntry( 2, typeof( OpenCoconut ) ), + new FillableEntry( 1, typeof( SplitCoconut ) ), + new FillableEntry( 1, typeof( Coconut ) ), + new FillableEntry( 1, typeof( Dates ) ), + new FillableEntry( 1, typeof( Grapes ) ), + new FillableEntry( 1, typeof( Lemon ) ), + new FillableEntry( 1, typeof( Lemons ) ), + new FillableEntry( 1, typeof( Lime ) ), + new FillableEntry( 1, typeof( Limes ) ), + new FillableEntry( 1, typeof( Peach ) ), + new FillableEntry( 1, typeof( Pear ) ), + new FillableEntry( 2, typeof( SlabOfBacon ) ), + new FillableEntry( 2, typeof( Bacon ) ), + new FillableEntry( 2, typeof( CookedBird ) ), + new FillableEntry( 2, typeof( RawBird ) ), + new FillableEntry( 2, typeof( Ham ) ), + new FillableEntry( 1, typeof( RawLambLeg ) ), + new FillableEntry( 1, typeof( LambLeg ) ), + new FillableEntry( 1, typeof( Ribs ) ), + new FillableEntry( 1, typeof( RawRibs ) ), + new FillableEntry( 2, typeof( Sausage ) ), + new FillableEntry( 1, typeof( RawChickenLeg ) ), + new FillableEntry( 1, typeof( ChickenLeg ) ), + new FillableEntry( 1, typeof( Watermelon ) ), + new FillableEntry( 1, typeof( SmallWatermelon ) ), + new FillableEntry( 3, typeof( Turnip ) ), + new FillableEntry( 2, typeof( YellowGourd ) ), + new FillableEntry( 2, typeof( GreenGourd ) ), + new FillableEntry( 2, typeof( Pumpkin ) ), + new FillableEntry( 1, typeof( SmallPumpkin ) ), + new FillableEntry( 2, typeof( Onion ) ), + new FillableEntry( 2, typeof( Lettuce ) ), + new FillableEntry( 2, typeof( Squash ) ), + new FillableEntry( 2, typeof( HoneydewMelon ) ), + new FillableEntry( 1, typeof( Carrot ) ), + new FillableEntry( 2, typeof( Cantaloupe ) ), + new FillableEntry( 2, typeof( Cabbage ) ), + new FillableEntry( 4, typeof( EarOfCorn ) ) + } ); + + public static FillableContent Mill = new FillableContent( + 1, + new Type[] + { + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( SackFlour ) ) + } ); + + public static FillableContent Mine = new FillableContent( + 1, + new Type[] + { + typeof( Miner ) + }, + new FillableEntry[] + { + new FillableEntry( 2, typeof( Pickaxe ) ), + new FillableEntry( 2, typeof( Shovel ) ), + new FillableEntry( 2, typeof( IronIngot ) ), + //new FillableEntry( 2, typeof( IronOre ) ), TODO: Smaller Ore + new FillableEntry( 1, typeof( ForgedMetal ) ) + } ); + + public static FillableContent Observatory = new FillableContent( + 1, + new Type[] + { + }, + new FillableEntry[] + { + new FillableEntry( 2, typeof( Sextant ) ), + new FillableEntry( 2, typeof( Clock ) ), + new FillableEntry( 1, typeof( Spyglass ) ) + } ); + + public static FillableContent Painter = new FillableContent( + 1, + new Type[] + { + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( PaintsAndBrush ) ), + new FillableEntry( 2, typeof( PenAndInk ) ) + } ); + + public static FillableContent Provisioner = new FillableContent( + 1, + new Type[] + { + typeof( Provisioner ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( CheeseWheel ) ), + new FillableEntry( 1, typeof( CheeseWedge ) ), + new FillableEntry( 1, typeof( CheeseSlice ) ), + new FillableEntry( 1, typeof( Eggs ) ), + new FillableEntry( 4, typeof( Fish ) ), + new FillableEntry( 1, typeof( DirtyFrypan ) ), + new FillableEntry( 1, typeof( DirtyPan ) ), + new FillableEntry( 1, typeof( DirtyKettle ) ), + new FillableEntry( 1, typeof( DirtySmallRoundPot ) ), + new FillableEntry( 1, typeof( DirtyRoundPot ) ), + new FillableEntry( 1, typeof( DirtySmallPot ) ), + new FillableEntry( 1, typeof( DirtyPot ) ), + new FillableEntry( 1, typeof( Apple ) ), + new FillableEntry( 2, typeof( Banana ) ), + new FillableEntry( 2, typeof( Bananas ) ), + new FillableEntry( 2, typeof( OpenCoconut ) ), + new FillableEntry( 1, typeof( SplitCoconut ) ), + new FillableEntry( 1, typeof( Coconut ) ), + new FillableEntry( 1, typeof( Dates ) ), + new FillableEntry( 1, typeof( Grapes ) ), + new FillableEntry( 1, typeof( Lemon ) ), + new FillableEntry( 1, typeof( Lemons ) ), + new FillableEntry( 1, typeof( Lime ) ), + new FillableEntry( 1, typeof( Limes ) ), + new FillableEntry( 1, typeof( Peach ) ), + new FillableEntry( 1, typeof( Pear ) ), + new FillableEntry( 2, typeof( SlabOfBacon ) ), + new FillableEntry( 2, typeof( Bacon ) ), + new FillableEntry( 1, typeof( RawFishSteak ) ), + new FillableEntry( 1, typeof( FishSteak ) ), + new FillableEntry( 2, typeof( CookedBird ) ), + new FillableEntry( 2, typeof( RawBird ) ), + new FillableEntry( 2, typeof( Ham ) ), + new FillableEntry( 1, typeof( RawLambLeg ) ), + new FillableEntry( 1, typeof( LambLeg ) ), + new FillableEntry( 1, typeof( Ribs ) ), + new FillableEntry( 1, typeof( RawRibs ) ), + new FillableEntry( 2, typeof( Sausage ) ), + new FillableEntry( 1, typeof( RawChickenLeg ) ), + new FillableEntry( 1, typeof( ChickenLeg ) ), + new FillableEntry( 1, typeof( Watermelon ) ), + new FillableEntry( 1, typeof( SmallWatermelon ) ), + new FillableEntry( 3, typeof( Turnip ) ), + new FillableEntry( 2, typeof( YellowGourd ) ), + new FillableEntry( 2, typeof( GreenGourd ) ), + new FillableEntry( 2, typeof( Pumpkin ) ), + new FillableEntry( 1, typeof( SmallPumpkin ) ), + new FillableEntry( 2, typeof( Onion ) ), + new FillableEntry( 2, typeof( Lettuce ) ), + new FillableEntry( 2, typeof( Squash ) ), + new FillableEntry( 2, typeof( HoneydewMelon ) ), + new FillableEntry( 1, typeof( Carrot ) ), + new FillableEntry( 2, typeof( Cantaloupe ) ), + new FillableEntry( 2, typeof( Cabbage ) ), + new FillableEntry( 4, typeof( EarOfCorn ) ) + } ); + + public static FillableContent Ranger = new FillableContent( + 2, + new Type[] + { + typeof( Ranger ), + typeof( RangerGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 2, typeof( StuddedChest ) ), + new FillableEntry( 2, typeof( StuddedLegs ) ), + new FillableEntry( 2, typeof( StuddedArms ) ), + new FillableEntry( 2, typeof( StuddedGloves ) ), + new FillableEntry( 1, typeof( StuddedGorget ) ), + + new FillableEntry( 2, typeof( LeatherChest ) ), + new FillableEntry( 2, typeof( LeatherLegs ) ), + new FillableEntry( 2, typeof( LeatherArms ) ), + new FillableEntry( 2, typeof( LeatherGloves ) ), + new FillableEntry( 1, typeof( LeatherGorget ) ), + + new FillableEntry( 2, typeof( FeatheredHat ) ), + new FillableEntry( 1, typeof( CloseHelm ) ), + new FillableEntry( 1, typeof( TallStrawHat ) ), + new FillableEntry( 1, typeof( Bandana ) ), + new FillableEntry( 1, typeof( Cloak ) ), + new FillableEntry( 2, typeof( Boots ) ), + new FillableEntry( 2, typeof( ThighBoots ) ), + + new FillableEntry( 2, typeof( GnarledStaff ) ), + new FillableEntry( 1, typeof( Whip ) ), + + new FillableEntry( 2, typeof( Bow ) ), + new FillableEntry( 2, typeof( Crossbow ) ), + new FillableEntry( 2, typeof( HeavyCrossbow ) ), + new FillableEntry( 4, typeof( Arrow ) ) + } ); + + public static FillableContent Stables = new FillableContent( + 1, + new Type[] + { + typeof( AnimalTrainer ), + typeof( GypsyAnimalTrainer ) + }, + new FillableEntry[] + { + //new FillableEntry( 1, typeof( Wheat ) ), + new FillableEntry( 1, typeof( Carrot ) ) + } ); + + public static FillableContent Tanner = new FillableContent( + 2, + new Type[] + { + typeof( Tanner ), + typeof( LeatherWorker ), + typeof( Furtrader ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( FeatheredHat ) ), + new FillableEntry( 1, typeof( LeatherArms ) ), + new FillableEntry( 2, typeof( LeatherLegs ) ), + new FillableEntry( 2, typeof( LeatherChest ) ), + new FillableEntry( 2, typeof( LeatherGloves ) ), + new FillableEntry( 1, typeof( LeatherGorget ) ), + new FillableEntry( 2, typeof( Leather ) ) + } ); + + public static FillableContent Tavern = new FillableContent( + 1, + new Type[] + { + typeof( TavernKeeper ), + typeof( Barkeeper ), + typeof( Waiter ), + typeof( Cook ) + }, + new FillableEntry[] + { + new FillableBvrge( 1, typeof( BeverageBottle ), BeverageType.Ale ), + new FillableBvrge( 1, typeof( BeverageBottle ), BeverageType.Wine ), + new FillableBvrge( 1, typeof( BeverageBottle ), BeverageType.Liquor ), + new FillableBvrge( 1, typeof( Jug ), BeverageType.Cider ) + } ); + + public static FillableContent ThiefGuild = new FillableContent( + 1, + new Type[] + { + typeof( Thief ), + typeof( ThiefGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Lockpick ) ), + new FillableEntry( 1, typeof( BearMask ) ), + new FillableEntry( 1, typeof( DeerMask ) ), + new FillableEntry( 1, typeof( TribalMask ) ), + new FillableEntry( 1, typeof( HornedTribalMask ) ), + new FillableEntry( 4, typeof( OrcHelm ) ) + } ); + + public static FillableContent Tinker = new FillableContent( + 1, + new Type[] + { + typeof( Tinker ), + typeof( TinkerGuildmaster ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Lockpick ) ), + //new FillableEntry( 1, typeof( KeyRing ) ), + new FillableEntry( 2, typeof( Clock ) ), + new FillableEntry( 2, typeof( ClockParts ) ), + new FillableEntry( 2, typeof( AxleGears ) ), + new FillableEntry( 2, typeof( Gears ) ), + new FillableEntry( 2, typeof( Hinge ) ), + //new FillableEntry( 1, typeof( ArrowShafts ) ), + new FillableEntry( 2, typeof( Sextant ) ), + new FillableEntry( 2, typeof( SextantParts ) ), + new FillableEntry( 2, typeof( Axle ) ), + new FillableEntry( 2, typeof( Springs ) ), + new FillableEntry( 5, typeof( TinkerTools ) ), + new FillableEntry( 4, typeof( Key ) ) + } ); + + public static FillableContent Veterinarian = new FillableContent( + 1, + new Type[] + { + typeof( Veterinarian ) + }, + new FillableEntry[] + { + new FillableEntry( 1, typeof( Bandage ) ), + new FillableEntry( 1, typeof( MortarPestle ) ), + new FillableEntry( 1, typeof( LesserHealPotion ) ), + //new FillableEntry( 1, typeof( Wheat ) ), + new FillableEntry( 1, typeof( Carrot ) ) + } ); + + public static FillableContent Weaponsmith = new FillableContent( + 2, + new Type[] + { + typeof( Weaponsmith ) + }, + new FillableEntry[] + { + new FillableEntry( 8, Loot.WeaponTypes ), + new FillableEntry( 1, typeof( Arrow ) ) + } ); + + public static FillableContent Lookup( FillableContentType type ) + { + int v = (int)type; + + if ( v >= 0 && v < m_ContentTypes.Length ) + return m_ContentTypes[v]; + + return null; + } + + public static FillableContentType Lookup( FillableContent content ) + { + if ( content == null ) + return FillableContentType.None; + + return (FillableContentType) Array.IndexOf( m_ContentTypes, content ); + } + + private static Hashtable m_AcquireTable; + + private static FillableContent[] m_ContentTypes = new FillableContent[] + { + Weaponsmith, Provisioner, Mage, + Alchemist, Armorer, ArtisanGuild, + Baker, Bard, Blacksmith, + Bowyer, Butcher, Carpenter, + Clothier, Cobbler, Docks, + Farm, FighterGuild, Guard, + Healer, Herbalist, Inn, + Jeweler, Library, Merchant, + Mill, Mine, Observatory, + Painter, Ranger, Stables, + Tanner, Tavern, ThiefGuild, + Tinker, Veterinarian + }; + + public static FillableContent Acquire( Point3D loc, Map map ) + { + if ( map == null || map == Map.Internal ) + return null; + + if ( m_AcquireTable == null ) + { + m_AcquireTable = new Hashtable(); + + for ( int i = 0; i < m_ContentTypes.Length; ++i ) + { + FillableContent fill = m_ContentTypes[i]; + + for ( int j = 0; j < fill.m_Vendors.Length; ++j ) + m_AcquireTable[fill.m_Vendors[j]] = fill; + } + } + + Mobile nearest = null; + FillableContent content = null; + + foreach ( Mobile mob in map.GetMobilesInRange( loc, 20 ) ) + { + if ( nearest != null && mob.GetDistanceToSqrt( loc ) > nearest.GetDistanceToSqrt( loc ) && !(nearest is Cobbler && mob is Provisioner) ) + continue; + + FillableContent check = m_AcquireTable[mob.GetType()] as FillableContent; + + if ( check != null ) + { + nearest = mob; + content = check; + } + } + + return content; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/FurnitureContainer.cs b/Scripts/Items/Containers/FurnitureContainer.cs new file mode 100644 index 000000000..75cc2eaef --- /dev/null +++ b/Scripts/Items/Containers/FurnitureContainer.cs @@ -0,0 +1,428 @@ +using System; +using System.Collections; +using Server; +using Server.Multis; +using Server.Network; + +namespace Server.Items +{ + [Furniture] + [Flipable( 0x2815, 0x2816 )] + public class TallCabinet : BaseContainer + { + [Constructable] + public TallCabinet() : base( 0x2815 ) + { + Weight = 1.0; + } + + public TallCabinet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0x2817, 0x2818 )] + public class ShortCabinet : BaseContainer + { + [Constructable] + public ShortCabinet() : base( 0x2817 ) + { + Weight = 1.0; + } + + public ShortCabinet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + + [Furniture] + [Flipable( 0x2857, 0x2858 )] + public class RedArmoire : BaseContainer + { + [Constructable] + public RedArmoire() : base( 0x2857 ) + { + Weight = 1.0; + } + + public RedArmoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0x285D, 0x285E )] + public class CherryArmoire : BaseContainer + { + [Constructable] + public CherryArmoire() : base( 0x285D ) + { + Weight = 1.0; + } + + public CherryArmoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0x285B, 0x285C )] + public class MapleArmoire : BaseContainer + { + [Constructable] + public MapleArmoire() : base( 0x285B ) + { + Weight = 1.0; + } + + public MapleArmoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0x2859, 0x285A )] + public class ElegantArmoire : BaseContainer + { + [Constructable] + public ElegantArmoire() : base( 0x2859 ) + { + Weight = 1.0; + } + + public ElegantArmoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0xa97, 0xa99, 0xa98, 0xa9a, 0xa9b, 0xa9c )] + public class FullBookcase : BaseContainer + { + [Constructable] + public FullBookcase() : base( 0xA97 ) + { + Weight = 1.0; + } + + public FullBookcase( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0xa9d, 0xa9e )] + public class EmptyBookcase : BaseContainer + { + [Constructable] + public EmptyBookcase() : base( 0xA9D ) + { + } + + public EmptyBookcase( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 1.0 ) + Weight = -1; + } + } + + [Furniture] + [Flipable( 0xa2c, 0xa34 )] + public class Drawer : BaseContainer + { + [Constructable] + public Drawer() : base( 0xA2C ) + { + Weight = 1.0; + } + + public Drawer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0xa30, 0xa38 )] + public class FancyDrawer : BaseContainer + { + [Constructable] + public FancyDrawer() : base( 0xA30 ) + { + Weight = 1.0; + } + + public FancyDrawer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [Furniture] + [Flipable( 0xa4f, 0xa53 )] + public class Armoire : BaseContainer + { + [Constructable] + public Armoire() : base( 0xA4F ) + { + Weight = 1.0; + } + + public override void DisplayTo( Mobile m ) + { + if ( DynamicFurniture.Open( this, m ) ) + base.DisplayTo( m ); + } + + public Armoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + DynamicFurniture.Close( this ); + } + } + + [Furniture] + [Flipable( 0xa4d, 0xa51 )] + public class FancyArmoire : BaseContainer + { + [Constructable] + public FancyArmoire() : base( 0xA4D ) + { + Weight = 1.0; + } + + public override void DisplayTo( Mobile m ) + { + if ( DynamicFurniture.Open( this, m ) ) + base.DisplayTo( m ); + } + + public FancyArmoire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + DynamicFurniture.Close( this ); + } + } + + public class DynamicFurniture + { + private static Hashtable m_Table = new Hashtable(); + + public static bool Open( Container c, Mobile m ) + { + if ( m_Table.Contains( c ) ) + { + c.SendRemovePacket(); + Close( c ); + c.Delta( ItemDelta.Update ); + c.ProcessDelta(); + return false; + } + + if ( c is Armoire || c is FancyArmoire ) + { + Timer t = new FurnitureTimer( c, m ); + t.Start(); + m_Table[c] = t; + + switch ( c.ItemID ) + { + case 0xA4D: c.ItemID = 0xA4C; break; + case 0xA4F: c.ItemID = 0xA4E; break; + case 0xA51: c.ItemID = 0xA50; break; + case 0xA53: c.ItemID = 0xA52; break; + } + } + + return true; + } + + public static void Close( Container c ) + { + Timer t = (Timer)m_Table[c]; + + if ( t != null ) + { + t.Stop(); + m_Table.Remove( c ); + } + + if ( c is Armoire || c is FancyArmoire ) + { + switch ( c.ItemID ) + { + case 0xA4C: c.ItemID = 0xA4D; break; + case 0xA4E: c.ItemID = 0xA4F; break; + case 0xA50: c.ItemID = 0xA51; break; + case 0xA52: c.ItemID = 0xA53; break; + } + } + } + } + + public class FurnitureTimer : Timer + { + private Container m_Container; + private Mobile m_Mobile; + + public FurnitureTimer( Container c, Mobile m ) : base( TimeSpan.FromSeconds( 0.5 ), TimeSpan.FromSeconds( 0.5 ) ) + { + Priority = TimerPriority.TwoFiftyMS; + + m_Container = c; + m_Mobile = m; + } + + protected override void OnTick() + { + if ( m_Mobile.Map != m_Container.Map || !m_Mobile.InRange( m_Container.GetWorldLocation(), 3 ) ) + DynamicFurniture.Close( m_Container ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/LockableContainer.cs b/Scripts/Items/Containers/LockableContainer.cs new file mode 100644 index 000000000..1bca76852 --- /dev/null +++ b/Scripts/Items/Containers/LockableContainer.cs @@ -0,0 +1,376 @@ +using System; +using Server.Network; +using Server.Items; +using Server.Engines.Craft; + +namespace Server.Items +{ + public abstract class LockableContainer : TrapableContainer, ILockable, ILockpickable, ICraftable//, ITelekinesisable + { + private bool m_Locked; + private int m_LockLevel, m_MaxLockLevel, m_RequiredSkill; + private uint m_KeyValue; + private Mobile m_Picker; + private bool m_TrapOnLockpick; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Picker + { + get + { + return m_Picker; + } + set + { + m_Picker = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxLockLevel + { + get + { + return m_MaxLockLevel; + } + set + { + m_MaxLockLevel = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int LockLevel + { + get + { + return m_LockLevel; + } + set + { + m_LockLevel = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RequiredSkill + { + get + { + return m_RequiredSkill; + } + set + { + m_RequiredSkill = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual bool Locked + { + get + { + return m_Locked; + } + set + { + m_Locked = value; + + if ( m_Locked ) + m_Picker = null; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public uint KeyValue + { + get + { + return m_KeyValue; + } + set + { + m_KeyValue = value; + } + } + + public override bool TrapOnOpen + { + get + { + return !m_TrapOnLockpick; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool TrapOnLockpick + { + get + { + return m_TrapOnLockpick; + } + set + { + m_TrapOnLockpick = value; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 5 ); // version + + writer.Write( (bool) m_TrapOnLockpick ); + + writer.Write( (int) m_RequiredSkill ); + + writer.Write( (int) m_MaxLockLevel ); + + writer.Write( m_KeyValue ); + writer.Write( (int) m_LockLevel ); + writer.Write( (bool) m_Locked ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 5: + { + m_TrapOnLockpick = reader.ReadBool(); + + goto case 4; + } + case 4: + { + m_RequiredSkill = reader.ReadInt(); + + goto case 3; + } + case 3: + { + m_MaxLockLevel = reader.ReadInt(); + + goto case 2; + } + case 2: + { + m_KeyValue = reader.ReadUInt(); + + goto case 1; + } + case 1: + { + m_LockLevel = reader.ReadInt(); + + goto case 0; + } + case 0: + { + if ( version < 3 ) + m_MaxLockLevel = 100; + + if ( version < 4 ) + { + if ( (m_MaxLockLevel - m_LockLevel) == 40 ) + { + m_RequiredSkill = m_LockLevel + 6; + m_LockLevel = m_RequiredSkill - 10; + m_MaxLockLevel = m_RequiredSkill + 39; + } + else + { + m_RequiredSkill = m_LockLevel; + } + } + + m_Locked = reader.ReadBool(); + + break; + } + } + } + + public LockableContainer( int itemID ) : base( itemID ) + { + m_MaxLockLevel = 100; + } + + public LockableContainer( Serial serial ) : base( serial ) + { + } + + public override bool CheckContentDisplay( Mobile from ) + { + return !m_Locked && base.CheckContentDisplay( from ); + } + + public override bool TryDropItem( Mobile from, Item dropped, bool sendFullMessage ) + { + if ( from.AccessLevel < AccessLevel.GameMaster && m_Locked ) + { + from.SendLocalizedMessage( 501747 ); // It appears to be locked. + return false; + } + + return base.TryDropItem( from, dropped, sendFullMessage ); + } + + public override bool OnDragDropInto( Mobile from, Item item, Point3D p ) + { + if ( from.AccessLevel < AccessLevel.GameMaster && m_Locked ) + { + from.SendLocalizedMessage( 501747 ); // It appears to be locked. + return false; + } + + return base.OnDragDropInto( from, item, p ); + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( !base.CheckLift( from, item, ref reject ) ) + return false; + + if ( item != this && from.AccessLevel < AccessLevel.GameMaster && m_Locked ) + return false; + + return true; + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + if ( !base.CheckItemUse( from, item ) ) + return false; + + if ( item != this && from.AccessLevel < AccessLevel.GameMaster && m_Locked ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return false; + } + + return true; + } + + public override bool DisplaysContent{ get{ return !m_Locked; } } + + public virtual bool CheckLocked( Mobile from ) + { + bool inaccessible = false; + + if ( m_Locked ) + { + int number; + + if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + number = 502502; // That is locked, but you open it with your godly powers. + } + else + { + number = 501747; // It appears to be locked. + inaccessible = true; + } + + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", "" ) ); + } + + return inaccessible; + } + + public override void OnTelekinesis( Mobile from ) + { + if ( CheckLocked( from ) ) + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x376A, 9, 32, 5022 ); + Effects.PlaySound( Location, Map, 0x1F5 ); + return; + } + + base.OnTelekinesis( from ); + } + + public override void OnDoubleClickSecureTrade( Mobile from ) + { + if ( CheckLocked( from ) ) + return; + + base.OnDoubleClickSecureTrade( from ); + } + + public override void Open( Mobile from ) + { + if ( CheckLocked( from ) ) + return; + + base.Open( from ); + } + + public override void OnSnoop( Mobile from ) + { + if ( CheckLocked( from ) ) + return; + + base.OnSnoop( from ); + } + + public virtual void LockPick( Mobile from ) + { + Locked = false; + Picker = from; + + if ( this.TrapOnLockpick && ExecuteTrap( from ) ) + { + this.TrapOnLockpick = false; + } + } + + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + if ( from.CheckSkill( SkillName.Tinkering, -5.0, 15.0 ) ) + { + from.SendLocalizedMessage( 500636 ); // Your tinker skill was sufficient to make the item lockable. + + Key key = new Key( KeyType.Copper, Key.RandomValue() ); + + KeyValue = key.KeyValue; + DropItem( key ); + + double tinkering = from.Skills[SkillName.Tinkering].Value; + int level = (int)(tinkering * 0.8); + + RequiredSkill = level - 4; + LockLevel = level - 14; + MaxLockLevel = level + 35; + + if ( LockLevel == 0 ) + LockLevel = -1; + else if ( LockLevel > 95 ) + LockLevel = 95; + + if ( RequiredSkill > 95 ) + RequiredSkill = 95; + + if ( MaxLockLevel > 95 ) + MaxLockLevel = 95; + } + else + { + from.SendLocalizedMessage( 500637 ); // Your tinker skill was insufficient to make the item lockable. + } + + return 1; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/MarkContainer.cs b/Scripts/Items/Containers/MarkContainer.cs new file mode 100644 index 000000000..e152e0d6b --- /dev/null +++ b/Scripts/Items/Containers/MarkContainer.cs @@ -0,0 +1,267 @@ +using System; +using Server; +using Server.Commands; + +namespace Server.Items +{ + public class MarkContainer : LockableContainer + { + public static void Initialize() + { + CommandSystem.Register( "SecretLocGen", AccessLevel.Administrator, new CommandEventHandler( SecretLocGen_OnCommand ) ); + } + + [Usage( "SecretLocGen" )] + [Description( "Generates mark containers to Malas secret locations." )] + public static void SecretLocGen_OnCommand( CommandEventArgs e ) + { + CreateMalasPassage( 951, 546, -70, 1006, 994, -70, false, false ); + CreateMalasPassage( 914, 192, -79, 1019, 1062, -70, false, false ); + CreateMalasPassage( 1614, 143, -90, 1214, 1313, -90, false, false ); + CreateMalasPassage( 2176, 324, -90, 1554, 172, -90, false, false ); + CreateMalasPassage( 864, 812, -90, 1061, 1161, -70, false, false ); + CreateMalasPassage( 1051, 1434, -85, 1076, 1244, -70, false, true ); + CreateMalasPassage( 1326, 523, -87, 1201, 1554, -70, false, false ); + CreateMalasPassage( 424, 189, -1, 2333, 1501, -90, true, false ); + CreateMalasPassage( 1313, 1115, -85, 1183, 462, -45, false, false ); + + e.Mobile.SendMessage( "Secret mark containers have been created." ); + } + + private static bool FindMarkContainer( Point3D p, Map map ) + { + IPooledEnumerable eable = map.GetItemsInRange( p, 0 ); + + foreach ( Item item in eable ) + { + if ( item.Z == p.Z && item is MarkContainer ) + { + eable.Free(); + return true; + } + } + + eable.Free(); + return false; + } + + private static void CreateMalasPassage( int x, int y, int z, int xTarget, int yTarget, int zTarget, bool bone, bool locked ) + { + Point3D location = new Point3D( x, y, z ); + + if ( FindMarkContainer( location, Map.Malas ) ) + return; + + MarkContainer cont = new MarkContainer( bone, locked ); + cont.TargetMap = Map.Malas; + cont.Target = new Point3D( xTarget, yTarget, zTarget ); + cont.Description = "strange location"; + + cont.MoveToWorld( location, Map.Malas ); + } + + private bool m_AutoLock; + private InternalTimer m_RelockTimer; + private Map m_TargetMap; + private Point3D m_Target; + private string m_Description; + + [CommandProperty( AccessLevel.GameMaster )] + public bool AutoLock + { + get { return m_AutoLock; } + set + { + m_AutoLock = value; + + if ( !m_AutoLock ) + StopTimer(); + else if ( !Locked && m_RelockTimer == null ) + m_RelockTimer = new InternalTimer( this ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map TargetMap { get { return m_TargetMap; } set { m_TargetMap = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Target { get { return m_Target; } set { m_Target = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Bone + { + get { return ItemID == 0xECA; } + set + { + ItemID = value ? 0xECA : 0xE79; + Hue = value ? 1102 : 0; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Description { get { return m_Description; } set { m_Description = value; } } + + public override bool IsDecoContainer + { + get{ return false; } + } + + [Constructable] + public MarkContainer() : this( false ) + { + } + + [Constructable] + public MarkContainer( bool bone ) : this( bone, false ) + { + } + + [Constructable] + public MarkContainer( bool bone, bool locked ) : base( bone ? 0xECA : 0xE79 ) + { + Movable = false; + + if ( bone ) + Hue = 1102; + + m_AutoLock = locked; + Locked = locked; + + if ( locked ) + LockLevel = -255; + } + + public MarkContainer( Serial serial ) : base( serial ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public override bool Locked + { + get { return base.Locked; } + set + { + base.Locked = value; + + if ( m_AutoLock ) + { + StopTimer(); + + if ( !Locked ) + m_RelockTimer = new InternalTimer( this ); + } + } + } + + public void StopTimer() + { + if ( m_RelockTimer != null ) + m_RelockTimer.Stop(); + + m_RelockTimer = null; + } + + private class InternalTimer : Timer + { + private MarkContainer m_Container; + private DateTime m_RelockTime; + + public MarkContainer Container { get { return m_Container; } } + public DateTime RelockTime { get { return m_RelockTime; } } + + public InternalTimer( MarkContainer container ) : this( container, TimeSpan.FromMinutes( 5.0 ) ) + { + } + + public InternalTimer( MarkContainer container, TimeSpan delay ) : base( delay ) + { + m_Container = container; + m_RelockTime = DateTime.Now + delay; + + Start(); + } + + protected override void OnTick() + { + m_Container.Locked = true; + m_Container.LockLevel = -255; + } + } + + public void Mark( RecallRune rune ) + { + if ( TargetMap != null ) + { + rune.Marked = true; + rune.TargetMap = m_TargetMap; + rune.Target = m_Target; + rune.Description = m_Description; + rune.House = null; + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + RecallRune rune = dropped as RecallRune; + + if ( rune != null && base.OnDragDrop( from, dropped ) ) + { + Mark( rune ); + + return true; + } + else + { + return false; + } + } + + public override bool OnDragDropInto( Mobile from, Item dropped, Point3D p ) + { + RecallRune rune = dropped as RecallRune; + + if ( rune != null && base.OnDragDropInto( from, dropped, p ) ) + { + Mark( rune ); + + return true; + } + else + { + return false; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( m_AutoLock ); + + if ( !Locked && m_AutoLock ) + writer.WriteDeltaTime( m_RelockTimer.RelockTime ); + + writer.Write( m_TargetMap ); + writer.Write( m_Target ); + writer.Write( m_Description ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_AutoLock = reader.ReadBool(); + + if ( !Locked && m_AutoLock ) + m_RelockTimer = new InternalTimer( this, reader.ReadDeltaTime() - DateTime.Now ); + + m_TargetMap = reader.ReadMap(); + m_Target = reader.ReadPoint3D(); + m_Description = reader.ReadString(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/ParagonChest.cs b/Scripts/Items/Containers/ParagonChest.cs new file mode 100644 index 000000000..54559e98c --- /dev/null +++ b/Scripts/Items/Containers/ParagonChest.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Network; +using Server.ContextMenus; +using Server.Engines.PartySystem; + +namespace Server.Items +{ + public class ParagonChest : LockableContainer + { + public override int LabelNumber{ get{ return 3000541; } } + + private static int[] m_ItemIDs = new int[] + { + 0x9AB, 0xE40, 0xE41, 0xE7C + }; + + private static int[] m_Hues = new int[] + { + 0x0, 0x455, 0x47E, 0x89F, 0x8A5, 0x8AB, + 0x966, 0x96D, 0x972, 0x973, 0x979, + }; + + private string m_Name; + + [Constructable] + public ParagonChest( string name, int level ) : base( Utility.RandomList( m_ItemIDs ) ) + { + m_Name = name; + Hue = Utility.RandomList( m_Hues ); + Fill( level ); + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, 1063449, m_Name ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1063449, m_Name ); + } + + private static void GetRandomAOSStats( out int attributeCount, out int min, out int max ) + { + int rnd = Utility.Random( 15 ); + + if ( rnd < 1 ) + { + attributeCount = Utility.RandomMinMax( 2, 6 ); + min = 20; max = 70; + } + else if ( rnd < 3 ) + { + attributeCount = Utility.RandomMinMax( 2, 4 ); + min = 20; max = 50; + } + else if ( rnd < 6 ) + { + attributeCount = Utility.RandomMinMax( 2, 3 ); + min = 20; max = 40; + } + else if ( rnd < 10 ) + { + attributeCount = Utility.RandomMinMax( 1, 2 ); + min = 10; max = 30; + } + else + { + attributeCount = 1; + min = 10; max = 20; + } + } + + private void Fill( int level ) + { + TrapType = TrapType.ExplosionTrap; + TrapPower = level * 25; + TrapLevel = level; + Locked = true; + + switch ( level ) + { + case 1: RequiredSkill = 36; break; + case 2: RequiredSkill = 76; break; + case 3: RequiredSkill = 84; break; + case 4: RequiredSkill = 92; break; + case 5: RequiredSkill = 100; break; + } + + LockLevel = RequiredSkill - 10; + MaxLockLevel = RequiredSkill + 40; + + DropItem( new Gold( level * 200 ) ); + + for ( int i = 0; i < level; ++i ) + DropItem( Loot.RandomScroll( 0, 63, SpellbookType.Regular ) ); + + for ( int i = 0; i < level * 2; ++i ) + { + Item item; + + if ( Core.AOS ) + item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(); + else + item = Loot.RandomArmorOrShieldOrWeapon(); + + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( Core.AOS ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( weapon, attributeCount, min, max ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random( 6 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random( 6 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random( 6 ); + } + + DropItem( item ); + } + else if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( Core.AOS ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( armor, attributeCount, min, max ); + } + else + { + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random( 6 ); + armor.Durability = (ArmorDurabilityLevel)Utility.Random( 6 ); + } + + DropItem( item ); + } + else if ( item is BaseJewel ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, attributeCount, min, max ); + + DropItem( item ); + } + } + + for ( int i = 0; i < level; i++ ) + { + Item item = Loot.RandomPossibleReagent(); + item.Amount = Utility.RandomMinMax( 40, 60 ); + DropItem( item ); + } + + for ( int i = 0; i < level; i++ ) + { + Item item = Loot.RandomGem(); + DropItem( item ); + } + + DropItem( new TreasureMap( level + 1, ( Utility.RandomBool() ? Map.Felucca : Map.Trammel ) ) ); + } + + public ParagonChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Name ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Name = Utility.Intern( reader.ReadString() ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/Strongbox.cs b/Scripts/Items/Containers/Strongbox.cs new file mode 100644 index 000000000..5d1a372e6 --- /dev/null +++ b/Scripts/Items/Containers/Strongbox.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections; +using Server; +using Server.Multis; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xe80, 0x9a8 )] + public class StrongBox : BaseContainer, IChopable + { + private Mobile m_Owner; + private BaseHouse m_House; + + public StrongBox( Mobile owner, BaseHouse house ) : base( 0xE80 ) + { + m_Owner = owner; + m_House = house; + + MaxItems = 25; + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get + { + return m_Owner; + } + set + { + m_Owner = value; + InvalidateProperties(); + } + } + + public override int DefaultMaxWeight{ get{ return 0; } } + + public StrongBox( Serial serial ) : base(serial) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Owner ); + writer.Write( m_House ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Owner = reader.ReadMobile(); + m_House = reader.ReadItem() as BaseHouse; + + break; + } + } + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerCallback( Validate ) ); + } + + private void Validate() + { + if ( m_Owner != null && m_House != null && !m_House.IsCoOwner( m_Owner ) ) + { + Console.WriteLine( "Warning: Destroying strongbox of {0}", m_Owner.Name ); + Destroy(); + } + } + + public override bool Decays + { + get + { + if ( m_House != null && m_Owner != null && !m_Owner.Deleted ) + return !m_House.IsCoOwner( m_Owner ); + else + return true; + } + } + + public override TimeSpan DecayTime + { + get + { + return TimeSpan.FromMinutes( 30.0 ); + } + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( m_Owner != null ) + list.Add( 1042887, m_Owner.Name ); // a strong box owned by ~1_OWNER_NAME~ + else + base.AddNameProperty( list ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Owner != null ) + { + LabelTo( from, 1042887, m_Owner.Name ); // a strong box owned by ~1_OWNER_NAME~ + + if ( CheckContentDisplay( from ) ) + LabelTo( from, "({0} items, {1} stones)", TotalItems, TotalWeight ); + } + else + { + base.OnSingleClick( from ); + } + } + + public override bool IsAccessibleTo( Mobile m ) + { + if ( m_Owner == null || m_Owner.Deleted || m_House == null || m_House.Deleted || m.AccessLevel >= AccessLevel.GameMaster ) + return true; + + return m == m_Owner && m_House.IsCoOwner( m ) && base.IsAccessibleTo( m ); + } + + private void Chop( Mobile from ) + { + Effects.PlaySound( Location, Map, 0x3B3 ); + from.SendLocalizedMessage( 500461 ); // You destroy the item. + Destroy(); + } + + public void OnChop( Mobile from ) + { + if ( m_House != null && !m_House.Deleted && m_Owner != null && !m_Owner.Deleted ) + { + if ( from == m_Owner || m_House.IsOwner( from ) ) + Chop( from ); + } + else + { + Chop( from ); + } + } + + public Container ConvertToStandardContainer() + { + Container metalBox = new MetalBox(); + ArrayList subItems = new ArrayList( Items ); + + foreach ( Item subItem in subItems ) + { + metalBox.AddItem( subItem ); + } + + this.Delete(); + + return metalBox; + } + } +} diff --git a/Scripts/Items/Containers/TrapableContainer.cs b/Scripts/Items/Containers/TrapableContainer.cs new file mode 100644 index 000000000..464e70539 --- /dev/null +++ b/Scripts/Items/Containers/TrapableContainer.cs @@ -0,0 +1,258 @@ +using System; + +namespace Server.Items +{ + public enum TrapType + { + None, + MagicTrap, + ExplosionTrap, + DartTrap, + PoisonTrap + } + + public abstract class TrapableContainer : BaseContainer, ITelekinesisable + { + private TrapType m_TrapType; + private int m_TrapPower; + private int m_TrapLevel; + + [CommandProperty( AccessLevel.GameMaster )] + public TrapType TrapType + { + get + { + return m_TrapType; + } + set + { + m_TrapType = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TrapPower + { + get + { + return m_TrapPower; + } + set + { + m_TrapPower = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TrapLevel + { + get + { + return m_TrapLevel; + } + set + { + m_TrapLevel = value; + } + } + + public virtual bool TrapOnOpen{ get{ return true; } } + + public TrapableContainer( int itemID ) : base( itemID ) + { + } + + public TrapableContainer( Serial serial ) : base( serial ) + { + } + + private void SendMessageTo( Mobile to, int number, int hue ) + { + if ( Deleted || !to.CanSee( this ) ) + return; + + to.Send( new Network.MessageLocalized( Serial, ItemID, Network.MessageType.Regular, hue, 3, number, "", "" ) ); + } + + private void SendMessageTo( Mobile to, string text, int hue ) + { + if ( Deleted || !to.CanSee( this ) ) + return; + + to.Send( new Network.UnicodeMessage( Serial, ItemID, Network.MessageType.Regular, hue, 3, "ENU", "", text ) ); + } + + public virtual bool ExecuteTrap( Mobile from ) + { + if ( m_TrapType != TrapType.None ) + { + Point3D loc = this.GetWorldLocation(); + Map facet = this.Map; + + if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + SendMessageTo( from, "That is trapped, but you open it with your godly powers.", 0x3B2 ); + return false; + } + + switch ( m_TrapType ) + { + case TrapType.ExplosionTrap: + { + SendMessageTo( from, 502999, 0x3B2 ); // You set off a trap! + + if ( from.InRange( loc, 3 ) ) + { + int damage; + + if ( m_TrapLevel > 0 ) + damage = Utility.RandomMinMax( 10, 30 ) * m_TrapLevel; + else + damage = m_TrapPower; + + AOS.Damage( from, damage, 0, 100, 0, 0, 0 ); + + // Your skin blisters from the heat! + from.LocalOverheadMessage( Network.MessageType.Regular, 0x2A, 503000 ); + } + + Effects.SendLocationEffect( loc, facet, 0x36BD, 15, 10 ); + Effects.PlaySound( loc, facet, 0x307 ); + + break; + } + case TrapType.MagicTrap: + { + if ( from.InRange( loc, 1 ) ) + from.Damage( m_TrapPower ); + //AOS.Damage( from, m_TrapPower, 0, 100, 0, 0, 0 ); + + Effects.PlaySound( loc, Map, 0x307 ); + + Effects.SendLocationEffect( new Point3D( loc.X - 1, loc.Y, loc.Z ), Map, 0x36BD, 15 ); + Effects.SendLocationEffect( new Point3D( loc.X + 1, loc.Y, loc.Z ), Map, 0x36BD, 15 ); + + Effects.SendLocationEffect( new Point3D( loc.X, loc.Y - 1, loc.Z ), Map, 0x36BD, 15 ); + Effects.SendLocationEffect( new Point3D( loc.X, loc.Y + 1, loc.Z ), Map, 0x36BD, 15 ); + + Effects.SendLocationEffect( new Point3D( loc.X + 1, loc.Y + 1, loc.Z + 11 ), Map, 0x36BD, 15 ); + + break; + } + case TrapType.DartTrap: + { + SendMessageTo( from, 502999, 0x3B2 ); // You set off a trap! + + if ( from.InRange( loc, 3 ) ) + { + int damage; + + if ( m_TrapLevel > 0 ) + damage = Utility.RandomMinMax( 5, 15 ) * m_TrapLevel; + else + damage = m_TrapPower; + + AOS.Damage( from, damage, 100, 0, 0, 0, 0 ); + + // A dart imbeds itself in your flesh! + from.LocalOverheadMessage( Network.MessageType.Regular, 0x62, 502998 ); + } + + Effects.PlaySound( loc, facet, 0x223 ); + + break; + } + case TrapType.PoisonTrap: + { + SendMessageTo( from, 502999, 0x3B2 ); // You set off a trap! + + if ( from.InRange( loc, 3 ) ) + { + Poison poison; + + if ( m_TrapLevel > 0 ) + { + poison = Poison.GetPoison( Math.Max( 0, Math.Min( 4, m_TrapLevel - 1 ) ) ); + } + else + { + AOS.Damage( from, m_TrapPower, 0, 0, 0, 100, 0 ); + poison = Poison.Greater; + } + + from.ApplyPoison( from, poison ); + + // You are enveloped in a noxious green cloud! + from.LocalOverheadMessage( Network.MessageType.Regular, 0x44, 503004 ); + } + + Effects.SendLocationEffect( loc, facet, 0x113A, 10, 20 ); + Effects.PlaySound( loc, facet, 0x231 ); + + break; + } + } + + m_TrapType = TrapType.None; + m_TrapPower = 0; + m_TrapLevel = 0; + return true; + } + + return false; + } + + public virtual void OnTelekinesis( Mobile from ) + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x376A, 9, 32, 5022 ); + Effects.PlaySound( Location, Map, 0x1F5 ); + + if ( !this.TrapOnOpen || !ExecuteTrap( from ) ) + base.DisplayTo( from ); + } + + public override void Open( Mobile from ) + { + if ( !this.TrapOnOpen || !ExecuteTrap( from ) ) + base.Open( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (int) m_TrapLevel ); + + writer.Write( (int) m_TrapPower ); + writer.Write( (int) m_TrapType ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_TrapLevel = reader.ReadInt(); + goto case 1; + } + case 1: + { + m_TrapPower = reader.ReadInt(); + goto case 0; + } + case 0: + { + m_TrapType = (TrapType)reader.ReadInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/TreasureChest.cs b/Scripts/Items/Containers/TreasureChest.cs new file mode 100644 index 000000000..87b83d48b --- /dev/null +++ b/Scripts/Items/Containers/TreasureChest.cs @@ -0,0 +1,89 @@ +using Server; +using Server.Items; +using Server.Multis; +using Server.Network; +using System; + +namespace Server.Items +{ + [FlipableAttribute( 0xe43, 0xe42 )] + public class WoodenTreasureChest : BaseTreasureChest + { + [Constructable] + public WoodenTreasureChest() : base( 0xE43 ) + { + } + + public WoodenTreasureChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xe41, 0xe40 )] + public class MetalGoldenTreasureChest : BaseTreasureChest + { + [Constructable] + public MetalGoldenTreasureChest() : base( 0xE41 ) + { + } + + public MetalGoldenTreasureChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x9ab, 0xe7c )] + public class MetalTreasureChest : BaseTreasureChest + { + [Constructable] + public MetalTreasureChest() : base( 0x9AB ) + { + } + + public MetalTreasureChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Containers/TreasureMapChest.cs b/Scripts/Items/Containers/TreasureMapChest.cs new file mode 100644 index 000000000..5c781259d --- /dev/null +++ b/Scripts/Items/Containers/TreasureMapChest.cs @@ -0,0 +1,508 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Network; +using Server.ContextMenus; +using Server.Engines.PartySystem; + +namespace Server.Items +{ + public class TreasureMapChest : LockableContainer + { + public override int LabelNumber{ get{ return 3000541; } } + + public static Type[] Artifacts { get { return m_Artifacts; } } + + private static Type[] m_Artifacts = new Type[] + { + typeof( CandelabraOfSouls ), typeof( GoldBricks ), typeof( PhillipsWoodenSteed ), + typeof( ArcticDeathDealer ), typeof( BlazeOfDeath ), typeof( BurglarsBandana ), + typeof( CavortingClub ), typeof( DreadPirateHat ), + typeof( EnchantedTitanLegBone ), typeof( GwennosHarp ), typeof( IolosLute ), + typeof( LunaLance ), typeof( NightsKiss ), typeof( NoxRangersHeavyCrossbow ), + typeof( PolarBearMask ), typeof( VioletCourage ), typeof( HeartOfTheLion ), + typeof( ColdBlood ), typeof( AlchemistsBauble ) + }; + + private int m_Level; + private DateTime m_DeleteTime; + private Timer m_Timer; + private Mobile m_Owner; + private bool m_Temporary; + + private ArrayList m_Guardians; + + [CommandProperty( AccessLevel.GameMaster )] + public int Level{ get{ return m_Level; } set{ m_Level = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner{ get{ return m_Owner; } set{ m_Owner = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime DeleteTime{ get{ return m_DeleteTime; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Temporary{ get{ return m_Temporary; } set{ m_Temporary = value; } } + + public ArrayList Guardians{ get{ return m_Guardians; } } + + [Constructable] + public TreasureMapChest( int level ) : this( null, level, false ) + { + } + + public TreasureMapChest( Mobile owner, int level, bool temporary ) : base( 0xE40 ) + { + m_Owner = owner; + m_Level = level; + m_DeleteTime = DateTime.Now + TimeSpan.FromHours( 3.0 ); + + m_Temporary = temporary; + m_Guardians = new ArrayList(); + + m_Timer = new DeleteTimer( this, m_DeleteTime ); + m_Timer.Start(); + + Fill( this, level ); + } + + private static void GetRandomAOSStats( out int attributeCount, out int min, out int max ) + { + int rnd = Utility.Random( 15 ); + + if ( rnd < 1 ) + { + attributeCount = Utility.RandomMinMax( 2, 6 ); + min = 20; max = 70; + } + else if ( rnd < 3 ) + { + attributeCount = Utility.RandomMinMax( 2, 4 ); + min = 20; max = 50; + } + else if ( rnd < 6 ) + { + attributeCount = Utility.RandomMinMax( 2, 3 ); + min = 20; max = 40; + } + else if ( rnd < 10 ) + { + attributeCount = Utility.RandomMinMax( 1, 2 ); + min = 10; max = 30; + } + else + { + attributeCount = 1; + min = 10; max = 20; + } + } + + public static void Fill( LockableContainer cont, int level ) + { + cont.Movable = false; + cont.Locked = true; + + if ( level == 0 ) + { + cont.LockLevel = 0; // Can't be unlocked + + cont.DropItem( new Gold( Utility.RandomMinMax( 50, 100 ) ) ); + + if ( Utility.RandomDouble() < 0.75 ) + cont.DropItem( new TreasureMap( 0, Map.Trammel ) ); + } + else + { + cont.TrapType = TrapType.ExplosionTrap; + cont.TrapPower = level * 25; + cont.TrapLevel = level; + + switch ( level ) + { + case 1: cont.RequiredSkill = 36; break; + case 2: cont.RequiredSkill = 76; break; + case 3: cont.RequiredSkill = 84; break; + case 4: cont.RequiredSkill = 92; break; + case 5: cont.RequiredSkill = 100; break; + case 6: cont.RequiredSkill = 100; break; + } + + cont.LockLevel = cont.RequiredSkill - 10; + cont.MaxLockLevel = cont.RequiredSkill + 40; + + cont.DropItem( new Gold( level * 1000 ) ); + + for ( int i = 0; i < level * 5; ++i ) + cont.DropItem( Loot.RandomScroll( 0, 63, SpellbookType.Regular ) ); + + for ( int i = 0; i < level * 6; ++i ) + { + Item item; + + if ( Core.AOS ) + item = Loot.RandomArmorOrShieldOrWeaponOrJewelry(); + else + item = Loot.RandomArmorOrShieldOrWeapon(); + + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( Core.AOS ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( weapon, attributeCount, min, max ); + } + else + { + weapon.DamageLevel = (WeaponDamageLevel)Utility.Random( 6 ); + weapon.AccuracyLevel = (WeaponAccuracyLevel)Utility.Random( 6 ); + weapon.DurabilityLevel = (WeaponDurabilityLevel)Utility.Random( 6 ); + } + + cont.DropItem( item ); + } + else if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( Core.AOS ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( armor, attributeCount, min, max ); + } + else + { + armor.ProtectionLevel = (ArmorProtectionLevel)Utility.Random( 6 ); + armor.Durability = (ArmorDurabilityLevel)Utility.Random( 6 ); + } + + cont.DropItem( item ); + } + else if ( item is BaseJewel ) + { + int attributeCount; + int min, max; + + GetRandomAOSStats( out attributeCount, out min, out max ); + + BaseRunicTool.ApplyAttributesTo( (BaseJewel)item, attributeCount, min, max ); + + cont.DropItem( item ); + } + } + } + + int reagents; + if ( level == 0 ) + reagents = 12; + else + reagents = level * 3; + + for ( int i = 0; i < reagents; i++ ) + { + Item item = Loot.RandomPossibleReagent(); + item.Amount = Utility.RandomMinMax( 40, 60 ); + cont.DropItem( item ); + } + + int gems; + if ( level == 0 ) + gems = 2; + else + gems = level * 3; + + for ( int i = 0; i < gems; i++ ) + { + Item item = Loot.RandomGem(); + cont.DropItem( item ); + } + + if ( level == 6 && Core.AOS ) + cont.DropItem( (Item)Activator.CreateInstance( m_Artifacts[Utility.Random(m_Artifacts.Length)] ) ); + } + + public override bool CheckLocked( Mobile from ) + { + if ( !this.Locked ) + return false; + + if ( this.Level == 0 && from.AccessLevel < AccessLevel.GameMaster ) + { + foreach ( Mobile m in this.Guardians ) + { + if ( m.Alive ) + { + from.SendLocalizedMessage( 1046448 ); // You must first kill the guardians before you may open this chest. + return true; + } + } + + LockPick( from ); + return false; + } + else + { + return base.CheckLocked( from ); + } + } + + private ArrayList m_Lifted = new ArrayList(); + + private bool CheckLoot( Mobile m, bool criminalAction ) + { + if ( m_Temporary ) + return false; + + if ( m.AccessLevel >= AccessLevel.GameMaster || m_Owner == null || m == m_Owner ) + return true; + + Party p = Party.Get( m_Owner ); + + if ( p != null && p.Contains( m ) ) + return true; + + Map map = this.Map; + + if ( map != null && (map.Rules & MapRules.HarmfulRestrictions) == 0 ) + { + if ( criminalAction ) + m.CriminalAction( true ); + else + m.SendLocalizedMessage( 1010630 ); // Taking someone else's treasure is a criminal offense! + + return true; + } + + m.SendLocalizedMessage( 1010631 ); // You did not discover this chest! + return false; + } + + public override bool IsDecoContainer + { + get{ return false; } + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + return CheckLoot( from, item != this ) && base.CheckItemUse( from, item ); + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + return CheckLoot( from, true ) && base.CheckLift( from, item, ref reject ); + } + + public override void OnItemLifted( Mobile from, Item item ) + { + bool notYetLifted = !m_Lifted.Contains( item ); + + from.RevealingAction(); + + if ( notYetLifted ) + { + m_Lifted.Add( item ); + + if ( 0.1 >= Utility.RandomDouble() ) // 10% chance to spawn a new monster + TreasureMap.Spawn( m_Level, GetWorldLocation(), Map, from, false ); + } + + base.OnItemLifted( from, item ); + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + if ( m.AccessLevel < AccessLevel.GameMaster ) + { + m.SendLocalizedMessage( 1048122, "", 0x8A5 ); // The chest refuses to be filled with treasure again. + return false; + } + + return base.CheckHold( m, item, message, checkItems, plusItems, plusWeight ); + } + + public TreasureMapChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.WriteMobileList( m_Guardians, true ); + writer.Write( (bool) m_Temporary ); + + writer.Write( m_Owner ); + + writer.Write( (int) m_Level ); + writer.WriteDeltaTime( m_DeleteTime ); + writer.WriteItemList( m_Lifted, true ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_Guardians = reader.ReadMobileList(); + m_Temporary = reader.ReadBool(); + + goto case 1; + } + case 1: + { + m_Owner = reader.ReadMobile(); + + goto case 0; + } + case 0: + { + m_Level = reader.ReadInt(); + m_DeleteTime = reader.ReadDeltaTime(); + m_Lifted = reader.ReadItemList(); + + if ( version < 2 ) + m_Guardians = new ArrayList(); + + break; + } + } + + if ( !m_Temporary ) + { + m_Timer = new DeleteTimer( this, m_DeleteTime ); + m_Timer.Start(); + } + else + { + Delete(); + } + } + + public override void OnAfterDelete() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + base.OnAfterDelete(); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive ) + list.Add( new RemoveEntry( from, this ) ); + } + + public void BeginRemove( Mobile from ) + { + if ( !from.Alive ) + return; + + from.CloseGump( typeof( RemoveGump ) ); + from.SendGump( new RemoveGump( from, this ) ); + } + + public void EndRemove( Mobile from ) + { + if ( Deleted || from != m_Owner || !from.InRange( GetWorldLocation(), 3 ) ) + return; + + from.SendLocalizedMessage( 1048124, "", 0x8A5 ); // The old, rusted chest crumbles when you hit it. + this.Delete(); + } + + private class RemoveGump : Gump + { + private Mobile m_From; + private TreasureMapChest m_Chest; + + public RemoveGump( Mobile from, TreasureMapChest chest ) : base( 15, 15 ) + { + m_From = from; + m_Chest = chest; + + Closable = false; + Disposable = false; + + AddPage( 0 ); + + AddBackground( 30, 0, 240, 240, 2620 ); + + AddHtmlLocalized( 45, 15, 200, 80, 1048125, 0xFFFFFF, false, false ); // When this treasure chest is removed, any items still inside of it will be lost. + AddHtmlLocalized( 45, 95, 200, 60, 1048126, 0xFFFFFF, false, false ); // Are you certain you're ready to remove this chest? + + AddButton( 40, 153, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 75, 155, 180, 40, 1048127, 0xFFFFFF, false, false ); // Remove the Treasure Chest + + AddButton( 40, 195, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 75, 197, 180, 35, 1006045, 0xFFFFFF, false, false ); // Cancel + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_Chest.EndRemove( m_From ); + } + } + + private class RemoveEntry : ContextMenuEntry + { + private Mobile m_From; + private TreasureMapChest m_Chest; + + public RemoveEntry( Mobile from, TreasureMapChest chest ) : base( 6149, 3 ) + { + m_From = from; + m_Chest = chest; + + Enabled = ( from == chest.Owner ); + } + + public override void OnClick() + { + if ( m_Chest.Deleted || m_From != m_Chest.Owner || !m_From.CheckAlive() ) + return; + + m_Chest.BeginRemove( m_From ); + } + } + + private class DeleteTimer : Timer + { + private Item m_Item; + + public DeleteTimer( Item item, DateTime time ) : base( time - DateTime.Now ) + { + m_Item = item; + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Decoration Artifacts/BaseDecorationArtifact.cs b/Scripts/Items/Decoration Artifacts/BaseDecorationArtifact.cs new file mode 100644 index 000000000..4600749e0 --- /dev/null +++ b/Scripts/Items/Decoration Artifacts/BaseDecorationArtifact.cs @@ -0,0 +1,79 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseDecorationArtifact : Item + { + public abstract int ArtifactRarity{ get; } + + public override bool ForceShowProperties{ get{ return true; } } + + public BaseDecorationArtifact( int itemID ) : base( itemID ) + { + Weight = 10.0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1061078, this.ArtifactRarity.ToString() ); // artifact rarity ~1_val~ + } + + public BaseDecorationArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public abstract class BaseDecorationContainerArtifact : BaseContainer + { + public abstract int ArtifactRarity{ get; } + + public override bool ForceShowProperties{ get{ return true; } } + + public BaseDecorationContainerArtifact( int itemID ) : base( itemID ) + { + Weight = 10.0; + } + + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + + list.Add( 1061078, this.ArtifactRarity.ToString() ); // artifact rarity ~1_val~ + } + + public BaseDecorationContainerArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Decoration Artifacts/DoomDecorationArtifacts.cs b/Scripts/Items/Decoration Artifacts/DoomDecorationArtifacts.cs new file mode 100644 index 000000000..c02613727 --- /dev/null +++ b/Scripts/Items/Decoration Artifacts/DoomDecorationArtifacts.cs @@ -0,0 +1,698 @@ +using System; +using Server; + +namespace Server.Items +{ + #region BackpackArtifact + public class BackpackArtifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public BackpackArtifact() : base( 0x9B2 ) + { + } + + public BackpackArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BloodyWaterArtifact + public class BloodyWaterArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public BloodyWaterArtifact() : base( 0xE23 ) + { + } + + public BloodyWaterArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BooksWestArtifact + public class BooksWestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public BooksWestArtifact() : base( 0x1E25 ) + { + } + + public BooksWestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BooksNorthArtifact + public class BooksNorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public BooksNorthArtifact() : base( 0x1E24 ) + { + } + + public BooksNorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BooksFaceDownArtifact + public class BooksFaceDownArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public BooksFaceDownArtifact() : base( 0x1E21 ) + { + } + + public BooksFaceDownArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BottleArtifact + public class BottleArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public BottleArtifact() : base( 0xE28 ) + { + } + + public BottleArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BrazierArtifact + public class BrazierArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public BrazierArtifact() : base( 0xE31 ) + { + Light = LightType.Circle150; + } + + public BrazierArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region CocoonArtifact + public class CocoonArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 7; } } + + [Constructable] + public CocoonArtifact() : base( 0x10DA ) + { + } + + public CocoonArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region DamagedBooksArtifact + public class DamagedBooksArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public DamagedBooksArtifact() : base( 0xC16 ) + { + } + + public DamagedBooksArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region EggCaseArtifact + public class EggCaseArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public EggCaseArtifact() : base( 0x10D9 ) + { + } + + public EggCaseArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region GruesomeStandardArtifact + public class GruesomeStandardArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public GruesomeStandardArtifact() : base( 0x428 ) + { + } + + public GruesomeStandardArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region LampPostArtifact + public class LampPostArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public LampPostArtifact() : base( 0xB24 ) + { + Light = LightType.Circle300; + } + + public LampPostArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region LeatherTunicArtifact + public class LeatherTunicArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public LeatherTunicArtifact() : base( 0x13CA ) + { + } + + public LeatherTunicArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region RockArtifact + public class RockArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public RockArtifact() : base( 0x1363 ) + { + } + + public RockArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region RuinedPaintingArtifact + public class RuinedPaintingArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 12; } } + + [Constructable] + public RuinedPaintingArtifact() : base( 0xC2C ) + { + } + + public RuinedPaintingArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SaddleArtifact + public class SaddleArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public SaddleArtifact() : base( 0xF38 ) + { + } + + public SaddleArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SkinnedDeerArtifact + public class SkinnedDeerArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public SkinnedDeerArtifact() : base( 0x1E91 ) + { + } + + public SkinnedDeerArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SkinnedGoatArtifact + public class SkinnedGoatArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public SkinnedGoatArtifact() : base( 0x1E88 ) + { + } + + public SkinnedGoatArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SkullCandleArtifact + public class SkullCandleArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public SkullCandleArtifact() : base( 0x1858 ) + { + Light = LightType.Circle150; + } + + public SkullCandleArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region StretchedHideArtifact + public class StretchedHideArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public StretchedHideArtifact() : base( 0x106B ) + { + } + + public StretchedHideArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region StuddedLeggingsArtifact + public class StuddedLeggingsArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public StuddedLeggingsArtifact() : base( 0x13D8 ) + { + } + + public StuddedLeggingsArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region StuddedTunicArtifact + public class StuddedTunicArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 7; } } + + [Constructable] + public StuddedTunicArtifact() : base( 0x13D9 ) + { + } + + public StuddedTunicArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region TarotCardsArtifact + public class TarotCardsArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public TarotCardsArtifact() : base( 0x12A5 ) + { + } + + public TarotCardsArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion +} \ No newline at end of file diff --git a/Scripts/Items/Decoration Artifacts/SEDecorationArtifacts.cs b/Scripts/Items/Decoration Artifacts/SEDecorationArtifacts.cs new file mode 100644 index 000000000..c668ae726 --- /dev/null +++ b/Scripts/Items/Decoration Artifacts/SEDecorationArtifacts.cs @@ -0,0 +1,1627 @@ +using System; +using Server; + +namespace Server.Items +{ + #region Basket1Artifact + public class Basket1Artifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public Basket1Artifact() : base( 0x24DD ) + { + } + + public Basket1Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket2Artifact + public class Basket2Artifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public Basket2Artifact() : base( 0x24D7 ) + { + } + + public Basket2Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket3WestArtifact + public class Basket3WestArtifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public Basket3WestArtifact() : base( 0x24D9 ) + { + } + + public Basket3WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket3NorthArtifact + public class Basket3NorthArtifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 1; } } + + [Constructable] + public Basket3NorthArtifact() : base( 0x24DA ) + { + } + + public Basket3NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket4Artifact + public class Basket4Artifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public Basket4Artifact() : base( 0x24D8 ) + { + } + + public Basket4Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket5WestArtifact + public class Basket5WestArtifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public Basket5WestArtifact() : base( 0x24DC ) + { + } + + public Basket5WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket5NorthArtifact + public class Basket5NorthArtifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public Basket5NorthArtifact() : base( 0x24DB ) + { + } + + public Basket5NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Basket6Artifact + public class Basket6Artifact : BaseDecorationContainerArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public Basket6Artifact() : base( 0x24D5 ) + { + } + + public Basket6Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BowlArtifact + public class BowlArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public BowlArtifact() : base( 0x24DE ) + { + } + + public BowlArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BowlsVerticalArtifact + public class BowlsVerticalArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public BowlsVerticalArtifact() : base( 0x24DF ) + { + } + + public BowlsVerticalArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region BowlsHorizontalArtifact + public class BowlsHorizontalArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public BowlsHorizontalArtifact() : base( 0x24E0 ) + { + } + + public BowlsHorizontalArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region CupsArtifact + public class CupsArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public CupsArtifact() : base( 0x24E1 ) + { + } + + public CupsArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region FanWestArtifact + public class FanWestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public FanWestArtifact() : base( 0x240A ) + { + } + + public FanWestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region FanNorthArtifact + public class FanNorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public FanNorthArtifact() : base( 0x2409 ) + { + } + + public FanNorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region TripleFanWestArtifact + public class TripleFanWestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public TripleFanWestArtifact() : base( 0x240C ) + { + } + + public TripleFanWestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region TripleFanNorthArtifact + public class TripleFanNorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public TripleFanNorthArtifact() : base( 0x240B ) + { + } + + public TripleFanNorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region FlowersArtifact + public class FlowersArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 7; } } + + [Constructable] + public FlowersArtifact() : base( 0x284A ) + { + } + + public FlowersArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting1WestArtifact + public class Painting1WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public Painting1WestArtifact() : base( 0x240E ) + { + } + + public Painting1WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting1NorthArtifact + public class Painting1NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public Painting1NorthArtifact() : base( 0x240D ) + { + } + + public Painting1NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting2WestArtifact + public class Painting2WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public Painting2WestArtifact() : base( 0x2410 ) + { + } + + public Painting2WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting2NorthArtifact + public class Painting2NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public Painting2NorthArtifact() : base( 0x240F ) + { + } + + public Painting2NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting3Artifact + public class Painting3Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public Painting3Artifact() : base( 0x2411 ) + { + } + + public Painting3Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting4WestArtifact + public class Painting4WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 6; } } + + [Constructable] + public Painting4WestArtifact() : base( 0x2412 ) + { + } + + public Painting4WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting4NorthArtifact + public class Painting4NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 6; } } + + [Constructable] + public Painting4NorthArtifact() : base( 0x2411 ) + { + } + + public Painting4NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting5WestArtifact + public class Painting5WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public Painting5WestArtifact() : base( 0x2416 ) + { + } + + public Painting5WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting5NorthArtifact + public class Painting5NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public Painting5NorthArtifact() : base( 0x2415 ) + { + } + + public Painting5NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting6WestArtifact + public class Painting6WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public Painting6WestArtifact() : base( 0x2418 ) + { + } + + public Painting6WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Painting6NorthArtifact + public class Painting6NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public Painting6NorthArtifact() : base( 0x2417 ) + { + } + + public Painting6NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SakeArtifact + public class SakeArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 4; } } + + [Constructable] + public SakeArtifact() : base( 0x24E2 ) + { + } + + public SakeArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Sculpture1Artifact + public class Sculpture1Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public Sculpture1Artifact() : base( 0x2419 ) + { + } + + public Sculpture1Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Sculpture2Artifact + public class Sculpture2Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public Sculpture2Artifact() : base( 0x241B ) + { + } + + public Sculpture2Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region DolphinLeftArtifact + public class DolphinLeftArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public DolphinLeftArtifact() : base( 0x2846 ) + { + } + + public DolphinLeftArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region DolphinRightArtifact + public class DolphinRightArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public DolphinRightArtifact() : base( 0x2847 ) + { + } + + public DolphinRightArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region ManStatuetteSouthArtifact + public class ManStatuetteSouthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public ManStatuetteSouthArtifact() : base( 0x2848 ) + { + } + + public ManStatuetteSouthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region ManStatuetteEastArtifact + public class ManStatuetteEastArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public ManStatuetteEastArtifact() : base( 0x2849 ) + { + } + + public ManStatuetteEastArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay1WestArtifact + public class SwordDisplay1WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public SwordDisplay1WestArtifact() : base( 0x2842 ) + { + } + + public SwordDisplay1WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay1NorthArtifact + public class SwordDisplay1NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 5; } } + + [Constructable] + public SwordDisplay1NorthArtifact() : base( 0x2843 ) + { + } + + public SwordDisplay1NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay2WestArtifact + public class SwordDisplay2WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 6; } } + + [Constructable] + public SwordDisplay2WestArtifact() : base( 0x2844 ) + { + } + + public SwordDisplay2WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay2NorthArtifact + public class SwordDisplay2NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 6; } } + + [Constructable] + public SwordDisplay2NorthArtifact() : base( 0x2845 ) + { + } + + public SwordDisplay2NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay3SouthArtifact + public class SwordDisplay3SouthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public SwordDisplay3SouthArtifact() : base( 0x2855 ) + { + } + + public SwordDisplay3SouthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay3EastArtifact + public class SwordDisplay3EastArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public SwordDisplay3EastArtifact() : base( 0x2856 ) + { + } + + public SwordDisplay3EastArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay4WestArtifact + public class SwordDisplay4WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 8; } } + + [Constructable] + public SwordDisplay4WestArtifact() : base( 0x2853 ) + { + } + + public SwordDisplay4WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay4NorthArtifact + public class SwordDisplay4NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public SwordDisplay4NorthArtifact() : base( 0x2854 ) + { + } + + public SwordDisplay4NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay5WestArtifact + public class SwordDisplay5WestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public SwordDisplay5WestArtifact() : base( 0x2851 ) + { + } + + public SwordDisplay5WestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region SwordDisplay5NorthArtifact + public class SwordDisplay5NorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 9; } } + + [Constructable] + public SwordDisplay5NorthArtifact() : base( 0x2852 ) + { + } + + public SwordDisplay5NorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region TeapotWestArtifact + public class TeapotWestArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public TeapotWestArtifact() : base( 0x24E7 ) + { + } + + public TeapotWestArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region TeapotNorthArtifact + public class TeapotNorthArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public TeapotNorthArtifact() : base( 0x24E6 ) + { + } + + public TeapotNorthArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region TowerLanternArtifact + public class TowerLanternArtifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsOn + { + get{ return this.ItemID == 0x24BF; } + set{ this.ItemID = value ? 0x24BF : 0x24C0; } + } + + [Constructable] + public TowerLanternArtifact() : base( 0x24C0 ) + { + this.Light = LightType.Circle225; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 2 ) ) + { + if ( this.IsOn ) + { + this.IsOn = false; + from.PlaySound( 0x3BE ); + } + else + { + this.IsOn = true; + from.PlaySound( 0x47 ); + } + } + else + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + + public TowerLanternArtifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + if ( version == 0 ) + this.Light = LightType.Circle225; + } + } + #endregion + + #region Urn1Artifact + public class Urn1Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public Urn1Artifact() : base( 0x241D ) + { + } + + public Urn1Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region Urn2Artifact + public class Urn2Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public Urn2Artifact() : base( 0x241E ) + { + } + + public Urn2Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region ZenRock1Artifact + public class ZenRock1Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 2; } } + + [Constructable] + public ZenRock1Artifact() : base( 0x24E4 ) + { + } + + public ZenRock1Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region ZenRock2Artifact + public class ZenRock2Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public ZenRock2Artifact() : base( 0x24E3 ) + { + } + + public ZenRock2Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion + + #region ZenRock3Artifact + public class ZenRock3Artifact : BaseDecorationArtifact + { + public override int ArtifactRarity{ get{ return 3; } } + + [Constructable] + public ZenRock3Artifact() : base( 0x24E5 ) + { + } + + public ZenRock3Artifact( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + #endregion +} \ No newline at end of file diff --git a/Scripts/Items/Decoration Artifacts/StealableArtifactsSpawner.cs b/Scripts/Items/Decoration Artifacts/StealableArtifactsSpawner.cs new file mode 100644 index 000000000..9a9bfc929 --- /dev/null +++ b/Scripts/Items/Decoration Artifacts/StealableArtifactsSpawner.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections; +using Server; +using Server.Commands; + +namespace Server.Items +{ + public class StealableArtifactsSpawner : Item + { + public class StealableEntry + { + private Map m_Map; + private Point3D m_Location; + private int m_MinDelay; + private int m_MaxDelay; + private Type m_Type; + private int m_Hue; + + public Map Map{ get{ return m_Map; } } + public Point3D Location{ get{ return m_Location; } } + public int MinDelay{ get{ return m_MinDelay; } } + public int MaxDelay{ get{ return m_MaxDelay; } } + public Type Type{ get{ return m_Type; } } + public int Hue{ get{ return m_Hue; } } + + public StealableEntry( Map map, Point3D location, int minDelay, int maxDelay, Type type ) : this( map, location, minDelay, maxDelay, type, 0 ) + { + } + + public StealableEntry( Map map, Point3D location, int minDelay, int maxDelay, Type type, int hue ) + { + m_Map = map; + m_Location = location; + m_MinDelay = minDelay; + m_MaxDelay = maxDelay; + m_Type = type; + m_Hue = hue; + } + + public Item CreateInstance() + { + Item item = (Item) Activator.CreateInstance( m_Type ); + + if ( m_Hue > 0 ) + item.Hue = m_Hue; + + item.Movable = false; + item.MoveToWorld( this.Location, this.Map ); + + return item; + } + } + + private static StealableEntry[] m_Entries = new StealableEntry[] + { + // Doom - Artifact rarity 1 + new StealableEntry( Map.Malas, new Point3D( 317, 56, -1 ), 72, 108, typeof( RockArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 360, 31, 8 ), 72, 108, typeof( SkullCandleArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 369, 372, -1 ), 72, 108, typeof( BottleArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 378, 372, 0 ), 72, 108, typeof( DamagedBooksArtifact ) ), + // Doom - Artifact rarity 2 + new StealableEntry( Map.Malas, new Point3D( 432, 16, -1 ), 144, 216, typeof( StretchedHideArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 489, 9, 0 ), 144, 216, typeof( BrazierArtifact ) ), + // Doom - Artifact rarity 3 + new StealableEntry( Map.Malas, new Point3D( 471, 96, -1 ), 288, 432, typeof( LampPostArtifact ), GetLampPostHue() ), + new StealableEntry( Map.Malas, new Point3D( 421, 198, 2 ), 288, 432, typeof( BooksNorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 431, 189, -1 ), 288, 432, typeof( BooksWestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 435, 196, -1 ), 288, 432, typeof( BooksFaceDownArtifact ) ), + // Doom - Artifact rarity 5 + new StealableEntry( Map.Malas, new Point3D( 447, 9, 8 ), 1152, 1728, typeof( StuddedLeggingsArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 423, 28, 0 ), 1152, 1728, typeof( EggCaseArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 347, 44, 4 ), 1152, 1728, typeof( SkinnedGoatArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 497, 57, -1 ), 1152, 1728, typeof( GruesomeStandardArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 381, 375, 11 ), 1152, 1728, typeof( BloodyWaterArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 489, 369, 2 ), 1152, 1728, typeof( TarotCardsArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 497, 369, 5 ), 1152, 1728, typeof( BackpackArtifact ) ), + // Doom - Artifact rarity 7 + new StealableEntry( Map.Malas, new Point3D( 475, 23, 4 ), 4608, 6912, typeof( StuddedTunicArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 423, 28, 0 ), 4608, 6912, typeof( CocoonArtifact ) ), + // Doom - Artifact rarity 8 + new StealableEntry( Map.Malas, new Point3D( 354, 36, -1 ), 9216, 13824, typeof( SkinnedDeerArtifact ) ), + // Doom - Artifact rarity 9 + new StealableEntry( Map.Malas, new Point3D( 433, 11, -1 ), 18432, 27648, typeof( SaddleArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 403, 31, 4 ), 18432, 27648, typeof( LeatherTunicArtifact ) ), + // Doom - Artifact rarity 10 + new StealableEntry( Map.Malas, new Point3D( 257, 70, -2 ), 36864, 55296, typeof( ZyronicClaw ) ), + new StealableEntry( Map.Malas, new Point3D( 354, 176, 7 ), 36864, 55296, typeof( TitansHammer ) ), + new StealableEntry( Map.Malas, new Point3D( 369, 389, -1 ), 36864, 55296, typeof( BladeOfTheRighteous ) ), + new StealableEntry( Map.Malas, new Point3D( 467, 92, 4 ), 36864, 55296, typeof( InquisitorsResolution ) ), + // Doom - Artifact rarity 12 + new StealableEntry( Map.Malas, new Point3D( 487, 364, -1 ), 147456, 221184, typeof( RuinedPaintingArtifact ) ), + + // Yomotsu Mines - Artifact rarity 1 + new StealableEntry( Map.Malas, new Point3D( 18, 110, -1 ), 72, 108, typeof( Basket1Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 66, 114, -1 ), 72, 108, typeof( Basket2Artifact ) ), + // Yomotsu Mines - Artifact rarity 2 + new StealableEntry( Map.Malas, new Point3D( 63, 12, 11 ), 144, 216, typeof( Basket4Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 5, 29, -1 ), 144, 216, typeof( Basket5NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 30, 81, 3 ), 144, 216, typeof( Basket5WestArtifact ) ), + // Yomotsu Mines - Artifact rarity 3 + new StealableEntry( Map.Malas, new Point3D( 115, 7, -1 ), 288, 432, typeof( Urn1Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 85, 13, -1 ), 288, 432, typeof( Urn2Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 110, 53, -1 ), 288, 432, typeof( Sculpture1Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 108, 37, -1 ), 288, 432, typeof( Sculpture2Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 121, 14, -1 ), 288, 432, typeof( TeapotNorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 121, 115, -1 ), 288, 432, typeof( TeapotWestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 84, 40, -1 ), 288, 432, typeof( TowerLanternArtifact ) ), + // Yomotsu Mines - Artifact rarity 9 + new StealableEntry( Map.Malas, new Point3D( 94, 7, -1 ), 18432, 27648, typeof( ManStatuetteSouthArtifact ) ), + + // Fan Dancer's Dojo - Artifact rarity 1 + new StealableEntry( Map.Malas, new Point3D( 113, 640, -2 ), 72, 108, typeof( Basket3NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 102, 355, -1 ), 72, 108, typeof( Basket3WestArtifact ) ), + // Fan Dancer's Dojo - Artifact rarity 2 + new StealableEntry( Map.Malas, new Point3D( 99, 370, -1 ), 144, 216, typeof( Basket6Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 100, 357, -1 ), 144, 216, typeof( ZenRock1Artifact ) ), + // Fan Dancer's Dojo - Artifact rarity 3 + new StealableEntry( Map.Malas, new Point3D( 73, 473, -1 ), 288, 432, typeof( FanNorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 99, 372, -1 ), 288, 432, typeof( FanWestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 92, 326, -1 ), 288, 432, typeof( BowlsVerticalArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 97, 470, -1 ), 288, 432, typeof( ZenRock2Artifact ) ), + new StealableEntry( Map.Malas, new Point3D( 103, 691, -1 ), 288, 432, typeof( ZenRock3Artifact ) ), + // Fan Dancer's Dojo - Artifact rarity 4 + new StealableEntry( Map.Malas, new Point3D( 103, 336, 4 ), 576, 864, typeof( Painting1NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 59, 381, 4 ), 576, 864, typeof( Painting1WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 84, 401, 2 ), 576, 864, typeof( Painting2NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 59, 392, 2 ), 576, 864, typeof( Painting2WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 107, 483, -1 ), 576, 864, typeof( TripleFanNorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 50, 475, -1 ), 576, 864, typeof( TripleFanWestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 107, 460, -1 ), 576, 864, typeof( BowlArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 90, 502, -1 ), 576, 864, typeof( CupsArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 107, 688, -1 ), 576, 864, typeof( BowlsHorizontalArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 112, 676, -1 ), 576, 864, typeof( SakeArtifact ) ), + // Fan Dancer's Dojo - Artifact rarity 5 + new StealableEntry( Map.Malas, new Point3D( 135, 614, -1 ), 1152, 1728, typeof( SwordDisplay1NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 50, 482, -1 ), 1152, 1728, typeof( SwordDisplay1WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 119, 672, -1 ), 1152, 1728, typeof( Painting3Artifact ) ), + // Fan Dancer's Dojo - Artifact rarity 6 + new StealableEntry( Map.Malas, new Point3D( 90, 326, -1 ), 2304, 3456, typeof( Painting4NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 99, 354, -1 ), 2304, 3456, typeof( Painting4WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 179, 652, -1 ), 2304, 3456, typeof( SwordDisplay2NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 118, 627, -1 ), 2304, 3456, typeof( SwordDisplay2WestArtifact ) ), + // Fan Dancer's Dojo - Artifact rarity 7 + new StealableEntry( Map.Malas, new Point3D( 90, 483, -1 ), 4608, 6912, typeof( FlowersArtifact ) ), + // Fan Dancer's Dojo - Artifact rarity 8 + new StealableEntry( Map.Malas, new Point3D( 71, 562, -1 ), 9216, 13824, typeof( DolphinLeftArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 102, 677, -1 ), 9216, 13824, typeof( DolphinRightArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 61, 499, 0 ), 9216, 13824, typeof( SwordDisplay3SouthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 182, 669, -1 ), 9216, 13824, typeof( SwordDisplay3EastArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 162, 647, -1 ), 9216, 13824, typeof( SwordDisplay4WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 124, 624, 0 ), 9216, 13824, typeof( Painting5NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 146, 649, 2 ), 9216, 13824, typeof( Painting5WestArtifact ) ), + // Fan Dancer's Dojo - Artifact rarity 9 + new StealableEntry( Map.Malas, new Point3D( 100, 488, -1 ), 18432, 27648, typeof( SwordDisplay4NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 175, 606, 0 ), 18432, 27648, typeof( SwordDisplay5NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 157, 608, -1 ), 18432, 27648, typeof( SwordDisplay5WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 187, 643, 1 ), 18432, 27648, typeof( Painting6NorthArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 146, 623, 1 ), 18432, 27648, typeof( Painting6WestArtifact ) ), + new StealableEntry( Map.Malas, new Point3D( 178, 629, -1 ), 18432, 27648, typeof( ManStatuetteEastArtifact ) ) + }; + + public static StealableEntry[] Entries{ get{ return m_Entries; } } + + private static Type[] m_TypesOfEntries = null; + public static Type[] TypesOfEntires + { + get + { + if( m_TypesOfEntries == null ) + { + m_TypesOfEntries = new Type[m_Entries.Length]; + + for( int i = 0; i < m_Entries.Length; i++ ) + m_TypesOfEntries[i] = m_Entries[i].Type; + } + + return m_TypesOfEntries; + } + } + + private static StealableArtifactsSpawner m_Instance; + + public static StealableArtifactsSpawner Instance{ get{ return m_Instance; } } + + private static int GetLampPostHue() + { + if ( 0.9 > Utility.RandomDouble() ) + return 0; + + return Utility.RandomList( 0x455, 0x47E, 0x482, 0x486, 0x48F, 0x4F2, 0x58C, 0x66C ); + } + + + public static void Initialize() + { + CommandSystem.Register( "GenStealArties", AccessLevel.Administrator, new CommandEventHandler( GenStealArties_OnCommand ) ); + CommandSystem.Register( "RemoveStealArties", AccessLevel.Administrator, new CommandEventHandler( RemoveStealArties_OnCommand ) ); + } + + [Usage( "GenStealArties" )] + [Description( "Generates the stealable artifacts spawner." )] + private static void GenStealArties_OnCommand( CommandEventArgs args ) + { + Mobile from = args.Mobile; + + if ( Create() ) + from.SendMessage( "Stealable artifacts spawner generated." ); + else + from.SendMessage( "Stealable artifacts spawner already present." ); + } + + [Usage( "RemoveStealArties" )] + [Description( "Removes the stealable artifacts spawner and every not yet stolen stealable artifacts." )] + private static void RemoveStealArties_OnCommand( CommandEventArgs args ) + { + Mobile from = args.Mobile; + + if ( Remove() ) + from.SendMessage( "Stealable artifacts spawner removed." ); + else + from.SendMessage( "Stealable artifacts spawner not present." ); + } + + public static bool Create() + { + if ( m_Instance != null && !m_Instance.Deleted ) + return false; + + m_Instance = new StealableArtifactsSpawner(); + return true; + } + + public static bool Remove() + { + if ( m_Instance == null ) + return false; + + m_Instance.Delete(); + m_Instance = null; + return true; + } + + public static StealableInstance GetStealableInstance( Item item ) + { + if ( Instance == null ) + return null; + + return (StealableInstance) Instance.m_Table[item]; + } + + + public class StealableInstance + { + private StealableEntry m_Entry; + private Item m_Item; + private DateTime m_NextRespawn; + + public StealableEntry Entry{ get{ return m_Entry; } } + + public Item Item + { + get{ return m_Item; } + set + { + if ( m_Item != null && value == null ) + { + int delay = Utility.RandomMinMax( this.Entry.MinDelay, this.Entry.MaxDelay ); + this.NextRespawn = DateTime.Now + TimeSpan.FromMinutes( delay ); + } + + if ( Instance != null ) + { + if ( m_Item != null ) + Instance.m_Table.Remove( m_Item ); + + if ( value != null ) + Instance.m_Table[value] = this; + } + + m_Item = value; + } + } + + public DateTime NextRespawn + { + get{ return m_NextRespawn; } + set{ m_NextRespawn = value; } + } + + public StealableInstance( StealableEntry entry ) : this( entry, null, DateTime.Now ) + { + } + + public StealableInstance( StealableEntry entry, Item item, DateTime nextRespawn ) + { + m_Item = item; + m_NextRespawn = nextRespawn; + m_Entry = entry; + } + + public void CheckRespawn() + { + if ( this.Item != null && ( this.Item.Deleted || this.Item.Movable || this.Item.Parent != null ) ) + this.Item = null; + + if ( this.Item == null && DateTime.Now >= this.NextRespawn ) + { + this.Item = this.Entry.CreateInstance(); + } + } + } + + private Timer m_RespawnTimer; + private StealableInstance[] m_Artifacts; + private Hashtable m_Table; + + public override string DefaultName + { + get { return "Stealable Artifacts Spawner - Internal"; } + } + + private StealableArtifactsSpawner() : base( 1 ) + { + Movable = false; + + m_Artifacts = new StealableInstance[m_Entries.Length]; + m_Table = new Hashtable( m_Entries.Length ); + + for ( int i = 0; i < m_Entries.Length; i++ ) + { + m_Artifacts[i] = new StealableInstance( m_Entries[i] ); + } + + m_RespawnTimer = Timer.DelayCall( TimeSpan.Zero, TimeSpan.FromMinutes( 15.0 ), new TimerCallback( CheckRespawn ) ); + } + + public override void OnDelete() + { + base.OnDelete(); + + if ( m_RespawnTimer != null ) + { + m_RespawnTimer.Stop(); + m_RespawnTimer = null; + } + + foreach ( StealableInstance si in m_Artifacts ) + { + if ( si.Item != null ) + si.Item.Delete(); + } + + m_Instance = null; + } + + public void CheckRespawn() + { + foreach ( StealableInstance si in m_Artifacts ) + { + si.CheckRespawn(); + } + } + + public StealableArtifactsSpawner( Serial serial ) : base( serial ) + { + m_Instance = this; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.WriteEncodedInt( m_Artifacts.Length ); + + for ( int i = 0; i < m_Artifacts.Length; i++ ) + { + StealableInstance si = m_Artifacts[i]; + + writer.Write( (Item) si.Item ); + writer.WriteDeltaTime( (DateTime) si.NextRespawn ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Artifacts = new StealableInstance[m_Entries.Length]; + m_Table = new Hashtable( m_Entries.Length ); + + int length = reader.ReadEncodedInt(); + + for ( int i = 0; i < length; i++ ) + { + Item item = reader.ReadItem(); + DateTime nextRespawn = reader.ReadDeltaTime(); + + if ( i < m_Artifacts.Length ) + { + StealableInstance si = new StealableInstance( m_Entries[i], item, nextRespawn ); + m_Artifacts[i] = si; + + if ( si.Item != null ) + m_Table[si.Item] = si; + } + } + + for ( int i = length; i < m_Entries.Length; i++ ) + { + m_Artifacts[i] = new StealableInstance( m_Entries[i] ); + } + + m_RespawnTimer = Timer.DelayCall( TimeSpan.Zero, TimeSpan.FromMinutes( 15.0 ), new TimerCallback( CheckRespawn ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/BarkeepContract.cs b/Scripts/Items/Deeds/BarkeepContract.cs new file mode 100644 index 000000000..2ea0e2d02 --- /dev/null +++ b/Scripts/Items/Deeds/BarkeepContract.cs @@ -0,0 +1,96 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Multis; + +namespace Server.Items +{ + public class BarkeepContract : Item + { + public override string DefaultName + { + get { return "a barkeep contract"; } + } + + [Constructable] + public BarkeepContract() : base( 0x14F0 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public BarkeepContract( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); //version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + from.SendLocalizedMessage( 503248 ); // Your godly powers allow you to place this vendor whereever you wish. + + Mobile v = new PlayerBarkeeper( from, BaseHouse.FindHouseAt( from ) ); + + v.Direction = from.Direction & Direction.Mask; + v.MoveToWorld( from.Location, from.Map ); + + this.Delete(); + } + else + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house == null || !house.IsOwner( from ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You are not the full owner of this house." ); + } + else if ( !house.CanPlaceNewBarkeep() ) + { + from.SendLocalizedMessage( 1062490 ); // That action would exceed the maximum number of barkeeps for this house. + } + else + { + bool vendor, contract; + BaseHouse.IsThereVendor( from.Location, from.Map, out vendor, out contract ); + + if ( vendor ) + { + from.SendLocalizedMessage( 1062677 ); // You cannot place a vendor or barkeep at this location. + } + else if ( contract ) + { + from.SendLocalizedMessage( 1062678 ); // You cannot place a vendor or barkeep on top of a rental contract! + } + else + { + Mobile v = new PlayerBarkeeper( from, house ); + + v.Direction = from.Direction & Direction.Mask; + v.MoveToWorld( from.Location, from.Map ); + + this.Delete(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/ClothingBlessDeed.cs b/Scripts/Items/Deeds/ClothingBlessDeed.cs new file mode 100644 index 000000000..03f0fa0ae --- /dev/null +++ b/Scripts/Items/Deeds/ClothingBlessDeed.cs @@ -0,0 +1,102 @@ +using System; +using Server.Network; +using Server.Prompts; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public class ClothingBlessTarget : Target // Create our targeting class (which we derive from the base target class) + { + private ClothingBlessDeed m_Deed; + + public ClothingBlessTarget( ClothingBlessDeed deed ) : base( 1, false, TargetFlags.None ) + { + m_Deed = deed; + } + + protected override void OnTarget( Mobile from, object target ) // Override the protected OnTarget() for our feature + { + if ( m_Deed.Deleted || m_Deed.RootParent != from ) + return; + + if ( target is BaseClothing ) + { + BaseClothing item = (BaseClothing)target; + + if ( item.LootType == LootType.Blessed || item.BlessedFor == from || (Mobile.InsuranceEnabled && item.Insured) ) // Check if its already newbied (blessed) + { + from.SendLocalizedMessage( 1045113 ); // That item is already blessed + } + else if ( item.LootType != LootType.Regular ) + { + from.SendLocalizedMessage( 1045114 ); // You can not bless that item + } + else if ( !item.CanBeBlessed || item.RootParent != from ) + { + from.SendLocalizedMessage( 500509 ); // You cannot bless that object + } + else + { + item.LootType = LootType.Blessed; + from.SendLocalizedMessage( 1010026 ); // You bless the item.... + + m_Deed.Delete(); // Delete the bless deed + } + } + else + { + from.SendLocalizedMessage( 500509 ); // You cannot bless that object + } + } + } + + public class ClothingBlessDeed : Item // Create the item class which is derived from the base item class + { + public override string DefaultName + { + get { return "a clothing bless deed"; } + } + + [Constructable] + public ClothingBlessDeed() : base( 0x14F0 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public ClothingBlessDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + } + + public override bool DisplayLootType{ get{ return false; } } + + public override void OnDoubleClick( Mobile from ) // Override double click of the deed to call our target + { + if ( !IsChildOf( from.Backpack ) ) // Make sure its in their pack + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + from.SendLocalizedMessage( 1005018 ); // What would you like to bless? (Clothes Only) + from.Target = new ClothingBlessTarget( this ); // Call our target + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/CommodityDeed.cs b/Scripts/Items/Deeds/CommodityDeed.cs new file mode 100644 index 000000000..b2cfcd86f --- /dev/null +++ b/Scripts/Items/Deeds/CommodityDeed.cs @@ -0,0 +1,199 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Items +{ + public interface ICommodity + { + string Description{ get; } + } + + public class CommodityDeed : Item + { + private Item m_Commodity; + + [CommandProperty( AccessLevel.GameMaster )] + public Item Commodity + { + get + { + return m_Commodity; + } + } + + public bool SetCommodity( Item item ) + { + InvalidateProperties(); + + if ( m_Commodity == null && item is ICommodity ) + { + m_Commodity = item; + m_Commodity.Internalize(); + InvalidateProperties(); + + return true; + } + else + { + return false; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Commodity ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Commodity = reader.ReadItem(); + + break; + } + } + } + + public CommodityDeed( Item commodity ) : base( 0x14F0 ) + { + Weight = 1.0; + Hue = 0x47; + + m_Commodity = commodity; + + LootType = LootType.Blessed; + } + + [Constructable] + public CommodityDeed() : this( null ) + { + } + + public CommodityDeed( Serial serial ) : base( serial ) + { + } + + public override void OnDelete() + { + if ( m_Commodity != null ) + m_Commodity.Delete(); + + base.OnDelete(); + } + + public override int LabelNumber{ get{ return m_Commodity == null ? 1047016 : 1047017; } } + + public override void GetProperties(ObjectPropertyList list) + { + base.GetProperties( list ); + + if ( m_Commodity != null ) + list.Add( 1060658, "#{0}\t{1}", m_Commodity.LabelNumber, m_Commodity.Amount ); // ~1_val~: ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( m_Commodity != null ) + from.Send( new UnicodeMessage( Serial, ItemID, MessageType.Label, 0x3B2, 3, "ENU", "", ((ICommodity)m_Commodity).Description ) ); + } + + public override void OnDoubleClick( Mobile from ) + { + int number; + + BankBox box = from.BankBox; + + if ( m_Commodity != null ) + { + if ( box != null && IsChildOf( box ) ) + { + number = 1047031; // The commodity has been redeemed. + + box.DropItem( m_Commodity ); + + m_Commodity = null; + Delete(); + } + else + { + number = 1047024; // To claim the resources .... + } + } + else if ( box == null || !IsChildOf( box ) ) + { + number = 1047026; // That must be in your bank box to use it. + } + else + { + number = 1047029; // Target the commodity to fill this deed with. + + from.Target = new InternalTarget( this ); + } + + from.SendLocalizedMessage( number ); + } + + private class InternalTarget : Target + { + private CommodityDeed m_Deed; + + public InternalTarget( CommodityDeed deed ) : base( 3, false, TargetFlags.None ) + { + m_Deed = deed; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Deed.Deleted ) + return; + + int number; + + if ( m_Deed.Commodity != null ) + { + number = 1047028; // The commodity deed has already been filled. + } + else if ( targeted is Item ) + { + BankBox box = from.BankBox; + + if ( box != null && m_Deed.IsChildOf( box ) && ((Item)targeted).IsChildOf( box ) ) + { + if ( m_Deed.SetCommodity( (Item) targeted ) ) + { + number = 1047030; // The commodity deed has been filled. + } + else + { + number = 1047027; // That is not a commodity the bankers will fill a commodity deed with. + } + } + else + { + number = 1047026; // That must be in your bank box to use it. + } + } + else + { + number = 1047027; // That is not a commodity the bankers will fill a commodity deed with. + } + + from.SendLocalizedMessage( number ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/DragonBardingDeed.cs b/Scripts/Items/Deeds/DragonBardingDeed.cs new file mode 100644 index 000000000..997d79d04 --- /dev/null +++ b/Scripts/Items/Deeds/DragonBardingDeed.cs @@ -0,0 +1,149 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Engines.Craft; +namespace Server.Items +{ + [TypeAlias( "Server.Items.DragonBarding" )] + public class DragonBardingDeed : Item, ICraftable + { + private bool m_Exceptional; + private Mobile m_Crafter; + private CraftResource m_Resource; + + public override int LabelNumber{ get{ return m_Exceptional ? 1053181 : 1053012; } } // dragon barding deed + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter{ get{ return m_Crafter; } set{ m_Crafter = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Exceptional{ get{ return m_Exceptional; } set{ m_Exceptional = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource{ get{ return m_Resource; } set{ m_Resource = value; Hue = CraftResources.GetHue( value ); InvalidateProperties(); } } + + public DragonBardingDeed() : base( 0x14F0 ) + { + Weight = 1.0; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Exceptional && m_Crafter != null ) + list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.BeginTarget( 6, false, TargetFlags.None, new TargetCallback( OnTarget ) ); + from.SendLocalizedMessage( 1053024 ); // Select the swamp dragon you wish to place the barding on. + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public virtual void OnTarget( Mobile from, object obj ) + { + if ( Deleted ) + return; + + SwampDragon pet = obj as SwampDragon; + + if ( pet == null || pet.HasBarding ) + { + from.SendLocalizedMessage( 1053025 ); // That is not an unarmored swamp dragon. + } + else if ( !pet.Controlled || pet.ControlMaster != from ) + { + from.SendLocalizedMessage( 1053026 ); // You can only put barding on a tamed swamp dragon that you own. + } + else if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1060640 ); // The item must be in your backpack to use it. + } + else + { + pet.BardingExceptional = this.Exceptional; + pet.BardingCrafter = this.Crafter; + pet.BardingHP = pet.BardingMaxHP; + pet.BardingResource = this.Resource; + pet.HasBarding = true; + pet.Hue = this.Hue; + + this.Delete(); + + from.SendLocalizedMessage( 1053027 ); // You place the barding on your swamp dragon. Use a bladed item on your dragon to remove the armor. + } + } + + public DragonBardingDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_Exceptional ); + writer.Write( (Mobile) m_Crafter ); + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_Exceptional = reader.ReadBool(); + m_Crafter = reader.ReadMobile(); + + if ( version < 1 ) + reader.ReadInt(); + + m_Resource = (CraftResource) reader.ReadInt(); + break; + } + } + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Exceptional = ( quality >= 2 ); + + if ( makersMark ) + Crafter = from; + + 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; + + return quality; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/HairRestylingDeed.cs b/Scripts/Items/Deeds/HairRestylingDeed.cs new file mode 100644 index 000000000..63864651f --- /dev/null +++ b/Scripts/Items/Deeds/HairRestylingDeed.cs @@ -0,0 +1,147 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Prompts; +using Server.Items; +using Server.Targeting; +using Server.Gumps; + +namespace Server.Items +{ + public class HairRestylingDeed : Item + { + public override int LabelNumber{ get{ return 1041061; } } // a coupon for a free hair restyling + + [Constructable] + public HairRestylingDeed() : base( 0x14F0 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public HairRestylingDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.Race == Race.Elf ) + { + from.SendMessage( "This isn't implemented for elves yet. Sorry!" ); + return; + } + + if ( !IsChildOf( from.Backpack ) ) + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + else + from.SendGump( new InternalGump( from, this ) ); + } + + private class InternalGump : Gump + { + private Mobile m_From; + private HairRestylingDeed m_Deed; + + public InternalGump( Mobile from, HairRestylingDeed deed ) : base( 50, 50 ) + { + m_From = from; + m_Deed = deed; + + from.CloseGump( typeof( InternalGump ) ); + + AddBackground( 100, 10, 400, 385, 0xA28 ); + + AddHtmlLocalized( 100, 25, 400, 35, 1013008, false, false ); + AddButton( 175, 340, 0xFA5, 0xFA7, 0x0, GumpButtonType.Reply, 0 ); // CANCEL + + AddHtmlLocalized( 210, 342, 90, 35, 1011012, false, false );//
HAIRSTYLE SELECTION MENU
+ + AddBackground( 220, 60, 50, 50, 0xA3C ); + AddBackground( 220, 115, 50, 50, 0xA3C ); + AddBackground( 220, 170, 50, 50, 0xA3C ); + AddBackground( 220, 225, 50, 50, 0xA3C ); + AddBackground( 425, 60, 50, 50, 0xA3C ); + AddBackground( 425, 115, 50, 50, 0xA3C ); + AddBackground( 425, 170, 50, 50, 0xA3C ); + AddBackground( 425, 225, 50, 50, 0xA3C ); + AddBackground( 425, 280, 50, 50, 0xA3C ); + + AddHtmlLocalized( 150, 75, 80, 35, 1011052, false, false ); // Short + AddHtmlLocalized( 150, 130, 80, 35, 1011053, false, false ); // Long + AddHtmlLocalized( 150, 185, 80, 35, 1011054, false, false ); // Ponytail + AddHtmlLocalized( 150, 240, 80, 35, 1011055, false, false ); // Mohawk + AddHtmlLocalized( 355, 75, 80, 35, 1011047, false, false ); // Pageboy + AddHtmlLocalized( 355, 130, 80, 35, 1011048, false, false ); // Receding + AddHtmlLocalized( 355, 185, 80, 35, 1011049, false, false ); // 2-tails + AddHtmlLocalized( 355, 240, 80, 35, 1011050, false, false ); // Topknot + AddHtmlLocalized( 355, 295, 80, 35, 1011064, false, false ); // Bald + + AddImage( 153, 20, 0xC60C ); + AddImage( 153, 65, 0xED24 ); + AddImage( 153, 120, 0xED1E ); + AddImage( 153, 185, 0xC60F ); + AddImage( 358, 18, 0xED26 ); + AddImage( 358, 75, 0xEDE5 ); + AddImage( 358, 120, 0xED23 ); + AddImage( 362, 190, 0xED29 ); + + AddButton( 118, 73, 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); + AddButton( 118, 128, 0xFA5, 0xFA7, 3, GumpButtonType.Reply, 0 ); + AddButton( 118, 183, 0xFA5, 0xFA7, 4, GumpButtonType.Reply, 0 ); + AddButton( 118, 238, 0xFA5, 0xFA7, 5, GumpButtonType.Reply, 0 ); + AddButton( 323, 73, 0xFA5, 0xFA7, 6, GumpButtonType.Reply, 0 ); + AddButton( 323, 128, 0xFA5, 0xFA7, 7, GumpButtonType.Reply, 0 ); + AddButton( 323, 183, 0xFA5, 0xFA7, 8, GumpButtonType.Reply, 0 ); + AddButton( 323, 238, 0xFA5, 0xFA7, 9, GumpButtonType.Reply, 0 ); + AddButton( 323, 292, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted ) + return; + + if ( info.ButtonID > 0 ) + { + int itemID = 0; + + switch ( info.ButtonID ) + { + case 2: itemID = 0x203B; break; + case 3: itemID = 0x203C; break; + case 4: itemID = 0x203D; break; + case 5: itemID = 0x2044; break; + case 6: itemID = 0x2045; break; + case 7: itemID = m_From.Female ? 0x2046 : 0x2048; break; + case 8: itemID = 0x2049; break; + case 9: itemID = 0x204A; break; + } + + if ( m_From is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m_From; + + pm.SetHairMods( -1, -1 ); // clear any hairmods (disguise kit, incognito) + } + + m_From.HairItemID = itemID; + + m_Deed.Delete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/HolidayTreeDeed.cs b/Scripts/Items/Deeds/HolidayTreeDeed.cs new file mode 100644 index 000000000..f3b8352ce --- /dev/null +++ b/Scripts/Items/Deeds/HolidayTreeDeed.cs @@ -0,0 +1,161 @@ +using System; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Multis; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class HolidayTreeDeed : Item + { + public override int LabelNumber{ get{ return 1041116; } } // a deed for a holiday tree + + [Constructable] + public HolidayTreeDeed() : base( 0x14F0 ) + { + Hue = 0x488; + Weight = 1.0; + LootType = LootType.Blessed; + } + + public HolidayTreeDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + LootType = LootType.Blessed; + } + + public bool ValidatePlacement( Mobile from, Point3D loc ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if ( !from.InRange( this.GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + return false; + } + + if ( DateTime.Now.Month != 12 ) + { + from.SendLocalizedMessage( 1005700 ); // You will have to wait till next December to put your tree back up for display. + return false; + } + + Map map = from.Map; + + if ( map == null ) + return false; + + BaseHouse house = BaseHouse.FindHouseAt( loc, map, 20 ); + + if ( house == null || !house.IsFriend( from ) ) + { + from.SendLocalizedMessage( 1005701 ); // The holiday tree can only be placed in your house. + return false; + } + + if ( !map.CanFit( loc, 20 ) ) + { + from.SendLocalizedMessage( 500269 ); // You cannot build that there. + return false; + } + + return true; + } + + public void BeginPlace( Mobile from, HolidayTreeType type ) + { + from.BeginTarget( -1, true, TargetFlags.None, new TargetStateCallback( Placement_OnTarget ), type ); + } + + public void Placement_OnTarget( Mobile from, object targeted, object state ) + { + IPoint3D p = targeted as IPoint3D; + + if ( p == null ) + return; + + Point3D loc = new Point3D( p ); + + if ( p is StaticTarget ) + loc.Z -= TileData.ItemTable[((StaticTarget)p).ItemID & 0x3FFF].CalcHeight; /* NOTE: OSI does not properly normalize Z positioning here. + * A side affect is that you can only place on floors (due to the CanFit call). + * That functionality may be desired. And so, it's included in this script. + */ + + if ( ValidatePlacement( from, loc ) ) + EndPlace( from, (HolidayTreeType) state, loc ); + } + + public void EndPlace( Mobile from, HolidayTreeType type, Point3D loc ) + { + this.Delete(); + new HolidayTree( from, type, loc ); + } + + public override void OnDoubleClick( Mobile from ) + { + from.CloseGump( typeof( HolidayTreeChoiceGump ) ); + from.SendGump( new HolidayTreeChoiceGump( from, this ) ); + } + } + + public class HolidayTreeChoiceGump : Gump + { + private Mobile m_From; + private HolidayTreeDeed m_Deed; + + public HolidayTreeChoiceGump( Mobile from, HolidayTreeDeed deed ) : base( 200, 200 ) + { + m_From = from; + m_Deed = deed; + + AddPage( 0 ); + + AddBackground( 0, 0, 220, 120, 5054 ); + AddBackground( 10, 10, 200, 100, 3000 ); + + AddButton( 20, 35, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 35, 145, 25, 1018322, false, false ); // Classic + + AddButton( 20, 65, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 65, 145, 25, 1018321, false, false ); // Modern + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted ) + return; + + switch ( info.ButtonID ) + { + case 1: + { + m_Deed.BeginPlace( m_From, HolidayTreeType.Classic ); + break; + } + case 2: + { + m_Deed.BeginPlace( m_From, HolidayTreeType.Modern ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/NameChangeDeed.cs b/Scripts/Items/Deeds/NameChangeDeed.cs new file mode 100644 index 000000000..287fe6806 --- /dev/null +++ b/Scripts/Items/Deeds/NameChangeDeed.cs @@ -0,0 +1,46 @@ +using System; +using Server.Network; +using Server.Prompts; +using Server.Items; + +namespace Server.Items +{ + public class NameChangeDeed : Item + { + public override string DefaultName + { + get { return "a name change deed"; } + } + + [Constructable] + public NameChangeDeed() : base( 0x14F0 ) + { + base.Weight = 1.0; + } + + public NameChangeDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + // Do namechange + } + } +} + + diff --git a/Scripts/Items/Deeds/NewPlayerTicket.cs b/Scripts/Items/Deeds/NewPlayerTicket.cs new file mode 100644 index 000000000..2029892a0 --- /dev/null +++ b/Scripts/Items/Deeds/NewPlayerTicket.cs @@ -0,0 +1,198 @@ +using System; +using Server.Network; +using Server.Prompts; +using Server.Items; +using Server.Targeting; +using Server.Gumps; + +namespace Server.Items +{ + public class NewPlayerTicket : Item + { + private Mobile m_Owner; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + public override int LabelNumber{ get{ return 1062094; } } // a young player ticket + + [Constructable] + public NewPlayerTicket() : base( 0x14EF ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1041492 ); // This is half a prize ticket! Double-click this ticket and target any other ticket marked NEW PLAYER and get a prize! This ticket will only work for YOU, so don't give it away! + } + + public override bool DisplayLootType{ get{ return false; } } + + public NewPlayerTicket( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Owner ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Owner = reader.ReadMobile(); + break; + } + } + + if ( Name == "a young player ticket" ) + Name = null; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from != m_Owner ) + { + from.SendLocalizedMessage( 501926 ); // This isn't your ticket! Shame on you! You have to use YOUR ticket. + } + else if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + from.SendLocalizedMessage( 501927 ); // Target any other ticket marked NEW PLAYER to win a prize. + from.Target = new InternalTarget( this ); + } + } + + private class InternalTarget : Target + { + private NewPlayerTicket m_Ticket; + + public InternalTarget( NewPlayerTicket ticket ) : base( 2, false, TargetFlags.None ) + { + m_Ticket = ticket; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted == m_Ticket ) + { + from.SendLocalizedMessage( 501928 ); // You can't target the same ticket! + } + else if ( targeted is NewPlayerTicket ) + { + NewPlayerTicket theirTicket = targeted as NewPlayerTicket; + Mobile them = theirTicket.m_Owner; + + if ( them == null || them.Deleted ) + { + from.SendLocalizedMessage( 501930 ); // That is not a valid ticket. + } + else + { + from.SendGump( new InternalGump( from, m_Ticket ) ); + them.SendGump( new InternalGump( them, theirTicket ) ); + } + } + else if ( targeted is Item && ((Item)targeted).ItemID == 0x14F0 ) + { + from.SendLocalizedMessage( 501931 ); // You need to find another ticket marked NEW PLAYER. + } + else + { + from.SendLocalizedMessage( 501929 ); // You will need to select a ticket. + } + } + } + + private class InternalGump : Gump + { + private Mobile m_From; + private NewPlayerTicket m_Ticket; + + public InternalGump( Mobile from, NewPlayerTicket ticket ) : base( 50, 50 ) + { + m_From = from; + m_Ticket = ticket; + + AddBackground( 0, 0, 400, 385, 0xA28 ); + + AddHtmlLocalized( 30, 45, 340, 70, 1013011, true, true ); // Choose the gift you prefer. WARNING: if you cancel, and your partner does not, you will need to find another matching ticket! + + AddButton( 46, 128, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 130, 320, 35, 1013012, false, false ); // A sextant + + AddButton( 46, 163, 0xFA5, 0xFA7, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 165, 320, 35, 1013013, false, false ); // A coupon for a single hair restyling + + AddButton( 46, 198, 0xFA5, 0xFA7, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 200, 320, 35, 1013014, false, false ); // A spellbook with all 1st - 4th spells. + + AddButton( 46, 233, 0xFA5, 0xFA7, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 235, 320, 35, 1013015, false, false ); // A wand of fireworks + + AddButton( 46, 268, 0xFA5, 0xFA7, 5, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 270, 320, 35, 1013016, false, false ); // A spyglass + + AddButton( 46, 303, 0xFA5, 0xFA7, 6, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 305, 320, 35, 1013017, false, false ); // Dyes and a dye tub + + AddButton( 120, 340, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 154, 342, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Ticket.Deleted ) + return; + + int number = 0; + + Item item = null; + Item item2 = null; + + switch ( info.ButtonID ) + { + case 1: item = new Sextant(); number = 1010494; break; // A sextant has been placed in your backpack. + case 2: item = new HairRestylingDeed(); number = 501933; break; // A coupon for a free hair restyling has been placed in your backpack. + case 3: item = new Spellbook( 0xFFFFFFFF ); number = 1010495; break; // A spellbook with all 1st to 4th circle spells has been placed in your backpack. + case 4: item = new FireworksWand(); number = 501935; break; // A wand of fireworks has been placed in your backpack. + case 5: item = new Spyglass(); number = 501936; break; // A spyglass has been placed in your backpack. + case 6: item = new DyeTub(); item2 = new Dyes(); number = 501937; break; // The dyes and dye tub have been placed in your backpack. + } + + if ( item != null ) + { + m_Ticket.Delete(); + + m_From.SendLocalizedMessage( number ); + m_From.AddToBackpack( item ); + + if ( item2 != null ) + m_From.AddToBackpack( item2 ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Deeds/VendorRentalContract.cs b/Scripts/Items/Deeds/VendorRentalContract.cs new file mode 100644 index 000000000..434cca02f --- /dev/null +++ b/Scripts/Items/Deeds/VendorRentalContract.cs @@ -0,0 +1,368 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Multis; +using Server.ContextMenus; +using Server.Gumps; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Items +{ + public class VendorRentalContract : Item + { + public override int LabelNumber{ get{ return 1062332; } } // a vendor rental contract + + private VendorRentalDuration m_Duration; + private int m_Price; + private bool m_LandlordRenew; + + private Mobile m_Offeree; + private Timer m_OfferExpireTimer; + + public VendorRentalDuration Duration + { + get{ return m_Duration; } + set + { + if ( value != null ) + m_Duration = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Price + { + get{ return m_Price; } + set{ m_Price = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool LandlordRenew + { + get{ return m_LandlordRenew; } + set{ m_LandlordRenew = value; } + } + + public Mobile Offeree + { + get{ return m_Offeree; } + set + { + if ( m_OfferExpireTimer != null ) + { + m_OfferExpireTimer.Stop(); + m_OfferExpireTimer = null; + } + + m_Offeree = value; + + if ( value != null ) + { + m_OfferExpireTimer = new OfferExpireTimer( this ); + m_OfferExpireTimer.Start(); + } + + InvalidateProperties(); + } + } + + [Constructable] + public VendorRentalContract() : base( 0x14F0 ) + { + Weight = 1.0; + Hue = 0x672; + + m_Duration = VendorRentalDuration.Instances[0]; + m_Price = 1500; + } + + public VendorRentalContract( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( Offeree != null ) + list.Add( 1062368, Offeree.Name ); // Being Offered To ~1_NAME~ + } + + public bool IsLandlord( Mobile m ) + { + if ( IsLockedDown ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.DecayType != DecayType.Condemned ) + return house.IsOwner( m ); + } + + return false; + } + + public bool IsUsableBy( Mobile from, bool byLandlord, bool byBackpack, bool noOfferee, bool sendMessage ) + { + if ( this.Deleted || !from.CheckAlive( sendMessage ) ) + return false; + + if ( noOfferee && Offeree != null ) + { + if ( sendMessage ) + from.SendLocalizedMessage( 1062343 ); // That item is currently in use. + + return false; + } + + if ( byBackpack && IsChildOf( from.Backpack ) ) + return true; + + if ( byLandlord && IsLandlord( from ) ) + { + if ( from.Map != this.Map || !from.InRange( this, 5 ) ) + { + if ( sendMessage ) + from.SendLocalizedMessage( 501853 ); // Target is too far away. + + return false; + } + + return true; + } + + return false; + } + + public override void OnDelete() + { + if ( IsLockedDown ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null ) + { + house.VendorRentalContracts.Remove( this ); + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( Offeree != null ) + { + from.SendLocalizedMessage( 1062343 ); // That item is currently in use. + } + else if ( !IsLockedDown ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062334 ); // This item must be in your backpack to be used. + return; + } + + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house == null || !house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 1062333 ); // You must be standing inside of a house that you own to make use of this contract. + } + else if ( !house.IsAosRules ) + { + from.SendMessage( "Rental contracts can only be placed in AOS-enabled houses." ); + } + else if ( !house.Public ) + { + from.SendLocalizedMessage( 1062335 ); // Rental contracts can only be placed in public houses. + } + else if ( !house.CanPlaceNewVendor() ) + { + from.SendLocalizedMessage( 1062352 ); // You do not have enought storage available to place this contract. + } + else + { + from.SendLocalizedMessage( 1062337 ); // Target the exact location you wish to rent out. + from.Target = new RentTarget( this ); + } + } + else if ( IsLandlord( from ) ) + { + if ( from.InRange( this, 5 ) ) + { + from.CloseGump( typeof( VendorRentalContractGump ) ); + from.SendGump( new VendorRentalContractGump( this, from ) ); + } + else + { + from.SendLocalizedMessage( 501853 ); // Target is too far away. + } + } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( IsUsableBy( from, true, true, true, false ) ) + { + list.Add( new ContractOptionEntry( this ) ); + } + } + + private class ContractOptionEntry : ContextMenuEntry + { + private VendorRentalContract m_Contract; + + public ContractOptionEntry( VendorRentalContract contract ) : base( 6209 ) + { + m_Contract = contract; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( m_Contract.IsUsableBy( from, true, true, true, true ) ) + { + from.CloseGump( typeof( VendorRentalContractGump ) ); + from.SendGump( new VendorRentalContractGump( m_Contract, from ) ); + } + } + } + + private class RentTarget : Target + { + private VendorRentalContract m_Contract; + + public RentTarget( VendorRentalContract contract ) : base( -1, false, TargetFlags.None ) + { + m_Contract = contract; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Contract.IsUsableBy( from, false, true, true, true ) ) + return; + + IPoint3D location = targeted as IPoint3D; + if ( location == null ) + return; + + Point3D pLocation = new Point3D( location ); + Map map = from.Map; + + BaseHouse house = BaseHouse.FindHouseAt( pLocation, map, 0 ); + + if ( house == null || !house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 1062338 ); // The location being rented out must be inside of your house. + } + else if ( BaseHouse.FindHouseAt( from ) != house ) + { + from.SendLocalizedMessage( 1062339 ); // You must be located inside of the house in which you are trying to place the contract. + } + else if ( !house.IsAosRules ) + { + from.SendMessage( "Rental contracts can only be placed in AOS-enabled houses." ); + } + else if ( !house.Public ) + { + from.SendLocalizedMessage( 1062335 ); // Rental contracts can only be placed in public houses. + } + else if ( house.DecayType == DecayType.Condemned ) + { + from.SendLocalizedMessage( 1062468 ); // You cannot place a contract in a condemned house. + } + else if ( !house.CanPlaceNewVendor() ) + { + from.SendLocalizedMessage( 1062352 ); // You do not have enought storage available to place this contract. + } + else if ( !map.CanFit( pLocation, 16, false, false ) ) + { + from.SendLocalizedMessage( 1062486 ); // A vendor cannot exist at that location. Please try again. + } + else + { + bool vendor, contract; + BaseHouse.IsThereVendor( pLocation, map, out vendor, out contract ); + + if ( vendor ) + { + from.SendLocalizedMessage( 1062342 ); // You may not place a rental contract at this location while other beings occupy it. + } + else if ( contract ) + { + from.SendLocalizedMessage( 1062341 ); // That location is cluttered. Please clear out any objects there and try again. + } + else + { + m_Contract.MoveToWorld( pLocation, map ); + + if ( !house.LockDown( from, m_Contract ) ) + { + from.AddToBackpack( m_Contract ); + } + } + } + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + from.SendLocalizedMessage( 1062336 ); // You decide not to place the contract at this time. + } + } + + private class OfferExpireTimer : Timer + { + private VendorRentalContract m_Contract; + + public OfferExpireTimer( VendorRentalContract contract ) : base( TimeSpan.FromSeconds( 30.0 ) ) + { + m_Contract = contract; + + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + Mobile offeree = m_Contract.Offeree; + + if ( offeree != null ) + { + offeree.CloseGump( typeof( VendorRentalOfferGump ) ); + + m_Contract.Offeree = null; + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.WriteEncodedInt( m_Duration.ID ); + + writer.Write( (int) m_Price ); + writer.Write( (bool) m_LandlordRenew ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + int durationID = reader.ReadEncodedInt(); + if ( durationID < VendorRentalDuration.Instances.Length ) + m_Duration = VendorRentalDuration.Instances[durationID]; + else + m_Duration = VendorRentalDuration.Instances[0]; + + m_Price = reader.ReadInt(); + m_LandlordRenew = reader.ReadBool(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Facial/Beard.cs b/Scripts/Items/Facial/Beard.cs new file mode 100644 index 000000000..8129dfd83 --- /dev/null +++ b/Scripts/Items/Facial/Beard.cs @@ -0,0 +1,330 @@ +using System; + +namespace Server.Items +{ + public abstract class Beard : Item + { + /*public static Beard CreateByID( int id, int hue ) + { + switch ( id ) + { + case 0x203E: return new LongBeard( hue ); + case 0x203F: return new ShortBeard( hue ); + case 0x2040: return new Goatee( hue ); + case 0x2041: return new Mustache( hue ); + case 0x204B: return new MediumShortBeard( hue ); + case 0x204C: return new MediumLongBeard( hue ); + case 0x204D: return new Vandyke( hue ); + default: return new GenericBeard( id, hue ); + } + }*/ + + protected Beard( int itemID ) : this( itemID, 0 ) + { + } + + protected Beard( int itemID, int hue ) : base( itemID ) + { + LootType = LootType.Blessed; + Layer = Layer.FacialHair; + Hue = hue; + } + + public Beard( Serial serial ) : base( serial ) + { + } + + public override bool DisplayLootType{ get{ return false; } } + + public override bool VerifyMove( Mobile from ) + { + return ( from.AccessLevel >= AccessLevel.GameMaster ); + } + + public override DeathMoveResult OnParentDeath( Mobile parent ) + { + //Dupe( Amount ); + + parent.FacialHairItemID = this.ItemID; + parent.FacialHairHue = this.Hue; + + return DeathMoveResult.MoveToCorpse; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + } + } + + public class GenericBeard : Beard + { + + private GenericBeard( int itemID ) : this( itemID, 0 ) + { + } + + + private GenericBeard( int itemID, int hue ) : base( itemID, hue ) + { + } + + public GenericBeard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LongBeard : Beard + { + + private LongBeard() + : this( 0 ) + { + } + + private LongBeard( int hue ) + : base( 0x203E, hue ) + { + } + + public LongBeard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ShortBeard : Beard + { + + private ShortBeard() + : this( 0 ) + { + } + + + private ShortBeard( int hue ) + : base( 0x203f, hue ) + { + } + + public ShortBeard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Goatee : Beard + { + + private Goatee() + : this( 0 ) + { + } + + + private Goatee( int hue ) + : base( 0x2040, hue ) + { + } + + public Goatee( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Mustache : Beard + { + + private Mustache() + : this( 0 ) + { + } + + + private Mustache( int hue ) + : base( 0x2041, hue ) + { + } + + public Mustache( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumShortBeard : Beard + { + + private MediumShortBeard() + : this( 0 ) + { + } + + + private MediumShortBeard( int hue ) + : base( 0x204B, hue ) + { + } + + public MediumShortBeard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MediumLongBeard : Beard + { + + private MediumLongBeard() + : this( 0 ) + { + } + + + private MediumLongBeard( int hue ) + : base( 0x204C, hue ) + { + } + + public MediumLongBeard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Vandyke : Beard + { + + private Vandyke() + : this( 0 ) + { + } + + + private Vandyke( int hue ) + : base( 0x204D, hue ) + { + } + + public Vandyke( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Facial/Hair.cs b/Scripts/Items/Facial/Hair.cs new file mode 100644 index 000000000..fc3797545 --- /dev/null +++ b/Scripts/Items/Facial/Hair.cs @@ -0,0 +1,491 @@ +using System; + +namespace Server.Items +{ + public abstract class Hair : Item + { + /* + + public static Hair GetRandomHair( bool female ) + { + return GetRandomHair( female, Utility.RandomHairHue() ); + } + + public static Hair GetRandomHair( bool female, int hairHue ) + { + if( female ) + { + switch ( Utility.Random( 9 ) ) + { + case 0: return new Afro( hairHue ); + case 1: return new KrisnaHair( hairHue ); + case 2: return new PageboyHair( hairHue ); + case 3: return new PonyTail( hairHue ); + case 4: return new ReceedingHair( hairHue ); + case 5: return new TwoPigTails( hairHue ); + case 6: return new ShortHair( hairHue ); + case 7: return new LongHair( hairHue ); + default: return new BunsHair( hairHue ); + } + } + else + { + switch ( Utility.Random( 8 ) ) + { + case 0: return new Afro( hairHue ); + case 1: return new KrisnaHair( hairHue ); + case 2: return new PageboyHair( hairHue ); + case 3: return new PonyTail( hairHue ); + case 4: return new ReceedingHair( hairHue ); + case 5: return new TwoPigTails( hairHue ); + case 6: return new ShortHair( hairHue ); + default: return new LongHair( hairHue ); + } + } + } + + + public static Hair CreateByID( int id, int hue ) + { + switch ( id ) + { + case 0x203B: return new ShortHair( hue ); + case 0x203C: return new LongHair( hue ); + case 0x203D: return new PonyTail( hue ); + case 0x2044: return new Mohawk( hue ); + case 0x2045: return new PageboyHair( hue ); + case 0x2046: return new BunsHair( hue ); + case 0x2047: return new Afro( hue ); + case 0x2048: return new ReceedingHair( hue ); + case 0x2049: return new TwoPigTails( hue ); + case 0x204A: return new KrisnaHair( hue ); + default: return new GenericHair( id, hue ); + } + } + * */ + + protected Hair( int itemID ) + : this( itemID, 0 ) + { + } + + protected Hair( int itemID, int hue ) + : base( itemID ) + { + LootType = LootType.Blessed; + Layer = Layer.Hair; + Hue = hue; + } + + public Hair( Serial serial ) + : base( serial ) + { + } + + public override bool DisplayLootType { get { return false; } } + + public override bool VerifyMove( Mobile from ) + { + return (from.AccessLevel >= AccessLevel.GameMaster); + } + + public override DeathMoveResult OnParentDeath( Mobile parent ) + { +// Dupe( Amount ); + + parent.HairItemID = this.ItemID; + parent.HairHue = this.Hue; + + return DeathMoveResult.MoveToCorpse; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + } + } + + public class GenericHair : Hair + { + + private GenericHair( int itemID ) + : this( itemID, 0 ) + { + } + + + private GenericHair( int itemID, int hue ) + : base( itemID, hue ) + { + } + + public GenericHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Mohawk : Hair + { + + private Mohawk() + : this( 0 ) + { + } + + + private Mohawk( int hue ) + : base( 0x2044, hue ) + { + } + + public Mohawk( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PageboyHair : Hair + { + + private PageboyHair() + : this( 0 ) + { + } + + + private PageboyHair( int hue ) + : base( 0x2045, hue ) + { + } + + public PageboyHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BunsHair : Hair + { + + private BunsHair() + : this( 0 ) + { + } + + + private BunsHair( int hue ) + : base( 0x2046, hue ) + { + } + + public BunsHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LongHair : Hair + { + + private LongHair() + : this( 0 ) + { + } + + + private LongHair( int hue ) + : base( 0x203C, hue ) + { + } + + public LongHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ShortHair : Hair + { + + private ShortHair() + : this( 0 ) + { + } + + + private ShortHair( int hue ) + : base( 0x203B, hue ) + { + } + + public ShortHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PonyTail : Hair + { + + private PonyTail() + : this( 0 ) + { + } + + + private PonyTail( int hue ) + : base( 0x203D, hue ) + { + } + + public PonyTail( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Afro : Hair + { + + private Afro() + : this( 0 ) + { + } + + + private Afro( int hue ) + : base( 0x2047, hue ) + { + } + + public Afro( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ReceedingHair : Hair + { + + private ReceedingHair() + : this( 0 ) + { + } + + + private ReceedingHair( int hue ) + : base( 0x2048, hue ) + { + } + + public ReceedingHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TwoPigTails : Hair + { + + private TwoPigTails() + : this( 0 ) + { + } + + + private TwoPigTails( int hue ) + : base( 0x2049, hue ) + { + } + + public TwoPigTails( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class KrisnaHair : Hair + { + + private KrisnaHair() + : this( 0 ) + { + } + + + private KrisnaHair( int hue ) + : base( 0x204A, hue ) + { + } + + public KrisnaHair( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableCabbage.cs b/Scripts/Items/Farming/FarmableCabbage.cs new file mode 100644 index 000000000..cbca259c4 --- /dev/null +++ b/Scripts/Items/Farming/FarmableCabbage.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableCabbage : FarmableCrop + { + public static int GetCropID() + { + return 3254; + } + + public override Item GetCropObject() + { + Cabbage cabbage = new Cabbage(); + + cabbage.ItemID = Utility.Random( 3195, 2 ); + + return cabbage; + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableCabbage() : base( GetCropID() ) + { + } + + public FarmableCabbage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableCarrot.cs b/Scripts/Items/Farming/FarmableCarrot.cs new file mode 100644 index 000000000..e90c74238 --- /dev/null +++ b/Scripts/Items/Farming/FarmableCarrot.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableCarrot : FarmableCrop + { + public static int GetCropID() + { + return 3190; + } + + public override Item GetCropObject() + { + Carrot carrot = new Carrot(); + + carrot.ItemID = Utility.Random( 3191, 2 ); + + return carrot; + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableCarrot() : base( GetCropID() ) + { + } + + public FarmableCarrot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableCotton.cs b/Scripts/Items/Farming/FarmableCotton.cs new file mode 100644 index 000000000..22de5ca63 --- /dev/null +++ b/Scripts/Items/Farming/FarmableCotton.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableCotton : FarmableCrop + { + public static int GetCropID() + { + return Utility.Random( 3153, 4 ); + } + + public override Item GetCropObject() + { + return new Cotton(); + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableCotton() : base( GetCropID() ) + { + } + + public FarmableCotton( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableCrop.cs b/Scripts/Items/Farming/FarmableCrop.cs new file mode 100644 index 000000000..adab6efbd --- /dev/null +++ b/Scripts/Items/Farming/FarmableCrop.cs @@ -0,0 +1,94 @@ +using System; +using Server; +using Server.Network; +using Server.Regions; + +namespace Server.Items +{ + public abstract class FarmableCrop : Item + { + private bool m_Picked; + + public abstract Item GetCropObject(); + public abstract int GetPickedID(); + + public FarmableCrop( int itemID ) : base( itemID ) + { + Movable = false; + } + + public override void OnDoubleClick( Mobile from ) + { + Map map = this.Map; + Point3D loc = this.Location; + + if ( Parent != null || Movable || IsLockedDown || IsSecure || map == null || map == Map.Internal ) + return; + + if ( !from.InRange( loc, 2 ) || !from.InLOS( this ) ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else if ( !m_Picked ) + OnPicked( from, loc, map ); + } + + public virtual void OnPicked( Mobile from, Point3D loc, Map map ) + { + ItemID = GetPickedID(); + + Item spawn = GetCropObject(); + + if ( spawn != null ) + spawn.MoveToWorld( loc, map ); + + m_Picked = true; + + Unlink(); + + Timer.DelayCall( TimeSpan.FromMinutes( 5.0 ), new TimerCallback( Delete ) ); + } + + public void Unlink() + { + SpawnEntry se = this.Spawner as SpawnEntry; + + if ( se != null ) + { + this.Spawner.Remove( this ); + this.Spawner = null; + } + + } + + public FarmableCrop( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.Write( m_Picked ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + m_Picked = reader.ReadBool(); + break; + } + if ( m_Picked ) + { + Unlink(); + Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableFlax.cs b/Scripts/Items/Farming/FarmableFlax.cs new file mode 100644 index 000000000..578174d7b --- /dev/null +++ b/Scripts/Items/Farming/FarmableFlax.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableFlax : FarmableCrop + { + public static int GetCropID() + { + return Utility.Random( 6809, 3 ); + } + + public override Item GetCropObject() + { + Flax flax = new Flax(); + + flax.ItemID = Utility.Random( 6812, 2 ); + + return flax; + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableFlax() : base( GetCropID() ) + { + } + + public FarmableFlax( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableLettuce.cs b/Scripts/Items/Farming/FarmableLettuce.cs new file mode 100644 index 000000000..30c150601 --- /dev/null +++ b/Scripts/Items/Farming/FarmableLettuce.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableLettuce : FarmableCrop + { + public static int GetCropID() + { + return 3254; + } + + public override Item GetCropObject() + { + Lettuce lettuce = new Lettuce(); + + lettuce.ItemID = Utility.Random( 3184, 2 ); + + return lettuce; + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableLettuce() : base( GetCropID() ) + { + } + + public FarmableLettuce( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableOnion.cs b/Scripts/Items/Farming/FarmableOnion.cs new file mode 100644 index 000000000..b4234b61c --- /dev/null +++ b/Scripts/Items/Farming/FarmableOnion.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableOnion : FarmableCrop + { + public static int GetCropID() + { + return 3183; + } + + public override Item GetCropObject() + { + Onion onion = new Onion(); + + onion.ItemID = Utility.Random( 3181, 2 ); + + return onion; + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableOnion() : base( GetCropID() ) + { + } + + public FarmableOnion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmablePumpkin.cs b/Scripts/Items/Farming/FarmablePumpkin.cs new file mode 100644 index 000000000..5a9ae6087 --- /dev/null +++ b/Scripts/Items/Farming/FarmablePumpkin.cs @@ -0,0 +1,53 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmablePumpkin : FarmableCrop + { + public static int GetCropID() + { + return Utility.Random( 3166, 3 ); + } + + public override Item GetCropObject() + { + Pumpkin pumpkin = new Pumpkin(); + + pumpkin.ItemID = Utility.Random( 3178, 3 ); + + return pumpkin; + } + + public override int GetPickedID() + { + return Utility.Random( 3166, 3 ); + } + + [Constructable] + public FarmablePumpkin() + : base( GetCropID() ) + { + } + + public FarmablePumpkin( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableTurnip.cs b/Scripts/Items/Farming/FarmableTurnip.cs new file mode 100644 index 000000000..227917cf1 --- /dev/null +++ b/Scripts/Items/Farming/FarmableTurnip.cs @@ -0,0 +1,51 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableTurnip : FarmableCrop + { + public static int GetCropID() + { + return Utility.Random( 3169, 3 ); + } + + public override Item GetCropObject() + { + Turnip turnip = new Turnip(); + + turnip.ItemID = Utility.Random( 3385, 2 ); + + return turnip; + } + + public override int GetPickedID() + { + return 3254; + } + + [Constructable] + public FarmableTurnip() : base( GetCropID() ) + { + } + + public FarmableTurnip( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Farming/FarmableWheat.cs b/Scripts/Items/Farming/FarmableWheat.cs new file mode 100644 index 000000000..87e983d82 --- /dev/null +++ b/Scripts/Items/Farming/FarmableWheat.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class FarmableWheat : FarmableCrop + { + public static int GetCropID() + { + return Utility.Random( 3157, 4 ); + } + + public override Item GetCropObject() + { + return new WheatSheaf(); + } + + public override int GetPickedID() + { + return Utility.Random( 3502, 2 ); + } + + [Constructable] + public FarmableWheat() : base( GetCropID() ) + { + } + + public FarmableWheat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/Asian.cs b/Scripts/Items/Food/Asian.cs new file mode 100644 index 000000000..19ed5cfd4 --- /dev/null +++ b/Scripts/Items/Food/Asian.cs @@ -0,0 +1,355 @@ +using System; + +namespace Server.Items +{ + public class Wasabi : Item + { + [Constructable] + public Wasabi() : base( 0x24E8 ) + { + Weight = 1.0; + } + + public Wasabi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WasabiClumps : Food + { + [Constructable] + public WasabiClumps() : base( 0x24EB ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public WasabiClumps( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyBentoBox : Item + { + [Constructable] + public EmptyBentoBox() : base( 0x2834 ) + { + Weight = 5.0; + } + + public EmptyBentoBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BentoBox : Food + { + [Constructable] + public BentoBox() : base( 0x2836 ) + { + Stackable = false; + Weight = 5.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyBentoBox() ); + return true; + } + + public BentoBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SushiRolls : Food + { + [Constructable] + public SushiRolls() : base( 0x283E ) + { + Stackable = false; + Weight = 3.0; + FillFactor = 2; + } + + public SushiRolls( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SushiPlatter : Food + { + [Constructable] + public SushiPlatter() : base( 0x2840 ) + { + Stackable = false; + Weight = 3.0; + FillFactor = 2; + } + + public SushiPlatter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenTeaBasket : Item + { + [Constructable] + public GreenTeaBasket() : base( 0x284B ) + { + Weight = 10.0; + } + + public GreenTeaBasket( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenTea : Food + { + [Constructable] + public GreenTea() : base( 0x284C ) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } + + public GreenTea( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MisoSoup : Food + { + [Constructable] + public MisoSoup() : base( 0x284D ) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } + + public MisoSoup( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WhiteMisoSoup : Food + { + [Constructable] + public WhiteMisoSoup() : base( 0x284E ) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } + + public WhiteMisoSoup( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RedMisoSoup : Food + { + [Constructable] + public RedMisoSoup() : base( 0x284F ) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } + + public RedMisoSoup( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AwaseMisoSoup : Food + { + [Constructable] + public AwaseMisoSoup() : base( 0x2850 ) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } + + public AwaseMisoSoup( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/Beverage.cs b/Scripts/Items/Food/Beverage.cs new file mode 100644 index 000000000..82a46a6fe --- /dev/null +++ b/Scripts/Items/Food/Beverage.cs @@ -0,0 +1,1162 @@ +using System; +using Server; +using Server.Network; +using Server.Targeting; +using Server.Engines.Plants; +using System.Collections; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Hag; +using Server.Engines.Quests.Matriarch; + +namespace Server.Items +{ + public enum BeverageType + { + Ale, + Cider, + Liquor, + Milk, + Wine, + Water + } + + public interface IHasQuantity + { + int Quantity{ get; set; } + } + + public interface IWaterSource : IHasQuantity + { + } + + // TODO: Flipable attributes + + [TypeAlias( "Server.Items.BottleAle", "Server.Items.BottleLiquor", "Server.Items.BottleWine" )] + public class BeverageBottle : BaseBeverage + { + public override int BaseLabelNumber{ get{ return 1042959; } } // a bottle of Ale + public override int MaxQuantity{ get{ return 5; } } + public override bool Fillable{ get{ return false; } } + + public override int ComputeItemID() + { + if ( !IsEmpty ) + { + switch ( Content ) + { + case BeverageType.Ale: return 0x99F; + case BeverageType.Cider: return 0x99F; + case BeverageType.Liquor: return 0x99B; + case BeverageType.Milk: return 0x99B; + case BeverageType.Wine: return 0x9C7; + case BeverageType.Water: return 0x99B; + } + } + + return 0; + } + + [Constructable] + public BeverageBottle( BeverageType type ) : base( type ) + { + Weight = 1.0; + } + + public BeverageBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + if ( CheckType( "BottleAle" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Ale; + } + else if ( CheckType( "BottleLiquor" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Liquor; + } + else if ( CheckType( "BottleWine" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Wine; + } + else + { + throw new Exception( World.LoadingType ); + } + + break; + } + } + } + } + + public class Jug : BaseBeverage + { + public override int BaseLabelNumber{ get{ return 1042965; } } // a jug of Ale + public override int MaxQuantity{ get{ return 10; } } + public override bool Fillable{ get{ return false; } } + + public override int ComputeItemID() + { + if ( !IsEmpty ) + return 0x9C8; + + return 0; + } + + [Constructable] + public Jug( BeverageType type ) : base( type ) + { + Weight = 1.0; + } + + public Jug( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CeramicMug : BaseBeverage + { + public override int BaseLabelNumber{ get{ return 1042982; } } // a ceramic mug of Ale + public override int MaxQuantity{ get{ return 1; } } + + public override int ComputeItemID() + { + if ( ItemID >= 0x995 && ItemID <= 0x999 ) + return ItemID; + else if ( ItemID == 0x9CA ) + return ItemID; + + return 0x995; + } + + [Constructable] + public CeramicMug() + { + Weight = 1.0; + } + + [Constructable] + public CeramicMug( BeverageType type ) : base( type ) + { + Weight = 1.0; + } + + public CeramicMug( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PewterMug : BaseBeverage + { + public override int BaseLabelNumber{ get{ return 1042994; } } // a pewter mug with Ale + public override int MaxQuantity{ get{ return 1; } } + + public override int ComputeItemID() + { + if ( ItemID >= 0xFFF && ItemID <= 0x1002 ) + return ItemID; + + return 0xFFF; + } + + [Constructable] + public PewterMug() + { + Weight = 1.0; + } + + [Constructable] + public PewterMug( BeverageType type ) : base( type ) + { + Weight = 1.0; + } + + public PewterMug( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Goblet : BaseBeverage + { + public override int BaseLabelNumber{ get{ return 1043000; } } // a goblet of Ale + public override int MaxQuantity{ get{ return 1; } } + + public override int ComputeItemID() + { + if ( ItemID == 0x99A || ItemID == 0x9B3 || ItemID == 0x9BF || ItemID == 0x9CB ) + return ItemID; + + return 0x99A; + } + + [Constructable] + public Goblet() + { + Weight = 1.0; + } + + [Constructable] + public Goblet( BeverageType type ) : base( type ) + { + Weight = 1.0; + } + + public Goblet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [TypeAlias( "Server.Items.MugAle", "Server.Items.GlassCider", "Server.Items.GlassLiquor", + "Server.Items.GlassMilk", "Server.Items.GlassWine", "Server.Items.GlassWater" )] + public class GlassMug : BaseBeverage + { + public override int EmptyLabelNumber{ get{ return 1022456; } } // mug + public override int BaseLabelNumber{ get{ return 1042976; } } // a mug of Ale + public override int MaxQuantity{ get{ return 5; } } + + public override int ComputeItemID() + { + if ( IsEmpty ) + return (ItemID >= 0x1F81 && ItemID <= 0x1F84 ? ItemID : 0x1F81); + + switch ( Content ) + { + case BeverageType.Ale: return (ItemID == 0x9EF ? 0x9EF : 0x9EE); + case BeverageType.Cider: return (ItemID >= 0x1F7D && ItemID <= 0x1F80 ? ItemID : 0x1F7D); + case BeverageType.Liquor: return (ItemID >= 0x1F85 && ItemID <= 0x1F88 ? ItemID : 0x1F85); + case BeverageType.Milk: return (ItemID >= 0x1F89 && ItemID <= 0x1F8C ? ItemID : 0x1F89); + case BeverageType.Wine: return (ItemID >= 0x1F8D && ItemID <= 0x1F90 ? ItemID : 0x1F8D); + case BeverageType.Water: return (ItemID >= 0x1F91 && ItemID <= 0x1F94 ? ItemID : 0x1F91); + } + + return 0; + } + + [Constructable] + public GlassMug() + { + Weight = 1.0; + } + + [Constructable] + public GlassMug( BeverageType type ) : base( type ) + { + Weight = 1.0; + } + + public GlassMug( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + if ( CheckType( "MugAle" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Ale; + } + else if ( CheckType( "GlassCider" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Cider; + } + else if ( CheckType( "GlassLiquor" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Liquor; + } + else if ( CheckType( "GlassMilk" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Milk; + } + else if ( CheckType( "GlassWine" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Wine; + } + else if ( CheckType( "GlassWater" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Water; + } + else + { + throw new Exception( World.LoadingType ); + } + + break; + } + } + } + } + + [TypeAlias( "Server.Items.PitcherAle", "Server.Items.PitcherCider", "Server.Items.PitcherLiquor", + "Server.Items.PitcherMilk", "Server.Items.PitcherWine", "Server.Items.PitcherWater", + "Server.Items.GlassPitcher" )] + public class Pitcher : BaseBeverage + { + public override int BaseLabelNumber{ get{ return 1048128; } } // a Pitcher of Ale + public override int MaxQuantity{ get{ return 5; } } + + public override int ComputeItemID() + { + if ( IsEmpty ) + { + if ( ItemID == 0x9A7 || ItemID == 0xFF7 ) + return ItemID; + + return 0xFF6; + } + + switch ( Content ) + { + case BeverageType.Ale: + { + if ( ItemID == 0x1F96 ) + return ItemID; + + return 0x1F95; + } + case BeverageType.Cider: + { + if ( ItemID == 0x1F98 ) + return ItemID; + + return 0x1F97; + } + case BeverageType.Liquor: + { + if ( ItemID == 0x1F9A ) + return ItemID; + + return 0x1F99; + } + case BeverageType.Milk: + { + if ( ItemID == 0x9AD ) + return ItemID; + + return 0x9F0; + } + case BeverageType.Wine: + { + if ( ItemID == 0x1F9C ) + return ItemID; + + return 0x1F9B; + } + case BeverageType.Water: + { + if ( ItemID == 0xFF8 || ItemID == 0xFF9 || ItemID == 0x1F9E ) + return ItemID; + + return 0x1F9D; + } + } + + return 0; + } + + [Constructable] + public Pitcher() + { + Weight = 2.0; + } + + [Constructable] + public Pitcher( BeverageType type ) : base( type ) + { + Weight = 2.0; + } + + public Pitcher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + if ( CheckType( "PitcherWater" ) || CheckType( "GlassPitcher" ) ) + base.Deserialize( reader, false ); + else + base.Deserialize( reader, true ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + if ( CheckType( "PitcherAle" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Ale; + } + else if ( CheckType( "PitcherCider" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Cider; + } + else if ( CheckType( "PitcherLiquor" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Liquor; + } + else if ( CheckType( "PitcherMilk" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Milk; + } + else if ( CheckType( "PitcherWine" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Wine; + } + else if ( CheckType( "PitcherWater" ) ) + { + Quantity = MaxQuantity; + Content = BeverageType.Water; + } + else if ( CheckType( "GlassPitcher" ) ) + { + Quantity = 0; + Content = BeverageType.Water; + } + else + { + throw new Exception( World.LoadingType ); + } + + break; + } + } + } + } + + public abstract class BaseBeverage : Item, IHasQuantity + { + private BeverageType m_Content; + private int m_Quantity; + private Mobile m_Poisoner; + private Poison m_Poison; + + public override int LabelNumber + { + get + { + int num = BaseLabelNumber; + + if ( IsEmpty || num == 0 ) + return EmptyLabelNumber; + + return BaseLabelNumber + (int)m_Content; + } + } + + public virtual bool ShowQuantity{ get{ return ( MaxQuantity > 1 ); } } + public virtual bool Fillable{ get{ return true; } } + public virtual bool Pourable{ get{ return true; } } + + public virtual int EmptyLabelNumber{ get{ return base.LabelNumber; } } + public virtual int BaseLabelNumber{ get{ return 0; } } + + public abstract int MaxQuantity{ get; } + public abstract int ComputeItemID(); + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsEmpty + { + get{ return ( m_Quantity <= 0 ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ContainsAlchohol + { + get{ return ( !IsEmpty && m_Content != BeverageType.Milk && m_Content != BeverageType.Water ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsFull + { + get{ return ( m_Quantity >= MaxQuantity ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get{ return m_Poison; } + set{ m_Poison = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Poisoner + { + get{ return m_Poisoner; } + set{ m_Poisoner = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BeverageType Content + { + get{ return m_Content; } + set + { + m_Content = value; + + InvalidateProperties(); + + int itemID = ComputeItemID(); + + if ( itemID > 0 ) + ItemID = itemID; + else + Delete(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Quantity + { + get{ return m_Quantity; } + set + { + if ( value < 0 ) + value = 0; + else if ( value > MaxQuantity ) + value = MaxQuantity; + + m_Quantity = value; + + InvalidateProperties(); + + int itemID = ComputeItemID(); + + if ( itemID > 0 ) + ItemID = itemID; + else + Delete(); + } + } + + public virtual int GetQuantityDescription() + { + int perc = ( m_Quantity * 100 ) / MaxQuantity; + + if ( perc <= 0 ) + return 1042975; // It's empty. + else if ( perc <= 33 ) + return 1042974; // It's nearly empty. + else if ( perc <= 66 ) + return 1042973; // It's half full. + else + return 1042972; // It's full. + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( ShowQuantity ) + list.Add( GetQuantityDescription() ); + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( ShowQuantity ) + LabelTo( from, GetQuantityDescription() ); + } + + public virtual bool ValidateUse( Mobile from, bool message ) + { + if ( Deleted ) + return false; + + if ( !Movable && !Fillable ) + { + Multis.BaseHouse house = Multis.BaseHouse.FindHouseAt( this ); + + if ( house == null || !house.IsLockedDown( this ) ) + { + if ( message ) + from.SendLocalizedMessage( 502946, "", 0x59 ); // That belongs to someone else. + + return false; + } + } + + if ( from.Map != Map || !from.InRange( GetWorldLocation(), 2 ) || !from.InLOS( this ) ) + { + if ( message ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + + return false; + } + + return true; + } + + public virtual void Fill_OnTarget( Mobile from, object targ ) + { + if ( !IsEmpty || !Fillable || !ValidateUse( from, false ) ) + return; + + if ( targ is BaseBeverage ) + { + BaseBeverage bev = (BaseBeverage)targ; + + if ( bev.IsEmpty || !bev.ValidateUse( from, true ) ) + return; + + this.Content = bev.Content; + this.Poison = bev.Poison; + this.Poisoner = bev.Poisoner; + + if ( bev.Quantity > this.MaxQuantity ) + { + this.Quantity = this.MaxQuantity; + bev.Quantity -= this.MaxQuantity; + } + else + { + this.Quantity += bev.Quantity; + bev.Quantity = 0; + } + } + else if ( targ is Item ) + { + Item item = (Item)targ; + IWaterSource src; + + src = ( item as IWaterSource ); + + if ( src == null && item is AddonComponent ) + src = ( ((AddonComponent)item).Addon as IWaterSource ); + + if ( src == null || src.Quantity <= 0 ) + return; + + if ( from.Map != item.Map || !from.InRange( item.GetWorldLocation(), 2 ) || !from.InLOS( item ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + this.Content = BeverageType.Water; + this.Poison = null; + this.Poisoner = null; + + if ( src.Quantity > this.MaxQuantity ) + { + this.Quantity = this.MaxQuantity; + src.Quantity -= this.MaxQuantity; + } + else + { + this.Quantity += src.Quantity; + src.Quantity = 0; + } + + from.SendLocalizedMessage( 1010089 ); // You fill the container with water. + } + else if ( targ is LandTarget ) + { + int tileID = ((LandTarget)targ).TileID; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindIngredientObjective obj = qs.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null && !obj.Completed && obj.Ingredient == Ingredient.SwampWater ) + { + bool contains = false; + + for ( int i = 0; !contains && i < m_SwampTiles.Length; i += 2 ) + contains = ( tileID >= m_SwampTiles[i] && tileID <= m_SwampTiles[i + 1] ); + + if ( contains ) + { + Delete(); + + player.SendLocalizedMessage( 1055035 ); // You dip the container into the disgusting swamp water, collecting enough for the Hag's vile stew. + obj.Complete(); + } + } + } + } + } + } + + private static int[] m_SwampTiles = new int[] + { + 0x9C4, 0x9EB, + 0x3D65, 0x3D65, + 0x3DC0, 0x3DD9, + 0x3DDB, 0x3DDC, + 0x3DDE, 0x3EF0, + 0x3FF6, 0x3FF6, + 0x3FFC, 0x3FFE, + }; + + #region Effects of achohol + private static Hashtable m_Table = new Hashtable(); + + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static void EventSink_Login( LoginEventArgs e ) + { + CheckHeaveTimer( e.Mobile ); + } + + public static void CheckHeaveTimer( Mobile from ) + { + if ( from.BAC > 0 && from.Map != Map.Internal && !from.Deleted ) + { + Timer t = (Timer)m_Table[from]; + + if ( t == null ) + { + if ( from.BAC > 60 ) + from.BAC = 60; + + t = new HeaveTimer( from ); + t.Start(); + + m_Table[from] = t; + } + } + else + { + Timer t = (Timer)m_Table[from]; + + if ( t != null ) + { + t.Stop(); + m_Table.Remove( from ); + + from.SendLocalizedMessage( 500850 ); // You feel sober. + } + } + } + + private class HeaveTimer : Timer + { + private Mobile m_Drunk; + + public HeaveTimer( Mobile drunk ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Drunk = drunk; + + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + if ( m_Drunk.Deleted || m_Drunk.Map == Map.Internal ) + { + Stop(); + m_Table.Remove( m_Drunk ); + } + else if ( m_Drunk.Alive ) + { + if ( m_Drunk.BAC > 60 ) + m_Drunk.BAC = 60; + + // chance to get sober + if ( 10 > Utility.Random( 100 ) ) + --m_Drunk.BAC; + + // lose some stats + m_Drunk.Stam -= 1; + m_Drunk.Mana -= 1; + + if ( Utility.Random( 1, 4 ) == 1 ) + { + if ( !m_Drunk.Mounted ) + { + // turn in a random direction + m_Drunk.Direction = (Direction)Utility.Random( 8 ); + + // heave + m_Drunk.Animate( 32, 5, 1, true, false, 0 ); + } + + // *hic* + m_Drunk.PublicOverheadMessage( Network.MessageType.Regular, 0x3B2, 500849 ); + } + + if ( m_Drunk.BAC <= 0 ) + { + Stop(); + m_Table.Remove( m_Drunk ); + + m_Drunk.SendLocalizedMessage( 500850 ); // You feel sober. + } + } + } + } + #endregion + + public virtual void Pour_OnTarget( Mobile from, object targ ) + { + if ( IsEmpty || !Pourable || !ValidateUse( from, false ) ) + return; + + if ( targ is BaseBeverage ) + { + BaseBeverage bev = (BaseBeverage)targ; + + if ( !bev.ValidateUse( from, true ) ) + return; + + if ( bev.IsFull && bev.Content == this.Content ) + { + from.SendLocalizedMessage( 500848 ); // Couldn't pour it there. It was already full. + } + else if ( !bev.IsEmpty ) + { + from.SendLocalizedMessage( 500846 ); // Can't pour it there. + } + else + { + bev.Content = this.Content; + bev.Poison = this.Poison; + bev.Poisoner = this.Poisoner; + + if ( this.Quantity > bev.MaxQuantity ) + { + bev.Quantity = bev.MaxQuantity; + this.Quantity -= bev.MaxQuantity; + } + else + { + bev.Quantity += this.Quantity; + this.Quantity = 0; + } + + from.PlaySound( 0x4E ); + } + } + else if ( from == targ ) + { + if ( from.Thirst < 20 ) + from.Thirst += 1; + + if ( ContainsAlchohol ) + { + int bac = 0; + + switch ( this.Content ) + { + case BeverageType.Ale: bac = 1; break; + case BeverageType.Wine: bac = 2; break; + case BeverageType.Cider: bac = 3; break; + case BeverageType.Liquor: bac = 4; break; + } + + from.BAC += bac; + + if ( from.BAC > 60 ) + from.BAC = 60; + + CheckHeaveTimer( from ); + } + + from.PlaySound( Utility.RandomList( 0x30, 0x2D6 ) ); + + if ( m_Poison != null ) + from.ApplyPoison( m_Poisoner, m_Poison ); + + --Quantity; + } + else if ( targ is PlantItem ) + { + ((PlantItem)targ).Pour( from, this ); + } + else if ( targ is AddonComponent && + ( ((AddonComponent)targ).Addon is WaterVatEast || ((AddonComponent)targ).Addon is WaterVatSouth ) && + this.Content == BeverageType.Water ) + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + SolenMatriarchQuest qs = player.Quest as SolenMatriarchQuest; + + if ( qs != null ) + { + QuestObjective obj = qs.FindObjective( typeof( GatherWaterObjective ) ); + + if ( obj != null && !obj.Completed ) + { + BaseAddon vat = ((AddonComponent)targ).Addon; + + if ( vat.X > 5784 && vat.X < 5814 && vat.Y > 1903 && vat.Y < 1934 && + ( (qs.RedSolen && vat.Map == Map.Trammel) || (!qs.RedSolen && vat.Map == Map.Felucca) ) ) + { + if ( obj.CurProgress + Quantity > obj.MaxProgress ) + { + int delta = obj.MaxProgress - obj.CurProgress; + + Quantity -= delta; + obj.CurProgress = obj.MaxProgress; + } + else + { + obj.CurProgress += Quantity; + Quantity = 0; + } + } + } + } + } + } + else + { + from.SendLocalizedMessage( 500846 ); // Can't pour it there. + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsEmpty ) + { + if ( !Fillable || !ValidateUse( from, true ) ) + return; + + from.BeginTarget( -1, true, TargetFlags.None, new TargetCallback( Fill_OnTarget ) ); + SendLocalizedMessageTo( from, 500837 ); // Fill from what? + } + else if ( Pourable && ValidateUse( from, true ) ) + { + from.BeginTarget( -1, true, TargetFlags.None, new TargetCallback( Pour_OnTarget ) ); + from.SendLocalizedMessage( 1010086 ); // What do you want to use this on? + } + } + + public static bool ConsumeTotal( Container pack, BeverageType content, int quantity ) + { + return ConsumeTotal( pack, typeof( BaseBeverage ), content, quantity ); + } + + public static bool ConsumeTotal( Container pack, Type itemType, BeverageType content, int quantity ) + { + Item[] items = pack.FindItemsByType( itemType ); + + // First pass, compute total + int total = 0; + + for ( int i = 0; i < items.Length; ++i ) + { + BaseBeverage bev = items[i] as BaseBeverage; + + if ( bev != null && bev.Content == content && !bev.IsEmpty ) + total += bev.Quantity; + } + + if ( total >= quantity ) + { + // We've enough, so consume it + + int need = quantity; + + for ( int i = 0; i < items.Length; ++i ) + { + BaseBeverage bev = items[i] as BaseBeverage; + + if ( bev == null || bev.Content != content || bev.IsEmpty ) + continue; + + int theirQuantity = bev.Quantity; + + if ( theirQuantity < need ) + { + bev.Quantity = 0; + need -= theirQuantity; + } + else + { + bev.Quantity -= need; + return true; + } + } + } + + return false; + } + + public BaseBeverage() + { + ItemID = ComputeItemID(); + } + + public BaseBeverage( BeverageType type ) + { + m_Content = type; + m_Quantity = MaxQuantity; + ItemID = ComputeItemID(); + } + + public BaseBeverage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (Mobile) m_Poisoner ); + + Poison.Serialize( m_Poison, writer ); + writer.Write( (int) m_Content ); + writer.Write( (int) m_Quantity ); + } + + protected bool CheckType( string name ) + { + return ( World.LoadingType == String.Format( "Server.Items.{0}", name ) ); + } + + public override void Deserialize( GenericReader reader ) + { + Deserialize( reader, true ); + } + + public void Deserialize( GenericReader reader, bool read ) + { + base.Deserialize( reader ); + + if ( !read ) + return; + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Poisoner = reader.ReadMobile(); + goto case 0; + } + case 0: + { + m_Poison = Poison.Deserialize( reader ); + m_Content = (BeverageType)reader.ReadInt(); + m_Quantity = reader.ReadInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/BeverageEmpty.cs b/Scripts/Items/Food/BeverageEmpty.cs new file mode 100644 index 000000000..1de135e25 --- /dev/null +++ b/Scripts/Items/Food/BeverageEmpty.cs @@ -0,0 +1,59 @@ +using System; + +namespace Server.Items +{ + [FlipableAttribute( 0x1f81, 0x1f82, 0x1f83, 0x1f84 )] + public class Glass : Item + { + [Constructable] + public Glass() : base( 0x1f81 ) + { + this.Weight = 0.1; + } + + public Glass( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GlassBottle : Item + { + [Constructable] + public GlassBottle() : base( 0xe2b ) + { + this.Weight = 0.3; + } + + public GlassBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Food/Bowls.cs b/Scripts/Items/Food/Bowls.cs new file mode 100644 index 000000000..78b275472 --- /dev/null +++ b/Scripts/Items/Food/Bowls.cs @@ -0,0 +1,532 @@ +using System; + +namespace Server.Items +{ + public class EmptyWoodenBowl : Item + { + [Constructable] + public EmptyWoodenBowl() : base( 0x15F8 ) + { + Weight = 1.0; + } + + public EmptyWoodenBowl( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyPewterBowl : Item + { + [Constructable] + public EmptyPewterBowl() : base( 0x15FD ) + { + Weight = 1.0; + } + + public EmptyPewterBowl( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodenBowlOfCarrots : Food + { + [Constructable] + public WoodenBowlOfCarrots() : base( 0x15F9 ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyWoodenBowl() ); + return true; + } + + public WoodenBowlOfCarrots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodenBowlOfCorn : Food + { + [Constructable] + public WoodenBowlOfCorn() : base( 0x15FA ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyWoodenBowl() ); + return true; + } + + public WoodenBowlOfCorn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodenBowlOfLettuce : Food + { + [Constructable] + public WoodenBowlOfLettuce() : base( 0x15FB ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyWoodenBowl() ); + return true; + } + + public WoodenBowlOfLettuce( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodenBowlOfPeas : Food + { + [Constructable] + public WoodenBowlOfPeas() : base( 0x15FC ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyWoodenBowl() ); + return true; + } + + public WoodenBowlOfPeas( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PewterBowlOfCarrots : Food + { + [Constructable] + public PewterBowlOfCarrots() : base( 0x15FE ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyPewterBowl() ); + return true; + } + + public PewterBowlOfCarrots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PewterBowlOfCorn : Food + { + [Constructable] + public PewterBowlOfCorn() : base( 0x15FF ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyPewterBowl() ); + return true; + } + + public PewterBowlOfCorn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PewterBowlOfLettuce : Food + { + [Constructable] + public PewterBowlOfLettuce() : base( 0x1600 ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyPewterBowl() ); + return true; + } + + public PewterBowlOfLettuce( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PewterBowlOfPeas : Food + { + [Constructable] + public PewterBowlOfPeas() : base( 0x1601 ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyPewterBowl() ); + return true; + } + + public PewterBowlOfPeas( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PewterBowlOfPotatos : Food + { + [Constructable] + public PewterBowlOfPotatos() : base( 0x1602 ) + { + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyPewterBowl() ); + return true; + } + + public PewterBowlOfPotatos( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [TypeAlias( "Server.Items.EmptyLargeWoodenBowl" )] + public class EmptyWoodenTub : Item + { + [Constructable] + public EmptyWoodenTub() : base( 0x1605 ) + { + Weight = 2.0; + } + + public EmptyWoodenTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [TypeAlias( "Server.Items.EmptyLargePewterBowl" )] + public class EmptyPewterTub : Item + { + [Constructable] + public EmptyPewterTub() : base( 0x1603 ) + { + Weight = 2.0; + } + + public EmptyPewterTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodenBowlOfStew : Food + { + [Constructable] + public WoodenBowlOfStew() : base( 0x1604 ) + { + Stackable = false; + Weight = 2.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyWoodenTub() ); + return true; + } + + public WoodenBowlOfStew( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodenBowlOfTomatoSoup : Food + { + [Constructable] + public WoodenBowlOfTomatoSoup() : base( 0x1606 ) + { + Stackable = false; + Weight = 2.0; + FillFactor = 2; + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new EmptyWoodenTub() ); + return true; + } + + public WoodenBowlOfTomatoSoup( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/CookableFood.cs b/Scripts/Items/Food/CookableFood.cs new file mode 100644 index 000000000..245fd9a46 --- /dev/null +++ b/Scripts/Items/Food/CookableFood.cs @@ -0,0 +1,893 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class CookableFood : Item + { + private int m_CookingLevel; + + [CommandProperty( AccessLevel.GameMaster )] + public int CookingLevel + { + get + { + return m_CookingLevel; + } + set + { + m_CookingLevel = value; + } + } + + public CookableFood( int itemID, int cookingLevel ) : base( itemID ) + { + m_CookingLevel = cookingLevel; + } + + public CookableFood( Serial serial ) : base( serial ) + { + } + + public abstract Food Cook(); + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + // Version 1 + writer.Write( (int) m_CookingLevel ); + + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_CookingLevel = reader.ReadInt(); + + break; + } + } + } + +#if false + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.Target = new InternalTarget( this ); + } +#endif + + public static bool IsHeatSource( object targeted ) + { + int itemID; + + if ( targeted is Item ) + itemID = ((Item)targeted).ItemID & 0x3FFF; + else if ( targeted is StaticTarget ) + itemID = ((StaticTarget)targeted).ItemID & 0x3FFF; + else + return false; + + if ( itemID >= 0xDE3 && itemID <= 0xDE9 ) + return true; // Campfire + else if ( itemID >= 0x461 && itemID <= 0x48E ) + return true; // Sandstone oven/fireplace + else if ( itemID >= 0x92B && itemID <= 0x96C ) + return true; // Stone oven/fireplace + else if ( itemID == 0xFAC ) + return true; // Firepit + else if ( itemID >= 0x184A && itemID <= 0x184C ) + return true; // Heating stand (left) + else if ( itemID >= 0x184E && itemID <= 0x1850 ) + return true; // Heating stand (right) + else if ( itemID >= 0x398C && itemID <= 0x399F ) + return true; // Fire field + + return false; + } + + private class InternalTarget : Target + { + private CookableFood m_Item; + + public InternalTarget( CookableFood item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) return; + + if ( CookableFood.IsHeatSource( targeted ) ) + { + if ( from.BeginAction( typeof( CookableFood ) ) ) + { + from.PlaySound( 0x225 ); + + m_Item.Consume(); + + InternalTimer t = new InternalTimer( from, targeted as IPoint3D, from.Map, m_Item ); + t.Start(); + } + else + { + from.SendLocalizedMessage( 500119 ); // You must wait to perform another action + } + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private IPoint3D m_Point; + private Map m_Map; + private CookableFood m_CookableFood; + + public InternalTimer( Mobile from, IPoint3D p, Map map, CookableFood cookableFood ) : base( TimeSpan.FromSeconds( 5.0 ) ) + { + m_From = from; + m_Point = p; + m_Map = map; + m_CookableFood = cookableFood; + } + + protected override void OnTick() + { + m_From.EndAction( typeof( CookableFood ) ); + + if ( m_From.Map != m_Map || (m_Point != null && m_From.GetDistanceToSqrt( m_Point ) > 3) ) + { + m_From.SendLocalizedMessage( 500686 ); // You burn the food to a crisp! It's ruined. + return; + } + + if ( m_From.CheckSkill( SkillName.Cooking, m_CookableFood.CookingLevel, 100 ) ) + { + Food cookedFood = m_CookableFood.Cook(); + + if ( m_From.AddToBackpack( cookedFood ) ) + m_From.PlaySound( 0x57 ); + } + else + { + m_From.SendLocalizedMessage( 500686 ); // You burn the food to a crisp! It's ruined. + } + } + } + } + } + + // ********** RawRibs ********** + public class RawRibs : CookableFood + { + [Constructable] + public RawRibs() : this( 1 ) + { + } + + [Constructable] + public RawRibs( int amount ) : base( 0x9F1, 10 ) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public RawRibs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new Ribs(); + } + } + + + // ********** RawLambLeg ********** + public class RawLambLeg : CookableFood + { + [Constructable] + public RawLambLeg() : this( 1 ) + { + } + + [Constructable] + public RawLambLeg( int amount ) : base( 0x1609, 10 ) + { + Stackable = true; + Amount = amount; + } + + public RawLambLeg( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 1 ) + Weight = -1; + } + + public override Food Cook() + { + return new LambLeg(); + } + } + + // ********** RawChickenLeg ********** + public class RawChickenLeg : CookableFood + { + [Constructable] + public RawChickenLeg() : base( 0x1607, 10 ) + { + Weight = 1.0; + Stackable = true; + } + + public RawChickenLeg( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new ChickenLeg(); + } + } + + + // ********** RawBird ********** + public class RawBird : CookableFood + { + [Constructable] + public RawBird() : this( 1 ) + { + } + + [Constructable] + public RawBird( int amount ) : base( 0x9B9, 10 ) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public RawBird( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new CookedBird(); + } + } + + + // ********** UnbakedPeachCobbler ********** + public class UnbakedPeachCobbler : CookableFood + { + public override int LabelNumber{ get{ return 1041335; } } // unbaked peach cobbler + + [Constructable] + public UnbakedPeachCobbler() : base( 0x1042, 25 ) + { + Weight = 1.0; + } + + public UnbakedPeachCobbler( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + public override Food Cook() + { + return new PeachCobbler(); + } + } + + // ********** UnbakedFruitPie ********** + public class UnbakedFruitPie : CookableFood + { + public override int LabelNumber{ get{ return 1041334; } } // unbaked fruit pie + + [Constructable] + public UnbakedFruitPie() : base( 0x1042, 25 ) + { + Weight = 1.0; + } + + public UnbakedFruitPie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new FruitPie(); + } + } + + // ********** UnbakedMeatPie ********** + public class UnbakedMeatPie : CookableFood + { + public override int LabelNumber{ get{ return 1041338; } } // unbaked meat pie + + [Constructable] + public UnbakedMeatPie() : base( 0x1042, 25 ) + { + Weight = 1.0; + } + + public UnbakedMeatPie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new MeatPie(); + } + } + + // ********** UnbakedPumpkinPie ********** + public class UnbakedPumpkinPie : CookableFood + { + public override int LabelNumber{ get{ return 1041342; } } // unbaked pumpkin pie + + [Constructable] + public UnbakedPumpkinPie() : base( 0x1042, 25 ) + { + Weight = 1.0; + } + + public UnbakedPumpkinPie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new PumpkinPie(); + } + } + + // ********** UnbakedApplePie ********** + public class UnbakedApplePie : CookableFood + { + public override int LabelNumber{ get{ return 1041336; } } // unbaked apple pie + + [Constructable] + public UnbakedApplePie() : base( 0x1042, 25 ) + { + Weight = 1.0; + } + + public UnbakedApplePie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new ApplePie(); + } + } + + // ********** UncookedCheesePizza ********** + [TypeAlias( "Server.Items.UncookedPizza" )] + public class UncookedCheesePizza : CookableFood + { + public override int LabelNumber{ get{ return 1041341; } } // uncooked cheese pizza + + [Constructable] + public UncookedCheesePizza() : base( 0x1083, 20 ) + { + Weight = 1.0; + } + + public UncookedCheesePizza( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( ItemID == 0x1040 ) + ItemID = 0x1083; + + if ( Hue == 51 ) + Hue = 0; + } + + public override Food Cook() + { + return new CheesePizza(); + } + } + + // ********** UncookedSausagePizza ********** + public class UncookedSausagePizza : CookableFood + { + public override int LabelNumber{ get{ return 1041337; } } // uncooked sausage pizza + + [Constructable] + public UncookedSausagePizza() : base( 0x1083, 20 ) + { + Weight = 1.0; + } + + public UncookedSausagePizza( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new SausagePizza(); + } + } + +#if false + // ********** UncookedPizza ********** + public class UncookedPizza : CookableFood + { + [Constructable] + public UncookedPizza() : base( 0x1083, 20 ) + { + Weight = 1.0; + } + + public UncookedPizza( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( ItemID == 0x1040 ) + ItemID = 0x1083; + + if ( Hue == 51 ) + Hue = 0; + } + + public override Food Cook() + { + return new Pizza(); + } + } +#endif + + // ********** UnbakedQuiche ********** + public class UnbakedQuiche : CookableFood + { + public override int LabelNumber{ get{ return 1041339; } } // unbaked quiche + + [Constructable] + public UnbakedQuiche() : base( 0x1042, 25 ) + { + Weight = 1.0; + } + + public UnbakedQuiche( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new Quiche(); + } + } + + // ********** Eggs ********** + public class Eggs : CookableFood + { + [Constructable] + public Eggs() : this( 1 ) + { + } + + [Constructable] + public Eggs( int amount ) : base( 0x9B5, 15 ) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public Eggs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + Stackable = true; + + if ( Weight == 0.5 ) + Weight = 1.0; + } + } + + public override Food Cook() + { + return new FriedEggs(); + } + } + + // ********** BrightlyColoredEggs ********** + public class BrightlyColoredEggs : CookableFood + { + public override string DefaultName + { + get { return "brightly colored eggs"; } + } + + [Constructable] + public BrightlyColoredEggs() : base( 0x9B5, 15 ) + { + Weight = 0.5; + Hue = 3 + (Utility.Random( 20 ) * 5); + } + + public BrightlyColoredEggs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new FriedEggs(); + } + } + + // ********** EasterEggs ********** + public class EasterEggs : CookableFood + { + public override int LabelNumber{ get{ return 1016105; } } // Easter Eggs + + [Constructable] + public EasterEggs() : base( 0x9B5, 15 ) + { + Weight = 0.5; + Hue = 3 + (Utility.Random( 20 ) * 5); + } + + public EasterEggs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new FriedEggs(); + } + } + + // ********** CookieMix ********** + public class CookieMix : CookableFood + { + [Constructable] + public CookieMix() : base( 0x103F, 20 ) + { + Weight = 1.0; + } + + public CookieMix( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new Cookies(); + } + } + + // ********** CakeMix ********** + public class CakeMix : CookableFood + { + public override int LabelNumber{ get{ return 1041002; } } // cake mix + + [Constructable] + public CakeMix() : base( 0x103F, 40 ) + { + Weight = 1.0; + } + + public CakeMix( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override Food Cook() + { + return new Cake(); + } + } + + public class RawFishSteak : CookableFood + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public RawFishSteak() : this( 1 ) + { + } + + [Constructable] + public RawFishSteak( int amount ) : base( 0x097A, 10 ) + { + Stackable = true; + Amount = amount; + } + + public RawFishSteak( Serial serial ) : base( serial ) + { + } + + public override Food Cook() + { + return new FishSteak(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Food/Cooking.cs b/Scripts/Items/Food/Cooking.cs new file mode 100644 index 000000000..f1fc7ceed --- /dev/null +++ b/Scripts/Items/Food/Cooking.cs @@ -0,0 +1,664 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class UtilityItem + { + static public int RandomChoice( int itemID1, int itemID2 ) + { + int iRet = 0; + switch ( Utility.Random( 2 ) ) + { + default: + case 0: iRet = itemID1; break; + case 1: iRet = itemID2; break; + } + return iRet; + } + } + + // ********** Dough ********** + public class Dough : Item + { + [Constructable] + public Dough() : base( 0x103d ) + { + Weight = 1.0; + } + + public Dough( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + +#if false + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.Target = new InternalTarget( this ); + } +#endif + + private class InternalTarget : Target + { + private Dough m_Item; + + public InternalTarget( Dough item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) return; + + if ( targeted is Eggs ) + { + m_Item.Delete(); + + ((Eggs)targeted).Consume(); + + from.AddToBackpack( new UnbakedQuiche() ); + from.AddToBackpack( new Eggshells() ); + } + else if ( targeted is CheeseWheel ) + { + m_Item.Delete(); + + ((CheeseWheel)targeted).Consume(); + + from.AddToBackpack( new CheesePizza() ); + } + else if ( targeted is Sausage ) + { + m_Item.Delete(); + + ((Sausage)targeted).Consume(); + + from.AddToBackpack( new SausagePizza() ); + } + else if ( targeted is Apple ) + { + m_Item.Delete(); + + ((Apple)targeted).Consume(); + + from.AddToBackpack( new UnbakedApplePie() ); + } + + else if ( targeted is Peach ) + { + m_Item.Delete(); + + ((Peach)targeted).Consume(); + + from.AddToBackpack( new UnbakedPeachCobbler() ); + } + } + } + } + + // ********** SweetDough ********** + public class SweetDough : Item + { + public override int LabelNumber{ get{ return 1041340; } } // sweet dough + + [Constructable] + public SweetDough() : base( 0x103d ) + { + Weight = 1.0; + Hue = 150; + } + + public SweetDough( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 51 ) + Hue = 150; + } + +#if false + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.Target = new InternalTarget( this ); + } +#endif + + private class InternalTarget : Target + { + private SweetDough m_Item; + + public InternalTarget( SweetDough item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) return; + + if ( targeted is BowlFlour ) + { + m_Item.Delete(); + ((BowlFlour)targeted).Delete(); + + from.AddToBackpack( new CakeMix() ); + } + else if ( targeted is Campfire ) + { + from.PlaySound( 0x225 ); + m_Item.Delete(); + InternalTimer t = new InternalTimer( from, (Campfire)targeted ); + t.Start(); + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private Campfire m_Campfire; + + public InternalTimer( Mobile from, Campfire campfire ) : base( TimeSpan.FromSeconds( 5.0 ) ) + { + m_From = from; + m_Campfire = campfire; + } + + protected override void OnTick() + { + if ( m_From.GetDistanceToSqrt( m_Campfire ) > 3 ) + { + m_From.SendLocalizedMessage( 500686 ); // You burn the food to a crisp! It's ruined. + return; + } + + if ( m_From.CheckSkill( SkillName.Cooking, 0, 10 ) ) + { + if ( m_From.AddToBackpack( new Muffins() ) ) + m_From.PlaySound( 0x57 ); + } + else + { + m_From.SendLocalizedMessage( 500686 ); // You burn the food to a crisp! It's ruined. + } + } + } + } + } + + // ********** JarHoney ********** + public class JarHoney : Item + { + [Constructable] + public JarHoney() : base( 0x9ec ) + { + Weight = 1.0; + Stackable = true; + } + + public JarHoney( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + Stackable = true; + } + + /*public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.Target = new InternalTarget( this ); + }*/ + + private class InternalTarget : Target + { + private JarHoney m_Item; + + public InternalTarget( JarHoney item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) return; + + if ( targeted is Dough ) + { + m_Item.Delete(); + ((Dough)targeted).Consume(); + + from.AddToBackpack( new SweetDough() ); + } + + if (targeted is BowlFlour) + { + m_Item.Consume(); + ((BowlFlour)targeted).Delete(); + + from.AddToBackpack( new CookieMix() ); + } + } + } + } + + // ********** BowlFlour ********** + public class BowlFlour : Item + { + [Constructable] + public BowlFlour() : base( 0xa1e ) + { + Weight = 1.0; + } + + public BowlFlour( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + // ********** WoodenBowl ********** + public class WoodenBowl : Item + { + [Constructable] + public WoodenBowl() : base( 0x15f8 ) + { + Weight = 1.0; + } + + public WoodenBowl( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + // ********** PitcherWater ********** + /*public class PitcherWater : Item + { + [Constructable] + public PitcherWater() : base(Utility.Random( 0x1f9d, 2 )) + { + Weight = 1.0; + } + + public PitcherWater( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private PitcherWater m_Item; + + public InternalTarget( PitcherWater item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) return; + + if ( targeted is BowlFlour ) + { + m_Item.Delete(); + ((BowlFlour)targeted).Delete(); + + from.AddToBackpack( new Dough() ); + from.AddToBackpack( new WoodenBowl() ); + } + } + } + }*/ + + // ********** SackFlour ********** + [TypeAlias( "Server.Items.SackFlourOpen" )] + public class SackFlour : Item, IHasQuantity + { + private int m_Quantity; + + [CommandProperty( AccessLevel.GameMaster )] + public int Quantity + { + get{ return m_Quantity; } + set + { + if ( value < 0 ) + value = 0; + else if ( value > 20 ) + value = 20; + + m_Quantity = value; + + if ( m_Quantity == 0 ) + Delete(); + else if ( m_Quantity < 20 && (ItemID == 0x1039 || ItemID == 0x1045) ) + ++ItemID; + } + } + + [Constructable] + public SackFlour() : base( 0x1039 ) + { + Weight = 5.0; + m_Quantity = 20; + } + + public SackFlour( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (int) m_Quantity ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + case 1: + { + m_Quantity = reader.ReadInt(); + break; + } + case 0: + { + m_Quantity = 20; + break; + } + } + + if ( version < 2 && Weight == 1.0 ) + Weight = 5.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + if ( (ItemID == 0x1039 || ItemID == 0x1045) ) + ++ItemID; + +#if false + this.Delete(); + + from.AddToBackpack( new SackFlourOpen() ); +#endif + } + + } + +#if false + // ********** SackFlourOpen ********** + public class SackFlourOpen : Item + { + public override int LabelNumber{ get{ return 1024166; } } // open sack of flour + + [Constructable] + public SackFlourOpen() : base(UtilityItem.RandomChoice( 0x1046, 0x103a )) + { + Weight = 1.0; + } + + public SackFlourOpen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private SackFlourOpen m_Item; + + public InternalTarget( SackFlourOpen item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) return; + + if ( targeted is WoodenBowl ) + { + m_Item.Delete(); + ((WoodenBowl)targeted).Delete(); + + from.AddToBackpack( new BowlFlour() ); + } + else if ( targeted is TribalBerry ) + { + if ( from.Skills[SkillName.Cooking].Base >= 80.0 ) + { + m_Item.Delete(); + ((TribalBerry)targeted).Delete(); + + from.AddToBackpack( new TribalPaint() ); + + from.SendLocalizedMessage( 1042002 ); // You combine the berry and the flour into the tribal paint worn by the savages. + } + else + { + from.SendLocalizedMessage( 1042003 ); // You don't have the cooking skill to create the body paint. + } + } + } + } + } +#endif + + // ********** Eggshells ********** + public class Eggshells : Item + { + [Constructable] + public Eggshells() : base( 0x9b4 ) + { + Weight = 0.5; + } + + public Eggshells( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WheatSheaf : Item + { + [Constructable] + public WheatSheaf() : this( 1 ) + { + } + + [Constructable] + public WheatSheaf( int amount ) : base( 7869 ) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + from.BeginTarget( 4, false, TargetFlags.None, new TargetCallback( OnTarget ) ); + } + + public virtual void OnTarget( Mobile from, object obj ) + { + if ( obj is AddonComponent ) + obj = (obj as AddonComponent).Addon; + + IFlourMill mill = obj as IFlourMill; + + if ( mill != null ) + { + int needs = mill.MaxFlour - mill.CurFlour; + + if ( needs > this.Amount ) + needs = this.Amount; + + mill.CurFlour += needs; + Consume( needs ); + } + } + + public WheatSheaf( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/Food.cs b/Scripts/Items/Food/Food.cs new file mode 100644 index 000000000..b1d67a7cd --- /dev/null +++ b/Scripts/Items/Food/Food.cs @@ -0,0 +1,1264 @@ +using System; +using System.Collections; +using Server.Network; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + public abstract class Food : Item + { + private Mobile m_Poisoner; + private Poison m_Poison; + private int m_FillFactor; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Poisoner + { + get { return m_Poisoner; } + set { m_Poisoner = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get { return m_Poison; } + set { m_Poison = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int FillFactor + { + get { return m_FillFactor; } + set { m_FillFactor = value; } + } + + public Food( int itemID ) : this( 1, itemID ) + { + } + + public Food( int amount, int itemID ) : base( itemID ) + { + Stackable = true; + Amount = amount; + m_FillFactor = 1; + } + + public Food( Serial serial ) : base( serial ) + { + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive ) + list.Add( new ContextMenus.EatEntry( from, this ) ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + Eat( from ); + } + } + + public virtual bool Eat( Mobile from ) + { + // Fill the Mobile with FillFactor + if ( FillHunger( from, m_FillFactor ) ) + { + // Play a random "eat" sound + from.PlaySound( Utility.Random( 0x3A, 3 ) ); + + if ( from.Body.IsHuman && !from.Mounted ) + from.Animate( 34, 5, 1, true, false, 0 ); + + if ( m_Poison != null ) + from.ApplyPoison( m_Poisoner, m_Poison ); + + Consume(); + + return true; + } + + return false; + } + + static public bool FillHunger( Mobile from, int fillFactor ) + { + if ( from.Hunger >= 20 ) + { + from.SendLocalizedMessage( 500867 ); // You are simply too full to eat any more! + return false; + } + + int iHunger = from.Hunger + fillFactor; + if ( from.Stam < from.StamMax ) + from.Stam += Utility.Random( 6, 3 ) + fillFactor/5;//restore some stamina + if ( iHunger >= 20 ) + { + from.Hunger = 20; + from.SendLocalizedMessage( 500872 ); // You manage to eat the food, but you are stuffed! + } + else + { + from.Hunger = iHunger; + + if ( iHunger < 5 ) + from.SendLocalizedMessage( 500868 ); // You eat the food, but are still extremely hungry. + else if ( iHunger < 10 ) + from.SendLocalizedMessage( 500869 ); // You eat the food, and begin to feel more satiated. + else if ( iHunger < 15 ) + from.SendLocalizedMessage( 500870 ); // After eating the food, you feel much less hungry. + else + from.SendLocalizedMessage( 500871 ); // You feel quite full after consuming the food. + } + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 4 ); // version + + writer.Write( m_Poisoner ); + + Poison.Serialize( m_Poison, writer ); + writer.Write( m_FillFactor ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + switch ( reader.ReadInt() ) + { + case 0: m_Poison = null; break; + case 1: m_Poison = Poison.Lesser; break; + case 2: m_Poison = Poison.Regular; break; + case 3: m_Poison = Poison.Greater; break; + case 4: m_Poison = Poison.Deadly; break; + } + + break; + } + case 2: + { + m_Poison = Poison.Deserialize( reader ); + break; + } + case 3: + { + m_Poison = Poison.Deserialize( reader ); + m_FillFactor = reader.ReadInt(); + break; + } + case 4: + { + m_Poisoner = reader.ReadMobile(); + goto case 3; + } + } + } + } + + public class BreadLoaf : Food + { + [Constructable] + public BreadLoaf() : this( 1 ) + { + } + + [Constructable] + public BreadLoaf( int amount ) : base( amount, 0x103B ) + { + this.Weight = 1.0; + this.FillFactor = 3; + } + + public BreadLoaf( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Bacon : Food + { + [Constructable] + public Bacon() : this( 1 ) + { + } + + [Constructable] + public Bacon( int amount ) : base( amount, 0x979 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Bacon( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SlabOfBacon : Food + { + [Constructable] + public SlabOfBacon() : this( 1 ) + { + } + + [Constructable] + public SlabOfBacon( int amount ) : base( amount, 0x976 ) + { + this.Weight = 1.0; + this.FillFactor = 3; + } + + public SlabOfBacon( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FishSteak : Food + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public FishSteak() : this( 1 ) + { + } + + [Constructable] + public FishSteak( int amount ) : base( amount, 0x97B ) + { + this.FillFactor = 3; + } + + public FishSteak( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CheeseWheel : Food + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public CheeseWheel() : this( 1 ) + { + } + + [Constructable] + public CheeseWheel( int amount ) : base( amount, 0x97E ) + { + this.FillFactor = 3; + } + + public CheeseWheel( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CheeseWedge : Food + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public CheeseWedge() : this( 1 ) + { + } + + [Constructable] + public CheeseWedge( int amount ) : base( amount, 0x97D ) + { + this.FillFactor = 3; + } + + public CheeseWedge( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CheeseSlice : Food + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public CheeseSlice() : this( 1 ) + { + } + + [Constructable] + public CheeseSlice( int amount ) : base( amount, 0x97C ) + { + this.FillFactor = 1; + } + + public CheeseSlice( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FrenchBread : Food + { + [Constructable] + public FrenchBread() : this( 1 ) + { + } + + [Constructable] + public FrenchBread( int amount ) : base( amount, 0x98C ) + { + this.Weight = 2.0; + this.FillFactor = 3; + } + + public FrenchBread( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class FriedEggs : Food + { + [Constructable] + public FriedEggs() : this( 1 ) + { + } + + [Constructable] + public FriedEggs( int amount ) : base( amount, 0x9B6 ) + { + this.Weight = 1.0; + this.FillFactor = 4; + } + + public FriedEggs( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CookedBird : Food + { + [Constructable] + public CookedBird() : this( 1 ) + { + } + + [Constructable] + public CookedBird( int amount ) : base( amount, 0x9B7 ) + { + this.Weight = 1.0; + this.FillFactor = 5; + } + + public CookedBird( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RoastPig : Food + { + [Constructable] + public RoastPig() : this( 1 ) + { + } + + [Constructable] + public RoastPig( int amount ) : base( amount, 0x9BB ) + { + this.Weight = 45.0; + this.FillFactor = 20; + } + + public RoastPig( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Sausage : Food + { + [Constructable] + public Sausage() : this( 1 ) + { + } + + [Constructable] + public Sausage( int amount ) : base( amount, 0x9C0 ) + { + this.Weight = 1.0; + this.FillFactor = 4; + } + + public Sausage( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Ham : Food + { + [Constructable] + public Ham() : this( 1 ) + { + } + + [Constructable] + public Ham( int amount ) : base( amount, 0x9C9 ) + { + this.Weight = 1.0; + this.FillFactor = 5; + } + + public Ham( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Cake : Food + { + [Constructable] + public Cake() : base( 0x9E9 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 10; + } + + public Cake( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Ribs : Food + { + [Constructable] + public Ribs() : this( 1 ) + { + } + + [Constructable] + public Ribs( int amount ) : base( amount, 0x9F2 ) + { + this.Weight = 1.0; + this.FillFactor = 5; + } + + public Ribs( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Cookies : Food + { + [Constructable] + public Cookies() : base( 0x160b ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 4; + } + + public Cookies( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Muffins : Food + { + [Constructable] + public Muffins() : base( 0x9eb ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 4; + } + + public Muffins( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [TypeAlias( "Server.Items.Pizza" )] + public class CheesePizza : Food + { + public override int LabelNumber{ get{ return 1044516; } } // cheese pizza + + [Constructable] + public CheesePizza() : base( 0x1040 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 6; + } + + public CheesePizza( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SausagePizza : Food + { + public override int LabelNumber{ get{ return 1044517; } } // sausage pizza + + [Constructable] + public SausagePizza() : base( 0x1040 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 6; + } + + public SausagePizza( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + +#if false + public class Pizza : Food + { + [Constructable] + public Pizza() : base( 0x1040 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 6; + } + + public Pizza( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +#endif + + public class FruitPie : Food + { + public override int LabelNumber{ get{ return 1041346; } } // baked fruit pie + + [Constructable] + public FruitPie() : base( 0x1041 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 5; + } + + public FruitPie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MeatPie : Food + { + public override int LabelNumber{ get{ return 1041347; } } // baked meat pie + + [Constructable] + public MeatPie() : base( 0x1041 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 5; + } + + public MeatPie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PumpkinPie : Food + { + public override int LabelNumber{ get{ return 1041348; } } // baked pumpkin pie + + [Constructable] + public PumpkinPie() : base( 0x1041 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 5; + } + + public PumpkinPie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ApplePie : Food + { + public override int LabelNumber{ get{ return 1041343; } } // baked apple pie + + [Constructable] + public ApplePie() : base( 0x1041 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 5; + } + + public ApplePie( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PeachCobbler : Food + { + public override int LabelNumber{ get{ return 1041344; } } // baked peach cobbler + + [Constructable] + public PeachCobbler() : base( 0x1041 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 5; + } + + public PeachCobbler( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Quiche : Food + { + public override int LabelNumber{ get{ return 1041345; } } // baked quiche + + [Constructable] + public Quiche() : base( 0x1041 ) + { + Stackable = false; + this.Weight = 1.0; + this.FillFactor = 5; + } + + public Quiche( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LambLeg : Food + { + [Constructable] + public LambLeg() : this( 1 ) + { + } + + [Constructable] + public LambLeg( int amount ) : base( amount, 0x160a ) + { + this.Weight = 2.0; + this.FillFactor = 5; + } + + public LambLeg( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ChickenLeg : Food + { + [Constructable] + public ChickenLeg() : this( 1 ) + { + } + + [Constructable] + public ChickenLeg( int amount ) : base( amount, 0x1608 ) + { + this.Weight = 1.0; + this.FillFactor = 4; + } + + public ChickenLeg( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC74, 0xC75 )] + public class HoneydewMelon : Food + { + [Constructable] + public HoneydewMelon() : this( 1 ) + { + } + + [Constructable] + public HoneydewMelon( int amount ) : base( amount, 0xC74 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public HoneydewMelon( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC64, 0xC65 )] + public class YellowGourd : Food + { + [Constructable] + public YellowGourd() : this( 1 ) + { + } + + [Constructable] + public YellowGourd( int amount ) : base( amount, 0xC64 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public YellowGourd( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC66, 0xC67 )] + public class GreenGourd : Food + { + [Constructable] + public GreenGourd() : this( 1 ) + { + } + + [Constructable] + public GreenGourd( int amount ) : base( amount, 0xC66 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public GreenGourd( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC7F, 0xC81 )] + public class EarOfCorn : Food + { + [Constructable] + public EarOfCorn() : this( 1 ) + { + } + + [Constructable] + public EarOfCorn( int amount ) : base( amount, 0xC81 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public EarOfCorn( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Turnip : Food + { + [Constructable] + public Turnip() : this( 1 ) + { + } + + [Constructable] + public Turnip( int amount ) : base( amount, 0xD3A ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Turnip( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SheafOfHay : Item + { + [Constructable] + public SheafOfHay() : base( 0xF36 ) + { + this.Weight = 10.0; + } + + public SheafOfHay( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/Fruits.cs b/Scripts/Items/Food/Fruits.cs new file mode 100644 index 000000000..8ee1b9c6f --- /dev/null +++ b/Scripts/Items/Food/Fruits.cs @@ -0,0 +1,632 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + public class FruitBasket : Food + { + [Constructable] + public FruitBasket() : base( 1, 0x993 ) + { + Weight = 2.0; + FillFactor = 5; + Stackable = false; + } + + public FruitBasket( Serial serial ) : base( serial ) + { + } + + public override bool Eat( Mobile from ) + { + if ( !base.Eat( from ) ) + return false; + + from.AddToBackpack( new Basket() ); + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x171f, 0x1720 )] + public class Banana : Food + { + [Constructable] + public Banana() : this( 1 ) + { + } + + [Constructable] + public Banana( int amount ) : base( amount, 0x171f ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Banana( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1721, 0x1722 )] + public class Bananas : Food + { + [Constructable] + public Bananas() : this( 1 ) + { + } + + [Constructable] + public Bananas( int amount ) : base( amount, 0x1721 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Bananas( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SplitCoconut : Food + { + [Constructable] + public SplitCoconut() : this( 1 ) + { + } + + [Constructable] + public SplitCoconut( int amount ) : base( amount, 0x1725 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public SplitCoconut( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Lemon : Food + { + [Constructable] + public Lemon() : this( 1 ) + { + } + + [Constructable] + public Lemon( int amount ) : base( amount, 0x1728 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Lemon( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Lemons : Food + { + [Constructable] + public Lemons() : this( 1 ) + { + } + + [Constructable] + public Lemons( int amount ) : base( amount, 0x1729 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Lemons( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Lime : Food + { + [Constructable] + public Lime() : this( 1 ) + { + } + + [Constructable] + public Lime( int amount ) : base( amount, 0x172a ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Lime( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Limes : Food + { + [Constructable] + public Limes() : this( 1 ) + { + } + + [Constructable] + public Limes( int amount ) : base( amount, 0x172B ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Limes( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Coconut : Food + { + [Constructable] + public Coconut() : this( 1 ) + { + } + + [Constructable] + public Coconut( int amount ) : base( amount, 0x1726 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Coconut( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class OpenCoconut : Food + { + [Constructable] + public OpenCoconut() : this( 1 ) + { + } + + [Constructable] + public OpenCoconut( int amount ) : base( amount, 0x1723 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public OpenCoconut( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Dates : Food + { + [Constructable] + public Dates() : this( 1 ) + { + } + + [Constructable] + public Dates( int amount ) : base( amount, 0x1727 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Dates( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Grapes : Food + { + [Constructable] + public Grapes() : this( 1 ) + { + } + + [Constructable] + public Grapes( int amount ) : base( amount, 0x9D1 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Grapes( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Peach : Food + { + [Constructable] + public Peach() : this( 1 ) + { + } + + [Constructable] + public Peach( int amount ) : base( amount, 0x9D2 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Peach( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Pear : Food + { + [Constructable] + public Pear() : this( 1 ) + { + } + + [Constructable] + public Pear( int amount ) : base( amount, 0x994 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Pear( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Apple : Food + { + [Constructable] + public Apple() : this( 1 ) + { + } + + [Constructable] + public Apple( int amount ) : base( amount, 0x9D0 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Apple( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Watermelon : Food + { + [Constructable] + public Watermelon() : this( 1 ) + { + } + + [Constructable] + public Watermelon( int amount ) : base( amount, 0xC5C ) + { + this.Weight = 5.0; + this.FillFactor = 5; + } + + public Watermelon( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( FillFactor == 2 ) + FillFactor = 5; + + if ( Weight == 2.0 ) + Weight = 5.0; + } + } + } + + public class SmallWatermelon : Food + { + [Constructable] + public SmallWatermelon() : this( 1 ) + { + } + + [Constructable] + public SmallWatermelon( int amount ) : base( amount, 0xC5D ) + { + this.Weight = 5.0; + this.FillFactor = 5; + } + + public SmallWatermelon( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xc72, 0xc73 )] + public class Squash : Food + { + [Constructable] + public Squash() : this( 1 ) + { + } + + [Constructable] + public Squash( int amount ) : base( amount, 0xc72 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Squash( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xc79, 0xc7a )] + public class Cantaloupe : Food + { + [Constructable] + public Cantaloupe() : this( 1 ) + { + } + + [Constructable] + public Cantaloupe( int amount ) : base( amount, 0xc79 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Cantaloupe( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Food/Vegetables.cs b/Scripts/Items/Food/Vegetables.cs new file mode 100644 index 000000000..2b84ee343 --- /dev/null +++ b/Scripts/Items/Food/Vegetables.cs @@ -0,0 +1,211 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xc77, 0xc78 )] + public class Carrot : Food + { + [Constructable] + public Carrot() : this( 1 ) + { + } + + [Constructable] + public Carrot( int amount ) : base( amount, 0xc78 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Carrot( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xc7b, 0xc7c )] + public class Cabbage : Food + { + [Constructable] + public Cabbage() : this( 1 ) + { + } + + [Constructable] + public Cabbage( int amount ) : base( amount, 0xc7b ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Cabbage( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xc6d, 0xc6e )] + public class Onion : Food + { + [Constructable] + public Onion() : this( 1 ) + { + } + + [Constructable] + public Onion( int amount ) : base( amount, 0xc6d ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Onion( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xc70, 0xc71 )] + public class Lettuce : Food + { + [Constructable] + public Lettuce() : this( 1 ) + { + } + + [Constructable] + public Lettuce( int amount ) : base( amount, 0xc70 ) + { + this.Weight = 1.0; + this.FillFactor = 1; + } + + public Lettuce( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0xC6A, 0xC6B )] + public class Pumpkin : Food + { + [Constructable] + public Pumpkin() : this( 1 ) + { + } + + [Constructable] + public Pumpkin( int amount ) : base( amount, 0xC6A ) + { + this.Weight = 1.0; + this.FillFactor = 8; + } + + public Pumpkin( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( FillFactor == 4 ) + FillFactor = 8; + + if ( Weight == 5.0 ) + Weight = 1.0; + } + } + } + + public class SmallPumpkin : Food + { + [Constructable] + public SmallPumpkin() : this( 1 ) + { + } + + [Constructable] + public SmallPumpkin( int amount ) : base( amount, 0xC6C ) + { + this.Weight = 1.0; + this.FillFactor = 8; + } + + public SmallPumpkin( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Backgammon.cs b/Scripts/Items/Games/Backgammon.cs new file mode 100644 index 000000000..0b906c3bd --- /dev/null +++ b/Scripts/Items/Games/Backgammon.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + [Flipable( 0xE1C, 0xFAD )] + public class Backgammon : BaseBoard + { + [Constructable] + public Backgammon() : base( 0xE1C ) + { + } + + public override void CreatePieces() + { + for ( int i = 0; i < 5; i++ ) + { + CreatePiece( new PieceWhiteChecker( this ), 42, ( 17 * i ) + 6 ); + CreatePiece( new PieceBlackChecker( this ), 42, ( 17 * i ) + 119 ); + + CreatePiece( new PieceBlackChecker( this ), 142, ( 17 * i ) + 6 ); + CreatePiece( new PieceWhiteChecker( this ), 142, ( 17 * i ) + 119 ); + } + + for ( int i = 0; i < 3; i++ ) + { + CreatePiece( new PieceBlackChecker( this ), 108, ( 17 * i ) + 6 ); + CreatePiece( new PieceWhiteChecker( this ), 108, ( 17 * i ) + 153 ); + } + + for ( int i = 0; i < 2; i++ ) + { + CreatePiece( new PieceWhiteChecker( this ), 223, ( 17 * i ) + 6 ); + CreatePiece( new PieceBlackChecker( this ), 223, ( 17 * i ) + 170 ); + } + } + + public Backgammon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + } +} diff --git a/Scripts/Items/Games/BaseBoard.cs b/Scripts/Items/Games/BaseBoard.cs new file mode 100644 index 000000000..2956b2446 --- /dev/null +++ b/Scripts/Items/Games/BaseBoard.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Multis; +using Server.Network; +using Server.ContextMenus; +using Server.Gumps; + +namespace Server.Items +{ + public abstract class BaseBoard : Container, ISecurable + { + private SecureLevel m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + public BaseBoard( int itemID ) : base( itemID ) + { + CreatePieces(); + + Weight = 5.0; + } + + public abstract void CreatePieces(); + + public void Reset() + { + for ( int i = Items.Count - 1; i >= 0; --i ) + { + if ( i < Items.Count ) + Items[i].Delete(); + } + + CreatePieces(); + } + + public void CreatePiece( BasePiece piece, int x, int y ) + { + AddItem( piece ); + piece.Location = new Point3D( x, y, 0 ); + } + + public override bool DisplaysContent{ get{ return false; } } // Do not display (x items, y stones) + + public override bool IsDecoContainer{ get{ return false; } } + + public BaseBoard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 1 ); // version + + writer.Write( (int)m_Level ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( version == 1 ) + m_Level = (SecureLevel)reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 5.0; + } + + public override TimeSpan DecayTime{ get{ return TimeSpan.FromDays( 1.0 ); } } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + BasePiece piece = dropped as BasePiece; + + return ( piece != null && piece.Board == this && base.OnDragDrop( from, dropped ) ); + } + + public override bool OnDragDropInto( Mobile from, Item dropped, Point3D point ) + { + BasePiece piece = dropped as BasePiece; + + if ( piece != null && piece.Board == this && base.OnDragDropInto( from, dropped, point ) ) + { + Packet p = new PlaySound( 0x127, GetWorldLocation() ); + + p.Acquire(); + + if ( RootParent == from ) + { + from.Send( p ); + } + else + { + foreach ( NetState state in this.GetClientsInRange( 2 ) ) + state.Send( p ); + } + + p.Release(); + + return true; + } + else + { + return false; + } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( ValidateDefault( from, this ) ) + list.Add( new DefaultEntry( from, this ) ); + + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public static bool ValidateDefault( Mobile from, BaseBoard board ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if ( !from.Alive ) + return false; + + if ( board.IsChildOf( from.Backpack ) ) + return true; + + object root = board.RootParent; + + if ( root is Mobile && root != from ) + return false; + + if ( board.Deleted || board.Map != from.Map || !from.InRange( board.GetWorldLocation(), 1 ) ) + return false; + + BaseHouse house = BaseHouse.FindHouseAt( board ); + + return ( house != null && house.IsOwner( from ) ); + } + + public class DefaultEntry : ContextMenuEntry + { + private Mobile m_From; + private BaseBoard m_Board; + + public DefaultEntry( Mobile from, BaseBoard board ) : base( 6162, from.AccessLevel >= AccessLevel.GameMaster ? -1 : 1 ) + { + m_From = from; + m_Board = board; + } + + public override void OnClick() + { + if ( BaseBoard.ValidateDefault( m_From, m_Board ) ) + m_Board.Reset(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/BasePiece.cs b/Scripts/Items/Games/BasePiece.cs new file mode 100644 index 000000000..d22b06fb9 --- /dev/null +++ b/Scripts/Items/Games/BasePiece.cs @@ -0,0 +1,105 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BasePiece : Item + { + private BaseBoard m_Board; + + public BaseBoard Board + { + get { return m_Board; } + set { m_Board = value; } + } + + public override bool IsVirtualItem{ get{ return true; } } + + public BasePiece( int itemID, BaseBoard board ) : base( itemID ) + { + m_Board = board; + } + + public BasePiece( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + writer.Write( m_Board ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Board = (BaseBoard)reader.ReadItem(); + + if ( m_Board == null || Parent == null ) + Delete(); + + break; + } + } + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Board == null || m_Board.Deleted ) + Delete(); + else if ( !IsChildOf( m_Board ) ) + m_Board.DropItem( this ); + else + base.OnSingleClick( from ); + } + + public override bool OnDragLift( Mobile from ) + { + if ( m_Board == null || m_Board.Deleted ) + { + Delete(); + return false; + } + else if ( !IsChildOf( m_Board ) ) + { + m_Board.DropItem( this ); + return false; + } + else + { + return true; + } + } + + public override bool CanTarget{ get{ return false; } } + + public override bool DropToMobile( Mobile from, Mobile target, Point3D p ) + { + return false; + } + + public override bool DropToItem( Mobile from, Item target, Point3D p ) + { + return ( target == m_Board && p.X != -1 && p.Y != -1 && base.DropToItem( from, target, p ) ); + } + + public override bool DropToWorld( Mobile from, Point3D p ) + { + return false; + } + + public override int GetLiftSound( Mobile from ) + { + return -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/CheckerBoard.cs b/Scripts/Items/Games/CheckerBoard.cs new file mode 100644 index 000000000..df7c3a50e --- /dev/null +++ b/Scripts/Items/Games/CheckerBoard.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + public class CheckerBoard : BaseBoard + { + public override int LabelNumber{ get{ return 1016449; } } // a checker board + + [Constructable] + public CheckerBoard() : base( 0xFA6 ) + { + } + + public override void CreatePieces() + { + for ( int i = 0; i < 4; i++ ) + { + CreatePiece( new PieceWhiteChecker( this ), ( 50 * i ) + 45, 25 ); + CreatePiece( new PieceWhiteChecker( this ), ( 50 * i ) + 70, 50 ); + CreatePiece( new PieceWhiteChecker( this ), ( 50 * i ) + 45, 75 ); + CreatePiece( new PieceBlackChecker( this ), ( 50 * i ) + 70, 150 ); + CreatePiece( new PieceBlackChecker( this ), ( 50 * i ) + 45, 175 ); + CreatePiece( new PieceBlackChecker( this ), ( 50 * i ) + 70, 200 ); + } + } + + public CheckerBoard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/CheckersPieces.cs b/Scripts/Items/Games/CheckersPieces.cs new file mode 100644 index 000000000..895c2440a --- /dev/null +++ b/Scripts/Items/Games/CheckersPieces.cs @@ -0,0 +1,61 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PieceWhiteChecker : BasePiece + { + public override string DefaultName + { + get { return "white checker"; } + } + + public PieceWhiteChecker( BaseBoard board ) : base( 0x3584, board ) + { + } + + public PieceWhiteChecker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackChecker : BasePiece + { + public override string DefaultName + { + get { return "black checker"; } + } + + public PieceBlackChecker( BaseBoard board ) : base( 0x358B, board ) + { + } + + public PieceBlackChecker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/ChessPieces.cs b/Scripts/Items/Games/ChessPieces.cs new file mode 100644 index 000000000..bf3c197ea --- /dev/null +++ b/Scripts/Items/Games/ChessPieces.cs @@ -0,0 +1,341 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PieceWhiteKing : BasePiece + { + public override string DefaultName + { + get { return "white king"; } + } + + public PieceWhiteKing( BaseBoard board ) : base( 0x3587, board ) + { + } + + public PieceWhiteKing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackKing : BasePiece + { + public override string DefaultName + { + get { return "black king"; } + } + + public PieceBlackKing( BaseBoard board ) : base( 0x358E, board ) + { + } + + public PieceBlackKing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceWhiteQueen : BasePiece + { + public override string DefaultName + { + get { return "white queen"; } + } + + public PieceWhiteQueen( BaseBoard board ) : base( 0x358A, board ) + { + } + + public PieceWhiteQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackQueen : BasePiece + { + public override string DefaultName + { + get { return "black queen"; } + } + + public PieceBlackQueen( BaseBoard board ) : base( 0x3591, board ) + { + } + + public PieceBlackQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceWhiteRook : BasePiece + { + public override string DefaultName + { + get { return "white rook"; } + } + + public PieceWhiteRook( BaseBoard board ) : base( 0x3586, board ) + { + } + + public PieceWhiteRook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackRook : BasePiece + { + public override string DefaultName + { + get { return "black rook"; } + } + + public PieceBlackRook( BaseBoard board ) : base( 0x358D, board ) + { + } + + public PieceBlackRook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceWhiteBishop : BasePiece + { + public override string DefaultName + { + get { return "white bishop"; } + } + + public PieceWhiteBishop( BaseBoard board ) : base( 0x3585, board ) + { + } + + public PieceWhiteBishop( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackBishop : BasePiece + { + public override string DefaultName + { + get { return "black bishop"; } + } + + public PieceBlackBishop( BaseBoard board ) : base( 0x358C, board ) + { + } + + public PieceBlackBishop( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceWhiteKnight : BasePiece + { + public override string DefaultName + { + get { return "white knight"; } + } + + public PieceWhiteKnight( BaseBoard board ) : base( 0x3588, board ) + { + } + + public PieceWhiteKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackKnight : BasePiece + { + public override string DefaultName + { + get { return "black knight"; } + } + + public PieceBlackKnight( BaseBoard board ) : base( 0x358F, board ) + { + } + + public PieceBlackKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceWhitePawn : BasePiece + { + public override string DefaultName + { + get { return "white pawn"; } + } + + public PieceWhitePawn( BaseBoard board ) : base( 0x3589, board ) + { + } + + public PieceWhitePawn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class PieceBlackPawn : BasePiece + { + public override string DefaultName + { + get { return "black pawn"; } + } + + public PieceBlackPawn( BaseBoard board ) : base( 0x3590, board ) + { + } + + public PieceBlackPawn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Games/Chessboard.cs b/Scripts/Items/Games/Chessboard.cs new file mode 100644 index 000000000..83b2b01dd --- /dev/null +++ b/Scripts/Items/Games/Chessboard.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + public class Chessboard : BaseBoard + { + public override int LabelNumber{ get{ return 1016450; } } // a chessboard + + [Constructable] + public Chessboard() : base( 0xFA6 ) + { + } + + public override void CreatePieces() + { + for ( int i = 0; i < 8; i++ ) + { + CreatePiece( new PieceBlackPawn( this ), ( 25 * i ) + 43, 42 ); + CreatePiece( new PieceWhitePawn( this ), ( 25 * i ) + 43, 167 ); + } + + // Rook + CreatePiece( new PieceBlackRook( this ), 42, 5 ); + CreatePiece( new PieceBlackRook( this ), 216, 5 ); + + CreatePiece( new PieceWhiteRook( this ), 42, 180 ); + CreatePiece( new PieceWhiteRook( this ), 216, 180 ); + + // Knight + CreatePiece( new PieceBlackKnight( this ), 66, 7 ); + CreatePiece( new PieceBlackKnight( this ), 190, 7 ); + + CreatePiece( new PieceWhiteKnight( this ), 66, 182 ); + CreatePiece( new PieceWhiteKnight( this ), 190, 182 ); + + // Bishop + CreatePiece( new PieceBlackBishop( this ), 94, 7 ); + CreatePiece( new PieceBlackBishop( this ), 168, 7 ); + + CreatePiece( new PieceWhiteBishop( this ), 94, 182 ); + CreatePiece( new PieceWhiteBishop( this ), 168, 182 ); + + // Queen + CreatePiece( new PieceBlackQueen( this ), 142, 5 ); + CreatePiece( new PieceWhiteQueen( this ), 142, 180 ); + + // King + CreatePiece( new PieceBlackKing( this ), 117, 5 ); + CreatePiece( new PieceWhiteKing( this ), 117, 180 ); + } + + public Chessboard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Dices.cs b/Scripts/Items/Games/Dices.cs new file mode 100644 index 000000000..e8503ff00 --- /dev/null +++ b/Scripts/Items/Games/Dices.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class Dices : Item + { + [Constructable] + public Dices() : base( 0xFA7 ) + { + Weight = 1.0; + } + + public Dices( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( this.GetWorldLocation(), 2 ) ) + return; + + this.PublicOverheadMessage( MessageType.Regular, 0, false, string.Format( "*{0} rolls {1}, {2}*", from.Name, Utility.Random( 1, 6 ), Utility.Random( 1, 6 ) ) ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongDealerIndicator.cs b/Scripts/Items/Games/Mahjong/MahjongDealerIndicator.cs new file mode 100644 index 000000000..f3351cfeb --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongDealerIndicator.cs @@ -0,0 +1,73 @@ +using System; +using Server; + +namespace Server.Engines.Mahjong +{ + public class MahjongDealerIndicator + { + public static MahjongPieceDim GetDimensions( Point2D position, MahjongPieceDirection direction ) + { + if ( direction == MahjongPieceDirection.Up || direction == MahjongPieceDirection.Down ) + return new MahjongPieceDim( position, 40, 20 ); + else + return new MahjongPieceDim( position, 20, 40 ); + } + + private MahjongGame m_Game; + private Point2D m_Position; + private MahjongPieceDirection m_Direction; + private MahjongWind m_Wind; + + public MahjongGame Game { get { return m_Game; } } + public Point2D Position { get { return m_Position; } } + public MahjongPieceDirection Direction { get { return m_Direction; } } + public MahjongWind Wind { get { return m_Wind; } } + + public MahjongDealerIndicator( MahjongGame game, Point2D position, MahjongPieceDirection direction, MahjongWind wind ) + { + m_Game = game; + m_Position = position; + m_Direction = direction; + m_Wind = wind; + } + + public MahjongPieceDim Dimensions + { + get { return GetDimensions( m_Position, m_Direction ); } + } + + public void Move( Point2D position, MahjongPieceDirection direction, MahjongWind wind ) + { + MahjongPieceDim dim = GetDimensions( position, direction ); + + if ( !dim.IsValid() ) + return; + + m_Position = position; + m_Direction = direction; + m_Wind = wind; + + m_Game.Players.SendGeneralPacket( true, true ); + } + + public void Save( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( m_Position ); + writer.Write( (int) m_Direction ); + writer.Write( (int) m_Wind ); + } + + public MahjongDealerIndicator( MahjongGame game, GenericReader reader ) + { + m_Game = game; + + int version = reader.ReadInt(); + + m_Position = reader.ReadPoint2D(); + m_Direction = (MahjongPieceDirection) reader.ReadInt(); + m_Wind = (MahjongWind) reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongDices.cs b/Scripts/Items/Games/Mahjong/MahjongDices.cs new file mode 100644 index 000000000..7f203e89e --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongDices.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Engines.Mahjong +{ + public class MahjongDices + { + private MahjongGame m_Game; + private int m_First; + private int m_Second; + + public MahjongGame Game { get { return m_Game; } } + public int First { get { return m_First; } } + public int Second { get { return m_Second; } } + + public MahjongDices( MahjongGame game ) + { + m_Game = game; + m_First = Utility.Random( 1, 6 ); + m_Second = Utility.Random( 1, 6 ); + } + + public void RollDices( Mobile from ) + { + m_First = Utility.Random( 1, 6 ); + m_Second = Utility.Random( 1, 6 ); + + m_Game.Players.SendGeneralPacket( true, true ); + + if ( from != null ) + m_Game.Players.SendLocalizedMessage( 1062695, string.Format( "{0}\t{1}\t{2}", from.Name, m_First, m_Second ) ); // ~1_name~ rolls the dice and gets a ~2_number~ and a ~3_number~! + } + + public void Save( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( m_First ); + writer.Write( m_Second ); + } + + public MahjongDices( MahjongGame game, GenericReader reader ) + { + m_Game = game; + + int version = reader.ReadInt(); + + m_First = reader.ReadInt(); + m_Second = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongEnums.cs b/Scripts/Items/Games/Mahjong/MahjongEnums.cs new file mode 100644 index 000000000..ead705157 --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongEnums.cs @@ -0,0 +1,58 @@ +using System; + +namespace Server.Engines.Mahjong +{ + public enum MahjongPieceDirection + { + Up, + Left, + Down, + Right + } + + public enum MahjongWind + { + North, + East, + South, + West + } + + public enum MahjongTileType + { + Dagger1 = 1, + Dagger2, + Dagger3, + Dagger4, + Dagger5, + Dagger6, + Dagger7, + Dagger8, + Dagger9, + Gem1, + Gem2, + Gem3, + Gem4, + Gem5, + Gem6, + Gem7, + Gem8, + Gem9, + Number1, + Number2, + Number3, + Number4, + Number5, + Number6, + Number7, + Number8, + Number9, + North, + East, + South, + West, + Green, + Red, + White + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongGame.cs b/Scripts/Items/Games/Mahjong/MahjongGame.cs new file mode 100644 index 000000000..c9d76c016 --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongGame.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.ContextMenus; + +namespace Server.Engines.Mahjong +{ + public class MahjongGame : Item, ISecurable + { + public const int MaxPlayers = 4; + public const int BaseScore = 30000; + + private MahjongTile[] m_Tiles; + private MahjongDealerIndicator m_DealerIndicator; + private MahjongWallBreakIndicator m_WallBreakIndicator; + private MahjongDices m_Dices; + private MahjongPlayers m_Players; + private bool m_ShowScores; + private bool m_SpectatorVision; + private DateTime m_LastReset; + + public MahjongTile[] Tiles { get { return m_Tiles; } } + public MahjongDealerIndicator DealerIndicator { get { return m_DealerIndicator; } } + public MahjongWallBreakIndicator WallBreakIndicator { get { return m_WallBreakIndicator; } } + public MahjongDices Dices { get { return m_Dices; } } + public MahjongPlayers Players { get { return m_Players; } } + + private SecureLevel m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ShowScores + { + get { return m_ShowScores; } + set + { + if ( m_ShowScores == value ) + return; + + m_ShowScores = value; + + if ( value ) + m_Players.SendPlayersPacket( true, true ); + + m_Players.SendGeneralPacket( true, true ); + + m_Players.SendLocalizedMessage( value ? 1062777 : 1062778 ); // The dealer has enabled/disabled score display. + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool SpectatorVision + { + get { return m_SpectatorVision; } + set + { + if ( m_SpectatorVision == value ) + return; + + m_SpectatorVision = value; + + if ( m_Players.IsInGamePlayer( m_Players.DealerPosition ) ) + m_Players.Dealer.Send( new MahjongGeneralInfo( this ) ); + + m_Players.SendTilesPacket( false, true ); + + m_Players.SendLocalizedMessage( value ? 1062715 : 1062716 ); // The dealer has enabled/disabled Spectator Vision. + + InvalidateProperties(); + } + } + + [Constructable] + public MahjongGame() : base( 0xFAA ) + { + Weight = 5.0; + + BuildWalls(); + m_DealerIndicator = new MahjongDealerIndicator( this, new Point2D( 300, 300 ), MahjongPieceDirection.Up, MahjongWind.North ); + m_WallBreakIndicator = new MahjongWallBreakIndicator( this, new Point2D( 335, 335 ) ); + m_Dices = new MahjongDices( this ); + m_Players = new MahjongPlayers( this, MaxPlayers, BaseScore ); + m_LastReset = DateTime.Now; + m_Level = SecureLevel.CoOwners; + } + + public MahjongGame( Serial serial ) : base( serial ) + { + } + + private void BuildHorizontalWall( ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, MahjongTileTypeGenerator typeGenerator ) + { + for ( int i = 0; i < 17; i++ ) + { + Point2D position = new Point2D( x + i*20, y ); + m_Tiles[index + i] = new MahjongTile( this, index + i, typeGenerator.Next(), position, stackLevel, direction, false ); + } + + index += 17; + } + + private void BuildVerticalWall( ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, MahjongTileTypeGenerator typeGenerator ) + { + for ( int i = 0; i < 17; i++ ) + { + Point2D position = new Point2D( x, y + i*20 ); + m_Tiles[index + i] = new MahjongTile( this, index + i, typeGenerator.Next(), position, stackLevel, direction, false ); + } + + index += 17; + } + + private void BuildWalls() + { + m_Tiles = new MahjongTile[17 * 8]; + + MahjongTileTypeGenerator typeGenerator = new MahjongTileTypeGenerator( 4 ); + + int i = 0; + + BuildHorizontalWall( ref i, 165, 110, 0, MahjongPieceDirection.Up, typeGenerator ); + BuildHorizontalWall( ref i, 165, 115, 1, MahjongPieceDirection.Up, typeGenerator ); + + BuildVerticalWall( ref i, 530, 165, 0, MahjongPieceDirection.Left, typeGenerator ); + BuildVerticalWall( ref i, 525, 165, 1, MahjongPieceDirection.Left, typeGenerator ); + + BuildHorizontalWall( ref i, 165, 530, 0, MahjongPieceDirection.Down, typeGenerator ); + BuildHorizontalWall( ref i, 165, 525, 1, MahjongPieceDirection.Down, typeGenerator ); + + BuildVerticalWall( ref i, 110, 165, 0, MahjongPieceDirection.Right, typeGenerator ); + BuildVerticalWall( ref i, 115, 165, 1, MahjongPieceDirection.Right, typeGenerator ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_SpectatorVision ) + list.Add( 1062717 ); // Spectator Vision Enabled + else + list.Add( 1062718 ); // Spectator Vision Disabled + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + m_Players.CheckPlayers(); + + if ( from.Alive && IsAccessibleTo( from ) && m_Players.GetInGameMobiles( true, false ).Count == 0 ) + list.Add( new ResetGameEntry( this ) ); + + SetSecureLevelEntry.AddTo( from, this, list ); + } + + private class ResetGameEntry : ContextMenuEntry + { + private MahjongGame m_Game; + + public ResetGameEntry( MahjongGame game ) : base( 6162 ) + { + m_Game = game; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( from.CheckAlive() && !m_Game.Deleted && m_Game.IsAccessibleTo( from ) && m_Game.Players.GetInGameMobiles( true, false ).Count == 0 ) + m_Game.ResetGame( from ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + m_Players.CheckPlayers(); + + m_Players.Join( from ); + } + + public void ResetGame( Mobile from ) + { + if ( DateTime.Now - m_LastReset < TimeSpan.FromSeconds( 5.0 ) ) + return; + + m_LastReset = DateTime.Now; + + if ( from != null ) + m_Players.SendLocalizedMessage( 1062771, from.Name ); // ~1_name~ has reset the game. + + m_Players.SendRelievePacket( true, true ); + + BuildWalls(); + m_DealerIndicator = new MahjongDealerIndicator( this, new Point2D( 300, 300 ), MahjongPieceDirection.Up, MahjongWind.North ); + m_WallBreakIndicator = new MahjongWallBreakIndicator( this, new Point2D( 335, 335 ) ); + m_Players = new MahjongPlayers( this, MaxPlayers, BaseScore ); + } + + public void ResetWalls( Mobile from ) + { + if ( DateTime.Now - m_LastReset < TimeSpan.FromSeconds( 5.0 ) ) + return; + + m_LastReset = DateTime.Now; + + BuildWalls(); + + m_Players.SendTilesPacket( true, true ); + + if ( from != null ) + m_Players.SendLocalizedMessage( 1062696 ); // The dealer rebuilds the wall. + } + + public int GetStackLevel( MahjongPieceDim dim ) + { + int level = -1; + foreach ( MahjongTile tile in m_Tiles ) + { + if ( tile.StackLevel > level && dim.IsOverlapping( tile.Dimensions ) ) + level = tile.StackLevel; + } + return level; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Level ); + + writer.Write( m_Tiles.Length ); + + for ( int i = 0; i < m_Tiles.Length; i++ ) + m_Tiles[i].Save( writer ); + + m_DealerIndicator.Save( writer ); + + m_WallBreakIndicator.Save( writer ); + + m_Dices.Save( writer ); + + m_Players.Save( writer ); + + writer.Write( m_ShowScores ); + writer.Write( m_SpectatorVision ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + + goto case 0; + } + case 0: + { + if ( version < 1 ) + m_Level = SecureLevel.CoOwners; + + int length = reader.ReadInt(); + m_Tiles = new MahjongTile[length]; + + for ( int i = 0; i < length; i++ ) + m_Tiles[i] = new MahjongTile( this, reader ); + + m_DealerIndicator = new MahjongDealerIndicator( this, reader ); + + m_WallBreakIndicator = new MahjongWallBreakIndicator( this, reader ); + + m_Dices = new MahjongDices( this, reader ); + + m_Players = new MahjongPlayers( this, reader ); + + m_ShowScores = reader.ReadBool(); + m_SpectatorVision = reader.ReadBool(); + + m_LastReset = DateTime.Now; + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongPacketHandlers.cs b/Scripts/Items/Games/Mahjong/MahjongPacketHandlers.cs new file mode 100644 index 000000000..bde6bf59e --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongPacketHandlers.cs @@ -0,0 +1,245 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Engines.Mahjong +{ + public delegate void OnMahjongPacketReceive( MahjongGame game, NetState state, PacketReader pvSrc ); + + public sealed class MahjongPacketHandlers + { + private static OnMahjongPacketReceive[] m_SubCommandDelegates = new OnMahjongPacketReceive[0x100]; + + public static void RegisterSubCommand( int subCmd, OnMahjongPacketReceive onReceive ) + { + m_SubCommandDelegates[subCmd] = onReceive; + } + + public static OnMahjongPacketReceive GetSubCommandDelegate( int cmd ) + { + if ( cmd >= 0 && cmd < 0x100 ) + { + return m_SubCommandDelegates[cmd]; + } + else + { + return null; + } + } + + public static void Initialize() + { + PacketHandlers.Register( 0xDA, 0, true, new OnPacketReceive( OnPacket ) ); + + RegisterSubCommand( 0x6, new OnMahjongPacketReceive( ExitGame ) ); + RegisterSubCommand( 0xA, new OnMahjongPacketReceive( GivePoints ) ); + RegisterSubCommand( 0xB, new OnMahjongPacketReceive( RollDice ) ); + RegisterSubCommand( 0xC, new OnMahjongPacketReceive( BuildWalls ) ); + RegisterSubCommand( 0xD, new OnMahjongPacketReceive( ResetScores ) ); + RegisterSubCommand( 0xF, new OnMahjongPacketReceive( AssignDealer ) ); + RegisterSubCommand( 0x10, new OnMahjongPacketReceive( OpenSeat ) ); + RegisterSubCommand( 0x11, new OnMahjongPacketReceive( ChangeOption ) ); + RegisterSubCommand( 0x15, new OnMahjongPacketReceive( MoveWallBreakIndicator ) ); + RegisterSubCommand( 0x16, new OnMahjongPacketReceive( TogglePublicHand ) ); + RegisterSubCommand( 0x17, new OnMahjongPacketReceive( MoveTile ) ); + RegisterSubCommand( 0x18, new OnMahjongPacketReceive( MoveDealerIndicator ) ); + } + + public static void OnPacket( NetState state, PacketReader pvSrc ) + { + MahjongGame game = World.FindItem( pvSrc.ReadInt32() ) as MahjongGame; + + if ( game != null ) + game.Players.CheckPlayers(); + + pvSrc.ReadByte(); + + int cmd = pvSrc.ReadByte(); + + OnMahjongPacketReceive onReceive = GetSubCommandDelegate( cmd ); + + if ( onReceive != null ) + { + onReceive( game, state, pvSrc ); + } + else + { + pvSrc.Trace( state ); + } + } + + private static MahjongPieceDirection GetDirection( int value ) + { + switch ( value ) + { + case 0: return MahjongPieceDirection.Up; + case 1: return MahjongPieceDirection.Left; + case 2: return MahjongPieceDirection.Down; + default: return MahjongPieceDirection.Right; + } + } + + private static MahjongWind GetWind( int value ) + { + switch ( value ) + { + case 0: return MahjongWind.North; + case 1: return MahjongWind.East; + case 2: return MahjongWind.South; + default: return MahjongWind.West; + } + } + + public static void ExitGame( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null ) + return; + + Mobile from = state.Mobile; + + game.Players.LeaveGame( from ); + } + + public static void GivePoints( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGamePlayer( state.Mobile ) ) + return; + + int to = pvSrc.ReadByte(); + int amount = pvSrc.ReadInt32(); + + game.Players.TransferScore( state.Mobile, to, amount ); + } + + public static void RollDice( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGamePlayer( state.Mobile ) ) + return; + + game.Dices.RollDices( state.Mobile ); + } + + public static void BuildWalls( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + game.ResetWalls( state.Mobile ); + } + + public static void ResetScores( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + game.Players.ResetScores( MahjongGame.BaseScore ); + } + + public static void AssignDealer( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + int position = pvSrc.ReadByte(); + + game.Players.AssignDealer( position ); + } + + public static void OpenSeat( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + int position = pvSrc.ReadByte(); + + if ( game.Players.GetPlayer( position ) == state.Mobile ) + return; + + game.Players.OpenSeat( position ); + } + + public static void ChangeOption( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + pvSrc.ReadInt16(); + pvSrc.ReadByte(); + + int options = pvSrc.ReadByte(); + + game.ShowScores = (options & 0x1) != 0; + game.SpectatorVision = (options & 0x2) != 0; + } + + public static void MoveWallBreakIndicator( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + int y = pvSrc.ReadInt16(); + int x = pvSrc.ReadInt16(); + + game.WallBreakIndicator.Move( new Point2D( x, y ) ); + } + + public static void TogglePublicHand( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGamePlayer( state.Mobile ) ) + return; + + pvSrc.ReadInt16(); + pvSrc.ReadByte(); + + bool publicHand = pvSrc.ReadBoolean(); + + game.Players.SetPublic( game.Players.GetPlayerIndex( state.Mobile ), publicHand ); + } + + public static void MoveTile( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGamePlayer( state.Mobile ) ) + return; + + int number = pvSrc.ReadByte(); + + if ( number < 0 || number >= game.Tiles.Length ) + return; + + pvSrc.ReadByte(); // Current direction + + MahjongPieceDirection direction = GetDirection( pvSrc.ReadByte() ); + + pvSrc.ReadByte(); + + bool flip = pvSrc.ReadBoolean(); + + pvSrc.ReadInt16(); // Current Y + pvSrc.ReadInt16(); // Current X + + pvSrc.ReadByte(); + + int y = pvSrc.ReadInt16(); + int x = pvSrc.ReadInt16(); + + pvSrc.ReadByte(); + + game.Tiles[number].Move( new Point2D( x, y ), direction, flip, game.Players.GetPlayerIndex( state.Mobile ) ); + } + + public static void MoveDealerIndicator( MahjongGame game, NetState state, PacketReader pvSrc ) + { + if ( game == null || !game.Players.IsInGameDealer( state.Mobile ) ) + return; + + MahjongPieceDirection direction = GetDirection( pvSrc.ReadByte() ); + + MahjongWind wind = GetWind( pvSrc.ReadByte() ); + + int y = pvSrc.ReadInt16(); + int x = pvSrc.ReadInt16(); + + game.DealerIndicator.Move( new Point2D( x, y ), direction, wind ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongPieceDim.cs b/Scripts/Items/Games/Mahjong/MahjongPieceDim.cs new file mode 100644 index 000000000..712be61f8 --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongPieceDim.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Engines.Mahjong +{ + public struct MahjongPieceDim + { + private Point2D m_Position; + private int m_Width; + private int m_Height; + + public Point2D Position { get { return m_Position; } } + public int Width { get { return m_Width; } } + public int Height { get { return m_Height; } } + + public MahjongPieceDim( Point2D position, int width, int height ) + { + m_Position = position; + m_Width = width; + m_Height = height; + } + + public bool IsValid() + { + return m_Position.X >= 0 && m_Position.Y >= 0 && m_Position.X + m_Width <= 670 && m_Position.Y + m_Height <= 670; + } + + public bool IsOverlapping( MahjongPieceDim dim ) + { + return m_Position.X < dim.m_Position.X + dim.m_Width && m_Position.Y < dim.m_Position.Y + dim.m_Height && m_Position.X + m_Width > dim.m_Position.X && m_Position.Y + m_Height > dim.m_Position.Y; + } + + public int GetHandArea() + { + if ( m_Position.X + m_Width > 150 && m_Position.X < 520 && m_Position.Y < 35 ) + return 0; + + if ( m_Position.X + m_Width > 635 && m_Position.Y + m_Height > 150 && m_Position.Y < 520 ) + return 1; + + if ( m_Position.X + m_Width > 150 && m_Position.X < 520 && m_Position.Y + m_Height > 635 ) + return 2; + + if ( m_Position.X < 35 && m_Position.Y + m_Height > 150 && m_Position.Y < 520 ) + return 3; + + return -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongPlayers.cs b/Scripts/Items/Games/Mahjong/MahjongPlayers.cs new file mode 100644 index 000000000..709dc8b13 --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongPlayers.cs @@ -0,0 +1,552 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.Mahjong +{ + public class MahjongPlayers + { + private MahjongGame m_Game; + private Mobile[] m_Players; + private bool[] m_InGame; + private bool[] m_PublicHand; + private int[] m_Scores; + private int m_DealerPosition; + private ArrayList m_Spectators; + + public MahjongGame Game { get { return m_Game; } } + public int Seats { get { return m_Players.Length; } } + public Mobile Dealer { get { return m_Players[m_DealerPosition]; } } + public int DealerPosition { get { return m_DealerPosition; } } + + public MahjongPlayers( MahjongGame game, int maxPlayers, int baseScore ) + { + m_Game = game; + m_Spectators = new ArrayList(); + + m_Players = new Mobile[maxPlayers]; + m_InGame = new bool[maxPlayers]; + m_PublicHand = new bool[maxPlayers]; + m_Scores = new int[maxPlayers]; + + for ( int i = 0; i < m_Scores.Length; i++ ) + m_Scores[i] = baseScore; + } + + public Mobile GetPlayer( int index ) + { + if ( index < 0 || index >= m_Players.Length ) + return null; + else + return m_Players[index]; + } + + public int GetPlayerIndex( Mobile mobile ) + { + for ( int i = 0; i < m_Players.Length; i++ ) + { + if ( m_Players[i] == mobile ) + return i; + } + return -1; + } + + public bool IsInGameDealer( Mobile mobile ) + { + if ( Dealer != mobile ) + return false; + else + return m_InGame[m_DealerPosition]; + } + + public bool IsInGamePlayer( int index ) + { + if ( index < 0 || index >= m_Players.Length || m_Players[index] == null ) + return false; + else + return m_InGame[index]; + } + + public bool IsInGamePlayer( Mobile mobile ) + { + int index = GetPlayerIndex( mobile ); + + return IsInGamePlayer( index ); + } + + public bool IsSpectator( Mobile mobile ) + { + return m_Spectators.Contains( mobile ); + } + + public int GetScore( int index ) + { + if ( index < 0 || index >= m_Scores.Length ) + return 0; + else + return m_Scores[index]; + } + + public bool IsPublic( int index ) + { + if ( index < 0 || index >= m_PublicHand.Length ) + return false; + else + return m_PublicHand[index]; + } + + public void SetPublic( int index, bool value ) + { + if ( index < 0 || index >= m_PublicHand.Length || m_PublicHand[index] == value ) + return; + + m_PublicHand[index] = value; + + SendTilesPacket( true, !m_Game.SpectatorVision ); + + if ( IsInGamePlayer( index ) ) + m_Players[index].SendLocalizedMessage( value ? 1062775 : 1062776 ); // Your hand is [not] publicly viewable. + } + + public ArrayList GetInGameMobiles( bool players, bool spectators ) + { + ArrayList list = new ArrayList(); + + if ( players ) + { + for ( int i = 0; i < m_Players.Length; i++ ) + { + if ( IsInGamePlayer( i ) ) + list.Add( m_Players[i] ); + } + } + + if ( spectators ) + { + list.AddRange( m_Spectators ); + } + + return list; + } + + public void CheckPlayers() + { + bool removed = false; + + for ( int i = 0; i < m_Players.Length; i++ ) + { + Mobile player = m_Players[i]; + + if ( player != null ) + { + if ( player.Deleted ) + { + m_Players[i] = null; + + SendPlayerExitMessage( player ); + UpdateDealer( true ); + + removed = true; + } + else if ( m_InGame[i] ) + { + if ( player.NetState == null ) + { + m_InGame[i] = false; + + SendPlayerExitMessage( player ); + UpdateDealer( true ); + + removed = true; + } + else if ( !m_Game.IsAccessibleTo( player ) || player.Map != m_Game.Map || !player.InRange( m_Game.GetWorldLocation(), 5 ) ) + { + m_InGame[i] = false; + + player.Send( new MahjongRelieve( m_Game ) ); + + SendPlayerExitMessage( player ); + UpdateDealer( true ); + + removed = true; + } + } + } + } + + for ( int i = 0; i < m_Spectators.Count; ) + { + Mobile mobile = (Mobile)m_Spectators[i]; + + if ( mobile.NetState == null || mobile.Deleted ) + { + m_Spectators.RemoveAt( i ); + } + else if ( !m_Game.IsAccessibleTo( mobile ) || mobile.Map != m_Game.Map || !mobile.InRange( m_Game.GetWorldLocation(), 5 ) ) + { + m_Spectators.RemoveAt( i ); + + mobile.Send( new MahjongRelieve( m_Game ) ); + } + else + { + i++; + } + } + + if ( removed && !UpdateSpectators() ) + SendPlayersPacket( true, true ); + } + + private void UpdateDealer( bool message ) + { + if ( IsInGamePlayer( m_DealerPosition ) ) + return; + + for ( int i = m_DealerPosition + 1; i < m_Players.Length; i++ ) + { + if ( IsInGamePlayer( i ) ) + { + m_DealerPosition = i; + + if ( message ) + SendDealerChangedMessage(); + + return; + } + } + + for ( int i = 0; i < m_DealerPosition; i++ ) + { + if ( IsInGamePlayer( i ) ) + { + m_DealerPosition = i; + + if ( message ) + SendDealerChangedMessage(); + + return; + } + } + } + + private int GetNextSeat() + { + for ( int i = m_DealerPosition; i < m_Players.Length; i++ ) + { + if ( m_Players[i] == null ) + return i; + } + + for ( int i = 0; i < m_DealerPosition; i++ ) + { + if ( m_Players[i] == null ) + return i; + } + + return -1; + } + + private bool UpdateSpectators() + { + if ( m_Spectators.Count == 0 ) + return false; + + int nextSeat = GetNextSeat(); + + if ( nextSeat >= 0 ) + { + Mobile newPlayer = (Mobile)m_Spectators[0]; + + m_Spectators.RemoveAt( 0 ); + + AddPlayer( newPlayer, nextSeat, false ); + + UpdateSpectators(); + + return true; + } + else + { + return false; + } + } + + private void AddPlayer( Mobile player, int index, bool sendJoinGame ) + { + m_Players[index] = player; + m_InGame[index] = true; + + UpdateDealer( false ); + + if ( sendJoinGame ) + player.Send( new MahjongJoinGame( m_Game ) ); + + SendPlayersPacket( true, true ); + + player.Send( new MahjongGeneralInfo( m_Game ) ); + player.Send( new MahjongTilesInfo( m_Game, player ) ); + + if ( m_DealerPosition == index ) + SendLocalizedMessage( 1062773, player.Name ); // ~1_name~ has entered the game as the dealer. + else + SendLocalizedMessage( 1062772, player.Name ); // ~1_name~ has entered the game as a player. + } + + private void AddSpectator( Mobile mobile ) + { + if ( !IsSpectator( mobile ) ) + { + m_Spectators.Add( mobile ); + } + + mobile.Send( new MahjongJoinGame( m_Game ) ); + mobile.Send( new MahjongPlayersInfo( m_Game, mobile ) ); + mobile.Send( new MahjongGeneralInfo( m_Game ) ); + mobile.Send( new MahjongTilesInfo( m_Game, mobile ) ); + } + + public void Join( Mobile mobile ) + { + int index = GetPlayerIndex( mobile ); + + if ( index >= 0 ) + { + AddPlayer( mobile, index, true ); + } + else + { + int nextSeat = GetNextSeat(); + + if ( nextSeat >= 0 ) + { + AddPlayer( mobile, nextSeat, true ); + } + else + { + AddSpectator( mobile ); + } + } + } + + public void LeaveGame( Mobile player ) + { + int index = GetPlayerIndex( player ); + if ( index >= 0 ) + { + m_InGame[index] = false; + + SendPlayerExitMessage( player ); + UpdateDealer( true ); + + SendPlayersPacket( true, true ); + } + else + { + m_Spectators.Remove( player ); + } + } + + public void ResetScores( int value ) + { + for ( int i = 0; i < m_Scores.Length; i++ ) + { + m_Scores[i] = value; + } + + SendPlayersPacket( true, m_Game.ShowScores ); + + SendLocalizedMessage( 1062697 ); // The dealer redistributes the score sticks evenly. + } + + public void TransferScore( Mobile from, int toPosition, int amount ) + { + int fromPosition = GetPlayerIndex( from ); + Mobile to = GetPlayer( toPosition ); + + if ( fromPosition < 0 || to == null || m_Scores[fromPosition] < amount ) + return; + + m_Scores[fromPosition] -= amount; + m_Scores[toPosition] += amount; + + if ( m_Game.ShowScores ) + { + SendPlayersPacket( true, true ); + } + else + { + from.Send( new MahjongPlayersInfo( m_Game, from ) ); + to.Send( new MahjongPlayersInfo( m_Game, to ) ); + } + + SendLocalizedMessage( 1062774, string.Format( "{0}\t{1}\t{2}", from.Name, to.Name, amount ) ); // ~1_giver~ gives ~2_reciever~ ~3_number~ points. + } + + public void OpenSeat( int index ) + { + Mobile player = GetPlayer( index ); + if ( player == null ) + return; + + if ( m_InGame[index] ) + player.Send( new MahjongRelieve( m_Game ) ); + + m_Players[index] = null; + + SendLocalizedMessage( 1062699, player.Name ); // ~1_name~ is relieved from the game by the dealer. + + UpdateDealer( true ); + + if ( !UpdateSpectators() ) + SendPlayersPacket( true, true ); + } + + public void AssignDealer( int index ) + { + Mobile to = GetPlayer( index ); + + if ( to == null || !m_InGame[index] ) + return; + + int oldDealer = m_DealerPosition; + + m_DealerPosition = index; + + if ( IsInGamePlayer( oldDealer ) ) + m_Players[oldDealer].Send( new MahjongPlayersInfo( m_Game, m_Players[oldDealer] ) ); + + to.Send( new MahjongPlayersInfo( m_Game, to ) ); + + SendDealerChangedMessage(); + } + + private void SendDealerChangedMessage() + { + if ( Dealer != null ) + SendLocalizedMessage( 1062698, Dealer.Name ); // ~1_name~ is assigned the dealer. + } + + private void SendPlayerExitMessage( Mobile who ) + { + SendLocalizedMessage( 1062762, who.Name ); // ~1_name~ has left the game. + } + + public void SendPlayersPacket( bool players, bool spectators ) + { + foreach ( Mobile mobile in GetInGameMobiles( players, spectators ) ) + { + mobile.Send( new MahjongPlayersInfo( m_Game, mobile ) ); + } + } + + public void SendGeneralPacket( bool players, bool spectators ) + { + ArrayList mobiles = GetInGameMobiles( players, spectators ); + + if ( mobiles.Count == 0 ) + return; + + MahjongGeneralInfo generalInfo = new MahjongGeneralInfo( m_Game ); + + generalInfo.Acquire(); + + foreach ( Mobile mobile in mobiles ) + { + mobile.Send( generalInfo ); + } + + generalInfo.Release(); + } + + public void SendTilesPacket( bool players, bool spectators ) + { + foreach ( Mobile mobile in GetInGameMobiles( players, spectators ) ) + { + mobile.Send( new MahjongTilesInfo( m_Game, mobile ) ); + } + } + + public void SendTilePacket( MahjongTile tile, bool players, bool spectators ) + { + foreach ( Mobile mobile in GetInGameMobiles( players, spectators ) ) + { + mobile.Send( new MahjongTileInfo( tile, mobile ) ); + } + } + + public void SendRelievePacket( bool players, bool spectators ) + { + ArrayList mobiles = GetInGameMobiles( players, spectators ); + + if ( mobiles.Count == 0 ) + return; + + MahjongRelieve relieve = new MahjongRelieve( m_Game ); + + relieve.Acquire(); + + foreach ( Mobile mobile in mobiles ) + { + mobile.Send( relieve ); + } + + relieve.Release(); + } + + public void SendLocalizedMessage( int number ) + { + foreach ( Mobile mobile in GetInGameMobiles( true, true ) ) + { + mobile.SendLocalizedMessage( number ); + } + } + + public void SendLocalizedMessage( int number, string args ) + { + foreach ( Mobile mobile in GetInGameMobiles( true, true ) ) + { + mobile.SendLocalizedMessage( number, args ); + } + } + + public void Save( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( Seats ); + + for ( int i = 0; i < Seats; i++ ) + { + writer.Write( m_Players[i] ); + writer.Write( m_PublicHand[i] ); + writer.Write( m_Scores[i] ); + } + + writer.Write( m_DealerPosition ); + } + + public MahjongPlayers( MahjongGame game, GenericReader reader ) + { + m_Game = game; + m_Spectators = new ArrayList(); + + int version = reader.ReadInt(); + + int seats = reader.ReadInt(); + m_Players = new Mobile[seats]; + m_InGame = new bool[seats]; + m_PublicHand = new bool[seats]; + m_Scores = new int[seats]; + + for ( int i = 0; i < seats; i++ ) + { + m_Players[i] = reader.ReadMobile(); + m_PublicHand[i] = reader.ReadBool(); + m_Scores[i] = reader.ReadInt(); + } + + m_DealerPosition = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongTile.cs b/Scripts/Items/Games/Mahjong/MahjongTile.cs new file mode 100644 index 000000000..7006dff1f --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongTile.cs @@ -0,0 +1,97 @@ +using System; +using Server; + +namespace Server.Engines.Mahjong +{ + public class MahjongTile + { + public static MahjongPieceDim GetDimensions( Point2D position, MahjongPieceDirection direction ) + { + if ( direction == MahjongPieceDirection.Up || direction == MahjongPieceDirection.Down ) + return new MahjongPieceDim( position, 20, 30 ); + else + return new MahjongPieceDim( position, 30, 20 ); + } + + private MahjongGame m_Game; + private int m_Number; + private MahjongTileType m_Value; + protected Point2D m_Position; + private int m_StackLevel; + private MahjongPieceDirection m_Direction; + private bool m_Flipped; + + public MahjongGame Game { get { return m_Game; } } + public int Number { get { return m_Number; } } + public MahjongTileType Value { get { return m_Value; } } + public Point2D Position { get { return m_Position; } } + public int StackLevel { get { return m_StackLevel; } } + public MahjongPieceDirection Direction { get { return m_Direction; } } + public bool Flipped { get { return m_Flipped; } } + + public MahjongTile( MahjongGame game, int number, MahjongTileType value, Point2D position, int stackLevel, MahjongPieceDirection direction, bool flipped ) + { + m_Game = game; + m_Number = number; + m_Value = value; + m_Position = position; + m_StackLevel = stackLevel; + m_Direction = direction; + m_Flipped = flipped; + } + + public MahjongPieceDim Dimensions + { + get { return GetDimensions( m_Position, m_Direction ); } + } + + public bool IsMovable + { + get { return m_Game.GetStackLevel( Dimensions ) <= m_StackLevel; } + } + + public void Move( Point2D position, MahjongPieceDirection direction, bool flip, int validHandArea ) + { + MahjongPieceDim dim = GetDimensions( position, direction ); + int curHandArea = Dimensions.GetHandArea(); + int newHandArea = dim.GetHandArea(); + + if ( !IsMovable || !dim.IsValid() || ( validHandArea >= 0 && ((curHandArea >= 0 && curHandArea != validHandArea) || (newHandArea >= 0 && newHandArea != validHandArea)) ) ) + return; + + m_Position = position; + m_Direction = direction; + m_StackLevel = -1; // Avoid self interference + m_StackLevel = m_Game.GetStackLevel( dim ) + 1; + m_Flipped = flip; + + m_Game.Players.SendTilePacket( this, true, true ); + } + + public void Save( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( m_Number ); + writer.Write( (int) m_Value ); + writer.Write( m_Position ); + writer.Write( m_StackLevel ); + writer.Write( (int) m_Direction ); + writer.Write( m_Flipped ); + } + + public MahjongTile( MahjongGame game, GenericReader reader ) + { + m_Game = game; + + int version = reader.ReadInt(); + + m_Number = reader.ReadInt(); + m_Value = (MahjongTileType) reader.ReadInt(); + m_Position = reader.ReadPoint2D(); + m_StackLevel = reader.ReadInt(); + m_Direction = (MahjongPieceDirection) reader.ReadInt(); + m_Flipped = reader.ReadBool(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongTileTypeGenerator.cs b/Scripts/Items/Games/Mahjong/MahjongTileTypeGenerator.cs new file mode 100644 index 000000000..4ab4262c0 --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongTileTypeGenerator.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Engines.Mahjong +{ + public class MahjongTileTypeGenerator + { + private ArrayList m_LeftTileTypes; + + public ArrayList LeftTileTypes { get { return m_LeftTileTypes; } } + + public MahjongTileTypeGenerator( int count ) + { + m_LeftTileTypes = new ArrayList( 34 * count ); + + for ( int i = 1; i <= 34; i++ ) + { + for ( int j = 0; j < count; j++ ) + { + m_LeftTileTypes.Add( (MahjongTileType)i ); + } + } + } + + public MahjongTileType Next() + { + int random = Utility.Random( m_LeftTileTypes.Count ); + MahjongTileType next = (MahjongTileType)m_LeftTileTypes[random]; + m_LeftTileTypes.RemoveAt( random ); + + return next; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/MahjongWallBreakIndicator.cs b/Scripts/Items/Games/Mahjong/MahjongWallBreakIndicator.cs new file mode 100644 index 000000000..d3fbbbe0d --- /dev/null +++ b/Scripts/Items/Games/Mahjong/MahjongWallBreakIndicator.cs @@ -0,0 +1,58 @@ +using System; +using Server; + +namespace Server.Engines.Mahjong +{ + public class MahjongWallBreakIndicator + { + public static MahjongPieceDim GetDimensions( Point2D position ) + { + return new MahjongPieceDim( position, 20, 20 ); + } + + private MahjongGame m_Game; + private Point2D m_Position; + + public MahjongGame Game { get { return m_Game; } } + public Point2D Position { get { return m_Position; } } + + public MahjongWallBreakIndicator( MahjongGame game, Point2D position ) + { + m_Game = game; + m_Position = position; + } + + public MahjongPieceDim Dimensions + { + get { return GetDimensions( m_Position ); } + } + + public void Move( Point2D position ) + { + MahjongPieceDim dim = GetDimensions( position ); + + if ( !dim.IsValid() ) + return; + + m_Position = position; + + m_Game.Players.SendGeneralPacket( true, true ); + } + + public void Save( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( m_Position ); + } + + public MahjongWallBreakIndicator( MahjongGame game, GenericReader reader ) + { + m_Game = game; + + int version = reader.ReadInt(); + + m_Position = reader.ReadPoint2D(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Games/Mahjong/Packets.cs b/Scripts/Items/Games/Mahjong/Packets.cs new file mode 100644 index 000000000..e4cd8d35e --- /dev/null +++ b/Scripts/Items/Games/Mahjong/Packets.cs @@ -0,0 +1,208 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Engines.Mahjong +{ + public sealed class MahjongJoinGame : Packet + { + public MahjongJoinGame( MahjongGame game ) : base( 0xDA ) + { + EnsureCapacity( 9 ); + + m_Stream.Write( (int) game.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0x19 ); + } + } + + public sealed class MahjongPlayersInfo : Packet + { + public MahjongPlayersInfo( MahjongGame game, Mobile to ) : base( 0xDA ) + { + MahjongPlayers players = game.Players; + + EnsureCapacity( 11 + 45 * players.Seats ); + + m_Stream.Write( (int) game.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0x2 ); + + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) players.Seats ); + + int n = 0; + for ( int i = 0; i < players.Seats; i++ ) + { + Mobile mobile = players.GetPlayer( i ); + + if ( mobile != null ) + { + m_Stream.Write( (int) mobile.Serial ); + m_Stream.Write( players.DealerPosition == i ? (byte) 0x1 : (byte) 0x2 ); + m_Stream.Write( (byte) i ); + + if ( game.ShowScores || mobile == to ) + m_Stream.Write( (int) players.GetScore( i ) ); + else + m_Stream.Write( (int) 0 ); + + m_Stream.Write( (short) 0 ); + m_Stream.Write( (byte) 0 ); + + m_Stream.Write( players.IsPublic( i ) ); + + m_Stream.WriteAsciiFixed( mobile.Name, 30 ); + m_Stream.Write( !players.IsInGamePlayer( i ) ); + + n++; + } + else if ( game.ShowScores ) + { + m_Stream.Write( (int) 0 ); + m_Stream.Write( (byte) 0x2 ); + m_Stream.Write( (byte) i ); + + m_Stream.Write( (int) players.GetScore( i ) ); + + m_Stream.Write( (short) 0 ); + m_Stream.Write( (byte) 0 ); + + m_Stream.Write( players.IsPublic( i ) ); + + m_Stream.WriteAsciiFixed( "", 30 ); + m_Stream.Write( true ); + + n++; + } + } + + if ( n != players.Seats ) + { + m_Stream.Seek( 10, System.IO.SeekOrigin.Begin ); + m_Stream.Write( (byte) n ); + } + } + } + + public sealed class MahjongGeneralInfo : Packet + { + public MahjongGeneralInfo( MahjongGame game ) : base( 0xDA ) + { + EnsureCapacity( 13 ); + + m_Stream.Write( (int) game.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0x5 ); + + m_Stream.Write( (short) 0 ); + m_Stream.Write( (byte) 0 ); + + m_Stream.Write( (byte) ((game.ShowScores ? 0x1 : 0x0) | (game.SpectatorVision ? 0x2 : 0x0)) ); + + m_Stream.Write( (byte) game.Dices.First ); + m_Stream.Write( (byte) game.Dices.Second ); + + m_Stream.Write( (byte) game.DealerIndicator.Wind ); + m_Stream.Write( (short) game.DealerIndicator.Position.Y ); + m_Stream.Write( (short) game.DealerIndicator.Position.X ); + m_Stream.Write( (byte) game.DealerIndicator.Direction ); + + m_Stream.Write( (short) game.WallBreakIndicator.Position.Y ); + m_Stream.Write( (short) game.WallBreakIndicator.Position.X ); + } + } + + public sealed class MahjongTilesInfo : Packet + { + public MahjongTilesInfo( MahjongGame game, Mobile to ) : base( 0xDA ) + { + MahjongTile[] tiles = game.Tiles; + MahjongPlayers players = game.Players; + + EnsureCapacity( 11 + 9 * tiles.Length ); + + m_Stream.Write( (int) game.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0x4 ); + + m_Stream.Write( (short) tiles.Length ); + + foreach ( MahjongTile tile in tiles ) + { + m_Stream.Write( (byte) tile.Number ); + + if ( tile.Flipped ) + { + int hand = tile.Dimensions.GetHandArea(); + + if ( hand < 0 || players.IsPublic( hand ) || players.GetPlayer( hand ) == to || (game.SpectatorVision && players.IsSpectator( to )) ) + m_Stream.Write( (byte)tile.Value ); + else + m_Stream.Write( (byte) 0 ); + } + else + { + m_Stream.Write( (byte) 0 ); + } + + m_Stream.Write( (short) tile.Position.Y ); + m_Stream.Write( (short) tile.Position.X ); + m_Stream.Write( (byte) tile.StackLevel ); + m_Stream.Write( (byte) tile.Direction ); + + m_Stream.Write( tile.Flipped ? (byte) 0x10 : (byte) 0x0 ); + } + } + } + + public sealed class MahjongTileInfo : Packet + { + public MahjongTileInfo( MahjongTile tile, Mobile to ) : base( 0xDA ) + { + MahjongGame game = tile.Game; + MahjongPlayers players = game.Players; + + EnsureCapacity( 18 ); + + m_Stream.Write( (int) tile.Game.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0x3 ); + + m_Stream.Write( (byte) tile.Number ); + + if ( tile.Flipped ) + { + int hand = tile.Dimensions.GetHandArea(); + + if ( hand < 0 || players.IsPublic( hand ) || players.GetPlayer( hand ) == to || (game.SpectatorVision && players.IsSpectator( to )) ) + m_Stream.Write( (byte)tile.Value ); + else + m_Stream.Write( (byte) 0 ); + } + else + { + m_Stream.Write( (byte) 0 ); + } + + m_Stream.Write( (short) tile.Position.Y ); + m_Stream.Write( (short) tile.Position.X ); + m_Stream.Write( (byte) tile.StackLevel ); + m_Stream.Write( (byte) tile.Direction ); + + m_Stream.Write( tile.Flipped ? (byte) 0x10 : (byte) 0x0 ); + } + } + + public sealed class MahjongRelieve : Packet + { + public MahjongRelieve( MahjongGame game ) : base( 0xDA ) + { + EnsureCapacity( 9 ); + + m_Stream.Write( (int) game.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0x1A ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Amber.cs b/Scripts/Items/Gems/Amber.cs new file mode 100644 index 000000000..64ca7987d --- /dev/null +++ b/Scripts/Items/Gems/Amber.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Amber : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Amber() : this( 1 ) + { + } + + [Constructable] + public Amber( int amount ) : base( 0xF25 ) + { + Stackable = true; + Amount = amount; + } + + public Amber( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Amethyst.cs b/Scripts/Items/Gems/Amethyst.cs new file mode 100644 index 000000000..2bd9e2673 --- /dev/null +++ b/Scripts/Items/Gems/Amethyst.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Amethyst : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Amethyst() : this( 1 ) + { + } + + [Constructable] + public Amethyst( int amount ) : base( 0xF16 ) + { + Stackable = true; + Amount = amount; + } + + public Amethyst( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Citrine.cs b/Scripts/Items/Gems/Citrine.cs new file mode 100644 index 000000000..f87fb3fbb --- /dev/null +++ b/Scripts/Items/Gems/Citrine.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Citrine : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Citrine() : this( 1 ) + { + } + + [Constructable] + public Citrine( int amount ) : base( 0xF15 ) + { + Stackable = true; + Amount = amount; + } + + public Citrine( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Diamond.cs b/Scripts/Items/Gems/Diamond.cs new file mode 100644 index 000000000..14b7935dc --- /dev/null +++ b/Scripts/Items/Gems/Diamond.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Diamond : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Diamond() : this( 1 ) + { + } + + [Constructable] + public Diamond( int amount ) : base( 0xF26 ) + { + Stackable = true; + Amount = amount; + } + + public Diamond( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Emerald.cs b/Scripts/Items/Gems/Emerald.cs new file mode 100644 index 000000000..437c3eea7 --- /dev/null +++ b/Scripts/Items/Gems/Emerald.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Emerald : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Emerald() : this( 1 ) + { + } + + [Constructable] + public Emerald( int amount ) : base( 0xF10 ) + { + Stackable = true; + Amount = amount; + } + + public Emerald( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Ruby.cs b/Scripts/Items/Gems/Ruby.cs new file mode 100644 index 000000000..62f3c0761 --- /dev/null +++ b/Scripts/Items/Gems/Ruby.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Ruby : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Ruby() : this( 1 ) + { + } + + [Constructable] + public Ruby( int amount ) : base( 0xF13 ) + { + Stackable = true; + Amount = amount; + } + + public Ruby( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Sapphire.cs b/Scripts/Items/Gems/Sapphire.cs new file mode 100644 index 000000000..c845532c6 --- /dev/null +++ b/Scripts/Items/Gems/Sapphire.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Sapphire : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Sapphire() : this( 1 ) + { + } + + [Constructable] + public Sapphire( int amount ) : base( 0xF19 ) + { + Stackable = true; + Amount = amount; + } + + public Sapphire( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/StarSapphire.cs b/Scripts/Items/Gems/StarSapphire.cs new file mode 100644 index 000000000..a94e278b3 --- /dev/null +++ b/Scripts/Items/Gems/StarSapphire.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StarSapphire : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public StarSapphire() : this( 1 ) + { + } + + [Constructable] + public StarSapphire( int amount ) : base( 0xF21 ) + { + Stackable = true; + Amount = amount; + } + + public StarSapphire( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Gems/Tourmaline.cs b/Scripts/Items/Gems/Tourmaline.cs new file mode 100644 index 000000000..18b233afa --- /dev/null +++ b/Scripts/Items/Gems/Tourmaline.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Tourmaline : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Tourmaline() : this( 1 ) + { + } + + [Constructable] + public Tourmaline( int amount ) : base( 0xF2D ) + { + Stackable = true; + Amount = amount; + } + + public Tourmaline( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Guilds/GuildDeed.cs b/Scripts/Items/Guilds/GuildDeed.cs new file mode 100644 index 000000000..4360ac781 --- /dev/null +++ b/Scripts/Items/Guilds/GuildDeed.cs @@ -0,0 +1,143 @@ +using System; +using Server.Network; +using Server.Prompts; +using Server.Guilds; +using Server.Multis; +using Server.Regions; + +namespace Server.Items +{ + public class GuildDeed : Item + { + public override int LabelNumber{ get{ return 1041055; } } // a guild deed + + [Constructable] + public GuildDeed() : base( 0x14F0 ) + { + Weight = 1.0; + } + + public GuildDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 0.0 ) + Weight = 1.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if( Guild.NewGuildSystem ) + return; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.Guild != null ) + { + from.SendLocalizedMessage( 501137 ); // You must resign from your current guild before founding another! + } + else + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house == null ) + { + from.SendLocalizedMessage( 501138 ); // You can only place a guildstone in a house. + } + else if ( house.FindGuildstone() != null ) + { + from.SendLocalizedMessage( 501142 );//Only one guildstone may reside in a given house. + } + else if ( !house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 501141 ); // You can only place a guildstone in a house you own! + } + else + { + from.SendLocalizedMessage( 1013060 ); // Enter new guild name (40 characters max): + from.Prompt = new InternalPrompt( this ); + } + } + } + + private class InternalPrompt : Prompt + { + private GuildDeed m_Deed; + + public InternalPrompt( GuildDeed deed ) + { + m_Deed = deed; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Deed.Deleted ) + return; + + if ( !m_Deed.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.Guild != null ) + { + from.SendLocalizedMessage( 501137 ); // You must resign from your current guild before founding another! + } + else + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house == null ) + { + from.SendLocalizedMessage( 501138 ); // You can only place a guildstone in a house. + } + else if ( house.FindGuildstone() != null ) + { + from.SendLocalizedMessage( 501142 );//Only one guildstone may reside in a given house. + } + else if ( !house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 501141 ); // You can only place a guildstone in a house you own! + } + else + { + m_Deed.Delete(); + + if ( text.Length > 40 ) + text = text.Substring( 0, 40 ); + + Guild guild = new Guild( from, text, "none" ); + + from.Guild = guild; + from.GuildTitle = "Guildmaster"; + + Guildstone stone = new Guildstone( guild ); + + stone.MoveToWorld( from.Location, from.Map ); + + guild.Guildstone = stone; + } + } + } + + public override void OnCancel( Mobile from ) + { + from.SendLocalizedMessage( 501145 ); // Placement of guildstone cancelled. + } + } + } +} diff --git a/Scripts/Items/Guilds/GuildTeleporter.cs b/Scripts/Items/Guilds/GuildTeleporter.cs new file mode 100644 index 000000000..ef756d094 --- /dev/null +++ b/Scripts/Items/Guilds/GuildTeleporter.cs @@ -0,0 +1,105 @@ +using System; +using Server.Network; +using Server.Prompts; +using Server.Guilds; +using Server.Multis; +using Server.Regions; + +namespace Server.Items +{ + public class GuildTeleporter : Item + { + private Item m_Stone; + + public override int LabelNumber{ get{ return 1041054; } } // guildstone teleporter + + [Constructable] + public GuildTeleporter() : this( null ) + { + } + + public GuildTeleporter( Item stone ) : base( 0x1869 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + + m_Stone = stone; + } + + public GuildTeleporter( Serial serial ) : base( serial ) + { + } + + public override bool DisplayLootType{ get{ return false; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Stone ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Stone = reader.ReadItem(); + + break; + } + } + + if ( Weight == 0.0 ) + Weight = 1.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if( Guild.NewGuildSystem ) + return; + + Guildstone stone = m_Stone as Guildstone; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( stone == null || stone.Deleted || stone.Guild == null || stone.Guild.Teleporter != this ) + { + from.SendLocalizedMessage( 501197 ); // This teleporting object can not determine what guildstone to teleport + } + else + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house == null ) + { + from.SendLocalizedMessage( 501138 ); // You can only place a guildstone in a house. + } + else if ( !house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 501141 ); // You can only place a guildstone in a house you own! + } + else if( house.FindGuildstone() != null ) + { + from.SendLocalizedMessage( 501142 );//Only one guildstone may reside in a given house. + } + else + { + m_Stone.MoveToWorld( from.Location, from.Map ); + Delete(); + stone.Guild.Teleporter = null; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Guilds/Guildstone.cs b/Scripts/Items/Guilds/Guildstone.cs new file mode 100644 index 000000000..87207b607 --- /dev/null +++ b/Scripts/Items/Guilds/Guildstone.cs @@ -0,0 +1,379 @@ +using System; +using System.IO; +using Server.Gumps; +using Server.Guilds; +using Server.Network; +using Server.Factions; +using Server.Multis; + +namespace Server.Items +{ + public class Guildstone : Item, IAddon, IChopable + { + private Guild m_Guild; + private string m_GuildName; + private string m_GuildAbbrev; + + public Guild Guild + { + get + { + return m_Guild; + } + } + + public override int LabelNumber { get { return 1041429; } } // a guildstone + + public Guildstone( Guild g ) : this( g, g.Name, g.Abbreviation ) + { + } + + public Guildstone( Guild g, string guildName, string abbrev ) : base( Guild.NewGuildSystem ? 0xED6 : 0xED4 ) + { + m_Guild = g; + m_GuildName = guildName; + m_GuildAbbrev = abbrev; + + Movable = false; + } + + public Guildstone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + if( m_Guild != null && !m_Guild.Disbanded ) + { + m_GuildName = m_Guild.Name; + m_GuildAbbrev = m_Guild.Abbreviation; + } + + writer.Write( (int)3 ); // version + + writer.Write( m_BeforeChangeover ); + + writer.Write( m_GuildName ); + writer.Write( m_GuildAbbrev ); + + writer.Write( m_Guild ); + } + + private bool m_BeforeChangeover; + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 3: + { + m_BeforeChangeover = reader.ReadBool(); + goto case 2; + } + case 2: + { + m_GuildName = reader.ReadString(); + m_GuildAbbrev = reader.ReadString(); + + goto case 1; + } + case 1: + { + m_Guild = reader.ReadGuild() as Guild; + + goto case 0; + } + case 0: + { + break; + } + } + + if( Guild.NewGuildSystem && ItemID == 0xED4 ) + ItemID = 0xED6; + + if( m_Guild != null ) + { + m_GuildName = m_Guild.Name; + m_GuildAbbrev = m_Guild.Abbreviation; + } + + if( version <= 2 ) + m_BeforeChangeover = true; + + if( Guild.NewGuildSystem && m_BeforeChangeover ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( AddToHouse ) ); + + if( !Guild.NewGuildSystem && m_Guild == null ) + this.Delete(); + } + + private void AddToHouse() + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if( Guild.NewGuildSystem && m_BeforeChangeover && house != null && !house.Addons.Contains( this ) ) + { + house.Addons.Add( this ); + m_BeforeChangeover = false; + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if( m_Guild != null && !m_Guild.Disbanded ) + { + string name; + string abbr; + + if( (name = m_Guild.Name) == null || (name = name.Trim()).Length <= 0 ) + name = "(unnamed)"; + + if( (abbr = m_Guild.Abbreviation) == null || (abbr = abbr.Trim()).Length <= 0 ) + abbr = ""; + + //list.Add( 1060802, Utility.FixHtml( name ) ); // Guild name: ~1_val~ + list.Add( 1060802, String.Format( "{0} [{1}]", Utility.FixHtml( name ), Utility.FixHtml( abbr ) ) ); + } + else + { + list.Add( 1060802, String.Format( "{0} [{1}]", Utility.FixHtml( m_GuildName ), Utility.FixHtml( m_GuildAbbrev ) ) ); + } + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + string name; + + if( m_Guild == null ) + name = "(unfounded)"; + else if( (name = m_Guild.Name) == null || (name = name.Trim()).Length <= 0 ) + name = "(unnamed)"; + + this.LabelTo( from, name ); + } + + public override void OnAfterDelete() + { + if( !Guild.NewGuildSystem && m_Guild != null && !m_Guild.Disbanded ) + m_Guild.Disband(); + } + + public override void OnDoubleClick( Mobile from ) + { + if( Guild.NewGuildSystem ) + return; + + if( m_Guild == null || m_Guild.Disbanded ) + { + Delete(); + } + else if( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if( m_Guild.Accepted.Contains( from ) ) + { + #region Factions + PlayerState guildState = PlayerState.Find( m_Guild.Leader ); + PlayerState targetState = PlayerState.Find( from ); + + Faction guildFaction = (guildState == null ? null : guildState.Faction); + Faction targetFaction = (targetState == null ? null : targetState.Faction); + + if( guildFaction != targetFaction || (targetState != null && targetState.IsLeaving) ) + return; + + if( guildState != null && targetState != null ) + targetState.Leaving = guildState.Leaving; + #endregion + + m_Guild.Accepted.Remove( from ); + m_Guild.AddMember( from ); + + GuildGump.EnsureClosed( from ); + from.SendGump( new GuildGump( from, m_Guild ) ); + } + else if( from.AccessLevel < AccessLevel.GameMaster && !m_Guild.IsMember( from ) ) + { + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, 501158, "", "" ) ); // You are not a member ... + } + else + { + GuildGump.EnsureClosed( from ); + from.SendGump( new GuildGump( from, m_Guild ) ); + } + } + + #region IAddon Members + public Item Deed + { + get { return new GuildstoneDeed( m_Guild, m_GuildName, m_GuildAbbrev ); } + } + + public bool CouldFit( IPoint3D p, Map map ) + { + return map.CanFit( p.X, p.Y, p.Z, this.ItemData.Height ); + } + + #endregion + + #region IChopable Members + + public void OnChop( Mobile from ) + { + if( !Guild.NewGuildSystem ) + return; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if( ( house == null && m_BeforeChangeover ) || ( house != null && house.IsOwner( from ) && house.Addons.Contains( this ) )) + { + Effects.PlaySound( GetWorldLocation(), Map, 0x3B3 ); + from.SendLocalizedMessage( 500461 ); // You destroy the item. + + Delete(); + + if( house != null && house.Addons.Contains( this ) ) + house.Addons.Remove( this ); + + Item deed = Deed; + + if( deed != null ) + { + from.AddToBackpack( deed ); + } + } + } + + #endregion + } + + [Flipable( 0x14F0, 0x14EF )] + public class GuildstoneDeed : Item + { + public override int LabelNumber { get { return 1041233; } } // deed to a guildstone + + private Guild m_Guild; + private string m_GuildName; + private string m_GuildAbbrev; + + [Constructable] + public GuildstoneDeed( Guild g, string guildName, string abbrev ) : base( 0x14F0 ) + { + m_Guild = g; + m_GuildName = guildName; + m_GuildAbbrev = abbrev; + + Weight = 1.0; + } + + public GuildstoneDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if( m_Guild != null && !m_Guild.Disbanded ) + { + string name; + string abbr; + + if( (name = m_Guild.Name) == null || (name = name.Trim()).Length <= 0 ) + name = "(unnamed)"; + + if( (abbr = m_Guild.Abbreviation) == null || (abbr = abbr.Trim()).Length <= 0 ) + abbr = ""; + + //list.Add( 1060802, Utility.FixHtml( name ) ); // Guild name: ~1_val~ + list.Add( 1060802, String.Format( "{0} [{1}]", Utility.FixHtml( name ), Utility.FixHtml( abbr ) ) ); + } + else + { + list.Add( 1060802, String.Format( "{0} [{1}]", Utility.FixHtml( m_GuildName ), Utility.FixHtml( m_GuildAbbrev ) ) ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + if( IsChildOf( from.Backpack ) ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if( house != null && house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 1062838 ); // Where would you like to place this decoration? + from.BeginTarget( -1, true, Targeting.TargetFlags.None, new TargetStateCallback( Placement_OnTarget ), null ); + } + else + { + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public void Placement_OnTarget( Mobile from, object targeted, object state ) + { + IPoint3D p = targeted as IPoint3D; + + if( p == null || Deleted ) + return; + + Point3D loc = new Point3D( p ); + + BaseHouse house = BaseHouse.FindHouseAt( loc, from.Map, 16 ); + + if( IsChildOf( from.Backpack ) ) + { + if( house != null && house.IsOwner( from ) ) + { + Item addon = new Guildstone( m_Guild, m_GuildName, m_GuildAbbrev ); + + addon.MoveToWorld( loc, from.Map ); + + house.Addons.Add( addon ); + Delete(); + } + else + { + from.SendLocalizedMessage( 1042036 ); // That location is not in your house. + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + } +} diff --git a/Scripts/Items/Jewels/Artifacts/BraceletOfHealth.cs b/Scripts/Items/Jewels/Artifacts/BraceletOfHealth.cs new file mode 100644 index 000000000..5871d504a --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/BraceletOfHealth.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BraceletOfHealth : GoldBracelet + { + public override int LabelNumber{ get{ return 1061103; } } // Bracelet of Health + public override int ArtifactRarity{ get{ return 11; } } + + [Constructable] + public BraceletOfHealth() + { + Hue = 0x21; + Attributes.BonusHits = 5; + Attributes.RegenHits = 10; + } + + public BraceletOfHealth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs b/Scripts/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs new file mode 100644 index 000000000..76dca030a --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/Craftable/EssenceOfBattle.cs @@ -0,0 +1,36 @@ +using System; + +namespace Server.Items +{ + public class EssenceOfBattle : GoldRing + { + public override int LabelNumber{ get{ return 1072935; } } // Essence of Battle + + [Constructable] + public EssenceOfBattle() + { + Hue = 0x550; + Attributes.BonusDex = 7; + Attributes.BonusStr = 7; + Attributes.WeaponDamage = 30; + } + + public EssenceOfBattle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs b/Scripts/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs new file mode 100644 index 000000000..2837a7d37 --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/Craftable/PendantOfTheMagi.cs @@ -0,0 +1,38 @@ +using System; + +namespace Server.Items +{ + public class PendantOfTheMagi : GoldNecklace + { + public override int LabelNumber{ get{ return 1072937; } } // Pendant of the Magi + + [Constructable] + public PendantOfTheMagi() + { + Hue = 0x48D; + Attributes.BonusInt = 10; + Attributes.RegenMana = 3; + Attributes.SpellDamage = 5; + Attributes.LowerManaCost = 10; + Attributes.LowerRegCost = 30; + } + + public PendantOfTheMagi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs b/Scripts/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs new file mode 100644 index 000000000..b518492e6 --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/Craftable/ResillientBracer.cs @@ -0,0 +1,41 @@ +using System; + +namespace Server.Items +{ + public class ResilientBracer : GoldBracelet + { + public override int LabelNumber{ get{ return 1072933; } } // Resillient Bracer + + public override int PhysicalResistance{ get { return 20; } } + + [Constructable] + public ResilientBracer() + { + Hue = 0x488; + + SkillBonuses.SetValues( 0, SkillName.MagicResist, 15.0 ); + + Attributes.BonusHits = 5; + Attributes.RegenHits = 2; + Attributes.DefendChance = 10; + } + + public ResilientBracer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs b/Scripts/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs new file mode 100644 index 000000000..c1490490d --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/OrnamentOfTheMagician.cs @@ -0,0 +1,43 @@ +using System; +using Server; + +namespace Server.Items +{ + public class OrnamentOfTheMagician : GoldBracelet + { + public override int LabelNumber{ get{ return 1061105; } } // Ornament of the Magician + public override int ArtifactRarity{ get{ return 11; } } + + [Constructable] + public OrnamentOfTheMagician() + { + Hue = 0x554; + Attributes.CastRecovery = 3; + Attributes.CastSpeed = 2; + Attributes.LowerManaCost = 10; + Attributes.LowerRegCost = 20; + Resistances.Energy = 15; + } + + public OrnamentOfTheMagician( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 0x12B ) + Hue = 0x554; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Artifacts/RingOfTheElements.cs b/Scripts/Items/Jewels/Artifacts/RingOfTheElements.cs new file mode 100644 index 000000000..590f807ba --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/RingOfTheElements.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RingOfTheElements : GoldRing + { + public override int LabelNumber{ get{ return 1061104; } } // Ring of the Elements + public override int ArtifactRarity{ get{ return 11; } } + + [Constructable] + public RingOfTheElements() + { + Hue = 0x4E9; + Attributes.Luck = 100; + Resistances.Fire = 16; + Resistances.Cold = 16; + Resistances.Poison = 16; + Resistances.Energy = 16; + } + + public RingOfTheElements( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Artifacts/RingOfTheVile.cs b/Scripts/Items/Jewels/Artifacts/RingOfTheVile.cs new file mode 100644 index 000000000..19fae708a --- /dev/null +++ b/Scripts/Items/Jewels/Artifacts/RingOfTheVile.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RingOfTheVile : GoldRing + { + public override int LabelNumber{ get{ return 1061102; } } // Ring of the Vile + public override int ArtifactRarity{ get{ return 11; } } + + [Constructable] + public RingOfTheVile() + { + Hue = 0x4F7; + Attributes.BonusDex = 8; + Attributes.RegenStam = 6; + Attributes.AttackChance = 15; + Resistances.Poison = 20; + } + + public RingOfTheVile( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 0x4F4 ) + Hue = 0x4F7; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/BaseJewel.cs b/Scripts/Items/Jewels/BaseJewel.cs new file mode 100644 index 000000000..226c6680a --- /dev/null +++ b/Scripts/Items/Jewels/BaseJewel.cs @@ -0,0 +1,353 @@ +using System; +using Server.Engines.Craft; + +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; + } + + 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 + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Beads.cs b/Scripts/Items/Jewels/Beads.cs new file mode 100644 index 000000000..95a087623 --- /dev/null +++ b/Scripts/Items/Jewels/Beads.cs @@ -0,0 +1,31 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class Beads : Item + { + [Constructable] + public Beads() : base( 0x108B ) + { + Weight = 1.0; + } + + public Beads( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Bracelet.cs b/Scripts/Items/Jewels/Bracelet.cs new file mode 100644 index 000000000..1d16332bd --- /dev/null +++ b/Scripts/Items/Jewels/Bracelet.cs @@ -0,0 +1,85 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseBracelet : BaseJewel + { + public override int BaseGemTypeNumber{ get{ return 1044221; } } // star sapphire bracelet + + public BaseBracelet( int itemID ) : base( itemID, Layer.Bracelet ) + { + } + + public BaseBracelet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GoldBracelet : BaseBracelet + { + [Constructable] + public GoldBracelet() : base( 0x1086 ) + { + Weight = 0.1; + } + + public GoldBracelet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SilverBracelet : BaseBracelet + { + [Constructable] + public SilverBracelet() : base( 0x1F06 ) + { + Weight = 0.1; + } + + public SilverBracelet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Jewels/Earrings.cs b/Scripts/Items/Jewels/Earrings.cs new file mode 100644 index 000000000..747810fb1 --- /dev/null +++ b/Scripts/Items/Jewels/Earrings.cs @@ -0,0 +1,85 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseEarrings : BaseJewel + { + public override int BaseGemTypeNumber{ get{ return 1044203; } } // star sapphire earrings + + public BaseEarrings( int itemID ) : base( itemID, Layer.Earrings ) + { + } + + public BaseEarrings( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GoldEarrings : BaseEarrings + { + [Constructable] + public GoldEarrings() : base( 0x1087 ) + { + Weight = 0.1; + } + + public GoldEarrings( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SilverEarrings : BaseEarrings + { + [Constructable] + public SilverEarrings() : base( 0x1F07 ) + { + Weight = 0.1; + } + + public SilverEarrings( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Necklace.cs b/Scripts/Items/Jewels/Necklace.cs new file mode 100644 index 000000000..5f239584a --- /dev/null +++ b/Scripts/Items/Jewels/Necklace.cs @@ -0,0 +1,167 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseNecklace : BaseJewel + { + public override int BaseGemTypeNumber{ get{ return 1044241; } } // star sapphire necklace + + public BaseNecklace( int itemID ) : base( itemID, Layer.Neck ) + { + } + + public BaseNecklace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Necklace : BaseNecklace + { + [Constructable] + public Necklace() : base( 0x1085 ) + { + Weight = 0.1; + } + + public Necklace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GoldNecklace : BaseNecklace + { + [Constructable] + public GoldNecklace() : base( 0x1088 ) + { + Weight = 0.1; + } + + public GoldNecklace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GoldBeadNecklace : BaseNecklace + { + [Constructable] + public GoldBeadNecklace() : base( 0x1089 ) + { + Weight = 0.1; + } + + public GoldBeadNecklace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class SilverNecklace : BaseNecklace + { + [Constructable] + public SilverNecklace() : base( 0x1F08 ) + { + Weight = 0.1; + } + + public SilverNecklace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SilverBeadNecklace : BaseNecklace + { + [Constructable] + public SilverBeadNecklace() : base( 0x1F05 ) + { + Weight = 0.1; + } + + public SilverBeadNecklace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Jewels/Ring.cs b/Scripts/Items/Jewels/Ring.cs new file mode 100644 index 000000000..71135fd8c --- /dev/null +++ b/Scripts/Items/Jewels/Ring.cs @@ -0,0 +1,85 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseRing : BaseJewel + { + public override int BaseGemTypeNumber{ get{ return 1044176; } } // star sapphire ring + + public BaseRing( int itemID ) : base( itemID, Layer.Ring ) + { + } + + public BaseRing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GoldRing : BaseRing + { + [Constructable] + public GoldRing() : base( 0x108a ) + { + Weight = 0.1; + } + + public GoldRing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SilverRing : BaseRing + { + [Constructable] + public SilverRing() : base( 0x1F09 ) + { + Weight = 0.1; + } + + public SilverRing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Lights/BaseEquipableLight.cs b/Scripts/Items/Lights/BaseEquipableLight.cs new file mode 100644 index 000000000..201777375 --- /dev/null +++ b/Scripts/Items/Lights/BaseEquipableLight.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseEquipableLight : BaseLight + { + [Constructable] + public BaseEquipableLight( int itemID ) : base( itemID ) + { + Layer = Layer.TwoHanded; + } + + public BaseEquipableLight( Serial serial ) : base( serial ) + { + } + + public override void Ignite() + { + if ( !(Parent is Mobile) && RootParent is Mobile ) + { + Mobile holder = (Mobile)RootParent; + + if ( holder.EquipItem( this ) ) + { + if ( this is Candle ) + holder.SendLocalizedMessage( 502969 ); // You put the candle in your left hand. + else if ( this is Torch ) + holder.SendLocalizedMessage( 502971 ); // You put the torch in your left hand. + + base.Ignite(); + } + else + { + holder.SendLocalizedMessage( 502449 ); // You cannot hold this item. + } + } + else + { + base.Ignite(); + } + } + + public override void OnAdded ( object parent ) + { + if ( Burning && parent is Container ) + Douse(); + + base.OnAdded( parent ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/BaseLight.cs b/Scripts/Items/Lights/BaseLight.cs new file mode 100644 index 000000000..342a57675 --- /dev/null +++ b/Scripts/Items/Lights/BaseLight.cs @@ -0,0 +1,233 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseLight : Item + { + private Timer m_Timer; + private DateTime m_End; + private bool m_BurntOut = false; + private bool m_Burning = false; + private bool m_Protected = false; + private TimeSpan m_Duration = TimeSpan.Zero; + + public abstract int LitItemID{ get; } + + public virtual int UnlitItemID{ get { return 0; } } + public virtual int BurntOutItemID{ get { return 0; } } + + public virtual int LitSound{ get { return 0x47; } } + public virtual int UnlitSound{ get { return 0x3be; } } + public virtual int BurntOutSound{ get { return 0x4b8; } } + + public static readonly bool Burnout = false; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Burning + { + get { return m_Burning; } + set + { + if ( m_Burning != value ) + { + m_Burning = true; + DoTimer( m_Duration ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool BurntOut + { + get { return m_BurntOut; } + set { m_BurntOut = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Protected + { + get { return m_Protected; } + set { m_Protected = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan Duration + { + get + { + if ( m_Duration != TimeSpan.Zero && m_Burning ) + { + return m_End - DateTime.Now; + } + else + return m_Duration; + } + + set { m_Duration = value; } + } + + [Constructable] + public BaseLight( int itemID ) : base( itemID ) + { + } + + public BaseLight( Serial serial ) : base( serial ) + { + } + + public virtual void PlayLitSound() + { + if ( LitSound != 0 ) + { + Point3D loc = GetWorldLocation(); + Effects.PlaySound( loc, Map, LitSound ); + } + } + + public virtual void PlayUnlitSound() + { + int sound = UnlitSound; + + if ( m_BurntOut && BurntOutSound != 0 ) + sound = BurntOutSound; + + + if ( sound != 0 ) + { + Point3D loc = GetWorldLocation(); + Effects.PlaySound( loc, Map, sound ); + } + } + + public virtual void Ignite() + { + if ( !m_BurntOut ) + { + PlayLitSound(); + + m_Burning = true; + ItemID = LitItemID; + DoTimer( m_Duration ); + } + } + + public virtual void Douse() + { + m_Burning = false; + + if ( m_BurntOut && BurntOutItemID != 0 ) + ItemID = BurntOutItemID; + else + ItemID = UnlitItemID; + + if ( m_BurntOut ) + m_Duration = TimeSpan.Zero; + else if ( m_Duration != TimeSpan.Zero ) + m_Duration = m_End - DateTime.Now; + + if ( m_Timer != null ) + m_Timer.Stop(); + + PlayUnlitSound(); + } + + public virtual void Burn() + { + m_BurntOut = true; + Douse(); + } + + private void DoTimer( TimeSpan delay ) + { + m_Duration = delay; + + if ( m_Timer != null ) + m_Timer.Stop(); + + if ( delay == TimeSpan.Zero ) + return; + + m_End = DateTime.Now + delay; + + m_Timer = new InternalTimer( this, delay ); + m_Timer.Start(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_BurntOut ) + return; + + if ( m_Protected && from.AccessLevel == AccessLevel.Player ) + return; + + if ( !from.InRange( this.GetWorldLocation(), 2 ) ) + return; + + if ( m_Burning ) + { + if ( UnlitItemID != 0 ) + Douse(); + } + else + { + Ignite(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + writer.Write( m_BurntOut ); + writer.Write( m_Burning ); + writer.Write( m_Duration ); + writer.Write( m_Protected ); + + if ( m_Burning && m_Duration != TimeSpan.Zero ) + writer.WriteDeltaTime( m_End ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_BurntOut = reader.ReadBool(); + m_Burning = reader.ReadBool(); + m_Duration = reader.ReadTimeSpan(); + m_Protected = reader.ReadBool(); + + if ( m_Burning && m_Duration != TimeSpan.Zero ) + DoTimer( reader.ReadDeltaTime() - DateTime.Now ); + + break; + } + } + } + + private class InternalTimer : Timer + { + private BaseLight m_Light; + + public InternalTimer( BaseLight light, TimeSpan delay ) : base( delay ) + { + m_Light = light; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + if ( m_Light != null && !m_Light.Deleted ) + m_Light.Burn(); + } + } + } +} diff --git a/Scripts/Items/Lights/Brazier.cs b/Scripts/Items/Lights/Brazier.cs new file mode 100644 index 000000000..223bacfb4 --- /dev/null +++ b/Scripts/Items/Lights/Brazier.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Brazier : BaseLight + { + public override int LitItemID{ get { return 0xE31; } } + + [Constructable] + public Brazier() : base( 0xE31 ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = true; + Light = LightType.Circle225; + Weight = 20.0; + } + + public Brazier( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/BrazierTall.cs b/Scripts/Items/Lights/BrazierTall.cs new file mode 100644 index 000000000..8c39312d7 --- /dev/null +++ b/Scripts/Items/Lights/BrazierTall.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BrazierTall : BaseLight + { + public override int LitItemID{ get { return 0x19AA; } } + + [Constructable] + public BrazierTall() : base( 0x19AA ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = true; + Light = LightType.Circle300; + Weight = 25.0; + } + + public BrazierTall( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/Candelabra.cs b/Scripts/Items/Lights/Candelabra.cs new file mode 100644 index 000000000..d22dd3164 --- /dev/null +++ b/Scripts/Items/Lights/Candelabra.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Candelabra : BaseLight + { + public override int LitItemID{ get { return 0xB1D; } } + public override int UnlitItemID{ get { return 0xA27; } } + + [Constructable] + public Candelabra() : base( 0xA27 ) + { + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle225; + Weight = 3.0; + } + + public Candelabra( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/CandelabraStand.cs b/Scripts/Items/Lights/CandelabraStand.cs new file mode 100644 index 000000000..314c210b4 --- /dev/null +++ b/Scripts/Items/Lights/CandelabraStand.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CandelabraStand : BaseLight + { + public override int LitItemID{ get { return 0xB26; } } + public override int UnlitItemID{ get { return 0xA29; } } + + [Constructable] + public CandelabraStand() : base( 0xA29 ) + { + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle225; + Weight = 20.0; + } + + public CandelabraStand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/Candle.cs b/Scripts/Items/Lights/Candle.cs new file mode 100644 index 000000000..e7b3eef45 --- /dev/null +++ b/Scripts/Items/Lights/Candle.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Candle : BaseEquipableLight + { + public override int LitItemID{ get { return 0xA0F; } } + public override int UnlitItemID{ get { return 0xA28; } } + + [Constructable] + public Candle() : base( 0xA28 ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 20 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle150; + Weight = 1.0; + } + + public Candle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/CandleLarge.cs b/Scripts/Items/Lights/CandleLarge.cs new file mode 100644 index 000000000..a930f2159 --- /dev/null +++ b/Scripts/Items/Lights/CandleLarge.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CandleLarge : BaseLight + { + public override int LitItemID{ get { return 0xB1A; } } + public override int UnlitItemID{ get { return 0xA26; } } + + [Constructable] + public CandleLarge() : base( 0xA26 ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 25 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle150; + Weight = 2.0; + } + + public CandleLarge( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/CandleLong.cs b/Scripts/Items/Lights/CandleLong.cs new file mode 100644 index 000000000..8f89b0ac5 --- /dev/null +++ b/Scripts/Items/Lights/CandleLong.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CandleLong : BaseLight + { + public override int LitItemID{ get { return 0x1430; } } + public override int UnlitItemID{ get { return 0x1433; } } + + [Constructable] + public CandleLong() : base( 0x1433 ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 30 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle150; + Weight = 1.0; + } + + public CandleLong( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/CandleShort.cs b/Scripts/Items/Lights/CandleShort.cs new file mode 100644 index 000000000..dca875883 --- /dev/null +++ b/Scripts/Items/Lights/CandleShort.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CandleShort : BaseLight + { + public override int LitItemID{ get { return 0x142C; } } + public override int UnlitItemID{ get { return 0x142F; } } + + [Constructable] + public CandleShort() : base( 0x142F ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 25 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle150; + Weight = 1.0; + } + + public CandleShort( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/CandleSkull.cs b/Scripts/Items/Lights/CandleSkull.cs new file mode 100644 index 000000000..080d4a9d0 --- /dev/null +++ b/Scripts/Items/Lights/CandleSkull.cs @@ -0,0 +1,59 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CandleSkull : BaseLight + { + public override int LitItemID + { + get + { + if ( ItemID == 0x1583 || ItemID == 0x1854 ) + return 0x1854; + + return 0x1858; + } + } + + public override int UnlitItemID + { + get + { + if ( ItemID == 0x1853 || ItemID == 0x1584 ) + return 0x1853; + + return 0x1857; + } + } + + [Constructable] + public CandleSkull() : base( 0x1853 ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 25 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle150; + Weight = 5.0; + } + + public CandleSkull( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/DarkSource.cs b/Scripts/Items/Lights/DarkSource.cs new file mode 100644 index 000000000..838d27de9 --- /dev/null +++ b/Scripts/Items/Lights/DarkSource.cs @@ -0,0 +1,30 @@ +using System; + +namespace Server.Items +{ + public class DarkSource : Item + { + [Constructable] + public DarkSource() : base( 0x1646 ) + { + Layer = Layer.TwoHanded; + Movable = false; + } + + public DarkSource( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/HangingLantern.cs b/Scripts/Items/Lights/HangingLantern.cs new file mode 100644 index 000000000..b48c4ac90 --- /dev/null +++ b/Scripts/Items/Lights/HangingLantern.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HangingLantern : BaseLight + { + public override int LitItemID{ get { return 0xA1A; } } + public override int UnlitItemID{ get { return 0xA1D; } } + + [Constructable] + public HangingLantern() : base( 0xA1D ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; + } + + public HangingLantern( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/HeatingStand.cs b/Scripts/Items/Lights/HeatingStand.cs new file mode 100644 index 000000000..6466fb8b3 --- /dev/null +++ b/Scripts/Items/Lights/HeatingStand.cs @@ -0,0 +1,60 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HeatingStand : BaseLight + { + public override int LitItemID{ get { return 0x184A; } } + public override int UnlitItemID{ get { return 0x1849; } } + + [Constructable] + public HeatingStand() : base( 0x1849 ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 25 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Empty; + Weight = 1.0; + } + + public override void Ignite() + { + base.Ignite(); + + if ( ItemID == LitItemID ) + Light = LightType.Circle150; + else if ( ItemID == UnlitItemID ) + Light = LightType.Empty; + } + + public override void Douse() + { + base.Douse(); + + if ( ItemID == LitItemID ) + Light = LightType.Circle150; + else if ( ItemID == UnlitItemID ) + Light = LightType.Empty; + } + + public HeatingStand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/LampPost1.cs b/Scripts/Items/Lights/LampPost1.cs new file mode 100644 index 000000000..55924f6f8 --- /dev/null +++ b/Scripts/Items/Lights/LampPost1.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LampPost1 : BaseLight + { + public override int LitItemID{ get { return 0xB20; } } + public override int UnlitItemID{ get { return 0xB21; } } + + [Constructable] + public LampPost1() : base( 0xB21 ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; + } + + public LampPost1( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/LampPost2.cs b/Scripts/Items/Lights/LampPost2.cs new file mode 100644 index 000000000..2fa44919e --- /dev/null +++ b/Scripts/Items/Lights/LampPost2.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LampPost2 : BaseLight + { + public override int LitItemID{ get { return 0xB22; } } + public override int UnlitItemID{ get { return 0xB23; } } + + [Constructable] + public LampPost2() : base( 0xB23 ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; + } + + public LampPost2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/LampPost3.cs b/Scripts/Items/Lights/LampPost3.cs new file mode 100644 index 000000000..00c38dfbe --- /dev/null +++ b/Scripts/Items/Lights/LampPost3.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LampPost3 : BaseLight + { + public override int LitItemID{ get { return 0xB24; } } + public override int UnlitItemID{ get { return 0xB25; } } + + [Constructable] + public LampPost3() : base( 0xb25 ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 40.0; + } + + public LampPost3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/Lantern.cs b/Scripts/Items/Lights/Lantern.cs new file mode 100644 index 000000000..5c266150e --- /dev/null +++ b/Scripts/Items/Lights/Lantern.cs @@ -0,0 +1,86 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Lantern : BaseEquipableLight + { + public override int LitItemID + { + get + { + if ( ItemID == 0xA15 || ItemID == 0xA17 ) + return ItemID; + + return 0xA22; + } + } + + public override int UnlitItemID + { + get + { + if ( ItemID == 0xA18 ) + return ItemID; + + return 0xA25; + } + } + + [Constructable] + public Lantern() : base( 0xA25 ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 20 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle300; + Weight = 2.0; + } + + public Lantern( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class LanternOfSouls : Lantern + { + public override int LabelNumber{ get{ return 1061618; } } // Lantern of Souls + + [Constructable] + public LanternOfSouls() + { + Hue = 0x482; + } + + public LanternOfSouls( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/LightSource.cs b/Scripts/Items/Lights/LightSource.cs new file mode 100644 index 000000000..04c3d7b31 --- /dev/null +++ b/Scripts/Items/Lights/LightSource.cs @@ -0,0 +1,30 @@ +using System; + +namespace Server.Items +{ + public class LightSource : Item + { + [Constructable] + public LightSource() : base( 0x1647 ) + { + Layer = Layer.TwoHanded; + Movable = false; + } + + public LightSource( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/PaperLantern.cs b/Scripts/Items/Lights/PaperLantern.cs new file mode 100644 index 000000000..a5cb53681 --- /dev/null +++ b/Scripts/Items/Lights/PaperLantern.cs @@ -0,0 +1,38 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class PaperLantern : BaseLight + { + public override int LitItemID{ get { return 0x24BD; } } + public override int UnlitItemID{ get { return 0x24BE; } } + + [Constructable] + public PaperLantern() : base( 0x24BE ) + { + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle150; + Weight = 3.0; + } + + public PaperLantern( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/RedHangingLantern.cs b/Scripts/Items/Lights/RedHangingLantern.cs new file mode 100644 index 000000000..1ed424694 --- /dev/null +++ b/Scripts/Items/Lights/RedHangingLantern.cs @@ -0,0 +1,71 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class RedHangingLantern : BaseLight + { + public override int LitItemID + { + get + { + if ( ItemID == 0x24C2 ) + return 0x24C1; + else + return 0x24C3; + } + } + + public override int UnlitItemID + { + get + { + if ( ItemID == 0x24C1 ) + return 0x24C2; + else + return 0x24C4; + } + } + + [Constructable] + public RedHangingLantern() : base( 0x24C2 ) + { + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 3.0; + } + + public RedHangingLantern( Serial serial ) : base( serial ) + { + } + + public void Flip() + { + Light = LightType.Circle300; + + switch ( ItemID ) + { + case 0x24C2: ItemID = 0x24C4; break; + case 0x24C1: ItemID = 0x24C3; break; + + case 0x24C4: ItemID = 0x24C2; break; + case 0x24C3: ItemID = 0x24C1; break; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/RoundPaperLantern.cs b/Scripts/Items/Lights/RoundPaperLantern.cs new file mode 100644 index 000000000..6b40d12fd --- /dev/null +++ b/Scripts/Items/Lights/RoundPaperLantern.cs @@ -0,0 +1,38 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class RoundPaperLantern : BaseLight + { + public override int LitItemID{ get { return 0x24C9; } } + public override int UnlitItemID{ get { return 0x24CA; } } + + [Constructable] + public RoundPaperLantern() : base( 0x24CA ) + { + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle150; + Weight = 3.0; + } + + public RoundPaperLantern( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/ShojiLantern.cs b/Scripts/Items/Lights/ShojiLantern.cs new file mode 100644 index 000000000..9944a427b --- /dev/null +++ b/Scripts/Items/Lights/ShojiLantern.cs @@ -0,0 +1,38 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class ShojiLantern : BaseLight + { + public override int LitItemID{ get { return 0x24BB; } } + public override int UnlitItemID{ get { return 0x24BC; } } + + [Constructable] + public ShojiLantern() : base( 0x24BC ) + { + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle150; + Weight = 3.0; + } + + public ShojiLantern( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/Torch.cs b/Scripts/Items/Lights/Torch.cs new file mode 100644 index 000000000..45f43b947 --- /dev/null +++ b/Scripts/Items/Lights/Torch.cs @@ -0,0 +1,62 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Torch : BaseEquipableLight + { + public override int LitItemID{ get { return 0xA12; } } + public override int UnlitItemID{ get { return 0xF6B; } } + + public override int LitSound{ get { return 0x54; } } + public override int UnlitSound{ get { return 0x4BB; } } + + [Constructable] + public Torch() : base( 0xF6B ) + { + if ( Burnout ) + Duration = TimeSpan.FromMinutes( 30 ); + else + Duration = TimeSpan.Zero; + + Burning = false; + Light = LightType.Circle300; + Weight = 1.0; + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( parent is Mobile && Burning ) + Mobiles.MeerMage.StopEffect( (Mobile)parent, true ); + } + + public override void Ignite() + { + base.Ignite(); + + if ( Parent is Mobile && Burning ) + Mobiles.MeerMage.StopEffect( (Mobile)Parent, true ); + } + + public Torch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/WallSconce.cs b/Scripts/Items/Lights/WallSconce.cs new file mode 100644 index 000000000..8edf3b76f --- /dev/null +++ b/Scripts/Items/Lights/WallSconce.cs @@ -0,0 +1,74 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class WallSconce : BaseLight + { + public override int LitItemID + { + get + { + if ( ItemID == 0x9FB ) + return 0x9FD; + else + return 0xA02; + } + } + + public override int UnlitItemID + { + get + { + if ( ItemID == 0x9FD ) + return 0x9FB; + else + return 0xA00; + } + } + + [Constructable] + public WallSconce() : base( 0x9FB ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.WestBig; + Weight = 3.0; + } + + public WallSconce( Serial serial ) : base( serial ) + { + } + + public void Flip() + { + if ( Light == LightType.WestBig ) + Light = LightType.NorthBig; + else if ( Light == LightType.NorthBig ) + Light = LightType.WestBig; + + switch ( ItemID ) + { + case 0x9FB: ItemID = 0xA00; break; + case 0x9FD: ItemID = 0xA02; break; + + case 0xA00: ItemID = 0x9FB; break; + case 0xA02: ItemID = 0x9FD; break; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/WallTorch.cs b/Scripts/Items/Lights/WallTorch.cs new file mode 100644 index 000000000..ba4c330c5 --- /dev/null +++ b/Scripts/Items/Lights/WallTorch.cs @@ -0,0 +1,74 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class WallTorch : BaseLight + { + public override int LitItemID + { + get + { + if ( ItemID == 0xA05 ) + return 0xA07; + else + return 0xA0C; + } + } + + public override int UnlitItemID + { + get + { + if ( ItemID == 0xA07 ) + return 0xA05; + else + return 0xA0A; + } + } + + [Constructable] + public WallTorch() : base( 0xA05 ) + { + Movable = false; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.WestBig; + Weight = 3.0; + } + + public WallTorch( Serial serial ) : base( serial ) + { + } + + public void Flip() + { + if ( Light == LightType.WestBig ) + Light = LightType.NorthBig; + else if ( Light == LightType.NorthBig ) + Light = LightType.WestBig; + + switch ( ItemID ) + { + case 0xA05: ItemID = 0xA0A; break; + case 0xA07: ItemID = 0xA0C; break; + + case 0xA0A: ItemID = 0xA05; break; + case 0xA0C: ItemID = 0xA07; break; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Lights/WhiteHangingLantern.cs b/Scripts/Items/Lights/WhiteHangingLantern.cs new file mode 100644 index 000000000..e0dcf122a --- /dev/null +++ b/Scripts/Items/Lights/WhiteHangingLantern.cs @@ -0,0 +1,71 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable] + public class WhiteHangingLantern : BaseLight + { + public override int LitItemID + { + get + { + if ( ItemID == 0x24C6 ) + return 0x24C5; + else + return 0x24C7; + } + } + + public override int UnlitItemID + { + get + { + if ( ItemID == 0x24C5 ) + return 0x24C6; + else + return 0x24C8; + } + } + + [Constructable] + public WhiteHangingLantern() : base( 0x24C6 ) + { + Movable = true; + Duration = TimeSpan.Zero; // Never burnt out + Burning = false; + Light = LightType.Circle300; + Weight = 3.0; + } + + public WhiteHangingLantern( Serial serial ) : base( serial ) + { + } + + public void Flip() + { + Light = LightType.Circle300; + + switch ( ItemID ) + { + case 0x24C6: ItemID = 0x24C8; break; + case 0x24C5: ItemID = 0x24C7; break; + + case 0x24C8: ItemID = 0x24C6; break; + case 0x24C7: ItemID = 0x24C5; break; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/BlankMap.cs b/Scripts/Items/Maps/BlankMap.cs new file mode 100644 index 000000000..e0dbb7928 --- /dev/null +++ b/Scripts/Items/Maps/BlankMap.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BlankMap : MapItem + { + [Constructable] + public BlankMap() + { + } + + public override void OnDoubleClick( Mobile from ) + { + SendLocalizedMessageTo( from, 500208 ); // It appears to be blank. + } + + public BlankMap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/CityMap.cs b/Scripts/Items/Maps/CityMap.cs new file mode 100644 index 000000000..26f6ed68b --- /dev/null +++ b/Scripts/Items/Maps/CityMap.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CityMap : MapItem + { + [Constructable] + public CityMap() + { + SetDisplay( 0, 0, 5119, 4095, 400, 400 ); + } + + public override void CraftInit( Mobile from ) + { + double skillValue = from.Skills[SkillName.Cartography].Value; + int dist = 64 + (int)(skillValue * 4); + + if ( dist < 200 ) + dist = 200; + + int size = 32 + (int)(skillValue * 2); + + if ( size < 200 ) + size = 200; + else if ( size > 400 ) + size = 400; + + SetDisplay( from.X - dist, from.Y - dist, from.X + dist, from.Y + dist, size, size ); + } + + public override int LabelNumber{ get{ return 1015231; } } // city map + + public CityMap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/IndecipherableMap.cs b/Scripts/Items/Maps/IndecipherableMap.cs new file mode 100644 index 000000000..b3675eac5 --- /dev/null +++ b/Scripts/Items/Maps/IndecipherableMap.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class IndecipherableMap : MapItem + { + public override int LabelNumber{ get{ return 1070799; } } // indecipherable map + + [Constructable] + public IndecipherableMap() + { + if ( Utility.RandomDouble() < 0.2 ) + Hue = 0x965; + else + Hue = 0x961; + } + + public IndecipherableMap( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 1070801 ); // You cannot decipher this ruined map. + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/LocalMap.cs b/Scripts/Items/Maps/LocalMap.cs new file mode 100644 index 000000000..41090ca77 --- /dev/null +++ b/Scripts/Items/Maps/LocalMap.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LocalMap : MapItem + { + [Constructable] + public LocalMap() + { + SetDisplay( 0, 0, 5119, 4095, 400, 400 ); + } + + public override void CraftInit( Mobile from ) + { + double skillValue = from.Skills[SkillName.Cartography].Value; + int dist = 64 + (int)(skillValue * 2); + + SetDisplay( from.X - dist, from.Y - dist, from.X + dist, from.Y + dist, 200, 200 ); + } + + public override int LabelNumber{ get{ return 1015230; } } // local map + + public LocalMap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/MapItem.cs b/Scripts/Items/Maps/MapItem.cs new file mode 100644 index 000000000..b5a3e2ecf --- /dev/null +++ b/Scripts/Items/Maps/MapItem.cs @@ -0,0 +1,384 @@ +using System; +using System.IO; +using System.Collections; +using Server; +using Server.Network; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x14EB, 0x14EC )] + public class MapItem : Item, ICraftable + { + private Rectangle2D m_Bounds; + + private int m_Width, m_Height; + + private bool m_Protected; + private bool m_Editable; + + private ArrayList m_Pins = new ArrayList(); + + private const int MaxUserPins = 50; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Protected + { + get { return m_Protected; } + set { m_Protected = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Rectangle2D Bounds + { + get { return m_Bounds; } + set { m_Bounds = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Width + { + get { return m_Width; } + set { m_Width = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Height + { + get { return m_Height; } + set { m_Height = value; } + } + + public ArrayList Pins + { + get { return m_Pins; } + } + + [Constructable] + public MapItem() : base( 0x14EC ) + { + Weight = 1.0; + + m_Width = 200; + m_Height = 200; + } + + public virtual void CraftInit( Mobile from ) + { + } + + public void SetDisplay( int x1, int y1, int x2, int y2, int w, int h ) + { + Width = w; + Height = h; + + if ( x1 < 0 ) + x1 = 0; + + if ( y1 < 0 ) + y1 = 0; + + if ( x2 >= 5120 ) + x2 = 5119; + + if ( y2 >= 4096 ) + y2 = 4095; + + Bounds = new Rectangle2D( x1, y1, x2-x1, y2-y1 ); + } + + public MapItem( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 2 ) ) + DisplayTo( from ); + else + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + + public virtual void DisplayTo( Mobile from ) + { + from.Send( new MapDetails( this ) ); + from.Send( new MapDisplay( this ) ); + + for ( int i = 0; i < m_Pins.Count; ++i ) + from.Send( new MapAddPin( this, (Point2D) m_Pins[i] ) ); + + from.Send( new MapSetEditable( this, ValidateEdit( from ) ) ); + } + + public virtual void OnAddPin( Mobile from, int x, int y ) + { + if ( !ValidateEdit( from ) ) + return; + else if ( m_Pins.Count >= MaxUserPins ) + return; + + Validate( ref x, ref y ); + AddPin( x, y ); + } + + public virtual void OnRemovePin( Mobile from, int number ) + { + if ( !ValidateEdit( from ) ) + return; + + RemovePin( number ); + } + + public virtual void OnChangePin( Mobile from, int number, int x, int y ) + { + if ( !ValidateEdit( from ) ) + return; + + Validate( ref x, ref y ); + ChangePin( number, x, y ); + } + + public virtual void OnInsertPin( Mobile from, int number, int x, int y ) + { + if ( !ValidateEdit( from ) ) + return; + else if ( m_Pins.Count >= MaxUserPins ) + return; + + Validate( ref x, ref y ); + InsertPin( number, x, y ); + } + + public virtual void OnClearPins( Mobile from ) + { + if ( !ValidateEdit( from ) ) + return; + + ClearPins(); + } + + public virtual void OnToggleEditable( Mobile from ) + { + if ( Validate( from ) ) + m_Editable = !m_Editable; + + from.Send( new MapSetEditable( this, Validate( from ) && m_Editable ) ); + } + + public virtual void Validate( ref int x, ref int y ) + { + if ( x < 0 ) + x = 0; + else if ( x >= m_Width ) + x = m_Width - 1; + + if ( y < 0 ) + y = 0; + else if ( y >= m_Height ) + y = m_Height - 1; + } + + public virtual bool ValidateEdit( Mobile from ) + { + return m_Editable && Validate( from ); + } + + public virtual bool Validate( Mobile from ) + { + if ( !from.CanSee( this ) || from.Map != this.Map || !from.Alive || InSecureTrade ) + return false; + else if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + else if ( !Movable || m_Protected || !from.InRange( GetWorldLocation(), 2 ) ) + return false; + + object root = RootParent; + + if ( root is Mobile && root != from ) + return false; + + return true; + } + + public void ConvertToWorld( int x, int y, out int worldX, out int worldY ) + { + worldX = ( ( m_Bounds.Width * x ) / Width ) + m_Bounds.X; + worldY = ( ( m_Bounds.Height * y ) / Height ) + m_Bounds.Y; + } + + public void ConvertToMap( int x, int y, out int mapX, out int mapY ) + { + mapX = ( ( x - m_Bounds.X ) * Width ) / m_Bounds.Width; + mapY = ( ( y - m_Bounds.Y ) * Width ) / m_Bounds.Height; + } + + public virtual void AddWorldPin( int x, int y ) + { + int mapX, mapY; + ConvertToMap( x, y, out mapX, out mapY ); + + AddPin( mapX, mapY ); + } + + public virtual void AddPin( int x, int y ) + { + m_Pins.Add( new Point2D( x, y ) ); + } + + public virtual void RemovePin( int index ) + { + if ( index > 0 && index < m_Pins.Count ) + m_Pins.RemoveAt( index ); + } + + public virtual void InsertPin( int index, int x, int y ) + { + if ( index < 0 || index >= m_Pins.Count ) + m_Pins.Add( new Point2D( x, y ) ); + else + m_Pins.Insert( index, new Point2D( x, y ) ); + } + + public virtual void ChangePin( int index, int x, int y ) + { + if ( index >= 0 && index < m_Pins.Count ) + m_Pins[index] = new Point2D( x, y ); + } + + public virtual void ClearPins() + { + m_Pins.Clear(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_Bounds ); + + writer.Write( m_Width ); + writer.Write( m_Height ); + + writer.Write( m_Protected ); + + writer.Write( m_Pins.Count ); + for ( int i = 0; i < m_Pins.Count; ++i ) + writer.Write( (Point2D) m_Pins[i] ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Bounds = reader.ReadRect2D(); + + m_Width = reader.ReadInt(); + m_Height = reader.ReadInt(); + + m_Protected = reader.ReadBool(); + + int count = reader.ReadInt(); + for ( int i = 0; i < count; i++ ) + m_Pins.Add( reader.ReadPoint2D() ); + + break; + } + } + } + + public static void Initialize() + { + PacketHandlers.Register( 0x56, 11, true, new OnPacketReceive( OnMapCommand ) ); + } + + private static void OnMapCommand( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + MapItem map = World.FindItem( pvSrc.ReadInt32() ) as MapItem; + + if ( map == null ) + return; + + int command = pvSrc.ReadByte(); + int number = pvSrc.ReadByte(); + + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + + switch ( command ) + { + case 1: map.OnAddPin( from, x, y ); break; + case 2: map.OnInsertPin( from, number, x, y ); break; + case 3: map.OnChangePin( from, number, x, y ); break; + case 4: map.OnRemovePin( from, number ); break; + case 5: map.OnClearPins( from ); break; + case 6: map.OnToggleEditable( from ); break; + } + } + + private sealed class MapDetails : Packet + { + public MapDetails( MapItem map ) : base ( 0x90, 19 ) + { + m_Stream.Write( (int) map.Serial ); + m_Stream.Write( (short) 0x139D ); + m_Stream.Write( (short) map.Bounds.Start.X ); + m_Stream.Write( (short) map.Bounds.Start.Y ); + m_Stream.Write( (short) map.Bounds.End.X ); + m_Stream.Write( (short) map.Bounds.End.Y ); + m_Stream.Write( (short) map.Width ); + m_Stream.Write( (short) map.Height ); + } + } + + private abstract class MapCommand : Packet + { + public MapCommand( MapItem map, int command, int number, int x, int y ) : base ( 0x56, 11 ) + { + m_Stream.Write( (int) map.Serial ); + m_Stream.Write( (byte) command ); + m_Stream.Write( (byte) number ); + m_Stream.Write( (short) x ); + m_Stream.Write( (short) y ); + } + } + + private sealed class MapDisplay : MapCommand + { + public MapDisplay( MapItem map ) : base( map, 5, 0, 0, 0 ) + { + } + } + + private sealed class MapAddPin : MapCommand + { + public MapAddPin( MapItem map, Point2D point ) : base( map, 1, 0, point.X, point.Y ) + { + } + } + + private sealed class MapSetEditable : MapCommand + { + public MapSetEditable( MapItem map, bool editable ) : base( map, 7, editable ? 1 : 0, 0, 0 ) + { + } + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + CraftInit( from ); + return 1; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/PresetMap.cs b/Scripts/Items/Maps/PresetMap.cs new file mode 100644 index 000000000..21547a06c --- /dev/null +++ b/Scripts/Items/Maps/PresetMap.cs @@ -0,0 +1,151 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PresetMap : MapItem + { + private int m_LabelNumber; + + [Constructable] + public PresetMap( PresetMapType type ) + { + int v = (int)type; + + if ( v >= 0 && v < PresetMapEntry.Table.Length ) + InitEntry( PresetMapEntry.Table[v] ); + } + + public PresetMap( PresetMapEntry entry ) + { + InitEntry( entry ); + } + + public void InitEntry( PresetMapEntry entry ) + { + m_LabelNumber = entry.Name; + + Width = entry.Width; + Height = entry.Height; + + Bounds = entry.Bounds; + } + + public override int LabelNumber{ get{ return (m_LabelNumber == 0 ? base.LabelNumber : m_LabelNumber); } } + + public PresetMap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (int) m_LabelNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_LabelNumber = reader.ReadInt(); + break; + } + } + } + } + + public class PresetMapEntry + { + private int m_Name; + private int m_Width, m_Height; + private Rectangle2D m_Bounds; + + public int Name{ get{ return m_Name; } } + public int Width{ get{ return m_Width; } } + public int Height{ get{ return m_Height; } } + public Rectangle2D Bounds{ get{ return m_Bounds; } } + + public PresetMapEntry( int name, int width, int height, int xLeft, int yTop, int xRight, int yBottom ) + { + m_Name = name; + m_Width = width; + m_Height = height; + m_Bounds = new Rectangle2D( xLeft, yTop, xRight - xLeft, yBottom - yTop ); + } + + private static PresetMapEntry[] m_Table = new PresetMapEntry[] + { + new PresetMapEntry( 1041189, 200, 200, 1092, 1396, 1736, 1924 ), // map of Britain + new PresetMapEntry( 1041203, 200, 200, 0256, 1792, 1736, 2560 ), // map of Britain to Skara Brae + new PresetMapEntry( 1041192, 200, 200, 1024, 1280, 2304, 3072 ), // map of Britain to Trinsic + new PresetMapEntry( 1041183, 200, 200, 2500, 1900, 3000, 2400 ), // map of Buccaneer's Den + new PresetMapEntry( 1041198, 200, 200, 2560, 1792, 3840, 2560 ), // map of Buccaneer's Den to Magincia + new PresetMapEntry( 1041194, 200, 200, 2560, 1792, 3840, 3072 ), // map of Buccaneer's Den to Ocllo + new PresetMapEntry( 1041181, 200, 200, 1088, 3572, 1528, 4056 ), // map of Jhelom + new PresetMapEntry( 1041186, 200, 200, 3530, 2022, 3818, 2298 ), // map of Magincia + new PresetMapEntry( 1041199, 200, 200, 3328, 1792, 3840, 2304 ), // map of Magincia to Ocllo + new PresetMapEntry( 1041182, 200, 200, 2360, 0356, 2706, 0702 ), // map of Minoc + new PresetMapEntry( 1041190, 200, 200, 0000, 0256, 2304, 3072 ), // map of Minoc to Yew + new PresetMapEntry( 1041191, 200, 200, 2467, 0572, 2878, 0746 ), // map of Minoc to Vesper + new PresetMapEntry( 1041188, 200, 200, 4156, 0808, 4732, 1528 ), // map of Moonglow + new PresetMapEntry( 1041201, 200, 200, 3328, 0768, 4864, 1536 ), // map of Moonglow to Nujelm + new PresetMapEntry( 1041185, 200, 200, 3446, 1030, 3832, 1424 ), // map of Nujelm + new PresetMapEntry( 1041197, 200, 200, 3328, 1024, 3840, 2304 ), // map of Nujelm to Magincia + new PresetMapEntry( 1041187, 200, 200, 3582, 2456, 3770, 2742 ), // map of Ocllo + new PresetMapEntry( 1041184, 200, 200, 2714, 3329, 3100, 3639 ), // map of Serpent's Hold + new PresetMapEntry( 1041200, 200, 200, 2560, 2560, 3840, 3840 ), // map of Serpent's Hold to Ocllo + new PresetMapEntry( 1041180, 200, 200, 0524, 2064, 0960, 2452 ), // map of Skara Brae + new PresetMapEntry( 1041204, 200, 200, 0000, 0000, 5199, 4095 ), // map of The World + new PresetMapEntry( 1041177, 200, 200, 1792, 2630, 2118, 2952 ), // map of Trinsic + new PresetMapEntry( 1041193, 200, 200, 1792, 1792, 3072, 3072 ), // map of Trinsic to Buccaneer's Den + new PresetMapEntry( 1041195, 200, 200, 0256, 1792, 2304, 4095 ), // map of Trinsic to Jhelom + new PresetMapEntry( 1041178, 200, 200, 2636, 0592, 3064, 1012 ), // map of Vesper + new PresetMapEntry( 1041196, 200, 200, 2636, 0592, 3840, 1536 ), // map of Vesper to Nujelm + new PresetMapEntry( 1041179, 200, 200, 0236, 0741, 0766, 1269 ), // map of Yew + new PresetMapEntry( 1041202, 200, 200, 0000, 0512, 1792, 2048 ) // map of Yew to Britain + }; + + public static PresetMapEntry[] Table{ get{ return m_Table; } } + } + + public enum PresetMapType + { + Britain, + BritainToSkaraBrae, + BritainToTrinsic, + BucsDen, + BucsDenToMagincia, + BucsDenToOcllo, + Jhelom, + Magincia, + MaginciaToOcllo, + Minoc, + MinocToYew, + MinocToVesper, + Moonglow, + MoonglowToNujelm, + Nujelm, + NujelmToMagincia, + Ocllo, + SerpentsHold, + SerpentsHoldToOcllo, + SkaraBrae, + TheWorld, + Trinsic, + TrinsicToBucsDen, + TrinsicToJhelom, + Vesper, + VesperToNujelm, + Yew, + YewToBritain + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/SeaChart.cs b/Scripts/Items/Maps/SeaChart.cs new file mode 100644 index 000000000..2757f696e --- /dev/null +++ b/Scripts/Items/Maps/SeaChart.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SeaChart : MapItem + { + [Constructable] + public SeaChart() + { + SetDisplay( 0, 0, 5119, 4095, 400, 400 ); + } + + public override void CraftInit( Mobile from ) + { + double skillValue = from.Skills[SkillName.Cartography].Value; + int dist = 64 + (int)(skillValue * 10); + + if ( dist < 200 ) + dist = 200; + + int size = 24 + (int)(skillValue * 3.3); + + if ( size < 200 ) + size = 200; + else if ( size > 400 ) + size = 400; + + SetDisplay( from.X - dist, from.Y - dist, from.X + dist, from.Y + dist, size, size ); + } + + public override int LabelNumber{ get{ return 1015232; } } // sea chart + + public SeaChart( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/TreasureMap.cs b/Scripts/Items/Maps/TreasureMap.cs new file mode 100644 index 000000000..c047d238b --- /dev/null +++ b/Scripts/Items/Maps/TreasureMap.cs @@ -0,0 +1,932 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Items +{ + public class TreasureMap : MapItem + { + private int m_Level; + private bool m_Completed; + private Mobile m_CompletedBy; + private Mobile m_Decoder; + private Map m_Map; + private Point2D m_Location; + + [CommandProperty( AccessLevel.GameMaster )] + public int Level{ get{ return m_Level; } set{ m_Level = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Completed{ get{ return m_Completed; } set{ m_Completed = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile CompletedBy{ get{ return m_CompletedBy; } set{ m_CompletedBy = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Decoder{ get{ return m_Decoder; } set{ m_Decoder = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Map ChestMap{ get{ return m_Map; } set{ m_Map = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point2D ChestLocation{ get{ return m_Location; } set{ m_Location = value; } } + + private static Point2D[] m_Locations; + private static Point2D[] m_HavenLocations; + + private static Type[][] m_SpawnTypes = new Type[][] + { + new Type[]{ typeof( HeadlessOne ), typeof( Skeleton ) }, + new Type[]{ typeof( Mongbat ), typeof( Ratman ), typeof( HeadlessOne ), typeof( Skeleton ), typeof( Zombie ) }, + new Type[]{ typeof( OrcishMage ), typeof( Gargoyle ), typeof( Gazer ), typeof( HellHound ), typeof( EarthElemental ) }, + new Type[]{ typeof( Lich ), typeof( OgreLord ), typeof( DreadSpider ), typeof( AirElemental ), typeof( FireElemental ) }, + new Type[]{ typeof( DreadSpider ), typeof( LichLord ), typeof( Daemon ), typeof( ElderGazer ), typeof( OgreLord ) }, + new Type[]{ typeof( LichLord ), typeof( Daemon ), typeof( ElderGazer ), typeof( PoisonElemental ), typeof( BloodElemental ) }, + new Type[]{ typeof( AncientWyrm ), typeof( Balron ), typeof( BloodElemental ), typeof( PoisonElemental ), typeof( Titan ) } + }; + + public const double LootChance = 0.01; // 1% chance to appear as loot + + public static Point2D GetRandomLocation() + { + if ( m_Locations == null ) + LoadLocations(); + + if ( m_Locations.Length > 0 ) + return m_Locations[Utility.Random( m_Locations.Length )]; + + return Point2D.Zero; + } + + public static Point2D GetRandomHavenLocation() + { + if ( m_HavenLocations == null ) + LoadLocations(); + + if ( m_HavenLocations.Length > 0 ) + return m_HavenLocations[Utility.Random( m_HavenLocations.Length )]; + + return Point2D.Zero; + } + + private static void LoadLocations() + { + string filePath = Path.Combine( Core.BaseDirectory, "Data/treasure.cfg" ); + + ArrayList list = new ArrayList(); + ArrayList havenList = new ArrayList(); + + if ( File.Exists( filePath ) ) + { + using ( StreamReader ip = new StreamReader( filePath ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + try + { + string[] split = line.Split( ' ' ); + + int x = Convert.ToInt32( split[0] ), y = Convert.ToInt32( split[1] ); + + Point2D loc = new Point2D( x, y ); + list.Add( loc ); + + if ( IsInHavenIsland( loc ) ) + havenList.Add( loc ); + } + catch + { + } + } + } + } + + m_Locations = (Point2D[])list.ToArray( typeof( Point2D ) ); + m_HavenLocations = (Point2D[])havenList.ToArray( typeof( Point2D ) ); + } + + public static bool IsInHavenIsland( IPoint2D loc ) + { + return ( loc.X >= 3314 && loc.X <= 3814 && loc.Y >= 2345 && loc.Y <= 3095 ); + } + + public static BaseCreature Spawn( int level, Point3D p, bool guardian ) + { + if ( level >= 0 && level < m_SpawnTypes.Length ) + { + BaseCreature bc; + + try + { + bc = (BaseCreature)Activator.CreateInstance( m_SpawnTypes[level][Utility.Random( m_SpawnTypes[level].Length )] ); + } + catch + { + return null; + } + + bc.Home = p; + bc.RangeHome = 5; + + if ( guardian && level == 0 ) + { + bc.Name = "a chest guardian"; + bc.Hue = 0x835; + } + + return bc; + } + + return null; + } + + public static BaseCreature Spawn( int level, Point3D p, Map map, Mobile target, bool guardian ) + { + if ( map == null ) + return null; + + BaseCreature c = Spawn( level, p, guardian ); + + if ( c != null ) + { + bool spawned = false; + + for ( int i = 0; !spawned && i < 10; ++i ) + { + int x = p.X - 3 + Utility.Random( 7 ); + int y = p.Y - 3 + Utility.Random( 7 ); + + if ( map.CanSpawnMobile( x, y, p.Z ) ) + { + c.MoveToWorld( new Point3D( x, y, p.Z ), map ); + spawned = true; + } + else + { + int z = map.GetAverageZ( x, y ); + + if ( map.CanSpawnMobile( x, y, z ) ) + { + c.MoveToWorld( new Point3D( x, y, z ), map ); + spawned = true; + } + } + } + + if ( !spawned ) + { + c.Delete(); + return null; + } + + if ( target != null ) + c.Combatant = target; + + return c; + } + + return null; + } + + [Constructable] + public TreasureMap( int level, Map map ) + { + m_Level = level; + m_Map = map; + + if ( level == 0 ) + m_Location = GetRandomHavenLocation(); + else + m_Location = GetRandomLocation(); + + Width = 300; + Height = 300; + + int width = 600; + int height = 600; + + int x1 = m_Location.X - Utility.RandomMinMax( width / 4, (width / 4) * 3 ); + int y1 = m_Location.Y - Utility.RandomMinMax( height / 4, (height / 4) * 3 ); + + if ( x1 < 0 ) + x1 = 0; + + if ( y1 < 0 ) + y1 = 0; + + int x2 = x1 + width; + int y2 = y1 + height; + + if ( x2 >= 5120 ) + x2 = 5119; + + if ( y2 >= 4096 ) + y2 = 4095; + + x1 = x2 - width; + y1 = y2 - height; + + Bounds = new Rectangle2D( x1, y1, width, height ); + Protected = true; + + AddWorldPin( m_Location.X, m_Location.Y ); + } + + public TreasureMap( Serial serial ) : base( serial ) + { + } + + public static bool HasDiggingTool( Mobile m ) + { + if ( m.Backpack == null ) + return false; + + Item[] items = m.Backpack.FindItemsByType( typeof( BaseHarvestTool ) ); + + foreach ( BaseHarvestTool tool in items ) + { + if ( tool.HarvestSystem == Engines.Harvest.Mining.System ) + return true; + } + + return false; + } + + public void OnBeginDig( Mobile from ) + { + if ( m_Completed ) + { + from.SendLocalizedMessage( 503028 ); // The treasure for this map has already been found. + } + else if ( m_Level == 0 && !CheckYoung( from ) ) + { + from.SendLocalizedMessage( 1046447 ); // Only a young player may use this treasure map. + } + /* + else if ( from != m_Decoder ) + { + from.SendLocalizedMessage( 503016 ); // Only the person who decoded this map may actually dig up the treasure. + } + */ + else if ( m_Decoder != from && !HasRequiredSkill( from ) ) + { + from.SendLocalizedMessage( 503031 ); // You did not decode this map and have no clue where to look for the treasure. + } + else if ( !from.CanBeginAction( typeof( TreasureMap ) ) ) + { + from.SendLocalizedMessage( 503020 ); // You are already digging treasure. + } + else if ( from.Map != this.m_Map ) + { + from.SendLocalizedMessage( 1010479 ); // You seem to be in the right place, but may be on the wrong facet! + } + else + { + from.SendLocalizedMessage( 503033 ); // Where do you wish to dig? + from.Target = new DigTarget( this ); + } + } + + private class DigTarget : Target + { + private TreasureMap m_Map; + + public DigTarget( TreasureMap map ) : base( 6, true, TargetFlags.None ) + { + m_Map = map; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Map.Deleted ) + return; + + Map map = m_Map.m_Map; + + if ( m_Map.m_Completed ) + { + from.SendLocalizedMessage( 503028 ); // The treasure for this map has already been found. + } + /* + else if ( from != m_Map.m_Decoder ) + { + from.SendLocalizedMessage( 503016 ); // Only the person who decoded this map may actually dig up the treasure. + } + */ + else if ( m_Map.m_Decoder != from && !m_Map.HasRequiredSkill( from ) ) + { + from.SendLocalizedMessage( 503031 ); // You did not decode this map and have no clue where to look for the treasure. + return; + } + else if ( !from.CanBeginAction( typeof( TreasureMap ) ) ) + { + from.SendLocalizedMessage( 503020 ); // You are already digging treasure. + } + else if ( !HasDiggingTool( from ) ) + { + from.SendMessage( "You must have a digging tool to dig for treasure." ); + } + else if ( from.Map != map ) + { + from.SendLocalizedMessage( 1010479 ); // You seem to be in the right place, but may be on the wrong facet! + } + else + { + IPoint3D p = targeted as IPoint3D; + + Point3D targ3D; + if ( p is Item ) + targ3D = ((Item)p).GetWorldLocation(); + else + targ3D = new Point3D( p ); + + int maxRange; + double skillValue = from.Skills[SkillName.Mining].Value; + + if ( skillValue >= 100.0 ) + maxRange = 4; + else if ( skillValue >= 81.0 ) + maxRange = 3; + else if ( skillValue >= 51.0 ) + maxRange = 2; + else + maxRange = 1; + + Point2D loc = m_Map.m_Location; + int x = loc.X, y = loc.Y; + + Point3D chest3D0 = new Point3D( loc, 0 ); + + if ( Utility.InRange( targ3D, chest3D0, maxRange ) ) + { + if ( from.Location.X == x && from.Location.Y == y ) + { + from.SendLocalizedMessage( 503030 ); // The chest can't be dug up because you are standing on top of it. + } + else if ( map != null ) + { + int z = map.GetAverageZ( x, y ); + + if ( !map.CanFit( x, y, z, 16, true, true ) ) + { + from.SendLocalizedMessage( 503021 ); // You have found the treasure chest but something is keeping it from being dug up. + } + else if ( from.BeginAction( typeof( TreasureMap ) ) ) + { + new DigTimer( from, m_Map, new Point3D( x, y, z ), map ).Start(); + } + else + { + from.SendLocalizedMessage( 503020 ); // You are already digging treasure. + } + } + } + else if ( m_Map.Level > 0 ) + { + if ( Utility.InRange( targ3D, chest3D0, 8 ) ) // We're close, but not quite + { + from.SendLocalizedMessage( 503032 ); // You dig and dig but no treasure seems to be here. + } + else + { + from.SendLocalizedMessage( 503035 ); // You dig and dig but fail to find any treasure. + } + } + else + { + if ( Utility.InRange( targ3D, chest3D0, 8 ) ) // We're close, but not quite + { + from.SendAsciiMessage( 0x44, "The treasure chest is very close!" ); + } + else + { + Direction dir = Utility.GetDirection( targ3D, chest3D0 ); + + string sDir; + switch ( dir ) + { + case Direction.North: sDir = "north"; break; + case Direction.Right: sDir = "northeast"; break; + case Direction.East: sDir = "east"; break; + case Direction.Down: sDir = "southeast"; break; + case Direction.South: sDir = "south"; break; + case Direction.Left: sDir = "southwest"; break; + case Direction.West: sDir = "west"; break; + default: sDir = "northwest"; break; + } + + from.SendAsciiMessage( 0x44, "Try looking for the treasure chest more to the {0}.", sDir ); + } + } + } + } + } + + private class DigTimer : Timer + { + private Mobile m_From; + private TreasureMap m_TreasureMap; + + private Point3D m_Location; + private Map m_Map; + + private TreasureChestDirt m_Dirt1; + private TreasureChestDirt m_Dirt2; + private TreasureMapChest m_Chest; + + private int m_Count; + + private DateTime m_NextSkillTime; + private DateTime m_NextSpellTime; + private DateTime m_NextActionTime; + private DateTime m_LastMoveTime; + + public DigTimer( Mobile from, TreasureMap treasureMap, Point3D location, Map map ) : base( TimeSpan.Zero, TimeSpan.FromSeconds( 1.0 ) ) + { + m_From = from; + m_TreasureMap = treasureMap; + + m_Location = location; + m_Map = map; + + m_NextSkillTime = from.NextSkillTime; + m_NextSpellTime = from.NextSpellTime; + m_NextActionTime = from.NextActionTime; + m_LastMoveTime = from.LastMoveTime; + + Priority = TimerPriority.TenMS; + } + + private void Terminate() + { + Stop(); + m_From.EndAction( typeof( TreasureMap ) ); + + if ( m_Chest != null ) + m_Chest.Delete(); + + if ( m_Dirt1 != null ) + { + m_Dirt1.Delete(); + m_Dirt2.Delete(); + } + } + + protected override void OnTick() + { + if ( m_NextSkillTime != m_From.NextSkillTime || m_NextSpellTime != m_From.NextSpellTime || m_NextActionTime != m_From.NextActionTime ) + { + Terminate(); + return; + } + + if ( m_LastMoveTime != m_From.LastMoveTime ) + { + m_From.SendLocalizedMessage( 503023 ); // You cannot move around while digging up treasure. You will need to start digging anew. + Terminate(); + return; + } + + int z = ( m_Chest != null ) ? m_Chest.Z + m_Chest.ItemData.Height : int.MinValue; + int height = 16; + + if ( z > m_Location.Z ) + height -= ( z - m_Location.Z ); + else + z = m_Location.Z; + + if ( !m_Map.CanFit( m_Location.X, m_Location.Y, z, height, true, true, false ) ) + { + m_From.SendLocalizedMessage( 503024 ); // You stop digging because something is directly on top of the treasure chest. + Terminate(); + return; + } + + m_Count++; + + m_From.RevealingAction(); + m_From.Direction = m_From.GetDirectionTo( m_Location ); + + if ( m_Count > 1 && m_Dirt1 == null ) + { + m_Dirt1 = new TreasureChestDirt(); + m_Dirt1.MoveToWorld( m_Location, m_Map ); + + m_Dirt2 = new TreasureChestDirt(); + m_Dirt2.MoveToWorld( new Point3D( m_Location.X, m_Location.Y - 1, m_Location.Z ), m_Map ); + } + + if ( m_Count == 5 ) + { + m_Dirt1.Turn1(); + } + else if ( m_Count == 10 ) + { + m_Dirt1.Turn2(); + m_Dirt2.Turn2(); + } + else if ( m_Count > 10 ) + { + if ( m_Chest == null ) + { + m_Chest = new TreasureMapChest( m_From, m_TreasureMap.Level, true ); + m_Chest.MoveToWorld( new Point3D( m_Location.X, m_Location.Y, m_Location.Z - 15 ), m_Map ); + } + else + { + m_Chest.Z++; + } + + Effects.PlaySound( m_Chest, m_Map, 0x33B ); + } + + if ( m_Chest != null && m_Chest.Location.Z >= m_Location.Z ) + { + Stop(); + m_From.EndAction( typeof( TreasureMap ) ); + + m_Chest.Temporary = false; + m_TreasureMap.Completed = true; + m_TreasureMap.CompletedBy = m_From; + + int spawns; + switch ( m_TreasureMap.Level ) + { + case 0: spawns = 3; break; + case 1: spawns = 0; break; + default: spawns = 4; break; + } + + for ( int i = 0; i < spawns; ++i ) + { + BaseCreature bc = Spawn( m_TreasureMap.Level, m_Chest.Location, m_Chest.Map, null, true ); + + if ( bc != null ) + m_Chest.Guardians.Add( bc ); + } + } + else + { + if ( m_From.Body.IsHuman && !m_From.Mounted ) + m_From.Animate( 11, 5, 1, true, false, 0 ); + + new SoundTimer( m_From, 0x125 + (m_Count % 2) ).Start(); + } + } + + private class SoundTimer : Timer + { + private Mobile m_From; + private int m_SoundID; + + public SoundTimer( Mobile from, int soundID ) : base( TimeSpan.FromSeconds( 0.9 ) ) + { + m_From = from; + m_SoundID = soundID; + + Priority = TimerPriority.TenMS; + } + + protected override void OnTick() + { + m_From.PlaySound( m_SoundID ); + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + if ( !m_Completed && m_Decoder == null ) + Decode( from ); + else + DisplayTo( from ); + } + + private bool CheckYoung( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if ( from is PlayerMobile && ((PlayerMobile)from).Young ) + return true; + + if ( from == this.Decoder ) + { + this.Level = 1; + from.SendLocalizedMessage( 1046446 ); // This is now a level one treasure map. + return true; + } + + return false; + } + + private double GetMinSkillLevel() + { + switch ( m_Level ) + { + case 1: return -3.0; + case 2: return 41.0; + case 3: return 51.0; + case 4: return 61.0; + case 5: return 70.0; + case 6: return 70.0; + + default: return 0.0; + } + } + + private bool HasRequiredSkill( Mobile from ) + { + return ( from.Skills[SkillName.Cartography].Value >= GetMinSkillLevel() ); + } + + public void Decode( Mobile from ) + { + if ( m_Completed || m_Decoder != null ) + return; + + if ( m_Level == 0 ) + { + if ( !CheckYoung( from ) ) + { + from.SendLocalizedMessage( 1046447 ); // Only a young player may use this treasure map. + return; + } + } + else + { + double minSkill = GetMinSkillLevel(); + + if ( from.Skills[SkillName.Cartography].Value < minSkill ) + from.SendLocalizedMessage( 503013 ); // The map is too difficult to attempt to decode. + + double maxSkill = minSkill + 60.0; + + if ( !from.CheckSkill( SkillName.Cartography, minSkill, maxSkill ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 503018 ); // You fail to make anything of the map. + return; + } + } + + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 503019 ); // You successfully decode a treasure map! + Decoder = from; + + DisplayTo( from ); + } + + public override void DisplayTo( Mobile from ) + { + if ( m_Completed ) + { + SendLocalizedMessageTo( from, 503014 ); // This treasure hunt has already been completed. + } + else if ( m_Level == 0 && !CheckYoung( from ) ) + { + from.SendLocalizedMessage( 1046447 ); // Only a young player may use this treasure map. + return; + } + else if ( m_Decoder != from && !HasRequiredSkill( from ) ) + { + from.SendLocalizedMessage( 503031 ); // You did not decode this map and have no clue where to look for the treasure. + return; + } + else + { + SendLocalizedMessageTo( from, 503017 ); // The treasure is marked by the red pin. Grab a shovel and go dig it up! + } + + from.PlaySound( 0x249 ); + base.DisplayTo( from ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( !m_Completed ) + { + if ( m_Decoder == null ) + { + list.Add( new DecodeMapEntry( this ) ); + } + else + { + bool digTool = HasDiggingTool( from ); + + list.Add( new OpenMapEntry( this ) ); + list.Add( new DigEntry( this, digTool ) ); + } + } + } + + private class DecodeMapEntry : ContextMenuEntry + { + private TreasureMap m_Map; + + public DecodeMapEntry( TreasureMap map ) : base( 6147, 2 ) + { + m_Map = map; + } + + public override void OnClick() + { + if ( !m_Map.Deleted ) + m_Map.Decode( Owner.From ); + } + } + + private class OpenMapEntry : ContextMenuEntry + { + private TreasureMap m_Map; + + public OpenMapEntry( TreasureMap map ) : base( 6150, 2 ) + { + m_Map = map; + } + + public override void OnClick() + { + if ( !m_Map.Deleted ) + m_Map.DisplayTo( Owner.From ); + } + } + + private class DigEntry : ContextMenuEntry + { + private TreasureMap m_Map; + + public DigEntry( TreasureMap map, bool enabled ) : base( 6148, 2 ) + { + m_Map = map; + + if ( !enabled ) + this.Flags |= CMEFlags.Disabled; + } + + public override void OnClick() + { + if ( m_Map.Deleted ) + return; + + Mobile from = Owner.From; + + if ( HasDiggingTool( from ) ) + m_Map.OnBeginDig( from ); + else + from.SendMessage( "You must have a digging tool to dig for treasure." ); + } + } + + public override int LabelNumber + { + get + { + if ( m_Decoder != null ) + { + if ( m_Level == 6 ) + return 1063453; + else + return 1041516 + m_Level; + } + else if ( m_Level == 6 ) + return 1063452; + else + return 1041510 + m_Level; + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( m_Map == Map.Felucca ? 1041502 : 1041503 ); // for somewhere in Felucca : for somewhere in Trammel + + if ( m_Completed ) + { + list.Add( 1041507, m_CompletedBy == null ? "someone" : m_CompletedBy.Name ); // completed by ~1_val~ + } + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Completed ) + { + from.Send( new MessageLocalizedAffix( Serial, ItemID, MessageType.Label, 0x3B2, 3, 1048030, "", AffixType.Append, String.Format( " completed by {0}", m_CompletedBy == null ? "someone" : m_CompletedBy.Name ), "" ) ); + } + else if ( m_Decoder != null ) + { + if ( m_Level == 6 ) + LabelTo( from, 1063453 ); + else + LabelTo( from, 1041516 + m_Level ); + } + else + { + if ( m_Level == 6 ) + LabelTo( from, 1041522, String.Format( "#{0}\t \t#{1}", 1063452, m_Map == Map.Felucca ? 1041502 : 1041503 ) ); + else + LabelTo( from, 1041522, String.Format( "#{0}\t \t#{1}", 1041510 + m_Level, m_Map == Map.Felucca ? 1041502 : 1041503 ) ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + + writer.Write( (Mobile) m_CompletedBy ); + + writer.Write( m_Level ); + writer.Write( m_Completed ); + writer.Write( m_Decoder ); + writer.Write( m_Map ); + writer.Write( m_Location ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_CompletedBy = reader.ReadMobile(); + + goto case 0; + } + case 0: + { + m_Level = (int)reader.ReadInt(); + m_Completed = reader.ReadBool(); + m_Decoder = reader.ReadMobile(); + m_Map = reader.ReadMap(); + m_Location = reader.ReadPoint2D(); + + if ( version == 0 && m_Completed ) + m_CompletedBy = m_Decoder; + + break; + } + } + } + } + + public class TreasureChestDirt : Item + { + public TreasureChestDirt() : base( 0x912 ) + { + Movable = false; + + Timer.DelayCall( TimeSpan.FromMinutes( 2.0 ), new TimerCallback( Delete ) ); + } + + public TreasureChestDirt( Serial serial ) : base( serial ) + { + } + + public void Turn1() + { + this.ItemID = 0x913; + } + + public void Turn2() + { + this.ItemID = 0x914; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Maps/WorldMap.cs b/Scripts/Items/Maps/WorldMap.cs new file mode 100644 index 000000000..c60c4d30c --- /dev/null +++ b/Scripts/Items/Maps/WorldMap.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WorldMap : MapItem + { + [Constructable] + public WorldMap() + { + SetDisplay( 0, 0, 5119, 4095, 400, 400 ); + } + + public override void CraftInit( Mobile from ) + { + // Unlike the others, world map is not based on crafted location + + double skillValue = from.Skills[SkillName.Cartography].Value; + int x20 = (int)(skillValue * 20); + int size = 25 + (int)(skillValue * 6.6); + + if ( size < 200 ) + size = 200; + else if ( size > 400 ) + size = 400; + + SetDisplay( 1344 - x20, 1600 - x20, 1472 + x20, 1728 + x20, size, size ); + } + + public override int LabelNumber{ get{ return 1015233; } } // world map + + public WorldMap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/AdmiralsHeartyRum.cs b/Scripts/Items/Minor Artifacts/AdmiralsHeartyRum.cs new file mode 100644 index 000000000..84dcdaa48 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/AdmiralsHeartyRum.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AdmiralsHeartyRum : BeverageBottle + { + public override int LabelNumber{ get{ return 1063477; } } + + [Constructable] + public AdmiralsHeartyRum() : base( BeverageType.Ale ) + { + Hue = 0x66C; + } + + public AdmiralsHeartyRum( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/AlchemistsBauble.cs b/Scripts/Items/Minor Artifacts/AlchemistsBauble.cs new file mode 100644 index 000000000..1a81ded76 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/AlchemistsBauble.cs @@ -0,0 +1,38 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AlchemistsBauble : GoldBracelet + { + public override int LabelNumber{ get{ return 1070638; } } + + [Constructable] + public AlchemistsBauble() + { + Hue = 0x290; + SkillBonuses.SetValues( 0, SkillName.Magery, 10.0 ); + Attributes.EnhancePotions = 30; + Attributes.LowerRegCost = 20; + Resistances.Poison = 10; + } + + public AlchemistsBauble( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ArcticDeathDealer.cs b/Scripts/Items/Minor Artifacts/ArcticDeathDealer.cs new file mode 100644 index 000000000..692a2b923 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ArcticDeathDealer.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcticDeathDealer : WarMace + { + public override int LabelNumber{ get{ return 1063481; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ArcticDeathDealer() + { + Hue = 0x480; + WeaponAttributes.HitHarm = 33; + WeaponAttributes.HitLowerAttack = 40; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 40; + WeaponAttributes.ResistColdBonus = 10; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + cold = 50; + phys = 50; + + pois = fire = nrgy = 0; + } + + public ArcticDeathDealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/BlazeOfDeath.cs b/Scripts/Items/Minor Artifacts/BlazeOfDeath.cs new file mode 100644 index 000000000..22fff61ff --- /dev/null +++ b/Scripts/Items/Minor Artifacts/BlazeOfDeath.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BlazeOfDeath : Halberd + { + public override int LabelNumber{ get{ return 1063486; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BlazeOfDeath() + { + Hue = 0x501; + WeaponAttributes.HitFireArea = 50; + WeaponAttributes.HitFireball = 50; + Attributes.WeaponSpeed = 25; + Attributes.WeaponDamage = 35; + WeaponAttributes.ResistFireBonus = 10; + WeaponAttributes.LowerStatReq = 100; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + fire = 50; + phys = 50; + + cold = pois = nrgy = 0; + } + + public BlazeOfDeath( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/BowOfTheJukaKing.cs b/Scripts/Items/Minor Artifacts/BowOfTheJukaKing.cs new file mode 100644 index 000000000..51fbb13c6 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/BowOfTheJukaKing.cs @@ -0,0 +1,41 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BowOfTheJukaKing : Bow + { + public override int LabelNumber{ get{ return 1070636; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BowOfTheJukaKing() + { + Hue = 0x460; + WeaponAttributes.HitMagicArrow = 25; + Slayer = SlayerName.ReptilianDeath; + Attributes.AttackChance = 15; + Attributes.WeaponDamage = 40; + } + + public BowOfTheJukaKing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/BurglarsBandana.cs b/Scripts/Items/Minor Artifacts/BurglarsBandana.cs new file mode 100644 index 000000000..e7a3a5ae5 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/BurglarsBandana.cs @@ -0,0 +1,58 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BurglarsBandana : Bandana + { + public override int LabelNumber{ get{ return 1063473; } } + + public override int BasePhysicalResistance{ get{ return 10; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 10; } } + public override int BaseEnergyResistance{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BurglarsBandana() + { + Hue = Utility.RandomBool() ? 0x58C : 0x10; + + SkillBonuses.SetValues( 0, SkillName.Stealing, 10.0 ); + SkillBonuses.SetValues( 1, SkillName.Stealth, 10.0 ); + SkillBonuses.SetValues( 2, SkillName.Snooping, 10.0 ); + + Attributes.BonusDex = 5; + } + + public BurglarsBandana( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 2 ) + { + Resistances.Physical = 0; + Resistances.Fire = 0; + Resistances.Cold = 0; + Resistances.Poison = 0; + Resistances.Energy = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/CandelabraOfSouls.cs b/Scripts/Items/Minor Artifacts/CandelabraOfSouls.cs new file mode 100644 index 000000000..29605d2c8 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/CandelabraOfSouls.cs @@ -0,0 +1,33 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CandelabraOfSouls : Item + { + public override int LabelNumber{ get{ return 1063478; } } + + [Constructable] + public CandelabraOfSouls() : base( 0xB26 ) + { + } + + public CandelabraOfSouls( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/CaptainQuacklebushsCutlass.cs b/Scripts/Items/Minor Artifacts/CaptainQuacklebushsCutlass.cs new file mode 100644 index 000000000..faab6ae5c --- /dev/null +++ b/Scripts/Items/Minor Artifacts/CaptainQuacklebushsCutlass.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CaptainQuacklebushsCutlass : Cutlass + { + public override int LabelNumber{ get{ return 1063474; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public CaptainQuacklebushsCutlass() + { + Hue = 0x66C; + Attributes.BonusDex = 5; + Attributes.AttackChance = 10; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 50; + WeaponAttributes.UseBestSkill = 1; + } + + public CaptainQuacklebushsCutlass( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( Attributes.AttackChance == 50 ) + Attributes.AttackChance = 10; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/CavortingClub.cs b/Scripts/Items/Minor Artifacts/CavortingClub.cs new file mode 100644 index 000000000..0296bc3c9 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/CavortingClub.cs @@ -0,0 +1,44 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CavortingClub : Club + { + public override int LabelNumber{ get{ return 1063472; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public CavortingClub() + { + Hue = 0x593; + WeaponAttributes.SelfRepair = 3; + Attributes.WeaponSpeed = 25; + Attributes.WeaponDamage = 35; + WeaponAttributes.ResistFireBonus = 8; + WeaponAttributes.ResistColdBonus = 8; + WeaponAttributes.ResistPoisonBonus = 8; + WeaponAttributes.ResistEnergyBonus = 8; + } + + public CavortingClub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ColdBlood.cs b/Scripts/Items/Minor Artifacts/ColdBlood.cs new file mode 100644 index 000000000..571cc22f1 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ColdBlood.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ColdBlood : Cleaver + { + public override int LabelNumber{ get{ return 1070818; } } // Cold Blood + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ColdBlood() + { + Hue = 0x4F2; + + Attributes.WeaponSpeed = 40; + + Attributes.BonusHits = 6; + Attributes.BonusStam = 6; + Attributes.BonusMana = 6; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + cold = 100; + + fire = phys = pois = nrgy = 0; + } + + public ColdBlood( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/DreadPirateHat.cs b/Scripts/Items/Minor Artifacts/DreadPirateHat.cs new file mode 100644 index 000000000..2153c9d1a --- /dev/null +++ b/Scripts/Items/Minor Artifacts/DreadPirateHat.cs @@ -0,0 +1,61 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DreadPirateHat : TricorneHat + { + public override int LabelNumber{ get{ return 1063467; } } + + public override int BaseColdResistance{ get{ return 14; } } + public override int BasePoisonResistance{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public DreadPirateHat() + { + Hue = 0x497; + + SkillBonuses.SetValues( 0, Utility.RandomCombatSkill(), 10.0 ); + + Attributes.BonusDex = 8; + Attributes.AttackChance = 10; + Attributes.NightSight = 1; + } + + public DreadPirateHat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 3 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 3 ) + { + Resistances.Cold = 0; + Resistances.Poison = 0; + } + + if ( version < 1 ) + { + Attributes.Luck = 0; + Attributes.AttackChance = 10; + Attributes.NightSight = 1; + SkillBonuses.SetValues( 0, Utility.RandomCombatSkill(), 10.0 ); + SkillBonuses.SetBonus( 1, 0 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/EnchantedTitanLegBone.cs b/Scripts/Items/Minor Artifacts/EnchantedTitanLegBone.cs new file mode 100644 index 000000000..d7d4e815f --- /dev/null +++ b/Scripts/Items/Minor Artifacts/EnchantedTitanLegBone.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class EnchantedTitanLegBone : ShortSpear + { + public override int LabelNumber{ get{ return 1063482; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public EnchantedTitanLegBone() + { + Hue = 0x8A5; + WeaponAttributes.HitLowerDefend = 40; + WeaponAttributes.HitLightning = 40; + Attributes.AttackChance = 10; + Attributes.WeaponDamage = 20; + WeaponAttributes.ResistPhysicalBonus = 10; + } + + public EnchantedTitanLegBone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/GhostShipAnchor.cs b/Scripts/Items/Minor Artifacts/GhostShipAnchor.cs new file mode 100644 index 000000000..2c9a78760 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/GhostShipAnchor.cs @@ -0,0 +1,37 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GhostShipAnchor : Item + { + public override int LabelNumber{ get{ return 1070816; } } // Ghost Ship Anchor + + [Constructable] + public GhostShipAnchor() : base( 0x14F7 ) + { + Hue = 0x47E; + } + + public GhostShipAnchor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( ItemID == 0x1F47 ) + ItemID = 0x14F7; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/GlovesOfThePugilist.cs b/Scripts/Items/Minor Artifacts/GlovesOfThePugilist.cs new file mode 100644 index 000000000..e97404611 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/GlovesOfThePugilist.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GlovesOfThePugilist : LeatherGloves + { + public override int LabelNumber{ get{ return 1070690; } } + + public override int BasePhysicalResistance{ get{ return 18; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public GlovesOfThePugilist() + { + Hue = 0x6D1; + SkillBonuses.SetValues( 0, SkillName.Wrestling, 10.0 ); + Attributes.BonusDex = 8; + Attributes.WeaponDamage = 15; + } + + public GlovesOfThePugilist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/GoldBricks.cs b/Scripts/Items/Minor Artifacts/GoldBricks.cs new file mode 100644 index 000000000..53231a634 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/GoldBricks.cs @@ -0,0 +1,33 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GoldBricks : Item + { + public override int LabelNumber{ get{ return 1063489; } } + + [Constructable] + public GoldBricks() : base( 0x1BEB ) + { + } + + public GoldBricks( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/GwennosHarp.cs b/Scripts/Items/Minor Artifacts/GwennosHarp.cs new file mode 100644 index 000000000..d8edc7b61 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/GwennosHarp.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GwennosHarp : LapHarp + { + public override int LabelNumber{ get{ return 1063480; } } + + public override int InitMinUses{ get{ return 1600; } } + public override int InitMaxUses{ get{ return 1600; } } + + [Constructable] + public GwennosHarp() + { + Hue = 0x47E; + Slayer = SlayerName.Repond; + Slayer2 = SlayerName.ReptilianDeath; + } + + public GwennosHarp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/HeartOfTheLion.cs b/Scripts/Items/Minor Artifacts/HeartOfTheLion.cs new file mode 100644 index 000000000..62664c926 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/HeartOfTheLion.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HeartOfTheLion : PlateChest + { + public override int LabelNumber{ get{ return 1070817; } } // Heart of the Lion + + public override int BasePhysicalResistance{ get{ return 15; } } + public override int BaseFireResistance{ get{ return 10; } } + public override int BaseColdResistance{ get{ return 10; } } + public override int BasePoisonResistance{ get{ return 10; } } + public override int BaseEnergyResistance{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public HeartOfTheLion() + { + Hue = 0x501; + Attributes.Luck = 95; + Attributes.DefendChance = 15; + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + } + + public HeartOfTheLion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/IolosLute.cs b/Scripts/Items/Minor Artifacts/IolosLute.cs new file mode 100644 index 000000000..f33010170 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/IolosLute.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class IolosLute : Lute + { + public override int LabelNumber{ get{ return 1063479; } } + + public override int InitMinUses{ get{ return 1600; } } + public override int InitMaxUses{ get{ return 1600; } } + + [Constructable] + public IolosLute() + { + Hue = 0x47E; + Slayer = SlayerName.Silver; + //Slayer2 = SlayerName.DaemonDismissal; + Slayer2 = SlayerName.Exorcism; + } + + public IolosLute( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/LunaLance.cs b/Scripts/Items/Minor Artifacts/LunaLance.cs new file mode 100644 index 000000000..71fa35794 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/LunaLance.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LunaLance : Lance + { + public override int LabelNumber{ get{ return 1063469; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public LunaLance() + { + Hue = 0x47E; + SkillBonuses.SetValues( 0, SkillName.Chivalry, 10.0 ); + Attributes.BonusStr = 5; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 35; + WeaponAttributes.UseBestSkill = 1; + } + + public LunaLance( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/AegisOfGrace.cs b/Scripts/Items/Minor Artifacts/ML/AegisOfGrace.cs new file mode 100644 index 000000000..683dc2c1a --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/AegisOfGrace.cs @@ -0,0 +1,56 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class AegisOfGrace : DragonHelm + { + public override int LabelNumber{ get{ return 1075047; } } // Aegis of Grace + + public override int BasePhysicalResistance{ get{ return 10; } } + public override int BaseFireResistance{ get{ return 9; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 7; } } + public override int BaseEnergyResistance{ get{ return 15; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Dragon; } } + public override CraftResource DefaultResource{ get{ return CraftResource.Iron; } } + + [Constructable] + public AegisOfGrace() + { + SkillBonuses.SetValues( 0, SkillName.MagicResist, 10.0 ); + + Attributes.DefendChance = 20; + + ArmorAttributes.SelfRepair = 2; + } + + public override Race RequiredRace + { + get + { + return Race.Elf; + } + } + + public AegisOfGrace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/BladeDance.cs b/Scripts/Items/Minor Artifacts/ML/BladeDance.cs new file mode 100644 index 000000000..04ec5d719 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/BladeDance.cs @@ -0,0 +1,44 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class BladeDance : RuneBlade + { + public override int LabelNumber{ get{ return 1075033; } } // Blade Dance + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BladeDance() + { + Hue = 0x66C; + + Attributes.BonusMana = 8; + Attributes.SpellChanneling = 1; + Attributes.WeaponDamage = 30; + WeaponAttributes.HitLeechMana = 20; + WeaponAttributes.UseBestSkill = 1; + } + + public BladeDance( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/Bonesmasher.cs b/Scripts/Items/Minor Artifacts/ML/Bonesmasher.cs new file mode 100644 index 000000000..4f4c84e72 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/Bonesmasher.cs @@ -0,0 +1,44 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class Bonesmasher : DiamondMace + { + public override int LabelNumber{ get{ return 1075030; } } // Bonesmasher + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public Bonesmasher() + { + ItemID = 0x2D30; + Hue = 0x482; + + SkillBonuses.SetValues( 0, SkillName.Macing, 10.0 ); + + WeaponAttributes.HitLeechMana = 40; + WeaponAttributes.SelfRepair = 2; + } + + public Bonesmasher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/FeyLeggings.cs b/Scripts/Items/Minor Artifacts/ML/FeyLeggings.cs new file mode 100644 index 000000000..9102556a4 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/FeyLeggings.cs @@ -0,0 +1,52 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class FeyLeggings : ChainLegs + { + public override int LabelNumber{ get{ return 1075041; } } // Fey Leggings + + public override int BasePhysicalResistance{ get{ return 12; } } + public override int BaseFireResistance{ get{ return 8; } } + public override int BaseColdResistance{ get{ return 7; } } + public override int BasePoisonResistance{ get{ return 4; } } + public override int BaseEnergyResistance{ get{ return 19; } } + + [Constructable] + public FeyLeggings() + { + Attributes.BonusHits = 6; + Attributes.DefendChance = 20; + + ArmorAttributes.MageArmor = 1; + } + + public override Race RequiredRace + { + get + { + return Race.Elf; + } + } + + public FeyLeggings( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/FleshRipper.cs b/Scripts/Items/Minor Artifacts/ML/FleshRipper.cs new file mode 100644 index 000000000..bb346310e --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/FleshRipper.cs @@ -0,0 +1,47 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class FleshRipper : AssassinSpike + { + public override int LabelNumber{ get{ return 1075045; } } // Flesh Ripper + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public FleshRipper() + { + Hue = 0x341; + + SkillBonuses.SetValues( 0, SkillName.Anatomy, 10.0 ); + + Attributes.BonusStr = 5; + Attributes.AttackChance = 15; + Attributes.WeaponSpeed = 40; + + WeaponAttributes.UseBestSkill = 1; + // TODO: Mage Slayer + } + + public FleshRipper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/MelisandesCorrodedHatchet.cs b/Scripts/Items/Minor Artifacts/ML/MelisandesCorrodedHatchet.cs new file mode 100644 index 000000000..53db3294e --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/MelisandesCorrodedHatchet.cs @@ -0,0 +1,43 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class MelisandesCorrodedHatchet : Hatchet + { + public override int LabelNumber{ get{ return 1072115; } } // Melisande's Corroded Hatchet + + [Constructable] + public MelisandesCorrodedHatchet() + { + Hue = 0x494; + + SkillBonuses.SetValues( 0, SkillName.Lumberjacking, 5.0 ); + + Attributes.SpellChanneling = 1; + Attributes.WeaponSpeed = 15; + Attributes.WeaponDamage = -50; + + WeaponAttributes.SelfRepair = 4; + } + + public MelisandesCorrodedHatchet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/PadsOfTheCuSidhe.cs b/Scripts/Items/Minor Artifacts/ML/PadsOfTheCuSidhe.cs new file mode 100644 index 000000000..58015fdd5 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/PadsOfTheCuSidhe.cs @@ -0,0 +1,33 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PadsOfTheCuSidhe : FurBoots + { + public override int LabelNumber{ get{ return 1075048; } } // Pads of the Cu Sidhe + + [Constructable] + public PadsOfTheCuSidhe() : base( 0x47E ) + { + } + + public PadsOfTheCuSidhe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/RaedsGlory.cs b/Scripts/Items/Minor Artifacts/ML/RaedsGlory.cs new file mode 100644 index 000000000..193bbc6fe --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/RaedsGlory.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class RaedsGlory : WarCleaver + { + public override int LabelNumber{ get{ return 1075036; } } // Raed's Glory + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public RaedsGlory() + { + ItemID = 0x2D23; + Hue = 0x1E6; + + Attributes.BonusMana = 8; + Attributes.SpellChanneling = 1; + Attributes.WeaponSpeed = 20; + + WeaponAttributes.HitLeechHits = 40; + } + + public RaedsGlory( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/RighteousAnger.cs b/Scripts/Items/Minor Artifacts/ML/RighteousAnger.cs new file mode 100644 index 000000000..8361e55e9 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/RighteousAnger.cs @@ -0,0 +1,43 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class RighteousAnger : ElvenMachete + { + public override int LabelNumber{ get{ return 1075049; } } // Righteous Anger + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public RighteousAnger() + { + Hue = 0x284; + + Attributes.AttackChance = 15; + Attributes.DefendChance = 5; + Attributes.WeaponSpeed = 35; + Attributes.WeaponDamage = 40; + } + + public RighteousAnger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs b/Scripts/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs new file mode 100644 index 000000000..e5d1cc1b2 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1F03, 0x1F04 )] + public class RobeOfTheEclipse : BaseOuterTorso + { + public override int LabelNumber{ get{ return 1075082; } } // Robe of the Eclipse + + [Constructable] + public RobeOfTheEclipse() : base( 0x1F03, 0x486 ) + { + Weight = 3.0; + + Attributes.Luck = 95; + + // TODO: Supports arcane? + } + + public RobeOfTheEclipse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs b/Scripts/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs new file mode 100644 index 000000000..2cfcf48e2 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1F03, 0x1F04 )] + public class RobeOfTheEquinox : BaseOuterTorso + { + public override int LabelNumber{ get{ return 1075042; } } // Robe of the Equinox + + [Constructable] + public RobeOfTheEquinox() : base( 0x1F04, 0xD6 ) + { + Weight = 3.0; + + Attributes.Luck = 95; + + // TODO: Supports arcane? + // TODO: Elves Only + } + + public RobeOfTheEquinox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/SoulSeeker.cs b/Scripts/Items/Minor Artifacts/ML/SoulSeeker.cs new file mode 100644 index 000000000..ba7a391ad --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/SoulSeeker.cs @@ -0,0 +1,51 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class SoulSeeker : RadiantScimitar + { + public override int LabelNumber{ get{ return 1075046; } } // Soul Seeker + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public SoulSeeker() + { + Hue = 0x38C; + + WeaponAttributes.HitLeechStam = 40; + WeaponAttributes.HitLeechMana = 40; + WeaponAttributes.HitLeechHits = 40; + Attributes.WeaponSpeed = 60; + Slayer = SlayerName.Repond; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + cold = 100; + + pois = fire = phys = nrgy = 0; + } + + public SoulSeeker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ML/TalonBite.cs b/Scripts/Items/Minor Artifacts/ML/TalonBite.cs new file mode 100644 index 000000000..e17c2db69 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ML/TalonBite.cs @@ -0,0 +1,48 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class TalonBite : OrnateAxe + { + public override int LabelNumber{ get{ return 1075029; } } // Talon Bite + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TalonBite() + { + ItemID = 0x2D34; + Hue = 0x47E; + + SkillBonuses.SetValues( 0, SkillName.Tactics, 10.0 ); + + Attributes.BonusDex = 8; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 35; + + WeaponAttributes.HitHarm = 33; + WeaponAttributes.UseBestSkill = 1; + } + + public TalonBite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/NightsKiss.cs b/Scripts/Items/Minor Artifacts/NightsKiss.cs new file mode 100644 index 000000000..3019e15d1 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/NightsKiss.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class NightsKiss : Dagger + { + public override int LabelNumber{ get{ return 1063475; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public NightsKiss() + { + ItemID = 0xF51; + Hue = 0x455; + WeaponAttributes.HitLeechHits = 40; + Slayer = SlayerName.Repond; + Attributes.WeaponSpeed = 30; + Attributes.WeaponDamage = 35; + } + + public NightsKiss( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/NoxRangersHeavyCrossbow.cs b/Scripts/Items/Minor Artifacts/NoxRangersHeavyCrossbow.cs new file mode 100644 index 000000000..efa51028f --- /dev/null +++ b/Scripts/Items/Minor Artifacts/NoxRangersHeavyCrossbow.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class NoxRangersHeavyCrossbow : HeavyCrossbow + { + public override int LabelNumber{ get{ return 1063485; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public NoxRangersHeavyCrossbow() + { + Hue = 0x58C; + WeaponAttributes.HitLeechStam = 40; + Attributes.SpellChanneling = 1; + Attributes.WeaponSpeed = 30; + Attributes.WeaponDamage = 20; + WeaponAttributes.ResistPoisonBonus = 10; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + pois = 50; + phys = 50; + + fire = cold = nrgy = 0; + } + + public NoxRangersHeavyCrossbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/OrcishVisage.cs b/Scripts/Items/Minor Artifacts/OrcishVisage.cs new file mode 100644 index 000000000..81535f7a1 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/OrcishVisage.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class OrcishVisage : OrcHelm + { + public override int LabelNumber{ get{ return 1070691; } } + + public override int BasePhysicalResistance{ get{ return 8; } } + public override int BaseFireResistance{ get{ return 5; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 5; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public OrcishVisage() + { + Hue = 0x592; + ArmorAttributes.SelfRepair = 3; + Attributes.BonusStr = 10; + Attributes.BonusStam = 5; + } + + public OrcishVisage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/PhillipsWoodenSteed.cs b/Scripts/Items/Minor Artifacts/PhillipsWoodenSteed.cs new file mode 100644 index 000000000..237f8421d --- /dev/null +++ b/Scripts/Items/Minor Artifacts/PhillipsWoodenSteed.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PhillipsWoodenSteed : MonsterStatuette + { + [Constructable] + public PhillipsWoodenSteed() : base( MonsterStatuetteType.PhillipsWoodenSteed ) + { + LootType = LootType.Regular; + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public PhillipsWoodenSteed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/PixieSwatter.cs b/Scripts/Items/Minor Artifacts/PixieSwatter.cs new file mode 100644 index 000000000..2924b351c --- /dev/null +++ b/Scripts/Items/Minor Artifacts/PixieSwatter.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PixieSwatter : Scepter + { + public override int LabelNumber{ get{ return 1070854; } } // Pixie Swatter + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public PixieSwatter() + { + Hue = 0x8A; + WeaponAttributes.HitPoisonArea = 75; + Attributes.WeaponSpeed = 30; + + WeaponAttributes.UseBestSkill = 1; + WeaponAttributes.ResistFireBonus = 12; + WeaponAttributes.ResistEnergyBonus = 12; + + Slayer = SlayerName.Fey; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + fire = 100; + + cold = pois = phys = nrgy = 0; + } + + public PixieSwatter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/PolarBearMask.cs b/Scripts/Items/Minor Artifacts/PolarBearMask.cs new file mode 100644 index 000000000..e1062e6d7 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/PolarBearMask.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PolarBearMask : BearMask + { + public override int LabelNumber{ get{ return 1070637; } } + + public override int BasePhysicalResistance{ get{ return 15; } } + public override int BaseColdResistance{ get{ return 21; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public PolarBearMask() + { + Hue = 0x481; + + ClothingAttributes.SelfRepair = 3; + + Attributes.RegenHits = 2; + Attributes.NightSight = 1; + } + + public PolarBearMask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 2 ) + { + Resistances.Physical = 0; + Resistances.Cold = 0; + } + + if ( Attributes.NightSight == 0 ) + Attributes.NightSight = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/SeahorseStatuette.cs b/Scripts/Items/Minor Artifacts/SeahorseStatuette.cs new file mode 100644 index 000000000..ee5732fe0 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/SeahorseStatuette.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SeahorseStatuette : MonsterStatuette + { + [Constructable] + public SeahorseStatuette() : base( MonsterStatuetteType.Seahorse ) + { + LootType = LootType.Regular; + + Hue = Utility.RandomList( 0, 0x482, 0x489, 0x495, 0x4F2 ); + } + + public SeahorseStatuette( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ShieldOfInvulnerability.cs b/Scripts/Items/Minor Artifacts/ShieldOfInvulnerability.cs new file mode 100644 index 000000000..847ef42ab --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ShieldOfInvulnerability.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ShieldOfInvulnerability : OrderShield + { + public override int LabelNumber{ get{ return 1070693; } } + + public override int BasePhysicalResistance{ get{ return 8; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ShieldOfInvulnerability() + { + Hue = 0x4F2; + + Attributes.SpellChanneling = 1; + Attributes.ReflectPhysical = 10; + Attributes.DefendChance = 15; + ArmorAttributes.LowerStatReq = 100; + } + + public override bool Validate( Mobile m ) + { + return true; + } + + public ShieldOfInvulnerability( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/ShipModelOfTheHMSCape.cs b/Scripts/Items/Minor Artifacts/ShipModelOfTheHMSCape.cs new file mode 100644 index 000000000..f0ea293f4 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/ShipModelOfTheHMSCape.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ShipModelOfTheHMSCape : Item + { + public override int LabelNumber{ get{ return 1063476; } } + + [Constructable] + public ShipModelOfTheHMSCape() : base( 0x14F3 ) + { + Hue = 0x37B; + } + + public ShipModelOfTheHMSCape( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/StaffOfPower.cs b/Scripts/Items/Minor Artifacts/StaffOfPower.cs new file mode 100644 index 000000000..bdf9a0883 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/StaffOfPower.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StaffOfPower : BlackStaff + { + public override int LabelNumber{ get{ return 1070692; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public StaffOfPower() + { + Hue = 0x4F2; + WeaponAttributes.MageWeapon = 15; + Attributes.SpellChanneling = 1; + Attributes.SpellDamage = 5; + Attributes.CastRecovery = 2; + Attributes.LowerManaCost = 5; + } + + public StaffOfPower( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/VioletCourage.cs b/Scripts/Items/Minor Artifacts/VioletCourage.cs new file mode 100644 index 000000000..d17f9d6e1 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/VioletCourage.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class VioletCourage : FemalePlateChest + { + public override int LabelNumber{ get{ return 1063471; } } + + public override int BasePhysicalResistance{ get{ return 14; } } + public override int BaseFireResistance{ get{ return 12; } } + public override int BaseColdResistance{ get{ return 12; } } + public override int BasePoisonResistance{ get{ return 8; } } + public override int BaseEnergyResistance{ get{ return 9; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public VioletCourage() + { + Hue = 0x486; + Attributes.Luck = 95; + Attributes.DefendChance = 15; + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + } + + public VioletCourage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Minor Artifacts/WrathOfTheDryad.cs b/Scripts/Items/Minor Artifacts/WrathOfTheDryad.cs new file mode 100644 index 000000000..3c01b1d38 --- /dev/null +++ b/Scripts/Items/Minor Artifacts/WrathOfTheDryad.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WrathOfTheDryad : GnarledStaff + { + public override int LabelNumber{ get{ return 1070853; } } // Wrath of the Dryad + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public WrathOfTheDryad() + { + Hue = 0x29C; + WeaponAttributes.HitLeechMana = 50; + WeaponAttributes.HitLightning = 33; + Attributes.AttackChance = 15; + Attributes.WeaponDamage = 40; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + pois = 100; + + cold = fire = phys = nrgy = 0; + } + + public WrathOfTheDryad( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/ArcaneGem.cs b/Scripts/Items/Misc/ArcaneGem.cs new file mode 100644 index 000000000..5a8b0bb09 --- /dev/null +++ b/Scripts/Items/Misc/ArcaneGem.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Targeting; + +namespace Server.Items +{ + public class ArcaneGem : Item + { + public override string DefaultName + { + get { return "arcane gem"; } + } + + [Constructable] + public ArcaneGem() : base( 0x1EA7 ) + { + Weight = 1.0; + } + + public ArcaneGem( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + from.BeginTarget( 2, false, TargetFlags.None, new TargetCallback( OnTarget ) ); + from.SendMessage( "What do you wish to use the gem on?" ); + } + } + + public int GetChargesFor( Mobile m ) + { + int v = (int)(m.Skills[SkillName.Tailoring].Value / 5); + + if ( v < 16 ) + return 16; + else if ( v > 24 ) + return 24; + + return v; + } + + public const int DefaultArcaneHue = 2117; + + public void OnTarget( Mobile from, object obj ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + return; + } + + if ( obj is IArcaneEquip && obj is Item ) + { + Item item = (Item)obj; + IArcaneEquip eq = (IArcaneEquip)obj; + + if ( !item.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + return; + } + + int charges = GetChargesFor( from ); + + if ( eq.IsArcane ) + { + if ( eq.CurArcaneCharges >= eq.MaxArcaneCharges ) + { + from.SendMessage( "That item is already fully charged." ); + } + else + { + if ( eq.CurArcaneCharges <= 0 ) + item.Hue = DefaultArcaneHue; + + if ( (eq.CurArcaneCharges + charges) > eq.MaxArcaneCharges ) + eq.CurArcaneCharges = eq.MaxArcaneCharges; + else + eq.CurArcaneCharges += charges; + + from.SendMessage( "You recharge the item." ); + Delete(); + } + } + else if ( from.Skills[SkillName.Tailoring].Value >= 80.0 ) + { + bool isExceptional = false; + + if ( item is BaseClothing ) + isExceptional = ( ((BaseClothing)item).Quality == ClothingQuality.Exceptional ); + else if ( item is BaseArmor ) + isExceptional = ( ((BaseArmor)item).Quality == ArmorQuality.Exceptional ); + else if ( item is BaseWeapon ) + isExceptional = ( ((BaseWeapon)item).Quality == WeaponQuality.Exceptional ); + + if ( isExceptional ) + { + if ( item is BaseClothing ) + ((BaseClothing)item).Crafter = from; + else if ( item is BaseArmor ) + ((BaseArmor)item).Crafter = from; + else if ( item is BaseWeapon ) + ((BaseWeapon)item).Crafter = from; + + eq.CurArcaneCharges = eq.MaxArcaneCharges = charges; + + item.Hue = DefaultArcaneHue; + + from.SendMessage( "You enhance the item with your gem." ); + Delete(); + } + else + { + from.SendMessage( "Only exceptional items can be enhanced with the gem." ); + } + } + else + { + from.SendMessage( "You do not have enough skill in tailoring to enhance the item." ); + } + } + else + { + from.SendMessage( "You cannot use the gem on that." ); + } + } + + public static bool ConsumeCharges( Mobile from, int amount ) + { + List items = from.Items; + int avail = 0; + + for ( int i = 0; i < items.Count; ++i ) + { + Item obj = items[i]; + + if ( obj is IArcaneEquip ) + { + IArcaneEquip eq = (IArcaneEquip)obj; + + if ( eq.IsArcane ) + avail += eq.CurArcaneCharges; + } + } + + if ( avail < amount ) + return false; + + for ( int i = 0; i < items.Count; ++i ) + { + Item obj = items[i]; + + if ( obj is IArcaneEquip ) + { + IArcaneEquip eq = (IArcaneEquip)obj; + + if ( eq.IsArcane ) + { + if ( eq.CurArcaneCharges > amount ) + { + eq.CurArcaneCharges -= amount; + break; + } + else + { + amount -= eq.CurArcaneCharges; + eq.CurArcaneCharges = 0; + } + } + } + } + + return true; + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/BankCheck.cs b/Scripts/Items/Misc/BankCheck.cs new file mode 100644 index 000000000..0ab54c4ba --- /dev/null +++ b/Scripts/Items/Misc/BankCheck.cs @@ -0,0 +1,161 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Engines.Quests; +using Necro = Server.Engines.Quests.Necro; +using Haven = Server.Engines.Quests.Haven; + +namespace Server.Items +{ + public class BankCheck : Item + { + private int m_Worth; + + [CommandProperty( AccessLevel.GameMaster )] + public int Worth + { + get{ return m_Worth; } + set{ m_Worth = value; InvalidateProperties(); } + } + + public BankCheck( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Worth ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Worth = reader.ReadInt(); + break; + } + } + } + + [Constructable] + public BankCheck( int worth ) : base( 0x14F0 ) + { + Weight = 1.0; + Hue = 0x34; + LootType = LootType.Blessed; + + m_Worth = worth; + } + + public override bool DisplayLootType{ get{ return Core.AOS; } } + + public override int LabelNumber{ get{ return 1041361; } } // A bank check + + public override void GetProperties(ObjectPropertyList list) + { + base.GetProperties( list ); + + list.Add( 1060738, m_Worth.ToString() ); // value: ~1_val~ + } + + public override void OnSingleClick( Mobile from ) + { + from.Send( new MessageLocalizedAffix( Serial, ItemID, MessageType.Label, 0x3B2, 3, 1041361, "", AffixType.Append, String.Concat( " ", m_Worth.ToString() ), "" ) ); // A bank check: + } + + public override void OnDoubleClick( Mobile from ) + { + BankBox box = from.BankBox; + + if ( box != null && IsChildOf( box ) ) + { + Delete(); + + int deposited = 0; + + int toAdd = m_Worth; + + Gold gold; + + while ( toAdd > 60000 ) + { + gold = new Gold( 60000 ); + + if ( box.TryDropItem( from, gold, false ) ) + { + toAdd -= 60000; + deposited += 60000; + } + else + { + gold.Delete(); + + from.AddToBackpack( new BankCheck( toAdd ) ); + toAdd = 0; + + break; + } + } + + if ( toAdd > 0 ) + { + gold = new Gold( toAdd ); + + if ( box.TryDropItem( from, gold, false ) ) + { + deposited += toAdd; + } + else + { + gold.Delete(); + + from.AddToBackpack( new BankCheck( toAdd ) ); + } + } + + // Gold was deposited in your account: + from.SendLocalizedMessage( 1042672, true, " " + deposited.ToString() ); + + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is Necro.DarkTidesQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( Necro.CashBankCheckObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + + if ( qs is Haven.UzeraanTurmoilQuest ) + { + QuestObjective obj = qs.FindObjective( typeof( Haven.CashBankCheckObjective ) ); + + if ( obj != null && !obj.Completed ) + obj.Complete(); + } + } + } + else + { + from.SendLocalizedMessage( 1047026 ); // That must be in your bank box to use it. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Beeswax.cs b/Scripts/Items/Misc/Beeswax.cs new file mode 100644 index 000000000..b9cb7fb78 --- /dev/null +++ b/Scripts/Items/Misc/Beeswax.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server.Items +{ + public class Beeswax : Item + { + [Constructable] + public Beeswax() : this( 1 ) + { + } + + [Constructable] + public Beeswax( int amount ) : base( 0x1422 ) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + } + + public Beeswax( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Blocker.cs b/Scripts/Items/Misc/Blocker.cs new file mode 100644 index 000000000..2d12785e5 --- /dev/null +++ b/Scripts/Items/Misc/Blocker.cs @@ -0,0 +1,110 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class Blocker : Item + { + public override int LabelNumber{ get{ return 503057; } } // Impassable! + + [Constructable] + public Blocker() : base( 0x21A4 ) + { + Movable = false; + } + + public Blocker( Serial serial ) : base( serial ) + { + } + + public override void SendInfoTo( NetState state ) + { + Mobile mob = state.Mobile; + + if ( mob != null && mob.AccessLevel >= AccessLevel.GameMaster ) + state.Send( new GMItemPacket( this ) ); + else + state.Send( WorldPacket ); + + if ( ObjectPropertyList.Enabled ) + state.Send( OPLPacket ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public sealed class GMItemPacket : Packet + { + public GMItemPacket( Item item ) : base( 0x1A ) + { + this.EnsureCapacity( 20 ); + + // 14 base length + // +2 - Amount + // +2 - Hue + // +1 - Flags + + uint serial = (uint)item.Serial.Value; + int itemID = 0x1183; + int amount = item.Amount; + Point3D loc = item.Location; + int x = loc.X; + int y = loc.Y; + int hue = item.Hue; + int flags = item.GetPacketFlags(); + int direction = (int)item.Direction; + + if ( amount != 0 ) + serial |= 0x80000000; + else + serial &= 0x7FFFFFFF; + + m_Stream.Write( (uint) serial ); + m_Stream.Write( (short) (itemID & 0x7FFF) ); + + if ( amount != 0 ) + m_Stream.Write( (short) amount ); + + x &= 0x7FFF; + + if ( direction != 0 ) + x |= 0x8000; + + m_Stream.Write( (short) x ); + + y &= 0x3FFF; + + if ( hue != 0 ) + y |= 0x8000; + + if ( flags != 0 ) + y |= 0x4000; + + m_Stream.Write( (short) y ); + + if ( direction != 0 ) + m_Stream.Write( (byte) direction ); + + m_Stream.Write( (sbyte) loc.Z ); + + if ( hue != 0 ) + m_Stream.Write( (ushort) hue ); + + if ( flags != 0 ) + m_Stream.Write( (byte) flags ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Blood.cs b/Scripts/Items/Misc/Blood.cs new file mode 100644 index 000000000..52f57088c --- /dev/null +++ b/Scripts/Items/Misc/Blood.cs @@ -0,0 +1,57 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Blood : Item + { + [Constructable] + public Blood() : this( 0x1645 ) + { + } + + [Constructable] + public Blood( int itemID ) : base( itemID ) + { + Movable = false; + + new InternalTimer( this ).Start(); + } + + public Blood( Serial serial ) : base( serial ) + { + new InternalTimer( this ).Start(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private class InternalTimer : Timer + { + private Item m_Blood; + + public InternalTimer( Item blood ) : base( TimeSpan.FromSeconds( 5.0 ) ) + { + Priority = TimerPriority.OneSecond; + + m_Blood = blood; + } + + protected override void OnTick() + { + m_Blood.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Bola.cs b/Scripts/Items/Misc/Bola.cs new file mode 100644 index 000000000..a4dc84a1d --- /dev/null +++ b/Scripts/Items/Misc/Bola.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class Bola : Item + { + [Constructable] + public Bola() : this( 1 ) + { + } + + [Constructable] + public Bola( int amount ) : base( 0x26AC ) + { + Weight = 4.0; + Stackable = true; + Amount = amount; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1040019 ); // The bola must be in your pack to use it. + } + else if ( !from.CanBeginAction( typeof( Bola ) ) ) + { + from.SendLocalizedMessage( 1049624 ); // You have to wait a few moments before you can use another bola! + } + else if ( from.Target is BolaTarget ) + { + from.SendLocalizedMessage( 1049631 ); // This bola is already being used. + } + else if ( !Core.AOS && (from.FindItemOnLayer( Layer.OneHanded ) != null || from.FindItemOnLayer( Layer.TwoHanded ) != null) ) + { + from.SendLocalizedMessage( 1040015 ); // Your hands must be free to use this + } + else if ( from.Mounted ) + { + from.SendLocalizedMessage( 1040016 ); // You cannot use this while riding a mount + } + else if ( Server.Spells.Ninjitsu.AnimalForm.UnderTransformation( from ) ) + { + from.SendLocalizedMessage( 1070902 ); // You can't use this while in an animal form! + } + else + { + EtherealMount.StopMounting( from ); + + Item one = from.FindItemOnLayer( Layer.OneHanded ); + Item two = from.FindItemOnLayer( Layer.TwoHanded ); + + if ( one != null ) + from.AddToBackpack( one ); + + if ( two != null ) + from.AddToBackpack( two ); + + from.Target = new BolaTarget( this ); + from.LocalOverheadMessage( MessageType.Emote, 0x3B2, 1049632 ); // * You begin to swing the bola...* + from.NonlocalOverheadMessage( MessageType.Emote, 0x3B2, 1049633, from.Name ); // ~1_NAME~ begins to menacingly swing a bola... + } + } + + private static void ReleaseBolaLock( object state ) + { + ((Mobile)state).EndAction( typeof( Bola ) ); + } + + private static void FinishThrow( object state ) + { + object[] states = (object[])state; + + Mobile from = (Mobile)states[0]; + Mobile to = (Mobile)states[1]; + + if ( Core.AOS ) + new Bola().MoveToWorld( to.Location, to.Map ); + + to.Damage( 1, from ); + + IMount mt = to.Mount; + + if ( mt != null ) + mt.Rider = null; + + to.SendLocalizedMessage( 1040023 ); // You have been knocked off of your mount! + + BaseMount.SetMountPrevention( to, BlockMountType.Dazed, TimeSpan.FromSeconds( 3.0 ) ); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( ReleaseBolaLock ), from ); + } + + private class BolaTarget : Target + { + private Bola m_Bola; + + public BolaTarget( Bola bola ) : base( 8, false, TargetFlags.Harmful ) + { + m_Bola = bola; + } + + protected override void OnTarget( Mobile from, object obj ) + { + if ( m_Bola.Deleted ) + return; + + if ( obj is Mobile ) + { + Mobile to = (Mobile)obj; + + if ( !m_Bola.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1040019 ); // The bola must be in your pack to use it. + } + else if ( !Core.AOS && (from.FindItemOnLayer( Layer.OneHanded ) != null || from.FindItemOnLayer( Layer.TwoHanded ) != null) ) + { + from.SendLocalizedMessage( 1040015 ); // Your hands must be free to use this + } + else if ( from.Mounted ) + { + from.SendLocalizedMessage( 1040016 ); // You cannot use this while riding a mount + } + else if ( Server.Spells.Ninjitsu.AnimalForm.UnderTransformation( from ) ) + { + from.SendLocalizedMessage( 1070902 ); // You can't use this while in an animal form! + } + else if ( !to.Mounted ) + { + from.SendLocalizedMessage( 1049628 ); // You have no reason to throw a bola at that. + } + else if ( !from.CanBeHarmful( to ) ) + { + } + else if ( from.BeginAction( typeof( Bola ) ) ) + { + EtherealMount.StopMounting( from ); + + Item one = from.FindItemOnLayer( Layer.OneHanded ); + Item two = from.FindItemOnLayer( Layer.TwoHanded ); + + if ( one != null ) + from.AddToBackpack( one ); + + if ( two != null ) + from.AddToBackpack( two ); + + from.DoHarmful( to ); + + if ( Core.AOS ) + BaseMount.SetMountPrevention( from, BlockMountType.BolaRecovery, TimeSpan.FromSeconds( 3.0 ) ); + + m_Bola.Consume(); + + from.Direction = from.GetDirectionTo( to ); + from.Animate( 11, 5, 1, true, false, 0 ); + from.MovingEffect( to, 0x26AC, 10, 0, false, false ); + + Timer.DelayCall( TimeSpan.FromSeconds( 0.5 ), new TimerStateCallback( FinishThrow ), new object[]{ from, to } ); + } + else + { + from.SendLocalizedMessage( 1049624 ); // You have to wait a few moments before you can use another bola! + } + } + else + { + from.SendLocalizedMessage( 1049629 ); // You cannot throw a bola at that. + } + } + } + + public Bola( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/BolaBall.cs b/Scripts/Items/Misc/BolaBall.cs new file mode 100644 index 000000000..ea416699e --- /dev/null +++ b/Scripts/Items/Misc/BolaBall.cs @@ -0,0 +1,44 @@ +using System; + +namespace Server.Items +{ + public class BolaBall : Item + { + [Constructable] + public BolaBall() : this( 1 ) + { + } + + [Constructable] + public BolaBall( int amount ) : base( 0xE73 ) + { + Weight = 4.0; + Stackable = true; + Amount = amount; + Hue = 0x8AC; + } + + public BolaBall( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 0 ) + Hue = 0x8AC; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/BulletinBoards.cs b/Scripts/Items/Misc/BulletinBoards.cs new file mode 100644 index 000000000..9e1061978 --- /dev/null +++ b/Scripts/Items/Misc/BulletinBoards.cs @@ -0,0 +1,614 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Server; +using Server.Network; + +namespace Server.Items +{ + [Flipable( 0x1E5E, 0x1E5F )] + public class BulletinBoard : BaseBulletinBoard + { + [Constructable] + public BulletinBoard() : base( 0x1E5E ) + { + } + + public BulletinBoard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public abstract class BaseBulletinBoard : Item + { + private string m_BoardName; + + [CommandProperty( AccessLevel.GameMaster )] + public string BoardName + { + get{ return m_BoardName; } + set{ m_BoardName = value; } + } + + public BaseBulletinBoard( int itemID ) : base( itemID ) + { + m_BoardName = "bulletin board"; + Movable = false; + } + + // Threads will be removed six hours after the last post was made + private static TimeSpan ThreadDeletionTime = TimeSpan.FromHours( 6.0 ); + + // A player may only create a thread once every two minutes + private static TimeSpan ThreadCreateTime = TimeSpan.FromMinutes( 2.0 ); + + // A player may only reply once every thirty seconds + private static TimeSpan ThreadReplyTime = TimeSpan.FromSeconds( 30.0 ); + + public static bool CheckTime( DateTime time, TimeSpan range ) + { + return (time + range) < DateTime.Now; + } + + public static string FormatTS( TimeSpan ts ) + { + int totalSeconds = (int)ts.TotalSeconds; + int seconds = totalSeconds % 60; + int minutes = totalSeconds / 60; + + if ( minutes != 0 && seconds != 0 ) + return String.Format( "{0} minute{1} and {2} second{3}", minutes, minutes==1?"":"s", seconds, seconds==1?"":"s" ); + else if ( minutes != 0 ) + return String.Format( "{0} minute{1}", minutes, minutes==1?"":"s" ); + else + return String.Format( "{0} second{1}", seconds, seconds==1?"":"s" ); + } + + public virtual void Cleanup() + { + List items = this.Items; + + for ( int i = items.Count - 1; i >= 0; --i ) + { + if ( i >= items.Count ) + continue; + + BulletinMessage msg = items[i] as BulletinMessage; + + if ( msg == null ) + continue; + + if ( msg.Thread == null && CheckTime( msg.LastPostTime, ThreadDeletionTime ) ) + { + msg.Delete(); + RecurseDelete( msg ); // A root-level thread has expired + } + } + } + + private void RecurseDelete( BulletinMessage msg ) + { + List found = new List(); + List items = this.Items; + + for ( int i = items.Count - 1; i >= 0; --i ) + { + if ( i >= items.Count ) + continue; + + BulletinMessage check = items[i] as BulletinMessage; + + if ( check == null ) + continue; + + if ( check.Thread == msg ) + { + check.Delete(); + found.Add( check ); + } + } + + for ( int i = 0; i < found.Count; ++i ) + RecurseDelete( (BulletinMessage)found[i] ); + } + + public virtual bool GetLastPostTime( Mobile poster, bool onlyCheckRoot, ref DateTime lastPostTime ) + { + List items = this.Items; + bool wasSet = false; + + for ( int i = 0; i < items.Count; ++i ) + { + BulletinMessage msg = items[i] as BulletinMessage; + + if ( msg == null || msg.Poster != poster ) + continue; + + if ( onlyCheckRoot && msg.Thread != null ) + continue; + + if ( msg.Time > lastPostTime ) + { + wasSet = true; + lastPostTime = msg.Time; + } + } + + return wasSet; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( CheckRange( from ) ) + { + Cleanup(); + from.Send( new BBDisplayBoard( this ) ); + from.Send( new ContainerContent( from, this ) ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + + public virtual bool CheckRange( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + return ( from.Map == this.Map && from.InRange( GetWorldLocation(), 2 ) ); + } + + public void PostMessage( Mobile from, BulletinMessage thread, string subject, string[] lines ) + { + if ( thread != null ) + thread.LastPostTime = DateTime.Now; + + AddItem( new BulletinMessage( from, thread, subject, lines ) ); + } + + public BaseBulletinBoard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (string) m_BoardName ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_BoardName = reader.ReadString(); + break; + } + } + } + + public static void Initialize() + { + PacketHandlers.Register( 0x71, 0, true, new OnPacketReceive( BBClientRequest ) ); + } + + public static void BBClientRequest( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + int packetID = pvSrc.ReadByte(); + BaseBulletinBoard board = World.FindItem( pvSrc.ReadInt32() ) as BaseBulletinBoard; + + if ( board == null || !board.CheckRange( from ) ) + return; + + switch ( packetID ) + { + case 3: BBRequestContent( from, board, pvSrc ); break; + case 4: BBRequestHeader( from, board, pvSrc ); break; + case 5: BBPostMessage( from, board, pvSrc ); break; + case 6: BBRemoveMessage( from, board, pvSrc ); break; + } + } + + public static void BBRequestContent( Mobile from, BaseBulletinBoard board, PacketReader pvSrc ) + { + BulletinMessage msg = World.FindItem( pvSrc.ReadInt32() ) as BulletinMessage; + + if ( msg == null || msg.Parent != board ) + return; + + from.Send( new BBMessageContent( board, msg ) ); + } + + public static void BBRequestHeader( Mobile from, BaseBulletinBoard board, PacketReader pvSrc ) + { + BulletinMessage msg = World.FindItem( pvSrc.ReadInt32() ) as BulletinMessage; + + if ( msg == null || msg.Parent != board ) + return; + + from.Send( new BBMessageHeader( board, msg ) ); + } + + public static void BBPostMessage( Mobile from, BaseBulletinBoard board, PacketReader pvSrc ) + { + BulletinMessage thread = World.FindItem( pvSrc.ReadInt32() ) as BulletinMessage; + + if ( thread != null && thread.Parent != board ) + thread = null; + + int breakout = 0; + + while ( thread != null && thread.Thread != null && breakout++ < 10 ) + thread = thread.Thread; + + DateTime lastPostTime = DateTime.MinValue; + + if ( board.GetLastPostTime( from, ( thread == null ), ref lastPostTime ) ) + { + if ( !CheckTime( lastPostTime, (thread == null ? ThreadCreateTime : ThreadReplyTime) ) ) + { + if ( thread == null ) + from.SendMessage( "You must wait {0} before creating a new thread.", FormatTS( ThreadCreateTime ) ); + else + from.SendMessage( "You must wait {0} before replying to another thread.", FormatTS( ThreadReplyTime ) ); + + return; + } + } + + string subject = pvSrc.ReadUTF8StringSafe( pvSrc.ReadByte() ); + + if ( subject.Length == 0 ) + return; + + string[] lines = new string[pvSrc.ReadByte()]; + + if ( lines.Length == 0 ) + return; + + for ( int i = 0; i < lines.Length; ++i ) + lines[i] = pvSrc.ReadUTF8StringSafe( pvSrc.ReadByte() ); + + board.PostMessage( from, thread, subject, lines ); + } + + public static void BBRemoveMessage( Mobile from, BaseBulletinBoard board, PacketReader pvSrc ) + { + BulletinMessage msg = World.FindItem( pvSrc.ReadInt32() ) as BulletinMessage; + + if ( msg == null || msg.Parent != board ) + return; + + if ( from.AccessLevel < AccessLevel.GameMaster && msg.Poster != from ) + return; + + msg.Delete(); + } + } + + public struct BulletinEquip + { + public int itemID; + public int hue; + + public BulletinEquip( int itemID, int hue ) + { + this.itemID = itemID; + this.hue = hue; + } + } + + public class BulletinMessage : Item + { + private Mobile m_Poster; + private string m_Subject; + private DateTime m_Time, m_LastPostTime; + private BulletinMessage m_Thread; + private string m_PostedName; + private int m_PostedBody; + private int m_PostedHue; + private BulletinEquip[] m_PostedEquip; + private string[] m_Lines; + + public string GetTimeAsString() + { + return m_Time.ToString( "MMM dd, yyyy" ); + } + + public override bool CheckTarget( Mobile from, Server.Targeting.Target targ, object targeted ) + { + return false; + } + + public override bool IsAccessibleTo( Mobile check ) + { + return false; + } + + public BulletinMessage( Mobile poster, BulletinMessage thread, string subject, string[] lines ) : base( 0xEB0 ) + { + Movable = false; + + m_Poster = poster; + m_Subject = subject; + m_Time = DateTime.Now; + m_LastPostTime = m_Time; + m_Thread = thread; + m_PostedName = m_Poster.Name; + m_PostedBody = m_Poster.Body; + m_PostedHue = m_Poster.Hue; + m_Lines = lines; + + ArrayList list = new ArrayList(); + + for ( int i = 0; i < poster.Items.Count; ++i ) + { + Item item = poster.Items[i]; + + if ( item.Layer >= Layer.OneHanded && item.Layer <= Layer.Mount ) + list.Add( new BulletinEquip( item.ItemID, item.Hue ) ); + } + + m_PostedEquip = (BulletinEquip[])list.ToArray( typeof( BulletinEquip ) ); + } + + public Mobile Poster{ get{ return m_Poster; } } + public BulletinMessage Thread{ get{ return m_Thread; } } + public string Subject{ get{ return m_Subject; } } + public DateTime Time{ get{ return m_Time; } } + public DateTime LastPostTime{ get{ return m_LastPostTime; } set{ m_LastPostTime = value; } } + public string PostedName{ get{ return m_PostedName; } } + public int PostedBody{ get{ return m_PostedBody; } } + public int PostedHue{ get{ return m_PostedHue; } } + public BulletinEquip[] PostedEquip{ get{ return m_PostedEquip; } } + public string[] Lines{ get{ return m_Lines; } } + + public BulletinMessage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Poster ); + writer.Write( (string) m_Subject ); + writer.Write( (DateTime) m_Time ); + writer.Write( (DateTime) m_LastPostTime ); + writer.Write( (bool) (m_Thread != null) ); + writer.Write( (Item) m_Thread ); + writer.Write( (string) m_PostedName ); + writer.Write( (int) m_PostedBody ); + writer.Write( (int) m_PostedHue ); + + writer.Write( (int) m_PostedEquip.Length ); + + for ( int i = 0; i < m_PostedEquip.Length; ++i ) + { + writer.Write( (int) m_PostedEquip[i].itemID ); + writer.Write( (int) m_PostedEquip[i].hue ); + } + + writer.Write( (int) m_Lines.Length ); + + for ( int i = 0; i < m_Lines.Length; ++i ) + writer.Write( (string) m_Lines[i] ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Poster = reader.ReadMobile(); + m_Subject = reader.ReadString(); + m_Time = reader.ReadDateTime(); + m_LastPostTime = reader.ReadDateTime(); + bool hasThread = reader.ReadBool(); + m_Thread = reader.ReadItem() as BulletinMessage; + m_PostedName = reader.ReadString(); + m_PostedBody = reader.ReadInt(); + m_PostedHue = reader.ReadInt(); + + m_PostedEquip = new BulletinEquip[reader.ReadInt()]; + + for ( int i = 0; i < m_PostedEquip.Length; ++i ) + { + m_PostedEquip[i].itemID = reader.ReadInt(); + m_PostedEquip[i].hue = reader.ReadInt(); + } + + m_Lines = new string[reader.ReadInt()]; + + for ( int i = 0; i < m_Lines.Length; ++i ) + m_Lines[i] = reader.ReadString(); + + if ( hasThread && m_Thread == null ) + Delete(); + + break; + } + } + } + } + + public class BBDisplayBoard : Packet + { + public BBDisplayBoard( BaseBulletinBoard board ) : base( 0x71 ) + { + string name = board.BoardName; + + if ( name == null ) + name = ""; + + EnsureCapacity( 38 ); + + byte[] buffer = Utility.UTF8.GetBytes( name ); + + m_Stream.Write( (byte) 0x00 ); // PacketID + m_Stream.Write( (int) board.Serial ); // Bulletin board serial + + // Bulletin board name + if ( buffer.Length >= 29 ) + { + m_Stream.Write( buffer, 0, 29 ); + m_Stream.Write( (byte) 0 ); + } + else + { + m_Stream.Write( buffer, 0, buffer.Length ); + m_Stream.Fill( 30 - buffer.Length ); + } + } + } + + public class BBMessageHeader : Packet + { + public BBMessageHeader( BaseBulletinBoard board, BulletinMessage msg ) : base( 0x71 ) + { + string poster = SafeString( msg.PostedName ); + string subject = SafeString( msg.Subject ); + string time = SafeString( msg.GetTimeAsString() ); + + EnsureCapacity( 22 + poster.Length + subject.Length + time.Length ); + + m_Stream.Write( (byte) 0x01 ); // PacketID + m_Stream.Write( (int) board.Serial ); // Bulletin board serial + m_Stream.Write( (int) msg.Serial ); // Message serial + + BulletinMessage thread = msg.Thread; + + if ( thread == null ) + m_Stream.Write( (int) 0 ); // Thread serial--root + else + m_Stream.Write( (int) thread.Serial ); // Thread serial--parent + + WriteString( poster ); + WriteString( subject ); + WriteString( time ); + } + + public void WriteString( string v ) + { + byte[] buffer = Utility.UTF8.GetBytes( v ); + int len = buffer.Length + 1; + + if ( len > 255 ) + len = 255; + + m_Stream.Write( (byte) len ); + m_Stream.Write( buffer, 0, len-1 ); + m_Stream.Write( (byte) 0 ); + } + + public string SafeString( string v ) + { + if ( v == null ) + return String.Empty; + + return v; + } + } + + public class BBMessageContent : Packet + { + public BBMessageContent( BaseBulletinBoard board, BulletinMessage msg ) : base( 0x71 ) + { + string poster = SafeString( msg.PostedName ); + string subject = SafeString( msg.Subject ); + string time = SafeString( msg.GetTimeAsString() ); + + EnsureCapacity( 22 + poster.Length + subject.Length + time.Length ); + + m_Stream.Write( (byte) 0x02 ); // PacketID + m_Stream.Write( (int) board.Serial ); // Bulletin board serial + m_Stream.Write( (int) msg.Serial ); // Message serial + + WriteString( poster ); + WriteString( subject ); + WriteString( time ); + + m_Stream.Write( (short) msg.PostedBody ); + m_Stream.Write( (short) msg.PostedHue ); + + int len = msg.PostedEquip.Length; + + if ( len > 255 ) + len = 255; + + m_Stream.Write( (byte) len ); + + for ( int i = 0; i < len; ++i ) + { + BulletinEquip eq = msg.PostedEquip[i]; + + m_Stream.Write( (short) eq.itemID ); + m_Stream.Write( (short) eq.hue ); + } + + len = msg.Lines.Length; + + if ( len > 255 ) + len = 255; + + m_Stream.Write( (byte) len ); + + for ( int i = 0; i < len; ++i ) + WriteString( msg.Lines[i] ); + } + + public void WriteString( string v ) + { + byte[] buffer = Utility.UTF8.GetBytes( v ); + int len = buffer.Length + 1; + + if ( len > 255 ) + len = 255; + + m_Stream.Write( (byte) len ); + m_Stream.Write( buffer, 0, len-1 ); + m_Stream.Write( (byte) 0 ); + } + + public string SafeString( string v ) + { + if ( v == null ) + return String.Empty; + + return v; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/ClockworkAssembly.cs b/Scripts/Items/Misc/ClockworkAssembly.cs new file mode 100644 index 000000000..c1d9373bb --- /dev/null +++ b/Scripts/Items/Misc/ClockworkAssembly.cs @@ -0,0 +1,134 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Spells; + +namespace Server.Items +{ + public class ClockworkAssembly : Item + { + public override string DefaultName + { + get { return "clockwork assembly"; } + } + + [Constructable] + public ClockworkAssembly() : base( 0x1EA8 ) + { + Weight = 5.0; + Hue = 1102; + } + + public ClockworkAssembly( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + return; + } + + double tinkerSkill = from.Skills[SkillName.Tinkering].Value; + + if ( tinkerSkill < 60.0 ) + { + from.SendMessage( "You must have at least 60.0 skill in tinkering to construct a golem." ); + return; + } + else if ( (from.Followers + 4) > from.FollowersMax ) + { + from.SendLocalizedMessage( 1049607 ); // You have too many followers to control that creature. + return; + } + + double scalar; + + if ( tinkerSkill >= 100.0 ) + scalar = 1.0; + else if ( tinkerSkill >= 90.0 ) + scalar = 0.9; + else if ( tinkerSkill >= 80.0 ) + scalar = 0.8; + else if ( tinkerSkill >= 70.0 ) + scalar = 0.7; + else + scalar = 0.6; + + Container pack = from.Backpack; + + if ( pack == null ) + return; + + int res = pack.ConsumeTotal( + new Type[] + { + typeof( PowerCrystal ), + typeof( IronIngot ), + typeof( BronzeIngot ), + typeof( Gears ) + }, + new int[] + { + 1, + 50, + 50, + 5 + } ); + + switch ( res ) + { + case 0: + { + from.SendMessage( "You must have a power crystal to construct the golem." ); + break; + } + case 1: + { + from.SendMessage( "You must have 50 iron ingots to construct the golem." ); + break; + } + case 2: + { + from.SendMessage( "You must have 50 bronze ingots to construct the golem." ); + break; + } + case 3: + { + from.SendMessage( "You must have 5 gears to construct the golem." ); + break; + } + default: + { + Golem g = new Golem( true, scalar ); + + if ( g.SetControlMaster( from ) ) + { + Delete(); + + g.MoveToWorld( from.Location, from.Map ); + from.PlaySound( 0x241 ); + } + + break; + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/CommunicationCrystals.cs b/Scripts/Items/Misc/CommunicationCrystals.cs new file mode 100644 index 000000000..109a8d218 --- /dev/null +++ b/Scripts/Items/Misc/CommunicationCrystals.cs @@ -0,0 +1,483 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class CrystalRechargeInfo + { + public static readonly CrystalRechargeInfo[] Table = new CrystalRechargeInfo[] + { + new CrystalRechargeInfo( typeof( Citrine ), 500 ), + new CrystalRechargeInfo( typeof( Amber ), 500 ), + new CrystalRechargeInfo( typeof( Tourmaline ), 750 ), + new CrystalRechargeInfo( typeof( Emerald ), 1000 ), + new CrystalRechargeInfo( typeof( Sapphire ), 1000 ), + new CrystalRechargeInfo( typeof( Amethyst ), 1000 ), + new CrystalRechargeInfo( typeof( StarSapphire ), 1250 ), + new CrystalRechargeInfo( typeof( Diamond ), 2000 ) + }; + + public static CrystalRechargeInfo Get( Type type ) + { + foreach ( CrystalRechargeInfo info in Table ) + { + if ( info.Type == type ) + return info; + } + + return null; + } + + private Type m_Type; + private int m_Amount; + + public Type Type{ get{ return m_Type; } } + public int Amount{ get{ return m_Amount; } } + + private CrystalRechargeInfo( Type type, int amount ) + { + m_Type = type; + m_Amount = amount; + } + } + + public class BroadcastCrystal : Item + { + public static readonly int MaxCharges = 2000; + + public override int LabelNumber{ get{ return 1060740; } } // communication crystal + + private int m_Charges; + private ArrayList m_Receivers; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get{ return this.ItemID == 0x1ECD; } + set + { + this.ItemID = value ? 0x1ECD : 0x1ED0; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set + { + m_Charges = value; + InvalidateProperties(); + } + } + + public ArrayList Receivers + { + get{ return m_Receivers; } + } + + [Constructable] + public BroadcastCrystal() : this( 2000 ) + { + } + + [Constructable] + public BroadcastCrystal( int charges ) : base( 0x1ED0 ) + { + Light = LightType.Circle150; + + m_Charges = charges; + + m_Receivers = new ArrayList(); + } + + public BroadcastCrystal( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( this.Active ? 1060742 : 1060743 ); // active / inactive + list.Add( 1060745 ); // broadcast + list.Add( 1060741, this.Charges.ToString() ); // charges: ~1_val~ + + if ( Receivers.Count > 0 ) + list.Add( 1060746, Receivers.Count.ToString() ); // links: ~1_val~ + } + + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick( from ); + + LabelTo( from, this.Active ? 1060742 : 1060743 ); // active / inactive + LabelTo( from, 1060745 ); // broadcast + LabelTo( from, 1060741, this.Charges.ToString() ); // charges: ~1_val~ + + if ( Receivers.Count > 0 ) + LabelTo( from, 1060746, Receivers.Count.ToString() ); // links: ~1_val~ + } + + public override bool HandlesOnSpeech + { + get{ return Active && Receivers.Count > 0 && ( RootParent == null || RootParent is Mobile ); } + } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( !Active || Receivers.Count == 0 || ( RootParent != null && !(RootParent is Mobile) ) ) + return; + + if ( e.Type == MessageType.Emote ) + return; + + Mobile from = e.Mobile; + string speech = e.Speech; + + foreach ( ReceiverCrystal receiver in new ArrayList( Receivers ) ) + { + if ( receiver.Deleted ) + { + Receivers.Remove( receiver ); + } + else if ( Charges > 0 ) + { + receiver.TransmitMessage( from, speech ); + Charges--; + } + else + { + this.Active = false; + break; + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private BroadcastCrystal m_Crystal; + + public InternalTarget( BroadcastCrystal crystal ) : base( 2, false, TargetFlags.None ) + { + m_Crystal = crystal; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Crystal.IsAccessibleTo( from ) ) + return; + + if ( from.Map != m_Crystal.Map || !from.InRange( m_Crystal.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + if ( targeted == m_Crystal ) + { + if ( m_Crystal.Active ) + { + m_Crystal.Active = false; + from.SendLocalizedMessage( 500672 ); // You turn the crystal off. + } + else + { + if ( m_Crystal.Charges > 0 ) + { + m_Crystal.Active = true; + from.SendLocalizedMessage( 500673 ); // You turn the crystal on. + } + else + { + from.SendLocalizedMessage( 500676 ); // This crystal is out of charges. + } + } + } + else if ( targeted is ReceiverCrystal ) + { + ReceiverCrystal receiver = (ReceiverCrystal) targeted; + + if ( m_Crystal.Receivers.Count >= 10 ) + { + from.SendLocalizedMessage( 1010042 ); // This broadcast crystal is already linked to 10 receivers. + } + else if ( receiver.Sender == m_Crystal ) + { + from.SendLocalizedMessage( 500674 ); // This crystal is already linked with that crystal. + } + else if ( receiver.Sender != null ) + { + from.SendLocalizedMessage( 1010043 ); // That receiver crystal is already linked to another broadcast crystal. + } + else + { + receiver.Sender = m_Crystal; + from.SendLocalizedMessage( 500675 ); // That crystal has been linked to this crystal. + } + } + else if ( targeted == from ) + { + foreach ( ReceiverCrystal receiver in new ArrayList( m_Crystal.Receivers ) ) + { + receiver.Sender = null; + } + + from.SendLocalizedMessage( 1010046 ); // You unlink the broadcast crystal from all of its receivers. + } + else + { + Item targItem = targeted as Item; + + if ( targItem != null && targItem.VerifyMove( from ) ) + { + CrystalRechargeInfo info = CrystalRechargeInfo.Get( targItem.GetType() ); + + if ( info != null ) + { + if ( m_Crystal.Charges >= MaxCharges ) + { + from.SendLocalizedMessage( 500678 ); // This crystal is already fully charged. + } + else + { + targItem.Consume(); + + if ( m_Crystal.Charges + info.Amount >= MaxCharges ) + { + m_Crystal.Charges = MaxCharges; + from.SendLocalizedMessage( 500679 ); // You completely recharge the crystal. + } + else + { + m_Crystal.Charges += info.Amount; + from.SendLocalizedMessage( 500680 ); // You recharge the crystal. + } + } + + return; + } + } + + from.SendLocalizedMessage( 500681 ); // You cannot use this crystal on that. + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.WriteEncodedInt( m_Charges ); + writer.WriteItemList( m_Receivers ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Charges = reader.ReadEncodedInt(); + m_Receivers = reader.ReadItemList(); + } + } + + public class ReceiverCrystal : Item + { + public override int LabelNumber{ get{ return 1060740; } } // communication crystal + + private BroadcastCrystal m_Sender; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get{ return this.ItemID == 0x1ED1; } + set + { + this.ItemID = value ? 0x1ED1 : 0x1ED0; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BroadcastCrystal Sender + { + get{ return m_Sender; } + set + { + if ( m_Sender != null ) + { + m_Sender.Receivers.Remove( this ); + m_Sender.InvalidateProperties(); + } + + m_Sender = value; + + if ( value != null ) + { + value.Receivers.Add( this ); + value.InvalidateProperties(); + } + } + } + + [Constructable] + public ReceiverCrystal() : base( 0x1ED0 ) + { + Light = LightType.Circle150; + } + + public ReceiverCrystal( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( this.Active ? 1060742 : 1060743 ); // active / inactive + list.Add( 1060744 ); // receiver + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, this.Active ? 1060742 : 1060743 ); // active / inactive + LabelTo( from, 1060744 ); // receiver + } + + public void TransmitMessage( Mobile from, string message ) + { + if ( !this.Active ) + return; + + string text = String.Format( "{0} says {1}", from.Name, message ); + + if ( this.RootParent is Mobile ) + { + ((Mobile)this.RootParent).SendMessage( 0x2B2, "Crystal: " + text ); + } + else if ( this.RootParent is Item ) + { + ((Item)this.RootParent).PublicOverheadMessage( MessageType.Regular, 0x2B2, false, "Crystal: " + text ); + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x2B2, false, text ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private ReceiverCrystal m_Crystal; + + public InternalTarget( ReceiverCrystal crystal ) : base( -1, false, TargetFlags.None ) + { + m_Crystal = crystal; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !m_Crystal.IsAccessibleTo( from ) ) + return; + + if ( from.Map != m_Crystal.Map || !from.InRange( m_Crystal.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + if ( targeted == m_Crystal ) + { + if ( m_Crystal.Active ) + { + m_Crystal.Active = false; + from.SendLocalizedMessage( 500672 ); // You turn the crystal off. + } + else + { + m_Crystal.Active = true; + from.SendLocalizedMessage( 500673 ); // You turn the crystal on. + } + } + else if ( targeted == from ) + { + if ( m_Crystal.Sender != null ) + { + m_Crystal.Sender = null; + from.SendLocalizedMessage( 1010044 ); // You unlink the receiver crystal. + } + else + { + from.SendLocalizedMessage( 1010045 ); // That receiver crystal is not linked. + } + } + else + { + Item targItem = targeted as Item; + + if ( targItem != null && targItem.VerifyMove( from ) ) + { + CrystalRechargeInfo info = CrystalRechargeInfo.Get( targItem.GetType() ); + + if ( info != null ) + { + from.SendLocalizedMessage( 500677 ); // This crystal cannot be recharged. + return; + } + } + + from.SendLocalizedMessage( 1010045 ); // That receiver crystal is not linked. + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (Item) m_Sender ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Sender = (BroadcastCrystal) reader.ReadItem(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Corpses/Corpse.cs b/Scripts/Items/Misc/Corpses/Corpse.cs new file mode 100644 index 000000000..a6fe3da24 --- /dev/null +++ b/Scripts/Items/Misc/Corpses/Corpse.cs @@ -0,0 +1,965 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.ContextMenus; +using Server.Engines.PartySystem; +using Server.Engines.Quests; +using Server.Engines.Quests.Doom; +using Server.Engines.Quests.Haven; +using Server.Guilds; +using Server.Misc; +using Server.Mobiles; +using Server.Network; + +namespace Server.Items +{ + public class Corpse : Container, ICarvable + { + private Mobile m_Owner; // Whos corpse is this? + private Mobile m_Killer; // Who killed the owner? + private bool m_Carved; // Has this corpse been carved? + + private List m_Looters; // Who's looted this corpse? + private List m_EquipItems; // List of items equiped when the owner died. Ingame, these items display /on/ the corpse, not just inside + private List m_Aggressors; // Anyone from this list will be able to loot this corpse; we attacked them, or they attacked us when we were freely attackable + + private string m_CorpseName; // Value of the CorpseNameAttribute attached to the owner when he died -or- null if the owner had no CorpseNameAttribute; use "the remains of ~name~" + private bool m_NoBones; // If true, this corpse will not turn into bones + + private bool m_VisitedByTaxidermist; // Has this corpse yet been visited by a taxidermist? + private bool m_Channeled; // Has this corpse yet been used to channel spiritual energy? (AOS Spirit Speak) + + // For notoriety: + private AccessLevel m_AccessLevel; // Which AccessLevel the owner had when he died + private Guild m_Guild; // Which Guild the owner was in when he died + private int m_Kills; // How many kills the owner had when he died + private bool m_Criminal; // Was the owner criminal when he died? + + private DateTime m_TimeOfDeath; // What time was this corpse created? + + private HairInfo m_Hair; // This contains the hair of the owner + private FacialHairInfo m_FacialHair; // This contains the facial hair of the owner + + public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes( 2.0 ); + + public override bool IsDecoContainer + { + get{ return false; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime TimeOfDeath + { + get{ return m_TimeOfDeath; } + set{ m_TimeOfDeath = value; } + } + + public HairInfo Hair { get { return m_Hair; } } + public FacialHairInfo FacialHair { get { return m_FacialHair; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Carved + { + get{ return m_Carved; } + set{ m_Carved = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool VisitedByTaxidermist + { + get{ return m_VisitedByTaxidermist; } + set{ m_VisitedByTaxidermist = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Channeled + { + get{ return m_Channeled; } + set{ m_Channeled = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public AccessLevel AccessLevel + { + get{ return m_AccessLevel; } + } + + public List Aggressors + { + get{ return m_Aggressors; } + } + + public List Looters + { + get{ return m_Looters; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Killer + { + get{ return m_Killer; } + } + + public List EquipItems + { + get{ return m_EquipItems; } + } + + public Guild Guild + { + get{ return m_Guild; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Kills + { + get{ return m_Kills; } + set{ m_Kills = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Criminal + { + get{ return m_Criminal; } + set{ m_Criminal = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get{ return m_Owner; } + } + + public void TurnToBones() + { + if ( Deleted ) + return; + + ProcessDelta(); + SendRemovePacket(); + ItemID = Utility.Random( 0xECA, 9 ); // bone graphic + Hue = 0; + ProcessDelta(); + + m_NoBones = true; + BeginDecay( m_BoneDecayTime ); + + /*DecayedCorpse c = new DecayedCorpse( Name ); + + c.MoveToWorld( Location, Map ); + + ArrayList list = Items; + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i < list.Count ) + c.AddItem( (Item)list[i] ); + } + + Delete();*/ + } + + private static TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes( 7.0 ); + private static TimeSpan m_BoneDecayTime = TimeSpan.FromMinutes( 7.0 ); + + private Timer m_DecayTimer; + private DateTime m_DecayTime; + + public void BeginDecay( TimeSpan delay ) + { + if ( m_DecayTimer != null ) + m_DecayTimer.Stop(); + + m_DecayTime = DateTime.Now + delay; + + m_DecayTimer = new InternalTimer( this, delay ); + m_DecayTimer.Start(); + } + + public override void OnAfterDelete() + { + if ( m_DecayTimer != null ) + m_DecayTimer.Stop(); + + m_DecayTimer = null; + } + + private class InternalTimer : Timer + { + private Corpse m_Corpse; + + public InternalTimer( Corpse c, TimeSpan delay ) : base( delay ) + { + m_Corpse = c; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + if ( !m_Corpse.m_NoBones ) + m_Corpse.TurnToBones(); + else + m_Corpse.Delete(); + } + } + + public static string GetCorpseName( Mobile m ) + { + Type t = m.GetType(); + + object[] attrs = t.GetCustomAttributes( typeof( CorpseNameAttribute ), true ); + + if ( attrs != null && attrs.Length > 0 ) + { + CorpseNameAttribute attr = attrs[0] as CorpseNameAttribute; + + if ( attr != null ) + return attr.Name; + } + + return null; + } + + public static void Initialize() + { + Mobile.CreateCorpseHandler += new CreateCorpseHandler( Mobile_CreateCorpseHandler ); + } + + public static Container Mobile_CreateCorpseHandler( Mobile owner, HairInfo hair, FacialHairInfo facialhair, List initialContent, List equipItems ) + { + bool shouldFillCorpse = true; + + //if ( owner is BaseCreature ) + // shouldFillCorpse = !((BaseCreature)owner).IsBonded; + + Corpse c; + if( owner is MilitiaFighter ) + c = new MilitiaFighterCorpse( owner, hair, facialhair, shouldFillCorpse ? equipItems : new List() ); + else + c = new Corpse( owner, hair, facialhair, shouldFillCorpse ? equipItems : new List() ); + + owner.Corpse = c; + + if ( shouldFillCorpse ) + { + for ( int i = 0; i < initialContent.Count; ++i ) + { + Item item = initialContent[i]; + + if ( Core.AOS && owner.Player && item.Parent == owner.Backpack ) + c.AddItem( item ); + else + c.DropItem( item ); + + if ( owner.Player && Core.AOS ) + c.SetRestoreInfo( item, item.Location ); + } + } + else + { + c.Carved = true; // TODO: Is it needed? + } + + Point3D loc = owner.Location; + Map map = owner.Map; + + if ( map == null || map == Map.Internal ) + { + loc = owner.LogoutLocation; + map = owner.LogoutMap; + } + + c.MoveToWorld( loc, map ); + + return c; + } + + public override bool IsPublicContainer{ get{ return true; } } + + public Corpse( Mobile owner, List equipItems ) : this( owner, null, null, equipItems ) + { + } + public Corpse( Mobile owner, HairInfo hair, FacialHairInfo facialhair, List equipItems ) + : base( 0x2006 ) + { + // To supress console warnings, stackable must be true + Stackable = true; + Amount = owner.Body; // protocol defines that for itemid 0x2006, amount=body + Stackable = false; + + Movable = false; + Hue = owner.Hue; + Direction = owner.Direction; + Name = owner.Name; + + m_Owner = owner; + + m_CorpseName = GetCorpseName( owner ); + + m_TimeOfDeath = DateTime.Now; + + m_AccessLevel = owner.AccessLevel; + m_Guild = owner.Guild as Guild; + m_Kills = owner.Kills; + m_Criminal = owner.Criminal; + + m_Hair = hair; + m_FacialHair = facialhair; + + +#if false + // This corpse does not turn to bones if: + // (the owner is not a player) and (the owner doesn't have a human body) + m_NoBones = !owner.Player && !owner.Body.IsHuman; +#else + // This corpse does not turn to bones if: + // (the owner is not a player) + m_NoBones = !owner.Player; +#endif + + m_Looters = new List(); + m_EquipItems = equipItems; + + m_Aggressors = new List( owner.Aggressors.Count + owner.Aggressed.Count ); + bool addToAggressors = !( owner is BaseCreature ); + + TimeSpan lastTime = TimeSpan.MaxValue; + + for ( int i = 0; i < owner.Aggressors.Count; ++i ) + { + AggressorInfo info = owner.Aggressors[i]; + + if ( (DateTime.Now - info.LastCombatTime) < lastTime ) + { + m_Killer = info.Attacker; + lastTime = (DateTime.Now - info.LastCombatTime); + } + + if ( addToAggressors && !info.CriminalAggression ) + m_Aggressors.Add( info.Attacker ); + } + + for ( int i = 0; i < owner.Aggressed.Count; ++i ) + { + AggressorInfo info = owner.Aggressed[i]; + + if ( (DateTime.Now - info.LastCombatTime) < lastTime ) + { + m_Killer = info.Defender; + lastTime = (DateTime.Now - info.LastCombatTime); + } + + if ( addToAggressors ) + m_Aggressors.Add( info.Defender ); + } + + if ( !addToAggressors ) + { + BaseCreature bc = (BaseCreature)owner; + + Mobile master = bc.GetMaster(); + if( master != null ) + m_Aggressors.Add( master ); + + List rights = BaseCreature.GetLootingRights( bc.DamageEntries, bc.HitsMax ); + for ( int i = 0; i < rights.Count; ++i ) + { + DamageStore ds = rights[i]; + + if ( ds.m_HasRight ) + m_Aggressors.Add( ds.m_Mobile ); + } + } + + BeginDecay( m_DefaultDecayTime ); + } + + public Corpse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 10 ); // version + + writer.WriteDeltaTime( m_TimeOfDeath ); + + ArrayList list = ( m_RestoreTable == null ? null : new ArrayList( m_RestoreTable ) ); + int count = ( list == null ? 0 : list.Count ); + + writer.Write( count ); + + for ( int i = 0; list != null && i < list.Count; ++i ) + { + DictionaryEntry de = (DictionaryEntry)list[i]; + Item item = (Item)de.Key; + Point3D loc = (Point3D)de.Value; + + writer.Write( item ); + + if ( item.Location == loc ) + { + writer.Write( false ); + } + else + { + writer.Write( true ); + writer.Write( loc ); + } + } + + writer.Write( m_VisitedByTaxidermist ); + + writer.Write( m_DecayTimer != null ); + + if ( m_DecayTimer != null ) + writer.WriteDeltaTime( m_DecayTime ); + + writer.Write( m_Looters ); + writer.Write( m_Killer ); + + writer.Write( (bool) m_Carved ); + + writer.Write( m_Aggressors ); + + writer.Write( m_Owner ); + + writer.Write( m_NoBones ); + + writer.Write( (string) m_CorpseName ); + + writer.Write( (int) m_AccessLevel ); + writer.Write( (Guild) m_Guild ); + writer.Write( (int) m_Kills ); + writer.Write( (bool) m_Criminal ); + + writer.Write( m_EquipItems ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 10: + { + m_TimeOfDeath = reader.ReadDeltaTime(); + + goto case 9; + } + case 9: + { + int count = reader.ReadInt(); + + for ( int i = 0; i < count; ++i ) + { + Item item = reader.ReadItem(); + + if ( reader.ReadBool() ) + SetRestoreInfo( item, reader.ReadPoint3D() ); + else if ( item != null ) + SetRestoreInfo( item, item.Location ); + } + + goto case 8; + } + case 8: + { + m_VisitedByTaxidermist = reader.ReadBool(); + + goto case 7; + } + case 7: + { + if ( reader.ReadBool() ) + BeginDecay( reader.ReadDeltaTime() - DateTime.Now ); + + goto case 6; + } + case 6: + { + m_Looters = reader.ReadStrongMobileList(); + m_Killer = reader.ReadMobile(); + + goto case 5; + } + case 5: + { + m_Carved = reader.ReadBool(); + + goto case 4; + } + case 4: + { + m_Aggressors = reader.ReadStrongMobileList(); + + goto case 3; + } + case 3: + { + m_Owner = reader.ReadMobile(); + + goto case 2; + } + case 2: + { + m_NoBones = reader.ReadBool(); + + goto case 1; + } + case 1: + { + m_CorpseName = reader.ReadString(); + + goto case 0; + } + case 0: + { + if ( version < 10 ) + m_TimeOfDeath = DateTime.Now; + + if ( version < 7 ) + BeginDecay( m_DefaultDecayTime ); + + if ( version < 6 ) + m_Looters = new List(); + + if ( version < 4 ) + m_Aggressors = new List(); + + m_AccessLevel = (AccessLevel)reader.ReadInt(); + reader.ReadInt(); // guild reserve + m_Kills = reader.ReadInt(); + m_Criminal = reader.ReadBool(); + + m_EquipItems = reader.ReadStrongItemList(); + + break; + } + } + } + + public override void SendInfoTo( NetState state ) + { + base.SendInfoTo( state ); + + if ( ItemID == 0x2006 ) + { + state.Send( new CorpseContent( state.Mobile, this ) ); + state.Send( new CorpseEquip( state.Mobile, this ) ); + } + } + + public bool IsCriminalAction( Mobile from ) + { + if ( from == m_Owner || from.AccessLevel >= AccessLevel.GameMaster ) + return false; + + Party p = Party.Get( m_Owner ); + + if ( p != null && p.Contains( from ) ) + { + PartyMemberInfo pmi = p[m_Owner]; + + if ( pmi != null && pmi.CanLoot ) + return false; + } + + return ( NotorietyHandlers.CorpseNotoriety( from, this ) == Notoriety.Innocent ); + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + if ( !base.CheckItemUse( from, item ) ) + return false; + + if ( item != this ) + return CanLoot( from ); + + return true; + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( !base.CheckLift( from, item, ref reject ) ) + return false; + + return CanLoot( from ); + } + + public override void OnItemUsed( Mobile from, Item item ) + { + base.OnItemUsed( from, item ); + + if ( from != m_Owner ) + from.RevealingAction(); + + if ( item != this && IsCriminalAction( from ) ) + from.CriminalAction( true ); + + if ( !m_Looters.Contains( from ) ) + m_Looters.Add( from ); + } + + public override void OnItemLifted( Mobile from, Item item ) + { + base.OnItemLifted( from, item ); + + if ( item != this && from != m_Owner ) + from.RevealingAction(); + + if ( item != this && IsCriminalAction( from ) ) + from.CriminalAction( true ); + + if ( !m_Looters.Contains( from ) ) + m_Looters.Add( from ); + } + + private class OpenCorpseEntry : ContextMenuEntry + { + public OpenCorpseEntry() : base( 6215, 2 ) + { + } + + public override void OnClick() + { + Corpse corpse = Owner.Target as Corpse; + + if ( corpse != null && Owner.From.CheckAlive() ) + corpse.Open( Owner.From, false ); + } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( Core.AOS && m_Owner == from && from.Alive ) + list.Add( new OpenCorpseEntry() ); + } + + private Hashtable m_RestoreTable; + + public bool GetRestoreInfo( Item item, ref Point3D loc ) + { + if ( m_RestoreTable == null || item == null ) + return false; + + object obj = m_RestoreTable[item]; + + if ( obj == null ) + return false; + + loc = (Point3D)obj; + return true; + } + + public void SetRestoreInfo( Item item, Point3D loc ) + { + if ( item == null ) + return; + + if ( m_RestoreTable == null ) + m_RestoreTable = new Hashtable(); + + m_RestoreTable[item] = loc; + } + + public void ClearRestoreInfo( Item item ) + { + if ( m_RestoreTable == null || item == null ) + return; + + m_RestoreTable.Remove( item ); + + if ( m_RestoreTable.Count == 0 ) + m_RestoreTable = null; + } + + public bool CanLoot( Mobile from ) + { + if ( !IsCriminalAction( from ) ) + return true; + + Map map = this.Map; + + if ( map == null || (map.Rules & MapRules.HarmfulRestrictions) != 0 ) + return false; + + return true; + } + + public bool CheckLoot( Mobile from ) + { + if ( !CanLoot( from ) ) + { + if ( m_Owner == null || !m_Owner.Player ) + from.SendLocalizedMessage( 1005035 ); // You did not earn the right to loot this creature! + else + from.SendLocalizedMessage( 1010049 ); // You may not loot this corpse. + + return false; + } + else if ( IsCriminalAction( from ) ) + { + if ( m_Owner == null || !m_Owner.Player ) + from.SendLocalizedMessage( 1005036 ); // Looting this monster corpse will be a criminal act! + else + from.SendLocalizedMessage( 1005038 ); // Looting this corpse will be a criminal act! + } + + return true; + } + + public virtual void Open( Mobile from, bool checkSelfLoot ) + { + if ( from.AccessLevel > AccessLevel.Player || from.InRange( this.GetWorldLocation(), 2 ) ) + { + bool selfLoot = ( checkSelfLoot && ( from == m_Owner ) ); + + if ( selfLoot ) + { + List items = new List( this.Items ); + + bool gathered = false; + bool didntFit = false; + + Container pack = from.Backpack; + + bool checkRobe = true; + + for ( int i = 0; !didntFit && i < items.Count; ++i ) + { + Item item = items[i]; + Point3D loc = item.Location; + + if ( (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair) || !item.Movable || !GetRestoreInfo( item, ref loc ) ) + continue; + + if ( checkRobe ) + { + DeathRobe robe = from.FindItemOnLayer( Layer.OuterTorso ) as DeathRobe; + + if ( robe != null ) + { + if ( Core.SE ) + { + robe.Delete(); + } + else + { + Map map = from.Map; + + if ( map != null && map != Map.Internal ) + robe.MoveToWorld( from.Location, map ); + } + } + } + + if ( m_EquipItems.Contains( item ) && from.EquipItem( item ) ) + { + gathered = true; + } + else if ( pack != null && pack.CheckHold( from, item, false, true ) ) + { + item.Location = loc; + pack.AddItem( item ); + gathered = true; + } + else + { + didntFit = true; + } + } + + if ( gathered && !didntFit ) + { + m_Carved = true; + + if ( ItemID == 0x2006 ) + { + ProcessDelta(); + SendRemovePacket(); + ItemID = Utility.Random( 0xECA, 9 ); // bone graphic + Hue = 0; + ProcessDelta(); + } + + from.PlaySound( 0x3E3 ); + from.SendLocalizedMessage( 1062471 ); // You quickly gather all of your belongings. + return; + } + + if ( gathered && didntFit ) + from.SendLocalizedMessage( 1062472 ); // You gather some of your belongings. The rest remain on the corpse. + } + + if ( !CheckLoot( from ) ) + return; + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest ) + { + GetDaemonBoneObjective obj = qs.FindObjective( typeof( GetDaemonBoneObjective ) ) as GetDaemonBoneObjective; + + if ( obj != null && obj.CorpseWithBone == this && ( !obj.Completed || UzeraanTurmoilQuest.HasLostDaemonBone( player ) ) ) + { + Item bone = new QuestDaemonBone(); + + if ( player.PlaceInBackpack( bone ) ) + { + obj.CorpseWithBone = null; + player.SendLocalizedMessage( 1049341, "", 0x22 ); // You rummage through the bones and find a Daemon Bone! You quickly place the item in your pack. + + if ( !obj.Completed ) + obj.Complete(); + } + else + { + bone.Delete(); + player.SendLocalizedMessage( 1049342, "", 0x22 ); // Rummaging through the bones you find a Daemon Bone, but can't pick it up because your pack is too full. Come back when you have more room in your pack. + } + + return; + } + } + else if ( qs is TheSummoningQuest ) + { + VanquishDaemonObjective obj = qs.FindObjective( typeof( VanquishDaemonObjective ) ) as VanquishDaemonObjective; + + if ( obj != null && obj.Completed && obj.CorpseWithSkull == this ) + { + GoldenSkull sk = new GoldenSkull(); + + if ( player.PlaceInBackpack( sk ) ) + { + obj.CorpseWithSkull = null; + player.SendLocalizedMessage( 1050022 ); // For your valor in combating the devourer, you have been awarded a golden skull. + qs.Complete(); + } + else + { + sk.Delete(); + player.SendLocalizedMessage( 1050023 ); // You find a golden skull, but your backpack is too full to carry it. + } + } + } + } + + base.OnDoubleClick( from ); + + if ( from != m_Owner ) + from.RevealingAction(); + } + else + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + return; + } + } + + public override void OnDoubleClick( Mobile from ) + { + Open( from, Core.AOS ); + } + + public override bool CheckContentDisplay( Mobile from ) + { + return false; + } + + public override bool DisplaysContent{ get{ return false; } } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( ItemID == 0x2006 ) // Corpse form + { + if ( m_CorpseName != null ) + list.Add( m_CorpseName ); + else + list.Add( 1046414, this.Name ); // the remains of ~1_NAME~ + } + else // Bone form + { + list.Add( 1046414, this.Name ); // the remains of ~1_NAME~ + } + } + + public override void OnAosSingleClick( Mobile from ) + { + int hue = Notoriety.GetHue( NotorietyHandlers.CorpseNotoriety( from, this ) ); + ObjectPropertyList opl = this.PropertyList; + + if ( opl.Header > 0 ) + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, opl.Header, Name, opl.HeaderArgs ) ); + } + + public override void OnSingleClick( Mobile from ) + { + int hue = Notoriety.GetHue( NotorietyHandlers.CorpseNotoriety( from, this ) ); + + if ( ItemID == 0x2006 ) // Corpse form + { + if ( m_CorpseName != null ) + from.Send( new AsciiMessage( Serial, ItemID, MessageType.Label, hue, 3, "", m_CorpseName ) ); + else + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, 1046414, "", Name ) ); + } + else // Bone form + { + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, hue, 3, 1046414, "", Name ) ); + } + } + + public void Carve( Mobile from, Item item ) + { + Mobile dead = m_Owner; + + if ( m_Carved || dead == null ) + { + from.SendLocalizedMessage( 500485 ); // You see nothing useful to carve from the corpse. + } + else if ( ((Body)Amount).IsHuman && ItemID == 0x2006 ) + { + new Blood( 0x122D ).MoveToWorld( Location, Map ); + + new Torso().MoveToWorld( Location, Map ); + new LeftLeg().MoveToWorld( Location, Map ); + new LeftArm().MoveToWorld( Location, Map ); + new RightLeg().MoveToWorld( Location, Map ); + new RightArm().MoveToWorld( Location, Map ); + new Head( dead.Name ).MoveToWorld( Location, Map ); + + m_Carved = true; + + ProcessDelta(); + SendRemovePacket(); + ItemID = Utility.Random( 0xECA, 9 ); // bone graphic + Hue = 0; + ProcessDelta(); + + if ( IsCriminalAction( from ) ) + from.CriminalAction( true ); + } + else if ( dead is BaseCreature ) + { + ((BaseCreature)dead).OnCarve( from, this ); + } + else + { + from.SendLocalizedMessage( 500485 ); // You see nothing useful to carve from the corpse. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Corpses/CorpseNameAttribute.cs b/Scripts/Items/Misc/Corpses/CorpseNameAttribute.cs new file mode 100644 index 000000000..d71d3f988 --- /dev/null +++ b/Scripts/Items/Misc/Corpses/CorpseNameAttribute.cs @@ -0,0 +1,20 @@ +using System; + +namespace Server +{ + [AttributeUsage( AttributeTargets.Class )] + public class CorpseNameAttribute : Attribute + { + private string m_Name; + + public string Name + { + get{ return m_Name; } + } + + public CorpseNameAttribute( string name ) + { + m_Name = name; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Corpses/DecayedCorpse.cs b/Scripts/Items/Misc/Corpses/DecayedCorpse.cs new file mode 100644 index 000000000..7d3ea52ea --- /dev/null +++ b/Scripts/Items/Misc/Corpses/DecayedCorpse.cs @@ -0,0 +1,115 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DecayedCorpse : Container + { + private Timer m_DecayTimer; + private DateTime m_DecayTime; + + private static TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes( 7.0 ); + + public DecayedCorpse( string name ) : base( Utility.Random( 0xECA, 9 ) ) + { + Movable = false; + Name = name; + + BeginDecay( m_DefaultDecayTime ); + } + + public void BeginDecay( TimeSpan delay ) + { + if ( m_DecayTimer != null ) + m_DecayTimer.Stop(); + + m_DecayTime = DateTime.Now + delay; + + m_DecayTimer = new InternalTimer( this, delay ); + m_DecayTimer.Start(); + } + + public override void OnAfterDelete() + { + if ( m_DecayTimer != null ) + m_DecayTimer.Stop(); + + m_DecayTimer = null; + } + + private class InternalTimer : Timer + { + private DecayedCorpse m_Corpse; + + public InternalTimer( DecayedCorpse c, TimeSpan delay ) : base( delay ) + { + m_Corpse = c; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Corpse.Delete(); + } + } + + // Do not display (x items, y stones) + public override bool CheckContentDisplay( Mobile from ) + { + return false; + } + + // Do not display (x items, y stones) + public override bool DisplaysContent{ get{ return false; } } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( 1046414, Name ); // the remains of ~1_NAME~ + } + + public override void OnSingleClick( Mobile from ) + { + this.LabelTo( from, 1046414, Name ); // the remains of ~1_NAME~ + } + + public DecayedCorpse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_DecayTimer != null ); + + if ( m_DecayTimer != null ) + writer.WriteDeltaTime( m_DecayTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + BeginDecay( m_DefaultDecayTime ); + + break; + } + case 1: + { + if ( reader.ReadBool() ) + BeginDecay( reader.ReadDeltaTime() - DateTime.Now ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Corpses/Packets.cs b/Scripts/Items/Misc/Corpses/Packets.cs new file mode 100644 index 000000000..0f7bca624 --- /dev/null +++ b/Scripts/Items/Misc/Corpses/Packets.cs @@ -0,0 +1,125 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; + +namespace Server.Network +{ + public sealed class CorpseEquip : Packet + { + public CorpseEquip( Mobile beholder, Corpse beheld ) : base( 0x89 ) + { + List list = beheld.EquipItems; + + int count = list.Count; + if( beheld.Hair != null && beheld.Hair.ItemID > 0 ) + count++; + if( beheld.FacialHair != null && beheld.FacialHair.ItemID > 0 ) + count++; + + EnsureCapacity( 8 + (count * 5) ); + + m_Stream.Write( (int) beheld.Serial ); + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = list[i]; + + if ( !item.Deleted && beholder.CanSee( item ) && item.Parent == beheld ) + { + m_Stream.Write( (byte) (item.Layer + 1) ); + m_Stream.Write( (int) item.Serial ); + } + } + + if( beheld.Hair != null && beheld.Hair.ItemID > 0 ) + { + m_Stream.Write( (byte)(Layer.Hair + 1) ); + m_Stream.Write( (int)HairInfo.FakeSerial( beheld.Owner ) - 2 ); + } + + if( beheld.FacialHair != null && beheld.FacialHair.ItemID > 0 ) + { + m_Stream.Write( (byte)(Layer.FacialHair + 1) ); + m_Stream.Write( (int)FacialHairInfo.FakeSerial( beheld.Owner ) - 2 ); + } + + m_Stream.Write( (byte) Layer.Invalid ); + } + } + + public sealed class CorpseContent : Packet + { + public CorpseContent( Mobile beholder, Corpse beheld ) + : base( 0x3C ) + { + List items = beheld.EquipItems; + int count = items.Count; + + if( beheld.Hair != null && beheld.Hair.ItemID > 0 ) + count++; + if( beheld.FacialHair != null && beheld.FacialHair.ItemID > 0 ) + count++; + + EnsureCapacity( 5 + (count * 19) ); + + long pos = m_Stream.Position; + + int written = 0; + + m_Stream.Write( (ushort)0 ); + + for( int i = 0; i < items.Count; ++i ) + { + Item child = items[i]; + + if( !child.Deleted && child.Parent == beheld && beholder.CanSee( child ) ) + { + m_Stream.Write( (int)child.Serial ); + m_Stream.Write( (ushort)child.ItemID ); + m_Stream.Write( (byte)0 ); // signed, itemID offset + m_Stream.Write( (ushort)child.Amount ); + m_Stream.Write( (short)child.X ); + m_Stream.Write( (short)child.Y ); + m_Stream.Write( (int)beheld.Serial ); + m_Stream.Write( (ushort)child.Hue ); + + ++written; + } + } + + if( beheld.Hair != null && beheld.Hair.ItemID > 0 ) + { + m_Stream.Write( (int)HairInfo.FakeSerial( beheld.Owner ) - 2 ); + m_Stream.Write( (ushort)beheld.Hair.ItemID ); + m_Stream.Write( (byte)0 ); // signed, itemID offset + m_Stream.Write( (ushort)1 ); + m_Stream.Write( (short)0 ); + m_Stream.Write( (short)0 ); + m_Stream.Write( (int)beheld.Serial ); + m_Stream.Write( (ushort)beheld.Hair.Hue ); + + ++written; + } + + if( beheld.FacialHair != null && beheld.FacialHair.ItemID > 0 ) + { + m_Stream.Write( (int)FacialHairInfo.FakeSerial( beheld.Owner ) - 2 ); + m_Stream.Write( (ushort)beheld.FacialHair.ItemID ); + m_Stream.Write( (byte)0 ); // signed, itemID offset + m_Stream.Write( (ushort)1 ); + m_Stream.Write( (short)0 ); + m_Stream.Write( (short)0 ); + m_Stream.Write( (int)beheld.Serial ); + m_Stream.Write( (ushort)beheld.FacialHair.Hue ); + + ++written; + } + + m_Stream.Seek( pos, SeekOrigin.Begin ); + m_Stream.Write( (ushort)written ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/EffectController.cs b/Scripts/Items/Misc/EffectController.cs new file mode 100644 index 000000000..0952b4f8a --- /dev/null +++ b/Scripts/Items/Misc/EffectController.cs @@ -0,0 +1,336 @@ +using System; + +namespace Server.Items +{ + public enum ECEffectType + { + None, + Moving, + Location, + Target, + Lightning + } + + public enum EffectTriggerType + { + None, + Sequenced, + DoubleClick, + InRange + } + + public class EffectController : Item + { + private TimeSpan m_EffectDelay; + + private ECEffectType m_EffectType; + private EffectTriggerType m_TriggerType; + + private IEntity m_Source; + private IEntity m_Target; + + private TimeSpan m_TriggerDelay; + private EffectController m_Trigger; + + private int m_ItemID; + private int m_Hue; + private int m_RenderMode; + + private int m_Speed; + private int m_Duration; + + private bool m_FixedDirection; + private bool m_Explodes; + + private int m_ParticleEffect; + private int m_ExplodeParticleEffect; + private int m_ExplodeSound; + + private EffectLayer m_EffectLayer; + private int m_Unknown; + + private TimeSpan m_SoundDelay; + private int m_SoundID; + private bool m_PlaySoundAtTrigger; + + private int m_TriggerRange; + + [CommandProperty( AccessLevel.GameMaster )] + public ECEffectType EffectType{ get{ return m_EffectType; } set{ m_EffectType = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public EffectTriggerType TriggerType{ get{ return m_TriggerType; } set{ m_TriggerType = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public EffectLayer EffectLayer{ get{ return m_EffectLayer; } set{ m_EffectLayer = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan EffectDelay{ get{ return m_EffectDelay; } set{ m_EffectDelay = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan TriggerDelay{ get{ return m_TriggerDelay; } set{ m_TriggerDelay = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan SoundDelay{ get{ return m_SoundDelay; } set{ m_SoundDelay = value; } } + + + [CommandProperty( AccessLevel.GameMaster )] + public Item SourceItem{ get{ return m_Source as Item; } set{ m_Source = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile SourceMobile{ get{ return m_Source as Mobile; } set{ m_Source = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool SourceNull{ get{ return ( m_Source == null ); } set{ if ( value ) m_Source = null; } } + + + [CommandProperty( AccessLevel.GameMaster )] + public Item TargetItem{ get{ return m_Target as Item; } set{ m_Target = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile TargetMobile{ get{ return m_Target as Mobile; } set{ m_Target = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool TargetNull{ get{ return ( m_Target == null ); } set{ if ( value ) m_Target = null; } } + + + [CommandProperty( AccessLevel.GameMaster )] + public EffectController Sequence{ get{ return m_Trigger; } set{ m_Trigger = value; } } + + + [CommandProperty( AccessLevel.GameMaster )] + private bool FixedDirection{ get{ return m_FixedDirection; } set{ m_FixedDirection = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + private bool Explodes{ get{ return m_Explodes; } set{ m_Explodes = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + private bool PlaySoundAtTrigger{ get{ return m_PlaySoundAtTrigger; } set{ m_PlaySoundAtTrigger = value; } } + + + [CommandProperty( AccessLevel.GameMaster )] + public int EffectItemID{ get{ return m_ItemID; } set{ m_ItemID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int EffectHue{ get{ return m_Hue; } set{ m_Hue = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int RenderMode{ get{ return m_RenderMode; } set{ m_RenderMode = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Speed{ get{ return m_Speed; } set{ m_Speed = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Duration{ get{ return m_Duration; } set{ m_Duration = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ParticleEffect{ get{ return m_ParticleEffect; } set{ m_ParticleEffect = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ExplodeParticleEffect{ get{ return m_ExplodeParticleEffect; } set{ m_ExplodeParticleEffect = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ExplodeSound{ get{ return m_ExplodeSound; } set{ m_ExplodeSound = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Unknown{ get{ return m_Unknown; } set{ m_Unknown = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int SoundID{ get{ return m_SoundID; } set{ m_SoundID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int TriggerRange{ get{ return m_TriggerRange; } set{ m_TriggerRange = value; } } + + public override string DefaultName + { + get { return "Effect Controller"; } + } + + [Constructable] + public EffectController() : base( 0x1B72 ) + { + Movable = false; + Visible = false; + m_TriggerType = EffectTriggerType.Sequenced; + m_EffectLayer = (EffectLayer)255; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_TriggerType == EffectTriggerType.DoubleClick ) + DoEffect( from ); + } + + public override bool HandlesOnMovement{ get{ return ( m_TriggerType == EffectTriggerType.InRange ); } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m.Location != oldLocation && m_TriggerType == EffectTriggerType.InRange && Utility.InRange( GetWorldLocation(), m.Location, m_TriggerRange ) && !Utility.InRange( GetWorldLocation(), oldLocation, m_TriggerRange ) ) + DoEffect( m ); + } + + public EffectController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_EffectDelay ); + writer.Write( m_TriggerDelay ); + writer.Write( m_SoundDelay ); + + if ( m_Source is Item ) + writer.Write( m_Source as Item ); + else + writer.Write( m_Source as Mobile ); + + if ( m_Target is Item ) + writer.Write( m_Target as Item ); + else + writer.Write( m_Target as Mobile ); + + writer.Write( m_Trigger as Item ); + + writer.Write( m_FixedDirection ); + writer.Write( m_Explodes ); + writer.Write( m_PlaySoundAtTrigger ); + + writer.WriteEncodedInt( (int) m_EffectType ); + writer.WriteEncodedInt( (int) m_EffectLayer ); + writer.WriteEncodedInt( (int) m_TriggerType ); + + writer.WriteEncodedInt( m_ItemID ); + writer.WriteEncodedInt( m_Hue ); + writer.WriteEncodedInt( m_RenderMode ); + writer.WriteEncodedInt( m_Speed ); + writer.WriteEncodedInt( m_Duration ); + writer.WriteEncodedInt( m_ParticleEffect ); + writer.WriteEncodedInt( m_ExplodeParticleEffect ); + writer.WriteEncodedInt( m_ExplodeSound ); + writer.WriteEncodedInt( m_Unknown ); + writer.WriteEncodedInt( m_SoundID ); + writer.WriteEncodedInt( m_TriggerRange ); + } + + private IEntity ReadEntity( GenericReader reader ) + { + return World.FindEntity( reader.ReadInt() ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_EffectDelay = reader.ReadTimeSpan(); + m_TriggerDelay = reader.ReadTimeSpan(); + m_SoundDelay = reader.ReadTimeSpan(); + + m_Source = ReadEntity( reader ); + m_Target = ReadEntity( reader ); + m_Trigger = reader.ReadItem() as EffectController; + + m_FixedDirection = reader.ReadBool(); + m_Explodes = reader.ReadBool(); + m_PlaySoundAtTrigger = reader.ReadBool(); + + m_EffectType = (ECEffectType)reader.ReadEncodedInt(); + m_EffectLayer = (EffectLayer)reader.ReadEncodedInt(); + m_TriggerType = (EffectTriggerType)reader.ReadEncodedInt(); + + m_ItemID = reader.ReadEncodedInt(); + m_Hue = reader.ReadEncodedInt(); + m_RenderMode = reader.ReadEncodedInt(); + m_Speed = reader.ReadEncodedInt(); + m_Duration = reader.ReadEncodedInt(); + m_ParticleEffect = reader.ReadEncodedInt(); + m_ExplodeParticleEffect = reader.ReadEncodedInt(); + m_ExplodeSound = reader.ReadEncodedInt(); + m_Unknown = reader.ReadEncodedInt(); + m_SoundID = reader.ReadEncodedInt(); + m_TriggerRange = reader.ReadEncodedInt(); + + break; + } + } + } + + public void PlaySound( object trigger ) + { + IEntity ent = null; + + if ( m_PlaySoundAtTrigger ) + ent = trigger as IEntity; + + if ( ent == null ) + ent = this; + + Effects.PlaySound( (ent is Item) ? ((Item)ent).GetWorldLocation() : ent.Location, ent.Map, m_SoundID ); + } + + public void DoEffect( object trigger ) + { + if ( Deleted || m_TriggerType == EffectTriggerType.None ) + return; + + if ( m_SoundID > 0 ) + Timer.DelayCall( m_SoundDelay, new TimerStateCallback( PlaySound ), trigger ); + + if ( m_Trigger != null ) + Timer.DelayCall( m_TriggerDelay, new TimerStateCallback( m_Trigger.DoEffect ), trigger ); + + if ( m_EffectType != ECEffectType.None ) + Timer.DelayCall( m_EffectDelay, new TimerStateCallback( InternalDoEffect ), trigger ); + } + + public void InternalDoEffect( object trigger ) + { + IEntity from = m_Source, to = m_Target; + + if ( from == null ) + from = (IEntity)trigger; + + if ( to == null ) + to = (IEntity)trigger; + + switch ( m_EffectType ) + { + case ECEffectType.Lightning: + { + Effects.SendBoltEffect( from, false, m_Hue ); + break; + } + case ECEffectType.Location: + { + Effects.SendLocationParticles( EffectItem.Create( from.Location, from.Map, EffectItem.DefaultDuration ), m_ItemID, m_Speed, m_Duration, m_Hue, m_RenderMode, m_ParticleEffect, m_Unknown ); + break; + } + case ECEffectType.Moving: + { + if ( from == this ) + from = EffectItem.Create( from.Location, from.Map, EffectItem.DefaultDuration ); + + if ( to == this ) + to = EffectItem.Create( to.Location, to.Map, EffectItem.DefaultDuration ); + + Effects.SendMovingParticles( from, to, m_ItemID, m_Speed, m_Duration, m_FixedDirection, m_Explodes, m_Hue, m_RenderMode, m_ParticleEffect, m_ExplodeParticleEffect, m_ExplodeSound, m_EffectLayer, m_Unknown ); + break; + } + case ECEffectType.Target: + { + Effects.SendTargetParticles( from, m_ItemID, m_Speed, m_Duration, m_Hue, m_RenderMode, m_ParticleEffect, m_EffectLayer, m_Unknown ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/EffectItem.cs b/Scripts/Items/Misc/EffectItem.cs new file mode 100644 index 000000000..0991b353a --- /dev/null +++ b/Scripts/Items/Misc/EffectItem.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Items +{ + public class EffectItem : Item + { + private static ArrayList m_Free = new ArrayList(); // List of available EffectItems + + public static readonly TimeSpan DefaultDuration = TimeSpan.FromSeconds( 5.0 ); + + public static EffectItem Create( Point3D p, Map map, TimeSpan duration ) + { + EffectItem item = null; + + for ( int i = m_Free.Count - 1; item == null && i >= 0; --i ) // We reuse new entries first so decay works better + { + EffectItem free = (EffectItem)m_Free[i]; + + m_Free.RemoveAt( i ); + + if ( !free.Deleted && free.Map == Map.Internal ) + item = free; + } + + if ( item == null ) + item = new EffectItem(); + else + item.ItemID = 1; + + item.MoveToWorld( p, map ); + item.BeginFree( duration ); + + return item; + } + + private EffectItem() : base( 1 ) // nodraw + { + Movable = false; + } + + public void BeginFree( TimeSpan duration ) + { + new FreeTimer( this, duration ).Start(); + } + + public override bool Decays + { + get + { + return true; + } + } + + public EffectItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + + private class FreeTimer : Timer + { + private Item m_Item; + + public FreeTimer( Item item, TimeSpan delay ) : base( delay ) + { + m_Item = item; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Item.Internalize(); + + m_Free.Add( m_Item ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/ExecutionersCap.cs b/Scripts/Items/Misc/ExecutionersCap.cs new file mode 100644 index 000000000..7396e74b7 --- /dev/null +++ b/Scripts/Items/Misc/ExecutionersCap.cs @@ -0,0 +1,31 @@ +using System; + +namespace Server.Items +{ + public class ExecutionersCap : Item + { + [Constructable] + public ExecutionersCap() : base(0xF83) + { + Weight = 1.0; + } + + public ExecutionersCap(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/FlipableAttribute.cs b/Scripts/Items/Misc/FlipableAttribute.cs new file mode 100644 index 000000000..1200d8d5e --- /dev/null +++ b/Scripts/Items/Misc/FlipableAttribute.cs @@ -0,0 +1,116 @@ +using System; +using Server; +using System.Reflection; +using Server.Targeting; +using Server.Commands; + +namespace Server.Items +{ + public class FlipCommandHandlers + { + public static void Initialize() + { + CommandSystem.Register( "Flip", AccessLevel.GameMaster, new CommandEventHandler( Flip_OnCommand ) ); + } + + [Usage( "Flip" )] + [Description( "Turns an item." )] + public static void Flip_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new FlipTarget(); + } + + private class FlipTarget : Target + { + public FlipTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Item ) + { + Item item = (Item)targeted; + + if ( item.Movable == false && from.AccessLevel == AccessLevel.Player ) + return; + + Type type = targeted.GetType(); + + FlipableAttribute [] AttributeArray = (FlipableAttribute []) type.GetCustomAttributes(typeof(FlipableAttribute), false); + + if( AttributeArray.Length == 0 ) + { + return ; + } + + FlipableAttribute fa = AttributeArray[0]; + + fa.Flip( (Item)targeted); + } + } + } + } + + [AttributeUsage( AttributeTargets.Class )] + public class DynamicFlipingAttribute : Attribute + { + public DynamicFlipingAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Class )] + public class FlipableAttribute : Attribute + { + private int[] m_ItemIDs; + + public int[] ItemIDs + { + get{ return m_ItemIDs; } + } + + public FlipableAttribute() : this ( null ) + { + } + + public FlipableAttribute( params int[] itemIDs ) + { + m_ItemIDs = itemIDs; + } + + public virtual void Flip( Item item ) + { + if ( m_ItemIDs == null ) + { + try + { + MethodInfo flipMethod = item.GetType().GetMethod( "Flip", Type.EmptyTypes ); + if ( flipMethod != null ) + flipMethod.Invoke( item, new object[0] ); + } + catch + { + } + + } + else + { + int index = 0; + for ( int i = 0; i < m_ItemIDs.Length; i++ ) + { + if ( item.ItemID == m_ItemIDs[i] ) + { + index = i + 1; + break; + } + } + + if ( index > m_ItemIDs.Length - 1) + index = 0; + + item.ItemID = m_ItemIDs[index]; + } + } + } +} diff --git a/Scripts/Items/Misc/GlassItems.cs b/Scripts/Items/Misc/GlassItems.cs new file mode 100644 index 000000000..902a33bdd --- /dev/null +++ b/Scripts/Items/Misc/GlassItems.cs @@ -0,0 +1,1528 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x182E, 0x182F, 0x1830, 0x1831 )] + public class SmallFlask : Item + { + [Constructable] + public SmallFlask() : base( 0x182E ) + { + Weight = 1.0; + Movable = true; + } + + public SmallFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x182A, 0x182B, 0x182C, 0x182D )] + public class MediumFlask : Item + { + [Constructable] + public MediumFlask() : base( 0x182A ) + { + Weight = 1.0; + Movable = true; + } + + public MediumFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x183B, 0x183C, 0x183D )] + public class LargeFlask : Item + { + [Constructable] + public LargeFlask() : base( 0x183B ) + { + Weight = 1.0; + Movable = true; + } + + public LargeFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837 )] + public class CurvedFlask : Item + { + [Constructable] + public CurvedFlask() : base( 0x1832 ) + { + Weight = 1.0; + Movable = true; + } + + public CurvedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x1838, 0x1839, 0x183A )] + public class LongFlask : Item + { + [Constructable] + public LongFlask() : base( 0x1838 ) + { + Weight = 1.0; + Movable = true; + } + + public LongFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x1810, 0x1811 )] + public class SpinningHourglass : Item + { + [Constructable] + public SpinningHourglass() : base( 0x1810 ) + { + Weight = 1.0; + Movable = true; + } + + public SpinningHourglass( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenBottle : Item + { + [Constructable] + public GreenBottle() : base( 0x0EFB ) + { + Weight = 1.0; + Movable = true; + } + + public GreenBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RedBottle : Item + { + [Constructable] + public RedBottle() : base( 0x0EFC ) + { + Weight = 1.0; + Movable = true; + } + + public RedBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallBrownBottle : Item + { + [Constructable] + public SmallBrownBottle() : base( 0x0EFD ) + { + Weight = 1.0; + Movable = true; + } + + public SmallBrownBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallGreenBottle : Item + { + [Constructable] + public SmallGreenBottle() : base( 0x0F01 ) + { + Weight = 1.0; + Movable = true; + } + + public SmallGreenBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallVioletBottle : Item + { + [Constructable] + public SmallVioletBottle() : base( 0x0F02 ) + { + Weight = 1.0; + Movable = true; + } + + public SmallVioletBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TinyYellowBottle : Item + { + [Constructable] + public TinyYellowBottle() : base( 0x0F03 ) + { + Weight = 1.0; + Movable = true; + } + + public TinyYellowBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + //remove + public class SmallBlueFlask : Item + { + [Constructable] + public SmallBlueFlask() : base( 0x182A ) + { + Weight = 1.0; + Movable = true; + } + + public SmallBlueFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallYellowFlask : Item + { + [Constructable] + public SmallYellowFlask() : base( 0x182B ) + { + Weight = 1.0; + Movable = true; + } + + public SmallYellowFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallRedFlask : Item + { + [Constructable] + public SmallRedFlask() : base( 0x182C ) + { + Weight = 1.0; + Movable = true; + } + + public SmallRedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallEmptyFlask : Item + { + [Constructable] + public SmallEmptyFlask() : base( 0x182D ) + { + Weight = 1.0; + Movable = true; + } + + public SmallEmptyFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class YellowBeaker : Item + { + [Constructable] + public YellowBeaker() : base( 0x182E ) + { + Weight = 1.0; + Movable = true; + } + + public YellowBeaker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RedBeaker : Item + { + [Constructable] + public RedBeaker() : base( 0x182F ) + { + Weight = 1.0; + Movable = true; + } + + public RedBeaker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BlueBeaker : Item + { + [Constructable] + public BlueBeaker() : base( 0x1830 ) + { + Weight = 1.0; + Movable = true; + } + + public BlueBeaker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenBeaker : Item + { + [Constructable] + public GreenBeaker() : base( 0x1831 ) + { + Weight = 1.0; + Movable = true; + } + + public GreenBeaker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyCurvedFlaskW : Item + { + [Constructable] + public EmptyCurvedFlaskW() : base( 0x1832 ) + { + Weight = 1.0; + Movable = true; + } + + public EmptyCurvedFlaskW( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RedCurvedFlask : Item + { + [Constructable] + public RedCurvedFlask() : base( 0x1833 ) + { + Weight = 1.0; + Movable = true; + } + + public RedCurvedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LtBlueCurvedFlask : Item + { + [Constructable] + public LtBlueCurvedFlask() : base( 0x1834 ) + { + Weight = 1.0; + Movable = true; + } + + public LtBlueCurvedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyCurvedFlaskE : Item + { + [Constructable] + public EmptyCurvedFlaskE() : base( 0x1835 ) + { + Weight = 1.0; + Movable = true; + } + + public EmptyCurvedFlaskE( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BlueCurvedFlask : Item + { + [Constructable] + public BlueCurvedFlask() : base( 0x1836 ) + { + Weight = 1.0; + Movable = true; + } + + public BlueCurvedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenCurvedFlask : Item + { + [Constructable] + public GreenCurvedFlask() : base( 0x1837 ) + { + Weight = 1.0; + Movable = true; + } + + public GreenCurvedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class RedRibbedFlask : Item + { + [Constructable] + public RedRibbedFlask() : base( 0x1838 ) + { + Weight = 1.0; + Movable = true; + } + + public RedRibbedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class VioletRibbedFlask : Item + { + [Constructable] + public VioletRibbedFlask() : base( 0x1839 ) + { + Weight = 1.0; + Movable = true; + } + + public VioletRibbedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyRibbedFlask : Item + { + [Constructable] + public EmptyRibbedFlask() : base( 0x183A ) + { + Weight = 1.0; + Movable = true; + } + + public EmptyRibbedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeYellowFlask : Item + { + [Constructable] + public LargeYellowFlask() : base( 0x183B ) + { + Weight = 1.0; + Movable = true; + } + + public LargeYellowFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeVioletFlask : Item + { + [Constructable] + public LargeVioletFlask() : base( 0x183C ) + { + Weight = 1.0; + Movable = true; + } + + public LargeVioletFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeEmptyFlask : Item + { + [Constructable] + public LargeEmptyFlask() : base( 0x183D ) + { + Weight = 1.0; + Movable = true; + } + + public LargeEmptyFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AniRedRibbedFlask : Item + { + [Constructable] + public AniRedRibbedFlask() : base( 0x183E ) + { + Weight = 1.0; + Movable = true; + } + + public AniRedRibbedFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AniLargeVioletFlask : Item + { + [Constructable] + public AniLargeVioletFlask() : base( 0x1841 ) + { + Weight = 1.0; + Movable = true; + } + + public AniLargeVioletFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AniSmallBlueFlask : Item + { + [Constructable] + public AniSmallBlueFlask() : base( 0x1844 ) + { + Weight = 1.0; + Movable = true; + } + + public AniSmallBlueFlask( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallBlueBottle : Item + { + [Constructable] + public SmallBlueBottle() : base( 0x1847 ) + { + Weight = 1.0; + Movable = true; + } + + public SmallBlueBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallGreenBottle2 : Item + { + [Constructable] + public SmallGreenBottle2() : base( 0x1848 ) + { + Weight = 1.0; + Movable = true; + } + + public SmallGreenBottle2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x185B, 0x185C )] + public class EmptyVialsWRack : Item + { + [Constructable] + public EmptyVialsWRack() : base( 0x185B ) + { + Weight = 1.0; + Movable = true; + } + + public EmptyVialsWRack( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x185D, 0x185E )] + public class FullVialsWRack : Item + { + [Constructable] + public FullVialsWRack() : base( 0x185D ) + { + Weight = 1.0; + Movable = true; + } + + public FullVialsWRack( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyJar : Item + { + [Constructable] + public EmptyJar() : base( 0x1005 ) + { + Weight = 1.0; + Movable = true; + } + + public EmptyJar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FullJar : Item + { + [Constructable] + public FullJar() : base( 0x1006 ) + { + Weight = 1.0; + Movable = true; + } + + public FullJar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class HalfEmptyJar : Item + { + [Constructable] + public HalfEmptyJar() : base( 0x1007 ) + { + Weight = 1.0; + Movable = true; + } + + public HalfEmptyJar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class VioletStemmedBottle : Item + { + [Constructable] + public VioletStemmedBottle() : base( 0x0F00 ) + { + Weight = 1.0; + Movable = true; + } + + public VioletStemmedBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyJars3 : Item + { + [Constructable] + public EmptyJars3() : base( 0x0E46 ) + { + Weight = 3.0; + Movable = true; + } + + public EmptyJars3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyJars4 : Item + { + [Constructable] + public EmptyJars4() : base( 0x0E47 ) + { + Weight = 4.0; + Movable = true; + } + + public EmptyJars4( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FullJars3 : Item + { + [Constructable] + public FullJars3() : base( 0x0E4A ) + { + Weight = 3.0; + Movable = true; + } + + public FullJars3( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FullJars4 : Item + { + [Constructable] + public FullJars4() : base( 0x0E4B ) + { + Weight = 4.0; + Movable = true; + } + + public FullJars4( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0E44, 0x0E45 )] + public class EmptyJars2 : Item + { + [Constructable] + public EmptyJars2() : base( 0x0E44 ) + { + Weight = 2.0; + Movable = true; + } + + public EmptyJars2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0E48, 0x0E49 )] + public class FullJars2 : Item + { + [Constructable] + public FullJars2() : base( 0x0E48 ) + { + Weight = 2.0; + Movable = true; + } + + public FullJars2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x0E4C, 0x0E4D )] + public class HalfEmptyJars2 : Item + { + [Constructable] + public HalfEmptyJars2() : base( 0x0E4C ) + { + Weight = 2.0; + Movable = true; + } + + public HalfEmptyJars2( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class EmptyVial : Item + { + [Constructable] + public EmptyVial() : base( 0x0E24 ) + { + Weight = 1.0; + Movable = true; + } + + public EmptyVial( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class HourglassAni : Item + { + [Constructable] + public HourglassAni() : base( 0x1811 ) + { + Weight = 1.0; + Movable = true; + } + + public HourglassAni( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Hourglass : Item + { + [Constructable] + public Hourglass() : base( 0x1810 ) + { + Weight = 1.0; + Movable = true; + } + + public Hourglass( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TinyRedBottle : Item + { + [Constructable] + public TinyRedBottle() : base( 0x0F04 ) + { + Weight = 1.0; + Movable = true; + } + + public TinyRedBottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Gold.cs b/Scripts/Items/Misc/Gold.cs new file mode 100644 index 000000000..533603f83 --- /dev/null +++ b/Scripts/Items/Misc/Gold.cs @@ -0,0 +1,76 @@ +using System; + +namespace Server.Items +{ + public class Gold : Item + { + public override double DefaultWeight + { + get { return 0.02; } + } + + [Constructable] + public Gold() : this( 1 ) + { + } + + [Constructable] + public Gold( int amountFrom, int amountTo ) : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Gold( int amount ) : base( 0xEED ) + { + Stackable = true; + Amount = amount; + } + + public Gold( Serial serial ) : base( serial ) + { + } + + public override int GetDropSound() + { + if ( Amount <= 1 ) + return 0x2E4; + else if ( Amount <= 5 ) + return 0x2E5; + else + return 0x2E6; + } + + protected override void OnAmountChange( int oldValue ) + { + int newValue = this.Amount; + + UpdateTotal( this, TotalType.Gold, newValue - oldValue ); + } + + public override int GetTotal( TotalType type ) + { + int baseTotal = base.GetTotal( type ); + + if ( type == TotalType.Gold ) + baseTotal += this.Amount; + + return baseTotal; + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Guillotine.cs b/Scripts/Items/Misc/Guillotine.cs new file mode 100644 index 000000000..5804e31a3 --- /dev/null +++ b/Scripts/Items/Misc/Guillotine.cs @@ -0,0 +1,115 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Items +{ + public class Guillotine : Item + { + [Constructable] + public Guillotine() + : base( 4656 ) + { + Movable = false; + } + + private DateTime m_NextUse; + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( this.GetWorldLocation(), 2 ) || !from.InLOS( this ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that + } + else if ( Visible && ( ItemID == 4656 || ItemID == 4702 ) && DateTime.Now >= m_NextUse ) + { + Point3D p = this.GetWorldLocation(); + + if ( 1 > Utility.Random( Math.Max( Math.Abs( from.X - p.X ), Math.Abs( from.Y - p.Y ) ) ) ) + { + Effects.PlaySound( from.Location, from.Map, from.GetHurtSound() ); + from.PublicOverheadMessage( MessageType.Regular, from.SpeechHue, true, "Ouch!" ); + Spells.SpellHelper.Damage( TimeSpan.FromSeconds( 0.5 ), from, Utility.Dice( 2, 10, 5 ) ); + } + + Effects.PlaySound( this.GetWorldLocation(), this.Map, 0x387 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 0.25 ), new TimerCallback( Down1 ) ); + Timer.DelayCall( TimeSpan.FromSeconds( 0.50 ), new TimerCallback( Down2 ) ); + + Timer.DelayCall( TimeSpan.FromSeconds( 5.00 ), new TimerCallback( BackUp ) ); + + m_NextUse = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + } + } + + private void Down1() + { + ItemID = ( ItemID == 4656 ? 4678 : 4712 ); + } + + private void Down2() + { + ItemID = ( ItemID == 4678 ? 4679 : 4713 ); + + Point3D p = this.GetWorldLocation(); + Map f = this.Map; + + if ( f == null ) + return; + + new Blood( 4650 ).MoveToWorld( p, f ); + + for ( int i = 0; i < 4; ++i ) + { + int x = p.X - 2 + Utility.Random( 5 ); + int y = p.Y - 2 + Utility.Random( 5 ); + int z = p.Z; + + if ( !f.CanFit( x, y, z, 1, false, false, true ) ) + { + z = f.GetAverageZ( x, y ); + + if ( !f.CanFit( x, y, z, 1, false, false, true ) ) + continue; + } + + new Blood().MoveToWorld( new Point3D( x, y, z ), f ); + } + } + + private void BackUp() + { + if ( ItemID == 4678 || ItemID == 4679 ) + ItemID = 4656; + else if ( ItemID == 4712 || ItemID == 4713 ) + ItemID = 4702; + } + + public Guillotine( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + + if ( ItemID == 4678 || ItemID == 4679 ) + ItemID = 4656; + else if ( ItemID == 4712 || ItemID == 4713 ) + ItemID = 4702; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/HairDye.cs b/Scripts/Items/Misc/HairDye.cs new file mode 100644 index 000000000..e111e0fe4 --- /dev/null +++ b/Scripts/Items/Misc/HairDye.cs @@ -0,0 +1,193 @@ +using System; +using System.Text; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class HairDye : Item + { + public override int LabelNumber{ get{ return 1041060; } } // Hair Dye + + [Constructable] + public HairDye() : base( 0xEFF ) + { + Weight = 1.0; + } + + public HairDye( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + from.CloseGump( typeof( HairDyeGump ) ); + from.SendGump( new HairDyeGump( this ) ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 906, 1019045 ); // I can't reach that. + } + } + } + + public class HairDyeGump : Gump + { + private HairDye m_HairDye; + + private class HairDyeEntry + { + private string m_Name; + private int m_HueStart; + private int m_HueCount; + + public string Name + { + get + { + return m_Name; + } + } + + public int HueStart + { + get + { + return m_HueStart; + } + } + + public int HueCount + { + get + { + return m_HueCount; + } + } + + public HairDyeEntry( string name, int hueStart, int hueCount ) + { + m_Name = name; + m_HueStart = hueStart; + m_HueCount = hueCount; + } + } + + private static HairDyeEntry[] m_Entries = new HairDyeEntry[] + { + new HairDyeEntry( "*****", 1602, 26 ), + new HairDyeEntry( "*****", 1628, 27 ), + new HairDyeEntry( "*****", 1502, 32 ), + new HairDyeEntry( "*****", 1302, 32 ), + new HairDyeEntry( "*****", 1402, 32 ), + new HairDyeEntry( "*****", 1202, 24 ), + new HairDyeEntry( "*****", 2402, 29 ), + new HairDyeEntry( "*****", 2213, 6 ), + new HairDyeEntry( "*****", 1102, 8 ), + new HairDyeEntry( "*****", 1110, 8 ), + new HairDyeEntry( "*****", 1118, 16 ), + new HairDyeEntry( "*****", 1134, 16 ) + }; + + public HairDyeGump( HairDye dye ) : base( 50, 50 ) + { + m_HairDye = dye; + + AddPage( 0 ); + + AddBackground( 100, 10, 350, 355, 2600 ); + AddBackground( 120, 54, 110, 270, 5100 ); + + AddHtmlLocalized( 70, 25, 400, 35, 1011013, false, false ); //
Hair Color Selection Menu
+ + AddButton( 149, 328, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 185, 329, 250, 35, 1011014, false, false ); // Dye my hair this color! + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + AddLabel( 130, 59 + (i * 22), m_Entries[i].HueStart - 1, m_Entries[i].Name ); + AddButton( 207, 60 + (i * 22), 5224, 5224, 0, GumpButtonType.Page, i + 1 ); + } + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + HairDyeEntry e = m_Entries[i]; + + AddPage( i + 1 ); + + for ( int j = 0; j < e.HueCount; ++j ) + { + AddLabel( 278 + ((j / 16) * 80), 52 + ((j % 16) * 17), e.HueStart + j - 1, "*****" ); + AddRadio( 260 + ((j / 16) * 80), 52 + ((j % 16) * 17), 210, 211, false, (i * 100) + j ); + } + } + } + + public override void OnResponse( NetState from, RelayInfo info ) + { + if ( m_HairDye.Deleted ) + return; + + Mobile m = from.Mobile; + int[] switches = info.Switches; + + if ( !m_HairDye.IsChildOf( m.Backpack ) ) + { + m.SendLocalizedMessage( 1042010 ); //You must have the objectin your backpack to use it. + return; + } + + if ( info.ButtonID != 0 && switches.Length > 0 ) + { + if( m.HairItemID == 0 && m.FacialHairItemID == 0 ) + { + m.SendLocalizedMessage( 502623 ); // You have no hair to dye and cannot use this + } + else + { + // To prevent this from being exploited, the hue is abstracted into an internal list + + int entryIndex = switches[0] / 100; + int hueOffset = switches[0] % 100; + + if ( entryIndex >= 0 && entryIndex < m_Entries.Length ) + { + HairDyeEntry e = m_Entries[entryIndex]; + + if ( hueOffset >= 0 && hueOffset < e.HueCount ) + { + int hue = e.HueStart + hueOffset; + + m.HairHue = hue; + m.FacialHairHue = hue; + + m.SendLocalizedMessage( 501199 ); // You dye your hair + m_HairDye.Delete(); + m.PlaySound( 0x4E ); + } + } + } + } + else + { + m.SendLocalizedMessage( 501200 ); // You decide not to dye your hair + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/IDurability.cs b/Scripts/Items/Misc/IDurability.cs new file mode 100644 index 000000000..cd5005995 --- /dev/null +++ b/Scripts/Items/Misc/IDurability.cs @@ -0,0 +1,21 @@ +using System; +using Server; + +namespace Server.Items +{ + interface IDurability + { + int InitMinHits { get; } + int InitMaxHits { get; } + + int HitPoints { get; set; } + int MaxHitPoints { get; set; } + + //Maybe a scale/unscale durability? + } + + interface IWearableDurability : IDurability + { + int OnHit( BaseWeapon weapon, int damageTaken ); + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/InteriorDecorator.cs b/Scripts/Items/Misc/InteriorDecorator.cs new file mode 100644 index 000000000..1295b6d76 --- /dev/null +++ b/Scripts/Items/Misc/InteriorDecorator.cs @@ -0,0 +1,247 @@ +using System; +using Server; +using Server.Network; +using Server.Regions; +using Server.Multis; +using Server.Gumps; +using Server.Targeting; + +namespace Server.Items +{ + public enum DecorateCommand + { + None, + Turn, + Up, + Down + } + + public class InteriorDecorator : Item + { + private DecorateCommand m_Command; + + [CommandProperty( AccessLevel.GameMaster )] + public DecorateCommand Command{ get{ return m_Command; } set{ m_Command = value; InvalidateProperties(); } } + + [Constructable] + public InteriorDecorator() : base( 0xFC1 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override int LabelNumber{ get{ return 1041280; } } // an interior decorator + + public InteriorDecorator( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Command != DecorateCommand.None ) + list.Add( 1018322 + (int)m_Command ); // Turn/Up/Down + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !CheckUse( this, from ) ) + return; + + if ( m_Command == DecorateCommand.None ) + from.SendGump( new InternalGump( this ) ); + else + from.Target = new InternalTarget( this ); + } + + public static bool InHouse( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + return ( house != null && house.IsCoOwner( from ) ); + } + + public static bool CheckUse( InteriorDecorator tool, Mobile from ) + { + /*if ( tool.Deleted || !tool.IsChildOf( from.Backpack ) ) + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + else*/ + if ( !InHouse( from ) ) + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + else + return true; + + return false; + } + + private class InternalGump : Gump + { + private InteriorDecorator m_Decorator; + + public InternalGump( InteriorDecorator decorator ) : base( 150, 50 ) + { + m_Decorator = decorator; + + AddBackground( 0, 0, 200, 200, 2600 ); + + AddButton( 50, 45, 2152, 2154, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 90, 50, 70, 40, 1018323, false, false ); // Turn + + AddButton( 50, 95, 2152, 2154, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 90, 100, 70, 40, 1018324, false, false ); // Up + + AddButton( 50, 145, 2152, 2154, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 90, 150, 70, 40, 1018325, false, false ); // Down + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + DecorateCommand command = DecorateCommand.None; + + switch ( info.ButtonID ) + { + case 1: command = DecorateCommand.Turn; break; + case 2: command = DecorateCommand.Up; break; + case 3: command = DecorateCommand.Down; break; + } + + if ( command != DecorateCommand.None ) + { + m_Decorator.Command = command; + sender.Mobile.Target = new InternalTarget( m_Decorator ); + } + } + } + + private class InternalTarget : Target + { + private InteriorDecorator m_Decorator; + + public InternalTarget( InteriorDecorator decorator ) : base( -1, false, TargetFlags.None ) + { + CheckLOS = false; + + m_Decorator = decorator; + } + + protected override void OnTargetNotAccessible( Mobile from, object targeted ) + { + OnTarget( from, targeted ); + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted == m_Decorator ) + { + m_Decorator.Command = DecorateCommand.None; + from.SendGump( new InternalGump( m_Decorator ) ); + } + else if ( targeted is Item && InteriorDecorator.CheckUse( m_Decorator, from ) ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + Item item = (Item)targeted; + + if ( house == null || !house.IsCoOwner( from ) ) + { + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + } + else if ( item.Parent != null || !house.IsInside( item ) ) + { + from.SendLocalizedMessage( 1042270 ); // That is not in your house. + } + else if ( !house.IsLockedDown( item ) && !house.IsSecure( item ) ) + { + from.SendLocalizedMessage( 1042271 ); // That is not locked down. + } + else if ( item is VendorRentalContract ) + { + from.SendLocalizedMessage( 1062491 ); // You cannot use the house decorator on that object. + } + else if ( item.TotalWeight + item.PileWeight > 100 ) + { + from.SendLocalizedMessage( 1042272 ); // That is too heavy. + } + else + { + switch ( m_Decorator.Command ) + { + case DecorateCommand.Up: Up( item, from ); break; + case DecorateCommand.Down: Down( item, from ); break; + case DecorateCommand.Turn: Turn( item, from ); break; + } + } + } + } + + private static void Turn( Item item, Mobile from ) + { + FlipableAttribute[] attributes = (FlipableAttribute[])item.GetType().GetCustomAttributes( typeof( FlipableAttribute ), false ); + + if( attributes.Length > 0 ) + attributes[0].Flip( item ); + else + from.SendLocalizedMessage( 1042273 ); // You cannot turn that. + } + + private static void Up( Item item, Mobile from ) + { + int floorZ = GetFloorZ( item ); + + if ( floorZ > int.MinValue && item.Z < (floorZ + 15) ) // Confirmed : no height checks here + item.Location = new Point3D( item.Location, item.Z + 1 ); + else + from.SendLocalizedMessage( 1042274 ); // You cannot raise it up any higher. + } + + private static void Down( Item item, Mobile from ) + { + int floorZ = GetFloorZ( item ); + + if ( floorZ > int.MinValue && item.Z > GetFloorZ( item ) ) + item.Location = new Point3D( item.Location, item.Z - 1 ); + else + from.SendLocalizedMessage( 1042275 ); // You cannot lower it down any further. + } + + private static int GetFloorZ( Item item ) + { + Map map = item.Map; + + if ( map == null ) + return int.MinValue; + + Tile[] tiles = map.Tiles.GetStaticTiles( item.X, item.Y, true ); + + int z = int.MinValue; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + int top = tile.Z; // Confirmed : no height checks here + + if ( id.Surface && !id.Impassable && top > z && top <= item.Z ) + z = top; + } + + return z; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Key.cs b/Scripts/Items/Misc/Key.cs new file mode 100644 index 000000000..da2563dd2 --- /dev/null +++ b/Scripts/Items/Misc/Key.cs @@ -0,0 +1,468 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Targeting; +using Server.Prompts; + +namespace Server.Items +{ + public enum KeyType + { + Copper = 0x100E, + Gold = 0x100F, + Iron = 0x1010, + Rusty = 0x1013 + } + + public interface ILockable + { + bool Locked{ get; set; } + uint KeyValue{ get; set; } + } + + public class Key : Item + { + private string m_Description; + private uint m_KeyVal; + private Item m_Link; + private int m_MaxRange; + + public static uint RandomValue() + { + return (uint)(0xFFFFFFFE * Utility.RandomDouble()) + 1; + } + + public static void RemoveKeys( Mobile m, uint keyValue ) + { + if ( keyValue == 0 ) + return; + + RemoveKeys( m.Backpack, keyValue ); + RemoveKeys( m.BankBox, keyValue ); + } + + public static void RemoveKeys( Container cont, uint keyValue ) + { + if ( cont == null || keyValue == 0 ) + return; + + Item[] items = cont.FindItemsByType( new Type[] { typeof( Key ), typeof( KeyRing ) } ); + + foreach ( Item item in items ) + { + if ( item is Key ) + { + Key key = (Key) item; + + if ( key.KeyValue == keyValue ) + key.Delete(); + } + else + { + KeyRing keyRing = (KeyRing) item; + + keyRing.RemoveKeys( keyValue ); + } + } + } + + public static bool ContainsKey( Container cont, uint keyValue ) + { + if ( cont == null ) + return false; + + Item[] items = cont.FindItemsByType( new Type[] { typeof( Key ), typeof( KeyRing ) } ); + + foreach ( Item item in items ) + { + if ( item is Key ) + { + Key key = (Key) item; + + if ( key.KeyValue == keyValue ) + return true; + } + else + { + KeyRing keyRing = (KeyRing) item; + + if ( keyRing.ContainsKey( keyValue ) ) + return true; + } + } + + return false; + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Description + { + get + { + return m_Description; + } + set + { + m_Description = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxRange + { + get + { + return m_MaxRange; + } + + set + { + m_MaxRange = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public uint KeyValue + { + get + { + return m_KeyVal; + } + + set + { + m_KeyVal = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Item Link + { + get + { + return m_Link; + } + + set + { + m_Link = value; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (int) m_MaxRange ); + + writer.Write( (Item) m_Link ); + + writer.Write( (string) m_Description ); + writer.Write( (uint) m_KeyVal ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_MaxRange = reader.ReadInt(); + + goto case 1; + } + case 1: + { + m_Link = reader.ReadItem(); + + goto case 0; + } + case 0: + { + if ( version < 2 || m_MaxRange == 0 ) + m_MaxRange = 3; + + m_Description = reader.ReadString(); + + m_KeyVal = reader.ReadUInt(); + + break; + } + } + } + + [Constructable] + public Key() : this( KeyType.Iron, 0 ) + { + } + + [Constructable] + public Key( KeyType type ) : this( type, 0 ) + { + } + + [Constructable] + public Key( uint val ) : this ( KeyType.Iron, val ) + { + } + + [Constructable] + public Key( KeyType type, uint LockVal ) : this( type, LockVal, null ) + { + m_KeyVal = LockVal; + } + + public Key( KeyType type, uint LockVal, Item link ) : base( (int)type ) + { + Weight = 1.0; + + m_MaxRange = 3; + m_KeyVal = LockVal; + m_Link = link; + } + + public Key( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !this.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 501661 ); // That key is unreachable. + return; + } + + Target t; + int number; + + if ( m_KeyVal != 0 ) + { + number = 501662; // What shall I use this key on? + t = new UnlockTarget( this ); + } + else + { + number = 501663; // This key is a key blank. Which key would you like to make a copy of? + t = new CopyTarget( this ); + } + + from.SendLocalizedMessage( number ); + from.Target = t; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + string desc; + + if ( m_KeyVal == 0 ) + desc = "(blank)"; + else if ( (desc = m_Description) == null || (desc = desc.Trim()).Length <= 0 ) + desc = null; + + if ( desc != null ) + list.Add( desc ); + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + string desc; + + if ( m_KeyVal == 0 ) + desc = "(blank)"; + else if ( (desc = m_Description) == null || (desc = desc.Trim()).Length <= 0 ) + desc = ""; + + if ( desc.Length > 0 ) + from.Send( new UnicodeMessage( Serial, ItemID, MessageType.Regular, 0x3B2, 3, "ENU", "", desc ) ); + } + + public bool UseOn( Mobile from, ILockable o ) + { + if ( o.KeyValue == this.KeyValue ) + { + if ( o is BaseDoor && !((BaseDoor)o).UseLocks() ) + { + return false; + } + else + { + o.Locked = !o.Locked; + + if ( o is LockableContainer ) + { + LockableContainer cont = (LockableContainer)o; + + if ( cont.LockLevel == -255 ) + cont.LockLevel = cont.RequiredSkill - 10; + } + + if ( o is Item ) + { + Item item = (Item) o; + + if ( o.Locked ) + item.SendLocalizedMessageTo( from, 1048000 ); // You lock it. + else + item.SendLocalizedMessageTo( from, 1048001 ); // You unlock it. + + if ( item is LockableContainer ) + { + LockableContainer cont = (LockableContainer) item; + + if ( cont.TrapType != TrapType.None && cont.TrapOnLockpick ) + { + if ( o.Locked ) + item.SendLocalizedMessageTo( from, 501673 ); // You re-enable the trap. + else + item.SendLocalizedMessageTo( from, 501672 ); // You disable the trap temporarily. Lock it again to re-enable it. + } + } + } + + return true; + } + } + else + { + return false; + } + } + + private class RenamePrompt : Prompt + { + private Key m_Key; + + public RenamePrompt( Key key ) + { + m_Key = key; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Key.Deleted || !m_Key.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 501661 ); // That key is unreachable. + return; + } + + m_Key.Description = Utility.FixHtml( text ); + } + } + + private class UnlockTarget : Target + { + private Key m_Key; + + public UnlockTarget( Key key ) : base( key.MaxRange, false, TargetFlags.None ) + { + m_Key = key; + CheckLOS = false; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Key.Deleted || !m_Key.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 501661 ); // That key is unreachable. + return; + } + + int number; + + if ( targeted == m_Key ) + { + number = 501665; // Enter a description for this key. + + from.Prompt = new RenamePrompt( m_Key ); + } + else if ( targeted is ILockable ) + { + if ( m_Key.UseOn( from, (ILockable) targeted ) ) + number = -1; + else + number = 501668; // This key doesn't seem to unlock that. + } + else + { + number = 501666; // You can't unlock that! + } + + if ( number != -1 ) + { + from.SendLocalizedMessage( number ); + } + } + } + + private class CopyTarget : Target + { + private Key m_Key; + + public CopyTarget( Key key ) : base( 3, false, TargetFlags.None ) + { + m_Key = key; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Key.Deleted || !m_Key.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 501661 ); // That key is unreachable. + return; + } + + int number; + + if ( targeted is Key ) + { + Key k = (Key)targeted; + + if ( k.m_KeyVal == 0 ) + { + number = 501675; // This key is also blank. + } + else if ( from.CheckTargetSkill( SkillName.Tinkering, k, 0, 75.0 ) ) + { + number = 501676; // You make a copy of the key. + + m_Key.Description = k.Description; + m_Key.KeyValue = k.KeyValue; + m_Key.Link = k.Link; + m_Key.MaxRange = k.MaxRange; + } + else if ( Utility.RandomDouble() <= 0.1 ) // 10% chance to destroy the key + { + from.SendLocalizedMessage( 501677 ); // You fail to make a copy of the key. + + number = 501678; // The key was destroyed in the attempt. + + m_Key.Delete(); + } + else + { + number = 501677; // You fail to make a copy of the key. + } + } + else + { + number = 501688; // Not a key. + } + + from.SendLocalizedMessage( number ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/KeyRing.cs b/Scripts/Items/Misc/KeyRing.cs new file mode 100644 index 000000000..424722325 --- /dev/null +++ b/Scripts/Items/Misc/KeyRing.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; + +namespace Server.Items +{ + public class KeyRing : Item + { + public static readonly int MaxKeys = 20; + + private ArrayList m_Keys; + + public ArrayList Keys{ get{ return m_Keys; } } + + [Constructable] + public KeyRing() : base( 0x1011 ) + { + Weight = 1.0; // They seem to have no weight on OSI ?! + + m_Keys = new ArrayList(); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( !this.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1060640 ); // The item must be in your backpack to use it. + return false; + } + + Key key = dropped as Key; + + if ( key == null || key.KeyValue == 0 ) + { + from.SendLocalizedMessage( 501689 ); // Only non-blank keys can be put on a keyring. + return false; + } + else if ( this.Keys.Count >= MaxKeys ) + { + from.SendLocalizedMessage( 1008138 ); // This keyring is full. + return false; + } + else + { + Add( key ); + from.SendLocalizedMessage( 501691 ); // You put the key on the keyring. + return true; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !this.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1060640 ); // The item must be in your backpack to use it. + return; + } + + from.SendLocalizedMessage( 501680 ); // What do you want to unlock? + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private KeyRing m_KeyRing; + + public InternalTarget( KeyRing keyRing ) : base( -1, false, TargetFlags.None ) + { + m_KeyRing = keyRing; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_KeyRing.Deleted || !m_KeyRing.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1060640 ); // The item must be in your backpack to use it. + return; + } + + if ( m_KeyRing == targeted ) + { + m_KeyRing.Open( from ); + from.SendLocalizedMessage( 501685 ); // You open the keyring. + } + else if ( targeted is ILockable ) + { + ILockable o = (ILockable) targeted; + + foreach ( Key key in m_KeyRing.Keys ) + { + if ( key.UseOn( from, o ) ) + return; + } + + from.SendLocalizedMessage( 1008140 ); // You do not have a key for that. + } + else + { + from.SendLocalizedMessage( 501666 ); // You can't unlock that! + } + } + } + + public override void OnDelete() + { + base.OnDelete(); + + foreach ( Key key in m_Keys ) + { + key.Delete(); + } + + m_Keys.Clear(); + } + + public void Add( Key key ) + { + key.Internalize(); + m_Keys.Add( key ); + + UpdateItemID(); + } + + public void Open( Mobile from ) + { + Container cont = this.Parent as Container; + + if ( cont == null ) + return; + + for ( int i = m_Keys.Count - 1; i >= 0; i-- ) + { + Key key = (Key) m_Keys[i]; + + if ( !key.Deleted && !cont.TryDropItem( from, key, true ) ) + break; + + m_Keys.RemoveAt( i ); + } + + UpdateItemID(); + } + + public void RemoveKeys( uint keyValue ) + { + for ( int i = m_Keys.Count - 1; i >= 0; i-- ) + { + Key key = (Key) m_Keys[i]; + + if ( key.KeyValue == keyValue ) + { + key.Delete(); + m_Keys.RemoveAt( i ); + } + } + + UpdateItemID(); + } + + public bool ContainsKey( uint keyValue ) + { + foreach ( Key key in m_Keys ) + { + if ( key.KeyValue == keyValue ) + return true; + } + + return false; + } + + private void UpdateItemID() + { + if ( this.Keys.Count < 1 ) + this.ItemID = 0x1011; + else if ( this.Keys.Count < 3 ) + this.ItemID = 0x1769; + else if ( this.Keys.Count < 5 ) + this.ItemID = 0x176A; + else + this.ItemID = 0x176B; + } + + public KeyRing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.WriteItemList( m_Keys ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Keys = reader.ReadItemList(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/LOSBlocker.cs b/Scripts/Items/Misc/LOSBlocker.cs new file mode 100644 index 000000000..1bf0b954d --- /dev/null +++ b/Scripts/Items/Misc/LOSBlocker.cs @@ -0,0 +1,119 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class LOSBlocker : Item + { + public static void Initialize() + { + TileData.ItemTable[0x2199].Flags = TileFlag.Wall | TileFlag.NoShoot; + TileData.ItemTable[0x2199].Height = 20; + } + + public override string DefaultName + { + get { return "no line of sight"; } + } + + [Constructable] + public LOSBlocker() : base( 0x2199 ) + { + Movable = false; + } + + public LOSBlocker( Serial serial ) : base( serial ) + { + } + + public override void SendInfoTo( NetState state ) + { + Mobile mob = state.Mobile; + + if ( mob != null && mob.AccessLevel >= AccessLevel.GameMaster ) + state.Send( new GMItemPacket( this ) ); + else + state.Send( WorldPacket ); + + if ( ObjectPropertyList.Enabled ) + state.Send( OPLPacket ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public sealed class GMItemPacket : Packet + { + public GMItemPacket( Item item ) : base( 0x1A ) + { + this.EnsureCapacity( 20 ); + + // 14 base length + // +2 - Amount + // +2 - Hue + // +1 - Flags + + uint serial = (uint)item.Serial.Value; + int itemID = 0x36FF; + int amount = item.Amount; + Point3D loc = item.Location; + int x = loc.X; + int y = loc.Y; + int hue = item.Hue; + int flags = item.GetPacketFlags(); + int direction = (int)item.Direction; + + if ( amount != 0 ) + serial |= 0x80000000; + else + serial &= 0x7FFFFFFF; + + m_Stream.Write( (uint) serial ); + m_Stream.Write( (short) (itemID & 0x7FFF) ); + + if ( amount != 0 ) + m_Stream.Write( (short) amount ); + + x &= 0x7FFF; + + if ( direction != 0 ) + x |= 0x8000; + + m_Stream.Write( (short) x ); + + y &= 0x3FFF; + + if ( hue != 0 ) + y |= 0x8000; + + if ( flags != 0 ) + y |= 0x4000; + + m_Stream.Write( (short) y ); + + if ( direction != 0 ) + m_Stream.Write( (byte) direction ); + + m_Stream.Write( (sbyte) loc.Z ); + + if ( hue != 0 ) + m_Stream.Write( (ushort) hue ); + + if ( flags != 0 ) + m_Stream.Write( (byte) flags ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Moonstone.cs b/Scripts/Items/Misc/Moonstone.cs new file mode 100644 index 000000000..a37411447 --- /dev/null +++ b/Scripts/Items/Misc/Moonstone.cs @@ -0,0 +1,203 @@ +using System; +using Server; +using Server.Network; +using Server.Mobiles; + +namespace Server.Items +{ + public enum MoonstoneType + { + Felucca, Trammel + } + + public class Moonstone : Item + { + private MoonstoneType m_Type; + + [CommandProperty( AccessLevel.GameMaster )] + public MoonstoneType Type + { + get + { + return m_Type; + } + set + { + m_Type = value; + InvalidateProperties(); + } + } + + public override int LabelNumber{ get{ return 1041490 + (int)m_Type; } } + + [Constructable] + public Moonstone( MoonstoneType type ) : base( 0xF8B ) + { + Weight = 1.0; + m_Type = type; + } + + public Moonstone( Serial serial ) : base( serial ) + { + } + + public override void OnSingleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + Hue = Utility.RandomBirdHue(); + ProcessDelta(); + from.SendLocalizedMessage( 1005398 ); // The stone's substance shifts as you examine it. + } + + base.OnSingleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.Mounted ) + { + from.SendLocalizedMessage( 1005399 ); // You can not bury a stone while you sit on a mount. + } + else if ( !from.Body.IsHuman ) + { + from.SendLocalizedMessage( 1005400 ); // You can not bury a stone in this form. + } + else if ( Factions.Sigil.ExistsOn( from ) ) + { + from.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( from.Map == GetTargetMap() || ( from.Map != Map.Trammel && from.Map != Map.Felucca ) ) + { + from.SendLocalizedMessage( 1005401 ); // You cannot bury the stone here. + } + else if ( from is PlayerMobile && ((PlayerMobile)from).Young ) + { + from.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young. + } + else if ( from.Kills >= 5 ) + { + from.SendLocalizedMessage( 1005402 ); // The magic of the stone cannot be evoked by someone with blood on their hands. + } + else if ( from.Criminal ) + { + from.SendLocalizedMessage( 1005403 ); // The magic of the stone cannot be evoked by the lawless. + } + else if ( !Region.Find( from.Location, from.Map ).IsDefault || !Region.Find( from.Location, GetTargetMap() ).IsDefault ) + { + from.SendLocalizedMessage( 1005401 ); // You cannot bury the stone here. + } + else if ( !GetTargetMap().CanFit( from.Location, 16 ) ) + { + from.SendLocalizedMessage( 1005408 ); // Something is blocking the facet gate exit. + } + else + { + Movable = false; + MoveToWorld( from.Location, from.Map ); + + from.Animate( 32, 5, 1, true, false, 0 ); + + new SettleTimer( this, from.Location, from.Map, GetTargetMap(), from ).Start(); + } + } + + public Map GetTargetMap() + { + return ( m_Type == MoonstoneType.Felucca ) ? Map.Felucca : Map.Trammel; + } + + private class SettleTimer : Timer + { + private Item m_Stone; + private Point3D m_Location; + private Map m_Map, m_TargetMap; + private Mobile m_Caster; + private int m_Count; + + public SettleTimer( Item stone, Point3D loc, Map map, Map targetMap, Mobile caster ) : base( TimeSpan.FromSeconds( 2.5 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Stone = stone; + + m_Location = loc; + m_Map = map; + m_TargetMap = targetMap; + + m_Caster = caster; + } + + protected override void OnTick() + { + ++m_Count; + + if ( m_Count == 1 ) + { + m_Stone.PublicOverheadMessage( MessageType.Regular, 0x3B2, 1005414 ); // The stone settles into the ground. + } + else if ( m_Count >= 10 ) + { + m_Stone.Location = new Point3D( m_Stone.X, m_Stone.Y, m_Stone.Z - 1 ); + + if ( m_Count == 16 ) + { + if ( !Region.Find( m_Location, m_Map ).IsDefault || !Region.Find( m_Location, m_TargetMap ).IsDefault ) + { + m_Stone.Movable = true; + m_Caster.AddToBackpack( m_Stone ); + Stop(); + return; + } + else if ( !m_TargetMap.CanFit( m_Location, 16 ) ) + { + m_Stone.Movable = true; + m_Caster.AddToBackpack( m_Stone ); + Stop(); + return; + } + + int hue = m_Stone.Hue; + + if ( hue == 0 ) + hue = Utility.RandomBirdHue(); + + new MoonstoneGate( m_Location, m_TargetMap, m_Map, m_Caster, hue ); + new MoonstoneGate( m_Location, m_Map, m_TargetMap, m_Caster, hue ); + + m_Stone.Delete(); + Stop(); + } + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Type ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Type = (MoonstoneType)reader.ReadInt(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/MoonstoneGate.cs b/Scripts/Items/Misc/MoonstoneGate.cs new file mode 100644 index 000000000..c3e6c050f --- /dev/null +++ b/Scripts/Items/Misc/MoonstoneGate.cs @@ -0,0 +1,85 @@ +using System; +using Server; +using Server.Network; +using Server.Engines.PartySystem; + +namespace Server.Items +{ + public class MoonstoneGate : Moongate + { + private Mobile m_Caster; + + public MoonstoneGate( Point3D loc, Map map, Map targetMap, Mobile caster, int hue ) : base( loc, targetMap ) + { + MoveToWorld( loc, map ); + Dispellable = false; + Hue = hue; + + m_Caster = caster; + + new InternalTimer( this ).Start(); + + Effects.PlaySound( loc, map, 0x20E ); + } + + public MoonstoneGate( Serial serial ) : base( serial ) + { + } + + public override void CheckGate( Mobile m, int range ) + { + if ( m.Kills >= 5 ) + return; + + Party casterParty = Party.Get( m_Caster ); + Party userParty = Party.Get( m ); + + if ( m == m_Caster || (casterParty != null && userParty == casterParty) ) + base.CheckGate( m, range ); + } + + public override void UseGate( Mobile m ) + { + if ( m.Kills >= 5 ) + return; + + Party casterParty = Party.Get( m_Caster ); + Party userParty = Party.Get( m ); + + if ( m == m_Caster || (casterParty != null && userParty == casterParty) ) + base.UseGate( m ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + + private class InternalTimer : Timer + { + private Item m_Item; + + public InternalTimer( Item item ) : base( TimeSpan.FromSeconds( 30.0 ) ) + { + m_Item = item; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/MorphItem.cs b/Scripts/Items/Misc/MorphItem.cs new file mode 100644 index 000000000..48cf014ba --- /dev/null +++ b/Scripts/Items/Misc/MorphItem.cs @@ -0,0 +1,146 @@ +using System; + +namespace Server.Items +{ + public class MorphItem : Item + { + private int m_InactiveItemID; + private int m_ActiveItemID; + private int m_InRange; + private int m_OutRange; + + [CommandProperty( AccessLevel.GameMaster )] + public int InactiveItemID + { + get{ return m_InactiveItemID; } + set{ m_InactiveItemID = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ActiveItemID + { + get{ return m_ActiveItemID; } + set{ m_ActiveItemID = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int InRange + { + get{ return m_InRange; } + set{ if ( value > 18 ) value = 18; m_InRange = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int OutRange + { + get{ return m_OutRange; } + set{ if ( value > 18 ) value = 18; m_OutRange = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurrentRange{ get{ return ItemID == InactiveItemID ? InRange : OutRange; } } + + [Constructable] + public MorphItem( int inactiveItemID, int activeItemID, int range ) : this( inactiveItemID, activeItemID, range, range ) + { + } + + [Constructable] + public MorphItem( int inactiveItemID, int activeItemID, int inRange, int outRange ) : base( inactiveItemID ) + { + Movable = false; + + InactiveItemID = inactiveItemID; + ActiveItemID = activeItemID; + InRange = inRange; + OutRange = outRange; + } + + public MorphItem( Serial serial ) : base( serial ) + { + } + + public override bool HandlesOnMovement{ get{ return true; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( Utility.InRange( m.Location, Location, CurrentRange ) || Utility.InRange( oldLocation, Location, CurrentRange ) ) + Refresh(); + } + + public override void OnMapChange() + { + if ( !Deleted ) + Refresh(); + } + + public override void OnLocationChange( Point3D oldLoc ) + { + if ( !Deleted ) + Refresh(); + } + + public void Refresh() + { + bool found = false; + + foreach ( Mobile mob in GetMobilesInRange( CurrentRange ) ) + { + if ( mob.Hidden && mob.AccessLevel > AccessLevel.Player ) + continue; + + found = true; + break; + } + + if ( found ) + ItemID = ActiveItemID; + else + ItemID = InactiveItemID; + + Visible = ( ItemID != 0x1 ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_OutRange ); + + writer.Write( (int) m_InactiveItemID ); + writer.Write( (int) m_ActiveItemID ); + writer.Write( (int) m_InRange ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_OutRange = reader.ReadInt(); + goto case 0; + } + case 0: + { + m_InactiveItemID = reader.ReadInt(); + m_ActiveItemID = reader.ReadInt(); + m_InRange = reader.ReadInt(); + + if ( version < 1 ) + m_OutRange = m_InRange; + + break; + } + } + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Refresh ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/OilCloth.cs b/Scripts/Items/Misc/OilCloth.cs new file mode 100644 index 000000000..56a30b536 --- /dev/null +++ b/Scripts/Items/Misc/OilCloth.cs @@ -0,0 +1,142 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Items +{ + public class OilCloth : Item, IScissorable, IDyable + { + public override int LabelNumber{ get{ return 1041498; } } // oil cloth + + public override double DefaultWeight + { + get { return 1.0; } + } + + [Constructable] + public OilCloth() : this( 1 ) + { + } + + [Constructable] + public OilCloth( int amount ) : base( 0x175D ) + { + Hue = 2001; + Stackable = true; + Amount = amount; + } + + + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) + return false; + + base.ScissorHelper( from, new Bandage(), 1 ); + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( OnTarget ) ); + from.SendLocalizedMessage( 1005424 ); // Select the weapon or armor you wish to use the cloth on. + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public void OnTarget( Mobile from, object obj ) + { + // TODO: Need details on how oil cloths should get consumed here + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( obj is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)obj; + + if ( weapon.RootParent != from ) + { + from.SendLocalizedMessage( 1005425 ); // You may only wipe down items you are holding or carrying. + } + else if ( weapon.Poison == null || weapon.PoisonCharges <= 0 ) + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x3B2, 1005422 ); // Hmmmm... this does not need to be cleaned. + } + else + { + if ( weapon.PoisonCharges < 2 ) + weapon.PoisonCharges = 0; + else + weapon.PoisonCharges -= 2; + + if ( weapon.PoisonCharges > 0 ) + from.SendLocalizedMessage( 1005423 ); // You have removed some of the caustic substance, but not all. + else + from.SendLocalizedMessage( 1010497 ); // You have cleaned the item. + } + } + else if ( obj == from && obj is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)obj; + + if ( pm.BodyMod == 183 || pm.BodyMod == 184 ) + { + pm.SavagePaintExpiration = TimeSpan.Zero; + + pm.BodyMod = 0; + pm.HueMod = -1; + + from.SendLocalizedMessage( 1040006 ); // You wipe away all of your body paint. + + Consume(); + } + else + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x3B2, 1005422 ); // Hmmmm... this does not need to be cleaned. + } + } + else + { + from.SendLocalizedMessage( 1005426 ); // The cloth will not work on that. + } + } + + public OilCloth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Origami.cs b/Scripts/Items/Misc/Origami.cs new file mode 100644 index 000000000..854c7f512 --- /dev/null +++ b/Scripts/Items/Misc/Origami.cs @@ -0,0 +1,247 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class OrigamiPaper : Item + { + public override int LabelNumber{ get{ return 1030288; } } // origami paper + + [Constructable] + public OrigamiPaper() : base( 0x2830 ) + { + } + + public OrigamiPaper( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + this.Delete(); + + Item i = null; + + switch ( Utility.Random( (from.BAC >= 5) ? 6 : 5) ) + { + case 0: i = new OrigamiButterfly(); break; + case 1: i = new OrigamiSwan(); break; + case 2: i = new OrigamiFrog(); break; + case 3: i = new OrigamiShape(); break; + case 4: i = new OrigamiSongbird(); break; + case 5: i = new OrigamiFish(); break; + } + + if( i != null ) + from.AddToBackpack( i ); + + from.SendLocalizedMessage( 1070822 ); // You fold the paper into an interesting shape. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrigamiButterfly : Item + { + public override int LabelNumber{ get{ return 1030296; } } // a delicate origami butterfly + + [Constructable] + public OrigamiButterfly() : base( 0x2838 ) + { + LootType = LootType.Blessed; + } + + public OrigamiButterfly( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrigamiSwan : Item + { + public override int LabelNumber{ get{ return 1030297; } } // a delicate origami swan + + [Constructable] + public OrigamiSwan() : base( 0x2839 ) + { + LootType = LootType.Blessed; + + + } + + public OrigamiSwan( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrigamiFrog : Item + { + public override int LabelNumber{ get{ return 1030298; } } // a delicate origami frog + + [Constructable] + public OrigamiFrog() : base( 0x283A ) + { + LootType = LootType.Blessed; + + + } + + public OrigamiFrog( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrigamiShape : Item + { + public override int LabelNumber{ get{ return 1030299; } } // an intricate geometric origami shape + + [Constructable] + public OrigamiShape() : base( 0x283B ) + { + LootType = LootType.Blessed; + + + } + + public OrigamiShape( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrigamiSongbird : Item + { + public override int LabelNumber{ get{ return 1030300; } } // a delicate origami songbird + + [Constructable] + public OrigamiSongbird() : base( 0x283C ) + { + LootType = LootType.Blessed; + + + } + + public OrigamiSongbird( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class OrigamiFish : Item + { + public override int LabelNumber{ get{ return 1030301; } } // a delicate origami fish + + [Constructable] + public OrigamiFish() : base( 0x283D ) + { + LootType = LootType.Blessed; + + + } + + public OrigamiFish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/PlayerBulletinBoards.cs b/Scripts/Items/Misc/PlayerBulletinBoards.cs new file mode 100644 index 000000000..79dc60fa8 --- /dev/null +++ b/Scripts/Items/Misc/PlayerBulletinBoards.cs @@ -0,0 +1,640 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Prompts; +using Server.Mobiles; +using Server.Network; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + public class PlayerBBSouth : BasePlayerBB + { + public override int LabelNumber{ get{ return 1062421; } } // bulletin board (south) + + [Constructable] + public PlayerBBSouth() : base( 0x2311 ) + { + Weight = 15.0; + } + + public PlayerBBSouth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PlayerBBEast : BasePlayerBB + { + public override int LabelNumber{ get{ return 1062420; } } // bulletin board (east) + + [Constructable] + public PlayerBBEast() : base( 0x2312 ) + { + Weight = 15.0; + } + + public PlayerBBEast( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public abstract class BasePlayerBB : Item, ISecurable + { + private PlayerBBMessage m_Greeting; + private ArrayList m_Messages; + private string m_Title; + private SecureLevel m_Level; + + public ArrayList Messages + { + get{ return m_Messages; } + } + + public PlayerBBMessage Greeting + { + get{ return m_Greeting; } + set{ m_Greeting = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Title + { + get{ return m_Title; } + set{ m_Title = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + public BasePlayerBB( int itemID ) : base( itemID ) + { + m_Messages = new ArrayList(); + m_Level = SecureLevel.Anyone; + } + + public BasePlayerBB( Serial serial ) : base( serial ) + { + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + + writer.Write( (int) m_Level ); + + writer.Write( m_Title ); + + if ( m_Greeting != null ) + { + writer.Write( true ); + m_Greeting.Serialize( writer ); + } + else + { + writer.Write( false ); + } + + writer.WriteEncodedInt( m_Messages.Count ); + + for ( int i = 0; i < m_Messages.Count; ++i ) + ((PlayerBBMessage)m_Messages[i]).Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + goto case 0; + } + case 0: + { + if ( version < 1 ) + m_Level = SecureLevel.Anyone; + + m_Title = reader.ReadString(); + + if ( reader.ReadBool() ) + m_Greeting = new PlayerBBMessage( reader ); + + int count = reader.ReadEncodedInt(); + + m_Messages = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + m_Messages.Add( new PlayerBBMessage( reader ) ); + + break; + } + } + } + + public static bool CheckAccess( BaseHouse house, Mobile from ) + { + if ( house.Public || !house.IsAosRules ) + return !house.IsBanned( from ); + + return house.HasAccess( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house == null || !house.IsLockedDown( this ) ) + from.SendLocalizedMessage( 1062396 ); // This bulletin board must be locked down in a house to be usable. + else if ( !from.InRange( this.GetWorldLocation(), 2 ) || !from.InLOS( this ) ) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else if ( CheckAccess( house, from ) ) + from.SendGump( new PlayerBBGump( from, house, this, 0 ) ); + } + + public class PostPrompt : Prompt + { + private int m_Page; + private BaseHouse m_House; + private BasePlayerBB m_Board; + private bool m_Greeting; + + public PostPrompt( int page, BaseHouse house, BasePlayerBB board, bool greeting ) + { + m_Page = page; + m_House = house; + m_Board = board; + m_Greeting = greeting; + } + + public override void OnCancel( Mobile from ) + { + OnResponse( from, "" ); + } + + public override void OnResponse( Mobile from, string text ) + { + int page = m_Page; + BaseHouse house = m_House; + BasePlayerBB board = m_Board; + + if ( house == null || !house.IsLockedDown( board ) ) + { + from.SendLocalizedMessage( 1062396 ); // This bulletin board must be locked down in a house to be usable. + return; + } + else if ( !from.InRange( board.GetWorldLocation(), 2 ) || !from.InLOS( board ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + else if ( !CheckAccess( house, from ) ) + { + from.SendLocalizedMessage( 1062398 ); // You are not allowed to post to this bulletin board. + return; + } + else if ( m_Greeting && !house.IsOwner( from ) ) + { + return; + } + + text = text.Trim(); + + if ( text.Length > 255 ) + text = text.Substring( 0, 255 ); + + if ( text.Length > 0 ) + { + PlayerBBMessage message = new PlayerBBMessage( DateTime.Now, from, text ); + + if ( m_Greeting ) + { + board.Greeting = message; + } + else + { + board.Messages.Add( message ); + + if ( board.Messages.Count > 50 ) + { + board.Messages.RemoveAt( 0 ); + + if ( page > 0 ) + --page; + } + } + } + + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + } + } + + public class SetTitlePrompt : Prompt + { + private int m_Page; + private BaseHouse m_House; + private BasePlayerBB m_Board; + + public SetTitlePrompt( int page, BaseHouse house, BasePlayerBB board ) + { + m_Page = page; + m_House = house; + m_Board = board; + } + + public override void OnCancel( Mobile from ) + { + OnResponse( from, "" ); + } + + public override void OnResponse( Mobile from, string text ) + { + int page = m_Page; + BaseHouse house = m_House; + BasePlayerBB board = m_Board; + + if ( house == null || !house.IsLockedDown( board ) ) + { + from.SendLocalizedMessage( 1062396 ); // This bulletin board must be locked down in a house to be usable. + return; + } + else if ( !from.InRange( board.GetWorldLocation(), 2 ) || !from.InLOS( board ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + else if ( !CheckAccess( house, from ) ) + { + from.SendLocalizedMessage( 1062398 ); // You are not allowed to post to this bulletin board. + return; + } + + text = text.Trim(); + + if ( text.Length > 255 ) + text = text.Substring( 0, 255 ); + + if ( text.Length > 0 ) + board.Title = text; + + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + } + } + } + + public class PlayerBBMessage + { + private DateTime m_Time; + private Mobile m_Poster; + private string m_Message; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime Time + { + get{ return m_Time; } + set{ m_Time = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Poster + { + get{ return m_Poster; } + set{ m_Poster = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Message + { + get{ return m_Message; } + set{ m_Message = value; } + } + + public PlayerBBMessage( DateTime time, Mobile poster, string message ) + { + m_Time = time; + m_Poster = poster; + m_Message = message; + } + + public PlayerBBMessage( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 0: + { + m_Time = reader.ReadDateTime(); + m_Poster = reader.ReadMobile(); + m_Message = reader.ReadString(); + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( m_Time ); + writer.Write( m_Poster ); + writer.Write( m_Message ); + } + } + + public class PlayerBBGump : Gump + { + private int m_Page; + private Mobile m_From; + private BaseHouse m_House; + private BasePlayerBB m_Board; + + private const int LabelColor = 0x7FFF; + private const int LabelHue = 1153; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int page = m_Page; + Mobile from = m_From; + BaseHouse house = m_House; + BasePlayerBB board = m_Board; + + if ( house == null || !house.IsLockedDown( board ) ) + { + from.SendLocalizedMessage( 1062396 ); // This bulletin board must be locked down in a house to be usable. + return; + } + else if ( !from.InRange( board.GetWorldLocation(), 2 ) || !from.InLOS( board ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + else if ( !BasePlayerBB.CheckAccess( house, from ) ) + { + from.SendLocalizedMessage( 1062398 ); // You are not allowed to post to this bulletin board. + return; + } + + switch ( info.ButtonID ) + { + case 1: // Post message + { + from.Prompt = new BasePlayerBB.PostPrompt( page, house, board, false ); + from.SendLocalizedMessage( 1062397 ); // Please enter your message: + + break; + } + case 2: // Set title + { + if ( house.IsOwner( from ) ) + { + from.Prompt = new BasePlayerBB.SetTitlePrompt( page, house, board ); + from.SendLocalizedMessage( 1062402 ); // Enter new title: + } + + break; + } + case 3: // Post greeting + { + if ( house.IsOwner( from ) ) + { + from.Prompt = new BasePlayerBB.PostPrompt( page, house, board, true ); + from.SendLocalizedMessage( 1062404 ); // Enter new greeting (this will always be the first post): + } + + break; + } + case 4: // Scroll up + { + if ( page == 0 ) + page = board.Messages.Count; + else + page -= 1; + + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + + break; + } + case 5: // Scroll down + { + page += 1; + page %= board.Messages.Count + 1; + + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + + break; + } + case 6: // Banish poster + { + if ( house.IsOwner( from ) ) + { + if ( page >= 1 && page <= board.Messages.Count ) + { + PlayerBBMessage message = (PlayerBBMessage)board.Messages[page - 1]; + Mobile poster = message.Poster; + + if ( poster == null ) + { + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + return; + } + + if ( poster.AccessLevel > AccessLevel.Player && from.AccessLevel <= poster.AccessLevel ) + { + from.SendLocalizedMessage( 501354 ); // Uh oh...a bigger boot may be required. + } + else if ( house.IsFriend( poster ) ) + { + from.SendLocalizedMessage( 1060750 ); // That person is a friend, co-owner, or owner of this house, and therefore cannot be banished! + } + else if ( poster is PlayerVendor ) + { + from.SendLocalizedMessage( 501351 ); // You cannot eject a vendor. + } + else if ( house.Bans.Count >= BaseHouse.MaxBans ) + { + from.SendLocalizedMessage( 501355 ); // The ban limit for this house has been reached! + } + else if ( house.IsBanned( poster ) ) + { + from.SendLocalizedMessage( 501356 ); // This person is already banned! + } + else if ( poster is BaseCreature && ((BaseCreature)poster).NoHouseRestrictions ) + { + from.SendLocalizedMessage( 1062040 ); // You cannot ban that. + } + else + { + if ( !house.Bans.Contains( poster ) ) + house.Bans.Add( poster ); + + from.SendLocalizedMessage( 1062417 ); // That person has been banned from this house. + + if ( house.IsInside( poster ) && !BasePlayerBB.CheckAccess( house, poster ) ) + poster.MoveToWorld( house.BanLocation, house.Map ); + } + } + + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + } + + break; + } + case 7: // Delete message + { + if ( house.IsOwner( from ) ) + { + if ( page >= 1 && page <= board.Messages.Count ) + board.Messages.RemoveAt( page - 1 ); + + from.SendGump( new PlayerBBGump( from, house, board, 0 ) ); + } + + break; + } + case 8: // Post props + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + PlayerBBMessage message = board.Greeting; + + if ( page >= 1 && page <= board.Messages.Count ) + message = (PlayerBBMessage)board.Messages[page - 1]; + + from.SendGump( new PlayerBBGump( from, house, board, page ) ); + from.SendGump( new PropertiesGump( from, message ) ); + } + + break; + } + } + } + + public PlayerBBGump( Mobile from, BaseHouse house, BasePlayerBB board, int page ) : base( 50, 10 ) + { + from.CloseGump( typeof( PlayerBBGump ) ); + + m_Page = page; + m_From = from; + m_House = house; + m_Board = board; + + AddPage( 0 ); + + AddImage( 30, 30, 5400 ); + + AddButton( 393, 145, 2084, 2084, 4, GumpButtonType.Reply, 0 ); // Scroll up + AddButton( 390, 371, 2085, 2085, 5, GumpButtonType.Reply, 0 ); // Scroll down + + AddButton( 32, 183, 5412, 5413, 1, GumpButtonType.Reply, 0 ); // Post message + + if ( house.IsOwner( from ) ) + { + AddButton( 63, 90, 5601, 5605, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 81, 89, 230, 20, 1062400, LabelColor, false, false ); // Set title + + AddButton( 63, 109, 5601, 5605, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 81, 108, 230, 20, 1062401, LabelColor, false, false ); // Post greeting + } + + string title = board.Title; + + if ( title != null ) + AddHtml( 183, 68, 180, 23, title, false, false ); + + AddHtmlLocalized( 385, 89, 60, 20, 1062409, LabelColor, false, false ); // Post + + AddLabel( 440, 89, LabelHue, page.ToString() ); + AddLabel( 455, 89, LabelHue, "/" ); + AddLabel( 470, 89, LabelHue, board.Messages.Count.ToString() ); + + PlayerBBMessage message = board.Greeting; + + if ( page >= 1 && page <= board.Messages.Count ) + message = (PlayerBBMessage)board.Messages[page - 1]; + + AddImageTiled( 150, 220, 240, 1, 2700 ); // Seperator + + AddHtmlLocalized( 150, 180, 100, 20, 1062405, 16715, false, false ); // Posted On: + AddHtmlLocalized( 150, 200, 100, 20, 1062406, 16715, false, false ); // Posted By: + + if ( message != null ) + { + AddHtml( 255, 180, 150, 20, message.Time.ToString( "yyyy-MM-dd HH:mm:ss" ), false, false ); + + Mobile poster = message.Poster; + string name = ( poster == null ? null : poster.Name ); + + if ( name == null || (name = name.Trim()).Length == 0 ) + name = "Someone"; + + AddHtml( 255, 200, 150, 20, name, false, false ); + + string body = message.Message; + + if ( body == null ) + body = ""; + + AddHtml( 150, 240, 250, 100, body, false, false ); + + if ( message != board.Greeting && house.IsOwner( from ) ) + { + AddButton( 130, 395, 1209, 1210, 6, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 150, 393, 150, 20, 1062410, LabelColor, false, false ); // Banish Poster + + AddButton( 310, 395, 1209, 1210, 7, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 330, 393, 150, 20, 1062411, LabelColor, false, false ); // Delete Message + } + + if ( from.AccessLevel >= AccessLevel.GameMaster ) + AddButton( 135, 242, 1209, 1210, 8, GumpButtonType.Reply, 0 ); // Post props + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/PlayerVendorDeed.cs b/Scripts/Items/Misc/PlayerVendorDeed.cs new file mode 100644 index 000000000..0226db102 --- /dev/null +++ b/Scripts/Items/Misc/PlayerVendorDeed.cs @@ -0,0 +1,104 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Multis; + +namespace Server.Items +{ + public class ContractOfEmployment : Item + { + public override int LabelNumber{ get{ return 1041243; } } // a contract of employment + + [Constructable] + public ContractOfEmployment() : base( 0x14F0 ) + { + Weight = 1.0; + //LootType = LootType.Blessed; + } + + public ContractOfEmployment( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); //version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + from.SendLocalizedMessage( 503248 ); // Your godly powers allow you to place this vendor whereever you wish. + + Mobile v = new PlayerVendor( from, BaseHouse.FindHouseAt( from ) ); + + v.Direction = from.Direction & Direction.Mask; + v.MoveToWorld( from.Location, from.Map ); + + v.SayTo( from, 503246 ); // Ah! it feels good to be working again. + + this.Delete(); + } + else + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house == null ) + { + from.SendLocalizedMessage( 503240 ); // Vendors can only be placed in houses. + } + else if ( !BaseHouse.NewVendorSystem && !house.IsFriend( from ) ) + { + from.SendLocalizedMessage( 503242 ); // You must ask the owner of this building to name you a friend of the household in order to place a vendor here. + } + else if ( BaseHouse.NewVendorSystem && !house.IsOwner( from ) ) + { + from.SendLocalizedMessage( 1062423 ); // Only the house owner can directly place vendors. Please ask the house owner to offer you a vendor contract so that you may place a vendor in this house. + } + else if ( !house.Public || !house.CanPlaceNewVendor() ) + { + from.SendLocalizedMessage( 503241 ); // You cannot place this vendor or barkeep. Make sure the house is public and has sufficient storage available. + } + else + { + bool vendor, contract; + BaseHouse.IsThereVendor( from.Location, from.Map, out vendor, out contract ); + + if ( vendor ) + { + from.SendLocalizedMessage( 1062677 ); // You cannot place a vendor or barkeep at this location. + } + else if ( contract ) + { + from.SendLocalizedMessage( 1062678 ); // You cannot place a vendor or barkeep on top of a rental contract! + } + else + { + Mobile v = new PlayerVendor( from, house ); + + v.Direction = from.Direction & Direction.Mask; + v.MoveToWorld( from.Location, from.Map ); + + v.SayTo( from, 503246 ); // Ah! it feels good to be working again. + + this.Delete(); + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/PoolOfAcid.cs b/Scripts/Items/Misc/PoolOfAcid.cs new file mode 100644 index 000000000..f58cd73d9 --- /dev/null +++ b/Scripts/Items/Misc/PoolOfAcid.cs @@ -0,0 +1,161 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Spells; +using System.Collections; + +namespace Server.Items +{ + public class PoolOfAcid : Item + { + private TimeSpan m_Duration; + private int m_MinDamage; + private int m_MaxDamage; + + private DateTime m_Created; + + private bool m_Drying; + + private Timer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Drying + { + get + { + return m_Drying; + } + set + { + m_Drying = value; + + if( m_Drying ) + ItemID = 0x122A; + else + ItemID = 0x122B; + } + } + + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan Duration{ get{ return m_Duration; } set{ m_Duration = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int MinDamage + { + get + { + return m_MinDamage; + } + set + { + if ( value < 1 ) + value = 1; + + m_MinDamage = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxDamage + { + get + { + return m_MaxDamage; + } + set + { + if ( value < 1 ) + value = 1; + + if ( value < MinDamage ) + value = MinDamage; + + m_MaxDamage = value; + } + } + + [Constructable] + public PoolOfAcid() : this( TimeSpan.FromSeconds( 10.0 ), 2, 5 ) + { + } + + public override string DefaultName { get { return "a pool of acid"; } } + + [Constructable] + public PoolOfAcid( TimeSpan duration, int minDamage, int maxDamage ) + : base( 0x122A ) + { + Hue = 0x3F; + Movable = false; + + m_MinDamage = minDamage; + m_MaxDamage = maxDamage; + m_Created = DateTime.Now; + m_Duration = duration; + + m_Timer = Timer.DelayCall( TimeSpan.Zero, TimeSpan.FromSeconds( 1 ), new TimerCallback( OnTick ) ); + } + + public override void OnAfterDelete() + { + if( m_Timer != null ) + m_Timer.Stop(); + } + + private void OnTick() + { + DateTime now = DateTime.Now; + TimeSpan age = now - m_Created; + + if( age > m_Duration ) + Delete(); + else + { + if( !Drying && age > (m_Duration - age) ) + Drying = true; + + ArrayList toDamage = new ArrayList(); + + foreach( Mobile m in GetMobilesInRange( 0 ) ) + { + BaseCreature bc = m as BaseCreature; + + if( m.Alive && !m.IsDeadBondedPet && (bc == null || bc.Controlled || bc.Summoned) ) + { + toDamage.Add( m ); + } + } + + for( int i = 0; i < toDamage.Count; i++ ) + Damage( (Mobile)toDamage[i] ); + + } + } + + + public override bool OnMoveOver( Mobile m ) + { + Damage( m ); + return true; + } + + public void Damage( Mobile m ) + { + m.Damage( Utility.RandomMinMax( MinDamage, MaxDamage ) ); + } + + public PoolOfAcid( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + //Don't serialize these + } + + public override void Deserialize( GenericReader reader ) + { + } + } +} diff --git a/Scripts/Items/Misc/PowerCrystal.cs b/Scripts/Items/Misc/PowerCrystal.cs new file mode 100644 index 000000000..ba052232e --- /dev/null +++ b/Scripts/Items/Misc/PowerCrystal.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + public class PowerCrystal : Item + { + public override string DefaultName + { + get { return "power crystal"; } + } + + [Constructable] + public PowerCrystal() : base( 0x1F1C ) + { + Weight = 1.0; + } + + public PowerCrystal( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( this.GetWorldLocation(), 3 )) + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + else + from.SendAsciiMessage( "This looks like part of a larger contraption." ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/PowerGenerator.cs b/Scripts/Items/Misc/PowerGenerator.cs new file mode 100644 index 000000000..2543eab92 --- /dev/null +++ b/Scripts/Items/Misc/PowerGenerator.cs @@ -0,0 +1,571 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class PowerGenerator : BaseAddon + { + [Constructable] + public PowerGenerator() : this( Utility.RandomMinMax( 3, 6 ) ) + { + } + + [Constructable] + public PowerGenerator( int sideLength ) + { + AddGeneratorComponent( 0x4FA1, 0, 0, 0 ); + AddGeneratorComponent( 0x76, -1, 0, 0 ); + AddGeneratorComponent( 0x75, 0, -1, 0 ); + AddGeneratorComponent( 0x37F4, 0, 0, 13 ); + + AddComponent( new ControlPanel( sideLength ), 1, 0, -2 ); + } + + public override bool ShareHue{ get{ return false; } } + + private void AddGeneratorComponent( int itemID, int x, int y, int z ) + { + AddonComponent component = new AddonComponent( itemID ); + component.Name = "a power generator"; + component.Hue = 0x451; + + AddComponent( component, x, y, z ); + } + + public PowerGenerator( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ControlPanel : AddonComponent + { + private static readonly TimeSpan m_UseTimeout = TimeSpan.FromMinutes( 2.0 ); + + public struct Node + { + private int m_X; + private int m_Y; + + public int X{ get{ return m_X; } set{ m_X = value; } } + public int Y{ get{ return m_Y; } set{ m_Y = value; } } + + public Node( int x, int y ) + { + m_X = x; + m_Y = y; + } + } + + private int m_SideLength; + private Node[] m_Path; + + [CommandProperty( AccessLevel.GameMaster )] + public int SideLength + { + get{ return m_SideLength; } + set + { + if ( value < 3 ) + value = 3; + else if ( value > 6 ) + value = 6; + + if ( m_SideLength != value ) + { + m_SideLength = value; + InitPath(); + } + } + } + + public Node[] Path{ get{ return m_Path; } } + + public override string DefaultName + { + get { return "a control panel"; } + } + + public ControlPanel( int sideLength ) : base( 0xBDC ) + { + Hue = 0x835; + + SideLength = sideLength; + } + + private enum PathDirection + { + Left, + Up, + Right, + Down + } + + public void InitPath() + { + // Depth-First Search algorithm + + int totalNodes = SideLength * SideLength; + + Node[] stack = new Node[totalNodes]; + Node current = stack[0] = new Node( 0, 0 ); + int stackSize = 1; + + bool[,] visited = new bool[SideLength, SideLength]; + visited[0, 0] = true; + + while ( true ) + { + PathDirection[] choices = new PathDirection[4]; + int count = 0; + + if ( current.X > 0 && !visited[current.X - 1, current.Y] ) + choices[count++] = PathDirection.Left; + + if ( current.Y > 0 && !visited[current.X, current.Y - 1] ) + choices[count++] = PathDirection.Up; + + if ( current.X < SideLength - 1 && !visited[current.X + 1, current.Y] ) + choices[count++] = PathDirection.Right; + + if ( current.Y < SideLength - 1 && !visited[current.X, current.Y + 1] ) + choices[count++] = PathDirection.Down; + + if ( count > 0 ) + { + PathDirection dir = choices[Utility.Random( count )]; + + switch ( dir ) + { + case PathDirection.Left: + current = new Node( current.X - 1, current.Y ); + break; + case PathDirection.Up: + current = new Node( current.X, current.Y - 1 ); + break; + case PathDirection.Right: + current = new Node( current.X + 1, current.Y ); + break; + default: + current = new Node( current.X, current.Y + 1 ); + break; + } + + stack[stackSize++] = current; + + if ( current.X == SideLength - 1 && current.Y == SideLength - 1 ) + break; + + visited[current.X, current.Y] = true; + } + else + { + current = stack[--stackSize - 1]; + } + } + + m_Path = new Node[stackSize]; + + for ( int i = 0; i < stackSize; i++ ) + { + m_Path[i] = stack[i]; + } + + if ( m_User != null ) + { + m_User.CloseGump( typeof( GameGump ) ); + m_User = null; + } + } + + private Mobile m_User; + private DateTime m_LastUse; + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( this, 3 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + return; + } + + if ( m_User != null ) + { + if ( m_User == from ) + return; + + if ( m_User.Deleted || m_User.Map != Map || !m_User.InRange( this, 3 ) + || m_User.NetState == null || DateTime.Now - m_LastUse >= m_UseTimeout ) + { + m_User.CloseGump( typeof( GameGump ) ); + } + else + { + from.SendMessage( "Someone is currently using the control panel." ); + return; + } + } + + m_User = from; + m_LastUse = DateTime.Now; + + from.SendGump( new GameGump( this, from, 0, false ) ); + } + + private class GameGump : Gump + { + private enum NodeHue + { + Gray, + Blue, + Red + } + + private ControlPanel m_Panel; + private Mobile m_From; + private int m_Step; + + public GameGump( ControlPanel panel, Mobile from, int step, bool hint ) : base( 5, 30 ) + { + m_Panel = panel; + m_From = from; + m_Step = step; + + int sideLength = panel.SideLength; + + AddBackground( 50, 0, 530, 410, 0xA28 ); + + AddImage( 0, 0, 0x28C8 ); + AddImage( 547, 0, 0x28C9 ); + + AddBackground( 95, 20, 442, 90, 0xA28 ); + + AddHtml( 229, 35, 300, 45, "GENERATOR CONTROL PANEL", false, false ); + + AddHtml( 223, 60, 300, 70, "Use the Directional Controls to", false, false ); + AddHtml( 253, 75, 300, 85, "Close the Grid Circuit", false, false ); + + AddImage( 140, 40, 0x28D3 ); + AddImage( 420, 40, 0x28D3 ); + + AddBackground( 365, 120, 178, 210, 0x1400 ); + + AddImage( 365, 115, 0x28D4 ); + AddImage( 365, 288, 0x28D4 ); + + AddImage( 414, 189, 0x589 ); + AddImage( 435, 210, 0xA52 ); + + AddButton( 408, 222, 0x29EA, 0x29EC, 1, GumpButtonType.Reply, 0 ); // Left + AddButton( 448, 185, 0x29CC, 0x29CE, 2, GumpButtonType.Reply, 0 ); // Up + AddButton( 473, 222, 0x29D6, 0x29D8, 3, GumpButtonType.Reply, 0 ); // Right + AddButton( 448, 243, 0x29E0, 0x29E2, 4, GumpButtonType.Reply, 0 ); // Down + + AddBackground( 90, 115, 30 + 40 * sideLength, 30 + 40 * sideLength, 0xA28 ); + AddBackground( 100, 125, 10 + 40 * sideLength, 10 + 40 * sideLength, 0x1400 ); + + for ( int i = 0; i < sideLength; i++ ) + { + for ( int j = 0; j < sideLength - 1; j++ ) + { + AddImage( 120 + 40 * i, 162 + 40 * j, 0x13F9 ); + } + } + + for ( int i = 0; i < sideLength - 1; i++ ) + { + for ( int j = 0; j < sideLength; j++ ) + { + AddImage( 138 + 40 * i, 147 + 40 * j, 0x13FD ); + } + } + + Node[] path = panel.Path; + + NodeHue[,] hues = new NodeHue[sideLength, sideLength]; + + for ( int i = 0; i <= step; i++ ) + { + Node n = path[i]; + hues[n.X, n.Y] = NodeHue.Blue; + } + + Node lastNode = path[path.Length - 1]; + hues[lastNode.X, lastNode.Y] = NodeHue.Red; + + for ( int i = 0; i < sideLength; i++ ) + { + for ( int j = 0; j < sideLength; j++ ) + { + AddNode( 110 + 40 * i, 135 + 40 * j, hues[i, j] ); + } + } + + Node curNode = path[step]; + AddImage( 118 + 40 * curNode.X, 143 + 40 * curNode.Y, 0x13A8 ); + + if ( hint ) + { + Node nextNode = path[step + 1]; + AddImage( 119 + 40 * nextNode.X, 143 + 40 * nextNode.Y, 0x939 ); + } + + if ( from.Skills.Lockpicking.Value >= 65.0 ) + { + AddButton( 365, 350, 0xFA6, 0xFA7, 5, GumpButtonType.Reply, 0 ); + AddHtml( 405, 345, 140, 40, "Attempt to Decipher the Circuit Path", false, false ); + } + } + + private void AddNode( int x, int y, NodeHue hue ) + { + int id; + switch ( hue ) + { + case NodeHue.Gray: id = 0x25F8; break; + case NodeHue.Blue: id = 0x868; break; + default: id = 0x9A8; break; + } + + AddImage( x, y, id ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Panel.Deleted || info.ButtonID == 0 || !m_From.CheckAlive() ) + { + m_Panel.m_User = null; + return; + } + + if ( m_From.Map != m_Panel.Map || !m_From.InRange( m_Panel, 3 ) ) + { + m_From.SendLocalizedMessage( 500446 ); // That is too far away. + m_Panel.m_User = null; + return; + } + + Node nextNode = m_Panel.Path[m_Step + 1]; + + if ( info.ButtonID == 5 ) // Attempt to Decipher + { + double lockpicking = m_From.Skills.Lockpicking.Value; + + if ( lockpicking < 65.0 ) + return; + + m_From.PlaySound( 0x241 ); + + if ( 40.0 + Utility.RandomDouble() * 80.0 < lockpicking ) + { + m_From.SendGump( new GameGump( m_Panel, m_From, m_Step, true ) ); + m_Panel.m_LastUse = DateTime.Now; + } + else + { + m_Panel.DoDamage( m_From ); + m_Panel.m_User = null; + } + } + else + { + Node curNode = m_Panel.Path[m_Step]; + + int newX, newY; + switch ( info.ButtonID ) + { + case 1: // Left + newX = curNode.X - 1; + newY = curNode.Y; + break; + case 2: // Up + newX = curNode.X; + newY = curNode.Y - 1; + break; + case 3: // Right + newX = curNode.X + 1; + newY = curNode.Y; + break; + case 4: // Down + newX = curNode.X; + newY = curNode.Y + 1; + break; + + default: + return; + } + + if ( nextNode.X == newX && nextNode.Y == newY ) + { + if ( m_Step + 1 == m_Panel.Path.Length - 1 ) + { + m_Panel.Solve( m_From ); + m_Panel.m_User = null; + } + else + { + m_From.PlaySound( 0x1F4 ); + m_From.SendGump( new GameGump( m_Panel, m_From, m_Step + 1, false ) ); + m_Panel.m_LastUse = DateTime.Now; + } + } + else + { + m_Panel.DoDamage( m_From ); + m_Panel.m_User = null; + } + } + } + } + + private Hashtable m_DamageTable = new Hashtable(); + + public void DoDamage( Mobile to ) + { + to.Send( new UnicodeMessage( Serial, ItemID, MessageType.Regular, 0x3B2, 3, "", "", "The generator shoots an arc of electricity at you!" ) ); + to.BoltEffect( 0 ); + to.LocalOverheadMessage( MessageType.Regular, 0xC9, true, "* Your body convulses from electric shock *" ); + to.NonlocalOverheadMessage( MessageType.Regular, 0xC9, true, string.Format( "* {0} spasms from electric shock *", to.Name ) ); + + AOS.Damage( to, to, 60, 0, 0, 0, 0, 100 ); + + if ( !to.Alive ) + return; + + if ( m_DamageTable[to] == null ) + { + to.Frozen = true; + + DamageTimer timer = new DamageTimer( this, to ); + m_DamageTable[to] = timer; + + timer.Start(); + } + } + + private class DamageTimer : Timer + { + private ControlPanel m_Panel; + private Mobile m_To; + private int m_Step; + + public DamageTimer( ControlPanel panel, Mobile to ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Panel = panel; + m_To = to; + m_Step = 0; + + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + if ( m_Panel.Deleted || m_To.Deleted || !m_To.Alive ) + { + End(); + return; + } + + m_To.PlaySound( 0x28 ); + + m_To.LocalOverheadMessage( MessageType.Regular, 0xC9, true, "* Your body convulses from electric shock *" ); + m_To.NonlocalOverheadMessage( MessageType.Regular, 0xC9, true, string.Format( "* {0} spasms from electric shock *", m_To.Name ) ); + + AOS.Damage( m_To, m_To, 20, 0, 0, 0, 0, 100 ); + + if ( ++m_Step >= 3 || !m_To.Alive ) + { + End(); + } + } + + private void End() + { + m_Panel.m_DamageTable.Remove( m_To ); + m_To.Frozen = false; + + Stop(); + } + } + + public void Solve( Mobile from ) + { + Effects.PlaySound( Location, Map, 0x211 ); + Effects.PlaySound( Location, Map, 0x1F3 ); + + Effects.SendLocationEffect( Location, Map, 0x36B0, 4, 4 ); + Effects.SendLocationEffect( new Point3D( X - 1, Y - 1, Z + 2 ), Map, 0x36B0, 4, 4 ); + Effects.SendLocationEffect( new Point3D( X - 2, Y - 1, Z + 2 ), Map, 0x36B0, 4, 4 ); + + from.SendMessage( "You scrounge some gems from the wreckage." ); + + for ( int i = 0; i < SideLength; i++ ) + { + from.AddToBackpack( new ArcaneGem() ); + } + + from.AddToBackpack( new Diamond( SideLength ) ); + + Item ore = new ShadowIronOre( 9 ); + ore.MoveToWorld( new Point3D( X - 1, Y, Z + 2 ), Map ); + + ore = new ShadowIronOre( 14 ); + ore.MoveToWorld( new Point3D( X - 2, Y - 1, Z + 2 ), Map ); + + Delete(); + } + + public ControlPanel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_SideLength ); + + writer.WriteEncodedInt( (int) m_Path.Length ); + for ( int i = 0; i < m_Path.Length; i++ ) + { + Node cur = m_Path[i]; + + writer.WriteEncodedInt( cur.X ); + writer.WriteEncodedInt( cur.Y ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_SideLength = reader.ReadEncodedInt(); + + m_Path = new Node[reader.ReadEncodedInt()]; + for ( int i = 0; i < m_Path.Length; i++ ) + { + m_Path[i] = new Node( reader.ReadEncodedInt(), reader.ReadEncodedInt() ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/PromotionalToken.cs b/Scripts/Items/Misc/PromotionalToken.cs new file mode 100644 index 000000000..f8472ce3c --- /dev/null +++ b/Scripts/Items/Misc/PromotionalToken.cs @@ -0,0 +1,159 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public abstract class PromotionalToken : Item + { + public abstract Item CreateItemFor( Mobile from ); + + public abstract TextDefinition ItemName{ get; } + public abstract TextDefinition ItemRecieveMessage { get; } + public abstract TextDefinition ItemGumpName { get; } + + public PromotionalToken() : base( 0x2AAA ) + { + LootType = LootType.Blessed; + Light = LightType.Circle300; + Weight = 5.0; + } + + public PromotionalToken( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070998, ItemName.ToString() ); // Use this to redeem
your ~1_PROMO~ + } + + public override void OnDoubleClick( Mobile from ) + { + if( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062334 ); // This item must be in your backpack to be used. + } + else + { + from.CloseGump( typeof( PromotionalTokenGump ) ); + from.SendGump( new PromotionalTokenGump( this ) ); + } + } + + public override void OnRemoved( object parent ) + { + Mobile m = null; + + if( parent is Item ) + m = ((Item)parent).RootParent as Mobile; + else if( parent is Mobile ) + m = (Mobile)parent; + + if( m != null ) + m.CloseGump( typeof( PromotionalTokenGump ) ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int LabelNumber { get { return 1070997; } } // A promotional token + + + private class PromotionalTokenGump : Gump + { + private PromotionalToken m_Token; + + public PromotionalTokenGump( PromotionalToken token ) : base( 10, 10 ) + { + m_Token = token; + + AddPage( 0 ); + + AddBackground( 0, 0, 240, 135, 0x2422 ); + AddHtmlLocalized( 15, 15, 210, 75, 1070972, 0x0, true, false ); // Click "OKAY" to redeem the following promotional item: + TextDefinition.AddHtmlText( this, 15, 60, 210, 75, m_Token.ItemGumpName, false, false ); + + AddButton( 160, 95, 0xF7, 0xF8, 1, GumpButtonType.Reply, 0 ); //Okay + AddButton( 90, 95, 0xF2, 0xF1, 0, GumpButtonType.Reply, 0 ); //Cancel + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if( info.ButtonID != 1 ) + return; + + Mobile from = sender.Mobile; + + if( !m_Token.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062334 ); // This item must be in your backpack to be used. + } + else + { + Item i = m_Token.CreateItemFor( from ); + + if( i != null ) + { + from.BankBox.AddItem( i ); + TextDefinition.SendMessageTo( from, m_Token.ItemRecieveMessage ); + m_Token.Delete(); + } + } + } + } + } + + public class SoulstoneFragmentToken : PromotionalToken + { + + public override Item CreateItemFor( Mobile from ) + { + if( from != null && from.Account != null ) + return new SoulstoneFragment( from.Account.ToString() ); + else + return null; + } + + public override TextDefinition ItemGumpName{ get{ return 1070999; } }//
Soulstone Fragment
+ public override TextDefinition ItemName { get { return 1071000; } }//soulstone fragment + public override TextDefinition ItemRecieveMessage{ get{ return 1070976; } } // A soulstone fragment has been created in your bank box. + + [Constructable] + public SoulstoneFragmentToken() : base() + { + } + + public SoulstoneFragmentToken( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Misc/PublicMoongate.cs b/Scripts/Items/Misc/PublicMoongate.cs new file mode 100644 index 000000000..3c6f7ad56 --- /dev/null +++ b/Scripts/Items/Misc/PublicMoongate.cs @@ -0,0 +1,467 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using Server.Commands; + +namespace Server.Items +{ + public class PublicMoongate : Item + { + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + [Constructable] + public PublicMoongate() : base( 0xF6C ) + { + Movable = false; + Light = LightType.Circle300; + } + + public PublicMoongate( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.Player ) + return; + + if ( from.InRange( GetWorldLocation(), 1 ) ) + UseGate( from ); + else + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + + public override bool OnMoveOver( Mobile m ) + { + // Changed so criminals are not blocked by it. + if ( m.Player ) + UseGate( m ); + + return true; + } + + public override bool HandlesOnMovement{ get{ return true; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m is PlayerMobile ) + { + if ( !Utility.InRange( m.Location, this.Location, 1 ) && Utility.InRange( oldLocation, this.Location, 1 ) ) + m.CloseGump( typeof( MoongateGump ) ); + } + } + + public bool UseGate( Mobile m ) + { + if ( m.Criminal ) + { + m.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + return false; + } + else if ( Server.Spells.SpellHelper.CheckCombat( m ) ) + { + m.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + return false; + } + else if ( m.Spell != null ) + { + m.SendLocalizedMessage( 1049616 ); // You are too busy to do that at the moment. + return false; + } + else + { + m.CloseGump( typeof( MoongateGump ) ); + m.SendGump( new MoongateGump( m, this ) ); + + if ( !m.Hidden || m.AccessLevel == AccessLevel.Player ) + Effects.PlaySound( m.Location, m.Map, 0x20E ); + + return true; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public static void Initialize() + { + CommandSystem.Register( "MoonGen", AccessLevel.Administrator, new CommandEventHandler( MoonGen_OnCommand ) ); + } + + [Usage( "MoonGen" )] + [Description( "Generates public moongates. Removes all old moongates." )] + public static void MoonGen_OnCommand( CommandEventArgs e ) + { + DeleteAll(); + + int count = 0; + + count += MoonGen( PMList.Trammel ); + count += MoonGen( PMList.Felucca ); + count += MoonGen( PMList.Ilshenar ); + count += MoonGen( PMList.Malas ); + count += MoonGen( PMList.Tokuno ); + + World.Broadcast( 0x35, true, "{0} moongates generated.", count ); + } + + private static void DeleteAll() + { + ArrayList list = new ArrayList(); + + foreach ( Item item in World.Items.Values ) + { + if ( item is PublicMoongate ) + list.Add( item ); + } + + foreach ( Item item in list ) + item.Delete(); + + if ( list.Count > 0 ) + World.Broadcast( 0x35, true, "{0} moongates removed.", list.Count ); + } + + private static int MoonGen( PMList list ) + { + foreach ( PMEntry entry in list.Entries ) + { + Item item = new PublicMoongate(); + + item.MoveToWorld( entry.Location, list.Map ); + + if ( entry.Number == 1060642 ) // Umbra + item.Hue = 0x497; + } + + return list.Entries.Length; + } + } + + public class PMEntry + { + private Point3D m_Location; + private int m_Number; + + public Point3D Location + { + get + { + return m_Location; + } + } + + public int Number + { + get + { + return m_Number; + } + } + + public PMEntry( Point3D loc, int number ) + { + m_Location = loc; + m_Number = number; + } + } + + public class PMList + { + private int m_Number, m_SelNumber; + private Map m_Map; + private PMEntry[] m_Entries; + + public int Number + { + get + { + return m_Number; + } + } + + public int SelNumber + { + get + { + return m_SelNumber; + } + } + + public Map Map + { + get + { + return m_Map; + } + } + + public PMEntry[] Entries + { + get + { + return m_Entries; + } + } + + public PMList( int number, int selNumber, Map map, PMEntry[] entries ) + { + m_Number = number; + m_SelNumber = selNumber; + m_Map = map; + m_Entries = entries; + } + + public static readonly PMList Trammel = + new PMList( 1012000, 1012012, Map.Trammel, new PMEntry[] + { + new PMEntry( new Point3D( 4467, 1283, 5 ), 1012003 ), // Moonglow + new PMEntry( new Point3D( 1336, 1997, 5 ), 1012004 ), // Britain + new PMEntry( new Point3D( 1499, 3771, 5 ), 1012005 ), // Jhelom + new PMEntry( new Point3D( 771, 752, 5 ), 1012006 ), // Yew + new PMEntry( new Point3D( 2701, 692, 5 ), 1012007 ), // Minoc + new PMEntry( new Point3D( 1828, 2948,-20), 1012008 ), // Trinsic + new PMEntry( new Point3D( 643, 2067, 5 ), 1012009 ), // Skara Brae + new PMEntry( new Point3D( 3563, 2139, 34), 1012010 ), // Magincia + new PMEntry( new Point3D( 3763, 2771, 50), 1046259 ) // Haven + } ); + + public static readonly PMList Felucca = + new PMList( 1012001, 1012013, Map.Felucca, new PMEntry[] + { + new PMEntry( new Point3D( 4467, 1283, 5 ), 1012003 ), // Moonglow + new PMEntry( new Point3D( 1336, 1997, 5 ), 1012004 ), // Britain + new PMEntry( new Point3D( 1499, 3771, 5 ), 1012005 ), // Jhelom + new PMEntry( new Point3D( 771, 752, 5 ), 1012006 ), // Yew + new PMEntry( new Point3D( 2701, 692, 5 ), 1012007 ), // Minoc + new PMEntry( new Point3D( 1828, 2948,-20), 1012008 ), // Trinsic + new PMEntry( new Point3D( 643, 2067, 5 ), 1012009 ), // Skara Brae + new PMEntry( new Point3D( 3563, 2139, 34), 1012010 ), // Magincia + new PMEntry( new Point3D( 2711, 2234, 0 ), 1019001 ) // Buccaneer's Den + } ); + + public static readonly PMList Ilshenar = + new PMList( 1012002, 1012014, Map.Ilshenar, new PMEntry[] + { + new PMEntry( new Point3D( 1215, 467, -13 ), 1012015 ), // Compassion + new PMEntry( new Point3D( 722, 1366, -60 ), 1012016 ), // Honesty + new PMEntry( new Point3D( 744, 724, -28 ), 1012017 ), // Honor + new PMEntry( new Point3D( 281, 1016, 0 ), 1012018 ), // Humility + new PMEntry( new Point3D( 987, 1011, -32 ), 1012019 ), // Justice + new PMEntry( new Point3D( 1174, 1286, -30 ), 1012020 ), // Sacrifice + new PMEntry( new Point3D( 1532, 1340, - 3 ), 1012021 ), // Spirituality + new PMEntry( new Point3D( 528, 216, -45 ), 1012022 ), // Valor + new PMEntry( new Point3D( 1721, 218, 96 ), 1019000 ) // Chaos + } ); + + public static readonly PMList Malas = + new PMList( 1060643, 1062039, Map.Malas, new PMEntry[] + { + new PMEntry( new Point3D( 1015, 527, -65 ), 1060641 ), // Luna + new PMEntry( new Point3D( 1997, 1386, -85 ), 1060642 ) // Umbra + } ); + + public static readonly PMList Tokuno = + new PMList( 1063258, 1063415, Map.Tokuno, new PMEntry[] + { + new PMEntry( new Point3D( 1169, 998, 41 ), 1063412 ), // Isamu-Jima + new PMEntry( new Point3D( 802, 1204, 25 ), 1063413 ), // Makoto-Jima + new PMEntry( new Point3D( 270, 628, 15 ), 1063414 ) // Homare-Jima + } ); + + public static readonly PMList[] UORLists = new PMList[] { Trammel, Felucca }; + public static readonly PMList[] UORlistsYoung = new PMList[] { Trammel }; + public static readonly PMList[] LBRLists = new PMList[] { Trammel, Felucca, Ilshenar }; + public static readonly PMList[] LBRListsYoung = new PMList[] { Trammel, Ilshenar }; + public static readonly PMList[] AOSLists = new PMList[] { Trammel, Felucca, Ilshenar, Malas }; + public static readonly PMList[] AOSListsYoung = new PMList[] { Trammel, Ilshenar, Malas }; + public static readonly PMList[] SELists = new PMList[] { Trammel, Felucca, Ilshenar, Malas, Tokuno }; + public static readonly PMList[] SEListsYoung = new PMList[] { Trammel, Ilshenar, Malas, Tokuno }; + public static readonly PMList[] RedLists = new PMList[] { Felucca }; + public static readonly PMList[] SigilLists = new PMList[] { Felucca }; + } + + public class MoongateGump : Gump + { + private Mobile m_Mobile; + private Item m_Moongate; + private PMList[] m_Lists; + + public MoongateGump( Mobile mobile, Item moongate ) : base( 100, 100 ) + { + m_Mobile = mobile; + m_Moongate = moongate; + + PMList[] checkLists; + + if ( mobile.Player ) + { + if ( Factions.Sigil.ExistsOn( mobile ) ) + { + checkLists = PMList.SigilLists; + } + else if ( mobile.Kills >= 5 ) + { + checkLists = PMList.RedLists; + } + else + { + int flags = mobile.NetState == null ? 0 : mobile.NetState.Flags; + bool young = mobile is PlayerMobile ? ((PlayerMobile)mobile).Young : false; + + if ( Core.SE && (flags & 0x10) != 0 ) + checkLists = young ? PMList.SEListsYoung : PMList.SELists; + else if ( Core.AOS && (flags & 0x8) != 0 ) + checkLists = young ? PMList.AOSListsYoung : PMList.AOSLists; + else if ( (flags & 0x4) != 0 ) + checkLists = young ? PMList.LBRListsYoung : PMList.LBRLists; + else + checkLists = young ? PMList.UORlistsYoung : PMList.UORLists; + } + } + else + { + checkLists = PMList.SELists; + } + + m_Lists = new PMList[checkLists.Length]; + + for ( int i = 0; i < m_Lists.Length; ++i ) + m_Lists[i] = checkLists[i]; + + for ( int i = 0; i < m_Lists.Length; ++i ) + { + if ( m_Lists[i].Map == mobile.Map ) + { + PMList temp = m_Lists[i]; + + m_Lists[i] = m_Lists[0]; + m_Lists[0] = temp; + + break; + } + } + + AddPage( 0 ); + + AddBackground( 0, 0, 380, 280, 5054 ); + + AddButton( 10, 210, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 210, 140, 25, 1011036, false, false ); // OKAY + + AddButton( 10, 235, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 235, 140, 25, 1011012, false, false ); // CANCEL + + AddHtmlLocalized( 5, 5, 200, 20, 1012011, false, false ); // Pick your destination: + + for ( int i = 0; i < checkLists.Length; ++i ) + { + AddButton( 10, 35 + (i * 25), 2117, 2118, 0, GumpButtonType.Page, Array.IndexOf( m_Lists, checkLists[i] ) + 1 ); + AddHtmlLocalized( 30, 35 + (i * 25), 150, 20, checkLists[i].Number, false, false ); + } + + for ( int i = 0; i < m_Lists.Length; ++i ) + RenderPage( i, Array.IndexOf( checkLists, m_Lists[i] ) ); + } + + private void RenderPage( int index, int offset ) + { + PMList list = m_Lists[index]; + + AddPage( index + 1 ); + + AddButton( 10, 35 + (offset * 25), 2117, 2118, 0, GumpButtonType.Page, index + 1 ); + AddHtmlLocalized( 30, 35 + (offset * 25), 150, 20, list.SelNumber, false, false ); + + PMEntry[] entries = list.Entries; + + for ( int i = 0; i < entries.Length; ++i ) + { + AddRadio( 200, 35 + (i * 25), 210, 211, false, (index * 100) + i ); + AddHtmlLocalized( 225, 35 + (i * 25), 150, 20, entries[i].Number, false, false ); + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 0 ) // Cancel + return; + else if ( m_Mobile.Deleted || m_Moongate.Deleted || m_Mobile.Map == null ) + return; + + int[] switches = info.Switches; + + if ( switches.Length == 0 ) + return; + + int switchID = switches[0]; + int listIndex = switchID / 100; + int listEntry = switchID % 100; + + if ( listIndex < 0 || listIndex >= m_Lists.Length ) + return; + + PMList list = m_Lists[listIndex]; + + if ( listEntry < 0 || listEntry >= list.Entries.Length ) + return; + + PMEntry entry = list.Entries[listEntry]; + + if ( !m_Mobile.InRange( m_Moongate.GetWorldLocation(), 1 ) || m_Mobile.Map != m_Moongate.Map ) + { + m_Mobile.SendLocalizedMessage( 1019002 ); // You are too far away to use the gate. + } + else if ( m_Mobile.Player && m_Mobile.Kills >= 5 && list.Map != Map.Felucca ) + { + m_Mobile.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + } + else if ( Factions.Sigil.ExistsOn( m_Mobile ) && list.Map != Factions.Faction.Facet ) + { + m_Mobile.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + } + else if ( m_Mobile.Criminal ) + { + m_Mobile.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + } + else if ( Server.Spells.SpellHelper.CheckCombat( m_Mobile ) ) + { + m_Mobile.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + } + else if ( m_Mobile.Spell != null ) + { + m_Mobile.SendLocalizedMessage( 1049616 ); // You are too busy to do that at the moment. + } + else if ( m_Mobile.Map == list.Map && m_Mobile.InRange( entry.Location, 1 ) ) + { + m_Mobile.SendLocalizedMessage( 1019003 ); // You are already there. + } + else + { + BaseCreature.TeleportPets( m_Mobile, entry.Location, list.Map ); + + m_Mobile.Combatant = null; + m_Mobile.Warmode = false; + m_Mobile.Hidden = true; + + m_Mobile.MoveToWorld( entry.Location, list.Map ); + + Effects.PlaySound( entry.Location, list.Map, 0x1FE ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Rares.cs b/Scripts/Items/Misc/Rares.cs new file mode 100644 index 000000000..bf4fcf4c5 --- /dev/null +++ b/Scripts/Items/Misc/Rares.cs @@ -0,0 +1,718 @@ +using System; + +namespace Server.Items +{ + public class Rope : Item + { + [Constructable] + public Rope() : this( 1 ) + { + } + + [Constructable] + public Rope( int amount ) : base( 0x14F8 ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + + + public Rope( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class IronWire : Item + { + [Constructable] + public IronWire() : this( 1 ) + { + } + + [Constructable] + public IronWire( int amount ) : base( 0x1876 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + } + + + + public IronWire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 && Weight == 2.0 ) + Weight = 5.0; + } + } + + public class SilverWire : Item + { + [Constructable] + public SilverWire() : this( 1 ) + { + } + + [Constructable] + public SilverWire( int amount ) : base( 0x1877 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + } + + + + public SilverWire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 && Weight == 2.0 ) + Weight = 5.0; + } + } + + public class GoldWire : Item + { + [Constructable] + public GoldWire() : this( 1 ) + { + } + + [Constructable] + public GoldWire( int amount ) : base( 0x1878 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + } + + + + public GoldWire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 && Weight == 2.0 ) + Weight = 5.0; + } + } + + public class CopperWire : Item + { + [Constructable] + public CopperWire() : this( 1 ) + { + } + + [Constructable] + public CopperWire( int amount ) : base( 0x1879 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + } + + + + public CopperWire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 && Weight == 2.0 ) + Weight = 5.0; + } + } + + public class WhiteDriedFlowers : Item + { + [Constructable] + public WhiteDriedFlowers() : this( 1 ) + { + } + + [Constructable] + public WhiteDriedFlowers( int amount ) : base( 0xC3C ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + + + public WhiteDriedFlowers( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GreenDriedFlowers : Item + { + [Constructable] + public GreenDriedFlowers() : this( 1 ) + { + } + + [Constructable] + public GreenDriedFlowers( int amount ) : base( 0xC3E ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + + + public GreenDriedFlowers( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DriedOnions : Item + { + [Constructable] + public DriedOnions() : this( 1 ) + { + } + + [Constructable] + public DriedOnions( int amount ) : base( 0xC40 ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + + + public DriedOnions( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DriedHerbs : Item + { + [Constructable] + public DriedHerbs() : this( 1 ) + { + } + + [Constructable] + public DriedHerbs( int amount ) : base( 0xC42 ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + + + public DriedHerbs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class HorseShoes : Item + { + [Constructable] + public HorseShoes() : base( 0xFB6 ) + { + Weight = 3.0; + } + + public HorseShoes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ForgedMetal : Item + { + [Constructable] + public ForgedMetal() : base( 0xFB8 ) + { + Weight = 5.0; + } + + public ForgedMetal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Whip : Item + { + [Constructable] + public Whip() : base( 0x166E ) + { + Weight = 1.0; + } + + public Whip( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PaintsAndBrush : Item + { + [Constructable] + public PaintsAndBrush() : base( 0xFC1 ) + { + Weight = 1.0; + } + + public PaintsAndBrush( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PenAndInk : Item + { + [Constructable] + public PenAndInk() : base( 0xFBF ) + { + Weight = 1.0; + } + + public PenAndInk( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ChiselsNorth : Item + { + [Constructable] + public ChiselsNorth() : base( 0x1026 ) + { + Weight = 1.0; + } + + public ChiselsNorth( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class ChiselsWest : Item + { + [Constructable] + public ChiselsWest() : base( 0x1027 ) + { + Weight = 1.0; + } + + public ChiselsWest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtyPan : Item + { + [Constructable] + public DirtyPan() : base( 0x9E8 ) + { + Weight = 1.0; + } + + public DirtyPan( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtySmallRoundPot : Item + { + [Constructable] + public DirtySmallRoundPot() : base( 0x9E7 ) + { + Weight = 1.0; + } + + public DirtySmallRoundPot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtyPot : Item + { + [Constructable] + public DirtyPot() : base( 0x9E6 ) + { + Weight = 1.0; + } + + public DirtyPot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtyRoundPot : Item + { + [Constructable] + public DirtyRoundPot() : base( 0x9DF ) + { + Weight = 1.0; + } + + public DirtyRoundPot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtyFrypan : Item + { + [Constructable] + public DirtyFrypan() : base( 0x9DE ) + { + Weight = 1.0; + } + + public DirtyFrypan( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtySmallPot : Item + { + [Constructable] + public DirtySmallPot() : base( 0x9DD ) + { + Weight = 1.0; + } + + public DirtySmallPot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class DirtyKettle : Item + { + [Constructable] + public DirtyKettle() : base( 0x9DC ) + { + Weight = 1.0; + } + + public DirtyKettle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/ResGate.cs b/Scripts/Items/Misc/ResGate.cs new file mode 100644 index 000000000..66fd5b35b --- /dev/null +++ b/Scripts/Items/Misc/ResGate.cs @@ -0,0 +1,57 @@ +using System; +using Server.Gumps; + +namespace Server.Items +{ + public class ResGate : Item + { + public override string DefaultName + { + get { return "a resurrection gate"; } + } + + [Constructable] + public ResGate() : base( 0xF6C ) + { + Movable = false; + Hue = 0x2D1; + Light = LightType.Circle300; + } + + public ResGate( Serial serial ) : base( serial ) + { + } + + public override bool OnMoveOver( Mobile m ) + { + if ( !m.Alive && m.Map != null && m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m ) ); + } + else + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Misc/Scales.cs b/Scripts/Items/Misc/Scales.cs new file mode 100644 index 000000000..7a75839d4 --- /dev/null +++ b/Scripts/Items/Misc/Scales.cs @@ -0,0 +1,93 @@ +using System; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class Scales : Item + { + [Constructable] + public Scales() : base( 0x1852 ) + { + Weight = 4.0; + } + + public Scales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 502431 ); // What would you like to weigh? + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private Scales m_Item; + + public InternalTarget( Scales item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + string message; + + if ( targeted == m_Item ) + { + message = "It cannot weight itself."; + } + else if ( targeted is Item ) + { + Item item = (Item)targeted; + object root = item.RootParent; + + if ( (root != null && root != from) || item.Parent == from ) + { + message = "You decide that item's current location is too awkward to get an accurate result."; + } + else if ( item.Movable ) + { + if ( item.Amount > 1 ) + message = "You place one item on the scale. "; + else + message = "You place that item on the scale. "; + + double weight = item.Weight; + + if ( weight <= 0.0 ) + message += "It is lighter than a feather."; + else + message += String.Format( "It weighs {0} stones.", weight ); + } + else + { + message = "You cannot weigh that object."; + } + } + else + { + message = "You cannot weigh that object."; + } + + from.SendMessage( message ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/SerpentPillar.cs b/Scripts/Items/Misc/SerpentPillar.cs new file mode 100644 index 000000000..6b02ba367 --- /dev/null +++ b/Scripts/Items/Misc/SerpentPillar.cs @@ -0,0 +1,126 @@ +using System; +using Server; +using Server.Multis; + +namespace Server.Items +{ + public class SerpentPillar : Item + { + private bool m_Active; + private string m_Word; + private Rectangle2D m_Destination; + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get{ return m_Active; } + set{ m_Active = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Word + { + get{ return m_Word; } + set{ m_Word = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Rectangle2D Destination + { + get{ return m_Destination; } + set{ m_Destination = value; } + } + + [Constructable] + public SerpentPillar() : this( null, new Rectangle2D(), false ) + { + } + + public SerpentPillar( string word, Rectangle2D destination ) : this( word, destination, true ) + { + } + + public SerpentPillar( string word, Rectangle2D destination, bool active ) : base( 0x233F ) + { + Movable = false; + + m_Active = active; + m_Word = word; + m_Destination = destination; + } + + public override bool HandlesOnSpeech{ get{ return true; } } + + public override void OnSpeech( SpeechEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !e.Handled && from.InRange( this, 10 ) && e.Speech.ToLower() == this.Word ) + { + BaseBoat boat = BaseBoat.FindBoatAt( from, from.Map ); + + if ( boat == null ) + return; + + if ( !this.Active ) + { + if ( boat.TillerMan != null ) + boat.TillerMan.Say( 502507 ); // Ar, Legend has it that these pillars are inactive! No man knows how it might be undone! + + return; + } + + Map map = from.Map; + + for ( int i = 0; i < 5; i++ ) // Try 5 times + { + int x = Utility.Random( Destination.X, Destination.Width ); + int y = Utility.Random( Destination.Y, Destination.Height ); + int z = map.GetAverageZ( x, y ); + + Point3D dest = new Point3D( x, y, z ); + + if ( boat.CanFit( dest, map, boat.ItemID ) ) + { + int xOffset = x - boat.X; + int yOffset = y - boat.Y; + int zOffset = z - boat.Z; + + boat.Teleport( xOffset, yOffset, zOffset ); + + return; + } + } + + if ( boat.TillerMan != null ) + boat.TillerMan.Say( 502508 ); // Ar, I refuse to take that matey through here! + } + } + + public SerpentPillar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (bool) m_Active ); + writer.Write( (string) m_Word ); + writer.Write( (Rectangle2D) m_Destination ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Active = reader.ReadBool(); + m_Word = reader.ReadString(); + m_Destination = reader.ReadRect2D(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/SpecialBeardDye.cs b/Scripts/Items/Misc/SpecialBeardDye.cs new file mode 100644 index 000000000..0b9b829be --- /dev/null +++ b/Scripts/Items/Misc/SpecialBeardDye.cs @@ -0,0 +1,186 @@ +using System; +using System.Text; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class SpecialBeardDye : Item + { + public override int LabelNumber{ get{ return 1041087; } } // Special Beard Dye + + [Constructable] + public SpecialBeardDye() : base( 0xE26 ) + { + Weight = 1.0; + LootType = LootType.Newbied; + } + + public SpecialBeardDye( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + from.CloseGump( typeof( SpecialBeardDyeGump ) ); + from.SendGump( new SpecialBeardDyeGump( this ) ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 906, 1019045 ); // I can't reach that. + } + } + } + + public class SpecialBeardDyeGump : Gump + { + private SpecialBeardDye m_SpecialBeardDye; + + private class SpecialBeardDyeEntry + { + private string m_Name; + private int m_HueStart; + private int m_HueCount; + + public string Name + { + get + { + return m_Name; + } + } + + public int HueStart + { + get + { + return m_HueStart; + } + } + + public int HueCount + { + get + { + return m_HueCount; + } + } + + public SpecialBeardDyeEntry( string name, int hueStart, int hueCount ) + { + m_Name = name; + m_HueStart = hueStart; + m_HueCount = hueCount; + } + } + + private static SpecialBeardDyeEntry[] m_Entries = new SpecialBeardDyeEntry[] + { + new SpecialBeardDyeEntry( "*****", 12, 10 ), + new SpecialBeardDyeEntry( "*****", 32, 5 ), + new SpecialBeardDyeEntry( "*****", 38, 8 ), + new SpecialBeardDyeEntry( "*****", 54, 3 ), + new SpecialBeardDyeEntry( "*****", 62, 10 ), + new SpecialBeardDyeEntry( "*****", 81, 2 ), + new SpecialBeardDyeEntry( "*****", 89, 2 ), + new SpecialBeardDyeEntry( "*****", 1153, 2 ) + }; + + public SpecialBeardDyeGump( SpecialBeardDye dye ) : base( 0, 0 ) + { + m_SpecialBeardDye = dye; + + AddPage( 0 ); + AddBackground( 150, 60, 350, 358, 2600 ); + AddBackground( 170, 104, 110, 270, 5100 ); + AddHtmlLocalized( 230, 75, 200, 20, 1011013, false, false ); // Hair Color Selection Menu + AddHtmlLocalized( 235, 380, 300, 20, 1013007, false, false ); // Dye my beard this color! + AddButton( 200, 380, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); // DYE HAIR + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + AddLabel( 180, 109 + (i * 22), m_Entries[i].HueStart - 1, m_Entries[i].Name ); + AddButton( 257, 110 + (i * 22), 5224, 5224, 0, GumpButtonType.Page, i + 1 ); + } + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + SpecialBeardDyeEntry e = m_Entries[i]; + + AddPage( i + 1 ); + + for ( int j = 0; j < e.HueCount; ++j ) + { + AddLabel( 328 + ((j / 16) * 80), 102 + ((j % 16) * 17), e.HueStart + j - 1, "*****" ); + AddRadio( 310 + ((j / 16) * 80), 102 + ((j % 16) * 17), 210, 211, false, (i * 100) + j ); + } + } + } + + public override void OnResponse( NetState from, RelayInfo info ) + { + if ( m_SpecialBeardDye.Deleted ) + return; + + Mobile m = from.Mobile; + int[] switches = info.Switches; + + if ( !m_SpecialBeardDye.IsChildOf( m.Backpack ) ) + { + m.SendLocalizedMessage( 1042010 ); //You must have the objectin your backpack to use it. + return; + } + + if ( info.ButtonID != 0 && switches.Length > 0 ) + { + if( m.FacialHairItemID == 0 ) + { + m.SendLocalizedMessage( 502623 ); // You have no hair to dye and cannot use this + } + else + { + // To prevent this from being exploited, the hue is abstracted into an internal list + + int entryIndex = switches[0] / 100; + int hueOffset = switches[0] % 100; + + if ( entryIndex >= 0 && entryIndex < m_Entries.Length ) + { + SpecialBeardDyeEntry e = m_Entries[entryIndex]; + + if ( hueOffset >= 0 && hueOffset < e.HueCount ) + { + int hue = e.HueStart + hueOffset; + + m.FacialHairHue = hue; + + m.SendLocalizedMessage( 501199 ); // You dye your hair + m_SpecialBeardDye.Delete(); + m.PlaySound( 0x4E ); + } + } + } + } + else + { + m.SendLocalizedMessage( 501200 ); // You decide not to dye your hair + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/SpecialHairDye.cs b/Scripts/Items/Misc/SpecialHairDye.cs new file mode 100644 index 000000000..c6563fa16 --- /dev/null +++ b/Scripts/Items/Misc/SpecialHairDye.cs @@ -0,0 +1,191 @@ +using System; +using System.Text; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class SpecialHairDye : Item + { + public override string DefaultName + { + get { return "Special Hair Dye"; } + } + + [Constructable] + public SpecialHairDye() : base( 0xE26 ) + { + Weight = 1.0; + LootType = LootType.Newbied; + } + + public SpecialHairDye( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + from.CloseGump( typeof( SpecialHairDyeGump ) ); + from.SendGump( new SpecialHairDyeGump( this ) ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 906, 1019045 ); // I can't reach that. + } + + } + } + + public class SpecialHairDyeGump : Gump + { + private SpecialHairDye m_SpecialHairDye; + + private class SpecialHairDyeEntry + { + private string m_Name; + private int m_HueStart; + private int m_HueCount; + + public string Name + { + get + { + return m_Name; + } + } + + public int HueStart + { + get + { + return m_HueStart; + } + } + + public int HueCount + { + get + { + return m_HueCount; + } + } + + public SpecialHairDyeEntry( string name, int hueStart, int hueCount ) + { + m_Name = name; + m_HueStart = hueStart; + m_HueCount = hueCount; + } + } + + private static SpecialHairDyeEntry[] m_Entries = new SpecialHairDyeEntry[] + { + new SpecialHairDyeEntry( "*****", 12, 10 ), + new SpecialHairDyeEntry( "*****", 32, 5 ), + new SpecialHairDyeEntry( "*****", 38, 8 ), + new SpecialHairDyeEntry( "*****", 54, 3 ), + new SpecialHairDyeEntry( "*****", 62, 10 ), + new SpecialHairDyeEntry( "*****", 81, 2 ), + new SpecialHairDyeEntry( "*****", 89, 2 ), + new SpecialHairDyeEntry( "*****", 1153, 2 ) + }; + + public SpecialHairDyeGump( SpecialHairDye dye ) : base( 0, 0 ) + { + m_SpecialHairDye = dye; + + AddPage( 0 ); + AddBackground( 150, 60, 350, 358, 2600 ); + AddBackground( 170, 104, 110, 270, 5100 ); + AddHtmlLocalized( 230, 75, 200, 20, 1011013, false, false ); // Hair Color Selection Menu + AddHtmlLocalized( 235, 380, 300, 20, 1011014, false, false ); // Dye my hair this color! + AddButton( 200, 380, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); // DYE HAIR + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + AddLabel( 180, 109 + (i * 22), m_Entries[i].HueStart - 1, m_Entries[i].Name ); + AddButton( 257, 110 + (i * 22), 5224, 5224, 0, GumpButtonType.Page, i + 1 ); + } + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + SpecialHairDyeEntry e = m_Entries[i]; + + AddPage( i + 1 ); + + for ( int j = 0; j < e.HueCount; ++j ) + { + AddLabel( 328 + ((j / 16) * 80), 102 + ((j % 16) * 17), e.HueStart + j - 1, "*****" ); + AddRadio( 310 + ((j / 16) * 80), 102 + ((j % 16) * 17), 210, 211, false, (i * 100) + j ); + } + } + } + + public override void OnResponse( NetState from, RelayInfo info ) + { + if ( m_SpecialHairDye.Deleted ) + return; + + Mobile m = from.Mobile; + int[] switches = info.Switches; + + if ( !m_SpecialHairDye.IsChildOf( m.Backpack ) ) + { + m.SendLocalizedMessage( 1042010 ); //You must have the objectin your backpack to use it. + return; + } + + if ( info.ButtonID != 0 && switches.Length > 0 ) + { + if( m.HairItemID == 0 ) + { + m.SendLocalizedMessage( 502623 ); // You have no hair to dye and cannot use this + } + else + { + // To prevent this from being exploited, the hue is abstracted into an internal list + + int entryIndex = switches[0] / 100; + int hueOffset = switches[0] % 100; + + if ( entryIndex >= 0 && entryIndex < m_Entries.Length ) + { + SpecialHairDyeEntry e = m_Entries[entryIndex]; + + if ( hueOffset >= 0 && hueOffset < e.HueCount ) + { + m_SpecialHairDye.Delete(); + + int hue = e.HueStart + hueOffset; + + m.HairHue = hue; + + m.SendLocalizedMessage( 501199 ); // You dye your hair + m.PlaySound( 0x4E ); + } + } + } + } + else + { + m.SendLocalizedMessage( 501200 ); // You decide not to dye your hair + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Static.cs b/Scripts/Items/Misc/Static.cs new file mode 100644 index 000000000..e283af4a0 --- /dev/null +++ b/Scripts/Items/Misc/Static.cs @@ -0,0 +1,99 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Static : Item + { + public Static() //Dupe-tastic! + : base( 0x80 ) + { + Movable = false; + } + + [Constructable] + public Static( int itemID ) : base( itemID ) + { + Movable = false; + } + + [Constructable] + public Static( int itemID, int count ) : this( Utility.Random( itemID, count ) ) + { + } + + public Static( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 0 ) + Weight = -1; + } + } + + public class LocalizedStatic : Static + { + private int m_LabelNumber; + + [CommandProperty( AccessLevel.GameMaster )] + public int Number + { + get{ return m_LabelNumber; } + set{ m_LabelNumber = value; InvalidateProperties(); } + } + + public override int LabelNumber{ get{ return m_LabelNumber; } } + + [Constructable] + public LocalizedStatic( int itemID ) : this( itemID, 1020000 + itemID ) + { + } + + [Constructable] + public LocalizedStatic( int itemID, int labelNumber ) : base( itemID ) + { + m_LabelNumber = labelNumber; + } + + public LocalizedStatic( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (byte) 0 ); // version + writer.WriteEncodedInt( (int) m_LabelNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadByte(); + + switch ( version ) + { + case 0: + { + m_LabelNumber = reader.ReadEncodedInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/SwarmOfFlies.cs b/Scripts/Items/Misc/SwarmOfFlies.cs new file mode 100644 index 000000000..ad44aa119 --- /dev/null +++ b/Scripts/Items/Misc/SwarmOfFlies.cs @@ -0,0 +1,37 @@ +using System; + +namespace Server.Items +{ + public class SwarmOfFlies : Item + { + public override string DefaultName + { + get { return "a swarm of flies"; } + } + + [Constructable] + public SwarmOfFlies() : base( 0x91B ) + { + Hue = 1; + Movable = false; + } + + public SwarmOfFlies( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Teleporter.cs b/Scripts/Items/Misc/Teleporter.cs new file mode 100644 index 000000000..7dd5bd37d --- /dev/null +++ b/Scripts/Items/Misc/Teleporter.cs @@ -0,0 +1,494 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class Teleporter : Item + { + private bool m_Active, m_Creatures; + private Point3D m_PointDest; + private Map m_MapDest; + private bool m_SourceEffect; + private bool m_DestEffect; + private int m_SoundID; + private TimeSpan m_Delay; + + [CommandProperty( AccessLevel.GameMaster )] + public bool SourceEffect + { + get{ return m_SourceEffect; } + set{ m_SourceEffect = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool DestEffect + { + get{ return m_DestEffect; } + set{ m_DestEffect = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int SoundID + { + get{ return m_SoundID; } + set{ m_SoundID = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan Delay + { + get{ return m_Delay; } + set{ m_Delay = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Active + { + get { return m_Active; } + set { m_Active = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D PointDest + { + get { return m_PointDest; } + set { m_PointDest = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map MapDest + { + get { return m_MapDest; } + set { m_MapDest = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Creatures + { + get { return m_Creatures; } + set { m_Creatures = value; InvalidateProperties(); } + } + + public override int LabelNumber{ get{ return 1026095; } } // teleporter + + [Constructable] + public Teleporter() : this( new Point3D( 0, 0, 0 ), null, false ) + { + } + + [Constructable] + public Teleporter( Point3D pointDest, Map mapDest ) : this( pointDest, mapDest, false ) + { + } + + [Constructable] + public Teleporter( Point3D pointDest, Map mapDest, bool creatures ) : base( 0x1BC3 ) + { + Movable = false; + Visible = false; + + m_Active = true; + m_PointDest = pointDest; + m_MapDest = mapDest; + m_Creatures = creatures; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Active ) + list.Add( 1060742 ); // active + else + list.Add( 1060743 ); // inactive + + if ( m_MapDest != null ) + list.Add( 1060658, "Map\t{0}", m_MapDest ); + + if ( m_PointDest != Point3D.Zero ) + list.Add( 1060659, "Coords\t{0}", m_PointDest ); + + list.Add( 1060660, "Creatures\t{0}", m_Creatures ? "Yes" : "No" ); + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( m_Active ) + { + if ( m_MapDest != null && m_PointDest != Point3D.Zero ) + LabelTo( from, "{0} [{1}]", m_PointDest, m_MapDest ); + else if ( m_MapDest != null ) + LabelTo( from, "[{0}]", m_MapDest ); + else if ( m_PointDest != Point3D.Zero ) + LabelTo( from, m_PointDest.ToString() ); + } + else + { + LabelTo( from, "(inactive)" ); + } + } + + public virtual void StartTeleport( Mobile m ) + { + if ( m_Delay == TimeSpan.Zero ) + DoTeleport( m ); + else + Timer.DelayCall( m_Delay, new TimerStateCallback( DoTeleport_Callback ), m ); + } + + private void DoTeleport_Callback( object state ) + { + DoTeleport( (Mobile) state ); + } + + public virtual void DoTeleport( Mobile m ) + { + Map map = m_MapDest; + + if ( map == null || map == Map.Internal ) + map = m.Map; + + Point3D p = m_PointDest; + + if ( p == Point3D.Zero ) + p = m.Location; + + Server.Mobiles.BaseCreature.TeleportPets( m, p, map ); + + bool sendEffect = ( !m.Hidden || m.AccessLevel == AccessLevel.Player ); + + if ( m_SourceEffect && sendEffect ) + Effects.SendLocationEffect( m.Location, m.Map, 0x3728, 10, 10 ); + + m.MoveToWorld( p, map ); + + if ( m_DestEffect && sendEffect ) + Effects.SendLocationEffect( m.Location, m.Map, 0x3728, 10, 10 ); + + if ( m_SoundID > 0 && sendEffect ) + Effects.PlaySound( m.Location, m.Map, m_SoundID ); + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m_Active ) + { + if ( !m_Creatures && !m.Player ) + return true; + + StartTeleport( m ); + return false; + } + + return true; + } + + public Teleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (bool) m_SourceEffect ); + writer.Write( (bool) m_DestEffect ); + writer.Write( (TimeSpan) m_Delay ); + writer.WriteEncodedInt( (int) m_SoundID ); + + writer.Write( m_Creatures ); + + writer.Write( m_Active ); + writer.Write( m_PointDest ); + writer.Write( m_MapDest ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_SourceEffect = reader.ReadBool(); + m_DestEffect = reader.ReadBool(); + m_Delay = reader.ReadTimeSpan(); + m_SoundID = reader.ReadEncodedInt(); + + goto case 1; + } + case 1: + { + m_Creatures = reader.ReadBool(); + + goto case 0; + } + case 0: + { + m_Active = reader.ReadBool(); + m_PointDest = reader.ReadPoint3D(); + m_MapDest = reader.ReadMap(); + + break; + } + } + } + } + + public class SkillTeleporter : Teleporter + { + private SkillName m_Skill; + private double m_Required; + private string m_MessageString; + private int m_MessageNumber; + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill + { + get{ return m_Skill; } + set{ m_Skill = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double Required + { + get{ return m_Required; } + set{ m_Required = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string MessageString + { + get{ return m_MessageString; } + set{ m_MessageString = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MessageNumber + { + get{ return m_MessageNumber; } + set{ m_MessageNumber = value; InvalidateProperties(); } + } + + private void EndMessageLock( object state ) + { + ((Mobile)state).EndAction( this ); + } + + public override bool OnMoveOver( Mobile m ) + { + if ( Active ) + { + if ( !Creatures && !m.Player ) + return true; + + Skill sk = m.Skills[m_Skill]; + + if ( sk == null || sk.Base < m_Required ) + { + if ( m.BeginAction( this ) ) + { + if ( m_MessageString != null ) + m.Send( new UnicodeMessage( Serial, ItemID, MessageType.Regular, 0x3B2, 3, "ENU", null, m_MessageString ) ); + else if ( m_MessageNumber != 0 ) + m.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, m_MessageNumber, null, "" ) ); + + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( EndMessageLock ), m ); + } + + return false; + } + + StartTeleport( m ); + return false; + } + + return true; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + int skillIndex = (int)m_Skill; + string skillName; + + if ( skillIndex >= 0 && skillIndex < SkillInfo.Table.Length ) + skillName = SkillInfo.Table[skillIndex].Name; + else + skillName = "(Invalid)"; + + list.Add( 1060661, "{0}\t{1:F1}", skillName, m_Required ); + + if ( m_MessageString != null ) + list.Add( 1060662, "Message\t{0}", m_MessageString ); + else if ( m_MessageNumber != 0 ) + list.Add( 1060662, "Message\t#{0}", m_MessageNumber ); + } + + [Constructable] + public SkillTeleporter() + { + } + + public SkillTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Skill ); + writer.Write( (double) m_Required ); + writer.Write( (string) m_MessageString ); + writer.Write( (int) m_MessageNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Skill = (SkillName)reader.ReadInt(); + m_Required = reader.ReadDouble(); + m_MessageString = reader.ReadString(); + m_MessageNumber = reader.ReadInt(); + + break; + } + } + } + } + + public class KeywordTeleporter : Teleporter + { + private string m_Substring; + private int m_Keyword; + private int m_Range; + + [CommandProperty( AccessLevel.GameMaster )] + public string Substring + { + get{ return m_Substring; } + set{ m_Substring = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Keyword + { + get{ return m_Keyword; } + set{ m_Keyword = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Range + { + get{ return m_Range; } + set{ m_Range = value; InvalidateProperties(); } + } + + public override bool HandlesOnSpeech{ get{ return true; } } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( !e.Handled && Active ) + { + Mobile m = e.Mobile; + + if ( !Creatures && !m.Player ) + return; + + if ( !m.InRange( GetWorldLocation(), m_Range ) ) + return; + + bool isMatch = false; + + if ( m_Keyword >= 0 && e.HasKeyword( m_Keyword ) ) + isMatch = true; + else if ( m_Substring != null && e.Speech.ToLower().IndexOf( m_Substring.ToLower() ) >= 0 ) + isMatch = true; + + if ( !isMatch ) + return; + + e.Handled = true; + StartTeleport( m ); + } + } + + public override bool OnMoveOver( Mobile m ) + { + return true; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060661, "Range\t{0}", m_Range ); + + if ( m_Keyword >= 0 ) + list.Add( 1060662, "Keyword\t{0}", m_Keyword ); + + if ( m_Substring != null ) + list.Add( 1060663, "Substring\t{0}", m_Substring ); + } + + [Constructable] + public KeywordTeleporter() + { + m_Keyword = -1; + m_Substring = null; + } + + public KeywordTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Substring ); + writer.Write( m_Keyword ); + writer.Write( m_Range ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Substring = reader.ReadString(); + m_Keyword = reader.ReadInt(); + m_Range = reader.ReadInt(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/TrashBarrel.cs b/Scripts/Items/Misc/TrashBarrel.cs new file mode 100644 index 000000000..ee8692d29 --- /dev/null +++ b/Scripts/Items/Misc/TrashBarrel.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Multis; + +namespace Server.Items +{ + public class TrashBarrel : Container, IChopable + { + public override int LabelNumber{ get{ return 1041064; } } // a trash barrel + + public override int DefaultMaxWeight{ get{ return 0; } } // A value of 0 signals unlimited weight + + public override bool IsDecoContainer + { + get{ return false; } + } + + [Constructable] + public TrashBarrel() : base( 0xE77 ) + { + Hue = 0x3B2; + Movable = false; + } + + public TrashBarrel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Items.Count > 0 ) + { + m_Timer = new EmptyTimer( this ); + m_Timer.Start(); + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( !base.OnDragDrop( from, dropped ) ) + return false; + + if ( TotalItems >= 50 ) + { + Empty( 501478 ); // The trash is full! Emptying! + } + else + { + SendLocalizedMessageTo( from, 1010442 ); // The item will be deleted in three minutes + + if ( m_Timer != null ) + m_Timer.Stop(); + else + m_Timer = new EmptyTimer( this ); + + m_Timer.Start(); + } + + return true; + } + + public override bool OnDragDropInto( Mobile from, Item item, Point3D p ) + { + if ( !base.OnDragDropInto( from, item, p ) ) + return false; + + if ( TotalItems >= 50 ) + { + Empty( 501478 ); // The trash is full! Emptying! + } + else + { + SendLocalizedMessageTo( from, 1010442 ); // The item will be deleted in three minutes + + if ( m_Timer != null ) + m_Timer.Stop(); + else + m_Timer = new EmptyTimer( this ); + + m_Timer.Start(); + } + + return true; + } + + public void OnChop( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house != null && house.IsCoOwner( from ) ) + { + Effects.PlaySound( Location, Map, 0x11C ); + from.SendLocalizedMessage( 500461 ); // You destroy the item. + Destroy(); + } + } + + public void Empty( int message ) + { + List items = this.Items; + + if ( items.Count > 0 ) + { + PublicOverheadMessage( Network.MessageType.Regular, 0x3B2, message, "" ); + + for ( int i = items.Count - 1; i >= 0; --i ) + { + if ( i >= items.Count ) + continue; + + items[i].Delete(); + } + } + + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + } + + private Timer m_Timer; + + private class EmptyTimer : Timer + { + private TrashBarrel m_Barrel; + + public EmptyTimer( TrashBarrel barrel ) : base( TimeSpan.FromMinutes( 3.0 ) ) + { + m_Barrel = barrel; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Barrel.Empty( 501479 ); // Emptying the trashcan! + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/TrashChest.cs b/Scripts/Items/Misc/TrashChest.cs new file mode 100644 index 000000000..7b03a784c --- /dev/null +++ b/Scripts/Items/Misc/TrashChest.cs @@ -0,0 +1,61 @@ +using System; + +namespace Server.Items +{ + [FlipableAttribute( 0xE41, 0xE40 )] + public class TrashChest : Container + { + public override int DefaultMaxWeight{ get{ return 0; } } // A value of 0 signals unlimited weight + + public override bool IsDecoContainer + { + get{ return false; } + } + + [Constructable] + public TrashChest() : base( 0xE41 ) + { + Movable = false; + } + + public TrashChest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( !base.OnDragDrop( from, dropped ) ) + return false; + + PublicOverheadMessage( Network.MessageType.Regular, 0x3B2, Utility.Random( 1042891, 8 ) ); + dropped.Delete(); + + return true; + } + + public override bool OnDragDropInto( Mobile from, Item item, Point3D p ) + { + if ( !base.OnDragDropInto( from, item, p ) ) + return false; + + PublicOverheadMessage( Network.MessageType.Regular, 0x3B2, Utility.Random( 1042891, 8 ) ); + item.Delete(); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/TribalBerry.cs b/Scripts/Items/Misc/TribalBerry.cs new file mode 100644 index 000000000..baf171f20 --- /dev/null +++ b/Scripts/Items/Misc/TribalBerry.cs @@ -0,0 +1,46 @@ +using System; + +namespace Server.Items +{ + public class TribalBerry : Item + { + public override int LabelNumber{ get{ return 1040001; } } // tribal berry + + [Constructable] + public TribalBerry() : this( 1 ) + { + } + + [Constructable] + public TribalBerry( int amount ) : base( 0x9D0 ) + { + Weight = 1.0; + Stackable = true; + Amount = amount; + Hue = 6; + } + + public TribalBerry( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 4 ) + Hue = 6; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/TribalPaint.cs b/Scripts/Items/Misc/TribalPaint.cs new file mode 100644 index 000000000..4fd51df4d --- /dev/null +++ b/Scripts/Items/Misc/TribalPaint.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Items +{ + public class TribalPaint : Item + { + public override int LabelNumber{ get{ return 1040000; } } // savage kin paint + + [Constructable] + public TribalPaint() : base( 0x9EC ) + { + Hue = 2101; + Weight = 2.0; + } + + public TribalPaint( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + if ( Factions.Sigil.ExistsOn( from ) ) + { + from.SendLocalizedMessage( 1010465 ); // You cannot disguise yourself while holding a sigil. + } + else if ( !from.CanBeginAction( typeof( Spells.Fifth.IncognitoSpell ) ) ) + { + from.SendLocalizedMessage( 501698 ); // You cannot disguise yourself while incognitoed. + } + else if ( !from.CanBeginAction( typeof( Spells.Seventh.PolymorphSpell ) ) ) + { + from.SendLocalizedMessage( 501699 ); // You cannot disguise yourself while polymorphed. + } + else if ( Spells.Necromancy.TransformationSpell.UnderTransformation( from ) ) + { + from.SendLocalizedMessage( 501699 ); // You cannot disguise yourself while polymorphed. + } + else if ( Spells.Ninjitsu.AnimalForm.UnderTransformation( from ) ) + { + from.SendLocalizedMessage( 1061634 ); // You cannot disguise yourself while in that form. + } + else if ( from.IsBodyMod || from.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + { + from.SendLocalizedMessage( 501605 ); // You are already disguised. + } + else + { + from.BodyMod = ( from.Female ? 184 : 183 ); + from.HueMod = 0; + + if ( from is PlayerMobile ) + ((PlayerMobile)from).SavagePaintExpiration = TimeSpan.FromDays( 7.0 ); + + from.SendLocalizedMessage( 1042537 ); // You now bear the markings of the savage tribe. Your body paint will last about a week or you can remove it with an oil cloth. + + Consume(); + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/UnholyBone.cs b/Scripts/Items/Misc/UnholyBone.cs new file mode 100644 index 000000000..30a81ae70 --- /dev/null +++ b/Scripts/Items/Misc/UnholyBone.cs @@ -0,0 +1,117 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Items +{ + public class UnholyBone : Item, ICarvable + { + private SpawnTimer m_Timer; + + public override string DefaultName + { + get { return "unholy bone"; } + } + + [Constructable] + public UnholyBone() : base( 0xF7E ) + { + Movable = false; + Hue = 0x497; + + m_Timer = new SpawnTimer( this ); + m_Timer.Start(); + } + + public void Carve( Mobile from, Item item ) + { + Effects.PlaySound( GetWorldLocation(), Map, 0x48F ); + Effects.SendLocationEffect( GetWorldLocation(), Map, 0x3728, 10, 10, 0, 0 ); + + if ( 0.3 > Utility.RandomDouble() ) + { + if ( ItemID == 0xF7E ) + from.SendMessage( "You destroy the bone." ); + else + from.SendMessage( "You destroy the bone pile." ); + + Gold gold = new Gold( 25, 100 ); + + gold.MoveToWorld( GetWorldLocation(), Map ); + + Delete(); + + m_Timer.Stop(); + } + else + { + if ( ItemID == 0xF7E ) + from.SendMessage( "You damage the bone." ); + else + from.SendMessage( "You damage the bone pile." ); + } + } + + public UnholyBone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Timer = new SpawnTimer( this ); + m_Timer.Start(); + } + + private class SpawnTimer : Timer + { + private Item m_Item; + + public SpawnTimer( Item item ) : base( TimeSpan.FromSeconds( Utility.RandomMinMax( 5, 10 ) ) ) + { + Priority = TimerPriority.FiftyMS; + + m_Item = item; + } + + protected override void OnTick() + { + if ( m_Item.Deleted ) + return; + + Mobile spawn; + + switch ( Utility.Random( 12 ) ) + { + default: + case 0: spawn = new Skeleton(); break; + case 1: spawn = new Zombie(); break; + case 2: spawn = new Wraith(); break; + case 3: spawn = new Spectre(); break; + case 4: spawn = new Ghoul(); break; + case 5: spawn = new Mummy(); break; + case 6: spawn = new Bogle(); break; + case 7: spawn = new RottingCorpse(); break; + case 8: spawn = new BoneKnight(); break; + case 9: spawn = new SkeletalKnight(); break; + case 10: spawn = new Lich(); break; + case 11: spawn = new LichLord(); break; + } + + spawn.MoveToWorld( m_Item.Location, m_Item.Map ); + + m_Item.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/WarningItem.cs b/Scripts/Items/Misc/WarningItem.cs new file mode 100644 index 000000000..d9c21bc84 --- /dev/null +++ b/Scripts/Items/Misc/WarningItem.cs @@ -0,0 +1,241 @@ +using System; +using Server; +using Server.Network; +using System.Collections; + +namespace Server.Items +{ + public class WarningItem : Item + { + private string m_WarningString; + private int m_WarningNumber; + private int m_Range; + private TimeSpan m_ResetDelay; + + [CommandProperty( AccessLevel.GameMaster )] + public string WarningString + { + get{ return m_WarningString; } + set{ m_WarningString = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int WarningNumber + { + get{ return m_WarningNumber; } + set{ m_WarningNumber = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Range + { + get{ return m_Range; } + set{ if ( value > 18 ) value = 18; m_Range = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan ResetDelay + { + get{ return m_ResetDelay; } + set{ m_ResetDelay = value; } + } + + [Constructable] + public WarningItem( int itemID, int range, int warning ) : base( itemID ) + { + if ( range > 18 ) + range = 18; + + Movable = false; + + m_WarningNumber = warning; + m_Range = range; + } + + [Constructable] + public WarningItem( int itemID, int range, string warning ) : base( itemID ) + { + if ( range > 18 ) + range = 18; + + Movable = false; + + m_WarningString = warning; + m_Range = range; + } + + public WarningItem( Serial serial ) : base( serial ) + { + } + + private bool m_Broadcasting; + + private DateTime m_LastBroadcast; + + public virtual void SendMessage( Mobile triggerer, bool onlyToTriggerer, string messageString, int messageNumber ) + { + if ( onlyToTriggerer ) + { + if ( messageString != null ) + triggerer.SendMessage( messageString ); + else + triggerer.SendLocalizedMessage( messageNumber ); + } + else + { + if ( messageString != null ) + PublicOverheadMessage( MessageType.Regular, 0x3B2, false, messageString ); + else + PublicOverheadMessage( MessageType.Regular, 0x3B2, messageNumber ); + } + } + + public virtual bool OnlyToTriggerer{ get{ return false; } } + public virtual int NeighborRange { get { return 5; } } + + public virtual void Broadcast( Mobile triggerer ) + { + if ( m_Broadcasting || (DateTime.Now < (m_LastBroadcast + m_ResetDelay)) ) + return; + + m_LastBroadcast = DateTime.Now; + + m_Broadcasting = true; + + SendMessage( triggerer, this.OnlyToTriggerer, m_WarningString, m_WarningNumber ); + + if ( NeighborRange >= 0 ) + { + ArrayList list = new ArrayList(); + + foreach ( Item item in GetItemsInRange( NeighborRange ) ) + { + if ( item != this && item is WarningItem ) + list.Add( item ); + } + + for ( int i = 0; i < list.Count; i++ ) + ( (WarningItem) list[i] ).Broadcast( triggerer ); + } + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( InternalCallback ) ); + } + + private void InternalCallback() + { + m_Broadcasting = false; + } + + public override bool HandlesOnMovement{ get{ return true; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m.Player && Utility.InRange( m.Location, Location, m_Range ) && !Utility.InRange( oldLocation, Location, m_Range ) ) + Broadcast( m ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (string) m_WarningString ); + writer.Write( (int) m_WarningNumber ); + writer.Write( (int) m_Range ); + + writer.Write( (TimeSpan) m_ResetDelay ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_WarningString = reader.ReadString(); + m_WarningNumber = reader.ReadInt(); + m_Range = reader.ReadInt(); + m_ResetDelay = reader.ReadTimeSpan(); + + break; + } + } + } + } + + public class HintItem : WarningItem + { + private string m_HintString; + private int m_HintNumber; + + [CommandProperty( AccessLevel.GameMaster )] + public string HintString + { + get{ return m_HintString; } + set{ m_HintString = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int HintNumber + { + get{ return m_HintNumber; } + set{ m_HintNumber = value; } + } + + public override bool OnlyToTriggerer{ get{ return true; } } + + [Constructable] + public HintItem( int itemID, int range, int warning, int hint ) : base( itemID, range, warning ) + { + m_HintNumber = hint; + } + + [Constructable] + public HintItem( int itemID, int range, string warning, string hint ) : base( itemID, range, warning ) + { + m_HintString = hint; + } + + public HintItem( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + SendMessage( from, true, m_HintString, m_HintNumber ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (string) m_HintString ); + writer.Write( (int) m_HintNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_HintString = reader.ReadString(); + m_HintNumber = reader.ReadInt(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Misc/Waypoint.cs b/Scripts/Items/Misc/Waypoint.cs new file mode 100644 index 000000000..608ea3001 --- /dev/null +++ b/Scripts/Items/Misc/Waypoint.cs @@ -0,0 +1,161 @@ +using System; +using Server; +using Server.Targeting; +using Server.Commands; + +namespace Server.Items +{ + [FlipableAttribute( 0x1f14, 0x1f15, 0x1f16, 0x1f17 )] + public class WayPoint : Item + { + public static void Initialize() + { + CommandSystem.Register( "WayPointSeq", AccessLevel.GameMaster, new CommandEventHandler( WayPointSeq_OnCommand ) ); + } + + public static void WayPointSeq_OnCommand( CommandEventArgs arg ) + { + arg.Mobile.SendMessage( "Target the position of the first way point." ); + arg.Mobile.Target = new WayPointSeqTarget( null ); + } + + private WayPoint m_Next; + + public override string DefaultName + { + get { return "AI Way Point"; } + } + + [Constructable] + public WayPoint() : base( 0x1f14 ) + { + this.Hue = 0x498; + this.Visible = false; + //this.Movable = false; + } + + public WayPoint( WayPoint prev ) : this() + { + if ( prev != null ) + prev.NextPoint = this; + } + + [CommandProperty( AccessLevel.GameMaster )] + public WayPoint NextPoint + { + get + { + return m_Next; + } + set + { + if ( m_Next != this ) + m_Next = value; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + from.SendMessage( "Target the next way point in the sequence." ); + + from.Target = new NextPointTarget( this ); + } + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( m_Next == null ) + LabelTo( from, "(Unlinked)" ); + else + LabelTo( from, "(Linked: {0})", m_Next.Location ); + } + + public WayPoint( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + m_Next = reader.ReadItem() as WayPoint; + break; + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_Next ); + } + } + + public class NextPointTarget : Target + { + private WayPoint m_Point; + + public NextPointTarget( WayPoint pt ) : base( -1, false, TargetFlags.None ) + { + m_Point = pt; + } + + protected override void OnTarget( Mobile from, object target ) + { + if ( target is WayPoint && m_Point != null ) + { + m_Point.NextPoint = (WayPoint)target; + } + else + { + from.SendMessage( "Target a way point." ); + } + } + } + + public class WayPointSeqTarget : Target + { + private WayPoint m_Last; + + public WayPointSeqTarget( WayPoint last ) : base( -1, true, TargetFlags.None ) + { + m_Last = last; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is WayPoint ) + { + if ( m_Last != null ) + m_Last.NextPoint = (WayPoint)targeted; + } + else if ( targeted is IPoint3D ) + { + Point3D p = new Point3D( (IPoint3D)targeted ); + + WayPoint point = new WayPoint( m_Last ); + point.MoveToWorld( p, from.Map ); + + from.Target = new WayPointSeqTarget( point ); + from.SendMessage( "Target the position of the next way point in the sequence, or target a way point link the newest way point to." ); + } + else + { + from.SendMessage( "Target a position, or another way point." ); + } + } + } +} diff --git a/Scripts/Items/Misc/WindChimes.cs b/Scripts/Items/Misc/WindChimes.cs new file mode 100644 index 000000000..7cf3f231e --- /dev/null +++ b/Scripts/Items/Misc/WindChimes.cs @@ -0,0 +1,189 @@ +using System; +using Server; +using Server.Multis; +using Server.Gumps; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseWindChimes : Item + { + private bool m_TurnedOn; + + [CommandProperty( AccessLevel.GameMaster )] + public bool TurnedOn + { + get{ return m_TurnedOn; } + set{ m_TurnedOn = value; InvalidateProperties(); } + } + + public BaseWindChimes( int itemID ) : base( itemID ) + { + } + + private static int[] m_Sounds = new int[] { 0x505, 0x506, 0x507 }; + + public static int[] Sounds + { + get{ return m_Sounds; } + } + + public override bool HandlesOnMovement{ get{ return m_TurnedOn && IsLockedDown; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m_TurnedOn && IsLockedDown && (!m.Hidden || m.AccessLevel == AccessLevel.Player) && Utility.InRange( m.Location, this.Location, 2 ) && !Utility.InRange( oldLocation, this.Location, 2 ) ) + Effects.PlaySound( this.Location, this.Map, m_Sounds[Utility.Random( m_Sounds.Length )] ); + + base.OnMovement( m, oldLocation ); + } + + public BaseWindChimes( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_TurnedOn ) + list.Add( 502695 ); // turned on + else + list.Add( 502696 ); // turned off + } + + public bool IsOwner( Mobile mob ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + return ( house != null && house.IsOwner( mob ) ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsOwner( from ) ) + { + OnOffGump onOffGump = new OnOffGump( this ); + from.SendGump( onOffGump ); + } + else + { + from.SendLocalizedMessage( 502691 ); // You must be the owner to use this. + } + } + + private class OnOffGump : Gump + { + private BaseWindChimes m_Chimes; + + public OnOffGump( BaseWindChimes chimes ) : base( 150, 200 ) + { + m_Chimes = chimes; + + AddBackground( 0, 0, 300, 150, 0xA28 ); + AddHtmlLocalized( 45, 20, 300, 35, chimes.TurnedOn ? 1011035 : 1011034, false, false ); // [De]Activate this item + AddButton( 40, 53, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 55, 65, 35, 1011036, false, false ); // OKAY + AddButton( 150, 53, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 190, 55, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 1 ) + { + bool newValue = !m_Chimes.TurnedOn; + + m_Chimes.TurnedOn = newValue; + + if ( newValue && !m_Chimes.IsLockedDown ) + from.SendLocalizedMessage( 502693 ); // Remember, this only works when locked down. + } + else + { + from.SendLocalizedMessage( 502694 ); // Cancelled action. + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (bool) m_TurnedOn ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_TurnedOn = reader.ReadBool(); + break; + } + } + } + } + + public class WindChimes : BaseWindChimes + { + public override int LabelNumber{ get{ return 1030290; } } + + [Constructable] + public WindChimes() : base( 0x2832 ) + { + } + + public WindChimes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class FancyWindChimes : BaseWindChimes + { + public override int LabelNumber{ get{ return 1030291; } } + + [Constructable] + public FancyWindChimes() : base( 0x2833 ) + { + } + + public FancyWindChimes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/PlantsFlowers/Potted/EmptyPots.cs b/Scripts/Items/PlantsFlowers/Potted/EmptyPots.cs new file mode 100644 index 000000000..ef869040c --- /dev/null +++ b/Scripts/Items/PlantsFlowers/Potted/EmptyPots.cs @@ -0,0 +1,58 @@ +using System; + +namespace Server.Items +{ + public class SmallEmptyPot : Item + { + [Constructable] + public SmallEmptyPot() : base(0x11C6) + { + Weight = 100; + } + + public SmallEmptyPot(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class LargeEmptyPot : Item + { + [Constructable] + public LargeEmptyPot() : base(0x11C7) + { + Weight = 6; + } + + public LargeEmptyPot(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/PlantsFlowers/Potted/PottedCactus.cs b/Scripts/Items/PlantsFlowers/Potted/PottedCactus.cs new file mode 100644 index 000000000..0b5accbde --- /dev/null +++ b/Scripts/Items/PlantsFlowers/Potted/PottedCactus.cs @@ -0,0 +1,166 @@ +using System; + +namespace Server.Items +{ + public class PottedCactus : Item + { + [Constructable] + public PottedCactus() : base(0x1E0F) + { + Weight = 100; + } + + public PottedCactus(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedCactus1 : Item + { + [Constructable] + public PottedCactus1() : base(0x1E10) + { + Weight = 100; + } + + public PottedCactus1(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedCactus2 : Item + { + [Constructable] + public PottedCactus2() : base(0x1E11) + { + Weight = 100; + } + + public PottedCactus2(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedCactus3 : Item + { + [Constructable] + public PottedCactus3() : base(0x1E12) + { + Weight = 100; + } + + public PottedCactus3(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedCactus4 : Item + { + [Constructable] + public PottedCactus4() : base(0x1E13) + { + Weight = 100; + } + + public PottedCactus4(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedCactus5 : Item + { + [Constructable] + public PottedCactus5() : base(0x1E14) + { + Weight = 100; + } + + public PottedCactus5(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/PlantsFlowers/Potted/PottedPlants.cs b/Scripts/Items/PlantsFlowers/Potted/PottedPlants.cs new file mode 100644 index 000000000..b085d6e9d --- /dev/null +++ b/Scripts/Items/PlantsFlowers/Potted/PottedPlants.cs @@ -0,0 +1,85 @@ +using System; + +namespace Server.Items +{ + public class PottedPlant : Item + { + [Constructable] + public PottedPlant() : base(0x11CA) + { + Weight = 100; + } + + public PottedPlant(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedPlant1 : Item + { + [Constructable] + public PottedPlant1() : base(0x11CB) + { + Weight = 100; + } + + public PottedPlant1(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedPlant2 : Item + { + [Constructable] + public PottedPlant2() : base(0x11CC) + { + Weight = 100; + } + + public PottedPlant2(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/PlantsFlowers/Potted/PottedTrees.cs b/Scripts/Items/PlantsFlowers/Potted/PottedTrees.cs new file mode 100644 index 000000000..f4ab44b2a --- /dev/null +++ b/Scripts/Items/PlantsFlowers/Potted/PottedTrees.cs @@ -0,0 +1,58 @@ +using System; + +namespace Server.Items +{ + public class PottedTree : Item + { + [Constructable] + public PottedTree() : base(0x11C8) + { + Weight = 100; + } + + public PottedTree(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } + + public class PottedTree1 : Item + { + [Constructable] + public PottedTree1() : base(0x11C9) + { + Weight = 100; + } + + public PottedTree1(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Arrows/Arrow.cs b/Scripts/Items/Resources/Arrows/Arrow.cs new file mode 100644 index 000000000..911420567 --- /dev/null +++ b/Scripts/Items/Resources/Arrows/Arrow.cs @@ -0,0 +1,52 @@ +using System; + +namespace Server.Items +{ + public class Arrow : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} arrow" : "{0} arrows", Amount ); + } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Arrow() : this( 1 ) + { + } + + [Constructable] + public Arrow( int amount ) : base( 0xF3F ) + { + Stackable = true; + Amount = amount; + } + + public Arrow( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Arrows/Bolt.cs b/Scripts/Items/Resources/Arrows/Bolt.cs new file mode 100644 index 000000000..5f3762938 --- /dev/null +++ b/Scripts/Items/Resources/Arrows/Bolt.cs @@ -0,0 +1,52 @@ +using System; + +namespace Server.Items +{ + public class Bolt : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} bolt" : "{0} bolts", Amount ); + } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Bolt() : this( 1 ) + { + } + + [Constructable] + public Bolt( int amount ) : base( 0x1BFB ) + { + Stackable = true; + Amount = amount; + } + + public Bolt( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Arrows/Feather.cs b/Scripts/Items/Resources/Arrows/Feather.cs new file mode 100644 index 000000000..9ace60b31 --- /dev/null +++ b/Scripts/Items/Resources/Arrows/Feather.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class Feather : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} feather" : "{0} feathers", Amount ); + } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Feather() : this( 1 ) + { + } + + [Constructable] + public Feather( int amount ) : base( 0x1BD1 ) + { + Stackable = true; + Amount = amount; + } + + public Feather( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Arrows/Shaft.cs b/Scripts/Items/Resources/Arrows/Shaft.cs new file mode 100644 index 000000000..cb0f4269a --- /dev/null +++ b/Scripts/Items/Resources/Arrows/Shaft.cs @@ -0,0 +1,53 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class Shaft : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} shaft" : "{0} shafts", Amount ); + } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Shaft() : this( 1 ) + { + } + + [Constructable] + public Shaft( int amount ) : base( 0x1BD4 ) + { + Stackable = true; + Amount = amount; + } + + public Shaft( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Blacksmithing/Ingots.cs b/Scripts/Items/Resources/Blacksmithing/Ingots.cs new file mode 100644 index 000000000..80e0a9bc6 --- /dev/null +++ b/Scripts/Items/Resources/Blacksmithing/Ingots.cs @@ -0,0 +1,434 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseIngot : Item, ICommodity + { + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; InvalidateProperties(); } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} {1} ingot" : "{0} {1} ingots", Amount, CraftResources.GetName( m_Resource ).ToLower() ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + case 0: + { + OreInfo info; + + switch ( reader.ReadInt() ) + { + case 0: info = OreInfo.Iron; break; + case 1: info = OreInfo.DullCopper; break; + case 2: info = OreInfo.ShadowIron; break; + case 3: info = OreInfo.Copper; break; + case 4: info = OreInfo.Bronze; break; + case 5: info = OreInfo.Gold; break; + case 6: info = OreInfo.Agapite; break; + case 7: info = OreInfo.Verite; break; + case 8: info = OreInfo.Valorite; break; + default: info = null; break; + } + + m_Resource = CraftResources.GetFromOreInfo( info ); + break; + } + } + } + + public BaseIngot( CraftResource resource ) : this( resource, 1 ) + { + } + + public BaseIngot( CraftResource resource, int amount ) : base( 0x1BF2 ) + { + Stackable = true; + Amount = amount; + Hue = CraftResources.GetHue( resource ); + + m_Resource = resource; + } + + public BaseIngot( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( Amount > 1 ) + list.Add( 1050039, "{0}\t#{1}", Amount, 1027154 ); // ~1_NUMBER~ ~2_ITEMNAME~ + else + list.Add( 1027154 ); // ingots + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( !CraftResources.IsStandard( m_Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( m_Resource ); + + if ( num > 0 ) + list.Add( num ); + else + list.Add( CraftResources.GetName( m_Resource ) ); + } + } + + public override int LabelNumber + { + get + { + if ( m_Resource >= CraftResource.DullCopper && m_Resource <= CraftResource.Valorite ) + return 1042684 + (int)(m_Resource - CraftResource.DullCopper); + + return 1042692; + } + } + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class IronIngot : BaseIngot + { + [Constructable] + public IronIngot() : this( 1 ) + { + } + + [Constructable] + public IronIngot( int amount ) : base( CraftResource.Iron, amount ) + { + } + + public IronIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class DullCopperIngot : BaseIngot + { + [Constructable] + public DullCopperIngot() : this( 1 ) + { + } + + [Constructable] + public DullCopperIngot( int amount ) : base( CraftResource.DullCopper, amount ) + { + } + + public DullCopperIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class ShadowIronIngot : BaseIngot + { + [Constructable] + public ShadowIronIngot() : this( 1 ) + { + } + + [Constructable] + public ShadowIronIngot( int amount ) : base( CraftResource.ShadowIron, amount ) + { + } + + public ShadowIronIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class CopperIngot : BaseIngot + { + [Constructable] + public CopperIngot() : this( 1 ) + { + } + + [Constructable] + public CopperIngot( int amount ) : base( CraftResource.Copper, amount ) + { + } + + public CopperIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class BronzeIngot : BaseIngot + { + [Constructable] + public BronzeIngot() : this( 1 ) + { + } + + [Constructable] + public BronzeIngot( int amount ) : base( CraftResource.Bronze, amount ) + { + } + + public BronzeIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class GoldIngot : BaseIngot + { + [Constructable] + public GoldIngot() : this( 1 ) + { + } + + [Constructable] + public GoldIngot( int amount ) : base( CraftResource.Gold, amount ) + { + } + + public GoldIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class AgapiteIngot : BaseIngot + { + [Constructable] + public AgapiteIngot() : this( 1 ) + { + } + + [Constructable] + public AgapiteIngot( int amount ) : base( CraftResource.Agapite, amount ) + { + } + + public AgapiteIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class VeriteIngot : BaseIngot + { + [Constructable] + public VeriteIngot() : this( 1 ) + { + } + + [Constructable] + public VeriteIngot( int amount ) : base( CraftResource.Verite, amount ) + { + } + + public VeriteIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1BF2, 0x1BEF )] + public class ValoriteIngot : BaseIngot + { + [Constructable] + public ValoriteIngot() : this( 1 ) + { + } + + [Constructable] + public ValoriteIngot( int amount ) : base( CraftResource.Valorite, amount ) + { + } + + public ValoriteIngot( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Blacksmithing/Ore.cs b/Scripts/Items/Resources/Blacksmithing/Ore.cs new file mode 100644 index 000000000..808db5879 --- /dev/null +++ b/Scripts/Items/Resources/Blacksmithing/Ore.cs @@ -0,0 +1,585 @@ +using System; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.Engines.Craft; + +namespace Server.Items +{ + public abstract class BaseOre : Item, ICommodity + { + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; InvalidateProperties(); } + } + + string ICommodity.Description + { + get + { + return String.Format( "{0} {1} ore", Amount, CraftResources.GetName( m_Resource ).ToLower() ); + } + } + + public abstract BaseIngot GetIngot(); + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + case 0: + { + OreInfo info; + + switch ( reader.ReadInt() ) + { + case 0: info = OreInfo.Iron; break; + case 1: info = OreInfo.DullCopper; break; + case 2: info = OreInfo.ShadowIron; break; + case 3: info = OreInfo.Copper; break; + case 4: info = OreInfo.Bronze; break; + case 5: info = OreInfo.Gold; break; + case 6: info = OreInfo.Agapite; break; + case 7: info = OreInfo.Verite; break; + case 8: info = OreInfo.Valorite; break; + default: info = null; break; + } + + m_Resource = CraftResources.GetFromOreInfo( info ); + break; + } + } + } + + public BaseOre( CraftResource resource ) : this( resource, 1 ) + { + } + + public BaseOre( CraftResource resource, int amount ) : base( 0x19B9 ) + { + Stackable = true; + Weight = 12.0; + Amount = amount; + Hue = CraftResources.GetHue( resource ); + + m_Resource = resource; + } + + public BaseOre( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( Amount > 1 ) + list.Add( 1050039, "{0}\t#{1}", Amount, 1026583 ); // ~1_NUMBER~ ~2_ITEMNAME~ + else + list.Add( 1026583 ); // ore + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( !CraftResources.IsStandard( m_Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( m_Resource ); + + if ( num > 0 ) + list.Add( num ); + else + list.Add( CraftResources.GetName( m_Resource ) ); + } + } + + public override int LabelNumber + { + get + { + if ( m_Resource >= CraftResource.DullCopper && m_Resource <= CraftResource.Valorite ) + return 1042845 + (int)(m_Resource - CraftResource.DullCopper); + + return 1042853; // iron ore; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + if ( from.InRange( this.GetWorldLocation(), 2 ) ) + { + from.SendLocalizedMessage( 501971 ); // Select the forge on which to smelt the ore, or another pile of ore with which to combine it. + from.Target = new InternalTarget( this ); + } + else + { + from.SendLocalizedMessage( 501976 ); // The ore is too far away. + } + } + + private class InternalTarget : Target + { + private BaseOre m_Ore; + + public InternalTarget( BaseOre ore ) : base ( 2, false, TargetFlags.None ) + { + m_Ore = ore; + } + + private bool IsForge( object obj ) + { + if ( obj.GetType().IsDefined( typeof( ForgeAttribute ), false ) ) + return true; + + int itemID = 0; + + if ( obj is Item ) + itemID = ((Item)obj).ItemID; + else if ( obj is StaticTarget ) + itemID = ((StaticTarget)obj).ItemID & 0x3FFF; + + return ( itemID == 4017 || (itemID >= 6522 && itemID <= 6569) ); + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Ore.Deleted ) + return; + + if ( !from.InRange( m_Ore.GetWorldLocation(), 2 ) ) + { + from.SendLocalizedMessage( 501976 ); // The ore is too far away. + return; + } + + if ( IsForge( targeted ) ) + { + double difficulty; + + switch ( m_Ore.Resource ) + { + default: difficulty = 50.0; break; + case CraftResource.DullCopper: difficulty = 65.0; break; + case CraftResource.ShadowIron: difficulty = 70.0; break; + case CraftResource.Copper: difficulty = 75.0; break; + case CraftResource.Bronze: difficulty = 80.0; break; + case CraftResource.Gold: difficulty = 85.0; break; + case CraftResource.Agapite: difficulty = 90.0; break; + case CraftResource.Verite: difficulty = 95.0; break; + case CraftResource.Valorite: difficulty = 99.0; break; + } + + double minSkill = difficulty - 25.0; + double maxSkill = difficulty + 25.0; + + if ( difficulty > 50.0 && difficulty > from.Skills[SkillName.Mining].Value ) + { + from.SendLocalizedMessage( 501986 ); // You have no idea how to smelt this strange ore! + return; + } + + if ( from.CheckTargetSkill( SkillName.Mining, targeted, minSkill, maxSkill ) ) + { + int toConsume = m_Ore.Amount; + + if ( toConsume <= 0 ) + { + from.SendLocalizedMessage( 501987 ); // There is not enough metal-bearing ore in this pile to make an ingot. + } + else + { + if ( toConsume > 30000 ) + toConsume = 30000; + + BaseIngot ingot = m_Ore.GetIngot(); + ingot.Amount = toConsume * 2; + + m_Ore.Consume( toConsume ); + from.AddToBackpack( ingot ); + //from.PlaySound( 0x57 ); + + + from.SendLocalizedMessage( 501988 ); // You smelt the ore removing the impurities and put the metal in your backpack. + } + } + else if ( m_Ore.Amount < 2 ) + { + from.SendLocalizedMessage( 501989 ); // You burn away the impurities but are left with no useable metal. + m_Ore.Delete(); + } + else + { + from.SendLocalizedMessage( 501990 ); // You burn away the impurities but are left with less useable metal. + m_Ore.Amount /= 2; + } + } + } + } + } + + public class IronOre : BaseOre + { + [Constructable] + public IronOre() : this( 1 ) + { + } + + [Constructable] + public IronOre( int amount ) : base( CraftResource.Iron, amount ) + { + } + + public IronOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new IronIngot(); + } + } + + public class DullCopperOre : BaseOre + { + [Constructable] + public DullCopperOre() : this( 1 ) + { + } + + [Constructable] + public DullCopperOre( int amount ) : base( CraftResource.DullCopper, amount ) + { + } + + public DullCopperOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new DullCopperIngot(); + } + } + + public class ShadowIronOre : BaseOre + { + [Constructable] + public ShadowIronOre() : this( 1 ) + { + } + + [Constructable] + public ShadowIronOre( int amount ) : base( CraftResource.ShadowIron, amount ) + { + } + + public ShadowIronOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new ShadowIronIngot(); + } + } + + public class CopperOre : BaseOre + { + [Constructable] + public CopperOre() : this( 1 ) + { + } + + [Constructable] + public CopperOre( int amount ) : base( CraftResource.Copper, amount ) + { + } + + public CopperOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new CopperIngot(); + } + } + + public class BronzeOre : BaseOre + { + [Constructable] + public BronzeOre() : this( 1 ) + { + } + + [Constructable] + public BronzeOre( int amount ) : base( CraftResource.Bronze, amount ) + { + } + + public BronzeOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new BronzeIngot(); + } + } + + public class GoldOre : BaseOre + { + [Constructable] + public GoldOre() : this( 1 ) + { + } + + [Constructable] + public GoldOre( int amount ) : base( CraftResource.Gold, amount ) + { + } + + public GoldOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new GoldIngot(); + } + } + + public class AgapiteOre : BaseOre + { + [Constructable] + public AgapiteOre() : this( 1 ) + { + } + + [Constructable] + public AgapiteOre( int amount ) : base( CraftResource.Agapite, amount ) + { + } + + public AgapiteOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new AgapiteIngot(); + } + } + + public class VeriteOre : BaseOre + { + [Constructable] + public VeriteOre() : this( 1 ) + { + } + + [Constructable] + public VeriteOre( int amount ) : base( CraftResource.Verite, amount ) + { + } + + public VeriteOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new VeriteIngot(); + } + } + + public class ValoriteOre : BaseOre + { + [Constructable] + public ValoriteOre() : this( 1 ) + { + } + + [Constructable] + public ValoriteOre( int amount ) : base( CraftResource.Valorite, amount ) + { + } + + public ValoriteOre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public override BaseIngot GetIngot() + { + return new ValoriteIngot(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Blacksmithing/Scales.cs b/Scripts/Items/Resources/Blacksmithing/Scales.cs new file mode 100644 index 000000000..eb1cef9f1 --- /dev/null +++ b/Scripts/Items/Resources/Blacksmithing/Scales.cs @@ -0,0 +1,275 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseScales : Item, ICommodity + { + public override int LabelNumber{ get{ return 1053139; } } // dragon scales + + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; InvalidateProperties(); } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{1}" : "{0} {1}", Amount, CraftResources.GetName( m_Resource ).ToLower() ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + } + } + + public BaseScales( CraftResource resource ) : this( resource, 1 ) + { + } + + public BaseScales( CraftResource resource, int amount ) : base( 0x26B4 ) + { + Stackable = true; + Amount = amount; + Hue = CraftResources.GetHue( resource ); + + m_Resource = resource; + } + + public BaseScales( Serial serial ) : base( serial ) + { + } + } + + public class RedScales : BaseScales + { + [Constructable] + public RedScales() : this( 1 ) + { + } + + [Constructable] + public RedScales( int amount ) : base( CraftResource.RedScales, amount ) + { + } + + public RedScales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + public class YellowScales : BaseScales + { + [Constructable] + public YellowScales() : this( 1 ) + { + } + + [Constructable] + public YellowScales( int amount ) : base( CraftResource.YellowScales, amount ) + { + } + + public YellowScales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + public class BlackScales : BaseScales + { + [Constructable] + public BlackScales() : this( 1 ) + { + } + + [Constructable] + public BlackScales( int amount ) : base( CraftResource.BlackScales, amount ) + { + } + + public BlackScales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + public class GreenScales : BaseScales + { + [Constructable] + public GreenScales() : this( 1 ) + { + } + + [Constructable] + public GreenScales( int amount ) : base( CraftResource.GreenScales, amount ) + { + } + + public GreenScales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + public class WhiteScales : BaseScales + { + [Constructable] + public WhiteScales() : this( 1 ) + { + } + + [Constructable] + public WhiteScales( int amount ) : base( CraftResource.WhiteScales, amount ) + { + } + + public WhiteScales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + public class BlueScales : BaseScales + { + public override int LabelNumber{ get{ return 1053140; } } // sea serpent scales + + [Constructable] + public BlueScales() : this( 1 ) + { + } + + [Constructable] + public BlueScales( int amount ) : base( CraftResource.BlueScales, amount ) + { + } + + public BlueScales( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Fishing/BigFish.cs b/Scripts/Items/Resources/Fishing/BigFish.cs new file mode 100644 index 000000000..6e0578f29 --- /dev/null +++ b/Scripts/Items/Resources/Fishing/BigFish.cs @@ -0,0 +1,79 @@ +using System; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class BigFish : Item, ICarvable + { + private Mobile m_Fisher; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Fisher + { + get{ return m_Fisher; } + set{ m_Fisher = value; InvalidateProperties(); } + } + + public void Carve( Mobile from, Item item ) + { + base.ScissorHelper( from, new RawFishSteak(), Math.Max( 16, (int)Weight ) / 4 , false ); + } + + public override int LabelNumber{ get{ return 1041112; } } // a big fish + + [Constructable] + public BigFish() : base( 0x09CC ) + { + Weight = Utility.RandomMinMax( 3, 200 ); //TODO: Find correct formula. max on OSI currently 200, OSI dev says it's not 200 as max, and ~ 1/1,000,000 chance to get highest + Hue = 0x847; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( Weight >= 20 ) + { + if ( m_Fisher != null ) + list.Add( 1070857, m_Fisher.Name ); // Caught by ~1_fisherman~ + + list.Add( 1070858, ((int)Weight).ToString() ); // ~1_weight~ stones + } + } + + public BigFish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (Mobile) m_Fisher ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Fisher = reader.ReadMobile(); + break; + } + case 0: + { + Weight = Utility.RandomMinMax( 3, 200 ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Fishing/Fish.cs b/Scripts/Items/Resources/Fishing/Fish.cs new file mode 100644 index 000000000..541439e0b --- /dev/null +++ b/Scripts/Items/Resources/Fishing/Fish.cs @@ -0,0 +1,47 @@ +using System; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class Fish : Item, ICarvable + { + public void Carve( Mobile from, Item item ) + { + base.ScissorHelper( from, new RawFishSteak(), 4 ); + } + + [Constructable] + public Fish() : this( 1 ) + { + } + + [Constructable] + public Fish( int amount ) : base( Utility.Random( 0x09CC, 4 ) ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + + + public Fish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Resources/Fishing/MagicFish.cs b/Scripts/Items/Resources/Fishing/MagicFish.cs new file mode 100644 index 000000000..761b9d45b --- /dev/null +++ b/Scripts/Items/Resources/Fishing/MagicFish.cs @@ -0,0 +1,201 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseMagicFish : Item + { + public virtual int Bonus{ get{ return 0; } } + public virtual StatType Type{ get{ return StatType.Str; } } + + public override double DefaultWeight + { + get { return 1.0; } + } + + public BaseMagicFish( int hue ) : base( 0xDD6 ) + { + Hue = hue; + } + + public BaseMagicFish( Serial serial ) : base( serial ) + { + } + + public virtual bool Apply( Mobile from ) + { + bool applied = Spells.SpellHelper.AddStatOffset( from, Type, Bonus, TimeSpan.FromMinutes( 1.0 ) ); + + if ( !applied ) + from.SendLocalizedMessage( 502173 ); // You are already under a similar effect. + + return applied; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( Apply( from ) ) + { + from.FixedEffect( 0x375A, 10, 15 ); + from.PlaySound( 0x1E7 ); + Delete(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PrizedFish : BaseMagicFish + { + public override int Bonus{ get{ return 5; } } + public override StatType Type{ get{ return StatType.Int; } } + + public override int LabelNumber{ get{ return 1041073; } } // prized fish + + [Constructable] + public PrizedFish() : base( 51 ) + { + } + + public PrizedFish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 151 ) + Hue = 51; + } + } + + public class WondrousFish : BaseMagicFish + { + public override int Bonus{ get{ return 5; } } + public override StatType Type{ get{ return StatType.Dex; } } + + public override int LabelNumber{ get{ return 1041074; } } // wondrous fish + + [Constructable] + public WondrousFish() : base( 86 ) + { + } + + public WondrousFish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 286 ) + Hue = 86; + } + } + + public class TrulyRareFish : BaseMagicFish + { + public override int Bonus{ get{ return 5; } } + public override StatType Type{ get{ return StatType.Str; } } + + public override int LabelNumber{ get{ return 1041075; } } // truly rare fish + + [Constructable] + public TrulyRareFish() : base( 76 ) + { + } + + public TrulyRareFish( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 376 ) + Hue = 76; + } + } + + public class PeculiarFish : BaseMagicFish + { + public override int LabelNumber{ get{ return 1041076; } } // highly peculiar fish + + [Constructable] + public PeculiarFish() : base( 66 ) + { + } + + public PeculiarFish( Serial serial ) : base( serial ) + { + } + + public override bool Apply( Mobile from ) + { + from.Stam += 10; + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 266 ) + Hue = 66; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Masonry/Granite.cs b/Scripts/Items/Resources/Masonry/Granite.cs new file mode 100644 index 000000000..ba26d2290 --- /dev/null +++ b/Scripts/Items/Resources/Masonry/Granite.cs @@ -0,0 +1,318 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseGranite : Item, ICommodity + { + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; InvalidateProperties(); } + } + + string ICommodity.Description + { + get + { + return String.Format( "{0} {1} high quality granite", Amount, CraftResources.GetName( m_Resource ).ToLower() ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + } + } + + public override double DefaultWeight + { + get { return 10.0; } + } + + public BaseGranite( CraftResource resource ) : base( 0x1779 ) + { + Hue = CraftResources.GetHue( resource ); + + m_Resource = resource; + } + + public BaseGranite( Serial serial ) : base( serial ) + { + } + + public override int LabelNumber{ get{ return 1044607; } } // high quality granite + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( !CraftResources.IsStandard( m_Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( m_Resource ); + + if ( num > 0 ) + list.Add( num ); + else + list.Add( CraftResources.GetName( m_Resource ) ); + } + } + } + + public class Granite : BaseGranite + { + [Constructable] + public Granite() : base( CraftResource.Iron ) + { + } + + public Granite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DullCopperGranite : BaseGranite + { + [Constructable] + public DullCopperGranite() : base( CraftResource.DullCopper ) + { + } + + public DullCopperGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ShadowIronGranite : BaseGranite + { + [Constructable] + public ShadowIronGranite() : base( CraftResource.ShadowIron ) + { + } + + public ShadowIronGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CopperGranite : BaseGranite + { + [Constructable] + public CopperGranite() : base( CraftResource.Copper ) + { + } + + public CopperGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BronzeGranite : BaseGranite + { + [Constructable] + public BronzeGranite() : base( CraftResource.Bronze ) + { + } + + public BronzeGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class GoldGranite : BaseGranite + { + [Constructable] + public GoldGranite() : base( CraftResource.Gold ) + { + } + + public GoldGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class AgapiteGranite : BaseGranite + { + [Constructable] + public AgapiteGranite() : base( CraftResource.Agapite ) + { + } + + public AgapiteGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class VeriteGranite : BaseGranite + { + [Constructable] + public VeriteGranite() : base( CraftResource.Verite ) + { + } + + public VeriteGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ValoriteGranite : BaseGranite + { + [Constructable] + public ValoriteGranite() : base( CraftResource.Valorite ) + { + } + + public ValoriteGranite( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/MiscMLResources.cs b/Scripts/Items/Resources/MiscMLResources.cs new file mode 100644 index 000000000..de498f604 --- /dev/null +++ b/Scripts/Items/Resources/MiscMLResources.cs @@ -0,0 +1,1003 @@ +using System; + +namespace Server.Items +{ + public class Blight : Item + { + [Constructable] + public Blight() + : this( 1 ) + { + } + + [Constructable] + public Blight( int amount ) + : base( 0x3183 ) + { + Stackable = true; + Amount = amount; + } + + public Blight( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LuminescentFungi : Item + { + [Constructable] + public LuminescentFungi() + : this( 1 ) + { + } + + [Constructable] + public LuminescentFungi( int amount ) + : base( 0x3191 ) + { + Stackable = true; + Amount = amount; + } + + public LuminescentFungi( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class CapturedEssence : Item + { + [Constructable] + public CapturedEssence() + : this( 1 ) + { + } + + [Constructable] + public CapturedEssence( int amount ) + : base( 0x318E ) + { + Stackable = true; + Amount = amount; + } + + public CapturedEssence( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class EyeOfTheTravesty : Item + { + [Constructable] + public EyeOfTheTravesty() + : this( 1 ) + { + } + + [Constructable] + public EyeOfTheTravesty( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public EyeOfTheTravesty( int amount ) + : base( 0x318D ) + { + Stackable = true; + Amount = amount; + } + + public EyeOfTheTravesty( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class Corruption : Item + { + [Constructable] + public Corruption() + : this( 1 ) + { + } + + [Constructable] + public Corruption( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Corruption( int amount ) + : base( 0x3184 ) + { + Stackable = true; + Amount = amount; + } + + public Corruption( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class DreadHornMane : Item + { + [Constructable] + public DreadHornMane() + : this( 1 ) + { + } + + [Constructable] + public DreadHornMane( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public DreadHornMane( int amount ) + : base( 0x318A ) + { + Stackable = true; + Amount = amount; + } + + public DreadHornMane( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class ParasiticPlant : Item + { + [Constructable] + public ParasiticPlant() + : this( 1 ) + { + } + + [Constructable] + public ParasiticPlant( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public ParasiticPlant( int amount ) + : base( 0x3190 ) + { + Stackable = true; + Amount = amount; + } + + public ParasiticPlant( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class Muculent : Item + { + [Constructable] + public Muculent() + : this( 1 ) + { + } + + [Constructable] + public Muculent( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Muculent( int amount ) + : base( 0x3188 ) + { + Stackable = true; + Amount = amount; + } + + public Muculent( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class DiseasedBark : Item + { + [Constructable] + public DiseasedBark() + : this( 1 ) + { + } + + [Constructable] + public DiseasedBark( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public DiseasedBark( int amount ) + : base( 0x318B ) + { + Stackable = true; + Amount = amount; + } + + public DiseasedBark( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class BarkFragment : Item + { + [Constructable] + public BarkFragment() + : this( 1 ) + { + } + + [Constructable] + public BarkFragment( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public BarkFragment( int amount ) + : base( 0x318F ) + { + Stackable = true; + Amount = amount; + } + + public BarkFragment( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class GrizzledBones : Item + { + [Constructable] + public GrizzledBones() + : this( 1 ) + { + } + + [Constructable] + public GrizzledBones( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public GrizzledBones( int amount ) + : base( 0x318F ) + { + Stackable = true; + Amount = amount; + } + + public GrizzledBones( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class LardOfParoxysmus : Item + { + [Constructable] + public LardOfParoxysmus() + : this( 1 ) + { + } + + [Constructable] + public LardOfParoxysmus( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public LardOfParoxysmus( int amount ) + : base( 0x3189 ) + { + Stackable = true; + Amount = amount; + } + + public LardOfParoxysmus( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PerfectEmerald : Item + { + [Constructable] + public PerfectEmerald() + : this( 1 ) + { + } + + [Constructable] + public PerfectEmerald( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public PerfectEmerald( int amount ) + : base( 0x3194 ) + { + Stackable = true; + Amount = amount; + } + + public PerfectEmerald( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class DarkSapphire : Item + { + [Constructable] + public DarkSapphire() + : this( 1 ) + { + } + + [Constructable] + public DarkSapphire( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public DarkSapphire( int amount ) + : base( 0x3192 ) + { + Stackable = true; + Amount = amount; + } + + public DarkSapphire( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class Turquoise : Item + { + [Constructable] + public Turquoise() + : this( 1 ) + { + } + + [Constructable] + public Turquoise( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Turquoise( int amount ) + : base( 0x3193 ) + { + Stackable = true; + Amount = amount; + } + + public Turquoise( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class EcruCitrine : Item + { + [Constructable] + public EcruCitrine() + : this( 1 ) + { + } + + [Constructable] + public EcruCitrine( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public EcruCitrine( int amount ) + : base( 0x3195 ) + { + Stackable = true; + Amount = amount; + } + + public EcruCitrine( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class WhitePearl : Item + { + [Constructable] + public WhitePearl() + : this( 1 ) + { + } + + [Constructable] + public WhitePearl( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public WhitePearl( int amount ) + : base( 0x3196 ) + { + Stackable = true; + Amount = amount; + } + + public WhitePearl( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class FireRuby : Item + { + [Constructable] + public FireRuby() + : this( 1 ) + { + } + + [Constructable] + public FireRuby( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public FireRuby( int amount ) + : base( 0x3197 ) + { + Stackable = true; + Amount = amount; + } + + public FireRuby( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class BlueDiamond : Item + { + [Constructable] + public BlueDiamond() + : this( 1 ) + { + } + + [Constructable] + public BlueDiamond( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public BlueDiamond( int amount ) + : base( 0x3198 ) + { + Stackable = true; + Amount = amount; + } + + public BlueDiamond( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class BrilliantAmber : Item + { + [Constructable] + public BrilliantAmber() + : this( 1 ) + { + } + + [Constructable] + public BrilliantAmber( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public BrilliantAmber( int amount ) + : base( 0x3199 ) + { + Stackable = true; + Amount = amount; + } + + public BrilliantAmber( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Scourge : Item + { + [Constructable] + public Scourge() + : this( 1 ) + { + } + + [Constructable] + public Scourge( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Scourge( int amount ) + : base( 0x3185 ) + { + Stackable = true; + Amount = amount; + Hue = 150; + } + + public Scourge( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class Putrefication : Item + { + [Constructable] + public Putrefication() + : this( 1 ) + { + } + + [Constructable] + public Putrefication( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Putrefication( int amount ) + : base( 0x3186 ) + { + Stackable = true; + Amount = amount; + Hue = 883; + } + + public Putrefication( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + + public class Taint : Item + { + [Constructable] + public Taint() + : this( 1 ) + { + } + + [Constructable] + public Taint( int amountFrom, int amountTo ) + : this( Utility.RandomMinMax( amountFrom, amountTo ) ) + { + } + + [Constructable] + public Taint( int amount ) + : base( 0x3187 ) + { + Stackable = true; + Amount = amount; + Hue = 731; + } + + public Taint( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x315A, 0x315B )] + public class PristineDreadHorn : Item + { + [Constructable] + public PristineDreadHorn() + : base( 0x315A ) + { + + } + + public PristineDreadHorn( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Resources/Reagents/BagOfAllReagents.cs b/Scripts/Items/Resources/Reagents/BagOfAllReagents.cs new file mode 100644 index 000000000..4791fbeeb --- /dev/null +++ b/Scripts/Items/Resources/Reagents/BagOfAllReagents.cs @@ -0,0 +1,50 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BagOfAllReagents : Bag + { + [Constructable] + public BagOfAllReagents() : this( 50 ) + { + } + + [Constructable] + public BagOfAllReagents( int amount ) + { + DropItem( new BlackPearl ( amount ) ); + DropItem( new Bloodmoss ( amount ) ); + DropItem( new Garlic ( amount ) ); + DropItem( new Ginseng ( amount ) ); + DropItem( new MandrakeRoot ( amount ) ); + DropItem( new Nightshade ( amount ) ); + DropItem( new SulfurousAsh ( amount ) ); + DropItem( new SpidersSilk ( amount ) ); + DropItem( new BatWing ( amount ) ); + DropItem( new GraveDust ( amount ) ); + DropItem( new DaemonBlood ( amount ) ); + DropItem( new NoxCrystal ( amount ) ); + DropItem( new PigIron ( amount ) ); + } + + public BagOfAllReagents( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/BagOfNecroReagents.cs b/Scripts/Items/Resources/Reagents/BagOfNecroReagents.cs new file mode 100644 index 000000000..5f4b48b3d --- /dev/null +++ b/Scripts/Items/Resources/Reagents/BagOfNecroReagents.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BagOfNecroReagents : Bag + { + [Constructable] + public BagOfNecroReagents() : this( 50 ) + { + } + + [Constructable] + public BagOfNecroReagents( int amount ) + { + DropItem( new BatWing ( amount ) ); + DropItem( new GraveDust ( amount ) ); + DropItem( new DaemonBlood( amount ) ); + DropItem( new NoxCrystal ( amount ) ); + DropItem( new PigIron ( amount ) ); + } + + public BagOfNecroReagents( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/BagOfReagents.cs b/Scripts/Items/Resources/Reagents/BagOfReagents.cs new file mode 100644 index 000000000..31cfe86af --- /dev/null +++ b/Scripts/Items/Resources/Reagents/BagOfReagents.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BagOfReagents : Bag + { + [Constructable] + public BagOfReagents() : this( 50 ) + { + } + + [Constructable] + public BagOfReagents( int amount ) + { + DropItem( new BlackPearl ( amount ) ); + DropItem( new Bloodmoss ( amount ) ); + DropItem( new Garlic ( amount ) ); + DropItem( new Ginseng ( amount ) ); + DropItem( new MandrakeRoot ( amount ) ); + DropItem( new Nightshade ( amount ) ); + DropItem( new SulfurousAsh ( amount ) ); + DropItem( new SpidersSilk ( amount ) ); + } + + public BagOfReagents( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/BaseReagent.cs b/Scripts/Items/Resources/Reagents/BaseReagent.cs new file mode 100644 index 000000000..2d2548dcc --- /dev/null +++ b/Scripts/Items/Resources/Reagents/BaseReagent.cs @@ -0,0 +1,41 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseReagent : Item + { + public override double DefaultWeight + { + get { return 0.1; } + } + + public BaseReagent( int itemID ) : this( itemID, 1 ) + { + } + + public BaseReagent( int itemID, int amount ) : base( itemID ) + { + Stackable = true; + Amount = amount; + } + + public BaseReagent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/BatWing.cs b/Scripts/Items/Resources/Reagents/BatWing.cs new file mode 100644 index 000000000..6bac1839b --- /dev/null +++ b/Scripts/Items/Resources/Reagents/BatWing.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BatWing : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} bat wing", Amount ); + } + } + + [Constructable] + public BatWing() : this( 1 ) + { + } + + [Constructable] + public BatWing( int amount ) : base( 0xF78, amount ) + { + } + + public BatWing( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/BlackPearl.cs b/Scripts/Items/Resources/Reagents/BlackPearl.cs new file mode 100644 index 000000000..8f3f43c06 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/BlackPearl.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BlackPearl : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} black pearl", Amount ); + } + } + + [Constructable] + public BlackPearl() : this( 1 ) + { + } + + [Constructable] + public BlackPearl( int amount ) : base( 0xF7A, amount ) + { + } + + public BlackPearl( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/Bloodmoss.cs b/Scripts/Items/Resources/Reagents/Bloodmoss.cs new file mode 100644 index 000000000..8b1e94e2b --- /dev/null +++ b/Scripts/Items/Resources/Reagents/Bloodmoss.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class Bloodmoss : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} bloodmoss", Amount ); + } + } + + [Constructable] + public Bloodmoss() : this( 1 ) + { + } + + [Constructable] + public Bloodmoss( int amount ) : base( 0xF7B, amount ) + { + } + + public Bloodmoss( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/DaemonBlood.cs b/Scripts/Items/Resources/Reagents/DaemonBlood.cs new file mode 100644 index 000000000..75027d068 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/DaemonBlood.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class DaemonBlood : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} daemon blood", Amount ); + } + } + + [Constructable] + public DaemonBlood() : this( 1 ) + { + } + + [Constructable] + public DaemonBlood( int amount ) : base( 0xF7D, amount ) + { + } + + public DaemonBlood( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/DaemonBone.cs b/Scripts/Items/Resources/Reagents/DaemonBone.cs new file mode 100644 index 000000000..4fbe465b0 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/DaemonBone.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + // TODO: Commodity? + public class DaemonBone : BaseReagent + { + public override double DefaultWeight + { + get { return 1.0; } + } + + [Constructable] + public DaemonBone() : this( 1 ) + { + } + + [Constructable] + public DaemonBone( int amount ) : base( 0xF80, amount ) + { + } + + public DaemonBone( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/DeadWood.cs b/Scripts/Items/Resources/Reagents/DeadWood.cs new file mode 100644 index 000000000..edd3eddd9 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/DeadWood.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class DeadWood : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} dead wood", Amount ); + } + } + + [Constructable] + public DeadWood() : this( 1 ) + { + } + + [Constructable] + public DeadWood( int amount ) : base( 0xF90, amount ) + { + } + + public DeadWood( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/Garlic.cs b/Scripts/Items/Resources/Reagents/Garlic.cs new file mode 100644 index 000000000..30dc561e9 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/Garlic.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class Garlic : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} garlic", Amount ); + } + } + + [Constructable] + public Garlic() : this( 1 ) + { + } + + [Constructable] + public Garlic( int amount ) : base( 0xF84, amount ) + { + } + + public Garlic( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/Ginseng.cs b/Scripts/Items/Resources/Reagents/Ginseng.cs new file mode 100644 index 000000000..b2dff4df4 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/Ginseng.cs @@ -0,0 +1,48 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class Ginseng : BaseReagent, ICommodity + { + + string ICommodity.Description + { + get + { + return String.Format( "{0} ginseng", Amount ); + } + } + + [Constructable] + public Ginseng() : this( 1 ) + { + } + + [Constructable] + public Ginseng( int amount ) : base( 0xF85, amount ) + { + } + + public Ginseng( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/GraveDust.cs b/Scripts/Items/Resources/Reagents/GraveDust.cs new file mode 100644 index 000000000..513d92481 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/GraveDust.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class GraveDust : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} grave dust", Amount ); + } + } + + [Constructable] + public GraveDust() : this( 1 ) + { + } + + [Constructable] + public GraveDust( int amount ) : base( 0xF8F, amount ) + { + } + + public GraveDust( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/MandrakeRoot.cs b/Scripts/Items/Resources/Reagents/MandrakeRoot.cs new file mode 100644 index 000000000..0f4f72ac4 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/MandrakeRoot.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MandrakeRoot : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} mandrake root", Amount ); + } + } + + [Constructable] + public MandrakeRoot() : this( 1 ) + { + } + + [Constructable] + public MandrakeRoot( int amount ) : base( 0xF86, amount ) + { + } + + public MandrakeRoot( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/Nightshade.cs b/Scripts/Items/Resources/Reagents/Nightshade.cs new file mode 100644 index 000000000..a38feb302 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/Nightshade.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class Nightshade : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} nightshade", Amount ); + } + } + + [Constructable] + public Nightshade() : this( 1 ) + { + } + + [Constructable] + public Nightshade( int amount ) : base( 0xF88, amount ) + { + } + + public Nightshade( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/NoxCrystal.cs b/Scripts/Items/Resources/Reagents/NoxCrystal.cs new file mode 100644 index 000000000..e12a54216 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/NoxCrystal.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class NoxCrystal : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} nox crystal", Amount ); + } + } + + [Constructable] + public NoxCrystal() : this( 1 ) + { + } + + [Constructable] + public NoxCrystal( int amount ) : base( 0xF8E, amount ) + { + } + + public NoxCrystal( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/PigIron.cs b/Scripts/Items/Resources/Reagents/PigIron.cs new file mode 100644 index 000000000..79265df97 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/PigIron.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PigIron : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} pig iron", Amount ); + } + } + + [Constructable] + public PigIron() : this( 1 ) + { + } + + [Constructable] + public PigIron( int amount ) : base( 0xF8A, amount ) + { + } + + public PigIron( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/SpidersSilk.cs b/Scripts/Items/Resources/Reagents/SpidersSilk.cs new file mode 100644 index 000000000..728dcef32 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/SpidersSilk.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SpidersSilk : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} spiders' silk", Amount ); + } + } + + [Constructable] + public SpidersSilk() : this( 1 ) + { + } + + [Constructable] + public SpidersSilk( int amount ) : base( 0xF8D, amount ) + { + } + + public SpidersSilk( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Reagents/SulfurousAsh.cs b/Scripts/Items/Resources/Reagents/SulfurousAsh.cs new file mode 100644 index 000000000..d50fa25b0 --- /dev/null +++ b/Scripts/Items/Resources/Reagents/SulfurousAsh.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SulfurousAsh : BaseReagent, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( "{0} sulfurous ash", Amount ); + } + } + + [Constructable] + public SulfurousAsh() : this( 1 ) + { + } + + [Constructable] + public SulfurousAsh( int amount ) : base( 0xF8C, amount ) + { + } + + public SulfurousAsh( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/BoltOfCloth.cs b/Scripts/Items/Resources/Tailor/BoltOfCloth.cs new file mode 100644 index 000000000..3cf924c01 --- /dev/null +++ b/Scripts/Items/Resources/Tailor/BoltOfCloth.cs @@ -0,0 +1,73 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xF95, 0xF96, 0xF97, 0xF98, 0xF99, 0xF9A, 0xF9B, 0xF9C )] + public class BoltOfCloth : Item, IScissorable, IDyable, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} bolt of cloth" : "{0} bolts of cloth", Amount ); + } + } + + [Constructable] + public BoltOfCloth() : this( 1 ) + { + } + + [Constructable] + public BoltOfCloth( int amount ) : base( 0xF95 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + } + + public BoltOfCloth( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) return false; + + Hue = sender.DyedHue; + + return true; + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new Cloth(), 50 ); + + return true; + } + + public override void OnSingleClick( Mobile from ) + { + int number = (Amount == 1) ? 1049122 : 1049121; + + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Label, 0x3B2, 3, number, "", (Amount * 50).ToString() ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Bone.cs b/Scripts/Items/Resources/Tailor/Bone.cs new file mode 100644 index 000000000..bf872f647 --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Bone.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class Bone : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} bone" : "{0} bones", Amount ); + } + } + + [Constructable] + public Bone() : this( 1 ) + { + } + + [Constructable] + public Bone( int amount ) : base( 0xf7e ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public Bone( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Cloth.cs b/Scripts/Items/Resources/Tailor/Cloth.cs new file mode 100644 index 000000000..545d4bc7a --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Cloth.cs @@ -0,0 +1,78 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x1766, 0x1768 )] + public class Cloth : Item, IScissorable, IDyable, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} piece of cloth" : "{0} pieces of cloth", Amount ); + } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Cloth() : this( 1 ) + { + } + + [Constructable] + public Cloth( int amount ) : base( 0x1766 ) + { + Stackable = true; + Amount = amount; + } + + public Cloth( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnSingleClick( Mobile from ) + { + int number = (Amount == 1) ? 1049124 : 1049123; + + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", Amount.ToString() ) ); + } + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new Bandage(), 1 ); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Cotton.cs b/Scripts/Items/Resources/Tailor/Cotton.cs new file mode 100644 index 000000000..a167d4e00 --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Cotton.cs @@ -0,0 +1,115 @@ +using System; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public class Cotton : Item, IDyable + { + [Constructable] + public Cotton() : this( 1 ) + { + } + + [Constructable] + public Cotton( int amount ) : base( 0xDF9 ) + { + Stackable = true; + Weight = 4.0; + Amount = amount; + } + + public Cotton( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 502655 ); // What spinning wheel do you wish to spin this on? + from.Target = new PickWheelTarget( this ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public static void OnSpun( ISpinningWheel wheel, Mobile from, int hue ) + { + Item item = new SpoolOfThread( 6 ); + item.Hue = hue; + + from.AddToBackpack( item ); + from.SendLocalizedMessage( 1010577 ); // You put the spools of thread in your backpack. + } + + private class PickWheelTarget : Target + { + private Cotton m_Cotton; + + public PickWheelTarget( Cotton cotton ) : base( 3, false, TargetFlags.None ) + { + m_Cotton = cotton; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Cotton.Deleted ) + return; + + ISpinningWheel wheel = targeted as ISpinningWheel; + + if ( wheel == null && targeted is AddonComponent ) + wheel = ((AddonComponent)targeted).Addon as ISpinningWheel; + + if ( wheel is Item ) + { + Item item = (Item)wheel; + + if ( !m_Cotton.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( wheel.Spinning ) + { + from.SendLocalizedMessage( 502656 ); // That spinning wheel is being used. + } + else + { + m_Cotton.Consume(); + wheel.BeginSpin( new SpinCallback( Cotton.OnSpun ), from, m_Cotton.Hue ); + } + } + else + { + from.SendLocalizedMessage( 502658 ); // Use that on a spinning wheel. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Flax.cs b/Scripts/Items/Resources/Tailor/Flax.cs new file mode 100644 index 000000000..e4ce574cf --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Flax.cs @@ -0,0 +1,105 @@ +using System; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public class Flax : Item + { + [Constructable] + public Flax() : this( 1 ) + { + } + + [Constructable] + public Flax( int amount ) : base( 0x1A9C ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + public Flax( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 502655 ); // What spinning wheel do you wish to spin this on? + from.Target = new PickWheelTarget( this ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public static void OnSpun( ISpinningWheel wheel, Mobile from, int hue ) + { + Item item = new SpoolOfThread( 6 ); + item.Hue = hue; + + from.AddToBackpack( item ); + from.SendLocalizedMessage( 1010577 ); // You put the spools of thread in your backpack. + } + + private class PickWheelTarget : Target + { + private Flax m_Flax; + + public PickWheelTarget( Flax flax ) : base( 3, false, TargetFlags.None ) + { + m_Flax = flax; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Flax.Deleted ) + return; + + ISpinningWheel wheel = targeted as ISpinningWheel; + + if ( wheel == null && targeted is AddonComponent ) + wheel = ((AddonComponent)targeted).Addon as ISpinningWheel; + + if ( wheel is Item ) + { + Item item = (Item)wheel; + + if ( !m_Flax.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( wheel.Spinning ) + { + from.SendLocalizedMessage( 502656 ); // That spinning wheel is being used. + } + else + { + m_Flax.Consume(); + wheel.BeginSpin( new SpinCallback( Flax.OnSpun ), from, m_Flax.Hue ); + } + } + else + { + from.SendLocalizedMessage( 502658 ); // Use that on a spinning wheel. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Hides.cs b/Scripts/Items/Resources/Tailor/Hides.cs new file mode 100644 index 000000000..6acb6976f --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Hides.cs @@ -0,0 +1,282 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseHides : Item, ICommodity + { + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; InvalidateProperties(); } + } + + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} pile of hides" : "{0} piles of hides", Amount ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + case 0: + { + OreInfo info = new OreInfo( reader.ReadInt(), reader.ReadInt(), reader.ReadString() ); + + m_Resource = CraftResources.GetFromOreInfo( info ); + break; + } + } + } + + public BaseHides( CraftResource resource ) : this( resource, 1 ) + { + } + + public BaseHides( CraftResource resource, int amount ) : base( 0x1079 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + Hue = CraftResources.GetHue( resource ); + + m_Resource = resource; + } + + public BaseHides( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( Amount > 1 ) + list.Add( 1050039, "{0}\t#{1}", Amount, 1024216 ); // ~1_NUMBER~ ~2_ITEMNAME~ + else + list.Add( 1024216 ); // pile of hides + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( !CraftResources.IsStandard( m_Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( m_Resource ); + + if ( num > 0 ) + list.Add( num ); + else + list.Add( CraftResources.GetName( m_Resource ) ); + } + } + + public override int LabelNumber + { + get + { + if ( m_Resource >= CraftResource.SpinedLeather && m_Resource <= CraftResource.BarbedLeather ) + return 1049687 + (int)(m_Resource - CraftResource.SpinedLeather); + + return 1047023; + } + } + } + + [FlipableAttribute( 0x1079, 0x1078 )] + public class Hides : BaseHides, IScissorable + { + [Constructable] + public Hides() : this( 1 ) + { + } + + [Constructable] + public Hides( int amount ) : base( CraftResource.RegularLeather, amount ) + { + } + + public Hides( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new Leather(), 1 ); + + return true; + } + } + + [FlipableAttribute( 0x1079, 0x1078 )] + public class SpinedHides : BaseHides, IScissorable + { + [Constructable] + public SpinedHides() : this( 1 ) + { + } + + [Constructable] + public SpinedHides( int amount ) : base( CraftResource.SpinedLeather, amount ) + { + } + + public SpinedHides( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new SpinedLeather(), 1 ); + + return true; + } + } + + [FlipableAttribute( 0x1079, 0x1078 )] + public class HornedHides : BaseHides, IScissorable + { + [Constructable] + public HornedHides() : this( 1 ) + { + } + + [Constructable] + public HornedHides( int amount ) : base( CraftResource.HornedLeather, amount ) + { + } + + public HornedHides( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new HornedLeather(), 1 ); + + return true; + } + } + + [FlipableAttribute( 0x1079, 0x1078 )] + public class BarbedHides : BaseHides, IScissorable + { + [Constructable] + public BarbedHides() : this( 1 ) + { + } + + [Constructable] + public BarbedHides( int amount ) : base( CraftResource.BarbedLeather, amount ) + { + } + + public BarbedHides( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new BarbedLeather(), 1 ); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Leathers.cs b/Scripts/Items/Resources/Tailor/Leathers.cs new file mode 100644 index 000000000..8e981d190 --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Leathers.cs @@ -0,0 +1,246 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseLeather : Item, ICommodity + { + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; InvalidateProperties(); } + } + + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} piece of leather" : "{0} pieces of leather", Amount ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + case 0: + { + OreInfo info = new OreInfo( reader.ReadInt(), reader.ReadInt(), reader.ReadString() ); + + m_Resource = CraftResources.GetFromOreInfo( info ); + break; + } + } + } + + public BaseLeather( CraftResource resource ) : this( resource, 1 ) + { + } + + public BaseLeather( CraftResource resource, int amount ) : base( 0x1081 ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + Hue = CraftResources.GetHue( resource ); + + m_Resource = resource; + } + + public BaseLeather( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( Amount > 1 ) + list.Add( 1050039, "{0}\t#{1}", Amount, 1024199 ); // ~1_NUMBER~ ~2_ITEMNAME~ + else + list.Add( 1024199 ); // cut leather + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( !CraftResources.IsStandard( m_Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( m_Resource ); + + if ( num > 0 ) + list.Add( num ); + else + list.Add( CraftResources.GetName( m_Resource ) ); + } + } + + public override int LabelNumber + { + get + { + if ( m_Resource >= CraftResource.SpinedLeather && m_Resource <= CraftResource.BarbedLeather ) + return 1049684 + (int)(m_Resource - CraftResource.SpinedLeather); + + return 1047022; + } + } + } + + [FlipableAttribute( 0x1081, 0x1082 )] + public class Leather : BaseLeather + { + [Constructable] + public Leather() : this( 1 ) + { + } + + [Constructable] + public Leather( int amount ) : base( CraftResource.RegularLeather, amount ) + { + } + + public Leather( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1081, 0x1082 )] + public class SpinedLeather : BaseLeather + { + [Constructable] + public SpinedLeather() : this( 1 ) + { + } + + [Constructable] + public SpinedLeather( int amount ) : base( CraftResource.SpinedLeather, amount ) + { + } + + public SpinedLeather( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1081, 0x1082 )] + public class HornedLeather : BaseLeather + { + [Constructable] + public HornedLeather() : this( 1 ) + { + } + + [Constructable] + public HornedLeather( int amount ) : base( CraftResource.HornedLeather, amount ) + { + } + + public HornedLeather( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } + + [FlipableAttribute( 0x1081, 0x1082 )] + public class BarbedLeather : BaseLeather + { + [Constructable] + public BarbedLeather() : this( 1 ) + { + } + + [Constructable] + public BarbedLeather( int amount ) : base( CraftResource.BarbedLeather, amount ) + { + } + + public BarbedLeather( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/UncutCloth.cs b/Scripts/Items/Resources/Tailor/UncutCloth.cs new file mode 100644 index 000000000..3ab488311 --- /dev/null +++ b/Scripts/Items/Resources/Tailor/UncutCloth.cs @@ -0,0 +1,78 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x1765, 0x1767 )] + public class UncutCloth : Item, IScissorable, IDyable, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} piece of cloth" : "{0} pieces of cloth", Amount ); + } + } + + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public UncutCloth() : this( 1 ) + { + } + + [Constructable] + public UncutCloth( int amount ) : base( 0x1767 ) + { + Stackable = true; + Amount = amount; + } + + public UncutCloth( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnSingleClick( Mobile from ) + { + int number = (Amount == 1) ? 1049124 : 1049123; + + from.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", Amount.ToString() ) ); + } + public bool Scissor( Mobile from, Scissors scissors ) + { + if ( Deleted || !from.CanSee( this ) ) return false; + + base.ScissorHelper( from, new Bandage(), 1 ); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/Wool.cs b/Scripts/Items/Resources/Tailor/Wool.cs new file mode 100644 index 000000000..54527e6a0 --- /dev/null +++ b/Scripts/Items/Resources/Tailor/Wool.cs @@ -0,0 +1,115 @@ +using System; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public class Wool : Item, IDyable + { + [Constructable] + public Wool() : this( 1 ) + { + } + + [Constructable] + public Wool( int amount ) : base( 0xDF8 ) + { + Stackable = true; + Weight = 4.0; + Amount = amount; + } + + public Wool( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 502655 ); // What spinning wheel do you wish to spin this on? + from.Target = new PickWheelTarget( this ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public static void OnSpun( ISpinningWheel wheel, Mobile from, int hue ) + { + Item item = new DarkYarn( 3 ); + item.Hue = hue; + + from.AddToBackpack( item ); + from.SendLocalizedMessage( 1010576 ); // You put the balls of yarn in your backpack. + } + + private class PickWheelTarget : Target + { + private Wool m_Wool; + + public PickWheelTarget( Wool wool ) : base( 3, false, TargetFlags.None ) + { + m_Wool = wool; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Wool.Deleted ) + return; + + ISpinningWheel wheel = targeted as ISpinningWheel; + + if ( wheel == null && targeted is AddonComponent ) + wheel = ((AddonComponent)targeted).Addon as ISpinningWheel; + + if ( wheel is Item ) + { + Item item = (Item)wheel; + + if ( !m_Wool.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( wheel.Spinning ) + { + from.SendLocalizedMessage( 502656 ); // That spinning wheel is being used. + } + else + { + m_Wool.Consume(); + wheel.BeginSpin( new SpinCallback( Wool.OnSpun ), from, m_Wool.Hue ); + } + } + else + { + from.SendLocalizedMessage( 502658 ); // Use that on a spinning wheel. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Resources/Tailor/YarnsAndThreads.cs b/Scripts/Items/Resources/Tailor/YarnsAndThreads.cs new file mode 100644 index 000000000..e27e6f25b --- /dev/null +++ b/Scripts/Items/Resources/Tailor/YarnsAndThreads.cs @@ -0,0 +1,235 @@ +using System; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public abstract class BaseClothMaterial : Item, IDyable + { + public BaseClothMaterial( int itemID ) : this( itemID, 1 ) + { + } + + public BaseClothMaterial( int itemID, int amount ) : base( itemID ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + public BaseClothMaterial( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 500366 ); // Select a loom to use that on. + from.Target = new PickLoomTarget( this ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + private class PickLoomTarget : Target + { + private BaseClothMaterial m_Material; + + public PickLoomTarget( BaseClothMaterial material ) : base( 3, false, TargetFlags.None ) + { + m_Material = material; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Material.Deleted ) + return; + + ILoom loom = targeted as ILoom; + + if ( loom == null && targeted is AddonComponent ) + loom = ((AddonComponent)targeted).Addon as ILoom; + + if ( loom != null ) + { + if ( !m_Material.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( loom.Phase < 4 ) + { + m_Material.Consume(); + + if ( targeted is Item ) + ((Item)targeted).SendLocalizedMessageTo( from, 1010001 + loom.Phase++ ); + } + else + { + Item create = new BoltOfCloth(); + create.Hue = m_Material.Hue; + + m_Material.Consume(); + loom.Phase = 0; + from.SendLocalizedMessage( 500368 ); // You create some cloth and put it in your backpack. + from.AddToBackpack( create ); + } + } + else + { + from.SendLocalizedMessage( 500367 ); // Try using that on a loom. + } + } + } + } + + public class DarkYarn : BaseClothMaterial + { + [Constructable] + public DarkYarn() : this( 1 ) + { + } + + [Constructable] + public DarkYarn( int amount ) : base( 0xE1D, amount ) + { + } + + public DarkYarn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightYarn : BaseClothMaterial + { + [Constructable] + public LightYarn() : this( 1 ) + { + } + + [Constructable] + public LightYarn( int amount ) : base( 0xE1E, amount ) + { + } + + public LightYarn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LightYarnUnraveled : BaseClothMaterial + { + [Constructable] + public LightYarnUnraveled() : this( 1 ) + { + } + + [Constructable] + public LightYarnUnraveled( int amount ) : base( 0xE1F, amount ) + { + } + + public LightYarnUnraveled( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SpoolOfThread : BaseClothMaterial + { + [Constructable] + public SpoolOfThread() : this( 1 ) + { + } + + [Constructable] + public SpoolOfThread( int amount ) : base( 0xFA0, amount ) + { + } + + public SpoolOfThread( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Shields/Artifacts/Aegis.cs b/Scripts/Items/Shields/Artifacts/Aegis.cs new file mode 100644 index 000000000..7f891eefd --- /dev/null +++ b/Scripts/Items/Shields/Artifacts/Aegis.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Aegis : HeaterShield + { + public override int LabelNumber{ get{ return 1061602; } } // Ægis + public override int ArtifactRarity{ get{ return 11; } } + + public override int BasePhysicalResistance{ get{ return 15; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public Aegis() + { + Hue = 0x47E; + ArmorAttributes.SelfRepair = 5; + Attributes.ReflectPhysical = 15; + Attributes.DefendChance = 15; + Attributes.LowerManaCost = 8; + } + + public Aegis( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + PhysicalBonus = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Shields/Artifacts/ArcaneShield.cs b/Scripts/Items/Shields/Artifacts/ArcaneShield.cs new file mode 100644 index 000000000..26ed115ad --- /dev/null +++ b/Scripts/Items/Shields/Artifacts/ArcaneShield.cs @@ -0,0 +1,46 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ArcaneShield : WoodenKiteShield + { + public override int LabelNumber{ get{ return 1061101; } } // Arcane Shield + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ArcaneShield() + { + ItemID = 0x1B78; + Hue = 0x556; + Attributes.NightSight = 1; + Attributes.SpellChanneling = 1; + Attributes.DefendChance = 15; + Attributes.CastSpeed = 1; + } + + public ArcaneShield( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Attributes.NightSight == 0 ) + Attributes.NightSight = 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Shields/BaseShield.cs b/Scripts/Items/Shields/BaseShield.cs new file mode 100644 index 000000000..b4b327055 --- /dev/null +++ b/Scripts/Items/Shields/BaseShield.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class BaseShield : BaseArmor + { + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + public BaseShield( int itemID ) : base( itemID ) + { + } + + public BaseShield( Serial serial ) : base(serial) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + { + if ( this is Aegis ) + return; + + // The 15 bonus points to resistances are not applied to shields on OSI. + PhysicalBonus = 0; + FireBonus = 0; + ColdBonus = 0; + PoisonBonus = 0; + EnergyBonus = 0; + } + } + + public override double ArmorRating + { + get + { + Mobile m = this.Parent as Mobile; + double ar = base.ArmorRating; + + if ( m != null ) + return ( ( m.Skills[SkillName.Parry].Value * ar ) / 200.0 ) + 1.0; + else + return ar; + } + } + + public override int OnHit( BaseWeapon weapon, int damage ) + { + if( Core.AOS ) + { + if( ArmorAttributes.SelfRepair > Utility.Random( 10 ) ) + { + HitPoints += 2; + } + else + { + double halfArmor = ArmorRating / 2.0; + int absorbed = (int)(halfArmor + (halfArmor*Utility.RandomDouble())); + + if( absorbed < 2 ) + absorbed = 2; + + int wear; + + if( weapon.Type == WeaponType.Bashing ) + wear = (absorbed / 2); + else + wear = Utility.Random( 2 ); + + if( wear > 0 && MaxHitPoints > 0 ) + { + if( HitPoints >= wear ) + { + HitPoints -= wear; + wear = 0; + } + else + { + wear -= HitPoints; + HitPoints = 0; + } + + if( wear > 0 ) + { + if( MaxHitPoints > wear ) + { + MaxHitPoints -= wear; + + if( Parent is Mobile ) + ((Mobile)Parent).LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061121 ); // Your equipment is severely damaged. + } + else + { + Delete(); + } + } + } + } + + return 0; + } + else + { + Mobile owner = this.Parent as Mobile; + if( owner == null ) + return damage; + + double ar = this.ArmorRating; + double chance = (owner.Skills[SkillName.Parry].Value - (ar * 2.0)) / 100.0; + + if( chance < 0.01 ) + chance = 0.01; + /* + FORMULA: Displayed AR = ((Parrying Skill * Base AR of Shield) ÷ 200) + 1 + + FORMULA: % Chance of Blocking = parry skill - (shieldAR * 2) + + FORMULA: Melee Damage Absorbed = (AR of Shield) / 2 | Archery Damage Absorbed = AR of Shield + */ + if( owner.CheckSkill( SkillName.Parry, chance ) ) + { + if( weapon.Skill == SkillName.Archery ) + damage -= (int)ar; + else + damage -= (int)(ar / 2.0); + + if( damage < 0 ) + damage = 0; + + owner.FixedEffect( 0x37B9, 10, 16 ); + + if( 25 > Utility.Random( 100 ) ) // 25% chance to lower durability + { + if( Core.AOS && ArmorAttributes.SelfRepair > Utility.Random( 10 ) ) + { + HitPoints += 2; + } + else + { + int wear = Utility.Random( 2 ); + + if( wear > 0 && MaxHitPoints > 0 ) + { + if( HitPoints >= wear ) + { + HitPoints -= wear; + wear = 0; + } + else + { + wear -= HitPoints; + HitPoints = 0; + } + + if( wear > 0 ) + { + if( MaxHitPoints > wear ) + { + MaxHitPoints -= wear; + + if( Parent is Mobile ) + ((Mobile)Parent).LocalOverheadMessage( MessageType.Regular, 0x3B2, 1061121 ); // Your equipment is severely damaged. + } + else + { + Delete(); + } + } + } + } + } + } + + return damage; + } + } + } +} diff --git a/Scripts/Items/Shields/BronzeShield.cs b/Scripts/Items/Shields/BronzeShield.cs new file mode 100644 index 000000000..8a3369e97 --- /dev/null +++ b/Scripts/Items/Shields/BronzeShield.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BronzeShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 1; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 30; } } + + public override int AosStrReq{ get{ return 35; } } + + public override int ArmorBase{ get{ return 10; } } + + [Constructable] + public BronzeShield() : base( 0x1B72 ) + { + Weight = 6.0; + } + + public BronzeShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Shields/Buckler.cs b/Scripts/Items/Shields/Buckler.cs new file mode 100644 index 000000000..f08937cd5 --- /dev/null +++ b/Scripts/Items/Shields/Buckler.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Buckler : BaseShield + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 1; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 7; } } + + [Constructable] + public Buckler() : base( 0x1B73 ) + { + Weight = 5.0; + } + + public Buckler( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Shields/ChaosShield.cs b/Scripts/Items/Shields/ChaosShield.cs new file mode 100644 index 000000000..424982274 --- /dev/null +++ b/Scripts/Items/Shields/ChaosShield.cs @@ -0,0 +1,78 @@ +using System; +using Server; +using Server.Guilds; + +namespace Server.Items +{ + public class ChaosShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 1; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 100; } } + public override int InitMaxHits{ get{ return 125; } } + + public override int AosStrReq{ get{ return 95; } } + + public override int ArmorBase{ get{ return 32; } } + + [Constructable] + public ChaosShield() : base( 0x1BC3 ) + { + if ( !Core.AOS ) + LootType = LootType.Newbied; + + Weight = 5.0; + } + + public ChaosShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + + public override bool OnEquip( Mobile from ) + { + return Validate( from ) && base.OnEquip( from ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( Validate( Parent as Mobile ) ) + base.OnSingleClick( from ); + } + + public virtual bool Validate( Mobile m ) + { + if ( m == null || !m.Player || m.AccessLevel != AccessLevel.Player || Core.AOS ) + return true; + + Guild g = m.Guild as Guild; + + if ( g == null || g.Type != GuildType.Chaos ) + { + m.FixedEffect( 0x3728, 10, 13 ); + Delete(); + + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Shields/HeaterShield.cs b/Scripts/Items/Shields/HeaterShield.cs new file mode 100644 index 000000000..c1e808284 --- /dev/null +++ b/Scripts/Items/Shields/HeaterShield.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HeaterShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 90; } } + + public override int ArmorBase{ get{ return 23; } } + + [Constructable] + public HeaterShield() : base( 0x1B76 ) + { + Weight = 8.0; + } + + public HeaterShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Shields/MetalKiteShield.cs b/Scripts/Items/Shields/MetalKiteShield.cs new file mode 100644 index 000000000..9ff4ee237 --- /dev/null +++ b/Scripts/Items/Shields/MetalKiteShield.cs @@ -0,0 +1,58 @@ +using System; +using Server; + +namespace Server.Items +{ + public class MetalKiteShield : BaseShield, IDyable + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 1; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 60; } } + + public override int AosStrReq{ get{ return 45; } } + + public override int ArmorBase{ get{ return 16; } } + + [Constructable] + public MetalKiteShield() : base( 0x1B74 ) + { + Weight = 7.0; + } + + public MetalKiteShield( Serial serial ) : base(serial) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 5.0 ) + Weight = 7.0; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Shields/MetalShield.cs b/Scripts/Items/Shields/MetalShield.cs new file mode 100644 index 000000000..5de9c530e --- /dev/null +++ b/Scripts/Items/Shields/MetalShield.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class MetalShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 1; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 45; } } + + public override int ArmorBase{ get{ return 11; } } + + [Constructable] + public MetalShield() : base( 0x1B7B ) + { + Weight = 6.0; + } + + public MetalShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Shields/OrderShield.cs b/Scripts/Items/Shields/OrderShield.cs new file mode 100644 index 000000000..3b0a9c55c --- /dev/null +++ b/Scripts/Items/Shields/OrderShield.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Guilds; + +namespace Server.Items +{ + public class OrderShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 1; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 0; } } + + public override int InitMinHits{ get{ return 100; } } + public override int InitMaxHits{ get{ return 125; } } + + public override int AosStrReq{ get{ return 95; } } + + public override int ArmorBase{ get{ return 30; } } + + [Constructable] + public OrderShield() : base( 0x1BC4 ) + { + if ( !Core.AOS ) + LootType = LootType.Newbied; + + Weight = 7.0; + } + + public OrderShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 6.0 ) + Weight = 7.0; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + + public override bool OnEquip( Mobile from ) + { + return Validate( from ) && base.OnEquip( from ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( Validate( Parent as Mobile ) ) + base.OnSingleClick( from ); + } + + public virtual bool Validate( Mobile m ) + { + if ( Core.AOS || m == null || !m.Player || m.AccessLevel != AccessLevel.Player ) + return true; + + Guild g = m.Guild as Guild; + + if ( g == null || g.Type != GuildType.Order ) + { + m.FixedEffect( 0x3728, 10, 13 ); + Delete(); + + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Shields/WoodenKiteShield.cs b/Scripts/Items/Shields/WoodenKiteShield.cs new file mode 100644 index 000000000..067818150 --- /dev/null +++ b/Scripts/Items/Shields/WoodenKiteShield.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WoodenKiteShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 1; } } + + public override int InitMinHits{ get{ return 50; } } + public override int InitMaxHits{ get{ return 65; } } + + public override int AosStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 12; } } + + [Constructable] + public WoodenKiteShield() : base( 0x1B79 ) + { + Weight = 5.0; + } + + public WoodenKiteShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 7.0 ) + Weight = 5.0; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Shields/WoodenShield.cs b/Scripts/Items/Shields/WoodenShield.cs new file mode 100644 index 000000000..188777d45 --- /dev/null +++ b/Scripts/Items/Shields/WoodenShield.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class WoodenShield : BaseShield + { + public override int BasePhysicalResistance{ get{ return 0; } } + public override int BaseFireResistance{ get{ return 0; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 0; } } + public override int BaseEnergyResistance{ get{ return 1; } } + + public override int InitMinHits{ get{ return 20; } } + public override int InitMaxHits{ get{ return 25; } } + + public override int AosStrReq{ get{ return 20; } } + + public override int ArmorBase{ get{ return 8; } } + + [Constructable] + public WoodenShield() : base( 0x1B7A ) + { + Weight = 5.0; + } + + public WoodenShield( Serial serial ) : base(serial) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 );//version + } + } +} diff --git a/Scripts/Items/Skill Items/Blacksmith Items/Misc/AnvilForge.cs b/Scripts/Items/Skill Items/Blacksmith Items/Misc/AnvilForge.cs new file mode 100644 index 000000000..6796eafc6 --- /dev/null +++ b/Scripts/Items/Skill Items/Blacksmith Items/Misc/AnvilForge.cs @@ -0,0 +1,61 @@ +using System; + +namespace Server.Items +{ + [FlipableAttribute( 0xFAF, 0xFB0 )] + [Server.Engines.Craft.Anvil] + public class Anvil : Item + { + [Constructable] + public Anvil() : base( 0xFAF ) + { + Movable = false; + } + + public Anvil( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Server.Engines.Craft.Forge] + public class Forge : Item + { + [Constructable] + public Forge() : base( 0xFB1 ) + { + Movable = false; + } + + public Forge( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Blacksmith Items/Misc/LargeForge.cs b/Scripts/Items/Skill Items/Blacksmith Items/Misc/LargeForge.cs new file mode 100644 index 000000000..f5c8024c7 --- /dev/null +++ b/Scripts/Items/Skill Items/Blacksmith Items/Misc/LargeForge.cs @@ -0,0 +1,357 @@ +using System; +using Server; + +namespace Server.Items +{ + [Server.Engines.Craft.Forge] + public class LargeForgeWest : Item + { + private InternalItem m_Item; + private InternalItem2 m_Item2; + + [Constructable] + public LargeForgeWest() : base( 0x199A ) + { + Movable = false; + + m_Item = new InternalItem( this ); + m_Item2 = new InternalItem2( this ); + } + + public LargeForgeWest( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X, Y + 1, Z ); + if ( m_Item2 != null ) + m_Item2.Location = new Point3D( X, Y + 2, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + if ( m_Item2 != null ) + m_Item2.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + if ( m_Item2 != null ) + m_Item2.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + writer.Write( m_Item2 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + m_Item2 = reader.ReadItem() as InternalItem2; + } + + [Server.Engines.Craft.Forge] + private class InternalItem : Item + { + private LargeForgeWest m_Item; + + public InternalItem( LargeForgeWest item ) : base( 0x1996 ) + { + Movable = false; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X, Y - 1, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as LargeForgeWest; + } + } + + [Server.Engines.Craft.Forge] + private class InternalItem2 : Item + { + private LargeForgeWest m_Item; + + public InternalItem2( LargeForgeWest item ) : base( 0x1992 ) + { + Movable = false; + + m_Item = item; + } + + public InternalItem2( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X, Y - 2, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as LargeForgeWest; + } + } + } + + [Server.Engines.Craft.Forge] + public class LargeForgeEast : Item + { + private InternalItem m_Item; + private InternalItem2 m_Item2; + + [Constructable] + public LargeForgeEast() : base( 0x197A ) + { + Movable = false; + + m_Item = new InternalItem( this ); + m_Item2 = new InternalItem2( this ); + } + + public LargeForgeEast( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X + 1, Y, Z ); + if ( m_Item2 != null ) + m_Item2.Location = new Point3D( X + 2, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + if ( m_Item2 != null ) + m_Item2.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + if ( m_Item2 != null ) + m_Item2.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + writer.Write( m_Item2 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as InternalItem; + m_Item2 = reader.ReadItem() as InternalItem2; + } + + [Server.Engines.Craft.Forge] + private class InternalItem : Item + { + private LargeForgeEast m_Item; + + public InternalItem( LargeForgeEast item ) : base( 0x197E ) + { + Movable = false; + + m_Item = item; + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 1, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as LargeForgeEast; + } + } + + [Server.Engines.Craft.Forge] + private class InternalItem2 : Item + { + private LargeForgeEast m_Item; + + public InternalItem2( LargeForgeEast item ) : base( 0x1982 ) + { + Movable = false; + + m_Item = item; + } + + public InternalItem2( Serial serial ) : base( serial ) + { + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_Item != null ) + m_Item.Location = new Point3D( X - 2, Y, Z ); + } + + public override void OnMapChange() + { + if ( m_Item != null ) + m_Item.Map = Map; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Item != null ) + m_Item.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Item ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Item = reader.ReadItem() as LargeForgeEast; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Camping/Bedroll.cs b/Scripts/Items/Skill Items/Camping/Bedroll.cs new file mode 100644 index 000000000..89af196e4 --- /dev/null +++ b/Scripts/Items/Skill Items/Camping/Bedroll.cs @@ -0,0 +1,130 @@ +using System; +using Server; +using Server.Network; +using Server.Mobiles; +using Server.Gumps; + +namespace Server.Items +{ + public class Bedroll : Item + { + [Constructable] + public Bedroll() : base( 0xA57 ) + { + Weight = 5.0; + } + + public Bedroll( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( this.Parent != null || !this.VerifyMove( from ) ) + return; + + if ( !from.InRange( this, 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + if ( this.ItemID == 0xA57 ) // rolled + { + Direction dir = PlayerMobile.GetDirection4( from.Location, this.Location ); + + if ( dir == Direction.North || dir == Direction.South ) + this.ItemID = 0xA55; + else + this.ItemID = 0xA56; + } + else // unrolled + { + this.ItemID = 0xA57; + + if ( !from.HasGump( typeof( LogoutGump ) ) ) + { + CampfireEntry entry = Campfire.GetEntry( from ); + + if ( entry != null && entry.Safe ) + from.SendGump( new LogoutGump( entry, this ) ); + } + } + } + + private class LogoutGump : Gump + { + private Timer m_CloseTimer; + + private CampfireEntry m_Entry; + private Bedroll m_Bedroll; + + public LogoutGump( CampfireEntry entry, Bedroll bedroll ) : base( 100, 0 ) + { + m_Entry = entry; + m_Bedroll = bedroll; + + m_CloseTimer = Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( CloseGump ) ); + + AddBackground( 0, 0, 400, 350, 0xA28 ); + + AddHtmlLocalized( 100, 20, 200, 35, 1011015, false, false ); //
Logging out via camping
+ + /* Using a bedroll in the safety of a camp will log you out of the game safely. + * If this is what you wish to do choose CONTINUE and you will be logged out. + * Otherwise, select the CANCEL button to avoid logging out at this time. + * The camp will remain secure for 10 seconds at which time this window will close + * and you not be logged out. + */ + AddHtmlLocalized( 50, 55, 300, 140, 1011016, true, true ); + + AddButton( 45, 298, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 300, 110, 35, 1011011, false, false ); // CONTINUE + + AddButton( 200, 298, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 235, 300, 110, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + PlayerMobile pm = m_Entry.Player; + + m_CloseTimer.Stop(); + + if ( Campfire.GetEntry( pm ) != m_Entry ) + return; + + if ( info.ButtonID == 1 && m_Entry.Safe && m_Bedroll.Parent == null && m_Bedroll.IsAccessibleTo( pm ) + && m_Bedroll.VerifyMove( pm ) && m_Bedroll.Map == pm.Map && pm.InRange( m_Bedroll, 2 ) ) + { + pm.PlaceInBackpack( m_Bedroll ); + + pm.BedrollLogout = true; + sender.Dispose(); + } + + Campfire.RemoveEntry( m_Entry ); + } + + private void CloseGump() + { + Campfire.RemoveEntry( m_Entry ); + m_Entry.Player.CloseGump( typeof( LogoutGump ) ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Camping/Campfire.cs b/Scripts/Items/Skill Items/Camping/Campfire.cs new file mode 100644 index 000000000..344e77bdd --- /dev/null +++ b/Scripts/Items/Skill Items/Camping/Campfire.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Mobiles; + +namespace Server.Items +{ + public enum CampfireStatus + { + Burning, + Extinguishing, + Off + } + + public class Campfire : Item + { + public static readonly int SecureRange = 7; + + private static readonly Hashtable m_Table = new Hashtable(); + + public static CampfireEntry GetEntry( Mobile player ) + { + return (CampfireEntry) m_Table[player]; + } + + public static void RemoveEntry( CampfireEntry entry ) + { + m_Table.Remove( entry.Player ); + entry.Fire.m_Entries.Remove( entry ); + } + + private Timer m_Timer; + private DateTime m_Created; + + private ArrayList m_Entries; + + public Campfire() : base( 0xDE3 ) + { + Movable = false; + Light = LightType.Circle300; + + m_Entries = new ArrayList(); + + m_Created = DateTime.Now; + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ), new TimerCallback( OnTick ) ); + } + + public Campfire( Serial serial ) : base( serial ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime Created + { + get{ return m_Created; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public CampfireStatus Status + { + get + { + switch ( this.ItemID ) + { + case 0xDE3: + return CampfireStatus.Burning; + + case 0xDE9: + return CampfireStatus.Extinguishing; + + default: + return CampfireStatus.Off; + } + } + set + { + if ( this.Status == value ) + return; + + switch ( value ) + { + case CampfireStatus.Burning: + this.ItemID = 0xDE3; + this.Light = LightType.Circle300; + break; + + case CampfireStatus.Extinguishing: + this.ItemID = 0xDE9; + this.Light = LightType.Circle150; + break; + + default: + this.ItemID = 0xDEA; + this.Light = LightType.ArchedWindowEast; + ClearEntries(); + break; + } + } + } + + private void OnTick() + { + DateTime now = DateTime.Now; + TimeSpan age = now - this.Created; + + if ( age >= TimeSpan.FromSeconds( 100.0 ) ) + this.Delete(); + else if ( age >= TimeSpan.FromSeconds( 90.0 ) ) + this.Status = CampfireStatus.Off; + else if ( age >= TimeSpan.FromSeconds( 60.0 ) ) + this.Status = CampfireStatus.Extinguishing; + + if ( this.Status == CampfireStatus.Off || this.Deleted ) + return; + + foreach ( CampfireEntry entry in new ArrayList( m_Entries ) ) + { + if ( !entry.Valid || entry.Player.NetState == null ) + { + RemoveEntry( entry ); + } + else if ( !entry.Safe && now - entry.Start >= TimeSpan.FromSeconds( 30.0 ) ) + { + entry.Safe = true; + entry.Player.SendLocalizedMessage( 500621 ); // The camp is now secure. + } + } + + IPooledEnumerable eable = this.GetClientsInRange( SecureRange ); + + foreach ( NetState state in eable ) + { + PlayerMobile pm = state.Mobile as PlayerMobile; + + if ( pm != null && GetEntry( pm ) == null ) + { + CampfireEntry entry = new CampfireEntry( pm, this ); + + m_Table[pm] = entry; + m_Entries.Add( entry ); + + pm.SendLocalizedMessage( 500620 ); // You feel it would take a few moments to secure your camp. + } + } + + eable.Free(); + } + + private void ClearEntries() + { + if ( m_Entries == null ) + return; + + foreach ( CampfireEntry entry in new ArrayList( m_Entries ) ) + { + RemoveEntry( entry ); + } + } + + public override void OnAfterDelete() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + ClearEntries(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + this.Delete(); + } + } + + public class CampfireEntry + { + private PlayerMobile m_Player; + private Campfire m_Fire; + private DateTime m_Start; + private bool m_Safe; + + public PlayerMobile Player{ get{ return m_Player; } } + public Campfire Fire{ get{ return m_Fire; } } + public DateTime Start{ get{ return m_Start; } } + + public bool Valid + { + get{ return !Fire.Deleted && Fire.Status != CampfireStatus.Off && Player.Map == Fire.Map && Player.InRange( Fire, Campfire.SecureRange ); } + } + + public bool Safe + { + get{ return Valid && m_Safe; } + set{ m_Safe = value; } + } + + public CampfireEntry( PlayerMobile player, Campfire fire ) + { + m_Player = player; + m_Fire = fire; + m_Start = DateTime.Now; + m_Safe = false; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Camping/Kindling.cs b/Scripts/Items/Skill Items/Camping/Kindling.cs new file mode 100644 index 000000000..49bbb2fb0 --- /dev/null +++ b/Scripts/Items/Skill Items/Camping/Kindling.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Regions; + +namespace Server.Items +{ + public class Kindling : Item + { + [Constructable] + public Kindling() : this( 1 ) + { + } + + [Constructable] + public Kindling( int amount ) : base( 0xDE1 ) + { + Stackable = true; + Weight = 5.0; + Amount = amount; + } + + public Kindling( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !this.VerifyMove( from ) ) + return; + + if ( !from.InRange( this.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + Point3D fireLocation = GetFireLocation( from ); + + if ( fireLocation == Point3D.Zero ) + { + from.SendLocalizedMessage( 501695 ); // There is not a spot nearby to place your campfire. + } + else if ( !from.CheckSkill( SkillName.Camping, 0.0, 100.0 ) ) + { + from.SendLocalizedMessage( 501696 ); // You fail to ignite the campfire. + } + else + { + Consume(); + + if ( !this.Deleted && this.Parent == null ) + from.PlaceInBackpack( this ); + + new Campfire().MoveToWorld( fireLocation, from.Map ); + } + } + + private Point3D GetFireLocation( Mobile from ) + { + if ( from.Region.IsPartOf( typeof( DungeonRegion ) ) ) + return Point3D.Zero; + + if ( this.Parent == null ) + return this.Location; + + ArrayList list = new ArrayList( 4 ); + + AddOffsetLocation( from, 0, -1, list ); + AddOffsetLocation( from, -1, 0, list ); + AddOffsetLocation( from, 0, 1, list ); + AddOffsetLocation( from, 1, 0, list ); + + if ( list.Count == 0 ) + return Point3D.Zero; + + int idx = Utility.Random( list.Count ); + return (Point3D) list[idx]; + } + + private void AddOffsetLocation( Mobile from, int offsetX, int offsetY, ArrayList list ) + { + Map map = from.Map; + + int x = from.X + offsetX; + int y = from.Y + offsetY; + + Point3D loc = new Point3D( x, y, from.Z ); + + if ( map.CanFit( loc, 1 ) && from.InLOS( loc ) ) + { + list.Add( loc ); + } + else + { + loc = new Point3D( x, y, map.GetAverageZ( x, y ) ); + + if ( map.CanFit( loc, 1 ) && from.InLOS( loc ) ) + list.Add( loc ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Carpenter Items/Board.cs b/Scripts/Items/Skill Items/Carpenter Items/Board.cs new file mode 100644 index 000000000..95921fa8d --- /dev/null +++ b/Scripts/Items/Skill Items/Carpenter Items/Board.cs @@ -0,0 +1,54 @@ +using System; + +namespace Server.Items +{ + [FlipableAttribute( 0x1BD7, 0x1BDA )] + public class Board : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} board" : "{0} boards", Amount ); + } + } + + [Constructable] + public Board() + : this( 1 ) + { + } + + [Constructable] + public Board( int amount ) + : base( 0x1BD7 ) + { + Stackable = true; + Amount = amount; + } + + public Board( Serial serial ) + : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 0.1 ) + Weight = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Carpenter Items/TaxidermyKit.cs b/Scripts/Items/Skill Items/Carpenter Items/TaxidermyKit.cs new file mode 100644 index 000000000..309252fd7 --- /dev/null +++ b/Scripts/Items/Skill Items/Carpenter Items/TaxidermyKit.cs @@ -0,0 +1,462 @@ +using System; +using Server; +using Server.Multis; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Items +{ + [FlipableAttribute( 0x1EBA, 0x1EBB )] + public class TaxidermyKit : Item + { + public override int LabelNumber{ get{ return 1041279; } } // a taxidermy kit + + [Constructable] + public TaxidermyKit() : base( 0x1EBA ) + { + Weight = 1.0; + } + + public TaxidermyKit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick(Mobile from) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.Skills[SkillName.Carpentry].Base < 90.0 ) + { + from.SendLocalizedMessage( 1042594 ); // You do not understand how to use this. + } + else + { + from.SendLocalizedMessage( 1042595 ); // Target the corpse to make a trophy out of. + from.Target = new CorpseTarget( this ); + } + } + + private static object[,] m_Table = new object[,] + { + { typeof( BrownBear ), 0x1E60, 1041093, 1041107 }, + { typeof( GreatHart ), 0x1E61, 1041095, 1041109 }, + { typeof( BigFish ), 0x1E62, 1041096, 1041110 }, + { typeof( Gorilla ), 0x1E63, 1041091, 1041105 }, + { typeof( Orc ), 0x1E64, 1041090, 1041104 }, + { typeof( PolarBear ), 0x1E65, 1041094, 1041108 }, + { typeof( Troll ), 0x1E66, 1041092, 1041106 } + }; + + private class CorpseTarget : Target + { + private TaxidermyKit m_Kit; + + public CorpseTarget( TaxidermyKit kit ) : base( 3, false, TargetFlags.None ) + { + m_Kit = kit; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Kit.Deleted ) + return; + + if ( !(targeted is Corpse) && !(targeted is BigFish) ) + { + from.SendLocalizedMessage( 1042600 ); // That is not a corpse! + } + else if ( targeted is Corpse && ((Corpse)targeted).VisitedByTaxidermist ) + { + from.SendLocalizedMessage( 1042596 ); // That corpse seems to have been visited by a taxidermist already. + } + else if ( !m_Kit.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.Skills[SkillName.Carpentry].Base < 90.0 ) + { + from.SendLocalizedMessage( 1042603 ); // You would not understand how to use the kit. + } + else + { + object obj = targeted; + + if ( obj is Corpse ) + obj = ((Corpse)obj).Owner; + + for ( int i = 0; obj != null && i < m_Table.GetLength( 0 ); ++i ) + { + if ( m_Table[i, 0] == obj.GetType() ) + { + Container pack = from.Backpack; + + if ( pack != null && pack.ConsumeTotal( typeof( Board ), 10 ) ) + { + from.SendLocalizedMessage( 1042278 ); // You review the corpse and find it worthy of a trophy. + from.SendLocalizedMessage( 1042602 ); // You use your kit up making the trophy. + + Mobile hunter = null; + int weight = 0; + + if ( targeted is BigFish ) + { + hunter = ((BigFish)targeted).Fisher; + weight = (int) ((BigFish)targeted).Weight; + } + + from.AddToBackpack( new TrophyDeed( (int)m_Table[i, 1] + 7, (int)m_Table[i, 1], (int)m_Table[i, 2], (int)m_Table[i, 3], hunter, weight ) ); + + if ( targeted is Corpse ) + ((Corpse)targeted).VisitedByTaxidermist = true; + else if ( targeted is BigFish ) + ((BigFish)targeted).Consume(); + + m_Kit.Delete(); + return; + } + else + { + from.SendLocalizedMessage( 1042598 ); // You do not have enough boards. + return; + } + } + } + + from.SendLocalizedMessage( 1042599 ); // That does not look like something you want hanging on a wall. + } + } + } + } + + public class TrophyAddon : Item, IAddon + { + private int m_WestID; + private int m_NorthID; + private int m_DeedNumber; + private int m_AddonNumber; + + private Mobile m_Hunter; + private int m_AnimalWeight; + + [CommandProperty( AccessLevel.GameMaster )] + public int WestID{ get{ return m_WestID; } set{ m_WestID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int NorthID{ get{ return m_NorthID; } set{ m_NorthID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DeedNumber{ get{ return m_DeedNumber; } set{ m_DeedNumber = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int AddonNumber{ get{ return m_AddonNumber; } set{ m_AddonNumber = value; InvalidateProperties(); } } + + public override int LabelNumber{ get{ return m_AddonNumber; } } + + [Constructable] + public TrophyAddon( Mobile from, int itemID, int westID, int northID, int deedNumber, int addonNumber ) : this( from, itemID, westID, northID, deedNumber, addonNumber, null, 0 ) + { + } + + public TrophyAddon( Mobile from, int itemID, int westID, int northID, int deedNumber, int addonNumber, Mobile hunter, int animalWeight ) : base( itemID ) + { + m_WestID = westID; + m_NorthID = northID; + m_DeedNumber = deedNumber; + m_AddonNumber = addonNumber; + + m_Hunter = hunter; + m_AnimalWeight = animalWeight; + + Movable = false; + + MoveToWorld( from.Location, from.Map ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_AnimalWeight >= 20 ) + { + if ( m_Hunter != null ) + list.Add( 1070857, m_Hunter.Name ); // Caught by ~1_fisherman~ + + list.Add( 1070858, m_AnimalWeight.ToString() ); // ~1_weight~ stones + } + } + + public TrophyAddon( Serial serial ) : base( serial ) + { + } + + public bool CouldFit( IPoint3D p, Map map ) + { + if ( !map.CanFit( p.X, p.Y, p.Z, this.ItemData.Height ) ) + return false; + + if ( this.ItemID == m_NorthID ) + return BaseAddon.IsWall( p.X, p.Y - 1, p.Z, map ); // North wall + else + return BaseAddon.IsWall( p.X - 1, p.Y, p.Z, map ); // West wall + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (Mobile) m_Hunter ); + writer.Write( (int) m_AnimalWeight ); + + writer.Write( (int) m_WestID ); + writer.Write( (int) m_NorthID ); + writer.Write( (int) m_DeedNumber ); + writer.Write( (int) m_AddonNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Hunter = reader.ReadMobile(); + m_AnimalWeight = reader.ReadInt(); + goto case 0; + } + case 0: + { + m_WestID = reader.ReadInt(); + m_NorthID = reader.ReadInt(); + m_DeedNumber = reader.ReadInt(); + m_AddonNumber = reader.ReadInt(); + break; + } + } + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( FixMovingCrate ) ); + } + + private void FixMovingCrate() + { + if ( this.Deleted ) + return; + + if ( this.Movable || this.IsLockedDown ) + { + Item deed = this.Deed; + + if ( this.Parent is Item ) + { + ((Item)this.Parent).AddItem( deed ); + deed.Location = this.Location; + } + else + { + deed.MoveToWorld( this.Location, this.Map ); + } + + Delete(); + } + } + + public Item Deed + { + get{ return new TrophyDeed( m_WestID, m_NorthID, m_DeedNumber, m_AddonNumber, m_Hunter, m_AnimalWeight ); } + } + + public override void OnDoubleClick( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsCoOwner( from ) ) + { + if ( from.InRange( GetWorldLocation(), 1 ) ) + { + from.AddToBackpack( this.Deed ); + Delete(); + } + else + { + from.SendLocalizedMessage( 500295 ); // You are too far away to do that. + } + } + } + } + + [Flipable( 0x14F0, 0x14EF )] + public class TrophyDeed : Item + { + private int m_WestID; + private int m_NorthID; + private int m_DeedNumber; + private int m_AddonNumber; + + private Mobile m_Hunter; + private int m_AnimalWeight; + + [CommandProperty( AccessLevel.GameMaster )] + public int WestID{ get{ return m_WestID; } set{ m_WestID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int NorthID{ get{ return m_NorthID; } set{ m_NorthID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DeedNumber{ get{ return m_DeedNumber; } set{ m_DeedNumber = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int AddonNumber{ get{ return m_AddonNumber; } set{ m_AddonNumber = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Hunter{ get{ return m_Hunter; } set{ m_Hunter = value; InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int AnimalWeight{ get{ return m_AnimalWeight; } set{ m_AnimalWeight = value; InvalidateProperties(); } } + + public override int LabelNumber{ get{ return m_DeedNumber; } } + + [Constructable] + public TrophyDeed( int westID, int northID, int deedNumber, int addonNumber ) : this( westID, northID, deedNumber, addonNumber, null, 0 ) + { + } + + public TrophyDeed( int westID, int northID, int deedNumber, int addonNumber, Mobile hunter, int animalWeight ) : base( 0x14F0 ) + { + m_WestID = westID; + m_NorthID = northID; + m_DeedNumber = deedNumber; + m_AddonNumber = addonNumber; + m_Hunter = hunter; + m_AnimalWeight = animalWeight; + } + + public TrophyDeed( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_AnimalWeight >= 20 ) + { + if ( m_Hunter != null ) + list.Add( 1070857, m_Hunter.Name ); // Caught by ~1_fisherman~ + + list.Add( 1070858, m_AnimalWeight.ToString() ); // ~1_weight~ stones + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (Mobile) m_Hunter ); + writer.Write( (int) m_AnimalWeight ); + + writer.Write( (int) m_WestID ); + writer.Write( (int) m_NorthID ); + writer.Write( (int) m_DeedNumber ); + writer.Write( (int) m_AddonNumber ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Hunter = reader.ReadMobile(); + m_AnimalWeight = reader.ReadInt(); + goto case 0; + } + case 0: + { + m_WestID = reader.ReadInt(); + m_NorthID = reader.ReadInt(); + m_DeedNumber = reader.ReadInt(); + m_AddonNumber = reader.ReadInt(); + break; + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house != null && house.IsCoOwner( from ) ) + { + bool northWall = BaseAddon.IsWall( from.X, from.Y - 1, from.Z, from.Map ); + bool westWall = BaseAddon.IsWall( from.X - 1, from.Y, from.Z, from.Map ); + + if ( northWall && westWall ) + { + switch ( from.Direction & Direction.Mask ) + { + case Direction.North: + case Direction.South: northWall = true; westWall = false; break; + + case Direction.East: + case Direction.West: northWall = false; westWall = true; break; + + default: from.SendMessage( "Turn to face the wall on which to hang this trophy." ); return; + } + } + + int itemID = 0; + + if ( northWall ) + itemID = m_NorthID; + else if ( westWall ) + itemID = m_WestID; + else + from.SendLocalizedMessage( 1042626 ); // The trophy must be placed next to a wall. + + if ( itemID > 0 ) + { + house.Addons.Add( new TrophyAddon( from, itemID, m_WestID, m_NorthID, m_DeedNumber, m_AddonNumber, m_Hunter, m_AnimalWeight ) ); + Delete(); + } + } + else + { + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Fishing/FishingPole.cs b/Scripts/Items/Skill Items/Fishing/FishingPole.cs new file mode 100644 index 000000000..779312206 --- /dev/null +++ b/Scripts/Items/Skill Items/Fishing/FishingPole.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Items; +using Server.Engines.Harvest; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + public class FishingPole : Item + { + [Constructable] + public FishingPole() : base( 0x0DC0 ) + { + Layer = Layer.OneHanded; + Weight = 8.0; + } + + public override void OnDoubleClick( Mobile from ) + { + Fishing.System.BeginHarvesting( from, this ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + BaseHarvestTool.AddContextMenuEntries( from, this, list, Fishing.System ); + } + + public FishingPole( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Fishing/Misc/MessageInABottle.cs b/Scripts/Items/Skill Items/Fishing/Misc/MessageInABottle.cs new file mode 100644 index 000000000..3785a4e91 --- /dev/null +++ b/Scripts/Items/Skill Items/Fishing/Misc/MessageInABottle.cs @@ -0,0 +1,111 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class MessageInABottle : Item + { + public static int GetRandomLevel() + { + if ( Core.AOS && 1 > Utility.Random( 25 ) ) + return 4; // ancient + + return Utility.RandomMinMax( 1, 3 ); + } + + public override int LabelNumber{ get{ return 1041080; } } // a message in a bottle + + private Map m_TargetMap; + private int m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public Map TargetMap + { + get{ return m_TargetMap; } + set{ m_TargetMap = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Level + { + get{ return m_Level; } + set{ m_Level = Math.Max( 1, Math.Min( value, 4 ) ); } + } + + [Constructable] + public MessageInABottle() : this( Map.Trammel ) + { + } + + public MessageInABottle( Map map ) : this( map, GetRandomLevel() ) + { + } + + [Constructable] + public MessageInABottle( Map map, int level ) : base( 0x099F ) + { + Weight = 1.0; + m_TargetMap = map; + m_Level = level; + } + + public MessageInABottle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (int) m_Level ); + + writer.Write( m_TargetMap ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_Level = reader.ReadInt(); + goto case 1; + } + case 1: + { + m_TargetMap = reader.ReadMap(); + break; + } + case 0: + { + m_TargetMap = Map.Trammel; + break; + } + } + + if ( version < 2 ) + m_Level = GetRandomLevel(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + Consume(); + from.AddToBackpack( new SOS( m_TargetMap, m_Level ) ); + from.LocalOverheadMessage( Network.MessageType.Regular, 0x3B2, 501891 ); // You extract the message from the bottle. + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Fishing/Misc/SOS.cs b/Scripts/Items/Skill Items/Fishing/Misc/SOS.cs new file mode 100644 index 000000000..65dfca95e --- /dev/null +++ b/Scripts/Items/Skill Items/Fishing/Misc/SOS.cs @@ -0,0 +1,336 @@ +using System; +using Server.Network; +using Server.Gumps; + +namespace Server.Items +{ + public class SOS : Item + { + public override int LabelNumber + { + get + { + if ( IsAncient ) + return 1063450; // an ancient SOS + + return 1041081; // a waterstained SOS + } + } + + private int m_Level; + private Map m_TargetMap; + private Point3D m_TargetLocation; + private int m_MessageIndex; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsAncient + { + get{ return ( m_Level >= 4 ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Level + { + get{ return m_Level; } + set + { + m_Level = Math.Max( 1, Math.Min( value, 4 ) ); + UpdateHue(); + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map TargetMap + { + get{ return m_TargetMap; } + set{ m_TargetMap = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D TargetLocation + { + get{ return m_TargetLocation; } + set{ m_TargetLocation = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MessageIndex + { + get{ return m_MessageIndex; } + set{ m_MessageIndex = value; } + } + + public void UpdateHue() + { + if ( IsAncient ) + Hue = 0x481; + else + Hue = 0; + } + + [Constructable] + public SOS() : this( Map.Trammel ) + { + } + + [Constructable] + public SOS( Map map ) : this( map, MessageInABottle.GetRandomLevel() ) + { + } + + [Constructable] + public SOS( Map map, int level ) : base( 0x14ED ) + { + Weight = 1.0; + + m_Level = level; + m_MessageIndex = Utility.Random( MessageEntry.Entries.Length ); + m_TargetMap = map; + m_TargetLocation = FindLocation( m_TargetMap ); + + UpdateHue(); + } + + public SOS( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 3 ); // version + + writer.Write( m_Level ); + + writer.Write( m_TargetMap ); + writer.Write( m_TargetLocation ); + writer.Write( m_MessageIndex ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + case 2: + { + m_Level = reader.ReadInt(); + goto case 1; + } + case 1: + { + m_TargetMap = reader.ReadMap(); + m_TargetLocation = reader.ReadPoint3D(); + m_MessageIndex = reader.ReadInt(); + + break; + } + case 0: + { + m_TargetMap = this.Map; + + if ( m_TargetMap == null || m_TargetMap == Map.Internal ) + m_TargetMap = Map.Trammel; + + m_TargetLocation = FindLocation( m_TargetMap ); + m_MessageIndex = Utility.Random( MessageEntry.Entries.Length ); + + break; + } + } + + if ( version < 2 ) + m_Level = MessageInABottle.GetRandomLevel(); + + if ( version < 3 ) + UpdateHue(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + MessageEntry entry; + + if ( m_MessageIndex >= 0 && m_MessageIndex < MessageEntry.Entries.Length ) + entry = MessageEntry.Entries[m_MessageIndex]; + else + entry = MessageEntry.Entries[m_MessageIndex = Utility.Random( MessageEntry.Entries.Length )]; + + from.CloseGump( typeof( MessageGump ) ); + from.SendGump( new MessageGump( entry, m_TargetMap, m_TargetLocation ) ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + private static int[] m_WaterTiles = new int[] + { + 0x00A8, 0x00AB, + 0x0136, 0x0137 + }; + + private static Rectangle2D[] m_BritRegions = new Rectangle2D[]{ new Rectangle2D( 0, 0, 5120, 4096 ) }; + private static Rectangle2D[] m_IlshRegions = new Rectangle2D[]{ new Rectangle2D( 1472, 272, 304, 240 ), new Rectangle2D( 1240, 1000, 312, 160 ) }; + private static Rectangle2D[] m_MalasRegions = new Rectangle2D[]{ new Rectangle2D( 1376, 1520, 464, 280 ) }; + + public static Point3D FindLocation( Map map ) + { + if ( map == null || map == Map.Internal ) + return Point3D.Zero; + + Rectangle2D[] regions; + + if ( map == Map.Felucca || map == Map.Trammel ) + regions = m_BritRegions; + else if ( map == Map.Ilshenar ) + regions = m_IlshRegions; + else if ( map == Map.Malas ) + regions = m_MalasRegions; + else + regions = new Rectangle2D[]{ new Rectangle2D( 0, 0, map.Width, map.Height ) }; + + if ( regions.Length == 0 ) + return Point3D.Zero; + + for ( int i = 0; i < 50; ++i ) + { + Rectangle2D reg = regions[Utility.Random( regions.Length )]; + int x = Utility.Random( reg.X, reg.Width ); + int y = Utility.Random( reg.Y, reg.Height ); + + if ( !ValidateDeepWater( map, x, y ) ) + continue; + + bool valid = true; + + for ( int j = 1, offset = 5; valid && j <= 5; ++j, offset += 5 ) + { + if ( !ValidateDeepWater( map, x + offset, y + offset ) ) + valid = false; + else if ( !ValidateDeepWater( map, x + offset, y - offset ) ) + valid = false; + else if ( !ValidateDeepWater( map, x - offset, y + offset ) ) + valid = false; + else if ( !ValidateDeepWater( map, x - offset, y - offset ) ) + valid = false; + } + + if ( valid ) + return new Point3D( x, y, 0 ); + } + + return Point3D.Zero; + } + + private static bool ValidateDeepWater( Map map, int x, int y ) + { + int tileID = map.Tiles.GetLandTile( x, y ).ID; + bool water = false; + + for ( int i = 0; !water && i < m_WaterTiles.Length; i += 2 ) + water = ( tileID >= m_WaterTiles[i] && tileID <= m_WaterTiles[i + 1] ); + + return water; + } + +#if false + private class MessageGump : Gump + { + public MessageGump( MessageEntry entry, Map map, Point3D loc ) : base( (640 - entry.Width) / 2, (480 - entry.Height) / 2 ) + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + string fmt; + + if ( Sextant.Format( loc, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + fmt = String.Format( "{0}°{1}'{2},{3}°{4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + else + fmt = "?????"; + + AddPage( 0 ); + AddBackground( 0, 0, entry.Width, entry.Height, 2520 ); + AddHtml( 38, 38, entry.Width - 83, entry.Height - 86, String.Format( entry.Message, fmt ), false, false ); + } + } +#else + private class MessageGump : Gump + { + public MessageGump( MessageEntry entry, Map map, Point3D loc ) : base( 150, 50 ) + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + string fmt; + + if ( Sextant.Format( loc, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + fmt = String.Format( "{0}°{1}'{2},{3}°{4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + else + fmt = "?????"; + + AddPage( 0 ); + + AddBackground( 0, 40, 350, 300, 2520 ); + + AddHtmlLocalized( 30, 80, 285, 160, 1018326, true, true ); /* This is a message hastily scribbled by a passenger aboard a sinking ship. + * While it is probably too late to save the passengers and crew, + * perhaps some treasure went down with the ship! + * The message gives the ship's last known sextant co-ordinates. + */ + + AddHtml( 35, 240, 230, 20, fmt, false, false ); + + AddButton( 35, 265, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 70, 265, 100, 20, 1011036, false, false ); // OKAY + } + } +#endif + + private class MessageEntry + { + private int m_Width, m_Height; + private string m_Message; + + public int Width{ get{ return m_Width; } } + public int Height{ get{ return m_Height; } } + public string Message{ get{ return m_Message; } } + + public MessageEntry( int width, int height, string message ) + { + m_Width = width; + m_Height = height; + m_Message = message; + } + + private static MessageEntry[] m_Entries = new MessageEntry[] + { + new MessageEntry( 280, 180, "...Ar! {0} and a fair wind! No chance... storms, though--ar! Is that a sea serp...

uh oh." ), + new MessageEntry( 280, 215, "...been inside this whale for three days now. I've run out of food I can pick out of his teeth. I took a sextant reading through the blowhole: {0}. I'll never see my treasure again..." ), + new MessageEntry( 280, 285, "...grand adventure! Captain Quacklebush had me swab down the decks daily...
...pirates came, I was in the rigging practicing with my sextant. {0} if I am not mistaken...
....scuttled the ship, and our precious cargo went with her and the screaming pirates, down to the bottom of the sea..." ), + new MessageEntry( 280, 180, "Help! Ship going dow...n heavy storms...precious cargo...st reach dest...current coordinates {0}...ve any survivors... ease!" ), + new MessageEntry( 280, 215, "...know that the wreck is near {0} but have not found it. Could the message passed down in my family for generations be wrong? No... I swear on the soul of my grandfather, I will find..." ), + new MessageEntry( 280, 195, "...never expected an iceberg...silly woman on bow crushed instantly...send help to {0}...ey'll never forget the tragedy of the sinking of the Miniscule..." ), + new MessageEntry( 280, 265, "...nobody knew I was a girl. They just assumed I was another sailor...then we met the undine. {0}. It was demanded sacrifice...I was youngset, they figured...
...grabbed the captain's treasure, screamed, 'It'll go down with me!'
...they took me up on it." ), + new MessageEntry( 280, 230, "...so I threw the treasure overboard, before the curse could get me too. But I was too late. Now I am doomed to wander these seas, a ghost forever. Join me: seek ye at {0} if thou wishest my company..." ), + new MessageEntry( 280, 285, "...then the ship exploded. A dragon swooped by. The slime swallowed Bertie whole--he screamed, it was amazing. The sky glowed orange. A sextant reading put us at {0}. Norma was chattering about sailing over the edge of the world. I looked at my hands and saw through them..." ), + new MessageEntry( 280, 285, "...trapped on a deserted island, with a magic fountain supplying wood, fresh water springs, gorgeous scenery, and my lovely young wife. I know the ship with all our life's earnings sank at {0} but I don't know what our coordinates are... someone has GOT to rescue me before Sunday's finals game or I'll go mad..." ), + new MessageEntry( 280, 160, "WANTED: divers exp...d in shipwre...overy. Must have own vess...pply at {0}
...good benefits, flexible hours..." ), + new MessageEntry( 280, 250, "...was a cad and a boor, no matter what momma s...rew him overboard! Oh, Anna, 'twas so exciting!
Unfort...y he grabbe...est, and all his riches went with him!
...sked the captain, and he says we're at {0}
...so maybe..." ) + }; + + public static MessageEntry[] Entries + { + get{ return m_Entries; } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Fishing/Misc/Sextant.cs b/Scripts/Items/Skill Items/Fishing/Misc/Sextant.cs new file mode 100644 index 000000000..f1c920ef9 --- /dev/null +++ b/Scripts/Items/Skill Items/Fishing/Misc/Sextant.cs @@ -0,0 +1,159 @@ +using System; +using Server.Network; + +namespace Server.Items +{ + public class Sextant : Item + { + [Constructable] + public Sextant() : base( 0x1058 ) + { + Weight = 2.0; + } + + public Sextant( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + int xLong = 0, yLat = 0; + int xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + if ( Sextant.Format( from.Location, from.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ) ) + { + string location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + from.LocalOverheadMessage( MessageType.Regular, from.SpeechHue, false, location ); + } + } + + public static bool ComputeMapDetails( Map map, int x, int y, out int xCenter, out int yCenter, out int xWidth, out int yHeight ) + { + xWidth = 5120; yHeight = 4096; + + if ( map == Map.Trammel || map == Map.Felucca ) + { + if ( x >= 0 && y >= 0 && x < 5120 && y < 4096 ) + { + xCenter = 1323; yCenter = 1624; + } + else if ( x >= 5120 && y >= 2304 && x < 6144 && y < 4096 ) + { + xCenter = 5936; yCenter = 3112; + } + else + { + xCenter = 0; yCenter = 0; + return false; + } + } + else if ( x >= 0 && y >= 0 && x < map.Width && y < map.Height ) + { + xCenter = 1323; yCenter = 1624; + } + else + { + xCenter = 0; yCenter = 0; + return false; + } + + return true; + } + + public static Point3D ReverseLookup( Map map, int xLong, int yLat, int xMins, int yMins, bool xEast, bool ySouth ) + { + if ( map == null || map == Map.Internal ) + return Point3D.Zero; + + int xCenter, yCenter; + int xWidth, yHeight; + + if ( !ComputeMapDetails( map, 0, 0, out xCenter, out yCenter, out xWidth, out yHeight ) ) + return Point3D.Zero; + + double absLong = xLong + ((double)xMins / 60); + double absLat = yLat + ((double)yMins / 60); + + if ( !xEast ) + absLong = 360.0 - absLong; + + if ( !ySouth ) + absLat = 360.0 - absLat; + + int x, y, z; + + x = xCenter + (int)((absLong * xWidth) / 360); + y = yCenter + (int)((absLat * yHeight) / 360); + + if ( x < 0 ) + x += xWidth; + else if ( x >= xWidth ) + x -= xWidth; + + if ( y < 0 ) + y += yHeight; + else if ( y >= yHeight ) + y -= yHeight; + + z = map.GetAverageZ( x, y ); + + return new Point3D( x, y, z ); + } + + public static bool Format( Point3D p, Map map, ref int xLong, ref int yLat, ref int xMins, ref int yMins, ref bool xEast, ref bool ySouth ) + { + if ( map == null || map == Map.Internal ) + return false; + + int x = p.X, y = p.Y; + int xCenter, yCenter; + int xWidth, yHeight; + + if ( !ComputeMapDetails( map, x, y, out xCenter, out yCenter, out xWidth, out yHeight ) ) + return false; + + double absLong = (double)((x - xCenter) * 360) / xWidth; + double absLat = (double)((y - yCenter) * 360) / yHeight; + + if ( absLong > 180.0 ) + absLong = -180.0 + (absLong % 180.0); + + if ( absLat > 180.0 ) + absLat = -180.0 + (absLat % 180.0); + + bool east = ( absLong >= 0 ), south = ( absLat >= 0 ); + + if ( absLong < 0.0 ) + absLong = -absLong; + + if ( absLat < 0.0 ) + absLat = -absLat; + + xLong = (int)absLong; + yLat = (int)absLat; + + xMins = (int)((absLong % 1.0) * 60); + yMins = (int)((absLat % 1.0) * 60); + + xEast = east; + ySouth = south; + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs b/Scripts/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs new file mode 100644 index 000000000..cfd409852 --- /dev/null +++ b/Scripts/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs @@ -0,0 +1,61 @@ +using System; + +namespace Server.Items +{ + public class ShipwreckedItem : Item, IDyable + { + public ShipwreckedItem( int itemID ) : base( itemID ) + { + int weight = this.ItemData.Weight; + + if ( weight >= 255 ) + weight = 1; + + this.Weight = weight; + } + + public override void OnSingleClick( Mobile from ) + { + this.LabelTo( from, 1050039, String.Format( "#{0}\t#1041645", LabelNumber ) ); + } + + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + list.Add( 1041645 ); // recovered from a shipwreck + } + + public ShipwreckedItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + if ( ItemID >= 0x13A4 && ItemID <= 0x13AE ) + { + Hue = sender.DyedHue; + return true; + } + + from.SendLocalizedMessage( sender.FailMessage ); + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs b/Scripts/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs new file mode 100644 index 000000000..9a2a3d770 --- /dev/null +++ b/Scripts/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs @@ -0,0 +1,344 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Items +{ + public class SpecialFishingNet : Item + { + public override int LabelNumber{ get{ return 1041079; } } // a special fishing net + + private bool m_InUse; + + [Constructable] + public SpecialFishingNet() : base( 0x0DCA ) + { + Weight = 1.0; + + if ( 0.01 > Utility.RandomDouble() ) + Hue = Utility.RandomList( m_Hues ); + else + Hue = 0x8A0; + } + + private static int[] m_Hues = new int[] + { + 0x09B, + 0x0CD, + 0x0D3, + 0x14D, + 0x1DD, + 0x1E9, + 0x1F4, + 0x373, + 0x451, + 0x47F, + 0x489, + 0x492, + 0x4B5, + 0x8AA + }; + + public SpecialFishingNet( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + // as if the name wasn't enough.. + list.Add( 1017410 ); // Special Fishing Net + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_InUse ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_InUse = reader.ReadBool(); + + if ( m_InUse ) + Delete(); + + break; + } + } + + Stackable = false; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_InUse ) + { + from.SendLocalizedMessage( 1010483 ); // Someone is already using that net! + } + else if ( IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1010484 ); // Where do you wish to use the net? + from.BeginTarget( -1, true, TargetFlags.None, new TargetCallback( OnTarget ) ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public void OnTarget( Mobile from, object obj ) + { + if ( Deleted || m_InUse ) + return; + + IPoint3D p3D = obj as IPoint3D; + + if ( p3D == null ) + return; + + Map map = from.Map; + + if ( map == null || map == Map.Internal ) + return; + + int x = p3D.X, y = p3D.Y; + + if ( !from.InRange( p3D, 6 ) ) + { + from.SendLocalizedMessage( 500976 ); // You need to be closer to the water to fish! + } + else if ( FullValidation( map, x, y ) ) + { + Point3D p = new Point3D( x, y, map.GetAverageZ( x, y ) ); + + for ( int i = 1; i < Amount; ++i ) // these were stackable before, doh + from.AddToBackpack( new SpecialFishingNet() ); + + m_InUse = true; + Movable = false; + MoveToWorld( p, map ); + + from.Animate( 12, 5, 1, true, false, 0 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 1.5 ), TimeSpan.FromSeconds( 1.0 ), 20, new TimerStateCallback( DoEffect ), new object[]{ p, 0, from } ); + + from.SendLocalizedMessage( 1010487 ); // You plunge the net into the sea... + } + else + { + from.SendLocalizedMessage( 1010485 ); // You can only use this net in deep water! + } + } + + private void DoEffect( object state ) + { + if ( Deleted ) + return; + + object[] states = (object[])state; + + Point3D p = (Point3D)states[0]; + int index = (int)states[1]; + Mobile from = (Mobile)states[2]; + + states[1] = ++index; + + if ( index == 1 ) + { + Effects.SendLocationEffect( p, Map, 0x352D, 16, 4 ); + Effects.PlaySound( p, Map, 0x364 ); + } + else if ( index <= 10 || index == 20 ) + { + for ( int i = 0; i < 3; ++i ) + { + int x, y; + + switch ( Utility.Random( 8 ) ) + { + default: + case 0: x = -1; y = -1; break; + case 1: x = -1; y = 0; break; + case 2: x = -1; y = +1; break; + case 3: x = 0; y = -1; break; + case 4: x = 0; y = +1; break; + case 5: x = +1; y = -1; break; + case 6: x = +1; y = 0; break; + case 7: x = +1; y = +1; break; + } + + Effects.SendLocationEffect( new Point3D( p.X + x, p.Y + y, p.Z ), Map, 0x352D, 16, 4 ); + } + + Effects.PlaySound( p, Map, 0x364 ); + + if ( index == 20 ) + FinishEffect( p, Map, from ); + else + this.Z -= 1; + } + } + + protected virtual int GetSpawnCount() + { + int count = Utility.RandomMinMax( 1, 3 ); + + if ( Hue != 0x8A0 ) + count += Utility.RandomMinMax( 1, 2 ); + + return count; + } + + protected void Spawn( Point3D p, Map map, BaseCreature spawn ) + { + if ( map == null ) + { + spawn.Delete(); + return; + } + + int x = p.X, y = p.Y; + + for ( int j = 0; j < 20; ++j ) + { + int tx = p.X - 2 + Utility.Random( 5 ); + int ty = p.Y - 2 + Utility.Random( 5 ); + + Tile t = map.Tiles.GetLandTile( tx, ty ); + + if ( t.Z == p.Z && ( (t.ID >= 0xA8 && t.ID <= 0xAB) || (t.ID >= 0x136 && t.ID <= 0x137) ) && !Spells.SpellHelper.CheckMulti( new Point3D( tx, ty, p.Z ), map ) ) + { + x = tx; + y = ty; + break; + } + } + + spawn.MoveToWorld( new Point3D( x, y, p.Z ), map ); + + if ( spawn is Kraken && 0.2 > Utility.RandomDouble() ) + spawn.PackItem( new MessageInABottle( map == Map.Felucca ? Map.Felucca : Map.Trammel ) ); + } + + protected virtual void FinishEffect( Point3D p, Map map, Mobile from ) + { + from.RevealingAction(); + + int count = GetSpawnCount(); + + for ( int i = 0; map != null && i < count; ++i ) + { + BaseCreature spawn; + + switch ( Utility.Random( 4 ) ) + { + default: + case 0: spawn = new SeaSerpent(); break; + case 1: spawn = new DeepSeaSerpent(); break; + case 2: spawn = new WaterElemental(); break; + case 3: spawn = new Kraken(); break; + } + + Spawn( p, map, spawn ); + + spawn.Combatant = from; + } + + Delete(); + } + + + public static bool FullValidation( Map map, int x, int y ) + { + bool valid = ValidateDeepWater( map, x, y ); + + for ( int j = 1, offset = 5; valid && j <= 5; ++j, offset += 5 ) + { + if ( !ValidateDeepWater( map, x + offset, y + offset ) ) + valid = false; + else if ( !ValidateDeepWater( map, x + offset, y - offset ) ) + valid = false; + else if ( !ValidateDeepWater( map, x - offset, y + offset ) ) + valid = false; + else if ( !ValidateDeepWater( map, x - offset, y - offset ) ) + valid = false; + } + + return valid; + } + + private static int[] m_WaterTiles = new int[] + { + 0x00A8, 0x00AB, + 0x0136, 0x0137 + }; + + private static bool ValidateDeepWater( Map map, int x, int y ) + { + int tileID = map.Tiles.GetLandTile( x, y ).ID; + bool water = false; + + for( int i = 0; !water && i < m_WaterTiles.Length; i += 2 ) + { + water = (tileID >= m_WaterTiles[i] && tileID <= m_WaterTiles[i + 1]); + } + + return water; + } + } + + public class FabledFishingNet : SpecialFishingNet + { + public override int LabelNumber{ get{ return 1063451; } } // a fabled fishing net + + [Constructable] + public FabledFishingNet() + { + Hue = 0x481; + } + + protected override int GetSpawnCount() + { + return base.GetSpawnCount() + 4; + } + + protected override void FinishEffect( Point3D p, Map map, Mobile from ) + { + Spawn( p, map, new Leviathan( from ) ); + + base.FinishEffect( p, map, from ); + } + + public FabledFishingNet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs b/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs new file mode 100644 index 000000000..2a5c96570 --- /dev/null +++ b/Scripts/Items/Skill Items/Harvest Tools/BaseHarvestTool.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Engines.Craft; +using Server.Engines.Harvest; +using Server.ContextMenus; + +namespace Server.Items +{ + interface IUsesRemaining + { + int UsesRemaining{ get; set; } + bool ShowUsesRemaining{ get; set; } + } + + public abstract class BaseHarvestTool : Item, IUsesRemaining, ICraftable + { + private Mobile m_Crafter; + private ToolQuality m_Quality; + private int m_UsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ToolQuality Quality + { + get{ return m_Quality; } + set{ UnscaleUses(); m_Quality = value; InvalidateProperties(); ScaleUses(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + public void ScaleUses() + { + m_UsesRemaining = (m_UsesRemaining * GetUsesScalar()) / 100; + InvalidateProperties(); + } + + public void UnscaleUses() + { + m_UsesRemaining = (m_UsesRemaining * 100) / GetUsesScalar(); + } + + public int GetUsesScalar() + { + if ( m_Quality == ToolQuality.Exceptional ) + return 200; + + return 100; + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + public abstract HarvestSystem HarvestSystem{ get; } + + public BaseHarvestTool( int itemID ) : this( 50, itemID ) + { + } + + public BaseHarvestTool( int usesRemaining, int itemID ) : base( itemID ) + { + m_UsesRemaining = usesRemaining; + m_Quality = ToolQuality.Regular; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + // Makers mark not displayed on OSI + //if ( m_Crafter != null ) + // list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + if ( m_Quality == ToolQuality.Exceptional ) + list.Add( 1060636 ); // exceptional + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + } + + public virtual void DisplayDurabilityTo( Mobile m ) + { + LabelToAffix( m, 1017323, AffixType.Append, ": " + m_UsesRemaining.ToString() ); // Durability + } + + public override void OnSingleClick( Mobile from ) + { + DisplayDurabilityTo( from ); + + base.OnSingleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) || Parent == from ) + HarvestSystem.BeginHarvesting( from, this ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + AddContextMenuEntries( from, this, list, HarvestSystem ); + } + + public static void AddContextMenuEntries( Mobile from, Item item, List list, HarvestSystem system ) + { + if ( system != Mining.System ) + return; + + if ( !item.IsChildOf( from.Backpack ) && item.Parent != from ) + return; + + PlayerMobile pm = from as PlayerMobile; + + if ( pm == null ) + return; + + ContextMenuEntry miningEntry = new ContextMenuEntry( pm.ToggleMiningStone ? 6179 : 6178 ); + miningEntry.Color = 0x421F; + list.Add( miningEntry ); + + list.Add( new ToggleMiningStoneEntry( pm, false, 6176 ) ); + list.Add( new ToggleMiningStoneEntry( pm, true, 6177 ) ); + } + + private class ToggleMiningStoneEntry : ContextMenuEntry + { + private PlayerMobile m_Mobile; + private bool m_Value; + + public ToggleMiningStoneEntry( PlayerMobile mobile, bool value, int number ) : base( number ) + { + m_Mobile = mobile; + m_Value = value; + + bool stoneMining = ( mobile.StoneMining && mobile.Skills[SkillName.Mining].Base >= 100.0 ); + + if ( mobile.ToggleMiningStone == value || ( value && !stoneMining ) ) + this.Flags |= CMEFlags.Disabled; + } + + public override void OnClick() + { + bool oldValue = m_Mobile.ToggleMiningStone; + + if ( m_Value ) + { + if ( oldValue ) + { + m_Mobile.SendLocalizedMessage( 1054023 ); // You are already set to mine both ore and stone! + } + else if ( !m_Mobile.StoneMining || m_Mobile.Skills[SkillName.Mining].Base < 100.0 ) + { + m_Mobile.SendLocalizedMessage( 1054024 ); // You have not learned how to mine stone or you do not have enough skill! + } + else + { + m_Mobile.ToggleMiningStone = true; + m_Mobile.SendLocalizedMessage( 1054022 ); // You are now set to mine both ore and stone. + } + } + else + { + if ( oldValue ) + { + m_Mobile.ToggleMiningStone = false; + m_Mobile.SendLocalizedMessage( 1054020 ); // You are now set to mine only ore. + } + else + { + m_Mobile.SendLocalizedMessage( 1054021 ); // You are already set to mine only ore! + } + } + } + } + + public BaseHarvestTool( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (Mobile) m_Crafter ); + writer.Write( (int) m_Quality ); + + writer.Write( (int) m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Crafter = reader.ReadMobile(); + m_Quality = (ToolQuality) reader.ReadInt(); + goto case 0; + } + case 0: + { + m_UsesRemaining = reader.ReadInt(); + break; + } + } + } + + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Quality = (ToolQuality)quality; + + if ( makersMark ) + Crafter = from; + + return quality; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs b/Scripts/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs new file mode 100644 index 000000000..94de2e78a --- /dev/null +++ b/Scripts/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs @@ -0,0 +1,59 @@ +using System; +using Server; +using Server.Engines.Harvest; + +namespace Server.Items +{ + public class GargoylesPickaxe : BaseAxe + { + public override int LabelNumber{ get{ return 1041281; } } // a gargoyle's pickaxe + public override HarvestSystem HarvestSystem{ get{ return Mining.System; } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 35; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 1; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 35; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash1H; } } + + [Constructable] + public GargoylesPickaxe() : this( 100 ) + { + } + + [Constructable] + public GargoylesPickaxe( int uses ) : base( 0xE86 ) + { + Weight = 11.0; + Hue = 0x973; + UsesRemaining = uses; + ShowUsesRemaining = true; + } + + public GargoylesPickaxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Harvest Tools/ProspectorsTool.cs b/Scripts/Items/Skill Items/Harvest Tools/ProspectorsTool.cs new file mode 100644 index 000000000..4be38eac7 --- /dev/null +++ b/Scripts/Items/Skill Items/Harvest Tools/ProspectorsTool.cs @@ -0,0 +1,173 @@ +using System; +using Server; +using Server.Targeting; +using Server.Engines.Harvest; + +namespace Server.Items +{ + public class ProspectorsTool : BaseBashing, IUsesRemaining + { + private int m_UsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + public override int LabelNumber{ get{ return 1049065; } } // prospector's tool + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 33; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 8; } } + public override int OldSpeed{ get{ return 33; } } + + [Constructable] + public ProspectorsTool() : base( 0xFB4 ) + { + Weight = 9.0; + UsesRemaining = 50; + } + + public ProspectorsTool( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) || Parent == from ) + from.Target = new InternalTarget( this ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + public void Prospect( Mobile from, object toProspect ) + { + if ( !IsChildOf( from.Backpack ) && Parent != from ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + return; + } + + HarvestSystem system = Mining.System; + + int tileID; + Map map; + Point3D loc; + + if ( !system.GetHarvestDetails( from, this, toProspect, out tileID, out map, out loc ) ) + { + from.SendLocalizedMessage( 1049048 ); // You cannot use your prospector tool on that. + return; + } + + HarvestDefinition def = system.GetDefinition( tileID ); + + if ( def == null || def.Veins.Length <= 1 ) + { + from.SendLocalizedMessage( 1049048 ); // You cannot use your prospector tool on that. + return; + } + + HarvestBank bank = def.GetBank( map, loc.X, loc.Y ); + + if ( bank == null ) + { + from.SendLocalizedMessage( 1049048 ); // You cannot use your prospector tool on that. + return; + } + + HarvestVein vein = bank.Vein, defaultVein = bank.DefaultVein; + + if ( vein == null || defaultVein == null ) + { + from.SendLocalizedMessage( 1049048 ); // You cannot use your prospector tool on that. + return; + } + else if ( vein != defaultVein ) + { + from.SendLocalizedMessage( 1049049 ); // That ore looks to be prospected already. + return; + } + + int veinIndex = Array.IndexOf( def.Veins, vein ); + + if ( veinIndex < 0 ) + { + from.SendLocalizedMessage( 1049048 ); // You cannot use your prospector tool on that. + } + else if ( veinIndex >= (def.Veins.Length - 1) ) + { + from.SendLocalizedMessage( 1049061 ); // You cannot improve valorite ore through prospecting. + } + else + { + bank.Vein = def.Veins[veinIndex + 1]; + from.SendLocalizedMessage( 1049050 + veinIndex ); + + --UsesRemaining; + + if ( UsesRemaining <= 0 ) + { + from.SendLocalizedMessage( 1049062 ); // You have used up your prospector's tool. + Delete(); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + writer.Write( (int) m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_UsesRemaining = reader.ReadInt(); + break; + } + case 0: + { + m_UsesRemaining = 50; + break; + } + } + } + + private class InternalTarget : Target + { + private ProspectorsTool m_Tool; + + public InternalTarget( ProspectorsTool tool ) : base( 2, true, TargetFlags.None ) + { + m_Tool = tool; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + m_Tool.Prospect( from, targeted ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Harvest Tools/Shovel.cs b/Scripts/Items/Skill Items/Harvest Tools/Shovel.cs new file mode 100644 index 000000000..3d4e1e493 --- /dev/null +++ b/Scripts/Items/Skill Items/Harvest Tools/Shovel.cs @@ -0,0 +1,40 @@ +using System; +using Server; +using Server.Engines.Harvest; + +namespace Server.Items +{ + public class Shovel : BaseHarvestTool + { + public override HarvestSystem HarvestSystem{ get{ return Mining.System; } } + + [Constructable] + public Shovel() : this( 50 ) + { + } + + [Constructable] + public Shovel( int uses ) : base( uses, 0xF39 ) + { + Weight = 5.0; + } + + public Shovel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs b/Scripts/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs new file mode 100644 index 000000000..74436313a --- /dev/null +++ b/Scripts/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs @@ -0,0 +1,59 @@ +using System; +using Server; +using Server.Engines.Harvest; + +namespace Server.Items +{ + public class SturdyPickaxe : BaseAxe + { + public override int LabelNumber{ get{ return 1045126; } } // sturdy pickaxe + public override HarvestSystem HarvestSystem{ get{ return Mining.System; } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 35; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 1; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 35; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash1H; } } + + [Constructable] + public SturdyPickaxe() : this( 180 ) + { + } + + [Constructable] + public SturdyPickaxe( int uses ) : base( 0xE86 ) + { + Weight = 11.0; + Hue = 0x973; + UsesRemaining = uses; + ShowUsesRemaining = true; + } + + public SturdyPickaxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Harvest Tools/SturdyShovel.cs b/Scripts/Items/Skill Items/Harvest Tools/SturdyShovel.cs new file mode 100644 index 000000000..090d8cff5 --- /dev/null +++ b/Scripts/Items/Skill Items/Harvest Tools/SturdyShovel.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Harvest; + +namespace Server.Items +{ + public class SturdyShovel : BaseHarvestTool + { + public override int LabelNumber{ get{ return 1045125; } } // sturdy shovel + public override HarvestSystem HarvestSystem{ get{ return Mining.System; } } + + [Constructable] + public SturdyShovel() : this( 180 ) + { + } + + [Constructable] + public SturdyShovel( int uses ) : base( uses, 0xF39 ) + { + Weight = 5.0; + Hue = 0x973; + } + + public SturdyShovel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Lumberjack/Log.cs b/Scripts/Items/Skill Items/Lumberjack/Log.cs new file mode 100644 index 000000000..bd1996357 --- /dev/null +++ b/Scripts/Items/Skill Items/Lumberjack/Log.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1bdd, 0x1be0 )] + public class Log : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} log" : "{0} logs", Amount ); + } + } + + [Constructable] + public Log() : this( 1 ) + { + } + + [Constructable] + public Log( int amount ) : base( 0x1BDD ) + { + Stackable = true; + Weight = 2.0; + Amount = amount; + } + + public Log( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/BookOfBushido.cs b/Scripts/Items/Skill Items/Magical/BookOfBushido.cs new file mode 100644 index 000000000..89e72dd46 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/BookOfBushido.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; +using Server.Spells; + +namespace Server.Items +{ + public class BookOfBushido : Spellbook + { + public override SpellbookType SpellbookType{ get{ return SpellbookType.Samurai; } } + public override int BookOffset{ get{ return 400; } } + public override int BookCount{ get{ return 6; } } + + [Constructable] + public BookOfBushido() : this( (ulong)0x3F ) + { + } + + [Constructable] + public BookOfBushido( ulong content ) : base( content, 0x238C ) + { + Layer = (Core.ML ? Layer.OneHanded : Layer.Invalid); + } + + public BookOfBushido( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( version == 0 && Core.ML ) + Layer = Layer.OneHanded; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/BookOfChivalry.cs b/Scripts/Items/Skill Items/Magical/BookOfChivalry.cs new file mode 100644 index 000000000..950ea6bff --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/BookOfChivalry.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; +using Server.Spells; + +namespace Server.Items +{ + public class BookOfChivalry : Spellbook + { + public override SpellbookType SpellbookType{ get{ return SpellbookType.Paladin; } } + public override int BookOffset{ get{ return 200; } } + public override int BookCount{ get{ return 10; } } + + [Constructable] + public BookOfChivalry() : this( (ulong)0x3FF ) + { + } + + [Constructable] + public BookOfChivalry( ulong content ) : base( content, 0x2252 ) + { + Layer = (Core.ML ? Layer.OneHanded : Layer.Invalid); + } + + public BookOfChivalry( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( version == 0 && Core.ML ) + Layer = Layer.OneHanded; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/BookOfNinjitsu.cs b/Scripts/Items/Skill Items/Magical/BookOfNinjitsu.cs new file mode 100644 index 000000000..b9897cadc --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/BookOfNinjitsu.cs @@ -0,0 +1,46 @@ +using System; +using Server.Network; +using Server.Spells; + +namespace Server.Items +{ + public class BookOfNinjitsu : Spellbook + { + public override SpellbookType SpellbookType{ get{ return SpellbookType.Ninja; } } + public override int BookOffset{ get{ return 500; } } + public override int BookCount{ get{ return 8; } } + + + [Constructable] + public BookOfNinjitsu() : this( (ulong)0xFF ) + { + } + + [Constructable] + public BookOfNinjitsu( ulong content ) : base( content, 0x23A0 ) + { + Layer = (Core.ML ? Layer.OneHanded : Layer.Invalid); + } + + public BookOfNinjitsu( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( version == 0 && Core.ML ) + Layer = Layer.OneHanded; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Misc/BlankScroll.cs b/Scripts/Items/Skill Items/Magical/Misc/BlankScroll.cs new file mode 100644 index 000000000..e3f9bffca --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Misc/BlankScroll.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server.Items +{ + public class BlankScroll : Item + { + [Constructable] + public BlankScroll() : this( 1 ) + { + } + + [Constructable] + public BlankScroll( int amount ) : base( 0xEF3 ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + public BlankScroll( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Misc/Bottle.cs b/Scripts/Items/Skill Items/Magical/Misc/Bottle.cs new file mode 100644 index 000000000..6f8190b24 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Misc/Bottle.cs @@ -0,0 +1,40 @@ +using System; + +namespace Server.Items +{ + public class Bottle : Item + { + [Constructable] + public Bottle() : this( 1 ) + { + } + + [Constructable] + public Bottle( int amount ) : base( 0xF0E ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + } + + public Bottle( Serial serial ) : base( serial ) + { + } + + + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Misc/Moongate.cs b/Scripts/Items/Skill Items/Magical/Misc/Moongate.cs new file mode 100644 index 000000000..9a1d1834e --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Misc/Moongate.cs @@ -0,0 +1,457 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Mobiles; +using Server.Network; +using Server.Gumps; +using Server.Regions; + +namespace Server.Items +{ + [DispellableFieldAttribute] + public class Moongate : Item + { + private Point3D m_Target; + private Map m_TargetMap; + private bool m_bDispellable; + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Target + { + get + { + return m_Target; + } + set + { + m_Target = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Map TargetMap + { + get + { + return m_TargetMap; + } + set + { + m_TargetMap = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Dispellable + { + get + { + return m_bDispellable; + } + set + { + m_bDispellable = value; + } + } + + public virtual bool ShowFeluccaWarning{ get{ return false; } } + + [Constructable] + public Moongate() : this( Point3D.Zero, null ) + { + m_bDispellable = true; + } + + [Constructable] + public Moongate(bool bDispellable) : this( Point3D.Zero, null ) + { + m_bDispellable = bDispellable; + } + + [Constructable] + public Moongate( Point3D target, Map targetMap ) : base( 0xF6C ) + { + Movable = false; + Light = LightType.Circle300; + + m_Target = target; + m_TargetMap = targetMap; + } + + public Moongate( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.Player ) + return; + + if ( from.InRange( GetWorldLocation(), 1 ) ) + CheckGate( from, 1 ); + else + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m.Player ) + CheckGate( m, 0 ); + + return true; + } + + public virtual void CheckGate( Mobile m, int range ) + { + new DelayTimer( m, this, range ).Start(); + } + + public virtual void OnGateUsed( Mobile m ) + { + } + + public virtual void UseGate( Mobile m ) + { + int flags = m.NetState == null ? 0 : m.NetState.Flags; + + if ( Factions.Sigil.ExistsOn( m ) ) + { + m.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( m_TargetMap == Map.Felucca && m is PlayerMobile && ((PlayerMobile)m).Young ) + { + m.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young. + } + else if ( (m.Kills >= 5 && m_TargetMap != Map.Felucca) || ( m_TargetMap == Map.Tokuno && (flags & 0x10) == 0 ) || ( m_TargetMap == Map.Malas && (flags & 0x8) == 0 ) || ( m_TargetMap == Map.Ilshenar && (flags & 0x4) == 0 ) ) + { + m.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + } + else if ( m.Spell != null ) + { + m.SendLocalizedMessage( 1049616 ); // You are too busy to do that at the moment. + } + else if ( m_TargetMap != null && m_TargetMap != Map.Internal ) + { + BaseCreature.TeleportPets( m, m_Target, m_TargetMap ); + + m.MoveToWorld( m_Target, m_TargetMap ); + + m.PlaySound( 0x1FE ); + + OnGateUsed( m ); + } + else + { + m.SendMessage( "This moongate does not seem to go anywhere." ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_Target ); + writer.Write( m_TargetMap ); + + // Version 1 + writer.Write( m_bDispellable ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Target = reader.ReadPoint3D(); + m_TargetMap = reader.ReadMap(); + + if ( version >= 1 ) + m_bDispellable = reader.ReadBool(); + } + + public virtual bool ValidateUse( Mobile from, bool message ) + { + if ( from.Deleted || this.Deleted ) + return false; + + if ( from.Map != this.Map || !from.InRange( this, 1 ) ) + { + if ( message ) + from.SendLocalizedMessage( 500446 ); // That is too far away. + + return false; + } + + return true; + } + + public virtual void BeginConfirmation( Mobile from ) + { + if ( IsInTown( from.Location, from.Map ) && !IsInTown( m_Target, m_TargetMap ) || (from.Map != Map.Felucca && TargetMap == Map.Felucca && ShowFeluccaWarning) ) + { + from.Send( new PlaySound( 0x20E, from.Location ) ); + from.CloseGump( typeof( MoongateConfirmGump ) ); + from.SendGump( new MoongateConfirmGump( from, this ) ); + } + else + { + EndConfirmation( from ); + } + } + + public virtual void EndConfirmation( Mobile from ) + { + if ( !ValidateUse( from, true ) ) + return; + + UseGate( from ); + } + + public virtual void DelayCallback( Mobile from, int range ) + { + if ( !ValidateUse( from, false ) || !from.InRange( this, range ) ) + return; + + if ( m_TargetMap != null ) + BeginConfirmation( from ); + else + from.SendMessage( "This moongate does not seem to go anywhere." ); + } + + public static bool IsInTown( Point3D p, Map map ) + { + if ( map == null ) + return false; + + GuardedRegion reg = (GuardedRegion) Region.Find( p, map ).GetRegion( typeof( GuardedRegion ) ); + + return ( reg != null && !reg.IsDisabled() ); + } + + private class DelayTimer : Timer + { + private Mobile m_From; + private Moongate m_Gate; + private int m_Range; + + public DelayTimer( Mobile from, Moongate gate, int range ) : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_From = from; + m_Gate = gate; + m_Range = range; + } + + protected override void OnTick() + { + m_Gate.DelayCallback( m_From, m_Range ); + } + } + } + + public class ConfirmationMoongate : Moongate + { + private int m_GumpWidth; + private int m_GumpHeight; + + private int m_TitleColor; + private int m_MessageColor; + + private int m_TitleNumber; + private int m_MessageNumber; + + private string m_MessageString; + + [CommandProperty( AccessLevel.GameMaster )] + public int GumpWidth + { + get{ return m_GumpWidth; } + set{ m_GumpWidth = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int GumpHeight + { + get{ return m_GumpHeight; } + set{ m_GumpHeight = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TitleColor + { + get{ return m_TitleColor; } + set{ m_TitleColor = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MessageColor + { + get{ return m_MessageColor; } + set{ m_MessageColor = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TitleNumber + { + get{ return m_TitleNumber; } + set{ m_TitleNumber = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MessageNumber + { + get{ return m_MessageNumber; } + set{ m_MessageNumber = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string MessageString + { + get{ return m_MessageString; } + set{ m_MessageString = value; } + } + + [Constructable] + public ConfirmationMoongate() : this( Point3D.Zero, null ) + { + } + + [Constructable] + public ConfirmationMoongate( Point3D target, Map targetMap ) : base( target, targetMap ) + { + } + + public ConfirmationMoongate( Serial serial ) : base( serial ) + { + } + + public virtual void Warning_Callback( Mobile from, bool okay, object state ) + { + if ( okay ) + EndConfirmation( from ); + } + + public override void BeginConfirmation( Mobile from ) + { + if ( m_GumpWidth > 0 && m_GumpHeight > 0 && m_TitleNumber > 0 && (m_MessageNumber > 0 || m_MessageString != null) ) + { + from.CloseGump( typeof( WarningGump ) ); + from.SendGump( new WarningGump( m_TitleNumber, m_TitleColor, m_MessageString == null ? (object)m_MessageNumber : (object)m_MessageString, m_MessageColor, m_GumpWidth, m_GumpHeight, new WarningGumpCallback( Warning_Callback ), from ) ); + } + else + { + base.BeginConfirmation( from ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteEncodedInt( m_GumpWidth ); + writer.WriteEncodedInt( m_GumpHeight ); + + writer.WriteEncodedInt( m_TitleColor ); + writer.WriteEncodedInt( m_MessageColor ); + + writer.WriteEncodedInt( m_TitleNumber ); + writer.WriteEncodedInt( m_MessageNumber ); + + writer.Write( m_MessageString ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_GumpWidth = reader.ReadEncodedInt(); + m_GumpHeight = reader.ReadEncodedInt(); + + m_TitleColor = reader.ReadEncodedInt(); + m_MessageColor = reader.ReadEncodedInt(); + + m_TitleNumber = reader.ReadEncodedInt(); + m_MessageNumber = reader.ReadEncodedInt(); + + m_MessageString = reader.ReadString(); + + break; + } + } + } + } + + public class MoongateConfirmGump : Gump + { + private Mobile m_From; + private Moongate m_Gate; + + public MoongateConfirmGump( Mobile from, Moongate gate ) : base( Core.AOS ? 110 : 20, Core.AOS ? 100 : 30 ) + { + m_From = from; + m_Gate = gate; + + if ( Core.AOS ) + { + Closable = false; + + AddPage( 0 ); + + AddBackground( 0, 0, 420, 280, 5054 ); + + AddImageTiled( 10, 10, 400, 20, 2624 ); + AddAlphaRegion( 10, 10, 400, 20 ); + + AddHtmlLocalized( 10, 10, 400, 20, 1062051, 30720, false, false ); // Gate Warning + + AddImageTiled( 10, 40, 400, 200, 2624 ); + AddAlphaRegion( 10, 40, 400, 200 ); + + if ( from.Map != Map.Felucca && gate.TargetMap == Map.Felucca && gate.ShowFeluccaWarning ) + AddHtmlLocalized( 10, 40, 400, 200, 1062050, 32512, false, true ); // This Gate goes to Felucca... Continue to enter the gate, Cancel to stay here + else + AddHtmlLocalized( 10, 40, 400, 200, 1062049, 32512, false, true ); // Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here + + AddImageTiled( 10, 250, 400, 20, 2624 ); + AddAlphaRegion( 10, 250, 400, 20 ); + + AddButton( 10, 250, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40, 250, 170, 20, 1011036, 32767, false, false ); // OKAY + + AddButton( 210, 250, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 240, 250, 170, 20, 1011012, 32767, false, false ); // CANCEL + } + else + { + AddPage( 0 ); + + AddBackground( 0, 0, 420, 400, 5054 ); + AddBackground( 10, 10, 400, 380, 3000 ); + + AddHtml( 20, 40, 380, 60, @"Dost thou wish to step into the moongate? Continue to enter the gate, Cancel to stay here", false, false ); + + AddHtmlLocalized( 55, 110, 290, 20, 1011012, false, false ); // CANCEL + AddButton( 20, 110, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 140, 290, 40, 1011011, false, false ); // CONTINUE + AddButton( 20, 140, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_Gate.EndConfirmation( m_From ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs new file mode 100644 index 000000000..018cba3e8 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -0,0 +1,326 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PotionKeg : Item + { + private PotionEffect m_Type; + private int m_Held; + + [CommandProperty( AccessLevel.GameMaster )] + public int Held + { + get + { + return m_Held; + } + set + { + if ( m_Held != value ) + { + m_Held = value; + UpdateWeight(); + InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public PotionEffect Type + { + get + { + return m_Type; + } + set + { + m_Type = value; + InvalidateProperties(); + } + } + + [Constructable] + public PotionKeg() : base( 0x1940 ) + { + UpdateWeight(); + } + + public virtual void UpdateWeight() + { + int held = Math.Max( 0, Math.Min( m_Held, 100 ) ); + + this.Weight = 20 + ((held * 80) / 100); + } + + public PotionKeg( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Type ); + writer.Write( (int) m_Held ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_Type = (PotionEffect)reader.ReadInt(); + m_Held = reader.ReadInt(); + + break; + } + } + + if ( version < 1 ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( UpdateWeight ) ); + } + + public override int LabelNumber{ get{ return (m_Held > 0 ? 1041620 + (int)m_Type : 1041641); } } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + int number; + + if ( m_Held <= 0 ) + number = 502246; // The keg is empty. + else if ( m_Held < 5 ) + number = 502248; // The keg is nearly empty. + else if ( m_Held < 20 ) + number = 502249; // The keg is not very full. + else if ( m_Held < 30 ) + number = 502250; // The keg is about one quarter full. + else if ( m_Held < 40 ) + number = 502251; // The keg is about one third full. + else if ( m_Held < 47 ) + number = 502252; // The keg is almost half full. + else if ( m_Held < 54 ) + number = 502254; // The keg is approximately half full. + else if ( m_Held < 70 ) + number = 502253; // The keg is more than half full. + else if ( m_Held < 80 ) + number = 502255; // The keg is about three quarters full. + else if ( m_Held < 96 ) + number = 502256; // The keg is very full. + else if ( m_Held < 100 ) + number = 502257; // The liquid is almost to the top of the keg. + else + number = 502258; // The keg is completely full. + + list.Add( number ); + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + int number; + + if ( m_Held <= 0 ) + number = 502246; // The keg is empty. + else if ( m_Held < 5 ) + number = 502248; // The keg is nearly empty. + else if ( m_Held < 20 ) + number = 502249; // The keg is not very full. + else if ( m_Held < 30 ) + number = 502250; // The keg is about one quarter full. + else if ( m_Held < 40 ) + number = 502251; // The keg is about one third full. + else if ( m_Held < 47 ) + number = 502252; // The keg is almost half full. + else if ( m_Held < 54 ) + number = 502254; // The keg is approximately half full. + else if ( m_Held < 70 ) + number = 502253; // The keg is more than half full. + else if ( m_Held < 80 ) + number = 502255; // The keg is about three quarters full. + else if ( m_Held < 96 ) + number = 502256; // The keg is very full. + else if ( m_Held < 100 ) + number = 502257; // The liquid is almost to the top of the keg. + else + number = 502258; // The keg is completely full. + + this.LabelTo( from, number ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 2 ) ) + { + if ( m_Held > 0 ) + { + Container pack = from.Backpack; + + if ( pack != null && pack.ConsumeTotal( typeof( Bottle ), 1 ) ) + { + from.SendLocalizedMessage( 502242 ); // You pour some of the keg's contents into an empty bottle... + + BasePotion pot = FillBottle(); + + if ( pack.TryDropItem( from, pot, false ) ) + { + from.SendLocalizedMessage( 502243 ); // ...and place it into your backpack. + from.PlaySound( 0x240 ); + + if ( --Held == 0 ) + from.SendLocalizedMessage( 502245 ); // The keg is now empty. + } + else + { + from.SendLocalizedMessage( 502244 ); // ...but there is no room for the bottle in your backpack. + pot.Delete(); + } + } + else + { + // TODO: Target a bottle + } + } + else + { + from.SendLocalizedMessage( 502246 ); // The keg is empty. + } + } + else + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( item is BasePotion ) + { + BasePotion pot = (BasePotion)item; + + if ( m_Held == 0 ) + { + if ( GiveBottle( from ) ) + { + m_Type = pot.PotionEffect; + Held = 1; + + from.PlaySound( 0x240 ); + + from.SendLocalizedMessage( 502237 ); // You place the empty bottle in your backpack. + + item.Delete(); + return true; + } + else + { + from.SendLocalizedMessage( 502238 ); // You don't have room for the empty bottle in your backpack. + return false; + } + } + else if ( pot.PotionEffect != m_Type ) + { + from.SendLocalizedMessage( 502236 ); // You decide that it would be a bad idea to mix different types of potions. + return false; + } + else if ( m_Held >= 100 ) + { + from.SendLocalizedMessage( 502233 ); // The keg will not hold any more! + return false; + } + else + { + if ( GiveBottle( from ) ) + { + ++Held; + item.Delete(); + + from.PlaySound( 0x240 ); + + from.SendLocalizedMessage( 502237 ); // You place the empty bottle in your backpack. + + item.Delete(); + return true; + } + else + { + from.SendLocalizedMessage( 502238 ); // You don't have room for the empty bottle in your backpack. + return false; + } + } + } + else + { + from.SendLocalizedMessage( 502232 ); // The keg is not designed to hold that type of object. + return false; + } + } + + public bool GiveBottle( Mobile m ) + { + Container pack = m.Backpack; + + Bottle bottle = new Bottle(); + + if ( pack == null || !pack.TryDropItem( m, bottle, false ) ) + { + bottle.Delete(); + return false; + } + + return true; + } + + public BasePotion FillBottle() + { + switch ( m_Type ) + { + default: + case PotionEffect.Nightsight: return new NightSightPotion(); + + case PotionEffect.CureLesser: return new LesserCurePotion(); + case PotionEffect.Cure: return new CurePotion(); + case PotionEffect.CureGreater: return new GreaterCurePotion(); + + case PotionEffect.Agility: return new AgilityPotion(); + case PotionEffect.AgilityGreater: return new GreaterAgilityPotion(); + + case PotionEffect.Strength: return new StrengthPotion(); + case PotionEffect.StrengthGreater: return new GreaterStrengthPotion(); + + case PotionEffect.PoisonLesser: return new LesserPoisonPotion(); + case PotionEffect.Poison: return new PoisonPotion(); + case PotionEffect.PoisonGreater: return new GreaterPoisonPotion(); + case PotionEffect.PoisonDeadly: return new DeadlyPoisonPotion(); + + case PotionEffect.Refresh: return new RefreshPotion(); + case PotionEffect.RefreshTotal: return new TotalRefreshPotion(); + + case PotionEffect.HealLesser: return new LesserHealPotion(); + case PotionEffect.Heal: return new HealPotion(); + case PotionEffect.HealGreater: return new GreaterHealPotion(); + + case PotionEffect.ExplosionLesser: return new LesserExplosionPotion(); + case PotionEffect.Explosion: return new ExplosionPotion(); + case PotionEffect.ExplosionGreater: return new GreaterExplosionPotion(); + } + } + + public static void Initialize() + { + TileData.ItemTable[0x1940].Height = 4; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Misc/RecallRune.cs b/Scripts/Items/Skill Items/Magical/Misc/RecallRune.cs new file mode 100644 index 000000000..bbfffa0f2 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Misc/RecallRune.cs @@ -0,0 +1,321 @@ +using System; +using Server.Network; +using Server.Prompts; +using Server.Multis; +using Server.Regions; + +namespace Server.Items +{ + [FlipableAttribute( 0x1f14, 0x1f15, 0x1f16, 0x1f17 )] + public class RecallRune : Item + { + private string m_Description; + private bool m_Marked; + private Point3D m_Target; + private Map m_TargetMap; + private BaseHouse m_House; + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + if ( m_House != null && !m_House.Deleted ) + { + writer.Write( (int) 1 ); // version + + writer.Write( (Item) m_House ); + } + else + { + writer.Write( (int) 0 ); // version + } + + writer.Write( (string) m_Description ); + writer.Write( (bool) m_Marked ); + writer.Write( (Point3D) m_Target ); + writer.Write( (Map) m_TargetMap ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_House = reader.ReadItem() as BaseHouse; + goto case 0; + } + case 0: + { + m_Description = reader.ReadString(); + m_Marked = reader.ReadBool(); + m_Target = reader.ReadPoint3D(); + m_TargetMap = reader.ReadMap(); + + CalculateHue(); + + break; + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public BaseHouse House + { + get + { + if ( m_House != null && m_House.Deleted ) + House = null; + + return m_House; + } + set{ m_House = value; CalculateHue(); InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public string Description + { + get + { + return m_Description; + } + set + { + m_Description = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public bool Marked + { + get + { + return m_Marked; + } + set + { + if ( m_Marked != value ) + { + m_Marked = value; + CalculateHue(); + InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public Point3D Target + { + get + { + return m_Target; + } + set + { + m_Target = value; + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public Map TargetMap + { + get + { + return m_TargetMap; + } + set + { + if ( m_TargetMap != value ) + { + m_TargetMap = value; + CalculateHue(); + InvalidateProperties(); + } + } + } + + private void CalculateHue() + { + if ( !m_Marked ) + Hue = 0; + else if ( m_TargetMap == Map.Trammel ) + Hue = (House != null ? 0x47F : 50); + else if ( m_TargetMap == Map.Felucca ) + Hue = (House != null ? 0x66D : 0); + else if ( m_TargetMap == Map.Ilshenar ) + Hue = (House != null ? 0x55F : 1102); + else if ( m_TargetMap == Map.Malas ) + Hue = (House != null ? 0x55F : 1102); + else if ( m_TargetMap == Map.Tokuno ) + Hue = (House != null ? 0x47F : 1154); + } + + public void Mark( Mobile m ) + { + m_Marked = true; + + bool setDesc = false; + if ( Core.AOS ) + { + m_House = BaseHouse.FindHouseAt( m ); + + if ( m_House == null ) + { + m_Target = m.Location; + m_TargetMap = m.Map; + } + else + { + HouseSign sign = m_House.Sign; + + if ( sign != null ) + m_Description = sign.Name; + else + m_Description = null; + + if ( m_Description == null || (m_Description = m_Description.Trim()).Length == 0 ) + m_Description = "an unnamed house"; + + setDesc = true; + + int x = m_House.BanLocation.X; + int y = m_House.BanLocation.Y + 2; + int z = m_House.BanLocation.Z; + + Map map = m_House.Map; + + if ( map != null && !map.CanFit( x, y, z, 16, false, false ) ) + z = map.GetAverageZ( x, y ); + + m_Target = new Point3D( x, y, z ); + m_TargetMap = map; + } + } + else + { + m_House = null; + m_Target = m.Location; + m_TargetMap = m.Map; + } + + if( !setDesc ) + m_Description = BaseRegion.GetRuneNameFor( Region.Find( m_Target, m_TargetMap ) ); + + CalculateHue(); + InvalidateProperties(); + } + + private const string RuneFormat = "a recall rune for {0}"; + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Marked ) + { + string desc; + + if ( (desc = m_Description) == null || (desc = desc.Trim()).Length == 0 ) + desc = "an unknown location"; + + if ( m_TargetMap == Map.Tokuno ) + list.Add( (House != null ? 1063260 : 1063259), RuneFormat, desc ); // ~1_val~ (Tokuno Islands)[(House)] + else if ( m_TargetMap == Map.Malas ) + list.Add( (House != null ? 1062454 : 1060804), RuneFormat, desc ); // ~1_val~ (Malas)[(House)] + else if ( m_TargetMap == Map.Felucca ) + list.Add( (House != null ? 1062452 : 1060805), RuneFormat, desc ); // ~1_val~ (Felucca)[(House)] + else if ( m_TargetMap == Map.Trammel ) + list.Add( (House != null ? 1062453 : 1060806), RuneFormat, desc ); // ~1_val~ (Trammel)[(House)] + else + list.Add( (House != null ? "{0} ({1})(House)" : "{0} ({1})"), String.Format( RuneFormat, desc ), m_TargetMap ); + } + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Marked ) + { + string desc; + + if ( (desc = m_Description) == null || (desc = desc.Trim()).Length == 0 ) + desc = "an unknown location"; + + if ( m_TargetMap == Map.Tokuno ) + LabelTo( from, (House != null ? 1063260 : 1063259), String.Format( RuneFormat, desc ) ); // ~1_val~ (Tokuno Islands)[(House)] + else if ( m_TargetMap == Map.Malas ) + LabelTo( from, (House != null ? 1062454 : 1060804), String.Format( RuneFormat, desc ) ); // ~1_val~ (Malas)[(House)] + else if ( m_TargetMap == Map.Felucca ) + LabelTo( from, (House != null ? 1062452 : 1060805), String.Format( RuneFormat, desc ) ); // ~1_val~ (Felucca)[(House)] + else if ( m_TargetMap == Map.Trammel ) + LabelTo( from, (House != null ? 1062453 : 1060806), String.Format( RuneFormat, desc ) ); // ~1_val~ (Trammel)[(House)] + else + LabelTo( from, (House != null ? "{0} ({1})(House)" : "{0} ({1})"), String.Format( RuneFormat, desc ), m_TargetMap ); + } + else + { + LabelTo( from, "an unmarked recall rune" ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + int number; + + if ( !IsChildOf( from.Backpack ) ) + { + number = 1042001; // That must be in your pack for you to use it. + } + else if ( House != null ) + { + number = 1062399; // You cannot edit the description for this rune. + } + else if ( m_Marked ) + { + number = 501804; // Please enter a description for this marked object. + + from.Prompt = new RenamePrompt( this ); + } + else + { + number = 501805; // That rune is not yet marked. + } + + from.SendLocalizedMessage( number ); + } + + private class RenamePrompt : Prompt + { + private RecallRune m_Rune; + + public RenamePrompt( RecallRune rune ) + { + m_Rune = rune; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Rune.House == null && m_Rune.Marked ) + { + m_Rune.Description = text; + from.SendLocalizedMessage( 1010474 ); // The etching on the rune has been changed. + } + } + } + + [Constructable] + public RecallRune() : base( 0x1F14 ) + { + Weight = 1.0; + CalculateHue(); + } + + public RecallRune( Serial serial ) : base( serial ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/NecromancerSpellbook.cs b/Scripts/Items/Skill Items/Magical/NecromancerSpellbook.cs new file mode 100644 index 000000000..7dae771a6 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/NecromancerSpellbook.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; +using Server.Spells; + +namespace Server.Items +{ + public class NecromancerSpellbook : Spellbook + { + public override SpellbookType SpellbookType{ get{ return SpellbookType.Necromancer; } } + public override int BookOffset{ get{ return 100; } } + public override int BookCount{ get{ return ((Core.SE) ? 17 : 16); } } + + [Constructable] + public NecromancerSpellbook() : this( (ulong)0 ) + { + } + + [Constructable] + public NecromancerSpellbook( ulong content ) : base( content, 0x2253 ) + { + Layer = (Core.ML ? Layer.OneHanded : Layer.Invalid); + } + + public NecromancerSpellbook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( version == 0 && Core.ML ) + Layer = Layer.OneHanded; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/AgilityPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/AgilityPotion.cs new file mode 100644 index 000000000..cf63301f7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/AgilityPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AgilityPotion : BaseAgilityPotion + { + public override int DexOffset{ get{ return 10; } } + public override TimeSpan Duration{ get{ return TimeSpan.FromMinutes( 2.0 ); } } + + [Constructable] + public AgilityPotion() : base( PotionEffect.Agility ) + { + } + + public AgilityPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs new file mode 100644 index 000000000..0ced4dab7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/BaseAgilityPotion.cs @@ -0,0 +1,57 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseAgilityPotion : BasePotion + { + public abstract int DexOffset{ get; } + public abstract TimeSpan Duration{ get; } + + public BaseAgilityPotion( PotionEffect effect ) : base( 0xF08, effect ) + { + } + + public BaseAgilityPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool DoAgility( Mobile from ) + { + // TODO: Verify scaled; is it offset, duration, or both? + if ( Spells.SpellHelper.AddStatOffset( from, StatType.Dex, Scale( from, DexOffset ), Duration ) ) + { + from.FixedEffect( 0x375A, 10, 15 ); + from.PlaySound( 0x1E7 ); + return true; + } + + from.SendLocalizedMessage( 502173 ); // You are already under a similar effect. + return false; + } + + public override void Drink( Mobile from ) + { + if ( DoAgility( from ) ) + { + BasePotion.PlayDrinkEffect( from ); + + this.Consume(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/GreaterAgilityPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/GreaterAgilityPotion.cs new file mode 100644 index 000000000..f084a690b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Agility Potions/GreaterAgilityPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterAgilityPotion : BaseAgilityPotion + { + public override int DexOffset{ get{ return 20; } } + public override TimeSpan Duration{ get{ return TimeSpan.FromMinutes( 2.0 ); } } + + [Constructable] + public GreaterAgilityPotion() : base( PotionEffect.AgilityGreater ) + { + } + + public GreaterAgilityPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs new file mode 100644 index 000000000..80899491a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/BasePotion.cs @@ -0,0 +1,199 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public enum PotionEffect + { + Nightsight, + CureLesser, + Cure, + CureGreater, + Agility, + AgilityGreater, + Strength, + StrengthGreater, + PoisonLesser, + Poison, + PoisonGreater, + PoisonDeadly, + Refresh, + RefreshTotal, + HealLesser, + Heal, + HealGreater, + ExplosionLesser, + Explosion, + ExplosionGreater + } + + public abstract class BasePotion : Item, ICraftable + { + private PotionEffect m_PotionEffect; + + public PotionEffect PotionEffect + { + get + { + return m_PotionEffect; + } + set + { + m_PotionEffect = value; + InvalidateProperties(); + } + } + + public override int LabelNumber{ get{ return 1041314 + (int)m_PotionEffect; } } + + public BasePotion( int itemID, PotionEffect effect ) : base( itemID ) + { + m_PotionEffect = effect; + + Stackable = false; + Weight = 1.0; + } + + public BasePotion( Serial serial ) : base( serial ) + { + } + + public virtual bool RequireFreeHand{ get{ return true; } } + + public static bool HasFreeHand( Mobile m ) + { + Item handOne = m.FindItemOnLayer( Layer.OneHanded ); + Item handTwo = m.FindItemOnLayer( Layer.TwoHanded ); + + if ( handTwo is BaseWeapon ) + handOne = handTwo; + + return ( handOne == null || handTwo == null ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Movable ) + return; + + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + if ( !RequireFreeHand || HasFreeHand( from ) ) + Drink( from ); + else + from.SendLocalizedMessage( 502172 ); // You must have a free hand to drink a potion. + } + else + { + from.SendLocalizedMessage( 502138 ); // That is too far away for you to use + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_PotionEffect ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_PotionEffect = (PotionEffect)reader.ReadInt(); + break; + } + } + } + + public abstract void Drink( Mobile from ); + + public static void PlayDrinkEffect( Mobile m ) + { + m.RevealingAction(); + + m.PlaySound( 0x2D6 ); + + m.AddToBackpack( new Bottle() ); + + if ( m.Body.IsHuman /*&& !m.Mounted*/ ) + m.Animate( 34, 5, 1, true, false, 0 ); + } + + public static TimeSpan Scale( Mobile m, TimeSpan v ) + { + if ( !Core.AOS ) + return v; + + double scalar = 1.0 + (0.01 * AosAttributes.GetValue( m, AosAttribute.EnhancePotions )); + + return TimeSpan.FromSeconds( v.TotalSeconds * scalar ); + } + + public static double Scale( Mobile m, double v ) + { + if ( !Core.AOS ) + return v; + + double scalar = 1.0 + (0.01 * AosAttributes.GetValue( m, AosAttribute.EnhancePotions )); + + return v * scalar; + } + + public static int Scale( Mobile m, int v ) + { + if ( !Core.AOS ) + return v; + + return AOS.Scale( v, 100 + AosAttributes.GetValue( m, AosAttribute.EnhancePotions ) ); + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + if ( craftSystem is DefAlchemy ) + { + Container pack = from.Backpack; + + if ( pack != null ) + { + Item[] kegs = pack.FindItemsByType( typeof( PotionKeg ), true ); + + for ( int i = 0; i < kegs.Length; ++i ) + { + PotionKeg keg = kegs[i] as PotionKeg; + + if ( keg == null ) + continue; + + if ( keg.Held <= 0 || keg.Held >= 100 ) + continue; + + if ( keg.Type != PotionEffect ) + continue; + + ++keg.Held; + + Delete(); + from.AddToBackpack( new Bottle() ); + + return -1; // signal placed in keg + } + } + } + + return 1; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs new file mode 100644 index 000000000..6673aaf27 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/BaseCurePotion.cs @@ -0,0 +1,107 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CureLevelInfo + { + private Poison m_Poison; + private double m_Chance; + + public Poison Poison + { + get{ return m_Poison; } + } + + public double Chance + { + get{ return m_Chance; } + } + + public CureLevelInfo( Poison poison, double chance ) + { + m_Poison = poison; + m_Chance = chance; + } + } + + public abstract class BaseCurePotion : BasePotion + { + public abstract CureLevelInfo[] LevelInfo{ get; } + + public BaseCurePotion( PotionEffect effect ) : base( 0xF07, effect ) + { + } + + public BaseCurePotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public void DoCure( Mobile from ) + { + bool cure = false; + + CureLevelInfo[] info = LevelInfo; + + for ( int i = 0; i < info.Length; ++i ) + { + CureLevelInfo li = info[i]; + + if ( li.Poison == from.Poison && Scale( from, li.Chance ) > Utility.RandomDouble() ) + { + cure = true; + break; + } + } + + if ( cure && from.CurePoison( from ) ) + { + from.SendLocalizedMessage( 500231 ); // You feel cured of poison! + + from.FixedEffect( 0x373A, 10, 15 ); + from.PlaySound( 0x1E0 ); + } + else if ( !cure ) + { + from.SendLocalizedMessage( 500232 ); // That potion was not strong enough to cure your ailment! + } + } + + public override void Drink( Mobile from ) + { + if ( Spells.Necromancy.TransformationSpell.UnderTransformation( from, typeof( Spells.Necromancy.VampiricEmbraceSpell ) ) ) + { + from.SendLocalizedMessage( 1061652 ); // The garlic in the potion would surely kill you. + } + else if ( from.Poisoned ) + { + DoCure( from ); + + BasePotion.PlayDrinkEffect( from ); + + from.FixedParticles( 0x373A, 10, 15, 5012, EffectLayer.Waist ); + from.PlaySound( 0x1E0 ); + + this.Consume(); + } + else + { + from.SendLocalizedMessage( 1042000 ); // You are not poisoned. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/CurePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/CurePotion.cs new file mode 100644 index 000000000..7e077334e --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/CurePotion.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CurePotion : BaseCurePotion + { + private static CureLevelInfo[] m_OldLevelInfo = new CureLevelInfo[] + { + new CureLevelInfo( Poison.Lesser, 1.00 ), // 100% chance to cure lesser poison + new CureLevelInfo( Poison.Regular, 0.75 ), // 75% chance to cure regular poison + new CureLevelInfo( Poison.Greater, 0.50 ), // 50% chance to cure greater poison + new CureLevelInfo( Poison.Deadly, 0.15 ) // 15% chance to cure deadly poison + }; + + private static CureLevelInfo[] m_AosLevelInfo = new CureLevelInfo[] + { + new CureLevelInfo( Poison.Lesser, 1.00 ), + new CureLevelInfo( Poison.Regular, 0.95 ), + new CureLevelInfo( Poison.Greater, 0.75 ), + new CureLevelInfo( Poison.Deadly, 0.50 ), + new CureLevelInfo( Poison.Lethal, 0.25 ) + }; + + public override CureLevelInfo[] LevelInfo{ get{ return Core.AOS ? m_AosLevelInfo : m_OldLevelInfo; } } + + [Constructable] + public CurePotion() : base( PotionEffect.Cure ) + { + } + + public CurePotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/GreaterCurePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/GreaterCurePotion.cs new file mode 100644 index 000000000..25703f4f9 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/GreaterCurePotion.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterCurePotion : BaseCurePotion + { + private static CureLevelInfo[] m_OldLevelInfo = new CureLevelInfo[] + { + new CureLevelInfo( Poison.Lesser, 1.00 ), // 100% chance to cure lesser poison + new CureLevelInfo( Poison.Regular, 1.00 ), // 100% chance to cure regular poison + new CureLevelInfo( Poison.Greater, 1.00 ), // 100% chance to cure greater poison + new CureLevelInfo( Poison.Deadly, 0.75 ), // 75% chance to cure deadly poison + new CureLevelInfo( Poison.Lethal, 0.25 ) // 25% chance to cure lethal poison + }; + + private static CureLevelInfo[] m_AosLevelInfo = new CureLevelInfo[] + { + new CureLevelInfo( Poison.Lesser, 1.00 ), + new CureLevelInfo( Poison.Regular, 1.00 ), + new CureLevelInfo( Poison.Greater, 1.00 ), + new CureLevelInfo( Poison.Deadly, 0.95 ), + new CureLevelInfo( Poison.Lethal, 0.75 ) + }; + + public override CureLevelInfo[] LevelInfo{ get{ return Core.AOS ? m_AosLevelInfo : m_OldLevelInfo; } } + + [Constructable] + public GreaterCurePotion() : base( PotionEffect.CureGreater ) + { + } + + public GreaterCurePotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/LesserCurePotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/LesserCurePotion.cs new file mode 100644 index 000000000..cb8a147a4 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Cure Potions/LesserCurePotion.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LesserCurePotion : BaseCurePotion + { + private static CureLevelInfo[] m_OldLevelInfo = new CureLevelInfo[] + { + new CureLevelInfo( Poison.Lesser, 0.75 ), // 75% chance to cure lesser poison + new CureLevelInfo( Poison.Regular, 0.50 ), // 50% chance to cure regular poison + new CureLevelInfo( Poison.Greater, 0.15 ) // 15% chance to cure greater poison + }; + + private static CureLevelInfo[] m_AosLevelInfo = new CureLevelInfo[] + { + new CureLevelInfo( Poison.Lesser, 0.75 ), + new CureLevelInfo( Poison.Regular, 0.50 ), + new CureLevelInfo( Poison.Greater, 0.25 ) + }; + + public override CureLevelInfo[] LevelInfo{ get{ return Core.AOS ? m_AosLevelInfo : m_OldLevelInfo; } } + + [Constructable] + public LesserCurePotion() : base( PotionEffect.CureLesser ) + { + } + + public LesserCurePotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs new file mode 100644 index 000000000..1e2b0fa80 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/BaseExplosionPotion.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Targeting; +using Server.Spells; + +namespace Server.Items +{ + public abstract class BaseExplosionPotion : BasePotion + { + public abstract int MinDamage { get; } + public abstract int MaxDamage { get; } + + public override bool RequireFreeHand{ get{ return false; } } + + private static bool LeveledExplosion = false; // Should explosion potions explode other nearby potions? + private static bool InstantExplosion = false; // Should explosion potions explode on impact? + private const int ExplosionRange = 2; // How long is the blast radius? + + public BaseExplosionPotion( PotionEffect effect ) : base( 0xF0D, effect ) + { + } + + public BaseExplosionPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public virtual object FindParent( Mobile from ) + { + Mobile m = this.HeldBy; + + if ( m != null && m.Holding == this ) + return m; + + object obj = this.RootParent; + + if ( obj != null ) + return obj; + + if ( Map == Map.Internal ) + return from; + + return this; + } + + private Timer m_Timer; + + private ArrayList m_Users; + + public override void Drink( Mobile from ) + { + if ( Core.AOS && (from.Paralyzed || from.Frozen || (from.Spell != null && from.Spell.IsCasting)) ) + { + from.SendLocalizedMessage( 1062725 ); // You can not use a purple potion while paralyzed. + return; + } + + ThrowTarget targ = from.Target as ThrowTarget; + + if ( targ != null && targ.Potion == this ) + return; + + from.RevealingAction(); + + if ( m_Users == null ) + m_Users = new ArrayList(); + + if ( !m_Users.Contains( from ) ) + m_Users.Add( from ); + + from.Target = new ThrowTarget( this ); + + if ( m_Timer == null ) + { + from.SendLocalizedMessage( 500236 ); // You should throw it now! + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 0.75 ), TimeSpan.FromSeconds( 1.0 ), 4, new TimerStateCallback( Detonate_OnTick ), new object[]{ from, 3 } ); + } + } + + private void Detonate_OnTick( object state ) + { + if ( Deleted ) + return; + + object[] states = (object[])state; + Mobile from = (Mobile)states[0]; + int timer = (int)states[1]; + + object parent = FindParent( from ); + + if ( timer == 0 ) + { + Point3D loc; + Map map; + + if ( parent is Item ) + { + Item item = (Item)parent; + + loc = item.GetWorldLocation(); + map = item.Map; + } + else if ( parent is Mobile ) + { + Mobile m = (Mobile)parent; + + loc = m.Location; + map = m.Map; + } + else + { + return; + } + + Explode( from, true, loc, map ); + } + else + { + if ( parent is Item ) + ((Item)parent).PublicOverheadMessage( MessageType.Regular, 0x22, false, timer.ToString() ); + else if ( parent is Mobile ) + ((Mobile)parent).PublicOverheadMessage( MessageType.Regular, 0x22, false, timer.ToString() ); + + states[1] = timer - 1; + } + } + + private void Reposition_OnTick( object state ) + { + if ( Deleted ) + return; + + object[] states = (object[])state; + Mobile from = (Mobile)states[0]; + IPoint3D p = (IPoint3D)states[1]; + Map map = (Map)states[2]; + + Point3D loc = new Point3D( p ); + + if ( InstantExplosion ) + Explode( from, true, loc, map ); + else + MoveToWorld( loc, map ); + } + + private class ThrowTarget : Target + { + private BaseExplosionPotion m_Potion; + + public BaseExplosionPotion Potion + { + get{ return m_Potion; } + } + + public ThrowTarget( BaseExplosionPotion potion ) : base( 12, true, TargetFlags.None ) + { + m_Potion = potion; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Potion.Deleted || m_Potion.Map == Map.Internal ) + return; + + IPoint3D p = targeted as IPoint3D; + + if ( p == null ) + return; + + Map map = from.Map; + + if ( map == null ) + return; + + SpellHelper.GetSurfaceTop( ref p ); + + from.RevealingAction(); + + IEntity to; + + if ( p is Mobile ) + to = (Mobile)p; + else + to = new Entity( Serial.Zero, new Point3D( p ), map ); + + Effects.SendMovingEffect( from, to, m_Potion.ItemID & 0x3FFF, 7, 0, false, false, m_Potion.Hue, 0 ); + + m_Potion.Internalize(); + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( m_Potion.Reposition_OnTick ), new object[]{ from, p, map } ); + } + } + + public void Explode( Mobile from, bool direct, Point3D loc, Map map ) + { + if ( Deleted ) + return; + + Delete(); + + for ( int i = 0; m_Users != null && i < m_Users.Count; ++i ) + { + Mobile m = (Mobile)m_Users[i]; + ThrowTarget targ = m.Target as ThrowTarget; + + if ( targ != null && targ.Potion == this ) + Target.Cancel( m ); + } + + if ( map == null ) + return; + + Effects.PlaySound( loc, map, 0x207 ); + Effects.SendLocationEffect( loc, map, 0x36BD, 20 ); + + int alchemyBonus = 0; + + if ( direct ) + alchemyBonus = (int)(from.Skills.Alchemy.Value / (Core.AOS ? 5 : 10)); + + IPooledEnumerable eable = LeveledExplosion ? map.GetObjectsInRange( loc, ExplosionRange ) : map.GetMobilesInRange( loc, ExplosionRange ); + ArrayList toExplode = new ArrayList(); + + int toDamage = 0; + + foreach ( object o in eable ) + { + if ( o is Mobile ) + { + toExplode.Add( o ); + ++toDamage; + } + else if ( o is BaseExplosionPotion && o != this ) + { + toExplode.Add( o ); + } + } + + eable.Free(); + + int min = Scale( from, MinDamage ); + int max = Scale( from, MaxDamage ); + + for ( int i = 0; i < toExplode.Count; ++i ) + { + object o = toExplode[i]; + + if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + if ( from == null || (SpellHelper.ValidIndirectTarget( from, m ) && from.CanBeHarmful( m, false )) ) + { + if ( from != null ) + from.DoHarmful( m ); + + int damage = Utility.RandomMinMax( min, max ); + + damage += alchemyBonus; + + if ( !Core.AOS && damage > 40 ) + damage = 40; + else if ( Core.AOS && toDamage > 2 ) + damage /= toDamage - 1; + + AOS.Damage( m, from, damage, 0, 100, 0, 0, 0 ); + } + } + else if ( o is BaseExplosionPotion ) + { + BaseExplosionPotion pot = (BaseExplosionPotion)o; + + pot.Explode( from, false, pot.GetWorldLocation(), pot.Map ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/ExplosionPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/ExplosionPotion.cs new file mode 100644 index 000000000..98648fcc9 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/ExplosionPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ExplosionPotion : BaseExplosionPotion + { + public override int MinDamage { get { return 10; } } + public override int MaxDamage { get { return 20; } } + + [Constructable] + public ExplosionPotion() : base( PotionEffect.Explosion ) + { + } + + public ExplosionPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/GreaterExplosionPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/GreaterExplosionPotion.cs new file mode 100644 index 000000000..a4d89b3fc --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/GreaterExplosionPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterExplosionPotion : BaseExplosionPotion + { + public override int MinDamage { get { return Core.AOS ? 20 : 15; } } + public override int MaxDamage { get { return Core.AOS ? 40 : 30; } } + + [Constructable] + public GreaterExplosionPotion() : base( PotionEffect.ExplosionGreater ) + { + } + + public GreaterExplosionPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/LesserExplosionPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/LesserExplosionPotion.cs new file mode 100644 index 000000000..3aa550f36 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Explosion Potions/LesserExplosionPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LesserExplosionPotion : BaseExplosionPotion + { + public override int MinDamage { get { return 5; } } + public override int MaxDamage { get { return 10; } } + + [Constructable] + public LesserExplosionPotion() : base( PotionEffect.ExplosionLesser ) + { + } + + public LesserExplosionPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs new file mode 100644 index 000000000..6002a7b7c --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/BaseHealPotion.cs @@ -0,0 +1,80 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public abstract class BaseHealPotion : BasePotion + { + public abstract int MinHeal { get; } + public abstract int MaxHeal { get; } + public abstract double Delay { get; } + + public BaseHealPotion( PotionEffect effect ) : base( 0xF0C, effect ) + { + } + + public BaseHealPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public void DoHeal( Mobile from ) + { + int min = Scale( from, MinHeal ); + int max = Scale( from, MaxHeal ); + + from.Heal( Utility.RandomMinMax( min, max ) ); + } + + public override void Drink( Mobile from ) + { + if ( from.Hits < from.HitsMax ) + { + if ( from.Poisoned || MortalStrike.IsWounded( from ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x22, 1005000 ); // You can not heal yourself in your current state. + } + else + { + if ( from.BeginAction( typeof( BaseHealPotion ) ) ) + { + DoHeal( from ); + + BasePotion.PlayDrinkEffect( from ); + + this.Consume(); + + Timer.DelayCall( TimeSpan.FromSeconds( Delay ), new TimerStateCallback( ReleaseHealLock ), from ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x22, 500235 ); // You must wait 10 seconds before using another healing potion. + } + } + } + else + { + from.SendLocalizedMessage( 1049547 ); // You decide against drinking this potion, as you are already at full health. + } + } + + private static void ReleaseHealLock( object state ) + { + ((Mobile)state).EndAction( typeof( BaseHealPotion ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/GreaterHealPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/GreaterHealPotion.cs new file mode 100644 index 000000000..a713f41de --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/GreaterHealPotion.cs @@ -0,0 +1,35 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterHealPotion : BaseHealPotion + { + public override int MinHeal { get { return (Core.AOS ? 20 : 9); } } + public override int MaxHeal { get { return (Core.AOS ? 25 : 30); } } + public override double Delay{ get{ return 10.0; } } + + [Constructable] + public GreaterHealPotion() : base( PotionEffect.HealGreater ) + { + } + + public GreaterHealPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/HealPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/HealPotion.cs new file mode 100644 index 000000000..8fc981259 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/HealPotion.cs @@ -0,0 +1,35 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HealPotion : BaseHealPotion + { + public override int MinHeal { get { return (Core.AOS ? 13 : 6); } } + public override int MaxHeal { get { return (Core.AOS ? 16 : 20); } } + public override double Delay{ get{ return (Core.AOS ? 8.0 : 10.0); } } + + [Constructable] + public HealPotion() : base( PotionEffect.Heal ) + { + } + + public HealPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/LesserHealPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/LesserHealPotion.cs new file mode 100644 index 000000000..c5aa13655 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Heal Potions/LesserHealPotion.cs @@ -0,0 +1,35 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LesserHealPotion : BaseHealPotion + { + public override int MinHeal { get { return (Core.AOS ? 6 : 3); } } + public override int MaxHeal { get { return (Core.AOS ? 8 : 10); } } + public override double Delay{ get{ return (Core.AOS ? 3.0 : 10.0); } } + + [Constructable] + public LesserHealPotion() : base( PotionEffect.HealLesser ) + { + } + + public LesserHealPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs b/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs new file mode 100644 index 000000000..71654329a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/NightSight.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Items +{ + public class NightSightPotion : BasePotion + { + [Constructable] + public NightSightPotion() : base( 0xF06, PotionEffect.Nightsight ) + { + } + + public NightSightPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Drink( Mobile from ) + { + if ( from.BeginAction( typeof( LightCycle ) ) ) + { + new LightCycle.NightSightTimer( from ).Start(); + from.LightLevel = LightCycle.DungeonLevel / 2; + + from.FixedParticles( 0x376A, 9, 32, 5007, EffectLayer.Waist ); + from.PlaySound( 0x1E3 ); + + BasePotion.PlayDrinkEffect( from ); + + this.Consume(); + } + else + { + from.SendMessage( "You already have nightsight." ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs new file mode 100644 index 000000000..95a3ea96b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/BasePoisonPotion.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BasePoisonPotion : BasePotion + { + public abstract Poison Poison{ get; } + + public abstract double MinPoisoningSkill{ get; } + public abstract double MaxPoisoningSkill{ get; } + + public BasePoisonPotion( PotionEffect effect ) : base( 0xF0A, effect ) + { + } + + public BasePoisonPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public void DoPoison( Mobile from ) + { + from.ApplyPoison( from, Poison ); + } + + public override void Drink( Mobile from ) + { + DoPoison( from ); + + BasePotion.PlayDrinkEffect( from ); + + this.Consume(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/DeadlyPoisonPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/DeadlyPoisonPotion.cs new file mode 100644 index 000000000..a06bfe542 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/DeadlyPoisonPotion.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DeadlyPoisonPotion : BasePoisonPotion + { + public override Poison Poison{ get{ return Poison.Deadly; } } + + public override double MinPoisoningSkill{ get{ return 95.0; } } + public override double MaxPoisoningSkill{ get{ return 100.0; } } + + [Constructable] + public DeadlyPoisonPotion() : base( PotionEffect.PoisonDeadly ) + { + } + + public DeadlyPoisonPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/GreaterPoisonPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/GreaterPoisonPotion.cs new file mode 100644 index 000000000..f667406aa --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/GreaterPoisonPotion.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterPoisonPotion : BasePoisonPotion + { + public override Poison Poison{ get{ return Poison.Greater; } } + + public override double MinPoisoningSkill{ get{ return 60.0; } } + public override double MaxPoisoningSkill{ get{ return 100.0; } } + + [Constructable] + public GreaterPoisonPotion() : base( PotionEffect.PoisonGreater ) + { + } + + public GreaterPoisonPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/LesserPoisonPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/LesserPoisonPotion.cs new file mode 100644 index 000000000..384a01eb3 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/LesserPoisonPotion.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LesserPoisonPotion : BasePoisonPotion + { + public override Poison Poison{ get{ return Poison.Lesser; } } + + public override double MinPoisoningSkill{ get{ return 0.0; } } + public override double MaxPoisoningSkill{ get{ return 60.0; } } + + [Constructable] + public LesserPoisonPotion() : base( PotionEffect.PoisonLesser ) + { + } + + public LesserPoisonPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/PoisonPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/PoisonPotion.cs new file mode 100644 index 000000000..41197f40c --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Poison Potions/PoisonPotion.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Items +{ + public class PoisonPotion : BasePoisonPotion + { + public override Poison Poison{ get{ return Poison.Regular; } } + + public override double MinPoisoningSkill{ get{ return 30.0; } } + public override double MaxPoisoningSkill{ get{ return 70.0; } } + + [Constructable] + public PoisonPotion() : base( PotionEffect.Poison ) + { + } + + public PoisonPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs new file mode 100644 index 000000000..aed40b466 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/BaseRefreshPotion.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseRefreshPotion : BasePotion + { + public abstract double Refresh{ get; } + + public BaseRefreshPotion( PotionEffect effect ) : base( 0xF0B, effect ) + { + } + + public BaseRefreshPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Drink( Mobile from ) + { + if ( from.Stam < from.StamMax ) + { + from.Stam += Scale( from, (int)(Refresh * from.StamMax) ); + + BasePotion.PlayDrinkEffect( from ); + + this.Consume(); + } + else + { + from.SendMessage( "You decide against drinking this potion, as you are already at full stamina." ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/RefreshPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/RefreshPotion.cs new file mode 100644 index 000000000..468fa11c0 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/RefreshPotion.cs @@ -0,0 +1,33 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RefreshPotion : BaseRefreshPotion + { + public override double Refresh{ get{ return 0.25; } } + + [Constructable] + public RefreshPotion() : base( PotionEffect.Refresh ) + { + } + + public RefreshPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/TotalRefreshPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/TotalRefreshPotion.cs new file mode 100644 index 000000000..daf0009ef --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Refresh Potions/TotalRefreshPotion.cs @@ -0,0 +1,33 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TotalRefreshPotion : BaseRefreshPotion + { + public override double Refresh{ get{ return 1.0; } } + + [Constructable] + public TotalRefreshPotion() : base( PotionEffect.RefreshTotal ) + { + } + + public TotalRefreshPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs new file mode 100644 index 000000000..a4292ede7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/BaseStrengthPotion.cs @@ -0,0 +1,57 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseStrengthPotion : BasePotion + { + public abstract int StrOffset{ get; } + public abstract TimeSpan Duration{ get; } + + public BaseStrengthPotion( PotionEffect effect ) : base( 0xF09, effect ) + { + } + + public BaseStrengthPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool DoStrength( Mobile from ) + { + // TODO: Verify scaled; is it offset, duration, or both? + if ( Spells.SpellHelper.AddStatOffset( from, StatType.Str, Scale( from, StrOffset ), Duration ) ) + { + from.FixedEffect( 0x375A, 10, 15 ); + from.PlaySound( 0x1E7 ); + return true; + } + + from.SendLocalizedMessage( 502173 ); // You are already under a similar effect. + return false; + } + + public override void Drink( Mobile from ) + { + if ( DoStrength( from ) ) + { + BasePotion.PlayDrinkEffect( from ); + + this.Consume(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/GreaterStrengthPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/GreaterStrengthPotion.cs new file mode 100644 index 000000000..819ae5073 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/GreaterStrengthPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GreaterStrengthPotion : BaseStrengthPotion + { + public override int StrOffset{ get{ return 20; } } + public override TimeSpan Duration{ get{ return TimeSpan.FromMinutes( 2.0 ); } } + + [Constructable] + public GreaterStrengthPotion() : base( PotionEffect.StrengthGreater ) + { + } + + public GreaterStrengthPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/StrengthPotion.cs b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/StrengthPotion.cs new file mode 100644 index 000000000..5bc1aad80 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Potions/Strength Potions/StrengthPotion.cs @@ -0,0 +1,34 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StrengthPotion : BaseStrengthPotion + { + public override int StrOffset{ get{ return 10; } } + public override TimeSpan Duration{ get{ return TimeSpan.FromMinutes( 2.0 ); } } + + [Constructable] + public StrengthPotion() : base( PotionEffect.Strength ) + { + } + + public StrengthPotion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Runebook.cs b/Scripts/Items/Skill Items/Magical/Runebook.cs new file mode 100644 index 000000000..9dbd4adc4 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Runebook.cs @@ -0,0 +1,523 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Multis; +using Server.Engines.Craft; +using Server.ContextMenus; + +namespace Server.Items +{ + public class Runebook : Item, ISecurable, ICraftable + { + public static readonly TimeSpan UseDelay = TimeSpan.FromSeconds( 7.0 ); + + private ArrayList m_Entries; + private string m_Description; + private int m_CurCharges, m_MaxCharges; + private int m_DefaultIndex; + private SecureLevel m_Level; + private Mobile m_Crafter; + + private DateTime m_NextUse; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NextUse + { + get{ return m_NextUse; } + set{ m_NextUse = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Description + { + get + { + return m_Description; + } + set + { + m_Description = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int CurCharges + { + get + { + return m_CurCharges; + } + set + { + m_CurCharges = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxCharges + { + get + { + return m_MaxCharges; + } + set + { + m_MaxCharges = value; + } + } + + public override int LabelNumber{ get{ return 1041267; } } // runebook + + [Constructable] + public Runebook( int maxCharges ) : base( Core.AOS ? 0x22C5 : 0xEFA ) + { + Weight = (Core.SE ? 1.0 : 3.0); + LootType = LootType.Blessed; + Hue = 0x461; + + Layer = (Core.AOS ? Layer.Invalid : Layer.OneHanded); + + m_Entries = new ArrayList(); + + m_MaxCharges = maxCharges; + + m_DefaultIndex = -1; + + m_Level = SecureLevel.CoOwners; + } + + [Constructable] + public Runebook() : this( Core.SE ? 12 : 6 ) + { + } + + public ArrayList Entries + { + get + { + return m_Entries; + } + } + + public RunebookEntry Default + { + get + { + if ( m_DefaultIndex >= 0 && m_DefaultIndex < m_Entries.Count ) + return (RunebookEntry)m_Entries[m_DefaultIndex]; + + return null; + } + set + { + if ( value == null ) + m_DefaultIndex = -1; + else + m_DefaultIndex = m_Entries.IndexOf( value ); + } + } + + public Runebook( Serial serial ) : base( serial ) + { + } + + public override bool AllowEquipedCast( Mobile from ) + { + return true; + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); + + writer.Write( m_Crafter ); + + writer.Write( (int) m_Level ); + + writer.Write( m_Entries.Count ); + + for ( int i = 0; i < m_Entries.Count; ++i ) + ((RunebookEntry)m_Entries[i]).Serialize( writer ); + + writer.Write( m_Description ); + writer.Write( m_CurCharges ); + writer.Write( m_MaxCharges ); + writer.Write( m_DefaultIndex ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + LootType = LootType.Blessed; + + if( Core.SE && Weight == 3.0 ) + Weight = 1.0; + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_Crafter = reader.ReadMobile(); + goto case 1; + } + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + goto case 0; + } + case 0: + { + int count = reader.ReadInt(); + + m_Entries = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + m_Entries.Add( new RunebookEntry( reader ) ); + + m_Description = reader.ReadString(); + m_CurCharges = reader.ReadInt(); + m_MaxCharges = reader.ReadInt(); + m_DefaultIndex = reader.ReadInt(); + + break; + } + } + } + + public void DropRune( Mobile from, RunebookEntry e, int index ) + { + if ( m_DefaultIndex == index ) + m_DefaultIndex = -1; + + m_Entries.RemoveAt( index ); + + RecallRune rune = new RecallRune(); + + rune.Target = e.Location; + rune.TargetMap = e.Map; + rune.Description = e.Description; + rune.House = e.House; + rune.Marked = true; + + from.AddToBackpack( rune ); + + from.SendLocalizedMessage( 502421 ); // You have removed the rune. + } + + public bool IsOpen( Mobile toCheck ) + { + NetState ns = toCheck.NetState; + + if ( ns == null ) + return false; + + List gumps = ns.Gumps; + + for ( int i = 0; i < gumps.Count; ++i ) + { + if ( gumps[i] is RunebookGump ) + { + RunebookGump gump = (RunebookGump)gumps[i]; + + if ( gump.Book == this ) + return true; + } + } + + return false; + } + + public override bool DisplayLootType{ get{ return Core.AOS; } } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Crafter != null ) + list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + if ( m_Description != null && m_Description.Length > 0 ) + list.Add( m_Description ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Description != null && m_Description.Length > 0 ) + LabelTo( from, m_Description ); + + base.OnSingleClick( from ); + + if ( m_Crafter != null ) + LabelTo( from, 1050043, m_Crafter.Name ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 1 ) ) + { + if ( DateTime.Now < NextUse ) + { + from.SendLocalizedMessage( 502406 ); // This book needs time to recharge. + return; + } + + from.CloseGump( typeof( RunebookGump ) ); + from.SendGump( new RunebookGump( from, this ) ); + } + } + + public virtual void OnTravel() + { + NextUse = DateTime.Now + UseDelay; + } + + /* + public override Item Dupe( int amount ) + { + Runebook book = new Runebook(); + + book.m_Level = m_Level; + book.m_CurCharges = m_CurCharges; + book.m_MaxCharges = m_MaxCharges; + book.m_DefaultIndex = m_DefaultIndex; + book.m_Description = m_Description; + book.m_Level = m_Level; + book.LootType = this.LootType; + + for( int i = 0; i < m_Entries.Count; i++ ) + { + RunebookEntry entry = m_Entries[i] as RunebookEntry; + + book.m_Entries.Add( new RunebookEntry( entry.Location, entry.Map, entry.Description, entry.House ) ); + } + + return base.Dupe( book, amount ); + }*/ + + public override void OnAfterDuped( Item newItem ) + { + Runebook book = newItem as Runebook; + + if ( book == null ) + return; + + book.m_Entries = new ArrayList(); //Currently, when duping, it just copies over the ref over the already made blank ArrayList + + for ( int i = 0; i < m_Entries.Count; i++ ) + { + RunebookEntry entry = m_Entries[i] as RunebookEntry; + + book.m_Entries.Add( new RunebookEntry( entry.Location, entry.Map, entry.Description, entry.House ) ); + } + } + + public bool CheckAccess( Mobile m ) + { + if ( !IsLockedDown || m.AccessLevel >= AccessLevel.GameMaster ) + return true; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsAosRules && (house.Public ? house.IsBanned( m ) : !house.HasAccess( m )) ) + return false; + + return ( house != null && house.HasSecureAccess( m, m_Level ) ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is RecallRune ) + { + if ( !CheckAccess( from ) ) + { + from.SendLocalizedMessage( 502413 ); // That cannot be done while the book is locked down. + } + else if ( IsOpen( from ) ) + { + from.SendLocalizedMessage( 1005571 ); // You cannot place objects in the book while viewing the contents. + } + else if ( m_Entries.Count < 16 ) + { + RecallRune rune = (RecallRune)dropped; + + if ( rune.Marked && rune.TargetMap != null ) + { + m_Entries.Add( new RunebookEntry( rune.Target, rune.TargetMap, rune.Description, rune.House ) ); + + dropped.Delete(); + + from.Send( new PlaySound( 0x42, GetWorldLocation() ) ); + + string desc = rune.Description; + + if ( desc == null || (desc = desc.Trim()).Length == 0 ) + desc = "(indescript)"; + + from.SendMessage( desc ); + + return true; + } + else + { + from.SendLocalizedMessage( 502409 ); // This rune does not have a marked location. + } + } + else + { + from.SendLocalizedMessage( 502401 ); // This runebook is full. + } + } + else if ( dropped is RecallScroll ) + { + if ( m_CurCharges < m_MaxCharges ) + { + from.Send( new PlaySound( 0x249, GetWorldLocation() ) ); + + int amount = dropped.Amount; + + if ( amount > (m_MaxCharges - m_CurCharges) ) + { + dropped.Consume( m_MaxCharges - m_CurCharges ); + m_CurCharges = m_MaxCharges; + } + else + { + m_CurCharges += amount; + dropped.Delete(); + + return true; + } + } + else + { + from.SendLocalizedMessage( 502410 ); // This book already has the maximum amount of charges. + } + } + + return false; + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + int charges = 5 + quality + (int)(from.Skills[SkillName.Inscribe].Value / 30); + + if ( charges > 10 ) + charges = 10; + + MaxCharges = (Core.SE ? charges * 2 : charges); + + if ( makersMark ) + Crafter = from; + + return quality; + } + + #endregion + } + + public class RunebookEntry + { + private Point3D m_Location; + private Map m_Map; + private string m_Description; + private BaseHouse m_House; + + public Point3D Location + { + get{ return m_Location; } + } + + public Map Map + { + get{ return m_Map; } + } + + public string Description + { + get{ return m_Description; } + } + + public BaseHouse House + { + get{ return m_House; } + } + + public RunebookEntry( Point3D loc, Map map, string desc, BaseHouse house ) + { + m_Location = loc; + m_Map = map; + m_Description = desc; + m_House = house; + } + + public RunebookEntry( GenericReader reader ) + { + int version = reader.ReadByte(); + + switch ( version ) + { + case 1: + { + m_House = reader.ReadItem() as BaseHouse; + goto case 0; + } + case 0: + { + m_Location = reader.ReadPoint3D(); + m_Map = reader.ReadMap(); + m_Description = reader.ReadString(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + if ( m_House != null && !m_House.Deleted ) + { + writer.Write( (byte) 1 ); // version + + writer.Write( m_House ); + } + else + { + writer.Write( (byte) 0 ); // version + } + + writer.Write( m_Location ); + writer.Write( m_Map ); + writer.Write( m_Description ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EarthquakeScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EarthquakeScroll.cs new file mode 100644 index 000000000..4f2621ddb --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EarthquakeScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class EarthquakeScroll : SpellScroll + { + [Constructable] + public EarthquakeScroll() : this( 1 ) + { + } + + [Constructable] + public EarthquakeScroll( int amount ) : base( 56, 0x1F65, amount ) + { + } + + public EarthquakeScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EnergyVortexScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EnergyVortexScroll.cs new file mode 100644 index 000000000..085d948cd --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/EnergyVortexScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class EnergyVortexScroll : SpellScroll + { + [Constructable] + public EnergyVortexScroll() : this( 1 ) + { + } + + [Constructable] + public EnergyVortexScroll( int amount ) : base( 57, 0x1F66, amount ) + { + } + + public EnergyVortexScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/ResurrectionScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/ResurrectionScroll.cs new file mode 100644 index 000000000..ef89ec790 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/ResurrectionScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ResurrectionScroll : SpellScroll + { + [Constructable] + public ResurrectionScroll() : this( 1 ) + { + } + + [Constructable] + public ResurrectionScroll( int amount ) : base( 58, 0x1F67, amount ) + { + } + + public ResurrectionScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonAirElementalScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonAirElementalScroll.cs new file mode 100644 index 000000000..99a48302f --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonAirElementalScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonAirElementalScroll : SpellScroll + { + [Constructable] + public SummonAirElementalScroll() : this( 1 ) + { + } + + [Constructable] + public SummonAirElementalScroll( int amount ) : base( 59, 0x1F68, amount ) + { + } + + public SummonAirElementalScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonDaemonScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonDaemonScroll.cs new file mode 100644 index 000000000..d23f183fe --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonDaemonScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonDaemonScroll : SpellScroll + { + [Constructable] + public SummonDaemonScroll() : this( 1 ) + { + } + + [Constructable] + public SummonDaemonScroll( int amount ) : base( 60, 0x1F69, amount ) + { + } + + public SummonDaemonScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonEarthElementalScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonEarthElementalScroll.cs new file mode 100644 index 000000000..a0487572b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonEarthElementalScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonEarthElementalScroll : SpellScroll + { + [Constructable] + public SummonEarthElementalScroll() : this( 1 ) + { + } + + [Constructable] + public SummonEarthElementalScroll( int amount ) : base( 61, 0x1F6A, amount ) + { + } + + public SummonEarthElementalScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonFireElementalScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonFireElementalScroll.cs new file mode 100644 index 000000000..197e16850 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonFireElementalScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonFireElementalScroll : SpellScroll + { + [Constructable] + public SummonFireElementalScroll() : this( 1 ) + { + } + + [Constructable] + public SummonFireElementalScroll( int amount ) : base( 62, 0x1F6B, amount ) + { + } + + public SummonFireElementalScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonWaterElementalScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonWaterElementalScroll.cs new file mode 100644 index 000000000..a096d8955 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Eighth Circle/SummonWaterElementalScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonWaterElementalScroll : SpellScroll + { + [Constructable] + public SummonWaterElementalScroll() : this( 1 ) + { + } + + [Constructable] + public SummonWaterElementalScroll( int amount ) : base( 63, 0x1F6C, amount ) + { + } + + public SummonWaterElementalScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/BladeSpiritsScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/BladeSpiritsScroll.cs new file mode 100644 index 000000000..271ef16b8 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/BladeSpiritsScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BladeSpiritsScroll : SpellScroll + { + [Constructable] + public BladeSpiritsScroll() : this( 1 ) + { + } + + [Constructable] + public BladeSpiritsScroll( int amount ) : base( 32, 0x1F4D, amount ) + { + } + + public BladeSpiritsScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/DispelFieldScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/DispelFieldScroll.cs new file mode 100644 index 000000000..1d559f4e5 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/DispelFieldScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class DispelFieldScroll : SpellScroll + { + [Constructable] + public DispelFieldScroll() : this( 1 ) + { + } + + [Constructable] + public DispelFieldScroll( int amount ) : base( 33, 0x1F4E, amount ) + { + } + + public DispelFieldScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/IncognitoScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/IncognitoScroll.cs new file mode 100644 index 000000000..388fc9bd1 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/IncognitoScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class IncognitoScroll : SpellScroll + { + [Constructable] + public IncognitoScroll() : this( 1 ) + { + } + + [Constructable] + public IncognitoScroll( int amount ) : base( 34, 0x1F4F, amount ) + { + } + + public IncognitoScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MagicReflectScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MagicReflectScroll.cs new file mode 100644 index 000000000..9d1068194 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MagicReflectScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MagicReflectScroll : SpellScroll + { + [Constructable] + public MagicReflectScroll() : this( 1 ) + { + } + + [Constructable] + public MagicReflectScroll( int amount ) : base( 35, 0x1F50, amount ) + { + } + + public MagicReflectScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MindBlastScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MindBlastScroll.cs new file mode 100644 index 000000000..7b29fb1ae --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/MindBlastScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MindBlastScroll : SpellScroll + { + [Constructable] + public MindBlastScroll() : this( 1 ) + { + } + + [Constructable] + public MindBlastScroll( int amount ) : base( 36, 0x1F51, amount ) + { + } + + public MindBlastScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/ParalyzeScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/ParalyzeScroll.cs new file mode 100644 index 000000000..a49d5b666 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/ParalyzeScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ParalyzeScroll : SpellScroll + { + [Constructable] + public ParalyzeScroll() : this( 1 ) + { + } + + [Constructable] + public ParalyzeScroll( int amount ) : base( 37, 0x1F52, amount ) + { + } + + public ParalyzeScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/PoisonFieldScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/PoisonFieldScroll.cs new file mode 100644 index 000000000..870cfa4c9 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/PoisonFieldScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PoisonFieldScroll : SpellScroll + { + [Constructable] + public PoisonFieldScroll() : this( 1 ) + { + } + + [Constructable] + public PoisonFieldScroll( int amount ) : base( 38, 0x1F53, amount ) + { + } + + public PoisonFieldScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/SummonCreatureScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/SummonCreatureScroll.cs new file mode 100644 index 000000000..ec2de9993 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fifth Circle/SummonCreatureScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonCreatureScroll : SpellScroll + { + [Constructable] + public SummonCreatureScroll() : this( 1 ) + { + } + + [Constructable] + public SummonCreatureScroll( int amount ) : base( 39, 0x1F54, amount ) + { + } + + public SummonCreatureScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ClumsyScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ClumsyScroll.cs new file mode 100644 index 000000000..6bec2620b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ClumsyScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ClumsyScroll : SpellScroll + { + [Constructable] + public ClumsyScroll() : this( 1 ) + { + } + + [Constructable] + public ClumsyScroll( int amount ) : base( 0, 0x1F2E, amount ) + { + } + + public ClumsyScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/CreateFoodScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/CreateFoodScroll.cs new file mode 100644 index 000000000..b48146ba1 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/CreateFoodScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class CreateFoodScroll : SpellScroll + { + [Constructable] + public CreateFoodScroll() : this( 1 ) + { + } + + [Constructable] + public CreateFoodScroll( int amount ) : base( 1, 0x1F2F, amount ) + { + } + + public CreateFoodScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/FeeblemindScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/FeeblemindScroll.cs new file mode 100644 index 000000000..b4df6a168 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/FeeblemindScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class FeeblemindScroll : SpellScroll + { + [Constructable] + public FeeblemindScroll() : this( 1 ) + { + } + + [Constructable] + public FeeblemindScroll( int amount ) : base( 2, 0x1F30, amount ) + { + } + + public FeeblemindScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/HealScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/HealScroll.cs new file mode 100644 index 000000000..b0ea069fd --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/HealScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class HealScroll : SpellScroll + { + [Constructable] + public HealScroll() : this( 1 ) + { + } + + [Constructable] + public HealScroll( int amount ) : base( 3, 0x1F31, amount ) + { + } + + public HealScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/MagicArrowScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/MagicArrowScroll.cs new file mode 100644 index 000000000..16e20f387 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/MagicArrowScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MagicArrowScroll : SpellScroll + { + [Constructable] + public MagicArrowScroll() : this( 1 ) + { + } + + [Constructable] + public MagicArrowScroll( int amount ) : base( 4, 0x1F32, amount ) + { + } + + public MagicArrowScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/NightSightScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/NightSightScroll.cs new file mode 100644 index 000000000..001dd4f7b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/NightSightScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class NightSightScroll : SpellScroll + { + [Constructable] + public NightSightScroll() : this( 1 ) + { + } + + [Constructable] + public NightSightScroll( int amount ) : base( 5, 0x1F33, amount ) + { + } + + public NightSightScroll( Serial ser ) : base(ser) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ReactiveArmorScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ReactiveArmorScroll.cs new file mode 100644 index 000000000..c04ffed02 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/ReactiveArmorScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ReactiveArmorScroll : SpellScroll + { + [Constructable] + public ReactiveArmorScroll() : this( 1 ) + { + } + + [Constructable] + public ReactiveArmorScroll( int amount ) : base( 6, 0x1F2D, amount ) + { + } + + public ReactiveArmorScroll( Serial ser ) : base(ser) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/WeakenScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/WeakenScroll.cs new file mode 100644 index 000000000..269669602 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/First Circle/WeakenScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class WeakenScroll : SpellScroll + { + [Constructable] + public WeakenScroll() : this( 1 ) + { + } + + [Constructable] + public WeakenScroll( int amount ) : base( 7, 0x1F34, amount ) + { + } + + public WeakenScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchProtectionScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchProtectionScroll.cs new file mode 100644 index 000000000..2e7c0b6b7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchProtectionScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ArchProtectionScroll : SpellScroll + { + [Constructable] + public ArchProtectionScroll() : this( 1 ) + { + } + + [Constructable] + public ArchProtectionScroll( int amount ) : base( 25, 0x1F46, amount ) + { + } + + public ArchProtectionScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchcureScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchcureScroll.cs new file mode 100644 index 000000000..6d75b5020 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ArchcureScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ArchCureScroll : SpellScroll + { + [Constructable] + public ArchCureScroll() : this( 1 ) + { + } + + [Constructable] + public ArchCureScroll( int amount ) : base( 24, 0x1F45, amount ) + { + } + + public ArchCureScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/CurseScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/CurseScroll.cs new file mode 100644 index 000000000..51f0da4e8 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/CurseScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class CurseScroll : SpellScroll + { + [Constructable] + public CurseScroll() : this( 1 ) + { + } + + [Constructable] + public CurseScroll( int amount ) : base( 26, 0x1F47, amount ) + { + } + + public CurseScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/FireFieldScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/FireFieldScroll.cs new file mode 100644 index 000000000..6fe281e76 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/FireFieldScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class FireFieldScroll : SpellScroll + { + [Constructable] + public FireFieldScroll() : this( 1 ) + { + } + + [Constructable] + public FireFieldScroll( int amount ) : base( 27, 0x1F48, amount ) + { + } + + public FireFieldScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/GreaterHealScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/GreaterHealScroll.cs new file mode 100644 index 000000000..23aa37d5d --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/GreaterHealScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class GreaterHealScroll : SpellScroll + { + [Constructable] + public GreaterHealScroll() : this( 1 ) + { + } + + [Constructable] + public GreaterHealScroll( int amount ) : base( 28, 0x1F49, amount ) + { + } + + public GreaterHealScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/LightningScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/LightningScroll.cs new file mode 100644 index 000000000..c3dd752d1 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/LightningScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class LightningScroll : SpellScroll + { + [Constructable] + public LightningScroll() : this( 1 ) + { + } + + [Constructable] + public LightningScroll( int amount ) : base( 29, 0x1F4A, amount ) + { + } + + public LightningScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ManaDrainScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ManaDrainScroll.cs new file mode 100644 index 000000000..b21a588ab --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/ManaDrainScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ManaDrainScroll : SpellScroll + { + [Constructable] + public ManaDrainScroll() : this( 1 ) + { + } + + [Constructable] + public ManaDrainScroll( int amount ) : base( 30, 0x1F4B, amount ) + { + } + + public ManaDrainScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/RecallScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/RecallScroll.cs new file mode 100644 index 000000000..90ed07266 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Fourth Circle/RecallScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class RecallScroll : SpellScroll + { + [Constructable] + public RecallScroll() : this( 1 ) + { + } + + [Constructable] + public RecallScroll( int amount ) : base( 31, 0x1F4C, amount ) + { + } + + public RecallScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/AnimateDeadScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/AnimateDeadScroll.cs new file mode 100644 index 000000000..a76c49019 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/AnimateDeadScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class AnimateDeadScroll : SpellScroll + { + [Constructable] + public AnimateDeadScroll() : this( 1 ) + { + } + + [Constructable] + public AnimateDeadScroll( int amount ) : base( 100, 0x2260, amount ) + { + } + + public AnimateDeadScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/BloodOathScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/BloodOathScroll.cs new file mode 100644 index 000000000..1334d9fb5 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/BloodOathScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BloodOathScroll : SpellScroll + { + [Constructable] + public BloodOathScroll() : this( 1 ) + { + } + + [Constructable] + public BloodOathScroll( int amount ) : base( 101, 0x2261, amount ) + { + } + + public BloodOathScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CorpseSkinScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CorpseSkinScroll.cs new file mode 100644 index 000000000..66b8ea85f --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CorpseSkinScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class CorpseSkinScroll : SpellScroll + { + [Constructable] + public CorpseSkinScroll() : this( 1 ) + { + } + + [Constructable] + public CorpseSkinScroll( int amount ) : base( 102, 0x2262, amount ) + { + } + + public CorpseSkinScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CurseWeaponScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CurseWeaponScroll.cs new file mode 100644 index 000000000..f356d8a5a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/CurseWeaponScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class CurseWeaponScroll : SpellScroll + { + [Constructable] + public CurseWeaponScroll() : this( 1 ) + { + } + + [Constructable] + public CurseWeaponScroll( int amount ) : base( 103, 0x2263, amount ) + { + } + + public CurseWeaponScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/EvilOmenScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/EvilOmenScroll.cs new file mode 100644 index 000000000..bf73f2d7e --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/EvilOmenScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class EvilOmenScroll : SpellScroll + { + [Constructable] + public EvilOmenScroll() : this( 1 ) + { + } + + [Constructable] + public EvilOmenScroll( int amount ) : base( 104, 0x2264, amount ) + { + } + + public EvilOmenScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/ExorcismScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/ExorcismScroll.cs new file mode 100644 index 000000000..0e9d2739c --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/ExorcismScroll.cs @@ -0,0 +1,37 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ExorcismScroll : SpellScroll + { + [Constructable] + public ExorcismScroll() : this( 1 ) + { + } + + [Constructable] + public ExorcismScroll( int amount ) : base( 116, 0x2270, amount ) + { + } + + public ExorcismScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/HorrificBeastScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/HorrificBeastScroll.cs new file mode 100644 index 000000000..91f094421 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/HorrificBeastScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class HorrificBeastScroll : SpellScroll + { + [Constructable] + public HorrificBeastScroll() : this( 1 ) + { + } + + [Constructable] + public HorrificBeastScroll( int amount ) : base( 105, 0x2265, amount ) + { + } + + public HorrificBeastScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/LichFormScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/LichFormScroll.cs new file mode 100644 index 000000000..b5aa49a5f --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/LichFormScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class LichFormScroll : SpellScroll + { + [Constructable] + public LichFormScroll() : this( 1 ) + { + } + + [Constructable] + public LichFormScroll( int amount ) : base( 106, 0x2266, amount ) + { + } + + public LichFormScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/MindRotScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/MindRotScroll.cs new file mode 100644 index 000000000..9b38f38e7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/MindRotScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MindRotScroll : SpellScroll + { + [Constructable] + public MindRotScroll() : this( 1 ) + { + } + + [Constructable] + public MindRotScroll( int amount ) : base( 107, 0x2267, amount ) + { + } + + public MindRotScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PainSpikeScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PainSpikeScroll.cs new file mode 100644 index 000000000..bb522881a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PainSpikeScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PainSpikeScroll : SpellScroll + { + [Constructable] + public PainSpikeScroll() : this( 1 ) + { + } + + [Constructable] + public PainSpikeScroll( int amount ) : base( 108, 0x2268, amount ) + { + } + + public PainSpikeScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PoisonStrikeScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PoisonStrikeScroll.cs new file mode 100644 index 000000000..1997a714d --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/PoisonStrikeScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PoisonStrikeScroll : SpellScroll + { + [Constructable] + public PoisonStrikeScroll() : this( 1 ) + { + } + + [Constructable] + public PoisonStrikeScroll( int amount ) : base( 109, 0x2269, amount ) + { + } + + public PoisonStrikeScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/StrangleScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/StrangleScroll.cs new file mode 100644 index 000000000..8a0f08414 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/StrangleScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class StrangleScroll : SpellScroll + { + [Constructable] + public StrangleScroll() : this( 1 ) + { + } + + [Constructable] + public StrangleScroll( int amount ) : base( 110, 0x226A, amount ) + { + } + + public StrangleScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/SummonFamiliarScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/SummonFamiliarScroll.cs new file mode 100644 index 000000000..f50fc858f --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/SummonFamiliarScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SummonFamiliarScroll : SpellScroll + { + [Constructable] + public SummonFamiliarScroll() : this( 1 ) + { + } + + [Constructable] + public SummonFamiliarScroll( int amount ) : base( 111, 0x226B, amount ) + { + } + + public SummonFamiliarScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VampiricEmbraceScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VampiricEmbraceScroll.cs new file mode 100644 index 000000000..e6a31874f --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VampiricEmbraceScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class VampiricEmbraceScroll : SpellScroll + { + [Constructable] + public VampiricEmbraceScroll() : this( 1 ) + { + } + + [Constructable] + public VampiricEmbraceScroll( int amount ) : base( 112, 0x226C, amount ) + { + } + + public VampiricEmbraceScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VengefulSpiritScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VengefulSpiritScroll.cs new file mode 100644 index 000000000..0c8a2aede --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/VengefulSpiritScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class VengefulSpiritScroll : SpellScroll + { + [Constructable] + public VengefulSpiritScroll() : this( 1 ) + { + } + + [Constructable] + public VengefulSpiritScroll( int amount ) : base( 113, 0x226D, amount ) + { + } + + public VengefulSpiritScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WitherScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WitherScroll.cs new file mode 100644 index 000000000..c7731e1f4 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WitherScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class WitherScroll : SpellScroll + { + [Constructable] + public WitherScroll() : this( 1 ) + { + } + + [Constructable] + public WitherScroll( int amount ) : base( 114, 0x226E, amount ) + { + } + + public WitherScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WraithFormScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WraithFormScroll.cs new file mode 100644 index 000000000..72d3333b0 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Necromancy/WraithFormScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class WraithFormScroll : SpellScroll + { + [Constructable] + public WraithFormScroll() : this( 1 ) + { + } + + [Constructable] + public WraithFormScroll( int amount ) : base( 115, 0x226F, amount ) + { + } + + public WraithFormScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/AgilityScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/AgilityScroll.cs new file mode 100644 index 000000000..e8e5b9363 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/AgilityScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class AgilityScroll : SpellScroll + { + [Constructable] + public AgilityScroll() : this( 1 ) + { + } + + [Constructable] + public AgilityScroll( int amount ) : base( 8, 0x1F35, amount ) + { + } + + public AgilityScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CunningScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CunningScroll.cs new file mode 100644 index 000000000..6ce075eb6 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CunningScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class CunningScroll : SpellScroll + { + [Constructable] + public CunningScroll() : this( 1 ) + { + } + + [Constructable] + public CunningScroll( int amount ) : base( 9, 0x1F36, amount ) + { + } + + public CunningScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CureScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CureScroll.cs new file mode 100644 index 000000000..ec21d3c1b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/CureScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class CureScroll : SpellScroll + { + [Constructable] + public CureScroll() : this( 1 ) + { + } + + [Constructable] + public CureScroll( int amount ) : base( 10, 0x1F37, amount ) + { + } + + public CureScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/HarmScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/HarmScroll.cs new file mode 100644 index 000000000..b11dac30b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/HarmScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class HarmScroll : SpellScroll + { + [Constructable] + public HarmScroll() : this( 1 ) + { + } + + [Constructable] + public HarmScroll( int amount ) : base( 11, 0x1F38, amount ) + { + } + + public HarmScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicTrapScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicTrapScroll.cs new file mode 100644 index 000000000..0881e649a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicTrapScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MagicTrapScroll : SpellScroll + { + [Constructable] + public MagicTrapScroll() : this( 1 ) + { + } + + [Constructable] + public MagicTrapScroll( int amount ) : base( 12, 0x1F39, amount ) + { + } + + public MagicTrapScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicUnTrapScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicUnTrapScroll.cs new file mode 100644 index 000000000..6c5e4c0c7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/MagicUnTrapScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MagicUnTrapScroll : SpellScroll + { + [Constructable] + public MagicUnTrapScroll() : this( 1 ) + { + } + + [Constructable] + public MagicUnTrapScroll( int amount ) : base( 13, 0x1F3A, amount ) + { + } + + public MagicUnTrapScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/ProtectionScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/ProtectionScroll.cs new file mode 100644 index 000000000..2afbde579 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/ProtectionScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ProtectionScroll : SpellScroll + { + [Constructable] + public ProtectionScroll() : this( 1 ) + { + } + + [Constructable] + public ProtectionScroll( int amount ) : base( 14, 0x1F3B, amount ) + { + } + + public ProtectionScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/StrengthScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/StrengthScroll.cs new file mode 100644 index 000000000..11ca302c8 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Second Circle/StrengthScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class StrengthScroll : SpellScroll + { + [Constructable] + public StrengthScroll() : this( 1 ) + { + } + + [Constructable] + public StrengthScroll( int amount ) : base( 15, 0x1F3C, amount ) + { + } + + public StrengthScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ChainLightningScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ChainLightningScroll.cs new file mode 100644 index 000000000..4995a2606 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ChainLightningScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ChainLightningScroll : SpellScroll + { + [Constructable] + public ChainLightningScroll() : this( 1 ) + { + } + + [Constructable] + public ChainLightningScroll( int amount ) : base( 48, 0x1F5D, amount ) + { + } + + public ChainLightningScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/EnergyFieldScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/EnergyFieldScroll.cs new file mode 100644 index 000000000..53daca2bb --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/EnergyFieldScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class EnergyFieldScroll : SpellScroll + { + [Constructable] + public EnergyFieldScroll() : this( 1 ) + { + } + + [Constructable] + public EnergyFieldScroll( int amount ) : base( 49, 0x1F5E, amount ) + { + } + + public EnergyFieldScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/FlamestrikeScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/FlamestrikeScroll.cs new file mode 100644 index 000000000..723349348 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/FlamestrikeScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class FlamestrikeScroll : SpellScroll + { + [Constructable] + public FlamestrikeScroll() : this( 1 ) + { + } + + [Constructable] + public FlamestrikeScroll( int amount ) : base( 50, 0x1F5F, amount ) + { + } + + public FlamestrikeScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/GateTravelScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/GateTravelScroll.cs new file mode 100644 index 000000000..f64254f8b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/GateTravelScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class GateTravelScroll : SpellScroll + { + [Constructable] + public GateTravelScroll() : this( 1 ) + { + } + + [Constructable] + public GateTravelScroll( int amount ) : base( 51, 0x1F60, amount ) + { + } + + public GateTravelScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ManaVampireScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ManaVampireScroll.cs new file mode 100644 index 000000000..087ea49c7 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/ManaVampireScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ManaVampireScroll : SpellScroll + { + [Constructable] + public ManaVampireScroll() : this( 1 ) + { + } + + [Constructable] + public ManaVampireScroll( int amount ) : base( 52, 0x1F61, amount ) + { + } + + public ManaVampireScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MassDispelScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MassDispelScroll.cs new file mode 100644 index 000000000..f4c8f2014 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MassDispelScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MassDispelScroll : SpellScroll + { + [Constructable] + public MassDispelScroll() : this( 1 ) + { + } + + [Constructable] + public MassDispelScroll( int amount ) : base( 53, 0x1F62, amount ) + { + } + + public MassDispelScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MeteorStormScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MeteorStormScroll.cs new file mode 100644 index 000000000..cab6482c1 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/MeteorStormScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MeteorSwarmScroll : SpellScroll + { + [Constructable] + public MeteorSwarmScroll() : this( 1 ) + { + } + + [Constructable] + public MeteorSwarmScroll( int amount ) : base( 54, 0x1F63, amount ) + { + } + + public MeteorSwarmScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/PolymorphScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/PolymorphScroll.cs new file mode 100644 index 000000000..69cbefb2c --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Seventh Circle/PolymorphScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PolymorphScroll : SpellScroll + { + [Constructable] + public PolymorphScroll() : this( 1 ) + { + } + + [Constructable] + public PolymorphScroll( int amount ) : base( 55, 0x1F64, amount ) + { + } + + public PolymorphScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/DispelScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/DispelScroll.cs new file mode 100644 index 000000000..c5e6df47f --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/DispelScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class DispelScroll : SpellScroll + { + [Constructable] + public DispelScroll() : this( 1 ) + { + } + + [Constructable] + public DispelScroll( int amount ) : base( 40, 0x1F55, amount ) + { + } + + public DispelScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/EnergyBoltScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/EnergyBoltScroll.cs new file mode 100644 index 000000000..1c973f58e --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/EnergyBoltScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class EnergyBoltScroll : SpellScroll + { + [Constructable] + public EnergyBoltScroll() : this( 1 ) + { + } + + [Constructable] + public EnergyBoltScroll( int amount ) : base( 41, 0x1F56, amount ) + { + } + + public EnergyBoltScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ExplosionScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ExplosionScroll.cs new file mode 100644 index 000000000..4671ecdd9 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ExplosionScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ExplosionScroll : SpellScroll + { + [Constructable] + public ExplosionScroll() : this( 1 ) + { + } + + [Constructable] + public ExplosionScroll( int amount ) : base( 42, 0x1F57, amount ) + { + } + + public ExplosionScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/InvisibilityScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/InvisibilityScroll.cs new file mode 100644 index 000000000..ad0f0eb8a --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/InvisibilityScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class InvisibilityScroll : SpellScroll + { + [Constructable] + public InvisibilityScroll() : this( 1 ) + { + } + + [Constructable] + public InvisibilityScroll( int amount ) : base( 43, 0x1F58, amount ) + { + } + + public InvisibilityScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MarkScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MarkScroll.cs new file mode 100644 index 000000000..3bc2c0483 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MarkScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MarkScroll : SpellScroll + { + [Constructable] + public MarkScroll() : this( 1 ) + { + } + + [Constructable] + public MarkScroll( int amount ) : base( 44, 0x1F59, amount ) + { + } + + public MarkScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MassCurseScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MassCurseScroll.cs new file mode 100644 index 000000000..4a9d8c01d --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/MassCurseScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MassCurseScroll : SpellScroll + { + [Constructable] + public MassCurseScroll() : this( 1 ) + { + } + + [Constructable] + public MassCurseScroll( int amount ) : base( 45, 0x1F5A, amount ) + { + } + + public MassCurseScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ParalyzeFieldSpell.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ParalyzeFieldSpell.cs new file mode 100644 index 000000000..f8a5f3d02 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/ParalyzeFieldSpell.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ParalyzeFieldScroll : SpellScroll + { + [Constructable] + public ParalyzeFieldScroll() : this( 1 ) + { + } + + [Constructable] + public ParalyzeFieldScroll( int amount ) : base( 46, 0x1F5B, amount ) + { + } + + public ParalyzeFieldScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/RevealScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/RevealScroll.cs new file mode 100644 index 000000000..c0523bf49 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Sixth Circle/RevealScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class RevealScroll : SpellScroll + { + [Constructable] + public RevealScroll() : this( 1 ) + { + } + + [Constructable] + public RevealScroll( int amount ) : base( 47, 0x1F5C, amount ) + { + } + + public RevealScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/SpellScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/SpellScroll.cs new file mode 100644 index 000000000..a3567b1ee --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/SpellScroll.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Spells; +using Server.ContextMenus; + +namespace Server.Items +{ + public class SpellScroll : Item + { + private int m_SpellID; + + public int SpellID + { + get + { + return m_SpellID; + } + } + + public SpellScroll( Serial serial ) : base( serial ) + { + } + + [Constructable] + public SpellScroll( int spellID, int itemID ) : this( spellID, itemID, 1 ) + { + } + + [Constructable] + public SpellScroll( int spellID, int itemID, int amount ) : base( itemID ) + { + Stackable = true; + Weight = 1.0; + Amount = amount; + + m_SpellID = spellID; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_SpellID ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_SpellID = reader.ReadInt(); + + break; + } + } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive && this.Movable ) + list.Add( new ContextMenus.AddToSpellbookEntry() ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !Multis.DesignContext.Check( from ) ) + return; // They are customizing + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + return; + } + + Spell spell = SpellRegistry.NewSpell( m_SpellID, from, this ); + + if ( spell != null ) + spell.Cast(); + else + from.SendLocalizedMessage( 502345 ); // This spell has been temporarily disabled. + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/BlessScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/BlessScroll.cs new file mode 100644 index 000000000..abafc9769 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/BlessScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BlessScroll : SpellScroll + { + [Constructable] + public BlessScroll() : this( 1 ) + { + } + + [Constructable] + public BlessScroll( int amount ) : base( 16, 0x1F3D, amount ) + { + } + + public BlessScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/FireballScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/FireballScroll.cs new file mode 100644 index 000000000..f056314c4 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/FireballScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class FireballScroll : SpellScroll + { + [Constructable] + public FireballScroll() : this( 1 ) + { + } + + [Constructable] + public FireballScroll( int amount ) : base( 17, 0x1F3E, amount ) + { + } + + public FireballScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/MagicLockScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/MagicLockScroll.cs new file mode 100644 index 000000000..78d67791b --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/MagicLockScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class MagicLockScroll : SpellScroll + { + [Constructable] + public MagicLockScroll() : this( 1 ) + { + } + + [Constructable] + public MagicLockScroll( int amount ) : base( 18, 0x1F3F, amount ) + { + } + + public MagicLockScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/PoisonScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/PoisonScroll.cs new file mode 100644 index 000000000..35bea889e --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/PoisonScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class PoisonScroll : SpellScroll + { + [Constructable] + public PoisonScroll() : this( 1 ) + { + } + + [Constructable] + public PoisonScroll( int amount ) : base( 19, 0x1F40, amount ) + { + } + + public PoisonScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TelekinisisScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TelekinisisScroll.cs new file mode 100644 index 000000000..1e017b08e --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TelekinisisScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class TelekinisisScroll : SpellScroll + { + [Constructable] + public TelekinisisScroll() : this( 1 ) + { + } + + [Constructable] + public TelekinisisScroll( int amount ) : base( 20, 0x1F41, amount ) + { + } + + public TelekinisisScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TeleportScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TeleportScroll.cs new file mode 100644 index 000000000..576067fe0 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/TeleportScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class TeleportScroll : SpellScroll + { + [Constructable] + public TeleportScroll() : this( 1 ) + { + } + + [Constructable] + public TeleportScroll( int amount ) : base( 21, 0x1F42, amount ) + { + } + + public TeleportScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/UnlockScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/UnlockScroll.cs new file mode 100644 index 000000000..d723ebab9 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/UnlockScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class UnlockScroll : SpellScroll + { + [Constructable] + public UnlockScroll() : this( 1 ) + { + } + + [Constructable] + public UnlockScroll( int amount ) : base( 22, 0x1F43, amount ) + { + } + + public UnlockScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/WallOfStoneScroll.cs b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/WallOfStoneScroll.cs new file mode 100644 index 000000000..23f564f84 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Scrolls/Third Circle/WallOfStoneScroll.cs @@ -0,0 +1,39 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class WallOfStoneScroll : SpellScroll + { + [Constructable] + public WallOfStoneScroll() : this( 1 ) + { + } + + [Constructable] + public WallOfStoneScroll( int amount ) : base( 23, 0x1F44, amount ) + { + } + + public WallOfStoneScroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/Spellbook.cs b/Scripts/Items/Skill Items/Magical/Spellbook.cs new file mode 100644 index 000000000..2e8fa6089 --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/Spellbook.cs @@ -0,0 +1,817 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Network; +using Server.Spells; +using Server.Commands; +using Server.Engines.Craft; + +namespace Server.Items +{ + public enum SpellbookType + { + Invalid = -1, + Regular, + Necromancer, + Paladin, + Ninja, + Samurai, + Arcanist + } + + public class Spellbook : Item, ICraftable, ISlayer + { + public static void Initialize() + { + EventSink.OpenSpellbookRequest += new OpenSpellbookRequestEventHandler( EventSink_OpenSpellbookRequest ); + EventSink.CastSpellRequest += new CastSpellRequestEventHandler( EventSink_CastSpellRequest ); + + CommandSystem.Register( "AllSpells", AccessLevel.GameMaster, new CommandEventHandler( AllSpells_OnCommand ) ); + } + + [Usage( "AllSpells" )] + [Description( "Completely fills a targeted spellbook with scrolls." )] + private static void AllSpells_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( AllSpells_OnTarget ) ); + e.Mobile.SendMessage( "Target the spellbook to fill." ); + } + + private static void AllSpells_OnTarget( Mobile from, object obj ) + { + if ( obj is Spellbook ) + { + Spellbook book = (Spellbook)obj; + + if ( book.BookCount == 64 ) + book.Content = ulong.MaxValue; + else + book.Content = (1ul << book.BookCount) - 1; + + from.SendMessage( "The spellbook has been filled." ); + + CommandLogging.WriteLine( from, "{0} {1} filling spellbook {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( book ) ); + } + else + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( AllSpells_OnTarget ) ); + from.SendMessage( "That is not a spellbook. Try again." ); + } + } + + private static void EventSink_OpenSpellbookRequest( OpenSpellbookRequestEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !Multis.DesignContext.Check( from ) ) + return; // They are customizing + + SpellbookType type; + + switch ( e.Type ) + { + default: + case 1: type = SpellbookType.Regular; break; + case 2: type = SpellbookType.Necromancer; break; + case 3: type = SpellbookType.Paladin; break; + case 4: type = SpellbookType.Ninja; break; + case 5: type = SpellbookType.Samurai; break; + case 6: type = SpellbookType.Arcanist; break; + } + + Spellbook book = Spellbook.Find( from, -1, type ); + + if ( book != null ) + book.DisplayTo( from ); + } + + private static void EventSink_CastSpellRequest( CastSpellRequestEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !Multis.DesignContext.Check( from ) ) + return; // They are customizing + + Spellbook book = e.Spellbook as Spellbook; + int spellID = e.SpellID; + + if ( book == null || !book.HasSpell( spellID ) ) + book = Find( from, spellID ); + + if ( book != null && book.HasSpell( spellID ) ) + { + SpecialMove move = SpellRegistry.GetSpecialMove( spellID ); + + if ( move != null ) + { + SpecialMove.SetCurrentMove( from, move ); + } + else + { + Spell spell = SpellRegistry.NewSpell( spellID, from, null ); + + if ( spell != null ) + spell.Cast(); + else + from.SendLocalizedMessage( 502345 ); // This spell has been temporarily disabled. + } + } + else + { + from.SendLocalizedMessage( 500015 ); // You do not have that spell! + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static SpellbookType GetTypeForSpell( int spellID ) + { + if ( spellID >= 0 && spellID < 64 ) + return SpellbookType.Regular; + else if ( spellID >= 100 && spellID < 117 ) + return SpellbookType.Necromancer; + else if ( spellID >= 200 && spellID < 210 ) + return SpellbookType.Paladin; + else if( spellID >= 400 && spellID < 406 ) + return SpellbookType.Samurai; + else if( spellID >= 500 && spellID < 508 ) + return SpellbookType.Ninja; + else if ( spellID >= 600 && spellID < 617 ) + return SpellbookType.Arcanist; + + return SpellbookType.Invalid; + } + + public static Spellbook FindRegular( Mobile from ) + { + return Find( from, -1, SpellbookType.Regular ); + } + + public static Spellbook FindNecromancer( Mobile from ) + { + return Find( from, -1, SpellbookType.Necromancer ); + } + + public static Spellbook FindPaladin( Mobile from ) + { + return Find( from, -1, SpellbookType.Paladin ); + } + + public static Spellbook FindSamurai( Mobile from ) + { + return Find( from, -1, SpellbookType.Samurai ); + } + + public static Spellbook FindNinja( Mobile from ) + { + return Find( from, -1, SpellbookType.Ninja ); + } + + public static Spellbook FindArcanist( Mobile from ) + { + return Find( from, -1, SpellbookType.Arcanist ); + } + + public static Spellbook Find( Mobile from, int spellID ) + { + return Find( from, spellID, GetTypeForSpell( spellID ) ); + } + + public static Spellbook Find( Mobile from, int spellID, SpellbookType type ) + { + if ( from == null ) + return null; + + ArrayList list = (ArrayList)m_Table[from]; + + if ( from.Deleted ) + { + m_Table.Remove( from ); + return null; + } + + bool searchAgain = false; + + if ( list == null ) + m_Table[from] = list = FindAllSpellbooks( from ); + else + searchAgain = true; + + Spellbook book = FindSpellbookInList( list, from, spellID, type ); + + if ( book == null && searchAgain ) + { + m_Table[from] = list = FindAllSpellbooks( from ); + + book = FindSpellbookInList( list, from, spellID, type ); + } + + return book; + } + + public static Spellbook FindSpellbookInList( ArrayList list, Mobile from, int spellID, SpellbookType type ) + { + Container pack = from.Backpack; + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i >= list.Count ) + continue; + + Spellbook book = (Spellbook)list[i]; + + if ( !book.Deleted && (book.Parent == from || (pack != null && book.Parent == pack)) && ValidateSpellbook( book, spellID, type ) ) + return book; + + list.Remove( i ); + } + + return null; + } + + public static ArrayList FindAllSpellbooks( Mobile from ) + { + ArrayList list = new ArrayList(); + + Item item = from.FindItemOnLayer( Layer.OneHanded ); + + if ( item is Spellbook ) + list.Add( item ); + + Container pack = from.Backpack; + + if ( pack == null ) + return list; + + for ( int i = 0; i < pack.Items.Count; ++i ) + { + item = pack.Items[i]; + + if ( item is Spellbook ) + list.Add( item ); + } + + return list; + } + + public static Spellbook FindEquippedSpellbook( Mobile from ) + { + return (from.FindItemOnLayer( Layer.OneHanded ) as Spellbook); + } + + public static bool ValidateSpellbook( Spellbook book, int spellID, SpellbookType type ) + { + return ( book.SpellbookType == type && ( spellID == -1 || book.HasSpell( spellID ) ) ); + } + + private AosAttributes m_AosAttributes; + private AosSkillBonuses m_AosSkillBonuses; + + [CommandProperty( AccessLevel.GameMaster )] + public AosAttributes Attributes + { + get{ return m_AosAttributes; } + set{} + } + + [CommandProperty( AccessLevel.GameMaster )] + public AosSkillBonuses SkillBonuses + { + get{ return m_AosSkillBonuses; } + set{} + } + + public virtual SpellbookType SpellbookType{ get{ return SpellbookType.Regular; } } + public virtual int BookOffset{ get{ return 0; } } + public virtual int BookCount{ get{ return 64; } } + + private ulong m_Content; + private int m_Count; + + 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 override bool CanEquip( Mobile from ) + { + if ( !Ethics.Ethic.CheckEquip( from, this ) ) + return false; + + return base.CanEquip( from ); + } + + public override bool AllowEquipedCast( Mobile from ) + { + return true; + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is SpellScroll && dropped.Amount == 1 ) + { + SpellScroll scroll = (SpellScroll)dropped; + + SpellbookType type = GetTypeForSpell( scroll.SpellID ); + + if ( type != this.SpellbookType ) + { + return false; + } + else if ( HasSpell( scroll.SpellID ) ) + { + from.SendLocalizedMessage( 500179 ); // That spell is already present in that spellbook. + return false; + } + else + { + int val = scroll.SpellID - BookOffset; + + if ( val >= 0 && val < BookCount ) + { + m_Content |= (ulong)1 << val; + ++m_Count; + + InvalidateProperties(); + + scroll.Delete(); + + from.Send( new PlaySound( 0x249, GetWorldLocation() ) ); + return true; + } + + return false; + } + } + else + { + return false; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ulong Content + { + get + { + return m_Content; + } + set + { + if ( m_Content != value ) + { + m_Content = value; + + m_Count = 0; + + while ( value > 0 ) + { + m_Count += (int)(value & 0x1); + value >>= 1; + } + + InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int SpellCount + { + get + { + return m_Count; + } + } + + [Constructable] + public Spellbook() : this( (ulong)0 ) + { + } + + [Constructable] + public Spellbook( ulong content ) : this( content, 0xEFA ) + { + } + + public Spellbook( ulong content, int itemID ) : base( itemID ) + { + m_AosAttributes = new AosAttributes( this ); + m_AosSkillBonuses = new AosSkillBonuses( this ); + + Weight = 3.0; + Layer = Layer.OneHanded; + LootType = LootType.Blessed; + + Content = content; + } + + 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 bool HasSpell( int spellID ) + { + spellID -= BookOffset; + + return ( spellID >= 0 && spellID < BookCount && (m_Content & ((ulong)1 << spellID)) != 0 ); + } + + public Spellbook( Serial serial ) : base( serial ) + { + } + + private static readonly ClientVersion Version_400a = new ClientVersion( "4.0.0a" ); + + public void DisplayTo( Mobile to ) + { + // The client must know about the spellbook or it will crash! + + if ( Parent == null ) + { + to.Send( this.WorldPacket ); + } + else if ( Parent is Item ) + { + // What will happen if the client doesn't know about our parent? + to.Send( new ContainerContentUpdate( this ) ); + } + else if ( Parent is Mobile ) + { + // What will happen if the client doesn't know about our parent? + to.Send( new EquipUpdate( this ) ); + } + + to.Send( new DisplaySpellbook( this ) ); + + if ( Core.AOS && to.NetState != null && to.NetState.Version != null && to.NetState.Version >= Version_400a ) + to.Send( new NewSpellbookContent( this, ItemID, BookOffset + 1, m_Content ) ); + else + to.Send( new SpellbookContent( m_Count, BookOffset + 1, m_Content, this ) ); + } + + private Mobile m_Crafter; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + public override bool DisplayLootType{ get{ return Core.AOS; } } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Crafter != null ) + list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + m_AosSkillBonuses.GetProperties( list ); + + 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 ); + } + + int prop; + + 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~% + + list.Add( 1042886, m_Count.ToString() ); // ~1_NUMBERS_OF_SPELLS~ Spells + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( m_Crafter != null ) + this.LabelTo( from, 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + this.LabelTo( from, 1042886, m_Count.ToString() ); + } + + public override void OnDoubleClick( Mobile from ) + { + Container pack = from.Backpack; + + if ( Parent == from || ( pack != null && Parent == pack ) ) + DisplayTo( from ); + else + from.SendLocalizedMessage( 500207 ); // The spellbook must be in your backpack (and not in a container within) to open. + } + + + private SlayerName m_Slayer; + private SlayerName m_Slayer2; + //Currently though there are no dual slayer spellbooks, OSI has a habit of putting dual slayer stuff in later + + [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(); } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 3 ); // version + writer.Write( m_Crafter ); + + writer.Write( (int)m_Slayer ); + writer.Write( (int)m_Slayer2 ); + + m_AosAttributes.Serialize( writer ); + m_AosSkillBonuses.Serialize( writer ); + + writer.Write( m_Content ); + writer.Write( m_Count ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + { + m_Crafter = reader.ReadMobile(); + goto case 2; + } + case 2: + { + m_Slayer = (SlayerName)reader.ReadInt(); + m_Slayer2 = (SlayerName)reader.ReadInt(); + goto case 1; + } + case 1: + { + m_AosAttributes = new AosAttributes( this, reader ); + m_AosSkillBonuses = new AosSkillBonuses( this, reader ); + + goto case 0; + } + case 0: + { + m_Content = reader.ReadULong(); + m_Count = reader.ReadInt(); + + break; + } + } + + if ( m_AosAttributes == null ) + m_AosAttributes = new AosAttributes( this ); + + if ( m_AosSkillBonuses == null ) + m_AosSkillBonuses = new AosSkillBonuses( this ); + + 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(); + } + + private static int[] m_LegendPropertyCounts = new int[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 properties : 21/52 : 40% + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 property : 15/52 : 29% + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 2 properties : 10/52 : 19% + 3, 3, 3, 3, 3, 3 // 3 properties : 6/52 : 12% + + }; + + private static int[] m_ElderPropertyCounts = new int[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,// 0 properties : 15/34 : 44% + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 property : 10/34 : 29% + 2, 2, 2, 2, 2, 2, // 2 properties : 6/34 : 18% + 3, 3, 3 // 3 properties : 3/34 : 9% + }; + + private static int[] m_GrandPropertyCounts = new int[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 properties : 10/20 : 50% + 1, 1, 1, 1, 1, 1, // 1 property : 6/20 : 30% + 2, 2, 2, // 2 properties : 3/20 : 15% + 3 // 3 properties : 1/20 : 5% + }; + + private static int[] m_MasterPropertyCounts = new int[] + { + 0, 0, 0, 0, 0, 0, // 0 properties : 6/10 : 60% + 1, 1, 1, // 1 property : 3/10 : 30% + 2 // 2 properties : 1/10 : 10% + }; + + private static int[] m_AdeptPropertyCounts = new int[] + { + 0, 0, 0, // 0 properties : 3/4 : 75% + 1 // 1 property : 1/4 : 25% + }; + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + int magery = from.Skills.Magery.BaseFixedPoint; + + if ( magery >= 800 ) + { + int[] propertyCounts; + int minIntensity; + int maxIntensity; + + if ( magery >= 1000 ) + { + if( magery >= 1200 ) + propertyCounts = m_LegendPropertyCounts; + else if( magery >= 1100 ) + propertyCounts = m_ElderPropertyCounts; + else + propertyCounts = m_GrandPropertyCounts; + + minIntensity = 55; + maxIntensity = 75; + } + else if ( magery >= 900 ) + { + propertyCounts = m_MasterPropertyCounts; + minIntensity = 25; + maxIntensity = 45; + } + else + { + propertyCounts = m_AdeptPropertyCounts; + minIntensity = 0; + maxIntensity = 15; + } + + int propertyCount = propertyCounts[Utility.Random( propertyCounts.Length )]; + + BaseRunicTool.ApplyAttributesTo( this, true, 0, propertyCount, minIntensity, maxIntensity ); + } + + if ( makersMark ) + Crafter = from; + + return quality; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Magical/SpellweavingBook.cs b/Scripts/Items/Skill Items/Magical/SpellweavingBook.cs new file mode 100644 index 000000000..d68a5d05d --- /dev/null +++ b/Scripts/Items/Skill Items/Magical/SpellweavingBook.cs @@ -0,0 +1,44 @@ +using System; +using Server.Network; +using Server.Spells; + +namespace Server.Items +{ + public class SpellweavingBook : Spellbook + { + public override SpellbookType SpellbookType{ get{ return SpellbookType.Arcanist; } } + public override int BookOffset{ get{ return 600; } } + public override int BookCount{ get{ return 16; } } + + [Constructable] + public SpellweavingBook() : this( (ulong)0 ) + { + } + + [Constructable] + public SpellweavingBook( ulong content ) : base( content, 0x2D50 ) + { + Hue = 0x8A2; + + Layer = Layer.OneHanded; + } + + public SpellweavingBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Misc/Bandage.cs b/Scripts/Items/Skill Items/Misc/Bandage.cs new file mode 100644 index 000000000..c79d5aae8 --- /dev/null +++ b/Scripts/Items/Skill Items/Misc/Bandage.cs @@ -0,0 +1,481 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.Gumps; +using System.Collections.Generic; + +namespace Server.Items +{ + public class Bandage : Item, IDyable + { + public override double DefaultWeight + { + get { return 0.1; } + } + + [Constructable] + public Bandage() : this( 1 ) + { + } + + [Constructable] + public Bandage( int amount ) : base( 0xE21 ) + { + Stackable = true; + Amount = amount; + } + + public Bandage( Serial serial ) : base( serial ) + { + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), Core.AOS ? 2 : 1 ) ) + { + from.RevealingAction(); + + from.SendLocalizedMessage( 500948 ); // Who will you use the bandages on? + + from.Target = new InternalTarget( this ); + } + else + { + from.SendLocalizedMessage( 500295 ); // You are too far away to do that. + } + } + private class InternalTarget : Target + { + private Bandage m_Bandage; + + public InternalTarget( Bandage bandage ) : base( 1, false, TargetFlags.Beneficial ) + { + m_Bandage = bandage; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Bandage.Deleted ) + return; + + if ( targeted is Mobile ) + { + if ( from.InRange( m_Bandage.GetWorldLocation(), Core.AOS ? 2 : 1 ) ) + { + if ( BandageContext.BeginHeal( from, (Mobile) targeted ) != null ) + { + m_Bandage.Consume(); + } + } + else + { + from.SendLocalizedMessage( 500295 ); // You are too far away to do that. + } + } + else + { + from.SendLocalizedMessage( 500970 ); // Bandages can not be used on that. + } + } + } + } + + public class BandageContext + { + private Mobile m_Healer; + private Mobile m_Patient; + private int m_Slips; + private Timer m_Timer; + + public Mobile Healer{ get{ return m_Healer; } } + public Mobile Patient{ get{ return m_Patient; } } + public int Slips{ get{ return m_Slips; } set{ m_Slips = value; } } + public Timer Timer{ get{ return m_Timer; } } + + public void Slip() + { + m_Healer.SendLocalizedMessage( 500961 ); // Your fingers slip! + ++m_Slips; + } + + public BandageContext( Mobile healer, Mobile patient, TimeSpan delay ) + { + m_Healer = healer; + m_Patient = patient; + + m_Timer = new InternalTimer( this, delay ); + m_Timer.Start(); + } + + public void StopHeal() + { + m_Table.Remove( m_Healer ); + + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + } + + private static Hashtable m_Table = new Hashtable(); + + public static BandageContext GetContext( Mobile healer ) + { + return (BandageContext)m_Table[healer]; + } + + public static SkillName GetPrimarySkill( Mobile m ) + { + if ( !m.Player && (m.Body.IsMonster || m.Body.IsAnimal) ) + return SkillName.Veterinary; + else + return SkillName.Healing; + } + + public static SkillName GetSecondarySkill( Mobile m ) + { + if ( !m.Player && (m.Body.IsMonster || m.Body.IsAnimal) ) + return SkillName.AnimalLore; + else + return SkillName.Anatomy; + } + + public void EndHeal() + { + StopHeal(); + + int healerNumber = -1, patientNumber = -1; + bool playSound = true; + bool checkSkills = false; + + SkillName primarySkill = GetPrimarySkill( m_Patient ); + SkillName secondarySkill = GetSecondarySkill( m_Patient ); + + BaseCreature petPatient = m_Patient as BaseCreature; + + if ( !m_Healer.Alive ) + { + healerNumber = 500962; // You were unable to finish your work before you died. + patientNumber = -1; + playSound = false; + } + else if ( !m_Healer.InRange( m_Patient, Core.AOS ? 2 : 1 ) ) + { + healerNumber = 500963; // You did not stay close enough to heal your target. + patientNumber = -1; + playSound = false; + } + else if ( !m_Patient.Alive || (petPatient != null && petPatient.IsDeadPet) ) + { + double healing = m_Healer.Skills[primarySkill].Value; + double anatomy = m_Healer.Skills[secondarySkill].Value; + double chance = ((healing - 68.0) / 50.0) - (m_Slips * 0.02); + + if (( (checkSkills = (healing >= 80.0 && anatomy >= 80.0)) && chance > Utility.RandomDouble() ) + || ( Core.SE && petPatient is Factions.FactionWarHorse && petPatient.ControlMaster == m_Healer) ) //TODO: Dbl check doesn't check for faction of the horse here? + { + if ( m_Patient.Map == null || !m_Patient.Map.CanFit( m_Patient.Location, 16, false, false ) ) + { + healerNumber = 501042; // Target can not be resurrected at that location. + patientNumber = 502391; // Thou can not be resurrected there! + } + else if ( m_Patient.Region != null && m_Patient.Region.IsPartOf( "Khaldun" ) ) + { + healerNumber = 1010395; // The veil of death in this area is too strong and resists thy efforts to restore life. + patientNumber = -1; + } + else + { + healerNumber = 500965; // You are able to resurrect your patient. + patientNumber = -1; + + m_Patient.PlaySound( 0x214 ); + m_Patient.FixedEffect( 0x376A, 10, 16 ); + + if ( petPatient != null && petPatient.IsDeadPet ) + { + Mobile master = petPatient.ControlMaster; + + if ( master != null && master.InRange( petPatient, 3 ) ) + { + healerNumber = 503255; // You are able to resurrect the creature. + + master.CloseGump( typeof( PetResurrectGump ) ); + master.SendGump( new PetResurrectGump( m_Healer, petPatient ) ); + } + else + { + bool found = false; + + List friends = petPatient.Friends; + + for ( int i = 0; friends != null && i < friends.Count; ++i ) + { + Mobile friend = friends[i]; + + if ( friend.InRange( petPatient, 3 ) ) + { + healerNumber = 503255; // You are able to resurrect the creature. + + friend.CloseGump( typeof( PetResurrectGump ) ); + friend.SendGump( new PetResurrectGump( m_Healer, petPatient ) ); + + found = true; + break; + } + } + + if ( !found ) + healerNumber = 1049670; // The pet's owner must be nearby to attempt resurrection. + } + } + else + { + m_Patient.CloseGump( typeof( ResurrectGump ) ); + m_Patient.SendGump( new ResurrectGump( m_Patient, m_Healer ) ); + } + } + } + else + { + if ( petPatient != null && petPatient.IsDeadPet ) + healerNumber = 503256; // You fail to resurrect the creature. + else + healerNumber = 500966; // You are unable to resurrect your patient. + + patientNumber = -1; + } + } + else if ( m_Patient.Poisoned ) + { + m_Healer.SendLocalizedMessage( 500969 ); // You finish applying the bandages. + + double healing = m_Healer.Skills[primarySkill].Value; + double anatomy = m_Healer.Skills[secondarySkill].Value; + double chance = ((healing - 30.0) / 50.0) - (m_Patient.Poison.Level * 0.1) - (m_Slips * 0.02); + + if ( (checkSkills = (healing >= 60.0 && anatomy >= 60.0)) && chance > Utility.RandomDouble() ) + { + if ( m_Patient.CurePoison( m_Healer ) ) + { + healerNumber = (m_Healer == m_Patient) ? -1 : 1010058; // You have cured the target of all poisons. + patientNumber = 1010059; // You have been cured of all poisons. + } + else + { + healerNumber = -1; + patientNumber = -1; + } + } + else + { + healerNumber = 1010060; // You have failed to cure your target! + patientNumber = -1; + } + } + else if ( BleedAttack.IsBleeding( m_Patient ) ) + { + healerNumber = -1; + patientNumber = 1060167; // The bleeding wounds have healed, you are no longer bleeding! + + BleedAttack.EndBleed( m_Patient, true ); + } + else if ( MortalStrike.IsWounded( m_Patient ) ) + { + healerNumber = ( m_Healer == m_Patient ? 1005000 : 1010398 ); + patientNumber = -1; + playSound = false; + } + else if ( m_Patient.Hits == m_Patient.HitsMax ) + { + healerNumber = 500967; // You heal what little damage your patient had. + patientNumber = -1; + } + else + { + checkSkills = true; + patientNumber = -1; + + double healing = m_Healer.Skills[primarySkill].Value; + double anatomy = m_Healer.Skills[secondarySkill].Value; + double chance = ((healing + 10.0) / 100.0) - (m_Slips * 0.02); + + if ( chance > Utility.RandomDouble() ) + { + healerNumber = 500969; // You finish applying the bandages. + + double min, max; + + if ( Core.AOS ) + { + min = (anatomy / 8.0) + (healing / 5.0) + 4.0; + max = (anatomy / 6.0) + (healing / 2.5) + 4.0; + } + else + { + min = (anatomy / 5.0) + (healing / 5.0) + 3.0; + max = (anatomy / 5.0) + (healing / 2.0) + 10.0; + } + + double toHeal = min + (Utility.RandomDouble() * (max - min)); + + if ( m_Patient.Body.IsMonster || m_Patient.Body.IsAnimal ) + toHeal += m_Patient.HitsMax / 100; + + if ( Core.AOS ) + toHeal -= toHeal * m_Slips * 0.35; // TODO: Verify algorithm + else + toHeal -= m_Slips * 4; + + if ( toHeal < 1 ) + { + toHeal = 1; + healerNumber = 500968; // You apply the bandages, but they barely help. + } + + m_Patient.Heal( (int) toHeal, false ); + } + else + { + healerNumber = 500968; // You apply the bandages, but they barely help. + playSound = false; + } + } + + if ( healerNumber != -1 ) + m_Healer.SendLocalizedMessage( healerNumber ); + + if ( patientNumber != -1 ) + m_Patient.SendLocalizedMessage( patientNumber ); + + if ( playSound ) + m_Patient.PlaySound( 0x57 ); + + if ( checkSkills ) + { + m_Healer.CheckSkill( secondarySkill, 0.0, 120.0 ); + m_Healer.CheckSkill( primarySkill, 0.0, 120.0 ); + } + } + + private class InternalTimer : Timer + { + private BandageContext m_Context; + + public InternalTimer( BandageContext context, TimeSpan delay ) : base( delay ) + { + m_Context = context; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + m_Context.EndHeal(); + } + } + + public static BandageContext BeginHeal( Mobile healer, Mobile patient ) + { + bool isDeadPet = ( patient is BaseCreature && ((BaseCreature)patient).IsDeadPet ); + + if ( patient is Golem ) + { + healer.SendLocalizedMessage( 500970 ); // Bandages cannot be used on that. + } + else if ( patient is BaseCreature && ((BaseCreature)patient).IsAnimatedDead ) + { + healer.SendLocalizedMessage( 500951 ); // You cannot heal that. + } + else if ( !patient.Poisoned && patient.Hits == patient.HitsMax && !BleedAttack.IsBleeding( patient ) && !isDeadPet ) + { + healer.SendLocalizedMessage( 500955 ); // That being is not damaged! + } + else if ( !patient.Alive && (patient.Map == null || !patient.Map.CanFit( patient.Location, 16, false, false )) ) + { + healer.SendLocalizedMessage( 501042 ); // Target cannot be resurrected at that location. + } + else if ( healer.CanBeBeneficial( patient, true, true ) ) + { + healer.DoBeneficial( patient ); + + bool onSelf = ( healer == patient ); + int dex = healer.Dex; + + double seconds; + double resDelay = ( patient.Alive ? 0.0 : 5.0 ); + + if ( onSelf ) + { + if ( Core.AOS ) + seconds = 5.0 + (0.5 * ((double)(120 - dex) / 10)); // TODO: Verify algorithm + else + seconds = 9.4 + (0.6 * ((double)(120 - dex) / 10)); + } + else + { + if ( Core.AOS && GetPrimarySkill( patient ) == SkillName.Veterinary ) + { + //if ( dex >= 40 ) + seconds = 2.0; + //else + // seconds = 3.0; + } + else + { + if ( dex >= 100 ) + seconds = 3.0 + resDelay; + else if ( dex >= 40 ) + seconds = 4.0 + resDelay; + else + seconds = 5.0 + resDelay; + } + } + + BandageContext context = GetContext( healer ); + + if ( context != null ) + context.StopHeal(); + + context = new BandageContext( healer, patient, TimeSpan.FromSeconds( seconds ) ); + + m_Table[healer] = context; + + if ( !onSelf ) + patient.SendLocalizedMessage( 1008078, false, healer.Name ); // : Attempting to heal you. + + healer.SendLocalizedMessage( 500956 ); // You begin applying the bandages. + return context; + } + + return null; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Misc/FireHorn.cs b/Scripts/Items/Skill Items/Misc/FireHorn.cs new file mode 100644 index 000000000..832be703f --- /dev/null +++ b/Scripts/Items/Skill Items/Misc/FireHorn.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Targeting; +using Server.Spells; + +namespace Server.Items +{ + public class FireHorn : Item + { + public override int LabelNumber{ get{ return 1060456; } } // fire horn + + [Constructable] + public FireHorn() : base( 0xFC7 ) + { + Hue = 0x466; + Weight = 1.0; + } + + public FireHorn( Serial serial ) : base( serial ) + { + } + + private bool CheckUse( Mobile from ) + { + if ( !this.IsAccessibleTo( from ) ) + return false; + + if ( from.Map != this.Map || !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return false; + } + + if ( !from.CanBeginAction( typeof( FireHorn ) ) ) + { + from.SendLocalizedMessage( 1049615 ); // You must take a moment to catch your breath. + return false; + } + + int sulfAsh = Core.AOS ? 4 : 15; + if ( from.Backpack == null || from.Backpack.GetAmount( typeof( SulfurousAsh ) ) < sulfAsh ) + { + from.SendLocalizedMessage( 1049617 ); // You do not have enough sulfurous ash. + return false; + } + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( CheckUse( from ) ) + { + from.SendLocalizedMessage( 1049620 ); // Select an area to incinerate. + from.Target = new InternalTarget( this ); + } + } + + public void Use( Mobile from, IPoint3D loc ) + { + if ( !CheckUse( from ) ) + return; + + from.BeginAction( typeof( FireHorn ) ); + Timer.DelayCall( Core.AOS ? TimeSpan.FromSeconds( 6.0 ) : TimeSpan.FromSeconds( 12.0 ), new TimerStateCallback( EndAction ), from ); + + int music = from.Skills[SkillName.Musicianship].Fixed; + + int sucChance = 500 + ( music - 775 ) * 2; + double dSucChance = ((double)sucChance) / 1000.0; + + if ( !from.CheckSkill( SkillName.Musicianship, dSucChance ) ) + { + from.SendLocalizedMessage( 1049618 ); // The horn emits a pathetic squeak. + from.PlaySound( 0x18A ); + return; + } + + int sulfAsh = Core.AOS ? 4 : 15; + from.Backpack.ConsumeUpTo( typeof( SulfurousAsh ), sulfAsh ); + + from.PlaySound( 0x15F ); + Effects.SendPacket( from, from.Map, new HuedEffect( EffectType.Moving, from.Serial, Serial.Zero, 0x36D4, from.Location, loc, 5, 0, false, true, 0, 0 ) ); + + ArrayList targets = new ArrayList(); + bool playerVsPlayer = false; + + IPooledEnumerable eable = from.Map.GetMobilesInRange( new Point3D( loc ), 2 ); + + foreach ( Mobile m in eable ) + { + if ( from != m && SpellHelper.ValidIndirectTarget( from, m ) && from.CanBeHarmful( m, false ) ) + { + if ( Core.AOS && !from.InLOS( m ) ) + continue; + + targets.Add( m ); + + if ( m.Player ) + playerVsPlayer = true; + } + } + + eable.Free(); + + if ( targets.Count > 0 ) + { + int prov = from.Skills[SkillName.Provocation].Fixed; + int disc = from.Skills[SkillName.Discordance].Fixed; + int peace = from.Skills[SkillName.Peacemaking].Fixed; + + int minDamage, maxDamage; + + if ( Core.AOS ) + { + int musicScaled = music + Math.Max( 0, music - 900 ) * 2; + int provScaled = prov + Math.Max( 0, prov - 900 ) * 2; + int discScaled = disc + Math.Max( 0, disc - 900 ) * 2; + int peaceScaled = peace + Math.Max( 0, peace - 900 ) * 2; + + int weightAvg = ( musicScaled + provScaled * 3 + discScaled * 3 + peaceScaled ) / 80; + + int avgDamage; + if ( playerVsPlayer ) + avgDamage = weightAvg / 3; + else + avgDamage = weightAvg / 2; + + minDamage = ( avgDamage * 9 ) / 10; + maxDamage = ( avgDamage * 10 ) / 9; + } + else + { + int total = prov + disc / 5 + peace / 5; + + if ( playerVsPlayer ) + total /= 3; + + maxDamage = ( total * 2 ) / 30; + minDamage = ( maxDamage * 7 ) / 10; + } + + double damage = Utility.RandomMinMax( minDamage, maxDamage ); + + if ( Core.AOS && targets.Count > 1 ) + damage = (damage * 2) / targets.Count; + else if ( !Core.AOS ) + damage /= targets.Count; + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + double toDeal = damage; + + if ( !Core.AOS && m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ) ) + { + toDeal *= 0.5; + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + from.DoHarmful( m ); + SpellHelper.Damage( TimeSpan.Zero, m, from, toDeal, 0, 100, 0, 0, 0 ); + + Effects.SendTargetEffect( m, 0x3709, 10, 30 ); + } + } + + double breakChance = Core.AOS ? 0.01 : 0.16; + if ( Utility.RandomDouble() < breakChance ) + { + from.SendLocalizedMessage( 1049619 ); // The fire horn crumbles in your hands. + this.Delete(); + } + } + + private static void EndAction( object state ) + { + Mobile m = (Mobile) state; + + m.EndAction( typeof( FireHorn ) ); + m.SendLocalizedMessage( 1049621 ); // You catch your breath. + } + + private class InternalTarget : Target + { + private FireHorn m_Horn; + + public InternalTarget( FireHorn horn ) : base( Core.AOS ? 3 : 2, true, TargetFlags.Harmful ) + { + m_Horn = horn; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Horn.Deleted ) + return; + + IPoint3D loc; + if ( targeted is Item ) + loc = ((Item)targeted).GetWorldLocation(); + else + loc = targeted as IPoint3D; + + m_Horn.Use( from, loc ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Misc/RecipeScroll.cs b/Scripts/Items/Skill Items/Misc/RecipeScroll.cs new file mode 100644 index 000000000..abf039edb --- /dev/null +++ b/Scripts/Items/Skill Items/Misc/RecipeScroll.cs @@ -0,0 +1,124 @@ +using System; +using Server; +using Server.Engines.Craft; +using Server.Mobiles; +using Server.Network; + +namespace Server.Items +{ + public class RecipeScroll : Item + { + public override int LabelNumber { get { return 1074560; } } // recipe scroll + + private int m_RecipeID; + + [CommandProperty( AccessLevel.GameMaster )] + public int RecipeID + { + get { return m_RecipeID; } + set { m_RecipeID = value; InvalidateProperties(); } + } + + public Recipe Recipe + { + get + { + if( Recipe.Recipes.ContainsKey( m_RecipeID ) ) + return Recipe.Recipes[m_RecipeID]; + + return null; + } + } + + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + Recipe r = this.Recipe; + + if( r != null ) + list.Add( 1049644, r.TextDefinition.ToString() ); // [~1_stuff~] + } + + public RecipeScroll( Recipe r ) + : this( r.ID ) + { + } + + [Constructable] + public RecipeScroll( int recipeID ) + : base( 0x2831 ) + { + } + + public RecipeScroll( Serial serial ) + : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if( !from.InRange( this.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return; + } + + Recipe r = this.Recipe; + + if( r != null && from is PlayerMobile ) + { + PlayerMobile pm = from as PlayerMobile; + + if( !pm.HasRecipe( r ) ) + { + bool allRequiredSkills = true; + double chance = r.CraftItem.GetSuccessChance( from, null, r.CraftSystem, false, ref allRequiredSkills ); + + if ( allRequiredSkills && chance >= 0.0 ) + { + pm.SendLocalizedMessage( 1073451, r.TextDefinition.ToString() ); // You have learned a new recipe: ~1_RECIPE~ + pm.AcquireRecipe( r ); + this.Delete(); + } + else + { + pm.SendLocalizedMessage( 1044153 ); // You don't have the required skills to attempt this item. + } + } + else + { + pm.SendLocalizedMessage( 1073427 ); // You already know this recipe. + } + + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( (int)m_RecipeID ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + m_RecipeID = reader.ReadInt(); + + break; + } + } + } + } +} diff --git a/Scripts/Items/Skill Items/Misc/RepairDeed.cs b/Scripts/Items/Skill Items/Misc/RepairDeed.cs new file mode 100644 index 000000000..7fce885f4 --- /dev/null +++ b/Scripts/Items/Skill Items/Misc/RepairDeed.cs @@ -0,0 +1,236 @@ +using System; +using Server; +using Server.Targeting; +using Server.Engines.Craft; +using Server.Mobiles; +using Server.Regions; + +namespace Server.Items +{ + public class RepairDeed : Item + { + private class RepairSkillInfo + { + private CraftSystem m_System; + private Type[] m_NearbyTypes; + private TextDefinition m_NotNearbyMessage, m_Name; + + public TextDefinition NotNearbyMessage{ get { return m_NotNearbyMessage; } } + public TextDefinition Name { get { return m_Name; } } + + + public CraftSystem System { get { return m_System; } } + public Type[] NearbyTypes { get { return m_NearbyTypes; } } + + public RepairSkillInfo( CraftSystem system, Type[] nearbyTypes, TextDefinition notNearbyMessage, TextDefinition name ) + { + m_System = system; + m_NearbyTypes = nearbyTypes; + m_NotNearbyMessage = notNearbyMessage; + m_Name = name; + } + + public RepairSkillInfo( CraftSystem system, Type nearbyType, TextDefinition notNearbyMessage, TextDefinition name ) + : this( system, new Type[] { nearbyType }, notNearbyMessage, name ) + { + } + + public static RepairSkillInfo[] Table { get { return m_Table; } } + private static RepairSkillInfo[] m_Table = new RepairSkillInfo[] + { + new RepairSkillInfo( DefBlacksmithy.CraftSystem, typeof( Blacksmith ), 1047013, 1023015 ), + new RepairSkillInfo( DefTailoring.CraftSystem, typeof( Tailor ), 1061132, 1022981 ), + new RepairSkillInfo( DefTinkering.CraftSystem, typeof( Tinker ), 1061166, 1022983 ), + new RepairSkillInfo( DefCarpentry.CraftSystem, typeof( Carpenter ), 1061135, 1060774 ), + new RepairSkillInfo( DefBowFletching.CraftSystem, typeof( Bowyer ), 1061134, 1023005 ) + }; + + public static RepairSkillInfo GetInfo( RepairSkillType type ) + { + int v = (int)type; + + if( v < 0 || v >= m_Table.Length ) + v = 0; + + return m_Table[v]; + } + } + public enum RepairSkillType + { + Smithing, + Tailoring, + Tinkering, + Carpentry, + Fletching + } + + public override bool DisplayLootType { get { return false; } } + + private RepairSkillType m_Skill; + private double m_SkillLevel; + + private Mobile m_Crafter; + + [CommandProperty( AccessLevel.GameMaster )] + public RepairSkillType RepairSkill + { + get { return m_Skill; } + set { m_Skill = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double SkillLevel + { + get { return m_SkillLevel; } + set { m_SkillLevel = Math.Max( Math.Min( value, 120.0 ), 0 ) ; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get { return m_Crafter; } + set { m_Crafter = value; InvalidateProperties(); } + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( 1061133, String.Format( "{0}\t{1}", GetSkillTitle( m_SkillLevel ).ToString(), RepairSkillInfo.GetInfo( m_Skill ).Name ) ); // A repair service contract from ~1_SKILL_TITLE~ ~2_SKILL_NAME~. + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if( m_Crafter != null ) + list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + //On OSI it says it's exceptional. Intentional difference. + } + + + [Constructable] + public RepairDeed() : this( RepairSkillType.Smithing, 100.0, null, true ) + { + } + + [Constructable] + public RepairDeed( RepairSkillType skill, double level ) : this( skill, level, null, true ) + { + } + + [Constructable] + public RepairDeed( RepairSkillType skill, double level, bool normalizeLevel ) : this( skill, level, null, normalizeLevel ) + { + } + + public RepairDeed( RepairSkillType skill, double level, Mobile crafter ) : this( skill, level, crafter, true ) + { + } + + public RepairDeed( RepairSkillType skill, double level, Mobile crafter, bool normalizeLevel ) : base( 0x14F0 ) + { + if( normalizeLevel ) + SkillLevel = (int)(level/10)*10; + else + SkillLevel = level; + + m_Skill = skill; + m_Crafter = crafter; + Hue = 0x1BC; + LootType = LootType.Blessed; + } + + public RepairDeed( Serial serial ) : base( serial ) + { + } + + private static TextDefinition GetSkillTitle( double skillLevel ) + { + int skill = (int)(skillLevel/10); + + if( skill >= 11 ) + return (1062008 + skill-11); + else if( skill >=5 ) + return (1061123 + skill-5); + + switch( skill ) + { + case 4: + return "a Novice"; + case 3: + return "a Neophyte"; + default: + return "a Newbie"; //On OSI, it shouldn't go below 50, but, this is for 'custom' support. + } + } + + public static RepairSkillType GetTypeFor( CraftSystem s ) + { + for( int i = 0; i < RepairSkillInfo.Table.Length; i++ ) + { + if( RepairSkillInfo.Table[i].System == s ) + return (RepairSkillType)i; + } + + return RepairSkillType.Smithing; + } + + public override void OnDoubleClick( Mobile from ) + { + if( Check( from ) ) + Repair.Do( from, RepairSkillInfo.GetInfo( m_Skill ).System, this ); + } + + public bool Check( Mobile from ) + { + if( !IsChildOf( from.Backpack ) ) + from.SendLocalizedMessage( 1047012 ); // The contract must be in your backpack to use it. + else if( !VerifyRegion( from ) ) + TextDefinition.SendMessageTo( from, RepairSkillInfo.GetInfo( m_Skill ).NotNearbyMessage ); + else + return true; + + return false; + } + + public bool VerifyRegion( Mobile m ) + { + //TODO: When the entire region system data is in, convert to that instead of a proximity thing. + + if( !m.Region.IsPartOf( typeof( TownRegion ) ) ) + return false; + + return Server.Factions.Faction.IsNearType( m, RepairSkillInfo.GetInfo( m_Skill ).NearbyTypes, 6 ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( (int)m_Skill ); + writer.Write( m_SkillLevel ); + writer.Write( m_Crafter ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + m_Skill = (RepairSkillType)reader.ReadInt(); + m_SkillLevel = reader.ReadDouble(); + m_Crafter = reader.ReadMobile(); + + break; + } + } + } + } +} diff --git a/Scripts/Items/Skill Items/Musical Instruments/BambooFlute.cs b/Scripts/Items/Skill Items/Musical Instruments/BambooFlute.cs new file mode 100644 index 000000000..8bfd3ef3c --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/BambooFlute.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class BambooFlute : BaseInstrument + { + [Constructable] + public BambooFlute() : base( 0x2805, 0x504, 0x503 ) + { + Weight = 2.0; + } + + public BambooFlute( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/BaseInstrument.cs b/Scripts/Items/Skill Items/Musical Instruments/BaseInstrument.cs new file mode 100644 index 000000000..dd0e5762b --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/BaseInstrument.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Mobiles; +using Server.Targeting; +using Server.Engines.Craft; + +namespace Server.Items +{ + public delegate void InstrumentPickedCallback( Mobile from, BaseInstrument instrument ); + + public enum InstrumentQuality + { + Low, + Regular, + Exceptional + } + + public abstract class BaseInstrument : Item, ICraftable, ISlayer + { + private int m_WellSound, m_BadlySound; + private SlayerName m_Slayer, m_Slayer2; + private InstrumentQuality m_Quality; + private Mobile m_Crafter; + private int m_UsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public int SuccessSound + { + get{ return m_WellSound; } + set{ m_WellSound = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int FailureSound + { + get{ return m_BadlySound; } + set{ m_BadlySound = value; } + } + + [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 InstrumentQuality Quality + { + get{ return m_Quality; } + set{ UnscaleUses(); m_Quality = value; InvalidateProperties(); ScaleUses(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + public virtual int InitMinUses{ get{ return 350; } } + public virtual int InitMaxUses{ get{ return 450; } } + + public virtual TimeSpan ChargeReplenishRate { get { return TimeSpan.FromMinutes( 5.0 ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get{ CheckReplenishUses(); return m_UsesRemaining; } + set{ m_UsesRemaining = value; InvalidateProperties(); } + } + + private DateTime m_LastReplenished; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastReplenished + { + get { return m_LastReplenished; } + set { m_LastReplenished = value; CheckReplenishUses(); } + } + + private bool m_ReplenishesCharges; + [CommandProperty( AccessLevel.GameMaster )] + public bool ReplenishesCharges + { + get { return m_ReplenishesCharges; } + set + { + if( value != m_ReplenishesCharges && value ) + m_LastReplenished = DateTime.Now; + + m_ReplenishesCharges = value; + } + } + + public void CheckReplenishUses() + { + CheckReplenishUses( true ); + } + + public void CheckReplenishUses( bool invalidate ) + { + if( !m_ReplenishesCharges || m_UsesRemaining >= InitMaxUses ) + return; + + if( m_LastReplenished + ChargeReplenishRate < DateTime.Now ) + { + TimeSpan timeDifference = DateTime.Now - m_LastReplenished; + + m_UsesRemaining = Math.Min( m_UsesRemaining + (int)( timeDifference.Ticks / ChargeReplenishRate.Ticks), InitMaxUses ); //How rude of TimeSpan to not allow timespan division. + m_LastReplenished = DateTime.Now; + + if( invalidate ) + InvalidateProperties(); + + } + } + + public void ScaleUses() + { + UsesRemaining = (UsesRemaining * GetUsesScalar()) / 100; + //InvalidateProperties(); + } + + public void UnscaleUses() + { + UsesRemaining = (UsesRemaining * 100) / GetUsesScalar(); + } + + public int GetUsesScalar() + { + if ( m_Quality == InstrumentQuality.Exceptional ) + return 200; + + return 100; + } + + public void ConsumeUse( Mobile from ) + { + // TODO: Confirm what must happen here? + + if ( UsesRemaining > 1 ) + { + --UsesRemaining; + } + else + { + if ( from != null ) + from.SendLocalizedMessage( 502079 ); // The instrument played its last tune. + + Delete(); + } + } + + private static Hashtable m_Instruments = new Hashtable(); + + public static BaseInstrument GetInstrument( Mobile from ) + { + BaseInstrument item = m_Instruments[from] as BaseInstrument; + + if ( item == null ) + return null; + + if ( !item.IsChildOf( from.Backpack ) ) + { + m_Instruments.Remove( from ); + return null; + } + + return item; + } + + public static int GetBardRange( Mobile bard, SkillName skill ) + { + return 8 + (int)(bard.Skills[skill].Value / 15); + } + + public static void PickInstrument( Mobile from, InstrumentPickedCallback callback ) + { + BaseInstrument instrument = GetInstrument( from ); + + if ( instrument != null ) + { + if ( callback != null ) + callback( from, instrument ); + } + else + { + from.SendLocalizedMessage( 500617 ); // What instrument shall you play? + from.BeginTarget( 1, false, TargetFlags.None, new TargetStateCallback( OnPickedInstrument ), callback ); + } + } + + public static void OnPickedInstrument( Mobile from, object targeted, object state ) + { + BaseInstrument instrument = targeted as BaseInstrument; + + if ( instrument == null ) + { + from.SendLocalizedMessage( 500619 ); // That is not a musical instrument. + } + else + { + SetInstrument( from, instrument ); + + InstrumentPickedCallback callback = state as InstrumentPickedCallback; + + if ( callback != null ) + callback( from, instrument ); + } + } + + public static bool IsMageryCreature( BaseCreature bc ) + { + return ( bc != null && bc.AI == AIType.AI_Mage && bc.Skills[SkillName.Magery].Base > 5.0 ); + } + + public static bool IsFireBreathingCreature( BaseCreature bc ) + { + if ( bc == null ) + return false; + + return bc.HasBreath; + } + + public static bool IsPoisonImmune( BaseCreature bc ) + { + return ( bc != null && bc.PoisonImmune != null ); + } + + public static int GetPoisonLevel( BaseCreature bc ) + { + if ( bc == null ) + return 0; + + Poison p = bc.HitPoison; + + if ( p == null ) + return 0; + + return p.Level + 1; + } + + public static double GetBaseDifficulty( Mobile targ ) + { + /* Difficulty TODO: Add another 100 points for each of the following abilities: + - Radiation or Aura Damage (Heat, Cold etc.) + - Summoning Undead + */ + + double val = (targ.Hits * 1.6) + targ.Stam + targ.Mana; + + val += targ.SkillsTotal / 10; + + if ( val > 700 ) + val = 700 + ((val - 700) * (3 / 11)); + + BaseCreature bc = targ as BaseCreature; + + if ( IsMageryCreature( bc ) ) + val += 100; + + if ( IsFireBreathingCreature( bc ) ) + val += 100; + + if ( IsPoisonImmune( bc ) ) + val += 100; + + if ( targ is VampireBat || targ is VampireBatFamiliar ) + val += 100; + + val += GetPoisonLevel( bc ) * 20; + + val /= 10; + + if ( bc != null && bc.IsParagon ) + val += 40.0; + + if ( Core.SE && val > 160.0 ) + val = 160.0; + + return val; + } + + public double GetDifficultyFor( Mobile targ ) + { + double val = GetBaseDifficulty( targ ); + + if ( m_Quality == InstrumentQuality.Exceptional ) + val -= 5.0; // 10% + + if ( m_Slayer != SlayerName.None ) + { + SlayerEntry entry = SlayerGroup.GetEntryByName( m_Slayer ); + + if ( entry != null ) + { + if ( entry.Slays( targ ) ) + val -= 10.0; // 20% + else if ( entry.Group.OppositionSuperSlays( targ ) ) + val += 10.0; // -20% + } + } + + if ( m_Slayer2 != SlayerName.None ) + { + SlayerEntry entry = SlayerGroup.GetEntryByName( m_Slayer2 ); + + if ( entry != null ) + { + if ( entry.Slays( targ ) ) + val -= 10.0; // 20% + else if ( entry.Group.OppositionSuperSlays( targ ) ) + val += 10.0; // -20% + } + } + + return val; + } + + public static void SetInstrument( Mobile from, BaseInstrument item ) + { + m_Instruments[from] = item; + } + + public BaseInstrument( int itemID, int wellSound, int badlySound ) : base( itemID ) + { + m_WellSound = wellSound; + m_BadlySound = badlySound; + UsesRemaining = Utility.RandomMinMax( InitMinUses, InitMaxUses ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + int oldUses = m_UsesRemaining; + CheckReplenishUses( false ); + + base.GetProperties( list ); + + if ( m_Crafter != null ) + list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + if ( m_Quality == InstrumentQuality.Exceptional ) + list.Add( 1060636 ); // exceptional + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + + if( m_ReplenishesCharges ) + list.Add( 1070928 ); // Replenish Charges + + 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 ); + } + + if( m_UsesRemaining != oldUses ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( InvalidateProperties ) ); + } + + public override void OnSingleClick( Mobile from ) + { + ArrayList attrs = new ArrayList(); + + if ( DisplayLootType ) + { + if ( LootType == LootType.Blessed ) + attrs.Add( new EquipInfoAttribute( 1038021 ) ); // blessed + else if ( LootType == LootType.Cursed ) + attrs.Add( new EquipInfoAttribute( 1049643 ) ); // cursed + } + + if ( m_Quality == InstrumentQuality.Exceptional ) + attrs.Add( new EquipInfoAttribute( 1018305 - (int)m_Quality ) ); + + if( m_ReplenishesCharges ) + attrs.Add( new EquipInfoAttribute( 1070928 ) ); // Replenish Charges + + // TODO: Must this support item identification? + 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 ) ); + } + + 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, (EquipInfoAttribute[])attrs.ToArray( typeof( EquipInfoAttribute ) ) ); + + from.Send( new DisplayEquipmentInfo( this, eqInfo ) ); + } + + public BaseInstrument( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 3 ); // version + + writer.Write( m_ReplenishesCharges ); + if( m_ReplenishesCharges ) + writer.Write( m_LastReplenished ); + + + writer.Write( m_Crafter ); + + writer.WriteEncodedInt( (int) m_Quality ); + writer.WriteEncodedInt( (int) m_Slayer ); + writer.WriteEncodedInt( (int) m_Slayer2 ); + + writer.WriteEncodedInt( (int)UsesRemaining ); + + writer.WriteEncodedInt( (int) m_WellSound ); + writer.WriteEncodedInt( (int) m_BadlySound ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + { + m_ReplenishesCharges = reader.ReadBool(); + + if( m_ReplenishesCharges ) + m_LastReplenished = reader.ReadDateTime(); + + goto case 2; + } + case 2: + { + m_Crafter = reader.ReadMobile(); + + m_Quality = (InstrumentQuality)reader.ReadEncodedInt(); + m_Slayer = (SlayerName)reader.ReadEncodedInt(); + m_Slayer2 = (SlayerName)reader.ReadEncodedInt(); + + UsesRemaining = reader.ReadEncodedInt(); + + m_WellSound = reader.ReadEncodedInt(); + m_BadlySound = reader.ReadEncodedInt(); + + break; + } + case 1: + { + m_Crafter = reader.ReadMobile(); + + m_Quality = (InstrumentQuality)reader.ReadEncodedInt(); + m_Slayer = (SlayerName)reader.ReadEncodedInt(); + + UsesRemaining = reader.ReadEncodedInt(); + + m_WellSound = reader.ReadEncodedInt(); + m_BadlySound = reader.ReadEncodedInt(); + + break; + } + case 0: + { + m_WellSound = reader.ReadInt(); + m_BadlySound = reader.ReadInt(); + UsesRemaining = Utility.RandomMinMax( InitMinUses, InitMaxUses ); + + break; + } + } + + CheckReplenishUses(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if ( from.BeginAction( typeof( BaseInstrument ) ) ) + { + SetInstrument( from, this ); + + // Delay of 7 second before beign able to play another instrument again + new InternalTimer( from ).Start(); + + if ( CheckMusicianship( from ) ) + PlayInstrumentWell( from ); + else + PlayInstrumentBadly( from ); + } + else + { + from.SendLocalizedMessage( 500119 ); // You must wait to perform another action + } + } + + public static bool CheckMusicianship( Mobile m ) + { + m.CheckSkill( SkillName.Musicianship, 0.0, 120.0 ); + + return ( (m.Skills[SkillName.Musicianship].Value / 100) > Utility.RandomDouble() ); + } + + public void PlayInstrumentWell( Mobile from ) + { + from.PlaySound( m_WellSound ); + } + + public void PlayInstrumentBadly( Mobile from ) + { + from.PlaySound( m_BadlySound ); + } + + private class InternalTimer : Timer + { + private Mobile m_From; + + public InternalTimer( Mobile from ) : base( TimeSpan.FromSeconds( 6.0 ) ) + { + m_From = from; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_From.EndAction( typeof( BaseInstrument ) ); + } + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Quality = (InstrumentQuality)quality; + + if ( makersMark ) + Crafter = from; + + return quality; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/Drums.cs b/Scripts/Items/Skill Items/Musical Instruments/Drums.cs new file mode 100644 index 000000000..027a63290 --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/Drums.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class Drums : BaseInstrument + { + [Constructable] + public Drums() : base( 0xE9C, 0x38, 0x39 ) + { + Weight = 4.0; + } + + public Drums( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 4.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/Harp.cs b/Scripts/Items/Skill Items/Musical Instruments/Harp.cs new file mode 100644 index 000000000..9652b04c9 --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/Harp.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class Harp : BaseInstrument + { + [Constructable] + public Harp() : base( 0xEB1, 0x43, 0x44 ) + { + Weight = 35.0; + } + + public Harp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 35.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/LapHarp.cs b/Scripts/Items/Skill Items/Musical Instruments/LapHarp.cs new file mode 100644 index 000000000..d1b32fb50 --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/LapHarp.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class LapHarp : BaseInstrument + { + [Constructable] + public LapHarp() : base( 0xEB2, 0x45, 0x46 ) + { + Weight = 10.0; + } + + public LapHarp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 10.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/Lute.cs b/Scripts/Items/Skill Items/Musical Instruments/Lute.cs new file mode 100644 index 000000000..b7c800944 --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/Lute.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class Lute : BaseInstrument + { + [Constructable] + public Lute() : base( 0xEB3, 0x4C, 0x4D ) + { + Weight = 5.0; + } + + public Lute( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 3.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/Tambourine.cs b/Scripts/Items/Skill Items/Musical Instruments/Tambourine.cs new file mode 100644 index 000000000..36977d252 --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/Tambourine.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class Tambourine : BaseInstrument + { + [Constructable] + public Tambourine() : base( 0xE9D, 0x52, 0x53 ) + { + Weight = 1.0; + } + + public Tambourine( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Musical Instruments/TambourineTassel.cs b/Scripts/Items/Skill Items/Musical Instruments/TambourineTassel.cs new file mode 100644 index 000000000..363ad3799 --- /dev/null +++ b/Scripts/Items/Skill Items/Musical Instruments/TambourineTassel.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + public class TambourineTassel : BaseInstrument + { + [Constructable] + public TambourineTassel() : base( 0xE9E, 0x52, 0x53 ) + { + Weight = 1.0; + } + + public TambourineTassel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/EggBomb.cs b/Scripts/Items/Skill Items/Ninjitsu/EggBomb.cs new file mode 100644 index 000000000..9a33d39cd --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/EggBomb.cs @@ -0,0 +1,72 @@ +using System; +using Server; + +namespace Server.Items +{ + public class EggBomb : Item + { + [Constructable] + public EggBomb() : base( 0x2809 ) + { + Weight = 1.0; + } + + public EggBomb( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + // The item must be in your backpack to use it. + from.SendLocalizedMessage( 1060640 ); + } + else if ( from.Skills.Ninjitsu.Value < 50.0 ) + { + // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + from.SendLocalizedMessage( 1063013, "50\tNinjitsu" ); + } + else if ( from.NextSkillTime > DateTime.Now ) + { + // You must wait a few seconds before you can use that item. + from.SendLocalizedMessage( 1070772 ); + } + else if ( from.Mana < 10 ) + { + // You don't have enough mana to do that. + from.SendLocalizedMessage( 1049456 ); + } + else + { + SkillHandlers.Hiding.CombatOverride = true; + + if ( from.UseSkill( SkillName.Hiding ) ) + { + from.Mana -= 10; + + from.FixedParticles( 0x3709, 1, 30, 9904, 1108, 6, EffectLayer.RightFoot ); + from.PlaySound( 0x22F ); + + Consume(); + } + + SkillHandlers.Hiding.CombatOverride = false; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs b/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs new file mode 100644 index 000000000..11b32b776 --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/Fukiya.cs @@ -0,0 +1,337 @@ +using System; +using Server; +using Server.Targeting; +using System.Collections; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + [FlipableAttribute( 0x27AA, 0x27F5 )] + public class Fukiya : Item, IUsesRemaining + { + private bool m_Using; + private int m_UsesRemaining; + + private Poison m_Poison; + private int m_PoisonCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get{ return m_Poison; } + set{ m_Poison = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int PoisonCharges + { + get { return m_PoisonCharges; } + set { m_PoisonCharges = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + [Constructable] + public Fukiya() : base( 0x27AA ) + { + Weight = 4.0; + Layer = Layer.OneHanded; + } + + public Fukiya( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + + if ( m_Poison != null && m_PoisonCharges > 0 ) + list.Add( 1062412 + m_Poison.Level, m_PoisonCharges.ToString() ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from ) ) + return; + + if ( m_UsesRemaining < 1 ) + { + // You have no fukiya darts! + from.SendLocalizedMessage( 1063325 ); + } + else if ( m_Using ) + { + // You are already using that fukiya. + from.SendLocalizedMessage( 1063326 ); + } + else if ( !BasePotion.HasFreeHand( from ) ) + { + // You must have a free hand to use a fukiya. + from.SendLocalizedMessage( 1063327 ); + } + else + { + from.BeginTarget( 5, false, TargetFlags.Harmful, new TargetCallback( OnTarget ) ); + } + } + + public void Shoot( Mobile from, Mobile target ) + { + if ( from == target ) + return; + + if ( m_UsesRemaining < 1 ) + { + // You have no fukiya darts! + from.SendLocalizedMessage( 1063325 ); + } + else if ( m_Using ) + { + // You are already using that fukiya. + from.SendLocalizedMessage( 1063326 ); + } + else if ( !BasePotion.HasFreeHand( from ) ) + { + // You must have a free hand to use a fukiya. + from.SendLocalizedMessage( 1063327 ); + } + else if ( from.CanBeHarmful( target ) ) + { + m_Using = true; + + from.Direction = from.GetDirectionTo( target ); + + if ( from.Body.IsHuman && !from.Mounted ) + from.Animate( 33, 2, 1, true, true, 0 ); + + from.PlaySound( 0x223 ); + from.MovingEffect( target, 0x2804, 5, 0, false, false ); + + if ( from.CheckSkill( SkillName.Ninjitsu, -10.0, 50.0 ) ) + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( OnDartHit ), new object[]{ from, target } ); + else + ConsumeUse(); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerCallback( ResetUsing ) ); + } + } + + private void OnDartHit( object state ) + { + object[] states = (object[])state; + Mobile from = (Mobile)states[0]; + Mobile target = (Mobile)states[1]; + + if ( !from.CanBeHarmful( target ) ) + return; + + from.DoHarmful( target ); + + AOS.Damage( target, from, Utility.RandomMinMax( 4, 6 ), 100, 0, 0, 0, 0 ); + + if ( m_Poison != null && m_PoisonCharges > 0 ) + target.ApplyPoison( from, m_Poison ); + + ConsumeUse(); + } + + public void ConsumeUse() + { + if ( m_UsesRemaining < 1 ) + return; + + --m_UsesRemaining; + + if ( m_PoisonCharges > 0 ) + { + --m_PoisonCharges; + + if ( m_PoisonCharges == 0 ) + m_Poison = null; + } + + InvalidateProperties(); + } + + public void ResetUsing() + { + m_Using = false; + } + + private const int MaxUses = 10; + + public void Unload( Mobile from ) + { + if ( UsesRemaining < 1 ) + return; + + FukiyaDarts darts = new FukiyaDarts( UsesRemaining ); + + darts.Poison = m_Poison; + darts.PoisonCharges = m_PoisonCharges; + + from.AddToBackpack( darts ); + + m_UsesRemaining = 0; + m_PoisonCharges = 0; + m_Poison = null; + + InvalidateProperties(); + } + + public void Reload( Mobile from, FukiyaDarts darts ) + { + int need = ( MaxUses - m_UsesRemaining ); + + if ( need <= 0 ) + { + // You cannot add anymore fukiya darts + from.SendLocalizedMessage( 1063330 ); + } + else if ( darts.UsesRemaining > 0 ) + { + if ( need > darts.UsesRemaining ) + need = darts.UsesRemaining; + + if ( darts.Poison != null && darts.PoisonCharges > 0 ) + { + if ( m_PoisonCharges <= 0 || m_Poison == null || m_Poison.Level <= darts.Poison.Level ) + { + if ( m_Poison != null && m_Poison.Level < darts.Poison.Level ) + Unload( from ); + + if ( need > darts.PoisonCharges ) + need = darts.PoisonCharges; + + if ( m_Poison == null || m_PoisonCharges <= 0 ) + m_PoisonCharges = need; + else + m_PoisonCharges += need; + + m_Poison = darts.Poison; + + darts.PoisonCharges -= need; + + if ( darts.PoisonCharges <= 0 ) + darts.Poison = null; + } + else + { + from.SendLocalizedMessage( 1070767 ); // Loaded projectile is stronger, unload it first + } + } + else + { + m_UsesRemaining += need; + darts.UsesRemaining -= need; + } + + if ( darts.UsesRemaining <= 0 ) + darts.Delete(); + + InvalidateProperties(); + } + } + + public void OnTarget( Mobile from, object obj ) + { + if ( Deleted || !IsChildOf( from ) ) + return; + + if ( obj is Mobile ) + Shoot( from, (Mobile) obj ); + else if ( obj is FukiyaDarts ) + Reload( from, (FukiyaDarts) obj ); + else + from.SendLocalizedMessage( 1063329 ); // You can only load fukiya darts + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( IsChildOf( from ) ) + { + list.Add( new LoadEntry( this ) ); + list.Add( new UnloadEntry( this ) ); + } + } + + private class LoadEntry : ContextMenuEntry + { + private Fukiya m_Fukiya; + + public LoadEntry( Fukiya fukiya ) : base( 6224, 0 ) + { + m_Fukiya = fukiya; + } + + public override void OnClick() + { + if ( !m_Fukiya.Deleted && m_Fukiya.IsChildOf( Owner.From ) ) + Owner.From.BeginTarget( 5, false, TargetFlags.Harmful, new TargetCallback( m_Fukiya.OnTarget ) ); + } + } + + private class UnloadEntry : ContextMenuEntry + { + private Fukiya m_Fukiya; + + public UnloadEntry( Fukiya fukiya ) : base( 6225, 0 ) + { + m_Fukiya = fukiya; + + Enabled = ( fukiya.UsesRemaining > 0 ); + } + + public override void OnClick() + { + if ( !m_Fukiya.Deleted && m_Fukiya.IsChildOf( Owner.From ) ) + m_Fukiya.Unload( Owner.From ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (int) m_UsesRemaining ); + + Poison.Serialize( m_Poison, writer ); + writer.Write( (int) m_PoisonCharges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_UsesRemaining = reader.ReadInt(); + + m_Poison = Poison.Deserialize( reader ); + m_PoisonCharges = reader.ReadInt(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs b/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs new file mode 100644 index 000000000..e43797a97 --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/FukiyaDarts.cs @@ -0,0 +1,104 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class FukiyaDarts : Item, IUsesRemaining, ICraftable + { + private int m_UsesRemaining; + + private Poison m_Poison; + private int m_PoisonCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get{ return m_Poison; } + set{ m_Poison = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int PoisonCharges + { + get { return m_PoisonCharges; } + set { m_PoisonCharges = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + [Constructable] + public FukiyaDarts() : this( 1 ) + { + } + + [Constructable] + public FukiyaDarts( int amount ) : base( 0x2806 ) + { + Weight = 1.0; + + m_UsesRemaining = amount; + } + + public FukiyaDarts( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + + if ( m_Poison != null && m_PoisonCharges > 0 ) + list.Add( 1062412 + m_Poison.Level, m_PoisonCharges.ToString() ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_UsesRemaining ); + + Poison.Serialize( m_Poison, writer ); + writer.Write( (int) m_PoisonCharges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_UsesRemaining = reader.ReadInt(); + + m_Poison = Poison.Deserialize( reader ); + m_PoisonCharges = reader.ReadInt(); + + break; + } + } + } + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + if ( quality == 2 ) + UsesRemaining *= 2; + + return quality; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs b/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs new file mode 100644 index 000000000..b3ca3f46a --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs @@ -0,0 +1,355 @@ +using System; +using Server; +using Server.Targeting; +using System.Collections; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + [FlipableAttribute( 0x2790, 0x27DB )] + public class LeatherNinjaBelt : BaseWaist, IUsesRemaining + { + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + private bool m_Using; + private int m_UsesRemaining; + + private Poison m_Poison; + private int m_PoisonCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get{ return m_Poison; } + set{ m_Poison = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int PoisonCharges + { + get { return m_PoisonCharges; } + set { m_PoisonCharges = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + [Constructable] + public LeatherNinjaBelt() : base( 0x2790 ) + { + Weight = 1.0; + Layer = Layer.Waist; + } + + public LeatherNinjaBelt( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + + if ( m_Poison != null && m_PoisonCharges > 0 ) + list.Add( 1062412 + m_Poison.Level, m_PoisonCharges.ToString() ); + } + + public override bool OnEquip( Mobile from ) + { + if ( !base.OnEquip( from ) ) + return false; + + from.SendLocalizedMessage( 1070785 ); // Double click this item each time you wish to throw a shuriken. + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from ) ) + return; + + if ( m_UsesRemaining < 1 ) + { + // You have no shuriken in your ninja belt! + from.SendLocalizedMessage( 1063297 ); + } + else if ( m_Using ) + { + // You cannot throw another shuriken yet. + from.SendLocalizedMessage( 1063298 ); + } + else if ( !BasePotion.HasFreeHand( from ) ) + { + // You must have a free hand to throw shuriken. + from.SendLocalizedMessage( 1063299 ); + } + else + { + from.BeginTarget( 10, false, TargetFlags.Harmful, new TargetCallback( OnTarget ) ); + } + } + + public void Shoot( Mobile from, Mobile target ) + { + if ( from == target ) + return; + + if ( m_UsesRemaining < 1 ) + { + // You have no shuriken in your ninja belt! + from.SendLocalizedMessage( 1063297 ); + } + else if ( m_Using ) + { + // You cannot throw another shuriken yet. + from.SendLocalizedMessage( 1063298 ); + } + else if ( !BasePotion.HasFreeHand( from ) ) + { + // You must have a free hand to throw shuriken. + from.SendLocalizedMessage( 1063299 ); + } + else if ( from.InRange( target, 2 ) ) + { + from.SendLocalizedMessage( 1063303 ); // Your target is too close! + } + else if ( from.CanBeHarmful( target ) ) + { + m_Using = true; + + from.Direction = from.GetDirectionTo( target ); + + if ( from.Body.IsHuman ) + from.Animate( from.Mounted ? 26 : 9, 7, 1, true, false, 0 ); + + from.PlaySound( 0x23A ); + from.MovingEffect( target, 0x27AC, 1, 0, false, false ); + + if ( from.CheckSkill( SkillName.Ninjitsu, -10.0, 65.0 ) ) + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( OnShurikenHit ), new object[]{ from, target } ); + else + ConsumeUse(); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerCallback( ResetUsing ) ); + } + } + + private void OnShurikenHit( object state ) + { + object[] states = (object[])state; + Mobile from = (Mobile)states[0]; + Mobile target = (Mobile)states[1]; + + if ( !from.CanBeHarmful( target ) ) + return; + + from.DoHarmful( target ); + + AOS.Damage( target, from, Utility.RandomMinMax( 3, 5 ), 100, 0, 0, 0, 0 ); + + if ( m_Poison != null && m_PoisonCharges > 0 ) + target.ApplyPoison( from, m_Poison ); + + ConsumeUse(); + } + + public void ConsumeUse() + { + if ( m_UsesRemaining < 1 ) + return; + + --m_UsesRemaining; + + if ( m_PoisonCharges > 0 ) + { + --m_PoisonCharges; + + if ( m_PoisonCharges == 0 ) + m_Poison = null; + } + + InvalidateProperties(); + } + + public void ResetUsing() + { + m_Using = false; + } + + private const int MaxUses = 10; + + public void Unload( Mobile from ) + { + if ( UsesRemaining < 1 ) + return; + + Shuriken shuriken = new Shuriken( UsesRemaining ); + + shuriken.Poison = m_Poison; + shuriken.PoisonCharges = m_PoisonCharges; + + from.AddToBackpack( shuriken ); + + m_UsesRemaining = 0; + m_PoisonCharges = 0; + m_Poison = null; + + InvalidateProperties(); + } + + public void Reload( Mobile from, Shuriken shuriken ) + { + int need = ( MaxUses - m_UsesRemaining ); + + if ( need <= 0 ) + { + // You cannot add any more shuriken. + from.SendLocalizedMessage( 1063302 ); + } + else if ( shuriken.UsesRemaining > 0 ) + { + if ( need > shuriken.UsesRemaining ) + need = shuriken.UsesRemaining; + + if ( shuriken.Poison != null && shuriken.PoisonCharges > 0 ) + { + if ( m_PoisonCharges <= 0 || m_Poison == shuriken.Poison ) + { + if ( m_Poison != null && m_Poison.Level < shuriken.Poison.Level ) + Unload( from ); + + if ( need > shuriken.PoisonCharges ) + need = shuriken.PoisonCharges; + + if ( m_Poison == null || m_PoisonCharges <= 0 ) + m_PoisonCharges = need; + else + m_PoisonCharges += need; + + m_Poison = shuriken.Poison; + + shuriken.PoisonCharges -= need; + + if ( shuriken.PoisonCharges <= 0 ) + shuriken.Poison = null; + + m_UsesRemaining += need; + shuriken.UsesRemaining -= need; + } + else + { + from.SendLocalizedMessage( 1070767 ); // Loaded projectile is stronger, unload it first + } + } + else + { + m_UsesRemaining += need; + shuriken.UsesRemaining -= need; + } + + if ( shuriken.UsesRemaining <= 0 ) + shuriken.Delete(); + + InvalidateProperties(); + } + } + + public void OnTarget( Mobile from, object obj ) + { + if ( Deleted || !IsChildOf( from ) ) + return; + + if ( obj is Mobile ) + Shoot( from, (Mobile) obj ); + else if ( obj is Shuriken ) + Reload( from, (Shuriken) obj ); + else + from.SendLocalizedMessage( 1063301 ); // You can only place shuriken in a ninja belt. + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( IsChildOf( from ) ) + { + list.Add( new LoadEntry( this ) ); + list.Add( new UnloadEntry( this ) ); + } + } + + private class LoadEntry : ContextMenuEntry + { + private LeatherNinjaBelt m_Belt; + + public LoadEntry( LeatherNinjaBelt belt ) : base( 6222, 0 ) + { + m_Belt = belt; + } + + public override void OnClick() + { + if ( !m_Belt.Deleted && m_Belt.IsChildOf( Owner.From ) ) + Owner.From.BeginTarget( 10, false, TargetFlags.Harmful, new TargetCallback( m_Belt.OnTarget ) ); + } + } + + private class UnloadEntry : ContextMenuEntry + { + private LeatherNinjaBelt m_Belt; + + public UnloadEntry( LeatherNinjaBelt belt ) : base( 6223, 0 ) + { + m_Belt = belt; + + Enabled = ( belt.UsesRemaining > 0 ); + } + + public override void OnClick() + { + if ( !m_Belt.Deleted && m_Belt.IsChildOf( Owner.From ) ) + m_Belt.Unload( Owner.From ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( (int) m_UsesRemaining ); + + Poison.Serialize( m_Poison, writer ); + writer.Write( (int) m_PoisonCharges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_UsesRemaining = reader.ReadInt(); + + m_Poison = Poison.Deserialize( reader ); + m_PoisonCharges = reader.ReadInt(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs b/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs new file mode 100644 index 000000000..420a55249 --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/Shuriken.cs @@ -0,0 +1,105 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x27AC, 0x27F7 )] + public class Shuriken : Item, IUsesRemaining, ICraftable + { + private int m_UsesRemaining; + + private Poison m_Poison; + private int m_PoisonCharges; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get{ return m_Poison; } + set{ m_Poison = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int PoisonCharges + { + get { return m_PoisonCharges; } + set { m_PoisonCharges = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + [Constructable] + public Shuriken() : this( 1 ) + { + } + + [Constructable] + public Shuriken( int amount ) : base( 0x27AC ) + { + Weight = 1.0; + + m_UsesRemaining = amount; + } + + public Shuriken( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + + if ( m_Poison != null && m_PoisonCharges > 0 ) + list.Add( 1062412 + m_Poison.Level, m_PoisonCharges.ToString() ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_UsesRemaining ); + + Poison.Serialize( m_Poison, writer ); + writer.Write( (int) m_PoisonCharges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_UsesRemaining = reader.ReadInt(); + + m_Poison = Poison.Deserialize( reader ); + m_PoisonCharges = reader.ReadInt(); + + break; + } + } + } + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + if ( quality == 2 ) + UsesRemaining *= 2; + + return quality; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Ninjitsu/SmokeBomb.cs b/Scripts/Items/Skill Items/Ninjitsu/SmokeBomb.cs new file mode 100644 index 000000000..582359abc --- /dev/null +++ b/Scripts/Items/Skill Items/Ninjitsu/SmokeBomb.cs @@ -0,0 +1,72 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SmokeBomb : Item + { + [Constructable] + public SmokeBomb() : base( 0x2808 ) + { + Weight = 1.0; + } + + public SmokeBomb( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + // The item must be in your backpack to use it. + from.SendLocalizedMessage( 1060640 ); + } + else if ( from.Skills.Ninjitsu.Value < 50.0 ) + { + // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + from.SendLocalizedMessage( 1063013, "50\tNinjitsu" ); + } + else if ( from.NextSkillTime > DateTime.Now ) + { + // You must wait a few seconds before you can use that item. + from.SendLocalizedMessage( 1070772 ); + } + else if ( from.Mana < 10 ) + { + // You don't have enough mana to do that. + from.SendLocalizedMessage( 1049456 ); + } + else + { + SkillHandlers.Hiding.CombatOverride = true; + + if ( from.UseSkill( SkillName.Hiding ) ) + { + from.Mana -= 10; + + from.FixedParticles( 0x3709, 1, 30, 9904, 1108, 6, EffectLayer.RightFoot ); + from.PlaySound( 0x22F ); + + Consume(); + } + + SkillHandlers.Hiding.CombatOverride = false; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Specialized/GlassblowingBook.cs b/Scripts/Items/Skill Items/Specialized/GlassblowingBook.cs new file mode 100644 index 000000000..35acd9e7c --- /dev/null +++ b/Scripts/Items/Skill Items/Specialized/GlassblowingBook.cs @@ -0,0 +1,62 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class GlassblowingBook : Item + { + public override string DefaultName + { + get { return "Crafting Glass With Glassblowing"; } + } + + [Constructable] + public GlassblowingBook() : base( 0xFF4 ) + { + Weight = 1.0; + } + + public GlassblowingBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( pm == null || from.Skills[SkillName.Alchemy].Base < 100.0 ) + { + pm.SendMessage( "Only a Grandmaster Alchemist can learn from this book." ); + } + else if ( pm.Glassblowing ) + { + pm.SendMessage( "You have already learned this information." ); + } + else + { + pm.Glassblowing = true; + pm.SendMessage( "You have learned to make items from glass. You will need to find miners to mine find sand for you to make these items." ); + Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Specialized/MasonryBook.cs b/Scripts/Items/Skill Items/Specialized/MasonryBook.cs new file mode 100644 index 000000000..6bbaddc3a --- /dev/null +++ b/Scripts/Items/Skill Items/Specialized/MasonryBook.cs @@ -0,0 +1,62 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class MasonryBook : Item + { + public override string DefaultName + { + get { return "Making Valuables With Stonecrafting"; } + } + + [Constructable] + public MasonryBook() : base( 0xFBE ) + { + Weight = 1.0; + } + + public MasonryBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( pm == null || from.Skills[SkillName.Carpentry].Base < 100.0 ) + { + pm.SendMessage( "Only a Grandmaster Carpenter can learn from this book." ); + } + else if ( pm.Masonry ) + { + pm.SendMessage( "You have already learned this information." ); + } + else + { + pm.Masonry = true; + pm.SendMessage( "You have learned to make items from stone. You will need miners to gather stones for you to make these items." ); + Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Specialized/Sand.cs b/Scripts/Items/Skill Items/Specialized/Sand.cs new file mode 100644 index 000000000..8dedf03cc --- /dev/null +++ b/Scripts/Items/Skill Items/Specialized/Sand.cs @@ -0,0 +1,52 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x11EA, 0x11EB )] + public class Sand : Item, ICommodity + { + string ICommodity.Description + { + get + { + return String.Format( Amount == 1 ? "{0} sand" : "{0} sand", Amount ); + } + } + + public override int LabelNumber{ get{ return 1044626; } } // sand + + [Constructable] + public Sand() : this( 1 ) + { + } + + [Constructable] + public Sand( int amount ) : base( 0x11EA ) + { + Stackable = false; + Weight = 1.0; + } + + public Sand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && this.Name == "sand" ) + this.Name = null; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Specialized/SandMiningBook.cs b/Scripts/Items/Skill Items/Specialized/SandMiningBook.cs new file mode 100644 index 000000000..868291c40 --- /dev/null +++ b/Scripts/Items/Skill Items/Specialized/SandMiningBook.cs @@ -0,0 +1,62 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class SandMiningBook : Item + { + public override string DefaultName + { + get { return "Find Glass-Quality Sand"; } + } + + [Constructable] + public SandMiningBook() : base( 0xFF4 ) + { + Weight = 1.0; + } + + public SandMiningBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( pm == null || from.Skills[SkillName.Mining].Base < 100.0 ) + { + pm.SendMessage( "Only a Grandmaster Miner can learn from this book." ); + } + else if ( pm.SandMining ) + { + pm.SendMessage( "You have already learned this information." ); + } + else + { + pm.SandMining = true; + pm.SendMessage( "You have learned how to mine fine sand. Target sand areas when mining to look for fine sand." ); + Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Specialized/StoneMiningBook.cs b/Scripts/Items/Skill Items/Specialized/StoneMiningBook.cs new file mode 100644 index 000000000..6d5ba1fce --- /dev/null +++ b/Scripts/Items/Skill Items/Specialized/StoneMiningBook.cs @@ -0,0 +1,62 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + public class StoneMiningBook : Item + { + public override string DefaultName + { + get { return "Mining For Quality Stone"; } + } + + [Constructable] + public StoneMiningBook() : base( 0xFBE ) + { + Weight = 1.0; + } + + public StoneMiningBook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( pm == null || from.Skills[SkillName.Mining].Base < 100.0 ) + { + from.SendMessage( "Only a Grandmaster Miner can learn from this book." ); + } + else if ( pm.StoneMining ) + { + pm.SendMessage( "You have already learned this knowledge." ); + } + else + { + pm.StoneMining = true; + pm.SendMessage( "You have learned to mine for stones. Target mountains when mining to find stones." ); + Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs new file mode 100644 index 000000000..8bb05a0cd --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlackDyeTub.cs @@ -0,0 +1,32 @@ +using System; + +namespace Server.Items +{ + public class BlackDyeTub : DyeTub + { + [Constructable] + public BlackDyeTub() + { + Hue = DyedHue = 0x0001; + Redyable = false; + } + + public BlackDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs new file mode 100644 index 000000000..bc0c7eda3 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/BlazeDyeTub.cs @@ -0,0 +1,33 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BlazeDyeTub : DyeTub + { + [Constructable] + public BlazeDyeTub() + { + Hue = DyedHue = 0x489; + Redyable = false; + } + + public BlazeDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/CustomHuePicker.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/CustomHuePicker.cs new file mode 100644 index 000000000..54efd9e02 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/CustomHuePicker.cs @@ -0,0 +1,224 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class CustomHueGroup + { + private int m_Name; + private string m_NameString; + private int[] m_Hues; + + public int Name{ get{ return m_Name; } } + public string NameString{ get{ return m_NameString; } } + + public int[] Hues{ get{ return m_Hues; } } + + public CustomHueGroup( int name, int[] hues ) + { + m_Name = name; + m_Hues = hues; + } + + public CustomHueGroup( string name, int[] hues ) + { + m_NameString = name; + m_Hues = hues; + } + } + + public class CustomHuePicker + { + private CustomHueGroup[] m_Groups; + private bool m_DefaultSupported; + private int m_Title; + private string m_TitleString; + + public bool DefaultSupported{ get{ return m_DefaultSupported; } } + public CustomHueGroup[] Groups{ get{ return m_Groups; } } + public int Title{ get{ return m_Title; } } + public string TitleString{ get{ return m_TitleString; } } + + public CustomHuePicker( CustomHueGroup[] groups, bool defaultSupported ) + { + m_Groups = groups; + m_DefaultSupported = defaultSupported; + } + + public CustomHuePicker( CustomHueGroup[] groups, bool defaultSupported, int title ) + { + m_Groups = groups; + m_DefaultSupported = defaultSupported; + m_Title = title; + } + + public CustomHuePicker( CustomHueGroup[] groups, bool defaultSupported, string title ) + { + m_Groups = groups; + m_DefaultSupported = defaultSupported; + m_TitleString = title; + } + + public static readonly CustomHuePicker SpecialDyeTub = new CustomHuePicker( new CustomHueGroup[] + { + /* Violet */ + new CustomHueGroup( 1018345, new int[]{ 1230, 1231, 1232, 1233, 1234, 1235 } ), + /* Tan */ + new CustomHueGroup( 1018346, new int[]{ 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508 } ), + /* Brown */ + new CustomHueGroup( 1018347, new int[]{ 2012, 2013, 2014, 2015, 2016, 2017 } ), + /* Dark Blue */ + new CustomHueGroup( 1018348, new int[]{ 1303, 1304, 1305, 1306, 1307, 1308 } ), + /* Forest Green */ + new CustomHueGroup( 1018349, new int[]{ 1420, 1421, 1422, 1423, 1424, 1425, 1426 } ), + /* Pink */ + new CustomHueGroup( 1018350, new int[]{ 1619, 1620, 1621, 1622, 1623, 1624, 1625, 1626 } ), + /* Red */ + new CustomHueGroup( 1018351, new int[]{ 1640, 1641, 1642, 1643, 1644 } ), + /* Olive */ + new CustomHueGroup( 1018352, new int[]{ 2001, 2002, 2003, 2004, 2005 } ) + }, false, 1018344 ); + + public static readonly CustomHuePicker LeatherDyeTub = new CustomHuePicker( new CustomHueGroup[] + { + /* Dull Copper */ + new CustomHueGroup( 1018332, new int[]{ 2419, 2420, 2421, 2422, 2423, 2424 } ), + /* Shadow Iron */ + new CustomHueGroup( 1018333, new int[]{ 2406, 2407, 2408, 2409, 2410, 2411, 2412 } ), + /* Copper */ + new CustomHueGroup( 1018334, new int[]{ 2413, 2414, 2415, 2416, 2417, 2418 } ), + /* Bronze */ + new CustomHueGroup( 1018335, new int[]{ 2414, 2415, 2416, 2417, 2418 } ), + /* Glden */ + new CustomHueGroup( 1018336, new int[]{ 2213, 2214, 2215, 2216, 2217, 2218 } ), + /* Agapite */ + new CustomHueGroup( 1018337, new int[]{ 2425, 2426, 2427, 2428, 2429, 2430 } ), + /* Verite */ + new CustomHueGroup( 1018338, new int[]{ 2207, 2208, 2209, 2210, 2211, 2212 } ), + /* Valorite */ + new CustomHueGroup( 1018339, new int[]{ 2219, 2220, 2221, 2222, 2223, 2224 } ), + /* Reds */ + new CustomHueGroup( 1018340, new int[]{ 2113, 2114, 2115, 2116, 2117, 2118 } ), + /* Blues */ + new CustomHueGroup( 1018341, new int[]{ 2119, 2120, 2121, 2122, 2123, 2124 } ), + /* Greens */ + new CustomHueGroup( 1018342, new int[]{ 2126, 2127, 2128, 2129, 2130 } ), + /* Yellows */ + new CustomHueGroup( 1018343, new int[]{ 2213, 2214, 2215, 2216, 2217, 2218 } ) + }, true ); + } + + public delegate void CustomHuePickerCallback( Mobile from, object state, int hue ); + + public class CustomHuePickerGump : Gump + { + private Mobile m_From; + private CustomHuePicker m_Definition; + private CustomHuePickerCallback m_Callback; + private object m_State; + + private int GetRadioID( int group, int index ) + { + return (index * m_Definition.Groups.Length) + group; + } + + private void RenderBackground() + { + AddPage( 0 ); + + AddBackground( 0, 0, 450, 450, 5054 ); + AddBackground( 10, 10, 430, 430, 3000 ); + + if ( m_Definition.TitleString != null ) + AddHtml( 20, 30, 400, 25, m_Definition.TitleString, false, false ); + else if ( m_Definition.Title > 0 ) + AddHtmlLocalized( 20, 30, 400, 25, m_Definition.Title, false, false ); + + AddButton( 20, 400, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 400, 200, 25, 1011036, false, false ); // OKAY + + if ( m_Definition.DefaultSupported ) + { + AddButton( 200, 400, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddLabel( 235, 400, 0, "DEFAULT" ); + } + } + + private void RenderCategories() + { + CustomHueGroup[] groups = m_Definition.Groups; + + for ( int i = 0; i < groups.Length; ++i ) + { + AddButton( 30, 85 + (i * 25), 5224, 5224, 0, GumpButtonType.Page, 1 + i ); + + if ( groups[i].NameString != null ) + AddHtml( 55, 85 + (i * 25), 200, 25, groups[i].NameString, false, false ); + else + AddHtmlLocalized( 55, 85 + (i * 25), 200, 25, groups[i].Name, false, false ); + } + + for ( int i = 0; i < groups.Length; ++i ) + { + AddPage( 1 + i ); + + int[] hues = groups[i].Hues; + + for ( int j = 0; j < hues.Length; ++j ) + { + AddRadio( 260, 90 + (j * 25), 210, 211, false, GetRadioID( i, j ) ); + AddLabel( 278, 90 + (j * 25), hues[j] - 1, "*****" ); + } + } + } + + public CustomHuePickerGump( Mobile from, CustomHuePicker definition, CustomHuePickerCallback callback, object state ) : base( 50, 50 ) + { + m_From = from; + m_Definition = definition; + m_Callback = callback; + m_State = state; + + RenderBackground(); + RenderCategories(); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 1: // Okay + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + int group = index % m_Definition.Groups.Length; + index /= m_Definition.Groups.Length; + + if ( group >= 0 && group < m_Definition.Groups.Length ) + { + int[] hues = m_Definition.Groups[group].Hues; + + if ( index >= 0 && index < hues.Length ) + m_Callback( m_From, m_State, hues[index] ); + } + } + + break; + } + case 2: // Default + { + if ( m_Definition.DefaultSupported ) + m_Callback( m_From, m_State, 0 ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs new file mode 100644 index 000000000..d6890e8e3 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs @@ -0,0 +1,257 @@ +using System; +using Server; +using Server.Multis; +using Server.Targeting; + +namespace Server.Items +{ + public interface IDyable + { + bool Dye( Mobile from, DyeTub sender ); + } + + public class DyeTub : Item + { + private bool m_Redyable; + private int m_DyedHue; + + public virtual CustomHuePicker CustomHuePicker{ get{ return null; } } + + public virtual bool AllowRunebooks + { + get{ return false; } + } + + public virtual bool AllowFurniture + { + get{ return false; } + } + + public virtual bool AllowStatuettes + { + get{ return false; } + } + + public virtual bool AllowLeather + { + get{ return false; } + } + + public virtual bool AllowDyables + { + get{ return true; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (bool) m_Redyable ); + writer.Write( (int) m_DyedHue ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Redyable = reader.ReadBool(); + m_DyedHue = reader.ReadInt(); + + break; + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Redyable + { + get + { + return m_Redyable; + } + set + { + m_Redyable = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int DyedHue + { + get + { + return m_DyedHue; + } + set + { + if ( m_Redyable ) + { + m_DyedHue = value; + Hue = value; + } + } + } + + [Constructable] + public DyeTub() : base( 0xFAB ) + { + Weight = 10.0; + m_Redyable = true; + } + + public DyeTub( Serial serial ) : base( serial ) + { + } + + // Select the clothing to dye. + public virtual int TargetMessage{ get{ return 500859; } } + + // You can not dye that. + public virtual int FailMessage{ get{ return 1042083; } } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( TargetMessage ); + from.Target = new InternalTarget( this ); + } + else + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + } + + private class InternalTarget : Target + { + private DyeTub m_Tub; + + public InternalTarget( DyeTub tub ) : base( 1, false, TargetFlags.None ) + { + m_Tub = tub; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Item ) + { + Item item = (Item)targeted; + + if ( item is IDyable && m_Tub.AllowDyables ) + { + if ( !from.InRange( m_Tub.GetWorldLocation(), 1 ) || !from.InRange( item.GetWorldLocation(), 1 ) ) + from.SendLocalizedMessage( 500446 ); // That is too far away. + else if ( item.Parent is Mobile ) + from.SendLocalizedMessage( 500861 ); // Can't Dye clothing that is being worn. + else if ( ((IDyable)item).Dye( from, m_Tub ) ) + from.PlaySound( 0x23E ); + } + else if ( (FurnitureAttribute.Check( item ) || (item is PotionKeg)) && m_Tub.AllowFurniture ) + { + if ( !from.InRange( m_Tub.GetWorldLocation(), 1 ) || !from.InRange( item.GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else + { + bool okay = ( item.IsChildOf( from.Backpack ) ); + + if ( !okay ) + { + if ( item.Parent == null ) + { + BaseHouse house = BaseHouse.FindHouseAt( item ); + + if ( house == null || !house.IsLockedDown( item ) ) + from.SendLocalizedMessage( 501022 ); // Furniture must be locked down to paint it. + else if ( !house.IsCoOwner( from ) ) + from.SendLocalizedMessage( 501023 ); // You must be the owner to use this item. + else + okay = true; + } + else + { + from.SendLocalizedMessage( 1048135 ); // The furniture must be in your backpack to be painted. + } + } + + if ( okay ) + { + item.Hue = m_Tub.DyedHue; + from.PlaySound( 0x23E ); + } + } + } + else if ( (item is Runebook || item is RecallRune ) && m_Tub.AllowRunebooks ) + { + if ( !from.InRange( m_Tub.GetWorldLocation(), 1 ) || !from.InRange( item.GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if ( !item.Movable ) + { + from.SendLocalizedMessage( 1049776 ); // You cannot dye runes or runebooks that are locked down. + } + else + { + item.Hue = m_Tub.DyedHue; + from.PlaySound( 0x23E ); + } + } + else if ( item is MonsterStatuette && m_Tub.AllowStatuettes ) + { + if ( !from.InRange( m_Tub.GetWorldLocation(), 1 ) || !from.InRange( item.GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if ( !item.Movable ) + { + from.SendLocalizedMessage( 1049779 ); // You cannot dye statuettes that are locked down. + } + else + { + item.Hue = m_Tub.DyedHue; + from.PlaySound( 0x23E ); + } + } + else if ( (item is BaseArmor && (((BaseArmor)item).MaterialType == ArmorMaterialType.Leather || ((BaseArmor)item).MaterialType == ArmorMaterialType.Studded)) && m_Tub.AllowLeather ) + { + if ( !from.InRange( m_Tub.GetWorldLocation(), 1 ) || !from.InRange( item.GetWorldLocation(), 1 ) ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + else if ( !item.Movable ) + { + from.SendLocalizedMessage( 1042419 ); // You may not dye leather items which are locked down. + } + else if ( item.Parent is Mobile ) + { + from.SendLocalizedMessage( 500861 ); // Can't Dye clothing that is being worn. + } + else + { + item.Hue = m_Tub.DyedHue; + from.PlaySound( 0x23E ); + } + } + else + { + from.SendLocalizedMessage( m_Tub.FailMessage ); + } + } + else + { + from.SendLocalizedMessage( m_Tub.FailMessage ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs new file mode 100644 index 000000000..50757ad86 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/FurnitureDyeTub.cs @@ -0,0 +1,68 @@ +using System; + +namespace Server.Items +{ + public class FurnitureDyeTub : DyeTub, Engines.VeteranRewards.IRewardItem + { + public override bool AllowDyables{ get{ return false; } } + public override bool AllowFurniture{ get{ return true; } } + public override int TargetMessage{ get{ return 501019; } } // Select the furniture to dye. + public override int FailMessage{ get{ return 501021; } } // That is not a piece of furniture. + public override int LabelNumber{ get{ return 1041246; } } // Furniture Dye Tub + + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [Constructable] + public FurnitureDyeTub() + { + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + return; + + base.OnDoubleClick( from ); + } + + public FurnitureDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsRewardItem = reader.ReadBool(); + break; + } + } + + if ( LootType == LootType.Regular ) + LootType = LootType.Blessed; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs new file mode 100644 index 000000000..ec4a8ef9e --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/LeatherDyeTub.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Items +{ + public class LeatherDyeTub : DyeTub, Engines.VeteranRewards.IRewardItem + { + public override bool AllowDyables{ get{ return false; } } + public override bool AllowLeather{ get{ return true; } } + public override int TargetMessage{ get{ return 1042416; } } // Select the leather item to dye. + public override int FailMessage{ get{ return 1042418; } } // You can only dye leather with this tub. + public override int LabelNumber{ get{ return 1041284; } } // Leather Dye Tub + public override CustomHuePicker CustomHuePicker{ get{ return CustomHuePicker.LeatherDyeTub; } } + + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [Constructable] + public LeatherDyeTub() + { + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + return; + + base.OnDoubleClick( from ); + } + + public LeatherDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsRewardItem = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs new file mode 100644 index 000000000..585488df2 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/RewardBlackDyeTub.cs @@ -0,0 +1,63 @@ +using System; + +namespace Server.Items +{ + public class RewardBlackDyeTub : DyeTub, Engines.VeteranRewards.IRewardItem + { + public override int LabelNumber{ get{ return 1006008; } } // Black Dye Tub + + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [Constructable] + public RewardBlackDyeTub() + { + Hue = DyedHue = 0x0001; + Redyable = false; + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + return; + + base.OnDoubleClick( from ); + } + + public RewardBlackDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsRewardItem = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs new file mode 100644 index 000000000..771188275 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/RunebookDyeTub.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Items +{ + public class RunebookDyeTub : DyeTub, Engines.VeteranRewards.IRewardItem + { + public override bool AllowDyables{ get{ return false; } } + public override bool AllowRunebooks{ get{ return true; } } + public override int TargetMessage{ get{ return 1049774; } } // Target the runebook or runestone to dye + public override int FailMessage{ get{ return 1049775; } } // You can only dye runestones or runebooks with this tub. + public override int LabelNumber{ get{ return 1049740; } } // Runebook Dye Tub + public override CustomHuePicker CustomHuePicker{ get{ return CustomHuePicker.LeatherDyeTub; } } + + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [Constructable] + public RunebookDyeTub() + { + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + return; + + base.OnDoubleClick( from ); + } + + public RunebookDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsRewardItem = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs new file mode 100644 index 000000000..b75661730 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/SpecialDyeTub.cs @@ -0,0 +1,62 @@ +using System; + +namespace Server.Items +{ + public class SpecialDyeTub : DyeTub, Engines.VeteranRewards.IRewardItem + { + public override CustomHuePicker CustomHuePicker{ get{ return CustomHuePicker.SpecialDyeTub; } } + public override int LabelNumber{ get{ return 1041285; } } // Special Dye Tub + + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [Constructable] + public SpecialDyeTub() + { + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + return; + + base.OnDoubleClick( from ); + } + + public SpecialDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsRewardItem = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs new file mode 100644 index 000000000..27ee69da3 --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Dyetubs/StatuetteDyeTub.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Items +{ + public class StatuetteDyeTub : DyeTub, Engines.VeteranRewards.IRewardItem + { + public override bool AllowDyables{ get{ return false; } } + public override bool AllowStatuettes{ get{ return true; } } + public override int TargetMessage{ get{ return 1049777; } } // Target the statuette to dye + public override int FailMessage{ get{ return 1049778; } } // You can only dye veteran reward statuettes with this tub. + public override int LabelNumber{ get{ return 1049741; } } // Reward Statuette Dye Tub + public override CustomHuePicker CustomHuePicker{ get{ return CustomHuePicker.LeatherDyeTub; } } + + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [Constructable] + public StatuetteDyeTub() + { + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + return; + + base.OnDoubleClick( from ); + } + + public StatuetteDyeTub( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_IsRewardItem = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Misc/Dressform.cs b/Scripts/Items/Skill Items/Tailor Items/Misc/Dressform.cs new file mode 100644 index 000000000..c18ea0dde --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Misc/Dressform.cs @@ -0,0 +1,32 @@ +using System; + +namespace Server.Items +{ + [FlipableAttribute(0xec6, 0xec7)] + public class Dressform : Item + { + [Constructable] + public Dressform() : base(0xec6) + { + Weight = 10; + } + + public Dressform(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Misc/Dyes.cs b/Scripts/Items/Skill Items/Tailor Items/Misc/Dyes.cs new file mode 100644 index 000000000..d4a8340dd --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Misc/Dyes.cs @@ -0,0 +1,97 @@ +using System; +using Server.Targeting; +using Server.HuePickers; + +namespace Server.Items +{ + public class Dyes : Item + { + [Constructable] + public Dyes() : base( 0xFA9 ) + { + Weight = 3.0; + } + + public Dyes( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 0.0 ) + Weight = 3.0; + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 500856 ); // Select the dye tub to use the dyes on. + from.Target = new InternalTarget(); + } + + private class InternalTarget : Target + { + public InternalTarget() : base( 1, false, TargetFlags.None ) + { + } + + private class InternalPicker : HuePicker + { + private DyeTub m_Tub; + + public InternalPicker( DyeTub tub ) : base( tub.ItemID ) + { + m_Tub = tub; + } + + public override void OnResponse( int hue ) + { + m_Tub.DyedHue = hue; + } + } + + private static void SetTubHue( Mobile from, object state, int hue ) + { + ((DyeTub)state).DyedHue = hue; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is DyeTub ) + { + DyeTub tub = (DyeTub) targeted; + + if ( tub.Redyable ) + { + if ( tub.CustomHuePicker == null ) + from.SendHuePicker( new InternalPicker( tub ) ); + else + from.SendGump( new CustomHuePickerGump( from, tub.CustomHuePicker, new CustomHuePickerCallback( SetTubHue ), tub ) ); + } + else if ( tub is BlackDyeTub ) + { + from.SendLocalizedMessage( 1010092 ); // You can not use this on a black dye tub. + } + else + { + from.SendMessage( "That dye tub may not be redyed." ); + } + } + else + { + from.SendLocalizedMessage( 500857 ); // Use this on a dye tub. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tailor Items/Misc/Scissors.cs b/Scripts/Items/Skill Items/Tailor Items/Misc/Scissors.cs new file mode 100644 index 000000000..e1e7b08ec --- /dev/null +++ b/Scripts/Items/Skill Items/Tailor Items/Misc/Scissors.cs @@ -0,0 +1,88 @@ +using System; +using Server.Network; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Items +{ + public interface IScissorable + { + bool Scissor( Mobile from, Scissors scissors ); + } + + [FlipableAttribute( 0xf9f, 0xf9e )] + public class Scissors : Item + { + [Constructable] + public Scissors() : base( 0xF9F ) + { + Weight = 1.0; + } + + public Scissors( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 502434 ); // What should I use these scissors on? + + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private Scissors m_Item; + + public InternalTarget( Scissors item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) + return; + + /*if ( targeted is Item && !((Item)targeted).IsStandardLoot() ) + { + from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. + } + else */ + if( Core.AOS && targeted == from ) + { + from.SendLocalizedMessage( 1062845 + Utility.Random( 3 ) ); //"That doesn't seem like the smartest thing to do." / "That was an encounter you don't wish to repeat." / "Ha! You missed!" + } + else if( Core.SE && Utility.RandomDouble() > .20 && (from.Direction & Direction.Running) != 0 && ( DateTime.Now - from.LastMoveTime ) < from.ComputeMovementSpeed( from.Direction ) ) + { + from.SendLocalizedMessage( 1063305 ); // Didn't your parents ever tell you not to run with scissors in your hand?! + } + else if( targeted is IScissorable ) + { + IScissorable obj = (IScissorable)targeted; + + if( obj.Scissor( from, m_Item ) ) + from.PlaySound( 0x248 ); + } + else + { + from.SendLocalizedMessage( 502440 ); // Scissors can not be used on that to produce anything. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Thief/DisguiseKit.cs b/Scripts/Items/Skill Items/Thief/DisguiseKit.cs new file mode 100644 index 000000000..310d671f8 --- /dev/null +++ b/Scripts/Items/Skill Items/Thief/DisguiseKit.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Spells; +using Server.Spells.Fifth; +using Server.Spells.Seventh; +using Server.Spells.Necromancy; +using Server.Mobiles; +using Server.Network; +using Server.SkillHandlers; + +namespace Server.Items +{ + public class DisguiseKit : Item + { + public override int LabelNumber{ get{ return 1041078; } } // a disguise kit + + [Constructable] + public DisguiseKit() : base( 0xE05 ) + { + Weight = 1.0; + } + + public DisguiseKit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public bool ValidateUse( Mobile from ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( !IsChildOf( from.Backpack ) ) + { + // That must be in your pack for you to use it. + from.SendLocalizedMessage( 1042001 ); + } + else if ( pm == null || pm.NpcGuild != NpcGuild.ThievesGuild ) + { + // Only Members of the thieves guild are trained to use this item. + from.SendLocalizedMessage( 501702 ); + } + else if ( Stealing.SuspendOnMurder && pm.Kills > 0 ) + { + // You are currently suspended from the thieves guild. They would frown upon your actions. + from.SendLocalizedMessage( 501703 ); + } + else if ( !from.CanBeginAction( typeof( IncognitoSpell ) ) ) + { + // You cannot disguise yourself while incognitoed. + from.SendLocalizedMessage( 501704 ); + } + else if ( Factions.Sigil.ExistsOn( from ) ) + { + from.SendLocalizedMessage( 1010465 ); // You cannot disguise yourself while holding a sigil + } + else if ( TransformationSpell.UnderTransformation( from ) ) + { + // You cannot disguise yourself while in that form. + from.SendLocalizedMessage( 1061634 ); + } + else if ( from.BodyMod == 183 || from.BodyMod == 184 ) + { + // You cannot disguise yourself while wearing body paint + from.SendLocalizedMessage( 1040002 ); + } + else if ( !from.CanBeginAction( typeof( PolymorphSpell ) ) || from.IsBodyMod ) + { + // You cannot disguise yourself while polymorphed. + from.SendLocalizedMessage( 501705 ); + } + else + { + return true; + } + + return false; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( ValidateUse( from ) ) + from.SendGump( new DisguiseGump( from, this, true, false ) ); + } + } + + public class DisguiseGump : Gump + { + private Mobile m_From; + private DisguiseKit m_Kit; + private bool m_Used; + + public DisguiseGump( Mobile from, DisguiseKit kit, bool startAtHair, bool used ) : base( 50, 50 ) + { + m_From = from; + m_Kit = kit; + m_Used = used; + + from.CloseGump( typeof( DisguiseGump ) ); + + AddPage( 0 ); + + AddBackground( 100, 10, 400, 385, 2600 ); + + //
THIEF DISGUISE KIT
+ AddHtmlLocalized( 100, 25, 400, 35, 1011045, false, false ); + + AddButton( 140, 353, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 172, 355, 90, 35, 1011036, false, false ); // OKAY + + AddButton( 257, 353, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 289, 355, 90, 35, 1011046, false, false ); // APPLY + + if ( from.Female || from.Body.IsFemale ) + { + DrawEntries( 0, 1, -1, m_HairEntries, -1 ); + } + else if ( startAtHair ) + { + DrawEntries( 0, 1, 2, m_HairEntries, 1011056 ); + DrawEntries( 1, 2, 1, m_BeardEntries, 1011059 ); + } + else + { + DrawEntries( 1, 1, 2, m_BeardEntries, 1011059 ); + DrawEntries( 0, 2, 1, m_HairEntries, 1011056 ); + } + } + + private void DrawEntries( int index, int page, int nextPage, DisguiseEntry[] entries, int nextNumber ) + { + AddPage( page ); + + if ( nextPage != -1 ) + { + AddButton( 155, 320, 250 + (index*2), 251 + (index*2), 0, GumpButtonType.Page, nextPage ); + AddHtmlLocalized( 180, 320, 150, 35, nextNumber, false, false ); + } + + for ( int i = 0; i < entries.Length; ++i ) + { + DisguiseEntry entry = entries[i]; + + if ( entry == null ) + continue; + + int x = (i % 2) * 205; + int y = (i / 2) * 55; + + if ( entry.m_GumpID != 0 ) + { + AddBackground( 220 + x, 60 + y, 50, 50, 2620 ); + AddImage( 153 + x + entry.m_OffsetX, 15 + y + entry.m_OffsetY, entry.m_GumpID ); + } + + AddHtmlLocalized( 140 + x, 72 + y, 80, 35, entry.m_Number, false, false ); + AddRadio( 118 + x, 73 + y, 208, 209, false, (i * 2) + index ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 ) + { + if ( m_Used ) + m_From.SendLocalizedMessage( 501706 ); // Disguises wear off after 2 hours. + else + m_From.SendLocalizedMessage( 501707 ); // You're looking good. + + return; + } + + int[] switches = info.Switches; + + if ( switches.Length == 0 ) + return; + + int switched = switches[0]; + int type = switched % 2; + int index = switched / 2; + + bool hair = ( type == 0 ); + + DisguiseEntry[] entries = ( hair ? m_HairEntries : m_BeardEntries ); + + if ( index >= 0 && index < entries.Length ) + { + DisguiseEntry entry = entries[index]; + + if ( entry == null ) + return; + + if ( !m_Kit.ValidateUse( m_From ) ) + return; + + if ( !hair && (m_From.Female || m_From.Body.IsFemale) ) + return; + + m_From.NameMod = NameList.RandomName( m_From.Female ? "female" : "male" ); + + if ( m_From is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m_From; + + if ( hair ) + pm.SetHairMods( entry.m_ItemID, -2 ); + else + pm.SetHairMods( -2, entry.m_ItemID ); + } + + m_From.SendGump( new DisguiseGump( m_From, m_Kit, hair, true ) ); + + StopTimer( m_From ); + + m_Timers[m_From] = Timer.DelayCall( TimeSpan.FromHours( 2.0 ), new TimerStateCallback( OnDisguiseExpire ), m_From ); + } + } + + public static void OnDisguiseExpire( object state ) + { + Mobile m = (Mobile)state; + + StopTimer( m ); + + m.NameMod = null; + + if ( m is PlayerMobile ) + ((PlayerMobile)m).SetHairMods( -1, -1 ); + } + + public static bool IsDisguised( Mobile m ) + { + return m_Timers.Contains( m ); + } + + public static bool StopTimer( Mobile m ) + { + Timer t = (Timer)m_Timers[m]; + + if ( t != null ) + { + t.Stop(); + m_Timers.Remove( m ); + } + + return ( t != null ); + } + + private static Hashtable m_Timers = new Hashtable(); + + private static DisguiseEntry[] m_HairEntries = new DisguiseEntry[] + { + new DisguiseEntry( 8251, 50700, 0, 5, 1011052 ), // Short + new DisguiseEntry( 8261, 60710, 0, 3, 1011047 ), // Pageboy + new DisguiseEntry( 8252, 60708, 0,- 5, 1011053 ), // Long + new DisguiseEntry( 8264, 60901, 0, 5, 1011048 ), // Receding + new DisguiseEntry( 8253, 60702, 0,- 5, 1011054 ), // Ponytail + new DisguiseEntry( 8265, 60707, 0,- 5, 1011049 ), // 2-tails + new DisguiseEntry( 8260, 50703, 0, 5, 1011055 ), // Mohawk + new DisguiseEntry( 8266, 60713, 0, 10, 1011050 ), // Topknot + null, + new DisguiseEntry( 0, 0, 0, 0, 1011051 ) // None + }; + + private static DisguiseEntry[] m_BeardEntries = new DisguiseEntry[] + { + new DisguiseEntry( 8269, 50906, 0, 0, 1011401 ), // Vandyke + new DisguiseEntry( 8257, 50808, 0,- 2, 1011062 ), // Mustache + new DisguiseEntry( 8255, 50802, 0, 0, 1011060 ), // Short beard + new DisguiseEntry( 8268, 50905, 0,-10, 1011061 ), // Long beard + new DisguiseEntry( 8267, 50904, 0, 0, 1011060 ), // Short beard + new DisguiseEntry( 8254, 50801, 0,-10, 1011061 ), // Long beard + null, + new DisguiseEntry( 0, 0, 0, 0, 1011051 ) // None + }; + + private class DisguiseEntry + { + public int m_Number; + public int m_ItemID; + public int m_GumpID; + public int m_OffsetX; + public int m_OffsetY; + + public DisguiseEntry( int itemID, int gumpID, int ox, int oy, int name ) + { + m_ItemID = itemID; + m_GumpID = gumpID; + m_OffsetX = ox; + m_OffsetY = oy; + m_Number = name; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Thief/LockPick.cs b/Scripts/Items/Skill Items/Thief/LockPick.cs new file mode 100644 index 000000000..909759c9e --- /dev/null +++ b/Scripts/Items/Skill Items/Thief/LockPick.cs @@ -0,0 +1,171 @@ +using System; +using Server.Network; +using Server.Targeting; +using Server.Items; + +namespace Server.Items +{ + public interface ILockpickable : IPoint2D + { + int LockLevel{ get; set; } + bool Locked{ get; set; } + Mobile Picker{ get; set; } + int MaxLockLevel{ get; set; } + int RequiredSkill{ get; set; } + + void LockPick( Mobile from ); + } + + + + [FlipableAttribute( 0x14fc, 0x14fb )] + public class Lockpick : Item + { + [Constructable] + public Lockpick() : this( 1 ) + { + } + + [Constructable] + public Lockpick( int amount ) : base( 0x14FC ) + { + Stackable = true; + Amount = amount; + } + + public Lockpick( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 && Weight == 0.1 ) + Weight = -1; + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 502068 ); // What do you want to pick? + from.Target = new InternalTarget( this ); + } + + private class InternalTarget : Target + { + private Lockpick m_Item; + + public InternalTarget( Lockpick item ) : base( 1, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) + return; + + if ( targeted is ILockpickable ) + { + Item item = (Item)targeted; + from.Direction = from.GetDirectionTo( item ); + + if ( ((ILockpickable)targeted).Locked ) + { + from.PlaySound( 0x241 ); + + new InternalTimer( from, (ILockpickable)targeted, m_Item ).Start(); + } + else + { + // The door is not locked + from.SendLocalizedMessage( 502069 ); // This does not appear to be locked + } + } + else + { + from.SendLocalizedMessage( 501666 ); // You can't unlock that! + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private ILockpickable m_Item; + private Lockpick m_Lockpick; + + public InternalTimer( Mobile from, ILockpickable item, Lockpick lockpick ) : base( TimeSpan.FromSeconds( 3.0 ) ) + { + m_From = from; + m_Item = item; + m_Lockpick = lockpick; + Priority = TimerPriority.TwoFiftyMS; + } + + protected void BrokeLockPickTest() + { + // When failed, a 25% chance to break the lockpick + if ( Utility.Random( 4 ) == 0 ) + { + Item item = (Item)m_Item; + + // You broke the lockpick. + item.SendLocalizedMessageTo( m_From, 502074 ); + + m_From.PlaySound( 0x3A4 ); + m_Lockpick.Consume(); + } + } + + protected override void OnTick() + { + Item item = (Item)m_Item; + + if ( !m_From.InRange( item.GetWorldLocation(), 1 ) ) + return; + + if ( m_Item.LockLevel == 0 || m_Item.LockLevel == -255 ) + { + // LockLevel of 0 means that the door can't be picklocked + // LockLevel of -255 means it's magic locked + item.SendLocalizedMessageTo( m_From, 502073 ); // This lock cannot be picked by normal means + return; + } + + if ( m_From.Skills[SkillName.Lockpicking].Value < m_Item.RequiredSkill ) + { + /* + // Do some training to gain skills + m_From.CheckSkill( SkillName.Lockpicking, 0, m_Item.LockLevel );*/ + + // The LockLevel is higher thant the LockPicking of the player + item.SendLocalizedMessageTo( m_From, 502072 ); // You don't see how that lock can be manipulated. + return; + } + + if ( m_From.CheckTargetSkill( SkillName.Lockpicking, m_Item, m_Item.LockLevel, m_Item.MaxLockLevel ) ) + { + // Success! Pick the lock! + item.SendLocalizedMessageTo( m_From, 502076 ); // The lock quickly yields to your skill. + m_From.PlaySound( 0x4A ); + m_Item.LockPick( m_From ); + } + else + { + // The player failed to pick the lock + BrokeLockPickTest(); + item.SendLocalizedMessageTo( m_From, 502075 ); // You are unable to pick the lock. + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Axle.cs b/Scripts/Items/Skill Items/Tinkering/Axle.cs new file mode 100644 index 000000000..6e9e814ff --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Axle.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x105B, 0x105C )] + public class Axle : Item + { + [Constructable] + public Axle() : this( 1 ) + { + } + + [Constructable] + public Axle( int amount ) : base( 0x105B ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public Axle( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/AxleGears.cs b/Scripts/Items/Skill Items/Tinkering/AxleGears.cs new file mode 100644 index 000000000..8df820fdb --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/AxleGears.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1051, 0x1052 )] + public class AxleGears : Item + { + [Constructable] + public AxleGears() : this( 1 ) + { + } + + [Constructable] + public AxleGears( int amount ) : base( 0x1051 ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public AxleGears( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/ClockFrame.cs b/Scripts/Items/Skill Items/Tinkering/ClockFrame.cs new file mode 100644 index 000000000..404481f3b --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/ClockFrame.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x104D, 0x104E )] + public class ClockFrame : Item + { + [Constructable] + public ClockFrame() : this( 1 ) + { + } + + [Constructable] + public ClockFrame( int amount ) : base( 0x104D ) + { + Stackable = true; + Amount = amount; + Weight = 2.0; + } + + public ClockFrame( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/ClockParts.cs b/Scripts/Items/Skill Items/Tinkering/ClockParts.cs new file mode 100644 index 000000000..6b8b6d483 --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/ClockParts.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x104F, 0x1050 )] + public class ClockParts : Item + { + [Constructable] + public ClockParts() : this( 1 ) + { + } + + [Constructable] + public ClockParts( int amount ) : base( 0x104F ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public ClockParts( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Clocks.cs b/Scripts/Items/Skill Items/Tinkering/Clocks.cs new file mode 100644 index 000000000..143225141 --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Clocks.cs @@ -0,0 +1,220 @@ +using System; +using Server; + +namespace Server.Items +{ + public enum MoonPhase + { + NewMoon, + WaxingCrescentMoon, + FirstQuarter, + WaxingGibbous, + FullMoon, + WaningGibbous, + LastQuarter, + WaningCrescent + } + + [Flipable( 0x104B, 0x104C )] + public class Clock : Item + { + private static DateTime m_ServerStart; + + public static DateTime ServerStart + { + get{ return m_ServerStart; } + } + + public static void Initialize() + { + m_ServerStart = DateTime.Now; + } + + [Constructable] + public Clock() : this( 0x104B ) + { + } + + [Constructable] + public Clock( int itemID ) : base( itemID ) + { + Weight = 3.0; + } + + public Clock( Serial serial ) : base( serial ) + { + } + + public const double SecondsPerUOMinute = 5.0; + public const double MinutesPerUODay = SecondsPerUOMinute * 24; + + private static DateTime WorldStart = new DateTime( 1997, 9, 1 ); + + public static MoonPhase GetMoonPhase( Map map, int x, int y ) + { + int hours, minutes, totalMinutes; + + GetTime( map, x, y, out hours, out minutes, out totalMinutes ); + + if ( map != null ) + totalMinutes /= 10 + (map.MapIndex * 20); + + return (MoonPhase)(totalMinutes % 8); + } + + public static void GetTime( Map map, int x, int y, out int hours, out int minutes ) + { + int totalMinutes; + + GetTime( map, x, y, out hours, out minutes, out totalMinutes ); + } + + public static void GetTime( Map map, int x, int y, out int hours, out int minutes, out int totalMinutes ) + { + TimeSpan timeSpan = DateTime.Now - WorldStart; + + totalMinutes = (int)(timeSpan.TotalSeconds / SecondsPerUOMinute); + + if ( map != null ) + totalMinutes += map.MapIndex * 320; + + // Really on OSI this must be by subserver + totalMinutes += x / 16; + + hours = (totalMinutes / 60) % 24; + minutes = totalMinutes % 60; + } + + public static void GetTime( out int generalNumber, out string exactTime ) + { + GetTime( null, 0, 0, out generalNumber, out exactTime ); + } + + public static void GetTime( Mobile from, out int generalNumber, out string exactTime ) + { + GetTime( from.Map, from.X, from.Y, out generalNumber, out exactTime ); + } + + public static void GetTime( Map map, int x, int y, out int generalNumber, out string exactTime ) + { + int hours, minutes; + + GetTime( map, x, y, out hours, out minutes ); + + // 00:00 AM - 00:59 AM : Witching hour + // 01:00 AM - 03:59 AM : Middle of night + // 04:00 AM - 07:59 AM : Early morning + // 08:00 AM - 11:59 AM : Late morning + // 12:00 PM - 12:59 PM : Noon + // 01:00 PM - 03:59 PM : Afternoon + // 04:00 PM - 07:59 PM : Early evening + // 08:00 PM - 11:59 AM : Late at night + + if ( hours >= 20 ) + generalNumber = 1042957; // It's late at night + else if ( hours >= 16 ) + generalNumber = 1042956; // It's early in the evening + else if ( hours >= 13 ) + generalNumber = 1042955; // It's the afternoon + else if ( hours >= 12 ) + generalNumber = 1042954; // It's around noon + else if ( hours >= 08 ) + generalNumber = 1042953; // It's late in the morning + else if ( hours >= 04 ) + generalNumber = 1042952; // It's early in the morning + else if ( hours >= 01 ) + generalNumber = 1042951; // It's the middle of the night + else + generalNumber = 1042950; // 'Tis the witching hour. 12 Midnight. + + hours %= 12; + + if ( hours == 0 ) + hours = 12; + + exactTime = String.Format( "{0}:{1:D2}", hours, minutes ); + } + + public override void OnDoubleClick( Mobile from ) + { + int genericNumber; + string exactTime; + + GetTime( from, out genericNumber, out exactTime ); + + SendLocalizedMessageTo( from, genericNumber ); + SendLocalizedMessageTo( from, 1042958, exactTime ); // ~1_TIME~ to be exact + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 3.0; + } + } + + [Flipable( 0x104B, 0x104C )] + public class ClockRight : Clock + { + [Constructable] + public ClockRight() : base( 0x104B ) + { + } + + public ClockRight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x104B, 0x104C )] + public class ClockLeft : Clock + { + [Constructable] + public ClockLeft() : base( 0x104C ) + { + } + + public ClockLeft( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Gears.cs b/Scripts/Items/Skill Items/Tinkering/Gears.cs new file mode 100644 index 000000000..f24b66946 --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Gears.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1053, 0x1054 )] + public class Gears : Item + { + [Constructable] + public Gears() : this( 1 ) + { + } + + [Constructable] + public Gears( int amount ) : base( 0x1053 ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public Gears( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Globe.cs b/Scripts/Items/Skill Items/Tinkering/Globe.cs new file mode 100644 index 000000000..f85da5df3 --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Globe.cs @@ -0,0 +1,32 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Globe : Item + { + [Constructable] + public Globe() : base( 0x1047 ) // It isn't flipable + { + Weight = 3.0; + } + + public Globe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Hinge.cs b/Scripts/Items/Skill Items/Tinkering/Hinge.cs new file mode 100644 index 000000000..625428c3d --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Hinge.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1055, 0x1056 )] + public class Hinge : Item + { + [Constructable] + public Hinge() : this( 1 ) + { + } + + [Constructable] + public Hinge( int amount ) : base( 0x1055 ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public Hinge( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/SextantParts.cs b/Scripts/Items/Skill Items/Tinkering/SextantParts.cs new file mode 100644 index 000000000..079ca035c --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/SextantParts.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x1059, 0x105A )] + public class SextantParts : Item + { + [Constructable] + public SextantParts() : this( 1 ) + { + } + + [Constructable] + public SextantParts( int amount ) : base( 0x1059 ) + { + Stackable = true; + Amount = amount; + Weight = 2.0; + } + + public SextantParts( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Springs.cs b/Scripts/Items/Skill Items/Tinkering/Springs.cs new file mode 100644 index 000000000..1963bb9a9 --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Springs.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Items +{ + [Flipable( 0x105D, 0x105E )] + public class Springs : Item + { + [Constructable] + public Springs() : this( 1 ) + { + } + + [Constructable] + public Springs( int amount ) : base( 0x105D ) + { + Stackable = true; + Amount = amount; + Weight = 1.0; + } + + public Springs( Serial serial ) : base( serial ) + { + } + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Spyglass.cs b/Scripts/Items/Skill Items/Tinkering/Spyglass.cs new file mode 100644 index 000000000..318151c9c --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Spyglass.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using Server.Items; +using Server.Engines.Quests; +using Server.Engines.Quests.Hag; + +namespace Server.Items +{ + [Flipable( 0x14F5, 0x14F6 )] + public class Spyglass : Item + { + [Constructable] + public Spyglass() : base( 0x14F5 ) + { + Weight = 3.0; + } + + public override void OnDoubleClick( Mobile from ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1008155 ); // You peer into the heavens, seeking the moons... + + from.Send( new MessageLocalizedAffix( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 1008146 + (int)Clock.GetMoonPhase( Map.Trammel, from.X, from.Y ), "", AffixType.Prepend, "Trammel : ", "" ) ); + from.Send( new MessageLocalizedAffix( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 1008146 + (int)Clock.GetMoonPhase( Map.Felucca, from.X, from.Y ), "", AffixType.Prepend, "Felucca : ", "" ) ); + + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindIngredientObjective obj = qs.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null && !obj.Completed && obj.Ingredient == Ingredient.StarChart ) + { + int hours, minutes; + Clock.GetTime( from.Map, from.X, from.Y, out hours, out minutes ); + + if ( hours < 5 || hours > 17 ) + { + player.SendLocalizedMessage( 1055040 ); // You gaze up into the glittering night sky. With great care, you compose a chart of the most prominent star patterns. + + obj.Complete(); + } + else + { + player.SendLocalizedMessage( 1055039 ); // You gaze up into the sky, but it is not dark enough to see any stars. + } + } + } + } + } + + public Spyglass( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tinkering/Utensils.cs b/Scripts/Items/Skill Items/Tinkering/Utensils.cs new file mode 100644 index 000000000..3b2c0b5b7 --- /dev/null +++ b/Scripts/Items/Skill Items/Tinkering/Utensils.cs @@ -0,0 +1,277 @@ +using System; + +namespace Server.Items +{ + [Flipable( 0x9F4, 0x9F5, 0x9A3, 0x9A4 )] + public class Fork : Item + { + [Constructable] + public Fork() : base( 0x9F4 ) + { + Weight = 1.0; + } + + public Fork( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ForkLeft : Item + { + [Constructable] + public ForkLeft() : base( 0x9F4 ) + { + Weight = 1.0; + } + + public ForkLeft( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ForkRight : Item + { + [Constructable] + public ForkRight() : base( 0x9F5 ) + { + Weight = 1.0; + } + + public ForkRight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x9F8, 0x9F9, 0x9C2, 0x9C3 )] + public class Spoon : Item + { + [Constructable] + public Spoon() : base( 0x9F8 ) + { + Weight = 1.0; + } + + public Spoon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SpoonLeft : Item + { + [Constructable] + public SpoonLeft() : base( 0x9F8 ) + { + Weight = 1.0; + } + + public SpoonLeft( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SpoonRight : Item + { + [Constructable] + public SpoonRight() : base( 0x9F9 ) + { + Weight = 1.0; + } + + public SpoonRight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + [Flipable( 0x9F6, 0x9F7, 0x9A5, 0x9A6 )] + public class Knife : Item + { + [Constructable] + public Knife() : base( 0x9F6 ) + { + Weight = 1.0; + } + + public Knife( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class KnifeLeft : Item + { + [Constructable] + public KnifeLeft() : base( 0x9F6 ) + { + Weight = 1.0; + } + + public KnifeLeft( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class KnifeRight : Item + { + [Constructable] + public KnifeRight() : base( 0x9F7 ) + { + Weight = 1.0; + } + + public KnifeRight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class Plate : Item + { + [Constructable] + public Plate() : base( 0x9D7 ) + { + Weight = 1.0; + } + + public Plate( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/BaseRunicTool.cs b/Scripts/Items/Skill Items/Tools/BaseRunicTool.cs new file mode 100644 index 000000000..f08c4187e --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/BaseRunicTool.cs @@ -0,0 +1,675 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + public abstract class BaseRunicTool : BaseTool + { + private CraftResource m_Resource; + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource Resource + { + get{ return m_Resource; } + set{ m_Resource = value; Hue = CraftResources.GetHue( m_Resource ); InvalidateProperties(); } + } + + public BaseRunicTool( CraftResource resource, int itemID ) : base( itemID ) + { + m_Resource = resource; + } + + public BaseRunicTool( CraftResource resource, int uses, int itemID ) : base( uses, itemID ) + { + m_Resource = resource; + } + + public BaseRunicTool( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + writer.Write( (int) m_Resource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Resource = (CraftResource)reader.ReadInt(); + break; + } + } + } + + private static bool m_IsRunicTool; + private static int m_LuckChance; + + private static int Scale( int min, int max, int low, int high ) + { + int percent; + + if ( m_IsRunicTool ) + { + percent = Utility.RandomMinMax( min, max ); + } + else + { + // Behold, the worst system ever! + int v = Utility.RandomMinMax( 0, 10000 ); + + v = (int) Math.Sqrt( v ); + v = 100 - v; + + if ( LootPack.CheckLuck( m_LuckChance ) ) + v += 10; + + if ( v < min ) + v = min; + else if ( v > max ) + v = max; + + percent = v; + } + + int scaledBy = Math.Abs( high - low ) + 1; + + if ( scaledBy != 0 ) + scaledBy = 10000 / scaledBy; + + percent *= (10000 + scaledBy); + + return low + (((high - low) * percent) / 1000001); + } + + private static void ApplyAttribute( AosAttributes attrs, int min, int max, AosAttribute attr, int low, int high ) + { + ApplyAttribute( attrs, min, max, attr, low, high, 1 ); + } + + private static void ApplyAttribute( AosAttributes attrs, int min, int max, AosAttribute attr, int low, int high, int scale ) + { + if ( attr == AosAttribute.CastSpeed ) + attrs[attr] += Scale( min, max, low / scale, high / scale ) * scale; + else + attrs[attr] = Scale( min, max, low / scale, high / scale ) * scale; + + if ( attr == AosAttribute.SpellChanneling ) + attrs[AosAttribute.CastSpeed] -= 1; + } + + private static void ApplyAttribute( AosArmorAttributes attrs, int min, int max, AosArmorAttribute attr, int low, int high ) + { + attrs[attr] = Scale( min, max, low, high ); + } + + private static void ApplyAttribute( AosArmorAttributes attrs, int min, int max, AosArmorAttribute attr, int low, int high, int scale ) + { + attrs[attr] = Scale( min, max, low / scale, high / scale ) * scale; + } + + private static void ApplyAttribute( AosWeaponAttributes attrs, int min, int max, AosWeaponAttribute attr, int low, int high ) + { + attrs[attr] = Scale( min, max, low, high ); + } + + private static void ApplyAttribute( AosWeaponAttributes attrs, int min, int max, AosWeaponAttribute attr, int low, int high, int scale ) + { + attrs[attr] = Scale( min, max, low / scale, high / scale ) * scale; + } + + private static void ApplyAttribute( AosElementAttributes attrs, int min, int max, AosElementAttribute attr, int low, int high ) + { + attrs[attr] = Scale( min, max, low, high ); + } + + private static void ApplyAttribute( AosElementAttributes attrs, int min, int max, AosElementAttribute attr, int low, int high, int scale ) + { + attrs[attr] = Scale( min, max, low / scale, high / scale ) * scale; + } + + private static SkillName[] m_PossibleBonusSkills = new SkillName[] + { + SkillName.Swords, + SkillName.Fencing, + SkillName.Macing, + SkillName.Archery, + SkillName.Wrestling, + SkillName.Parry, + SkillName.Tactics, + SkillName.Anatomy, + SkillName.Healing, + SkillName.Magery, + SkillName.Meditation, + SkillName.EvalInt, + SkillName.MagicResist, + SkillName.AnimalTaming, + SkillName.AnimalLore, + SkillName.Veterinary, + SkillName.Musicianship, + SkillName.Provocation, + SkillName.Discordance, + SkillName.Peacemaking, + SkillName.Chivalry, + SkillName.Focus, + SkillName.Necromancy, + SkillName.Stealing, + SkillName.Stealth, + SkillName.SpiritSpeak, + SkillName.Bushido, + SkillName.Ninjitsu + }; + + private static SkillName[] m_PossibleSpellbookSkills = new SkillName[] + { + SkillName.Magery, + SkillName.Meditation, + SkillName.EvalInt, + SkillName.MagicResist + }; + + private static void ApplySkillBonus( AosSkillBonuses attrs, int min, int max, int index, int low, int high ) + { + SkillName[] possibleSkills = ( attrs.Owner is Spellbook ? m_PossibleSpellbookSkills : m_PossibleBonusSkills ); + int count = ( Core.SE ? possibleSkills.Length : possibleSkills.Length - 2 ); + + SkillName sk, check; + double bonus; + bool found; + + do + { + found = false; + sk = possibleSkills[Utility.Random( count )]; + + for ( int i = 0; !found && i < 5; ++i ) + found = ( attrs.GetValues( i, out check, out bonus ) && check == sk ); + } while ( found ); + + attrs.SetValues( index, sk, Scale( min, max, low, high ) ); + } + + private static void ApplyResistance( BaseArmor ar, int min, int max, ResistanceType res, int low, int high ) + { + switch ( res ) + { + case ResistanceType.Physical: ar.PhysicalBonus += Scale( min, max, low, high ); break; + case ResistanceType.Fire: ar.FireBonus += Scale( min, max, low, high ); break; + case ResistanceType.Cold: ar.ColdBonus += Scale( min, max, low, high ); break; + case ResistanceType.Poison: ar.PoisonBonus += Scale( min, max, low, high ); break; + case ResistanceType.Energy: ar.EnergyBonus += Scale( min, max, low, high ); break; + } + } + + private const int MaxProperties = 32; + private static BitArray m_Props = new BitArray( MaxProperties ); + private static int[] m_Possible = new int[MaxProperties]; + + public static int GetUniqueRandom( int count ) + { + int avail = 0; + + for ( int i = 0; i < count; ++i ) + { + if ( !m_Props[i] ) + m_Possible[avail++] = i; + } + + if ( avail == 0 ) + return -1; + + int v = m_Possible[Utility.Random( avail )]; + + m_Props.Set( v, true ); + + return v; + } + + public void ApplyAttributesTo( BaseWeapon weapon ) + { + CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); + + if ( resInfo == null ) + return; + + CraftAttributeInfo attrs = resInfo.AttributeInfo; + + if ( attrs == null ) + return; + + int attributeCount = Utility.RandomMinMax( attrs.RunicMinAttributes, attrs.RunicMaxAttributes ); + int min = attrs.RunicMinIntensity; + int max = attrs.RunicMaxIntensity; + + ApplyAttributesTo( weapon, true, 0, attributeCount, min, max ); + } + + public static void ApplyAttributesTo( BaseWeapon weapon, int attributeCount, int min, int max ) + { + ApplyAttributesTo( weapon, false, 0, attributeCount, min, max ); + } + + public static void 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: + { + 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 2: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitColdArea, 2, 50, 2 ); break; + case 3: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitPoisonArea, 2, 50, 2 ); break; + case 4: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitEnergyArea, 2, 50, 2 ); break; + } + + break; + } + case 1: + { + switch ( Utility.Random( 4 ) ) + { + case 0: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitMagicArrow, 2, 50, 2 ); break; + case 1: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitHarm, 2, 50, 2 ); break; + case 2: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitFireball, 2, 50, 2 ); break; + case 3: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitLightning, 2, 50, 2 ); break; + } + + break; + } + case 2: + { + switch ( Utility.Random( 2 ) ) + { + case 0: ApplyAttribute( secondary, min, max, AosWeaponAttribute.UseBestSkill, 1, 1 ); break; + case 1: ApplyAttribute( secondary, min, max, AosWeaponAttribute.MageWeapon, 1, 10 ); break; + } + + break; + } + case 3: ApplyAttribute( primary, min, max, AosAttribute.WeaponDamage, 1, 50 ); break; + case 4: ApplyAttribute( primary, min, max, AosAttribute.DefendChance, 1, 15 ); break; + case 5: ApplyAttribute( primary, min, max, AosAttribute.CastSpeed, 1, 1 ); break; + case 6: ApplyAttribute( primary, min, max, AosAttribute.AttackChance, 1, 15 ); break; + case 7: ApplyAttribute( primary, min, max, AosAttribute.Luck, 1, 100 ); break; + case 8: ApplyAttribute( primary, min, max, AosAttribute.WeaponSpeed, 5, 30, 5 ); break; + case 9: ApplyAttribute( primary, min, max, AosAttribute.SpellChanneling, 1, 1 ); break; + case 10: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitDispel, 2, 50, 2 ); break; + case 11: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitLeechHits, 2, 50, 2 ); break; + case 12: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitLowerAttack, 2, 50, 2 ); break; + case 13: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitLowerDefend, 2, 50, 2 ); break; + case 14: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitLeechMana, 2, 50, 2 ); break; + case 15: ApplyAttribute( secondary, min, max, AosWeaponAttribute.HitLeechStam, 2, 50, 2 ); break; + case 16: ApplyAttribute( secondary, min, max, AosWeaponAttribute.LowerStatReq, 10, 100, 10 ); break; + case 17: ApplyAttribute( secondary, min, max, AosWeaponAttribute.ResistPhysicalBonus, 1, 15 ); break; + case 18: ApplyAttribute( secondary, min, max, AosWeaponAttribute.ResistFireBonus, 1, 15 ); break; + case 19: ApplyAttribute( secondary, min, max, AosWeaponAttribute.ResistColdBonus, 1, 15 ); break; + case 20: ApplyAttribute( secondary, min, max, AosWeaponAttribute.ResistPoisonBonus, 1, 15 ); break; + case 21: ApplyAttribute( secondary, min, max, AosWeaponAttribute.ResistEnergyBonus, 1, 15 ); break; + case 22: ApplyAttribute( secondary, min, max, AosWeaponAttribute.DurabilityBonus, 10, 100, 10 ); break; + case 23: weapon.Slayer = GetRandomSlayer(); break; + case 24: GetElementalDamages( weapon ); break; + } + } + } + + public static void GetElementalDamages( BaseWeapon weapon ) + { + GetElementalDamages( weapon, true ); + } + + public static void GetElementalDamages( BaseWeapon weapon, bool randomizeOrder ) + { + int fire, phys, cold, nrgy, pois; + + weapon.GetDamageTypes( null, out phys, out fire, out cold, out pois, out nrgy ); + + int totalDamage = phys; + + AosElementAttribute[] attrs = new AosElementAttribute[] + { + AosElementAttribute.Cold, + AosElementAttribute.Energy, + AosElementAttribute.Fire, + AosElementAttribute.Poison + }; + + if( randomizeOrder ) + { + for( int i = 0; i < attrs.Length; i++ ) + { + int rand = Utility.Random( attrs.Length ); + AosElementAttribute temp = attrs[i]; + + attrs[i] = attrs[rand]; + attrs[rand] = temp; + } + } + + + /* + totalDamage = AssignElementalDamage( weapon, AosElementAttribute.Cold, totalDamage ); + totalDamage = AssignElementalDamage( weapon, AosElementAttribute.Energy, totalDamage ); + totalDamage = AssignElementalDamage( weapon, AosElementAttribute.Fire, totalDamage ); + totalDamage = AssignElementalDamage( weapon, AosElementAttribute.Poison, totalDamage ); + + weapon.AosElementDamages[AosElementAttribute.Physical] = 100 - totalDamage; + * */ + + for( int i = 0; i < attrs.Length; i++ ) + totalDamage = AssignElementalDamage( weapon, attrs[i], totalDamage ); + + + //Order is Cold, Energy, Fire, Poison -> Physical left + //Cannot be looped, AoselementAttribute is 'out of order' + + weapon.Hue = weapon.GetElementalDamageHue(); + } + + private static int AssignElementalDamage( BaseWeapon weapon, AosElementAttribute attr, int totalDamage ) + { + if( totalDamage <= 0 ) + return 0; + + int random = Utility.Random( (int)(totalDamage/10) + 1 ) * 10; + weapon.AosElementDamages[attr] = random; + + return (totalDamage - random); + } + + public static SlayerName GetRandomSlayer() + { + // TODO: Check random algorithm on OSI + + SlayerGroup[] groups = SlayerGroup.Groups; + + if ( groups.Length == 0 ) + return SlayerName.None; + + SlayerGroup group = groups[Utility.Random( groups.Length -1 )]; //-1 To Exclude the Fey Slayer which appears ONLY on a certain artifact. + SlayerEntry entry; + + if ( 10 > Utility.Random( 100 ) ) // 10% chance to do super slayer + { + entry = group.Super; + } + else + { + SlayerEntry[] entries = group.Entries; + + if ( entries.Length == 0 ) + return SlayerName.None; + + entry = entries[Utility.Random( entries.Length )]; + } + + return entry.Name; + } + + public void ApplyAttributesTo( BaseArmor armor ) + { + CraftResourceInfo resInfo = CraftResources.GetInfo( m_Resource ); + + if ( resInfo == null ) + return; + + CraftAttributeInfo attrs = resInfo.AttributeInfo; + + if ( attrs == null ) + return; + + int attributeCount = Utility.RandomMinMax( attrs.RunicMinAttributes, attrs.RunicMaxAttributes ); + int min = attrs.RunicMinIntensity; + int max = attrs.RunicMaxIntensity; + + ApplyAttributesTo( armor, true, 0, attributeCount, min, max ); + } + + public static void ApplyAttributesTo( BaseArmor armor, int attributeCount, int min, int max ) + { + ApplyAttributesTo( armor, false, 0, attributeCount, min, max ); + } + + public static void ApplyAttributesTo( BaseArmor armor, bool isRunicTool, int luckChance, int attributeCount, int min, int max ) + { + m_IsRunicTool = isRunicTool; + m_LuckChance = luckChance; + + AosAttributes primary = armor.Attributes; + AosArmorAttributes secondary = armor.ArmorAttributes; + + m_Props.SetAll( false ); + + bool isShield = ( armor is BaseShield ); + int baseCount = ( isShield ? 7 : 20 ); + int baseOffset = ( isShield ? 0 : 4 ); + + if ( !isShield && armor.MeditationAllowance == ArmorMeditationAllowance.All ) + m_Props.Set( 3, true ); // remove mage armor from possible properties + + for ( int i = 0; i < attributeCount; ++i ) + { + int random = GetUniqueRandom( baseCount ); + + if ( random == -1 ) + break; + + random += baseOffset; + + switch ( random ) + { + /* Begin Sheilds */ + case 0: ApplyAttribute( primary, min, max, AosAttribute.SpellChanneling, 1, 1 ); break; + case 1: ApplyAttribute( primary, min, max, AosAttribute.DefendChance, 1, 15 ); break; + case 2: ApplyAttribute( primary, min, max, AosAttribute.AttackChance, 1, 15 ); break; + case 3: ApplyAttribute( primary, min, max, AosAttribute.CastSpeed, 1, 1 ); break; + /* Begin Armor */ + case 4: ApplyAttribute( secondary, min, max, AosArmorAttribute.LowerStatReq, 10, 100, 10 ); break; + case 5: ApplyAttribute( secondary, min, max, AosArmorAttribute.SelfRepair, 1, 5 ); break; + case 6: ApplyAttribute( secondary, min, max, AosArmorAttribute.DurabilityBonus, 10, 100, 10 ); break; + /* End Shields */ + case 7: ApplyAttribute( secondary, min, max, AosArmorAttribute.MageArmor, 1, 1 ); break; + case 8: ApplyAttribute( primary, min, max, AosAttribute.RegenHits, 1, 2 ); break; + case 9: ApplyAttribute( primary, min, max, AosAttribute.RegenStam, 1, 3 ); break; + case 10: ApplyAttribute( primary, min, max, AosAttribute.RegenMana, 1, 2 ); break; + case 11: ApplyAttribute( primary, min, max, AosAttribute.NightSight, 1, 1 ); break; + case 12: ApplyAttribute( primary, min, max, AosAttribute.BonusHits, 1, 5 ); break; + case 13: ApplyAttribute( primary, min, max, AosAttribute.BonusStam, 1, 8 ); break; + case 14: ApplyAttribute( primary, min, max, AosAttribute.BonusMana, 1, 8 ); break; + case 15: ApplyAttribute( primary, min, max, AosAttribute.LowerManaCost, 1, 8 ); break; + case 16: ApplyAttribute( primary, min, max, AosAttribute.LowerRegCost, 1, 20 ); break; + case 17: ApplyAttribute( primary, min, max, AosAttribute.Luck, 1, 100 ); break; + case 18: ApplyAttribute( primary, min, max, AosAttribute.ReflectPhysical, 1, 15 ); break; + case 19: ApplyResistance( armor, min, max, ResistanceType.Physical, 1, 15 ); break; + case 20: ApplyResistance( armor, min, max, ResistanceType.Fire, 1, 15 ); break; + case 21: ApplyResistance( armor, min, max, ResistanceType.Cold, 1, 15 ); break; + case 22: ApplyResistance( armor, min, max, ResistanceType.Poison, 1, 15 ); break; + case 23: ApplyResistance( armor, min, max, ResistanceType.Energy, 1, 15 ); break; + /* End Armor */ + } + } + } + + public static void ApplyAttributesTo( BaseHat hat, int attributeCount, int min, int max ) + { + ApplyAttributesTo( hat, false, 0, attributeCount, min, max ); + } + + public static void ApplyAttributesTo( BaseHat hat, bool isRunicTool, int luckChance, int attributeCount, int min, int max ) + { + m_IsRunicTool = isRunicTool; + m_LuckChance = luckChance; + + AosAttributes primary = hat.Attributes; + AosArmorAttributes secondary = hat.ClothingAttributes; + AosElementAttributes resists = hat.Resistances; + + m_Props.SetAll( false ); + + for ( int i = 0; i < attributeCount; ++i ) + { + int random = GetUniqueRandom( 19 ); + + if ( random == -1 ) + break; + + switch ( random ) + { + case 0: ApplyAttribute( primary, min, max, AosAttribute.ReflectPhysical, 1, 15 ); break; + case 1: ApplyAttribute( primary, min, max, AosAttribute.RegenHits, 1, 2 ); break; + case 2: ApplyAttribute( primary, min, max, AosAttribute.RegenStam, 1, 3 ); break; + case 3: ApplyAttribute( primary, min, max, AosAttribute.RegenMana, 1, 2 ); break; + case 4: ApplyAttribute( primary, min, max, AosAttribute.NightSight, 1, 1 ); break; + case 5: ApplyAttribute( primary, min, max, AosAttribute.BonusHits, 1, 5 ); break; + case 6: ApplyAttribute( primary, min, max, AosAttribute.BonusStam, 1, 8 ); break; + case 7: ApplyAttribute( primary, min, max, AosAttribute.BonusMana, 1, 8 ); break; + case 8: ApplyAttribute( primary, min, max, AosAttribute.LowerManaCost, 1, 8 ); break; + case 9: ApplyAttribute( primary, min, max, AosAttribute.LowerRegCost, 1, 20 ); break; + case 10: ApplyAttribute( primary, min, max, AosAttribute.Luck, 1, 100 ); break; + case 11: ApplyAttribute( secondary, min, max, AosArmorAttribute.LowerStatReq, 10, 100, 10 ); break; + case 12: ApplyAttribute( secondary, min, max, AosArmorAttribute.SelfRepair, 1, 5 ); break; + case 13: ApplyAttribute( secondary, min, max, AosArmorAttribute.DurabilityBonus, 10, 100, 10 ); break; + case 14: ApplyAttribute( resists, min, max, AosElementAttribute.Physical, 1, 15 ); break; + case 15: ApplyAttribute( resists, min, max, AosElementAttribute.Fire, 1, 15 ); break; + case 16: ApplyAttribute( resists, min, max, AosElementAttribute.Cold, 1, 15 ); break; + case 17: ApplyAttribute( resists, min, max, AosElementAttribute.Poison, 1, 15 ); break; + case 18: ApplyAttribute( resists, min, max, AosElementAttribute.Energy, 1, 15 ); break; + } + } + } + + public static void ApplyAttributesTo( BaseJewel jewelry, int attributeCount, int min, int max ) + { + ApplyAttributesTo( jewelry, false, 0, attributeCount, min, max ); + } + + public static void ApplyAttributesTo( BaseJewel jewelry, bool isRunicTool, int luckChance, int attributeCount, int min, int max ) + { + m_IsRunicTool = isRunicTool; + m_LuckChance = luckChance; + + AosAttributes primary = jewelry.Attributes; + AosElementAttributes resists = jewelry.Resistances; + AosSkillBonuses skills = jewelry.SkillBonuses; + + m_Props.SetAll( false ); + + for ( int i = 0; i < attributeCount; ++i ) + { + int random = GetUniqueRandom( 24 ); + + if ( random == -1 ) + break; + + switch ( random ) + { + case 0: ApplyAttribute( resists, min, max, AosElementAttribute.Physical, 1, 15 ); break; + case 1: ApplyAttribute( resists, min, max, AosElementAttribute.Fire, 1, 15 ); break; + case 2: ApplyAttribute( resists, min, max, AosElementAttribute.Cold, 1, 15 ); break; + case 3: ApplyAttribute( resists, min, max, AosElementAttribute.Poison, 1, 15 ); break; + case 4: ApplyAttribute( resists, min, max, AosElementAttribute.Energy, 1, 15 ); break; + case 5: ApplyAttribute( primary, min, max, AosAttribute.WeaponDamage, 1, 25 ); break; + case 6: ApplyAttribute( primary, min, max, AosAttribute.DefendChance, 1, 15 ); break; + case 7: ApplyAttribute( primary, min, max, AosAttribute.AttackChance, 1, 15 ); break; + case 8: ApplyAttribute( primary, min, max, AosAttribute.BonusStr, 1, 8 ); break; + case 9: ApplyAttribute( primary, min, max, AosAttribute.BonusDex, 1, 8 ); break; + case 10: ApplyAttribute( primary, min, max, AosAttribute.BonusInt, 1, 8 ); break; + case 11: ApplyAttribute( primary, min, max, AosAttribute.EnhancePotions, 5, 25, 5 ); break; + case 12: ApplyAttribute( primary, min, max, AosAttribute.CastSpeed, 1, 1 ); break; + case 13: ApplyAttribute( primary, min, max, AosAttribute.CastRecovery, 1, 3 ); break; + case 14: ApplyAttribute( primary, min, max, AosAttribute.LowerManaCost, 1, 8 ); break; + case 15: ApplyAttribute( primary, min, max, AosAttribute.LowerRegCost, 1, 20 ); break; + case 16: ApplyAttribute( primary, min, max, AosAttribute.Luck, 1, 100 ); break; + case 17: ApplyAttribute( primary, min, max, AosAttribute.SpellDamage, 1, 12 ); break; + case 18: ApplyAttribute( primary, min, max, AosAttribute.NightSight, 1, 1 ); break; + case 19: ApplySkillBonus( skills, min, max, 0, 1, 15 ); break; + case 20: ApplySkillBonus( skills, min, max, 1, 1, 15 ); break; + case 21: ApplySkillBonus( skills, min, max, 2, 1, 15 ); break; + case 22: ApplySkillBonus( skills, min, max, 3, 1, 15 ); break; + case 23: ApplySkillBonus( skills, min, max, 4, 1, 15 ); break; + } + } + } + + public static void ApplyAttributesTo( Spellbook spellbook, int attributeCount, int min, int max ) + { + ApplyAttributesTo( spellbook, false, 0, attributeCount, min, max ); + } + + public static void ApplyAttributesTo( Spellbook spellbook, bool isRunicTool, int luckChance, int attributeCount, int min, int max ) + { + m_IsRunicTool = isRunicTool; + m_LuckChance = luckChance; + + AosAttributes primary = spellbook.Attributes; + AosSkillBonuses skills = spellbook.SkillBonuses; + + m_Props.SetAll( false ); + + for ( int i = 0; i < attributeCount; ++i ) + { + int random = GetUniqueRandom( 16 ); + + if ( random == -1 ) + break; + + switch ( random ) + { + case 0: + case 1: + case 2: + case 3: + { + ApplyAttribute( primary, min, max, AosAttribute.BonusInt, 1, 8 ); + + for ( int j = 0; j < 4; ++j ) + m_Props.Set( j, true ); + + break; + } + case 4: ApplyAttribute( primary, min, max, AosAttribute.BonusMana, 1, 8 ); break; + case 5: ApplyAttribute( primary, min, max, AosAttribute.CastSpeed, 1, 1 ); break; + case 6: ApplyAttribute( primary, min, max, AosAttribute.CastRecovery, 1, 3 ); break; + case 7: ApplyAttribute( primary, min, max, AosAttribute.SpellDamage, 1, 12 ); break; + case 8: ApplySkillBonus( skills, min, max, 0, 1, 15 ); break; + case 9: ApplySkillBonus( skills, min, max, 1, 1, 15 ); break; + case 10: ApplySkillBonus( skills, min, max, 2, 1, 15 ); break; + case 11: ApplySkillBonus( skills, min, max, 3, 1, 15 ); break; + case 12: ApplyAttribute( primary, min, max, AosAttribute.LowerRegCost, 1, 20 ); break; + case 13: ApplyAttribute( primary, min, max, AosAttribute.LowerManaCost, 1, 8 ); break; + case 14: ApplyAttribute( primary, min, max, AosAttribute.RegenMana, 1, 2 ); break; + case 15: spellbook.Slayer = GetRandomSlayer(); break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/BaseTool.cs b/Scripts/Items/Skill Items/Tools/BaseTool.cs new file mode 100644 index 000000000..51b8b2e43 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/BaseTool.cs @@ -0,0 +1,197 @@ +using System; +using Server; +using Server.Network; +using Server.Engines.Craft; + +namespace Server.Items +{ + public enum ToolQuality + { + Low, + Regular, + Exceptional + } + + public abstract class BaseTool : Item, IUsesRemaining, ICraftable + { + private Mobile m_Crafter; + private ToolQuality m_Quality; + private int m_UsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Crafter + { + get{ return m_Crafter; } + set{ m_Crafter = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public ToolQuality Quality + { + get{ return m_Quality; } + set{ UnscaleUses(); m_Quality = value; InvalidateProperties(); ScaleUses(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + public void ScaleUses() + { + m_UsesRemaining = (m_UsesRemaining * GetUsesScalar()) / 100; + InvalidateProperties(); + } + + public void UnscaleUses() + { + m_UsesRemaining = (m_UsesRemaining * 100) / GetUsesScalar(); + } + + public int GetUsesScalar() + { + if ( m_Quality == ToolQuality.Exceptional ) + return 200; + + return 100; + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + public abstract CraftSystem CraftSystem{ get; } + + public BaseTool( int itemID ) : this( Utility.RandomMinMax( 25, 75 ), itemID ) + { + } + + public BaseTool( int uses, int itemID ) : base( itemID ) + { + m_UsesRemaining = uses; + m_Quality = ToolQuality.Regular; + } + + public BaseTool( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + // Makers mark not displayed on OSI + //if ( m_Crafter != null ) + // list.Add( 1050043, m_Crafter.Name ); // crafted by ~1_NAME~ + + if ( m_Quality == ToolQuality.Exceptional ) + list.Add( 1060636 ); // exceptional + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + } + + public virtual void DisplayDurabilityTo( Mobile m ) + { + LabelToAffix( m, 1017323, AffixType.Append, ": " + m_UsesRemaining.ToString() ); // Durability + } + + public static bool CheckAccessible( Item tool, Mobile m ) + { + return ( tool.IsChildOf( m ) || tool.Parent == m ); + } + + public static bool CheckTool( Item tool, Mobile m ) + { + Item check = m.FindItemOnLayer( Layer.OneHanded ); + + if ( check is BaseTool && check != tool && !(check is AncientSmithyHammer) ) + return false; + + check = m.FindItemOnLayer( Layer.TwoHanded ); + + if ( check is BaseTool && check != tool && !(check is AncientSmithyHammer) ) + return false; + + return true; + } + + public override void OnSingleClick( Mobile from ) + { + DisplayDurabilityTo( from ); + + base.OnSingleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) || Parent == from ) + { + CraftSystem system = this.CraftSystem; + + int num = system.CanCraft( from, this, null ); + + if ( num > 0 ) + { + from.SendLocalizedMessage( num ); + } + else + { + CraftContext context = system.GetContext( from ); + + from.SendGump( new CraftGump( from, system, this, null ) ); + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (Mobile) m_Crafter ); + writer.Write( (int) m_Quality ); + + writer.Write( (int) m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Crafter = reader.ReadMobile(); + m_Quality = (ToolQuality) reader.ReadInt(); + goto case 0; + } + case 0: + { + m_UsesRemaining = reader.ReadInt(); + break; + } + } + } + #region ICraftable Members + + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue ) + { + Quality = (ToolQuality)quality; + + if ( makersMark ) + Crafter = from; + + return quality; + } + + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Blowpipe.cs b/Scripts/Items/Skill Items/Tools/Blowpipe.cs new file mode 100644 index 000000000..c48d9c192 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Blowpipe.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0xE8A, 0xE89 )] + public class Blowpipe : BaseTool + { + public override CraftSystem CraftSystem { get { return DefGlassblowing.CraftSystem; } } + + public override int LabelNumber{ get{ return 1044608; } } // blow pipe + + [Constructable] + public Blowpipe() : base( 0xE8A ) + { + Weight = 4.0; + Hue = 0x3B9; + } + + [Constructable] + public Blowpipe( int uses ) : base( uses, 0xE8A ) + { + Weight = 4.0; + Hue = 0x3B9; + } + + public Blowpipe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 4.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/DovetailSaw.cs b/Scripts/Items/Skill Items/Tools/DovetailSaw.cs new file mode 100644 index 000000000..6757712a7 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/DovetailSaw.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x1028, 0x1029 )] + public class DovetailSaw : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public DovetailSaw() : base( 0x1028 ) + { + Weight = 2.0; + } + + [Constructable] + public DovetailSaw( int uses ) : base( uses, 0x1028 ) + { + Weight = 2.0; + } + + public DovetailSaw( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/DrawKnife.cs b/Scripts/Items/Skill Items/Tools/DrawKnife.cs new file mode 100644 index 000000000..a9d57bd67 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/DrawKnife.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class DrawKnife : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public DrawKnife() : base( 0x10E4 ) + { + Weight = 1.0; + } + + [Constructable] + public DrawKnife( int uses ) : base( uses, 0x10E4 ) + { + Weight = 1.0; + } + + public DrawKnife( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/FletcherTools.cs b/Scripts/Items/Skill Items/Tools/FletcherTools.cs new file mode 100644 index 000000000..0b369a264 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/FletcherTools.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x1022, 0x1023 )] + public class FletcherTools : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefBowFletching.CraftSystem; } } + + [Constructable] + public FletcherTools() : base( 0x1022 ) + { + Weight = 2.0; + } + + [Constructable] + public FletcherTools( int uses ) : base( uses, 0x1022 ) + { + Weight = 2.0; + } + + public FletcherTools( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/FlourSifter.cs b/Scripts/Items/Skill Items/Tools/FlourSifter.cs new file mode 100644 index 000000000..551357021 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/FlourSifter.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class FlourSifter : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCooking.CraftSystem; } } + + [Constructable] + public FlourSifter() : base( 0x103E ) + { + Weight = 1.0; + } + + [Constructable] + public FlourSifter( int uses ) : base( uses, 0x103E ) + { + Weight = 1.0; + } + + public FlourSifter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Froe.cs b/Scripts/Items/Skill Items/Tools/Froe.cs new file mode 100644 index 000000000..b82822601 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Froe.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class Froe : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public Froe() : base( 0x10E5 ) + { + Weight = 1.0; + } + + [Constructable] + public Froe( int uses ) : base( uses, 0x10E5 ) + { + Weight = 1.0; + } + + public Froe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Hammer.cs b/Scripts/Items/Skill Items/Tools/Hammer.cs new file mode 100644 index 000000000..2b0d0b7f3 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Hammer.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class Hammer : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public Hammer() : base( 0x102A ) + { + Weight = 2.0; + } + + [Constructable] + public Hammer( int uses ) : base( uses, 0x102A ) + { + Weight = 2.0; + } + + public Hammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Inshave.cs b/Scripts/Items/Skill Items/Tools/Inshave.cs new file mode 100644 index 000000000..b06602724 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Inshave.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class Inshave : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public Inshave() : base( 0x10E6 ) + { + Weight = 1.0; + } + + [Constructable] + public Inshave( int uses ) : base( uses, 0x10E6 ) + { + Weight = 1.0; + } + + public Inshave( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/JointingPlane.cs b/Scripts/Items/Skill Items/Tools/JointingPlane.cs new file mode 100644 index 000000000..39ead3357 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/JointingPlane.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x1030, 0x1031 )] + public class JointingPlane : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public JointingPlane() : base( 0x1030 ) + { + Weight = 2.0; + } + + [Constructable] + public JointingPlane( int uses ) : base( uses, 0x1030 ) + { + Weight = 2.0; + } + + public JointingPlane( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/MalletAndChisel.cs b/Scripts/Items/Skill Items/Tools/MalletAndChisel.cs new file mode 100644 index 000000000..af82adeb4 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/MalletAndChisel.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class MalletAndChisel : BaseTool + { + public override CraftSystem CraftSystem { get { return DefMasonry.CraftSystem; } } + + [Constructable] + public MalletAndChisel() : base( 0x12B3 ) + { + Weight = 1.0; + } + + [Constructable] + public MalletAndChisel( int uses ) : base( uses, 0x12B3 ) + { + Weight = 1.0; + } + + public MalletAndChisel( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/MapmakersPen.cs b/Scripts/Items/Skill Items/Tools/MapmakersPen.cs new file mode 100644 index 000000000..d46f6e765 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/MapmakersPen.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x0FBF, 0x0FC0 )] + public class MapmakersPen : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCartography.CraftSystem; } } + + public override int LabelNumber{ get{ return 1044167; } } // mapmaker's pen + + [Constructable] + public MapmakersPen() : base( 0x0FBF ) + { + Weight = 1.0; + } + + [Constructable] + public MapmakersPen( int uses ) : base( uses, 0x0FBF ) + { + Weight = 1.0; + } + + public MapmakersPen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/MortarPestle.cs b/Scripts/Items/Skill Items/Tools/MortarPestle.cs new file mode 100644 index 000000000..bd3f7b036 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/MortarPestle.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class MortarPestle : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefAlchemy.CraftSystem; } } + + [Constructable] + public MortarPestle() : base( 0xE9B ) + { + Weight = 1.0; + } + + [Constructable] + public MortarPestle( int uses ) : base( uses, 0xE9B ) + { + Weight = 1.0; + } + + public MortarPestle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/MouldingPlane.cs b/Scripts/Items/Skill Items/Tools/MouldingPlane.cs new file mode 100644 index 000000000..961af56e6 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/MouldingPlane.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x102C, 0x102D )] + public class MouldingPlane : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public MouldingPlane() : base( 0x102C ) + { + Weight = 2.0; + } + + [Constructable] + public MouldingPlane( int uses ) : base( uses, 0x102C ) + { + Weight = 2.0; + } + + public MouldingPlane( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Nails.cs b/Scripts/Items/Skill Items/Tools/Nails.cs new file mode 100644 index 000000000..d48ffbf4d --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Nails.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x102E, 0x102F )] + public class Nails : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public Nails() : base( 0x102E ) + { + Weight = 2.0; + } + + [Constructable] + public Nails( int uses ) : base( uses, 0x102C ) + { + Weight = 2.0; + } + + public Nails( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/RollingPin.cs b/Scripts/Items/Skill Items/Tools/RollingPin.cs new file mode 100644 index 000000000..aa2a3a78b --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/RollingPin.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class RollingPin : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCooking.CraftSystem; } } + + [Constructable] + public RollingPin() : base( 0x1043 ) + { + Weight = 1.0; + } + + [Constructable] + public RollingPin( int uses ) : base( uses, 0x1043 ) + { + Weight = 1.0; + } + + public RollingPin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/RunicHammer.cs b/Scripts/Items/Skill Items/Tools/RunicHammer.cs new file mode 100644 index 000000000..fa68252f9 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/RunicHammer.cs @@ -0,0 +1,79 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x13E4, 0x13E3 )] + public class RunicHammer : BaseRunicTool + { + public override CraftSystem CraftSystem{ get{ return DefBlacksmithy.CraftSystem; } } + + public override int LabelNumber + { + get + { + int index = CraftResources.GetIndex( Resource ); + + if ( index >= 1 && index <= 8 ) + return 1049019 + index; + + return 1045128; // runic smithy hammer + } + } + + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + + int index = CraftResources.GetIndex( Resource ); + + if ( index >= 1 && index <= 8 ) + return; + + if ( !CraftResources.IsStandard( Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( Resource ); + + if ( num > 0 ) + list.Add( num ); + else + list.Add( CraftResources.GetName( Resource ) ); + } + } + + [Constructable] + public RunicHammer( CraftResource resource ) : base( resource, 0x13E3 ) + { + Weight = 8.0; + Layer = Layer.OneHanded; + Hue = CraftResources.GetHue( resource ); + } + + [Constructable] + public RunicHammer( CraftResource resource, int uses ) : base( resource, uses, 0x13E3 ) + { + Weight = 8.0; + Layer = Layer.OneHanded; + Hue = CraftResources.GetHue( resource ); + } + + public RunicHammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/RunicSewingKit.cs b/Scripts/Items/Skill Items/Tools/RunicSewingKit.cs new file mode 100644 index 000000000..05fd1491e --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/RunicSewingKit.cs @@ -0,0 +1,80 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class RunicSewingKit : BaseRunicTool + { + public override CraftSystem CraftSystem{ get{ return DefTailoring.CraftSystem; } } + + public override void AddNameProperty( ObjectPropertyList list ) + { + string v = " "; + + if ( !CraftResources.IsStandard( Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( Resource ); + + if ( num > 0 ) + v = String.Format( "#{0}", num ); + else + v = CraftResources.GetName( Resource ); + } + + list.Add( 1061119, v ); // ~1_LEATHER_TYPE~ runic sewing kit + } + + public override void OnSingleClick( Mobile from ) + { + string v = " "; + + if ( !CraftResources.IsStandard( Resource ) ) + { + int num = CraftResources.GetLocalizationNumber( Resource ); + + if ( num > 0 ) + v = String.Format( "#{0}", num ); + else + v = CraftResources.GetName( Resource ); + } + + LabelTo( from, 1061119, v ); // ~1_LEATHER_TYPE~ runic sewing kit + } + + [Constructable] + public RunicSewingKit( CraftResource resource ) : base( resource, 0xF9D ) + { + Weight = 2.0; + Hue = CraftResources.GetHue( resource ); + } + + [Constructable] + public RunicSewingKit( CraftResource resource, int uses ) : base( resource, uses, 0xF9D ) + { + Weight = 2.0; + Hue = CraftResources.GetHue( resource ); + } + + public RunicSewingKit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( ItemID == 0x13E4 || ItemID == 0x13E3 ) + ItemID = 0xF9D; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Saw.cs b/Scripts/Items/Skill Items/Tools/Saw.cs new file mode 100644 index 000000000..0a117aeb9 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Saw.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x1034, 0x1035 )] + public class Saw : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public Saw() : base( 0x1034 ) + { + Weight = 2.0; + } + + [Constructable] + public Saw( int uses ) : base( uses, 0x1034 ) + { + Weight = 2.0; + } + + public Saw( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Scorp.cs b/Scripts/Items/Skill Items/Tools/Scorp.cs new file mode 100644 index 000000000..c54a622b1 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Scorp.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class Scorp : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public Scorp() : base( 0x10E7 ) + { + Weight = 1.0; + } + + [Constructable] + public Scorp( int uses ) : base( uses, 0x10E7 ) + { + Weight = 1.0; + } + + public Scorp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/ScribesPen.cs b/Scripts/Items/Skill Items/Tools/ScribesPen.cs new file mode 100644 index 000000000..6fda8eb6b --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/ScribesPen.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x0FBF, 0x0FC0 )] + public class ScribesPen : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefInscription.CraftSystem; } } + + public override int LabelNumber{ get{ return 1044168; } } // scribe's pen + + [Constructable] + public ScribesPen() : base( 0x0FBF ) + { + Weight = 1.0; + } + + [Constructable] + public ScribesPen( int uses ) : base( uses, 0x0FBF ) + { + Weight = 1.0; + } + + public ScribesPen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 1.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/SewingKit.cs b/Scripts/Items/Skill Items/Tools/SewingKit.cs new file mode 100644 index 000000000..62d2ad127 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/SewingKit.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class SewingKit : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefTailoring.CraftSystem; } } + + [Constructable] + public SewingKit() : base( 0xF9D ) + { + Weight = 2.0; + } + + [Constructable] + public SewingKit( int uses ) : base( uses, 0xF9D ) + { + Weight = 2.0; + } + + public SewingKit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Skillet.cs b/Scripts/Items/Skill Items/Tools/Skillet.cs new file mode 100644 index 000000000..a5de55d07 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Skillet.cs @@ -0,0 +1,43 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + public class Skillet : BaseTool + { + public override int LabelNumber{ get{ return 1044567; } } // skillet + + public override CraftSystem CraftSystem{ get{ return DefCooking.CraftSystem; } } + + [Constructable] + public Skillet() : base( 0x97F ) + { + Weight = 1.0; + } + + [Constructable] + public Skillet( int uses ) : base( uses, 0x97F ) + { + Weight = 1.0; + } + + public Skillet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/SledgeHammer.cs b/Scripts/Items/Skill Items/Tools/SledgeHammer.cs new file mode 100644 index 000000000..e1ef2837f --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/SledgeHammer.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0xFB5, 0xFB4 )] + public class SledgeHammer : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefBlacksmithy.CraftSystem; } } + + [Constructable] + public SledgeHammer() : base( 0xFB5 ) + { + Layer = Layer.OneHanded; + } + + [Constructable] + public SledgeHammer( int uses ) : base( uses, 0xFB5 ) + { + Layer = Layer.OneHanded; + } + + public SledgeHammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Skill Items/Tools/SmithHammer.cs b/Scripts/Items/Skill Items/Tools/SmithHammer.cs new file mode 100644 index 000000000..2a11176e2 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/SmithHammer.cs @@ -0,0 +1,44 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x13E4, 0x13E3 )] + public class SmithHammer : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefBlacksmithy.CraftSystem; } } + + [Constructable] + public SmithHammer() : base( 0x13E3 ) + { + Weight = 8.0; + Layer = Layer.OneHanded; + } + + [Constructable] + public SmithHammer( int uses ) : base( uses, 0x13E3 ) + { + Weight = 8.0; + Layer = Layer.OneHanded; + } + + public SmithHammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/SmoothingPlane.cs b/Scripts/Items/Skill Items/Tools/SmoothingPlane.cs new file mode 100644 index 000000000..d95f05b15 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/SmoothingPlane.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x1032, 0x1033 )] + public class SmoothingPlane : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefCarpentry.CraftSystem; } } + + [Constructable] + public SmoothingPlane() : base( 0x1032 ) + { + Weight = 1.0; + } + + [Constructable] + public SmoothingPlane( int uses ) : base( uses, 0x1032 ) + { + Weight = 1.0; + } + + public SmoothingPlane( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/TinkerTools.cs b/Scripts/Items/Skill Items/Tools/TinkerTools.cs new file mode 100644 index 000000000..32dad917b --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/TinkerTools.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Flipable( 0x1EB8, 0x1EB9 )] + public class TinkerTools : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefTinkering.CraftSystem; } } + + [Constructable] + public TinkerTools() : base( 0x1EB8 ) + { + Weight = 1.0; + } + + [Constructable] + public TinkerTools( int uses ) : base( uses, 0x1EB8 ) + { + Weight = 1.0; + } + + public TinkerTools( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Skill Items/Tools/Tongs.cs b/Scripts/Items/Skill Items/Tools/Tongs.cs new file mode 100644 index 000000000..ca70ab945 --- /dev/null +++ b/Scripts/Items/Skill Items/Tools/Tongs.cs @@ -0,0 +1,42 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0xfbb, 0xfbc )] + public class Tongs : BaseTool + { + public override CraftSystem CraftSystem{ get{ return DefBlacksmithy.CraftSystem; } } + + [Constructable] + public Tongs() : base( 0xFBB ) + { + Weight = 2.0; + } + + [Constructable] + public Tongs( int uses ) : base( uses, 0xFBB ) + { + Weight = 2.0; + } + + public Tongs( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs new file mode 100644 index 000000000..ca4986af5 --- /dev/null +++ b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs @@ -0,0 +1,134 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [FlipableAttribute( 0x13E4, 0x13E3 )] + public class AncientSmithyHammer : BaseTool + { + private int m_Bonus; + private SkillMod m_SkillMod; + + [CommandProperty( AccessLevel.GameMaster )] + public int Bonus + { + get + { + return m_Bonus; + } + set + { + m_Bonus = value; + InvalidateProperties(); + + if ( m_Bonus == 0 ) + { + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = null; + } + else if ( m_SkillMod == null && Parent is Mobile ) + { + m_SkillMod = new DefaultSkillMod( SkillName.Blacksmith, true, m_Bonus ); + ((Mobile)Parent).AddSkillMod( m_SkillMod ); + } + else if ( m_SkillMod != null ) + { + m_SkillMod.Value = m_Bonus; + } + } + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( m_Bonus != 0 && parent is Mobile ) + { + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = new DefaultSkillMod( SkillName.Blacksmith, true, m_Bonus ); + ((Mobile)parent).AddSkillMod( m_SkillMod ); + } + } + + public override void OnRemoved( object parent ) + { + base.OnRemoved( parent ); + + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = null; + } + + public override CraftSystem CraftSystem{ get{ return DefBlacksmithy.CraftSystem; } } + public override int LabelNumber{ get{ return 1045127; } } // ancient smithy hammer + + [Constructable] + public AncientSmithyHammer( int bonus ) : this( bonus, 600 ) + { + } + + [Constructable] + public AncientSmithyHammer( int bonus, int uses ) : base( uses, 0x13E4 ) + { + m_Bonus = bonus; + Weight = 8.0; + Layer = Layer.OneHanded; + Hue = 0x482; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Bonus != 0 ) + list.Add( 1060451, "#1042354\t{0}", m_Bonus.ToString() ); // ~1_skillname~ +~2_val~ + } + + public AncientSmithyHammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Bonus ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Bonus = reader.ReadInt(); + break; + } + } + + if ( m_Bonus != 0 && Parent is Mobile ) + { + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = new DefaultSkillMod( SkillName.Blacksmith, true, m_Bonus ); + ((Mobile)Parent).AddSkillMod( m_SkillMod ); + } + + if ( Hue == 0 ) + Hue = 0x482; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs new file mode 100644 index 000000000..f5aa60777 --- /dev/null +++ b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Engines.Craft; + +namespace Server.Items +{ + [Anvil, Flipable( 0xFAF, 0xFB0 )] + public class ColoredAnvil : Item + { + [Constructable] + public ColoredAnvil() : this( CraftResources.GetHue( (CraftResource)Utility.RandomMinMax( (int)CraftResource.DullCopper, (int)CraftResource.Valorite ) ) ) + { + } + + [Constructable] + public ColoredAnvil( int hue ) : base( 0xFAF ) + { + Hue = hue; + Weight = 20; + } + + public ColoredAnvil( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs new file mode 100644 index 000000000..89883363d --- /dev/null +++ b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs @@ -0,0 +1,256 @@ +using System; + +namespace Server.Items +{ + [FlipableAttribute( 0x13c6, 0x13ce )] + public class LeatherGlovesOfMining : BaseGlovesOfMining + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 40; } } + + public override int AosStrReq{ get{ return 20; } } + public override int OldStrReq{ get{ return 10; } } + + public override int ArmorBase{ get{ return 13; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Leather; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override ArmorMeditationAllowance DefMedAllowance{ get{ return ArmorMeditationAllowance.All; } } + + public override int LabelNumber{ get{ return 1045122; } } // leather blacksmith gloves of mining + + [Constructable] + public LeatherGlovesOfMining( int bonus ) : base( bonus, 0x13C6 ) + { + Weight = 1; + } + + public LeatherGlovesOfMining( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x13d5, 0x13dd )] + public class StuddedGlovesOfMining : BaseGlovesOfMining + { + public override int BasePhysicalResistance{ get{ return 2; } } + public override int BaseFireResistance{ get{ return 4; } } + public override int BaseColdResistance{ get{ return 3; } } + public override int BasePoisonResistance{ get{ return 3; } } + public override int BaseEnergyResistance{ get{ return 4; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 45; } } + + public override int AosStrReq{ get{ return 25; } } + public override int OldStrReq{ get{ return 25; } } + + public override int ArmorBase{ get{ return 16; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Studded; } } + public override CraftResource DefaultResource{ get{ return CraftResource.RegularLeather; } } + + public override int LabelNumber{ get{ return 1045123; } } // studded leather blacksmith gloves of mining + + [Constructable] + public StuddedGlovesOfMining( int bonus ) : base( bonus, 0x13D5 ) + { + Weight = 2; + } + + public StuddedGlovesOfMining( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + [FlipableAttribute( 0x13eb, 0x13f2 )] + public class RingmailGlovesOfMining : BaseGlovesOfMining + { + public override int BasePhysicalResistance{ get{ return 3; } } + public override int BaseFireResistance{ get{ return 3; } } + public override int BaseColdResistance{ get{ return 1; } } + public override int BasePoisonResistance{ get{ return 5; } } + public override int BaseEnergyResistance{ get{ return 3; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 50; } } + + public override int AosStrReq{ get{ return 40; } } + public override int OldStrReq{ get{ return 20; } } + + public override int OldDexBonus{ get{ return -1; } } + + public override int ArmorBase{ get{ return 22; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Ringmail; } } + + public override int LabelNumber{ get{ return 1045124; } } // ringmail blacksmith gloves of mining + + [Constructable] + public RingmailGlovesOfMining( int bonus ) : base( bonus, 0x13EB ) + { + Weight = 1; + } + + public RingmailGlovesOfMining( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public abstract class BaseGlovesOfMining : BaseArmor + { + private int m_Bonus; + private SkillMod m_SkillMod; + + [CommandProperty( AccessLevel.GameMaster )] + public int Bonus + { + get + { + return m_Bonus; + } + set + { + m_Bonus = value; + InvalidateProperties(); + + if ( m_Bonus == 0 ) + { + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = null; + } + else if ( m_SkillMod == null && Parent is Mobile ) + { + m_SkillMod = new DefaultSkillMod( SkillName.Mining, true, m_Bonus ); + ((Mobile)Parent).AddSkillMod( m_SkillMod ); + } + else if ( m_SkillMod != null ) + { + m_SkillMod.Value = m_Bonus; + } + } + } + + public override void OnAdded( object parent ) + { + base.OnAdded( parent ); + + if ( m_Bonus != 0 && parent is Mobile ) + { + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = new DefaultSkillMod( SkillName.Mining, true, m_Bonus ); + ((Mobile)parent).AddSkillMod( m_SkillMod ); + } + } + + public override void OnRemoved( object parent ) + { + base.OnRemoved( parent ); + + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = null; + } + + public BaseGlovesOfMining( int bonus, int itemID ) : base( itemID ) + { + m_Bonus = bonus; + + this.Hue = CraftResources.GetHue( (CraftResource)Utility.RandomMinMax( (int)CraftResource.DullCopper, (int)CraftResource.Valorite ) ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Bonus != 0 ) + list.Add( 1062005, m_Bonus.ToString() ); // mining bonus +~1_val~ + } + + public BaseGlovesOfMining( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Bonus ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Bonus = reader.ReadInt(); + break; + } + } + + if ( m_Bonus != 0 && Parent is Mobile ) + { + if ( m_SkillMod != null ) + m_SkillMod.Remove(); + + m_SkillMod = new DefaultSkillMod( SkillName.Mining, true, m_Bonus ); + ((Mobile)Parent).AddSkillMod( m_SkillMod ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs new file mode 100644 index 000000000..549c9459a --- /dev/null +++ b/Scripts/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs @@ -0,0 +1,285 @@ +using System; +using Server; +using Server.Targeting; +using Server.Engines.Craft; +using Server.Network; + +namespace Server.Items +{ + public class PowderOfTemperament : Item, IUsesRemaining + { + private int m_UsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + public bool ShowUsesRemaining{ get{ return true; } set{} } + + public override int LabelNumber{ get{ return 1049082; } } // powder of temperament + + [Constructable] + public PowderOfTemperament() : this( 10 ) + { + } + + [Constructable] + public PowderOfTemperament( int charges ) : base( 4102 ) + { + Weight = 1.0; + Hue = 2419; + UsesRemaining = charges; + } + + public PowderOfTemperament( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + writer.Write( (int) m_UsesRemaining ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_UsesRemaining = reader.ReadInt(); + break; + } + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + } + + public virtual void DisplayDurabilityTo( Mobile m ) + { + LabelToAffix( m, 1017323, AffixType.Append, ": " + m_UsesRemaining.ToString() ); // Durability + } + + public override void OnSingleClick( Mobile from ) + { + DisplayDurabilityTo( from ); + + base.OnSingleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + from.Target = new InternalTarget( this ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + private class InternalTarget : Target + { + private PowderOfTemperament m_Powder; + + public InternalTarget( PowderOfTemperament powder ) : base( 2, false, TargetFlags.None ) + { + m_Powder = powder; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Powder.Deleted || m_Powder.UsesRemaining <= 0 ) + { + from.SendLocalizedMessage( 1049086 ); // You have used up your powder of temperament. + return; + } + + if ( targeted is BaseArmor /*&& (DefBlacksmithy.CraftSystem.CraftItems.SearchForSubclass( targeted.GetType() ) != null)*/ ) + { + BaseArmor ar = (BaseArmor)targeted; + + if ( ar.IsChildOf( from.Backpack ) && m_Powder.IsChildOf( from.Backpack ) ) + { + int origMaxHP = ar.MaxHitPoints; + int origCurHP = ar.HitPoints; + + int initMaxHP = Core.AOS ? 255 : ar.InitMaxHits; + + ar.UnscaleDurability(); + + if ( ar.MaxHitPoints < initMaxHP ) + { + int bonus = initMaxHP - ar.MaxHitPoints; + + if ( bonus > 10 ) + bonus = 10; + + ar.MaxHitPoints += bonus; + ar.HitPoints += bonus; + + ar.ScaleDurability(); + + if ( ar.MaxHitPoints > 255 ) ar.MaxHitPoints = 255; + if ( ar.HitPoints > 255 ) ar.HitPoints = 255; + + if ( ar.MaxHitPoints > origMaxHP ) + { + from.SendLocalizedMessage( 1049084 ); // You successfully use the powder on the item. + + --m_Powder.UsesRemaining; + + if ( m_Powder.UsesRemaining <= 0 ) + { + from.SendLocalizedMessage( 1049086 ); // You have used up your powder of temperament. + m_Powder.Delete(); + } + } + else + { + ar.MaxHitPoints = origMaxHP; + ar.HitPoints = origCurHP; + from.SendLocalizedMessage( 1049085 ); // The item cannot be improved any further. + } + } + else + { + from.SendLocalizedMessage( 1049085 ); // The item cannot be improved any further. + ar.ScaleDurability(); + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + else if ( targeted is BaseWeapon /*&& (DefBlacksmithy.CraftSystem.CraftItems.SearchForSubclass( targeted.GetType() ) != null)*/ ) + { + BaseWeapon wep = (BaseWeapon)targeted; + + if ( wep.IsChildOf( from.Backpack ) && m_Powder.IsChildOf( from.Backpack ) ) + { + int origMaxHP = wep.MaxHitPoints; + int origCurHP = wep.HitPoints; + + int initMaxHP = Core.AOS ? 255 : wep.InitMaxHits; + + wep.UnscaleDurability(); + + if ( wep.MaxHitPoints < initMaxHP ) + { + int bonus = initMaxHP - wep.MaxHitPoints; + + if ( bonus > 10 ) + bonus = 10; + + wep.MaxHitPoints += bonus; + wep.HitPoints += bonus; + + wep.ScaleDurability(); + + if ( wep.MaxHitPoints > 255 ) wep.MaxHitPoints = 255; + if ( wep.HitPoints > 255 ) wep.HitPoints = 255; + + if ( wep.MaxHitPoints > origMaxHP ) + { + from.SendLocalizedMessage( 1049084 ); // You successfully use the powder on the item. + + --m_Powder.UsesRemaining; + + if ( m_Powder.UsesRemaining <= 0 ) + { + from.SendLocalizedMessage( 1049086 ); // You have used up your powder of temperament. + m_Powder.Delete(); + } + } + else + { + wep.MaxHitPoints = origMaxHP; + wep.HitPoints = origCurHP; + from.SendLocalizedMessage( 1049085 ); // The item cannot be improved any further. + } + } + else + { + from.SendLocalizedMessage( 1049085 ); // The item cannot be improved any further. + wep.ScaleDurability(); + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + else if ( targeted is BaseClothing /*&& (DefBlacksmithy.CraftSystem.CraftItems.SearchForSubclass( targeted.GetType() ) != null)*/ ) + { + BaseClothing clothing = (BaseClothing)targeted; + + if ( clothing.IsChildOf( from.Backpack ) && m_Powder.IsChildOf( from.Backpack ) ) + { + int origMaxHP = clothing.MaxHitPoints; + int origCurHP = clothing.HitPoints; + + int initMaxHP = Core.AOS ? 255 : clothing.InitMaxHits; + + if ( clothing.MaxHitPoints < initMaxHP ) + { + int bonus = initMaxHP - clothing.MaxHitPoints; + + if ( bonus > 10 ) + bonus = 10; + + clothing.MaxHitPoints += bonus; + clothing.HitPoints += bonus; + + if ( clothing.MaxHitPoints > 255 ) clothing.MaxHitPoints = 255; + if ( clothing.HitPoints > 255 ) clothing.HitPoints = 255; + + if ( clothing.MaxHitPoints > origMaxHP ) + { + from.SendLocalizedMessage( 1049084 ); // You successfully use the powder on the item. + + --m_Powder.UsesRemaining; + + if ( m_Powder.UsesRemaining <= 0 ) + { + from.SendLocalizedMessage( 1049086 ); // You have used up your powder of temperament. + m_Powder.Delete(); + } + } + else + { + clothing.MaxHitPoints = origMaxHP; + clothing.HitPoints = origCurHP; + from.SendLocalizedMessage( 1049085 ); // The item cannot be improved any further. + } + } + else + { + from.SendLocalizedMessage( 1049085 ); // The item cannot be improved any further. + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + else + { + from.SendLocalizedMessage( 1049083 ); // You cannot use the powder on that item. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/HearthOfHomeFire.cs b/Scripts/Items/Special/Gifts/HearthOfHomeFire.cs new file mode 100644 index 000000000..1426fced7 --- /dev/null +++ b/Scripts/Items/Special/Gifts/HearthOfHomeFire.cs @@ -0,0 +1,132 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class HearthOfHomeFire : BaseAddon + { + public override BaseAddonDeed Deed{ get{ return new HearthOfHomeFireDeed(); } } + + [Constructable] + public HearthOfHomeFire( bool east ) + { + if ( east ) + { + AddLightComponent( new AddonComponent( 0x2352 ), 0, 0, 0 ); + AddLightComponent( new AddonComponent( 0x2358 ), 0, -1, 0 ); + } + else + { + AddLightComponent( new AddonComponent( 0x2360 ), 0, 0, 0 ); + AddLightComponent( new AddonComponent( 0x2366 ), -1, 0, 0 ); + } + } + + private void AddLightComponent( AddonComponent component, int x, int y, int z ) + { + component.Light = LightType.Circle150; + + AddComponent( component, x, y, z ); + } + + public HearthOfHomeFire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } + + public class HearthOfHomeFireDeed : BaseAddonDeed + { + private bool m_East; + + public override BaseAddon Addon{ get{ return new HearthOfHomeFire( m_East ); } } + + public override int LabelNumber{ get{ return 1062919; } } // Hearth of the Home Fire + + [Constructable] + public HearthOfHomeFireDeed() + { + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + from.CloseGump( typeof( InternalGump ) ); + from.SendGump( new InternalGump( this ) ); + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + private void SendTarget( Mobile m ) + { + base.OnDoubleClick( m ); + } + + private class InternalGump : Gump + { + private HearthOfHomeFireDeed m_Deed; + + public InternalGump( HearthOfHomeFireDeed deed ) : base( 150, 50 ) + { + m_Deed = deed; + + AddBackground( 0, 0, 350, 250, 0xA28 ); + + AddItem( 90, 52, 0x2367 ); + AddItem( 112, 35, 0x2360 ); + AddButton( 70, 35, 0x868, 0x869, 1, GumpButtonType.Reply, 0 ); // South + + AddItem( 220, 35, 0x2352 ); + AddItem( 242, 52, 0x2358 ); + AddButton( 185, 35, 0x868, 0x869, 2, GumpButtonType.Reply, 0 ); // East + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted || info.ButtonID == 0 ) + return; + + m_Deed.m_East = (info.ButtonID != 1); + m_Deed.SendTarget( sender.Mobile ); + } + } + + public HearthOfHomeFireDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/HolySword.cs b/Scripts/Items/Special/Gifts/HolySword.cs new file mode 100644 index 000000000..0e0d44587 --- /dev/null +++ b/Scripts/Items/Special/Gifts/HolySword.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class HolySword : Longsword + { + public override int LabelNumber{ get{ return 1062921; } } // The Holy Sword + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public HolySword() + { + Hue = 0x482; + LootType = LootType.Blessed; + + Slayer = SlayerName.Silver; + + Attributes.WeaponDamage = 40; + WeaponAttributes.SelfRepair = 10; + WeaponAttributes.LowerStatReq = 100; + WeaponAttributes.UseBestSkill = 1; + } + + public HolySword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/LeggingsOfEmbers.cs b/Scripts/Items/Special/Gifts/LeggingsOfEmbers.cs new file mode 100644 index 000000000..337df8c7b --- /dev/null +++ b/Scripts/Items/Special/Gifts/LeggingsOfEmbers.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LeggingsOfEmbers : PlateLegs + { + public override int LabelNumber{ get{ return 1062911; } } // Royal Leggings of Embers + + public override int BasePhysicalResistance{ get{ return 15; } } + public override int BaseFireResistance{ get{ return 25; } } + public override int BaseColdResistance{ get{ return 0; } } + public override int BasePoisonResistance{ get{ return 15; } } + public override int BaseEnergyResistance{ get{ return 15; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public LeggingsOfEmbers() + { + Hue = 0x2C; + LootType = LootType.Blessed; + + ArmorAttributes.SelfRepair = 10; + ArmorAttributes.MageArmor = 1; + ArmorAttributes.LowerStatReq = 100; + } + + public LeggingsOfEmbers( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/RoseOfTrinsic.cs b/Scripts/Items/Special/Gifts/RoseOfTrinsic.cs new file mode 100644 index 000000000..58afa6859 --- /dev/null +++ b/Scripts/Items/Special/Gifts/RoseOfTrinsic.cs @@ -0,0 +1,223 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; +using Server.Gumps; +using Server.Multis; +using Server.ContextMenus; + +namespace Server.Items +{ + [FlipableAttribute( 0x234C, 0x234D )] + public class RoseOfTrinsic : Item, ISecurable + { + private static readonly TimeSpan m_SpawnTime = TimeSpan.FromHours( 4.0 ); + + private int m_Petals; + private DateTime m_NextSpawnTime; + private SpawnTimer m_SpawnTimer; + + private SecureLevel m_Level; + + public override int LabelNumber{ get{ return 1062913; } } // Rose of Trinsic + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Petals + { + get{ return m_Petals; } + set + { + if ( value >= 10 ) + { + m_Petals = 10; + + StopSpawnTimer(); + } + else + { + if ( value <= 0 ) + m_Petals = 0; + else + m_Petals = value; + + StartSpawnTimer( m_SpawnTime ); + } + + InvalidateProperties(); + } + } + + [Constructable] + public RoseOfTrinsic() : base( 0x234D ) + { + Weight = 1.0; + LootType = LootType.Blessed; + + m_Petals = 0; + StartSpawnTimer( TimeSpan.FromMinutes( 1.0 ) ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1062925, Petals.ToString() ); // Petals: ~1_COUNT~ + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + SetSecureLevelEntry.AddTo( from, this, list ); + } + + private void StartSpawnTimer( TimeSpan delay ) + { + StopSpawnTimer(); + + m_SpawnTimer = new SpawnTimer( this, delay ); + m_SpawnTimer.Start(); + + m_NextSpawnTime = DateTime.Now + delay; + } + + private void StopSpawnTimer() + { + if ( m_SpawnTimer != null ) + { + m_SpawnTimer.Stop(); + m_SpawnTimer = null; + } + } + + private class SpawnTimer : Timer + { + private RoseOfTrinsic m_Rose; + + public SpawnTimer( RoseOfTrinsic rose, TimeSpan delay ) : base( delay ) + { + m_Rose = rose; + + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + if ( m_Rose.Deleted ) + return; + + m_Rose.m_SpawnTimer = null; + m_Rose.Petals++; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + else if ( Petals > 0 ) + { + from.AddToBackpack( new RoseOfTrinsicPetal( Petals ) ); + Petals = 0; + } + } + + public RoseOfTrinsic( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Petals ); + writer.WriteDeltaTime( (DateTime) m_NextSpawnTime ); + writer.WriteEncodedInt( (int) m_Level ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Petals = reader.ReadEncodedInt(); + m_NextSpawnTime = reader.ReadDeltaTime(); + m_Level = (SecureLevel) reader.ReadEncodedInt(); + + if ( m_Petals < 10 ) + StartSpawnTimer( m_NextSpawnTime - DateTime.Now ); + } + } + + public class RoseOfTrinsicPetal : Item + { + public override int LabelNumber{ get{ return 1062926; } } // Petal of the Rose of Trinsic + + [Constructable] + public RoseOfTrinsicPetal() : this( 1 ) + { + } + + [Constructable] + public RoseOfTrinsicPetal( int amount ) : base( 0x1021 ) + { + Stackable = true; + Amount = amount; + + Weight = 1.0; + Hue = 0xE; + } + + + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042038 ); // You must have the object in your backpack to use it. + } + else if ( from.GetStatMod( "RoseOfTrinsicPetal" ) != null ) + { + from.SendLocalizedMessage( 1062927 ); // You have eaten one of these recently and eating another would provide no benefit. + } + else + { + from.PlaySound( 0x1EE ); + from.AddStatMod( new StatMod( StatType.Str, "RoseOfTrinsicPetal", 5, TimeSpan.FromMinutes( 5.0 ) ) ); + + Consume(); + } + } + + public RoseOfTrinsicPetal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/SamuraiHelm.cs b/Scripts/Items/Special/Gifts/SamuraiHelm.cs new file mode 100644 index 000000000..5fb176a51 --- /dev/null +++ b/Scripts/Items/Special/Gifts/SamuraiHelm.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Items +{ + [FlipableAttribute( 0x236C, 0x236D )] + public class SamuraiHelm : BaseArmor + { + public override int LabelNumber{ get{ return 1062923; } } // Ancient Samurai Helm + + public override int BasePhysicalResistance{ get{ return 15; } } + public override int BaseFireResistance{ get{ return 10; } } + public override int BaseColdResistance{ get{ return 10; } } + public override int BasePoisonResistance{ get{ return 15; } } + public override int BaseEnergyResistance{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + public override ArmorMaterialType MaterialType{ get{ return ArmorMaterialType.Plate; } } + + [Constructable] + public SamuraiHelm() : base( 0x236C ) + { + Weight = 5.0; + LootType = LootType.Blessed; + + Attributes.DefendChance = 15; + ArmorAttributes.SelfRepair = 10; + ArmorAttributes.LowerStatReq = 100; + ArmorAttributes.MageArmor = 1; + } + + public SamuraiHelm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/ShaminoCrossbow.cs b/Scripts/Items/Special/Gifts/ShaminoCrossbow.cs new file mode 100644 index 000000000..96465a837 --- /dev/null +++ b/Scripts/Items/Special/Gifts/ShaminoCrossbow.cs @@ -0,0 +1,43 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ShaminoCrossbow : RepeatingCrossbow + { + public override int LabelNumber{ get{ return 1062915; } } // Shamino’s Best Crossbow + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ShaminoCrossbow() + { + Hue = 0x504; + LootType = LootType.Blessed; + + Attributes.AttackChance = 15; + Attributes.WeaponDamage = 40; + WeaponAttributes.SelfRepair = 10; + WeaponAttributes.LowerStatReq = 100; + } + + public ShaminoCrossbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Gifts/TapestryOfSosaria.cs b/Scripts/Items/Special/Gifts/TapestryOfSosaria.cs new file mode 100644 index 000000000..0e03de35c --- /dev/null +++ b/Scripts/Items/Special/Gifts/TapestryOfSosaria.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Multis; +using Server.ContextMenus; + +namespace Server.Items +{ + [FlipableAttribute( 0x234E, 0x234F )] + public class TapestryOfSosaria : Item, ISecurable + { + private SecureLevel m_Level; + + public override int LabelNumber{ get{ return 1062917; } } // The Tapestry of Sosaria + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + [Constructable] + public TapestryOfSosaria() : base( 0x234E ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 2 ) ) + { + from.CloseGump( typeof( InternalGump ) ); + from.SendGump( new InternalGump() ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + + private class InternalGump : Gump + { + public InternalGump() : base( 50, 50 ) + { + AddImage( 0, 0, 0x2C95 ); + } + } + + public TapestryOfSosaria( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Level ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Level = (SecureLevel) reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/Christmas/HolidayTree.cs b/Scripts/Items/Special/Holiday/Christmas/HolidayTree.cs new file mode 100644 index 000000000..ba7bbf906 --- /dev/null +++ b/Scripts/Items/Special/Holiday/Christmas/HolidayTree.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public enum HolidayTreeType + { + Classic, + Modern + } + + public class HolidayTree : Item + { + private ArrayList m_Components; + private Mobile m_Placer; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Placer + { + get{ return m_Placer; } + set{ m_Placer = value; } + } + + private class Ornament : Item + { + public override int LabelNumber{ get{ return 1041118; } } // a tree ornament + + public Ornament( int itemID ) : base( itemID ) + { + Movable = false; + } + + public Ornament( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + private class TreeTrunk : Item + { + private HolidayTree m_Tree; + + public override int LabelNumber{ get{ return 1041117; } } // a tree for the holidays + + public TreeTrunk( HolidayTree tree, int itemID ) : base( itemID ) + { + Movable = false; + MoveToWorld( tree.Location, tree.Map ); + + m_Tree = tree; + } + + public TreeTrunk( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Tree != null && !m_Tree.Deleted ) + m_Tree.OnDoubleClick( from ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Tree ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Tree = reader.ReadItem() as HolidayTree; + + if ( m_Tree == null ) + Delete(); + + break; + } + } + } + } + + public override int LabelNumber{ get{ return 1041117; } } // a tree for the holidays + + public HolidayTree( Mobile from, HolidayTreeType type, Point3D loc ) : base( 1 ) + { + Movable = false; + MoveToWorld( loc, from.Map ); + + m_Placer = from; + m_Components = new ArrayList(); + + switch ( type ) + { + case HolidayTreeType.Classic: + { + ItemID = 0xCD7; + + AddItem( 0, 0, 0, new TreeTrunk( this, 0xCD6 ) ); + + AddOrnament( 0, 0, 2, 0xF22 ); + AddOrnament( 0, 0, 9, 0xF18 ); + AddOrnament( 0, 0, 15, 0xF20 ); + AddOrnament( 0, 0, 19, 0xF17 ); + AddOrnament( 0, 0, 20, 0xF24 ); + AddOrnament( 0, 0, 20, 0xF1F ); + AddOrnament( 0, 0, 20, 0xF19 ); + AddOrnament( 0, 0, 21, 0xF1B ); + AddOrnament( 0, 0, 28, 0xF2F ); + AddOrnament( 0, 0, 30, 0xF23 ); + AddOrnament( 0, 0, 32, 0xF2A ); + AddOrnament( 0, 0, 33, 0xF30 ); + AddOrnament( 0, 0, 34, 0xF29 ); + AddOrnament( 0, 1, 7, 0xF16 ); + AddOrnament( 0, 1, 7, 0xF1E ); + AddOrnament( 0, 1, 12, 0xF0F ); + AddOrnament( 0, 1, 13, 0xF13 ); + AddOrnament( 0, 1, 18, 0xF12 ); + AddOrnament( 0, 1, 19, 0xF15 ); + AddOrnament( 0, 1, 25, 0xF28 ); + AddOrnament( 0, 1, 29, 0xF1A ); + AddOrnament( 0, 1, 37, 0xF2B ); + AddOrnament( 1, 0, 13, 0xF10 ); + AddOrnament( 1, 0, 14, 0xF1C ); + AddOrnament( 1, 0, 16, 0xF14 ); + AddOrnament( 1, 0, 17, 0xF26 ); + AddOrnament( 1, 0, 22, 0xF27 ); + + break; + } + case HolidayTreeType.Modern: + { + ItemID = 0x1B7E; + + AddOrnament( 0, 0, 2, 0xF2F ); + AddOrnament( 0, 0, 2, 0xF20 ); + AddOrnament( 0, 0, 2, 0xF22 ); + AddOrnament( 0, 0, 5, 0xF30 ); + AddOrnament( 0, 0, 5, 0xF15 ); + AddOrnament( 0, 0, 5, 0xF1F ); + AddOrnament( 0, 0, 5, 0xF2B ); + AddOrnament( 0, 0, 6, 0xF0F ); + AddOrnament( 0, 0, 7, 0xF1E ); + AddOrnament( 0, 0, 7, 0xF24 ); + AddOrnament( 0, 0, 8, 0xF29 ); + AddOrnament( 0, 0, 9, 0xF18 ); + AddOrnament( 0, 0, 14, 0xF1C ); + AddOrnament( 0, 0, 15, 0xF13 ); + AddOrnament( 0, 0, 15, 0xF20 ); + AddOrnament( 0, 0, 16, 0xF26 ); + AddOrnament( 0, 0, 17, 0xF12 ); + AddOrnament( 0, 0, 18, 0xF17 ); + AddOrnament( 0, 0, 20, 0xF1B ); + AddOrnament( 0, 0, 23, 0xF28 ); + AddOrnament( 0, 0, 25, 0xF18 ); + AddOrnament( 0, 0, 25, 0xF2A ); + AddOrnament( 0, 1, 7, 0xF16 ); + + break; + } + } + } + + public override void OnAfterDelete() + { + for ( int i = 0; i < m_Components.Count; ++i ) + ((Item)m_Components[i]).Delete(); + } + + private void AddOrnament( int x, int y, int z, int itemID ) + { + AddItem( x + 1, y + 1, z + 11, new Ornament( itemID ) ); + } + + private void AddItem( int x, int y, int z, Item item ) + { + item.MoveToWorld( new Point3D( this.Location.X + x, this.Location.Y + y, this.Location.Z + z ), this.Map ); + + m_Components.Add( item ); + } + + public HolidayTree( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_Placer ); + + writer.Write( (int) m_Components.Count ); + + for ( int i = 0; i < m_Components.Count; ++i ) + writer.Write( (Item)m_Components[i] ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Placer = reader.ReadMobile(); + + goto case 0; + } + case 0: + { + int count = reader.ReadInt(); + + m_Components = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + Item item = reader.ReadItem(); + + if ( item != null ) + m_Components.Add( item ); + } + + break; + } + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 1 ) ) + { + if ( m_Placer == null || from == m_Placer || from.AccessLevel >= AccessLevel.GameMaster ) + { + from.AddToBackpack( new HolidayTreeDeed() ); + + this.Delete(); + + from.SendLocalizedMessage( 503393 ); // A deed for the tree has been placed in your backpack. + } + else + { + from.SendLocalizedMessage( 503396 ); // You cannot take this tree down. + } + } + else + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/GiftBox.cs b/Scripts/Items/Special/Holiday/GiftBox.cs new file mode 100644 index 000000000..6db071b99 --- /dev/null +++ b/Scripts/Items/Special/Holiday/GiftBox.cs @@ -0,0 +1,41 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [Furniture] + [Flipable( 0x232A, 0x232B )] + public class GiftBox : BaseContainer + { + [Constructable] + public GiftBox() : this( Utility.RandomDyedHue() ) + { + } + + [Constructable] + public GiftBox( int hue ) : base( Utility.Random( 0x232A, 2 ) ) + { + Weight = 2.0; + Hue = hue; + } + + public GiftBox( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/Poinsettias.cs b/Scripts/Items/Special/Holiday/Poinsettias.cs new file mode 100644 index 000000000..79fb042de --- /dev/null +++ b/Scripts/Items/Special/Holiday/Poinsettias.cs @@ -0,0 +1,62 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class RedPoinsettia : Item + { + [Constructable] + public RedPoinsettia() : base( 0x2330 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public RedPoinsettia( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WhitePoinsettia : Item + { + [Constructable] + public WhitePoinsettia() : base( 0x2331 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public WhitePoinsettia( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/Snowflakes.cs b/Scripts/Items/Special/Holiday/Snowflakes.cs new file mode 100644 index 000000000..a57b693a6 --- /dev/null +++ b/Scripts/Items/Special/Holiday/Snowflakes.cs @@ -0,0 +1,62 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class BlueSnowflake : Item + { + [Constructable] + public BlueSnowflake() : base( 0x232E ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public BlueSnowflake( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WhiteSnowflake : Item + { + [Constructable] + public WhiteSnowflake() : base( 0x232F ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public WhiteSnowflake( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/Snowman.cs b/Scripts/Items/Special/Holiday/Snowman.cs new file mode 100644 index 000000000..7de7d45ac --- /dev/null +++ b/Scripts/Items/Special/Holiday/Snowman.cs @@ -0,0 +1,159 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [Flipable( 0x2328, 0x2329 )] + public class Snowman : Item, IDyable + { + public static string GetRandomTitle() + { + // All hail OSI staff + string[] titles = new string[] + { + /* 1 */ "Backflash", + /* 2 */ "Carbon", + /* 3 */ "Colbalistic", + /* 4 */ "Comforl", + /* 5 */ "Coppacchia", + /* 6 */ "Cyrus", + /* 7 */ "DannyB", + /* 8 */ "DJSoul", + /* 9 */ "DraconisRex", + /* 10 */ "Earia", + /* 11 */ "Foster", + /* 12 */ "Gonzo", + /* 13 */ "Haan", + /* 14 */ "Halona", + /* 15 */ "Hugo", + /* 16 */ "Hyacinth", + /* 17 */ "Imirian", + /* 18 */ "Jinsol", + /* 19 */ "Liciatia", + /* 20 */ "Loewen", + /* 21 */ "Loke", + /* 22 */ "Magnus", + /* 23 */ "Maleki", + /* 24 */ "Morpheus", + /* 25 */ "Obberron", + /* 26 */ "Odee", + /* 27 */ "Orbeus", + /* 28 */ "Pax", + /* 29 */ "Phields", + /* 30 */ "Pigpen", + /* 31 */ "Platinum", + /* 32 */ "Polpol", + /* 33 */ "Prume", + /* 34 */ "Quinnly", + /* 35 */ "Ragnarok", + /* 36 */ "Rend", + /* 37 */ "Roland", + /* 38 */ "RyanM", + /* 39 */ "Screach", + /* 40 */ "Seraph", + /* 41 */ "Silvani", + /* 42 */ "Sherbear", + /* 43 */ "SkyWalker", + /* 44 */ "Snark", + /* 45 */ "Sowl", + /* 46 */ "Spada", + /* 47 */ "Starblade", + /* 48 */ "Tenacious", + /* 49 */ "Tnez", + /* 50 */ "Wasia", + /* 51 */ "Zilo", + /* 52 */ "Zippy", + /* 53 */ "Zoer" + }; + + if ( titles.Length > 0 ) + return titles[Utility.Random( titles.Length )]; + + return null; + } + + private string m_Title; + + [CommandProperty( AccessLevel.GameMaster )] + public string Title + { + get{ return m_Title; } + set{ m_Title = value; InvalidateProperties(); } + } + + [Constructable] + public Snowman() : this( Utility.RandomDyedHue(), GetRandomTitle() ) + { + } + + [Constructable] + public Snowman( int hue ) : this( hue, GetRandomTitle() ) + { + } + + [Constructable] + public Snowman( string title ) : this( Utility.RandomDyedHue(), title ) + { + } + + [Constructable] + public Snowman( int hue, string title ) : base( Utility.Random( 0x2328, 2 ) ) + { + Weight = 10.0; + Hue = hue; + LootType = LootType.Blessed; + + m_Title = title; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Title != null ) + list.Add( 1062841, m_Title ); // ~1_NAME~ the Snowman + } + + public bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + Hue = sender.DyedHue; + + return true; + } + + public Snowman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (string) m_Title ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Title = reader.ReadString(); + break; + } + } + + Utility.Intern( ref m_Title ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/WinterGiftPackage2003.cs b/Scripts/Items/Special/Holiday/WinterGiftPackage2003.cs new file mode 100644 index 000000000..220983902 --- /dev/null +++ b/Scripts/Items/Special/Holiday/WinterGiftPackage2003.cs @@ -0,0 +1,37 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [Flipable( 0x232A, 0x232B )] + public class WinterGiftPackage2003 : GiftBox + { + [Constructable] + public WinterGiftPackage2003() + { + DropItem( new Snowman() ); + DropItem( new WreathDeed() ); + DropItem( new BlueSnowflake() ); + DropItem( new RedPoinsettia() ); + } + + public WinterGiftPackage2003( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Holiday/Wreath.cs b/Scripts/Items/Special/Holiday/Wreath.cs new file mode 100644 index 000000000..5772e6033 --- /dev/null +++ b/Scripts/Items/Special/Holiday/Wreath.cs @@ -0,0 +1,328 @@ +using System; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class WreathAddon : Item, IDyable, IAddon + { + [Constructable] + public WreathAddon() : this( Utility.RandomDyedHue() ) + { + } + + [Constructable] + public WreathAddon( int hue ) : base( 0x232C ) + { + Hue = hue; + Movable = false; + } + + public WreathAddon( Serial serial ) : base( serial ) + { + } + + public bool CouldFit( IPoint3D p, Map map ) + { + if ( !map.CanFit( p.X, p.Y, p.Z, this.ItemData.Height ) ) + return false; + + if ( this.ItemID == 0x232C ) + return BaseAddon.IsWall( p.X, p.Y - 1, p.Z, map ); // North wall + else + return BaseAddon.IsWall( p.X - 1, p.Y, p.Z, map ); // West wall + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( FixMovingCrate ) ); + } + + private void FixMovingCrate() + { + if ( this.Deleted ) + return; + + if ( this.Movable || this.IsLockedDown ) + { + Item deed = this.Deed; + + if ( this.Parent is Item ) + { + ((Item)this.Parent).AddItem( deed ); + deed.Location = this.Location; + } + else + { + deed.MoveToWorld( this.Location, this.Map ); + } + + Delete(); + } + } + + public Item Deed + { + get{ return new WreathDeed( this.Hue ); } + } + + public override void OnDoubleClick( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsCoOwner( from ) ) + { + if ( from.InRange( this.GetWorldLocation(), 3 ) ) + { + from.CloseGump( typeof( WreathAddonGump ) ); + from.SendGump( new WreathAddonGump( from, this ) ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + } + + public virtual bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsCoOwner( from ) ) + { + if ( from.InRange( GetWorldLocation(), 1 ) ) + { + Hue = sender.DyedHue; + return true; + } + else + { + from.SendLocalizedMessage( 500295 ); // You are too far away to do that. + return false; + } + } + else + { + return false; + } + } + + private class WreathAddonGump : Gump + { + private Mobile m_From; + private WreathAddon m_Addon; + + public WreathAddonGump( Mobile from, WreathAddon addon ) : base( 150, 50 ) + { + m_From = from; + m_Addon = addon; + + AddPage( 0 ); + + AddBackground( 0, 0, 220, 170, 0x13BE ); + AddBackground( 10, 10, 200, 150, 0xBB8 ); + AddHtmlLocalized( 20, 30, 180, 60, 1062839, false, false ); // Do you wish to re-deed this decoration? + AddHtmlLocalized( 55, 100, 160, 25, 1011011, false, false ); // CONTINUE + AddButton( 20, 100, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 125, 160, 25, 1011012, false, false ); // CANCEL + AddButton( 20, 125, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Addon.Deleted ) + return; + + if ( info.ButtonID == 1 ) + { + if ( m_From.InRange( m_Addon.GetWorldLocation(), 3 ) ) + { + m_From.AddToBackpack( m_Addon.Deed ); + m_Addon.Delete(); + } + else + { + m_From.SendLocalizedMessage( 500295 ); // You are too far away to do that. + } + } + } + } + } + + [Flipable( 0x14F0, 0x14EF )] + public class WreathDeed : Item + { + public override int LabelNumber{ get{ return 1062837; } } // holiday wreath deed + + [Constructable] + public WreathDeed() : this( Utility.RandomDyedHue() ) + { + } + + [Constructable] + public WreathDeed( int hue ) : base( 0x14F0 ) + { + Weight = 1.0; + Hue = hue; + LootType = LootType.Blessed; + } + + public WreathDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house != null && house.IsCoOwner( from ) ) + { + from.SendLocalizedMessage( 1062838 ); // Where would you like to place this decoration? + from.BeginTarget( -1, true, TargetFlags.None, new TargetStateCallback( Placement_OnTarget ), null ); + } + else + { + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public void Placement_OnTarget( Mobile from, object targeted, object state ) + { + IPoint3D p = targeted as IPoint3D; + + if ( p == null ) + return; + + Point3D loc = new Point3D( p ); + + BaseHouse house = BaseHouse.FindHouseAt( loc, from.Map, 16 ); + + if ( house != null && house.IsCoOwner( from ) ) + { + bool northWall = BaseAddon.IsWall( loc.X, loc.Y - 1, loc.Z, from.Map ); + bool westWall = BaseAddon.IsWall( loc.X - 1, loc.Y, loc.Z, from.Map ); + + if ( northWall && westWall ) + from.SendGump( new WreathDeedGump( from, loc, this ) ); + else + PlaceAddon( from, loc, northWall, westWall ); + } + else + { + from.SendLocalizedMessage( 1042036 ); // That location is not in your house. + } + } + + private void PlaceAddon( Mobile from, Point3D loc, bool northWall, bool westWall ) + { + if ( Deleted ) + return; + + BaseHouse house = BaseHouse.FindHouseAt( loc, from.Map, 16 ); + + if ( house == null || !house.IsCoOwner( from ) ) + { + from.SendLocalizedMessage( 1042036 ); // That location is not in your house. + return; + } + + int itemID = 0; + + if ( northWall ) + itemID = 0x232C; + else if ( westWall ) + itemID = 0x232D; + else + from.SendLocalizedMessage( 1062840 ); // The decoration must be placed next to a wall. + + if ( itemID > 0 ) + { + Item addon = new WreathAddon( this.Hue ); + + addon.ItemID = itemID; + addon.MoveToWorld( loc, from.Map ); + + house.Addons.Add( addon ); + Delete(); + } + } + + private class WreathDeedGump : Gump + { + private Mobile m_From; + private Point3D m_Loc; + private WreathDeed m_Deed; + + public WreathDeedGump( Mobile from, Point3D loc, WreathDeed deed ) : base( 150, 50 ) + { + m_From = from; + m_Loc = loc; + m_Deed = deed; + + AddBackground( 0, 0, 300, 150, 0xA28 ); + + AddPage( 0 ); + + AddItem( 90, 30, 0x232D ); + AddItem( 180, 30, 0x232C ); + AddButton( 50, 35, 0x868, 0x869, 1, GumpButtonType.Reply, 0 ); + AddButton( 145, 35, 0x868, 0x869, 2, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted ) + return; + + switch( info.ButtonID ) + { + case 1: + m_Deed.PlaceAddon( m_From, m_Loc, false, true ); + break; + case 2: + m_Deed.PlaceAddon( m_From, m_Loc, true, false ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/MiniHouses.cs b/Scripts/Items/Special/MiniHouses.cs new file mode 100644 index 000000000..f2bb34796 --- /dev/null +++ b/Scripts/Items/Special/MiniHouses.cs @@ -0,0 +1,226 @@ +using System; +using Server; + +namespace Server.Items +{ + public class MiniHouseAddon : BaseAddon + { + private MiniHouseType m_Type; + + [CommandProperty( AccessLevel.GameMaster )] + public MiniHouseType Type + { + get{ return m_Type; } + set{ m_Type = value; Construct(); } + } + + public override BaseAddonDeed Deed{ get{ return new MiniHouseDeed( m_Type ); } } + + [Constructable] + public MiniHouseAddon() : this( MiniHouseType.StoneAndPlaster ) + { + } + + [Constructable] + public MiniHouseAddon( MiniHouseType type ) + { + m_Type = type; + + Construct(); + } + + public void Construct() + { + Components.Clear(); + + MiniHouseInfo info = MiniHouseInfo.GetInfo( m_Type ); + + int size = (int)Math.Sqrt( info.Graphics.Length ); + int num = 0; + + for ( int y = 0; y < size; ++y ) + for ( int x = 0; x < size; ++x ) + AddComponent( new AddonComponent( info.Graphics[num++] ), size - x - 1, size - y - 1, 0 ); + } + + public MiniHouseAddon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Type ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Type = (MiniHouseType)reader.ReadInt(); + break; + } + } + } + } + + public class MiniHouseDeed : BaseAddonDeed + { + private MiniHouseType m_Type; + + [CommandProperty( AccessLevel.GameMaster )] + public MiniHouseType Type + { + get{ return m_Type; } + set{ m_Type = value; InvalidateProperties(); } + } + + public override BaseAddon Addon{ get{ return new MiniHouseAddon( m_Type ); } } + public override int LabelNumber{ get{ return 1062096; } } // a mini house deed + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( MiniHouseInfo.GetInfo( m_Type ).LabelNumber ); + } + + [Constructable] + public MiniHouseDeed() : this( MiniHouseType.StoneAndPlaster ) + { + } + + [Constructable] + public MiniHouseDeed( MiniHouseType type ) + { + m_Type = type; + + Weight = 1.0; + LootType = LootType.Blessed; + } + + public MiniHouseDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Type ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Type = (MiniHouseType)reader.ReadInt(); + break; + } + } + + if ( Weight == 0.0 ) + Weight = 1.0; + } + } + + public enum MiniHouseType + { + StoneAndPlaster, + FieldStone, + SmallBrick, + Wooden, + WoodAndPlaster, + ThatchedRoof, + Brick, + TwoStoryWoodAndPlaster, + TwoStoryStoneAndPlaster, + Tower, + SmallStoneKeep, + Castle, + LargeHouseWithPatio, + MarbleHouseWithPatio, + SmallStoneTower, + TwoStoryLogCabin, + TwoStoryVilla, + SandstoneHouseWithPatio, + SmallStoneWorkshop, + SmallMarbleWorkshop + } + + public class MiniHouseInfo + { + private int[] m_Graphics; + private int m_LabelNumber; + + public int[] Graphics{ get{ return m_Graphics; } } + public int LabelNumber{ get{ return m_LabelNumber; } } + + public MiniHouseInfo( int start, int count, int labelNumber ) + { + m_Graphics = new int[count]; + + for ( int i = 0; i < count; ++i ) + m_Graphics[i] = start + i; + + m_LabelNumber = labelNumber; + } + + public MiniHouseInfo( int labelNumber, params int[] graphics ) + { + m_LabelNumber = labelNumber; + m_Graphics = graphics; + } + + private static MiniHouseInfo[] m_Info = new MiniHouseInfo[] + { + /* Stone and plaster house */ new MiniHouseInfo( 0x22C4, 1, 1011303 ), + /* Field stone house */ new MiniHouseInfo( 0x22DE, 1, 1011304 ), + /* Small brick house */ new MiniHouseInfo( 0x22DF, 1, 1011305 ), + /* Wooden house */ new MiniHouseInfo( 0x22C9, 1, 1011306 ), + /* Wood and plaster house */ new MiniHouseInfo( 0x22E0, 1, 1011307 ), + /* Thatched-roof cottage */ new MiniHouseInfo( 0x22E1, 1, 1011308 ), + /* Brick house */ new MiniHouseInfo( 1011309, 0x22CD, 0x22CB, 0x22CC, 0x22CA ), + /* Two-story wood and plaster house */ new MiniHouseInfo( 1011310, 0x2301, 0x2302, 0x2304, 0x2303 ), + /* Two-story stone and plaster house */ new MiniHouseInfo( 1011311, 0x22FC, 0x22FD, 0x22FF, 0x22FE ), + /* Tower */ new MiniHouseInfo( 1011312, 0x22F7, 0x22F8, 0x22FA, 0x22F9 ), + /* Small stone keep */ new MiniHouseInfo( 0x22E6, 9, 1011313 ), + /* Castle */ new MiniHouseInfo( 1011314, 0x22CE, 0x22D0, 0x22D2, 0x22D7, 0x22CF, 0x22D1, 0x22D4, 0x22D9, 0x22D3, 0x22D5, 0x22D6, 0x22DB, 0x22D8, 0x22DA, 0x22DC, 0x22DD ), + /* Large house with patio */ new MiniHouseInfo( 0x22E2, 4, 1011315 ), + /* Marble house with patio */ new MiniHouseInfo( 0x22EF, 4, 1011316 ), + /* Small stone tower */ new MiniHouseInfo( 0x22F5, 1, 1011317 ), + /* Two-story log cabin */ new MiniHouseInfo( 0x22FB, 1, 1011318 ), + /* Two-story villa */ new MiniHouseInfo( 0x2300, 1, 1011319 ), + /* Sandstone house with patio */ new MiniHouseInfo( 0x22F3, 1, 1011320 ), + /* Small stone workshop */ new MiniHouseInfo( 0x22F6, 1, 1011321 ), + /* Small marble workshop */ new MiniHouseInfo( 0x22F4, 1, 1011322 ) + }; + + public static MiniHouseInfo GetInfo( MiniHouseType type ) + { + int v = (int)type; + + if ( v < 0 || v >= m_Info.Length ) + v = 0; + + return m_Info[v]; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/MonsterStatuette.cs b/Scripts/Items/Special/MonsterStatuette.cs new file mode 100644 index 000000000..e2db66502 --- /dev/null +++ b/Scripts/Items/Special/MonsterStatuette.cs @@ -0,0 +1,266 @@ +using System; +using Server; +using Server.Multis; +using Server.Gumps; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public enum MonsterStatuetteType + { + Crocodile, + Daemon, + Dragon, + EarthElemental, + Ettin, + Gargoyle, + Gorilla, + Lich, + Lizardman, + Ogre, + Orc, + Ratman, + Skeleton, + Troll, + Cow, + Zombie, + Llama, + Ophidian, + Reaper, + Mongbat, + Gazer, + FireElemental, + Wolf, + PhillipsWoodenSteed, + Seahorse + } + + public class MonsterStatuetteInfo + { + private int m_LabelNumber; + private int m_ItemID; + private int[] m_Sounds; + + public int LabelNumber{ get{ return m_LabelNumber; } } + public int ItemID{ get{ return m_ItemID; } } + public int[] Sounds{ get{ return m_Sounds; } } + + public MonsterStatuetteInfo( int labelNumber, int itemID, int baseSoundID ) + { + m_LabelNumber = labelNumber; + m_ItemID = itemID; + m_Sounds = new int[]{ baseSoundID, baseSoundID + 1, baseSoundID + 2, baseSoundID + 3, baseSoundID + 4 }; + } + + private static MonsterStatuetteInfo[] m_Table = new MonsterStatuetteInfo[] + { + /* Crocodile */ new MonsterStatuetteInfo( 1041249, 0x20DA, 660 ), + /* Daemon */ new MonsterStatuetteInfo( 1041250, 0x20D3, 357 ), + /* Dragon */ new MonsterStatuetteInfo( 1041251, 0x20D6, 362 ), + /* EarthElemental */ new MonsterStatuetteInfo( 1041252, 0x20D7, 268 ), + /* Ettin */ new MonsterStatuetteInfo( 1041253, 0x20D8, 367 ), + /* Gargoyle */ new MonsterStatuetteInfo( 1041254, 0x20D9, 372 ), + /* Gorilla */ new MonsterStatuetteInfo( 1041255, 0x20F5, 158 ), + /* Lich */ new MonsterStatuetteInfo( 1041256, 0x20F8, 1001 ), + /* Lizardman */ new MonsterStatuetteInfo( 1041257, 0x20DE, 417 ), + /* Ogre */ new MonsterStatuetteInfo( 1041258, 0x20DF, 427 ), + /* Orc */ new MonsterStatuetteInfo( 1041259, 0x20E0, 1114 ), + /* Ratman */ new MonsterStatuetteInfo( 1041260, 0x20E3, 437 ), + /* Skeleton */ new MonsterStatuetteInfo( 1041261, 0x20E7, 1165 ), + /* Troll */ new MonsterStatuetteInfo( 1041262, 0x20E9, 461 ), + /* Cow */ new MonsterStatuetteInfo( 1041263, 0x2103, 120 ), + /* Zombie */ new MonsterStatuetteInfo( 1041264, 0x20EC, 471 ), + /* Llama */ new MonsterStatuetteInfo( 1041265, 0x20F6, 1011 ), + /* Ophidian */ new MonsterStatuetteInfo( 1049742, 0x2133, 634 ), + /* Reaper */ new MonsterStatuetteInfo( 1049743, 0x20FA, 442 ), + /* Mongbat */ new MonsterStatuetteInfo( 1049744, 0x20F9, 422 ), + /* Gazer */ new MonsterStatuetteInfo( 1049768, 0x20F4, 377 ), + /* FireElemental */ new MonsterStatuetteInfo( 1049769, 0x20F3, 838 ), + /* Wolf */ new MonsterStatuetteInfo( 1049770, 0x2122, 229 ), + /* Phillip's Steed */ new MonsterStatuetteInfo( 1063488, 0x3FFE, 205 ), + /* Seahorse */ new MonsterStatuetteInfo( 1070819, 0x25BA, 138 ) + }; + + public static MonsterStatuetteInfo GetInfo( MonsterStatuetteType type ) + { + int v = (int)type; + + if ( v < 0 || v >= m_Table.Length ) + v = 0; + + return m_Table[v]; + } + } + + public class MonsterStatuette : Item, Engines.VeteranRewards.IRewardItem + { + private MonsterStatuetteType m_Type; + private bool m_TurnedOn; + private bool m_IsRewardItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool TurnedOn + { + get{ return m_TurnedOn; } + set{ m_TurnedOn = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public MonsterStatuetteType Type + { + get{ return m_Type; } + set + { + m_Type = value; + ItemID = MonsterStatuetteInfo.GetInfo( m_Type ).ItemID; + InvalidateProperties(); + } + } + + public override int LabelNumber + { + get{ return MonsterStatuetteInfo.GetInfo( m_Type ).LabelNumber; } + } + + public override double DefaultWeight + { + get { return 1.0; } + } + + [Constructable] + public MonsterStatuette() : this( MonsterStatuetteType.Crocodile ) + { + } + + [Constructable] + public MonsterStatuette( MonsterStatuetteType type ) : base( MonsterStatuetteInfo.GetInfo( type ).ItemID ) + { + LootType = LootType.Blessed; + + m_Type = type; + } + + public override bool HandlesOnMovement{ get{ return m_TurnedOn && IsLockedDown; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( m_TurnedOn && IsLockedDown && (!m.Hidden || m.AccessLevel == AccessLevel.Player) && Utility.InRange( m.Location, this.Location, 2 ) && !Utility.InRange( oldLocation, this.Location, 2 ) ) + { + int[] sounds = MonsterStatuetteInfo.GetInfo( m_Type ).Sounds; + + Effects.PlaySound( this.Location, this.Map, sounds[Utility.Random( sounds.Length )] ); + } + + base.OnMovement( m, oldLocation ); + } + + public MonsterStatuette( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_TurnedOn ) + list.Add( 502695 ); // turned on + else + list.Add( 502696 ); // turned off + } + + public bool IsOwner( Mobile mob ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + return ( house != null && house.IsOwner( mob ) ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsOwner( from ) ) + { + OnOffGump onOffGump = new OnOffGump( this ); + from.SendGump( onOffGump ); + } + else + { + from.SendLocalizedMessage( 502691 ); // You must be the owner to use this. + } + } + + private class OnOffGump : Gump + { + private MonsterStatuette m_Statuette; + + public OnOffGump( MonsterStatuette statuette ) : base( 150, 200 ) + { + m_Statuette = statuette; + + AddBackground( 0, 0, 300, 150, 0xA28 ); + + AddHtmlLocalized( 45, 20, 300, 35, statuette.TurnedOn ? 1011035 : 1011034, false, false ); // [De]Activate this item + + AddButton( 40, 53, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 80, 55, 65, 35, 1011036, false, false ); // OKAY + + AddButton( 150, 53, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 190, 55, 100, 35, 1011012, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + Mobile from = sender.Mobile; + + if ( info.ButtonID == 1 ) + { + bool newValue = !m_Statuette.TurnedOn; + m_Statuette.TurnedOn = newValue; + + if ( newValue && !m_Statuette.IsLockedDown ) + from.SendLocalizedMessage( 502693 ); // Remember, this only works when locked down. + } + else + { + from.SendLocalizedMessage( 502694 ); // Cancelled action. + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteEncodedInt( (int) m_Type ); + writer.Write( (bool) m_TurnedOn ); + writer.Write( (bool) m_IsRewardItem ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Type = (MonsterStatuetteType)reader.ReadEncodedInt(); + m_TurnedOn = reader.ReadBool(); + m_IsRewardItem = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/RewardCake.cs b/Scripts/Items/Special/RewardCake.cs new file mode 100644 index 000000000..95b49fa77 --- /dev/null +++ b/Scripts/Items/Special/RewardCake.cs @@ -0,0 +1,49 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class RewardCake : Item + { + public override int LabelNumber{ get{ return 1049786; } } // Happy Birthday! ... + + [Constructable] + public RewardCake() : base( 0x9e9 ) + { + Stackable = false; + Weight = 1.0; + Hue = Utility.RandomList(0x135, 0xcd, 0x38, 0x3b, 0x42, 0x4f, 0x11e, 0x60, 0x317, 0x10, 0x136, 0x1f9, 0x1a, 0xeb, 0x86, 0x2e); + LootType = LootType.Blessed; + } + + public RewardCake( Serial serial ) : base( serial ) + { + } + + public override bool DisplayLootType{ get{ return false; } } + + public override void OnDoubleClick( Mobile from ) + { + if (! from.InRange( this.GetWorldLocation(), 1 )) + { + from.LocalOverheadMessage( MessageType.Regular, 906, 1019045 ); // I can't reach that. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/BagOfSending.cs b/Scripts/Items/Special/Solen Items/BagOfSending.cs new file mode 100644 index 000000000..ea4b51419 --- /dev/null +++ b/Scripts/Items/Special/Solen Items/BagOfSending.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.ContextMenus; +using Server.Targeting; +using Server.Network; + +namespace Server.Items +{ + public enum BagOfSendingHue + { + Yellow, + Blue, + Red + } + + public class BagOfSending : Item, TranslocationItem + { + public static BagOfSendingHue RandomHue() + { + switch ( Utility.Random( 3 ) ) + { + case 0: return BagOfSendingHue.Yellow; + case 1: return BagOfSendingHue.Blue; + default: return BagOfSendingHue.Red; + } + } + + private int m_Charges; + private int m_Recharges; + private BagOfSendingHue m_BagOfSendingHue; + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set + { + if ( value > this.MaxCharges ) + m_Charges = this.MaxCharges; + else if ( value < 0 ) + m_Charges = 0; + else + m_Charges = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Recharges + { + get{ return m_Recharges; } + set + { + if ( value > this.MaxRecharges ) + m_Recharges = this.MaxRecharges; + else if ( value < 0 ) + m_Recharges = 0; + else + m_Recharges = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxCharges{ get{ return 30; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxRecharges{ get{ return 255; } } + + public string TranslocationItemName{ get{ return "bag of sending"; } } + + public override int LabelNumber{ get{ return 1054104; } } // a bag of sending + + [CommandProperty( AccessLevel.GameMaster )] + public BagOfSendingHue BagOfSendingHue + { + get{ return m_BagOfSendingHue; } + set + { + m_BagOfSendingHue = value; + + switch ( value ) + { + case BagOfSendingHue.Yellow: this.Hue = 0x8A5; break; + case BagOfSendingHue.Blue: this.Hue = 0x8AD; break; + case BagOfSendingHue.Red: this.Hue = 0x89B; break; + } + } + } + + [Constructable] + public BagOfSending() : this( RandomHue() ) + { + } + + [Constructable] + public BagOfSending( BagOfSendingHue hue ) : base( 0xE76 ) + { + Weight = 2.0; + + this.BagOfSendingHue = hue; + + m_Charges = Utility.RandomMinMax( 3, 9 ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060741, m_Charges.ToString() ); // charges: ~1_val~ + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1060741, m_Charges.ToString() ); // charges: ~1_val~ + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive ) + list.Add( new UseBagEntry( this, Charges > 0 && IsChildOf( from.Backpack ) ) ); + } + + private class UseBagEntry : ContextMenuEntry + { + private BagOfSending m_Bag; + + public UseBagEntry( BagOfSending bag, bool enabled ) : base( 6189 ) + { + m_Bag = bag; + + if ( !enabled ) + Flags |= CMEFlags.Disabled; + } + + public override void OnClick() + { + if ( m_Bag.Deleted ) + return; + + Mobile from = Owner.From; + + if ( from.CheckAlive() ) + m_Bag.OnDoubleClick( from ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !this.IsChildOf( from.Backpack ) ) + { + MessageHelper.SendLocalizedMessageTo( this, from, 1054107, 0x59 ); // The bag of sending must be in your backpack. + } + else if ( this.Charges == 0 ) + { + MessageHelper.SendLocalizedMessageTo( this, from, 1042544, 0x59 ); // This item is out of charges. + } + else + { + from.Target = new SendTarget( this ); + } + } + + private class SendTarget : Target + { + private BagOfSending m_Bag; + + public SendTarget( BagOfSending bag ) : base( -1, false, TargetFlags.None ) + { + m_Bag = bag; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Bag.Deleted ) + return; + + if ( !m_Bag.IsChildOf( from.Backpack ) ) + { + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1054107, 0x59 ); // The bag of sending must be in your backpack. + } + else if ( m_Bag.Charges == 0 ) + { + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1042544, 0x59 ); // This item is out of charges. + } + else if ( targeted is Item ) + { + Item item = (Item)targeted; + + if ( !item.IsChildOf( from.Backpack ) ) + { + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1054152, 0x59 ); // You may only send items from your backpack to your bank box. + } + else if ( item is BagOfSending || item is Container ) + { + from.Send( new AsciiMessage( m_Bag.Serial, m_Bag.ItemID, MessageType.Regular, 0x3B2, 3, "", "You cannot send a container through the bag of sending." ) ); + } + else if ( item.LootType == LootType.Cursed ) + { + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1054108, 0x59 ); // The bag of sending rejects the cursed item. + } + else if ( !item.VerifyMove( from ) || item is Server.Engines.Quests.QuestItem ) + { + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1054109, 0x59 ); // The bag of sending rejects that item. + } + else if ( Spells.SpellHelper.IsDoomGauntlet( from.Map, from.Location ) ) + { + from.SendLocalizedMessage( 1062089 ); // You cannot use that here. + } + else if ( from.BankBox == null || !from.BankBox.TryDropItem( from, item, false ) ) + { + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1054110, 0x59 ); // Your bank box is full. + } + else + { + m_Bag.Charges--; + + MessageHelper.SendLocalizedMessageTo( m_Bag, from, 1054150, 0x59 ); // The item was placed in your bank box. + } + } + } + } + + public BagOfSending( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 1 ); // version + + writer.WriteEncodedInt( (int) m_Recharges ); + + writer.WriteEncodedInt( (int) m_Charges ); + writer.WriteEncodedInt( (int) m_BagOfSendingHue ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Recharges = reader.ReadEncodedInt(); + goto case 0; + } + case 0: + { + m_Charges = Math.Min( reader.ReadEncodedInt(), MaxCharges ); + m_BagOfSendingHue = (BagOfSendingHue) reader.ReadEncodedInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/BallOfSummoning.cs b/Scripts/Items/Special/Solen Items/BallOfSummoning.cs new file mode 100644 index 000000000..b9b7fa5c0 --- /dev/null +++ b/Scripts/Items/Special/Solen Items/BallOfSummoning.cs @@ -0,0 +1,345 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.ContextMenus; +using Server.Network; +using Server.Regions; + +namespace Server.Items +{ + public class BallOfSummoning : Item, TranslocationItem + { + private int m_Charges; + private int m_Recharges; + private BaseCreature m_Pet; + private string m_PetName; + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set + { + if ( value > this.MaxCharges ) + m_Charges = this.MaxCharges; + else if ( value < 0 ) + m_Charges = 0; + else + m_Charges = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Recharges + { + get{ return m_Recharges; } + set + { + if ( value > this.MaxRecharges ) + m_Recharges = this.MaxRecharges; + else if ( value < 0 ) + m_Recharges = 0; + else + m_Recharges = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxCharges{ get{ return 20; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxRecharges{ get{ return 255; } } + + public string TranslocationItemName{ get{ return "crystal ball of pet summoning"; } } + + [CommandProperty( AccessLevel.GameMaster )] + public BaseCreature Pet + { + get + { + if ( m_Pet != null && m_Pet.Deleted ) + { + m_Pet = null; + InternalUpdatePetName(); + } + + return m_Pet; + } + set + { + m_Pet = value; + InternalUpdatePetName(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string PetName{ get{ return m_PetName; } } + + [Constructable] + public BallOfSummoning() : base( 0xE2E ) + { + Weight = 10.0; + Light = LightType.Circle150; + + m_Charges = Utility.RandomMinMax( 3, 9 ); + + m_PetName = ""; + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( 1054131, m_Charges.ToString() + ( m_PetName == "" ? "\t " : "\t" + m_PetName ) ); // a crystal ball of pet summoning: [charges: ~1_charges~] : [linked pet: ~2_petName~] + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, 1054131, m_Charges.ToString() + ( m_PetName == "" ? "\t " : "\t" + m_PetName ) ); // a crystal ball of pet summoning: [charges: ~1_charges~] : [linked pet: ~2_petName~] + } + + private delegate void BallCallback( Mobile from ); + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive && from.InRange( this.GetWorldLocation(), 2 ) ) + { + if ( Pet == null ) + { + list.Add( new BallEntry( new BallCallback( LinkPet ), 6180 ) ); + } + else + { + list.Add( new BallEntry( new BallCallback( SummonPet ), 6181 ) ); + list.Add( new BallEntry( new BallCallback( UpdatePetName ), 6183 ) ); + list.Add( new BallEntry( new BallCallback( UnlinkPet ), 6182 ) ); + } + } + } + + private class BallEntry : ContextMenuEntry + { + private BallCallback m_Callback; + + public BallEntry( BallCallback callback, int number ) : base( number, 2 ) + { + m_Callback = callback; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( from.CheckAlive() ) + m_Callback( from ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 2 ) ) + { + if ( Pet == null ) + { + LinkPet( from ); + } + else + { + SummonPet( from ); + } + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + + public void LinkPet( Mobile from ) + { + BaseCreature pet = this.Pet; + + if ( Deleted || pet != null ) + return; + + from.SendLocalizedMessage( 1054114 ); // Target your pet that you wish to link to this Crystal Ball of Pet Summoning. + from.Target = new PetLinkTarget( this ); + } + + private class PetLinkTarget : Target + { + private BallOfSummoning m_Ball; + + public PetLinkTarget( BallOfSummoning ball ) : base( -1, false, TargetFlags.None ) + { + m_Ball = ball; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Ball.Deleted || m_Ball.Pet != null ) + return; + + if ( !from.InRange( m_Ball.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + else if ( targeted is BaseCreature ) + { + BaseCreature creature = (BaseCreature)targeted; + + if ( !creature.Controlled || creature.ControlMaster != from ) + { + MessageHelper.SendLocalizedMessageTo( m_Ball, from, 1054117, 0x59 ); // You may only link your own pets to a Crystal Ball of Pet Summoning. + } + else if ( !creature.IsBonded ) + { + MessageHelper.SendLocalizedMessageTo( m_Ball, from, 1054118, 0x59 ); // You must bond with your pet before it can be linked to a Crystal Ball of Pet Summoning. + } + else + { + MessageHelper.SendLocalizedMessageTo( m_Ball, from, 1054119, 0x59 ); // Your pet is now linked to this Crystal Ball of Pet Summoning. + + m_Ball.Pet = creature; + } + } + else if ( targeted == m_Ball ) + { + MessageHelper.SendLocalizedMessageTo( m_Ball, from, 1054115, 0x59 ); // The Crystal Ball of Pet Summoning cannot summon itself. + } + else + { + MessageHelper.SendLocalizedMessageTo( m_Ball, from, 1054116, 0x59 ); // Only pets can be linked to this Crystal Ball of Pet Summoning. + } + } + } + + public void SummonPet( Mobile from ) + { + BaseCreature pet = this.Pet; + + if ( Deleted || pet == null ) + return; + + if ( Charges == 0 ) + { + SendLocalizedMessageTo( from, 1054122 ); // The Crystal Ball darkens. It must be charged before it can be used again. + } + else if ( pet is BaseMount && ((BaseMount)pet).Rider == from ) + { + MessageHelper.SendLocalizedMessageTo( this, from, 1054124, 0x36 ); // The Crystal Ball fills with a yellow mist. Why would you summon your pet while riding it? + } + else if ( pet.Map == Map.Internal && ( !pet.IsStabled || (from.Followers + pet.ControlSlots) > from.FollowersMax ) ) + { + MessageHelper.SendLocalizedMessageTo( this, from, 1054125, 0x5 ); // The Crystal Ball fills with a blue mist. Your pet is not responding to the summons. + } + else if ( ( !pet.Controlled || pet.ControlMaster != from ) && !from.Stabled.Contains( pet ) ) + { + MessageHelper.SendLocalizedMessageTo( this, from, 1054126, 0x8FD ); // The Crystal Ball fills with a grey mist. You are not the owner of the pet you are attempting to summon. + } + else if ( !pet.IsBonded ) + { + MessageHelper.SendLocalizedMessageTo( this, from, 1054127, 0x22 ); // The Crystal Ball fills with a red mist. You appear to have let your bond to your pet deteriorate. + } + else if ( from.Map == Map.Ilshenar || from.Region.IsPartOf( typeof( DungeonRegion ) ) || from.Region.IsPartOf( typeof( Jail ) ) ) + { + from.Send( new AsciiMessage( this.Serial, this.ItemID, MessageType.Regular, 0x22, 3, "", "You cannot summon your pet to this location." ) ); + } + else + { + Charges--; + + if ( pet.IsStabled ) + { + pet.SetControlMaster( from ); + + if ( pet.Summoned ) + pet.SummonMaster = from; + + pet.ControlTarget = from; + pet.ControlOrder = OrderType.Follow; + + pet.IsStabled = false; + from.Stabled.Remove( pet ); + } + + pet.MoveToWorld( from.Location, from.Map ); + + MessageHelper.SendLocalizedMessageTo( this, from, 1054128, 0x43 ); // The Crystal Ball fills with a green mist. Your pet has been summoned. + } + } + + public void UnlinkPet( Mobile from ) + { + if ( !Deleted && Pet != null ) + { + Pet = null; + + SendLocalizedMessageTo( from, 1054120 ); // This crystal ball is no longer linked to a pet. + } + } + + public void UpdatePetName( Mobile from ) + { + InternalUpdatePetName(); + } + + private void InternalUpdatePetName() + { + BaseCreature pet = this.Pet; + + if ( pet == null ) + m_PetName = ""; + else + m_PetName = pet.Name; + + InvalidateProperties(); + } + + public BallOfSummoning( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 1 ); // version + + writer.WriteEncodedInt( (int) m_Recharges ); + + writer.WriteEncodedInt( (int) m_Charges ); + writer.Write( (Mobile) this.Pet ); + writer.Write( (string) m_PetName ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Recharges = reader.ReadEncodedInt(); + goto case 0; + } + case 0: + { + m_Charges = Math.Min( reader.ReadEncodedInt(), MaxCharges ); + this.Pet = (BaseCreature) reader.ReadMobile(); + m_PetName = reader.ReadString(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs b/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs new file mode 100644 index 000000000..b07589c14 --- /dev/null +++ b/Scripts/Items/Special/Solen Items/BraceletOfBinding.cs @@ -0,0 +1,474 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.ContextMenus; +using Server.Network; +using Server.Prompts; +using Server.Targeting; +using Server.Spells; +using Server.Mobiles; + +namespace Server.Items +{ + public class BraceletOfBinding : BaseBracelet, TranslocationItem + { + private int m_Charges; + private int m_Recharges; + private string m_Inscription; + private BraceletOfBinding m_Bound; + private TransportTimer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set + { + if ( value > this.MaxCharges ) + m_Charges = this.MaxCharges; + else if ( value < 0 ) + m_Charges = 0; + else + m_Charges = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Recharges + { + get{ return m_Recharges; } + set + { + if ( value > this.MaxRecharges ) + m_Recharges = this.MaxRecharges; + else if ( value < 0 ) + m_Recharges = 0; + else + m_Recharges = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxCharges{ get{ return 20; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxRecharges{ get{ return 255; } } + + public string TranslocationItemName{ get{ return "bracelet of binding"; } } + + [CommandProperty( AccessLevel.GameMaster )] + public string Inscription + { + get{ return m_Inscription; } + set + { + m_Inscription = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BraceletOfBinding Bound + { + get + { + if ( m_Bound != null && m_Bound.Deleted ) + m_Bound = null; + + return m_Bound; + } + set{ m_Bound = value; } + } + + [Constructable] + public BraceletOfBinding() : base( 0x1086 ) + { + Hue = 0x489; + Weight = 1.0; + + m_Inscription = ""; + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + list.Add( 1054000, m_Charges.ToString() + ( m_Inscription == "" ? "\t " : " :\t" + m_Inscription ) ); // a bracelet of binding : ~1_val~ ~2_val~ + } + + public override void OnSingleClick( Mobile from ) + { + LabelTo( from, 1054000, m_Charges.ToString() + ( m_Inscription == "" ? "\t " : " :\t" + m_Inscription ) ); // a bracelet of binding : ~1_val~ ~2_val~ + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive && this.IsChildOf( from ) ) + { + BraceletOfBinding bound = this.Bound; + + list.Add( new BraceletEntry( new BraceletCallback( Activate ), 6170, bound != null ) ); + list.Add( new BraceletEntry( new BraceletCallback( Search ), 6171, bound != null ) ); + list.Add( new BraceletEntry( new BraceletCallback( Bind ), bound == null ? 6173 : 6174, true ) ); + list.Add( new BraceletEntry( new BraceletCallback( Inscribe ), 6175, true ) ); + } + } + + private delegate void BraceletCallback( Mobile from ); + + private class BraceletEntry : ContextMenuEntry + { + private BraceletCallback m_Callback; + + public BraceletEntry( BraceletCallback callback, int number, bool enabled ) : base( number ) + { + m_Callback = callback; + + if ( !enabled ) + Flags |= CMEFlags.Disabled; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( from.CheckAlive() ) + m_Callback( from ); + } + } + + public override void OnDoubleClick( Mobile from ) + { + BraceletOfBinding bound = this.Bound; + + if ( Bound == null ) + { + Bind( from ); + } + else + { + Activate( from ); + } + } + + public void Activate( Mobile from ) + { + BraceletOfBinding bound = this.Bound; + + if ( Deleted || bound == null ) + return; + + if ( !this.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else if ( m_Timer != null ) + { + from.SendLocalizedMessage( 1054013 ); // The bracelet is already attempting contact. You decide to wait a moment. + } + else + { + from.PlaySound( 0xF9 ); + from.LocalOverheadMessage( MessageType.Regular, 0x5D, true, "* You concentrate on the bracelet to summon its power *" ); + + from.Frozen = true; + + m_Timer = new TransportTimer( this, from ); + m_Timer.Start(); + } + } + + private class TransportTimer : Timer + { + private BraceletOfBinding m_Bracelet; + private Mobile m_From; + + public TransportTimer( BraceletOfBinding bracelet, Mobile from ) : base( TimeSpan.FromSeconds( 2.0 ) ) + { + m_Bracelet = bracelet; + m_From = from; + } + + protected override void OnTick() + { + m_Bracelet.m_Timer = null; + m_From.Frozen = false; + + if ( m_Bracelet.Deleted || m_From.Deleted ) + return; + + if ( m_Bracelet.CheckUse( m_From, false ) ) + { + Mobile boundRoot = m_Bracelet.Bound.RootParent as Mobile; + + if ( boundRoot != null ) + { + m_Bracelet.Charges--; + + BaseCreature.TeleportPets( m_From, boundRoot.Location, boundRoot.Map, true ); + + m_From.PlaySound( 0x1FC ); + m_From.MoveToWorld( boundRoot.Location, boundRoot.Map ); + m_From.PlaySound( 0x1FC ); + } + } + } + } + + public void Search( Mobile from ) + { + BraceletOfBinding bound = this.Bound; + + if ( Deleted || bound == null ) + return; + + if ( !this.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else + { + CheckUse( from, true ); + } + } + + private bool CheckUse( Mobile from, bool successMessage ) + { + BraceletOfBinding bound = this.Bound; + + if ( bound == null ) + return false; + + Mobile boundRoot = bound.RootParent as Mobile; + + if ( Charges == 0 ) + { + from.SendLocalizedMessage( 1054005 ); // The bracelet glows black. It must be charged before it can be used again. + return false; + } + else if ( from.FindItemOnLayer( Layer.Bracelet ) != this ) + { + from.SendLocalizedMessage( 1054004 ); // You must equip the bracelet in order to use its power. + return false; + } + else if ( boundRoot == null || boundRoot.NetState == null || boundRoot.FindItemOnLayer( Layer.Bracelet ) != bound ) + { + from.SendLocalizedMessage( 1054006 ); // The bracelet emits a red glow. The bracelet's twin is not available for transport. + return false; + } + else if ( !Core.AOS && from.Map != boundRoot.Map ) + { + from.SendLocalizedMessage( 1054014 ); // The bracelet glows black. The bracelet's target is on another facet. + return false; + } + else if ( Factions.Sigil.ExistsOn( from ) ) + { + from.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return false; + } + else if ( !SpellHelper.CheckTravel( from, TravelCheckType.RecallFrom ) ) + { + return false; + } + else if ( !SpellHelper.CheckTravel( from, boundRoot.Map, boundRoot.Location, TravelCheckType.RecallTo ) ) + { + return false; + } + else if ( boundRoot.Map == Map.Felucca && from is PlayerMobile && ((PlayerMobile)from).Young ) + { + from.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young. + return false; + } + else if ( from.Kills >= 5 && boundRoot.Map != Map.Felucca ) + { + from.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + return false; + } + else if ( from.Criminal ) + { + from.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + return false; + } + else if ( SpellHelper.CheckCombat( from ) ) + { + from.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + return false; + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( from ) ) + { + from.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + return false; + } + else if ( from.Region.IsPartOf( typeof( Server.Regions.Jail ) ) ) + { + from.SendLocalizedMessage( 1041530, "", 0x35 ); // You'll need a better jailbreak plan then that! + return false; + } + else if ( boundRoot.Region.IsPartOf( typeof( Server.Regions.Jail ) ) ) + { + from.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + return false; + } + else + { + if ( successMessage ) + from.SendLocalizedMessage( 1054015 ); // The bracelet's twin is available for transport. + + return true; + } + } + + public void Bind( Mobile from ) + { + if ( Deleted ) + return; + + if ( !this.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else + { + from.SendLocalizedMessage( 1054001 ); // Target the bracelet of binding you wish to bind this bracelet to. + from.Target = new BindTarget( this ); + } + } + + private class BindTarget : Target + { + private BraceletOfBinding m_Bracelet; + + public BindTarget( BraceletOfBinding bracelet ) : base( -1, false, TargetFlags.None ) + { + m_Bracelet = bracelet; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Bracelet.Deleted ) + return; + + if ( !m_Bracelet.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else if ( targeted is BraceletOfBinding ) + { + BraceletOfBinding bindBracelet = (BraceletOfBinding)targeted; + + if ( bindBracelet == m_Bracelet ) + { + from.SendLocalizedMessage( 1054012 ); // You cannot bind a bracelet of binding to itself! + } + else if ( !bindBracelet.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else + { + from.SendLocalizedMessage( 1054003 ); // You bind the bracelet to its counterpart. The bracelets glow with power. + from.PlaySound( 0x1FA ); + + m_Bracelet.Bound = bindBracelet; + } + } + else + { + from.SendLocalizedMessage( 1054002 ); // You can only bind this bracelet to another bracelet of binding! + } + } + } + + public void Inscribe( Mobile from ) + { + if ( Deleted ) + return; + + if ( !this.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else + { + from.SendLocalizedMessage( 1054009 ); // Enter the text to inscribe upon the bracelet : + from.Prompt = new InscribePrompt( this ); + } + } + + private class InscribePrompt : Prompt + { + private BraceletOfBinding m_Bracelet; + + public InscribePrompt( BraceletOfBinding bracelet ) + { + m_Bracelet = bracelet; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Bracelet.Deleted ) + return; + + if ( !m_Bracelet.IsChildOf( from ) ) + { + from.SendLocalizedMessage( 1042664 ); // You must have the object in your backpack to use it. + } + else + { + from.SendLocalizedMessage( 1054011 ); // You mark the bracelet with your inscription. + m_Bracelet.Inscription = text; + } + } + + public override void OnCancel( Mobile from ) + { + from.SendLocalizedMessage( 1054010 ); // You decide not to inscribe the bracelet at this time. + } + } + + public BraceletOfBinding( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 1 ); // version + + writer.WriteEncodedInt( (int) m_Recharges ); + + writer.WriteEncodedInt( (int) m_Charges ); + writer.Write( (string) m_Inscription ); + writer.Write( (Item) this.Bound ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch ( version ) + { + case 1: + { + m_Recharges = reader.ReadEncodedInt(); + goto case 0; + } + case 0: + { + m_Charges = Math.Min( reader.ReadEncodedInt(), MaxCharges ); + m_Inscription = reader.ReadString(); + this.Bound = (BraceletOfBinding) reader.ReadItem(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/MessageHelper.cs b/Scripts/Items/Special/Solen Items/MessageHelper.cs new file mode 100644 index 000000000..16d9addbf --- /dev/null +++ b/Scripts/Items/Special/Solen Items/MessageHelper.cs @@ -0,0 +1,18 @@ +using System; +using Server.Network; + +namespace Server +{ + public class MessageHelper + { + public static void SendLocalizedMessageTo( Item from, Mobile to, int number, int hue ) + { + SendLocalizedMessageTo( from, to, number, "", hue ); + } + + public static void SendLocalizedMessageTo( Item from, Mobile to, int number, string args, int hue ) + { + to.Send( new MessageLocalized( from.Serial, from.ItemID, MessageType.Regular, hue, 3, number, "", args ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/PowderOfTranslocation.cs b/Scripts/Items/Special/Solen Items/PowderOfTranslocation.cs new file mode 100644 index 000000000..965e99e26 --- /dev/null +++ b/Scripts/Items/Special/Solen Items/PowderOfTranslocation.cs @@ -0,0 +1,125 @@ +using System; +using Server; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public interface TranslocationItem + { + int Charges{ get; set; } + int Recharges{ get; set; } + int MaxCharges{ get; } + int MaxRecharges{ get; } + string TranslocationItemName{ get; } + } + + public class PowderOfTranslocation : Item + { + [Constructable] + public PowderOfTranslocation() : this( 1 ) + { + } + + [Constructable] + public PowderOfTranslocation( int amount ) : base( 0x26B8 ) + { + Stackable = true; + Weight = 0.1; + Amount = amount; + } + + + + public override void OnDoubleClick( Mobile from ) + { + if ( from.InRange( this.GetWorldLocation(), 2 ) ) + { + from.Target = new InternalTarget( this ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + + private class InternalTarget : Target + { + private PowderOfTranslocation m_Powder; + + public InternalTarget( PowderOfTranslocation powder ) : base( -1, false, TargetFlags.None ) + { + m_Powder = powder; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Powder.Deleted ) + return; + + if ( !from.InRange( m_Powder.GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + else if ( targeted is TranslocationItem ) + { + TranslocationItem transItem = (TranslocationItem)targeted; + + if ( transItem.Charges >= transItem.MaxCharges ) + { + MessageHelper.SendLocalizedMessageTo( m_Powder, from, 1054137, 0x59 ); // This item cannot absorb any more powder of translocation. + } + else if ( transItem.Recharges >= transItem.MaxRecharges ) + { + MessageHelper.SendLocalizedMessageTo( m_Powder, from, 1054138, 0x59 ); // This item has been oversaturated with powder of translocation and can no longer be recharged. + } + else + { + if ( transItem.Charges + m_Powder.Amount > transItem.MaxCharges ) + { + int delta = transItem.MaxCharges - transItem.Charges; + + m_Powder.Amount -= delta; + transItem.Charges = transItem.MaxCharges; + transItem.Recharges += delta; + } + else + { + transItem.Charges += m_Powder.Amount; + transItem.Recharges += m_Powder.Amount; + m_Powder.Delete(); + } + + if ( transItem is Item ) + { + // The ~1_translocationItem~ glows with green energy and absorbs magical power from the powder. + MessageHelper.SendLocalizedMessageTo( (Item)transItem, from, 1054139, transItem.TranslocationItemName, 0x43 ); + } + } + } + else + { + MessageHelper.SendLocalizedMessageTo( m_Powder, from, 1054140, 0x59 ); // Powder of translocation has no effect on this item. + } + } + } + + public PowderOfTranslocation( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Solen Items/ZoogiFungus.cs b/Scripts/Items/Special/Solen Items/ZoogiFungus.cs new file mode 100644 index 000000000..f0af9b50a --- /dev/null +++ b/Scripts/Items/Special/Solen Items/ZoogiFungus.cs @@ -0,0 +1,41 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ZoogiFungus : Item + { + [Constructable] + public ZoogiFungus() : this( 1 ) + { + } + + [Constructable] + public ZoogiFungus( int amount ) : base( 0x26B7 ) + { + Stackable = true; + Weight = 0.1; + Amount = amount; + } + + + + public ZoogiFungus( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/SoulStone.cs b/Scripts/Items/Special/SoulStone.cs new file mode 100644 index 000000000..d04bb00d9 --- /dev/null +++ b/Scripts/Items/Special/SoulStone.cs @@ -0,0 +1,877 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Accounting; + +namespace Server.Items +{ + public class SoulStone : Item + { + private int m_ActiveItemID; + private int m_InactiveItemID; + + [CommandProperty( AccessLevel.GameMaster )] + public virtual int ActiveItemID + { + get { return m_ActiveItemID; } + set + { + m_ActiveItemID = value; + + if( !IsEmpty ) + this.ItemID = m_ActiveItemID; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual int InactiveItemID + { + get { return m_InactiveItemID; } + set + { + m_InactiveItemID = value; + + if( IsEmpty ) + this.ItemID = m_InactiveItemID; + } + } + + public static readonly TimeSpan UseDelay = TimeSpan.FromDays( 1.0 ); + + private string m_Account; + private DateTime m_NextUse; + + private SkillName m_Skill; + private double m_SkillValue; + + [CommandProperty( AccessLevel.GameMaster )] + public string Account + { + get{ return m_Account; } + set{ m_Account = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NextUse + { + get{ return m_NextUse; } + set{ m_NextUse = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill + { + get{ return m_Skill; } + set{ m_Skill = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double SkillValue + { + get{ return m_SkillValue; } + set + { + m_SkillValue = value; + + if ( !IsEmpty ) + this.ItemID = m_ActiveItemID; + else + this.ItemID = m_InactiveItemID; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsEmpty + { + get{ return m_SkillValue <= 0.0; } + } + + [Constructable] + public SoulStone() : this( null ) + { + } + + [Constructable] + public SoulStone( string account ) + : this( account, 0x2A93, 0x2A94 ) + { + } + + public SoulStone( string account, int itemID ) + : this( account, itemID, itemID ) + { + } + + public SoulStone( string account, int inactiveItemID, int activeItemID ) : base( inactiveItemID ) + { + Light = LightType.Circle300; + LootType = LootType.Blessed; + + m_InactiveItemID = inactiveItemID; + m_ActiveItemID = activeItemID; + + m_Account = account; + m_NextUse = DateTime.MinValue; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( !this.IsEmpty ) + { + list.Add( 1070721, "#{0}\t{1:0.0}", 1044060 + (int)this.Skill, this.SkillValue ); // Skill stored: ~1_skillname~ ~2_skillamount~ + } + } + + private static bool CheckCombat( Mobile m, TimeSpan time ) + { + for ( int i = 0; i < m.Aggressed.Count; ++i ) + { + AggressorInfo info = m.Aggressed[i]; + + if ( DateTime.Now - info.LastCombatTime < time ) + return true; + } + + return false; + } + + protected virtual bool CheckUse( Mobile from ) + { + DateTime now = DateTime.Now; + + if ( this.Deleted || !this.IsAccessibleTo( from ) ) + { + return false; + } + else if ( from.Map != this.Map || !from.InRange( GetWorldLocation(), 2 ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + return false; + } + else if ( this.Account != null && ( !(from.Account is Account) || from.Account.Username != this.Account ) ) + { + from.SendLocalizedMessage( 1070714 ); // This is an Account Bound Soulstone, and your character is not bound to it. You cannot use this Soulstone. + return false; + } + else if ( CheckCombat( from, TimeSpan.FromMinutes( 2.0 ) ) ) + { + from.SendLocalizedMessage( 1070727 ); // You must wait two minutes after engaging in combat before you can use a Soulstone. + return false; + } + else if ( from.Criminal ) + { + from.SendLocalizedMessage( 1070728 ); // You must wait two minutes after committing a criminal act before you can use a Soulstone. + return false; + } + else if ( from.Region.GetLogoutDelay( from ) > TimeSpan.Zero ) + { + from.SendLocalizedMessage( 1070729 ); // In order to use your Soulstone, you must be in a safe log-out location. + return false; + } + else if ( !from.Alive ) + { + from.SendLocalizedMessage( 1070730 ); // You may not use a Soulstone while your character is dead. + return false; + } + else if ( Factions.Sigil.ExistsOn( from ) ) + { + from.SendLocalizedMessage( 1070731 ); // You may not use a Soulstone while your character has a faction town sigil. + return false; + } + else if ( from.Spell != null && from.Spell.IsCasting ) + { + from.SendLocalizedMessage( 1070733 ); // You may not use a Soulstone while your character is casting a spell. + return false; + } + else if ( from.Poisoned ) + { + from.SendLocalizedMessage( 1070734 ); // You may not use a Soulstone while your character is poisoned. + return false; + } + else if ( from.Paralyzed ) + { + from.SendLocalizedMessage( 1070735 ); // You may not use a Soulstone while your character is paralyzed. + return false; + } + else if ( now < this.NextUse ) + { + TimeSpan time = this.NextUse - now; + + if ( time.TotalHours > 0.0 ) + from.SendLocalizedMessage( 1070736, ((int)time.TotalHours).ToString() ); // You must wait ~1_hours~ hours before you can use your Soulstone. + else if ( time.TotalMinutes > 0.0 ) + from.SendLocalizedMessage( 1070737, ((int)time.TotalMinutes).ToString() ); // You must wait ~1_minutes~ minutes before you can use your Soulstone. + else + from.SendLocalizedMessage( 1070738, ((int)time.TotalSeconds).ToString() ); // You must wait ~1_seconds~ seconds before you can use your Soulstone. + + return false; + } + else + { + return true; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !CheckUse( from ) ) + return; + + from.CloseGump( typeof( SelectSkillGump ) ); + from.CloseGump( typeof( ConfirmSkillGump ) ); + from.CloseGump( typeof( ConfirmTransferGump ) ); + from.CloseGump( typeof( ConfirmRemovalGump ) ); + from.CloseGump( typeof( ErrorGump ) ); + + if ( this.IsEmpty ) + from.SendGump( new SelectSkillGump( this, from ) ); + else + from.SendGump( new ConfirmTransferGump( this, from ) ); + } + + private class SelectSkillGump : Gump + { + private SoulStone m_Stone; + + public SelectSkillGump( SoulStone stone, Mobile from ) : base( 50, 50 ) + { + m_Stone = stone; + + AddPage( 0 ); + + AddBackground( 0, 0, 520, 440, 0x13BE ); + + AddImageTiled( 10, 10, 500, 20, 0xA40 ); + AddImageTiled( 10, 40, 500, 360, 0xA40 ); + AddImageTiled( 10, 410, 500, 20, 0xA40 ); + + AddAlphaRegion( 10, 10, 500, 420 ); + + AddHtmlLocalized( 10, 12, 500, 20, 1061087, 0x7FFF, false, false ); // Which skill do you wish to transfer to the Soulstone? + + AddButton( 10, 410, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 412, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + + for ( int i = 0, n = 0; i < from.Skills.Length; i++ ) + { + Skill skill = from.Skills[i]; + + if ( skill.Base > 0.0 ) + { + int p = n % 30; + + if ( p == 0 ) + { + int page = n / 30; + + if ( page > 0 ) + { + AddButton( 260, 380, 0xFA5, 0xFA6, 0, GumpButtonType.Page, page + 1 ); + AddHtmlLocalized( 305, 382, 200, 20, 1011066, 0x7FFF, false, false ); // Next page + } + + AddPage( page + 1 ); + + if ( page > 0 ) + { + AddButton( 10, 380, 0xFAE, 0xFAF, 0, GumpButtonType.Page, page ); + AddHtmlLocalized( 55, 382, 200, 20, 1011067, 0x7FFF, false, false ); // Previous page + } + } + + int x = ( p % 2 == 0 ) ? 10 : 260; + int y = ( p / 2 ) * 20 + 40; + + AddButton( x, y, 0xFA5, 0xFA6, i + 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( x + 45, y + 2, 200, 20, 1044060 + i, 0x7FFF, false, false ); + + n++; + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 || !m_Stone.IsEmpty ) + return; + + Mobile from = sender.Mobile; + + int iSkill = info.ButtonID - 1; + if ( iSkill < 0 || iSkill >= from.Skills.Length ) + return; + + Skill skill = from.Skills[iSkill]; + if ( skill.Base <= 0.0 ) + return; + + if ( !m_Stone.CheckUse( from ) ) + return; + + from.SendGump( new ConfirmSkillGump( m_Stone, skill ) ); + } + } + + private class ConfirmSkillGump : Gump + { + private SoulStone m_Stone; + private Skill m_Skill; + + public ConfirmSkillGump( SoulStone stone, Skill skill ) : base( 50, 50 ) + { + m_Stone = stone; + m_Skill = skill; + + AddBackground( 0, 0, 520, 440, 0x13BE ); + + AddImageTiled( 10, 10, 500, 20, 0xA40 ); + AddImageTiled( 10, 40, 500, 360, 0xA40 ); + AddImageTiled( 10, 410, 500, 20, 0xA40 ); + + AddAlphaRegion( 10, 10, 500, 420 ); + + AddHtmlLocalized( 10, 12, 500, 20, 1070709, 0x7FFF, false, false ); //
Confirm Soulstone Transfer
+ + /*
Soulstone

+ * You are using a Soulstone. This powerful artifact allows you to remove skill points + * from your character and store them in the stone for later retrieval. In order to use + * the stone, you must make sure your Skill Lock for the indicated skill is pointed downward. + * Click the "Skills" button on your Paperdoll to access the Skill List, and double-check + * your skill lock.

+ * + * Once you activate the stone, all skill points in the indicated skill will be removed from + * your character. These skill points can later be retrieved. IMPORTANT: When retrieving + * skill points from a Soulstone, the Soulstone WILL REPLACE any existing skill points + * already on your character!

+ * + * This is an Account Bound Soulstone. Skill pointsstored inside can be retrieved by any + * character on the same account as the character who placed them into the stone. + */ + AddHtmlLocalized( 10, 42, 500, 110, 1061067, 0x7FFF, false, true ); + + AddHtmlLocalized( 10, 200, 390, 20, 1062297, 0x7FFF, false, false ); // Skill Chosen: + AddHtmlLocalized( 210, 200, 390, 20, 1044060 + skill.SkillID, 0x7FFF, false, false ); + + AddHtmlLocalized( 10, 220, 390, 20, 1062298, 0x7FFF, false, false ); // Current Value: + AddLabel( 210, 220, 0x481, skill.Base.ToString( "0.0" ) ); + + AddHtmlLocalized( 10, 240, 390, 20, 1062299, 0x7FFF, false, false ); // Current Cap: + AddLabel( 210, 240, 0x481, skill.Cap.ToString( "0.0" ) ); + + AddHtmlLocalized( 10, 260, 390, 20, 1062300, 0x7FFF, false, false ); // New Value: + AddLabel( 210, 260, 0x481, "0.0" ); + + AddButton( 10, 360, 0xFA5, 0xFA6, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 362, 450, 20, 1070720, 0x7FFF, false, false ); // Activate the stone. I am ready to transfer the skill points to it. + + AddButton( 10, 380, 0xFA5, 0xFA6, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 382, 450, 20, 1062279, 0x7FFF, false, false ); // No, let me make another selection. + + AddButton( 10, 410, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 412, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 || !m_Stone.IsEmpty ) + return; + + Mobile from = sender.Mobile; + + if ( !m_Stone.CheckUse( from ) ) + return; + + if ( info.ButtonID == 1 ) // Is asking for another selection + { + from.SendGump( new SelectSkillGump( m_Stone, from ) ); + return; + } + + if ( m_Skill.Base <= 0.0 ) + return; + + if ( m_Skill.Lock != SkillLock.Down ) + { + //
Unable to Transfer Selected Skill to Soulstone
+ + /* You cannot transfer the selected skill to the Soulstone at this time. The selected + * skill may be locked or set to raise in your skill menu. Click on "Skills" in your + * paperdoll menu to check your raise/locked/lower settings and your total skills. + * Make any needed adjustments, then click "Continue". If you do not wish to transfer + * the selected skill at this time, click "Cancel". + */ + + from.SendGump( new ErrorGump( m_Stone, 1070710, 1070711 ) ); + return; + } + + m_Stone.Skill = m_Skill.SkillName; + m_Stone.SkillValue = m_Skill.Base; + + m_Skill.Base = 0.0; + + from.SendLocalizedMessage( 1070712 ); // You have successfully transferred your skill points into the Soulstone. + + Effects.SendLocationParticles( EffectItem.Create( from.Location, from.Map, EffectItem.DefaultDuration ), 0, 0, 0, 0, 0, 5060, 0 ); + Effects.PlaySound( from.Location, from.Map, 0x243 ); + + Effects.SendMovingParticles( new Entity( Server.Serial.Zero, new Point3D( from.X - 6, from.Y - 6, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + + Effects.SendTargetParticles( from, 0x375A, 35, 90, 0x00, 0x00, 9502, (EffectLayer)255, 0x100 ); + } + } + + private class ConfirmTransferGump : Gump + { + private SoulStone m_Stone; + + public ConfirmTransferGump( SoulStone stone, Mobile from ) : base( 50, 50 ) + { + m_Stone = stone; + + AddBackground( 0, 0, 520, 440, 0x13BE ); + + AddImageTiled( 10, 10, 500, 20, 0xA40 ); + AddImageTiled( 10, 40, 500, 360, 0xA40 ); + AddImageTiled( 10, 410, 500, 20, 0xA40 ); + + AddAlphaRegion( 10, 10, 500, 420 ); + + AddHtmlLocalized( 10, 12, 500, 20, 1070709, 0x7FFF, false, false ); //
Confirm Soulstone Transfer
+ + /*
Soulstone

+ * You are using a Soulstone. This powerful artifact allows you to remove skill points + * from your character and store them in the stone for later retrieval. In order to use + * the stone, you must make sure your Skill Lock for the indicated skill is pointed downward. + * Click the "Skills" button on your Paperdoll to access the Skill List, and double-check + * your skill lock.

+ * + * Once you activate the stone, all skill points in the indicated skill will be removed from + * your character. These skill points can later be retrieved. IMPORTANT: When retrieving + * skill points from a Soulstone, the Soulstone WILL REPLACE any existing skill points + * already on your character!

+ * + * This is an Account Bound Soulstone. Skill pointsstored inside can be retrieved by any + * character on the same account as the character who placed them into the stone. + */ + AddHtmlLocalized( 10, 42, 500, 110, 1061067, 0x7FFF, false, true ); + + AddHtmlLocalized( 10, 200, 390, 20, 1070718, 0x7FFF, false, false ); // Skill Stored: + AddHtmlLocalized( 210, 200, 390, 20, 1044060 + (int)stone.Skill, 0x7FFF, false, false ); + + Skill fromSkill = from.Skills[stone.Skill]; + + AddHtmlLocalized( 10, 220, 390, 20, 1062298, 0x7FFF, false, false ); // Current Value: + AddLabel( 210, 220, 0x481, fromSkill.Base.ToString( "0.0" ) ); + + AddHtmlLocalized( 10, 240, 390, 20, 1062299, 0x7FFF, false, false ); // Current Cap: + AddLabel( 210, 240, 0x481, fromSkill.Cap.ToString( "0.0" ) ); + + AddHtmlLocalized( 10, 260, 390, 20, 1062300, 0x7FFF, false, false ); // New Value: + AddLabel( 210, 260, 0x481, stone.SkillValue.ToString( "0.0" ) ); + + AddButton( 10, 360, 0xFA5, 0xFA6, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 362, 450, 20, 1070719, 0x7FFF, false, false ); // Activate the stone. I am ready to retrieve the skill points from it. + + AddButton( 10, 380, 0xFA5, 0xFA6, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 382, 450, 20, 1070723, 0x7FFF, false, false ); // Remove all skill points from this stone and DO NOT absorb them. + + AddButton( 10, 410, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 412, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 || m_Stone.IsEmpty ) + return; + + Mobile from = sender.Mobile; + + if ( !m_Stone.CheckUse( from ) ) + return; + + if ( info.ButtonID == 1 ) // Remove skill points + { + from.SendGump( new ConfirmRemovalGump( m_Stone ) ); + return; + } + + SkillName skill = m_Stone.Skill; + double skillValue = m_Stone.SkillValue; + Skill fromSkill = from.Skills[m_Stone.Skill]; + + /* If we have, say, 88.4 in our skill and the stone holds 100, we need + * 11.6 free points. Also, if we're below our skillcap by, say, 8.2 points, + * we only need 11.6 - 8.2 = 3.4 points. + */ + int requiredAmount = (int)(skillValue * 10) - fromSkill.BaseFixedPoint - (from.SkillsCap - from.SkillsTotal); + + bool cannotAbsorb = false; + + if ( fromSkill.Lock != SkillLock.Up ) + { + cannotAbsorb = true; + } + else if ( requiredAmount > 0 ) + { + int available = 0; + + for ( int i = 0; i < from.Skills.Length; ++i ) + { + if ( from.Skills[i].Lock != SkillLock.Down ) + continue; + + available += from.Skills[i].BaseFixedPoint; + } + + if ( requiredAmount > available ) + cannotAbsorb = true; + } + + //if ( fromSkill.Lock != SkillLock.Up || ( from.SkillsTotal - fromSkill.BaseFixedPoint + (int)(skillValue * 10) > from.SkillsCap ) ) + if ( cannotAbsorb ) + { + //
Unable to Absorb Selected Skill from Soulstone
+ + /* You cannot absorb the selected skill from the Soulstone at this time. The selected + * skill may be locked or set to lower in your skill menu. You may also be at your + * total skill cap. Click on "Skills" in your paperdoll menu to check your + * raise/locked/lower settings and your total skills. Make any needed adjustments, + * then click "Continue". If you do not wish to transfer the selected skill at this + * time, click "Cancel". + */ + + from.SendGump( new ErrorGump( m_Stone, 1070717, 1070716 ) ); + return; + } + + if ( skillValue > fromSkill.Cap ) + { + //
Unable to Absorb Selected Skill from Soulstone
+ + /* The amount of skill stored in this stone exceeds your individual skill cap for + * that skill. In order to retrieve the skill points stored in this stone, you must + * obtain a Power Scroll of the appropriate type and level in order to increase your + * skill cap. You cannot currently retrieve the skill points stored in this stone. + */ + + from.SendGump( new ErrorGump( m_Stone, 1070717, 1070715 ) ); + return; + } + + if ( fromSkill.Base >= skillValue ) + { + //
Unable to Absorb Selected Skill from Soulstone
+ + /* You cannot transfer the selected skill to the Soulstone at this time. The selected + * skill has a skill level higher than what is stored in the Soulstone. + */ + + // Wrong message?! + + from.SendGump( new ErrorGump( m_Stone, 1070717, 1070802 ) ); + return; + } + + if ( requiredAmount > 0 ) + { + for ( int i = 0; i < from.Skills.Length; ++i ) + { + if ( from.Skills[i].Lock != SkillLock.Down ) + continue; + + if ( requiredAmount >= from.Skills[i].BaseFixedPoint ) + { + requiredAmount -= from.Skills[i].BaseFixedPoint; + from.Skills[i].Base = 0.0; + } + else + { + from.Skills[i].BaseFixedPoint -= requiredAmount; + break; + } + } + } + + fromSkill.Base = skillValue; + m_Stone.SkillValue = 0.0; + + m_Stone.NextUse = DateTime.Now + UseDelay; + + from.SendLocalizedMessage( 1070713 ); // You have successfully absorbed the Soulstone's skill points. + + Effects.SendLocationParticles( EffectItem.Create( from.Location, from.Map, EffectItem.DefaultDuration ), 0, 0, 0, 0, 0, 5060, 0 ); + Effects.PlaySound( from.Location, from.Map, 0x243 ); + + Effects.SendMovingParticles( new Entity( Server.Serial.Zero, new Point3D( from.X - 6, from.Y - 6, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + + Effects.SendTargetParticles( from, 0x375A, 35, 90, 0x00, 0x00, 9502, (EffectLayer)255, 0x100 ); + + if( m_Stone is SoulstoneFragment ) + { + SoulstoneFragment frag = m_Stone as SoulstoneFragment; + + if( --frag.UsesRemaining <= 0 ) + from.SendLocalizedMessage( 1070974 ); // You have used up your soulstone fragment. + } + } + } + + private class ConfirmRemovalGump : Gump + { + private SoulStone m_Stone; + + public ConfirmRemovalGump( SoulStone stone ) : base( 50, 50 ) + { + m_Stone = stone; + + AddBackground( 0, 0, 520, 440, 0x13BE ); + + AddImageTiled( 10, 10, 500, 20, 0xA40 ); + AddImageTiled( 10, 40, 500, 360, 0xA40 ); + AddImageTiled( 10, 410, 500, 20, 0xA40 ); + + AddAlphaRegion( 10, 10, 500, 420 ); + + AddHtmlLocalized( 10, 12, 500, 20, 1070725, 0x7FFF, false, false ); //
Confirm Soulstone Skill Removal
+ + /* WARNING!

+ * + * You are about to permanently remove all skill points stored in this Soulstone. + * You WILL NOT absorb these skill points. They will be DELETED.

+ * + * Are you sure you wish to do this? If not, press the Cancel button. + */ + AddHtmlLocalized( 10, 42, 500, 110, 1070724, 0x7FFF, false, true ); + + AddButton( 10, 380, 0xFA5, 0xFA6, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 382, 450, 20, 1052072, 0x7FFF, false, false ); // Continue + + AddButton( 10, 410, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 412, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 || m_Stone.IsEmpty ) + return; + + Mobile from = sender.Mobile; + + if ( !m_Stone.CheckUse( from ) ) + return; + + m_Stone.SkillValue = 0.0; + from.SendLocalizedMessage( 1070726 ); // You have successfully deleted the Soulstone's skill points. + } + } + + private class ErrorGump : Gump + { + private SoulStone m_Stone; + + public ErrorGump( SoulStone stone, int title, int message ) : base( 50, 50 ) + { + m_Stone = stone; + + AddBackground( 0, 0, 520, 440, 0x13BE ); + + AddImageTiled( 10, 10, 500, 20, 0xA40 ); + AddImageTiled( 10, 40, 500, 360, 0xA40 ); + AddImageTiled( 10, 410, 500, 20, 0xA40 ); + + AddAlphaRegion( 10, 10, 500, 420 ); + + AddHtmlLocalized( 10, 12, 500, 20, title, 0x7FFF, false, false ); + + AddHtmlLocalized( 10, 42, 500, 110, message, 0x7FFF, false, true ); + + AddButton( 10, 380, 0xFA5, 0xFA6, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 382, 450, 20, 1052072, 0x7FFF, false, false ); // Continue + + AddButton( 10, 410, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 412, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 0 ) + return; + + Mobile from = sender.Mobile; + + if ( !m_Stone.CheckUse( from ) ) + return; + + if ( m_Stone.IsEmpty ) + from.SendGump( new SelectSkillGump( m_Stone, from ) ); + else + from.SendGump( new ConfirmTransferGump( m_Stone, from ) ); + } + } + + public SoulStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); // version + + writer.Write( m_ActiveItemID ); + writer.Write( m_InactiveItemID ); + + writer.Write( (string) m_Account ); + writer.Write( (DateTime) m_NextUse ); + + writer.WriteEncodedInt( (int) m_Skill ); + writer.Write( (double) m_SkillValue ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + switch( version ) + { + case 1: + { + m_ActiveItemID = reader.ReadInt(); + m_InactiveItemID = reader.ReadInt(); + + goto case 0; + } + case 0: + { + m_Account = reader.ReadString(); + m_NextUse = reader.ReadDateTime(); + + m_Skill = (SkillName)reader.ReadEncodedInt(); + m_SkillValue = reader.ReadDouble(); + break; + } + } + + if( version == 0 ) + { + m_ActiveItemID = 0x2A94; + m_InactiveItemID = 0x2A93; + } + } + } + + public class SoulstoneFragment : SoulStone, IUsesRemaining + { + private int m_UsesRemaining; + + public override int LabelNumber { get { return 1071000; } } // soulstone fragment + + [Constructable] + public SoulstoneFragment() : this( 5, null ) + { + } + + [Constructable] + public SoulstoneFragment( int usesRemaining ) : this( usesRemaining, null ) + { + } + + [Constructable] + public SoulstoneFragment( string account ) : this( 5, account ) + { + } + + [Constructable] + public SoulstoneFragment( int usesRemaining, string account ) : base( account, Utility.Random( 0x2AA1, 9 ) ) + { + m_UsesRemaining = usesRemaining; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1060584, m_UsesRemaining.ToString() ); // uses remaining: ~1_val~ + } + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get + { + return m_UsesRemaining; + } + set + { + m_UsesRemaining = value; InvalidateProperties(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 2 ); // version + + writer.WriteEncodedInt( m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_UsesRemaining = reader.ReadEncodedInt(); + + if( version <= 1 ) + { + if( ItemID == 0x2A93 || ItemID == 0x2A94 ) + { + ActiveItemID = Utility.Random( 0x2AA1, 9 ); + } + else + { + ActiveItemID = ItemID; + } + + InactiveItemID = ActiveItemID; + } + + if ( version == 0 && Weight == 1 ) + Weight = -1; + } + + public SoulstoneFragment( Serial serial ) : base( serial ) + { + } + + protected override bool CheckUse( Mobile from ) + { + bool canUse = base.CheckUse( from ); + + if( canUse ) + { + if( m_UsesRemaining <= 0 ) + { + from.SendLocalizedMessage( 1070975 ); // That soulstone fragment has no more uses. + return false; + } + } + + return canUse; + } + + + public bool ShowUsesRemaining{ get{ return true; } set{} } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Special Scrolls/PowerScroll.cs b/Scripts/Items/Special/Special Scrolls/PowerScroll.cs new file mode 100644 index 000000000..f35c22cca --- /dev/null +++ b/Scripts/Items/Special/Special Scrolls/PowerScroll.cs @@ -0,0 +1,368 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class PowerScroll : Item + { + private SkillName m_Skill; + private double m_Value; + + private static SkillName[] m_Skills = new SkillName[] + { + SkillName.Blacksmith, + SkillName.Tailoring, + SkillName.Swords, + SkillName.Fencing, + SkillName.Macing, + SkillName.Archery, + SkillName.Wrestling, + SkillName.Parry, + SkillName.Tactics, + SkillName.Anatomy, + SkillName.Healing, + SkillName.Magery, + SkillName.Meditation, + SkillName.EvalInt, + SkillName.MagicResist, + SkillName.AnimalTaming, + SkillName.AnimalLore, + SkillName.Veterinary, + SkillName.Musicianship, + SkillName.Provocation, + SkillName.Discordance, + SkillName.Peacemaking + }; + + private static SkillName[] m_AOSSkills = new SkillName[] + { + SkillName.Blacksmith, + SkillName.Tailoring, + SkillName.Swords, + SkillName.Fencing, + SkillName.Macing, + SkillName.Archery, + SkillName.Wrestling, + SkillName.Parry, + SkillName.Tactics, + SkillName.Anatomy, + SkillName.Healing, + SkillName.Magery, + SkillName.Meditation, + SkillName.EvalInt, + SkillName.MagicResist, + SkillName.AnimalTaming, + SkillName.AnimalLore, + SkillName.Veterinary, + SkillName.Musicianship, + SkillName.Provocation, + SkillName.Discordance, + SkillName.Peacemaking, + SkillName.Chivalry, + SkillName.Focus, + SkillName.Necromancy, + SkillName.Stealing, + SkillName.Stealth, + SkillName.SpiritSpeak + }; + + private static SkillName[] m_SESkills = new SkillName[] + { + SkillName.Blacksmith, + SkillName.Tailoring, + SkillName.Swords, + SkillName.Fencing, + SkillName.Macing, + SkillName.Archery, + SkillName.Wrestling, + SkillName.Parry, + SkillName.Tactics, + SkillName.Anatomy, + SkillName.Healing, + SkillName.Magery, + SkillName.Meditation, + SkillName.EvalInt, + SkillName.MagicResist, + SkillName.AnimalTaming, + SkillName.AnimalLore, + SkillName.Veterinary, + SkillName.Musicianship, + SkillName.Provocation, + SkillName.Discordance, + SkillName.Peacemaking, + SkillName.Chivalry, + SkillName.Focus, + SkillName.Necromancy, + SkillName.Stealing, + SkillName.Stealth, + SkillName.SpiritSpeak, + SkillName.Ninjitsu, + SkillName.Bushido + }; + + public static SkillName[] Skills{ get{ return ( Core.SE ? m_SESkills : Core.AOS ? m_AOSSkills : m_Skills ); } } + + public static PowerScroll CreateRandom( int min, int max ) + { + min /= 5; + max /= 5; + + SkillName[] skills = PowerScroll.Skills; + + return new PowerScroll( skills[Utility.Random( skills.Length )], 100 + (Utility.RandomMinMax( min, max ) * 5)); + } + + public static PowerScroll CreateRandomNoCraft( int min, int max ) + { + min /= 5; + max /= 5; + + SkillName[] skills = PowerScroll.Skills; + SkillName skillName; + + do + { + skillName = skills[Utility.Random( skills.Length )]; + } while ( skillName == SkillName.Blacksmith || skillName == SkillName.Tailoring ); + + return new PowerScroll( skillName, 100 + (Utility.RandomMinMax( min, max ) * 5)); + } + + [Constructable] + public PowerScroll( SkillName skill, double value ) : base( 0x14F0 ) + { + base.Hue = 0x481; + base.Weight = 1.0; + + + m_Skill = skill; + m_Value = value; + if ( m_Value > 105.0 ) + LootType = LootType.Cursed; + } + + public PowerScroll( Serial serial ) : base( serial ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill + { + get + { + return m_Skill; + } + set + { + m_Skill = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public double Value + { + get + { + return m_Value; + } + set + { + m_Value = value; + } + } + + private string GetNameLocalized() + { + return String.Concat( "#", (1044060 + (int)m_Skill).ToString() ); + } + + private string GetName() + { + int index = (int)m_Skill; + SkillInfo[] table = SkillInfo.Table; + + if ( index >= 0 && index < table.Length ) + return table[index].Name.ToLower(); + else + return "???"; + } + + public override void AddNameProperty(ObjectPropertyList list) + { + if ( m_Value == 105.0 ) + list.Add( 1049639, GetNameLocalized() ); // a wonderous scroll of ~1_type~ (105 Skill) + else if ( m_Value == 110.0 ) + list.Add( 1049640, GetNameLocalized() ); // an exalted scroll of ~1_type~ (110 Skill) + else if ( m_Value == 115.0 ) + list.Add( 1049641, GetNameLocalized() ); // a mythical scroll of ~1_type~ (115 Skill) + else if ( m_Value == 120.0 ) + list.Add( 1049642, GetNameLocalized() ); // a legendary scroll of ~1_type~ (120 Skill) + else + list.Add( "a power scroll of {0} ({1} Skill)", GetName(), m_Value ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Value == 105.0 ) + base.LabelTo( from, 1049639, GetNameLocalized() ); // a wonderous scroll of ~1_type~ (105 Skill) + else if ( m_Value == 110.0 ) + base.LabelTo( from, 1049640, GetNameLocalized() ); // an exalted scroll of ~1_type~ (110 Skill) + else if ( m_Value == 115.0 ) + base.LabelTo( from, 1049641, GetNameLocalized() ); // a mythical scroll of ~1_type~ (115 Skill) + else if ( m_Value == 120.0 ) + base.LabelTo( from, 1049642, GetNameLocalized() ); // a legendary scroll of ~1_type~ (120 Skill) + else + base.LabelTo( from, "a power scroll of {0} ({1} Skill)", GetName(), m_Value ); + } + + public void Use( Mobile from, bool firstStage ) + { + if ( Deleted ) + return; + + if ( IsChildOf( from.Backpack ) ) + { + Skill skill = from.Skills[m_Skill]; + + if ( skill != null ) + { + if ( skill.Cap >= m_Value ) + { + from.SendLocalizedMessage( 1049511, GetNameLocalized() ); // Your ~1_type~ is too high for this power scroll. + } + else + { + if ( firstStage ) + { + from.CloseGump( typeof( StatCapScroll.InternalGump ) ); + from.CloseGump( typeof( PowerScroll.InternalGump ) ); + from.SendGump( new InternalGump( from, this ) ); + } + else + { + from.SendLocalizedMessage( 1049513, GetNameLocalized() ); // You feel a surge of magic as the scroll enhances your ~1_type~! + + skill.Cap = m_Value; + + Effects.SendLocationParticles( EffectItem.Create( from.Location, from.Map, EffectItem.DefaultDuration ), 0, 0, 0, 0, 0, 5060, 0 ); + Effects.PlaySound( from.Location, from.Map, 0x243 ); + + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( from.X - 6, from.Y - 6, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( from.X - 4, from.Y - 6, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( from.X - 6, from.Y - 4, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + + Effects.SendTargetParticles( from, 0x375A, 35, 90, 0x00, 0x00, 9502, (EffectLayer)255, 0x100 ); + + Delete(); + } + } + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public override void OnDoubleClick( Mobile from ) + { + Use( from, true ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Skill ); + writer.Write( (double) m_Value ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Skill = (SkillName)reader.ReadInt(); + m_Value = reader.ReadDouble(); + + break; + } + } + + if ( m_Value == 105.0 ) + { + LootType = LootType.Regular; + } + else + { + LootType = LootType.Cursed; + + if ( Insured ) + Insured = false; + } + } + + public class InternalGump : Gump + { + private Mobile m_Mobile; + private PowerScroll m_Scroll; + + public InternalGump( Mobile mobile, PowerScroll scroll ) : base( 25, 50 ) + { + m_Mobile = mobile; + m_Scroll = scroll; + + AddPage( 0 ); + + AddBackground( 25, 10, 420, 200, 5054 ); + + AddImageTiled( 33, 20, 401, 181, 2624 ); + AddAlphaRegion( 33, 20, 401, 181 ); + + AddHtmlLocalized( 40, 48, 387, 100, 1049469, true, true ); /* Using a scroll increases the maximum amount of a specific skill or your maximum statistics. + * When used, the effect is not immediately seen without a gain of points with that skill or statistics. + * You can view your maximum skill values in your skills window. + * You can view your maximum statistic value in your statistics window. + */ + + AddHtmlLocalized( 125, 148, 200, 20, 1049478, 0xFFFFFF, false, false ); // Do you wish to use this scroll? + + AddButton( 100, 172, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 135, 172, 120, 20, 1046362, 0xFFFFFF, false, false ); // Yes + + AddButton( 275, 172, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 310, 172, 120, 20, 1046363, 0xFFFFFF, false, false ); // No + + double value = scroll.m_Value; + + if ( value == 105.0 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049635, 0xFFFFFF, false, false ); // Wonderous Scroll (105 Skill): + else if ( value == 110.0 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049636, 0xFFFFFF, false, false ); // Exalted Scroll (110 Skill): + else if ( value == 115.0 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049637, 0xFFFFFF, false, false ); // Mythical Scroll (115 Skill): + else if ( value == 120.0 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049638, 0xFFFFFF, false, false ); // Legendary Scroll (120 Skill): + else + AddHtml( 40, 20, 260, 20, String.Format( "Power Scroll ({0} Skill):", value ), false, false ); + + AddHtmlLocalized( 310, 20, 120, 20, 1044060 + (int)scroll.m_Skill, 0xFFFFFF, false, false ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_Scroll.Use( m_Mobile, false ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Special/Special Scrolls/StatScroll.cs b/Scripts/Items/Special/Special Scrolls/StatScroll.cs new file mode 100644 index 000000000..9cd255175 --- /dev/null +++ b/Scripts/Items/Special/Special Scrolls/StatScroll.cs @@ -0,0 +1,204 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Items +{ + public class StatCapScroll : Item + { + private int m_Value; + + [CommandProperty( AccessLevel.GameMaster )] + public int Value + { + get + { + return m_Value; + } + } + + [Constructable] + public StatCapScroll( int value ) : base( 0x14F0 ) + { + Hue = 0x481; + Weight = 1.0; + + LootType = LootType.Cursed; + + m_Value = value; + } + + public StatCapScroll( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperty(ObjectPropertyList list) + { + if ( m_Value == 230 ) + list.Add( 1049463, "#1049476" ); // a wonderous scroll of ~1_type~ (+5 Maximum Stats) + else if ( m_Value == 235 ) + list.Add( 1049464, "#1049476" ); // an exalted scroll of ~1_type~ (+10 Maximum Stats) + else if ( m_Value == 240 ) + list.Add( 1049465, "#1049476" ); // a mythical scroll of ~1_type~ (+15 Maximum Stats) + else if ( m_Value == 245 ) + list.Add( 1049466, "#1049476" ); // a legendary scroll of ~1_type~ (+20 Maximum Stats) + else if ( m_Value == 250 ) + list.Add( 1049467, "#1049476" ); // an ultimate scroll of ~1_type~ (+25 Maximum Stats) + else + list.Add( "a scroll of power ({0}{1} Maximum Stats)", (m_Value - 225) >= 0 ? "+" : "", m_Value - 225 ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Value == 230 ) + base.LabelTo( from, 1049463, "#1049476" ); // a wonderous scroll of ~1_type~ (+5 Maximum Stats) + else if ( m_Value == 235 ) + base.LabelTo( from, 1049464, "#1049476" ); // an exalted scroll of ~1_type~ (+10 Maximum Stats) + else if ( m_Value == 240 ) + base.LabelTo( from, 1049465, "#1049476" ); // a mythical scroll of ~1_type~ (+15 Maximum Stats) + else if ( m_Value == 245 ) + base.LabelTo( from, 1049466, "#1049476" ); // a legendary scroll of ~1_type~ (+20 Maximum Stats) + else if ( m_Value == 250 ) + base.LabelTo( from, 1049467, "#1049476" ); // an ultimate scroll of ~1_type~ (+25 Maximum Stats) + else + base.LabelTo( from, "a scroll of power ({0}{1} Maximum Stats)", (m_Value - 225) >= 0 ? "+" : "", m_Value - 225 ); + } + + public void Use( Mobile from, bool firstStage ) + { + if ( Deleted ) + return; + + if ( IsChildOf( from.Backpack ) ) + { + if ( from.StatCap >= m_Value ) + { + from.SendLocalizedMessage( 1049510 ); // Your stats are too high for this power scroll. + } + else + { + if ( firstStage ) + { + from.CloseGump( typeof( StatCapScroll.InternalGump ) ); + from.CloseGump( typeof( PowerScroll.InternalGump ) ); + from.SendGump( new InternalGump( from, this ) ); + } + else + { + from.SendLocalizedMessage( 1049512 ); // You feel a surge of magic as the scroll enhances your powers! + + from.StatCap = m_Value; + + Effects.SendLocationParticles( EffectItem.Create( from.Location, from.Map, EffectItem.DefaultDuration ), 0, 0, 0, 0, 0, 5060, 0 ); + Effects.PlaySound( from.Location, from.Map, 0x243 ); + + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( from.X - 6, from.Y - 6, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( from.X - 4, from.Y - 6, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( from.X - 6, from.Y - 4, from.Z + 15 ), from.Map ), from, 0x36D4, 7, 0, false, true, 0x497, 0, 9502, 1, 0, (EffectLayer)255, 0x100 ); + + Effects.SendTargetParticles( from, 0x375A, 35, 90, 0x00, 0x00, 9502, (EffectLayer)255, 0x100 ); + + Delete(); + } + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public override void OnDoubleClick( Mobile from ) + { + Use( from, true ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Value ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Value = reader.ReadInt(); + + break; + } + } + + if ( LootType != LootType.Cursed ) + LootType = LootType.Cursed; + + if ( Insured ) + Insured = false; + } + + public class InternalGump : Gump + { + private Mobile m_Mobile; + private StatCapScroll m_Scroll; + + public InternalGump( Mobile mobile, StatCapScroll scroll ) : base( 25, 50 ) + { + m_Mobile = mobile; + m_Scroll = scroll; + + AddPage( 0 ); + + AddBackground( 25, 10, 420, 200, 5054 ); + + AddImageTiled( 33, 20, 401, 181, 2624 ); + AddAlphaRegion( 33, 20, 401, 181 ); + + AddHtmlLocalized( 40, 48, 387, 100, 1049469, true, true ); /* Using a scroll increases the maximum amount of a specific skill or your maximum statistics. + * When used, the effect is not immediately seen without a gain of points with that skill or statistics. + * You can view your maximum skill values in your skills window. + * You can view your maximum statistic value in your statistics window. + */ + AddHtmlLocalized( 125, 148, 200, 20, 1049478, 0xFFFFFF, false, false ); // Do you wish to use this scroll? + + AddButton( 100, 172, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 135, 172, 120, 20, 1046362, 0xFFFFFF, false, false ); // Yes + + AddButton( 275, 172, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 310, 172, 120, 20, 1046363, 0xFFFFFF, false, false ); // No + + int value = scroll.m_Value; + + if ( value == 230 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049458, 0xFFFFFF, false, false ); // Wonderous Scroll (+5 Maximum Stats): + else if ( value == 235 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049459, 0xFFFFFF, false, false ); // Exalted Scroll (+10 Maximum Stats): + else if ( value == 240 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049460, 0xFFFFFF, false, false ); // Mythical Scroll (+15 Maximum Stats): + else if ( value == 245 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049461, 0xFFFFFF, false, false ); // Legendary Scroll (+20 Maximum Stats): + else if ( value == 250 ) + AddHtmlLocalized( 40, 20, 260, 20, 1049462, 0xFFFFFF, false, false ); // Ultimate Scroll (+25 Maximum Stats): + else + AddHtml( 40, 20, 260, 20, String.Format( "Power Scroll ({0}{1} Maximum Stats):", (value - 225) >= 0 ? "+" : "", value - 225), false, false ); + + AddHtmlLocalized( 310, 20, 120, 20, 1038019, 0xFFFFFF, false, false ); // Power + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + m_Scroll.Use( m_Mobile, false ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/AlchemyStone.cs b/Scripts/Items/Stones/AlchemyStone.cs new file mode 100644 index 000000000..a22455393 --- /dev/null +++ b/Scripts/Items/Stones/AlchemyStone.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class AlchemyStone : Item + { + public override string DefaultName + { + get { return "an Alchemist Supply Stone"; } + } + + [Constructable] + public AlchemyStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x250; + } + + public override void OnDoubleClick( Mobile from ) + { + AlchemyBag alcBag = new AlchemyBag(); + + if ( !from.AddToBackpack( alcBag ) ) + alcBag.Delete(); + } + + public AlchemyStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/GMStone.cs b/Scripts/Items/Stones/GMStone.cs new file mode 100644 index 000000000..5576ab5cc --- /dev/null +++ b/Scripts/Items/Stones/GMStone.cs @@ -0,0 +1,56 @@ +#if false +using System; +using Server.Network; +using Server.Commands; + +namespace Server.Items +{ + public class GMStone : Item + { + public override string DefaultName + { + get { return "a GM stone"; } + } + + [Constructable] + public GMStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x489; + } + + public GMStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel < AccessLevel.GameMaster ) + { + from.AccessLevel = AccessLevel.GameMaster; + + from.SendAsciiMessage( 0x482, "The command prefix is \"{0}\"", CommandSystem.Prefix ); + CommandHandlers.Help_OnCommand( new CommandEventArgs( from, "help", "", new string[0] ) ); + } + else + { + from.SendMessage( "The stone has no effect." ); + } + } + } +} +#endif \ No newline at end of file diff --git a/Scripts/Items/Stones/GamblingStone.cs b/Scripts/Items/Stones/GamblingStone.cs new file mode 100644 index 000000000..4faa0e06d --- /dev/null +++ b/Scripts/Items/Stones/GamblingStone.cs @@ -0,0 +1,123 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class GamblingStone : Item + { + private int m_GamblePot = 2500; + + [CommandProperty( AccessLevel.GameMaster )] + public int GamblePot + { + get + { + return m_GamblePot; + } + set + { + m_GamblePot = value; + InvalidateProperties(); + } + } + + public override string DefaultName + { + get { return "a gambling stone"; } + } + + [Constructable] + public GamblingStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x56; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( "Jackpot: {0}gp", m_GamblePot ); + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + base.LabelTo( from, "Jackpot: {0}gp", m_GamblePot ); + } + + public override void OnDoubleClick( Mobile from ) + { + Container pack = from.Backpack; + + if ( pack != null && pack.ConsumeTotal( typeof( Gold ), 250 ) ) + { + m_GamblePot += 150; + InvalidateProperties(); + + int roll = Utility.Random( 1200 ); + + if ( roll == 0 ) // Jackpot + { + from.SendMessage( 0x35, "You win the {0}gp jackpot!", m_GamblePot ); + from.AddToBackpack( new BankCheck( m_GamblePot ) ); + + m_GamblePot = 2500; + } + else if ( roll <= 20 ) // Chance for a regbag + { + from.SendMessage( 0x35, "You win a bag of reagents!" ); + from.AddToBackpack( new BagOfReagents( 50 ) ); + } + else if ( roll <= 40 ) // Chance for gold + { + from.SendMessage( 0x35, "You win 1500gp!" ); + from.AddToBackpack( new BankCheck( 1500 ) ); + } + else if ( roll <= 100 ) // Another chance for gold + { + from.SendMessage( 0x35, "You win 1000gp!" ); + from.AddToBackpack( new BankCheck( 1000 ) ); + } + else // Loser! + { + from.SendMessage( 0x22, "You lose!" ); + } + } + else + { + from.SendMessage( 0x22, "You need at least 250gp in your backpack to use this." ); + } + } + + public GamblingStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_GamblePot ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_GamblePot = reader.ReadInt(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/IngotStone.cs b/Scripts/Items/Stones/IngotStone.cs new file mode 100644 index 000000000..8da5a3064 --- /dev/null +++ b/Scripts/Items/Stones/IngotStone.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class IngotStone : Item + { + public override string DefaultName + { + get { return "an Ingot stone"; } + } + + [Constructable] + public IngotStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x480; + } + + public override void OnDoubleClick( Mobile from ) + { + BagOfingots ingotBag = new BagOfingots( 5000 ); + + if ( !from.AddToBackpack( ingotBag ) ) + ingotBag.Delete(); + } + + public IngotStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/RegStone.cs b/Scripts/Items/Stones/RegStone.cs new file mode 100644 index 000000000..10baff918 --- /dev/null +++ b/Scripts/Items/Stones/RegStone.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class RegStone : Item + { + public override string DefaultName + { + get { return "a reagent stone"; } + } + + [Constructable] + public RegStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x2D1; + } + + public override void OnDoubleClick( Mobile from ) + { + BagOfReagents regBag = new BagOfReagents( 50 ); + + if ( !from.AddToBackpack( regBag ) ) + regBag.Delete(); + } + + public RegStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/ScribeStone.cs b/Scripts/Items/Stones/ScribeStone.cs new file mode 100644 index 000000000..57ad6498d --- /dev/null +++ b/Scripts/Items/Stones/ScribeStone.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class ScribeStone : Item + { + public override string DefaultName + { + get { return "a Scribe Supply Stone"; } + } + + [Constructable] + public ScribeStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x105; + } + + public override void OnDoubleClick( Mobile from ) + { + ScribeBag scribeBag = new ScribeBag(); + + if ( !from.AddToBackpack( scribeBag ) ) + scribeBag.Delete(); + } + + public ScribeStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/SmithStone.cs b/Scripts/Items/Stones/SmithStone.cs new file mode 100644 index 000000000..c6c9502c3 --- /dev/null +++ b/Scripts/Items/Stones/SmithStone.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class SmithStone : Item + { + public override string DefaultName + { + get { return "a Blacksmith Supply Stone"; } + } + + [Constructable] + public SmithStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x476; + } + + public override void OnDoubleClick( Mobile from ) + { + SmithBag SmithBag = new SmithBag( 5000 ); + + if ( !from.AddToBackpack( SmithBag ) ) + SmithBag.Delete(); + } + + public SmithStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Stones/TailorStone.cs b/Scripts/Items/Stones/TailorStone.cs new file mode 100644 index 000000000..184c0e7e9 --- /dev/null +++ b/Scripts/Items/Stones/TailorStone.cs @@ -0,0 +1,46 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class TailorStone : Item + { + public override string DefaultName + { + get { return "a Tailor Supply Stone"; } + } + + [Constructable] + public TailorStone() : base( 0xED4 ) + { + Movable = false; + Hue = 0x315; + } + + public override void OnDoubleClick( Mobile from ) + { + TailorBag tailorBag = new TailorBag(); + + if ( !from.AddToBackpack( tailorBag ) ) + tailorBag.Delete(); + } + + public TailorStone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/AdminRobe.cs b/Scripts/Items/Suits/AdminRobe.cs new file mode 100644 index 000000000..c8e8609e8 --- /dev/null +++ b/Scripts/Items/Suits/AdminRobe.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AdminRobe : BaseSuit + { + [Constructable] + public AdminRobe() : base( AccessLevel.Administrator, 0x0, 0x204F ) // Blank hue + { + } + + public AdminRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/BaseSuit.cs b/Scripts/Items/Suits/BaseSuit.cs new file mode 100644 index 000000000..2d857554c --- /dev/null +++ b/Scripts/Items/Suits/BaseSuit.cs @@ -0,0 +1,91 @@ +using System; +using Server; + +namespace Server.Items +{ + public abstract class BaseSuit : Item + { + private AccessLevel m_AccessLevel; + + [CommandProperty( AccessLevel.Administrator )] + public AccessLevel AccessLevel{ get{ return m_AccessLevel; } set{ m_AccessLevel = value; } } + + public BaseSuit( AccessLevel level, int hue, int itemID ) : base( itemID ) + { + Hue = hue; + Weight = 1.0; + Movable = false; + LootType = LootType.Newbied; + Layer = Layer.OuterTorso; + + m_AccessLevel = level; + } + + public BaseSuit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_AccessLevel ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_AccessLevel = (AccessLevel)reader.ReadInt(); + break; + } + } + } + + public bool Validate() + { + object root = RootParent; + + if ( root is Mobile && ((Mobile)root).AccessLevel < m_AccessLevel ) + { + Delete(); + return false; + } + + return true; + } + + public override void OnSingleClick( Mobile from ) + { + if ( Validate() ) + base.OnSingleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( Validate() ) + base.OnDoubleClick( from ); + } + + public override bool VerifyMove( Mobile from ) + { + return ( from.AccessLevel >= m_AccessLevel ); + } + + public override bool OnEquip( Mobile from ) + { + if ( from.AccessLevel < m_AccessLevel ) + from.SendMessage( "You may not wear this." ); + + return ( from.AccessLevel >= m_AccessLevel ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/CounselorRobe.cs b/Scripts/Items/Suits/CounselorRobe.cs new file mode 100644 index 000000000..f2db61abc --- /dev/null +++ b/Scripts/Items/Suits/CounselorRobe.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class CounselorRobe : BaseSuit + { + [Constructable] + public CounselorRobe() : base( AccessLevel.Counselor, 0x3, 0x204F ) + { + } + + public CounselorRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/DeathShroud.cs b/Scripts/Items/Suits/DeathShroud.cs new file mode 100644 index 000000000..d43dc6ebe --- /dev/null +++ b/Scripts/Items/Suits/DeathShroud.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DeathShroud : BaseSuit + { + [Constructable] + public DeathShroud() : base( AccessLevel.GameMaster, 0x0, 0x204E ) + { + } + + public DeathShroud( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/DupreSuit.cs b/Scripts/Items/Suits/DupreSuit.cs new file mode 100644 index 000000000..c3a97c302 --- /dev/null +++ b/Scripts/Items/Suits/DupreSuit.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class DupreSuit : BaseSuit + { + [Constructable] + public DupreSuit() : base( AccessLevel.GameMaster, 0x0, 0x2050 ) + { + } + + public DupreSuit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/GMRobe.cs b/Scripts/Items/Suits/GMRobe.cs new file mode 100644 index 000000000..8ad7e4a7c --- /dev/null +++ b/Scripts/Items/Suits/GMRobe.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class GMRobe : BaseSuit + { + [Constructable] + public GMRobe() : base( AccessLevel.GameMaster, 0x26, 0x204F ) + { + } + + public GMRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/LordBlackthorneSuit.cs b/Scripts/Items/Suits/LordBlackthorneSuit.cs new file mode 100644 index 000000000..5ed410370 --- /dev/null +++ b/Scripts/Items/Suits/LordBlackthorneSuit.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LordBlackthorneSuit : BaseSuit + { + [Constructable] + public LordBlackthorneSuit() : base( AccessLevel.GameMaster, 0x0, 0x2043 ) + { + } + + public LordBlackthorneSuit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/LordBritishSuit.cs b/Scripts/Items/Suits/LordBritishSuit.cs new file mode 100644 index 000000000..9f8bdcdd0 --- /dev/null +++ b/Scripts/Items/Suits/LordBritishSuit.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LordBritishSuit : BaseSuit + { + [Constructable] + public LordBritishSuit() : base( AccessLevel.GameMaster, 0x0, 0x2042 ) + { + } + + public LordBritishSuit( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Suits/SeerRobe.cs b/Scripts/Items/Suits/SeerRobe.cs new file mode 100644 index 000000000..e62735017 --- /dev/null +++ b/Scripts/Items/Suits/SeerRobe.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SeerRobe : BaseSuit + { + [Constructable] + public SeerRobe() : base( AccessLevel.Seer, 0x1D3, 0x204F ) + { + } + + public SeerRobe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/SupplyBags/AlchemyBag.cs b/Scripts/Items/SupplyBags/AlchemyBag.cs new file mode 100644 index 000000000..f2176b5d0 --- /dev/null +++ b/Scripts/Items/SupplyBags/AlchemyBag.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class AlchemyBag : Bag + { + public override string DefaultName + { + get { return "an Alchemy Kit"; } + } + + [Constructable] + public AlchemyBag() : this( 1 ) + { + Movable = true; + Hue = 0x250; + } + + [Constructable] + public AlchemyBag( int amount ) + { + DropItem( new MortarPestle( 5 ) ); + DropItem( new BagOfReagents( 5000 ) ); + DropItem( new Bottle( 5000 ) ); + } + + public AlchemyBag( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/SupplyBags/BagOfIngots.cs b/Scripts/Items/SupplyBags/BagOfIngots.cs new file mode 100644 index 000000000..213e0cd97 --- /dev/null +++ b/Scripts/Items/SupplyBags/BagOfIngots.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class BagOfingots : Bag + { + [Constructable] + public BagOfingots() : this( 5000 ) + { + } + + [Constructable] + public BagOfingots( int amount ) + { + DropItem( new DullCopperIngot ( amount ) ); + DropItem( new ShadowIronIngot ( amount ) ); + DropItem( new CopperIngot ( amount ) ); + DropItem( new BronzeIngot ( amount ) ); + DropItem( new GoldIngot ( amount ) ); + DropItem( new AgapiteIngot ( amount ) ); + DropItem( new VeriteIngot ( amount ) ); + DropItem( new ValoriteIngot ( amount ) ); + DropItem( new IronIngot ( amount ) ); + DropItem( new Tongs() ); + DropItem( new TinkerTools() ); + + } + + public BagOfingots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/SupplyBags/ScribeBag.cs b/Scripts/Items/SupplyBags/ScribeBag.cs new file mode 100644 index 000000000..e4d82a584 --- /dev/null +++ b/Scripts/Items/SupplyBags/ScribeBag.cs @@ -0,0 +1,46 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class ScribeBag : Bag + { + public override string DefaultName + { + get { return "a Scribe Kit"; } + } + + [Constructable] + public ScribeBag() : this( 1 ) + { + Movable = true; + Hue = 0x105; + } + + [Constructable] + public ScribeBag( int amount ) + { + DropItem( new BagOfReagents( 5000 ) ); + DropItem( new BlankScroll( 500 ) ); + } + + public ScribeBag( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/SupplyBags/SmithBag.cs b/Scripts/Items/SupplyBags/SmithBag.cs new file mode 100644 index 000000000..994fafe37 --- /dev/null +++ b/Scripts/Items/SupplyBags/SmithBag.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class SmithBag : Bag + { + [Constructable] + public SmithBag() : this( 5000 ) + { + } + + [Constructable] + public SmithBag( int amount ) + { + DropItem( new DullCopperIngot ( amount ) ); + DropItem( new ShadowIronIngot ( amount ) ); + DropItem( new CopperIngot ( amount ) ); + DropItem( new BronzeIngot ( amount ) ); + DropItem( new GoldIngot ( amount ) ); + DropItem( new AgapiteIngot ( amount ) ); + DropItem( new VeriteIngot ( amount ) ); + DropItem( new ValoriteIngot ( amount ) ); + DropItem( new IronIngot ( amount ) ); + DropItem( new Tongs ( amount ) ); + DropItem( new TinkerTools ( amount ) ); + + } + + public SmithBag( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/SupplyBags/TailorBag.cs b/Scripts/Items/SupplyBags/TailorBag.cs new file mode 100644 index 000000000..6cbdad498 --- /dev/null +++ b/Scripts/Items/SupplyBags/TailorBag.cs @@ -0,0 +1,52 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public class TailorBag : Bag + { + public override string DefaultName + { + get { return "a Tailoring Kit"; } + } + + [Constructable] + public TailorBag() : this( 1 ) + { + Movable = true; + Hue = 0x315; + } + + [Constructable] + public TailorBag( int amount ) + { + DropItem( new SewingKit( 5 ) ); + DropItem( new Scissors() ); + DropItem( new Hides( 500 ) ); + DropItem( new BoltOfCloth( 20 ) ); + DropItem( new DyeTub() ); + DropItem( new DyeTub() ); + DropItem( new BlackDyeTub() ); + DropItem( new Dyes() ); + } + + public TailorBag( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/BaseTrap.cs b/Scripts/Items/Traps/BaseTrap.cs new file mode 100644 index 000000000..cfe471516 --- /dev/null +++ b/Scripts/Items/Traps/BaseTrap.cs @@ -0,0 +1,85 @@ +using System; + +namespace Server.Items +{ + public abstract class BaseTrap : Item + { + public virtual bool PassivelyTriggered{ get{ return false; } } + public virtual TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public virtual int PassiveTriggerRange{ get{ return -1; } } + public virtual TimeSpan ResetDelay{ get{ return TimeSpan.Zero; } } + + private DateTime m_NextPassiveTrigger, m_NextActiveTrigger; + + public virtual void OnTrigger( Mobile from ) + { + } + + public override bool HandlesOnMovement{ get{ return true; } } // Tell the core that we implement OnMovement + + public virtual int GetEffectHue() + { + int hue = this.Hue & 0x3FFF; + + if ( hue < 2 ) + return 0; + + return hue - 1; + } + + public bool CheckRange( Point3D loc, Point3D oldLoc, int range ) + { + return CheckRange( loc, range ) && !CheckRange( oldLoc, range ); + } + + public bool CheckRange( Point3D loc, int range ) + { + return ( (this.Z + 8) >= loc.Z && (loc.Z + 16) > this.Z ) + && Utility.InRange( GetWorldLocation(), loc, range ); + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( m.Location == oldLocation ) + return; + + if ( CheckRange( m.Location, oldLocation, 0 ) && DateTime.Now >= m_NextActiveTrigger ) + { + m_NextActiveTrigger = m_NextPassiveTrigger = DateTime.Now + ResetDelay; + + OnTrigger( m ); + } + else if ( PassivelyTriggered && CheckRange( m.Location, oldLocation, PassiveTriggerRange ) && DateTime.Now >= m_NextPassiveTrigger ) + { + m_NextPassiveTrigger = DateTime.Now + PassiveTriggerDelay; + + OnTrigger( m ); + } + } + + public BaseTrap( int itemID ) : base( itemID ) + { + Movable = false; + } + + public BaseTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/FireColumnTrap.cs b/Scripts/Items/Traps/FireColumnTrap.cs new file mode 100644 index 000000000..6fd95d861 --- /dev/null +++ b/Scripts/Items/Traps/FireColumnTrap.cs @@ -0,0 +1,47 @@ +using System; + +namespace Server.Items +{ + public class FireColumnTrap : BaseTrap + { + [Constructable] + public FireColumnTrap() : base( 0x1B71 ) + { + } + + public override bool PassivelyTriggered{ get{ return true; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.FromSeconds( 2.0 ); } } + public override int PassiveTriggerRange{ get{ return 3; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.FromSeconds( 0.5 ); } } + + public override void OnTrigger( Mobile from ) + { + if ( from.AccessLevel > AccessLevel.Player ) + return; + + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3709, 10, 30, 5052 ); + Effects.PlaySound( Location, Map, 0x225 ); + + if ( from.Alive && CheckRange( from.Location, 0 ) ) + Spells.SpellHelper.Damage( TimeSpan.FromSeconds( 0.5 ), from, from, Utility.RandomMinMax( 10, 40 ), 0, 100, 0, 0, 0 ); + } + + public FireColumnTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/FlameSpurtTrap.cs b/Scripts/Items/Traps/FlameSpurtTrap.cs new file mode 100644 index 000000000..fa3fa0243 --- /dev/null +++ b/Scripts/Items/Traps/FlameSpurtTrap.cs @@ -0,0 +1,181 @@ +using System; + +namespace Server.Items +{ + public class FlameSpurtTrap : BaseTrap + { + private Item m_Spurt; + private Timer m_Timer; + + [Constructable] + public FlameSpurtTrap() : base( 0x1B71 ) + { + Visible = false; + } + + public virtual void StartTimer() + { + if ( m_Timer == null ) + m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ), new TimerCallback( Refresh ) ); + } + + public virtual void StopTimer() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + } + + public virtual void CheckTimer() + { + Map map = this.Map; + + if ( map != null && map.GetSector( GetWorldLocation() ).Active ) + StartTimer(); + else + StopTimer(); + } + + public override void OnLocationChange( Point3D oldLocation ) + { + base.OnLocationChange( oldLocation ); + + CheckTimer(); + } + + public override void OnMapChange() + { + base.OnMapChange(); + + CheckTimer(); + } + + public override void OnSectorActivate() + { + base.OnSectorActivate(); + + StartTimer(); + } + + public override void OnSectorDeactivate() + { + base.OnSectorDeactivate(); + + StopTimer(); + } + + public override void OnDelete() + { + base.OnDelete(); + + if ( m_Spurt != null ) + m_Spurt.Delete(); + } + + public virtual void Refresh() + { + if ( Deleted ) + return; + + bool foundPlayer = false; + + foreach ( Mobile mob in GetMobilesInRange( 3 ) ) + { + if ( !mob.Player || !mob.Alive || mob.AccessLevel > AccessLevel.Player ) + continue; + + if ( ( (this.Z + 8) >= mob.Z && (mob.Z + 16) > this.Z ) ) + { + foundPlayer = true; + break; + } + } + + if ( !foundPlayer ) + { + if ( m_Spurt != null ) + m_Spurt.Delete(); + + m_Spurt = null; + } + else if ( m_Spurt == null || m_Spurt.Deleted ) + { + m_Spurt = new Static( 0x3709 ); + m_Spurt.MoveToWorld( this.Location, this.Map ); + + Effects.PlaySound( GetWorldLocation(), Map, 0x309 ); + } + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m.AccessLevel > AccessLevel.Player ) + return true; + + if ( m.Player && m.Alive ) + { + CheckTimer(); + + Spells.SpellHelper.Damage( TimeSpan.FromTicks( 1 ), m, m, Utility.RandomMinMax( 1, 30 ) ); + m.PlaySound( m.Female ? 0x327 : 0x437 ); + } + + return false; + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + base.OnMovement( m, oldLocation ); + + if ( m.Location == oldLocation || !m.Player || !m.Alive || m.AccessLevel > AccessLevel.Player ) + return; + + if ( CheckRange( m.Location, oldLocation, 1 ) ) + { + CheckTimer(); + + Spells.SpellHelper.Damage( TimeSpan.FromTicks( 1 ), m, m, Utility.RandomMinMax( 1, 10 ) ); + m.PlaySound( m.Female ? 0x327 : 0x437 ); + + if ( m.Body.IsHuman ) + m.Animate( 20, 1, 1, true, false, 0 ); + } + } + + public FlameSpurtTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Item) m_Spurt ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + Item item = reader.ReadItem(); + + if ( item != null ) + item.Delete(); + + CheckTimer(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/GasTrap.cs b/Scripts/Items/Traps/GasTrap.cs new file mode 100644 index 000000000..ede6005b9 --- /dev/null +++ b/Scripts/Items/Traps/GasTrap.cs @@ -0,0 +1,125 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public enum GasTrapType + { + NorthWall, + WestWall, + Floor + } + + public class GasTrap : BaseTrap + { + private Poison m_Poison; + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get{ return m_Poison; } + set{ m_Poison = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public GasTrapType Type + { + get + { + switch ( ItemID ) + { + case 0x113C: return GasTrapType.NorthWall; + case 0x1147: return GasTrapType.WestWall; + case 0x11A8: return GasTrapType.Floor; + } + + return GasTrapType.WestWall; + } + set + { + ItemID = GetBaseID( value ); + } + } + + public static int GetBaseID( GasTrapType type ) + { + switch ( type ) + { + case GasTrapType.NorthWall: return 0x113C; + case GasTrapType.WestWall: return 0x1147; + case GasTrapType.Floor: return 0x11A8; + } + + return 0; + } + + [Constructable] + public GasTrap() : this( GasTrapType.Floor ) + { + } + + [Constructable] + public GasTrap( GasTrapType type ) : this( type, Poison.Lesser ) + { + } + + [Constructable] + public GasTrap( Poison poison ) : this( GasTrapType.Floor, Poison.Lesser ) + { + } + + [Constructable] + public GasTrap( GasTrapType type, Poison poison ) : base( GetBaseID( type ) ) + { + m_Poison = poison; + } + + public override bool PassivelyTriggered{ get{ return false; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public override int PassiveTriggerRange{ get{ return 0; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.FromSeconds( 0.0 ); } } + + public override void OnTrigger( Mobile from ) + { + if ( m_Poison == null || !from.Player || !from.Alive || from.AccessLevel > AccessLevel.Player ) + return; + + Effects.SendLocationEffect( Location, Map, GetBaseID( this.Type ) - 2, 16, 3, GetEffectHue(), 0 ); + Effects.PlaySound( Location, Map, 0x231 ); + + from.ApplyPoison( from, m_Poison ); + + from.LocalOverheadMessage( MessageType.Regular, 0x22, 500855 ); // You are enveloped by a noxious gas cloud! + } + + public GasTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + Poison.Serialize( m_Poison, writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Poison = Poison.Deserialize( reader ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/GiantSpikeTrap.cs b/Scripts/Items/Traps/GiantSpikeTrap.cs new file mode 100644 index 000000000..c84331890 --- /dev/null +++ b/Scripts/Items/Traps/GiantSpikeTrap.cs @@ -0,0 +1,48 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public class GiantSpikeTrap : BaseTrap + { + [Constructable] + public GiantSpikeTrap() : base( 1 ) + { + } + + public override bool PassivelyTriggered{ get{ return true; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public override int PassiveTriggerRange{ get{ return 3; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.FromSeconds( 0.0 ); } } + + public override void OnTrigger( Mobile from ) + { + if ( from.AccessLevel > AccessLevel.Player ) + return; + + Effects.SendLocationEffect( Location, Map, 0x1D99, 48, 2, GetEffectHue(), 0 ); + + if ( from.Alive && CheckRange( from.Location, 0 ) ) + Spells.SpellHelper.Damage( TimeSpan.FromTicks( 1 ), from, from, Utility.Dice( 10, 7, 0 ) ); + } + + public GiantSpikeTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/MushroomTrap.cs b/Scripts/Items/Traps/MushroomTrap.cs new file mode 100644 index 000000000..e6e9cc5d3 --- /dev/null +++ b/Scripts/Items/Traps/MushroomTrap.cs @@ -0,0 +1,62 @@ +using System; +using Server; +using Server.Network; +using Server.Regions; + +namespace Server.Items +{ + public class MushroomTrap : BaseTrap + { + [Constructable] + public MushroomTrap() : base( 0x1125 ) + { + } + + public override bool PassivelyTriggered{ get{ return true; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public override int PassiveTriggerRange{ get{ return 2; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.Zero; } } + + public override void OnTrigger( Mobile from ) + { + if ( !from.Alive || ItemID != 0x1125 || from.AccessLevel > AccessLevel.Player ) + return; + + ItemID = 0x1126; + Effects.PlaySound( Location, Map, 0x306 ); + + Spells.SpellHelper.Damage( TimeSpan.FromSeconds( 0.5 ), from, from, Utility.Dice( 2, 4, 0 ) ); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerCallback( OnMushroomReset ) ); + } + + public virtual void OnMushroomReset() + { + if ( Region.Find( Location, Map ).IsPartOf( typeof( DungeonRegion ) ) ) + ItemID = 0x1125; // reset + else + Delete(); + } + + public MushroomTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( ItemID == 0x1126 ) + OnMushroomReset(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/SawTrap.cs b/Scripts/Items/Traps/SawTrap.cs new file mode 100644 index 000000000..de8bfb901 --- /dev/null +++ b/Scripts/Items/Traps/SawTrap.cs @@ -0,0 +1,97 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public enum SawTrapType + { + WestWall, + NorthWall, + WestFloor, + NorthFloor + } + + public class SawTrap : BaseTrap + { + [CommandProperty( AccessLevel.GameMaster )] + public SawTrapType Type + { + get + { + switch ( ItemID ) + { + case 0x1103: return SawTrapType.NorthWall; + case 0x1116: return SawTrapType.WestWall; + case 0x11AC: return SawTrapType.NorthFloor; + case 0x11B1: return SawTrapType.WestFloor; + } + + return SawTrapType.NorthWall; + } + set + { + ItemID = GetBaseID( value ); + } + } + + public static int GetBaseID( SawTrapType type ) + { + switch ( type ) + { + case SawTrapType.NorthWall: return 0x1103; + case SawTrapType.WestWall: return 0x1116; + case SawTrapType.NorthFloor: return 0x11AC; + case SawTrapType.WestFloor: return 0x11B1; + } + + return 0; + } + + [Constructable] + public SawTrap() : this( SawTrapType.NorthFloor ) + { + } + + [Constructable] + public SawTrap( SawTrapType type ) : base( GetBaseID( type ) ) + { + } + + public override bool PassivelyTriggered{ get{ return false; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public override int PassiveTriggerRange{ get{ return 0; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.FromSeconds( 0.0 ); } } + + public override void OnTrigger( Mobile from ) + { + if ( !from.Alive || from.AccessLevel > AccessLevel.Player ) + return; + + Effects.SendLocationEffect( Location, Map, GetBaseID( this.Type ) + 1, 6, 3, GetEffectHue(), 0 ); + Effects.PlaySound( Location, Map, 0x21C ); + + Spells.SpellHelper.Damage( TimeSpan.FromTicks( 1 ), from, from, Utility.RandomMinMax( 5, 15 ) ); + + from.LocalOverheadMessage( MessageType.Regular, 0x22, 500853 ); // You stepped onto a blade trap! + } + + public SawTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/SpikeTrap.cs b/Scripts/Items/Traps/SpikeTrap.cs new file mode 100644 index 000000000..f1333a629 --- /dev/null +++ b/Scripts/Items/Traps/SpikeTrap.cs @@ -0,0 +1,150 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public enum SpikeTrapType + { + WestWall, + NorthWall, + WestFloor, + NorthFloor + } + + public class SpikeTrap : BaseTrap + { + [CommandProperty( AccessLevel.GameMaster )] + public SpikeTrapType Type + { + get + { + switch ( ItemID ) + { + case 4360: case 4361: case 4366: return SpikeTrapType.WestWall; + case 4379: case 4380: case 4385: return SpikeTrapType.NorthWall; + case 4506: case 4507: case 4511: return SpikeTrapType.WestFloor; + case 4512: case 4513: case 4517: return SpikeTrapType.NorthFloor; + } + + return SpikeTrapType.WestWall; + } + set + { + bool extended = this.Extended; + + ItemID = ( extended ? GetExtendedID( value ) : GetBaseID( value ) ); + } + } + + public bool Extended + { + get{ return ( ItemID == GetExtendedID( this.Type ) ); } + set + { + if ( value ) + ItemID = GetExtendedID( this.Type ); + else + ItemID = GetBaseID( this.Type ); + } + } + + public static int GetBaseID( SpikeTrapType type ) + { + switch ( type ) + { + case SpikeTrapType.WestWall: return 4360; + case SpikeTrapType.NorthWall: return 4379; + case SpikeTrapType.WestFloor: return 4506; + case SpikeTrapType.NorthFloor: return 4512; + } + + return 0; + } + + public static int GetExtendedID( SpikeTrapType type ) + { + return GetBaseID( type ) + GetExtendedOffset( type ); + } + + public static int GetExtendedOffset( SpikeTrapType type ) + { + switch ( type ) + { + case SpikeTrapType.WestWall: return 6; + case SpikeTrapType.NorthWall: return 6; + + case SpikeTrapType.WestFloor: return 5; + case SpikeTrapType.NorthFloor: return 5; + } + + return 0; + } + + [Constructable] + public SpikeTrap() : this( SpikeTrapType.WestFloor ) + { + } + + [Constructable] + public SpikeTrap( SpikeTrapType type ) : base( GetBaseID( type ) ) + { + } + + public override bool PassivelyTriggered{ get{ return false; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public override int PassiveTriggerRange{ get{ return 0; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.FromSeconds( 6.0 ); } } + + public override void OnTrigger( Mobile from ) + { + if ( !from.Alive || from.AccessLevel > AccessLevel.Player ) + return; + + Effects.SendLocationEffect( Location, Map, GetBaseID( this.Type ) + 1, 18, 3, GetEffectHue(), 0 ); + Effects.PlaySound( Location, Map, 0x22C ); + + foreach ( Mobile mob in GetMobilesInRange( 0 ) ) + { + if ( mob.Alive && !mob.IsDeadBondedPet ) + Spells.SpellHelper.Damage( TimeSpan.FromTicks( 1 ), mob, mob, Utility.RandomMinMax( 1, 6 ) * 6 ); + } + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerCallback( OnSpikeExtended ) ); + + from.LocalOverheadMessage( MessageType.Regular, 0x22, 500852 ); // You stepped onto a spike trap! + } + + public virtual void OnSpikeExtended() + { + Extended = true; + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerCallback( OnSpikeRetracted ) ); + } + + public virtual void OnSpikeRetracted() + { + Extended = false; + Effects.SendLocationEffect( Location, Map, GetExtendedID( this.Type ) - 1, 6, 3, GetEffectHue(), 0 ); + } + + public SpikeTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Extended = false; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Traps/StoneFaceTrap.cs b/Scripts/Items/Traps/StoneFaceTrap.cs new file mode 100644 index 000000000..d3f54b4c0 --- /dev/null +++ b/Scripts/Items/Traps/StoneFaceTrap.cs @@ -0,0 +1,163 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Items +{ + public enum StoneFaceTrapType + { + NorthWestWall, + NorthWall, + WestWall + } + + public class StoneFaceTrap : BaseTrap + { + [CommandProperty( AccessLevel.GameMaster )] + public StoneFaceTrapType Type + { + get + { + switch ( ItemID ) + { + case 0x10F5: case 0x10F6: case 0x10F7: return StoneFaceTrapType.NorthWestWall; + case 0x10FC: case 0x10FD: case 0x10FE: return StoneFaceTrapType.NorthWall; + case 0x110F: case 0x1110: case 0x1111: return StoneFaceTrapType.WestWall; + } + + return StoneFaceTrapType.NorthWestWall; + } + set + { + bool breathing = this.Breathing; + + ItemID = ( breathing ? GetFireID( value ) : GetBaseID( value ) ); + } + } + + public bool Breathing + { + get{ return ( ItemID == GetFireID( this.Type ) ); } + set + { + if ( value ) + ItemID = GetFireID( this.Type ); + else + ItemID = GetBaseID( this.Type ); + } + } + + public static int GetBaseID( StoneFaceTrapType type ) + { + switch ( type ) + { + case StoneFaceTrapType.NorthWestWall: return 0x10F5; + case StoneFaceTrapType.NorthWall: return 0x10FC; + case StoneFaceTrapType.WestWall: return 0x110F; + } + + return 0; + } + + public static int GetFireID( StoneFaceTrapType type ) + { + switch ( type ) + { + case StoneFaceTrapType.NorthWestWall: return 0x10F7; + case StoneFaceTrapType.NorthWall: return 0x10FE; + case StoneFaceTrapType.WestWall: return 0x1111; + } + + return 0; + } + + [Constructable] + public StoneFaceTrap() : base( 0x10FC ) + { + Light = LightType.Circle225; + } + + public override bool PassivelyTriggered{ get{ return true; } } + public override TimeSpan PassiveTriggerDelay{ get{ return TimeSpan.Zero; } } + public override int PassiveTriggerRange{ get{ return 2; } } + public override TimeSpan ResetDelay{ get{ return TimeSpan.Zero; } } + + public override void OnTrigger( Mobile from ) + { + if ( !from.Alive || from.AccessLevel > AccessLevel.Player ) + return; + + Effects.PlaySound( Location, Map, 0x359 ); + + Breathing = true; + + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerCallback( FinishBreath ) ); + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerCallback( TriggerDamage ) ); + } + + public virtual void FinishBreath() + { + Breathing = false; + } + + public virtual void TriggerDamage() + { + foreach ( Mobile mob in GetMobilesInRange( 1 ) ) + { + if ( mob.Alive && !mob.IsDeadBondedPet && mob.AccessLevel == AccessLevel.Player ) + Spells.SpellHelper.Damage( TimeSpan.FromTicks( 1 ), mob, mob, Utility.Dice( 3, 15, 0 ) ); + } + } + + public StoneFaceTrap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Breathing = false; + } + } + + public class StoneFaceTrapNoDamage : StoneFaceTrap + { + [Constructable] + public StoneFaceTrapNoDamage() + { + } + + public StoneFaceTrapNoDamage( Serial serial ) : base( serial ) + { + } + + public override void TriggerDamage() + { + // nothing.. + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/BaseWand.cs b/Scripts/Items/Wands/BaseWand.cs new file mode 100644 index 000000000..7a50fc18b --- /dev/null +++ b/Scripts/Items/Wands/BaseWand.cs @@ -0,0 +1,231 @@ +using System; +using System.Text; +using System.Collections; +using Server.Network; +using Server.Targeting; +using Server.Spells; + +namespace Server.Items +{ + public enum WandEffect + { + Clumsiness, + Identification, + Healing, + Feeblemindedness, + Weakness, + MagicArrow, + Harming, + Fireball, + GreaterHealing, + Lightning, + ManaDraining + } + + public abstract class BaseWand : BaseBashing + { + private WandEffect m_WandEffect; + private int m_Charges; + + public virtual TimeSpan GetUseDelay{ get{ return TimeSpan.FromSeconds( 4.0 ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public WandEffect Effect + { + get{ return m_WandEffect; } + set{ m_WandEffect = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set{ m_Charges = value; InvalidateProperties(); } + } + + public BaseWand( WandEffect effect, int minCharges, int maxCharges ) : base( Utility.RandomList( 0xDF2, 0xDF3, 0xDF4, 0xDF5 ) ) + { + Weight = 1.0; + Effect = effect; + Charges = Utility.RandomMinMax( minCharges, maxCharges ); + } + + public void ConsumeCharge( Mobile from ) + { + --Charges; + + if ( Charges == 0 ) + from.SendLocalizedMessage( 1019073 ); // This item is out of charges. + + ApplyDelayTo( from ); + } + + public BaseWand( Serial serial ) : base( serial ) + { + } + + public virtual void ApplyDelayTo( Mobile from ) + { + from.BeginAction( typeof( BaseWand ) ); + Timer.DelayCall( GetUseDelay, new TimerStateCallback( ReleaseWandLock_Callback ), from ); + } + + public virtual void ReleaseWandLock_Callback( object state ) + { + ((Mobile)state).EndAction( typeof( BaseWand ) ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !from.CanBeginAction( typeof( BaseWand ) ) ) + return; + + if ( Parent == from ) + { + if ( Charges > 0 ) + OnWandUse( from ); + else + from.SendLocalizedMessage( 1019073 ); // This item is out of charges. + } + else + { + from.SendLocalizedMessage( 502641 ); // You must equip this item to use it. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_WandEffect ); + writer.Write( (int) m_Charges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_WandEffect = (WandEffect)reader.ReadInt(); + m_Charges = (int)reader.ReadInt(); + + break; + } + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + switch ( m_WandEffect ) + { + case WandEffect.Clumsiness: list.Add( 1017326, m_Charges.ToString() ); break; // clumsiness charges: ~1_val~ + case WandEffect.Identification: list.Add( 1017350, m_Charges.ToString() ); break; // identification charges: ~1_val~ + case WandEffect.Healing: list.Add( 1017329, m_Charges.ToString() ); break; // healing charges: ~1_val~ + case WandEffect.Feeblemindedness: list.Add( 1017327, m_Charges.ToString() ); break; // feeblemind charges: ~1_val~ + case WandEffect.Weakness: list.Add( 1017328, m_Charges.ToString() ); break; // weakness charges: ~1_val~ + case WandEffect.MagicArrow: list.Add( 1060492, m_Charges.ToString() ); break; // magic arrow charges: ~1_val~ + case WandEffect.Harming: list.Add( 1017334, m_Charges.ToString() ); break; // harm charges: ~1_val~ + case WandEffect.Fireball: list.Add( 1060487, m_Charges.ToString() ); break; // fireball charges: ~1_val~ + case WandEffect.GreaterHealing: list.Add( 1017330, m_Charges.ToString() ); break; // greater healing charges: ~1_val~ + case WandEffect.Lightning: list.Add( 1060491, m_Charges.ToString() ); break; // lightning charges: ~1_val~ + case WandEffect.ManaDraining: list.Add( 1017339, m_Charges.ToString() ); break; // mana drain charges: ~1_val~ + } + } + + public override void OnSingleClick( Mobile from ) + { + ArrayList attrs = new ArrayList(); + + if ( DisplayLootType ) + { + if ( LootType == LootType.Blessed ) + attrs.Add( new EquipInfoAttribute( 1038021 ) ); // blessed + else if ( LootType == LootType.Cursed ) + attrs.Add( new EquipInfoAttribute( 1049643 ) ); // cursed + } + + if ( !Identified ) + { + attrs.Add( new EquipInfoAttribute( 1038000 ) ); // Unidentified + } + else + { + int num = 0; + + switch ( m_WandEffect ) + { + case WandEffect.Clumsiness: num = 3002011; break; + case WandEffect.Identification: num = 1044063; break; + case WandEffect.Healing: num = 3002014; break; + case WandEffect.Feeblemindedness: num = 3002013; break; + case WandEffect.Weakness: num = 3002018; break; + case WandEffect.MagicArrow: num = 3002015; break; + case WandEffect.Harming: num = 3002022; break; + case WandEffect.Fireball: num = 3002028; break; + case WandEffect.GreaterHealing: num = 3002039; break; + case WandEffect.Lightning: num = 3002040; break; + case WandEffect.ManaDraining: num = 3002041; break; + } + + if ( num > 0 ) + attrs.Add( new EquipInfoAttribute( num, m_Charges ) ); + } + + int number; + + if ( Name == null ) + { + number = 1017085; + } + else + { + this.LabelTo( from, Name ); + number = 1041000; + } + + if ( attrs.Count == 0 && Crafter == null && Name != null ) + return; + + EquipmentInfo eqInfo = new EquipmentInfo( number, Crafter, false, (EquipInfoAttribute[])attrs.ToArray( typeof( EquipInfoAttribute ) ) ); + + from.Send( new DisplayEquipmentInfo( this, eqInfo ) ); + } + + public void Cast( Spell spell ) + { + bool m = Movable; + + Movable = false; + spell.Cast(); + Movable = m; + } + + public virtual void OnWandUse( Mobile from ) + { + from.Target = new WandTarget( this ); + } + + public virtual void DoWandTarget( Mobile from, object o ) + { + if ( Deleted || Charges <= 0 || Parent != from || o is StaticTarget || o is LandTarget ) + return; + + if ( OnWandTarget( from, o ) ) + ConsumeCharge( from ); + } + + public virtual bool OnWandTarget( Mobile from, object o ) + { + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/ClumsyWand.cs b/Scripts/Items/Wands/ClumsyWand.cs new file mode 100644 index 000000000..96c556ef5 --- /dev/null +++ b/Scripts/Items/Wands/ClumsyWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.First; +using Server.Targeting; + +namespace Server.Items +{ + public class ClumsyWand : BaseWand + { + [Constructable] + public ClumsyWand() : base( WandEffect.Clumsiness, 5, 30 ) + { + } + + public ClumsyWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new ClumsySpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/FeebleWand.cs b/Scripts/Items/Wands/FeebleWand.cs new file mode 100644 index 000000000..9418be66a --- /dev/null +++ b/Scripts/Items/Wands/FeebleWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.First; +using Server.Targeting; + +namespace Server.Items +{ + public class FeebleWand : BaseWand + { + [Constructable] + public FeebleWand() : base( WandEffect.Feeblemindedness, 5, 30 ) + { + } + + public FeebleWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new FeeblemindSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/FireballWand.cs b/Scripts/Items/Wands/FireballWand.cs new file mode 100644 index 000000000..d795367d6 --- /dev/null +++ b/Scripts/Items/Wands/FireballWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.Third; +using Server.Targeting; + +namespace Server.Items +{ + public class FireballWand : BaseWand + { + [Constructable] + public FireballWand() : base( WandEffect.Fireball, 5, 15 ) + { + } + + public FireballWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new FireballSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/GreaterHealWand.cs b/Scripts/Items/Wands/GreaterHealWand.cs new file mode 100644 index 000000000..6c087d643 --- /dev/null +++ b/Scripts/Items/Wands/GreaterHealWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.Fourth; +using Server.Targeting; + +namespace Server.Items +{ + public class GreaterHealWand : BaseWand + { + [Constructable] + public GreaterHealWand() : base( WandEffect.GreaterHealing, 1, 5 ) + { + } + + public GreaterHealWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new GreaterHealSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/HarmWand.cs b/Scripts/Items/Wands/HarmWand.cs new file mode 100644 index 000000000..cf0053283 --- /dev/null +++ b/Scripts/Items/Wands/HarmWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.Second; +using Server.Targeting; + +namespace Server.Items +{ + public class HarmWand : BaseWand + { + [Constructable] + public HarmWand() : base( WandEffect.Harming, 5, 30 ) + { + } + + public HarmWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new HarmSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/HealWand.cs b/Scripts/Items/Wands/HealWand.cs new file mode 100644 index 000000000..9a5fe358b --- /dev/null +++ b/Scripts/Items/Wands/HealWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.First; +using Server.Targeting; + +namespace Server.Items +{ + public class HealWand : BaseWand + { + [Constructable] + public HealWand() : base( WandEffect.Healing, 10, 25 ) + { + } + + public HealWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new HealSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/IDWand.cs b/Scripts/Items/Wands/IDWand.cs new file mode 100644 index 000000000..7ff7edfb2 --- /dev/null +++ b/Scripts/Items/Wands/IDWand.cs @@ -0,0 +1,47 @@ +using System; +using Server; +using Server.Targeting; + +namespace Server.Items +{ + public class IDWand : BaseWand + { + public override TimeSpan GetUseDelay{ get{ return TimeSpan.Zero; } } + + [Constructable] + public IDWand() : base( WandEffect.Identification, 25, 175 ) + { + } + + public IDWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override bool OnWandTarget( Mobile from, object o ) + { + if ( o is BaseWeapon ) + ((BaseWeapon)o).Identified = true; + else if ( o is BaseArmor ) + ((BaseArmor)o).Identified = true; + + if ( !Core.AOS && o is Item ) + ((Item)o).OnSingleClick( from ); + + return ( o is Item ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/LightningWand.cs b/Scripts/Items/Wands/LightningWand.cs new file mode 100644 index 000000000..18cb3e315 --- /dev/null +++ b/Scripts/Items/Wands/LightningWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.Fourth; +using Server.Targeting; + +namespace Server.Items +{ + public class LightningWand : BaseWand + { + [Constructable] + public LightningWand() : base( WandEffect.Lightning, 5, 20 ) + { + } + + public LightningWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new LightningSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/MagicArrowWand.cs b/Scripts/Items/Wands/MagicArrowWand.cs new file mode 100644 index 000000000..6b6af3ffd --- /dev/null +++ b/Scripts/Items/Wands/MagicArrowWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.First; +using Server.Targeting; + +namespace Server.Items +{ + public class MagicArrowWand : BaseWand + { + [Constructable] + public MagicArrowWand() : base( WandEffect.MagicArrow, 5, 30 ) + { + } + + public MagicArrowWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new MagicArrowSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/ManaDrainWand.cs b/Scripts/Items/Wands/ManaDrainWand.cs new file mode 100644 index 000000000..c27a3b796 --- /dev/null +++ b/Scripts/Items/Wands/ManaDrainWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.Fourth; +using Server.Targeting; + +namespace Server.Items +{ + public class ManaDrainWand : BaseWand + { + [Constructable] + public ManaDrainWand() : base( WandEffect.ManaDraining, 5, 30 ) + { + } + + public ManaDrainWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new ManaDrainSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/RandomWand.cs b/Scripts/Items/Wands/RandomWand.cs new file mode 100644 index 000000000..8c982e0c8 --- /dev/null +++ b/Scripts/Items/Wands/RandomWand.cs @@ -0,0 +1,32 @@ +using System; +using Server; + +namespace Server.Items +{ + public class RandomWand + { + public static BaseWand CreateWand() + { + return CreateRandomWand(); + } + + public static BaseWand CreateRandomWand( ) + { + switch ( Utility.Random( 11 ) ) + { + default: + case 0: return new ClumsyWand(); + case 1: return new FeebleWand(); + case 2: return new FireballWand(); + case 3: return new GreaterHealWand(); + case 4: return new HarmWand(); + case 5: return new HealWand(); + case 6: return new IDWand(); + case 7: return new LightningWand(); + case 8: return new MagicArrowWand(); + case 9: return new ManaDrainWand(); + case 10: return new WeaknessWand(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/WandTarget.cs b/Scripts/Items/Wands/WandTarget.cs new file mode 100644 index 000000000..29c35ac1c --- /dev/null +++ b/Scripts/Items/Wands/WandTarget.cs @@ -0,0 +1,26 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Targeting +{ + public class WandTarget : Target + { + private BaseWand m_Item; + + public WandTarget( BaseWand item ) : base( 6, false, TargetFlags.None ) + { + m_Item = item; + } + + private static int GetOffset( Mobile caster ) + { + return 5 + (int)(caster.Skills[SkillName.Magery].Value * 0.02 ); + } + + protected override void OnTarget( Mobile from, object targeted ) + { + m_Item.DoWandTarget( from, targeted ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Wands/WeaknessWand.cs b/Scripts/Items/Wands/WeaknessWand.cs new file mode 100644 index 000000000..b90b5ea97 --- /dev/null +++ b/Scripts/Items/Wands/WeaknessWand.cs @@ -0,0 +1,38 @@ +using System; +using Server; +using Server.Spells.First; +using Server.Targeting; + +namespace Server.Items +{ + public class WeaknessWand : BaseWand + { + [Constructable] + public WeaknessWand() : base( WandEffect.Weakness, 5, 30 ) + { + } + + public WeaknessWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnWandUse( Mobile from ) + { + Cast( new WeakenSpell( from, this ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/ArmorIgnore.cs b/Scripts/Items/Weapons/Abilities/ArmorIgnore.cs new file mode 100644 index 000000000..e1a736857 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/ArmorIgnore.cs @@ -0,0 +1,33 @@ +using System; + +namespace Server.Items +{ + /// + /// This special move allows the skilled warrior to bypass his target's physical resistance, for one shot only. + /// The Armor Ignore shot does slightly less damage than normal. + /// Against a heavily armored opponent, this ability is a big win, but when used against a very lightly armored foe, it might be better to use a standard strike! + /// + public class ArmorIgnore : WeaponAbility + { + public ArmorIgnore() + { + } + + public override int BaseMana{ get{ return 30; } } + public override double DamageScalar{ get{ return 0.9; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060076 ); // Your attack penetrates their armor! + defender.SendLocalizedMessage( 1060077 ); // The blow penetrated your armor! + + defender.PlaySound( 0x56 ); + defender.FixedParticles( 0x3728, 200, 25, 9942, EffectLayer.Waist ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/ArmorPierce.cs b/Scripts/Items/Weapons/Abilities/ArmorPierce.cs new file mode 100644 index 000000000..66280c906 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/ArmorPierce.cs @@ -0,0 +1,44 @@ +using System; +using Server; + +namespace Server.Items +{ + /// + /// Strike your opponent with great force, partially bypassing their armor and inflicting greater damage. Requires either Bushido or Ninjitsu skill + /// + public class ArmorPierce : WeaponAbility + { + public ArmorPierce() + { + } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 && GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1063347, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override int BaseMana{ get{ return 30; } } + public override double DamageScalar{ get{ return 1.5; } } + + public override bool RequiresSE { get { return true; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063350 ); // You pierce your opponent's armor! + defender.SendLocalizedMessage( 1063351 ); // Your attacker pierced your armor! + + defender.FixedParticles( 0x3728, 1, 26, 0x26D6, 0, 0, EffectLayer.Waist ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/BleedAttack.cs b/Scripts/Items/Weapons/Abilities/BleedAttack.cs new file mode 100644 index 000000000..f659b1cb7 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/BleedAttack.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections; +using Server.Mobiles; +using Server.Spells.Necromancy; +using Server.Network; + +namespace Server.Items +{ + /// + /// Make your opponent bleed profusely with this wicked use of your weapon. + /// When successful, the target will bleed for several seconds, taking damage as time passes for up to ten seconds. + /// The rate of damage slows down as time passes, and the blood loss can be completely staunched with the use of bandages. + /// + public class BleedAttack : WeaponAbility + { + public BleedAttack() + { + } + + public override int BaseMana{ get{ return 30; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + // Necromancers under Lich or Wraith Form are immune to Bleed Attacks. + TransformContext context = TransformationSpell.GetContext( defender ); + + if ( (context != null && ( context.Type == typeof( LichFormSpell ) || context.Type == typeof( WraithFormSpell ))) || + (defender is BaseCreature && ((BaseCreature)defender).BleedImmune) ) + { + attacker.SendLocalizedMessage( 1062052 ); // Your target is not affected by the bleed attack! + return; + } + + attacker.SendLocalizedMessage( 1060159 ); // Your target is bleeding! + defender.SendLocalizedMessage( 1060160 ); // You are bleeding! + + if ( defender is PlayerMobile ) + { + defender.LocalOverheadMessage( MessageType.Regular, 0x21, 1060757 ); // You are bleeding profusely + defender.NonlocalOverheadMessage( MessageType.Regular, 0x21, 1060758, defender.Name ); // ~1_NAME~ is bleeding profusely + } + + defender.PlaySound( 0x133 ); + defender.FixedParticles( 0x377A, 244, 25, 9950, 31, 0, EffectLayer.Waist ); + + BeginBleed( defender, attacker ); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsBleeding( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void BeginBleed( Mobile m, Mobile from ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( from, m ); + m_Table[m] = t; + + t.Start(); + } + + public static void DoBleed( Mobile m, Mobile from, int level ) + { + if ( m.Alive ) + { + int damage = Utility.RandomMinMax( level, level * 2 ); + + if ( !m.Player ) + damage *= 2; + + m.PlaySound( 0x133 ); + m.Damage( damage, from ); + + Blood blood = new Blood(); + + blood.ItemID = Utility.Random( 0x122A, 5 ); + + blood.MoveToWorld( m.Location, m.Map ); + } + else + { + EndBleed( m, false ); + } + } + + public static void EndBleed( Mobile m, bool message ) + { + Timer t = (Timer)m_Table[m]; + + if ( t == null ) + return; + + t.Stop(); + m_Table.Remove( m ); + + m.SendLocalizedMessage( 1060167 ); // The bleeding wounds have healed, you are no longer bleeding! + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private Mobile m_Mobile; + private int m_Count; + + public InternalTimer( Mobile from, Mobile m ) : base( TimeSpan.FromSeconds( 2.0 ), TimeSpan.FromSeconds( 2.0 ) ) + { + m_From = from; + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + DoBleed( m_Mobile, m_From, 5 - m_Count ); + + if ( ++m_Count == 5 ) + EndBleed( m_Mobile, true ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/Block.cs b/Scripts/Items/Weapons/Abilities/Block.cs new file mode 100644 index 000000000..681d4bf01 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/Block.cs @@ -0,0 +1,111 @@ +using System; +using Server; +using System.Collections; + +namespace Server.Items +{ + /// + /// Raises your defenses for a short time. Requires Bushido or Ninjitsu skill. + /// + public class Block : WeaponAbility + { + public Block() + { + } + + public override int BaseMana{ get{ return 30; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 && GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1063347, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063345 ); // You block an attack! + defender.SendLocalizedMessage( 1063346 ); // Your attack was blocked! + + attacker.FixedParticles( 0x37C4, 1, 16, 0x251D, 0x39D, 0x3, EffectLayer.RightHand ); + + int bonus = (int)(10.0 * ((Math.Max( attacker.Skills[SkillName.Bushido].Value, attacker.Skills[SkillName.Ninjitsu].Value ) - 50.0) / 70.0 + 5)); + + BeginBlock( attacker, bonus ); + } + + private class BlockInfo + { + public Mobile m_Target; + public Timer m_Timer; + public int m_Bonus; + + public BlockInfo( Mobile target, int bonus ) + { + m_Target = target; + m_Bonus = bonus; + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool GetBonus( Mobile targ, ref int bonus ) + { + BlockInfo info = m_Table[targ] as BlockInfo; + + if ( info == null ) + return false; + + bonus = info.m_Bonus; + return true; + } + + public static void BeginBlock( Mobile m, int bonus ) + { + EndBlock( m ); + + BlockInfo info = new BlockInfo( m, bonus ); + info.m_Timer = new InternalTimer( m ); + + m_Table[m] = info; + } + + public static void EndBlock( Mobile m ) + { + BlockInfo info = m_Table[m] as BlockInfo; + + if ( info != null ) + { + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + m_Table.Remove( m ); + } + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m ) : base( TimeSpan.FromSeconds( 6.0 ) ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + EndBlock( m_Mobile ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/ConcussionBlow.cs b/Scripts/Items/Weapons/Abilities/ConcussionBlow.cs new file mode 100644 index 000000000..5a83af91a --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/ConcussionBlow.cs @@ -0,0 +1,52 @@ +using System; + +namespace Server.Items +{ + /// + /// This devastating strike is most effective against those who are in good health and whose reserves of mana are low, or vice versa. + /// + public class ConcussionBlow : WeaponAbility + { + public ConcussionBlow() + { + } + + public override int BaseMana{ get{ return 25; } } + + public override bool OnBeforeDamage( Mobile attacker, Mobile defender ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return false; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060165 ); // You have delivered a concussion! + defender.SendLocalizedMessage( 1060166 ); // You feel disoriented! + + defender.PlaySound( 0x213 ); + defender.FixedParticles( 0x377A, 1, 32, 9949, 1153, 0, EffectLayer.Head ); + + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( defender.X, defender.Y, defender.Z + 10 ), defender.Map ), new Entity( Serial.Zero, new Point3D( defender.X, defender.Y, defender.Z + 20 ), defender.Map ), 0x36FE, 1, 0, false, false, 1133, 3, 9501, 1, 0, EffectLayer.Waist, 0x100 ); + + int damage = 10; // Base damage is 10. + + if ( defender.HitsMax > 0 ) + { + double hitsPercent = ( (double)defender.Hits / (double)defender.HitsMax ) * 100.0; + + double manaPercent = 0; + + if ( defender.ManaMax > 0 ) + manaPercent = ( (double)defender.Mana / (double)defender.ManaMax ) * 100.0; + + damage += Math.Min( (int)(Math.Abs( hitsPercent - manaPercent ) / 4), 20 ); + } + + // Total damage is 10 + (0~20) = 10~30, physical, non-resistable. + + defender.Damage( damage, attacker ); + + return true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/CrushingBlow.cs b/Scripts/Items/Weapons/Abilities/CrushingBlow.cs new file mode 100644 index 000000000..0aea1a861 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/CrushingBlow.cs @@ -0,0 +1,34 @@ +using System; + +namespace Server.Items +{ + /// + /// Also known as the Haymaker, this attack dramatically increases the damage done by a weapon reaching its mark. + /// + public class CrushingBlow : WeaponAbility + { + public CrushingBlow() + { + } + + public override int BaseMana{ get{ return 25; } } + public override double DamageScalar{ get{ return 1.5; } } + + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060090 ); // You have delivered a crushing blow! + defender.SendLocalizedMessage( 1060091 ); // You take extra damage from the crushing attack! + + defender.PlaySound( 0x1E1 ); + defender.FixedParticles( 0, 1, 0, 9946, EffectLayer.Head ); + + Effects.SendMovingParticles( new Entity( Serial.Zero, new Point3D( defender.X, defender.Y, defender.Z + 50 ), defender.Map ), new Entity( Serial.Zero, new Point3D( defender.X, defender.Y, defender.Z + 20 ), defender.Map ), 0xFB4, 1, 0, false, false, 0, 3, 9501, 1, 0, EffectLayer.Head, 0x100 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/DefenseMastery.cs b/Scripts/Items/Weapons/Abilities/DefenseMastery.cs new file mode 100644 index 000000000..b6cb3d47f --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/DefenseMastery.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using System.Collections; + +namespace Server.Items +{ + /// + /// Raises your physical resistance for a short time while lowering your ability to inflict damage. Requires Bushido or Ninjitsu skill. + /// + public class DefenseMastery : WeaponAbility + { + public DefenseMastery() + { + } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 && GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1063347, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override int BaseMana { get { return 30; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063353 ); // You perform a masterful defense! + + attacker.FixedParticles( 0x375A, 1, 17, 0x7F2, 0x3E8, 0x3, EffectLayer.Waist ); + + int modifier = (int)(30.0 * ((Math.Max( attacker.Skills[SkillName.Bushido].Value, attacker.Skills[SkillName.Ninjitsu].Value ) - 50.0) / 70.0)); + + DefenseMasteryInfo info = m_Table[attacker] as DefenseMasteryInfo; + + if( info != null ) + EndDefense( (object)info ); + + ResistanceMod mod = new ResistanceMod( ResistanceType.Physical, 50 + modifier ); + attacker.AddResistanceMod( mod ); + + info = new DefenseMasteryInfo( attacker, 80 - modifier, mod ); + info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 3.0 ), new TimerStateCallback( EndDefense ), info ); + + m_Table[attacker] = info; + + attacker.Delta( MobileDelta.WeaponDamage ); + } + + private class DefenseMasteryInfo + { + public Mobile m_From; + public Timer m_Timer; + public int m_DamageMalus; + public ResistanceMod m_Mod; + + public DefenseMasteryInfo( Mobile from, int damageMalus, ResistanceMod mod ) + { + m_From = from; + m_DamageMalus = damageMalus; + m_Mod = mod; + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool GetMalus( Mobile targ, ref int damageMalus ) + { + DefenseMasteryInfo info = m_Table[targ] as DefenseMasteryInfo; + + if( info == null ) + return false; + + damageMalus = info.m_DamageMalus; + return true; + } + + private static void EndDefense( object state ) + { + DefenseMasteryInfo info = (DefenseMasteryInfo)state; + + if( info.m_Mod != null ) + info.m_From.RemoveResistanceMod( info.m_Mod ); + + if( info.m_Timer != null ) + info.m_Timer.Stop(); + + // No message is sent to the player. + + m_Table.Remove( info.m_From ); + + info.m_From.Delta( MobileDelta.WeaponDamage ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/Disarm.cs b/Scripts/Items/Weapons/Abilities/Disarm.cs new file mode 100644 index 000000000..220816167 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/Disarm.cs @@ -0,0 +1,74 @@ +using System; + +namespace Server.Items +{ + /// + /// This attack allows you to disarm your foe. + /// Now in Age of Shadows, a successful Disarm leaves the victim unable to re-arm another weapon for several seconds. + /// + public class Disarm : WeaponAbility + { + public Disarm() + { + } + + public override int BaseMana{ get{ return 20; } } + + // No longer active in pub21: + /*public override bool CheckSkills( Mobile from ) + { + if ( !base.CheckSkills( from ) ) + return false; + + if ( !(from.Weapon is Fists) ) + return true; + + Skill skill = from.Skills[SkillName.ArmsLore]; + + if ( skill != null && skill.Base >= 80.0 ) + return true; + + from.SendLocalizedMessage( 1061812 ); // You lack the required skill in armslore to perform that attack! + + return false; + }*/ + + public static readonly TimeSpan BlockEquipDuration = TimeSpan.FromSeconds( 5.0 ); + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) ) + return; + + ClearCurrentAbility( attacker ); + + Item toDisarm = defender.FindItemOnLayer( Layer.OneHanded ); + + if ( toDisarm == null || !toDisarm.Movable ) + toDisarm = defender.FindItemOnLayer( Layer.TwoHanded ); + + Container pack = defender.Backpack; + + if ( pack == null || (toDisarm != null && !toDisarm.Movable) ) + { + attacker.SendLocalizedMessage( 1004001 ); // You cannot disarm your opponent. + } + else if ( toDisarm == null || toDisarm is BaseShield || toDisarm is Spellbook ) + { + attacker.SendLocalizedMessage( 1060849 ); // Your target is already unarmed! + } + else if ( CheckMana( attacker, true ) ) + { + attacker.SendLocalizedMessage( 1060092 ); // You disarm their weapon! + defender.SendLocalizedMessage( 1060093 ); // Your weapon has been disarmed! + + defender.PlaySound( 0x3B9 ); + defender.FixedParticles( 0x37BE, 232, 25, 9948, EffectLayer.LeftHand ); + + pack.DropItem( toDisarm ); + + BaseWeapon.BlockEquip( defender, BlockEquipDuration ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/Dismount.cs b/Scripts/Items/Weapons/Abilities/Dismount.cs new file mode 100644 index 000000000..673696f83 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/Dismount.cs @@ -0,0 +1,77 @@ +using System; +using Server.Mobiles; + +namespace Server.Items +{ + /// + /// Perfect for the foot-soldier, the Dismount special attack can unseat a mounted opponent. + /// The fighter using this ability must be on his own two feet and not in the saddle of a steed + /// (with one exception: players may use a lance to dismount other players while mounted). + /// If it works, the target will be knocked off his own mount and will take some extra damage from the fall! + /// + public class Dismount : WeaponAbility + { + public Dismount() + { + } + + public override int BaseMana{ get{ return 20; } } + + public override bool Validate( Mobile from ) + { + if ( !base.Validate( from ) ) + return false; + + if ( from.Mounted && !(from.Weapon is Lance) ) + { + from.SendLocalizedMessage( 1061283 ); // You cannot perform that attack while mounted! + return false; + } + + return true; + } + + public static readonly TimeSpan DefenderRemountDelay = TimeSpan.FromSeconds( 10.0 ); // TODO: Taken from bola script, needs to be verified + public static readonly TimeSpan AttackerRemountDelay = TimeSpan.FromSeconds( 3.0 ); + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) ) + return; + + if ( attacker.Mounted && !(defender.Weapon is Lance) ) // TODO: Should there be a message here? + return; + + ClearCurrentAbility( attacker ); + + IMount mount = defender.Mount; + + if ( mount == null ) + { + attacker.SendLocalizedMessage( 1060848 ); // This attack only works on mounted targets + return; + } + + if ( !CheckMana( attacker, true ) ) + return; + + attacker.SendLocalizedMessage( 1060082 ); // The force of your attack has dislodged them from their mount! + + if ( attacker.Mounted ) + defender.SendLocalizedMessage( 1062315 ); // You fall off your mount! + else + defender.SendLocalizedMessage( 1060083 ); // You fall off of your mount and take damage! + + defender.PlaySound( 0x140 ); + defender.FixedParticles( 0x3728, 10, 15, 9955, EffectLayer.Waist ); + + mount.Rider = null; + + BaseMount.SetMountPrevention( defender, BlockMountType.Dazed, DefenderRemountDelay ); + BaseMount.SetMountPrevention( attacker, BlockMountType.DismountRecovery, AttackerRemountDelay ); + + if ( !attacker.Mounted ) + AOS.Damage( defender, attacker, Utility.RandomMinMax( 15, 25 ), 100, 0, 0, 0, 0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/DoubleShot.cs b/Scripts/Items/Weapons/Abilities/DoubleShot.cs new file mode 100644 index 000000000..10d522684 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/DoubleShot.cs @@ -0,0 +1,69 @@ +using System; +using Server; + +namespace Server.Items +{ + /// + /// Send two arrows flying at your opponent if you're mounted. Requires Bushido or Ninjitsu skill. + /// + public class DoubleShot : WeaponAbility + { + public DoubleShot() + { + } + + public override int BaseMana { get { return 30; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 && GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1063347, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + Use( attacker, defender ); + } + + public override void OnMiss( Mobile attacker, Mobile defender ) + { + Use( attacker, defender ); + } + + public override bool Validate( Mobile from ) + { + if( base.Validate( from ) ) + { + if( from.Mounted ) + return true; + else + { + from.SendLocalizedMessage( 1070770 ); // You can only execute this attack while mounted! + ClearCurrentAbility( from ); + } + } + + return false; + } + + public void Use( Mobile attacker, Mobile defender ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) || attacker.Weapon == null ) //sanity + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063348 ); // You launch two shots at once! + defender.SendLocalizedMessage( 1063349 ); // You're attacked with a barrage of shots! + + defender.FixedParticles( 0x37B9, 1, 19, 0x251D, EffectLayer.Waist ); + + attacker.Weapon.OnSwing( attacker, defender ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/DoubleStrike.cs b/Scripts/Items/Weapons/Abilities/DoubleStrike.cs new file mode 100644 index 000000000..ded1ec3ff --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/DoubleStrike.cs @@ -0,0 +1,57 @@ +using System; + +namespace Server.Items +{ + /// + /// The highly skilled warrior can use this special attack to make two quick swings in succession. + /// Landing both blows would be devastating! + /// + public class DoubleStrike : WeaponAbility + { + public DoubleStrike() + { + } + + public override int BaseMana{ get{ return 30; } } + public override double DamageScalar{ get{ return 0.9; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060084 ); // You attack with lightning speed! + defender.SendLocalizedMessage( 1060085 ); // Your attacker strikes with lightning speed! + + defender.PlaySound( 0x3BB ); + defender.FixedEffect( 0x37B9, 244, 25 ); + + // Swing again: + + // If no combatant, wrong map, one of us is a ghost, or cannot see, or deleted, then stop combat + if ( defender == null || defender.Deleted || attacker.Deleted || defender.Map != attacker.Map || !defender.Alive || !attacker.Alive || !attacker.CanSee( defender ) ) + { + attacker.Combatant = null; + return; + } + + IWeapon weapon = attacker.Weapon; + + if ( weapon == null ) + return; + + if ( !attacker.InRange( defender, weapon.MaxRange ) ) + return; + + if ( attacker.InLOS( defender ) ) + { + BaseWeapon.InDoubleStrike = true; + attacker.RevealingAction(); + attacker.NextCombatTime = DateTime.Now + weapon.OnSwing( attacker, defender ); + BaseWeapon.InDoubleStrike = false; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/DualWield.cs b/Scripts/Items/Weapons/Abilities/DualWield.cs new file mode 100644 index 000000000..b878e1ce9 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/DualWield.cs @@ -0,0 +1,78 @@ + +using System; +using Server; +using System.Collections; + +namespace Server.Items +{ + /// + /// Attack faster as you swing with both weapons. + /// + public class DualWield : WeaponAbility + { + private static Hashtable m_Registry = new Hashtable(); + public static Hashtable Registry { get { return m_Registry; } } + + public DualWield() + { + } + + public override int BaseMana { get { return 30; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 ) + { + from.SendLocalizedMessage( 1063352, "50" ); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + if( Registry.Contains( attacker ) ) + { + DualWieldTimer existingtimer = (DualWieldTimer)Registry[attacker]; + existingtimer.Stop(); + Registry.Remove( attacker ); + } + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063362 ); // You dually wield for increased speed! + + attacker.FixedParticles( 0x3779, 1, 15, 0x7F6, 0x3E8, 3, EffectLayer.LeftHand ); + + Timer t = new DualWieldTimer( attacker, (int)(20.0 + 3.0 * (attacker.Skills[SkillName.Ninjitsu].Value - 50.0) / 7.0) ); //20-50 % increase + + t.Start(); + Registry.Add( attacker, t ); + } + + public class DualWieldTimer : Timer + { + private Mobile m_Owner; + private int m_BonusSwingSpeed; + + public int BonusSwingSpeed { get { return m_BonusSwingSpeed; } } + + public DualWieldTimer( Mobile owner, int bonusSwingSpeed ) + : base( TimeSpan.FromSeconds( 6.0 ) ) + { + m_Owner = owner; + m_BonusSwingSpeed = bonusSwingSpeed; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + Registry.Remove( m_Owner ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/Feint.cs b/Scripts/Items/Weapons/Abilities/Feint.cs new file mode 100644 index 000000000..c61e13816 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/Feint.cs @@ -0,0 +1,79 @@ + +using System; +using System.Collections; +using Server.Items; + +namespace Server.Items +{ + /// + /// Gain a defensive advantage over your primary opponent for a short time. + /// + public class Feint : WeaponAbility + { + private static Hashtable m_Registry = new Hashtable(); + public static Hashtable Registry { get { return m_Registry; } } + + public Feint() + { + } + + public override int BaseMana { get { return 30; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 && GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1063347, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + if( Registry.Contains( defender ) ) + { + FeintTimer existingtimer = (FeintTimer)Registry[defender]; + existingtimer.Stop(); + Registry.Remove( defender ); + } + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063360 ); // You baffle your target with a feint! + defender.SendLocalizedMessage( 1063361 ); // You were deceived by an attacker's feint! + + attacker.FixedParticles( 0x3728, 1, 13, 0x7F3, 0x962, 0, EffectLayer.Waist ); + + Timer t = new FeintTimer( defender, (int)(20.0 + 3.0 * (Math.Max( attacker.Skills[SkillName.Ninjitsu].Value, attacker.Skills[SkillName.Bushido].Value ) - 50.0) / 7.0) ); //20-50 % decrease + + t.Start(); + Registry.Add( defender, t ); + } + + public class FeintTimer : Timer + { + private Mobile m_Defender; + private int m_SwingSpeedReduction; + + public int SwingSpeedReduction { get { return m_SwingSpeedReduction; } } + + public FeintTimer( Mobile defender, int swingSpeedReduction ) + : base( TimeSpan.FromSeconds( 6.0 ) ) + { + m_Defender = defender; + m_SwingSpeedReduction = swingSpeedReduction; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + Registry.Remove( m_Defender ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/FrenziedWhirlwind.cs b/Scripts/Items/Weapons/Abilities/FrenziedWhirlwind.cs new file mode 100644 index 000000000..48e979f8c --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/FrenziedWhirlwind.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections; +using Server; +using Server.Spells; +using Server.Engines.PartySystem; + +namespace Server.Items +{ + /// + /// A quick attack to all enemies in range of your weapon that causes damage over time. Requires Bushido or Ninjitsu skill. + /// + public class FrenziedWhirlwind : WeaponAbility + { + public FrenziedWhirlwind() + { + } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 && GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1063347, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido or Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override int BaseMana { get { return 30; } } + + private static Hashtable m_Registry = new Hashtable(); + public static Hashtable Registry { get { return m_Registry; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) ) //Mana check after check that there are targets + return; + + ClearCurrentAbility( attacker ); + + Map map = attacker.Map; + + if( map == null ) + return; + + BaseWeapon weapon = attacker.Weapon as BaseWeapon; + + if( weapon == null ) + return; + + ArrayList list = new ArrayList(); + + foreach( Mobile m in attacker.GetMobilesInRange( 1 ) ) + list.Add( m ); + + ArrayList targets = new ArrayList(); + + for( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + if( m != defender && m != attacker && SpellHelper.ValidIndirectTarget( attacker, m ) ) + { + if( m == null || m.Deleted || m.Map != attacker.Map || !m.Alive || !attacker.CanSee( m ) || !attacker.CanBeHarmful( m ) ) + continue; + + if( !attacker.InRange( m, weapon.MaxRange ) ) + continue; + + if( attacker.InLOS( m ) ) + targets.Add( m ); + } + } + + if( targets.Count > 0 ) + { + if( !CheckMana( attacker, true ) ) + return; + + attacker.FixedEffect( 0x3728, 10, 15 ); + attacker.PlaySound( 0x2A1 ); + + // 5-15 damage + int amount = (int)(10.0 * ((Math.Max( attacker.Skills[SkillName.Bushido].Value, attacker.Skills[SkillName.Ninjitsu].Value ) - 50.0) / 70.0 + 5)); + + for( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + Timer t = Registry[m] as Timer; + + if( t != null ) + { + t.Stop(); + Registry.Remove( m ); + } + + t = new InternalTimer( attacker, m, amount ); + t.Start(); + Registry.Add( m, t ); + } + + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( RepeatEffect ), attacker ); + } + } + + private void RepeatEffect( object state ) + { + Mobile attacker = (Mobile)state; + + attacker.FixedEffect( 0x3728, 10, 15 ); + attacker.PlaySound( 0x2A1 ); + } + + private class InternalTimer : Timer + { + private Mobile m_Attacker; + private Mobile m_Defender; + private double m_DamageRemaining; + private double m_DamageToDo; + + private readonly double DamagePerTick; + + public InternalTimer( Mobile attacker, Mobile defender, int totalDamage ) + : base( TimeSpan.Zero, TimeSpan.FromSeconds( 0.25 ), 12 ) // 3 seconds at .25 seconds apart = 12. Confirm delay inbetween of .25 each. + { + m_Attacker = attacker; + m_Defender = defender; + + m_DamageRemaining = (double)totalDamage; + DamagePerTick = (double)totalDamage / 12 + 0.01; + + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + if( !m_Defender.Alive || m_DamageRemaining <= 0 ) + { + Stop(); + Server.Items.FrenziedWhirlwind.Registry.Remove( m_Defender ); + return; + } + + m_DamageRemaining -= DamagePerTick; + m_DamageToDo += DamagePerTick; + + if( m_DamageRemaining <= 0 && m_DamageToDo < 1 ) + m_DamageToDo = 1.0; //Confirm this 'round up' at the end + + int damage = (int)m_DamageToDo; + + if( damage > 0 ) + { + m_Defender.Damage( damage, m_Attacker ); + m_DamageToDo -= damage; + } + + if( !m_Defender.Alive || m_DamageRemaining <= 0 ) + { + Stop(); + Server.Items.FrenziedWhirlwind.Registry.Remove( m_Defender ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/InfectiousStrike.cs b/Scripts/Items/Weapons/Abilities/InfectiousStrike.cs new file mode 100644 index 000000000..40e879f2a --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/InfectiousStrike.cs @@ -0,0 +1,77 @@ +using System; + +namespace Server.Items +{ + /// + /// This special move represents a significant change to the use of poisons in Age of Shadows. + /// Now, only certain weapon types — those that have Infectious Strike as an available special move — will be able to be poisoned. + /// Targets will no longer be poisoned at random when hit by poisoned weapons. + /// Instead, the wielder must use this ability to deliver the venom. + /// While no skill in Poisoning is directly required to use this ability, being knowledgeable in the application and use of toxins + /// will allow a character to use Infectious Strike at reduced mana cost and with a chance to inflict more deadly poison on his victim. + /// With this change, weapons will no longer be corroded by poison. + /// Level 5 poison will be possible when using this special move. + /// + public class InfectiousStrike : WeaponAbility + { + public InfectiousStrike() + { + } + + public override int BaseMana{ get{ return 15; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) ) + return; + + ClearCurrentAbility( attacker ); + + BaseWeapon weapon = attacker.Weapon as BaseWeapon; + + if ( weapon == null ) + return; + + Poison p = weapon.Poison; + + if ( p == null || weapon.PoisonCharges <= 0 ) + { + attacker.SendLocalizedMessage( 1061141 ); // Your weapon must have a dose of poison to perform an infectious strike! + return; + } + + if ( !CheckMana( attacker, true ) ) + return; + + --weapon.PoisonCharges; + + // Infectious strike special move now uses poisoning skill to help determine potency + int maxLevel = attacker.Skills[SkillName.Poisoning].Fixed / 200; + if ( maxLevel < 0 ) maxLevel = 0; + if ( p.Level > maxLevel ) p = Poison.GetPoison( maxLevel ); + + if ( (attacker.Skills[SkillName.Poisoning].Value / 100.0) > Utility.RandomDouble() ) + { + int level = p.Level + 1; + Poison newPoison = Poison.GetPoison( level ); + + if ( newPoison != null ) + { + p = newPoison; + + attacker.SendLocalizedMessage( 1060080 ); // Your precise strike has increased the level of the poison by 1 + defender.SendLocalizedMessage( 1060081 ); // The poison seems extra effective! + } + } + + defender.PlaySound( 0xDD ); + defender.FixedParticles( 0x3728, 244, 25, 9941, 1266, 0, EffectLayer.Waist ); + + if ( defender.ApplyPoison( attacker, p ) != ApplyPoisonResult.Immune ) + { + attacker.SendLocalizedMessage( 1008096, true, defender.Name ); // You have poisoned your target : + defender.SendLocalizedMessage( 1008097, false, attacker.Name ); // : poisoned you! + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/MortalStrike.cs b/Scripts/Items/Weapons/Abilities/MortalStrike.cs new file mode 100644 index 000000000..dd5e5f6bc --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/MortalStrike.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + /// + /// The assassin's friend. + /// A successful Mortal Strike will render its victim unable to heal any damage for several seconds. + /// Use a gruesome follow-up to finish off your foe. + /// + public class MortalStrike : WeaponAbility + { + public MortalStrike() + { + } + + public override int BaseMana{ get{ return 30; } } + + public static readonly TimeSpan PlayerDuration = TimeSpan.FromSeconds( 6.0 ); + public static readonly TimeSpan NPCDuration = TimeSpan.FromSeconds( 12.0 ); + + public override void OnHit(Mobile attacker, Mobile defender, int damage) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060086 ); // You deliver a mortal wound! + defender.SendLocalizedMessage( 1060087 ); // You have been mortally wounded! + + defender.PlaySound( 0x1E1 ); + defender.FixedParticles( 0x37B9, 244, 25, 9944, 31, 0, EffectLayer.Waist ); + + // Do not reset timer if one is already in place. + if ( !IsWounded( defender ) ) + BeginWound( defender, defender.Player ? PlayerDuration : NPCDuration ); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsWounded( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void BeginWound( Mobile m, TimeSpan duration ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( m, duration ); + m_Table[m] = t; + + t.Start(); + + m.YellowHealthbar = true; + } + + public static void EndWound( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table.Remove( m ); + + m.YellowHealthbar = false; + m.SendLocalizedMessage( 1060208 ); // You are no longer mortally wounded. + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m, TimeSpan duration ) : base( duration ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + EndWound( m_Mobile ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/MovingShot.cs b/Scripts/Items/Weapons/Abilities/MovingShot.cs new file mode 100644 index 000000000..0f736f101 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/MovingShot.cs @@ -0,0 +1,43 @@ +using System; + +namespace Server.Items +{ + /// + /// Available on some crossbows, this special move allows archers to fire while on the move. + /// This shot is somewhat less accurate than normal, but the ability to fire while running is a clear advantage. + /// + public class MovingShot : WeaponAbility + { + public MovingShot() + { + } + + public override int BaseMana{ get{ return 15; } } + public override double AccuracyScalar{ get{ return 0.75; } } + + public override bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + return ( Validate( attacker ) && CheckMana( attacker, true ) ); + } + + public override void OnMiss( Mobile attacker, Mobile defender ) + { + //Validates in OnSwing for accuracy scalar + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060089 ); // You fail to execute your special move + } + + public override bool ValidatesDuringHit { get { return false; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + //Validates in OnSwing for accuracy scalar + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060216 ); // Your shot was successful + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/NerveStrike.cs b/Scripts/Items/Weapons/Abilities/NerveStrike.cs new file mode 100644 index 000000000..bcd680a71 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/NerveStrike.cs @@ -0,0 +1,66 @@ +using System; +using Server; + +namespace Server.Items +{ + /// + /// Does damage and paralyses your opponent for a short time. + /// + public class NerveStrike : WeaponAbility + { + public NerveStrike() + { + } + + public override int BaseMana { get { return 30; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1070768, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + if( defender.Paralyzed ) + { + attacker.SendLocalizedMessage( 1061923 ); // The target is already frozen. + return false; + } + + return true; + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + if( Server.Items.ParalyzingBlow.IsImmune( defender ) ) //After mana consumption intentional + { + attacker.SendLocalizedMessage( 1070804 ); // Your target resists paralysis. + defender.SendLocalizedMessage( 1070813 ); // You resist paralysis. + return; + } + + attacker.SendLocalizedMessage( 1063356 ); // You cripple your target with a nerve strike! + defender.SendLocalizedMessage( 1063357 ); // Your attacker dealt a crippling nerve strike! + + attacker.PlaySound( 0x204 ); + defender.FixedEffect( 0x376A, 9, 32 ); + defender.FixedParticles( 0x37C4, 1, 8, 0x13AF, 0, 0, EffectLayer.Waist ); + + AOS.Damage( defender, attacker, (int)(15.0 * (attacker.Skills[SkillName.Bushido].Value - 50.0) / 70.0 + 10), 100, 0, 0, 0, 0 ); //10-25 + + defender.Paralyze( TimeSpan.FromSeconds( 3.0 ) ); + Server.Items.ParalyzingBlow.BeginImmunity( defender, Server.Items.ParalyzingBlow.FreezeDelayDuration ); + } + } +} diff --git a/Scripts/Items/Weapons/Abilities/ParalyzingBlow.cs b/Scripts/Items/Weapons/Abilities/ParalyzingBlow.cs new file mode 100644 index 000000000..e8740762c --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/ParalyzingBlow.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + /// + /// A successful Paralyzing Blow will leave the target stunned, unable to move, attack, or cast spells, for a few seconds. + /// + public class ParalyzingBlow : WeaponAbility + { + public ParalyzingBlow() + { + } + + public override int BaseMana{ get{ return 30; } } + + public static readonly TimeSpan PlayerFreezeDuration = TimeSpan.FromSeconds( 3.0 ); + public static readonly TimeSpan NPCFreezeDuration = TimeSpan.FromSeconds( 6.0 ); + + public static readonly TimeSpan FreezeDelayDuration = TimeSpan.FromSeconds( 8.0 ); + + // No longer active in pub21: + /*public override bool CheckSkills( Mobile from ) + { + if ( !base.CheckSkills( from ) ) + return false; + + if ( !(from.Weapon is Fists) ) + return true; + + Skill skill = from.Skills[SkillName.Anatomy]; + + if ( skill != null && skill.Base >= 80.0 ) + return true; + + from.SendLocalizedMessage( 1061811 ); // You lack the required anatomy skill to perform that attack! + + return false; + }*/ + + public override bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + if( defender.Paralyzed ) + { + attacker.SendLocalizedMessage( 1061923 ); // The target is already frozen. + return false; + } + + return true; + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + if( IsImmune( defender ) ) //Intentionally going after Mana consumption + { + attacker.SendLocalizedMessage( 1070804 ); // Your target resists paralysis. + defender.SendLocalizedMessage( 1070813 ); // You resist paralysis. + return; + } + + defender.FixedEffect( 0x376A, 9, 32 ); + defender.PlaySound( 0x204 ); + + attacker.SendLocalizedMessage( 1060163 ); // You deliver a paralyzing blow! + defender.SendLocalizedMessage( 1060164 ); // The attack has temporarily paralyzed you! + + TimeSpan duration = defender.Player ? PlayerFreezeDuration : NPCFreezeDuration; + + // Treat it as paralyze not as freeze, effect must be removed when damaged. + defender.Paralyze( duration ); + + BeginImmunity( defender, duration + FreezeDelayDuration ); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsImmune( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void BeginImmunity( Mobile m, TimeSpan duration ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( m, duration ); + m_Table[m] = t; + + t.Start(); + } + + public static void EndImmunity( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table.Remove( m ); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m, TimeSpan duration ) : base( duration ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + EndImmunity( m_Mobile ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/RidingSwipe.cs b/Scripts/Items/Weapons/Abilities/RidingSwipe.cs new file mode 100644 index 000000000..94e7dcee7 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/RidingSwipe.cs @@ -0,0 +1,72 @@ + +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Items +{ + /// + /// If you are on foot, dismounts your opponent and damage the ethereal's rider or the + /// living mount(which must be healed before ridden again). If you are mounted, damages + /// and stuns the mounted opponent. + /// + public class RidingSwipe : WeaponAbility + { + public RidingSwipe() + { + } + + public override int BaseMana { get { return 30; } } + + public override bool RequiresSE { get { return true; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Bushido ) < 50.0 ) + { + from.SendLocalizedMessage( 1070768, "50" ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !defender.Mounted ) + { + attacker.SendLocalizedMessage( 1060848 ); // This attack only works on mounted targets + ClearCurrentAbility( attacker ); + return; + } + + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + if( !attacker.Mounted ) + { + Mobile mount = defender.Mount as Mobile; + BaseMount.Dismount( defender ); + + if( mount != null ) //Ethy mounts don't take damage + { + int amount = 10 + (int)(10.0 * (attacker.Skills[SkillName.Bushido].Value - 50.0) / 70.0 + 5); + + AOS.Damage( mount, null, amount, 100, 0, 0, 0, 0 ); //The mount just takes damage, there's no flagging as if it was attacking the mount directly + + //TODO: Mount prevention until mount healed + } + } + else + { + int amount = 10 + (int)(10.0 * (attacker.Skills[SkillName.Bushido].Value - 50.0) / 70.0 + 5); + + AOS.Damage( defender, attacker, amount, 100, 0, 0, 0, 0 ); + + defender.Paralyze( TimeSpan.FromSeconds( 3.0 ) ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/ShadowStrike.cs b/Scripts/Items/Weapons/Abilities/ShadowStrike.cs new file mode 100644 index 000000000..a4236b000 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/ShadowStrike.cs @@ -0,0 +1,54 @@ +using System; + +namespace Server.Items +{ + /// + /// This powerful ability requires secondary skills to activate. + /// Successful use of Shadowstrike deals extra damage to the target — and renders the attacker invisible! + /// Only those who are adept at the art of stealth will be able to use this ability. + /// + public class ShadowStrike : WeaponAbility + { + public ShadowStrike() + { + } + + public override int BaseMana{ get{ return 20; } } + public override double DamageScalar{ get{ return 1.25; } } + + public override bool CheckSkills( Mobile from ) + { + if ( !base.CheckSkills( from ) ) + return false; + + Skill skill = from.Skills[SkillName.Stealth]; + + if ( skill != null && skill.Value >= 80.0 ) + return true; + + from.SendLocalizedMessage( 1060183 ); // You lack the required stealth to perform that attack + + return false; + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1060078 ); // You strike and hide in the shadows! + defender.SendLocalizedMessage( 1060079 ); // You are dazed by the attack and your attacker vanishes! + + Effects.SendLocationParticles( EffectItem.Create( attacker.Location, attacker.Map, EffectItem.DefaultDuration ), 0x376A, 8, 12, 9943 ); + attacker.PlaySound( 0x482 ); + + defender.FixedEffect( 0x37BE, 20, 25 ); + + attacker.Combatant = null; + attacker.Warmode = false; + attacker.Hidden = true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/TalonStrike.cs b/Scripts/Items/Weapons/Abilities/TalonStrike.cs new file mode 100644 index 000000000..648f0697f --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/TalonStrike.cs @@ -0,0 +1,106 @@ + +using System; +using Server; +using System.Collections; + +namespace Server.Items +{ + /// + /// Attack with increased damage with additional damage over time. + /// + public class TalonStrike : WeaponAbility + { + private static Hashtable m_Registry = new Hashtable(); + public static Hashtable Registry { get { return m_Registry; } } + + public TalonStrike() + { + } + + public override int BaseMana { get { return 30; } } + public override double DamageScalar { get { return 1.2; } } + + public override bool CheckSkills( Mobile from ) + { + if( GetSkill( from, SkillName.Ninjitsu ) < 50.0 ) + { + from.SendLocalizedMessage( 1063352, "50" ); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + return false; + } + + return base.CheckSkills( from ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( Registry.Contains( defender ) || !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentAbility( attacker ); + + attacker.SendLocalizedMessage( 1063358 ); // You deliver a talon strike! + defender.SendLocalizedMessage( 1063359 ); // Your attacker delivers a talon strike! + + defender.FixedParticles( 0x373A, 1, 17, 0x26BC, 0x662, 0, EffectLayer.Waist ); + + Timer t = new InternalTimer( defender, (int)(10.0 * (attacker.Skills[SkillName.Ninjitsu].Value - 50.0) / 70.0 + 5), attacker ); //5 - 15 damage + + t.Start(); + + Registry.Add( defender, t ); + } + + private class InternalTimer : Timer + { + private Mobile m_Defender; + private double m_DamageRemaining; + private double m_DamageToDo; + private Mobile m_Attacker; + + private readonly double DamagePerTick; + + public InternalTimer( Mobile defender, int totalDamage, Mobile attacker ) + : base( TimeSpan.Zero, TimeSpan.FromSeconds( 0.25 ), 12 ) // 3 seconds at .25 seconds apart = 12. Confirm delay inbetween of .25 each. + { + m_Defender = defender; + m_DamageRemaining = (double)totalDamage; + Priority = TimerPriority.TwentyFiveMS; + + m_Attacker = attacker; + + DamagePerTick = (double)totalDamage / 12+.01; + } + + protected override void OnTick() + { + if( !m_Defender.Alive || m_DamageRemaining <= 0 ) + { + Stop(); + Server.Items.TalonStrike.Registry.Remove( m_Defender ); + return; + } + + m_DamageRemaining -= DamagePerTick; + m_DamageToDo += DamagePerTick; + + if( m_DamageRemaining <= 0 && m_DamageToDo < 1 ) + m_DamageToDo = 1.0; //Confirm this 'round up' at the end + + int damage = (int)m_DamageToDo; + + if( damage > 0 ) + { + //m_Defender.Damage( damage, m_Attacker, false ); + m_Defender.Hits -= damage; //Don't show damage, don't disrupt + m_DamageToDo -= damage; + } + + if( !m_Defender.Alive || m_DamageRemaining <= 0 ) + { + Stop(); + Server.Items.TalonStrike.Registry.Remove( m_Defender ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/WeaponAbility.cs b/Scripts/Items/Weapons/Abilities/WeaponAbility.cs new file mode 100644 index 000000000..841d79df6 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/WeaponAbility.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Spells; + + +namespace Server.Items +{ + public abstract class WeaponAbility + { + public virtual int BaseMana{ get{ return 0; } } + + public virtual double AccuracyScalar{ get{ return 1.0; } } + public virtual double DamageScalar{ get{ return 1.0; } } + + public virtual bool RequiresSE { get { return false; } } + + public virtual void OnHit( Mobile attacker, Mobile defender, int damage ) + { + } + + public virtual void OnMiss( Mobile attacker, Mobile defender ) + { + } + + public virtual bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + // Here because you must be sure you can use the skill before calling CheckHit if the ability has a HCI bonus for example + return true; + } + + public virtual bool OnBeforeDamage( Mobile attacker, Mobile defender ) + { + return true; + } + + public virtual double GetRequiredSkill( Mobile from ) + { + BaseWeapon weapon = from.Weapon as BaseWeapon; + + if ( weapon != null && weapon.PrimaryAbility == this ) + return 70.0; + else if ( weapon != null && weapon.SecondaryAbility == this ) + return 90.0; + + return 200.0; + } + + public virtual int CalculateMana( Mobile from ) + { + int mana = BaseMana; + + double skillTotal = GetSkill( from, SkillName.Swords ) + GetSkill( from, SkillName.Macing ) + + GetSkill( from, SkillName.Fencing ) + GetSkill( from, SkillName.Archery ) + GetSkill( from, SkillName.Parry ) + + GetSkill( from, SkillName.Lumberjacking ) + GetSkill( from, SkillName.Stealth ) + + GetSkill( from, SkillName.Poisoning ) + GetSkill( from, SkillName.Bushido ) + GetSkill( from, SkillName.Ninjitsu ); + + if ( skillTotal >= 300.0 ) + mana -= 10; + else if ( skillTotal >= 200.0 ) + mana -= 5; + + double scalar = 1.0; + if ( !Server.Spells.Necromancy.MindRotSpell.GetMindRotScalar( from, ref scalar ) ) + scalar = 1.0; + + // Lower Mana Cost = 40% + int lmc = Math.Min( AosAttributes.GetValue( from, AosAttribute.LowerManaCost ), 40 ); + + scalar -= (double)lmc / 100; + mana = (int)(mana * scalar); + + // Using a special move within 3 seconds of the previous special move costs double mana + if ( GetContext( from ) != null ) + mana *= 2; + + return mana; + } + + public virtual bool CheckWeaponSkill( Mobile from ) + { + BaseWeapon weapon = from.Weapon as BaseWeapon; + + if ( weapon == null ) + return false; + + Skill skill = from.Skills[weapon.Skill]; + double reqSkill = GetRequiredSkill( from ); + + if ( skill != null && skill.Base >= reqSkill ) + return true; + + /* */ + if ( weapon.WeaponAttributes.UseBestSkill > 0 && (from.Skills[SkillName.Swords].Base >= reqSkill || from.Skills[SkillName.Macing].Base >= reqSkill || from.Skills[SkillName.Fencing].Base >= reqSkill) ) + return true; + /* */ + + from.SendLocalizedMessage( 1060182, reqSkill.ToString() ); // You need ~1_SKILL_REQUIREMENT~ weapon skill to perform that attack + + return false; + } + + public virtual bool CheckSkills( Mobile from ) + { + return CheckWeaponSkill( from ); + } + + public virtual double GetSkill( Mobile from, SkillName skillName ) + { + Skill skill = from.Skills[skillName]; + + if ( skill == null ) + return 0.0; + + return skill.Value; + } + + public virtual bool CheckMana( Mobile from, bool consume ) + { + int mana = CalculateMana( from ); + + if ( from.Mana < mana ) + { + from.SendLocalizedMessage( 1060181, mana.ToString() ); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack + return false; + } + + if ( consume ) + { + if ( GetContext( from ) == null ) + { + Timer timer = new WeaponAbilityTimer( from ); + timer.Start(); + + AddContext( from, new WeaponAbilityContext( timer ) ); + } + + from.Mana -= mana; + } + + return true; + } + + public virtual bool Validate( Mobile from ) + { + if ( !from.Player ) + return true; + + int flags = (from.NetState == null) ? 0 : from.NetState.Flags; + + if( RequiresSE && (flags & 0x10) == 0 ) //TODO: Convert to expansionInfo + { + from.SendLocalizedMessage( 1063456 ); // You must upgrade to Samurai Empire in order to use that ability. + return false; + } + + if ( Spells.Bushido.HonorableExecution.IsUnderPenalty( from ) || Spells.Ninjitsu.AnimalForm.UnderTransformation( from ) ) + { + from.SendLocalizedMessage( 1063024 ); // You cannot perform this special move right now. + return false; + } + + return CheckSkills( from ) && CheckMana( from, false ); + } + + private static WeaponAbility[] m_Abilities = new WeaponAbility[30] + { + null, + new ArmorIgnore(), + new BleedAttack(), + new ConcussionBlow(), + new CrushingBlow(), + new Disarm(), + new Dismount(), + new DoubleStrike(), + new InfectiousStrike(), + new MortalStrike(), + new MovingShot(), + new ParalyzingBlow(), + new ShadowStrike(), + new WhirlwindAttack(), + + new RidingSwipe(), + new FrenziedWhirlwind(), + new Block(), + new DefenseMastery(), + new NerveStrike(), + new TalonStrike(), + new Feint(), + new DualWield(), + new DoubleShot(), + new ArmorPierce(), + null, + null, + null, + null, + null, + null + }; + + public static WeaponAbility[] Abilities{ get{ return m_Abilities; } } + + private static Hashtable m_Table = new Hashtable(); + + public static Hashtable Table{ get{ return m_Table; } } + + public static readonly WeaponAbility ArmorIgnore = m_Abilities[ 1]; + public static readonly WeaponAbility BleedAttack = m_Abilities[ 2]; + public static readonly WeaponAbility ConcussionBlow = m_Abilities[ 3]; + public static readonly WeaponAbility CrushingBlow = m_Abilities[ 4]; + public static readonly WeaponAbility Disarm = m_Abilities[ 5]; + public static readonly WeaponAbility Dismount = m_Abilities[ 6]; + public static readonly WeaponAbility DoubleStrike = m_Abilities[ 7]; + public static readonly WeaponAbility InfectiousStrike = m_Abilities[ 8]; + public static readonly WeaponAbility MortalStrike = m_Abilities[ 9]; + public static readonly WeaponAbility MovingShot = m_Abilities[10]; + public static readonly WeaponAbility ParalyzingBlow = m_Abilities[11]; + public static readonly WeaponAbility ShadowStrike = m_Abilities[12]; + public static readonly WeaponAbility WhirlwindAttack = m_Abilities[13]; + + public static readonly WeaponAbility RidingSwipe = m_Abilities[14]; + public static readonly WeaponAbility FrenziedWhirlwind = m_Abilities[15]; + public static readonly WeaponAbility Block = m_Abilities[16]; + public static readonly WeaponAbility DefenseMastery = m_Abilities[17]; + public static readonly WeaponAbility NerveStrike = m_Abilities[18]; + public static readonly WeaponAbility TalonStrike = m_Abilities[19]; + public static readonly WeaponAbility Feint = m_Abilities[20]; + public static readonly WeaponAbility DualWield = m_Abilities[21]; + public static readonly WeaponAbility DoubleShot = m_Abilities[22]; + public static readonly WeaponAbility ArmorPierce = m_Abilities[23]; + + public static readonly WeaponAbility Bladeweave = m_Abilities[24]; + public static readonly WeaponAbility ForceArrow = m_Abilities[25]; + public static readonly WeaponAbility LightningArrow = m_Abilities[26]; + public static readonly WeaponAbility PsychicAttack = m_Abilities[27]; + public static readonly WeaponAbility SerpentArrow = m_Abilities[28]; + public static readonly WeaponAbility ForceOfNature = m_Abilities[29]; + + public static bool IsWeaponAbility( Mobile m, WeaponAbility a ) + { + if ( a == null ) + return true; + + if ( !m.Player ) + return true; + + BaseWeapon weapon = m.Weapon as BaseWeapon; + + return ( weapon != null && (weapon.PrimaryAbility == a || weapon.SecondaryAbility == a) ); + } + + public virtual bool ValidatesDuringHit{ get { return true; } } + + public static WeaponAbility GetCurrentAbility( Mobile m ) + { + if ( !Core.AOS ) + { + ClearCurrentAbility( m ); + return null; + } + + WeaponAbility a = (WeaponAbility)m_Table[m]; + + if ( !IsWeaponAbility( m, a ) ) + { + ClearCurrentAbility( m ); + return null; + } + + if ( a != null && a.ValidatesDuringHit && !a.Validate( m ) ) + { + ClearCurrentAbility( m ); + return null; + } + + return a; + } + + public static bool SetCurrentAbility( Mobile m, WeaponAbility a ) + { + if ( !Core.AOS ) + { + ClearCurrentAbility( m ); + return false; + } + + if ( !IsWeaponAbility( m, a ) ) + { + ClearCurrentAbility( m ); + return false; + } + + if ( a != null && !a.Validate( m ) ) + { + ClearCurrentAbility( m ); + return false; + } + + if ( a == null ) + { + m_Table.Remove( m ); + } + else + { + SpecialMove.ClearCurrentMove( m ); + + m_Table[m] = a; + } + + return true; + } + + public static void ClearCurrentAbility( Mobile m ) + { + m_Table.Remove( m ); + + if ( Core.AOS && m.NetState != null ) + m.Send( ClearWeaponAbility.Instance ); + } + + public static void Initialize() + { + EventSink.SetAbility += new SetAbilityEventHandler( EventSink_SetAbility ); + } + + public WeaponAbility() + { + } + + private static void EventSink_SetAbility( SetAbilityEventArgs e ) + { + int index = e.Index; + + if ( index == 0 ) + ClearCurrentAbility( e.Mobile ); + else if ( index >= 1 && index < m_Abilities.Length ) + SetCurrentAbility( e.Mobile, m_Abilities[index] ); + } + + + private static Hashtable m_PlayersTable = new Hashtable(); + + private static void AddContext( Mobile m, WeaponAbilityContext context ) + { + m_PlayersTable[m] = context; + } + + private static void RemoveContext( Mobile m ) + { + WeaponAbilityContext context = GetContext( m ); + + if ( context != null ) + RemoveContext( m, context ); + } + + private static void RemoveContext( Mobile m, WeaponAbilityContext context ) + { + m_PlayersTable.Remove( m ); + + context.Timer.Stop(); + } + + private static WeaponAbilityContext GetContext( Mobile m ) + { + return ( m_PlayersTable[m] as WeaponAbilityContext ); + } + + private class WeaponAbilityTimer : Timer + { + private Mobile m_Mobile; + + public WeaponAbilityTimer( Mobile from ) : base ( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Mobile = from; + + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + RemoveContext( m_Mobile ); + } + } + + private class WeaponAbilityContext + { + private Timer m_Timer; + + public Timer Timer{ get{ return m_Timer; } } + + public WeaponAbilityContext( Timer timer ) + { + m_Timer = timer; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Abilities/WhirlwindAttack.cs b/Scripts/Items/Weapons/Abilities/WhirlwindAttack.cs new file mode 100644 index 000000000..e376b1b49 --- /dev/null +++ b/Scripts/Items/Weapons/Abilities/WhirlwindAttack.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using Server; +using Server.Spells; + +namespace Server.Items +{ + /// + /// A godsend to a warrior surrounded, the Whirlwind Attack allows the fighter to strike at all nearby targets in one mighty spinning swing. + /// + public class WhirlwindAttack : WeaponAbility + { + public WhirlwindAttack() + { + } + + public override int BaseMana{ get{ return 15; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) ) + return; + + ClearCurrentAbility( attacker ); + + Map map = attacker.Map; + + if ( map == null ) + return; + + BaseWeapon weapon = attacker.Weapon as BaseWeapon; + + if ( weapon == null ) + return; + + if ( !CheckMana( attacker, true ) ) + return; + + attacker.FixedEffect( 0x3728, 10, 15 ); + attacker.PlaySound( 0x2A1 ); + + ArrayList list = new ArrayList(); + + foreach ( Mobile m in attacker.GetMobilesInRange( 1 ) ) + list.Add( m ); + + ArrayList targets = new ArrayList(); + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + if ( m != defender && m != attacker && SpellHelper.ValidIndirectTarget( attacker, m ) ) + { + if ( m == null || m.Deleted || m.Map != attacker.Map || !m.Alive || !attacker.CanSee( m ) || !attacker.CanBeHarmful( m ) ) + continue; + + if ( !attacker.InRange( m, weapon.MaxRange ) ) + continue; + + if ( attacker.InLOS( m ) ) + targets.Add( m ); + } + } + + if ( targets.Count > 0 ) + { + double bushido = attacker.Skills.Bushido.Value; + double damageBonus = 1.0 + Math.Pow( (targets.Count * bushido) / 60, 2 ) / 100; + + if ( damageBonus > 2.0 ) + damageBonus = 2.0; + + attacker.RevealingAction(); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + attacker.SendLocalizedMessage( 1060161 ); // The whirling attack strikes a target! + m.SendLocalizedMessage( 1060162 ); // You are struck by the whirling attack and take damage! + + weapon.OnHit( attacker, m, damageBonus ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/AxeOfTheHeavens.cs b/Scripts/Items/Weapons/Artifacts/AxeOfTheHeavens.cs new file mode 100644 index 000000000..55fb5d7d7 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/AxeOfTheHeavens.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class AxeOfTheHeavens : DoubleAxe + { + public override int LabelNumber{ get{ return 1061106; } } // Axe of the Heavens + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public AxeOfTheHeavens() + { + Hue = 0x4D5; + WeaponAttributes.HitLightning = 50; + Attributes.AttackChance = 15; + Attributes.DefendChance = 15; + Attributes.WeaponDamage = 50; + } + + public AxeOfTheHeavens( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/BladeOfInsanity.cs b/Scripts/Items/Weapons/Artifacts/BladeOfInsanity.cs new file mode 100644 index 000000000..c8efd5269 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/BladeOfInsanity.cs @@ -0,0 +1,45 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BladeOfInsanity : Katana + { + public override int LabelNumber{ get{ return 1061088; } } // Blade of Insanity + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BladeOfInsanity() + { + Hue = 0x76D; + WeaponAttributes.HitLeechStam = 100; + Attributes.RegenStam = 2; + Attributes.WeaponSpeed = 30; + Attributes.WeaponDamage = 50; + } + + public BladeOfInsanity( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 0x44F ) + Hue = 0x76D; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/BladeOfTheRighteous.cs b/Scripts/Items/Weapons/Artifacts/BladeOfTheRighteous.cs new file mode 100644 index 000000000..bbe251484 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/BladeOfTheRighteous.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BladeOfTheRighteous : Longsword + { + public override int LabelNumber{ get{ return 1061107; } } // Blade of the Righteous + public override int ArtifactRarity{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BladeOfTheRighteous() + { + Hue = 0x47E; + //Slayer = SlayerName.DaemonDismissal; + Slayer = SlayerName.Exorcism; + WeaponAttributes.HitLeechHits = 50; + WeaponAttributes.UseBestSkill = 1; + Attributes.BonusHits = 10; + Attributes.WeaponDamage = 50; + } + + public BladeOfTheRighteous( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Slayer == SlayerName.None ) + Slayer = SlayerName.DaemonDismissal; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/BoneCrusher.cs b/Scripts/Items/Weapons/Artifacts/BoneCrusher.cs new file mode 100644 index 000000000..ee9e36091 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/BoneCrusher.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BoneCrusher : WarMace + { + public override int LabelNumber{ get{ return 1061596; } } // Bone Crusher + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BoneCrusher() + { + ItemID = 0x1406; + Hue = 0x60C; + WeaponAttributes.HitLowerDefend = 50; + Attributes.BonusStr = 10; + Attributes.WeaponDamage = 75; + } + + public BoneCrusher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 0x604 ) + Hue = 0x60C; + + if ( ItemID == 0x1407 ) + ItemID = 0x1406; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/BreathOfTheDead.cs b/Scripts/Items/Weapons/Artifacts/BreathOfTheDead.cs new file mode 100644 index 000000000..ba752be10 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/BreathOfTheDead.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class BreathOfTheDead : BoneHarvester + { + public override int LabelNumber{ get{ return 1061109; } } // Breath of the Dead + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public BreathOfTheDead() + { + Hue = 0x455; + WeaponAttributes.HitLeechHits = 100; + WeaponAttributes.HitHarm = 25; + Attributes.SpellDamage = 5; + Attributes.WeaponDamage = 50; + } + + public BreathOfTheDead( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/Frostbringer.cs b/Scripts/Items/Weapons/Artifacts/Frostbringer.cs new file mode 100644 index 000000000..8a43bf884 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/Frostbringer.cs @@ -0,0 +1,47 @@ +using System; +using Server; + +namespace Server.Items +{ + public class Frostbringer : Bow + { + public override int LabelNumber{ get{ return 1061111; } } // Frostbringer + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public Frostbringer() + { + Hue = 0x4F2; + WeaponAttributes.HitDispel = 50; + Attributes.RegenStam = 10; + Attributes.WeaponDamage = 50; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = pois = nrgy = 0; + cold = 100; + } + + public Frostbringer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs b/Scripts/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs new file mode 100644 index 000000000..189e61ec6 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/LegacyOfTheDreadLord.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public class LegacyOfTheDreadLord : Bardiche + { + public override int LabelNumber{ get{ return 1060860; } } // Legacy of the Dread Lord + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public LegacyOfTheDreadLord() + { + Hue = 0x676; + Attributes.SpellChanneling = 1; + Attributes.CastRecovery = 3; + Attributes.WeaponSpeed = 30; + Attributes.WeaponDamage = 50; + } + + public LegacyOfTheDreadLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Attributes.CastSpeed == 3 ) + Attributes.CastRecovery = 3; + + if ( Hue == 0x4B9 ) + Hue = 0x676; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/SerpentsFang.cs b/Scripts/Items/Weapons/Artifacts/SerpentsFang.cs new file mode 100644 index 000000000..5d11eac1d --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/SerpentsFang.cs @@ -0,0 +1,53 @@ +using System; +using Server; + +namespace Server.Items +{ + public class SerpentsFang : Kryss + { + public override int LabelNumber{ get{ return 1061601; } } // Serpent's Fang + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public SerpentsFang() + { + ItemID = 0x1400; + Hue = 0x488; + WeaponAttributes.HitPoisonArea = 100; + WeaponAttributes.ResistPoisonBonus = 20; + Attributes.AttackChance = 15; + Attributes.WeaponDamage = 50; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + fire = cold = nrgy = 0; + phys = 25; + pois = 75; + } + + public SerpentsFang( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( ItemID == 0x1401 ) + ItemID = 0x1400; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/StaffOfTheMagi.cs b/Scripts/Items/Weapons/Artifacts/StaffOfTheMagi.cs new file mode 100644 index 000000000..99cb12379 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/StaffOfTheMagi.cs @@ -0,0 +1,54 @@ +using System; +using Server; + +namespace Server.Items +{ + public class StaffOfTheMagi : BlackStaff + { + public override int LabelNumber{ get{ return 1061600; } } // Staff of the Magi + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public StaffOfTheMagi() + { + Hue = 0x481; + WeaponAttributes.MageWeapon = 30; + Attributes.SpellChanneling = 1; + Attributes.CastSpeed = 1; + Attributes.WeaponDamage = 50; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = 0; + nrgy = 100; + } + + public StaffOfTheMagi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( WeaponAttributes.MageWeapon == 0 ) + WeaponAttributes.MageWeapon = 30; + + if ( ItemID == 0xDF1 ) + ItemID = 0xDF0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/TheBeserkersMaul.cs b/Scripts/Items/Weapons/Artifacts/TheBeserkersMaul.cs new file mode 100644 index 000000000..d2d1a6120 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/TheBeserkersMaul.cs @@ -0,0 +1,40 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TheBeserkersMaul : Maul + { + public override int LabelNumber{ get{ return 1061108; } } // The Berserker's Maul + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TheBeserkersMaul() + { + Hue = 0x21; + Attributes.WeaponSpeed = 75; + Attributes.WeaponDamage = 50; + } + + public TheBeserkersMaul( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/TheDragonSlayer.cs b/Scripts/Items/Weapons/Artifacts/TheDragonSlayer.cs new file mode 100644 index 000000000..60074bc1d --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/TheDragonSlayer.cs @@ -0,0 +1,52 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TheDragonSlayer : Lance + { + public override int LabelNumber{ get{ return 1061248; } } // The Dragon Slayer + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TheDragonSlayer() + { + Hue = 0x530; + Slayer = SlayerName.DragonSlaying; + Attributes.Luck = 110; + Attributes.WeaponDamage = 50; + WeaponAttributes.ResistFireBonus = 20; + WeaponAttributes.UseBestSkill = 1; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = 0; + nrgy = 100; + } + + public TheDragonSlayer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Slayer == SlayerName.None ) + Slayer = SlayerName.DragonSlaying; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/TheDryadBow.cs b/Scripts/Items/Weapons/Artifacts/TheDryadBow.cs new file mode 100644 index 000000000..688cac760 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/TheDryadBow.cs @@ -0,0 +1,77 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TheDryadBow : Bow + { + public override int LabelNumber{ get{ return 1061090; } } // The Dryad Bow + public override int ArtifactRarity{ get{ return 11; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TheDryadBow() + { + ItemID = 0x13B1; + Hue = 0x48F; + SkillBonuses.SetValues( 0, m_PossibleBonusSkills[Utility.Random(m_PossibleBonusSkills.Length)], (Utility.Random( 4 ) == 0 ? 10.0 : 5.0) ); + WeaponAttributes.SelfRepair = 5; + Attributes.WeaponSpeed = 50; + Attributes.WeaponDamage = 35; + WeaponAttributes.ResistPoisonBonus = 15; + } + + private static SkillName[] m_PossibleBonusSkills = new SkillName[] + { + SkillName.Swords, + SkillName.Fencing, + SkillName.Macing, + SkillName.Archery, + SkillName.Wrestling, + SkillName.Parry, + SkillName.Tactics, + SkillName.Anatomy, + SkillName.Healing, + SkillName.Magery, + SkillName.Meditation, + SkillName.EvalInt, + SkillName.MagicResist, + SkillName.AnimalTaming, + SkillName.AnimalLore, + SkillName.Veterinary, + SkillName.Musicianship, + SkillName.Provocation, + SkillName.Discordance, + SkillName.Peacemaking, + SkillName.Chivalry, + SkillName.Focus, + SkillName.Necromancy, + SkillName.Stealing, + SkillName.Stealth, + SkillName.SpiritSpeak + }; + + public TheDryadBow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 ) + SkillBonuses.SetValues( 0, m_PossibleBonusSkills[Utility.Random(m_PossibleBonusSkills.Length)], (Utility.Random( 4 ) == 0 ? 10.0 : 5.0) ); + } + } +} diff --git a/Scripts/Items/Weapons/Artifacts/TheTaskmaster.cs b/Scripts/Items/Weapons/Artifacts/TheTaskmaster.cs new file mode 100644 index 000000000..3f1eb3274 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/TheTaskmaster.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TheTaskmaster : WarFork + { + public override int LabelNumber{ get{ return 1061110; } } // The Taskmaster + public override int ArtifactRarity{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TheTaskmaster() + { + Hue = 0x4F8; + WeaponAttributes.HitPoisonArea = 100; + Attributes.BonusDex = 5; + Attributes.AttackChance = 15; + Attributes.WeaponDamage = 50; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = nrgy = 0; + pois = 100; + } + + public TheTaskmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/TitansHammer.cs b/Scripts/Items/Weapons/Artifacts/TitansHammer.cs new file mode 100644 index 000000000..d6d932cd3 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/TitansHammer.cs @@ -0,0 +1,42 @@ +using System; +using Server; + +namespace Server.Items +{ + public class TitansHammer : WarHammer + { + public override int LabelNumber{ get{ return 1060024; } } // Titan's Hammer + public override int ArtifactRarity{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public TitansHammer() + { + Hue = 0x482; + WeaponAttributes.HitEnergyArea = 100; + Attributes.BonusStr = 15; + Attributes.AttackChance = 15; + Attributes.WeaponDamage = 50; + } + + public TitansHammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Artifacts/ZyronicClaw.cs b/Scripts/Items/Weapons/Artifacts/ZyronicClaw.cs new file mode 100644 index 000000000..f019b9411 --- /dev/null +++ b/Scripts/Items/Weapons/Artifacts/ZyronicClaw.cs @@ -0,0 +1,50 @@ +using System; +using Server; + +namespace Server.Items +{ + public class ZyronicClaw : ExecutionersAxe + { + public override int LabelNumber{ get{ return 1061593; } } // Zyronic Claw + public override int ArtifactRarity{ get{ return 10; } } + + public override int InitMinHits{ get{ return 255; } } + public override int InitMaxHits{ get{ return 255; } } + + [Constructable] + public ZyronicClaw() + { + Hue = 0x485; + Slayer = SlayerName.ElementalBan; + WeaponAttributes.HitLeechMana = 50; + Attributes.AttackChance = 30; + Attributes.WeaponDamage = 50; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = nrgy = 20; + } + + public ZyronicClaw( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Slayer == SlayerName.None ) + Slayer = SlayerName.ElementalBan; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/Axe.cs b/Scripts/Items/Weapons/Axes/Axe.cs new file mode 100644 index 000000000..9a136d951 --- /dev/null +++ b/Scripts/Items/Weapons/Axes/Axe.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xF49, 0xF4a )] + public class Axe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Dismount; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 37; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 33; } } + public override int OldSpeed{ get{ return 37; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public Axe() : base( 0xF49 ) + { + Weight = 4.0; + } + + public Axe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/BaseAxe.cs b/Scripts/Items/Weapons/Axes/BaseAxe.cs new file mode 100644 index 000000000..820d0c38d --- /dev/null +++ b/Scripts/Items/Weapons/Axes/BaseAxe.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Engines.Harvest; +using Server.ContextMenus; + +namespace Server.Items +{ + public abstract class BaseAxe : BaseMeleeWeapon, IUsesRemaining + { + public override int DefHitSound{ get{ return 0x232; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override SkillName DefSkill{ get{ return SkillName.Swords; } } + public override WeaponType DefType{ get{ return WeaponType.Axe; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash2H; } } + + public virtual HarvestSystem HarvestSystem{ get{ return Lumberjacking.System; } } + + private int m_UsesRemaining; + private bool m_ShowUsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ShowUsesRemaining + { + get { return m_ShowUsesRemaining; } + set { m_ShowUsesRemaining = value; InvalidateProperties(); } + } + + public virtual int GetUsesScalar() + { + if ( Quality == WeaponQuality.Exceptional ) + return 200; + + return 100; + } + + public override void UnscaleDurability() + { + base.UnscaleDurability(); + + int scale = GetUsesScalar(); + + m_UsesRemaining = ((m_UsesRemaining * 100) + (scale - 1)) / scale; + InvalidateProperties(); + } + + public override void ScaleDurability() + { + base.ScaleDurability(); + + int scale = GetUsesScalar(); + + m_UsesRemaining = ((m_UsesRemaining * scale) + 99) / 100; + InvalidateProperties(); + } + + public BaseAxe( int itemID ) : base( itemID ) + { + m_UsesRemaining = 150; + } + + public BaseAxe( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( HarvestSystem == null ) + return; + + if ( IsChildOf( from.Backpack ) || Parent == from ) + HarvestSystem.BeginHarvesting( from, this ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( HarvestSystem != null ) + BaseHarvestTool.AddContextMenuEntries( from, this, list, HarvestSystem ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (bool) m_ShowUsesRemaining ); + + writer.Write( (int) m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_ShowUsesRemaining = reader.ReadBool(); + goto case 1; + } + case 1: + { + m_UsesRemaining = reader.ReadInt(); + goto case 0; + } + case 0: + { + if ( m_UsesRemaining < 1 ) + m_UsesRemaining = 150; + + break; + } + } + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + { + StatMod mod = defender.GetStatMod( "Concussion" ); + + if ( mod == null ) + { + defender.SendMessage( "You receive a concussion blow!" ); + defender.AddStatMod( new StatMod( StatType.Int, "Concussion", -(defender.RawInt / 2), TimeSpan.FromSeconds( 30.0 ) ) ); + + attacker.SendMessage( "You deliver a concussion blow!" ); + attacker.PlaySound( 0x308 ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/BattleAxe.cs b/Scripts/Items/Weapons/Axes/BattleAxe.cs new file mode 100644 index 000000000..b079e4546 --- /dev/null +++ b/Scripts/Items/Weapons/Axes/BattleAxe.cs @@ -0,0 +1,51 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xF47, 0xF48 )] + public class BattleAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 31; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 38; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public BattleAxe() : base( 0xF47 ) + { + Weight = 4.0; + Layer = Layer.TwoHanded; + } + + public BattleAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/DoubleAxe.cs b/Scripts/Items/Weapons/Axes/DoubleAxe.cs new file mode 100644 index 000000000..6b8d466ff --- /dev/null +++ b/Scripts/Items/Weapons/Axes/DoubleAxe.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xf4b, 0xf4c )] + public class DoubleAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 33; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 35; } } + public override int OldSpeed{ get{ return 37; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public DoubleAxe() : base( 0xF4B ) + { + Weight = 8.0; + } + + public DoubleAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/ExecutionersAxe.cs b/Scripts/Items/Weapons/Axes/ExecutionersAxe.cs new file mode 100644 index 000000000..a66511119 --- /dev/null +++ b/Scripts/Items/Weapons/Axes/ExecutionersAxe.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xf45, 0xf46 )] + public class ExecutionersAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 33; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 33; } } + public override int OldSpeed{ get{ return 37; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public ExecutionersAxe() : base( 0xF45 ) + { + Weight = 8.0; + } + + public ExecutionersAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/Hatchet.cs b/Scripts/Items/Weapons/Axes/Hatchet.cs new file mode 100644 index 000000000..4d08f849f --- /dev/null +++ b/Scripts/Items/Weapons/Axes/Hatchet.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xF43, 0xF44 )] + public class Hatchet : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 41; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 2; } } + public override int OldMaxDamage{ get{ return 17; } } + public override int OldSpeed{ get{ return 40; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public Hatchet() : base( 0xF43 ) + { + Weight = 4.0; + } + + public Hatchet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/LargeBattleAxe.cs b/Scripts/Items/Weapons/Axes/LargeBattleAxe.cs new file mode 100644 index 000000000..32a47e355 --- /dev/null +++ b/Scripts/Items/Weapons/Axes/LargeBattleAxe.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x13FB, 0x13FA )] + public class LargeBattleAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.BleedAttack; } } + + public override int AosStrengthReq{ get{ return 80; } } + public override int AosMinDamage{ get{ return 16; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 29; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 38; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public LargeBattleAxe() : base( 0x13FB ) + { + Weight = 6.0; + } + + public LargeBattleAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/Pickaxe.cs b/Scripts/Items/Weapons/Axes/Pickaxe.cs new file mode 100644 index 000000000..97db13ba6 --- /dev/null +++ b/Scripts/Items/Weapons/Axes/Pickaxe.cs @@ -0,0 +1,58 @@ +using System; +using Server.Items; +using Server.Network; +using Server.Engines.Harvest; + +namespace Server.Items +{ + [FlipableAttribute( 0xE86, 0xE85 )] + public class Pickaxe : BaseAxe + { + public override HarvestSystem HarvestSystem{ get{ return Mining.System; } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 35; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 1; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 35; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 60; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash1H; } } + + [Constructable] + public Pickaxe() : base( 0xE86 ) + { + Weight = 11.0; + UsesRemaining = 50; + ShowUsesRemaining = true; + } + + public Pickaxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + ShowUsesRemaining = true; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/TwoHandedAxe.cs b/Scripts/Items/Weapons/Axes/TwoHandedAxe.cs new file mode 100644 index 000000000..a6ae7fc91 --- /dev/null +++ b/Scripts/Items/Weapons/Axes/TwoHandedAxe.cs @@ -0,0 +1,50 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x1443, 0x1442 )] + public class TwoHandedAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 16; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 31; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 39; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 90; } } + + [Constructable] + public TwoHandedAxe() : base( 0x1443 ) + { + Weight = 8.0; + } + + public TwoHandedAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Axes/WarAxe.cs b/Scripts/Items/Weapons/Axes/WarAxe.cs new file mode 100644 index 000000000..1c3f3334a --- /dev/null +++ b/Scripts/Items/Weapons/Axes/WarAxe.cs @@ -0,0 +1,60 @@ +using System; +using Server.Items; +using Server.Network; +using Server.Engines.Harvest; + +namespace Server.Items +{ + [FlipableAttribute( 0x13B0, 0x13AF )] + public class WarAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.BleedAttack; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 33; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 9; } } + public override int OldMaxDamage{ get{ return 27; } } + public override int OldSpeed{ get{ return 40; } } + + public override int DefHitSound{ get{ return 0x233; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + public override SkillName DefSkill{ get{ return SkillName.Macing; } } + public override WeaponType DefType{ get{ return WeaponType.Bashing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Bash1H; } } + + public override HarvestSystem HarvestSystem{ get{ return null; } } + + [Constructable] + public WarAxe() : base( 0x13B0 ) + { + Weight = 8.0; + } + + public WarAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/BaseMeleeWeapon.cs b/Scripts/Items/Weapons/BaseMeleeWeapon.cs new file mode 100644 index 000000000..1358db4ca --- /dev/null +++ b/Scripts/Items/Weapons/BaseMeleeWeapon.cs @@ -0,0 +1,65 @@ +using System; +using Server; +using Server.Items; +using Server.Engines.Harvest; + +namespace Server.Items +{ + public abstract class BaseMeleeWeapon : BaseWeapon + { + public BaseMeleeWeapon( int itemID ) : base( itemID ) + { + } + + public BaseMeleeWeapon( Serial serial ) : base( serial ) + { + } + + public override int AbsorbDamage( Mobile attacker, Mobile defender, int damage ) + { + damage = base.AbsorbDamage( attacker, defender, damage ); + + if ( Core.AOS ) + return damage; + + int absorb = defender.MeleeDamageAbsorb; + + if ( absorb > 0 ) + { + if ( absorb > damage ) + { + int react = damage / 5; + + if ( react <= 0 ) + react = 1; + + defender.MeleeDamageAbsorb -= damage; + damage = 0; + + attacker.Damage( react, defender ); + + attacker.PlaySound( 0x1F1 ); + attacker.FixedEffect( 0x374A, 10, 16 ); + } + else + { + defender.MeleeDamageAbsorb = 0; + defender.SendLocalizedMessage( 1005556 ); // Your reactive armor spell has been nullified. + DefensiveSpell.Nullify( defender ); + } + } + + return damage; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/BaseWeapon.cs b/Scripts/Items/Weapons/BaseWeapon.cs new file mode 100644 index 000000000..138c4c38c --- /dev/null +++ b/Scripts/Items/Weapons/BaseWeapon.cs @@ -0,0 +1,3457 @@ +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 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 + 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 list = new List(); + + 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 ); + + 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 attrs = new List(); + + 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 ) + { + 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 + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Fists.cs b/Scripts/Items/Weapons/Fists.cs new file mode 100644 index 000000000..0517784a0 --- /dev/null +++ b/Scripts/Items/Weapons/Fists.cs @@ -0,0 +1,289 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class Fists : BaseMeleeWeapon + { + public static void Initialize() + { + Mobile.DefaultWeapon = new Fists(); + + EventSink.DisarmRequest += new DisarmRequestEventHandler( EventSink_DisarmRequest ); + EventSink.StunRequest += new StunRequestEventHandler( EventSink_StunRequest ); + } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Disarm; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 0; } } + public override int AosMinDamage{ get{ return 1; } } + public override int AosMaxDamage{ get{ return 4; } } + public override int AosSpeed{ get{ return 50; } } + + public override int OldStrengthReq{ get{ return 0; } } + public override int OldMinDamage{ get{ return 1; } } + public override int OldMaxDamage{ get{ return 8; } } + public override int OldSpeed{ get{ return 30; } } + + public override int DefHitSound{ get{ return -1; } } + public override int DefMissSound{ get{ return -1; } } + + public override SkillName DefSkill{ get{ return SkillName.Wrestling; } } + public override WeaponType DefType{ get{ return WeaponType.Fists; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Wrestle; } } + + public Fists() : base( 0 ) + { + Visible = false; + Movable = false; + Quality = WeaponQuality.Regular; + } + + public Fists( Serial serial ) : base( serial ) + { + } + + public override double GetDefendSkillValue( Mobile attacker, Mobile defender ) + { + double wresValue = defender.Skills[SkillName.Wrestling].Value; + double anatValue = defender.Skills[SkillName.Anatomy].Value; + double evalValue = defender.Skills[SkillName.EvalInt].Value; + double incrValue = (anatValue + evalValue + 20.0) * 0.5; + + if ( incrValue > 120.0 ) + incrValue = 120.0; + + if ( wresValue > incrValue ) + return wresValue; + else + return incrValue; + } + + public override TimeSpan OnSwing( Mobile attacker, Mobile defender ) + { + if ( attacker.StunReady ) + { + if ( attacker.CanBeginAction( typeof( Fists ) ) ) + { + if ( attacker.Skills[SkillName.Anatomy].Value >= 80.0 && attacker.Skills[SkillName.Wrestling].Value >= 80.0 ) + { + if ( attacker.Stam >= 15 ) + { + attacker.Stam -= 15; + + if ( CheckMove( attacker, SkillName.Anatomy ) ) + { + StartMoveDelay( attacker ); + + attacker.StunReady = false; + + attacker.SendLocalizedMessage( 1004013 ); // You successfully stun your opponent! + defender.SendLocalizedMessage( 1004014 ); // You have been stunned! + + defender.Freeze( TimeSpan.FromSeconds( 4.0 ) ); + } + else + { + attacker.SendLocalizedMessage( 1004010 ); // You failed in your attempt to stun. + defender.SendLocalizedMessage( 1004011 ); // Your opponent tried to stun you and failed. + } + } + else + { + attacker.SendLocalizedMessage( 1004009 ); // You are too fatigued to attempt anything. + } + } + else + { + attacker.SendLocalizedMessage( 1004008 ); // You are not skilled enough to stun your opponent. + attacker.StunReady = false; + } + } + } + else if ( attacker.DisarmReady ) + { + if ( attacker.CanBeginAction( typeof( Fists ) ) ) + { + if ( defender.Player || defender.Body.IsHuman ) + { + if ( attacker.Skills[SkillName.ArmsLore].Value >= 80.0 && attacker.Skills[SkillName.Wrestling].Value >= 80.0 ) + { + if ( attacker.Stam >= 15 ) + { + Item toDisarm = defender.FindItemOnLayer( Layer.OneHanded ); + + if ( toDisarm == null || !toDisarm.Movable ) + toDisarm = defender.FindItemOnLayer( Layer.TwoHanded ); + + Container pack = defender.Backpack; + + if ( pack == null || toDisarm == null || !toDisarm.Movable ) + { + attacker.SendLocalizedMessage( 1004001 ); // You cannot disarm your opponent. + } + else if ( CheckMove( attacker, SkillName.ArmsLore ) ) + { + StartMoveDelay( attacker ); + + attacker.Stam -= 15; + attacker.DisarmReady = false; + + attacker.SendLocalizedMessage( 1004006 ); // You successfully disarm your opponent! + defender.SendLocalizedMessage( 1004007 ); // You have been disarmed! + + pack.DropItem( toDisarm ); + } + else + { + attacker.Stam -= 15; + + attacker.SendLocalizedMessage( 1004004 ); // You failed in your attempt to disarm. + defender.SendLocalizedMessage( 1004005 ); // Your opponent tried to disarm you but failed. + } + } + else + { + attacker.SendLocalizedMessage( 1004003 ); // You are too fatigued to attempt anything. + } + } + else + { + attacker.SendLocalizedMessage( 1004002 ); // You are not skilled enough to disarm your opponent. + attacker.DisarmReady = false; + } + } + else + { + attacker.SendLocalizedMessage( 1004001 ); // You cannot disarm your opponent. + } + } + } + + return base.OnSwing( attacker, defender ); + } + + /*public override void OnMiss( Mobile attacker, Mobile defender ) + { + base.PlaySwingAnimation( attacker ); + }*/ + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + + /* Wrestling moves */ + + private static bool CheckMove( Mobile m, SkillName other ) + { + double wresValue = m.Skills[SkillName.Wrestling].Value; + double scndValue = m.Skills[other].Value; + + /* 40% chance at 80, 80 + * 50% chance at 100, 100 + * 60% chance at 120, 120 + */ + + double chance = (wresValue + scndValue) / 400.0; + + return ( chance >= Utility.RandomDouble() ); + } + + private static bool HasFreeHands( Mobile m ) + { + Item item = m.FindItemOnLayer( Layer.OneHanded ); + + if ( item != null && !(item is Spellbook) ) + return false; + + return m.FindItemOnLayer( Layer.TwoHanded ) == null; + } + + private static void EventSink_DisarmRequest( DisarmRequestEventArgs e ) + { + Mobile m = e.Mobile; + + double armsValue = m.Skills[SkillName.ArmsLore].Value; + double wresValue = m.Skills[SkillName.Wrestling].Value; + + if ( !HasFreeHands( m ) ) + { + m.SendLocalizedMessage( 1004029 ); // You must have your hands free to attempt to disarm your opponent. + m.DisarmReady = false; + } + else if ( armsValue >= 80.0 && wresValue >= 80.0 ) + { + m.DisruptiveAction(); + m.DisarmReady = !m.DisarmReady; + m.SendLocalizedMessage( m.DisarmReady ? 1019013 : 1019014 ); + } + else + { + m.SendLocalizedMessage( 1004002 ); // You are not skilled enough to disarm your opponent. + m.DisarmReady = false; + } + } + + private static void EventSink_StunRequest( StunRequestEventArgs e ) + { + Mobile m = e.Mobile; + + double anatValue = m.Skills[SkillName.Anatomy].Value; + double wresValue = m.Skills[SkillName.Wrestling].Value; + + if ( !HasFreeHands( m ) ) + { + m.SendLocalizedMessage( 1004031 ); // You must have your hands free to attempt to stun your opponent. + m.StunReady = false; + } + else if ( anatValue >= 80.0 && wresValue >= 80.0 ) + { + m.DisruptiveAction(); + m.StunReady = !m.StunReady; + m.SendLocalizedMessage( m.StunReady ? 1019011 : 1019012 ); + } + else + { + m.SendLocalizedMessage( 1004008 ); // You are not skilled enough to stun your opponent. + m.StunReady = false; + } + } + + private class MoveDelayTimer : Timer + { + private Mobile m_Mobile; + + public MoveDelayTimer( Mobile m ) : base( TimeSpan.FromSeconds( 10.0 ) ) + { + m_Mobile = m; + + Priority = TimerPriority.TwoFiftyMS; + + m_Mobile.BeginAction( typeof( Fists ) ); + } + + protected override void OnTick() + { + m_Mobile.EndAction( typeof( Fists ) ); + } + } + + private static void StartMoveDelay( Mobile m ) + { + new MoveDelayTimer( m ).Start(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/HitLower.cs b/Scripts/Items/Weapons/HitLower.cs new file mode 100644 index 000000000..40cbda58d --- /dev/null +++ b/Scripts/Items/Weapons/HitLower.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Items +{ + public class HitLower + { + public static readonly TimeSpan AttackEffectDuration = TimeSpan.FromSeconds( 10.0 ); + public static readonly TimeSpan DefenseEffectDuration = TimeSpan.FromSeconds( 8.0 ); + + private static Hashtable m_AttackTable = new Hashtable(); + + public static bool IsUnderAttackEffect( Mobile m ) + { + return m_AttackTable.Contains( m ); + } + + public static bool ApplyAttack( Mobile m ) + { + if ( IsUnderAttackEffect( m ) ) + return false; + + m_AttackTable[m] = new AttackTimer( m ); + m.SendLocalizedMessage( 1062319 ); // Your attack chance has been reduced! + return true; + } + + private static void RemoveAttack( Mobile m ) + { + m_AttackTable.Remove( m ); + m.SendLocalizedMessage( 1062320 ); // Your attack chance has returned to normal. + } + + private class AttackTimer : Timer + { + private Mobile m_Player; + + public AttackTimer( Mobile player ) : base( AttackEffectDuration ) + { + m_Player = player; + + Priority = TimerPriority.TwoFiftyMS; + + Start(); + } + + protected override void OnTick() + { + RemoveAttack( m_Player ); + } + } + + private static Hashtable m_DefenseTable = new Hashtable(); + + public static bool IsUnderDefenseEffect( Mobile m ) + { + return m_DefenseTable.Contains( m ); + } + + public static bool ApplyDefense( Mobile m ) + { + if ( IsUnderDefenseEffect( m ) ) + return false; + + m_DefenseTable[m] = new DefenseTimer( m ); + m.SendLocalizedMessage( 1062318 ); // Your defense chance has been reduced! + return true; + } + + private static void RemoveDefense( Mobile m ) + { + m_DefenseTable.Remove( m ); + m.SendLocalizedMessage( 1062321 ); // Your defense chance has returned to normal. + } + + private class DefenseTimer : Timer + { + private Mobile m_Player; + + public DefenseTimer( Mobile player ) : base( DefenseEffectDuration ) + { + m_Player = player; + + Priority = TimerPriority.TwoFiftyMS; + + Start(); + } + + protected override void OnTick() + { + RemoveDefense( m_Player ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Knives/BaseKnife.cs b/Scripts/Items/Weapons/Knives/BaseKnife.cs new file mode 100644 index 000000000..724646ac0 --- /dev/null +++ b/Scripts/Items/Weapons/Knives/BaseKnife.cs @@ -0,0 +1,59 @@ +using System; +using Server; +using Server.Items; +using Server.Targets; + +namespace Server.Items +{ + public abstract class BaseKnife : BaseMeleeWeapon + { + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override SkillName DefSkill{ get{ return SkillName.Swords; } } + public override WeaponType DefType{ get{ return WeaponType.Slashing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash1H; } } + + public BaseKnife( int itemID ) : base( itemID ) + { + } + + public BaseKnife( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 1010018 ); // What do you want to use this item on? + + from.Target = new BladedItemTarget( this ); + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + if ( !Core.AOS && Poison != null && PoisonCharges > 0 ) + { + --PoisonCharges; + + if ( Utility.RandomDouble() >= 0.5 ) // 50% chance to poison + defender.ApplyPoison( attacker, Poison ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Knives/ButcherKnife.cs b/Scripts/Items/Weapons/Knives/ButcherKnife.cs new file mode 100644 index 000000000..7916bfadf --- /dev/null +++ b/Scripts/Items/Weapons/Knives/ButcherKnife.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13F6, 0x13F7 )] + public class ButcherKnife : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 5; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 49; } } + + public override int OldStrengthReq{ get{ return 5; } } + public override int OldMinDamage{ get{ return 2; } } + public override int OldMaxDamage{ get{ return 14; } } + public override int OldSpeed{ get{ return 40; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 40; } } + + [Constructable] + public ButcherKnife() : base( 0x13F6 ) + { + Weight = 1.0; + } + + public ButcherKnife( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Knives/Cleaver.cs b/Scripts/Items/Weapons/Knives/Cleaver.cs new file mode 100644 index 000000000..4fafa1e0a --- /dev/null +++ b/Scripts/Items/Weapons/Knives/Cleaver.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xEC3, 0xEC2 )] + public class Cleaver : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + + public override int AosStrengthReq{ get{ return 10; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 46; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 2; } } + public override int OldMaxDamage{ get{ return 13; } } + public override int OldSpeed{ get{ return 40; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 50; } } + + [Constructable] + public Cleaver() : base( 0xEC3 ) + { + Weight = 2.0; + } + + public Cleaver( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Knives/Dagger.cs b/Scripts/Items/Weapons/Knives/Dagger.cs new file mode 100644 index 000000000..7e8f02f72 --- /dev/null +++ b/Scripts/Items/Weapons/Knives/Dagger.cs @@ -0,0 +1,55 @@ +using System; +using Server.Network; +using Server.Targeting; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF52, 0xF51 )] + public class Dagger : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + + public override int AosStrengthReq{ get{ return 10; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 56; } } + + public override int OldStrengthReq{ get{ return 1; } } + public override int OldMinDamage{ get{ return 3; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 55; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 40; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Dagger() : base( 0xF52 ) + { + Weight = 1.0; + } + + public Dagger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Knives/SkinningKnife.cs b/Scripts/Items/Weapons/Knives/SkinningKnife.cs new file mode 100644 index 000000000..2e2d9c84f --- /dev/null +++ b/Scripts/Items/Weapons/Knives/SkinningKnife.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xEC4, 0xEC5 )] + public class SkinningKnife : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 5; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 49; } } + + public override int OldStrengthReq{ get{ return 5; } } + public override int OldMinDamage{ get{ return 1; } } + public override int OldMaxDamage{ get{ return 10; } } + public override int OldSpeed{ get{ return 40; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 40; } } + + [Constructable] + public SkinningKnife() : base( 0xEC4 ) + { + Weight = 1.0; + } + + public SkinningKnife( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Knives/ThrowingDagger.cs b/Scripts/Items/Weapons/Knives/ThrowingDagger.cs new file mode 100644 index 000000000..459075acc --- /dev/null +++ b/Scripts/Items/Weapons/Knives/ThrowingDagger.cs @@ -0,0 +1,125 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0xF52, 0xF51 )] + public class ThrowingDagger : Item + { + public override string DefaultName + { + get { return "a throwing dagger"; } + } + + [Constructable] + public ThrowingDagger() : base( 0xF52 ) + { + Weight = 1.0; + Layer = Layer.OneHanded; + } + + public ThrowingDagger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.Items.Contains( this ) ) + { + InternalTarget t = new InternalTarget( this ); + from.Target = t; + } + else + { + from.SendMessage( "You must be holding that weapon to use it." ); + } + } + + private class InternalTarget : Target + { + private ThrowingDagger m_Dagger; + + public InternalTarget( ThrowingDagger dagger ) : base( 10, false, TargetFlags.Harmful ) + { + m_Dagger = dagger; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Dagger.Deleted ) + { + return; + } + else if ( !from.Items.Contains( m_Dagger ) ) + { + from.SendMessage( "You must be holding that weapon to use it." ); + } + else if ( targeted is Mobile ) + { + Mobile m = (Mobile)targeted; + + if ( m != from && from.HarmfulCheck( m ) ) + { + Direction to = from.GetDirectionTo( m ); + + from.Direction = to; + + from.Animate( from.Mounted ? 26 : 9, 7, 1, true, false, 0 ); + + if ( Utility.RandomDouble() >= (Math.Sqrt( m.Dex / 100.0 ) * 0.8) ) + { + from.MovingEffect( m, 0x1BFE, 7, 1, false, false, 0x481, 0 ); + + AOS.Damage( m, from, Utility.Random( 5, from.Str / 10 ), 100, 0, 0, 0, 0 ); + + m_Dagger.MoveToWorld( m.Location, m.Map ); + } + else + { + int x = 0, y = 0; + + switch ( to & Direction.Mask ) + { + case Direction.North: --y; break; + case Direction.South: ++y; break; + case Direction.West: --x; break; + case Direction.East: ++x; break; + case Direction.Up: --x; --y; break; + case Direction.Down: ++x; ++y; break; + case Direction.Left: --x; ++y; break; + case Direction.Right: ++x; --y; break; + } + + x += Utility.Random( -1, 3 ); + y += Utility.Random( -1, 3 ); + + x += m.X; + y += m.Y; + + m_Dagger.MoveToWorld( new Point3D( x, y, m.Z ), m.Map ); + + from.MovingEffect( m_Dagger, 0x1BFE, 7, 1, false, false, 0x481, 0 ); + + from.SendMessage( "You miss." ); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs new file mode 100644 index 000000000..b2c2a4f8e --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/BlightGrippedLongbow.cs @@ -0,0 +1,40 @@ +using System; +using Server.Items; + +namespace Server.Items +{ + public class BlightGrippedLongbow : ElvenCompositeLongbow + { + public override int LabelNumber{ get{ return 1072907; } } // Blight Gripped Longbow + + [Constructable] + public BlightGrippedLongbow() + { + Hue = 0x8A4; + + WeaponAttributes.HitPoisonArea = 20; + Attributes.RegenStam = 3; + Attributes.NightSight = 1; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 35; + } + + public BlightGrippedLongbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs new file mode 100644 index 000000000..2716c99bf --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/ColdForgedBlade.cs @@ -0,0 +1,47 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class ColdForgedBlade : ElvenSpellblade + { + public override int LabelNumber{ get{ return 1072916; } } // Cold Forged Blade + + [Constructable] + public ColdForgedBlade() + { + WeaponAttributes.HitHarm = 40; + Attributes.SpellChanneling = 1; + Attributes.NightSight = 1; + Attributes.WeaponSpeed = 25; + Attributes.WeaponDamage = 50; + + Hue = this.GetElementalDamageHue(); + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = pois = nrgy = 0; + cold = 100; + } + + public ColdForgedBlade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs new file mode 100644 index 000000000..21df7610a --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/LuminousRuneBlade.cs @@ -0,0 +1,47 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class LuminousRuneBlade : RuneBlade + { + public override int LabelNumber{ get{ return 1072922; } } // Luminous Rune Blade + + [Constructable] + public LuminousRuneBlade() + { + WeaponAttributes.HitLightning = 40; + WeaponAttributes.SelfRepair = 5; + Attributes.NightSight = 1; + Attributes.WeaponSpeed = 25; + Attributes.WeaponDamage = 55; + + Hue = this.GetElementalDamageHue(); + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = 0; + nrgy = 100; + } + + public LuminousRuneBlade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs new file mode 100644 index 000000000..ae877997f --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/OverseerSunderedBlade.cs @@ -0,0 +1,49 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class OverseerSunderedBlade : RadiantScimitar + { + public override int LabelNumber{ get{ return 1072920; } } // Overseer Sundered Blade + + [Constructable] + public OverseerSunderedBlade() + { + ItemID = 0x2D27; + Hue = 0x485; + + Attributes.RegenStam = 2; + Attributes.AttackChance = 10; + Attributes.WeaponSpeed = 35; + Attributes.WeaponDamage = 45; + + Hue = this.GetElementalDamageHue(); + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = cold = pois = nrgy = 0; + fire = 100; + } + + public OverseerSunderedBlade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs new file mode 100644 index 000000000..7b6beea28 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/PhantomStaff.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class PhantomStaff : WildStaff + { + public override int LabelNumber{ get{ return 1072919; } } // Phantom Staff + + [Constructable] + public PhantomStaff() + { + Hue = 0x1; + Attributes.RegenHits = 2; + Attributes.NightSight = 1; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 60; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = nrgy = 0; + cold = pois = 50; + } + + public PhantomStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs new file mode 100644 index 000000000..0f7fd5a90 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/RuneCarvingKnife.cs @@ -0,0 +1,41 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class RuneCarvingKnife : AssassinSpike + { + public override int LabelNumber{ get{ return 1072915; } } // Rune Carving Knife + + [Constructable] + public RuneCarvingKnife() + { + Hue = 0x48D; + + WeaponAttributes.HitLeechMana = 40; + Attributes.RegenStam = 2; + Attributes.LowerManaCost = 10; + Attributes.WeaponSpeed = 35; + Attributes.WeaponDamage = 30; + } + + public RuneCarvingKnife( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs new file mode 100644 index 000000000..43452eb2f --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/ShardTrasher.cs @@ -0,0 +1,41 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class ShardThrasher : DiamondMace + { + public override int LabelNumber{ get{ return 1072918; } } // Shard Thrasher + + [Constructable] + public ShardThrasher() + { + Hue = 0x4F2; + + WeaponAttributes.HitPhysicalArea = 30; + Attributes.BonusStam = 8; + Attributes.AttackChance = 10; + Attributes.WeaponSpeed = 35; + Attributes.WeaponDamage = 40; + } + + public ShardThrasher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs new file mode 100644 index 000000000..88bb775d5 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/SilvanisFeywoodBow.cs @@ -0,0 +1,46 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class SilvanisFeywoodBow : ElvenCompositeLongbow + { + public override int LabelNumber{ get{ return 1072955; } } // Silvani's Feywood Bow + + [Constructable] + public SilvanisFeywoodBow() + { + Hue = 0x1A; + + Attributes.SpellChanneling = 1; + Attributes.AttackChance = 12; + Attributes.WeaponSpeed = 30; + Attributes.WeaponDamage = 35; + } + + public override void GetDamageTypes( Mobile wielder, out int phys, out int fire, out int cold, out int pois, out int nrgy ) + { + phys = fire = cold = pois = 0; + nrgy = 100; + } + + public SilvanisFeywoodBow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs b/Scripts/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs new file mode 100644 index 000000000..1f5d410d3 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Artifacts/TheNightReaper.cs @@ -0,0 +1,41 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class TheNightReaper : RepeatingCrossbow + { + public override int LabelNumber{ get{ return 1072912; } } // The Night Reaper + + [Constructable] + public TheNightReaper() + { + ItemID = 0x26CD; + Hue = 0x41C; + + Slayer = SlayerName.Exorcism; + Attributes.NightSight = 1; + Attributes.WeaponSpeed = 25; + Attributes.WeaponDamage = 55; + } + + public TheNightReaper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/AssassinSpike.cs b/Scripts/Items/Weapons/ML Weapons/AssassinSpike.cs new file mode 100644 index 000000000..561f36b07 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/AssassinSpike.cs @@ -0,0 +1,52 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D21, 0x2D2D )] + public class AssassinSpike : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + + public override int AosStrengthReq{ get{ return 15; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 12; } } + public override int AosSpeed{ get{ return 50; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 12; } } + public override int OldSpeed{ get{ return 50; } } + + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } // TODO + public override int InitMaxHits{ get{ return 60; } } // TODO + + [Constructable] + public AssassinSpike() : base( 0x2D21 ) + { + Weight = 4.0; + } + + public AssassinSpike( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/DiamondMace.cs b/Scripts/Items/Weapons/ML Weapons/DiamondMace.cs new file mode 100644 index 000000000..d7595ab87 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/DiamondMace.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D24, 0x2D30 )] + public class DiamondMace : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 37; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 14; } } + public override int OldMaxDamage{ get{ return 17; } } + public override int OldSpeed{ get{ return 37; } } + + public override int InitMinHits{ get{ return 30; } } // TODO + public override int InitMaxHits{ get{ return 60; } } // TODO + + [Constructable] + public DiamondMace() : base( 0x2D24 ) + { + Weight = 10.0; + } + + public DiamondMace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs b/Scripts/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs new file mode 100644 index 000000000..4cf88e610 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D1E, 0x2D2A )] + public class ElvenCompositeLongbow : BaseRanged + { + public override int EffectID{ get{ return 0xF42; } } + public override Type AmmoType{ get{ return typeof( Arrow ); } } + public override Item Ammo{ get{ return new Arrow(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ForceArrow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.SerpentArrow; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 19; } } + public override int AosMaxDamage{ get{ return 22; } } + public override int AosSpeed{ get{ return 27; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 19; } } + public override int OldMaxDamage{ get{ return 22; } } + public override int OldSpeed{ get{ return 27; } } + + public override int DefMaxRange{ get{ return 10; } } + + public override int InitMinHits{ get{ return 41; } } + public override int InitMaxHits{ get{ return 90; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.ShootBow; } } + + [Constructable] + public ElvenCompositeLongbow() : base( 0x2D1E ) + { + Weight = 8.0; + } + + public ElvenCompositeLongbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/ElvenMachete.cs b/Scripts/Items/Weapons/ML Weapons/ElvenMachete.cs new file mode 100644 index 000000000..ef67007f1 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/ElvenMachete.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D35, 0x2D29 )] + public class ElvenMachete : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DefenseMastery; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Bladeweave; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 41; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 13; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 41; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public ElvenMachete() : base( 0x2D35 ) + { + Weight = 6.0; + } + + public ElvenMachete( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/ElvenSpellblade.cs b/Scripts/Items/Weapons/ML Weapons/ElvenSpellblade.cs new file mode 100644 index 000000000..5c8b932c9 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/ElvenSpellblade.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D20, 0x2D2C )] + public class ElvenSpellblade : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.PsychicAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.BleedAttack; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 12; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 44; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 12; } } + public override int OldMaxDamage{ get{ return 14; } } + public override int OldSpeed{ get{ return 44; } } + + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } // TODO + public override int InitMaxHits{ get{ return 60; } } // TODO + + [Constructable] + public ElvenSpellblade() : base( 0x2D20 ) + { + Weight = 5.0; + Layer = Layer.TwoHanded; + } + + public ElvenSpellblade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/Leafblade.cs b/Scripts/Items/Weapons/ML Weapons/Leafblade.cs new file mode 100644 index 000000000..0db6c0be9 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/Leafblade.cs @@ -0,0 +1,52 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D22, 0x2D2E )] + public class Leafblade : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Feint; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 42; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 13; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 42; } } + + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } // TODO + public override int InitMaxHits{ get{ return 60; } } // TODO + + [Constructable] + public Leafblade() : base( 0x2D22 ) + { + Weight = 8.0; + } + + public Leafblade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/MagicalShortbow.cs b/Scripts/Items/Weapons/ML Weapons/MagicalShortbow.cs new file mode 100644 index 000000000..614b25121 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/MagicalShortbow.cs @@ -0,0 +1,56 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D2B, 0x2D1F )] + public class MagicalShortbow : BaseRanged + { + public override int EffectID{ get{ return 0xF42; } } + public override Type AmmoType{ get{ return typeof( Arrow ); } } + public override Item Ammo{ get{ return new Arrow(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.LightningArrow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.PsychicAttack; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 38; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 14; } } + public override int OldMaxDamage{ get{ return 16; } } + public override int OldSpeed{ get{ return 38; } } + + public override int DefMaxRange{ get{ return 10; } } + + public override int InitMinHits{ get{ return 41; } } + public override int InitMaxHits{ get{ return 90; } } + + [Constructable] + public MagicalShortbow() : base( 0x2D2B ) + { + Weight = 6.0; + } + + public MagicalShortbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/OrnateAxe.cs b/Scripts/Items/Weapons/ML Weapons/OrnateAxe.cs new file mode 100644 index 000000000..fb3782e40 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/OrnateAxe.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D28, 0x2D34 )] + public class OrnateAxe : BaseAxe + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Disarm; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 18; } } + public override int AosMaxDamage{ get{ return 20; } } + public override int AosSpeed{ get{ return 26; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 18; } } + public override int OldMaxDamage{ get{ return 20; } } + public override int OldSpeed{ get{ return 26; } } + + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public OrnateAxe() : base( 0x2D28 ) + { + Weight = 12.0; + Layer = Layer.TwoHanded; + } + + public OrnateAxe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/RadiantScimitar.cs b/Scripts/Items/Weapons/ML Weapons/RadiantScimitar.cs new file mode 100644 index 000000000..02a43da3a --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/RadiantScimitar.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D33, 0x2D27 )] + public class RadiantScimitar : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Bladeweave; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 12; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 43; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 12; } } + public override int OldMaxDamage{ get{ return 14; } } + public override int OldSpeed{ get{ return 43; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public RadiantScimitar() : base( 0x2D33 ) + { + Weight = 9.0; + } + + public RadiantScimitar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/RuneBlade.cs b/Scripts/Items/Weapons/ML Weapons/RuneBlade.cs new file mode 100644 index 000000000..1b0d5ce8d --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/RuneBlade.cs @@ -0,0 +1,54 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D32, 0x2D26 )] + public class RuneBlade : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Disarm; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Bladeweave; } } + + public override int AosStrengthReq{ get{ return 30; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 35; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 15; } } + public override int OldMaxDamage{ get{ return 17; } } + public override int OldSpeed{ get{ return 35; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public RuneBlade() : base( 0x2D32 ) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } + + public RuneBlade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/WarCleaver.cs b/Scripts/Items/Weapons/ML Weapons/WarCleaver.cs new file mode 100644 index 000000000..cb9e1fc69 --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/WarCleaver.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D2F, 0x2D23 )] + public class WarCleaver : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Disarm; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Bladeweave; } } + + public override int AosStrengthReq{ get{ return 15; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 48; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 9; } } + public override int OldMaxDamage{ get{ return 11; } } + public override int OldSpeed{ get{ return 48; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 30; } } // TODO + public override int InitMaxHits{ get{ return 60; } } // TODO + + [Constructable] + public WarCleaver() : base( 0x2D2F ) + { + Weight = 10.0; + } + + public WarCleaver( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/ML Weapons/WildStaff.cs b/Scripts/Items/Weapons/ML Weapons/WildStaff.cs new file mode 100644 index 000000000..6f7fbccee --- /dev/null +++ b/Scripts/Items/Weapons/ML Weapons/WildStaff.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x2D25, 0x2D31 )] + public class WildStaff : BaseStaff + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Block; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ForceOfNature; } } + + public override int AosStrengthReq{ get{ return 15; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 12; } } + public override int AosSpeed{ get{ return 48; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 12; } } + public override int OldSpeed{ get{ return 48; } } + + public override int InitMinHits{ get{ return 30; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public WildStaff() : base( 0x2D25 ) + { + Weight = 8.0; + } + + public WildStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/BaseBashing.cs b/Scripts/Items/Weapons/Maces/BaseBashing.cs new file mode 100644 index 000000000..ca2edcf1b --- /dev/null +++ b/Scripts/Items/Weapons/Maces/BaseBashing.cs @@ -0,0 +1,60 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public abstract class BaseBashing : BaseMeleeWeapon + { + public override int DefHitSound{ get{ return 0x233; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override SkillName DefSkill{ get{ return SkillName.Macing; } } + public override WeaponType DefType{ get{ return WeaponType.Bashing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Bash1H; } } + + public BaseBashing( int itemID ) : base( itemID ) + { + } + + public BaseBashing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + defender.Stam -= Utility.Random( 3, 3 ); // 3-5 points of stamina loss + } + + public override double GetBaseDamage( Mobile attacker ) + { + double damage = base.GetBaseDamage( attacker ); + + if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + { + damage *= 1.5; + + attacker.SendMessage( "You deliver a crushing blow!" ); // Is this not localized? + attacker.PlaySound( 0x11C ); + } + + return damage; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/Club.cs b/Scripts/Items/Weapons/Maces/Club.cs new file mode 100644 index 000000000..5d935f761 --- /dev/null +++ b/Scripts/Items/Weapons/Maces/Club.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13b4, 0x13b3 )] + public class Club : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Dismount; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 44; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 8; } } + public override int OldMaxDamage{ get{ return 24; } } + public override int OldSpeed{ get{ return 40; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 40; } } + + [Constructable] + public Club() : base( 0x13B4 ) + { + Weight = 9.0; + } + + public Club( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/FireworksWand.cs b/Scripts/Items/Weapons/Maces/FireworksWand.cs new file mode 100644 index 000000000..52990d13f --- /dev/null +++ b/Scripts/Items/Weapons/Maces/FireworksWand.cs @@ -0,0 +1,138 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class FireworksWand : MagicWand + { + public override int LabelNumber{ get{ return 1041424; } } // a fireworks wand + + private int m_Charges; + + [CommandProperty( AccessLevel.GameMaster )] + public int Charges + { + get{ return m_Charges; } + set{ m_Charges = value; InvalidateProperties(); } + } + + [Constructable] + public FireworksWand() : this( 100 ) + { + } + + [Constructable] + public FireworksWand( int charges ) + { + m_Charges = charges; + LootType = LootType.Blessed; + } + + public FireworksWand( Serial serial ) : base( serial ) + { + } + + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + + list.Add( 1060741, m_Charges.ToString() ); // charges: ~1_val~ + } + + public override void OnDoubleClick( Mobile from ) + { + BeginLaunch( from, true ); + } + + public void BeginLaunch( Mobile from, bool useCharges ) + { + Map map = from.Map; + + if ( map == null || map == Map.Internal ) + return; + + if ( useCharges ) + { + if ( Charges > 0 ) + { + --Charges; + } + else + { + from.SendLocalizedMessage( 502412 ); // There are no charges left on that item. + return; + } + } + + from.SendLocalizedMessage( 502615 ); // You launch a firework! + + Point3D ourLoc = GetWorldLocation(); + + Point3D startLoc = new Point3D( ourLoc.X, ourLoc.Y, ourLoc.Z + 10 ); + Point3D endLoc = new Point3D( startLoc.X + Utility.RandomMinMax( -2, 2 ), startLoc.Y + Utility.RandomMinMax( -2, 2 ), startLoc.Z + 32 ); + + Effects.SendMovingEffect( new Entity( Serial.Zero, startLoc, map ), new Entity( Serial.Zero, endLoc, map ), + 0x36E4, 5, 0, false, false ); + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( FinishLaunch ), new object[]{ from, endLoc, map } ); + } + + private void FinishLaunch( object state ) + { + object[] states = (object[])state; + + Mobile from = (Mobile)states[0]; + Point3D endLoc = (Point3D)states[1]; + Map map = (Map)states[2]; + + int hue = Utility.Random( 40 ); + + if ( hue < 8 ) + hue = 0x66D; + else if ( hue < 10 ) + hue = 0x482; + else if ( hue < 12 ) + hue = 0x47E; + else if ( hue < 16 ) + hue = 0x480; + else if ( hue < 20 ) + hue = 0x47F; + else + hue = 0; + + if ( Utility.RandomBool() ) + hue = Utility.RandomList( 0x47E, 0x47F, 0x480, 0x482, 0x66D ); + + int renderMode = Utility.RandomList( 0, 2, 3, 4, 5, 7 ); + + Effects.PlaySound( endLoc, map, Utility.Random( 0x11B, 4 ) ); + Effects.SendLocationEffect( endLoc, map, 0x373A + (0x10 * Utility.Random( 4 )), 16, 10, hue, renderMode ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Charges ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Charges = reader.ReadInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/HammerPick.cs b/Scripts/Items/Weapons/Maces/HammerPick.cs new file mode 100644 index 000000000..6d121f888 --- /dev/null +++ b/Scripts/Items/Weapons/Maces/HammerPick.cs @@ -0,0 +1,51 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x143D, 0x143C )] + public class HammerPick : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 28; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 33; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public HammerPick() : base( 0x143D ) + { + Weight = 9.0; + Layer = Layer.OneHanded; + } + + public HammerPick( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/Mace.cs b/Scripts/Items/Weapons/Maces/Mace.cs new file mode 100644 index 000000000..254d3037d --- /dev/null +++ b/Scripts/Items/Weapons/Maces/Mace.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF5C, 0xF5D )] + public class Mace : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 12; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 40; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 8; } } + public override int OldMaxDamage{ get{ return 32; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public Mace() : base( 0xF5C ) + { + Weight = 14.0; + } + + public Mace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/MagicWand.cs b/Scripts/Items/Weapons/Maces/MagicWand.cs new file mode 100644 index 000000000..c3dc66748 --- /dev/null +++ b/Scripts/Items/Weapons/Maces/MagicWand.cs @@ -0,0 +1,46 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class MagicWand : BaseBashing + { + public override int AosStrengthReq{ get{ return 5; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 40; } } + + public override int OldStrengthReq{ get{ return 0; } } + public override int OldMinDamage{ get{ return 2; } } + public override int OldMaxDamage{ get{ return 6; } } + public override int OldSpeed{ get{ return 35; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public MagicWand() : base( 0xDF2 ) + { + Weight = 1.0; + } + + public MagicWand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/Maul.cs b/Scripts/Items/Weapons/Maces/Maul.cs new file mode 100644 index 000000000..0152098b2 --- /dev/null +++ b/Scripts/Items/Weapons/Maces/Maul.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x143B, 0x143A )] + public class Maul : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 32; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 30; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public Maul() : base( 0x143B ) + { + Weight = 10.0; + } + + public Maul( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 14.0 ) + Weight = 10.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/Scepter.cs b/Scripts/Items/Weapons/Maces/Scepter.cs new file mode 100644 index 000000000..4998442fc --- /dev/null +++ b/Scripts/Items/Weapons/Maces/Scepter.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26BC, 0x26C6 )] + public class Scepter : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 30; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 14; } } + public override int OldMaxDamage{ get{ return 17; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public Scepter() : base( 0x26BC ) + { + Weight = 8.0; + } + + public Scepter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/WarHammer.cs b/Scripts/Items/Weapons/Maces/WarHammer.cs new file mode 100644 index 000000000..44a67af61 --- /dev/null +++ b/Scripts/Items/Weapons/Maces/WarHammer.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1439, 0x1438 )] + public class WarHammer : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + + public override int AosStrengthReq{ get{ return 95; } } + public override int AosMinDamage{ get{ return 17; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 28; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 8; } } + public override int OldMaxDamage{ get{ return 36; } } + public override int OldSpeed{ get{ return 31; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Bash2H; } } + + [Constructable] + public WarHammer() : base( 0x1439 ) + { + Weight = 10.0; + Layer = Layer.TwoHanded; + } + + public WarHammer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Maces/WarMace.cs b/Scripts/Items/Weapons/Maces/WarMace.cs new file mode 100644 index 000000000..feafd2944 --- /dev/null +++ b/Scripts/Items/Weapons/Maces/WarMace.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1407, 0x1406 )] + public class WarMace : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.BleedAttack; } } + + public override int AosStrengthReq{ get{ return 80; } } + public override int AosMinDamage{ get{ return 16; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 26; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 30; } } + public override int OldSpeed{ get{ return 32; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public WarMace() : base( 0x1407 ) + { + Weight = 17.0; + } + + public WarMace( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/PoleArms/Bardiche.cs b/Scripts/Items/Weapons/PoleArms/Bardiche.cs new file mode 100644 index 000000000..131c8dd81 --- /dev/null +++ b/Scripts/Items/Weapons/PoleArms/Bardiche.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF4D, 0xF4E )] + public class Bardiche : BasePoleArm + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Dismount; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 17; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 28; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 43; } } + public override int OldSpeed{ get{ return 26; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 100; } } + + [Constructable] + public Bardiche() : base( 0xF4D ) + { + Weight = 7.0; + } + + public Bardiche( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs b/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs new file mode 100644 index 000000000..052710572 --- /dev/null +++ b/Scripts/Items/Weapons/PoleArms/BasePoleArm.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Engines.Harvest; +using Server.ContextMenus; + +namespace Server.Items +{ + public abstract class BasePoleArm : BaseMeleeWeapon, IUsesRemaining + { + public override int DefHitSound{ get{ return 0x237; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override SkillName DefSkill{ get{ return SkillName.Swords; } } + public override WeaponType DefType{ get{ return WeaponType.Polearm; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash2H; } } + + public virtual HarvestSystem HarvestSystem{ get{ return Lumberjacking.System; } } + + private int m_UsesRemaining; + private bool m_ShowUsesRemaining; + + [CommandProperty( AccessLevel.GameMaster )] + public int UsesRemaining + { + get { return m_UsesRemaining; } + set { m_UsesRemaining = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ShowUsesRemaining + { + get { return m_ShowUsesRemaining; } + set { m_ShowUsesRemaining = value; InvalidateProperties(); } + } + + public BasePoleArm( int itemID ) : base( itemID ) + { + m_UsesRemaining = 150; + } + + public BasePoleArm( Serial serial ) : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( HarvestSystem == null ) + return; + + if ( IsChildOf( from.Backpack ) || Parent == from ) + HarvestSystem.BeginHarvesting( from, this ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( HarvestSystem != null ) + BaseHarvestTool.AddContextMenuEntries( from, this, list, HarvestSystem ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + writer.Write( (bool) m_ShowUsesRemaining ); + + writer.Write( (int) m_UsesRemaining ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + m_ShowUsesRemaining = reader.ReadBool(); + goto case 1; + } + case 1: + { + m_UsesRemaining = reader.ReadInt(); + goto case 0; + } + case 0: + { + if ( m_UsesRemaining < 1 ) + m_UsesRemaining = 150; + + break; + } + } + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + if ( !Core.AOS && (attacker.Player || attacker.Body.IsHuman) && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + { + StatMod mod = defender.GetStatMod( "Concussion" ); + + if ( mod == null ) + { + defender.SendMessage( "You receive a concussion blow!" ); + defender.AddStatMod( new StatMod( StatType.Int, "Concussion", -(defender.RawInt / 2), TimeSpan.FromSeconds( 30.0 ) ) ); + + attacker.SendMessage( "You deliver a concussion blow!" ); + attacker.PlaySound( 0x11C ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/PoleArms/Halberd.cs b/Scripts/Items/Weapons/PoleArms/Halberd.cs new file mode 100644 index 000000000..7a62380c4 --- /dev/null +++ b/Scripts/Items/Weapons/PoleArms/Halberd.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x143E, 0x143F )] + public class Halberd : BasePoleArm + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + + public override int AosStrengthReq{ get{ return 95; } } + public override int AosMinDamage{ get{ return 18; } } + public override int AosMaxDamage{ get{ return 19; } } + public override int AosSpeed{ get{ return 25; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 49; } } + public override int OldSpeed{ get{ return 25; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public Halberd() : base( 0x143E ) + { + Weight = 16.0; + } + + public Halberd( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/PoleArms/Scythe.cs b/Scripts/Items/Weapons/PoleArms/Scythe.cs new file mode 100644 index 000000000..a0229aff9 --- /dev/null +++ b/Scripts/Items/Weapons/PoleArms/Scythe.cs @@ -0,0 +1,56 @@ +using System; +using Server.Network; +using Server.Items; +using Server.Engines.Harvest; + +namespace Server.Items +{ + [FlipableAttribute( 0x26BA, 0x26C4 )] + public class Scythe : BasePoleArm + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 32; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 15; } } + public override int OldMaxDamage{ get{ return 18; } } + public override int OldSpeed{ get{ return 32; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 100; } } + + public override HarvestSystem HarvestSystem{ get{ return null; } } + + [Constructable] + public Scythe() : base( 0x26BA ) + { + Weight = 5.0; + } + + public Scythe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 15.0 ) + Weight = 5.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/BaseRanged.cs b/Scripts/Items/Weapons/Ranged/BaseRanged.cs new file mode 100644 index 000000000..07a0e3f71 --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/BaseRanged.cs @@ -0,0 +1,141 @@ +using System; +using Server.Items; +using Server.Network; +using Server.Spells; +using Server.Mobiles; + +namespace Server.Items +{ + public abstract class BaseRanged : BaseMeleeWeapon + { + public abstract int EffectID{ get; } + public abstract Type AmmoType{ get; } + public abstract Item Ammo{ get; } + + public override int DefHitSound{ get{ return 0x234; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override SkillName DefSkill{ get{ return SkillName.Archery; } } + public override WeaponType DefType{ get{ return WeaponType.Ranged; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.ShootXBow; } } + + public override SkillName AccuracySkill{ get{ return SkillName.Archery; } } + + public BaseRanged( int itemID ) : base( itemID ) + { + } + + public BaseRanged( Serial serial ) : base( serial ) + { + } + + public override TimeSpan OnSwing( Mobile attacker, Mobile defender ) + { + WeaponAbility a = WeaponAbility.GetCurrentAbility( attacker ); + + // Make sure we've been standing still for .25/.5/1 second depending on Era + if ( DateTime.Now > (attacker.LastMoveTime + TimeSpan.FromSeconds( Core.SE ? 0.25 : (Core.AOS ? 0.5 : 1.0) )) || (Core.AOS && WeaponAbility.GetCurrentAbility( attacker ) is MovingShot) ) + { + 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(); + attacker.Send( new Swing( 0, attacker, defender ) ); + + if ( OnFired( attacker, defender ) ) + { + if ( CheckHit( attacker, defender ) ) + OnHit( attacker, defender ); + else + OnMiss( attacker, defender ); + } + } + + attacker.RevealingAction(); + + return GetDelay( attacker ); + } + else + { + attacker.RevealingAction(); + + return TimeSpan.FromSeconds( 0.25 ); + } + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + if ( attacker.Player && !defender.Player && (defender.Body.IsAnimal || defender.Body.IsMonster) && 0.4 >= Utility.RandomDouble() ) + defender.AddToBackpack( Ammo ); + + base.OnHit( attacker, defender ); + } + + public override void OnMiss( Mobile attacker, Mobile defender ) + { + if ( attacker.Player && 0.4 >= Utility.RandomDouble() ) + Ammo.MoveToWorld( new Point3D( defender.X + Utility.RandomMinMax( -1, 1 ), defender.Y + Utility.RandomMinMax( -1, 1 ), defender.Z ), defender.Map ); + + base.OnMiss( attacker, defender ); + } + + public virtual bool OnFired( Mobile attacker, Mobile defender ) + { + Container pack = attacker.Backpack; + + if ( attacker.Player && (pack == null || !pack.ConsumeTotal( AmmoType, 1 )) ) + return false; + + attacker.MovingEffect( defender, EffectID, 18, 1, false, false ); + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + case 1: + { + break; + } + case 0: + { + /*m_EffectID =*/ reader.ReadInt(); + break; + } + } + + if ( version < 2 ) + { + WeaponAttributes.MageWeapon = 0; + WeaponAttributes.UseBestSkill = 0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/Bow.cs b/Scripts/Items/Weapons/Ranged/Bow.cs new file mode 100644 index 000000000..99d676eb1 --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/Bow.cs @@ -0,0 +1,62 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13B2, 0x13B1 )] + public class Bow : BaseRanged + { + public override int EffectID{ get{ return 0xF42; } } + public override Type AmmoType{ get{ return typeof( Arrow ); } } + public override Item Ammo{ get{ return new Arrow(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 30; } } + public override int AosMinDamage{ get{ return 16; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 25; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 9; } } + public override int OldMaxDamage{ get{ return 41; } } + public override int OldSpeed{ get{ return 20; } } + + public override int DefMaxRange{ get{ return 10; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 60; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.ShootBow; } } + + [Constructable] + public Bow() : base( 0x13B2 ) + { + Weight = 6.0; + Layer = Layer.TwoHanded; + } + + public Bow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 7.0 ) + Weight = 6.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/CompositeBow.cs b/Scripts/Items/Weapons/Ranged/CompositeBow.cs new file mode 100644 index 000000000..0bc7c448c --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/CompositeBow.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26C2, 0x26CC )] + public class CompositeBow : BaseRanged + { + public override int EffectID{ get{ return 0xF42; } } + public override Type AmmoType{ get{ return typeof( Arrow ); } } + public override Item Ammo{ get{ return new Arrow(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MovingShot; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 25; } } + + public override int OldStrengthReq{ get{ return 45; } } + public override int OldMinDamage{ get{ return 15; } } + public override int OldMaxDamage{ get{ return 17; } } + public override int OldSpeed{ get{ return 25; } } + + public override int DefMaxRange{ get{ return 10; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.ShootBow; } } + + [Constructable] + public CompositeBow() : base( 0x26C2 ) + { + Weight = 5.0; + } + + public CompositeBow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/Crossbow.cs b/Scripts/Items/Weapons/Ranged/Crossbow.cs new file mode 100644 index 000000000..65c09c9f0 --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/Crossbow.cs @@ -0,0 +1,57 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF50, 0xF4F )] + public class Crossbow : BaseRanged + { + public override int EffectID{ get{ return 0x1BFE; } } + public override Type AmmoType{ get{ return typeof( Bolt ); } } + public override Item Ammo{ get{ return new Bolt(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 18; } } + public override int AosMaxDamage{ get{ return 20; } } + public override int AosSpeed{ get{ return 24; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 8; } } + public override int OldMaxDamage{ get{ return 43; } } + public override int OldSpeed{ get{ return 18; } } + + public override int DefMaxRange{ get{ return 8; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public Crossbow() : base( 0xF50 ) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } + + public Crossbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/HeavyCrossbow.cs b/Scripts/Items/Weapons/Ranged/HeavyCrossbow.cs new file mode 100644 index 000000000..5f594f329 --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/HeavyCrossbow.cs @@ -0,0 +1,57 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13FD, 0x13FC )] + public class HeavyCrossbow : BaseRanged + { + public override int EffectID{ get{ return 0x1BFE; } } + public override Type AmmoType{ get{ return typeof( Bolt ); } } + public override Item Ammo{ get{ return new Bolt(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.MovingShot; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Dismount; } } + + public override int AosStrengthReq{ get{ return 80; } } + public override int AosMinDamage{ get{ return 19; } } + public override int AosMaxDamage{ get{ return 20; } } + public override int AosSpeed{ get{ return 22; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 11; } } + public override int OldMaxDamage{ get{ return 56; } } + public override int OldSpeed{ get{ return 10; } } + + public override int DefMaxRange{ get{ return 8; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 100; } } + + [Constructable] + public HeavyCrossbow() : base( 0x13FD ) + { + Weight = 9.0; + Layer = Layer.TwoHanded; + } + + public HeavyCrossbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/JukaBow.cs b/Scripts/Items/Weapons/Ranged/JukaBow.cs new file mode 100644 index 000000000..80ecb4c43 --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/JukaBow.cs @@ -0,0 +1,97 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13B2, 0x13B1 )] + public class JukaBow : Bow + { + public override int AosStrengthReq{ get{ return 80; } } + public override int AosDexterityReq{ get{ return 80; } } + + public override int OldStrengthReq{ get{ return 80; } } + public override int OldDexterityReq{ get{ return 80; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsModified + { + get{ return ( Hue == 0x453 ); } + } + + [Constructable] + public JukaBow() + { + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsModified ) + { + from.SendMessage( "That has already been modified." ); + } + else if ( !IsChildOf( from.Backpack ) ) + { + from.SendMessage( "This must be in your backpack to modify it." ); + } + else if ( from.Skills[SkillName.Fletching].Base < 100.0 ) + { + from.SendMessage( "Only a grandmaster bowcrafter can modify this weapon." ); + } + else + { + from.BeginTarget( 2, false, Targeting.TargetFlags.None, new TargetCallback( OnTargetGears ) ); + from.SendMessage( "Select the gears you wish to use." ); + } + } + + public void OnTargetGears( Mobile from, object targ ) + { + Gears g = targ as Gears; + + if ( g == null || !g.IsChildOf( from.Backpack ) ) + { + from.SendMessage( "Those are not gears." ); // Apparently gears that aren't in your backpack aren't really gears at all. :-( + } + else if ( IsModified ) + { + from.SendMessage( "That has already been modified." ); + } + else if ( !IsChildOf( from.Backpack ) ) + { + from.SendMessage( "This must be in your backpack to modify it." ); + } + else if ( from.Skills[SkillName.Fletching].Base < 100.0 ) + { + from.SendMessage( "Only a grandmaster bowcrafter can modify this weapon." ); + } + else + { + g.Consume(); + + Hue = 0x453; + Slayer = (SlayerName)Utility.Random( 2, 25 ); + + from.SendMessage( "You modify it." ); + } + } + + public JukaBow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Ranged/RepeatingCrossbow.cs b/Scripts/Items/Weapons/Ranged/RepeatingCrossbow.cs new file mode 100644 index 000000000..9bf190a96 --- /dev/null +++ b/Scripts/Items/Weapons/Ranged/RepeatingCrossbow.cs @@ -0,0 +1,56 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26C3, 0x26CD )] + public class RepeatingCrossbow : BaseRanged + { + public override int EffectID{ get{ return 0x1BFE; } } + public override Type AmmoType{ get{ return typeof( Bolt ); } } + public override Item Ammo{ get{ return new Bolt(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MovingShot; } } + + public override int AosStrengthReq{ get{ return 30; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 12; } } + public override int AosSpeed{ get{ return 41; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 12; } } + public override int OldSpeed{ get{ return 41; } } + + public override int DefMaxRange{ get{ return 7; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public RepeatingCrossbow() : base( 0x26C3 ) + { + Weight = 6.0; + } + + public RepeatingCrossbow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Bokuto.cs b/Scripts/Items/Weapons/SE Weapons/Bokuto.cs new file mode 100644 index 000000000..939f9d0fc --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Bokuto.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A8, 0x27F3 )] + public class Bokuto : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Feint; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.NerveStrike; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 53; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 9; } } + public override int OldMaxDamage{ get{ return 11; } } + public override int OldSpeed{ get{ return 53; } } + + public override int DefHitSound{ get{ return 0x536; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 25; } } + public override int InitMaxHits{ get{ return 50; } } + + [Constructable] + public Bokuto() : base( 0x27A8 ) + { + Weight = 7.0; + } + + public Bokuto( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Daisho.cs b/Scripts/Items/Weapons/SE Weapons/Daisho.cs new file mode 100644 index 000000000..736c8a8af --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Daisho.cs @@ -0,0 +1,54 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A9, 0x27F4 )] + public class Daisho : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Feint; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 40; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 13; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 40; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 65; } } + + [Constructable] + public Daisho() : base( 0x27A9 ) + { + Weight = 8.0; + Layer = Layer.TwoHanded; + } + + public Daisho( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Kama.cs b/Scripts/Items/Weapons/SE Weapons/Kama.cs new file mode 100644 index 000000000..ecd7adb27 --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Kama.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27AD, 0x27F8 )] + public class Kama : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.DefenseMastery; } } + + public override int AosStrengthReq{ get{ return 15; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 55; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 9; } } + public override int OldMaxDamage{ get{ return 11; } } + public override int OldSpeed{ get{ return 55; } } + + public override int DefHitSound{ get{ return 0x232; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 60; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Kama() : base( 0x27AD ) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } + + public Kama( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Lajatang.cs b/Scripts/Items/Weapons/SE Weapons/Lajatang.cs new file mode 100644 index 000000000..07afb40fe --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Lajatang.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A7, 0x27F2 )] + public class Lajatang : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DefenseMastery; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.FrenziedWhirlwind; } } + + public override int AosStrengthReq{ get{ return 65; } } + public override int AosMinDamage{ get{ return 16; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 32; } } + + public override int OldStrengthReq{ get{ return 65; } } + public override int OldMinDamage{ get{ return 16; } } + public override int OldMaxDamage{ get{ return 18; } } + public override int OldSpeed{ get{ return 55; } } + + public override int DefHitSound{ get{ return 0x232; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 90; } } + public override int InitMaxHits{ get{ return 95; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Lajatang() : base( 0x27A7 ) + { + Weight = 12.0; + Layer = Layer.TwoHanded; + } + + public Lajatang( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/NoDachi.cs b/Scripts/Items/Weapons/SE Weapons/NoDachi.cs new file mode 100644 index 000000000..572a6e767 --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/NoDachi.cs @@ -0,0 +1,54 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A2, 0x27ED )] + public class NoDachi : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.RidingSwipe; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 16; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 35; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 16; } } + public override int OldMaxDamage{ get{ return 18; } } + public override int OldSpeed{ get{ return 35; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 90; } } + + [Constructable] + public NoDachi() : base( 0x27A2 ) + { + Weight = 10.0; + Layer = Layer.TwoHanded; + } + + public NoDachi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Nunchaku.cs b/Scripts/Items/Weapons/SE Weapons/Nunchaku.cs new file mode 100644 index 000000000..93796f359 --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Nunchaku.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27AE, 0x27F9 )] + public class Nunchaku : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Block; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Feint; } } + + public override int AosStrengthReq{ get{ return 15; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 47; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 11; } } + public override int OldMaxDamage{ get{ return 13; } } + public override int OldSpeed{ get{ return 47; } } + + public override int DefHitSound{ get{ return 0x535; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override int InitMinHits{ get{ return 40; } } + public override int InitMaxHits{ get{ return 55; } } + + [Constructable] + public Nunchaku() : base( 0x27AE ) + { + Weight = 5.0; + } + + public Nunchaku( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Sai.cs b/Scripts/Items/Weapons/SE Weapons/Sai.cs new file mode 100644 index 000000000..ccfb15fc3 --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Sai.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27AF, 0x27FA )] + public class Sai : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Block; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ArmorPierce; } } + + public override int AosStrengthReq{ get{ return 15; } } + public override int AosMinDamage{ get{ return 9; } } + public override int AosMaxDamage{ get{ return 11; } } + public override int AosSpeed{ get{ return 55; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 9; } } + public override int OldMaxDamage{ get{ return 11; } } + public override int OldSpeed{ get{ return 55; } } + + public override int DefHitSound{ get{ return 0x23C; } } + public override int DefMissSound{ get{ return 0x232; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 60; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Sai() : base( 0x27AF ) + { + Weight = 7.0; + Layer = Layer.TwoHanded; + } + + public Sai( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Tekagi.cs b/Scripts/Items/Weapons/SE Weapons/Tekagi.cs new file mode 100644 index 000000000..57d53c478 --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Tekagi.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27Ab, 0x27F6 )] + public class Tekagi : BaseKnife + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DualWield; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.TalonStrike; } } + + public override int AosStrengthReq{ get{ return 10; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 12; } } + public override int AosSpeed{ get{ return 53; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 12; } } + public override int OldSpeed{ get{ return 53; } } + + public override int DefHitSound{ get{ return 0x238; } } + public override int DefMissSound{ get{ return 0x232; } } + + public override int InitMinHits{ get{ return 35; } } + public override int InitMaxHits{ get{ return 60; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Tekagi() : base( 0x27AB ) + { + Weight = 5.0; + Layer = Layer.TwoHanded; + } + + public Tekagi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Tessen.cs b/Scripts/Items/Weapons/SE Weapons/Tessen.cs new file mode 100644 index 000000000..58cd26b2e --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Tessen.cs @@ -0,0 +1,56 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A3, 0x27EE )] + public class Tessen : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Feint; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Block; } } + + public override int AosStrengthReq{ get{ return 10; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 12; } } + public override int AosSpeed{ get{ return 50; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 12; } } + public override int OldSpeed{ get{ return 50; } } + + public override int DefHitSound{ get{ return 0x232; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 60; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Bash2H; } } + + [Constructable] + public Tessen() : base( 0x27A3 ) + { + Weight = 6.0; + Layer = Layer.TwoHanded; + } + + public Tessen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Tetsubo.cs b/Scripts/Items/Weapons/SE Weapons/Tetsubo.cs new file mode 100644 index 000000000..d2ed71ceb --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Tetsubo.cs @@ -0,0 +1,56 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A6, 0x27F1 )] + public class Tetsubo : BaseBashing + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.FrenziedWhirlwind; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 12; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 45; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 12; } } + public override int OldMaxDamage{ get{ return 14; } } + public override int OldSpeed{ get{ return 45; } } + + public override int DefHitSound{ get{ return 0x233; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 60; } } + public override int InitMaxHits{ get{ return 65; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Bash2H; } } + + [Constructable] + public Tetsubo() : base( 0x27A6 ) + { + Weight = 8.0; + Layer = Layer.TwoHanded; + } + + public Tetsubo( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Wakizashi.cs b/Scripts/Items/Weapons/SE Weapons/Wakizashi.cs new file mode 100644 index 000000000..dc7bd6119 --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Wakizashi.cs @@ -0,0 +1,54 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A4, 0x27EF )] + public class Wakizashi : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.FrenziedWhirlwind; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 44; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 11; } } + public override int OldMaxDamage{ get{ return 13; } } + public override int OldSpeed{ get{ return 44; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 45; } } + public override int InitMaxHits{ get{ return 50; } } + + [Constructable] + public Wakizashi() : base( 0x27A4 ) + { + Weight = 5.0; + Layer = Layer.OneHanded; + } + + public Wakizashi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SE Weapons/Yumi.cs b/Scripts/Items/Weapons/SE Weapons/Yumi.cs new file mode 100644 index 000000000..278c3120e --- /dev/null +++ b/Scripts/Items/Weapons/SE Weapons/Yumi.cs @@ -0,0 +1,62 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x27A5, 0x27F0 )] + public class Yumi : BaseRanged + { + public override int EffectID{ get{ return 0xF42; } } + public override Type AmmoType{ get{ return typeof( Arrow ); } } + public override Item Ammo{ get{ return new Arrow(); } } + + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorPierce; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.DoubleShot; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 18; } } + public override int AosMaxDamage{ get{ return 20; } } + public override int AosSpeed{ get{ return 25; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 18; } } + public override int OldMaxDamage{ get{ return 20; } } + public override int OldSpeed{ get{ return 25; } } + + public override int DefMaxRange{ get{ return 10; } } + + public override int InitMinHits{ get{ return 55; } } + public override int InitMaxHits{ get{ return 60; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.ShootBow; } } + + [Constructable] + public Yumi() : base( 0x27A5 ) + { + Weight = 9.0; + Layer = Layer.TwoHanded; + } + + public Yumi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 7.0 ) + Weight = 6.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SlayerEntry.cs b/Scripts/Items/Weapons/SlayerEntry.cs new file mode 100644 index 000000000..598c6deb2 --- /dev/null +++ b/Scripts/Items/Weapons/SlayerEntry.cs @@ -0,0 +1,108 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Items +{ + public class SlayerEntry + { + private SlayerGroup m_Group; + private SlayerName m_Name; + private Type[] m_Types; + + public SlayerGroup Group{ get{ return m_Group; } set{ m_Group = value; } } + public SlayerName Name{ get{ return m_Name; } } + public Type[] Types{ get{ return m_Types; } } + + private static int[] m_AosTitles = new int[] + { + 1060479, // undead slayer + 1060470, // orc slayer + 1060480, // troll slayer + 1060468, // ogre slayer + 1060472, // repond slayer + 1060462, // dragon slayer + 1060478, // terathan slayer + 1060475, // snake slayer + 1060467, // lizardman slayer + 1060473, // reptile slayer + 1060460, // demon slayer + 1060466, // gargoyle slayer + 1017396, // Balron Damnation + 1060461, // demon slayer + 1060469, // ophidian slayer + 1060477, // spider slayer + 1060474, // scorpion slayer + 1060458, // arachnid slayer + 1060465, // fire elemental slayer + 1060481, // water elemental slayer + 1060457, // air elemental slayer + 1060471, // poison elemental slayer + 1060463, // earth elemental slayer + 1060459, // blood elemental slayer + 1060476, // snow elemental slayer + 1060464, // elemental slayer + 1070855 // fey slayer + }; + + private static int[] m_OldTitles = new int[] + { + 1017384, // Silver + 1017385, // Orc Slaying + 1017386, // Troll Slaughter + 1017387, // Ogre Thrashing + 1017388, // Repond + 1017389, // Dragon Slaying + 1017390, // Terathan + 1017391, // Snake's Bane + 1017392, // Lizardman Slaughter + 1017393, // Reptilian Death + 1017394, // Daemon Dismissal + 1017395, // Gargoyle's Foe + 1017396, // Balron Damnation + 1017397, // Exorcism + 1017398, // Ophidian + 1017399, // Spider's Death + 1017400, // Scorpion's Bane + 1017401, // Arachnid Doom + 1017402, // Flame Dousing + 1017403, // Water Dissipation + 1017404, // Vacuum + 1017405, // Elemental Health + 1017406, // Earth Shatter + 1017407, // Blood Drinking + 1017408, // Summer Wind + 1017409, // Elemental Ban + 1070855 // fey slayer + }; + + public int Title + { + get + { + int[] titles = ( Core.AOS ? m_AosTitles : m_OldTitles ); + + return titles[(int)m_Name - 1]; + } + } + + public SlayerEntry( SlayerName name, params Type[] types ) + { + m_Name = name; + m_Types = types; + } + + public bool Slays( Mobile m ) + { + Type t = m.GetType(); + + for ( int i = 0; i < m_Types.Length; ++i ) + { + if ( m_Types[i] == t ) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SlayerGroup.cs b/Scripts/Items/Weapons/SlayerGroup.cs new file mode 100644 index 000000000..7583fbc85 --- /dev/null +++ b/Scripts/Items/Weapons/SlayerGroup.cs @@ -0,0 +1,207 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Items +{ + public class SlayerGroup + { + private static SlayerEntry[] m_TotalEntries; + private static SlayerGroup[] m_Groups; + + public static SlayerEntry[] TotalEntries + { + get{ return m_TotalEntries; } + } + + public static SlayerGroup[] Groups + { + get{ return m_Groups; } + } + + public static SlayerEntry GetEntryByName( SlayerName name ) + { + int v = (int)name; + + if ( v >= 0 && v < m_TotalEntries.Length ) + return m_TotalEntries[v]; + + return null; + } + + public static SlayerName GetLootSlayerType( Type type ) + { + for ( int i = 0; i < m_Groups.Length; ++i ) + { + SlayerGroup group = m_Groups[i]; + Type[] foundOn = group.FoundOn; + + bool inGroup = false; + + for ( int j = 0; foundOn != null && !inGroup && j < foundOn.Length; ++j ) + inGroup = ( foundOn[j] == type ); + + if ( inGroup ) + { + int index = Utility.Random( 1 + group.Entries.Length ); + + if ( index == 0 ) + return group.m_Super.Name; + + return group.Entries[index - 1].Name; + } + } + + return SlayerName.Silver; + } + + static SlayerGroup() + { + SlayerGroup humanoid = new SlayerGroup(); + SlayerGroup undead = new SlayerGroup(); + SlayerGroup elemental = new SlayerGroup(); + SlayerGroup abyss = new SlayerGroup(); + SlayerGroup arachnid = new SlayerGroup(); + SlayerGroup reptilian = new SlayerGroup(); + SlayerGroup fey = new SlayerGroup(); + + humanoid.Opposition = new SlayerGroup[]{ undead }; + humanoid.FoundOn = new Type[]{ typeof( BoneKnight ), typeof( Lich ), typeof( LichLord ) }; + humanoid.Super = new SlayerEntry( SlayerName.Repond, typeof( ArcticOgreLord ), typeof( Cyclops ), typeof( Ettin ), typeof( EvilMage ), typeof( EvilMageLord ), typeof( FrostTroll ), typeof( MeerCaptain ), typeof( MeerEternal ), typeof( MeerMage ), typeof( MeerWarrior ), typeof( Ogre ), typeof( OgreLord ), typeof( Orc ), typeof( OrcBomber ), typeof( OrcBrute ), typeof( OrcCaptain ), /*typeof( OrcChopper ), typeof( OrcScout ),*/ typeof( OrcishLord ), typeof( OrcishMage ), typeof( Ratman ), typeof( RatmanArcher ), typeof( RatmanMage ), typeof( SavageRider ), typeof( SavageShaman ), typeof( Savage ), typeof( Titan ), typeof( Troll ) ); + humanoid.Entries = new SlayerEntry[] + { + new SlayerEntry( SlayerName.OgreTrashing, typeof( Ogre ), typeof( OgreLord ), typeof( ArcticOgreLord ) ), + new SlayerEntry( SlayerName.OrcSlaying, typeof( Orc ), typeof( OrcBomber ), typeof( OrcBrute ), typeof( OrcCaptain ),/* typeof( OrcChopper ), typeof( OrcScout ),*/ typeof( OrcishLord ), typeof( OrcishMage ) ), + new SlayerEntry( SlayerName.TrollSlaughter, typeof( Troll ), typeof( FrostTroll ) ) + }; + + undead.Opposition = new SlayerGroup[]{ humanoid }; + undead.Super = new SlayerEntry( SlayerName.Silver, typeof( AncientLich ), typeof( Bogle ), typeof( BoneKnight ), typeof( BoneMagi ),/* typeof( DarkGuardian ), */typeof( DarknightCreeper ), typeof( FleshGolem ), typeof( Ghoul ), typeof( GoreFiend ), typeof( HellSteed ), typeof( LadyOfTheSnow ), typeof( Lich ), typeof( LichLord ), typeof( Mummy ), typeof( Revenant ), typeof( RevenantLion ), typeof( RottingCorpse ), typeof( Shade ), typeof( ShadowKnight ), typeof( SkeletalKnight ), typeof( SkeletalMage ), typeof( SkeletalMount ), typeof( Skeleton ), typeof( Spectre ), typeof( Wraith ), typeof( Zombie ) ); + undead.Entries = new SlayerEntry[0]; + + fey.Opposition = new SlayerGroup[]{ abyss }; + fey.Super = new SlayerEntry( SlayerName.Fey, typeof( Centaur ), typeof( EtherealWarrior ), typeof( Kirin ), typeof( LordOaks ), typeof( Pixie ), typeof( Silvani ), typeof( Treefellow ), typeof( Unicorn ), typeof( Wisp ) ); + fey.Entries = new SlayerEntry[0]; + + elemental.Opposition = new SlayerGroup[]{ abyss }; + elemental.FoundOn = new Type[]{ typeof( Balron ), typeof( Daemon ) }; + elemental.Super = new SlayerEntry( SlayerName.ElementalBan,/* typeof( AcidElemental ),*/ typeof( AgapiteElemental ), typeof( AirElemental ), typeof( SummonedAirElemental ), typeof( BloodElemental ), typeof( BronzeElemental ), typeof( CopperElemental ), typeof( CrystalElemental ), typeof( DullCopperElemental ), typeof( EarthElemental ), typeof( SummonedEarthElemental ), typeof( Efreet ), typeof( FireElemental ), typeof( SummonedFireElemental ), typeof( GoldenElemental ), typeof( IceElemental ), typeof( KazeKemono ), typeof( PoisonElemental ), typeof( RaiJu ), typeof( SandVortex ), typeof( ShadowIronElemental ), typeof( SnowElemental ), typeof( ValoriteElemental ), typeof( VeriteElemental ), typeof( WaterElemental ), typeof( SummonedWaterElemental ) ); + elemental.Entries = new SlayerEntry[] + { + new SlayerEntry( SlayerName.BloodDrinking, typeof( BloodElemental ) ), + new SlayerEntry( SlayerName.EarthShatter, typeof( AgapiteElemental ), typeof( BronzeElemental ), typeof( CopperElemental ), typeof( DullCopperElemental ), typeof( EarthElemental ), typeof( SummonedEarthElemental ), typeof( GoldenElemental ), typeof( ShadowIronElemental ), typeof( ValoriteElemental ), typeof( VeriteElemental ) ), + new SlayerEntry( SlayerName.ElementalHealth, typeof( PoisonElemental ) ), + new SlayerEntry( SlayerName.FlameDousing, typeof( FireElemental ), typeof( SummonedFireElemental ) ), + new SlayerEntry( SlayerName.SummerWind, typeof( SnowElemental ), typeof( IceElemental ) ), + new SlayerEntry( SlayerName.Vacuum, typeof( AirElemental ), typeof( SummonedAirElemental ) ), + new SlayerEntry( SlayerName.WaterDissipation, typeof( WaterElemental ), typeof( SummonedWaterElemental ) ) + }; + + abyss.Opposition = new SlayerGroup[]{ elemental, fey }; + abyss.FoundOn = new Type[]{ typeof( BloodElemental ) }; + + if( Core.AOS ) + { + abyss.Super = new SlayerEntry( SlayerName.Exorcism, typeof( AbysmalHorror ), typeof( ArcaneDaemon ), typeof( Balron ), typeof( BoneDemon ), typeof( ChaosDaemon ), typeof( Daemon ), typeof( SummonedDaemon ), typeof( DemonKnight ), typeof( Devourer ), typeof( EnslavedGargoyle ), typeof( FanDancer ), typeof( FireGargoyle ), typeof( Gargoyle ), typeof( GargoyleDestroyer ), typeof( GargoyleEnforcer ), typeof( Gibberling ), typeof( HordeMinion ), typeof( IceFiend ), typeof( Imp ), typeof( Impaler ), typeof( Moloch ), typeof( Oni ), typeof( Ravager ), typeof( Semidar ), typeof( StoneGargoyle ), typeof( Succubus ), typeof( TsukiWolf ) ); + + abyss.Entries = new SlayerEntry[] + { + // Daemon Dismissal & Balron Damnation have been removed and moved up to super slayer on OSI. + new SlayerEntry( SlayerName.GargoylesFoe, typeof( EnslavedGargoyle ), typeof( FireGargoyle ), typeof( Gargoyle ), typeof( GargoyleDestroyer ), typeof( GargoyleEnforcer ), typeof( StoneGargoyle ) ), + }; + } + else + { + abyss.Super = new SlayerEntry( SlayerName.Exorcism, typeof( AbysmalHorror ), typeof( Balron ), typeof( BoneDemon ), typeof( ChaosDaemon ), typeof( Daemon ), typeof( SummonedDaemon ), typeof( DemonKnight ), typeof( Devourer ), typeof( Gargoyle ), typeof( FireGargoyle ), typeof( Gibberling ), typeof( HordeMinion ), typeof( IceFiend ), typeof( Imp ), typeof( Impaler ), typeof( Ravager ), typeof( StoneGargoyle ), typeof( ArcaneDaemon ), typeof( EnslavedGargoyle ), typeof( GargoyleDestroyer ), typeof( GargoyleEnforcer ), typeof( Moloch ) ); + + abyss.Entries = new SlayerEntry[] + { + new SlayerEntry( SlayerName.DaemonDismissal, typeof( AbysmalHorror ), typeof( Balron ), typeof( BoneDemon ), typeof( ChaosDaemon ), typeof( Daemon ), typeof( SummonedDaemon ), typeof( DemonKnight ), typeof( Devourer ), typeof( Gibberling ), typeof( HordeMinion ), typeof( IceFiend ), typeof( Imp ), typeof( Impaler ), typeof( Ravager ), typeof( ArcaneDaemon ), typeof( Moloch ) ), + new SlayerEntry( SlayerName.GargoylesFoe, typeof( FireGargoyle ), typeof( Gargoyle ), typeof( StoneGargoyle ), typeof( EnslavedGargoyle ), typeof( GargoyleDestroyer ), typeof( GargoyleEnforcer ) ), + new SlayerEntry( SlayerName.BalronDamnation, typeof( Balron ) ) + }; + } + + arachnid.Opposition = new SlayerGroup[]{ reptilian }; + arachnid.FoundOn = new Type[]{ typeof( AncientWyrm ), typeof( Dragon ), typeof( OphidianMatriarch ), typeof( ShadowWyrm ) }; + arachnid.Super = new SlayerEntry( SlayerName.ArachnidDoom, typeof( DreadSpider ), typeof( FrostSpider ), typeof( GiantBlackWidow ), typeof( GiantSpider ), typeof( Mephitis ), typeof( Scorpion ), typeof( TerathanAvenger ), typeof( TerathanDrone ), typeof( TerathanMatriarch ), typeof( TerathanWarrior ) ); + arachnid.Entries = new SlayerEntry[] + { + new SlayerEntry( SlayerName.ScorpionsBane, typeof( Scorpion ) ), + new SlayerEntry( SlayerName.SpidersDeath, typeof( DreadSpider ), typeof( FrostSpider ), typeof( GiantBlackWidow ), typeof( GiantSpider ), typeof( Mephitis ) ), + new SlayerEntry( SlayerName.Terathan, typeof( TerathanAvenger ), typeof( TerathanDrone ), typeof( TerathanMatriarch ), typeof( TerathanWarrior ) ) + }; + + reptilian.Opposition = new SlayerGroup[]{ arachnid }; + reptilian.FoundOn = new Type[]{ typeof( TerathanAvenger ), typeof( TerathanMatriarch ) }; + reptilian.Super = new SlayerEntry( SlayerName.ReptilianDeath, typeof( AncientWyrm ), typeof( DeepSeaSerpent ), typeof( Dragon ), typeof( Drake ), typeof( GiantIceWorm ), typeof( IceSerpent ), typeof( GiantSerpent ), typeof( Hiryu ), typeof( IceSnake ), typeof( JukaLord ), typeof( JukaMage ), typeof( JukaWarrior ), typeof( LavaSerpent ), typeof( LavaSnake ), typeof( LesserHiryu ), typeof( Lizardman ), typeof( OphidianArchmage ), typeof( OphidianKnight ), typeof( OphidianMage ), typeof( OphidianMatriarch ), typeof( OphidianWarrior ), typeof( SeaSerpent ), typeof( Serado ), typeof( SerpentineDragon ), typeof( ShadowWyrm ), typeof( SilverSerpent ), typeof( SkeletalDragon ), typeof( Snake ), typeof( SwampDragon ), typeof( WhiteWyrm ), typeof( Wyvern ), typeof( Yamandon ) ); + reptilian.Entries = new SlayerEntry[] + { + new SlayerEntry( SlayerName.DragonSlaying, typeof( AncientWyrm ), typeof( Dragon ), typeof( Drake ), typeof( Hiryu ), typeof( LesserHiryu ), typeof( SerpentineDragon ), typeof( ShadowWyrm ), typeof( SkeletalDragon ), typeof( SwampDragon ), typeof( WhiteWyrm ), typeof( Wyvern ) ), + new SlayerEntry( SlayerName.LizardmanSlaughter, typeof( Lizardman ) ), + new SlayerEntry( SlayerName.Ophidian, typeof( OphidianArchmage ), typeof( OphidianKnight ), typeof( OphidianMage ), typeof( OphidianMatriarch ), typeof( OphidianWarrior ) ), + new SlayerEntry( SlayerName.SnakesBane, typeof( DeepSeaSerpent ), typeof( GiantIceWorm ), typeof( GiantSerpent ), typeof( IceSerpent ), typeof( IceSnake ), typeof( LavaSerpent ), typeof( LavaSnake ), typeof( SeaSerpent ), typeof( Serado ), typeof( SilverSerpent ), typeof( Snake ), typeof( Yamandon ) ) + }; + + m_Groups = new SlayerGroup[] + { + humanoid, + undead, + elemental, + abyss, + arachnid, + reptilian, + fey + }; + + m_TotalEntries = CompileEntries( m_Groups ); + } + + private static SlayerEntry[] CompileEntries( SlayerGroup[] groups ) + { + SlayerEntry[] entries = new SlayerEntry[28]; + + for ( int i = 0; i < groups.Length; ++i ) + { + SlayerGroup g = groups[i]; + + g.Super.Group = g; + + entries[(int)g.Super.Name] = g.Super; + + for ( int j = 0; j < g.Entries.Length; ++j ) + { + g.Entries[j].Group = g; + entries[(int)g.Entries[j].Name] = g.Entries[j]; + } + } + + return entries; + } + + private SlayerGroup[] m_Opposition; + private SlayerEntry m_Super; + private SlayerEntry[] m_Entries; + private Type[] m_FoundOn; + + public SlayerGroup[] Opposition{ get{ return m_Opposition; } set{ m_Opposition = value; } } + public SlayerEntry Super{ get{ return m_Super; } set{ m_Super = value; } } + public SlayerEntry[] Entries{ get{ return m_Entries; } set{ m_Entries = value; } } + public Type[] FoundOn{ get{ return m_FoundOn; } set{ m_FoundOn = value; } } + + public bool OppositionSuperSlays( Mobile m ) + { + for( int i = 0; i < Opposition.Length; i++ ) + { + if ( Opposition[i].Super.Slays( m ) ) + return true; + } + + return false; + } + + public SlayerGroup() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SlayerName.cs b/Scripts/Items/Weapons/SlayerName.cs new file mode 100644 index 000000000..f8c6f33a1 --- /dev/null +++ b/Scripts/Items/Weapons/SlayerName.cs @@ -0,0 +1,36 @@ +using System; + +namespace Server.Items +{ + public enum SlayerName + { + None, + Silver, + OrcSlaying, + TrollSlaughter, + OgreTrashing, + Repond, + DragonSlaying, + Terathan, + SnakesBane, + LizardmanSlaughter, + ReptilianDeath, + DaemonDismissal, + GargoylesFoe, + BalronDamnation, + Exorcism, + Ophidian, + SpidersDeath, + ScorpionsBane, + ArachnidDoom, + FlameDousing, + WaterDissipation, + Vacuum, + ElementalHealth, + EarthShatter, + BloodDrinking, + SummerWind, + ElementalBan, // Bane? + Fey + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs b/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs new file mode 100644 index 000000000..0b489d469 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/BaseSpear.cs @@ -0,0 +1,60 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public abstract class BaseSpear : BaseMeleeWeapon + { + public override int DefHitSound{ get{ return 0x23C; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce2H; } } + + public BaseSpear( int itemID ) : base( itemID ) + { + } + + public BaseSpear( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + if ( !Core.AOS && Layer == Layer.TwoHanded && (attacker.Skills[SkillName.Anatomy].Value / 400.0) >= Utility.RandomDouble() ) + { + defender.SendMessage( "You receive a paralyzing blow!" ); // Is this not localized? + defender.Freeze( TimeSpan.FromSeconds( 2.0 ) ); + + attacker.SendMessage( "You deliver a paralyzing blow!" ); // Is this not localized? + attacker.PlaySound( 0x11C ); + } + + if ( !Core.AOS && Poison != null && PoisonCharges > 0 ) + { + --PoisonCharges; + + if ( Utility.RandomDouble() >= 0.5 ) // 50% chance to poison + defender.ApplyPoison( attacker, Poison ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/BladedStaff.cs b/Scripts/Items/Weapons/SpearsAndForks/BladedStaff.cs new file mode 100644 index 000000000..3791c3b27 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/BladedStaff.cs @@ -0,0 +1,52 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26BD, 0x26C7 )] + public class BladedStaff : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Dismount; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 37; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 14; } } + public override int OldMaxDamage{ get{ return 16; } } + public override int OldSpeed{ get{ return 37; } } + + public override int InitMinHits{ get{ return 21; } } + public override int InitMaxHits{ get{ return 110; } } + + public override SkillName DefSkill{ get{ return SkillName.Swords; } } + + [Constructable] + public BladedStaff() : base( 0x26BD ) + { + Weight = 4.0; + } + + public BladedStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs b/Scripts/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs new file mode 100644 index 000000000..29a95bafb --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26BF, 0x26C9 )] + public class DoubleBladedStaff : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 12; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 49; } } + + public override int OldStrengthReq{ get{ return 50; } } + public override int OldMinDamage{ get{ return 12; } } + public override int OldMaxDamage{ get{ return 13; } } + public override int OldSpeed{ get{ return 49; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public DoubleBladedStaff() : base( 0x26BF ) + { + Weight = 2.0; + } + + public DoubleBladedStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/Pike.cs b/Scripts/Items/Weapons/SpearsAndForks/Pike.cs new file mode 100644 index 000000000..326ec3609 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/Pike.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26BE, 0x26C8 )] + public class Pike : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 37; } } + + public override int OldStrengthReq{ get{ return 50; } } + public override int OldMinDamage{ get{ return 14; } } + public override int OldMaxDamage{ get{ return 16; } } + public override int OldSpeed{ get{ return 37; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public Pike() : base( 0x26BE ) + { + Weight = 8.0; + } + + public Pike( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/Pitchfork.cs b/Scripts/Items/Weapons/SpearsAndForks/Pitchfork.cs new file mode 100644 index 000000000..505c27c70 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/Pitchfork.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xE87, 0xE88 )] + public class Pitchfork : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Dismount; } } + + public override int AosStrengthReq{ get{ return 55; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 43; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 4; } } + public override int OldMaxDamage{ get{ return 16; } } + public override int OldSpeed{ get{ return 45; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public Pitchfork() : base( 0xE87 ) + { + Weight = 11.0; + } + + public Pitchfork( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 10.0 ) + Weight = 11.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/ShortSpear.cs b/Scripts/Items/Weapons/SpearsAndForks/ShortSpear.cs new file mode 100644 index 000000000..5679e2149 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/ShortSpear.cs @@ -0,0 +1,52 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1403, 0x1402 )] + public class ShortSpear : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 55; } } + + public override int OldStrengthReq{ get{ return 15; } } + public override int OldMinDamage{ get{ return 4; } } + public override int OldMaxDamage{ get{ return 32; } } + public override int OldSpeed{ get{ return 50; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public ShortSpear() : base( 0x1403 ) + { + Weight = 4.0; + } + + public ShortSpear( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/Spear.cs b/Scripts/Items/Weapons/SpearsAndForks/Spear.cs new file mode 100644 index 000000000..79df7f4a9 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/Spear.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF62, 0xF63 )] + public class Spear : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 42; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 2; } } + public override int OldMaxDamage{ get{ return 36; } } + public override int OldSpeed{ get{ return 46; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public Spear() : base( 0xF62 ) + { + Weight = 7.0; + } + + public Spear( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/TribalSpear.cs b/Scripts/Items/Weapons/SpearsAndForks/TribalSpear.cs new file mode 100644 index 000000000..9a99d06a4 --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/TribalSpear.cs @@ -0,0 +1,58 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF62, 0xF63 )] + public class TribalSpear : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 50; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 42; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 2; } } + public override int OldMaxDamage{ get{ return 36; } } + public override int OldSpeed{ get{ return 46; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 80; } } + + public override int VirtualDamageBonus{ get{ return 25; } } + + public override string DefaultName + { + get { return "a tribal spear"; } + } + + [Constructable] + public TribalSpear() : base( 0xF62 ) + { + Weight = 7.0; + Hue = 837; + } + + public TribalSpear( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/SpearsAndForks/WarFork.cs b/Scripts/Items/Weapons/SpearsAndForks/WarFork.cs new file mode 100644 index 000000000..3b9845bff --- /dev/null +++ b/Scripts/Items/Weapons/SpearsAndForks/WarFork.cs @@ -0,0 +1,55 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1405, 0x1404 )] + public class WarFork : BaseSpear + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 45; } } + public override int AosMinDamage{ get{ return 12; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 43; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 4; } } + public override int OldMaxDamage{ get{ return 32; } } + public override int OldSpeed{ get{ return 45; } } + + public override int DefHitSound{ get{ return 0x236; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public WarFork() : base( 0x1405 ) + { + Weight = 9.0; + } + + public WarFork( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Staves/BaseStaff.cs b/Scripts/Items/Weapons/Staves/BaseStaff.cs new file mode 100644 index 000000000..f87d52f04 --- /dev/null +++ b/Scripts/Items/Weapons/Staves/BaseStaff.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Items +{ + public abstract class BaseStaff : BaseMeleeWeapon + { + public override int DefHitSound{ get{ return 0x233; } } + public override int DefMissSound{ get{ return 0x239; } } + + public override SkillName DefSkill{ get{ return SkillName.Macing; } } + public override WeaponType DefType{ get{ return WeaponType.Staff; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Bash2H; } } + + public BaseStaff( int itemID ) : base( itemID ) + { + } + + public BaseStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + defender.Stam -= Utility.Random( 3, 3 ); // 3-5 points of stamina loss + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Staves/BlackStaff.cs b/Scripts/Items/Weapons/Staves/BlackStaff.cs new file mode 100644 index 000000000..878c4cfc9 --- /dev/null +++ b/Scripts/Items/Weapons/Staves/BlackStaff.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xDF1, 0xDF0 )] + public class BlackStaff : BaseStaff + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.WhirlwindAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 39; } } + + public override int OldStrengthReq{ get{ return 35; } } + public override int OldMinDamage{ get{ return 8; } } + public override int OldMaxDamage{ get{ return 33; } } + public override int OldSpeed{ get{ return 35; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public BlackStaff() : base( 0xDF0 ) + { + Weight = 6.0; + } + + public BlackStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Staves/GlacialStaff.cs b/Scripts/Items/Weapons/Staves/GlacialStaff.cs new file mode 100644 index 000000000..a592f0fca --- /dev/null +++ b/Scripts/Items/Weapons/Staves/GlacialStaff.cs @@ -0,0 +1,41 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + public class GlacialStaff : BlackStaff + { + //TODO: Pre-AoS stuff + public override int LabelNumber{ get{ return 1017413; } } // Glacial Staff + + [Constructable] + public GlacialStaff() + { + Hue = 0x480; + WeaponAttributes.HitHarm = 5 * Utility.RandomMinMax( 1, 5 ); + WeaponAttributes.MageWeapon = Utility.RandomMinMax( 5, 10 ); + + AosElementDamages[AosElementAttribute.Cold] = 20 + (5 * Utility.RandomMinMax( 0, 6 )); + + } + + public GlacialStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Items/Weapons/Staves/GnarledStaff.cs b/Scripts/Items/Weapons/Staves/GnarledStaff.cs new file mode 100644 index 000000000..26a158615 --- /dev/null +++ b/Scripts/Items/Weapons/Staves/GnarledStaff.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13F8, 0x13F9 )] + public class GnarledStaff : BaseStaff + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 33; } } + + public override int OldStrengthReq{ get{ return 20; } } + public override int OldMinDamage{ get{ return 10; } } + public override int OldMaxDamage{ get{ return 30; } } + public override int OldSpeed{ get{ return 33; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 50; } } + + [Constructable] + public GnarledStaff() : base( 0x13F8 ) + { + Weight = 3.0; + } + + public GnarledStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Staves/QuarterStaff.cs b/Scripts/Items/Weapons/Staves/QuarterStaff.cs new file mode 100644 index 000000000..32bb9f48e --- /dev/null +++ b/Scripts/Items/Weapons/Staves/QuarterStaff.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xE89, 0xE8a )] + public class QuarterStaff : BaseStaff + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + + public override int AosStrengthReq{ get{ return 30; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 48; } } + + public override int OldStrengthReq{ get{ return 30; } } + public override int OldMinDamage{ get{ return 8; } } + public override int OldMaxDamage{ get{ return 28; } } + public override int OldSpeed{ get{ return 48; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 60; } } + + [Constructable] + public QuarterStaff() : base( 0xE89 ) + { + Weight = 4.0; + } + + public QuarterStaff( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Staves/ShepherdsCrook.cs b/Scripts/Items/Weapons/Staves/ShepherdsCrook.cs new file mode 100644 index 000000000..c3e312425 --- /dev/null +++ b/Scripts/Items/Weapons/Staves/ShepherdsCrook.cs @@ -0,0 +1,123 @@ +using System; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Items +{ + [FlipableAttribute( 0xE81, 0xE82 )] + public class ShepherdsCrook : BaseStaff + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.Disarm; } } + + public override int AosStrengthReq{ get{ return 20; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 40; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 3; } } + public override int OldMaxDamage{ get{ return 12; } } + public override int OldSpeed{ get{ return 30; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 50; } } + + [Constructable] + public ShepherdsCrook() : base( 0xE81 ) + { + Weight = 4.0; + } + + public ShepherdsCrook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 2.0 ) + Weight = 4.0; + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 502464 ); // Target the animal you wish to herd. + from.Target = new HerdingTarget(); + } + + private class HerdingTarget : Target + { + public HerdingTarget() : base( 10, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targ ) + { + if ( targ is BaseCreature ) + { + BaseCreature bc = (BaseCreature)targ; + + if ( bc.Body.IsAnimal ) + { + if ( bc.Controlled ) + { + bc.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502467, from.NetState ); // That animal looks tame already. + } + else + { + from.SendLocalizedMessage( 502475 ); // Click where you wish the animal to go. + from.Target = new InternalTarget( bc ); + } + } + else + { + from.SendLocalizedMessage( 502468 ); // That is not a herdable animal. + } + } + else + { + from.SendLocalizedMessage( 502472 ); // You don't seem to be able to persuade that to move. + } + } + + private class InternalTarget : Target + { + private BaseCreature m_Creature; + + public InternalTarget( BaseCreature c ) : base( 10, true, TargetFlags.None ) + { + m_Creature = c; + } + + protected override void OnTarget( Mobile from, object targ ) + { + if ( targ is IPoint2D ) + { + if ( from.CheckTargetSkill( SkillName.Herding, m_Creature, 0, 100 ) ) + { + m_Creature.TargetLocation = new Point2D( (IPoint2D)targ ); + from.SendLocalizedMessage( 502479 ); // The animal walks where it was instructed to. + } + else + { + from.SendLocalizedMessage( 502472 ); // You don't seem to be able to persuade that to move. + } + } + } + } + } + } +} diff --git a/Scripts/Items/Weapons/Swords/BaseSword.cs b/Scripts/Items/Weapons/Swords/BaseSword.cs new file mode 100644 index 000000000..06f1cd825 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/BaseSword.cs @@ -0,0 +1,56 @@ +using System; +using Server; +using Server.Items; +using Server.Targets; + +namespace Server.Items +{ + public abstract class BaseSword : BaseMeleeWeapon + { + public override SkillName DefSkill{ get{ return SkillName.Swords; } } + public override WeaponType DefType{ get{ return WeaponType.Slashing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Slash1H; } } + + public BaseSword( int itemID ) : base( itemID ) + { + } + + public BaseSword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnDoubleClick( Mobile from ) + { + from.SendLocalizedMessage( 1010018 ); // What do you want to use this item on? + + from.Target = new BladedItemTarget( this ); + } + + public override void OnHit( Mobile attacker, Mobile defender ) + { + base.OnHit( attacker, defender ); + + if ( !Core.AOS && Poison != null && PoisonCharges > 0 ) + { + --PoisonCharges; + + if ( Utility.RandomDouble() >= 0.5 ) // 50% chance to poison + defender.ApplyPoison( attacker, Poison ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/BoneHarvester.cs b/Scripts/Items/Weapons/Swords/BoneHarvester.cs new file mode 100644 index 000000000..c77288df2 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/BoneHarvester.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26BB, 0x26C5 )] + public class BoneHarvester : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 25; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 36; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 13; } } + public override int OldMaxDamage{ get{ return 15; } } + public override int OldSpeed{ get{ return 36; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public BoneHarvester() : base( 0x26BB ) + { + Weight = 3.0; + } + + public BoneHarvester( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Broadsword.cs b/Scripts/Items/Weapons/Swords/Broadsword.cs new file mode 100644 index 000000000..27cca7404 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Broadsword.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF5E, 0xF5F )] + public class Broadsword : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + + public override int AosStrengthReq{ get{ return 30; } } + public override int AosMinDamage{ get{ return 14; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 33; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 29; } } + public override int OldSpeed{ get{ return 45; } } + + public override int DefHitSound{ get{ return 0x237; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 100; } } + + [Constructable] + public Broadsword() : base( 0xF5E ) + { + Weight = 6.0; + } + + public Broadsword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/CrescentBlade.cs b/Scripts/Items/Weapons/Swords/CrescentBlade.cs new file mode 100644 index 000000000..28f03d122 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/CrescentBlade.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26C1, 0x26CB )] + public class CrescentBlade : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.MortalStrike; } } + + public override int AosStrengthReq{ get{ return 55; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 14; } } + public override int AosSpeed{ get{ return 47; } } + + public override int OldStrengthReq{ get{ return 55; } } + public override int OldMinDamage{ get{ return 11; } } + public override int OldMaxDamage{ get{ return 14; } } + public override int OldSpeed{ get{ return 47; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 51; } } + public override int InitMaxHits{ get{ return 80; } } + + [Constructable] + public CrescentBlade() : base( 0x26C1 ) + { + Weight = 1.0; + } + + public CrescentBlade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Cutlass.cs b/Scripts/Items/Weapons/Swords/Cutlass.cs new file mode 100644 index 000000000..48d54d0e4 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Cutlass.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1441, 0x1440 )] + public class Cutlass : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.BleedAttack; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ShadowStrike; } } + + public override int AosStrengthReq{ get{ return 25; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 44; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 28; } } + public override int OldSpeed{ get{ return 45; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 70; } } + + [Constructable] + public Cutlass() : base( 0x1441 ) + { + Weight = 8.0; + } + + public Cutlass( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Katana.cs b/Scripts/Items/Weapons/Swords/Katana.cs new file mode 100644 index 000000000..f58f42e3a --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Katana.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13FF, 0x13FE )] + public class Katana : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + + public override int AosStrengthReq{ get{ return 25; } } + public override int AosMinDamage{ get{ return 11; } } + public override int AosMaxDamage{ get{ return 13; } } + public override int AosSpeed{ get{ return 46; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 26; } } + public override int OldSpeed{ get{ return 58; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 90; } } + + [Constructable] + public Katana() : base( 0x13FF ) + { + Weight = 6.0; + } + + public Katana( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Kryss.cs b/Scripts/Items/Weapons/Swords/Kryss.cs new file mode 100644 index 000000000..fc5544607 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Kryss.cs @@ -0,0 +1,60 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x1401, 0x1400 )] + public class Kryss : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.InfectiousStrike; } } + + public override int AosStrengthReq{ get{ return 10; } } + public override int AosMinDamage{ get{ return 10; } } + public override int AosMaxDamage{ get{ return 12; } } + public override int AosSpeed{ get{ return 53; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 3; } } + public override int OldMaxDamage{ get{ return 28; } } + public override int OldSpeed{ get{ return 53; } } + + public override int DefHitSound{ get{ return 0x23C; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 90; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Kryss() : base( 0x1401 ) + { + Weight = 2.0; + } + + public Kryss( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 1.0 ) + Weight = 2.0; + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Lance.cs b/Scripts/Items/Weapons/Swords/Lance.cs new file mode 100644 index 000000000..6942c30df --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Lance.cs @@ -0,0 +1,57 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x26C0, 0x26CA )] + public class Lance : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.Dismount; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + + public override int AosStrengthReq{ get{ return 95; } } + public override int AosMinDamage{ get{ return 17; } } + public override int AosMaxDamage{ get{ return 18; } } + public override int AosSpeed{ get{ return 24; } } + + public override int OldStrengthReq{ get{ return 95; } } + public override int OldMinDamage{ get{ return 17; } } + public override int OldMaxDamage{ get{ return 18; } } + public override int OldSpeed{ get{ return 24; } } + + public override int DefHitSound{ get{ return 0x23C; } } + public override int DefMissSound{ get{ return 0x238; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + public override SkillName DefSkill{ get{ return SkillName.Fencing; } } + public override WeaponType DefType{ get{ return WeaponType.Piercing; } } + public override WeaponAnimation DefAnimation{ get{ return WeaponAnimation.Pierce1H; } } + + [Constructable] + public Lance() : base( 0x26C0 ) + { + Weight = 12.0; + } + + public Lance( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Longsword.cs b/Scripts/Items/Weapons/Swords/Longsword.cs new file mode 100644 index 000000000..6978ea153 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Longsword.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0xF61, 0xF60 )] + public class Longsword : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.ArmorIgnore; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ConcussionBlow; } } + + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 30; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 33; } } + public override int OldSpeed{ get{ return 35; } } + + public override int DefHitSound{ get{ return 0x237; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public Longsword() : base( 0xF61 ) + { + Weight = 7.0; + } + + public Longsword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/Scimitar.cs b/Scripts/Items/Weapons/Swords/Scimitar.cs new file mode 100644 index 000000000..72ce06ee0 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/Scimitar.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13B6, 0x13B5 )] + public class Scimitar : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.DoubleStrike; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 25; } } + public override int AosMinDamage{ get{ return 13; } } + public override int AosMaxDamage{ get{ return 15; } } + public override int AosSpeed{ get{ return 37; } } + + public override int OldStrengthReq{ get{ return 10; } } + public override int OldMinDamage{ get{ return 4; } } + public override int OldMaxDamage{ get{ return 30; } } + public override int OldSpeed{ get{ return 43; } } + + public override int DefHitSound{ get{ return 0x23B; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 90; } } + + [Constructable] + public Scimitar() : base( 0x13B6 ) + { + Weight = 5.0; + } + + public Scimitar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/ThinLongsword.cs b/Scripts/Items/Weapons/Swords/ThinLongsword.cs new file mode 100644 index 000000000..8f47351b5 --- /dev/null +++ b/Scripts/Items/Weapons/Swords/ThinLongsword.cs @@ -0,0 +1,50 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13B8, 0x13B7 )] + public class ThinLongsword : BaseSword + { + public override int AosStrengthReq{ get{ return 35; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 16; } } + public override int AosSpeed{ get{ return 30; } } + + public override int OldStrengthReq{ get{ return 25; } } + public override int OldMinDamage{ get{ return 5; } } + public override int OldMaxDamage{ get{ return 33; } } + public override int OldSpeed{ get{ return 35; } } + + public override int DefHitSound{ get{ return 0x237; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 110; } } + + [Constructable] + public ThinLongsword() : base( 0x13B8 ) + { + Weight = 1.0; + } + + public ThinLongsword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/Swords/VikingSword.cs b/Scripts/Items/Weapons/Swords/VikingSword.cs new file mode 100644 index 000000000..588516a0a --- /dev/null +++ b/Scripts/Items/Weapons/Swords/VikingSword.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; +using Server.Items; + +namespace Server.Items +{ + [FlipableAttribute( 0x13B9, 0x13Ba )] + public class VikingSword : BaseSword + { + public override WeaponAbility PrimaryAbility{ get{ return WeaponAbility.CrushingBlow; } } + public override WeaponAbility SecondaryAbility{ get{ return WeaponAbility.ParalyzingBlow; } } + + public override int AosStrengthReq{ get{ return 40; } } + public override int AosMinDamage{ get{ return 15; } } + public override int AosMaxDamage{ get{ return 17; } } + public override int AosSpeed{ get{ return 28; } } + + public override int OldStrengthReq{ get{ return 40; } } + public override int OldMinDamage{ get{ return 6; } } + public override int OldMaxDamage{ get{ return 34; } } + public override int OldSpeed{ get{ return 30; } } + + public override int DefHitSound{ get{ return 0x237; } } + public override int DefMissSound{ get{ return 0x23A; } } + + public override int InitMinHits{ get{ return 31; } } + public override int InitMaxHits{ get{ return 100; } } + + [Constructable] + public VikingSword() : base( 0x13B9 ) + { + Weight = 6.0; + } + + public VikingSword( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Items/Weapons/WeaponEnums.cs b/Scripts/Items/Weapons/WeaponEnums.cs new file mode 100644 index 000000000..c75b388dc --- /dev/null +++ b/Scripts/Items/Weapons/WeaponEnums.cs @@ -0,0 +1,66 @@ +using System; + +namespace Server.Items +{ + public enum WeaponQuality + { + Low, + Regular, + Exceptional + } + + public enum WeaponType + { + Axe, // Axes, Hatches, etc. These can give concussion blows + Slashing, // Katana, Broadsword, Longsword, etc. Slashing weapons are poisonable + Staff, // Staves + Bashing, // War Hammers, Maces, Mauls, etc. Two-handed bashing delivers crushing blows + Piercing, // Spears, Warforks, Daggers, etc. Two-handed piercing delivers paralyzing blows + Polearm, // Halberd, Bardiche + Ranged, // Bow, Crossbows + Fists // Fists + } + + public enum WeaponDamageLevel + { + Regular, + Ruin, + Might, + Force, + Power, + Vanq + } + + public enum WeaponAccuracyLevel + { + Regular, + Accurate, + Surpassingly, + Eminently, + Exceedingly, + Supremely + } + + public enum WeaponDurabilityLevel + { + Regular, + Durable, + Substantial, + Massive, + Fortified, + Indestructible + } + + public enum WeaponAnimation + { + Slash1H = 9, + Pierce1H = 10, + Bash1H = 11, + Bash2H = 12, + Slash2H = 13, + Pierce2H = 14, + ShootBow = 18, + ShootXBow = 19, + Wrestle = 31 + } +} \ No newline at end of file diff --git a/Scripts/Misc/AOS.cs b/Scripts/Misc/AOS.cs new file mode 100644 index 000000000..469943626 --- /dev/null +++ b/Scripts/Misc/AOS.cs @@ -0,0 +1,989 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server +{ + public class AOS + { + public static void DisableStatInfluences() + { + for( int i = 0; i < SkillInfo.Table.Length; ++i ) + { + SkillInfo info = SkillInfo.Table[i]; + + info.StrScale = 0.0; + info.DexScale = 0.0; + info.IntScale = 0.0; + info.StatTotal = 0.0; + } + } + + public static int Damage( Mobile m, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois, int nrgy ) + { + return Damage( m, null, damage, ignoreArmor, phys, fire, cold, pois, nrgy ); + } + + public static int Damage( Mobile m, int damage, int phys, int fire, int cold, int pois, int nrgy ) + { + return Damage( m, null, damage, phys, fire, cold, pois, nrgy ); + } + + public static int Damage( Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy ) + { + return Damage( m, from, damage, false, phys, fire, cold, pois, nrgy, false ); + } + + public static int Damage( Mobile m, Mobile from, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois, int nrgy ) + { + return Damage( m, from, damage, ignoreArmor, phys, fire, cold, pois, nrgy, false ); + } + + public static int Damage( Mobile m, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy, bool keepAlive ) + { + return Damage( m, from, damage, false, phys, fire, cold, pois, nrgy, keepAlive ); + } + + public static int Damage( Mobile m, Mobile from, int damage, bool ignoreArmor, int phys, int fire, int cold, int pois, int nrgy, bool keepAlive ) + { + if( m == null || m.Deleted || !m.Alive || damage <= 0 ) + return 0; + + if( phys == 0 && fire == 100 && cold == 0 && pois == 0 && nrgy == 0 ) + Mobiles.MeerMage.StopEffect( m, true ); + + if( !Core.AOS ) + { + m.Damage( damage, from ); + return damage; + } + + Fix( ref phys ); + Fix( ref fire ); + Fix( ref cold ); + Fix( ref pois ); + Fix( ref nrgy ); + + int totalDamage; + + if( !ignoreArmor ) + { + // Armor Ignore on OSI ignores all defenses, not just physical. + int resPhys = m.PhysicalResistance; + int resFire = m.FireResistance; + int resCold = m.ColdResistance; + int resPois = m.PoisonResistance; + int resNrgy = m.EnergyResistance; + + totalDamage = damage * phys * (100 - resPhys); + totalDamage += damage * fire * (100 - resFire); + totalDamage += damage * cold * (100 - resCold); + totalDamage += damage * pois * (100 - resPois); + totalDamage += damage * nrgy * (100 - resNrgy); + + totalDamage /= 10000; + + if( totalDamage < 1 ) + totalDamage = 1; + } + else if( Core.ML && m is PlayerMobile && from is PlayerMobile ) + { + totalDamage = Math.Min( damage, 35 ); //Direct Damage cap of 35 + } + else + { + totalDamage = damage; + } + + #region Dragon Barding + if( (!Core.AOS || from == null || !from.Player) && m.Player && m.Mount is SwampDragon ) + { + SwampDragon pet = m.Mount as SwampDragon; + + if( pet != null && pet.HasBarding ) + { + int percent = (pet.BardingExceptional ? 20 : 10); + int absorbed = Scale( totalDamage, percent ); + + totalDamage -= absorbed; + pet.BardingHP -= absorbed; + + if( pet.BardingHP < 0 ) + { + pet.HasBarding = false; + pet.BardingHP = 0; + + m.SendLocalizedMessage( 1053031 ); // Your dragon's barding has been destroyed! + } + } + } + #endregion + + if( keepAlive && totalDamage > m.Hits ) + totalDamage = m.Hits; + + if( from != null && !from.Deleted && from.Alive ) + { + int reflectPhys = AosAttributes.GetValue( m, AosAttribute.ReflectPhysical ); + + if( reflectPhys != 0 ) + { + if( from is ExodusMinion && ((ExodusMinion)from).FieldActive || from is ExodusOverseer && ((ExodusOverseer)from).FieldActive ) + { + from.FixedParticles( 0x376A, 20, 10, 0x2530, EffectLayer.Waist ); + from.PlaySound( 0x2F4 ); + m.SendAsciiMessage( "Your weapon cannot penetrate the creature's magical barrier" ); + } + else + { + from.Damage( Scale( (damage * phys * (100 - (ignoreArmor ? 0 : m.PhysicalResistance))) / 10000, reflectPhys ), m ); + } + } + } + + m.Damage( totalDamage, from ); + return totalDamage; + } + + public static void Fix( ref int val ) + { + if( val < 0 ) + val = 0; + } + + public static int Scale( int input, int percent ) + { + return (input * percent) / 100; + } + } + + [Flags] + public enum AosAttribute + { + RegenHits=0x00000001, + RegenStam=0x00000002, + RegenMana=0x00000004, + DefendChance=0x00000008, + AttackChance=0x00000010, + BonusStr=0x00000020, + BonusDex=0x00000040, + BonusInt=0x00000080, + BonusHits=0x00000100, + BonusStam=0x00000200, + BonusMana=0x00000400, + WeaponDamage=0x00000800, + WeaponSpeed=0x00001000, + SpellDamage=0x00002000, + CastRecovery=0x00004000, + CastSpeed=0x00008000, + LowerManaCost=0x00010000, + LowerRegCost=0x00020000, + ReflectPhysical=0x00040000, + EnhancePotions=0x00080000, + Luck=0x00100000, + SpellChanneling=0x00200000, + NightSight=0x00400000 + } + + public sealed class AosAttributes : BaseAttributes + { + public AosAttributes( Item owner ) + : base( owner ) + { + } + + public AosAttributes( Item owner, GenericReader reader ) + : base( owner, reader ) + { + } + + public static int GetValue( Mobile m, AosAttribute attribute ) + { + if( !Core.AOS ) + return 0; + + List items = m.Items; + int value = 0; + + for( int i = 0; i < items.Count; ++i ) + { + Item obj = items[i]; + + if( obj is BaseWeapon ) + { + AosAttributes attrs = ((BaseWeapon)obj).Attributes; + + if( attrs != null ) + value += attrs[attribute]; + + if( attribute == AosAttribute.Luck ) + value += ((BaseWeapon)obj).GetLuckBonus(); + } + else if( obj is BaseArmor ) + { + AosAttributes attrs = ((BaseArmor)obj).Attributes; + + if( attrs != null ) + value += attrs[attribute]; + + if( attribute == AosAttribute.Luck ) + value += ((BaseArmor)obj).GetLuckBonus(); + } + else if( obj is BaseJewel ) + { + AosAttributes attrs = ((BaseJewel)obj).Attributes; + + if( attrs != null ) + value += attrs[attribute]; + } + else if( obj is BaseClothing ) + { + AosAttributes attrs = ((BaseClothing)obj).Attributes; + + if( attrs != null ) + value += attrs[attribute]; + } + else if( obj is Spellbook ) + { + AosAttributes attrs = ((Spellbook)obj).Attributes; + + if( attrs != null ) + value += attrs[attribute]; + } + } + + return value; + } + + public int this[AosAttribute attribute] + { + get { return GetValue( (int)attribute ); } + set { SetValue( (int)attribute, value ); } + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RegenHits { get { return this[AosAttribute.RegenHits]; } set { this[AosAttribute.RegenHits] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int RegenStam { get { return this[AosAttribute.RegenStam]; } set { this[AosAttribute.RegenStam] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int RegenMana { get { return this[AosAttribute.RegenMana]; } set { this[AosAttribute.RegenMana] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DefendChance { get { return this[AosAttribute.DefendChance]; } set { this[AosAttribute.DefendChance] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int AttackChance { get { return this[AosAttribute.AttackChance]; } set { this[AosAttribute.AttackChance] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int BonusStr { get { return this[AosAttribute.BonusStr]; } set { this[AosAttribute.BonusStr] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int BonusDex { get { return this[AosAttribute.BonusDex]; } set { this[AosAttribute.BonusDex] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int BonusInt { get { return this[AosAttribute.BonusInt]; } set { this[AosAttribute.BonusInt] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int BonusHits { get { return this[AosAttribute.BonusHits]; } set { this[AosAttribute.BonusHits] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int BonusStam { get { return this[AosAttribute.BonusStam]; } set { this[AosAttribute.BonusStam] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int BonusMana { get { return this[AosAttribute.BonusMana]; } set { this[AosAttribute.BonusMana] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int WeaponDamage { get { return this[AosAttribute.WeaponDamage]; } set { this[AosAttribute.WeaponDamage] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int WeaponSpeed { get { return this[AosAttribute.WeaponSpeed]; } set { this[AosAttribute.WeaponSpeed] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int SpellDamage { get { return this[AosAttribute.SpellDamage]; } set { this[AosAttribute.SpellDamage] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int CastRecovery { get { return this[AosAttribute.CastRecovery]; } set { this[AosAttribute.CastRecovery] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int CastSpeed { get { return this[AosAttribute.CastSpeed]; } set { this[AosAttribute.CastSpeed] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int LowerManaCost { get { return this[AosAttribute.LowerManaCost]; } set { this[AosAttribute.LowerManaCost] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int LowerRegCost { get { return this[AosAttribute.LowerRegCost]; } set { this[AosAttribute.LowerRegCost] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ReflectPhysical { get { return this[AosAttribute.ReflectPhysical]; } set { this[AosAttribute.ReflectPhysical] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int EnhancePotions { get { return this[AosAttribute.EnhancePotions]; } set { this[AosAttribute.EnhancePotions] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Luck { get { return this[AosAttribute.Luck]; } set { this[AosAttribute.Luck] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int SpellChanneling { get { return this[AosAttribute.SpellChanneling]; } set { this[AosAttribute.SpellChanneling] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int NightSight { get { return this[AosAttribute.NightSight]; } set { this[AosAttribute.NightSight] = value; } } + } + + [Flags] + public enum AosWeaponAttribute + { + LowerStatReq=0x00000001, + SelfRepair=0x00000002, + HitLeechHits=0x00000004, + HitLeechStam=0x00000008, + HitLeechMana=0x00000010, + HitLowerAttack=0x00000020, + HitLowerDefend=0x00000040, + HitMagicArrow=0x00000080, + HitHarm=0x00000100, + HitFireball=0x00000200, + HitLightning=0x00000400, + HitDispel=0x00000800, + HitColdArea=0x00001000, + HitFireArea=0x00002000, + HitPoisonArea=0x00004000, + HitEnergyArea=0x00008000, + HitPhysicalArea=0x00010000, + ResistPhysicalBonus=0x00020000, + ResistFireBonus=0x00040000, + ResistColdBonus=0x00080000, + ResistPoisonBonus=0x00100000, + ResistEnergyBonus=0x00200000, + UseBestSkill=0x00400000, + MageWeapon=0x00800000, + DurabilityBonus=0x01000000 + } + + public sealed class AosWeaponAttributes : BaseAttributes + { + public AosWeaponAttributes( Item owner ) + : base( owner ) + { + } + + public AosWeaponAttributes( Item owner, GenericReader reader ) + : base( owner, reader ) + { + } + + public static int GetValue( Mobile m, AosWeaponAttribute attribute ) + { + if( !Core.AOS ) + return 0; + + List items = m.Items; + int value = 0; + + for( int i = 0; i < items.Count; ++i ) + { + Item obj = items[i]; + + if( obj is BaseWeapon ) + { + AosWeaponAttributes attrs = ((BaseWeapon)obj).WeaponAttributes; + + if( attrs != null ) + value += attrs[attribute]; + } + } + + return value; + } + + public int this[AosWeaponAttribute attribute] + { + get { return GetValue( (int)attribute ); } + set { SetValue( (int)attribute, value ); } + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int LowerStatReq { get { return this[AosWeaponAttribute.LowerStatReq]; } set { this[AosWeaponAttribute.LowerStatReq] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int SelfRepair { get { return this[AosWeaponAttribute.SelfRepair]; } set { this[AosWeaponAttribute.SelfRepair] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitLeechHits { get { return this[AosWeaponAttribute.HitLeechHits]; } set { this[AosWeaponAttribute.HitLeechHits] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitLeechStam { get { return this[AosWeaponAttribute.HitLeechStam]; } set { this[AosWeaponAttribute.HitLeechStam] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitLeechMana { get { return this[AosWeaponAttribute.HitLeechMana]; } set { this[AosWeaponAttribute.HitLeechMana] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitLowerAttack { get { return this[AosWeaponAttribute.HitLowerAttack]; } set { this[AosWeaponAttribute.HitLowerAttack] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitLowerDefend { get { return this[AosWeaponAttribute.HitLowerDefend]; } set { this[AosWeaponAttribute.HitLowerDefend] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitMagicArrow { get { return this[AosWeaponAttribute.HitMagicArrow]; } set { this[AosWeaponAttribute.HitMagicArrow] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitHarm { get { return this[AosWeaponAttribute.HitHarm]; } set { this[AosWeaponAttribute.HitHarm] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitFireball { get { return this[AosWeaponAttribute.HitFireball]; } set { this[AosWeaponAttribute.HitFireball] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitLightning { get { return this[AosWeaponAttribute.HitLightning]; } set { this[AosWeaponAttribute.HitLightning] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitDispel { get { return this[AosWeaponAttribute.HitDispel]; } set { this[AosWeaponAttribute.HitDispel] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitColdArea { get { return this[AosWeaponAttribute.HitColdArea]; } set { this[AosWeaponAttribute.HitColdArea] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitFireArea { get { return this[AosWeaponAttribute.HitFireArea]; } set { this[AosWeaponAttribute.HitFireArea] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitPoisonArea { get { return this[AosWeaponAttribute.HitPoisonArea]; } set { this[AosWeaponAttribute.HitPoisonArea] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitEnergyArea { get { return this[AosWeaponAttribute.HitEnergyArea]; } set { this[AosWeaponAttribute.HitEnergyArea] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HitPhysicalArea { get { return this[AosWeaponAttribute.HitPhysicalArea]; } set { this[AosWeaponAttribute.HitPhysicalArea] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ResistPhysicalBonus { get { return this[AosWeaponAttribute.ResistPhysicalBonus]; } set { this[AosWeaponAttribute.ResistPhysicalBonus] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ResistFireBonus { get { return this[AosWeaponAttribute.ResistFireBonus]; } set { this[AosWeaponAttribute.ResistFireBonus] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ResistColdBonus { get { return this[AosWeaponAttribute.ResistColdBonus]; } set { this[AosWeaponAttribute.ResistColdBonus] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ResistPoisonBonus { get { return this[AosWeaponAttribute.ResistPoisonBonus]; } set { this[AosWeaponAttribute.ResistPoisonBonus] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ResistEnergyBonus { get { return this[AosWeaponAttribute.ResistEnergyBonus]; } set { this[AosWeaponAttribute.ResistEnergyBonus] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int UseBestSkill { get { return this[AosWeaponAttribute.UseBestSkill]; } set { this[AosWeaponAttribute.UseBestSkill] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int MageWeapon { get { return this[AosWeaponAttribute.MageWeapon]; } set { this[AosWeaponAttribute.MageWeapon] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DurabilityBonus { get { return this[AosWeaponAttribute.DurabilityBonus]; } set { this[AosWeaponAttribute.DurabilityBonus] = value; } } + } + + [Flags] + public enum AosArmorAttribute + { + LowerStatReq=0x00000001, + SelfRepair=0x00000002, + MageArmor=0x00000004, + DurabilityBonus=0x00000008 + } + + public sealed class AosArmorAttributes : BaseAttributes + { + public AosArmorAttributes( Item owner ) + : base( owner ) + { + } + + public AosArmorAttributes( Item owner, GenericReader reader ) + : base( owner, reader ) + { + } + + public static int GetValue( Mobile m, AosArmorAttribute attribute ) + { + if( !Core.AOS ) + return 0; + + List items = m.Items; + int value = 0; + + for( int i = 0; i < items.Count; ++i ) + { + Item obj = items[i]; + + if( obj is BaseArmor ) + { + AosArmorAttributes attrs = ((BaseArmor)obj).ArmorAttributes; + + if( attrs != null ) + value += attrs[attribute]; + } + else if( obj is BaseClothing ) + { + AosArmorAttributes attrs = ((BaseClothing)obj).ClothingAttributes; + + if( attrs != null ) + value += attrs[attribute]; + } + } + + return value; + } + + public int this[AosArmorAttribute attribute] + { + get { return GetValue( (int)attribute ); } + set { SetValue( (int)attribute, value ); } + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int LowerStatReq { get { return this[AosArmorAttribute.LowerStatReq]; } set { this[AosArmorAttribute.LowerStatReq] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int SelfRepair { get { return this[AosArmorAttribute.SelfRepair]; } set { this[AosArmorAttribute.SelfRepair] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int MageArmor { get { return this[AosArmorAttribute.MageArmor]; } set { this[AosArmorAttribute.MageArmor] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DurabilityBonus { get { return this[AosArmorAttribute.DurabilityBonus]; } set { this[AosArmorAttribute.DurabilityBonus] = value; } } + } + + public sealed class AosSkillBonuses : BaseAttributes + { + private ArrayList m_Mods; + + public AosSkillBonuses( Item owner ) + : base( owner ) + { + } + + public AosSkillBonuses( Item owner, GenericReader reader ) + : base( owner, reader ) + { + } + + public void GetProperties( ObjectPropertyList list ) + { + for( int i = 0; i < 5; ++i ) + { + SkillName skill; + double bonus; + + if( !GetValues( i, out skill, out bonus ) ) + continue; + + list.Add( 1060451 + i, "#{0}\t{1}", 1044060 + (int)skill, bonus ); + } + } + + public void AddTo( Mobile m ) + { + Remove(); + + for( int i = 0; i < 5; ++i ) + { + SkillName skill; + double bonus; + + if( !GetValues( i, out skill, out bonus ) ) + continue; + + if( m_Mods == null ) + m_Mods = new ArrayList(); + + SkillMod sk = new DefaultSkillMod( skill, true, bonus ); + sk.ObeyCap = true; + m.AddSkillMod( sk ); + m_Mods.Add( sk ); + } + } + + public void Remove() + { + if( m_Mods == null ) + return; + + for( int i = 0; i < m_Mods.Count; ++i ) + ((SkillMod)m_Mods[i]).Remove(); + + m_Mods = null; + } + + public bool GetValues( int index, out SkillName skill, out double bonus ) + { + int v = GetValue( 1 << index ); + int vSkill = 0; + int vBonus = 0; + + for( int i = 0; i < 16; ++i ) + { + vSkill <<= 1; + vSkill |= (v & 1); + v >>= 1; + + vBonus <<= 1; + vBonus |= (v & 1); + v >>= 1; + } + + skill = (SkillName)vSkill; + bonus = (double)vBonus / 10; + + return (bonus != 0); + } + + public void SetValues( int index, SkillName skill, double bonus ) + { + int v = 0; + int vSkill = (int)skill; + int vBonus = (int)(bonus * 10); + + for( int i = 0; i < 16; ++i ) + { + v <<= 1; + v |= (vBonus & 1); + vBonus >>= 1; + + v <<= 1; + v |= (vSkill & 1); + vSkill >>= 1; + } + + SetValue( 1 << index, v ); + } + + public SkillName GetSkill( int index ) + { + SkillName skill; + double bonus; + + GetValues( index, out skill, out bonus ); + + return skill; + } + + public void SetSkill( int index, SkillName skill ) + { + SetValues( index, skill, GetBonus( index ) ); + } + + public double GetBonus( int index ) + { + SkillName skill; + double bonus; + + GetValues( index, out skill, out bonus ); + + return bonus; + } + + public void SetBonus( int index, double bonus ) + { + SetValues( index, GetSkill( index ), bonus ); + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.GameMaster )] + public double Skill_1_Value { get { return GetBonus( 0 ); } set { SetBonus( 0, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill_1_Name { get { return GetSkill( 0 ); } set { SetSkill( 0, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public double Skill_2_Value { get { return GetBonus( 1 ); } set { SetBonus( 1, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill_2_Name { get { return GetSkill( 1 ); } set { SetSkill( 1, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public double Skill_3_Value { get { return GetBonus( 2 ); } set { SetBonus( 2, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill_3_Name { get { return GetSkill( 2 ); } set { SetSkill( 2, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public double Skill_4_Value { get { return GetBonus( 3 ); } set { SetBonus( 3, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill_4_Name { get { return GetSkill( 3 ); } set { SetSkill( 3, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public double Skill_5_Value { get { return GetBonus( 4 ); } set { SetBonus( 4, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public SkillName Skill_5_Name { get { return GetSkill( 4 ); } set { SetSkill( 4, value ); } } + } + + [Flags] + public enum AosElementAttribute + { + Physical=0x00000001, + Fire=0x00000002, + Cold=0x00000004, + Poison=0x00000008, + Energy=0x00000010 + } + + public sealed class AosElementAttributes : BaseAttributes + { + public AosElementAttributes( Item owner ) + : base( owner ) + { + } + + public AosElementAttributes( Item owner, GenericReader reader ) + : base( owner, reader ) + { + } + + public int this[AosElementAttribute attribute] + { + get { return GetValue( (int)attribute ); } + set { SetValue( (int)attribute, value ); } + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Physical { get { return this[AosElementAttribute.Physical]; } set { this[AosElementAttribute.Physical] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Fire { get { return this[AosElementAttribute.Fire]; } set { this[AosElementAttribute.Fire] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Cold { get { return this[AosElementAttribute.Cold]; } set { this[AosElementAttribute.Cold] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Poison { get { return this[AosElementAttribute.Poison]; } set { this[AosElementAttribute.Poison] = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Energy { get { return this[AosElementAttribute.Energy]; } set { this[AosElementAttribute.Energy] = value; } } + } + + [PropertyObject] + public abstract class BaseAttributes + { + private Item m_Owner; + private uint m_Names; + private int[] m_Values; + + private static int[] m_Empty = new int[0]; + + public bool IsEmpty { get { return (m_Names == 0); } } + public Item Owner { get { return m_Owner; } } + + public BaseAttributes( Item owner ) + { + m_Owner = owner; + m_Values = m_Empty; + } + + public BaseAttributes( Item owner, GenericReader reader ) + { + m_Owner = owner; + + int version = reader.ReadByte(); + + switch( version ) + { + case 1: + { + m_Names = reader.ReadUInt(); + m_Values = new int[reader.ReadEncodedInt()]; + + for( int i = 0; i < m_Values.Length; ++i ) + m_Values[i] = reader.ReadEncodedInt(); + + break; + } + case 0: + { + m_Names = reader.ReadUInt(); + m_Values = new int[reader.ReadInt()]; + + for( int i = 0; i < m_Values.Length; ++i ) + m_Values[i] = reader.ReadInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (byte)1 ); // version; + + writer.Write( (uint)m_Names ); + writer.WriteEncodedInt( (int)m_Values.Length ); + + for( int i = 0; i < m_Values.Length; ++i ) + writer.WriteEncodedInt( (int)m_Values[i] ); + } + + public int GetValue( int bitmask ) + { + if( !Core.AOS ) + return 0; + + uint mask = (uint)bitmask; + + if( (m_Names & mask) == 0 ) + return 0; + + int index = GetIndex( mask ); + + if( index >= 0 && index < m_Values.Length ) + return m_Values[index]; + + return 0; + } + + public void SetValue( int bitmask, int value ) + { + if( (bitmask == (int)AosWeaponAttribute.DurabilityBonus) && (this is AosWeaponAttributes) ) + { + if( m_Owner is BaseWeapon ) + ((BaseWeapon)m_Owner).UnscaleDurability(); + } + else if( (bitmask == (int)AosArmorAttribute.DurabilityBonus) && (this is AosArmorAttributes) ) + { + if( m_Owner is BaseArmor ) + ((BaseArmor)m_Owner).UnscaleDurability(); + } + + uint mask = (uint)bitmask; + + if( value != 0 ) + { + if( (m_Names & mask) != 0 ) + { + int index = GetIndex( mask ); + + if( index >= 0 && index < m_Values.Length ) + m_Values[index] = value; + } + else + { + int index = GetIndex( mask ); + + if( index >= 0 && index <= m_Values.Length ) + { + int[] old = m_Values; + m_Values = new int[old.Length + 1]; + + for( int i = 0; i < index; ++i ) + m_Values[i] = old[i]; + + m_Values[index] = value; + + for( int i = index; i < old.Length; ++i ) + m_Values[i + 1] = old[i]; + + m_Names |= mask; + } + } + } + else if( (m_Names & mask) != 0 ) + { + int index = GetIndex( mask ); + + if( index >= 0 && index < m_Values.Length ) + { + m_Names &= ~mask; + + if( m_Values.Length == 1 ) + { + m_Values = m_Empty; + } + else + { + int[] old = m_Values; + m_Values = new int[old.Length - 1]; + + for( int i = 0; i < index; ++i ) + m_Values[i] = old[i]; + + for( int i = index + 1; i < old.Length; ++i ) + m_Values[i - 1] = old[i]; + } + } + } + + if( (bitmask == (int)AosWeaponAttribute.DurabilityBonus) && (this is AosWeaponAttributes) ) + { + if( m_Owner is BaseWeapon ) + ((BaseWeapon)m_Owner).ScaleDurability(); + } + else if( (bitmask == (int)AosArmorAttribute.DurabilityBonus) && (this is AosArmorAttributes) ) + { + if( m_Owner is BaseArmor ) + ((BaseArmor)m_Owner).ScaleDurability(); + } + + if( m_Owner.Parent is Mobile ) + { + Mobile m = (Mobile)m_Owner.Parent; + + m.CheckStatTimers(); + m.UpdateResistances(); + m.Delta( MobileDelta.Stat | MobileDelta.WeaponDamage | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana ); + + if( this is AosSkillBonuses ) + { + ((AosSkillBonuses)this).Remove(); + ((AosSkillBonuses)this).AddTo( m ); + } + } + + m_Owner.InvalidateProperties(); + } + + private int GetIndex( uint mask ) + { + int index = 0; + uint ourNames = m_Names; + uint currentBit = 1; + + while( currentBit != mask ) + { + if( (ourNames & currentBit) != 0 ) + ++index; + + if( currentBit == 0x80000000 ) + return -1; + + currentBit <<= 1; + } + + return index; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/AccountPrompt.cs b/Scripts/Misc/AccountPrompt.cs new file mode 100644 index 000000000..feef74a62 --- /dev/null +++ b/Scripts/Misc/AccountPrompt.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Accounting; + +namespace Server.Misc +{ + public class AccountPrompt + { + // This script prompts the console for a username and password when 0 accounts have been loaded + public static void Initialize() + { + if ( Accounts.Count == 0 && !Core.Service ) + { + Console.WriteLine( "This server has no accounts." ); + Console.Write( "Do you want to create an administrator account now? (y/n)" ); + + if( Console.ReadKey( true ).Key == ConsoleKey.Y ) + { + Console.WriteLine(); + + Console.Write( "Username: " ); + string username = Console.ReadLine(); + + Console.Write( "Password: " ); + string password = Console.ReadLine(); + + Account a = new Account( username, password ); + a.AccessLevel = AccessLevel.Owner; + + Console.WriteLine( "Account created." ); + } + else + { + Console.WriteLine(); + + Console.WriteLine( "Account not created." ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Animations.cs b/Scripts/Misc/Animations.cs new file mode 100644 index 000000000..4e79335f7 --- /dev/null +++ b/Scripts/Misc/Animations.cs @@ -0,0 +1,30 @@ +using System; +using Server; + +namespace Server.Misc +{ + public class Animations + { + public static void Initialize() + { + EventSink.AnimateRequest += new AnimateRequestEventHandler( EventSink_AnimateRequest ); + } + + private static void EventSink_AnimateRequest( AnimateRequestEventArgs e ) + { + Mobile from = e.Mobile; + + int action; + + switch ( e.Action ) + { + case "bow": action = 32; break; + case "salute": action = 33; break; + default: return; + } + + if ( from.Alive && !from.Mounted && from.Body.IsHuman ) + from.Animate( action, 5, 1, true, false, 0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/AttackMessage.cs b/Scripts/Misc/AttackMessage.cs new file mode 100644 index 000000000..7f0300465 --- /dev/null +++ b/Scripts/Misc/AttackMessage.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; + +namespace Server.Misc +{ + public class AttackMessage + { + private const string AggressorFormat = "You are attacking {0}!"; + private const string AggressedFormat = "{0} is attacking you!"; + private const int Hue = 0x22; + + private static TimeSpan Delay = TimeSpan.FromMinutes( 1.0 ); + + public static void Initialize() + { + EventSink.AggressiveAction += new AggressiveActionEventHandler( EventSink_AggressiveAction ); + } + + public static void EventSink_AggressiveAction( AggressiveActionEventArgs e ) + { + Mobile aggressor = e.Aggressor; + Mobile aggressed = e.Aggressed; + + if ( !aggressor.Player || !aggressed.Player ) + return; + + if ( !CheckAggressions( aggressor, aggressed ) ) + { + aggressor.LocalOverheadMessage( MessageType.Regular, Hue, true, String.Format( AggressorFormat, aggressed.Name ) ); + aggressed.LocalOverheadMessage( MessageType.Regular, Hue, true, String.Format( AggressedFormat, aggressor.Name ) ); + } + } + + public static bool CheckAggressions( Mobile m1, Mobile m2 ) + { + List list = m1.Aggressors; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Attacker == m2 && DateTime.Now < (info.LastCombatTime + Delay) ) + return true; + } + + list = m2.Aggressors; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Attacker == m1 && DateTime.Now < (info.LastCombatTime + Delay) ) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/AutoRestart.cs b/Scripts/Misc/AutoRestart.cs new file mode 100644 index 000000000..6cbce6098 --- /dev/null +++ b/Scripts/Misc/AutoRestart.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Diagnostics; +using Server; +using System.Text; +using Server.Commands; + +namespace Server.Misc +{ + public class AutoRestart : Timer + { + public static bool Enabled = false; // is the script enabled? + + private static TimeSpan RestartTime = TimeSpan.FromHours( 2.0 ); // time of day at which to restart + private static TimeSpan RestartDelay = TimeSpan.Zero; // how long the server should remain active before restart (period of 'server wars') + + private static TimeSpan WarningDelay = TimeSpan.FromMinutes( 1.0 ); // at what interval should the shutdown message be displayed? + + private static bool m_Restarting; + private static DateTime m_RestartTime; + + public static bool Restarting + { + get{ return m_Restarting; } + } + + public static void Initialize() + { + CommandSystem.Register( "Restart", AccessLevel.Administrator, new CommandEventHandler( Restart_OnCommand ) ); + new AutoRestart().Start(); + } + + public static void Restart_OnCommand( CommandEventArgs e ) + { + if ( m_Restarting ) + { + e.Mobile.SendMessage( "The server is already restarting." ); + } + else + { + e.Mobile.SendMessage( "You have initiated server shutdown." ); + Enabled = true; + m_RestartTime = DateTime.Now; + } + } + + public AutoRestart() : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + Priority = TimerPriority.FiveSeconds; + + m_RestartTime = DateTime.Now.Date + RestartTime; + + if ( m_RestartTime < DateTime.Now ) + m_RestartTime += TimeSpan.FromDays( 1.0 ); + } + + private void Warning_Callback() + { + World.Broadcast( 0x22, true, "The server is going down shortly." ); + } + + private void Restart_Callback() + { + Process.Start( Core.ExePath, Core.Arguments ); + Core.Process.Kill(); + } + + protected override void OnTick() + { + if ( m_Restarting || !Enabled ) + return; + + if ( DateTime.Now < m_RestartTime ) + return; + + if ( WarningDelay > TimeSpan.Zero ) + { + Warning_Callback(); + Timer.DelayCall( WarningDelay, WarningDelay, new TimerCallback( Warning_Callback ) ); + } + + AutoSave.Save(); + + m_Restarting = true; + + Timer.DelayCall( RestartDelay, new TimerCallback( Restart_Callback ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/AutoSave.cs b/Scripts/Misc/AutoSave.cs new file mode 100644 index 000000000..28d464a67 --- /dev/null +++ b/Scripts/Misc/AutoSave.cs @@ -0,0 +1,181 @@ +using System; +using System.IO; +using Server; +using Server.Commands; + +namespace Server.Misc +{ + public class AutoSave : Timer + { + private static TimeSpan m_Delay = TimeSpan.FromMinutes( 5.0 ); + private static TimeSpan m_Warning = TimeSpan.Zero; + //private static TimeSpan m_Warning = TimeSpan.FromSeconds( 15.0 ); + + public static void Initialize() + { + new AutoSave().Start(); + CommandSystem.Register( "SetSaves", AccessLevel.Administrator, new CommandEventHandler( SetSaves_OnCommand ) ); + } + + private static bool m_SavesEnabled = true; + + public static bool SavesEnabled + { + get{ return m_SavesEnabled; } + set{ m_SavesEnabled = value; } + } + + [Usage( "SetSaves " )] + [Description( "Enables or disables automatic shard saving." )] + public static void SetSaves_OnCommand( CommandEventArgs e ) + { + if ( e.Length == 1 ) + { + m_SavesEnabled = e.GetBoolean( 0 ); + e.Mobile.SendMessage( "Saves have been {0}.", m_SavesEnabled ? "enabled" : "disabled" ); + } + else + { + e.Mobile.SendMessage( "Format: SetSaves " ); + } + } + + public AutoSave() : base( m_Delay - m_Warning, m_Delay ) + { + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + if ( !m_SavesEnabled || AutoRestart.Restarting ) + return; + + if ( m_Warning == TimeSpan.Zero ) + { + Save(); + } + else + { + int s = (int)m_Warning.TotalSeconds; + int m = s / 60; + s %= 60; + + if ( m > 0 && s > 0 ) + World.Broadcast( 0x35, true, "The world will save in {0} minute{1} and {2} second{3}.", m, m != 1 ? "s" : "", s, s != 1 ? "s" : "" ); + else if ( m > 0 ) + World.Broadcast( 0x35, true, "The world will save in {0} minute{1}.", m, m != 1 ? "s" : "" ); + else + World.Broadcast( 0x35, true, "The world will save in {0} second{1}.", s, s != 1 ? "s" : "" ); + + Timer.DelayCall( m_Warning, new TimerCallback( Save ) ); + } + } + + public static void Save() + { + if ( AutoRestart.Restarting ) + return; + + try{ Backup(); } + catch{} + + World.Save(); + } + + private static string[] m_Backups = new string[] + { + "Third Backup", + "Second Backup", + "Most Recent" + }; + + private static void Backup() + { + if ( m_Backups.Length == 0 ) + return; + + string root = Path.Combine( Core.BaseDirectory, "Backups\\Automatic" ); + + if ( !Directory.Exists( root ) ) + Directory.CreateDirectory( root ); + + string[] existing = Directory.GetDirectories( root ); + + for ( int i = 0; i < m_Backups.Length; ++i ) + { + DirectoryInfo dir = Match( existing, m_Backups[i] ); + + if ( dir == null ) + continue; + + if ( i > 0 ) + { + string timeStamp = FindTimeStamp( dir.Name ); + + if ( timeStamp != null ) + { + try{ dir.MoveTo( FormatDirectory( root, m_Backups[i - 1], timeStamp ) ); } + catch{} + } + } + else + { + try{ dir.Delete( true ); } + catch{} + } + } + + string saves = Path.Combine( Core.BaseDirectory, "Saves" ); + + if ( Directory.Exists( saves ) ) + Directory.Move( saves, FormatDirectory( root, m_Backups[m_Backups.Length - 1], GetTimeStamp() ) ); + } + + private static DirectoryInfo Match( string[] paths, string match ) + { + for ( int i = 0; i < paths.Length; ++i ) + { + DirectoryInfo info = new DirectoryInfo( paths[i] ); + + if ( info.Name.StartsWith( match ) ) + return info; + } + + return null; + } + + private static string FormatDirectory( string root, string name, string timeStamp ) + { + return Path.Combine( root, String.Format( "{0} ({1})", name, timeStamp ) ); + } + + private static string FindTimeStamp( string input ) + { + int start = input.IndexOf( '(' ); + + if ( start >= 0 ) + { + int end = input.IndexOf( ')', ++start ); + + if ( end >= start ) + return input.Substring( start, end-start ); + } + + return null; + } + + private static string GetTimeStamp() + { + DateTime now = DateTime.Now; + + return String.Format( "{0}-{1}-{2} {3}-{4:D2}-{5:D2}", + now.Day, + now.Month, + now.Year, + now.Hour, + now.Minute, + now.Second + ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Broadcasts.cs b/Scripts/Misc/Broadcasts.cs new file mode 100644 index 000000000..39bfb1964 --- /dev/null +++ b/Scripts/Misc/Broadcasts.cs @@ -0,0 +1,36 @@ +using System; +using Server; + +namespace Server.Misc +{ + public class Broadcasts + { + public static void Initialize() + { + EventSink.Crashed += new CrashedEventHandler( EventSink_Crashed ); + EventSink.Shutdown += new ShutdownEventHandler( EventSink_Shutdown ); + } + + public static void EventSink_Crashed( CrashedEventArgs e ) + { + try + { + World.Broadcast( 0x35, true, "The server has crashed." ); + } + catch + { + } + } + + public static void EventSink_Shutdown( ShutdownEventArgs e ) + { + try + { + World.Broadcast( 0x35, true, "The server has shut down." ); + } + catch + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/BuffIcons.cs b/Scripts/Misc/BuffIcons.cs new file mode 100644 index 000000000..a3a0053bf --- /dev/null +++ b/Scripts/Misc/BuffIcons.cs @@ -0,0 +1,301 @@ +using System; +using Server; +using Server.Network; +using System.Collections.Generic; +using Server.Mobiles; + +namespace Server +{ + public class BuffInfo + { + public static bool Enabled { get { return Core.ML; } } + + public static void Initialize() + { + //To Ensure that it's sent only AFTER we get the client's version, and right after. + if( Enabled ) + { + PacketHandlers.Register( 0xBD, 0, true, new OnPacketReceive + ( + delegate( NetState state, PacketReader pvSrc ) + { + PacketHandlers.ClientVersion( state, pvSrc ); + + PlayerMobile pm = state.Mobile as PlayerMobile; + + if( pm != null ) + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( pm.ResendBuffs ) ); + } + } + ) ); + } + } + + private static readonly ClientVersion m_RequiredClient = new ClientVersion( "5.0.2b" ); + public static ClientVersion RequiredClient { get { return m_RequiredClient; } } + + #region Properties + private BuffIcon m_ID; + public BuffIcon ID { get { return m_ID; } } + + private int m_TitleCliloc; + public int TitleCliloc { get { return m_TitleCliloc; } } + + private int m_SecondaryCliloc; + public int SecondaryCliloc { get { return m_SecondaryCliloc; } } + + private TimeSpan m_TimeLength; + public TimeSpan TimeLength { get { return m_TimeLength; } } + + private DateTime m_TimeStart; + public DateTime TimeStart { get { return m_TimeStart; } } + + private Timer m_Timer; + public Timer Timer { get { return m_Timer; } } + + private bool m_RetainThroughDeath; + public bool RetainThroughDeath { get { return m_RetainThroughDeath; } } + + private TextDefinition m_Args; + public TextDefinition Args { get { return m_Args; } } + + #endregion + + #region Constructors + public BuffInfo( BuffIcon iconID, int titleCliloc ) + : this( iconID, titleCliloc, titleCliloc + 1 ) + { + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc ) + { + m_ID = iconID; + m_TitleCliloc = titleCliloc; + m_SecondaryCliloc = secondaryCliloc; + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m ) + : this( iconID, titleCliloc, titleCliloc + 1, length, m ) + { + } + + //Only the timed one needs to Mobile to know when to automagically remove it. + public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m ) + : this( iconID, titleCliloc, secondaryCliloc ) + { + m_TimeLength = length; + m_TimeStart = DateTime.Now; + + m_Timer = Timer.DelayCall( length, new TimerCallback( + delegate + { + PlayerMobile pm = m as PlayerMobile; + + if( pm == null ) + return; + + pm.RemoveBuff( this ); + } ) ); + } + + + public BuffInfo( BuffIcon iconID, int titleCliloc, TextDefinition args ) + : this( iconID, titleCliloc, titleCliloc + 1, args ) + { + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args ) + : this( iconID, titleCliloc, secondaryCliloc ) + { + m_Args = args; + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, bool retainThroughDeath ) + : this( iconID, titleCliloc, titleCliloc + 1, retainThroughDeath ) + { + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, bool retainThroughDeath ) + : this( iconID, titleCliloc, secondaryCliloc ) + { + m_RetainThroughDeath = retainThroughDeath; + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, TextDefinition args, bool retainThroughDeath ) + : this( iconID, titleCliloc, titleCliloc + 1, args, retainThroughDeath ) + { + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, bool retainThroughDeath ) + : this( iconID, titleCliloc, secondaryCliloc, args ) + { + m_RetainThroughDeath = retainThroughDeath; + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, TimeSpan length, Mobile m, TextDefinition args ) + : this( iconID, titleCliloc, titleCliloc + 1, length, m, args ) + { + } + + public BuffInfo( BuffIcon iconID, int titleCliloc, int secondaryCliloc, TimeSpan length, Mobile m, TextDefinition args ) + : this( iconID, titleCliloc, secondaryCliloc, length, m ) + { + m_Args = args; + } + #endregion + + #region Convience Methods + public static void AddBuff( Mobile m, BuffInfo b ) + { + PlayerMobile pm = m as PlayerMobile; + + if( pm != null ) + pm.AddBuff( b ); + } + + public static void RemoveBuff( Mobile m, BuffInfo b ) + { + PlayerMobile pm = m as PlayerMobile; + + if( pm != null ) + pm.RemoveBuff( b ); + } + + public static void RemoveBuff( Mobile m, BuffIcon b ) + { + PlayerMobile pm = m as PlayerMobile; + + if( pm != null ) + pm.RemoveBuff( b ); + } + #endregion + } + + public enum BuffIcon : short + { + DismountPrevention=0x3E9, + NoRearm=0x3EA, + //Currently, no 0x3EB or 0x3EC + NightSight=0x3ED, //* + DeathStrike, + EvilOmen, + UnknownStandingSwirl, //Which is healing throttle & Stamina throttle? + UnknownKneelingSword, + DivineFury, + EnemyOfOne, + HidingAndOrStealth, //* + ActiveMeditation, //* + BloodOathCaster, + BloodOathCurse, + CorpseSkin, //* + Mindrot, //* + PainSpike, //* + Strangle, + GiftOfRenewal, + AttuneWeapon, + Thunderstorm, + EssenceOfWind, + EtherealVoyage, + GiftOfLife, + ArcaneEmpowerment, + MortalStrike, + ReactiveArmor, + Protection, + ArchProtection, + MagicReflection, //* + Incognito, //* + Disguised, + AnimalForm, + Polymorph, + Invisibility, //* + Paralyze, //* + Poison, + Bleed, + Clumsy, //* + FeebleMind, //* + Weaken, //* + Curse, + MassCurse, + Agility, //* + Cunning, //* + Strength, //* + Bless + } + + public class AddBuffPacket : Packet + { + public AddBuffPacket( Mobile m, BuffInfo info ) + : this( m, info.ID, info.TitleCliloc, info.SecondaryCliloc, info.Args, (info.TimeStart != DateTime.MinValue) ? ((info.TimeStart + info.TimeLength) - DateTime.Now) : TimeSpan.Zero ) + { + } + public AddBuffPacket( Mobile mob, BuffIcon iconID, int titleCliloc, int secondaryCliloc, TextDefinition args, TimeSpan length ) + : base( 0xDF ) + { + bool hasArgs = (args != null); + + this.EnsureCapacity( (hasArgs ? (48 + args.ToString().Length * 2): 44) ); + m_Stream.Write( (int)mob.Serial ); + + + m_Stream.Write( (short)iconID ); //ID + m_Stream.Write( (short)0x1 ); //Type 0 for removal. 1 for add 2 for Data + + m_Stream.Fill( 4 ); + + m_Stream.Write( (short)iconID ); //ID + m_Stream.Write( (short)0x01 ); //Type 0 for removal. 1 for add 2 for Data + + m_Stream.Fill( 4 ); + + if( length < TimeSpan.Zero ) + length = TimeSpan.Zero; + + m_Stream.Write( (short)length.TotalSeconds ); //Time in seconds + + m_Stream.Fill( 3 ); + m_Stream.Write( (int)titleCliloc ); + m_Stream.Write( (int)secondaryCliloc ); + + if( !hasArgs ) + { + //m_Stream.Fill( 2 ); + m_Stream.Fill( 10 ); + } + else + { + m_Stream.Fill( 4 ); + m_Stream.Write( (short)0x1 ); //Unknown -> Possibly something saying 'hey, I have more data!'? + m_Stream.Fill( 2 ); + + //m_Stream.WriteLittleUniNull( "\t#1018280" ); + m_Stream.WriteLittleUniNull( String.Format( "\t{0}", args.ToString() ) ); + + m_Stream.Write( (short)0x1 ); //Even more Unknown -> Possibly something saying 'hey, I have more data!'? + m_Stream.Fill( 2 ); + } + } + } + + public class RemoveBuffPacket : Packet + { + public RemoveBuffPacket( Mobile mob, BuffInfo info ) + : this( mob, info.ID ) + { + } + + public RemoveBuffPacket( Mobile mob, BuffIcon iconID ) + : base( 0xDF ) + { + this.EnsureCapacity( 13 ); + m_Stream.Write( (int)mob.Serial ); + + + m_Stream.Write( (short)iconID ); //ID + m_Stream.Write( (short)0x0 ); //Type 0 for removal. 1 for add 2 for Data + + m_Stream.Fill( 4 ); + } + } + +} diff --git a/Scripts/Misc/CharacterCreation.cs b/Scripts/Misc/CharacterCreation.cs new file mode 100644 index 000000000..8b00e52cb --- /dev/null +++ b/Scripts/Misc/CharacterCreation.cs @@ -0,0 +1,1678 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Accounting; + +namespace Server.Misc +{ + public class CharacterCreation + { + public static void Initialize() + { + // Register our event handler + EventSink.CharacterCreated += new CharacterCreatedEventHandler( EventSink_CharacterCreated ); + } + + private static void AddBackpack( Mobile m ) + { + Container pack = m.Backpack; + + if ( pack == null ) + { + pack = new Backpack(); + pack.Movable = false; + + m.AddItem( pack ); + } + + PackItem( new RedBook( "a book", m.Name, 20, true ) ); + PackItem( new Gold( 1000 ) ); // Starting gold can be customized here + PackItem( new Dagger() ); + PackItem( new Candle() ); + } + + private static Item MakeNewbie( Item item ) + { + if ( !Core.AOS ) + item.LootType = LootType.Newbied; + + return item; + } + + private static void PlaceItemIn( Container parent, int x, int y, Item item ) + { + parent.AddItem( item ); + item.Location = new Point3D( x, y, 0 ); + } + + private static Item MakePotionKeg( PotionEffect type, int hue ) + { + PotionKeg keg = new PotionKeg(); + + keg.Held = 100; + keg.Type = type; + keg.Hue = hue; + + return MakeNewbie( keg ); + } + + private static void FillBankAOS( Mobile m ) + { + BankBox bank = m.BankBox; + + if ( bank == null ) + return; + + // The new AOS bankboxes don't have powerscrolls, they are automatically 'applied': + + for ( int i = 0; i < PowerScroll.Skills.Length; ++i ) + m.Skills[PowerScroll.Skills[ i ]].Cap = 120.0; + + m.StatCap = 250; + + + Container cont; + + + // Begin box of money + cont = new WoodenBox(); + cont.ItemID = 0xE7D; + cont.Hue = 0x489; + + PlaceItemIn( cont, 16, 51, new BankCheck( 500000 ) ); + PlaceItemIn( cont, 28, 51, new BankCheck( 250000 ) ); + PlaceItemIn( cont, 40, 51, new BankCheck( 100000 ) ); + PlaceItemIn( cont, 52, 51, new BankCheck( 100000 ) ); + PlaceItemIn( cont, 64, 51, new BankCheck( 50000 ) ); + + PlaceItemIn( cont, 16, 115, new Factions.Silver( 9000 ) ); + PlaceItemIn( cont, 34, 115, new Gold( 60000 ) ); + + PlaceItemIn( bank, 18, 169, cont ); + // End box of money + + + // Begin bag of potion kegs + cont = new Backpack(); + cont.Name = "Various Potion Kegs"; + + PlaceItemIn( cont, 45, 149, MakePotionKeg( PotionEffect.CureGreater, 0x2D ) ); + PlaceItemIn( cont, 69, 149, MakePotionKeg( PotionEffect.HealGreater, 0x499 ) ); + PlaceItemIn( cont, 93, 149, MakePotionKeg( PotionEffect.PoisonDeadly, 0x46 ) ); + PlaceItemIn( cont, 117, 149, MakePotionKeg( PotionEffect.RefreshTotal, 0x21 ) ); + PlaceItemIn( cont, 141, 149, MakePotionKeg( PotionEffect.ExplosionGreater, 0x74 ) ); + + PlaceItemIn( cont, 93, 82, new Bottle( 1000 ) ); + + PlaceItemIn( bank, 53, 169, cont ); + // End bag of potion kegs + + + // Begin bag of tools + cont = new Bag(); + cont.Name = "Tool Bag"; + + PlaceItemIn( cont, 30, 35, new TinkerTools( 1000 ) ); + PlaceItemIn( cont, 60, 35, new HousePlacementTool() ); + PlaceItemIn( cont, 90, 35, new DovetailSaw( 1000 ) ); + PlaceItemIn( cont, 30, 68, new Scissors() ); + PlaceItemIn( cont, 45, 68, new MortarPestle( 1000 ) ); + PlaceItemIn( cont, 75, 68, new ScribesPen( 1000 ) ); + PlaceItemIn( cont, 90, 68, new SmithHammer( 1000 ) ); + PlaceItemIn( cont, 30, 118, new TwoHandedAxe() ); + PlaceItemIn( cont, 60, 118, new FletcherTools( 1000 ) ); + PlaceItemIn( cont, 90, 118, new SewingKit( 1000 ) ); + + PlaceItemIn( cont, 36, 51, new RunicHammer( CraftResource.DullCopper, 1000 ) ); + PlaceItemIn( cont, 42, 51, new RunicHammer( CraftResource.ShadowIron, 1000 ) ); + PlaceItemIn( cont, 48, 51, new RunicHammer( CraftResource.Copper, 1000 ) ); + PlaceItemIn( cont, 54, 51, new RunicHammer( CraftResource.Bronze, 1000 ) ); + PlaceItemIn( cont, 61, 51, new RunicHammer( CraftResource.Gold, 1000 ) ); + PlaceItemIn( cont, 67, 51, new RunicHammer( CraftResource.Agapite, 1000 ) ); + PlaceItemIn( cont, 73, 51, new RunicHammer( CraftResource.Verite, 1000 ) ); + PlaceItemIn( cont, 79, 51, new RunicHammer( CraftResource.Valorite, 1000 ) ); + + PlaceItemIn( cont, 36, 55, new RunicSewingKit( CraftResource.SpinedLeather, 1000 ) ); + PlaceItemIn( cont, 42, 55, new RunicSewingKit( CraftResource.HornedLeather, 1000 ) ); + PlaceItemIn( cont, 48, 55, new RunicSewingKit( CraftResource.BarbedLeather, 1000 ) ); + + PlaceItemIn( bank, 118, 169, cont ); + // End bag of tools + + + // Begin bag of archery ammo + cont = new Bag(); + cont.Name = "Bag Of Archery Ammo"; + + PlaceItemIn( cont, 48, 76, new Arrow( 5000 ) ); + PlaceItemIn( cont, 72, 76, new Bolt( 5000 ) ); + + PlaceItemIn( bank, 118, 124, cont ); + // End bag of archery ammo + + + // Begin bag of treasure maps + cont = new Bag(); + cont.Name = "Bag Of Treasure Maps"; + + PlaceItemIn( cont, 30, 35, new TreasureMap( 1, Map.Trammel ) ); + PlaceItemIn( cont, 45, 35, new TreasureMap( 2, Map.Trammel ) ); + PlaceItemIn( cont, 60, 35, new TreasureMap( 3, Map.Trammel ) ); + PlaceItemIn( cont, 75, 35, new TreasureMap( 4, Map.Trammel ) ); + PlaceItemIn( cont, 90, 35, new TreasureMap( 5, Map.Trammel ) ); + PlaceItemIn( cont, 90, 35, new TreasureMap( 6, Map.Trammel ) ); + + PlaceItemIn( cont, 30, 50, new TreasureMap( 1, Map.Trammel ) ); + PlaceItemIn( cont, 45, 50, new TreasureMap( 2, Map.Trammel ) ); + PlaceItemIn( cont, 60, 50, new TreasureMap( 3, Map.Trammel ) ); + PlaceItemIn( cont, 75, 50, new TreasureMap( 4, Map.Trammel ) ); + PlaceItemIn( cont, 90, 50, new TreasureMap( 5, Map.Trammel ) ); + PlaceItemIn( cont, 90, 50, new TreasureMap( 6, Map.Trammel ) ); + + PlaceItemIn( cont, 55, 100, new Lockpick( 30 ) ); + PlaceItemIn( cont, 60, 100, new Pickaxe() ); + + PlaceItemIn( bank, 98, 124, cont ); + // End bag of treasure maps + + + // Begin bag of raw materials + cont = new Bag(); + cont.Hue = 0x835; + cont.Name = "Raw Materials Bag"; + + PlaceItemIn( cont, 92, 60, new BarbedLeather( 5000 ) ); + PlaceItemIn( cont, 92, 68, new HornedLeather( 5000 ) ); + PlaceItemIn( cont, 92, 76, new SpinedLeather( 5000 ) ); + PlaceItemIn( cont, 92, 84, new Leather( 5000 ) ); + + PlaceItemIn( cont, 30, 118, new Cloth( 5000 ) ); + PlaceItemIn( cont, 30, 84, new Board( 5000 ) ); + PlaceItemIn( cont, 57, 80, new BlankScroll( 500 ) ); + + PlaceItemIn( cont, 30, 35, new DullCopperIngot( 5000 ) ); + PlaceItemIn( cont, 37, 35, new ShadowIronIngot( 5000 ) ); + PlaceItemIn( cont, 44, 35, new CopperIngot( 5000 ) ); + PlaceItemIn( cont, 51, 35, new BronzeIngot( 5000 ) ); + PlaceItemIn( cont, 58, 35, new GoldIngot( 5000 ) ); + PlaceItemIn( cont, 65, 35, new AgapiteIngot( 5000 ) ); + PlaceItemIn( cont, 72, 35, new VeriteIngot( 5000 ) ); + PlaceItemIn( cont, 79, 35, new ValoriteIngot( 5000 ) ); + PlaceItemIn( cont, 86, 35, new IronIngot( 5000 ) ); + + PlaceItemIn( cont, 30, 59, new RedScales( 5000 ) ); + PlaceItemIn( cont, 36, 59, new YellowScales( 5000 ) ); + PlaceItemIn( cont, 42, 59, new BlackScales( 5000 ) ); + PlaceItemIn( cont, 48, 59, new GreenScales( 5000 ) ); + PlaceItemIn( cont, 54, 59, new WhiteScales( 5000 ) ); + PlaceItemIn( cont, 60, 59, new BlueScales( 5000 ) ); + + PlaceItemIn( bank, 98, 169, cont ); + // End bag of raw materials + + + // Begin bag of spell casting stuff + cont = new Backpack(); + cont.Hue = 0x480; + cont.Name = "Spell Casting Stuff"; + + PlaceItemIn( cont, 45, 105, new Spellbook( UInt64.MaxValue ) ); + PlaceItemIn( cont, 65, 105, new NecromancerSpellbook( (UInt64)0xFFFF ) ); + PlaceItemIn( cont, 85, 105, new BookOfChivalry( (UInt64)0x3FF ) ); + PlaceItemIn( cont, 105, 105, new BookOfBushido() ); //Default ctor = full + PlaceItemIn( cont, 125, 105, new BookOfNinjitsu() ); //Default ctor = full + + Runebook runebook = new Runebook( 10 ); + runebook.CurCharges = runebook.MaxCharges; + PlaceItemIn( cont, 145, 105, runebook ); + + Item toHue = new BagOfReagents( 150 ); + toHue.Hue = 0x2D; + PlaceItemIn( cont, 45, 150, toHue ); + + toHue = new BagOfNecroReagents( 150 ); + toHue.Hue = 0x488; + PlaceItemIn( cont, 65, 150, toHue ); + + PlaceItemIn( cont, 140, 150, new BagOfAllReagents( 500 ) ); + + for ( int i = 0; i < 9; ++i ) + PlaceItemIn( cont, 45 + (i * 10), 75, new RecallRune() ); + + PlaceItemIn( cont, 141, 74, new FireHorn() ); + + PlaceItemIn( bank, 78, 169, cont ); + // End bag of spell casting stuff + + + // Begin bag of ethereals + cont = new Backpack(); + cont.Hue = 0x490; + cont.Name = "Bag Of Ethy's!"; + + PlaceItemIn( cont, 45, 66, new EtherealHorse() ); + PlaceItemIn( cont, 69, 82, new EtherealOstard() ); + PlaceItemIn( cont, 93, 99, new EtherealLlama() ); + PlaceItemIn( cont, 117, 115, new EtherealKirin() ); + PlaceItemIn( cont, 45, 132, new EtherealUnicorn() ); + PlaceItemIn( cont, 69, 66, new EtherealRidgeback() ); + PlaceItemIn( cont, 93, 82, new EtherealSwampDragon() ); + PlaceItemIn( cont, 117, 99, new EtherealBeetle() ); + + PlaceItemIn( bank, 38, 124, cont ); + // End bag of ethereals + + + // Begin first bag of artifacts + cont = new Backpack(); + cont.Hue = 0x48F; + cont.Name = "Bag of Artifacts"; + + PlaceItemIn( cont, 45, 66, new TitansHammer() ); + PlaceItemIn( cont, 69, 82, new InquisitorsResolution() ); + PlaceItemIn( cont, 93, 99, new BladeOfTheRighteous() ); + PlaceItemIn( cont, 117, 115, new ZyronicClaw() ); + + PlaceItemIn( bank, 58, 124, cont ); + // End first bag of artifacts + + + // Begin second bag of artifacts + cont = new Backpack(); + cont.Hue = 0x48F; + cont.Name = "Bag of Artifacts"; + + PlaceItemIn( cont, 45, 66, new GauntletsOfNobility() ); + PlaceItemIn( cont, 69, 82, new MidnightBracers() ); + PlaceItemIn( cont, 93, 99, new VoiceOfTheFallenKing() ); + PlaceItemIn( cont, 117, 115, new OrnateCrownOfTheHarrower() ); + PlaceItemIn( cont, 45, 132, new HelmOfInsight() ); + PlaceItemIn( cont, 69, 66, new HolyKnightsBreastplate() ); + PlaceItemIn( cont, 93, 82, new ArmorOfFortune() ); + PlaceItemIn( cont, 117, 99, new TunicOfFire() ); + PlaceItemIn( cont, 45, 115, new LeggingsOfBane() ); + PlaceItemIn( cont, 69, 132, new ArcaneShield() ); + PlaceItemIn( cont, 93, 66, new Aegis() ); + PlaceItemIn( cont, 117, 82, new RingOfTheVile() ); + PlaceItemIn( cont, 45, 99, new BraceletOfHealth() ); + PlaceItemIn( cont, 69, 115, new RingOfTheElements() ); + PlaceItemIn( cont, 93, 132, new OrnamentOfTheMagician() ); + PlaceItemIn( cont, 117, 66, new DivineCountenance() ); + PlaceItemIn( cont, 45, 82, new JackalsCollar() ); + PlaceItemIn( cont, 69, 99, new HuntersHeaddress() ); + PlaceItemIn( cont, 93, 115, new HatOfTheMagi() ); + PlaceItemIn( cont, 117, 132, new ShadowDancerLeggings() ); + PlaceItemIn( cont, 45, 66, new SpiritOfTheTotem() ); + PlaceItemIn( cont, 69, 82, new BladeOfInsanity() ); + PlaceItemIn( cont, 93, 99, new AxeOfTheHeavens() ); + PlaceItemIn( cont, 117, 115, new TheBeserkersMaul() ); + PlaceItemIn( cont, 45, 132, new Frostbringer() ); + PlaceItemIn( cont, 69, 66, new BreathOfTheDead() ); + PlaceItemIn( cont, 93, 82, new TheDragonSlayer() ); + PlaceItemIn( cont, 117, 99, new BoneCrusher() ); + PlaceItemIn( cont, 45, 115, new StaffOfTheMagi() ); + PlaceItemIn( cont, 69, 132, new SerpentsFang() ); + PlaceItemIn( cont, 93, 66, new LegacyOfTheDreadLord() ); + PlaceItemIn( cont, 117, 82, new TheTaskmaster() ); + PlaceItemIn( cont, 45, 99, new TheDryadBow() ); + + PlaceItemIn( bank, 78, 124, cont ); + // End second bag of artifacts + + // Begin bag of minor artifacts + cont = new Backpack(); + cont.Hue = 0x48F; + cont.Name = "Bag of Minor Artifacts"; + + + PlaceItemIn( cont, 45, 66, new LunaLance() ); + PlaceItemIn( cont, 69, 82, new VioletCourage() ); + PlaceItemIn( cont, 93, 99, new CavortingClub() ); + PlaceItemIn( cont, 117, 115, new CaptainQuacklebushsCutlass() ); + PlaceItemIn( cont, 45, 132, new NightsKiss() ); + PlaceItemIn( cont, 69, 66, new ShipModelOfTheHMSCape() ); + PlaceItemIn( cont, 93, 82, new AdmiralsHeartyRum() ); + PlaceItemIn( cont, 117, 99, new CandelabraOfSouls() ); + PlaceItemIn( cont, 45, 115, new IolosLute() ); + PlaceItemIn( cont, 69, 132, new GwennosHarp() ); + PlaceItemIn( cont, 93, 66, new ArcticDeathDealer() ); + PlaceItemIn( cont, 117, 82, new EnchantedTitanLegBone() ); + PlaceItemIn( cont, 45, 99, new NoxRangersHeavyCrossbow() ); + PlaceItemIn( cont, 69, 115, new BlazeOfDeath() ); + PlaceItemIn( cont, 93, 132, new DreadPirateHat() ); + PlaceItemIn( cont, 117, 66, new BurglarsBandana() ); + PlaceItemIn( cont, 45, 82, new GoldBricks() ); + PlaceItemIn( cont, 69, 99, new AlchemistsBauble() ); + PlaceItemIn( cont, 93, 115, new PhillipsWoodenSteed() ); + PlaceItemIn( cont, 117, 132, new PolarBearMask() ); + PlaceItemIn( cont, 45, 66, new BowOfTheJukaKing() ); + PlaceItemIn( cont, 69, 82, new GlovesOfThePugilist() ); + PlaceItemIn( cont, 93, 99, new OrcishVisage() ); + PlaceItemIn( cont, 117, 115, new StaffOfPower() ); + PlaceItemIn( cont, 45, 132, new ShieldOfInvulnerability() ); + PlaceItemIn( cont, 69, 66, new HeartOfTheLion() ); + PlaceItemIn( cont, 93, 82, new ColdBlood() ); + PlaceItemIn( cont, 117, 99, new GhostShipAnchor() ); + PlaceItemIn( cont, 45, 115, new SeahorseStatuette() ); + PlaceItemIn( cont, 69, 132, new WrathOfTheDryad() ); + PlaceItemIn( cont, 93, 66, new PixieSwatter() ); + + for( int i = 0; i < 10; i++ ) + PlaceItemIn( cont, 117, 128, new MessageInABottle( Utility.RandomBool() ? Map.Trammel : Map.Felucca, 4 ) ); + + PlaceItemIn( bank, 18, 124, cont ); + + if( TreasuresOfTokuno.Enabled ) + { + cont = new Bag(); + cont.Hue = 0x501; + cont.Name = "Tokuno Minor Artifacts"; + + PlaceItemIn( cont, 42, 70, new Exiler() ); + PlaceItemIn( cont, 38, 53, new HanzosBow() ); + PlaceItemIn( cont, 45, 40, new TheDestroyer() ); + PlaceItemIn( cont, 92, 80, new DragonNunchaku() ); + PlaceItemIn( cont, 42, 56, new PeasantsBokuto() ); + PlaceItemIn( cont, 44, 71, new TomeOfEnlightenment() ); + //PlaceItemIn( cont, 35, 35, new ChestOfHeirlooms() ); //Chest of Heirlooms + PlaceItemIn( cont, 29, 0, new HonorableSwords() ); + PlaceItemIn( cont, 49, 85, new AncientUrn() ); + PlaceItemIn( cont, 51, 58, new FluteOfRenewal() ); + PlaceItemIn( cont, 70, 51, new PigmentsOfTokuno() ); + PlaceItemIn( cont, 40, 79, new AncientSamuraiDo() ); + PlaceItemIn( cont, 51, 61, new LegsOfStability() ); + PlaceItemIn( cont, 88, 78, new GlovesOfTheSun() ); + PlaceItemIn( cont, 55, 62, new AncientFarmersKasa() ); + PlaceItemIn( cont, 55, 83, new ArmsOfTacticalExcellence() ); + PlaceItemIn( cont, 50, 85, new DaimyosHelm() ); + PlaceItemIn( cont, 52, 78, new BlackLotusHood() ); + PlaceItemIn( cont, 52, 79, new DemonForks() ); + PlaceItemIn( cont, 33, 49, new PilferedDancerFans() ); + + PlaceItemIn( bank, 58, 124, cont ); + } + + if( Core.SE ) //This bag came only after SE. + { + cont = new Bag(); + cont.Name = "Bag of Bows"; + + PlaceItemIn( cont, 31, 84, new Bow() ); + PlaceItemIn( cont, 78, 74, new CompositeBow() ); + PlaceItemIn( cont, 53, 71, new Crossbow() ); + PlaceItemIn( cont, 56, 39, new HeavyCrossbow() ); + PlaceItemIn( cont, 82, 72, new RepeatingCrossbow() ); + PlaceItemIn( cont, 49, 45, new Yumi() ); + + for( int i = 0; i < cont.Items.Count; i++ ) + { + BaseRanged bow = cont.Items[i] as BaseRanged; + + if( bow != null ) + { + bow.Attributes.WeaponSpeed = 35; + bow.Attributes.WeaponDamage = 35; + } + } + + PlaceItemIn( bank, 108, 135, cont ); + } + } + + private static void FillBankbox( Mobile m ) + { + if ( Core.AOS ) + { + FillBankAOS( m ); + return; + } + + BankBox bank = m.BankBox; + + if ( bank != null ) + { + bank.DropItem( new BankCheck( 1000000 ) ); + + // Full spellbook + Spellbook book = new Spellbook(); + + book.Content = ulong.MaxValue; + + bank.DropItem( book ); + + Bag bag = new Bag(); + + for ( int i = 0; i < 5; ++i ) + bag.DropItem( new Moonstone( MoonstoneType.Felucca ) ); + + // Felucca moonstones + bank.DropItem( bag ); + + bag = new Bag(); + + for ( int i = 0; i < 5; ++i ) + bag.DropItem( new Moonstone( MoonstoneType.Trammel ) ); + + // Trammel moonstones + bank.DropItem( bag ); + + // Treasure maps + bank.DropItem( new TreasureMap( 1, Map.Trammel ) ); + bank.DropItem( new TreasureMap( 2, Map.Trammel ) ); + bank.DropItem( new TreasureMap( 3, Map.Trammel ) ); + bank.DropItem( new TreasureMap( 4, Map.Trammel ) ); + bank.DropItem( new TreasureMap( 5, Map.Trammel ) ); + + // Bag containing 50 of each reagent + bank.DropItem( new BagOfReagents( 50 ) ); + + // Craft tools + bank.DropItem( MakeNewbie( new Scissors() ) ); + bank.DropItem( MakeNewbie( new SewingKit( 1000 ) ) ); + bank.DropItem( MakeNewbie( new SmithHammer( 1000 ) ) ); + bank.DropItem( MakeNewbie( new FletcherTools( 1000 ) ) ); + bank.DropItem( MakeNewbie( new DovetailSaw( 1000 ) ) ); + bank.DropItem( MakeNewbie( new MortarPestle( 1000 ) ) ); + bank.DropItem( MakeNewbie( new ScribesPen( 1000 ) ) ); + bank.DropItem( MakeNewbie( new TinkerTools( 1000 ) ) ); + + // A few dye tubs + bank.DropItem( new Dyes() ); + bank.DropItem( new DyeTub() ); + bank.DropItem( new DyeTub() ); + bank.DropItem( new BlackDyeTub() ); + + DyeTub darkRedTub = new DyeTub(); + + darkRedTub.DyedHue = 0x485; + darkRedTub.Redyable = false; + + bank.DropItem( darkRedTub ); + + // Some food + bank.DropItem( MakeNewbie( new Apple( 1000 ) ) ); + + // Resources + bank.DropItem( MakeNewbie( new Feather( 1000 ) ) ); + bank.DropItem( MakeNewbie( new BoltOfCloth( 1000 ) ) ); + bank.DropItem( MakeNewbie( new BlankScroll( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Hides( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Bandage( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Bottle( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Log( 1000 ) ) ); + + bank.DropItem( MakeNewbie( new IronIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new DullCopperIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new ShadowIronIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new CopperIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new BronzeIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new GoldIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new AgapiteIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new VeriteIngot( 5000 ) ) ); + bank.DropItem( MakeNewbie( new ValoriteIngot( 5000 ) ) ); + + // Reagents + bank.DropItem( MakeNewbie( new BlackPearl( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Bloodmoss( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Garlic( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Ginseng( 1000 ) ) ); + bank.DropItem( MakeNewbie( new MandrakeRoot( 1000 ) ) ); + bank.DropItem( MakeNewbie( new Nightshade( 1000 ) ) ); + bank.DropItem( MakeNewbie( new SulfurousAsh( 1000 ) ) ); + bank.DropItem( MakeNewbie( new SpidersSilk( 1000 ) ) ); + + // Some extra starting gold + bank.DropItem( MakeNewbie( new Gold( 9000 ) ) ); + + // 5 blank recall runes + for ( int i = 0; i < 5; ++i ) + bank.DropItem( MakeNewbie( new RecallRune() ) ); + + AddPowerScrolls( bank ); + } + } + + private static void AddPowerScrolls( BankBox bank ) + { + Bag bag = new Bag(); + + for ( int i = 0; i < PowerScroll.Skills.Length; ++i ) + bag.DropItem( new PowerScroll( PowerScroll.Skills[i], 120.0 ) ); + + bag.DropItem( new StatCapScroll( 250 ) ); + + bank.DropItem( bag ); + } + + private static void AddShirt( Mobile m, int shirtHue ) + { + int hue = Utility.ClipDyedHue( shirtHue & 0x3FFF ); + + if ( m.Race == Race.Elf ) + { + EquipItem( new ElvenShirt( hue ), true ); + } + else + { + switch ( Utility.Random( 3 ) ) + { + case 0: EquipItem( new Shirt( hue ), true ); break; + case 1: EquipItem( new FancyShirt( hue ), true ); break; + case 2: EquipItem( new Doublet( hue ), true ); break; + } + } + } + + private static void AddPants( Mobile m, int pantsHue ) + { + int hue = Utility.ClipDyedHue( pantsHue & 0x3FFF ); + + if ( m.Race == Race.Elf ) + { + EquipItem( new ElvenPants( hue ), true ); + } + else + { + if ( m.Female ) + { + switch ( Utility.Random( 2 ) ) + { + case 0: EquipItem( new Skirt( hue ), true ); break; + case 1: EquipItem( new Kilt( hue ), true ); break; + } + } + else + { + switch ( Utility.Random( 2 ) ) + { + case 0: EquipItem( new LongPants( hue ), true ); break; + case 1: EquipItem( new ShortPants( hue ), true ); break; + } + } + } + } + + private static void AddShoes( Mobile m ) + { + if( m.Race == Race.Elf ) + EquipItem( new ElvenBoots(), true ); + else + EquipItem( new Shoes( Utility.RandomYellowHue() ), true ); + } + + private static Mobile CreateMobile( Account a ) + { + if ( a.Count >= a.Limit ) + return null; + + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] == null ) + return (a[i] = new PlayerMobile()); + } + + return null; + } + + private static void EventSink_CharacterCreated( CharacterCreatedEventArgs args ) + { + if ( !VerifyProfession( args.Profession ) ) + args.Profession = 0; + + Mobile newChar = CreateMobile( args.Account as Account ); + + if ( newChar == null ) + { + Console.WriteLine( "Login: {0}: Character creation failed, account full", args.State ); + return; + } + + args.Mobile = newChar; + m_Mobile = newChar; + + newChar.Player = true; + newChar.AccessLevel = args.Account.AccessLevel; + newChar.Female = args.Female; + //newChar.Body = newChar.Female ? 0x191 : 0x190; + + if( Core.Expansion >= args.Race.RequiredExpasnion ) + newChar.Race = args.Race; //Sets body + else + newChar.Race = Race.DefaultRace; + + //newChar.Hue = Utility.ClipSkinHue( args.Hue & 0x3FFF ) | 0x8000; + newChar.Hue = newChar.Race.ClipSkinHue( args.Hue & 0x3FFF ) | 0x8000; + + newChar.Hunger = 20; + + bool young = false; + + if ( newChar is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile) newChar; + + pm.Profession = args.Profession; + + if ( pm.AccessLevel == AccessLevel.Player && ((Account)pm.Account).Young ) + young = pm.Young = true; + } + + SetName( newChar, args.Name ); + + AddBackpack( newChar ); + + SetStats( newChar, args.Str, args.Dex, args.Int ); + SetSkills( newChar, args.Skills, args.Profession ); + + Race race = newChar.Race; + + if( race.ValidateHair( newChar, args.HairID ) ) + { + newChar.HairItemID = args.HairID; + newChar.HairHue = race.ClipHairHue( args.HairHue & 0x3FFF ); + } + + if( race.ValidateFacialHair( newChar, args.BeardID ) ) + { + newChar.FacialHairItemID = args.BeardID; + newChar.FacialHairHue = race.ClipHairHue( args.BeardHue & 0x3FFF ); + } + + if ( args.Profession <= 3 ) + { + AddShirt( newChar, args.ShirtHue ); + AddPants( newChar, args.PantsHue ); + AddShoes( newChar ); + } + + if( TestCenter.Enabled ) + FillBankbox( newChar ); + + if ( young && newChar.BankBox != null ) + { + NewPlayerTicket ticket = new NewPlayerTicket(); + ticket.Owner = newChar; + newChar.BankBox.DropItem( ticket ); + } + + CityInfo city = GetStartLocation( args, young ); + //CityInfo city = new CityInfo( "Britain", "Sweet Dreams Inn", 1496, 1628, 10, Map.Felucca ); + + newChar.MoveToWorld( city.Location, city.Map ); + + Console.WriteLine( "Login: {0}: New character being created (account={1})", args.State, args.Account.Username ); + Console.WriteLine( " - Character: {0} (serial={1})", newChar.Name, newChar.Serial ); + Console.WriteLine( " - Started: {0} {1} in {2}", city.City, city.Location, city.Map.ToString() ); + + new WelcomeTimer( newChar ).Start(); + } + + public static bool VerifyProfession( int profession ) + { + if ( profession < 0 ) + return false; + else if ( profession < 4 ) + return true; + else if ( Core.AOS && profession < 6 ) + return true; + else if ( Core.SE && profession < 8 ) + return true; + else + return false; + } + + private class BadStartMessage : Timer + { + Mobile m_Mobile; + int m_Message; + public BadStartMessage( Mobile m, int message ) : base( TimeSpan.FromSeconds ( 3.5 ) ) + { + m_Mobile = m; + m_Message = message; + this.Start(); + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage( m_Message ); + } + } + private static CityInfo GetStartLocation( CharacterCreatedEventArgs args, bool isYoung ) + { + bool useHaven = isYoung; + + int flags = args.State == null ? 0 : args.State.Flags; + Mobile m = args.Mobile; + + switch ( args.Profession ) + { + case 4: //Necro + { + if ( (flags & 0x8) != 0 ) + { + return new CityInfo( "Umbra", "Mardoth's Tower", 2114, 1301, -50, Map.Malas ); + } + else + { + useHaven = true; + + new BadStartMessage( m, 1062205 ); + /* + * Unfortunately you are playing on a *NON-Age-Of-Shadows* game + * installation and cannot be transported to Malas. + * You will not be able to take your new player quest in Malas + * without an AOS client. You are now being taken to the city of + * Haven on the Trammel facet. + * */ + } + + break; + } + case 5: //Paladin + { + return new CityInfo( "Haven", "Uzeraan's Mansion", 3578, 2589, 0, Map.Trammel ); + } + case 6: //Samurai + { + if ( (flags & 0x10) != 0 ) + { + return new CityInfo( "Samurai DE", "Haoti's Grounds", 368, 780, -1, Map.Malas ); + } + else + { + useHaven = true; + + new BadStartMessage( m, 1063487 ); + /* + * Unfortunately you are playing on a *NON-Samurai-Empire* game + * installation and cannot be transported to Tokuno. + * You will not be able to take your new player quest in Tokuno + * without an SE client. You are now being taken to the city of + * Haven on the Trammel facet. + * */ + } + + break; + } + case 7: //Ninja + { + if ( (flags & 0x10) != 0 ) + { + return new CityInfo( "Ninja DE", "Enimo's Residence", 414, 823, -1, Map.Malas ); + } + else + { + useHaven = true; + + new BadStartMessage( m, 1063487 ); + /* + * Unfortunately you are playing on a *NON-Samurai-Empire* game + * installation and cannot be transported to Tokuno. + * You will not be able to take your new player quest in Tokuno + * without an SE client. You are now being taken to the city of + * Haven on the Trammel facet. + * */ + } + + break; + } + } + + if( useHaven ) + return new CityInfo( "Haven", "Uzeraan's Mansion", 3582, 2587, 0, Map.Trammel ); + else + return args.City; + } + + private static void FixStats( ref int str, ref int dex, ref int intel ) + { + int vStr = str - 10; + int vDex = dex - 10; + int vInt = intel - 10; + + if ( vStr < 0 ) + vStr = 0; + + if ( vDex < 0 ) + vDex = 0; + + if ( vInt < 0 ) + vInt = 0; + + int total = vStr + vDex + vInt; + + if ( total == 0 || total == 50 ) + return; + + double scalar = 50 / (double)total; + + vStr = (int)(vStr * scalar); + vDex = (int)(vDex * scalar); + vInt = (int)(vInt * scalar); + + FixStat( ref vStr, (vStr + vDex + vInt) - 50 ); + FixStat( ref vDex, (vStr + vDex + vInt) - 50 ); + FixStat( ref vInt, (vStr + vDex + vInt) - 50 ); + + str = vStr + 10; + dex = vDex + 10; + intel = vInt + 10; + } + + private static void FixStat( ref int stat, int diff ) + { + stat += diff; + + if ( stat < 0 ) + stat = 0; + else if ( stat > 50 ) + stat = 50; + } + + private static void SetStats( Mobile m, int str, int dex, int intel ) + { + FixStats( ref str, ref dex, ref intel ); + + if ( str < 10 || str > 60 || dex < 10 || dex > 60 || intel < 10 || intel > 60 || (str + dex + intel) != 80 ) + { + str = 10; + dex = 10; + intel = 10; + } + + m.InitStats( str, dex, intel ); + } + + private static void SetName( Mobile m, string name ) + { + name = name.Trim(); + + if ( !NameVerification.Validate( name, 2, 16, true, true, true, 1, NameVerification.SpaceDashPeriodQuote ) ) + name = "Generic Player"; + + m.Name = name; + } + + private static bool ValidSkills( SkillNameValue[] skills ) + { + int total = 0; + + for ( int i = 0; i < skills.Length; ++i ) + { + if ( skills[i].Value < 0 || skills[i].Value > 50 ) + return false; + + total += skills[i].Value; + + for ( int j = i + 1; j < skills.Length; ++j ) + { + if ( skills[j].Value > 0 && skills[j].Name == skills[i].Name ) + return false; + } + } + + return ( total == 100 ); + } + + private static Mobile m_Mobile; + + private static void SetSkills( Mobile m, SkillNameValue[] skills, int prof ) + { + switch ( prof ) + { + case 1: // Warrior + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.Anatomy, 30 ), + new SkillNameValue( SkillName.Healing, 45 ), + new SkillNameValue( SkillName.Swords, 35 ), + new SkillNameValue( SkillName.Tactics, 50 ) + }; + + break; + } + case 2: // Magician + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.EvalInt, 30 ), + new SkillNameValue( SkillName.Wrestling, 30 ), + new SkillNameValue( SkillName.Magery, 50 ), + new SkillNameValue( SkillName.Meditation, 50 ) + }; + + break; + } + case 3: // Blacksmith + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.Mining, 30 ), + new SkillNameValue( SkillName.ArmsLore, 30 ), + new SkillNameValue( SkillName.Blacksmith, 50 ), + new SkillNameValue( SkillName.Tinkering, 50 ) + }; + + break; + } + case 4: // Necromancer + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.Necromancy, 50 ), + new SkillNameValue( SkillName.Focus, 30 ), + new SkillNameValue( SkillName.SpiritSpeak, 30 ), + new SkillNameValue( SkillName.Swords, 30 ), + new SkillNameValue( SkillName.Tactics, 20 ) + }; + + break; + } + case 5: // Paladin + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.Chivalry, 51 ), + new SkillNameValue( SkillName.Swords, 49 ), + new SkillNameValue( SkillName.Focus, 30 ), + new SkillNameValue( SkillName.Tactics, 30 ) + }; + + break; + } + case 6: //Samurai + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.Bushido, 50 ), + new SkillNameValue( SkillName.Swords, 50 ), + new SkillNameValue( SkillName.Anatomy, 30 ), + new SkillNameValue( SkillName.Healing, 30 ) + }; + break; + } + case 7: //Ninja + { + skills = new SkillNameValue[] + { + new SkillNameValue( SkillName.Ninjitsu, 50 ), + new SkillNameValue( SkillName.Hiding, 50 ), + new SkillNameValue( SkillName.Fencing, 30 ), + new SkillNameValue( SkillName.Stealth, 30 ) + }; + break; + } + default: + { + if ( !ValidSkills( skills ) ) + return; + + break; + } + } + + bool addSkillItems = true; + bool elf = (m.Race == Race.Elf); + + switch ( prof ) + { + case 1: // Warrior + { + if ( elf ) + EquipItem( new LeafChest() ); + else + EquipItem( new LeatherChest() ); + break; + } + case 4: // Necromancer + { + Container regs = new BagOfNecroReagents( 50 ); + + if ( !Core.AOS ) + { + foreach ( Item item in regs.Items ) + item.LootType = LootType.Newbied; + } + + PackItem( regs ); + + regs.LootType = LootType.Regular; + + + EquipItem( new BoneHelm() ); + + if ( elf ) + { + EquipItem( new ElvenMachete() ); + EquipItem( NecroHue( new LeafChest() ) ); + EquipItem( NecroHue( new LeafArms() ) ); + EquipItem( NecroHue( new LeafGloves() ) ); + EquipItem( NecroHue( new LeafGorget() ) ); + EquipItem( NecroHue( new LeafGorget() ) ); + EquipItem( NecroHue( new ElvenPants() ) ); //TODO: Verify the pants + EquipItem( new ElvenBoots() ); + } + else + { + EquipItem( new BoneHarvester() ); + EquipItem( NecroHue( new LeatherChest() ) ); + EquipItem( NecroHue( new LeatherArms() ) ); + EquipItem( NecroHue( new LeatherGloves() ) ); + EquipItem( NecroHue( new LeatherGorget() ) ); + EquipItem( NecroHue( new LeatherLegs() ) ); + EquipItem( NecroHue( new Skirt() ) ); + EquipItem( new Sandals( 0x8FD ) ); + } + + Spellbook book = new NecromancerSpellbook( (ulong)0x8981 ); // animate dead, evil omen, pain spike, summon familiar, wraith form + + PackItem( book ); + + book.LootType = LootType.Blessed; + + addSkillItems = false; + + break; + } + case 5: // Paladin + { + if ( elf ) + { + EquipItem( new ElvenMachete() ); + EquipItem( new WingedHelm() ); + EquipItem( new LeafGorget() ); + EquipItem( new LeafArms() ); + EquipItem( new LeafChest() ); + EquipItem( new LeafLegs() ); + EquipItem( new ElvenBoots() ); //Verify hue + } + else + { + EquipItem( new Broadsword() ); + EquipItem( new Helmet() ); + EquipItem( new PlateGorget() ); + EquipItem( new RingmailArms() ); + EquipItem( new RingmailChest() ); + EquipItem( new RingmailLegs() ); + EquipItem( new ThighBoots( 0x748 ) ); + EquipItem( new Cloak( 0xCF ) ); + EquipItem( new BodySash( 0xCF ) ); + } + + Spellbook book = new BookOfChivalry( (ulong)0x3FF ); + + PackItem( book ); + + book.LootType = LootType.Blessed; + + break; + } + + case 6: // Samurai + { + addSkillItems = false; + EquipItem( new HakamaShita( 0x2C3 ) ); + EquipItem( new Hakama( 0x2C3 ) ); + EquipItem( new SamuraiTabi( 0x2C3 ) ); + EquipItem( new TattsukeHakama( 0x22D ) ); + EquipItem( new Bokuto() ); + + if ( elf ) + EquipItem( new RavenHelm() ); + else + EquipItem( new LeatherJingasa() ); + + PackItem( new Scissors() ); + PackItem( new Bandage( 50 ) ); + + Spellbook book = new BookOfBushido(); + PackItem( book ); + + break; + } + case 7: // Ninja + { + addSkillItems = false; + EquipItem( new Kasa() ); + + int[] hues = new int[] { 0x1A8, 0xEC, 0x99, 0x90, 0xB5, 0x336, 0x89 }; + //TODO: Verify that's ALL the hues for that above. + + EquipItem( new TattsukeHakama( hues[Utility.Random(hues.Length)] ) ); + + EquipItem( new HakamaShita( 0x2C3 ) ); + EquipItem( new NinjaTabi( 0x2C3 ) ); + + if ( elf ) + EquipItem( new AssassinSpike() ); + else + EquipItem( new Tekagi() ); + + PackItem( new SmokeBomb() ); + + Spellbook book = new BookOfNinjitsu(); + PackItem( book ); + + break; + } + } + + for ( int i = 0; i < skills.Length; ++i ) + { + SkillNameValue snv = skills[i]; + + if ( snv.Value > 0 && ( snv.Name != SkillName.Stealth || prof == 7 ) && snv.Name != SkillName.RemoveTrap && snv.Name != SkillName.Spellweaving ) + { + Skill skill = m.Skills[snv.Name]; + + if ( skill != null ) + { + skill.BaseFixedPoint = snv.Value * 10; + + if ( addSkillItems ) + AddSkillItems( snv.Name, m ); + } + } + } + } + + private static void EquipItem( Item item ) + { + EquipItem( item, false ); + } + + private static void EquipItem( Item item, bool mustEquip ) + { + if ( !Core.AOS ) + item.LootType = LootType.Newbied; + + if ( m_Mobile != null && m_Mobile.EquipItem( item ) ) + return; + + Container pack = m_Mobile.Backpack; + + if ( !mustEquip && pack != null ) + pack.DropItem( item ); + else + item.Delete(); + } + + private static void PackItem( Item item ) + { + if ( !Core.AOS ) + item.LootType = LootType.Newbied; + + Container pack = m_Mobile.Backpack; + + if ( pack != null ) + pack.DropItem( item ); + else + item.Delete(); + } + + private static void PackInstrument() + { + switch ( Utility.Random( 6 ) ) + { + case 0: PackItem( new Drums() ); break; + case 1: PackItem( new Harp() ); break; + case 2: PackItem( new LapHarp() ); break; + case 3: PackItem( new Lute() ); break; + case 4: PackItem( new Tambourine() ); break; + case 5: PackItem( new TambourineTassel() ); break; + } + } + + private static void PackScroll( int circle ) + { + switch ( Utility.Random( 8 ) * (circle * 8) ) + { + case 0: PackItem( new ClumsyScroll() ); break; + case 1: PackItem( new CreateFoodScroll() ); break; + case 2: PackItem( new FeeblemindScroll() ); break; + case 3: PackItem( new HealScroll() ); break; + case 4: PackItem( new MagicArrowScroll() ); break; + case 5: PackItem( new NightSightScroll() ); break; + case 6: PackItem( new ReactiveArmorScroll() ); break; + case 7: PackItem( new WeakenScroll() ); break; + case 8: PackItem( new AgilityScroll() ); break; + case 9: PackItem( new CunningScroll() ); break; + case 10: PackItem( new CureScroll() ); break; + case 11: PackItem( new HarmScroll() ); break; + case 12: PackItem( new MagicTrapScroll() ); break; + case 13: PackItem( new MagicUnTrapScroll() ); break; + case 14: PackItem( new ProtectionScroll() ); break; + case 15: PackItem( new StrengthScroll() ); break; + case 16: PackItem( new BlessScroll() ); break; + case 17: PackItem( new FireballScroll() ); break; + case 18: PackItem( new MagicLockScroll() ); break; + case 19: PackItem( new PoisonScroll() ); break; + case 20: PackItem( new TelekinisisScroll() ); break; + case 21: PackItem( new TeleportScroll() ); break; + case 22: PackItem( new UnlockScroll() ); break; + case 23: PackItem( new WallOfStoneScroll() ); break; + } + } + + private static Item NecroHue( Item item ) + { + item.Hue = 0x2C3; + + return item; + } + + private static void AddSkillItems( SkillName skill, Mobile m ) + { + bool elf = (m.Race == Race.Elf); + + switch ( skill ) + { + case SkillName.Alchemy: + { + PackItem( new Bottle( 4 ) ); + PackItem( new MortarPestle() ); + + int hue = Utility.RandomPinkHue(); + + if ( elf ) + { + if ( m.Female ) + EquipItem( new FemaleElvenRobe( hue ) ); + else + EquipItem( new MaleElvenRobe( hue ) ); + } + else + { + EquipItem( new Robe( Utility.RandomPinkHue() ) ); + } + break; + } + case SkillName.Anatomy: + { + PackItem( new Bandage( 3 ) ); + + int hue = Utility.RandomYellowHue(); + + if ( elf ) + { + if ( m.Female ) + EquipItem( new FemaleElvenRobe( hue ) ); + else + EquipItem( new MaleElvenRobe( hue ) ); + } + else + { + EquipItem( new Robe( Utility.RandomPinkHue() ) ); + } + break; + } + case SkillName.AnimalLore: + { + + + int hue = Utility.RandomBlueHue(); + + if ( elf ) + { + EquipItem( new WildStaff() ); + + if ( m.Female ) + EquipItem( new FemaleElvenRobe( hue ) ); + else + EquipItem( new MaleElvenRobe( hue ) ); + } + else + { + EquipItem( new ShepherdsCrook() ); + EquipItem( new Robe( hue ) ); + } + break; + } + case SkillName.Archery: + { + PackItem( new Arrow( 25 ) ); + + if ( elf ) + EquipItem( new ElvenCompositeLongbow() ); + else + EquipItem( new Bow() ); + + break; + } + case SkillName.ArmsLore: + { + if ( elf ) + { + switch ( Utility.Random( 3 ) ) + { + case 0: EquipItem( new Leafblade() ); break; + case 1: EquipItem( new RuneBlade() ); break; + case 2: EquipItem( new DiamondMace() ); break; + } + } + else + { + switch ( Utility.Random( 3 ) ) + { + case 0: EquipItem( new Kryss() ); break; + case 1: EquipItem( new Katana() ); break; + case 2: EquipItem( new Club() ); break; + } + } + + break; + } + case SkillName.Begging: + { + if ( elf ) + EquipItem( new WildStaff() ); + else + EquipItem( new GnarledStaff() ); + break; + } + case SkillName.Blacksmith: + { + PackItem( new Tongs() ); + PackItem( new Pickaxe() ); + PackItem( new Pickaxe() ); + PackItem( new IronIngot( 50 ) ); + EquipItem( new HalfApron( Utility.RandomYellowHue() ) ); + break; + } + case SkillName.Bushido: + { + EquipItem( new Hakama() ); + EquipItem( new Kasa() ); + EquipItem( new BookOfBushido() ); + break; + } + case SkillName.Fletching: + { + PackItem( new Board( 14 ) ); + PackItem( new Feather( 5 ) ); + PackItem( new Shaft( 5 ) ); + break; + } + case SkillName.Camping: + { + PackItem( new Bedroll() ); + PackItem( new Kindling( 5 ) ); + break; + } + case SkillName.Carpentry: + { + PackItem( new Board( 10 ) ); + PackItem( new Saw() ); + EquipItem( new HalfApron( Utility.RandomYellowHue() ) ); + break; + } + case SkillName.Cartography: + { + PackItem( new BlankMap() ); + PackItem( new BlankMap() ); + PackItem( new BlankMap() ); + PackItem( new BlankMap() ); + PackItem( new Sextant() ); + break; + } + case SkillName.Cooking: + { + PackItem( new Kindling( 2 ) ); + PackItem( new RawLambLeg() ); + PackItem( new RawChickenLeg() ); + PackItem( new RawFishSteak() ); + PackItem( new SackFlour() ); + PackItem( new Pitcher( BeverageType.Water ) ); + break; + } + case SkillName.DetectHidden: + { + EquipItem( new Cloak( 0x455 ) ); + break; + } + case SkillName.Discordance: + { + PackInstrument(); + break; + } + case SkillName.Fencing: + { + if ( elf ) + EquipItem( new Leafblade() ); + else + EquipItem( new Kryss() ); + + break; + } + case SkillName.Fishing: + { + EquipItem( new FishingPole() ); + + int hue = Utility.RandomYellowHue(); + + if ( elf ) + { + Item i = new Circlet(); + i.Hue = hue; + EquipItem( i ); + } + else + { + EquipItem( new FloppyHat( Utility.RandomYellowHue() ) ); + } + + break; + } + case SkillName.Healing: + { + PackItem( new Bandage( 50 ) ); + PackItem( new Scissors() ); + break; + } + case SkillName.Herding: + { + if ( elf ) + EquipItem( new WildStaff() ); + else + EquipItem( new ShepherdsCrook() ); + + break; + } + case SkillName.Hiding: + { + EquipItem( new Cloak( 0x455 ) ); + break; + } + case SkillName.Inscribe: + { + PackItem( new BlankScroll( 2 ) ); + PackItem( new BlueBook() ); + break; + } + case SkillName.ItemID: + { + if ( elf ) + EquipItem( new WildStaff() ); + else + EquipItem( new GnarledStaff() ); + break; + } + case SkillName.Lockpicking: + { + PackItem( new Lockpick( 20 ) ); + break; + } + case SkillName.Lumberjacking: + { + EquipItem( new Hatchet() ); + break; + } + case SkillName.Macing: + { + if ( elf ) + EquipItem( new DiamondMace() ); + else + EquipItem( new Club() ); + + break; + } + case SkillName.Magery: + { + BagOfReagents regs = new BagOfReagents( 30 ); + + if ( !Core.AOS ) + { + foreach ( Item item in regs.Items ) + item.LootType = LootType.Newbied; + } + + PackItem( regs ); + + regs.LootType = LootType.Regular; + + PackScroll( 0 ); + PackScroll( 1 ); + PackScroll( 2 ); + + Spellbook book = new Spellbook( (ulong)0x382A8C38 ); + + EquipItem( book ); + + book.LootType = LootType.Blessed; + + if ( elf ) + { + EquipItem( new Circlet() ); + + if( m.Female ) + EquipItem( new FemaleElvenRobe( Utility.RandomBlueHue() ) ); + else + EquipItem( new MaleElvenRobe( Utility.RandomBlueHue() ) ); + } + else + { + EquipItem( new WizardsHat() ); + EquipItem( new Robe( Utility.RandomBlueHue() ) ); + } + + break; + } + case SkillName.Mining: + { + PackItem( new Pickaxe() ); + break; + } + case SkillName.Musicianship: + { + PackInstrument(); + break; + } + case SkillName.Ninjitsu: + { + EquipItem( new Hakama( 0x2C3 ) ); //Only ninjas get the hued one. + EquipItem( new Kasa() ); + EquipItem( new BookOfNinjitsu() ); + break; + } + case SkillName.Parry: + { + EquipItem( new WoodenShield() ); + break; + } + case SkillName.Peacemaking: + { + PackInstrument(); + break; + } + case SkillName.Poisoning: + { + PackItem( new LesserPoisonPotion() ); + PackItem( new LesserPoisonPotion() ); + break; + } + case SkillName.Provocation: + { + PackInstrument(); + break; + } + case SkillName.Snooping: + { + PackItem( new Lockpick( 20 ) ); + break; + } + case SkillName.SpiritSpeak: + { + EquipItem( new Cloak( 0x455 ) ); + break; + } + case SkillName.Stealing: + { + PackItem( new Lockpick( 20 ) ); + break; + } + case SkillName.Swords: + { + if ( elf ) + EquipItem( new RuneBlade() ); + else + EquipItem( new Katana() ); + + break; + } + case SkillName.Tactics: + { + if ( elf ) + EquipItem( new RuneBlade() ); + else + EquipItem( new Katana() ); + + break; + } + case SkillName.Tailoring: + { + PackItem( new BoltOfCloth() ); + PackItem( new SewingKit() ); + break; + } + case SkillName.Tracking: + { + if ( m_Mobile != null ) + { + Item shoes = m_Mobile.FindItemOnLayer( Layer.Shoes ); + + if ( shoes != null ) + shoes.Delete(); + } + + int hue = Utility.RandomYellowHue(); + + if ( elf ) + EquipItem( new ElvenBoots( hue ) ); + else + EquipItem( new Boots( hue ) ); + + EquipItem( new SkinningKnife() ); + break; + } + case SkillName.Veterinary: + { + PackItem( new Bandage( 5 ) ); + PackItem( new Scissors() ); + break; + } + case SkillName.Wrestling: + { + if ( elf ) + EquipItem( new LeafGloves() ); + else + EquipItem( new LeatherGloves() ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Cleanup.cs b/Scripts/Misc/Cleanup.cs new file mode 100644 index 000000000..8bb8b2b1e --- /dev/null +++ b/Scripts/Misc/Cleanup.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Multis; +using Server.Mobiles; +using System.Collections.Generic; + +namespace Server.Misc +{ + public class Cleanup + { + public static void Initialize() + { + Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerCallback( Run ) ); + } + + public static void Run() + { + List items = new List(); + List validItems = new List(); + List hairCleanup = new List(); + + int boxes = 0; + + foreach ( Item item in World.Items.Values ) + { + if ( item.Map == null ) + { + items.Add( item ); + continue; + } + else if ( item is CommodityDeed ) + { + CommodityDeed deed = (CommodityDeed)item; + + if ( deed.Commodity != null ) + validItems.Add( deed.Commodity ); + + continue; + } + else if ( item is BaseHouse ) + { + BaseHouse house = (BaseHouse)item; + + foreach ( RelocatedEntity relEntity in house.RelocatedEntities ) + { + if ( relEntity.Entity is Item ) + validItems.Add( (Item)relEntity.Entity ); + } + + foreach ( VendorInventory inventory in house.VendorInventories ) + { + foreach ( Item subItem in inventory.Items ) + validItems.Add( subItem ); + } + } + else if ( item is BankBox ) + { + BankBox box = (BankBox)item; + Mobile owner = box.Owner; + + if ( owner == null ) + { + items.Add( box ); + ++boxes; + } + else if ( box.Items.Count == 0 ) + { + items.Add( box ); + ++boxes; + } + + continue; + } + else if ( (item.Layer == Layer.Hair || item.Layer == Layer.FacialHair) ) + { + object rootParent = item.RootParent; + + if ( rootParent is Mobile ) + { + Mobile rootMobile = (Mobile)rootParent; + if ( item.Parent != rootMobile && rootMobile.AccessLevel == AccessLevel.Player ) + { + items.Add( item ); + continue; + } + else if( item.Parent == rootMobile ) + { + hairCleanup.Add( rootMobile ); + continue; + } + } + } + + if ( item.Parent != null || item.Map != Map.Internal || item.HeldBy != null ) + continue; + + if ( item.Location != Point3D.Zero ) + continue; + + if ( !IsBuggable( item ) ) + continue; + + items.Add( item ); + } + + for ( int i = 0; i < validItems.Count; ++i ) + items.Remove( validItems[i] ); + + if ( items.Count > 0 ) + { + if ( boxes > 0 ) + Console.WriteLine( "Cleanup: Detected {0} inaccessible items, including {1} bank boxes, removing..", items.Count, boxes ); + else + Console.WriteLine( "Cleanup: Detected {0} inaccessible items, removing..", items.Count ); + + for ( int i = 0; i < items.Count; ++i ) + items[i].Delete(); + } + + if ( hairCleanup.Count > 0 ) + { + Console.WriteLine( "Cleanup: Detected {0} hair and facial hair items being worn, converting to their virtual counterparts..", hairCleanup.Count ); + + for ( int i = 0; i < hairCleanup.Count; i++ ) + hairCleanup[i].ConvertHair(); + } + } + + public static bool IsBuggable(Item item) + { + if ( item is Fists ) + return false; + + if ( item is ICommodity || item is Multis.BaseBoat + || item is Fish || item is BigFish + || item is BasePotion || item is Food || item is CookableFood + || item is SpecialFishingNet || item is BaseMagicFish + || item is Shoes || item is Sandals + || item is Boots || item is ThighBoots + || item is TreasureMap || item is MessageInABottle + || item is BaseArmor || item is BaseWeapon + || item is BaseClothing + || (item is BaseJewel && Core.AOS) ) + return true; + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/ClientVerification.cs b/Scripts/Misc/ClientVerification.cs new file mode 100644 index 000000000..52ff42c1c --- /dev/null +++ b/Scripts/Misc/ClientVerification.cs @@ -0,0 +1,20 @@ +using System; +using Server; + +namespace Server.Misc +{ + public class ClientVerification + { + public static void Initialize() + { + ClientVersion.Required = null; + //ClientVersion.Required = new ClientVersion( "3.0.8q" ); + + ClientVersion.AllowGod = true; + ClientVersion.AllowUOTD = true; + ClientVersion.AllowRegular = true; + + ClientVersion.KickDelay = TimeSpan.FromSeconds( 10.0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/CrashGuard.cs b/Scripts/Misc/CrashGuard.cs new file mode 100644 index 000000000..826adfab9 --- /dev/null +++ b/Scripts/Misc/CrashGuard.cs @@ -0,0 +1,261 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net.Mail; +using Server; +using Server.Accounting; +using Server.Network; + +namespace Server.Misc +{ + public class CrashGuard + { + private static bool Enabled = true; + private static bool SaveBackup = true; + private static bool RestartServer = true; + private static bool GenerateReport = true; + + public static void Initialize() + { + if ( Enabled ) // If enabled, register our crash event handler + EventSink.Crashed += new CrashedEventHandler( CrashGuard_OnCrash ); + } + + public static void CrashGuard_OnCrash( CrashedEventArgs e ) + { + if ( SaveBackup ) + Backup(); + + if ( GenerateReport ) + GenerateCrashReport( e ); + + if ( Core.Service ) + e.Close = true; + else if ( RestartServer ) + Restart( e ); + } + + private static void SendEmail( string filePath ) + { + Console.Write( "Crash: Sending email..." ); + + MailMessage message = new MailMessage( "RunUO", Email.CrashAddresses ); + + message.Subject = "Automated RunUO Crash Report"; + + message.Body = "Automated RunUO Crash Report. See attachment for details."; + + message.Attachments.Add( new Attachment( filePath ) ); + + if ( Email.Send( message ) ) + Console.WriteLine( "done" ); + else + Console.WriteLine( "failed" ); + } + + private static string GetRoot() + { + try + { + return Path.GetDirectoryName( Environment.GetCommandLineArgs()[0] ); + } + catch + { + return ""; + } + } + + private static string Combine( string path1, string path2 ) + { + if ( path1 == "" ) + return path2; + + return Path.Combine( path1, path2 ); + } + + private static void Restart( CrashedEventArgs e ) + { + string root = GetRoot(); + + Console.Write( "Crash: Restarting..." ); + + try + { + Process.Start( Core.ExePath ); + Console.WriteLine( "done" ); + + e.Close = true; + } + catch + { + Console.WriteLine( "failed" ); + } + } + + private static void CreateDirectory( string path ) + { + if ( !Directory.Exists( path ) ) + Directory.CreateDirectory( path ); + } + + private static void CreateDirectory( string path1, string path2 ) + { + CreateDirectory( Combine( path1, path2 ) ); + } + + private static void CopyFile( string rootOrigin, string rootBackup, string path ) + { + string originPath = Combine( rootOrigin, path ); + string backupPath = Combine( rootBackup, path ); + + try + { + if ( File.Exists( originPath ) ) + File.Copy( originPath, backupPath ); + } + catch + { + } + } + + private static void Backup() + { + Console.Write( "Crash: Backing up..." ); + + try + { + string timeStamp = GetTimeStamp(); + + string root = GetRoot(); + string rootBackup = Combine( root, String.Format( "Backups/Crashed/{0}/", timeStamp ) ); + string rootOrigin = Combine( root, String.Format( "Saves/" ) ); + + // Create new directories + CreateDirectory( rootBackup ); + CreateDirectory( rootBackup, "Accounts/" ); + CreateDirectory( rootBackup, "Items/" ); + CreateDirectory( rootBackup, "Mobiles/" ); + CreateDirectory( rootBackup, "Guilds/" ); + CreateDirectory( rootBackup, "Regions/" ); + + // Copy files + CopyFile( rootOrigin, rootBackup, "Accounts/Accounts.xml" ); + + CopyFile( rootOrigin, rootBackup, "Items/Items.bin" ); + CopyFile( rootOrigin, rootBackup, "Items/Items.idx" ); + CopyFile( rootOrigin, rootBackup, "Items/Items.tdb" ); + + CopyFile( rootOrigin, rootBackup, "Mobiles/Mobiles.bin" ); + CopyFile( rootOrigin, rootBackup, "Mobiles/Mobiles.idx" ); + CopyFile( rootOrigin, rootBackup, "Mobiles/Mobiles.tdb" ); + + CopyFile( rootOrigin, rootBackup, "Guilds/Guilds.bin" ); + CopyFile( rootOrigin, rootBackup, "Guilds/Guilds.idx" ); + + CopyFile( rootOrigin, rootBackup, "Regions/Regions.bin" ); + CopyFile( rootOrigin, rootBackup, "Regions/Regions.idx" ); + + Console.WriteLine( "done" ); + } + catch + { + Console.WriteLine( "failed" ); + } + } + + private static void GenerateCrashReport( CrashedEventArgs e ) + { + Console.Write( "Crash: Generating report..." ); + + try + { + string timeStamp = GetTimeStamp(); + string fileName = String.Format( "Crash {0}.log", timeStamp ); + + string root = GetRoot(); + string filePath = Combine( root, fileName ); + + using ( StreamWriter op = new StreamWriter( filePath ) ) + { + Version ver = Core.Assembly.GetName().Version; + + op.WriteLine( "Server Crash Report" ); + op.WriteLine( "===================" ); + op.WriteLine(); + op.WriteLine( "RunUO Version {0}.{1}, Build {2}.{3}", ver.Major, ver.Minor, ver.Build, ver.Revision ); + op.WriteLine( "Operating System: {0}", Environment.OSVersion ); + op.WriteLine( ".NET Framework: {0}", Environment.Version ); + op.WriteLine( "Time: {0}", DateTime.Now ); + + try { op.WriteLine( "Mobiles: {0}", World.Mobiles.Count ); } + catch {} + + try { op.WriteLine( "Items: {0}", World.Items.Count ); } + catch {} + + op.WriteLine( "Clients:" ); + + try + { + List states = NetState.Instances; + + op.WriteLine( "- Count: {0}", states.Count ); + + for ( int i = 0; i < states.Count; ++i ) + { + NetState state = states[i]; + + op.Write( "+ {0}:", state ); + + Account a = state.Account as Account; + + if ( a != null ) + op.Write( " (account = {0})", a.Username ); + + Mobile m = state.Mobile; + + if ( m != null ) + op.Write( " (mobile = 0x{0:X} '{1}')", m.Serial.Value, m.Name ); + + op.WriteLine(); + } + } + catch + { + op.WriteLine( "- Failed" ); + } + + op.WriteLine(); + + op.WriteLine( "Exception:" ); + op.WriteLine( e.Exception ); + } + + Console.WriteLine( "done" ); + + if ( Email.CrashAddresses != null ) + SendEmail( filePath ); + } + catch + { + Console.WriteLine( "failed" ); + } + } + + private static string GetTimeStamp() + { + DateTime now = DateTime.Now; + + return String.Format( "{0}-{1}-{2}-{3}-{4}-{5}", + now.Day, + now.Month, + now.Year, + now.Hour, + now.Minute, + now.Second + ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/CurrentExpansion.cs b/Scripts/Misc/CurrentExpansion.cs new file mode 100644 index 000000000..82a8d8227 --- /dev/null +++ b/Scripts/Misc/CurrentExpansion.cs @@ -0,0 +1,31 @@ +using System; +using Server.Network; + +namespace Server +{ + public class CurrentExpansion + { + private static readonly Expansion Expansion = Expansion.ML; + + public static void Configure() + { + Core.Expansion = Expansion; + + bool Enabled = Core.AOS; + + Mobile.InsuranceEnabled = Enabled; + ObjectPropertyList.Enabled = Enabled; + Mobile.VisibleDamageType = Enabled ? VisibleDamageType.Related : VisibleDamageType.None; + Mobile.GuildClickMessage = !Enabled; + Mobile.AsciiClickMessage = !Enabled; + + if ( Enabled ) + { + AOS.DisableStatInfluences(); + + if ( ObjectPropertyList.Enabled ) + PacketHandlers.SingleClickProps = true; // single click for everything is overriden to check object property list + } + } + } +} diff --git a/Scripts/Misc/DataPath.cs b/Scripts/Misc/DataPath.cs new file mode 100644 index 000000000..a12bfa43d --- /dev/null +++ b/Scripts/Misc/DataPath.cs @@ -0,0 +1,105 @@ +using System; +using System.IO; +using Microsoft.Win32; +using Server; + +namespace Server.Misc +{ + public class DataPath + { + /* If you have not installed Ultima Online, + * or wish the server to use a seperate set of datafiles, + * change the 'CustomPath' value, example: + * + * private const string CustomPath = @"C:\Program Files\Ultima Online"; + */ + private static string CustomPath = null; + + /* The following is a list of files which a required for proper execution: + * + * Multi.idx + * Multi.mul + * VerData.mul + * TileData.mul + * Map*.mul + * StaIdx*.mul + * Statics*.mul + * MapDif*.mul + * MapDifL*.mul + * StaDif*.mul + * StaDifL*.mul + * StaDifI*.mul + */ + + public static void Configure() + { + string pathReg = GetExePath( "Ultima Online" ); + string pathTD = GetExePath( "Ultima Online Third Dawn" ); //These refer to 2D & 3D, not the Third Dawn expansion + + if ( CustomPath != null ) + Core.DataDirectories.Add( CustomPath ); + + if ( pathReg != null ) + Core.DataDirectories.Add( pathReg ); + + if ( pathTD != null ) + Core.DataDirectories.Add( pathTD ); + + + if ( CustomPath != null ) + Core.DataDirectories.Add( CustomPath ); + + if ( pathReg != null ) + Core.DataDirectories.Add( pathReg ); + + if ( pathTD != null ) + Core.DataDirectories.Add( pathTD ); + + if ( Core.DataDirectories.Count == 0 ) + { + Console.WriteLine( "Enter the Ultima Online directory:" ); + Console.Write( "> " ); + + Core.DataDirectories.Add( Console.ReadLine() ); + } + } + + private static string GetExePath( string subName ) + { + try + { + String keyString; + + if( Core.Is64Bit ) + keyString = @"SOFTWARE\Wow6432Node\Origin Worlds Online\{0}\1.0"; + else + keyString = @"SOFTWARE\Origin Worlds Online\{0}\1.0"; + + using( RegistryKey key = Registry.LocalMachine.OpenSubKey( String.Format( keyString, subName ) ) ) + { + if( key == null ) + return null; + + string v = key.GetValue( "ExePath" ) as string; + + if( v == null || v.Length <= 0 ) + return null; + + if( !File.Exists( v ) ) + return null; + + v = Path.GetDirectoryName( v ); + + if( v == null ) + return null; + + return v; + } + } + catch + { + return null; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/DispellableAttribute.cs b/Scripts/Misc/DispellableAttribute.cs new file mode 100644 index 000000000..829138bbe --- /dev/null +++ b/Scripts/Misc/DispellableAttribute.cs @@ -0,0 +1,10 @@ +using System; +using Server; + +namespace Server.Misc +{ + [AttributeUsage( AttributeTargets.Class )] + public class DispellableAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Scripts/Misc/DispellableFieldAttribute.cs b/Scripts/Misc/DispellableFieldAttribute.cs new file mode 100644 index 000000000..a3faf7be2 --- /dev/null +++ b/Scripts/Misc/DispellableFieldAttribute.cs @@ -0,0 +1,10 @@ +using System; +using Server; + +namespace Server.Misc +{ + [AttributeUsage( AttributeTargets.Class )] + public class DispellableFieldAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Scripts/Misc/DoorGenerator.cs b/Scripts/Misc/DoorGenerator.cs new file mode 100644 index 000000000..a9ebc9e67 --- /dev/null +++ b/Scripts/Misc/DoorGenerator.cs @@ -0,0 +1,566 @@ +using System; +using Server; +using Server.Items; +using Server.Commands; + +namespace Server +{ + public class DoorGenerator + { + private static Rectangle2D[] m_BritRegions = new Rectangle2D[] + { + new Rectangle2D( new Point2D( 250, 750 ), new Point2D( 775, 1330 ) ), + new Rectangle2D( new Point2D( 525, 2095 ), new Point2D( 925, 2430 ) ), + new Rectangle2D( new Point2D( 1025, 2155 ), new Point2D( 1265, 2310 ) ), + new Rectangle2D( new Point2D( 1635, 2430 ), new Point2D( 1705, 2508 ) ), + new Rectangle2D( new Point2D( 1775, 2605 ), new Point2D( 2165, 2975 ) ), + new Rectangle2D( new Point2D( 1055, 3520 ), new Point2D( 1570, 4075 ) ), + new Rectangle2D( new Point2D( 2860, 3310 ), new Point2D( 3120, 3630 ) ), + new Rectangle2D( new Point2D( 2470, 1855 ), new Point2D( 3950, 3045 ) ), + new Rectangle2D( new Point2D( 3425, 990 ), new Point2D( 3900, 1455 ) ), + new Rectangle2D( new Point2D( 4175, 735 ), new Point2D( 4840, 1600 ) ), + new Rectangle2D( new Point2D( 2375, 330 ), new Point2D( 3100, 1045 ) ), + new Rectangle2D( new Point2D( 2100, 1090 ), new Point2D( 2310, 1450 ) ), + new Rectangle2D( new Point2D( 1495, 1400 ), new Point2D( 1550, 1475 ) ), + new Rectangle2D( new Point2D( 1085, 1520 ), new Point2D( 1415, 1910 ) ), + new Rectangle2D( new Point2D( 1410, 1500 ), new Point2D( 1745, 1795 ) ), + new Rectangle2D( new Point2D( 5120, 2300 ), new Point2D( 6143, 4095 ) ) + }; + + private static Rectangle2D[] m_IlshRegions = new Rectangle2D[] + { + new Rectangle2D( new Point2D( 0, 0 ), new Point2D( 288*8, 200*8 ) ) + }; + + private static Rectangle2D[] m_MalasRegions = new Rectangle2D[] + { + new Rectangle2D( new Point2D( 0, 0 ), new Point2D( 320*8, 256*8 ) ) + }; + + private static int[] m_SouthFrames = new int[] + { + 0x0006, + 0x0008, + 0x000B, + 0x001A, + 0x001B, + 0x001F, + 0x0038, + 0x0057, + 0x0059, + 0x005B, + 0x005D, + 0x0080, + 0x0081, + 0x0082, + 0x0084, + 0x0090, + 0x0091, + 0x0094, + 0x0096, + 0x0099, + 0x00A6, + 0x00A7, + 0x00AA, + 0x00AE, + 0x00B0, + 0x00B3, + 0x00C7, + 0x00C9, + 0x00F8, + 0x00FA, + 0x00FD, + 0x00FE, + 0x0100, + 0x0103, + 0x0104, + 0x0106, + 0x0109, + 0x0127, + 0x0129, + 0x012B, + 0x012D, + 0x012F, + 0x0131, + 0x0132, + 0x0134, + 0x0135, + 0x0137, + 0x0139, + 0x013B, + 0x014C, + 0x014E, + 0x014F, + 0x0151, + 0x0153, + 0x0155, + 0x0157, + 0x0158, + 0x015A, + 0x015D, + 0x015E, + 0x015F, + 0x0162, + 0x01CF, + 0x01D1, + 0x01D4, + 0x01FF, + 0x0204, + 0x0206, + 0x0208, + 0x020A + }; + + private static int[] m_NorthFrames = new int[] + { + 0x0006, + 0x0008, + 0x000D, + 0x001A, + 0x001B, + 0x0020, + 0x003A, + 0x0057, + 0x0059, + 0x005B, + 0x005D, + 0x0080, + 0x0081, + 0x0082, + 0x0084, + 0x0090, + 0x0091, + 0x0094, + 0x0096, + 0x0099, + 0x00A6, + 0x00A7, + 0x00AC, + 0x00AE, + 0x00B0, + 0x00C7, + 0x00C9, + 0x00F8, + 0x00FA, + 0x00FD, + 0x00FE, + 0x0100, + 0x0103, + 0x0104, + 0x0106, + 0x0109, + 0x0127, + 0x0129, + 0x012B, + 0x012D, + 0x012F, + 0x0131, + 0x0132, + 0x0134, + 0x0135, + 0x0137, + 0x0139, + 0x013B, + 0x014C, + 0x014E, + 0x014F, + 0x0151, + 0x0153, + 0x0155, + 0x0157, + 0x0158, + 0x015A, + 0x015D, + 0x015E, + 0x015F, + 0x0162, + 0x01CF, + 0x01D1, + 0x01D4, + 0x01FF, + 0x0201, + 0x0204, + 0x0208, + 0x020A + }; + + private static int[] m_EastFrames = new int[] + { + 0x0007, + 0x000A, + 0x001A, + 0x001C, + 0x001E, + 0x0037, + 0x0058, + 0x0059, + 0x005C, + 0x005E, + 0x0080, + 0x0081, + 0x0082, + 0x0084, + 0x0090, + 0x0092, + 0x0095, + 0x0097, + 0x0098, + 0x00A6, + 0x00A8, + 0x00AB, + 0x00AE, + 0x00AF, + 0x00B2, + 0x00C7, + 0x00C8, + 0x00EA, + 0x00F8, + 0x00F9, + 0x00FC, + 0x00FE, + 0x00FF, + 0x0102, + 0x0104, + 0x0105, + 0x0108, + 0x0127, + 0x0128, + 0x012B, + 0x012C, + 0x012E, + 0x0130, + 0x0132, + 0x0133, + 0x0135, + 0x0136, + 0x0138, + 0x013A, + 0x014C, + 0x014D, + 0x014F, + 0x0150, + 0x0152, + 0x0154, + 0x0156, + 0x0158, + 0x0159, + 0x015C, + 0x015E, + 0x0160, + 0x0163, + 0x01CF, + 0x01D0, + 0x01D3, + 0x01FF, + 0x0203, + 0x0205, + 0x0207, + 0x0209 + }; + + private static int[] m_WestFrames = new int[] + { + 0x0007, + 0x000C, + 0x001A, + 0x001C, + 0x0021, + 0x0039, + 0x0058, + 0x0059, + 0x005C, + 0x005E, + 0x0080, + 0x0081, + 0x0082, + 0x0084, + 0x0090, + 0x0092, + 0x0095, + 0x0097, + 0x0098, + 0x00A6, + 0x00A8, + 0x00AD, + 0x00AE, + 0x00AF, + 0x00B5, + 0x00C7, + 0x00C8, + 0x00EA, + 0x00F8, + 0x00F9, + 0x00FC, + 0x00FE, + 0x00FF, + 0x0102, + 0x0104, + 0x0105, + 0x0108, + 0x0127, + 0x0128, + 0x012C, + 0x012E, + 0x0130, + 0x0132, + 0x0133, + 0x0135, + 0x0136, + 0x0138, + 0x013A, + 0x014C, + 0x014D, + 0x014F, + 0x0150, + 0x0152, + 0x0154, + 0x0156, + 0x0158, + 0x0159, + 0x015C, + 0x015E, + 0x0160, + 0x0163, + 0x01CF, + 0x01D0, + 0x01D3, + 0x01FF, + 0x0200, + 0x0203, + 0x0207, + 0x0209 + }; + + public static void Initialize() + { + CommandSystem.Register( "DoorGen", AccessLevel.Administrator, new CommandEventHandler( DoorGen_OnCommand ) ); + } + + [Usage( "DoorGen" )] + [Description( "Generates doors by analyzing the map. Slow." )] + public static void DoorGen_OnCommand( CommandEventArgs e ) + { + Generate(); + } + + private static Map m_Map; + private static int m_Count; + + public static void Generate() + { + World.Broadcast( 0x35, true, "Generating doors, please wait." ); + + Network.NetState.FlushAll(); + Network.NetState.Pause(); + + m_Map = Map.Trammel; + m_Count = 0; + + for ( int i = 0; i < m_BritRegions.Length; ++i ) + Generate( m_BritRegions[i] ); + + int trammelCount = m_Count; + + m_Map = Map.Felucca; + m_Count = 0; + + for ( int i = 0; i < m_BritRegions.Length; ++i ) + Generate( m_BritRegions[i] ); + + int feluccaCount = m_Count; + + m_Map = Map.Ilshenar; + m_Count = 0; + + for ( int i = 0; i < m_IlshRegions.Length; ++i ) + Generate( m_IlshRegions[i] ); + + int ilshenarCount = m_Count; + + m_Map = Map.Malas; + m_Count = 0; + + for ( int i = 0; i < m_MalasRegions.Length; ++i ) + Generate( m_MalasRegions[i] ); + + int malasCount = m_Count; + + Network.NetState.Resume(); + + World.Broadcast( 0x35, true, "Door generation complete. Trammel: {0}; Felucca: {1}; Ilshenar: {2}; Malas: {3};", trammelCount, feluccaCount, ilshenarCount, malasCount ); + } + + public static bool IsFrame( int id, int[] list ) + { + id &= 0x3FFF; + + if ( id > list[list.Length - 1] ) + return false; + + for ( int i = 0; i < list.Length; ++i ) + { + int delta = id - list[i]; + + if ( delta < 0 ) + return false; + else if ( delta == 0 ) + return true; + } + + return false; + } + + public static bool IsNorthFrame( int id ) + { + return IsFrame( id, m_NorthFrames ); + } + + public static bool IsSouthFrame( int id ) + { + return IsFrame( id, m_SouthFrames ); + } + + public static bool IsWestFrame( int id ) + { + return IsFrame( id, m_WestFrames ); + } + + public static bool IsEastFrame( int id ) + { + return IsFrame( id, m_EastFrames ); + } + + public static bool IsEastFrame( int x, int y, int z ) + { + Tile[] tiles = m_Map.Tiles.GetStaticTiles( x, y ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + if ( tile.Z == z && IsEastFrame( tile.ID ) ) + return true; + } + + return false; + } + + public static bool IsSouthFrame( int x, int y, int z ) + { + Tile[] tiles = m_Map.Tiles.GetStaticTiles( x, y ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + if ( tile.Z == z && IsSouthFrame( tile.ID ) ) + return true; + } + + return false; + } + + public static BaseDoor AddDoor( int x, int y, int z, DoorFacing facing ) + { + int doorZ = z; + int doorTop = doorZ + 20; + + if ( !m_Map.CanFit( x, y, z, 16, false, false ) ) + return null; + + if ( y == 1743 && x >= 1343 && x <= 1344 ) + return null; + + if ( y == 1679 && x >= 1392 && x <= 1393 ) + return null; + + if ( x == 1320 && y >= 1618 && y <= 1640 ) + return null; + + if ( x == 1383 && y >= 1642 && y <= 1643 ) + return null; + + BaseDoor door = new DarkWoodDoor( facing ); + door.MoveToWorld( new Point3D( x, y, z ), m_Map ); + + ++m_Count; + + return door; + } + + public static void Generate( Rectangle2D region ) + { + for ( int rx = 0; rx < region.Width; ++rx ) + { + for ( int ry = 0; ry < region.Height; ++ry ) + { + int vx = rx + region.X; + int vy = ry + region.Y; + + Tile[] tiles = m_Map.Tiles.GetStaticTiles( vx, vy ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + int id = tile.ID; + int z = tile.Z; + + if ( IsWestFrame( id ) ) + { + if ( IsEastFrame( vx + 2, vy, z ) ) + { + AddDoor( vx + 1, vy, z, DoorFacing.WestCW ); + } + else if ( IsEastFrame( vx + 3, vy, z ) ) + { + BaseDoor first = AddDoor( vx + 1, vy, z, DoorFacing.WestCW ); + BaseDoor second = AddDoor( vx + 2, vy, z, DoorFacing.EastCCW ); + + if ( first != null && second != null ) + { + first.Link = second; + second.Link = first; + } + else + { + if ( first != null ) + first.Delete(); + + if ( second != null ) + second.Delete(); + } + } + } + else if ( IsNorthFrame( id ) ) + { + if ( IsSouthFrame( vx, vy + 2, z ) ) + { + AddDoor( vx, vy + 1, z, DoorFacing.SouthCW ); + } + else if ( IsSouthFrame( vx, vy + 3, z ) ) + { + BaseDoor first = AddDoor( vx, vy + 1, z, DoorFacing.NorthCCW ); + BaseDoor second = AddDoor( vx, vy + 2, z, DoorFacing.SouthCW ); + + if ( first != null && second != null ) + { + first.Link = second; + second.Link = first; + } + else + { + if ( first != null ) + first.Delete(); + + if ( second != null ) + second.Delete(); + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Email.cs b/Scripts/Misc/Email.cs new file mode 100644 index 000000000..1e55a77e8 --- /dev/null +++ b/Scripts/Misc/Email.cs @@ -0,0 +1,65 @@ +using System; +using System.Net.Mail; +using System.Threading; +using Server; + +namespace Server.Misc +{ + public class Email + { + /* In order to support emailing, fill in EmailServer: + * Example: + * public static readonly string EmailServer = "mail.domain.com"; + * + * If you want to add crash reporting emailing, fill in CrashAddresses: + * Example: + * public static readonly string CrashAddresses = "first@email.here;second@email.here;third@email.here"; + * + * If you want to add speech log page emailing, fill in SpeechLogPageAddresses: + * Example: + * public static readonly string SpeechLogPageAddresses = "first@email.here;second@email.here;third@email.here"; + */ + + public static readonly string EmailServer = null; + + public static readonly string CrashAddresses = null; + public static readonly string SpeechLogPageAddresses = null; + + public static SmtpClient Client; + + public static void Configure() + { + if ( EmailServer != null ) + Client = new SmtpClient( EmailServer ); + } + + public static bool Send( MailMessage message ) + { + try + { + Client.Send( message ); + } + catch + { + return false; + } + + return true; + } + + public static void AsyncSend( MailMessage message ) + { + ThreadPool.QueueUserWorkItem( new WaitCallback( SendCallback ), message ); + } + + private static void SendCallback( object state ) + { + MailMessage message = (MailMessage) state; + + if ( Send( message ) ) + Console.WriteLine( "Sent e-mail '{0}' to '{1}'.", message.Subject, message.To ); + else + Console.WriteLine( "Failure sending e-mail '{0}' to '{1}'.", message.Subject, message.To ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Emitter.cs b/Scripts/Misc/Emitter.cs new file mode 100644 index 000000000..811264f4b --- /dev/null +++ b/Scripts/Misc/Emitter.cs @@ -0,0 +1,726 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Reflection.Emit; +using Emit = System.Reflection.Emit; + +namespace Server +{ + public class AssemblyEmitter + { + private string m_AssemblyName; + + private AppDomain m_AppDomain; + private AssemblyBuilder m_AssemblyBuilder; + private ModuleBuilder m_ModuleBuilder; + + public AssemblyEmitter( string assemblyName, bool canSave ) + { + m_AssemblyName = assemblyName; + + m_AppDomain = AppDomain.CurrentDomain; + + m_AssemblyBuilder = m_AppDomain.DefineDynamicAssembly( + new AssemblyName( assemblyName ), + canSave ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run + ); + + if ( canSave ) + { + m_ModuleBuilder = m_AssemblyBuilder.DefineDynamicModule( + assemblyName, + String.Format( "{0}.dll", assemblyName.ToLower() ), + false + ); + } + else + { + m_ModuleBuilder = m_AssemblyBuilder.DefineDynamicModule( + assemblyName, + false + ); + } + } + + public TypeBuilder DefineType( string typeName, TypeAttributes attrs, Type parentType ) + { + return m_ModuleBuilder.DefineType( typeName, attrs, parentType ); + } + + public void Save() + { + m_AssemblyBuilder.Save( + String.Format( "{0}.dll", m_AssemblyName.ToLower() ) + ); + } + } + + public class MethodEmitter + { + private TypeBuilder m_TypeBuilder; + + private MethodBuilder m_Builder; + private ILGenerator m_Generator; + + private Type[] m_ArgumentTypes; + + public TypeBuilder Type + { + get { return m_TypeBuilder; } + } + + public ILGenerator Generator + { + get { return m_Generator; } + } + + private class CallInfo + { + public Type type; + public MethodInfo method; + + public int index; + public ParameterInfo[] parms; + + public CallInfo( Type type, MethodInfo method ) + { + this.type = type; + this.method = method; + + this.parms = method.GetParameters(); + } + } + + private Stack m_Stack; + private Stack m_Calls; + + private Dictionary> m_Temps; + + public MethodBuilder Method + { + get { return m_Builder; } + } + + public MethodEmitter( TypeBuilder typeBuilder ) + { + m_TypeBuilder = typeBuilder; + + m_Temps = new Dictionary>(); + + m_Stack = new Stack(); + m_Calls = new Stack(); + } + + public void Define( string name, MethodAttributes attr, Type returnType, Type[] parms ) + { + m_Builder = m_TypeBuilder.DefineMethod( name, attr, returnType, parms ); + m_Generator = m_Builder.GetILGenerator(); + + m_ArgumentTypes = parms; + } + + public LocalBuilder CreateLocal( Type localType ) + { + return m_Generator.DeclareLocal( localType ); + } + + public LocalBuilder AcquireTemp( Type localType ) + { + Queue list; + + if ( !m_Temps.TryGetValue( localType, out list ) ) + m_Temps[localType] = list = new Queue(); + + if ( list.Count > 0 ) + return list.Dequeue(); + + return CreateLocal( localType ); + } + + public void ReleaseTemp( LocalBuilder local ) + { + Queue list; + + if ( !m_Temps.TryGetValue( local.LocalType, out list ) ) + m_Temps[local.LocalType] = list = new Queue(); + + list.Enqueue( local ); + } + + public void Branch( Label label ) + { + m_Generator.Emit( OpCodes.Br, label ); + } + + public void BranchIfFalse( Label label ) + { + Pop( typeof( object ) ); + + m_Generator.Emit( OpCodes.Brfalse, label ); + } + + public void BranchIfTrue( Label label ) + { + Pop( typeof( object ) ); + + m_Generator.Emit( OpCodes.Brtrue, label ); + } + + public Label CreateLabel() + { + return m_Generator.DefineLabel(); + } + + public void MarkLabel( Label label ) + { + m_Generator.MarkLabel( label ); + } + + public void Pop() + { + m_Stack.Pop(); + } + + public void Pop( Type expected ) + { + if ( expected == null ) + throw new InvalidOperationException( "Expected type cannot be null." ); + + Type onStack = m_Stack.Pop(); + + if ( expected == typeof( bool ) ) + expected = typeof( int ); + + if ( onStack == typeof( bool ) ) + onStack = typeof( int ); + + if ( !expected.IsAssignableFrom( onStack ) ) + throw new InvalidOperationException( "Unexpected stack state." ); + } + + public void Push( Type type ) + { + m_Stack.Push( type ); + } + + public void Return() + { + if ( m_Stack.Count != ( m_Builder.ReturnType == typeof( void ) ? 0 : 1 ) ) + throw new InvalidOperationException( "Stack return mismatch." ); + + m_Generator.Emit( OpCodes.Ret ); + } + + public void LoadNull() + { + LoadNull( typeof( object ) ); + } + + public void LoadNull( Type type ) + { + Push( type ); + + m_Generator.Emit( OpCodes.Ldnull ); + } + + public void Load( string value ) + { + Push( typeof( string ) ); + + if ( value != null ) + m_Generator.Emit( OpCodes.Ldstr, value ); + else + m_Generator.Emit( OpCodes.Ldnull ); + } + + public void Load( Enum value ) + { + Load( (int) (object) value ); + + Pop(); + Push( value.GetType() ); + } + + public void Load( long value ) + { + Push( typeof( long ) ); + + m_Generator.Emit( OpCodes.Ldc_I8, value ); + } + + public void Load( float value ) + { + Push( typeof( float ) ); + + m_Generator.Emit( OpCodes.Ldc_R4, value ); + } + + public void Load( double value ) + { + Push( typeof( double ) ); + + m_Generator.Emit( OpCodes.Ldc_R8, value ); + } + + public void Load( char value ) + { + Load( (int) value ); + + Pop(); + Push( typeof( char ) ); + } + + public void Load( bool value ) + { + Push( typeof( bool ) ); + + if ( value ) + m_Generator.Emit( OpCodes.Ldc_I4_1 ); + else + m_Generator.Emit( OpCodes.Ldc_I4_0 ); + } + + public void Load( int value ) + { + Push( typeof( int ) ); + + switch ( value ) + { + case -1: + m_Generator.Emit( OpCodes.Ldc_I4_M1 ); + break; + + case 0: + m_Generator.Emit( OpCodes.Ldc_I4_0 ); + break; + + case 1: + m_Generator.Emit( OpCodes.Ldc_I4_1 ); + break; + + case 2: + m_Generator.Emit( OpCodes.Ldc_I4_2 ); + break; + + case 3: + m_Generator.Emit( OpCodes.Ldc_I4_3 ); + break; + + case 4: + m_Generator.Emit( OpCodes.Ldc_I4_4 ); + break; + + case 5: + m_Generator.Emit( OpCodes.Ldc_I4_5 ); + break; + + case 6: + m_Generator.Emit( OpCodes.Ldc_I4_6 ); + break; + + case 7: + m_Generator.Emit( OpCodes.Ldc_I4_7 ); + break; + + case 8: + m_Generator.Emit( OpCodes.Ldc_I4_8 ); + break; + + default: + if ( value >= sbyte.MinValue && value <= sbyte.MaxValue ) + m_Generator.Emit( OpCodes.Ldc_I4_S, (sbyte) value ); + else + m_Generator.Emit( OpCodes.Ldc_I4, value ); + + break; + } + } + + public void LoadField( FieldInfo field ) + { + Pop( field.DeclaringType ); + + Push( field.FieldType ); + + m_Generator.Emit( OpCodes.Ldfld, field ); + } + + public void LoadLocal( LocalBuilder local ) + { + Push( local.LocalType ); + + int index = local.LocalIndex; + + switch ( index ) + { + case 0: + m_Generator.Emit( OpCodes.Ldloc_0 ); + break; + + case 1: + m_Generator.Emit( OpCodes.Ldloc_1 ); + break; + + case 2: + m_Generator.Emit( OpCodes.Ldloc_2 ); + break; + + case 3: + m_Generator.Emit( OpCodes.Ldloc_3 ); + break; + + default: + if ( index >= byte.MinValue && index <= byte.MinValue ) + m_Generator.Emit( OpCodes.Ldloc_S, (byte) index ); + else + m_Generator.Emit( OpCodes.Ldloc, (short) index ); + + break; + } + } + + public void StoreLocal( LocalBuilder local ) + { + Pop( local.LocalType ); + + m_Generator.Emit( OpCodes.Stloc, local ); + } + + public void LoadArgument( int index ) + { + if ( index > 0 ) + Push( m_ArgumentTypes[index - 1] ); + else + Push( m_TypeBuilder ); + + switch ( index ) + { + case 0: + m_Generator.Emit( OpCodes.Ldarg_0 ); + break; + + case 1: + m_Generator.Emit( OpCodes.Ldarg_1 ); + break; + + case 2: + m_Generator.Emit( OpCodes.Ldarg_2 ); + break; + + case 3: + m_Generator.Emit( OpCodes.Ldarg_3 ); + break; + + default: + if ( index >= byte.MinValue && index <= byte.MaxValue ) + m_Generator.Emit( OpCodes.Ldarg_S, (byte) index ); + else + m_Generator.Emit( OpCodes.Ldarg, (short) index ); + + break; + } + } + + public void CastAs( Type type ) + { + Pop( typeof( object ) ); + Push( type ); + + m_Generator.Emit( OpCodes.Isinst, type ); + } + + public void Neg() + { + Pop( typeof( int ) ); + + Push( typeof( int ) ); + + m_Generator.Emit( OpCodes.Neg ); + } + + public void Compare( OpCode opCode ) + { + Pop(); + Pop(); + + Push( typeof( int ) ); + + m_Generator.Emit( opCode ); + } + + public void LogicalNot() + { + Pop( typeof( int ) ); + + Push( typeof( int ) ); + + m_Generator.Emit( OpCodes.Ldc_I4_0 ); + m_Generator.Emit( OpCodes.Ceq ); + } + + public void Xor() + { + Pop( typeof( int ) ); + Pop( typeof( int ) ); + + Push( typeof( int ) ); + + m_Generator.Emit( OpCodes.Xor ); + } + + public Type Active + { + get { return m_Stack.Peek(); } + } + + public void Chain( Property prop ) + { + for ( int i = 0; i < prop.Chain.Length; ++i ) + Call( prop.Chain[i].GetGetMethod() ); + } + + public void Call( MethodInfo method ) + { + BeginCall( method ); + + CallInfo call = m_Calls.Peek(); + + if ( call.parms.Length > 0 ) + throw new InvalidOperationException( "Method requires parameters." ); + + FinishCall(); + } + + public delegate void Callback(); + + public bool CompareTo( int sign, Callback argGenerator ) + { + Type active = this.Active; + + MethodInfo compareTo = active.GetMethod( "CompareTo", new Type[] { active } ); + + if ( compareTo == null ) + { + /* This gets a little tricky... + * + * There's a scenario where we might be trying to use CompareTo on an interface + * which, while it doesn't explicitly implement CompareTo itself, is said to + * extend IComparable indirectly. The implementation is implicitly passed off + * to implementers... + * + * interface ISomeInterface : IComparable + * { + * void SomeMethod(); + * } + * + * class SomeClass : ISomeInterface + * { + * void SomeMethod() { ... } + * int CompareTo( object other ) { ... } + * } + * + * In this case, calling ISomeInterface.GetMethod( "CompareTo" ) will return null. + * + * Bleh. + */ + + Type[] ifaces = active.FindInterfaces( delegate( Type type, object obj ) + { + return ( type.IsGenericType ) + && ( type.GetGenericTypeDefinition() == typeof( IComparable<> ) ) + && ( type.GetGenericArguments()[0].IsAssignableFrom( active ) ); + }, null ); + + if ( ifaces.Length > 0 ) + { + compareTo = ifaces[0].GetMethod( "CompareTo", new Type[] { active } ); + } + else + { + ifaces = active.FindInterfaces( delegate( Type type, object obj ) + { + return ( type == typeof( IComparable ) ); + }, null ); + + if ( ifaces.Length > 0 ) + compareTo = ifaces[0].GetMethod( "CompareTo", new Type[] { active } ); + } + } + + if ( compareTo == null ) + return false; + + if ( !active.IsValueType ) + { + /* This object is a reference type, so we have to make it behave + * + * null.CompareTo( null ) = 0 + * real.CompareTo( null ) = -1 + * null.CompareTo( real ) = +1 + * + */ + + LocalBuilder aValue = AcquireTemp( active ); + LocalBuilder bValue = AcquireTemp( active ); + + StoreLocal( aValue ); + + argGenerator(); + + StoreLocal( bValue ); + + /* if ( aValue == null ) + * { + * if ( bValue == null ) + * v = 0; + * else + * v = +1; + * } + * else if ( bValue == null ) + * { + * v = -1; + * } + * else + * { + * v = aValue.CompareTo( bValue ); + * } + */ + + Label store = CreateLabel(); + + Label aNotNull = CreateLabel(); + + LoadLocal( aValue ); + BranchIfTrue( aNotNull ); + // if ( aValue == null ) + { + Label bNotNull = CreateLabel(); + + LoadLocal( bValue ); + BranchIfTrue( bNotNull ); + // if ( bValue == null ) + { + Load( 0 ); + Pop( typeof( int ) ); + Branch( store ); + } + MarkLabel( bNotNull ); + // else + { + Load( sign ); + Pop( typeof( int ) ); + Branch( store ); + } + } + MarkLabel( aNotNull ); + // else + { + Label bNotNull = CreateLabel(); + + LoadLocal( bValue ); + BranchIfTrue( bNotNull ); + // bValue == null + { + Load( -sign ); + Pop( typeof( int ) ); + Branch( store ); + } + MarkLabel( bNotNull ); + // else + { + LoadLocal( aValue ); + BeginCall( compareTo ); + + LoadLocal( bValue ); + ArgumentPushed(); + + FinishCall(); + + if ( sign == -1 ) + Neg(); + } + } + + MarkLabel( store ); + + ReleaseTemp( aValue ); + ReleaseTemp( bValue ); + } + else + { + BeginCall( compareTo ); + + argGenerator(); + + ArgumentPushed(); + + FinishCall(); + + if ( sign == -1 ) + Neg(); + } + + return true; + } + + public void BeginCall( MethodInfo method ) + { + Type type; + + if ( ( method.CallingConvention & CallingConventions.HasThis ) != 0 ) + type = m_Stack.Peek(); + else + type = method.DeclaringType; + + m_Calls.Push( new CallInfo( type, method ) ); + + if ( type.IsValueType ) + { + LocalBuilder temp = AcquireTemp( type ); + + m_Generator.Emit( OpCodes.Stloc, temp ); + m_Generator.Emit( OpCodes.Ldloca, temp ); + + ReleaseTemp( temp ); + } + } + + public void FinishCall() + { + CallInfo call = m_Calls.Pop(); + + if ( ( call.type.IsValueType || call.type.IsByRef ) && call.method.DeclaringType != call.type ) + m_Generator.Emit( OpCodes.Constrained, call.type ); + + if ( call.method.DeclaringType.IsValueType || call.method.IsStatic ) + m_Generator.Emit( OpCodes.Call, call.method ); + else + m_Generator.Emit( OpCodes.Callvirt, call.method ); + + for ( int i = call.parms.Length - 1; i >= 0; --i ) + Pop( call.parms[i].ParameterType ); + + if ( ( call.method.CallingConvention & CallingConventions.HasThis ) != 0 ) + Pop( call.method.DeclaringType ); + + if ( call.method.ReturnType != typeof( void ) ) + Push( call.method.ReturnType ); + } + + public void ArgumentPushed() + { + CallInfo call = m_Calls.Peek(); + + ParameterInfo parm = call.parms[call.index++]; + + Type argumentType = m_Stack.Peek(); + + if ( !parm.ParameterType.IsAssignableFrom( argumentType ) ) + throw new InvalidOperationException( "Parameter type mismatch." ); + + if ( argumentType.IsValueType && !parm.ParameterType.IsValueType ) + m_Generator.Emit( OpCodes.Box, argumentType ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Fastwalk.cs b/Scripts/Misc/Fastwalk.cs new file mode 100644 index 000000000..6e8a79109 --- /dev/null +++ b/Scripts/Misc/Fastwalk.cs @@ -0,0 +1,32 @@ +using System; +using Server; + +namespace Server.Misc +{ + // This fastwalk detection is no longer required + // As of B36 PlayerMobile implements movement packet throttling which more reliably controls movement speeds + public class Fastwalk + { + private static int MaxSteps = 4; // Maximum number of queued steps until fastwalk is detected + private static bool Enabled = false; // Is fastwalk detection enabled? + private static bool UOTDOverride = false; // Should UO:TD clients not be checked for fastwalk? + private static AccessLevel AccessOverride = AccessLevel.GameMaster; // Anyone with this or higher access level is not checked for fastwalk + + public static void Initialize() + { + Mobile.FwdMaxSteps = MaxSteps; + Mobile.FwdEnabled = Enabled; + Mobile.FwdUOTDOverride = UOTDOverride; + Mobile.FwdAccessOverride = AccessOverride; + + if ( Enabled ) + EventSink.FastWalk += new FastWalkEventHandler( OnFastWalk ); + } + + public static void OnFastWalk( FastWalkEventArgs e ) + { + e.Blocked = true;//disallow this fastwalk + Console.WriteLine( "Client: {0}: Fast movement detected (name={1})", e.NetState, e.NetState.Mobile.Name ); + } + } +} diff --git a/Scripts/Misc/FoodDecay.cs b/Scripts/Misc/FoodDecay.cs new file mode 100644 index 000000000..d53851dc4 --- /dev/null +++ b/Scripts/Misc/FoodDecay.cs @@ -0,0 +1,45 @@ +using System; +using Server.Network; +using Server; + +namespace Server.Misc +{ + public class FoodDecayTimer : Timer + { + public static void Initialize() + { + new FoodDecayTimer().Start(); + } + + public FoodDecayTimer() : base( TimeSpan.FromMinutes( 5 ), TimeSpan.FromMinutes( 5 ) ) + { + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + FoodDecay(); + } + + public static void FoodDecay() + { + foreach ( NetState state in NetState.Instances ) + { + HungerDecay( state.Mobile ); + ThirstDecay( state.Mobile ); + } + } + + public static void HungerDecay( Mobile m ) + { + if ( m != null && m.Hunger >= 1 ) + m.Hunger -= 1; + } + + public static void ThirstDecay( Mobile m ) + { + if ( m != null && m.Thirst >= 1 ) + m.Thirst -= 1; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/GiftGiving.cs b/Scripts/Misc/Gifts/GiftGiving.cs new file mode 100644 index 000000000..e7009df70 --- /dev/null +++ b/Scripts/Misc/Gifts/GiftGiving.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Accounting; + +namespace Server.Misc +{ + public enum GiftResult + { + Backpack, + BankBox + } + + public class GiftGiving + { + private static ArrayList m_Givers = new ArrayList(); + + public static void Register( GiftGiver giver ) + { + m_Givers.Add( giver ); + } + + public static void Configure() + { + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static void EventSink_Login( LoginEventArgs e ) + { + Account acct = e.Mobile.Account as Account; + + if ( acct == null ) + return; + + DateTime now = DateTime.Now; + + for ( int i = 0; i < m_Givers.Count; ++i ) + { + GiftGiver giver = (GiftGiver) m_Givers[i]; + + if ( now < giver.Start || now >= giver.Finish ) + continue; // not in the correct timefream + + if ( acct.Created > (giver.Start - giver.MinimumAge) ) + continue; // newly created account + + if ( acct.LastLogin >= giver.Start ) + continue; // already got one + + giver.DelayGiveGift( TimeSpan.FromSeconds( 5.0 ), e.Mobile ); + } + + acct.LastLogin = now; + } + } + + public abstract class GiftGiver + { + public virtual TimeSpan MinimumAge{ get{ return TimeSpan.FromDays( 30.0 ); } } + + public abstract DateTime Start{ get; } + public abstract DateTime Finish{ get; } + public abstract void GiveGift( Mobile mob ); + + public virtual void DelayGiveGift( TimeSpan delay, Mobile mob ) + { + Timer.DelayCall( delay, new TimerStateCallback( DelayGiveGift_Callback ), mob ); + } + + protected virtual void DelayGiveGift_Callback( object state ) + { + GiveGift( (Mobile) state ); + } + + public virtual GiftResult GiveGift( Mobile mob, Item item ) + { + if ( mob.PlaceInBackpack( item ) ) + { + if ( !WeightOverloading.IsOverloaded( mob ) ) + return GiftResult.Backpack; + } + + mob.BankBox.DropItem( item ); + return GiftResult.BankBox; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/DecorativeTopiary.cs b/Scripts/Misc/Gifts/Winter2004/DecorativeTopiary.cs new file mode 100644 index 000000000..62f7ba779 --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/DecorativeTopiary.cs @@ -0,0 +1,48 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class DecorativeTopiary : Item + { + [Constructable] + public DecorativeTopiary() : base( 0x2378 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public DecorativeTopiary( Serial serial ) : base( serial ) + { + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1070880 ); // Winter 2004 + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070880 ); // Winter 2004 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/FestiveCactus.cs b/Scripts/Misc/Gifts/Winter2004/FestiveCactus.cs new file mode 100644 index 000000000..1383c07d4 --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/FestiveCactus.cs @@ -0,0 +1,48 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class FestiveCactus : Item + { + [Constructable] + public FestiveCactus() : base( 0x2376 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public FestiveCactus( Serial serial ) : base( serial ) + { + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1070880 ); // Winter 2004 + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070880 ); // Winter 2004 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs b/Scripts/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs new file mode 100644 index 000000000..feec134e4 --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/LightOfTheWinterSolstice.cs @@ -0,0 +1,103 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + [FlipableAttribute( 0x236E, 0x2371 )] + public class LightOfTheWinterSolstice : Item + { + private static string[] m_StaffNames = new string[] + { + "Aenima", + "Alkiser", + "ASayre", + "David", + "Krrios", + "Mark", + "Merlin", + "Merlix", //LordMerlix + "Phantom", + "Phenos", + "psz", + "Ryan", + "Quantos", + "Outkast", //TheOutkastDev + "V", //Admin_V + "Zippy" + }; + + private string m_Dipper; + + [CommandProperty( AccessLevel.GameMaster )] + public string Dipper{ get{ return m_Dipper; } set{ m_Dipper = value; } } + + [Constructable] + public LightOfTheWinterSolstice() : this( m_StaffNames[Utility.Random( m_StaffNames.Length )] ) + { + } + + [Constructable] + public LightOfTheWinterSolstice( string dipper ) : base( 0x236E ) + { + m_Dipper = dipper; + + Weight = 1.0; + LootType = LootType.Blessed; + Light = LightType.Circle300; + Hue = Utility.RandomDyedHue(); + } + + public LightOfTheWinterSolstice( Serial serial ) : base( serial ) + { + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1070881, m_Dipper ); // Hand Dipped by ~1_name~ + LabelTo( from, 1070880 ); // Winter 2004 + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070881, m_Dipper ); // Hand Dipped by ~1_name~ + list.Add( 1070880 ); // Winter 2004 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (string) m_Dipper ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Dipper = reader.ReadString(); + break; + } + case 0: + { + m_Dipper = m_StaffNames[Utility.Random( m_StaffNames.Length )]; + break; + } + } + + Utility.Intern( ref m_Dipper ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/Mistletoe.cs b/Scripts/Misc/Gifts/Winter2004/Mistletoe.cs new file mode 100644 index 000000000..3ddf5edb3 --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/Mistletoe.cs @@ -0,0 +1,342 @@ +using System; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Items +{ + public class MistletoeAddon : Item, IDyable, IAddon + { + [Constructable] + public MistletoeAddon() : this( Utility.RandomDyedHue() ) + { + } + + [Constructable] + public MistletoeAddon( int hue ) : base( 0x2375 ) + { + Hue = hue; + Movable = false; + } + + public MistletoeAddon( Serial serial ) : base( serial ) + { + } + + public bool CouldFit( IPoint3D p, Map map ) + { + if ( !map.CanFit( p.X, p.Y, p.Z, this.ItemData.Height ) ) + return false; + + if ( this.ItemID == 0x2375 ) + return BaseAddon.IsWall( p.X, p.Y - 1, p.Z, map ); // North wall + else + return BaseAddon.IsWall( p.X - 1, p.Y, p.Z, map ); // West wall + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( FixMovingCrate ) ); + } + + private void FixMovingCrate() + { + if ( this.Deleted ) + return; + + if ( this.Movable || this.IsLockedDown ) + { + Item deed = this.Deed; + + if ( this.Parent is Item ) + { + ((Item)this.Parent).AddItem( deed ); + deed.Location = this.Location; + } + else + { + deed.MoveToWorld( this.Location, this.Map ); + } + + Delete(); + } + } + + public Item Deed + { + get{ return new MistletoeDeed( this.Hue ); } + } + + public override void OnDoubleClick( Mobile from ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsCoOwner( from ) ) + { + if ( from.InRange( this.GetWorldLocation(), 3 ) ) + { + from.CloseGump( typeof( MistletoeAddonGump ) ); + from.SendGump( new MistletoeAddonGump( from, this ) ); + } + else + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019045 ); // I can't reach that. + } + } + } + + public virtual bool Dye( Mobile from, DyeTub sender ) + { + if ( Deleted ) + return false; + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && house.IsCoOwner( from ) ) + { + if ( from.InRange( GetWorldLocation(), 1 ) ) + { + Hue = sender.DyedHue; + return true; + } + else + { + from.SendLocalizedMessage( 500295 ); // You are too far away to do that. + return false; + } + } + else + { + return false; + } + } + + private class MistletoeAddonGump : Gump + { + private Mobile m_From; + private MistletoeAddon m_Addon; + + public MistletoeAddonGump( Mobile from, MistletoeAddon addon ) : base( 150, 50 ) + { + m_From = from; + m_Addon = addon; + + AddPage( 0 ); + + AddBackground( 0, 0, 220, 170, 0x13BE ); + AddBackground( 10, 10, 200, 150, 0xBB8 ); + AddHtmlLocalized( 20, 30, 180, 60, 1062839, false, false ); // Do you wish to re-deed this decoration? + AddHtmlLocalized( 55, 100, 160, 25, 1011011, false, false ); // CONTINUE + AddButton( 20, 100, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 55, 125, 160, 25, 1011012, false, false ); // CANCEL + AddButton( 20, 125, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Addon.Deleted ) + return; + + if ( info.ButtonID == 1 ) + { + if ( m_From.InRange( m_Addon.GetWorldLocation(), 3 ) ) + { + m_From.AddToBackpack( m_Addon.Deed ); + m_Addon.Delete(); + } + else + { + m_From.SendLocalizedMessage( 500295 ); // You are too far away to do that. + } + } + } + } + } + + [Flipable( 0x14F0, 0x14EF )] + public class MistletoeDeed : Item + { + public override int LabelNumber{ get{ return 1070882; } } // Mistletoe Deed + + [Constructable] + public MistletoeDeed() : this( 0 ) + { + } + + [Constructable] + public MistletoeDeed( int hue ) : base( 0x14F0 ) + { + Hue = hue; + Weight = 1.0; + LootType = LootType.Blessed; + } + + public MistletoeDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1070880 ); // Winter 2004 + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070880 ); // Winter 2004 + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + { + BaseHouse house = BaseHouse.FindHouseAt( from ); + + if ( house != null && house.IsCoOwner( from ) ) + { + from.SendLocalizedMessage( 1062838 ); // Where would you like to place this decoration? + from.BeginTarget( -1, true, TargetFlags.None, new TargetStateCallback( Placement_OnTarget ), null ); + } + else + { + from.SendLocalizedMessage( 502092 ); // You must be in your house to do this. + } + } + else + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + } + + public void Placement_OnTarget( Mobile from, object targeted, object state ) + { + IPoint3D p = targeted as IPoint3D; + + if ( p == null ) + return; + + Point3D loc = new Point3D( p ); + + BaseHouse house = BaseHouse.FindHouseAt( loc, from.Map, 16 ); + + if ( house != null && house.IsCoOwner( from ) ) + { + bool northWall = BaseAddon.IsWall( loc.X, loc.Y - 1, loc.Z, from.Map ); + bool westWall = BaseAddon.IsWall( loc.X - 1, loc.Y, loc.Z, from.Map ); + + if ( northWall && westWall ) + from.SendGump( new MistletoeDeedGump( from, loc, this ) ); + else + PlaceAddon( from, loc, northWall, westWall ); + } + else + { + from.SendLocalizedMessage( 1042036 ); // That location is not in your house. + } + } + + private void PlaceAddon( Mobile from, Point3D loc, bool northWall, bool westWall ) + { + if ( Deleted ) + return; + + BaseHouse house = BaseHouse.FindHouseAt( loc, from.Map, 16 ); + + if ( house == null || !house.IsCoOwner( from ) ) + { + from.SendLocalizedMessage( 1042036 ); // That location is not in your house. + return; + } + + int itemID = 0; + + if ( northWall ) + itemID = 0x2374; + else if ( westWall ) + itemID = 0x2375; + else + from.SendLocalizedMessage( 1070883 ); // The mistletoe must be placed next to a wall. + + if ( itemID > 0 ) + { + Item addon = new MistletoeAddon( this.Hue ); + + addon.ItemID = itemID; + addon.MoveToWorld( loc, from.Map ); + + house.Addons.Add( addon ); + Delete(); + } + } + + private class MistletoeDeedGump : Gump + { + private Mobile m_From; + private Point3D m_Loc; + private MistletoeDeed m_Deed; + + public MistletoeDeedGump( Mobile from, Point3D loc, MistletoeDeed deed ) : base( 150, 50 ) + { + m_From = from; + m_Loc = loc; + m_Deed = deed; + + AddBackground( 0, 0, 300, 150, 0xA28 ); + + AddPage( 0 ); + + AddItem( 90, 30, 0x2375 ); + AddItem( 180, 30, 0x2374 ); + AddButton( 50, 35, 0x868, 0x869, 1, GumpButtonType.Reply, 0 ); + AddButton( 145, 35, 0x868, 0x869, 2, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Deed.Deleted ) + return; + + switch( info.ButtonID ) + { + case 1: + m_Deed.PlaceAddon( m_From, m_Loc, false, true ); + break; + case 2: + m_Deed.PlaceAddon( m_From, m_Loc, true, false ); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs b/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs new file mode 100644 index 000000000..dcf52a10f --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/PileOfGlacialSnow.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public class PileOfGlacialSnow : Item + { + [Constructable] + public PileOfGlacialSnow() : base( 0x913 ) + { + Hue = 0x480; + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override int LabelNumber{ get{ return 1070874; } } // a Pile of Glacial Snow + + public PileOfGlacialSnow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1070880 ); // Winter 2004 + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070880 ); // Winter 2004 + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042010 ); // You must have the object in your backpack to use it. + } + else if ( from.CanBeginAction( typeof( SnowPile ) ) ) + { + from.SendLocalizedMessage( 1005575 ); // You carefully pack the snow into a ball... + from.Target = new SnowTarget( from, this ); + } + else + { + from.SendLocalizedMessage( 1005574 ); // The snow is not ready to be packed yet. Keep trying. + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + + public InternalTimer( Mobile from ) : base( TimeSpan.FromSeconds( 5.0 ) ) + { + m_From = from; + } + + protected override void OnTick() + { + m_From.EndAction( typeof( SnowPile ) ); + } + } + + private class SnowTarget : Target + { + private Mobile m_Thrower; + private Item m_Snow; + + public SnowTarget( Mobile thrower, Item snow ) : base ( 10, false, TargetFlags.None ) + { + m_Thrower = thrower; + m_Snow = snow; + } + + protected override void OnTarget( Mobile from, object target ) + { + if ( target == from ) + { + from.SendLocalizedMessage( 1005576 ); // You can't throw this at yourself. + } + else if ( target is Mobile ) + { + Mobile targ = (Mobile) target; + Container pack = targ.Backpack; + + if ( pack != null && pack.FindItemByType( new Type[]{ typeof( SnowPile ), typeof( PileOfGlacialSnow ) } ) != null ) + { + if ( from.BeginAction( typeof( SnowPile ) ) ) + { + new InternalTimer( from ).Start(); + + from.PlaySound( 0x145 ); + + from.Animate( 9, 1, 1, true, false, 0 ); + + targ.SendLocalizedMessage( 1010572 ); // You have just been hit by a snowball! + from.SendLocalizedMessage( 1010573 ); // You throw the snowball and hit the target! + + Effects.SendMovingEffect( from, targ, 0x36E4, 7, 0, false, true, 0x47F, 0 ); + } + else + { + from.SendLocalizedMessage( 1005574 ); // The snow is not ready to be packed yet. Keep trying. + } + } + else + { + from.SendLocalizedMessage( 1005577 ); // You can only throw a snowball at something that can throw one back. + } + } + else + { + from.SendLocalizedMessage( 1005577 ); // You can only throw a snowball at something that can throw one back. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/SnowPile.cs b/Scripts/Misc/Gifts/Winter2004/SnowPile.cs new file mode 100644 index 000000000..58e1afc19 --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/SnowPile.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Items +{ + public class SnowPile : Item + { + [Constructable] + public SnowPile() : base( 0x913 ) + { + Hue = 0x481; + Weight = 1.0; + LootType = LootType.Blessed; + } + + public override int LabelNumber{ get{ return 1005578; } } // a pile of snow + + public SnowPile( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042010 ); // You must have the object in your backpack to use it. + } + else if ( from.CanBeginAction( typeof( SnowPile ) ) ) + { + from.SendLocalizedMessage( 1005575 ); // You carefully pack the snow into a ball... + from.Target = new SnowTarget( from, this ); + } + else + { + from.SendLocalizedMessage( 1005574 ); // The snow is not ready to be packed yet. Keep trying. + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + + public InternalTimer( Mobile from ) : base( TimeSpan.FromSeconds( 5.0 ) ) + { + m_From = from; + } + + protected override void OnTick() + { + m_From.EndAction( typeof( SnowPile ) ); + } + } + + private class SnowTarget : Target + { + private Mobile m_Thrower; + private Item m_Snow; + + public SnowTarget( Mobile thrower, Item snow ) : base ( 10, false, TargetFlags.None ) + { + m_Thrower = thrower; + m_Snow = snow; + } + + protected override void OnTarget( Mobile from, object target ) + { + if ( target == from ) + { + from.SendLocalizedMessage( 1005576 ); // You can't throw this at yourself. + } + else if ( target is Mobile ) + { + Mobile targ = (Mobile) target; + Container pack = targ.Backpack; + + if ( pack != null && pack.FindItemByType( new Type[]{ typeof( SnowPile ), typeof( PileOfGlacialSnow ) } ) != null ) + { + if ( from.BeginAction( typeof( SnowPile ) ) ) + { + new InternalTimer( from ).Start(); + + from.PlaySound( 0x145 ); + + from.Animate( 9, 1, 1, true, false, 0 ); + + targ.SendLocalizedMessage( 1010572 ); // You have just been hit by a snowball! + from.SendLocalizedMessage( 1010573 ); // You throw the snowball and hit the target! + + Effects.SendMovingEffect( from, targ, 0x36E4, 7, 0, false, true, 0x480, 0 ); + } + else + { + from.SendLocalizedMessage( 1005574 ); // The snow is not ready to be packed yet. Keep trying. + } + } + else + { + from.SendLocalizedMessage( 1005577 ); // You can only throw a snowball at something that can throw one back. + } + } + else + { + from.SendLocalizedMessage( 1005577 ); // You can only throw a snowball at something that can throw one back. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/SnowyTree.cs b/Scripts/Misc/Gifts/Winter2004/SnowyTree.cs new file mode 100644 index 000000000..0fd264968 --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/SnowyTree.cs @@ -0,0 +1,48 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Items +{ + public class SnowyTree : Item + { + [Constructable] + public SnowyTree() : base( 0x2377 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + } + + public SnowyTree( Serial serial ) : base( serial ) + { + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + LabelTo( from, 1070880 ); // Winter 2004 + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1070880 ); // Winter 2004 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Gifts/Winter2004/Winter2004.cs b/Scripts/Misc/Gifts/Winter2004/Winter2004.cs new file mode 100644 index 000000000..a771b73aa --- /dev/null +++ b/Scripts/Misc/Gifts/Winter2004/Winter2004.cs @@ -0,0 +1,45 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Misc +{ + public class WinterGiftGiver2004 : GiftGiver + { + public static void Initialize() + { + GiftGiving.Register( new WinterGiftGiver2004() ); + } + + public override DateTime Start{ get{ return new DateTime( 2004, 12, 24 ); } } + public override DateTime Finish{ get{ return new DateTime( 2005, 1, 1 ); } } + + public override void GiveGift( Mobile mob ) + { + GiftBox box = new GiftBox(); + + box.DropItem( new MistletoeDeed() ); + box.DropItem( new PileOfGlacialSnow() ); + box.DropItem( new LightOfTheWinterSolstice() ); + + int random = Utility.Random( 100 ); + + if ( random < 60 ) + box.DropItem( new DecorativeTopiary() ); + else if ( random < 84 ) + box.DropItem( new FestiveCactus() ); + else + box.DropItem( new SnowyTree() ); + + switch ( GiveGift( mob, box ) ) + { + case GiftResult.Backpack: + mob.SendMessage( 0x482, "Happy Holidays from the team! Gift items have been placed in your backpack." ); + break; + case GiftResult.BankBox: + mob.SendMessage( 0x482, "Happy Holidays from the team! Gift items have been placed in your bank box." ); + break; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Guild.cs b/Scripts/Misc/Guild.cs new file mode 100644 index 000000000..aa76e834d --- /dev/null +++ b/Scripts/Misc/Guild.cs @@ -0,0 +1,1740 @@ +using System; +using Server; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Mobiles; +using Server.Gumps; +using Server.Network; +using Server.Targeting; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Guilds +{ + #region Ranks + [Flags] + public enum RankFlags + { + None = 0x00000000, + CanInvitePlayer = 0x00000001, + AccessGuildItems = 0x00000002, + RemoveLowestRank = 0x00000004, + RemovePlayers = 0x00000008, + CanPromoteDemote = 0x00000010, + ControlWarStatus = 0x00000020, + AllianceControl = 0x00000040, + CanSetGuildTitle = 0x00000080, + CanVote = 0x00000100, + + All = Member | CanInvitePlayer | RemovePlayers | CanPromoteDemote | ControlWarStatus | AllianceControl | CanSetGuildTitle, + Member = RemoveLowestRank | AccessGuildItems | CanVote + } + + public class RankDefinition + { + public static RankDefinition[] Ranks = new RankDefinition[] + { + new RankDefinition( 1062963, 0, RankFlags.None ), //Ronin + new RankDefinition( 1062962, 1, RankFlags.Member ), //Member + new RankDefinition( 1062961, 2, RankFlags.Member | RankFlags.RemovePlayers | RankFlags.CanInvitePlayer | RankFlags.CanSetGuildTitle | RankFlags.CanPromoteDemote ), //Emmissary + new RankDefinition( 1062960, 3, RankFlags.Member | RankFlags.ControlWarStatus ), //Warlord + new RankDefinition( 1062959, 4, RankFlags.All ) //Leader + }; + public static RankDefinition Leader{ get{ return Ranks[4]; } } + public static RankDefinition Member{ get{ return Ranks[1]; } } + public static RankDefinition Lowest{ get{ return Ranks[0]; } } + + private TextDefinition m_Name; + private int m_Rank; + private RankFlags m_Flags; + + public TextDefinition Name{ get{ return m_Name; } } + public int Rank{ get{ return m_Rank; } } + public RankFlags Flags{ get{ return m_Flags; } } + + public RankDefinition( TextDefinition name, int rank, RankFlags flags ) + { + m_Name = name; + m_Rank = rank; + m_Flags = flags; + } + + public bool GetFlag( RankFlags flag ) + { + return ( (m_Flags & flag) != 0 ); + } + + public void SetFlag( RankFlags flag, bool value ) + { + if ( value ) + m_Flags |= flag; + else + m_Flags &= ~flag; + } + } + + #endregion + + #region Alliances + public class AllianceInfo + { + private static Hashtable m_Alliances = new Hashtable(); + + public static Hashtable Alliances + { + get{ return m_Alliances; } + } + + private string m_Name; + private Guild m_Leader; + private List m_Members; + private List m_PendingMembers; + + public string Name + { + get{ return m_Name; } + } + + public void CalculateAllianceLeader() + { + m_Leader = ((m_Members.Count >= 2) ? m_Members[Utility.Random( m_Members.Count )] : null); + } + + public void CheckLeader() + { + if( m_Leader == null || m_Leader.Disbanded ) + { + CalculateAllianceLeader(); + + if( m_Leader == null ) + Disband(); + } + } + + public Guild Leader + { + get + { + CheckLeader(); + return m_Leader; + } + set + { + if( m_Leader != value && value != null ) + AllianceMessage( 1070765, value.Name ); // Your Alliance is now led by ~1_GUILDNAME~ + + m_Leader = value; + + if( m_Leader == null ) + CalculateAllianceLeader(); + } + } + + public bool IsPendingMember( Guild g ) + { + if( g.Alliance != this ) + return false; + + return m_PendingMembers.Contains( g ); + } + + public bool IsMember( Guild g ) + { + if( g.Alliance != this ) + return false; + + return m_Members.Contains( g ); + } + + public AllianceInfo( Guild leader, string name, Guild partner ) + { + m_Leader = leader; + m_Name = name; + + m_Members = new List(); + m_PendingMembers = new List(); + + leader.Alliance = this; + partner.Alliance = this; + + if( !m_Alliances.Contains( m_Name.ToLower() ) ) + m_Alliances.Add( m_Name.ToLower(), this ); + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int)0 ); //Version + + writer.Write( m_Name ); + writer.Write( m_Leader ); + + writer.WriteGuildList( m_Members, true ); + writer.WriteGuildList( m_PendingMembers, true ); + + if( !m_Alliances.Contains( m_Name.ToLower() ) ) + m_Alliances.Add( m_Name.ToLower(), this ); + } + + public AllianceInfo( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + m_Name = reader.ReadString(); + m_Leader = reader.ReadGuild() as Guild; + + m_Members = reader.ReadStrongGuildList(); + m_PendingMembers = reader.ReadStrongGuildList(); + + break; + } + } + } + + public void AddPendingGuild( Guild g ) + { + if( g.Alliance != this || m_PendingMembers.Contains( g ) || m_Members.Contains( g ) ) + return; + + m_PendingMembers.Add( g ); + } + + public void TurnToMember( Guild g ) + { + if( g.Alliance != this || !m_PendingMembers.Contains( g ) || m_Members.Contains( g ) ) + return; + + g.GuildMessage( 1070760, this.Name ); // Your Guild has joined the ~1_ALLIANCENAME~ Alliance. + AllianceMessage( 1070761, g.Name ); // A new Guild has joined your Alliance: ~1_GUILDNAME~ + + m_PendingMembers.Remove( g ); + m_Members.Add( g ); + g.InvalidateMemberProperties(); + } + + public void RemoveGuild( Guild g ) + { + if( g.Alliance != this ) + return; + + if( m_PendingMembers.Contains( g ) ) + { + m_PendingMembers.Remove( g ); + } + + if( m_Members.Contains( g ) ) //Sanity, just incase someone with a custom script adds a character to BOTH arrays + { + m_Members.Remove( g ); + g.InvalidateMemberProperties(); + + g.GuildMessage( 1070763, this.Name ); // Your Guild has been removed from the ~1_ALLIANCENAME~ Alliance. + AllianceMessage( 1070764, g.Name ); // A Guild has left your Alliance: ~1_GUILDNAME~ + } + + //g.Alliance = null; //NO G.Alliance call here. Set the Guild's Alliance to null, if you JUST use RemoveGuild, it removes it from the alliance, but doesn't remove the link from the guild to the alliance. setting g.Alliance will call this method. + //to check on OSI: have 3 guilds, make 2 of them a member, one pending. remove one of the memebers. alliance still exist? + //ANSWER: NO + + if( g == m_Leader ) + { + CalculateAllianceLeader(); + + /* + if( m_Leader == null ) //only when m_members.count < 2 + Disband(); + else + AllianceMessage( 1070765, m_Leader.Name ); // Your Alliance is now led by ~1_GUILDNAME~ + */ + } + + if( m_Members.Count < 2 ) + Disband(); + } + + public void Disband() + { + AllianceMessage( 1070762 ); // Your Alliance has dissolved. + + for( int i = 0; i < m_PendingMembers.Count; i++ ) + m_PendingMembers[i].Alliance = null; + + for( int i =0; i < m_Members.Count; i++ ) + m_Members[i].Alliance = null; + + if( m_Alliances[m_Name.ToLower()] == this ) + m_Alliances.Remove( m_Name.ToLower() ); + } + + public void InvalidateMemberProperties() + { + InvalidateMemberProperties( false ); + } + + public void InvalidateMemberProperties( bool onlyOPL ) + { + for ( int i = 0; i < m_Members.Count; i++ ) + { + Guild g = m_Members[i]; + + g.InvalidateMemberProperties( onlyOPL ); + } + } + + public void InvalidateMemberNotoriety() + { + for ( int i = 0; i < m_Members.Count; i++ ) + m_Members[i].InvalidateMemberNotoriety(); + } + + #region Alliance[Text]Message(...) + public void AllianceMessage( int num, bool append, string format, params object[] args ) + { + AllianceMessage( num, append, String.Format( format, args) ); + } + public void AllianceMessage( int number ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].GuildMessage( number ); + } + public void AllianceMessage( int number, string args ) + { + AllianceMessage( number, args, 0x3B2 ); + } + public void AllianceMessage( int number, string args, int hue ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].GuildMessage( number, args, hue ); + } + public void AllianceMessage( int number, bool append, string affix ) + { + AllianceMessage( number, append, affix, "", 0x3B2 ); + } + public void AllianceMessage( int number, bool append, string affix, string args ) + { + AllianceMessage( number, append, affix, args, 0x3B2 ); + } + public void AllianceMessage( int number, bool append, string affix, string args, int hue ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].GuildMessage( number, append, affix, args, hue ); + } + + public void AllianceTextMessage( string text ) + { + AllianceTextMessage( 0x3B2, text ); + } + public void AllianceTextMessage( string format, params object[] args ) + { + AllianceTextMessage( 0x3B2, String.Format( format, args ) ); + } + public void AllianceTextMessage( int hue, string text ) + { + for( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].GuildTextMessage( hue, text ); + } + public void AllianceTextMessage( int hue, string format, params object[] args ) + { + AllianceTextMessage( hue, String.Format( format, args ) ); + } + + public void AllianceChat( Mobile from, int hue, string text ) + { + Packet p = null; + for( int i = 0; i < m_Members.Count; i++ ) + { + Guild g = m_Members[i]; + + for( int j = 0; j < g.Members.Count; j++ ) + { + Mobile m = g.Members[j]; + + NetState state = m.NetState; + + if( state != null ) + { + if( p == null ) + p = Packet.Acquire( new UnicodeMessage( from.Serial, from.Body, MessageType.Alliance, hue, 3, from.Language, from.Name, text ) ); + + state.Send( p ); + } + } + } + + Packet.Release( p ); + } + + public void AllianceChat( Mobile from, string text ) + { + PlayerMobile pm = from as PlayerMobile; + + AllianceChat( from, (pm == null) ? 0x3B2 : pm.AllianceMessageHue, text ); + } + #endregion + + public class AllianceRosterGump : GuildDiplomacyGump + { + protected override bool AllowAdvancedSearch{ get{ return false; } } + + private AllianceInfo m_Alliance; + + public AllianceRosterGump( PlayerMobile pm, Guild g, AllianceInfo alliance ): base( pm, g, true, "", 0, alliance.m_Members, alliance.Name ) + { + m_Alliance = alliance; + } + + public AllianceRosterGump( PlayerMobile pm, Guild g, AllianceInfo alliance, IComparer currentComparer, bool ascending, string filter, int startNumber ) : base( pm, g, currentComparer, ascending, filter, startNumber, alliance.m_Members, alliance.Name ) + { + m_Alliance = alliance; + } + + public override Gump GetResentGump( PlayerMobile pm, Guild g, IComparer comparer, bool ascending, string filter, int startNumber ) + { + return new AllianceRosterGump( pm, g, m_Alliance, comparer, ascending, filter, startNumber ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if( info.ButtonID != 8 ) //So that they can't get to the AdvancedSearch button + base.OnResponse( sender, info ); + } + } + } + #endregion + + #region Wars + public enum WarStatus + { + InProgress = -1, + Win, + Lose, + Draw, + Pending + } + + public class WarDeclaration + { + private int m_Kills; + private int m_MaxKills; + + private TimeSpan m_WarLength; + private DateTime m_WarBeginning; + + private Guild m_Guild; + private Guild m_Opponent; + + private bool m_WarRequester; + + public int Kills + { + get{ return m_Kills; } + set{ m_Kills = value; } + } + public int MaxKills + { + get{ return m_MaxKills; } + set{ m_MaxKills = value; } + } + public TimeSpan WarLength + { + get{ return m_WarLength; } + set{ m_WarLength = value; } + } + public Guild Opponent + { + get{ return m_Opponent; } + } + public Guild Guild + { + get{ return m_Guild; } + } + public DateTime WarBeginning + { + get{ return m_WarBeginning; } + set{ m_WarBeginning = value; } + } + public bool WarRequester + { + get{ return m_WarRequester; } + set{ m_WarRequester = value; } + } + + public WarDeclaration( Guild g, Guild opponent, int maxKills, TimeSpan warLength, bool warRequester ) + { + m_Guild = g; + m_MaxKills = maxKills; + m_Opponent = opponent; + m_WarLength = warLength; + m_WarRequester = warRequester; + } + + public WarDeclaration( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Kills = reader.ReadInt(); + m_MaxKills = reader.ReadInt(); + + m_WarLength = reader.ReadTimeSpan(); + m_WarBeginning = reader.ReadDateTime(); + + m_Guild = reader.ReadGuild() as Guild; + m_Opponent = reader.ReadGuild() as Guild; + + m_WarRequester = reader.ReadBool(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int)0 ); //version + + writer.Write( m_Kills ); + writer.Write( m_MaxKills ); + + writer.Write( m_WarLength ); + writer.Write( m_WarBeginning ); + + writer.Write( m_Guild ); + writer.Write( m_Opponent ); + + writer.Write( m_WarRequester ); + } + + public WarStatus Status + { + get + { + if( m_Opponent == null || m_Opponent.Disbanded ) + return WarStatus.Win; + + if( m_Guild == null || m_Guild.Disbanded ) + return WarStatus.Lose; + + WarDeclaration w = m_Opponent.FindActiveWar( m_Guild ); + + if( m_Opponent.FindPendingWar( m_Guild ) != null && m_Guild.FindPendingWar( m_Opponent ) != null ) + return WarStatus.Pending; + + + if( w == null ) + return WarStatus.Win; + + if( m_WarLength != TimeSpan.Zero && (m_WarBeginning + m_WarLength) < DateTime.Now ) + { + if( m_Kills > w.m_Kills ) + return WarStatus.Win; + else if( m_Kills < w.m_Kills ) + return WarStatus.Lose; + else + return WarStatus.Draw; + } + else if( m_MaxKills > 0 ) + { + if( m_Kills >= m_MaxKills ) + return WarStatus.Win; + else if( w.m_Kills >= w.MaxKills ) + return WarStatus.Lose; + } + + return WarStatus.InProgress; + } + } + } + + public class WarTimer : Timer + { + private static TimeSpan InternalDelay = TimeSpan.FromMinutes( 1.0 ); + + public static void Initialize() + { + if( Guild.NewGuildSystem ) + new WarTimer().Start(); + } + + public WarTimer() : base( InternalDelay, InternalDelay ) + { + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + foreach( Guild g in Guild.List.Values ) + g.CheckExpiredWars(); + } + } + + #endregion + + public class Guild : BaseGuild + { + public static void Configure() + { + EventSink.CreateGuild += new CreateGuildHandler( EventSink_CreateGuild ); + EventSink.GuildGumpRequest += new GuildGumpRequestHandler( EventSink_GuildGumpRequest ); + + CommandSystem.Register( "GuildProps", AccessLevel.Counselor, new CommandEventHandler( GuildProps_OnCommand ) ); + } + + #region GuildProps + [Usage( "GuildProps" )] + [Description( "Opens a menu where you can view and edit guild properties of a targeted player or guild stone. If the new Guild system is active, also brings up the guild gump." )] + private static void GuildProps_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new GuildPropsTarget(); + } + + private class GuildPropsTarget : Target + { + public GuildPropsTarget() : base( -1, true, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object o ) + { + if( !BaseCommand.IsAccessible( from, o ) ) + { + from.SendMessage( "That is not accessible." ); + return; + } + + Guild g = null; + + if( o is Guildstone ) + { + Guildstone stone = o as Guildstone; + if( stone.Guild == null || stone.Guild.Disbanded ) + { + from.SendMessage( "The guild associated with that Guildstone no longer exists" ); + return; + } + else + g = stone.Guild; + } + else if( o is Mobile ) + { + g = ((Mobile)o).Guild as Guild; + } + + if( g != null ) + { + from.SendGump( new PropertiesGump( from, g ) ); + + if( NewGuildSystem && from.AccessLevel >= AccessLevel.GameMaster && from is PlayerMobile ) + from.SendGump( new GuildInfoGump( (PlayerMobile)from, g ) ); + } + else + { + from.SendMessage( "That is not in a guild!" ); + } + } + } + #endregion + + #region EventSinks + public static void EventSink_GuildGumpRequest( GuildGumpRequestArgs args ) + { + PlayerMobile pm = args.Mobile as PlayerMobile; + if( !(NewGuildSystem || pm == null) ) + return; + + if( pm.Guild == null ) + pm.SendGump( new CreateGuildGump( pm ) ); + else + pm.SendGump( new GuildInfoGump( pm, pm.Guild as Guild ) ); + } + + public static BaseGuild EventSink_CreateGuild( CreateGuildEventArgs args ) + { + return (BaseGuild)(new Guild( args.Id )); + } + #endregion + + public static bool NewGuildSystem{ get{ return Core.SE; } } + + public static readonly int RegistrationFee = 25000; + public static readonly int AbbrevLimit = 4; + public static readonly int NameLimit = 20; + public static readonly int MajorityPercentage = 66; + public static readonly TimeSpan InactiveTime = TimeSpan.FromDays( 30 ); + + + #region New Alliances + + public AllianceInfo Alliance + { + get{ + if( m_AllianceInfo != null ) + return m_AllianceInfo; + else if ( m_AllianceLeader != null ) + return m_AllianceLeader.m_AllianceInfo; + else + return null; + } + set + { + AllianceInfo current = this.Alliance; + + if( value == current ) + return; + + if( current != null ) + { + current.RemoveGuild( this ); + } + + if( value != null ) + { + + if( value.Leader == this ) + m_AllianceInfo = value; + else + m_AllianceLeader = value.Leader; + + value.AddPendingGuild( this ); + } + else + { + m_AllianceInfo = null; + m_AllianceLeader = null; + } + } + } + + #endregion + + #region New Wars + + public List PendingWars + { + get{ return m_PendingWars; } + } + public List AcceptedWars + { + get{ return m_AcceptedWars; } + } + + + public WarDeclaration FindPendingWar( Guild g ) + { + for( int i = 0; i < PendingWars.Count; i++ ) + { + WarDeclaration w = PendingWars[i]; + + if( w.Opponent == g ) + return w; + } + + return null; + } + + public WarDeclaration FindActiveWar( Guild g ) + { + for( int i = 0; i < AcceptedWars.Count; i++ ) + { + WarDeclaration w = AcceptedWars[i]; + + if( w.Opponent == g ) + return w; + } + + return null; + } + + + + public void CheckExpiredWars() + { + for( int i = 0; i < AcceptedWars.Count; i++ ) + { + WarDeclaration w = AcceptedWars[i]; + Guild g = w.Opponent; + + WarStatus status = w.Status; + + if( status != WarStatus.InProgress ) + { + AllianceInfo myAlliance = this.Alliance; + bool inAlliance = ( myAlliance != null && myAlliance.IsMember( this ) ); + + AllianceInfo otherAlliance = ((g != null) ? g.Alliance : null); + bool otherInAlliance = ( otherAlliance != null && otherAlliance.IsMember( this ) ); + + if( inAlliance ) + { + myAlliance.AllianceMessage( 1070739 + (int)status, (g == null) ? "a deleted opponent" : (otherInAlliance ? otherAlliance.Name : g.Name) ); + myAlliance.InvalidateMemberProperties(); + } + else + { + GuildMessage( 1070739 + (int)status, (g == null) ? "a deleted opponent" : (otherInAlliance ? otherAlliance.Name : g.Name) ); + InvalidateMemberProperties(); + } + + this.AcceptedWars.Remove( w ); + + if( g != null ) + { + if( status != WarStatus.Draw ) + status = (WarStatus)((int)status + 1 % 2); + + if( otherInAlliance ) + { + otherAlliance.AllianceMessage( 1070739 + (int)status, ( inAlliance ? this.Alliance.Name : this.Name ) ); + otherAlliance.InvalidateMemberProperties(); + } + else + { + g.GuildMessage( 1070739 + (int)status, (inAlliance ? this.Alliance.Name : this.Name) ); + g.InvalidateMemberProperties(); + } + + g.AcceptedWars.Remove( g.FindActiveWar( this ) ); + } + } + } + + for( int i = 0; i < PendingWars.Count; i++ ) + { + WarDeclaration w = PendingWars[i]; + Guild g = w.Opponent; + + if( w.Status != WarStatus.Pending ) + { + //All sanity in here + this.PendingWars.Remove( w ); + + if( g != null ) + { + g.PendingWars.Remove( g.FindPendingWar( this ) ); + } + } + } + } + + public static void HandleDeath( Mobile victim ) + { + HandleDeath( victim, null ); + } + + public static void HandleDeath( Mobile victim, Mobile killer ) + { + if( !NewGuildSystem ) + return; + + if ( killer == null ) + killer = victim.FindMostRecentDamager( false ); + + if( killer == null || victim.Guild == null || killer.Guild == null ) + return; + + Guild victimGuild = victim.Guild as Guild; + Guild killerGuild = killer.Guild as Guild; + + WarDeclaration war = killerGuild.FindActiveWar( victimGuild ); + if( war != null ) + war.Kills++; + + victimGuild.CheckExpiredWars(); + killerGuild.CheckExpiredWars(); + } + #endregion + + #region Var declarations + private Mobile m_Leader; + + private string m_Name; + private string m_Abbreviation; + + private List m_Allies; + private List m_Enemies; + + private List m_Members; + + private Item m_Guildstone; + private Item m_Teleporter; + + private string m_Charter; + private string m_Website; + + private DateTime m_LastFealty; + + private GuildType m_Type; + private DateTime m_TypeLastChange; + + private List m_AllyDeclarations, m_AllyInvitations; + + private List m_WarDeclarations, m_WarInvitations; + private List m_Candidates, m_Accepted; + + private List m_PendingWars, m_AcceptedWars; + + private AllianceInfo m_AllianceInfo; + private Guild m_AllianceLeader; + #endregion + + public Guild( Mobile leader, string name, string abbreviation ) + { + #region Ctor mumbo-jumbo + m_Leader = leader; + + m_Members = new List(); + m_Allies = new List(); + m_Enemies = new List(); + m_WarDeclarations = new List(); + m_WarInvitations = new List(); + m_AllyDeclarations = new List(); + m_AllyInvitations = new List(); + m_Candidates = new List(); + m_Accepted = new List(); + + m_LastFealty = DateTime.Now; + + m_Name = name; + m_Abbreviation = abbreviation; + + m_TypeLastChange = DateTime.MinValue; + + AddMember( m_Leader ); + + if( m_Leader is PlayerMobile ) + ((PlayerMobile)m_Leader).GuildRank = RankDefinition.Leader; + + m_AcceptedWars = new List(); + m_PendingWars = new List(); + #endregion + } + + public Guild( int id ) : base( id )//serialization ctor + { + } + + public void InvalidateMemberProperties() + { + InvalidateMemberProperties( false ); + } + + public void InvalidateMemberProperties( bool onlyOPL ) + { + if ( m_Members != null ) + { + for ( int i = 0; i < m_Members.Count; i++ ) + { + Mobile m = m_Members[i]; + m.InvalidateProperties(); + + if ( !onlyOPL ) + m.Delta( MobileDelta.Noto ); + } + } + } + + public void InvalidateMemberNotoriety() + { + if ( m_Members != null ) + { + for (int i=0;i= 0; --i ) + if ( i < m_Allies.Count ) + RemoveAlly( m_Allies[i] ); + + for ( int i = m_Enemies.Count - 1; i >= 0; --i ) + if ( i < m_Enemies.Count ) + RemoveEnemy( m_Enemies[i] ); + + if ( !NewGuildSystem && m_Guildstone != null ) + m_Guildstone.Delete(); + + m_Guildstone = null; + + CheckExpiredWars(); + + Alliance = null; + } + + #region Is(...) + public bool IsMember( Mobile m ) + { + return m_Members.Contains( m ); + } + + public bool IsAlly( Guild g ) + { + if( NewGuildSystem ) + { + return (Alliance != null && Alliance.IsMember( this ) && Alliance.IsMember( g )); + } + + return m_Allies.Contains( g ); + } + + public bool IsEnemy( Guild g ) + { + if( NewGuildSystem ) + return IsWar( g ); + + if( m_Type != GuildType.Regular && g.m_Type != GuildType.Regular && m_Type != g.m_Type ) + return true; + + return m_Enemies.Contains( g ); + } + + public bool IsWar( Guild g ) + { + if( NewGuildSystem ) + { + if( FindActiveWar( g ) != null ) + return true; + + if( Alliance != null && this != Alliance.Leader && Alliance.Leader != null && Alliance.IsMember( this ) ) + if( Alliance.Leader.FindActiveWar( g ) != null ) + return true; + + if( g.Alliance != null && g.Alliance.Leader != null && g.Alliance.Leader != g && g.Alliance.IsMember( g ) ) + if( FindActiveWar( g.Alliance.Leader ) != null ) + return true; + + return false; + } + + + return m_Enemies.Contains( g ); + } + #endregion + + #region Serialization + public override void Serialize( GenericWriter writer ) + { + if ( this.LastFealty+TimeSpan.FromDays( 1.0 ) < DateTime.Now ) + this.CalculateGuildmaster(); + + CheckExpiredWars(); + + if( Alliance != null ) + Alliance.CheckLeader(); + + writer.Write( (int) 5 );//version + + #region War Serialization + writer.Write( m_PendingWars.Count ); + + for( int i = 0; i < m_PendingWars.Count; i++ ) + { + m_PendingWars[i].Serialize( writer ); + } + + writer.Write( m_AcceptedWars.Count ); + + for( int i = 0; i < m_AcceptedWars.Count; i++ ) + { + m_AcceptedWars[i].Serialize( writer ); + } + #endregion + + #region Alliances + + bool isAllianceLeader = (m_AllianceLeader == null && m_AllianceInfo != null ); + writer.Write( isAllianceLeader ); + + if( isAllianceLeader ) + m_AllianceInfo.Serialize( writer ); + else + writer.Write( m_AllianceLeader ); + + #endregion + + // + + writer.WriteGuildList( m_AllyDeclarations, true ); + writer.WriteGuildList( m_AllyInvitations, true ); + + writer.Write( m_TypeLastChange ); + + writer.Write( (int)m_Type ); + + writer.Write( m_LastFealty ); + + writer.Write( m_Leader ); + writer.Write( m_Name ); + writer.Write( m_Abbreviation ); + + writer.WriteGuildList( m_Allies, true ); + writer.WriteGuildList( m_Enemies, true ); + writer.WriteGuildList( m_WarDeclarations, true ); + writer.WriteGuildList( m_WarInvitations, true ); + + writer.WriteMobileList( m_Members, true ); + writer.WriteMobileList( m_Candidates, true ); + writer.WriteMobileList( m_Accepted, true ); + + writer.Write( m_Guildstone ); + writer.Write( m_Teleporter ); + + writer.Write( m_Charter ); + writer.Write( m_Website ); + } + + public override void Deserialize( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch ( version ) + { + case 5: + { + int count = reader.ReadInt(); + + m_PendingWars = new List(); + for( int i = 0; i < count; i++ ) + { + m_PendingWars.Add( new WarDeclaration( reader ) ); + } + + count = reader.ReadInt(); + m_AcceptedWars = new List(); + for( int i = 0; i < count; i++ ) + { + m_AcceptedWars.Add( new WarDeclaration( reader ) ); + } + + bool isAllianceLeader = reader.ReadBool(); + + if( isAllianceLeader ) + m_AllianceInfo = new AllianceInfo( reader ); + else + m_AllianceLeader = reader.ReadGuild() as Guild; + + + goto case 4; + } + case 4: + { + m_AllyDeclarations = reader.ReadStrongGuildList(); + m_AllyInvitations = reader.ReadStrongGuildList(); + + goto case 3; + } + case 3: + { + m_TypeLastChange = reader.ReadDateTime(); + + goto case 2; + } + case 2: + { + m_Type = (GuildType)reader.ReadInt(); + + goto case 1; + } + case 1: + { + m_LastFealty = reader.ReadDateTime(); + + goto case 0; + } + case 0: + { + Leader = reader.ReadMobile(); + m_Name = reader.ReadString(); + m_Abbreviation = reader.ReadString(); + + m_Allies = reader.ReadStrongGuildList(); + m_Enemies = reader.ReadStrongGuildList(); + m_WarDeclarations = reader.ReadStrongGuildList(); + m_WarInvitations = reader.ReadStrongGuildList(); + + m_Members = reader.ReadStrongMobileList(); + m_Candidates = reader.ReadStrongMobileList(); + m_Accepted = reader.ReadStrongMobileList(); + + m_Guildstone = reader.ReadItem(); + m_Teleporter = reader.ReadItem(); + + m_Charter = reader.ReadString(); + m_Website = reader.ReadString(); + + break; + } + } + + if ( m_AllyDeclarations == null ) + m_AllyDeclarations = new List(); + + if ( m_AllyInvitations == null ) + m_AllyInvitations = new List(); + + + if ( m_AcceptedWars == null ) + m_AcceptedWars = new List(); + + if ( m_PendingWars == null ) + m_PendingWars = new List(); + + + /* + if ( ( !NewGuildSystem && m_Guildstone == null )|| m_Members.Count == 0 ) + Disband(); + */ + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( VerifyGuild_Callback ) ); + } + + private void VerifyGuild_Callback() + { + if( (!NewGuildSystem && m_Guildstone == null) || m_Members.Count == 0 ) + Disband(); + + CheckExpiredWars(); + + AllianceInfo alliance = this.Alliance; + + if( alliance != null ) + alliance.CheckLeader(); + + alliance = this.Alliance; //CheckLeader could possibly change the value of this.Alliance + + if( alliance != null && !alliance.IsMember( this ) && !alliance.IsPendingMember( this ) ) //This block is there to fix a bug in the code in an older version. + this.Alliance = null; //Will call Alliance.RemoveGuild which will set it null & perform all the pertient checks as far as alliacne disbanding + + } + + #endregion + + #region Add/Remove Member/Old Ally/Old Enemy + public void AddMember( Mobile m ) + { + if ( !m_Members.Contains( m ) ) + { + if ( m.Guild != null && m.Guild != this ) + ((Guild)m.Guild).RemoveMember( m ); + + m_Members.Add( m ); + m.Guild = this; + + if( !NewGuildSystem ) + m.GuildFealty = m_Leader; + else + m.GuildFealty = null; + + if( m is PlayerMobile ) + ((PlayerMobile)m).GuildRank = RankDefinition.Lowest; + } + } + + public void RemoveMember( Mobile m ) + { + RemoveMember( m, 1018028 ); // You have been dismissed from your guild. + } + public void RemoveMember( Mobile m, int message ) + { + if ( m_Members.Contains( m ) ) + { + m_Members.Remove( m ); + m.Guild = null; + + if( m is PlayerMobile ) + ((PlayerMobile)m).GuildRank = RankDefinition.Lowest; + + if( message > 0 ) + m.SendLocalizedMessage( message ); + + if ( m == m_Leader ) + { + CalculateGuildmaster(); + + if ( m_Leader == null ) + Disband(); + } + + if ( m_Members.Count == 0 ) + Disband(); + } + } + + public void AddAlly( Guild g ) + { + if ( !m_Allies.Contains( g ) ) + { + m_Allies.Add( g ); + + g.AddAlly( this ); + } + } + + public void RemoveAlly( Guild g ) + { + if ( m_Allies.Contains( g ) ) + { + m_Allies.Remove( g ); + + g.RemoveAlly( this ); + } + } + + public void AddEnemy( Guild g ) + { + if ( !m_Enemies.Contains( g ) ) + { + m_Enemies.Add( g ); + + g.AddEnemy( this ); + } + } + + public void RemoveEnemy( Guild g ) + { + if ( m_Enemies != null && m_Enemies.Contains( g ) ) + { + m_Enemies.Remove( g ); + + g.RemoveEnemy( this ); + } + } + + #endregion + + #region Guild[Text]Message(...) + public void GuildMessage( int num, bool append, string format, params object[] args ) + { + GuildMessage( num, append, String.Format( format, args) ); + } + public void GuildMessage( int number ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].SendLocalizedMessage( number ); + } + public void GuildMessage( int number, string args ) + { + GuildMessage( number, args, 0x3B2 ); + } + public void GuildMessage( int number, string args, int hue ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].SendLocalizedMessage( number, args, hue ); + } + public void GuildMessage( int number, bool append, string affix ) + { + GuildMessage( number, append, affix, "", 0x3B2 ); + } + public void GuildMessage( int number, bool append, string affix, string args ) + { + GuildMessage( number, append, affix, args, 0x3B2 ); + } + public void GuildMessage( int number, bool append, string affix, string args, int hue ) + { + for ( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].SendLocalizedMessage( number, append, affix, args, hue ); + } + + public void GuildTextMessage( string text ) + { + GuildTextMessage( 0x3B2, text ); + } + public void GuildTextMessage( string format, params object[] args ) + { + GuildTextMessage( 0x3B2, String.Format( format, args ) ); + } + public void GuildTextMessage( int hue, string text ) + { + for( int i = 0; i < m_Members.Count; ++i ) + m_Members[i].SendMessage( hue, text ); + } + public void GuildTextMessage( int hue, string format, params object[] args ) + { + GuildTextMessage( hue, String.Format( format, args ) ); + } + + public void GuildChat( Mobile from, int hue, string text ) + { + Packet p = null; + for( int i = 0; i < m_Members.Count; i++ ) + { + Mobile m = m_Members[i]; + + NetState state = m.NetState; + + if( state != null ) + { + if( p == null ) + p = Packet.Acquire( new UnicodeMessage( from.Serial, from.Body, MessageType.Guild, hue, 3, from.Language, from.Name, text ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + } + + public void GuildChat( Mobile from, string text ) + { + PlayerMobile pm = from as PlayerMobile; + + GuildChat( from, (pm == null) ? 0x3B2 : pm.GuildMessageHue, text ); + } + #endregion + + #region Voting + public bool CanVote( Mobile m ) + { + if( NewGuildSystem ) + { + PlayerMobile pm = m as PlayerMobile; + if( pm == null || !pm.GuildRank.GetFlag( RankFlags.CanVote ) ) + return false; + } + + return ( m == null || m.Deleted || m.Guild != this ); + } + public bool CanBeVotedFor( Mobile m ) + { + if( NewGuildSystem ) + { + PlayerMobile pm = m as PlayerMobile; + if( pm == null || pm.LastOnline + InactiveTime < DateTime.Now ) + return false; + } + + return ( m == null || m.Deleted || m.Guild != this ); + } + + public void CalculateGuildmaster() + { + Hashtable votes = new Hashtable(); + + int votingMembers = 0; + + for ( int i = 0; m_Members != null && i < m_Members.Count; ++i ) + { + Mobile memb = m_Members[i]; + + if ( !CanVote( memb ) ) + continue; + + Mobile m = memb.GuildFealty; + + //if ( m == null || m.Deleted || m.Guild != this ) + if( !CanBeVotedFor( m ) ) + { + if ( m_Leader != null && !m_Leader.Deleted && m_Leader.Guild == this ) + m = m_Leader; + else + m = memb; + } + + if ( m == null ) + continue; + + if ( votes[m] == null ) + votes[m] = (int)1; + else + votes[m] = (int)(votes[m]) + 1; + + votingMembers++; + } + + Mobile winner = null; + int highVotes = 0; + + foreach ( DictionaryEntry de in votes ) + { + Mobile m = (Mobile)de.Key; + int val = (int)de.Value; + + if ( winner == null || val > highVotes ) + { + winner = m; + highVotes = val; + } + } + + if( NewGuildSystem && (highVotes * 100) / Math.Max( votingMembers, 1 ) < MajorityPercentage && m_Leader != null && winner != m_Leader && !m_Leader.Deleted && m_Leader.Guild == this ) + winner = m_Leader; + + if ( m_Leader != winner && winner != null ) + GuildMessage( 1018015, true, winner.Name ); // Guild Message: Guildmaster changed to: + + Leader = winner; + m_LastFealty = DateTime.Now; + } + + #endregion + + #region Getters & Setters + [CommandProperty( AccessLevel.GameMaster )] + public Item Guildstone + { + get + { + return m_Guildstone; + } + set + { + m_Guildstone = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Item Teleporter + { + get + { + return m_Teleporter; + } + set + { + m_Teleporter = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override string Name + { + get + { + return m_Name; + } + set + { + m_Name = value; + + InvalidateMemberProperties( true ); + + if ( m_Guildstone != null ) + m_Guildstone.InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Website + { + get + { + return m_Website; + } + set + { + m_Website = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override string Abbreviation + { + get + { + return m_Abbreviation; + } + set + { + m_Abbreviation = value; + + InvalidateMemberProperties( true ); + + if( m_Guildstone != null ) + m_Guildstone.InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Charter + { + get + { + return m_Charter; + } + set + { + m_Charter = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override GuildType Type + { + get + { + return m_Type; + } + set + { + if ( m_Type != value ) + { + m_Type = value; + m_TypeLastChange = DateTime.Now; + + InvalidateMemberProperties(); + } + } + } + + + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastFealty + { + get + { + return m_LastFealty; + } + set + { + m_LastFealty = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime TypeLastChange + { + get + { + return m_TypeLastChange; + } + } + + + public List Allies + { + get + { + return m_Allies; + } + } + + public List Enemies + { + get + { + return m_Enemies; + } + } + + public List AllyDeclarations + { + get + { + return m_AllyDeclarations; + } + } + + public List AllyInvitations + { + get + { + return m_AllyInvitations; + } + } + + public List WarDeclarations + { + get + { + return m_WarDeclarations; + } + } + + public List WarInvitations + { + get + { + return m_WarInvitations; + } + } + + public List Candidates + { + get + { + return m_Candidates; + } + } + + public List Accepted + { + get + { + return m_Accepted; + } + } + + public List Members + { + get + { + return m_Members; + } + } + + #endregion + + } +} diff --git a/Scripts/Misc/HardwareInfo.cs b/Scripts/Misc/HardwareInfo.cs new file mode 100644 index 000000000..8e2df65a8 --- /dev/null +++ b/Scripts/Misc/HardwareInfo.cs @@ -0,0 +1,183 @@ +using System; +using Server; +using Server.Commands; +using Server.Accounting; +using Server.Network; +using Server.Targeting; + +namespace Server +{ + public class HardwareInfo + { + private int m_InstanceID; + private int m_OSMajor, m_OSMinor, m_OSRevision; + private int m_CpuManufacturer, m_CpuFamily, m_CpuModel, m_CpuClockSpeed, m_CpuQuantity; + private int m_PhysicalMemory; + private int m_ScreenWidth, m_ScreenHeight, m_ScreenDepth; + private int m_DXMajor, m_DXMinor; + private int m_VCVendorID, m_VCDeviceID, m_VCMemory; + private int m_Distribution, m_ClientsRunning, m_ClientsInstalled, m_PartialInstalled; + private string m_VCDescription; + private string m_Language; + private string m_Unknown; + + [CommandProperty( AccessLevel.GameMaster )] + public int CpuModel{ get{ return m_CpuModel; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int CpuClockSpeed{ get{ return m_CpuClockSpeed; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int CpuQuantity{ get{ return m_CpuQuantity; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int OSMajor{ get{ return m_OSMajor; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int OSMinor{ get{ return m_OSMinor; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int OSRevision{ get{ return m_OSRevision; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int InstanceID{ get{ return m_InstanceID; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ScreenWidth{ get{ return m_ScreenWidth; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ScreenHeight{ get{ return m_ScreenHeight; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ScreenDepth{ get{ return m_ScreenDepth; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int PhysicalMemory{ get{ return m_PhysicalMemory; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int CpuManufacturer{ get{ return m_CpuManufacturer; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int CpuFamily{ get{ return m_CpuFamily; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int VCVendorID{ get{ return m_VCVendorID; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int VCDeviceID{ get{ return m_VCDeviceID; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int VCMemory{ get{ return m_VCMemory; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DXMajor{ get{ return m_DXMajor; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int DXMinor{ get{ return m_DXMinor; } } + + [CommandProperty( AccessLevel.GameMaster )] + public string VCDescription{ get{ return m_VCDescription; } } + + [CommandProperty( AccessLevel.GameMaster )] + public string Language{ get{ return m_Language; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Distribution{ get{ return m_Distribution; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ClientsRunning{ get{ return m_ClientsRunning; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ClientsInstalled{ get{ return m_ClientsInstalled; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int PartialInstalled{ get{ return m_PartialInstalled; } } + + [CommandProperty( AccessLevel.GameMaster )] + public string Unknown{ get{ return m_Unknown; } } + + public static void Initialize() + { + PacketHandlers.Register( 0xD9, 0x10C, false, new OnPacketReceive( OnReceive ) ); + + CommandSystem.Register( "HWInfo", AccessLevel.GameMaster, new CommandEventHandler( HWInfo_OnCommand ) ); + } + + [Usage( "HWInfo" )] + [Description( "Displays information about a targeted player's hardware." )] + public static void HWInfo_OnCommand( CommandEventArgs e ) + { + e.Mobile.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( HWInfo_OnTarget ) ); + e.Mobile.SendMessage( "Target a player to view their hardware information." ); + } + + public static void HWInfo_OnTarget( Mobile from, object obj ) + { + if ( obj is Mobile && ((Mobile)obj).Player ) + { + Mobile m = (Mobile)obj; + Account acct = m.Account as Account; + + if ( acct != null ) + { + HardwareInfo hwInfo = acct.HardwareInfo; + + if ( hwInfo != null ) + CommandLogging.WriteLine( from, "{0} {1} viewing hardware info of {2}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( m ) ); + + if ( hwInfo != null ) + from.SendGump( new Gumps.PropertiesGump( from, hwInfo ) ); + else + from.SendMessage( "No hardware information for that account was found." ); + } + else + { + from.SendMessage( "No account has been attached to that player." ); + } + } + else + { + from.BeginTarget( -1, false, TargetFlags.None, new TargetCallback( HWInfo_OnTarget ) ); + from.SendMessage( "That is not a player. Try again." ); + } + } + + public static void OnReceive( NetState state, PacketReader pvSrc ) + { + pvSrc.ReadByte(); // 1: <4.0.1a, 2>=4.0.1a + + HardwareInfo info = new HardwareInfo(); + + info.m_InstanceID = pvSrc.ReadInt32(); + info.m_OSMajor = pvSrc.ReadInt32(); + info.m_OSMinor = pvSrc.ReadInt32(); + info.m_OSRevision = pvSrc.ReadInt32(); + info.m_CpuManufacturer = pvSrc.ReadByte(); + info.m_CpuFamily = pvSrc.ReadInt32(); + info.m_CpuModel = pvSrc.ReadInt32(); + info.m_CpuClockSpeed = pvSrc.ReadInt32(); + info.m_CpuQuantity = pvSrc.ReadByte(); + info.m_PhysicalMemory = pvSrc.ReadInt32(); + info.m_ScreenWidth = pvSrc.ReadInt32(); + info.m_ScreenHeight = pvSrc.ReadInt32(); + info.m_ScreenDepth = pvSrc.ReadInt32(); + info.m_DXMajor = pvSrc.ReadInt16(); + info.m_DXMinor = pvSrc.ReadInt16(); + info.m_VCDescription = pvSrc.ReadUnicodeStringLESafe( 64 ); + info.m_VCVendorID = pvSrc.ReadInt32(); + info.m_VCDeviceID = pvSrc.ReadInt32(); + info.m_VCMemory = pvSrc.ReadInt32(); + info.m_Distribution = pvSrc.ReadByte(); + info.m_ClientsRunning = pvSrc.ReadByte(); + info.m_ClientsInstalled = pvSrc.ReadByte(); + info.m_PartialInstalled = pvSrc.ReadByte(); + info.m_Language = pvSrc.ReadUnicodeStringLESafe( 4 ); + info.m_Unknown = pvSrc.ReadStringSafe( 64 ); + + Account acct = state.Account as Account; + + if ( acct != null ) + acct.HardwareInfo = info; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/InhumanSpeech.cs b/Scripts/Misc/InhumanSpeech.cs new file mode 100644 index 000000000..3764f5680 --- /dev/null +++ b/Scripts/Misc/InhumanSpeech.cs @@ -0,0 +1,595 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using Server; + +namespace Server.Misc +{ + [Flags] + public enum IHSFlags + { + None = 0x00, + OnDamaged = 0x01, + OnDeath = 0x02, + OnMovement = 0x04, + OnSpeech = 0x08, + All = OnDamaged | OnDeath | OnMovement + } // NOTE: To enable monster conversations, add " | OnSpeech" to the "All" line + + public class InhumanSpeech + { + private static InhumanSpeech m_RatmanSpeech; + + public static InhumanSpeech Ratman + { + get + { + if ( m_RatmanSpeech == null ) + { + m_RatmanSpeech = new InhumanSpeech(); + + m_RatmanSpeech.Hue = 149; + m_RatmanSpeech.Sound = 438; + + m_RatmanSpeech.Flags = IHSFlags.All; + + m_RatmanSpeech.Keywords = new string[] + { + "meat", "gold", "kill", "killing", "slay", + "sword", "axe", "spell", "magic", "spells", + "swords", "axes", "mace", "maces", "monster", + "monsters", "food", "run", "escape", "away", + "help", "dead", "die", "dying", "lose", + "losing", "life", "lives", "death", "ghost", + "ghosts", "british", "blackthorn", "guild", + "guilds", "dragon", "dragons", "game", "games", + "ultima", "silly", "stupid", "dumb", "idiot", + "idiots", "cheesy", "cheezy", "crazy", "dork", + "jerk", "fool", "foolish", "ugly", "insult", "scum" + }; + + m_RatmanSpeech.Responses = new string[] + { + "meat", "kill", "pound", "crush", "yum yum", + "crunch", "destroy", "murder", "eat", "munch", + "massacre", "food", "monster", "evil", "run", + "die", "lose", "dumb", "idiot", "fool", "crazy", + "dinner", "lunch", "breakfast", "fight", "battle", + "doomed", "rip apart", "tear apart", "smash", + "edible?", "shred", "disembowel", "ugly", "smelly", + "stupid", "hideous", "smell", "tasty", "invader", + "attack", "raid", "plunder", "pillage", "treasure", + "loser", "lose", "scum" + }; + + m_RatmanSpeech.Syllables = new string[] + { + "skrit", + + "ch", "ch", + "it", "ti", "it", "ti", + + "ak", "ek", "ik", "ok", "uk", "yk", + "ka", "ke", "ki", "ko", "ku", "ky", + "at", "et", "it", "ot", "ut", "yt", + + "cha", "che", "chi", "cho", "chu", "chy", + "ach", "ech", "ich", "och", "uch", "ych", + "att", "ett", "itt", "ott", "utt", "ytt", + "tat", "tet", "tit", "tot", "tut", "tyt", + "tta", "tte", "tti", "tto", "ttu", "tty", + "tak", "tek", "tik", "tok", "tuk", "tyk", + "ack", "eck", "ick", "ock", "uck", "yck", + "cka", "cke", "cki", "cko", "cku", "cky", + "rak", "rek", "rik", "rok", "ruk", "ryk", + + "tcha", "tche", "tchi", "tcho", "tchu", "tchy", + "rach", "rech", "rich", "roch", "ruch", "rych", + "rrap", "rrep", "rrip", "rrop", "rrup", "rryp", + "ccka", "ccke", "ccki", "ccko", "ccku", "ccky" + }; + } + + return m_RatmanSpeech; + } + } + + private static InhumanSpeech m_OrcSpeech; + + public static InhumanSpeech Orc + { + get + { + if ( m_OrcSpeech == null ) + { + m_OrcSpeech = new InhumanSpeech(); + + m_OrcSpeech.Hue = 34; + m_OrcSpeech.Sound = 432; + + m_OrcSpeech.Flags = IHSFlags.All; + + m_OrcSpeech.Keywords = new string[] + { + "meat", "gold", "kill", "killing", "slay", + "sword", "axe", "spell", "magic", "spells", + "swords", "axes", "mace", "maces", "monster", + "monsters", "food", "run", "escape", "away", + "help", "dead", "die", "dying", "lose", + "losing", "life", "lives", "death", "ghost", + "ghosts", "british", "blackthorn", "guild", + "guilds", "dragon", "dragons", "game", "games", + "ultima", "silly", "stupid", "dumb", "idiot", + "idiots", "cheesy", "cheezy", "crazy", "dork", + "jerk", "fool", "foolish", "ugly", "insult", "scum" + }; + + m_OrcSpeech.Responses = new string[] + { + "meat", "kill", "pound", "crush", "yum yum", + "crunch", "destroy", "murder", "eat", "munch", + "massacre", "food", "monster", "evil", "run", + "die", "lose", "dumb", "idiot", "fool", "crazy", + "dinner", "lunch", "breakfast", "fight", "battle", + "doomed", "rip apart", "tear apart", "smash", + "edible?", "shred", "disembowel", "ugly", "smelly", + "stupid", "hideous", "smell", "tasty", "invader", + "attack", "raid", "plunder", "pillage", "treasure", + "loser", "lose", "scum" + }; + + m_OrcSpeech.Syllables = new string[] + { + "bu", "du", "fu", "ju", "gu", + "ulg", "gug", "gub", "gur", "oog", + "gub", "log", "ru", "stu", "glu", + "ug", "ud", "og", "log", "ro", "flu", + "bo", "duf", "fun", "nog", "dun", "bog", + "dug", "gh", "ghu", "gho", "nug", "ig", + "igh", "ihg", "luh", "duh", "bug", "dug", + "dru", "urd", "gurt", "grut", "grunt", + "snarf", "urgle", "igg", "glu", "glug", + "foo", "bar", "baz", "ghat", "ab", "ad", + "gugh", "guk", "ag", "alm", "thu", "log", + "bilge", "augh", "gha", "gig", "goth", + "zug", "pig", "auh", "gan", "azh", "bag", + "hig", "oth", "dagh", "gulg", "ugh", "ba", + "bid", "gug", "bug", "rug", "hat", "brui", + "gagh", "buad", "buil", "buim", "bum", + "hug", "hug", "buo", "ma", "buor", "ghed", + "buu", "ca", "guk", "clog", "thurg", "car", + "cro", "thu", "da", "cuk", "gil", "cur", "dak", + "dar", "deak", "der", "dil", "dit", "at", "ag", + "dor", "gar", "dre", "tk", "dri", "gka", "rim", + "eag", "egg", "ha", "rod", "eg", "lat", "eichel", + "ek", "ep", "ka", "it", "ut", "ewk", "ba", "dagh", + "faugh", "foz", "fog", "fid", "fruk", "gag", "fub", + "fud", "fur", "bog", "fup", "hagh", "gaa", "kt", + "rekk", "lub", "lug", "tug", "gna", "urg", "l", + "gno", "gnu", "gol", "gom", "kug", "ukk", "jak", + "jek", "rukk", "jja", "akt", "nuk", "hok", "hrol", + "olm", "natz", "i", "i", "o", "u", "ikk", "ign", + "juk", "kh", "kgh", "ka", "hig", "ke", "ki", "klap", + "klu", "knod", "kod", "knu", "thnu", "krug", "nug", + "nar", "nag", "neg", "neh", "oag", "ob", "ogh", "oh", + "om", "dud", "oo", "pa", "hrak", "qo", "quad", "quil", + "ghig", "rur", "sag", "sah", "sg" + }; + } + + return m_OrcSpeech; + } + } + + private static InhumanSpeech m_LizardmanSpeech; + + public static InhumanSpeech Lizardman + { + get + { + if ( m_LizardmanSpeech == null ) + { + m_LizardmanSpeech = new InhumanSpeech(); + + m_LizardmanSpeech.Hue = 58; + m_LizardmanSpeech.Sound = 418; + + m_LizardmanSpeech.Flags = IHSFlags.All; + + m_LizardmanSpeech.Keywords = new string[] + { + "meat", "gold", "kill", "killing", "slay", + "sword", "axe", "spell", "magic", "spells", + "swords", "axes", "mace", "maces", "monster", + "monsters", "food", "run", "escape", "away", + "help", "dead", "die", "dying", "lose", + "losing", "life", "lives", "death", "ghost", + "ghosts", "british", "blackthorn", "guild", + "guilds", "dragon", "dragons", "game", "games", + "ultima", "silly", "stupid", "dumb", "idiot", + "idiots", "cheesy", "cheezy", "crazy", "dork", + "jerk", "fool", "foolish", "ugly", "insult", "scum" + }; + + m_LizardmanSpeech.Responses = new string[] + { + "meat", "kill", "pound", "crush", "yum yum", + "crunch", "destroy", "murder", "eat", "munch", + "massacre", "food", "monster", "evil", "run", + "die", "lose", "dumb", "idiot", "fool", "crazy", + "dinner", "lunch", "breakfast", "fight", "battle", + "doomed", "rip apart", "tear apart", "smash", + "edible?", "shred", "disembowel", "ugly", "smelly", + "stupid", "hideous", "smell", "tasty", "invader", + "attack", "raid", "plunder", "pillage", "treasure", + "loser", "lose", "scum" + }; + + m_LizardmanSpeech.Syllables = new string[] + { + "ss", "sth", "iss", "is", "ith", "kth", + "sith", "this", "its", "sit", "tis", "tsi", + "ssi", "sil", "lis", "sis", "lil", "thil", + "lith", "sthi", "lish", "shi", "shash", "sal", + "miss", "ra", "tha", "thes", "ses", "sas", "las", + "les", "sath", "sia", "ais", "isa", "asi", "asth", + "stha", "sthi", "isth", "asa", "ath", "tha", "als", + "sla", "thth", "ci", "ce", "cy", "yss", "ys", "yth", + "syth", "thys", "yts", "syt", "tys", "tsy", "ssy", + "syl", "lys", "sys", "lyl", "thyl", "lyth", "sthy", + "lysh", "shy", "myss", "ysa", "sthy", "ysth" + }; + } + + return m_LizardmanSpeech; + } + } + + private static InhumanSpeech m_WispSpeech; + + public static InhumanSpeech Wisp + { + get + { + if ( m_WispSpeech == null ) + { + m_WispSpeech = new InhumanSpeech(); + + m_WispSpeech.Hue = 89; + m_WispSpeech.Sound = 466; + + m_WispSpeech.Flags = IHSFlags.OnMovement; + + m_WispSpeech.Syllables = new string[] + { + "b", "c", "d", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "p", "r", + "s", "t", "v", "w", "x", "z", "c", + "c", "x", "x", "x", "x", "x", "y", + "y", "y", "y", "t", "t", "k", "k", + "l", "l", "m", "m", "m", "m", "z" + }; + } + + return m_WispSpeech; + } + } + + private string[] m_Syllables; + private string[] m_Keywords; + private string[] m_Responses; + + private Hashtable m_KeywordHash; + + private int m_Hue; + private int m_Sound; + + private IHSFlags m_Flags; + + public string[] Syllables + { + get{ return m_Syllables; } + set{ m_Syllables = value; } + } + + public string[] Keywords + { + get{ return m_Keywords; } + set + { + m_Keywords = value; + m_KeywordHash = new Hashtable( m_Keywords.Length, StringComparer.OrdinalIgnoreCase ); + for ( int i = 0; i < m_Keywords.Length; ++i ) + m_KeywordHash[m_Keywords[i]] = m_Keywords[i]; + } + } + + public string[] Responses + { + get{ return m_Responses; } + set{ m_Responses = value; } + } + + public int Hue + { + get{ return m_Hue; } + set{ m_Hue = value; } + } + + public int Sound + { + get{ return m_Sound; } + set{ m_Sound = value; } + } + + public IHSFlags Flags + { + get{ return m_Flags; } + set{ m_Flags = value; } + } + + public string GetRandomSyllable() + { + return m_Syllables[Utility.Random( m_Syllables.Length )]; + } + + public string ConstructWord( int syllableCount ) + { + string[] syllables = new string[syllableCount]; + + for ( int i = 0; i < syllableCount; ++i ) + syllables[i] = GetRandomSyllable(); + + return String.Concat( syllables ); + } + + public string ConstructSentance( int wordCount ) + { + StringBuilder sentance = new StringBuilder(); + + bool needUpperCase = true; + + for ( int i = 0; i < wordCount; ++i ) + { + if ( i > 0 ) // not first word ) + { + int random = Utility.RandomMinMax( 1, 15 ); + + if ( random < 11 ) + { + sentance.Append( ' ' ); + } + else + { + needUpperCase = true; + + if ( random > 13 ) + sentance.Append( "! " ); + else + sentance.Append( ". " ); + } + } + + int syllableCount; + + if ( 30 > Utility.Random( 100 ) ) + syllableCount = Utility.Random( 1, 5 ); + else + syllableCount = Utility.Random( 1, 3 ); + + string word = ConstructWord( syllableCount ); + + sentance.Append( word ); + + if ( needUpperCase ) + sentance.Replace( word[0], Char.ToUpper( word[0] ), sentance.Length - word.Length, 1 ); + + needUpperCase = false; + } + + if ( Utility.RandomMinMax( 1, 5 ) == 1 ) + sentance.Append( '!' ); + else + sentance.Append( '.' ); + + return sentance.ToString(); + } + + public void SayRandomTranslate( Mobile mob, params string[] sentancesInEnglish ) + { + SaySentance( mob, Utility.RandomMinMax( 2, 3 ) ); + mob.Say( sentancesInEnglish[Utility.Random( sentancesInEnglish.Length )] ); + } + + private string GetRandomResponseWord( ArrayList keywordsFound ) + { + int random = Utility.Random( keywordsFound.Count + m_Responses.Length ); + + if ( random < keywordsFound.Count ) + return (string)keywordsFound[random]; + + return m_Responses[random - keywordsFound.Count]; + } + + public bool OnSpeech( Mobile mob, Mobile speaker, string text ) + { + if ( (m_Flags & IHSFlags.OnSpeech) == 0 || m_Keywords == null || m_Responses == null || m_KeywordHash == null ) + return false; // not enabled + + if ( !speaker.Alive ) + return false; + + if ( !speaker.InRange( mob, 3 ) ) + return false; + + if ( (speaker.Direction & Direction.Mask) != speaker.GetDirectionTo( mob ) ) + return false; + + if ( (mob.Direction & Direction.Mask) != mob.GetDirectionTo( speaker ) ) + return false; + + string[] split = text.Split( ' ' ); + ArrayList keywordsFound = new ArrayList(); + + for ( int i = 0; i < split.Length; ++i ) + { + string keyword = (string) m_KeywordHash[split[i]]; + + if ( keyword != null ) + keywordsFound.Add( keyword ); + } + + if ( keywordsFound.Count > 0 ) + { + string responseWord; + + if ( Utility.RandomBool() ) + responseWord = GetRandomResponseWord( keywordsFound ); + else + responseWord = (string)keywordsFound[Utility.Random( keywordsFound.Count )]; + + string secondResponseWord = GetRandomResponseWord( keywordsFound ); + + StringBuilder response = new StringBuilder(); + + switch ( Utility.Random( 6 ) ) + { + default: + case 0: + { + response.Append( "Me " ).Append( responseWord ).Append( '?' ); + break; + } + case 1: + { + response.Append( responseWord ).Append( " thee!" ); + response.Replace( responseWord[0], Char.ToUpper( responseWord[0] ), 0, 1 ); + break; + } + case 2: + { + response.Append( responseWord ).Append( '?' ); + response.Replace( responseWord[0], Char.ToUpper( responseWord[0] ), 0, 1 ); + break; + } + case 3: + { + response.Append( responseWord ).Append( "! " ).Append( secondResponseWord ).Append( '.' ); + response.Replace( responseWord[0], Char.ToUpper( responseWord[0] ), 0, 1 ); + response.Replace( secondResponseWord[0], Char.ToUpper( secondResponseWord[0] ), responseWord.Length + 2, 1 ); + break; + } + case 4: + { + response.Append( responseWord ).Append( '.' ); + response.Replace( responseWord[0], Char.ToUpper( responseWord[0] ), 0, 1 ); + break; + } + case 5: + { + response.Append( responseWord ).Append( "? " ).Append( secondResponseWord ).Append( '.' ); + response.Replace( responseWord[0], Char.ToUpper( responseWord[0] ), 0, 1 ); + response.Replace( secondResponseWord[0], Char.ToUpper( secondResponseWord[0] ), responseWord.Length + 2, 1 ); + break; + } + } + + int maxWords = (split.Length / 2) + 1; + + if ( maxWords < 2 ) + maxWords = 2; + else if ( maxWords > 6 ) + maxWords = 6; + + SaySentance( mob, Utility.RandomMinMax( 2, maxWords ) ); + mob.Say( response.ToString() ); + + return true; + } + + return false; + } + + public void OnDeath( Mobile mob ) + { + if ( (m_Flags & IHSFlags.OnDeath) == 0 ) + return; // not enabled + + if ( 90 > Utility.Random( 100 ) ) + return; // 90% chance to do nothing; 10% chance to talk + + SayRandomTranslate( mob, + "Revenge!", + "NOOooo!", + "I... I...", + "Me no die!", + "Me die!", + "Must... not die...", + "Oooh, me hurt...", + "Me dying?" ); + } + + public void OnMovement( Mobile mob, Mobile mover, Point3D oldLocation ) + { + if ( (m_Flags & IHSFlags.OnMovement) == 0 ) + return; // not enabled + + if ( !mover.Player || (mover.Hidden && mover.AccessLevel > AccessLevel.Player) ) + return; + + if ( !mob.InRange( mover, 5 ) || mob.InRange( oldLocation, 5 ) ) + return; // only talk when they enter 5 tile range + + if ( 90 > Utility.Random( 100 ) ) + return; // 90% chance to do nothing; 10% chance to talk + + SaySentance( mob, 6 ); + } + + public void OnDamage( Mobile mob, int amount ) + { + if ( (m_Flags & IHSFlags.OnDamaged) == 0 ) + return; // not enabled + + if ( 90 > Utility.Random( 100 ) ) + return; // 90% chance to do nothing; 10% chance to talk + + if ( amount < 5 ) + { + SayRandomTranslate( mob, + "Ouch!", + "Me not hurt bad!", + "Thou fight bad.", + "Thy blows soft!", + "You bad with weapon!" ); + } + else + { + SayRandomTranslate( mob, + "Ouch! Me hurt!", + "No, kill me not!", + "Me hurt!", + "Away with thee!", + "Oof! That hurt!", + "Aaah! That hurt...", + "Good blow!" ); + } + } + + public void OnConstruct( Mobile mob ) + { + mob.SpeechHue = m_Hue; + } + + public void SaySentance( Mobile mob, int wordCount ) + { + mob.Say( ConstructSentance( wordCount ) ); + mob.PlaySound( m_Sound ); + } + + public InhumanSpeech() + { + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Keywords.cs b/Scripts/Misc/Keywords.cs new file mode 100644 index 000000000..b29c3cefd --- /dev/null +++ b/Scripts/Misc/Keywords.cs @@ -0,0 +1,60 @@ +using System; +using Server; +using Server.Items; +using Server.Guilds; +using Server.Mobiles; +using Server.Gumps; + +namespace Server.Misc +{ + public class Keywords + { + public static void Initialize() + { + // Register our speech handler + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + } + + public static void EventSink_Speech( SpeechEventArgs args ) + { + Mobile from = args.Mobile; + int[] keywords = args.Keywords; + + for ( int i = 0; i < keywords.Length; ++i ) + { + switch ( keywords[i] ) + { + case 0x002A: // *i resign from my guild* + { + if ( from.Guild != null ) + ((Guild)from.Guild).RemoveMember( from ); + + break; + } + case 0x0032: // *i must consider my sins* + { + if( !Core.SE ) + { + from.SendMessage( "Short Term Murders : {0}", from.ShortTermMurders ); + from.SendMessage( "Long Term Murders : {0}", from.Kills ); + } + else + { + from.SendMessage( 0x3B2, "Short Term Murders: {0} Long Term Murders: {1}", from.ShortTermMurders, from.Kills ); + } + break; + } + case 0x0035: // i renounce my young player status* + { + if ( from is PlayerMobile && ((PlayerMobile)from).Young && !from.HasGump( typeof( RenounceYoungGump ) ) ) + { + from.SendGump( new RenounceYoungGump() ); + } + + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/LanguageStatistics.cs b/Scripts/Misc/LanguageStatistics.cs new file mode 100644 index 000000000..5d9055f3f --- /dev/null +++ b/Scripts/Misc/LanguageStatistics.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Server; +using Server.Accounting; +using Server.Commands; +using Server.Mobiles; + +namespace Server.Misc +{ + /** + * This file requires to be saved in a Unicode + * compatible format. + * + * Warning: if you change String.Format methods, + * please note that the following character + * is suggested before any left-to-right text + * in order to prevent undesired formatting + * resulting from mixing LR and RL text: ‎ + * + * Use this one if you need to force RL: †+ * + * If you do not see the above chars, please + * enable showing of unicode control chars + **/ + + public class LanguageStatistics + { + struct InternationalCode + { + string m_Code; + string m_Language; + string m_Country; + string m_Language_LocalName; + string m_Country_LocalName; + bool m_HasLocalInfo; + + public string Code{ get{ return m_Code; } } + public string Language{ get{ return m_Language; } } + public string Country{ get{ return m_Country; } } + public string Language_LocalName{ get{ return m_Language_LocalName; } } + public string Country_LocalName{ get{ return m_Country_LocalName; } } + + public InternationalCode( string code, string language, string country ) : this( code, language, country, null, null ) + { + m_HasLocalInfo = false; + } + + public InternationalCode( string code, string language, string country, string language_localname, string country_localname ) + { + m_Code = code; + m_Language = language; + m_Country = country; + m_Language_LocalName = language_localname; + m_Country_LocalName = country_localname; + m_HasLocalInfo = true; + } + + public string GetName() + { + string s; + + if ( m_HasLocalInfo ) + { + s = String.Format( "{0}‎ - {1}", DefaultLocalNames ? m_Language_LocalName : m_Language, DefaultLocalNames ? m_Country_LocalName : m_Country ); + + if ( ShowAlternatives ) + s += String.Format( "‎ ã€{0}‎ - {1}‎】", DefaultLocalNames ? m_Language : m_Language_LocalName, DefaultLocalNames ? m_Country : m_Country_LocalName ); + } + else + { + s = String.Format( "{0}‎ - {1}", m_Language, m_Country ); + } + + return s; + } + } + + private static InternationalCode[] InternationalCodes = + { + new InternationalCode( "ARA", "Arabic", "Saudi Arabia", "العربية", "السعودية" ), + new InternationalCode( "ARI", "Arabic", "Iraq", "العربية", "العراق" ), + new InternationalCode( "ARE", "Arabic", "Egypt", "العربية", "مصر" ), + new InternationalCode( "ARL", "Arabic", "Libya", "العربية", "ليبيا" ), + new InternationalCode( "ARG", "Arabic", "Algeria", "العربية", "الجزائر" ), + new InternationalCode( "ARM", "Arabic", "Morocco", "العربية", "المغرب" ), + new InternationalCode( "ART", "Arabic", "Tunisia", "العربية", "تونس" ), + new InternationalCode( "ARO", "Arabic", "Oman", "العربية", "عمان" ), + new InternationalCode( "ARY", "Arabic", "Yemen", "العربية", "اليمن" ), + new InternationalCode( "ARS", "Arabic", "Syria", "العربية", "سورية" ), + new InternationalCode( "ARJ", "Arabic", "Jordan", "العربية", "الأردن" ), + new InternationalCode( "ARB", "Arabic", "Lebanon", "العربية", "لبنان" ), + new InternationalCode( "ARK", "Arabic", "Kuwait", "العربية", "الكويت" ), + new InternationalCode( "ARU", "Arabic", "U.A.E.", "العربية", "الامارات" ), + new InternationalCode( "ARH", "Arabic", "Bahrain", "العربية", "البحرين" ), + new InternationalCode( "ARQ", "Arabic", "Qatar", "العربية", "قطر" ), + new InternationalCode( "BGR", "Bulgarian", "Bulgaria", "БългарÑки", "БългариÑ" ), + new InternationalCode( "CAT", "Catalan", "Spain", "Català", "Espanya" ), + new InternationalCode( "CHT", "Chinese", "Taiwan", "å°èªž", "臺ç£" ), + new InternationalCode( "CHS", "Chinese", "PRC", "中文", "中国" ), + new InternationalCode( "ZHH", "Chinese", "Hong Kong", "中文", "香港" ), + new InternationalCode( "ZHI", "Chinese", "Singapore", "中文", "新加å¡" ), + new InternationalCode( "ZHM", "Chinese", "Macau", "中文", "澳門" ), + new InternationalCode( "CSY", "Czech", "Czech Republic", "ÄŒeÅ¡tina", "ÄŒeská republika" ), + new InternationalCode( "DAN", "Danish", "Denmark", "Dansk", "Danmark" ), + new InternationalCode( "DEU", "German", "Germany", "Deutsch", "Deutschland" ), + new InternationalCode( "DES", "German", "Switzerland", "Deutsch", "der Schweiz" ), + new InternationalCode( "DEA", "German", "Austria", "Deutsch", "Österreich" ), + new InternationalCode( "DEL", "German", "Luxembourg", "Deutsch", "Luxembourg" ), + new InternationalCode( "DEC", "German", "Liechtenstein", "Deutsch", "Liechtenstein" ), + new InternationalCode( "ELL", "Greek", "Greece", "Ελληνικά", "Ελλάδα" ), + new InternationalCode( "ENU", "English", "United States" ), + new InternationalCode( "ENG", "English", "United Kingdom" ), + new InternationalCode( "ENA", "English", "Australia" ), + new InternationalCode( "ENC", "English", "Canada" ), + new InternationalCode( "ENZ", "English", "New Zealand" ), + new InternationalCode( "ENI", "English", "Ireland" ), + new InternationalCode( "ENS", "English", "South Africa" ), + new InternationalCode( "ENJ", "English", "Jamaica" ), + new InternationalCode( "ENB", "English", "Caribbean" ), + new InternationalCode( "ENL", "English", "Belize" ), + new InternationalCode( "ENT", "English", "Trinidad" ), + new InternationalCode( "ENW", "English", "Zimbabwe" ), + new InternationalCode( "ENP", "English", "Philippines" ), + new InternationalCode( "ESP", "Spanish", "Spain (Traditional Sort)", "Español", "España (tipo tradicional)" ), + new InternationalCode( "ESM", "Spanish", "Mexico", "Español", "México" ), + new InternationalCode( "ESN", "Spanish", "Spain (International Sort)", "Español", "España (tipo internacional)" ), + new InternationalCode( "ESG", "Spanish", "Guatemala", "Español", "Guatemala" ), + new InternationalCode( "ESC", "Spanish", "Costa Rica", "Español", "Costa Rica" ), + new InternationalCode( "ESA", "Spanish", "Panama", "Español", "Panama" ), + new InternationalCode( "ESD", "Spanish", "Dominican Republic", "Español", "Republica Dominicana" ), + new InternationalCode( "ESV", "Spanish", "Venezuela", "Español", "Venezuela" ), + new InternationalCode( "ESO", "Spanish", "Colombia", "Español", "Colombia" ), + new InternationalCode( "ESR", "Spanish", "Peru", "Español", "Peru" ), + new InternationalCode( "ESS", "Spanish", "Argentina", "Español", "Argentina" ), + new InternationalCode( "ESF", "Spanish", "Ecuador", "Español", "Ecuador" ), + new InternationalCode( "ESL", "Spanish", "Chile", "Español", "Chile" ), + new InternationalCode( "ESY", "Spanish", "Uruguay", "Español", "Uruguay" ), + new InternationalCode( "ESZ", "Spanish", "Paraguay", "Español", "Paraguay" ), + new InternationalCode( "ESB", "Spanish", "Bolivia", "Español", "Bolivia" ), + new InternationalCode( "ESE", "Spanish", "El Salvador", "Español", "El Salvador" ), + new InternationalCode( "ESH", "Spanish", "Honduras", "Español", "Honduras" ), + new InternationalCode( "ESI", "Spanish", "Nicaragua", "Español", "Nicaragua" ), + new InternationalCode( "ESU", "Spanish", "Puerto Rico", "Español", "Puerto Rico" ), + new InternationalCode( "FIN", "Finnish", "Finland", "Suomi", "Suomi" ), + new InternationalCode( "FRA", "French", "France", "Français", "France" ), + new InternationalCode( "FRB", "French", "Belgium", "Français", "Belgique" ), + new InternationalCode( "FRC", "French", "Canada", "Français", "Canada" ), + new InternationalCode( "FRS", "French", "Switzerland", "Français", "Suisse" ), + new InternationalCode( "FRL", "French", "Luxembourg", "Français", "Luxembourg" ), + new InternationalCode( "FRM", "French", "Monaco", "Français", "Monaco" ), + new InternationalCode( "HEB", "Hebrew", "Israel", "עִבְרִית", "ישׂר×ל" ), + new InternationalCode( "HUN", "Hungarian", "Hungary", "Magyar", "Magyarország" ), + new InternationalCode( "ISL", "Icelandic", "Iceland", "Ãslenska", "Ãsland" ), + new InternationalCode( "ITA", "Italian", "Italy", "Italiano", "Italia" ), + new InternationalCode( "ITS", "Italian", "Switzerland", "Italiano", "Svizzera" ), + new InternationalCode( "JPN", "Japanese", "Japan", "日本語", "日本" ), + new InternationalCode( "KOR", "Korean (Extended Wansung)", "Korea", "한국어", "한국" ), + new InternationalCode( "NLD", "Dutch", "Netherlands", "Nederlands", "Nederland" ), + new InternationalCode( "NLB", "Dutch", "Belgium", "Nederlands", "België" ), + new InternationalCode( "NOR", "Norwegian", "Norway (BokmÃ¥l)", "Norsk", "Norge (BokmÃ¥l)" ), + new InternationalCode( "NON", "Norwegian", "Norway (Nynorsk)", "Norsk", "Norge (Nynorsk)" ), + new InternationalCode( "PLK", "Polish", "Poland", "Polski", "Polska" ), + new InternationalCode( "PTB", "Portuguese", "Brazil", "Português", "Brasil" ), + new InternationalCode( "PTG", "Portuguese", "Portugal", "Português", "Brasil" ), + new InternationalCode( "ROM", "Romanian", "Romania", "Limba Română", "România" ), + new InternationalCode( "RUS", "Russian", "Russia", "РуÑÑкий", "РоÑÑиÑ" ), + new InternationalCode( "HRV", "Croatian", "Croatia", "Hrvatski", "Hrvatska" ), + new InternationalCode( "SRL", "Serbian", "Serbia (Latin)", "Srpski", "Srbija i Crna Gora" ), + new InternationalCode( "SRB", "Serbian", "Serbia (Cyrillic)", "СрпÑки", "Србија и Црна Гора" ), + new InternationalCode( "SKY", "Slovak", "Slovakia", "SlovenÄina", "Slovensko" ), + new InternationalCode( "SQI", "Albanian", "Albania", "Shqip", "Shqipëria" ), + new InternationalCode( "SVE", "Swedish", "Sweden", "Svenska", "Sverige" ), + new InternationalCode( "SVF", "Swedish", "Finland", "Svenska", "Finland" ), + new InternationalCode( "THA", "Thai", "Thailand", "ภาษาไทย", "ประเทศไทย" ), + new InternationalCode( "TRK", "Turkish", "Turkey", "Türkçe", "Türkiye" ), + new InternationalCode( "URP", "Urdu", "Pakistan", "اردو", "پاکستان" ), + new InternationalCode( "IND", "Indonesian", "Indonesia", "Bahasa Indonesia", "Indonesia" ), + new InternationalCode( "UKR", "Ukrainian", "Ukraine", "УкраїнÑька", "Украина" ), + new InternationalCode( "BEL", "Belarusian", "Belarus", "БеларуÑкі", "БеларуÑÑŒ" ), + new InternationalCode( "SLV", "Slovene", "Slovenia", "SlovenÅ¡Äina", "Slovenija" ), + new InternationalCode( "ETI", "Estonian", "Estonia", "Eesti", "Eesti" ), + new InternationalCode( "LVI", "Latvian", "Latvia", "LatvieÅ¡u", "Latvija" ), + new InternationalCode( "LTH", "Lithuanian", "Lithuania", "Lietuvių", "Lietuva" ), + new InternationalCode( "LTC", "Classic Lithuanian", "Lithuania", "LietuviÅ¡kai", "Lietuva" ), + new InternationalCode( "FAR", "Farsi", "Iran", "ÙØ§Ø±Ø³Ù‰", "ايران" ), + new InternationalCode( "VIT", "Vietnamese", "Viet Nam", "tiêÌng Việt", "Việt Nam" ), + new InternationalCode( "HYE", "Armenian", "Armenia", "Õ€Õ¡ÕµÕ¥Ö€Õ§Õ¶", "Õ€Õ¡ÕµÕ¡Õ½Õ¿Õ¡Õ¶" ), + new InternationalCode( "AZE", "Azeri", "Azerbaijan (Latin)", "AzÉ™rbaycanca", "AzÉ™rbaycan" ), + new InternationalCode( "AZE", "Azeri", "Azerbaijan (Cyrillic)", "Ðзәрбајҹанҹа", "Ðзәрбајҹан" ), + new InternationalCode( "EUQ", "Basque", "Spain", "Euskera", "Espainia" ), + new InternationalCode( "MKI", "Macedonian", "Macedonia", "МакедонÑки", "Македонија" ), + new InternationalCode( "AFK", "Afrikaans", "South Africa", "Afrikaans", "Republiek van Suid-Afrika" ), + new InternationalCode( "KAT", "Georgian", "Georgia", "ქáƒáƒ áƒ—ული", "სáƒáƒ™áƒáƒ áƒ¢áƒ•ელáƒ" ), + new InternationalCode( "FOS", "Faeroese", "Faeroe Islands", "Føroyska", "Føroya" ), + new InternationalCode( "HIN", "Hindi", "India", "हिनà¥à¤¦à¥€", "भारत" ), + new InternationalCode( "MSL", "Malay", "Malaysia", "Bahasa melayu", "Malaysia" ), + new InternationalCode( "MSB", "Malay", "Brunei Darussalam", "Bahasa melayu", "Negara Brunei Darussalam" ), + new InternationalCode( "KAZ", "Kazak", "Kazakstan", "Қазақ", "ҚазақÑтан" ), + new InternationalCode( "SWK", "Swahili", "Kenya", "Kiswahili", "Kenya" ), + new InternationalCode( "UZB", "Uzbek", "Uzbekistan (Latin)", "O'zbek", "O'zbekiston" ), + new InternationalCode( "UZB", "Uzbek", "Uzbekistan (Cyrillic)", "Ўзбек", "ЎзбекиÑтон" ), + new InternationalCode( "TAT", "Tatar", "Tatarstan", "Татарча", "ТатарÑтан" ), + new InternationalCode( "BEN", "Bengali", "India", "বাংলা", "ভারত" ), + new InternationalCode( "PAN", "Punjabi", "India", "ਪੰਜਾਬੀ", "ਭਾਰਤ" ), + new InternationalCode( "GUJ", "Gujarati", "India", "ગà«àªœàª°àª¾àª¤à«€", "ભારત" ), + new InternationalCode( "ORI", "Oriya", "India", "ଓଡ଼ିଆ", "ଭାରତ" ), + new InternationalCode( "TAM", "Tamil", "India", "தமிழà¯", "இநà¯à®¤à®¿à®¯à®¾" ), + new InternationalCode( "TEL", "Telugu", "India", "తెలà±à°—à±", "భారత" ), + new InternationalCode( "KAN", "Kannada", "India", "ಕನà³à²¨à²¡", "ಭಾರತ" ), + new InternationalCode( "MAL", "Malayalam", "India", "മലയാളം", "ഭാരത" ), + new InternationalCode( "ASM", "Assamese", "India", "অসমিয়া", "BhÄrat" ), // missing correct country name + new InternationalCode( "MAR", "Marathi", "India", "मराठी", "भारत" ), + new InternationalCode( "SAN", "Sanskrit", "India", "संसà¥à¤•ृत", "भारतमà¥" ), + new InternationalCode( "KOK", "Konkani", "India", "कोंकणी", "भारत" ) + }; + + private static string GetFormattedInfo( string code ) + { + if ( code == null || code.Length != 3 ) + return String.Format( "Unknown code {0}", code ); + + for ( int i = 0; i < InternationalCodes.Length; i++ ) + { + if ( code == InternationalCodes[i].Code ) + { + return String.Format( "{0}", InternationalCodes[i].GetName() ); + } + } + + return String.Format( "Unknown code {0}", code ); + } + + private static bool DefaultLocalNames = false; + private static bool ShowAlternatives = true; + private static bool CountAccounts = true; // will consider only first character's valid language + + public static void Initialize() + { + CommandSystem.Register( "LanguageStatistics", AccessLevel.Administrator, new CommandEventHandler( LanguageStatistics_OnCommand ) ); + } + + [Usage( "LanguageStatistics" )] + [Description( "Generate a file containing the list of languages for each PlayerMobile." )] + public static void LanguageStatistics_OnCommand( CommandEventArgs e ) + { + Dictionary ht = new Dictionary(); + + using ( StreamWriter writer = new StreamWriter( "languages.txt" ) ) + { + if ( CountAccounts ) + { + // count accounts + foreach ( Account acc in Accounts.GetAccounts() ) + { + for ( int i = 0; i < acc.Length; i++ ) + { + Mobile mob = acc[i]; + + if ( mob == null ) + continue; + + string lang = mob.Language; + + if ( lang != null ) + { + lang = lang.ToUpper(); + + if ( !ht.ContainsKey( lang ) ) + ht[lang] = new InternationalCodeCounter( lang ); + else + ht[lang].Increase(); + + break; + } + } + } + } + else + { + // count playermobiles + foreach( Mobile mob in World.Mobiles.Values ) + { + if ( mob.Player ) + { + string lang = mob.Language; + + if ( lang != null ) + { + lang = lang.ToUpper(); + + if ( !ht.ContainsKey( lang ) ) + ht[lang] = new InternationalCodeCounter( lang ); + else + ht[lang].Increase(); + } + } + } + } + + writer.WriteLine( String.Format( "Language statistics. Numbers show how many {0} use the specified language.", CountAccounts ? "accounts" : "playermobile" ) ); + writer.WriteLine( "====================================================================================================" ); + writer.WriteLine(); + + // sort the list + List list = new List( ht.Values ); + list.Sort( InternationalCodeComparer.Instance ); + + foreach ( InternationalCodeCounter c in list ) + writer.WriteLine( String.Format( "{0}‎ : {1}", GetFormattedInfo( c.Code ), c.Count ) ); + + e.Mobile.SendMessage( "Languages list generated." ); + } + } + + private class InternationalCodeCounter + { + private string m_Code; + private int m_Count; + + public string Code{ get{ return m_Code; } } + public int Count{ get{ return m_Count; } } + + public InternationalCodeCounter( string code ) + { + m_Code = code; + m_Count = 1; + } + + public void Increase() + { + m_Count++; + } + } + + private class InternationalCodeComparer : IComparer + { + public static readonly InternationalCodeComparer Instance = new InternationalCodeComparer(); + + public InternationalCodeComparer() + { + } + + public int Compare( InternationalCodeCounter x, InternationalCodeCounter y ) + { + string a = null, b = null; + int ca = 0, cb = 0; + + a = x.Code; + ca = x.Count; + b = y.Code; + cb = y.Count; + + + if ( ca > cb ) + return -1; + + if ( ca < cb ) + return 1; + + if ( a == null && b == null ) + return 0; + + if ( a == null ) + return 1; + + if ( b == null ) + return -1; + + return a.CompareTo( b ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/LightCycle.cs b/Scripts/Misc/LightCycle.cs new file mode 100644 index 000000000..ba0590dd1 --- /dev/null +++ b/Scripts/Misc/LightCycle.cs @@ -0,0 +1,141 @@ +using System; +using Server; +using Server.Network; +using Server.Commands; + +namespace Server +{ + public class LightCycle + { + public const int DayLevel = 0; + public const int NightLevel = 12; + public const int DungeonLevel = 26; + public const int JailLevel = 9; + + private static int m_LevelOverride = int.MinValue; + + public static int LevelOverride + { + get{ return m_LevelOverride; } + set + { + m_LevelOverride = value; + + for ( int i = 0; i < NetState.Instances.Count; ++i ) + { + NetState ns = NetState.Instances[i]; + Mobile m = ns.Mobile; + + if ( m != null ) + m.CheckLightLevels( false ); + } + } + } + + public static void Initialize() + { + new LightCycleTimer().Start(); + EventSink.Login += new LoginEventHandler( OnLogin ); + + CommandSystem.Register( "GlobalLight", AccessLevel.GameMaster, new CommandEventHandler( Light_OnCommand ) ); + } + + [Usage( "GlobalLight " )] + [Description( "Sets the current global light level." )] + private static void Light_OnCommand( CommandEventArgs e ) + { + if ( e.Length >= 1 ) + { + LevelOverride = e.GetInt32( 0 ); + e.Mobile.SendMessage( "Global light level override has been changed to {0}.", m_LevelOverride ); + } + else + { + LevelOverride = int.MinValue; + e.Mobile.SendMessage( "Global light level override has been cleared." ); + } + } + + public static void OnLogin( LoginEventArgs args ) + { + Mobile m = args.Mobile; + + m.CheckLightLevels( true ); + } + + public static int ComputeLevelFor( Mobile from ) + { + if ( m_LevelOverride > int.MinValue ) + return m_LevelOverride; + + int hours, minutes; + + Server.Items.Clock.GetTime( from.Map, from.X, from.Y, out hours, out minutes ); + + /* OSI times: + * + * Midnight -> 3:59 AM : Night + * 4:00 AM -> 11:59 PM : Day + * + * RunUO times: + * + * 10:00 PM -> 11:59 PM : Scale to night + * Midnight -> 3:59 AM : Night + * 4:00 AM -> 5:59 AM : Scale to day + * 6:00 AM -> 9:59 PM : Day + */ + + if ( hours < 4 ) + return NightLevel; + + if ( hours < 6 ) + return NightLevel + (((((hours - 4) * 60) + minutes) * (DayLevel - NightLevel)) / 120); + + if ( hours < 22 ) + return DayLevel; + + if ( hours < 24 ) + return DayLevel + (((((hours - 22) * 60) + minutes) * (NightLevel - DayLevel)) / 120); + + return NightLevel; // should never be + } + + private class LightCycleTimer : Timer + { + public LightCycleTimer() : base( TimeSpan.FromSeconds( 0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + for ( int i = 0; i < NetState.Instances.Count; ++i ) + { + NetState ns = NetState.Instances[i]; + Mobile m = ns.Mobile; + + if ( m != null ) + m.CheckLightLevels( false ); + } + } + } + + public class NightSightTimer : Timer + { + private Mobile m_Owner; + + public NightSightTimer( Mobile owner ) : base( TimeSpan.FromMinutes( Utility.Random( 15, 25 ) ) ) + { + m_Owner = owner; + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + m_Owner.EndAction( typeof( LightCycle ) ); + m_Owner.LightLevel = 0; + BuffInfo.RemoveBuff( m_Owner, BuffIcon.NightSight ); + } + } + } +} diff --git a/Scripts/Misc/LoginStats.cs b/Scripts/Misc/LoginStats.cs new file mode 100644 index 000000000..a2c40816b --- /dev/null +++ b/Scripts/Misc/LoginStats.cs @@ -0,0 +1,30 @@ +using System; +using Server.Network; + +namespace Server.Misc +{ + public class LoginStats + { + public static void Initialize() + { + // Register our event handler + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static void EventSink_Login( LoginEventArgs args ) + { + int userCount = NetState.Instances.Count; + int itemCount = World.Items.Count; + int mobileCount = World.Mobiles.Count; + + Mobile m = args.Mobile; + + m.SendMessage( "Welcome, {0}! There {1} currently {2} user{3} online, with {4} item{5} and {6} mobile{7} in the world.", + args.Mobile.Name, + userCount == 1 ? "is" : "are", + userCount, userCount == 1 ? "" : "s", + itemCount, itemCount == 1 ? "" : "s", + mobileCount, mobileCount == 1 ? "" : "s" ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Loot.cs b/Scripts/Misc/Loot.cs new file mode 100644 index 000000000..a22f41262 --- /dev/null +++ b/Scripts/Misc/Loot.cs @@ -0,0 +1,671 @@ +using System; +using System.IO; +using System.Reflection; +using Server; +using Server.Items; + +namespace Server +{ + public class Loot + { + #region List definitions + private static Type[] m_SEWeaponTypes = new Type[] + { + typeof( Bokuto ), typeof( Daisho ), typeof( Kama ), + typeof( Lajatang ), typeof( NoDachi ), typeof( Nunchaku ), + typeof( Sai ), typeof( Tekagi ), typeof( Tessen ), + typeof( Tetsubo ), typeof( Wakizashi ) + }; + + public static Type[] SEWeaponTypes{ get{ return m_SEWeaponTypes; } } + + private static Type[] m_AosWeaponTypes = new Type[] + { + typeof( Scythe ), typeof( BoneHarvester ), typeof( Scepter ), + typeof( BladedStaff ), typeof( Pike ), typeof( DoubleBladedStaff ), + typeof( Lance ), typeof( CrescentBlade ) + }; + + public static Type[] AosWeaponTypes{ get{ return m_AosWeaponTypes; } } + + private static Type[] m_WeaponTypes = new Type[] + { + typeof( Axe ), typeof( BattleAxe ), typeof( DoubleAxe ), + typeof( ExecutionersAxe ), typeof( Hatchet ), typeof( LargeBattleAxe ), + typeof( TwoHandedAxe ), typeof( WarAxe ), typeof( Club ), + typeof( Mace ), typeof( Maul ), typeof( WarHammer ), + typeof( WarMace ), typeof( Bardiche ), typeof( Halberd ), + typeof( Spear ), typeof( ShortSpear ), typeof( Pitchfork ), + typeof( WarFork ), typeof( BlackStaff ), typeof( GnarledStaff ), + typeof( QuarterStaff ), typeof( Broadsword ), typeof( Cutlass ), + typeof( Katana ), typeof( Kryss ), typeof( Longsword ), + typeof( Scimitar ), typeof( VikingSword ), typeof( Pickaxe ), + typeof( HammerPick ), typeof( ButcherKnife ), typeof( Cleaver ), + typeof( Dagger ), typeof( SkinningKnife ), typeof( ShepherdsCrook ) + }; + + public static Type[] WeaponTypes{ get{ return m_WeaponTypes; } } + + private static Type[] m_SERangedWeaponTypes = new Type[] + { + typeof( Yumi ) + }; + + public static Type[] SERangedWeaponTypes{ get{ return m_SERangedWeaponTypes; } } + + private static Type[] m_AosRangedWeaponTypes = new Type[] + { + typeof( CompositeBow ), typeof( RepeatingCrossbow ) + }; + + public static Type[] AosRangedWeaponTypes{ get{ return m_AosRangedWeaponTypes; } } + + private static Type[] m_RangedWeaponTypes = new Type[] + { + typeof( Bow ), typeof( Crossbow ), typeof( HeavyCrossbow ) + }; + + public static Type[] RangedWeaponTypes{ get{ return m_WeaponTypes; } } + + private static Type[] m_SEArmorTypes = new Type[] + { + typeof( ChainHatsuburi ), typeof( LeatherDo ), typeof( LeatherHaidate ), + typeof( LeatherHiroSode ), typeof( LeatherJingasa ), typeof( LeatherMempo ), + typeof( LeatherNinjaHood ), typeof( LeatherNinjaJacket ), typeof( LeatherNinjaMitts ), + typeof( LeatherNinjaPants ), typeof( LeatherSuneate ), typeof( DecorativePlateKabuto ), + typeof( HeavyPlateJingasa ), typeof( LightPlateJingasa ), typeof( PlateBattleKabuto ), + typeof( PlateDo ), typeof( PlateHaidate ), typeof( PlateHatsuburi ), + typeof( PlateHiroSode ), typeof( PlateMempo ), typeof( PlateSuneate ), + typeof( SmallPlateJingasa ), typeof( StandardPlateKabuto ), typeof( StuddedDo ), + typeof( StuddedHaidate ), typeof( StuddedHiroSode ), typeof( StuddedMempo ), + typeof( StuddedSuneate ) + }; + + public static Type[] SEArmorTypes{ get{ return m_SEArmorTypes; } } + + private static Type[] m_ArmorTypes = new Type[] + { + typeof( BoneArms ), typeof( BoneChest ), typeof( BoneGloves ), + typeof( BoneLegs ), typeof( BoneHelm ), typeof( ChainChest ), + typeof( ChainLegs ), typeof( ChainCoif ), typeof( Bascinet ), + typeof( CloseHelm ), typeof( Helmet ), typeof( NorseHelm ), + typeof( OrcHelm ), typeof( FemaleLeatherChest ), typeof( LeatherArms ), + typeof( LeatherBustierArms ), typeof( LeatherChest ), typeof( LeatherGloves ), + typeof( LeatherGorget ), typeof( LeatherLegs ), typeof( LeatherShorts ), + typeof( LeatherSkirt ), typeof( LeatherCap ), typeof( FemalePlateChest ), + typeof( PlateArms ), typeof( PlateChest ), typeof( PlateGloves ), + typeof( PlateGorget ), typeof( PlateHelm ), typeof( PlateLegs ), + typeof( RingmailArms ), typeof( RingmailChest ), typeof( RingmailGloves ), + typeof( RingmailLegs ), typeof( FemaleStuddedChest ), typeof( StuddedArms ), + typeof( StuddedBustierArms ), typeof( StuddedChest ), typeof( StuddedGloves ), + typeof( StuddedGorget ), typeof( StuddedLegs ) + }; + + public static Type[] ArmorTypes{ get{ return m_ArmorTypes; } } + + private static Type[] m_AosShieldTypes = new Type[] + { + typeof( ChaosShield ), typeof( OrderShield ) + }; + + public static Type[] AosShieldTypes{ get{ return m_AosShieldTypes; } } + + private static Type[] m_ShieldTypes = new Type[] + { + typeof( BronzeShield ), typeof( Buckler ), typeof( HeaterShield ), + typeof( MetalShield ), typeof( MetalKiteShield ), typeof( WoodenKiteShield ), + typeof( WoodenShield ) + }; + + public static Type[] ShieldTypes{ get{ return m_ShieldTypes; } } + + private static Type[] m_GemTypes = new Type[] + { + typeof( Amber ), typeof( Amethyst ), typeof( Citrine ), + typeof( Diamond ), typeof( Emerald ), typeof( Ruby ), + typeof( Sapphire ), typeof( StarSapphire ), typeof( Tourmaline ) + }; + + public static Type[] GemTypes{ get{ return m_GemTypes; } } + + private static Type[] m_JewelryTypes = new Type[] + { + typeof( GoldRing ), typeof( GoldBracelet ), + typeof( SilverRing ), typeof( SilverBracelet ) + }; + + public static Type[] JewelryTypes{ get{ return m_JewelryTypes; } } + + private static Type[] m_RegTypes = new Type[] + { + typeof( BlackPearl ), typeof( Bloodmoss ), typeof( Garlic ), + typeof( Ginseng ), typeof( MandrakeRoot ), typeof( Nightshade ), + typeof( SulfurousAsh ), typeof( SpidersSilk ) + }; + + public static Type[] RegTypes{ get{ return m_RegTypes; } } + + private static Type[] m_NecroRegTypes = new Type[] + { + typeof( BatWing ), typeof( GraveDust ), typeof( DaemonBlood ), + typeof( NoxCrystal ), typeof( PigIron ) + }; + + public static Type[] NecroRegTypes{ get{ return m_NecroRegTypes; } } + + private static Type[] m_PotionTypes = new Type[] + { + typeof( AgilityPotion ), typeof( StrengthPotion ), typeof( RefreshPotion ), + typeof( LesserCurePotion ), typeof( LesserHealPotion ), typeof( LesserPoisonPotion ) + }; + + public static Type[] PotionTypes{ get{ return m_PotionTypes; } } + + private static Type[] m_SEInstrumentTypes = new Type[] + { + typeof( BambooFlute ) + }; + + public static Type[] SEInstrumentTypes{ get{ return m_SEInstrumentTypes; } } + + private static Type[] m_InstrumentTypes = new Type[] + { + typeof( Drums ), typeof( Harp ), typeof( LapHarp ), + typeof( Lute ), typeof( Tambourine ), typeof( TambourineTassel ) + }; + + public static Type[] InstrumentTypes{ get{ return m_InstrumentTypes; } } + + private static Type[] m_StatueTypes = new Type[] + { + typeof( StatueSouth ), typeof( StatueSouth2 ), typeof( StatueNorth ), + typeof( StatueWest ), typeof( StatueEast ), typeof( StatueEast2 ), + typeof( StatueSouthEast ), typeof( BustSouth ), typeof( BustEast ) + }; + + public static Type[] StatueTypes{ get{ return m_StatueTypes; } } + + private static Type[] m_RegularScrollTypes = new Type[] + { + typeof( ClumsyScroll ), typeof( CreateFoodScroll ), typeof( FeeblemindScroll ), typeof( HealScroll ), + typeof( MagicArrowScroll ), typeof( NightSightScroll ), typeof( ReactiveArmorScroll ), typeof( WeakenScroll ), + typeof( AgilityScroll ), typeof( CunningScroll ), typeof( CureScroll ), typeof( HarmScroll ), + typeof( MagicTrapScroll ), typeof( MagicUnTrapScroll ), typeof( ProtectionScroll ), typeof( StrengthScroll ), + typeof( BlessScroll ), typeof( FireballScroll ), typeof( MagicLockScroll ), typeof( PoisonScroll ), + typeof( TelekinisisScroll ), typeof( TeleportScroll ), typeof( UnlockScroll ), typeof( WallOfStoneScroll ), + typeof( ArchCureScroll ), typeof( ArchProtectionScroll ), typeof( CurseScroll ), typeof( FireFieldScroll ), + typeof( GreaterHealScroll ), typeof( LightningScroll ), typeof( ManaDrainScroll ), typeof( RecallScroll ), + typeof( BladeSpiritsScroll ), typeof( DispelFieldScroll ), typeof( IncognitoScroll ), typeof( MagicReflectScroll ), + typeof( MindBlastScroll ), typeof( ParalyzeScroll ), typeof( PoisonFieldScroll ), typeof( SummonCreatureScroll ), + typeof( DispelScroll ), typeof( EnergyBoltScroll ), typeof( ExplosionScroll ), typeof( InvisibilityScroll ), + typeof( MarkScroll ), typeof( MassCurseScroll ), typeof( ParalyzeFieldScroll ), typeof( RevealScroll ), + typeof( ChainLightningScroll ), typeof( EnergyFieldScroll ), typeof( FlamestrikeScroll ), typeof( GateTravelScroll ), + typeof( ManaVampireScroll ), typeof( MassDispelScroll ), typeof( MeteorSwarmScroll ), typeof( PolymorphScroll ), + typeof( EarthquakeScroll ), typeof( EnergyVortexScroll ), typeof( ResurrectionScroll ), typeof( SummonAirElementalScroll ), + typeof( SummonDaemonScroll ), typeof( SummonEarthElementalScroll ), typeof( SummonFireElementalScroll ), typeof( SummonWaterElementalScroll ) + }; + + private static Type[] m_NecromancyScrollTypes = new Type[] + { + typeof( AnimateDeadScroll ), typeof( BloodOathScroll ), typeof( CorpseSkinScroll ), typeof( CurseWeaponScroll ), + typeof( EvilOmenScroll ), typeof( HorrificBeastScroll ), typeof( LichFormScroll ), typeof( MindRotScroll ), + typeof( PainSpikeScroll ), typeof( PoisonStrikeScroll ), typeof( StrangleScroll ), typeof( SummonFamiliarScroll ), + typeof( VampiricEmbraceScroll ), typeof( VengefulSpiritScroll ), typeof( WitherScroll ), typeof( WraithFormScroll ) + }; + + private static Type[] m_SENecromancyScrollTypes = new Type[] + { + typeof( AnimateDeadScroll ), typeof( BloodOathScroll ), typeof( CorpseSkinScroll ), typeof( CurseWeaponScroll ), + typeof( EvilOmenScroll ), typeof( HorrificBeastScroll ), typeof( LichFormScroll ), typeof( MindRotScroll ), + typeof( PainSpikeScroll ), typeof( PoisonStrikeScroll ), typeof( StrangleScroll ), typeof( SummonFamiliarScroll ), + typeof( VampiricEmbraceScroll ), typeof( VengefulSpiritScroll ), typeof( WitherScroll ), typeof( WraithFormScroll ), + typeof( ExorcismScroll ) + }; + + private static Type[] m_PaladinScrollTypes = new Type[0]; + + public static Type[] RegularScrollTypes{ get{ return m_RegularScrollTypes; } } + public static Type[] NecromancyScrollTypes{ get{ return m_NecromancyScrollTypes; } } + public static Type[] SENecromancyScrollTypes{ get{ return m_SENecromancyScrollTypes; } } + public static Type[] PaladinScrollTypes{ get{ return m_PaladinScrollTypes; } } + + private static Type[] m_GrimmochJournalTypes = new Type[] + { + typeof( GrimmochJournal1 ), typeof( GrimmochJournal2 ), typeof( GrimmochJournal3 ), + typeof( GrimmochJournal6 ), typeof( GrimmochJournal7 ), typeof( GrimmochJournal11 ), + typeof( GrimmochJournal14 ), typeof( GrimmochJournal17 ), typeof( GrimmochJournal23 ) + }; + + public static Type[] GrimmochJournalTypes{ get{ return m_GrimmochJournalTypes; } } + + private static Type[] m_LysanderNotebookTypes = new Type[] + { + typeof( LysanderNotebook1 ), typeof( LysanderNotebook2 ), typeof( LysanderNotebook3 ), + typeof( LysanderNotebook7 ), typeof( LysanderNotebook8 ), typeof( LysanderNotebook11 ) + }; + + public static Type[] LysanderNotebookTypes{ get{ return m_LysanderNotebookTypes; } } + + private static Type[] m_TavarasJournalTypes = new Type[] + { + typeof( TavarasJournal1 ), typeof( TavarasJournal2 ), typeof( TavarasJournal3 ), + typeof( TavarasJournal6 ), typeof( TavarasJournal7 ), typeof( TavarasJournal8 ), + typeof( TavarasJournal9 ), typeof( TavarasJournal11 ), typeof( TavarasJournal14 ), + typeof( TavarasJournal16 ), typeof( TavarasJournal16b ), typeof( TavarasJournal17 ), + typeof( TavarasJournal19 ) + }; + + public static Type[] TavarasJournalTypes{ get{ return m_TavarasJournalTypes; } } + + + private static Type[] m_WandTypes = new Type[] + { + typeof( ClumsyWand ), typeof( FeebleWand ), typeof( FireballWand ), + typeof( GreaterHealWand ), typeof( HarmWand ), typeof( HealWand ), + typeof( IDWand ), typeof( LightningWand ), typeof( MagicArrowWand ), + typeof( ManaDrainWand ), typeof( WeaknessWand ) + + }; + public static Type[] WandTypes{ get{ return m_WandTypes; } } + + private static Type[] m_SEClothingTypes = new Type[] + { + typeof( ClothNinjaJacket ), typeof( FemaleKimono ), typeof( Hakama ), + typeof( HakamaShita ), typeof( JinBaori ), typeof( Kamishimo ), + typeof( MaleKimono ), typeof( NinjaTabi ), typeof( Obi ), + typeof( SamuraiTabi ), typeof( TattsukeHakama ), typeof( Waraji ) + }; + + public static Type[] SEClothingTypes{ get{ return m_SEClothingTypes; } } + + private static Type[] m_AosClothingTypes = new Type[] + { + typeof( FurSarong ), typeof( FurCape ), typeof( FlowerGarland ), + typeof( GildedDress ), typeof( FurBoots ), typeof( FormalShirt ), + }; + + public static Type[] AosClothingTypes{ get{ return m_AosClothingTypes; } } + + private static Type[] m_ClothingTypes = new Type[] + { + typeof( Cloak ), + typeof( Bonnet ), typeof( Cap ), typeof( FeatheredHat ), + typeof( FloppyHat ), typeof( JesterHat ), typeof( Surcoat ), + typeof( SkullCap ), typeof( StrawHat ), typeof( TallStrawHat ), + typeof( TricorneHat ), typeof( WideBrimHat ), typeof( WizardsHat ), + typeof( BodySash ), typeof( Doublet ), typeof( Boots ), + typeof( FullApron ), typeof( JesterSuit ), typeof( Sandals ), + typeof( Tunic ), typeof( Shoes ), typeof( Shirt ), + typeof( Kilt ), typeof( Skirt ), typeof( FancyShirt ), + typeof( FancyDress ), typeof( ThighBoots ), typeof( LongPants ), + typeof( PlainDress ), typeof( Robe ), typeof( ShortPants ), + typeof( HalfApron ) + }; + public static Type[] ClothingTypes{ get{ return m_ClothingTypes; } } + + private static Type[] m_SEHatTypes = new Type[] + { + typeof( ClothNinjaHood ), typeof( Kasa ) + }; + + public static Type[] SEHatTypes{ get{ return m_SEHatTypes; } } + + private static Type[] m_AosHatTypes = new Type[] + { + typeof( FlowerGarland ), typeof( BearMask ), typeof( DeerMask ) //Are Bear& Deer mask inside the Pre-AoS loottables too? + }; + + public static Type[] AosHatTypes{ get{ return m_AosHatTypes; } } + + private static Type[] m_HatTypes = new Type[] + { + typeof( SkullCap ), typeof( Bandana ), typeof( FloppyHat ), + typeof( Cap ), typeof( WideBrimHat ), typeof( StrawHat ), + typeof( TallStrawHat ), typeof( WizardsHat ), typeof( Bonnet ), + typeof( FeatheredHat ), typeof( TricorneHat ), typeof( JesterHat ) + }; + + public static Type[] HatTypes{ get{ return m_HatTypes; } } + + private static Type[] m_LibraryBookTypes = new Type[] + { + typeof( GrammarOfOrcish ), typeof( CallToAnarchy ), typeof( ArmsAndWeaponsPrimer ), + typeof( SongOfSamlethe ), typeof( TaleOfThreeTribes ), typeof( GuideToGuilds ), + typeof( BirdsOfBritannia ), typeof( BritannianFlora ), typeof( ChildrenTalesVol2 ), + typeof( TalesOfVesperVol1 ), typeof( DeceitDungeonOfHorror ), typeof( DimensionalTravel ), + typeof( EthicalHedonism ), typeof( MyStory ), typeof( DiversityOfOurLand ), + typeof( QuestOfVirtues ), typeof( RegardingLlamas ), typeof( TalkingToWisps ), + typeof( TamingDragons ), typeof( BoldStranger ), typeof( BurningOfTrinsic ), + typeof( TheFight ), typeof( LifeOfATravellingMinstrel ), typeof( MajorTradeAssociation ), + typeof( RankingsOfTrades ), typeof( WildGirlOfTheForest ), typeof( TreatiseOnAlchemy ), + typeof( VirtueBook ) + }; + + public static Type[] LibraryBookTypes{ get{ return m_LibraryBookTypes; } } + + #endregion + + #region Accessors + + public static BaseWand RandomWand() + { + return Construct( m_WandTypes ) as BaseWand; + } + + public static BaseClothing RandomClothing() + { + return RandomClothing( false ); + } + + public static BaseClothing RandomClothing( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEClothingTypes, m_AosClothingTypes, m_ClothingTypes ) as BaseClothing; + + if ( Core.AOS ) + return Construct( m_AosClothingTypes, m_ClothingTypes ) as BaseClothing; + + return Construct( m_ClothingTypes ) as BaseClothing; + } + + public static BaseWeapon RandomRangedWeapon() + { + return RandomRangedWeapon( false ); + } + + public static BaseWeapon RandomRangedWeapon( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SERangedWeaponTypes, m_AosRangedWeaponTypes, m_RangedWeaponTypes ) as BaseWeapon; + + if ( Core.AOS ) + return Construct( m_AosRangedWeaponTypes, m_RangedWeaponTypes ) as BaseWeapon; + + return Construct( m_RangedWeaponTypes ) as BaseWeapon; + } + + public static BaseWeapon RandomWeapon() + { + return RandomWeapon( false ); + } + + public static BaseWeapon RandomWeapon( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEWeaponTypes, m_AosWeaponTypes, m_WeaponTypes ) as BaseWeapon; + + if ( Core.AOS ) + return Construct( m_AosWeaponTypes, m_WeaponTypes ) as BaseWeapon; + + return Construct( m_WeaponTypes ) as BaseWeapon; + } + + public static Item RandomWeaponOrJewelry() + { + return RandomWeaponOrJewelry( false ); + } + + public static Item RandomWeaponOrJewelry( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEWeaponTypes, m_AosWeaponTypes, m_WeaponTypes, m_JewelryTypes ); + + if ( Core.AOS ) + return Construct( m_AosWeaponTypes, m_WeaponTypes, m_JewelryTypes ); + + return Construct( m_WeaponTypes, m_JewelryTypes ); + } + + public static BaseJewel RandomJewelry() + { + return Construct( m_JewelryTypes ) as BaseJewel; + } + + public static BaseArmor RandomArmor() + { + return RandomArmor( false ); + } + + public static BaseArmor RandomArmor( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEArmorTypes, m_ArmorTypes ) as BaseArmor; + + return Construct( m_ArmorTypes ) as BaseArmor; + } + + public static BaseHat RandomHat() + { + return RandomHat( false ); + } + + public static BaseHat RandomHat( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEHatTypes, m_AosHatTypes, m_HatTypes ) as BaseHat; + + if ( Core.AOS ) + return Construct( m_AosHatTypes, m_HatTypes ) as BaseHat; + + return Construct( m_HatTypes ) as BaseHat; + } + + public static Item RandomArmorOrHat() + { + return RandomArmorOrHat( false ); + } + + public static Item RandomArmorOrHat( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEArmorTypes, m_ArmorTypes, m_SEHatTypes, m_AosHatTypes, m_HatTypes ); + + if ( Core.AOS ) + return Construct( m_ArmorTypes, m_AosHatTypes, m_HatTypes ); + + return Construct( m_ArmorTypes, m_HatTypes ); + } + + public static BaseShield RandomShield() + { + if ( Core.AOS ) + return Construct( m_AosShieldTypes, m_ShieldTypes ) as BaseShield; + + return Construct( m_ShieldTypes ) as BaseShield; + } + + public static BaseArmor RandomArmorOrShield() + { + return RandomArmorOrShield( false ); + } + + public static BaseArmor RandomArmorOrShield( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEArmorTypes, m_ArmorTypes, m_AosShieldTypes, m_ShieldTypes ) as BaseArmor; + + if ( Core.AOS ) + return Construct( m_ArmorTypes, m_AosShieldTypes, m_ShieldTypes ) as BaseArmor; + + return Construct( m_ArmorTypes, m_ShieldTypes ) as BaseArmor; + } + + public static Item RandomArmorOrShieldOrJewelry() + { + return RandomArmorOrShieldOrJewelry( false ); + } + + public static Item RandomArmorOrShieldOrJewelry( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEArmorTypes, m_ArmorTypes, m_SEHatTypes, m_AosHatTypes, m_HatTypes, m_AosShieldTypes, m_ShieldTypes, m_JewelryTypes ); + + if ( Core.AOS ) + return Construct( m_ArmorTypes, m_AosHatTypes, m_HatTypes, m_AosShieldTypes, m_ShieldTypes, m_JewelryTypes ); + + return Construct( m_ArmorTypes, m_HatTypes, m_ShieldTypes, m_JewelryTypes ); + } + + public static Item RandomArmorOrShieldOrWeapon() + { + return RandomArmorOrShieldOrWeapon( false ); + } + + public static Item RandomArmorOrShieldOrWeapon( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEWeaponTypes, m_AosWeaponTypes, m_WeaponTypes, m_SERangedWeaponTypes, m_AosRangedWeaponTypes, m_RangedWeaponTypes, m_SEArmorTypes, m_ArmorTypes, m_SEHatTypes, m_AosHatTypes, m_HatTypes, m_AosShieldTypes, m_ShieldTypes ); + + if ( Core.AOS ) + return Construct( m_AosWeaponTypes, m_WeaponTypes, m_AosRangedWeaponTypes, m_RangedWeaponTypes, m_ArmorTypes, m_AosHatTypes, m_HatTypes, m_AosShieldTypes, m_ShieldTypes ); + + return Construct( m_WeaponTypes, m_RangedWeaponTypes, m_ArmorTypes, m_HatTypes, m_ShieldTypes ); + } + + public static Item RandomArmorOrShieldOrWeaponOrJewelry() + { + return RandomArmorOrShieldOrWeaponOrJewelry( false ); + } + + public static Item RandomArmorOrShieldOrWeaponOrJewelry( bool inTokuno ) + { + if ( Core.SE && inTokuno ) + return Construct( m_SEWeaponTypes, m_AosWeaponTypes, m_WeaponTypes, m_SERangedWeaponTypes, m_AosRangedWeaponTypes, m_RangedWeaponTypes, m_SEArmorTypes, m_ArmorTypes, m_SEHatTypes, m_AosHatTypes, m_HatTypes, m_AosShieldTypes, m_ShieldTypes, m_JewelryTypes ); + + if ( Core.AOS ) + return Construct( m_AosWeaponTypes, m_WeaponTypes, m_AosRangedWeaponTypes, m_RangedWeaponTypes, m_ArmorTypes, m_AosHatTypes, m_HatTypes, m_AosShieldTypes, m_ShieldTypes, m_JewelryTypes ); + + return Construct( m_WeaponTypes, m_RangedWeaponTypes, m_ArmorTypes, m_HatTypes, m_ShieldTypes, m_JewelryTypes ); + } + + public static Item RandomGem() + { + return Construct( m_GemTypes ); + } + + public static Item RandomReagent() + { + return Construct( m_RegTypes ); + } + + public static Item RandomNecromancyReagent() + { + return Construct( m_NecroRegTypes ); + } + + public static Item RandomPossibleReagent() + { + if ( Core.AOS ) + return Construct( m_RegTypes, m_NecroRegTypes ); + + return Construct( m_RegTypes ); + } + + public static Item RandomPotion() + { + return Construct( m_PotionTypes ); + } + + public static BaseInstrument RandomInstrument() + { + if ( Core.SE ) + return Construct( m_InstrumentTypes, m_SEInstrumentTypes ) as BaseInstrument; + + return Construct( m_InstrumentTypes ) as BaseInstrument; + } + + public static Item RandomStatue() + { + return Construct( m_StatueTypes ); + } + + public static SpellScroll RandomScroll( int minIndex, int maxIndex, SpellbookType type ) + { + Type[] types; + + switch ( type ) + { + default: + case SpellbookType.Regular: types = m_RegularScrollTypes; break; + case SpellbookType.Necromancer: types = (Core.SE ? m_SENecromancyScrollTypes : m_NecromancyScrollTypes ); break; + case SpellbookType.Paladin: types = m_PaladinScrollTypes; break; + } + + return Construct( types, Utility.RandomMinMax( minIndex, maxIndex ) ) as SpellScroll; + } + + public static BaseBook RandomGrimmochJournal() + { + return Construct( m_GrimmochJournalTypes ) as BaseBook; + } + + public static BaseBook RandomLysanderNotebook() + { + return Construct( m_LysanderNotebookTypes ) as BaseBook; + } + + public static BaseBook RandomTavarasJournal() + { + return Construct( m_TavarasJournalTypes ) as BaseBook; + } + + public static BaseBook RandomLibraryBook() + { + return Construct( m_LibraryBookTypes ) as BaseBook; + } + #endregion + + #region Construction methods + public static Item Construct( Type type ) + { + try + { + return Activator.CreateInstance( type ) as Item; + } + catch + { + return null; + } + } + + public static Item Construct( Type[] types ) + { + if ( types.Length > 0 ) + return Construct( types, Utility.Random( types.Length ) ); + + return null; + } + + public static Item Construct( Type[] types, int index ) + { + if ( index >= 0 && index < types.Length ) + return Construct( types[index] ); + + return null; + } + + public static Item Construct( params Type[][] types ) + { + int totalLength = 0; + + for ( int i = 0; i < types.Length; ++i ) + totalLength += types[i].Length; + + if ( totalLength > 0 ) + { + int index = Utility.Random( totalLength ); + + for ( int i = 0; i < types.Length; ++i ) + { + if ( index >= 0 && index < types[i].Length ) + return Construct( types[i][index] ); + + index -= types[i].Length; + } + } + + return null; + } + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Misc/LootPack.cs b/Scripts/Misc/LootPack.cs new file mode 100644 index 000000000..9046f5cd6 --- /dev/null +++ b/Scripts/Misc/LootPack.cs @@ -0,0 +1,961 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using System.Collections.Generic; + +namespace Server +{ + public class LootPack + { + public static int GetLuckChance( Mobile killer, Mobile victim ) + { + if ( !Core.AOS ) + return 0; + + int luck = killer.Luck; + + PlayerMobile pmKiller = killer as PlayerMobile; + if ( pmKiller != null && pmKiller.SentHonorContext != null && pmKiller.SentHonorContext.Target == victim ) + pmKiller.SentHonorContext.ApplyPerfectionLuckBonus( ref luck ); + + if ( luck < 0 ) + return 0; + + if ( !Core.SE && luck > 1200 ) + luck = 1200; + + return (int)(Math.Pow( luck, 1 / 1.8 ) * 100); + } + + public static int GetLuckChanceForKiller( Mobile dead ) + { + List list = BaseCreature.GetLootingRights( dead.DamageEntries, dead.HitsMax ); + + DamageStore highest = null; + + for ( int i = 0; i < list.Count; ++i ) + { + DamageStore ds = list[i]; + + if ( ds.m_HasRight && (highest == null || ds.m_Damage > highest.m_Damage) ) + highest = ds; + } + + if ( highest == null ) + return 0; + + return GetLuckChance( highest.m_Mobile, dead ); + } + + public static bool CheckLuck( int chance ) + { + return ( chance > Utility.Random( 10000 ) ); + } + + private LootPackEntry[] m_Entries; + + public LootPack( LootPackEntry[] entries ) + { + m_Entries = entries; + } + + public void Generate( Mobile from, Container cont, bool spawning, int luckChance ) + { + if ( cont == null ) + return; + + bool checkLuck = Core.AOS; + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + LootPackEntry entry = m_Entries[i]; + + bool shouldAdd = ( entry.Chance > Utility.Random( 10000 ) ); + + if ( !shouldAdd && checkLuck ) + { + checkLuck = false; + + if ( LootPack.CheckLuck( luckChance ) ) + shouldAdd = ( entry.Chance > Utility.Random( 10000 ) ); + } + + if ( !shouldAdd ) + continue; + + Item item = entry.Construct( from, luckChance, spawning ); + + if ( item != null ) + { + if ( !item.Stackable || !cont.TryDropItem( from, item, false ) ) + cont.DropItem( item ); + } + } + } + + public static readonly LootPackItem[] Gold = new LootPackItem[] + { + new LootPackItem( typeof( Gold ), 1 ) + }; + + public static readonly LootPackItem[] Instruments = new LootPackItem[] + { + new LootPackItem( typeof( BaseInstrument ), 1 ) + }; + + + public static readonly LootPackItem[] LowScrollItems = new LootPackItem[] + { + new LootPackItem( typeof( ClumsyScroll ), 1 ) + }; + + public static readonly LootPackItem[] MedScrollItems = new LootPackItem[] + { + new LootPackItem( typeof( ArchCureScroll ), 1 ) + }; + + public static readonly LootPackItem[] HighScrollItems = new LootPackItem[] + { + new LootPackItem( typeof( SummonAirElementalScroll ), 1 ) + }; + + public static readonly LootPackItem[] GemItems = new LootPackItem[] + { + new LootPackItem( typeof( Amber ), 1 ) + }; + + public static readonly LootPackItem[] PotionItems = new LootPackItem[] + { + new LootPackItem( typeof( AgilityPotion ), 1 ), + new LootPackItem( typeof( StrengthPotion ), 1 ), + new LootPackItem( typeof( RefreshPotion ), 1 ), + new LootPackItem( typeof( LesserCurePotion ), 1 ), + new LootPackItem( typeof( LesserHealPotion ), 1 ), + new LootPackItem( typeof( LesserPoisonPotion ), 1 ) + }; + + #region Old Magic Items + public static readonly LootPackItem[] OldMagicItems = new LootPackItem[] + { + new LootPackItem( typeof( BaseJewel ), 1 ), + new LootPackItem( typeof( BaseArmor ), 4 ), + new LootPackItem( typeof( BaseWeapon ), 3 ), + new LootPackItem( typeof( BaseRanged ), 1 ), + new LootPackItem( typeof( BaseShield ), 1 ) + }; + #endregion + + #region AOS Magic Items + public static readonly LootPackItem[] AosMagicItemsPoor = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 3 ), + new LootPackItem( typeof( BaseRanged ), 1 ), + new LootPackItem( typeof( BaseArmor ), 4 ), + new LootPackItem( typeof( BaseShield ), 1 ), + new LootPackItem( typeof( BaseJewel ), 2 ) + }; + + public static readonly LootPackItem[] AosMagicItemsMeagerType1 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 56 ), + new LootPackItem( typeof( BaseRanged ), 14 ), + new LootPackItem( typeof( BaseArmor ), 81 ), + new LootPackItem( typeof( BaseShield ), 11 ), + new LootPackItem( typeof( BaseJewel ), 42 ) + }; + + public static readonly LootPackItem[] AosMagicItemsMeagerType2 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 28 ), + new LootPackItem( typeof( BaseRanged ), 7 ), + new LootPackItem( typeof( BaseArmor ), 40 ), + new LootPackItem( typeof( BaseShield ), 5 ), + new LootPackItem( typeof( BaseJewel ), 21 ) + }; + + public static readonly LootPackItem[] AosMagicItemsAverageType1 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 90 ), + new LootPackItem( typeof( BaseRanged ), 23 ), + new LootPackItem( typeof( BaseArmor ), 130 ), + new LootPackItem( typeof( BaseShield ), 17 ), + new LootPackItem( typeof( BaseJewel ), 68 ) + }; + + public static readonly LootPackItem[] AosMagicItemsAverageType2 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 54 ), + new LootPackItem( typeof( BaseRanged ), 13 ), + new LootPackItem( typeof( BaseArmor ), 77 ), + new LootPackItem( typeof( BaseShield ), 10 ), + new LootPackItem( typeof( BaseJewel ), 40 ) + }; + + public static readonly LootPackItem[] AosMagicItemsRichType1 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 211 ), + new LootPackItem( typeof( BaseRanged ), 53 ), + new LootPackItem( typeof( BaseArmor ), 303 ), + new LootPackItem( typeof( BaseShield ), 39 ), + new LootPackItem( typeof( BaseJewel ), 158 ) + }; + + public static readonly LootPackItem[] AosMagicItemsRichType2 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 170 ), + new LootPackItem( typeof( BaseRanged ), 43 ), + new LootPackItem( typeof( BaseArmor ), 245 ), + new LootPackItem( typeof( BaseShield ), 32 ), + new LootPackItem( typeof( BaseJewel ), 128 ) + }; + + public static readonly LootPackItem[] AosMagicItemsFilthyRichType1 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 219 ), + new LootPackItem( typeof( BaseRanged ), 55 ), + new LootPackItem( typeof( BaseArmor ), 315 ), + new LootPackItem( typeof( BaseShield ), 41 ), + new LootPackItem( typeof( BaseJewel ), 164 ) + }; + + public static readonly LootPackItem[] AosMagicItemsFilthyRichType2 = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 239 ), + new LootPackItem( typeof( BaseRanged ), 60 ), + new LootPackItem( typeof( BaseArmor ), 343 ), + new LootPackItem( typeof( BaseShield ), 90 ), + new LootPackItem( typeof( BaseJewel ), 45 ) + }; + + public static readonly LootPackItem[] AosMagicItemsUltraRich = new LootPackItem[] + { + new LootPackItem( typeof( BaseWeapon ), 276 ), + new LootPackItem( typeof( BaseRanged ), 69 ), + new LootPackItem( typeof( BaseArmor ), 397 ), + new LootPackItem( typeof( BaseShield ), 52 ), + new LootPackItem( typeof( BaseJewel ), 207 ) + }; + #endregion + + #region SE definitions + public static readonly LootPack SePoor = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "2d10+20" ), + new LootPackEntry( false, AosMagicItemsPoor, 1.00, 1, 5, 0, 100 ), + new LootPackEntry( false, Instruments, 0.02, 1 ) + } ); + + public static readonly LootPack SeMeager = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "4d10+40" ), + new LootPackEntry( false, AosMagicItemsMeagerType1, 20.40, 1, 2, 0, 50 ), + new LootPackEntry( false, AosMagicItemsMeagerType2, 10.20, 1, 5, 0, 100 ), + new LootPackEntry( false, Instruments, 0.10, 1 ) + } ); + + public static readonly LootPack SeAverage = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "8d10+100" ), + new LootPackEntry( false, AosMagicItemsAverageType1, 32.80, 1, 3, 0, 50 ), + new LootPackEntry( false, AosMagicItemsAverageType1, 32.80, 1, 4, 0, 75 ), + new LootPackEntry( false, AosMagicItemsAverageType2, 19.50, 1, 5, 0, 100 ), + new LootPackEntry( false, Instruments, 0.40, 1 ) + } ); + + public static readonly LootPack SeRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "15d10+225" ), + new LootPackEntry( false, AosMagicItemsRichType1, 76.30, 1, 4, 0, 75 ), + new LootPackEntry( false, AosMagicItemsRichType1, 76.30, 1, 4, 0, 75 ), + new LootPackEntry( false, AosMagicItemsRichType2, 61.70, 1, 5, 0, 100 ), + new LootPackEntry( false, Instruments, 1.00, 1 ) + } ); + + public static readonly LootPack SeFilthyRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "3d100+400" ), + new LootPackEntry( false, AosMagicItemsFilthyRichType1, 79.50, 1, 5, 0, 100 ), + new LootPackEntry( false, AosMagicItemsFilthyRichType1, 79.50, 1, 5, 0, 100 ), + new LootPackEntry( false, AosMagicItemsFilthyRichType2, 77.60, 1, 5, 25, 100 ), + new LootPackEntry( false, Instruments, 2.00, 1 ) + } ); + + public static readonly LootPack SeUltraRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "6d100+600" ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, Instruments, 2.00, 1 ) + } ); + + public static readonly LootPack SeSuperBoss = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "10d100+800" ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100 ), + new LootPackEntry( false, Instruments, 2.00, 1 ) + } ); + #endregion + + #region AOS definitions + public static readonly LootPack AosPoor = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "1d10+10" ), + new LootPackEntry( false, AosMagicItemsPoor, 0.02, 1, 5, 0, 90 ), + new LootPackEntry( false, Instruments, 0.02, 1 ) + } ); + + public static readonly LootPack AosMeager = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "3d10+20" ), + new LootPackEntry( false, AosMagicItemsMeagerType1, 1.00, 1, 2, 0, 10 ), + new LootPackEntry( false, AosMagicItemsMeagerType2, 0.20, 1, 5, 0, 90 ), + new LootPackEntry( false, Instruments, 0.10, 1 ) + } ); + + public static readonly LootPack AosAverage = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "5d10+50" ), + new LootPackEntry( false, AosMagicItemsAverageType1, 5.00, 1, 4, 0, 20 ), + new LootPackEntry( false, AosMagicItemsAverageType1, 2.00, 1, 3, 0, 50 ), + new LootPackEntry( false, AosMagicItemsAverageType2, 0.50, 1, 5, 0, 90 ), + new LootPackEntry( false, Instruments, 0.40, 1 ) + } ); + + public static readonly LootPack AosRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "10d10+150" ), + new LootPackEntry( false, AosMagicItemsRichType1, 20.00, 1, 4, 0, 40 ), + new LootPackEntry( false, AosMagicItemsRichType1, 10.00, 1, 5, 0, 60 ), + new LootPackEntry( false, AosMagicItemsRichType2, 1.00, 1, 5, 0, 90 ), + new LootPackEntry( false, Instruments, 1.00, 1 ) + } ); + + public static readonly LootPack AosFilthyRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "2d100+200" ), + new LootPackEntry( false, AosMagicItemsFilthyRichType1, 33.00, 1, 4, 0, 50 ), + new LootPackEntry( false, AosMagicItemsFilthyRichType1, 33.00, 1, 4, 0, 60 ), + new LootPackEntry( false, AosMagicItemsFilthyRichType2, 20.00, 1, 5, 0, 75 ), + new LootPackEntry( false, AosMagicItemsFilthyRichType2, 5.00, 1, 5, 0, 100 ), + new LootPackEntry( false, Instruments, 2.00, 1 ) + } ); + + public static readonly LootPack AosUltraRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "5d100+500" ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 35, 100 ), + new LootPackEntry( false, Instruments, 2.00, 1 ) + } ); + + public static readonly LootPack AosSuperBoss = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "5d100+500" ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 25, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 33, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100 ), + new LootPackEntry( false, AosMagicItemsUltraRich, 100.00, 1, 5, 50, 100 ), + new LootPackEntry( false, Instruments, 2.00, 1 ) + } ); + #endregion + + #region Pre-AOS definitions + public static readonly LootPack OldPoor = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "1d25" ), + new LootPackEntry( false, Instruments, 0.02, 1 ) + } ); + + public static readonly LootPack OldMeager = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "5d10+25" ), + new LootPackEntry( false, Instruments, 0.10, 1 ), + new LootPackEntry( false, OldMagicItems, 1.00, 1, 1, 0, 60 ), + new LootPackEntry( false, OldMagicItems, 0.20, 1, 1, 10, 70 ) + } ); + + public static readonly LootPack OldAverage = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "10d10+50" ), + new LootPackEntry( false, Instruments, 0.40, 1 ), + new LootPackEntry( false, OldMagicItems, 5.00, 1, 1, 20, 80 ), + new LootPackEntry( false, OldMagicItems, 2.00, 1, 1, 30, 90 ), + new LootPackEntry( false, OldMagicItems, 0.50, 1, 1, 40, 100 ) + } ); + + public static readonly LootPack OldRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "10d10+250" ), + new LootPackEntry( false, Instruments, 1.00, 1 ), + new LootPackEntry( false, OldMagicItems, 20.00, 1, 1, 60, 100 ), + new LootPackEntry( false, OldMagicItems, 10.00, 1, 1, 65, 100 ), + new LootPackEntry( false, OldMagicItems, 1.00, 1, 1, 70, 100 ) + } ); + + public static readonly LootPack OldFilthyRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "2d125+400" ), + new LootPackEntry( false, Instruments, 2.00, 1 ), + new LootPackEntry( false, OldMagicItems, 33.00, 1, 1, 50, 100 ), + new LootPackEntry( false, OldMagicItems, 33.00, 1, 1, 60, 100 ), + new LootPackEntry( false, OldMagicItems, 20.00, 1, 1, 70, 100 ), + new LootPackEntry( false, OldMagicItems, 5.00, 1, 1, 80, 100 ) + } ); + + public static readonly LootPack OldUltraRich = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "5d100+500" ), + new LootPackEntry( false, Instruments, 2.00, 1 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 40, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 40, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 50, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 50, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 60, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 60, 100 ) + } ); + + public static readonly LootPack OldSuperBoss = new LootPack( new LootPackEntry[] + { + new LootPackEntry( true, Gold, 100.00, "5d100+500" ), + new LootPackEntry( false, Instruments, 2.00, 1 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 40, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 40, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 40, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 50, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 50, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 50, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 60, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 60, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 60, 100 ), + new LootPackEntry( false, OldMagicItems, 100.00, 1, 1, 70, 100 ) + } ); + #endregion + + #region Generic accessors + public static LootPack Poor{ get{ return Core.AOS ? AosPoor : OldPoor; } } + public static LootPack Meager{ get{ return Core.AOS ? AosMeager : OldMeager; } } + public static LootPack Average{ get{ return Core.AOS ? AosAverage : OldAverage; } } + public static LootPack Rich{ get{ return Core.AOS ? AosRich : OldRich; } } + public static LootPack FilthyRich{ get{ return Core.AOS ? AosFilthyRich : OldFilthyRich; } } + public static LootPack UltraRich{ get{ return Core.AOS ? AosUltraRich : OldUltraRich; } } + public static LootPack SuperBoss{ get{ return Core.AOS ? AosSuperBoss : OldSuperBoss; } } + #endregion + + public static readonly LootPack LowScrolls = new LootPack( new LootPackEntry[] + { + new LootPackEntry( false, LowScrollItems, 100.00, 1 ) + } ); + + public static readonly LootPack MedScrolls = new LootPack( new LootPackEntry[] + { + new LootPackEntry( false, MedScrollItems, 100.00, 1 ) + } ); + + public static readonly LootPack HighScrolls = new LootPack( new LootPackEntry[] + { + new LootPackEntry( false, HighScrollItems, 100.00, 1 ) + } ); + + public static readonly LootPack Gems = new LootPack( new LootPackEntry[] + { + new LootPackEntry( false, GemItems, 100.00, 1 ) + } ); + + public static readonly LootPack Potions = new LootPack( new LootPackEntry[] + { + new LootPackEntry( false, PotionItems, 100.00, 1 ) + } ); + } + + public class LootPackEntry + { + private int m_Chance; + private LootPackDice m_Quantity; + + private int m_MaxProps, m_MinIntensity, m_MaxIntensity; + + private bool m_AtSpawnTime; + + private LootPackItem[] m_Items; + + public int Chance + { + get{ return m_Chance; } + set{ m_Chance = value; } + } + + public LootPackDice Quantity + { + get{ return m_Quantity; } + set{ m_Quantity = value; } + } + + public int MaxProps + { + get{ return m_MaxProps; } + set{ m_MaxProps = value; } + } + + public int MinIntensity + { + get{ return m_MinIntensity; } + set{ m_MinIntensity = value; } + } + + public int MaxIntensity + { + get{ return m_MaxIntensity; } + set{ m_MaxIntensity = value; } + } + + public LootPackItem[] Items + { + get{ return m_Items; } + set{ m_Items = value; } + } + + private static bool IsInTokuno( Mobile m ) + { + if ( m.Region.IsPartOf( "Fan Dancer's Dojo" ) ) + return true; + + if ( m.Region.IsPartOf( "Yomotsu Mines" ) ) + return true; + + return ( m.Map == Map.Tokuno ); + } + + public Item Construct( Mobile from, int luckChance, bool spawning ) + { + if ( m_AtSpawnTime != spawning ) + return null; + + int totalChance = 0; + + for ( int i = 0; i < m_Items.Length; ++i ) + totalChance += m_Items[i].Chance; + + int rnd = Utility.Random( totalChance ); + + for ( int i = 0; i < m_Items.Length; ++i ) + { + LootPackItem item = m_Items[i]; + + if ( rnd < item.Chance ) + return Mutate( from, luckChance, item.Construct( IsInTokuno( from ) ) ); + + rnd -= item.Chance; + } + + return null; + } + + private int GetRandomOldBonus() + { + int rnd = Utility.RandomMinMax( m_MinIntensity, m_MaxIntensity ); + + if ( 50 > rnd ) + return 1; + else + rnd -= 50; + + if ( 25 > rnd ) + return 2; + else + rnd -= 25; + + if ( 14 > rnd ) + return 3; + else + rnd -= 14; + + if ( 8 > rnd ) + return 4; + + return 5; + } + + 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 ); + } + else // not aos + { + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( 80 > Utility.Random( 100 ) ) + weapon.AccuracyLevel = (WeaponAccuracyLevel)GetRandomOldBonus(); + + if ( 60 > Utility.Random( 100 ) ) + weapon.DamageLevel = (WeaponDamageLevel)GetRandomOldBonus(); + + if ( 40 > Utility.Random( 100 ) ) + weapon.DurabilityLevel = (WeaponDurabilityLevel)GetRandomOldBonus(); + + if ( 5 > Utility.Random( 100 ) ) + weapon.Slayer = SlayerName.Silver; + + if ( from != null && weapon.AccuracyLevel == 0 && weapon.DamageLevel == 0 && weapon.DurabilityLevel == 0 && weapon.Slayer == SlayerName.None && 5 > Utility.Random( 100 ) ) + weapon.Slayer = SlayerGroup.GetLootSlayerType( from.GetType() ); + } + else if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( 80 > Utility.Random( 100 ) ) + armor.ProtectionLevel = (ArmorProtectionLevel)GetRandomOldBonus(); + + if ( 40 > Utility.Random( 100 ) ) + armor.Durability = (ArmorDurabilityLevel)GetRandomOldBonus(); + } + } + } + else if ( item is BaseInstrument ) + { + SlayerName slayer = SlayerName.None; + + if ( Core.AOS ) + slayer = BaseRunicTool.GetRandomSlayer(); + else + slayer = SlayerGroup.GetLootSlayerType( from.GetType() ); + + if ( slayer == SlayerName.None ) + { + item.Delete(); + return null; + } + + BaseInstrument instr = (BaseInstrument)item; + + instr.Quality = InstrumentQuality.Regular; + instr.Slayer = slayer; + } + + if ( item.Stackable ) + item.Amount = m_Quantity.Roll(); + } + + return item; + } + + public LootPackEntry( bool atSpawnTime, LootPackItem[] items, double chance, string quantity ) : this( atSpawnTime, items, chance, new LootPackDice( quantity ), 0, 0, 0 ) + { + } + + public LootPackEntry( bool atSpawnTime, LootPackItem[] items, double chance, int quantity ) : this( atSpawnTime, items, chance, new LootPackDice( 0, 0, quantity ), 0, 0, 0 ) + { + } + + public LootPackEntry( bool atSpawnTime, LootPackItem[] items, double chance, string quantity, int maxProps, int minIntensity, int maxIntensity ) : this( atSpawnTime, items, chance, new LootPackDice( quantity ), maxProps, minIntensity, maxIntensity ) + { + } + + public LootPackEntry( bool atSpawnTime, LootPackItem[] items, double chance, int quantity, int maxProps, int minIntensity, int maxIntensity ) : this( atSpawnTime, items, chance, new LootPackDice( 0, 0, quantity ), maxProps, minIntensity, maxIntensity ) + { + } + + public LootPackEntry( bool atSpawnTime, LootPackItem[] items, double chance, LootPackDice quantity, int maxProps, int minIntensity, int maxIntensity ) + { + m_AtSpawnTime = atSpawnTime; + m_Items = items; + m_Chance = (int)(100 * chance); + m_Quantity = quantity; + m_MaxProps = maxProps; + m_MinIntensity = minIntensity; + m_MaxIntensity = maxIntensity; + } + + public int GetBonusProperties() + { + int p0=0, p1=0, p2=0, p3=0, p4=0, p5=0; + + switch ( m_MaxProps ) + { + case 1: p0= 3; p1= 1; break; + case 2: p0= 6; p1= 3; p2= 1; break; + case 3: p0=10; p1= 6; p2= 3; p3= 1; break; + case 4: p0=16; p1=12; p2= 6; p3= 5; p4=1; break; + case 5: p0=30; p1=25; p2=20; p3=15; p4=9; p5=1; break; + } + + int pc = p0+p1+p2+p3+p4+p5; + + int rnd = Utility.Random( pc ); + + if ( rnd < p5 ) + return 5; + else + rnd -= p5; + + if ( rnd < p4 ) + return 4; + else + rnd -= p4; + + if ( rnd < p3 ) + return 3; + else + rnd -= p3; + + if ( rnd < p2 ) + return 2; + else + rnd -= p2; + + if ( rnd < p1 ) + return 1; + + return 0; + } + } + + public class LootPackItem + { + private Type m_Type; + private int m_Chance; + + public Type Type + { + get{ return m_Type; } + set{ m_Type = value; } + } + + public int Chance + { + get{ return m_Chance; } + set{ m_Chance = value; } + } + + private static Type[] m_BlankTypes = new Type[]{ typeof( BlankScroll ) }; + private static Type[][] m_NecroTypes = new Type[][] + { + new Type[] // low + { + typeof( AnimateDeadScroll ), typeof( BloodOathScroll ), typeof( CorpseSkinScroll ), typeof( CurseWeaponScroll ), + typeof( EvilOmenScroll ), typeof( HorrificBeastScroll ), typeof( MindRotScroll ), typeof( PainSpikeScroll ), + typeof( SummonFamiliarScroll ), typeof( WraithFormScroll ) + }, + new Type[] // med + { + typeof( LichFormScroll ), typeof( PoisonStrikeScroll ), typeof( StrangleScroll ), typeof( WitherScroll ) + }, + + ((Core.SE) ? + new Type[] // high + { + typeof( VengefulSpiritScroll ), typeof( VampiricEmbraceScroll ), typeof( ExorcismScroll ) + } : + new Type[] // high + { + typeof( VengefulSpiritScroll ), typeof( VampiricEmbraceScroll ) + }) + }; + + public static Item RandomScroll( int index, int minCircle, int maxCircle ) + { + --minCircle; + --maxCircle; + + int scrollCount = ((maxCircle - minCircle) + 1) * 8; + + if ( index == 0 ) + scrollCount += m_BlankTypes.Length; + + if ( Core.AOS ) + scrollCount += m_NecroTypes[index].Length; + + int rnd = Utility.Random( scrollCount ); + + if ( index == 0 && rnd < m_BlankTypes.Length ) + return Loot.Construct( m_BlankTypes ); + else if ( index == 0 ) + rnd -= m_BlankTypes.Length; + + if ( Core.AOS && rnd < m_NecroTypes.Length ) + return Loot.Construct( m_NecroTypes[index] ); + else if ( Core.AOS ) + rnd -= m_NecroTypes[index].Length; + + return Loot.RandomScroll( minCircle * 8, (maxCircle * 8) + 7, SpellbookType.Regular ); + } + + public Item Construct( bool inTokuno ) + { + try + { + Item item; + + if ( m_Type == typeof( BaseRanged ) ) + item = Loot.RandomRangedWeapon( inTokuno ); + else if ( m_Type == typeof( BaseWeapon ) ) + item = Loot.RandomWeapon( inTokuno ); + else if ( m_Type == typeof( BaseArmor ) ) + item = Loot.RandomArmorOrHat( inTokuno ); + else if ( m_Type == typeof( BaseShield ) ) + item = Loot.RandomShield(); + else if ( m_Type == typeof( BaseJewel ) ) + item = Core.AOS ? Loot.RandomJewelry() : Loot.RandomArmorOrShieldOrWeapon(); + else if ( m_Type == typeof( BaseInstrument ) ) + item = Loot.RandomInstrument(); + else if ( m_Type == typeof( Amber ) ) // gem + item = Loot.RandomGem(); + else if ( m_Type == typeof( ClumsyScroll ) ) // low scroll + item = RandomScroll( 0, 1, 3 ); + else if ( m_Type == typeof( ArchCureScroll ) ) // med scroll + item = RandomScroll( 1, 4, 7 ); + else if ( m_Type == typeof( SummonAirElementalScroll ) ) // high scroll + item = RandomScroll( 2, 8, 8 ); + else + item = Activator.CreateInstance( m_Type ) as Item; + + return item; + } + catch + { + } + + return null; + } + + public LootPackItem( Type type, int chance ) + { + m_Type = type; + m_Chance = chance; + } + } + + public class LootPackDice + { + private int m_Count, m_Sides, m_Bonus; + + public int Count + { + get{ return m_Count; } + set{ m_Count = value; } + } + + public int Sides + { + get{ return m_Sides; } + set{ m_Sides = value; } + } + + public int Bonus + { + get{ return m_Bonus; } + set{ m_Bonus = value; } + } + + public int Roll() + { + int v = m_Bonus; + + for ( int i = 0; i < m_Count; ++i ) + v += Utility.Random( 1, m_Sides ); + + return v; + } + + public LootPackDice( string str ) + { + int start = 0; + int index = str.IndexOf( 'd', start ); + + if ( index < start ) + return; + + m_Count = Utility.ToInt32( str.Substring( start, index-start ) ); + + bool negative; + + start = index + 1; + index = str.IndexOf( '+', start ); + + if ( negative = (index < start) ) + index = str.IndexOf( '-', start ); + + if ( index < start ) + index = str.Length; + + m_Sides = Utility.ToInt32( str.Substring( start, index-start ) ); + + if ( index == str.Length ) + return; + + start = index + 1; + index = str.Length; + + m_Bonus = Utility.ToInt32( str.Substring( start, index-start ) ); + + if ( negative ) + m_Bonus *= -1; + } + + public LootPackDice( int count, int sides, int bonus ) + { + m_Count = count; + m_Sides = sides; + m_Bonus = bonus; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/MapDefinitions.cs b/Scripts/Misc/MapDefinitions.cs new file mode 100644 index 000000000..20bb27550 --- /dev/null +++ b/Scripts/Misc/MapDefinitions.cs @@ -0,0 +1,48 @@ +using System; +using Server; + +namespace Server.Misc +{ + public class MapDefinitions + { + public static void Configure() + { + /* Here we configure all maps. Some notes: + * + * 1) The first 32 maps are reserved for core use. + * 2) Map 0x7F is reserved for core use. + * 3) Map 0xFF is reserved for core use. + * 4) Changing or removing any predefined maps may cause server instability. + */ + + RegisterMap( 0, 0, 0, 7168, 4096, 4, "Felucca", MapRules.FeluccaRules ); + RegisterMap( 1, 1, 0, 7168, 4096, 0, "Trammel", MapRules.TrammelRules ); + RegisterMap( 2, 2, 2, 2304, 1600, 1, "Ilshenar", MapRules.TrammelRules ); + RegisterMap( 3, 3, 3, 2560, 2048, 1, "Malas", MapRules.TrammelRules ); + RegisterMap( 4, 4, 4, 1448, 1448, 1, "Tokuno", MapRules.TrammelRules ); + + RegisterMap( 0x7F, 0x7F, 0x7F, Map.SectorSize, Map.SectorSize, 1, "Internal", MapRules.Internal ); + + /* Example of registering a custom map: + * RegisterMap( 32, 0, 0, 6144, 4096, 3, "Iceland", MapRules.FeluccaRules ); + * + * Defined: + * RegisterMap( , , , , , , , ); + * - : An unreserved unique index for this map + * - : An identification number used in client communications. For any visible maps, this value must be from 0-3 + * - : A file identification number. For any visible maps, this value must be 0, 2, 3, or 4 + * - , : Size of the map (in tiles) + * - : Reference name for the map, used in props gump, get/set commands, region loading, etc + * - : Rules and restrictions associated with the map. See documentation for details + */ + } + + public static void RegisterMap( int mapIndex, int mapID, int fileIndex, int width, int height, int season, string name, MapRules rules ) + { + Map newMap = new Map( mapID, mapIndex, fileIndex, width, height, season, name, rules ); + + Map.Maps[mapIndex] = newMap; + Map.AllMaps.Add( newMap ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/NameList.cs b/Scripts/Misc/NameList.cs new file mode 100644 index 000000000..51f9b6854 --- /dev/null +++ b/Scripts/Misc/NameList.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Xml; +using Server; + +namespace Server +{ + public class NameList + { + private string m_Type; + private string[] m_List; + + public string Type{ get{ return m_Type; } } + public string[] List{ get{ return m_List; } } + + public bool ContainsName( string name ) + { + for ( int i = 0; i < m_List.Length; i++ ) + if ( name == m_List[i] ) + return true; + + return false; + } + + public NameList( string type, XmlElement xml ) + { + m_Type = type; + m_List = xml.InnerText.Split( ',' ); + + for ( int i = 0; i < m_List.Length; ++i ) + m_List[i] = Utility.Intern( m_List[i] ); + } + + public string GetRandomName() + { + if ( m_List.Length > 0 ) + return m_List[Utility.Random( m_List.Length )].Trim(); + + return ""; + } + + public static NameList GetNameList( string type ) + { + NameList n = null; + m_Table.TryGetValue( type, out n ); + return n; + } + + public static string RandomName( string type ) + { + NameList list = GetNameList( type ); + + if ( list != null ) + return list.GetRandomName(); + + return ""; + } + + private static Dictionary m_Table; + + static NameList() + { + m_Table = new Dictionary( StringComparer.OrdinalIgnoreCase ); + + string filePath = Path.Combine( Core.BaseDirectory, "Data/names.xml" ); + + if ( !File.Exists( filePath ) ) + return; + + try + { + Load( filePath ); + } + catch ( Exception e ) + { + Console.WriteLine( "Warning: Exception caught loading name lists:" ); + Console.WriteLine( e ); + } + } + + private static void Load( string filePath ) + { + XmlDocument doc = new XmlDocument(); + doc.Load( filePath ); + + XmlElement root = doc["names"]; + + foreach ( XmlElement element in root.GetElementsByTagName( "namelist" ) ) + { + string type = element.GetAttribute( "type" ); + + if ( type == null || type == String.Empty ) + continue; + + try + { + NameList list = new NameList( type, element ); + + m_Table[type] = list; + } + catch + { + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/NameVerification.cs b/Scripts/Misc/NameVerification.cs new file mode 100644 index 000000000..d08f37e37 --- /dev/null +++ b/Scripts/Misc/NameVerification.cs @@ -0,0 +1,207 @@ +using System; +using Server; +using Server.Commands; + +namespace Server.Misc +{ + public class NameVerification + { + public static readonly char[] SpaceDashPeriodQuote = new char[] + { + ' ', '-', '.', '\'' + }; + + public static readonly char[] Empty = new char[0]; + + public static void Initialize() + { + CommandSystem.Register( "ValidateName", AccessLevel.Administrator, new CommandEventHandler( ValidateName_OnCommand ) ); + } + + [Usage( "ValidateName" )] + [Description( "Checks the result of NameValidation on the specified name." )] + public static void ValidateName_OnCommand( CommandEventArgs e ) + { + if ( Validate( e.ArgString, 2, 16, true, true, true, 1, SpaceDashPeriodQuote ) ) + e.Mobile.SendMessage( 0x59, "That name is considered valid." ); + else + e.Mobile.SendMessage( 0x22, "That name is considered invalid." ); + } + + public static bool Validate( string name, int minLength, int maxLength, bool allowLetters, bool allowDigits, bool noExceptionsAtStart, int maxExceptions, char[] exceptions ) + { + return Validate( name, minLength, maxLength, allowLetters, allowDigits, noExceptionsAtStart, maxExceptions, exceptions, m_Disallowed, m_StartDisallowed ); + } + + public static bool Validate( string name, int minLength, int maxLength, bool allowLetters, bool allowDigits, bool noExceptionsAtStart, int maxExceptions, char[] exceptions, string[] disallowed, string[] startDisallowed ) + { + if ( name.Length < minLength || name.Length > maxLength ) + return false; + + int exceptCount = 0; + + name = name.ToLower(); + + if ( !allowLetters || !allowDigits || (exceptions.Length > 0 && (noExceptionsAtStart || maxExceptions < int.MaxValue)) ) + { + for ( int i = 0; i < name.Length; ++i ) + { + char c = name[i]; + + if ( c >= 'a' && c <= 'z' ) + { + if ( !allowLetters ) + return false; + + exceptCount = 0; + } + else if ( c >= '0' && c <= '9' ) + { + if ( !allowDigits ) + return false; + + exceptCount = 0; + } + else + { + bool except = false; + + for ( int j = 0; !except && j < exceptions.Length; ++j ) + if ( c == exceptions[j] ) + except = true; + + if ( !except || (i == 0 && noExceptionsAtStart) ) + return false; + + if ( exceptCount++ == maxExceptions ) + return false; + } + } + } + + for ( int i = 0; i < disallowed.Length; ++i ) + { + int indexOf = name.IndexOf( disallowed[i] ); + + if ( indexOf == -1 ) + continue; + + bool badPrefix = ( indexOf == 0 ); + + for ( int j = 0; !badPrefix && j < exceptions.Length; ++j ) + badPrefix = ( name[indexOf - 1] == exceptions[j] ); + + if ( !badPrefix ) + continue; + + bool badSuffix = ( (indexOf + disallowed[i].Length) >= name.Length ); + + for ( int j = 0; !badSuffix && j < exceptions.Length; ++j ) + badSuffix = ( name[indexOf + disallowed[i].Length] == exceptions[j] ); + + if ( badSuffix ) + return false; + } + + for ( int i = 0; i < startDisallowed.Length; ++i ) + { + if ( name.StartsWith( startDisallowed[i] ) ) + return false; + } + + return true; + } + + public static string[] StartDisallowed { get { return m_StartDisallowed; } } + public static string[] Disallowed { get { return m_Disallowed; } } + + private static string[] m_StartDisallowed = new string[] + { + "seer", + "counselor", + "gm", + "admin", + "lady", + "lord" + }; + + private static string[] m_Disallowed = new string[] + { + "jigaboo", + "chigaboo", + "wop", + "kyke", + "kike", + "tit", + "spic", + "prick", + "piss", + "lezbo", + "lesbo", + "felatio", + "dyke", + "dildo", + "chinc", + "chink", + "cunnilingus", + "cum", + "cocksucker", + "cock", + "clitoris", + "clit", + "ass", + "hitler", + "penis", + "nigga", + "nigger", + "klit", + "kunt", + "jiz", + "jism", + "jerkoff", + "jackoff", + "goddamn", + "fag", + "blowjob", + "bitch", + "asshole", + "dick", + "pussy", + "snatch", + "cunt", + "twat", + "shit", + "fuck", + "tailor", + "smith", + "scholar", + "rogue", + "novice", + "neophyte", + "merchant", + "medium", + "master", + "mage", + "lb", + "journeyman", + "grandmaster", + "fisherman", + "expert", + "chef", + "carpenter", + "british", + "blackthorne", + "blackthorn", + "beggar", + "archer", + "apprentice", + "adept", + "gamemaster", + "frozen", + "squelched", + "invulnerable", + "osi", + "origin" + }; + } +} \ No newline at end of file diff --git a/Scripts/Misc/Notoriety.cs b/Scripts/Misc/Notoriety.cs new file mode 100644 index 000000000..37235707a --- /dev/null +++ b/Scripts/Misc/Notoriety.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Guilds; +using Server.Multis; +using Server.Mobiles; +using Server.Engines.PartySystem; +using Server.Factions; + +namespace Server.Misc +{ + public class NotorietyHandlers + { + public static void Initialize() + { + Notoriety.Hues[Notoriety.Innocent] = 0x59; + Notoriety.Hues[Notoriety.Ally] = 0x3F; + Notoriety.Hues[Notoriety.CanBeAttacked] = 0x3B2; + Notoriety.Hues[Notoriety.Criminal] = 0x3B2; + Notoriety.Hues[Notoriety.Enemy] = 0x90; + Notoriety.Hues[Notoriety.Murderer] = 0x22; + Notoriety.Hues[Notoriety.Invulnerable] = 0x35; + + Notoriety.Handler = new NotorietyHandler( MobileNotoriety ); + + Mobile.AllowBeneficialHandler = new AllowBeneficialHandler( Mobile_AllowBeneficial ); + Mobile.AllowHarmfulHandler = new AllowHarmfulHandler( Mobile_AllowHarmful ); + } + + private enum GuildStatus { None, Peaceful, Waring } + + private static GuildStatus GetGuildStatus( Mobile m ) + { + if( m.Guild == null ) + return GuildStatus.None; + else if( ((Guild)m.Guild).Enemies.Count == 0 && m.Guild.Type == GuildType.Regular ) + return GuildStatus.Peaceful; + + return GuildStatus.Waring; + } + + private static bool CheckBeneficialStatus( GuildStatus from, GuildStatus target ) + { + if( from == GuildStatus.Waring || target == GuildStatus.Waring ) + return false; + + return true; + } + + /*private static bool CheckHarmfulStatus( GuildStatus from, GuildStatus target ) + { + if ( from == GuildStatus.Waring && target == GuildStatus.Waring ) + return true; + + return false; + }*/ + + public static bool Mobile_AllowBeneficial( Mobile from, Mobile target ) + { + if( from == null || target == null || from.AccessLevel > AccessLevel.Player || target.AccessLevel > AccessLevel.Player ) + return true; + + #region Factions + Faction targetFaction = Faction.Find( target, true ); + + if( targetFaction != null ) + { + if( Faction.Find( from, true ) != targetFaction ) + return false; + } + #endregion + + Map map = from.Map; + + if( map != null && (map.Rules & MapRules.BeneficialRestrictions) == 0 ) + return true; // In felucca, anything goes + + if( !from.Player ) + return true; // NPCs have no restrictions + + if( target is BaseCreature && !((BaseCreature)target).Controlled ) + return false; // Players cannot heal uncontrolled mobiles + + if( from is PlayerMobile && ((PlayerMobile)from).Young && (!(target is PlayerMobile) || !((PlayerMobile)target).Young) ) + return false; // Young players cannot perform beneficial actions towards older players + + Guild fromGuild = from.Guild as Guild; + Guild targetGuild = target.Guild as Guild; + + if( fromGuild != null && targetGuild != null && (targetGuild == fromGuild || fromGuild.IsAlly( targetGuild )) ) + return true; // Guild members can be beneficial + + return CheckBeneficialStatus( GetGuildStatus( from ), GetGuildStatus( target ) ); + } + + public static bool Mobile_AllowHarmful( Mobile from, Mobile target ) + { + if( from == null || target == null || from.AccessLevel > AccessLevel.Player || target.AccessLevel > AccessLevel.Player ) + return true; + + Map map = from.Map; + + if( map != null && (map.Rules & MapRules.HarmfulRestrictions) == 0 ) + return true; // In felucca, anything goes + + if( !from.Player && !(from is BaseCreature && (((BaseCreature)from).Controlled || ((BaseCreature)from).Summoned)) ) + { + if( !CheckAggressor( from.Aggressors, target ) && !CheckAggressed( from.Aggressed, target ) && target is PlayerMobile && ((PlayerMobile)target).CheckYoungProtection( from ) ) + return false; + + return true; // Uncontrolled NPCs are only restricted by the young system + } + + Guild fromGuild = GetGuildFor( from.Guild as Guild, from ); + Guild targetGuild = GetGuildFor( target.Guild as Guild, target ); + + if( fromGuild != null && targetGuild != null && (fromGuild == targetGuild || fromGuild.IsAlly( targetGuild ) || fromGuild.IsEnemy( targetGuild )) ) + return true; // Guild allies or enemies can be harmful + + if( target is BaseCreature && (((BaseCreature)target).Controlled || (((BaseCreature)target).Summoned && from != ((BaseCreature)target).SummonMaster)) ) + return false; // Cannot harm other controlled mobiles + + if( target.Player ) + return false; // Cannot harm other players + + if( !(target is BaseCreature && ((BaseCreature)target).InitialInnocent) ) + { + if( Notoriety.Compute( from, target ) == Notoriety.Innocent ) + return false; // Cannot harm innocent mobiles + } + + return true; + } + + public static Guild GetGuildFor( Guild def, Mobile m ) + { + Guild g = def; + + BaseCreature c = m as BaseCreature; + + if( c != null && c.Controlled && c.ControlMaster != null ) + { + c.DisplayGuildTitle = false; + + if( c.Map != Map.Internal && (Core.AOS || Guild.NewGuildSystem || c.ControlOrder == OrderType.Attack || c.ControlOrder == OrderType.Guard) ) + g = (Guild)(c.Guild = c.ControlMaster.Guild); + else if( c.Map == Map.Internal || c.ControlMaster.Guild == null ) + g = (Guild)(c.Guild = null); + } + + return g; + } + + public static int CorpseNotoriety( Mobile source, Corpse target ) + { + if( target.AccessLevel > AccessLevel.Player ) + return Notoriety.CanBeAttacked; + + Body body = (Body)target.Amount; + + BaseCreature cretOwner = target.Owner as BaseCreature; + + if( cretOwner != null ) + { + Guild sourceGuild = GetGuildFor( source.Guild as Guild, source ); + Guild targetGuild = GetGuildFor( target.Guild as Guild, target.Owner ); + + if( sourceGuild != null && targetGuild != null ) + { + if( sourceGuild == targetGuild || sourceGuild.IsAlly( targetGuild ) ) + return Notoriety.Ally; + else if( sourceGuild.IsEnemy( targetGuild ) ) + return Notoriety.Enemy; + } + + Faction srcFaction = Faction.Find( source, true, true ); + Faction trgFaction = Faction.Find( target.Owner, true, true ); + + if( srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet ) + return Notoriety.Enemy; + + if( CheckHouseFlag( source, target.Owner, target.Location, target.Map ) ) + return Notoriety.CanBeAttacked; + + int actual = Notoriety.CanBeAttacked; + + if( target.Kills >= 5 || (body.IsMonster && IsSummoned( target.Owner as BaseCreature )) || (target.Owner is BaseCreature && (((BaseCreature)target.Owner).AlwaysMurderer || ((BaseCreature)target.Owner).IsAnimatedDead)) ) + actual = Notoriety.Murderer; + + if( DateTime.Now >= (target.TimeOfDeath + Corpse.MonsterLootRightSacrifice) ) + return actual; + + Party sourceParty = Party.Get( source ); + + List list = target.Aggressors; + + for( int i = 0; i < list.Count; ++i ) + { + if( list[i] == source || (sourceParty != null && Party.Get( list[i] ) == sourceParty) ) + return actual; + } + + return Notoriety.Innocent; + } + else + { + if( target.Kills >= 5 || (body.IsMonster && IsSummoned( target.Owner as BaseCreature )) || (target.Owner is BaseCreature && (((BaseCreature)target.Owner).AlwaysMurderer || ((BaseCreature)target.Owner).IsAnimatedDead)) ) + return Notoriety.Murderer; + + if( target.Criminal ) + return Notoriety.Criminal; + + Guild sourceGuild = GetGuildFor( source.Guild as Guild, source ); + Guild targetGuild = GetGuildFor( target.Guild as Guild, target.Owner ); + + if( sourceGuild != null && targetGuild != null ) + { + if( sourceGuild == targetGuild || sourceGuild.IsAlly( targetGuild ) ) + return Notoriety.Ally; + else if( sourceGuild.IsEnemy( targetGuild ) ) + return Notoriety.Enemy; + } + + Faction srcFaction = Faction.Find( source, true, true ); + Faction trgFaction = Faction.Find( target.Owner, true, true ); + + if( srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet ) + { + List secondList = target.Aggressors; + + for( int i = 0; i < secondList.Count; ++i ) + { + if( secondList[i] == source || secondList[i] is BaseFactionGuard ) + return Notoriety.Enemy; + } + } + + if( target.Owner != null && target.Owner is BaseCreature && ((BaseCreature)target.Owner).AlwaysAttackable ) + return Notoriety.CanBeAttacked; + + if( CheckHouseFlag( source, target.Owner, target.Location, target.Map ) ) + return Notoriety.CanBeAttacked; + + if( !(target.Owner is PlayerMobile) && !IsPet( target.Owner as BaseCreature ) ) + return Notoriety.CanBeAttacked; + + List list = target.Aggressors; + + for( int i = 0; i < list.Count; ++i ) + { + if( list[i] == source ) + return Notoriety.CanBeAttacked; + } + + return Notoriety.Innocent; + } + } + + public static int MobileNotoriety( Mobile source, Mobile target ) + { + if( Core.AOS && (target.Blessed || (target is BaseVendor && ((BaseVendor)target).IsInvulnerable) || target is PlayerVendor || target is TownCrier) ) + return Notoriety.Invulnerable; + + if( target.AccessLevel > AccessLevel.Player ) + return Notoriety.CanBeAttacked; + + if( source.Player && !target.Player && source is PlayerMobile && target is BaseCreature ) + { + BaseCreature bc = (BaseCreature)target; + + if( !bc.Summoned && !bc.Controlled && ((PlayerMobile)source).EnemyOfOneType == target.GetType() ) + return Notoriety.Enemy; + } + + if( target.Kills >= 5 || (target.Body.IsMonster && IsSummoned( target as BaseCreature ) && !(target is BaseFamiliar) && !(target is Golem)) || (target is BaseCreature && (((BaseCreature)target).AlwaysMurderer || ((BaseCreature)target).IsAnimatedDead)) ) + return Notoriety.Murderer; + + if( target.Criminal ) + return Notoriety.Criminal; + + Guild sourceGuild = GetGuildFor( source.Guild as Guild, source ); + Guild targetGuild = GetGuildFor( target.Guild as Guild, target ); + + if( sourceGuild != null && targetGuild != null ) + { + if( sourceGuild == targetGuild || sourceGuild.IsAlly( targetGuild ) ) + return Notoriety.Ally; + else if( sourceGuild.IsEnemy( targetGuild ) ) + return Notoriety.Enemy; + } + + Faction srcFaction = Faction.Find( source, true, true ); + Faction trgFaction = Faction.Find( target, true, true ); + + if( srcFaction != null && trgFaction != null && srcFaction != trgFaction && source.Map == Faction.Facet ) + return Notoriety.Enemy; + + if( SkillHandlers.Stealing.ClassicMode && target is PlayerMobile && ((PlayerMobile)target).PermaFlags.Contains( source ) ) + return Notoriety.CanBeAttacked; + + if( target is BaseCreature && ((BaseCreature)target).AlwaysAttackable ) + return Notoriety.CanBeAttacked; + + if( CheckHouseFlag( source, target, target.Location, target.Map ) ) + return Notoriety.CanBeAttacked; + + if( !(target is BaseCreature && ((BaseCreature)target).InitialInnocent) ) + { + if( !target.Body.IsHuman && !target.Body.IsGhost && !IsPet( target as BaseCreature ) && !Server.Spells.Necromancy.TransformationSpell.UnderTransformation( target ) ) + return Notoriety.CanBeAttacked; + } + + if( CheckAggressor( source.Aggressors, target ) ) + return Notoriety.CanBeAttacked; + + if( CheckAggressed( source.Aggressed, target ) ) + return Notoriety.CanBeAttacked; + + if( target is BaseCreature ) + { + BaseCreature bc = (BaseCreature)target; + + if( bc.Controlled && bc.ControlOrder == OrderType.Guard && bc.ControlTarget == source ) + return Notoriety.CanBeAttacked; + } + + if( source is BaseCreature ) + { + BaseCreature bc = (BaseCreature)source; + + Mobile master = bc.GetMaster(); + if( master != null && CheckAggressor( master.Aggressors, target ) ) + return Notoriety.CanBeAttacked; + } + + return Notoriety.Innocent; + } + + public static bool CheckHouseFlag( Mobile from, Mobile m, Point3D p, Map map ) + { + BaseHouse house = BaseHouse.FindHouseAt( p, map, 16 ); + + if( house == null || house.Public || !house.IsFriend( from ) ) + return false; + + if( m != null && house.IsFriend( m ) ) + return false; + + BaseCreature c = m as BaseCreature; + + if( c != null && !c.Deleted && c.Controlled && c.ControlMaster != null ) + return !house.IsFriend( c.ControlMaster ); + + return true; + } + + public static bool IsPet( BaseCreature c ) + { + return (c != null && c.Controlled); + } + + public static bool IsSummoned( BaseCreature c ) + { + return (c != null && /*c.Controlled &&*/ c.Summoned); + } + + public static bool CheckAggressor( List list, Mobile target ) + { + for( int i = 0; i < list.Count; ++i ) + if( list[i].Attacker == target ) + return true; + + return false; + } + + public static bool CheckAggressed( List list, Mobile target ) + { + for( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if( !info.CriminalAggression && info.Defender == target ) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/OreInfo.cs b/Scripts/Misc/OreInfo.cs new file mode 100644 index 000000000..47143426f --- /dev/null +++ b/Scripts/Misc/OreInfo.cs @@ -0,0 +1,594 @@ +using System; +using System.Collections; + +namespace Server.Items +{ + public enum CraftResource + { + None = 0, + Iron = 1, + DullCopper, + ShadowIron, + Copper, + Bronze, + Gold, + Agapite, + Verite, + Valorite, + + RegularLeather = 101, + SpinedLeather, + HornedLeather, + BarbedLeather, + + RedScales = 201, + YellowScales, + BlackScales, + GreenScales, + WhiteScales, + BlueScales + } + + public enum CraftResourceType + { + None, + Metal, + Leather, + Scales + } + + public class CraftAttributeInfo + { + private int m_WeaponFireDamage; + private int m_WeaponColdDamage; + private int m_WeaponPoisonDamage; + private int m_WeaponEnergyDamage; + private int m_WeaponDurability; + private int m_WeaponLuck; + private int m_WeaponGoldIncrease; + private int m_WeaponLowerRequirements; + + private int m_ArmorPhysicalResist; + private int m_ArmorFireResist; + private int m_ArmorColdResist; + private int m_ArmorPoisonResist; + private int m_ArmorEnergyResist; + private int m_ArmorDurability; + private int m_ArmorLuck; + private int m_ArmorGoldIncrease; + private int m_ArmorLowerRequirements; + + private int m_RunicMinAttributes; + private int m_RunicMaxAttributes; + private int m_RunicMinIntensity; + private int m_RunicMaxIntensity; + + public int WeaponFireDamage{ get{ return m_WeaponFireDamage; } set{ m_WeaponFireDamage = value; } } + public int WeaponColdDamage{ get{ return m_WeaponColdDamage; } set{ m_WeaponColdDamage = value; } } + public int WeaponPoisonDamage{ get{ return m_WeaponPoisonDamage; } set{ m_WeaponPoisonDamage = value; } } + public int WeaponEnergyDamage{ get{ return m_WeaponEnergyDamage; } set{ m_WeaponEnergyDamage = value; } } + public int WeaponDurability{ get{ return m_WeaponDurability; } set{ m_WeaponDurability = value; } } + public int WeaponLuck{ get{ return m_WeaponLuck; } set{ m_WeaponLuck = value; } } + public int WeaponGoldIncrease{ get{ return m_WeaponGoldIncrease; } set{ m_WeaponGoldIncrease = value; } } + public int WeaponLowerRequirements{ get{ return m_WeaponLowerRequirements; } set{ m_WeaponLowerRequirements = value; } } + + public int ArmorPhysicalResist{ get{ return m_ArmorPhysicalResist; } set{ m_ArmorPhysicalResist = value; } } + public int ArmorFireResist{ get{ return m_ArmorFireResist; } set{ m_ArmorFireResist = value; } } + public int ArmorColdResist{ get{ return m_ArmorColdResist; } set{ m_ArmorColdResist = value; } } + public int ArmorPoisonResist{ get{ return m_ArmorPoisonResist; } set{ m_ArmorPoisonResist = value; } } + public int ArmorEnergyResist{ get{ return m_ArmorEnergyResist; } set{ m_ArmorEnergyResist = value; } } + public int ArmorDurability{ get{ return m_ArmorDurability; } set{ m_ArmorDurability = value; } } + public int ArmorLuck{ get{ return m_ArmorLuck; } set{ m_ArmorLuck = value; } } + public int ArmorGoldIncrease{ get{ return m_ArmorGoldIncrease; } set{ m_ArmorGoldIncrease = value; } } + public int ArmorLowerRequirements{ get{ return m_ArmorLowerRequirements; } set{ m_ArmorLowerRequirements = value; } } + + public int RunicMinAttributes{ get{ return m_RunicMinAttributes; } set{ m_RunicMinAttributes = value; } } + public int RunicMaxAttributes{ get{ return m_RunicMaxAttributes; } set{ m_RunicMaxAttributes = value; } } + public int RunicMinIntensity{ get{ return m_RunicMinIntensity; } set{ m_RunicMinIntensity = value; } } + public int RunicMaxIntensity{ get{ return m_RunicMaxIntensity; } set{ m_RunicMaxIntensity = value; } } + + public CraftAttributeInfo() + { + } + + public static readonly CraftAttributeInfo Blank; + public static readonly CraftAttributeInfo DullCopper, ShadowIron, Copper, Bronze, Golden, Agapite, Verite, Valorite; + public static readonly CraftAttributeInfo Spined, Horned, Barbed; + public static readonly CraftAttributeInfo RedScales, YellowScales, BlackScales, GreenScales, WhiteScales, BlueScales; + + static CraftAttributeInfo() + { + Blank = new CraftAttributeInfo(); + + CraftAttributeInfo dullCopper = DullCopper = new CraftAttributeInfo(); + + dullCopper.ArmorPhysicalResist = 6; + dullCopper.ArmorDurability = 50; + dullCopper.ArmorLowerRequirements = 20; + dullCopper.WeaponDurability = 100; + dullCopper.WeaponLowerRequirements = 50; + dullCopper.RunicMinAttributes = 1; + dullCopper.RunicMaxAttributes = 2; + dullCopper.RunicMinIntensity = 10; + dullCopper.RunicMaxIntensity = 35; + + CraftAttributeInfo shadowIron = ShadowIron = new CraftAttributeInfo(); + + shadowIron.ArmorPhysicalResist = 2; + shadowIron.ArmorFireResist = 1; + shadowIron.ArmorEnergyResist = 5; + shadowIron.ArmorDurability = 100; + shadowIron.WeaponColdDamage = 20; + shadowIron.WeaponDurability = 50; + shadowIron.RunicMinAttributes = 2; + shadowIron.RunicMaxAttributes = 2; + shadowIron.RunicMinIntensity = 20; + shadowIron.RunicMaxIntensity = 45; + + CraftAttributeInfo copper = Copper = new CraftAttributeInfo(); + + copper.ArmorPhysicalResist = 1; + copper.ArmorFireResist = 1; + copper.ArmorPoisonResist = 5; + copper.ArmorEnergyResist = 2; + copper.WeaponPoisonDamage = 10; + copper.WeaponEnergyDamage = 20; + copper.RunicMinAttributes = 2; + copper.RunicMaxAttributes = 3; + copper.RunicMinIntensity = 25; + copper.RunicMaxIntensity = 50; + + CraftAttributeInfo bronze = Bronze = new CraftAttributeInfo(); + + bronze.ArmorPhysicalResist = 3; + bronze.ArmorColdResist = 5; + bronze.ArmorPoisonResist = 1; + bronze.ArmorEnergyResist = 1; + bronze.WeaponFireDamage = 40; + bronze.RunicMinAttributes = 3; + bronze.RunicMaxAttributes = 3; + bronze.RunicMinIntensity = 30; + bronze.RunicMaxIntensity = 65; + + CraftAttributeInfo golden = Golden = new CraftAttributeInfo(); + + golden.ArmorPhysicalResist = 1; + golden.ArmorFireResist = 1; + golden.ArmorColdResist = 2; + golden.ArmorEnergyResist = 2; + golden.ArmorLuck = 40; + golden.ArmorLowerRequirements = 30; + golden.WeaponLuck = 40; + golden.WeaponLowerRequirements = 50; + golden.RunicMinAttributes = 3; + golden.RunicMaxAttributes = 4; + golden.RunicMinIntensity = 35; + golden.RunicMaxIntensity = 75; + + CraftAttributeInfo agapite = Agapite = new CraftAttributeInfo(); + + agapite.ArmorPhysicalResist = 2; + agapite.ArmorFireResist = 3; + agapite.ArmorColdResist = 2; + agapite.ArmorPoisonResist = 2; + agapite.ArmorEnergyResist = 2; + agapite.WeaponColdDamage = 30; + agapite.WeaponEnergyDamage = 20; + agapite.RunicMinAttributes = 4; + agapite.RunicMaxAttributes = 4; + agapite.RunicMinIntensity = 40; + agapite.RunicMaxIntensity = 80; + + CraftAttributeInfo verite = Verite = new CraftAttributeInfo(); + + verite.ArmorPhysicalResist = 3; + verite.ArmorFireResist = 3; + verite.ArmorColdResist = 2; + verite.ArmorPoisonResist = 3; + verite.ArmorEnergyResist = 1; + verite.WeaponPoisonDamage = 40; + verite.WeaponEnergyDamage = 20; + verite.RunicMinAttributes = 4; + verite.RunicMaxAttributes = 5; + verite.RunicMinIntensity = 45; + verite.RunicMaxIntensity = 90; + + CraftAttributeInfo valorite = Valorite = new CraftAttributeInfo(); + + valorite.ArmorPhysicalResist = 4; + valorite.ArmorColdResist = 3; + valorite.ArmorPoisonResist = 3; + valorite.ArmorEnergyResist = 3; + valorite.ArmorDurability = 50; + valorite.WeaponFireDamage = 10; + valorite.WeaponColdDamage = 20; + valorite.WeaponPoisonDamage = 10; + valorite.WeaponEnergyDamage = 20; + valorite.RunicMinAttributes = 5; + valorite.RunicMaxAttributes = 5; + valorite.RunicMinIntensity = 50; + valorite.RunicMaxIntensity = 100; + + CraftAttributeInfo spined = Spined = new CraftAttributeInfo(); + + spined.ArmorPhysicalResist = 5; + spined.ArmorLuck = 40; + spined.RunicMinAttributes = 1; + spined.RunicMaxAttributes = 3; + spined.RunicMinIntensity = 20; + spined.RunicMaxIntensity = 40; + + CraftAttributeInfo horned = Horned = new CraftAttributeInfo(); + + horned.ArmorPhysicalResist = 2; + horned.ArmorFireResist = 3; + horned.ArmorColdResist = 2; + horned.ArmorPoisonResist = 2; + horned.ArmorEnergyResist = 2; + horned.RunicMinAttributes = 3; + horned.RunicMaxAttributes = 4; + horned.RunicMinIntensity = 30; + horned.RunicMaxIntensity = 70; + + CraftAttributeInfo barbed = Barbed = new CraftAttributeInfo(); + + barbed.ArmorPhysicalResist = 2; + barbed.ArmorFireResist = 1; + barbed.ArmorColdResist = 2; + barbed.ArmorPoisonResist = 3; + barbed.ArmorEnergyResist = 4; + barbed.RunicMinAttributes = 4; + barbed.RunicMaxAttributes = 5; + barbed.RunicMinIntensity = 40; + barbed.RunicMaxIntensity = 100; + + CraftAttributeInfo red = RedScales = new CraftAttributeInfo(); + + red.ArmorFireResist = 10; + red.ArmorColdResist = -3; + + CraftAttributeInfo yellow = YellowScales = new CraftAttributeInfo(); + + yellow.ArmorPhysicalResist = -3; + yellow.ArmorLuck = 20; + + CraftAttributeInfo black = BlackScales = new CraftAttributeInfo(); + + black.ArmorPhysicalResist = 10; + black.ArmorEnergyResist = -3; + + CraftAttributeInfo green = GreenScales = new CraftAttributeInfo(); + + green.ArmorFireResist = -3; + green.ArmorPoisonResist = 10; + + CraftAttributeInfo white = WhiteScales = new CraftAttributeInfo(); + + white.ArmorPhysicalResist = -3; + white.ArmorColdResist = 10; + + CraftAttributeInfo blue = BlueScales = new CraftAttributeInfo(); + + blue.ArmorPoisonResist = -3; + blue.ArmorEnergyResist = 10; + } + } + + public class CraftResourceInfo + { + private int m_Hue; + private int m_Number; + private string m_Name; + private CraftAttributeInfo m_AttributeInfo; + private CraftResource m_Resource; + private Type[] m_ResourceTypes; + + public int Hue{ get{ return m_Hue; } } + public int Number{ get{ return m_Number; } } + public string Name{ get{ return m_Name; } } + public CraftAttributeInfo AttributeInfo{ get{ return m_AttributeInfo; } } + public CraftResource Resource{ get{ return m_Resource; } } + public Type[] ResourceTypes{ get{ return m_ResourceTypes; } } + + public CraftResourceInfo( int hue, int number, string name, CraftAttributeInfo attributeInfo, CraftResource resource, params Type[] resourceTypes ) + { + m_Hue = hue; + m_Number = number; + m_Name = name; + m_AttributeInfo = attributeInfo; + m_Resource = resource; + m_ResourceTypes = resourceTypes; + + for ( int i = 0; i < resourceTypes.Length; ++i ) + CraftResources.RegisterType( resourceTypes[i], resource ); + } + } + + public class CraftResources + { + private static CraftResourceInfo[] m_MetalInfo = new CraftResourceInfo[] + { + new CraftResourceInfo( 0x000, 1053109, "Iron", CraftAttributeInfo.Blank, CraftResource.Iron, typeof( IronIngot ), typeof( IronOre ), typeof( Granite ) ), + new CraftResourceInfo( 0x973, 1053108, "Dull Copper", CraftAttributeInfo.DullCopper, CraftResource.DullCopper, typeof( DullCopperIngot ), typeof( DullCopperOre ), typeof( DullCopperGranite ) ), + new CraftResourceInfo( 0x966, 1053107, "Shadow Iron", CraftAttributeInfo.ShadowIron, CraftResource.ShadowIron, typeof( ShadowIronIngot ), typeof( ShadowIronOre ), typeof( ShadowIronGranite ) ), + new CraftResourceInfo( 0x96D, 1053106, "Copper", CraftAttributeInfo.Copper, CraftResource.Copper, typeof( CopperIngot ), typeof( CopperOre ), typeof( CopperGranite ) ), + new CraftResourceInfo( 0x972, 1053105, "Bronze", CraftAttributeInfo.Bronze, CraftResource.Bronze, typeof( BronzeIngot ), typeof( BronzeOre ), typeof( BronzeGranite ) ), + new CraftResourceInfo( 0x8A5, 1053104, "Gold", CraftAttributeInfo.Golden, CraftResource.Gold, typeof( GoldIngot ), typeof( GoldOre ), typeof( GoldGranite ) ), + new CraftResourceInfo( 0x979, 1053103, "Agapite", CraftAttributeInfo.Agapite, CraftResource.Agapite, typeof( AgapiteIngot ), typeof( AgapiteOre ), typeof( AgapiteGranite ) ), + new CraftResourceInfo( 0x89F, 1053102, "Verite", CraftAttributeInfo.Verite, CraftResource.Verite, typeof( VeriteIngot ), typeof( VeriteOre ), typeof( VeriteGranite ) ), + new CraftResourceInfo( 0x8AB, 1053101, "Valorite", CraftAttributeInfo.Valorite, CraftResource.Valorite, typeof( ValoriteIngot ), typeof( ValoriteOre ), typeof( ValoriteGranite ) ), + }; + + private static CraftResourceInfo[] m_ScaleInfo = new CraftResourceInfo[] + { + new CraftResourceInfo( 0x66D, 1053129, "Red Scales", CraftAttributeInfo.RedScales, CraftResource.RedScales, typeof( RedScales ) ), + new CraftResourceInfo( 0x8A8, 1053130, "Yellow Scales", CraftAttributeInfo.YellowScales, CraftResource.YellowScales, typeof( YellowScales ) ), + new CraftResourceInfo( 0x455, 1053131, "Black Scales", CraftAttributeInfo.BlackScales, CraftResource.BlackScales, typeof( BlackScales ) ), + new CraftResourceInfo( 0x851, 1053132, "Green Scales", CraftAttributeInfo.GreenScales, CraftResource.GreenScales, typeof( GreenScales ) ), + new CraftResourceInfo( 0x8FD, 1053133, "White Scales", CraftAttributeInfo.WhiteScales, CraftResource.WhiteScales, typeof( WhiteScales ) ), + new CraftResourceInfo( 0x8B0, 1053134, "Blue Scales", CraftAttributeInfo.BlueScales, CraftResource.BlueScales, typeof( BlueScales ) ) + }; + + private static CraftResourceInfo[] m_LeatherInfo = new CraftResourceInfo[] + { + new CraftResourceInfo( 0x000, 1049353, "Normal", CraftAttributeInfo.Blank, CraftResource.RegularLeather, typeof( Leather ), typeof( Hides ) ), + new CraftResourceInfo( 0x283, 1049354, "Spined", CraftAttributeInfo.Spined, CraftResource.SpinedLeather, typeof( SpinedLeather ), typeof( SpinedHides ) ), + new CraftResourceInfo( 0x227, 1049355, "Horned", CraftAttributeInfo.Horned, CraftResource.HornedLeather, typeof( HornedLeather ), typeof( HornedHides ) ), + new CraftResourceInfo( 0x1C1, 1049356, "Barbed", CraftAttributeInfo.Barbed, CraftResource.BarbedLeather, typeof( BarbedLeather ), typeof( BarbedHides ) ) + }; + + private static CraftResourceInfo[] m_AOSLeatherInfo = new CraftResourceInfo[] + { + new CraftResourceInfo( 0x000, 1049353, "Normal", CraftAttributeInfo.Blank, CraftResource.RegularLeather, typeof( Leather ), typeof( Hides ) ), + new CraftResourceInfo( 0x8AC, 1049354, "Spined", CraftAttributeInfo.Spined, CraftResource.SpinedLeather, typeof( SpinedLeather ), typeof( SpinedHides ) ), + new CraftResourceInfo( 0x845, 1049355, "Horned", CraftAttributeInfo.Horned, CraftResource.HornedLeather, typeof( HornedLeather ), typeof( HornedHides ) ), + new CraftResourceInfo( 0x851, 1049356, "Barbed", CraftAttributeInfo.Barbed, CraftResource.BarbedLeather, typeof( BarbedLeather ), typeof( BarbedHides ) ), + }; + + /// + /// Returns true if '' is None, Iron, or RegularLeather. False if otherwise. + /// + public static bool IsStandard( CraftResource resource ) + { + return ( resource == CraftResource.None || resource == CraftResource.Iron || resource == CraftResource.RegularLeather ); + } + + private static Hashtable m_TypeTable; + + /// + /// Registers that '' uses '' so that it can later be queried by + /// + public static void RegisterType( Type resourceType, CraftResource resource ) + { + if ( m_TypeTable == null ) + m_TypeTable = new Hashtable(); + + m_TypeTable[resourceType] = resource; + } + + /// + /// Returns the value for which '' uses -or- CraftResource.None if an unregistered type was specified. + /// + public static CraftResource GetFromType( Type resourceType ) + { + if ( m_TypeTable == null ) + return CraftResource.None; + + object obj = m_TypeTable[resourceType]; + + if ( !(obj is CraftResource) ) + return CraftResource.None; + + return (CraftResource)obj; + } + + /// + /// Returns a instance describing '' -or- null if an invalid resource was specified. + /// + public static CraftResourceInfo GetInfo( CraftResource resource ) + { + CraftResourceInfo[] list = null; + + switch ( GetType( resource ) ) + { + case CraftResourceType.Metal: list = m_MetalInfo; break; + case CraftResourceType.Leather: list = Core.AOS ? m_AOSLeatherInfo : m_LeatherInfo; break; + case CraftResourceType.Scales: list = m_ScaleInfo; break; + } + + if ( list != null ) + { + int index = GetIndex( resource ); + + if ( index >= 0 && index < list.Length ) + return list[index]; + } + + return null; + } + + /// + /// Returns a value indiciating the type of ''. + /// + public static CraftResourceType GetType( CraftResource resource ) + { + if ( resource >= CraftResource.Iron && resource <= CraftResource.Valorite ) + return CraftResourceType.Metal; + + if ( resource >= CraftResource.RegularLeather && resource <= CraftResource.BarbedLeather ) + return CraftResourceType.Leather; + + if ( resource >= CraftResource.RedScales && resource <= CraftResource.BlueScales ) + return CraftResourceType.Scales; + + return CraftResourceType.None; + } + + /// + /// Returns the first in the series of resources for which '' belongs. + /// + public static CraftResource GetStart( CraftResource resource ) + { + switch ( GetType( resource ) ) + { + case CraftResourceType.Metal: return CraftResource.Iron; + case CraftResourceType.Leather: return CraftResource.RegularLeather; + case CraftResourceType.Scales: return CraftResource.RedScales; + } + + return CraftResource.None; + } + + /// + /// Returns the index of '' in the seriest of resources for which it belongs. + /// + public static int GetIndex( CraftResource resource ) + { + CraftResource start = GetStart( resource ); + + if ( start == CraftResource.None ) + return 0; + + return (int)(resource - start); + } + + /// + /// Returns the property of '' -or- 0 if an invalid resource was specified. + /// + public static int GetLocalizationNumber( CraftResource resource ) + { + CraftResourceInfo info = GetInfo( resource ); + + return ( info == null ? 0 : info.Number ); + } + + /// + /// Returns the property of '' -or- 0 if an invalid resource was specified. + /// + public static int GetHue( CraftResource resource ) + { + CraftResourceInfo info = GetInfo( resource ); + + return ( info == null ? 0 : info.Hue ); + } + + /// + /// Returns the property of '' -or- an empty string if the resource specified was invalid. + /// + public static string GetName( CraftResource resource ) + { + CraftResourceInfo info = GetInfo( resource ); + + return ( info == null ? String.Empty : info.Name ); + } + + /// + /// Returns the value which represents '' -or- CraftResource.None if unable to convert. + /// + public static CraftResource GetFromOreInfo( OreInfo info ) + { + if ( info.Name.IndexOf( "Spined" ) >= 0 ) + return CraftResource.SpinedLeather; + else if ( info.Name.IndexOf( "Horned" ) >= 0 ) + return CraftResource.HornedLeather; + else if ( info.Name.IndexOf( "Barbed" ) >= 0 ) + return CraftResource.BarbedLeather; + else if ( info.Name.IndexOf( "Leather" ) >= 0 ) + return CraftResource.RegularLeather; + + if ( info.Level == 0 ) + return CraftResource.Iron; + else if ( info.Level == 1 ) + return CraftResource.DullCopper; + else if ( info.Level == 2 ) + return CraftResource.ShadowIron; + else if ( info.Level == 3 ) + return CraftResource.Copper; + else if ( info.Level == 4 ) + return CraftResource.Bronze; + else if ( info.Level == 5 ) + return CraftResource.Gold; + else if ( info.Level == 6 ) + return CraftResource.Agapite; + else if ( info.Level == 7 ) + return CraftResource.Verite; + else if ( info.Level == 8 ) + return CraftResource.Valorite; + + return CraftResource.None; + } + + /// + /// Returns the value which represents '', using '' to help resolve leather OreInfo instances. + /// + public static CraftResource GetFromOreInfo( OreInfo info, ArmorMaterialType material ) + { + if ( material == ArmorMaterialType.Studded || material == ArmorMaterialType.Leather || material == ArmorMaterialType.Spined || + material == ArmorMaterialType.Horned || material == ArmorMaterialType.Barbed ) + { + if ( info.Level == 0 ) + return CraftResource.RegularLeather; + else if ( info.Level == 1 ) + return CraftResource.SpinedLeather; + else if ( info.Level == 2 ) + return CraftResource.HornedLeather; + else if ( info.Level == 3 ) + return CraftResource.BarbedLeather; + + return CraftResource.None; + } + + return GetFromOreInfo( info ); + } + } + + // NOTE: This class is only for compatability with very old RunUO versions. + // No changes to it should be required for custom resources. + public class OreInfo + { + public static readonly OreInfo Iron = new OreInfo( 0, 0x000, "Iron" ); + public static readonly OreInfo DullCopper = new OreInfo( 1, 0x973, "Dull Copper" ); + public static readonly OreInfo ShadowIron = new OreInfo( 2, 0x966, "Shadow Iron" ); + public static readonly OreInfo Copper = new OreInfo( 3, 0x96D, "Copper" ); + public static readonly OreInfo Bronze = new OreInfo( 4, 0x972, "Bronze" ); + public static readonly OreInfo Gold = new OreInfo( 5, 0x8A5, "Gold" ); + public static readonly OreInfo Agapite = new OreInfo( 6, 0x979, "Agapite" ); + public static readonly OreInfo Verite = new OreInfo( 7, 0x89F, "Verite" ); + public static readonly OreInfo Valorite = new OreInfo( 8, 0x8AB, "Valorite" ); + + private int m_Level; + private int m_Hue; + private string m_Name; + + public OreInfo( int level, int hue, string name ) + { + m_Level = level; + m_Hue = hue; + m_Name = name; + } + + public int Level + { + get + { + return m_Level; + } + } + + public int Hue + { + get + { + return m_Hue; + } + } + + public string Name + { + get + { + return m_Name; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Paperdoll.cs b/Scripts/Misc/Paperdoll.cs new file mode 100644 index 000000000..b46f6260d --- /dev/null +++ b/Scripts/Misc/Paperdoll.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Network; +using Server.Multis; +using Server.Mobiles; + +namespace Server.Misc +{ + public class Paperdoll + { + public static void Initialize() + { + EventSink.PaperdollRequest += new PaperdollRequestEventHandler( EventSink_PaperdollRequest ); + } + + public static void EventSink_PaperdollRequest( PaperdollRequestEventArgs e ) + { + Mobile beholder = e.Beholder; + Mobile beheld = e.Beheld; + + beholder.Send( new DisplayPaperdoll( beheld, Titles.ComputeTitle( beholder, beheld ), beheld.AllowEquipFrom( beholder ) ) ); + + if ( ObjectPropertyList.Enabled ) + { + List items = beheld.Items; + + for ( int i = 0; i < items.Count; ++i ) + beholder.Send( items[i].OPLPacket ); + + // NOTE: OSI sends MobileUpdate when opening your own paperdoll. + // It has a very bad rubber-banding affect. What positive affects does it have? + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Poison.cs b/Scripts/Misc/Poison.cs new file mode 100644 index 000000000..f961b1552 --- /dev/null +++ b/Scripts/Misc/Poison.cs @@ -0,0 +1,153 @@ +using System; +using Server; +using Server.Items; +using Server.Network; +using Server.Mobiles; +using Server.Spells; +using Server.Spells.Necromancy; +using Server.Spells.Ninjitsu; + +namespace Server +{ + public class PoisonImpl : Poison + { + [CallPriority( 10 )] + public static void Configure() + { + if ( Core.AOS ) + { + Register( new PoisonImpl( "Lesser", 0, 4, 16, 7.5, 3.0, 2.25, 10, 4 ) ); + Register( new PoisonImpl( "Regular", 1, 8, 18, 10.0, 3.0, 3.25, 10, 3 ) ); + Register( new PoisonImpl( "Greater", 2, 12, 20, 15.0, 3.0, 4.25, 10, 2 ) ); + Register( new PoisonImpl( "Deadly", 3, 16, 30, 30.0, 3.0, 5.25, 15, 2 ) ); + Register( new PoisonImpl( "Lethal", 4, 20, 50, 35.0, 3.0, 5.25, 20, 2 ) ); + } + else + { + Register( new PoisonImpl( "Lesser", 0, 4, 26, 2.500, 3.5, 3.0, 10, 2 ) ); + Register( new PoisonImpl( "Regular", 1, 5, 26, 3.125, 3.5, 3.0, 10, 2 ) ); + Register( new PoisonImpl( "Greater", 2, 6, 26, 6.250, 3.5, 3.0, 10, 2 ) ); + Register( new PoisonImpl( "Deadly", 3, 7, 26, 12.500, 3.5, 4.0, 10, 2 ) ); + Register( new PoisonImpl( "Lethal", 4, 9, 26, 25.000, 3.5, 5.0, 10, 2 ) ); + } + } + + public static Poison IncreaseLevel( Poison oldPoison ) + { + Poison newPoison = ( oldPoison == null ? null : GetPoison( oldPoison.Level + 1 ) ); + + return ( newPoison == null ? oldPoison : newPoison ); + } + + // Info + private string m_Name; + private int m_Level; + + // Damage + private int m_Minimum, m_Maximum; + private double m_Scalar; + + // Timers + private TimeSpan m_Delay; + private TimeSpan m_Interval; + private int m_Count, m_MessageInterval; + + public PoisonImpl( string name, int level, int min, int max, double percent, double delay, double interval, int count, int messageInterval ) + { + m_Name = name; + m_Level = level; + m_Minimum = min; + m_Maximum = max; + m_Scalar = percent * 0.01; + m_Delay = TimeSpan.FromSeconds( delay ); + m_Interval = TimeSpan.FromSeconds( interval ); + m_Count = count; + m_MessageInterval = messageInterval; + } + + public override string Name{ get{ return m_Name; } } + public override int Level{ get{ return m_Level; } } + + public class PoisonTimer : Timer + { + private PoisonImpl m_Poison; + private Mobile m_Mobile; + private Mobile m_From; + private int m_LastDamage; + private int m_Index; + + public Mobile From{ get{ return m_From; } set{ m_From = value; } } + + public PoisonTimer( Mobile m, PoisonImpl p ) : base( p.m_Delay, p.m_Interval ) + { + m_From = m; + m_Mobile = m; + m_Poison = p; + } + + protected override void OnTick() + { + if ( (Core.AOS && m_Poison.Level < 4 && TransformationSpell.UnderTransformation( m_Mobile, typeof( VampiricEmbraceSpell ) )) || + (m_Poison.Level < 3 && OrangePetals.UnderEffect( m_Mobile )) || + AnimalForm.UnderTransformation( m_Mobile, typeof( Unicorn ) ) ) + { + if ( m_Mobile.CurePoison( m_Mobile ) ) + { + m_Mobile.LocalOverheadMessage( MessageType.Emote, 0x3F, true, + "* You feel yourself resisting the effects of the poison *" ); + + m_Mobile.NonlocalOverheadMessage( MessageType.Emote, 0x3F, true, + String.Format( "* {0} seems resistant to the poison *", m_Mobile.Name ) ); + + Stop(); + return; + } + } + + if ( m_Index++ == m_Poison.m_Count ) + { + m_Mobile.SendLocalizedMessage( 502136 ); // The poison seems to have worn off. + m_Mobile.Poison = null; + + Stop(); + return; + } + + int damage; + + if ( !Core.AOS && m_LastDamage != 0 && Utility.RandomBool() ) + { + damage = m_LastDamage; + } + else + { + damage = 1 + (int)(m_Mobile.Hits * m_Poison.m_Scalar); + + if ( damage < m_Poison.m_Minimum ) + damage = m_Poison.m_Minimum; + else if ( damage > m_Poison.m_Maximum ) + damage = m_Poison.m_Maximum; + + m_LastDamage = damage; + } + + if ( m_From != null ) + m_From.DoHarmful( m_Mobile, true ); + + IHonorTarget honorTarget = m_Mobile as IHonorTarget; + if ( honorTarget != null && honorTarget.ReceivedHonorContext != null ) + honorTarget.ReceivedHonorContext.OnTargetPoisoned(); + + AOS.Damage( m_Mobile, m_From, damage, 0, 0, 0, 100, 0 ); + + if ( (m_Index % m_Poison.m_MessageInterval) == 0 ) + m_Mobile.OnPoisoned( m_From, m_Poison, m_Poison ); + } + } + + public override Timer ConstructTimer( Mobile m ) + { + return new PoisonTimer( m, this ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/ProfanityProtection.cs b/Scripts/Misc/ProfanityProtection.cs new file mode 100644 index 000000000..c03018982 --- /dev/null +++ b/Scripts/Misc/ProfanityProtection.cs @@ -0,0 +1,124 @@ +using System; +using Server; +using Server.Network; + +namespace Server.Misc +{ + public enum ProfanityAction + { + None, // no action taken + Disallow, // speech is not displayed + Criminal, // makes the player criminal, not killable by guards + CriminalAction, // makes the player criminal, can be killed by guards + Disconnect, // player is kicked + Other // some other implementation + } + + public class ProfanityProtection + { + private static bool Enabled = false; + private static ProfanityAction Action = ProfanityAction.Disallow; // change here what to do when profanity is detected + + public static void Initialize() + { + if ( Enabled ) + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + } + + private static bool OnProfanityDetected( Mobile from, string speech ) + { + switch ( Action ) + { + case ProfanityAction.None: return true; + case ProfanityAction.Disallow: return false; + case ProfanityAction.Criminal: from.Criminal = true; return true; + case ProfanityAction.CriminalAction: from.CriminalAction( false ); return true; + case ProfanityAction.Disconnect: + { + NetState ns = from.NetState; + + if ( ns != null ) + ns.Dispose(); + + return false; + } + default: + case ProfanityAction.Other: // TODO: Provide custom implementation if this is chosen + { + return true; + } + } + } + + private static void EventSink_Speech( SpeechEventArgs e ) + { + Mobile from = e.Mobile; + + if ( from.AccessLevel > AccessLevel.Player ) + return; + + if ( !NameVerification.Validate( e.Speech, 0, int.MaxValue, true, true, false, int.MaxValue, m_Exceptions, m_Disallowed, m_StartDisallowed ) ) + e.Blocked = !OnProfanityDetected( from, e.Speech ); + } + + public static char[] Exceptions{ get{ return m_Exceptions; } } + public static string[] StartDisallowed{ get{ return m_StartDisallowed; } } + public static string[] Disallowed{ get{ return m_Disallowed; } } + + private static char[] m_Exceptions = new char[] + { + ' ', '-', '.', '\'', '"', ',', '_', '+', '=', '~', '`', '!', '^', '*', '\\', '/', ';', ':', '<', '>', '[', ']', '{', '}', '?', '|', '(', ')', '%', '$', '&', '#', '@' + }; + + private static string[] m_StartDisallowed = new string[]{}; + + private static string[] m_Disallowed = new string[] + { + "jigaboo", + "chigaboo", + "wop", + "kyke", + "kike", + "tit", + "spic", + "prick", + "piss", + "lezbo", + "lesbo", + "felatio", + "dyke", + "dildo", + "chinc", + "chink", + "cunnilingus", + "cum", + "cocksucker", + "cock", + "clitoris", + "clit", + "ass", + "hitler", + "penis", + "nigga", + "nigger", + "klit", + "kunt", + "jiz", + "jism", + "jerkoff", + "jackoff", + "goddamn", + "fag", + "blowjob", + "bitch", + "asshole", + "dick", + "pussy", + "snatch", + "cunt", + "twat", + "shit", + "fuck" + }; + } +} \ No newline at end of file diff --git a/Scripts/Misc/Profile.cs b/Scripts/Misc/Profile.cs new file mode 100644 index 000000000..16eb28996 --- /dev/null +++ b/Scripts/Misc/Profile.cs @@ -0,0 +1,98 @@ +using System; +using Server; +using Server.Network; +using Server.Accounting; + +namespace Server.Misc +{ + public class Profile + { + public static void Initialize() + { + EventSink.ProfileRequest += new ProfileRequestEventHandler( EventSink_ProfileRequest ); + EventSink.ChangeProfileRequest += new ChangeProfileRequestEventHandler( EventSink_ChangeProfileRequest ); + } + + public static void EventSink_ChangeProfileRequest( ChangeProfileRequestEventArgs e ) + { + Mobile from = e.Beholder; + + if ( from.ProfileLocked ) + from.SendMessage( "Your profile is locked. You may not change it." ); + else + from.Profile = e.Text; + } + + public static void EventSink_ProfileRequest( ProfileRequestEventArgs e ) + { + Mobile beholder = e.Beholder; + Mobile beheld = e.Beheld; + + if ( !beheld.Player ) + return; + + if ( beholder.Map != beheld.Map || !beholder.InRange( beheld, 12 ) || !beholder.CanSee( beheld ) ) + return; + + string header = Titles.ComputeTitle( beholder, beheld ); + + string footer = ""; + + if ( beheld.ProfileLocked ) + { + if ( beholder == beheld ) + footer = "Your profile has been locked."; + else if ( beholder.AccessLevel >= AccessLevel.Counselor ) + footer = "This profile has been locked."; + } + + if ( footer == "" && beholder == beheld ) + footer = GetAccountDuration( beheld ); + + string body = beheld.Profile; + + if ( body == null || body.Length <= 0 ) + body = ""; + + beholder.Send( new DisplayProfile( beholder != beheld || !beheld.ProfileLocked, beheld, header, body, footer ) ); + } + + private static string GetAccountDuration( Mobile m ) + { + Account a = m.Account as Account; + + if ( a == null ) + return ""; + + TimeSpan ts = DateTime.Now - a.Created; + + string v; + + if ( Format( ts.TotalDays, "This account is {0} day{1} old.", out v ) ) + return v; + + if ( Format( ts.TotalHours, "This account is {0} hour{1} old.", out v ) ) + return v; + + if ( Format( ts.TotalMinutes, "This account is {0} minute{1} old.", out v ) ) + return v; + + if ( Format( ts.TotalSeconds, "This account is {0} second{1} old.", out v ) ) + return v; + + return ""; + } + + public static bool Format( double value, string format, out string op ) + { + if ( value >= 1.0 ) + { + op = String.Format( format, (int)value, (int)value != 1 ? "s" : "" ); + return true; + } + + op = null; + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/ProtocolExtensions.cs b/Scripts/Misc/ProtocolExtensions.cs new file mode 100644 index 000000000..dac3cd138 --- /dev/null +++ b/Scripts/Misc/ProtocolExtensions.cs @@ -0,0 +1,107 @@ +using System; +using Server; +using Server.Network; +using Server.Mobiles; +using Server.Engines.PartySystem; + +namespace Server.Misc +{ + public class ProtocolExtensions + { + private static PacketHandler[] m_Handlers = new PacketHandler[0x100]; + + public static void Initialize() + { + PacketHandlers.Register( 0xF0, 0, true, new OnPacketReceive( DecodeBundledPacket ) ); + + Register( 0x00, true, new OnPacketReceive( QueryPartyLocations ) ); + } + + public static void QueryPartyLocations( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + Party party = Party.Get( from ); + + if ( party != null ) + { + AckPartyLocations ack = new AckPartyLocations( from, party ); + + if ( ack.UnderlyingStream.Length > 8 ) + state.Send( ack ); + } + } + + public static void Register( int packetID, bool ingame, OnPacketReceive onReceive ) + { + m_Handlers[packetID] = new PacketHandler( packetID, 0, ingame, onReceive ); + } + + public static PacketHandler GetHandler( int packetID ) + { + if ( packetID >= 0 && packetID < m_Handlers.Length ) + return m_Handlers[packetID]; + + return null; + } + + public static void DecodeBundledPacket( NetState state, PacketReader pvSrc ) + { + int packetID = pvSrc.ReadByte(); + + PacketHandler ph = GetHandler( packetID ); + + if ( ph != null ) + { + if ( ph.Ingame && state.Mobile == null ) + { + Console.WriteLine( "Client: {0}: Sent ingame packet (0xF0x{1:X2}) before having been attached to a mobile", state, packetID ); + state.Dispose(); + } + else if ( ph.Ingame && state.Mobile.Deleted ) + { + state.Dispose(); + } + else + { + ph.OnReceive( state, pvSrc ); + } + } + } + } + + public abstract class ProtocolExtension : Packet + { + public ProtocolExtension( int packetID, int capacity ) : base( 0xF0 ) + { + EnsureCapacity( 4 + capacity ); + + m_Stream.Write( (byte) packetID ); + } + } + + public class AckPartyLocations : ProtocolExtension + { + public AckPartyLocations( Mobile from, Party party ) : base( 0x01, ((party.Members.Count - 1) * 9) + 4 ) + { + for ( int i = 0; i < party.Members.Count; ++i ) + { + PartyMemberInfo pmi = (PartyMemberInfo)party.Members[i]; + + if ( pmi == null || pmi.Mobile == from ) + continue; + + Mobile mob = pmi.Mobile; + + if ( Utility.InUpdateRange( from, mob ) && from.CanSee( mob ) ) + continue; + + m_Stream.Write( (int) mob.Serial ); + m_Stream.Write( (short) mob.X ); + m_Stream.Write( (short) mob.Y ); + m_Stream.Write( (byte) (mob.Map == null ? 0 : mob.Map.MapID) ); + } + + m_Stream.Write( (int) 0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/RaceDefinitions.cs b/Scripts/Misc/RaceDefinitions.cs new file mode 100644 index 000000000..b9eeb5c26 --- /dev/null +++ b/Scripts/Misc/RaceDefinitions.cs @@ -0,0 +1,219 @@ +using System; +using Server; + +namespace Server.Misc +{ + public class RaceDefinitions + { + public static void Configure() + { + /* Here we configure all races. Some notes: + * + * 1) The first 32 races are reserved for core use. + * 2) Race 0x7F is reserved for core use. + * 3) Race 0xFF is reserved for core use. + * 4) Changing or removing any predefined races may cause server instability. + */ + + RegisterRace( new Human ( 0, 0 ) ); + RegisterRace( new Elf ( 1, 1 ) ); + } + + public static void RegisterRace( Race race ) + { + Race.Races[race.RaceIndex] = race; + Race.AllRaces.Add( race ); + } + + private class Human : Race + { + public Human( int raceID, int raceIndex ) + : base( raceID, raceIndex, "Human", "Humans", 400, 401, 402, 403, Expansion.None ) + { + } + + public override bool ValidateHair( bool female, int itemID ) + { + if( itemID == 0 ) + return true; + + if( (female && itemID == 0x2048) || (!female && itemID == 0x2046 ) ) + return false; //Buns & Receeding Hair + + if( itemID >= 0x203B && itemID <= 0x203D ) + return true; + + if( itemID >= 0x2044 && itemID <= 0x204A ) + return true; + + return false; + } + public override int RandomHair( bool female ) //Random hair doesn't include baldness + { + switch( Utility.Random( 9 ) ) + { + case 0: return 0x203B; //Short + case 1: return 0x203C; //Long + case 2: return 0x203D; //Pony Tail + case 3: return 0x2044; //Mohawk + case 4: return 0x2045; //Pageboy + case 5: return 0x2047; //Afro + case 6: return 0x2049; //Pig tails + case 7: return 0x204A; //Krisna + default: return (female ? 0x2046 : 0x2048); //Buns or Receeding Hair + } + } + + public override bool ValidateFacialHair( bool female, int itemID ) + { + if( itemID == 0 ) + return true; + + if( female ) + return false; + + if( itemID >= 0x203E && itemID <= 0x2041 ) + return true; + + if( itemID >= 0x204B && itemID <= 0x204D ) + return true; + + return false; + } + public override int RandomFacialHair( bool female ) + { + if( female ) + return 0; + + int rand = Utility.Random( 7 ); + + return ((rand < 4) ? 0x203E : 0x2047) + rand; + } + + public override int ClipSkinHue( int hue ) + { + if( hue < 1002 ) + return 1002; + else if( hue > 1058 ) + return 1058; + else + return hue; + } + + public override int RandomSkinHue() + { + return Utility.Random( 1002, 57 ) | 0x8000; + } + + public override int ClipHairHue( int hue ) + { + if( hue < 1102 ) + return 1102; + else if( hue > 1149 ) + return 1149; + else + return hue; + } + + public override int RandomHairHue() + { + return Utility.Random( 1102, 48 ); + } + } + + private class Elf : Race + { + private static int[] m_SkinHues = new int[] + { + 0x0BF, 0x24D, 0x24E, 0x24F, 0x353, 0x361, 0x367, 0x374, + 0x375, 0x376, 0x381, 0x382, 0x383, 0x384, 0x385, 0x389, + 0x3DE, 0x3E5, 0x3E6, 0x3E8, 0x3E9, 0x430, 0x4A7, 0x4DE, + 0x51D, 0x53F, 0x579, 0x76B, 0x76C, 0x76D, 0x835, 0x903 + }; + + private static int[] m_HairHues = new int[] + { + 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x058, 0x08E, + 0x08F, 0x090, 0x091, 0x092, 0x101, 0x159, 0x15A, 0x15B, + 0x15C, 0x15D, 0x15E, 0x128, 0x12F, 0x1BD, 0x1E4, 0x1F3, + 0x207, 0x211, 0x239, 0x251, 0x26C, 0x2C3, 0x2C9, 0x31D, + 0x31E, 0x31F, 0x320, 0x321, 0x322, 0x323, 0x324, 0x325, + 0x326, 0x369, 0x386, 0x387, 0x388, 0x389, 0x38A, 0x59D, + 0x6B8, 0x725, 0x853 + }; + + public Elf( int raceID, int raceIndex ) + : base( raceID, raceIndex, "Elf", "Elves", 605, 606, 607, 608, Expansion.ML ) + { + } + + public override bool ValidateHair( bool female, int itemID ) + { + if( itemID == 0 ) + return true; + + if( (female && (itemID == 0x2FCD || itemID == 0x2FBF)) || (!female && (itemID == 0x2FCC || itemID == 0x2FD0)) ) + return false; + + if( itemID >= 0x2FBF && itemID <= 0x2FC2 ) + return true; + + if( itemID >= 0x2FCC && itemID <= 0x2FD1 ) + return true; + + return false; + } + public override int RandomHair( bool female ) //Random hair doesn't include baldness + { + switch( Utility.Random( 8 ) ) + { + case 0: return 0x2FC0; //Long Feather + case 1: return 0x2FC1; //Short + case 2: return 0x2FC2; //Mullet + case 3: return 0x2FCE; //Knob + case 4: return 0x2FCF; //Braided + case 5: return 0x2FD1; //Spiked + case 6: return (female ? 0x2FCC : 0x2FBF); //Flower or Mid-long + default: return (female ? 0x2FD0 : 0x2FCD); //Bun or Long + } + } + + public override bool ValidateFacialHair( bool female, int itemID ) + { + return (itemID == 0); + } + public override int RandomFacialHair( bool female ) + { + return 0; + } + + public override int ClipSkinHue( int hue ) + { + for( int i = 0; i < m_SkinHues.Length; i++ ) + if( m_SkinHues[i] == hue ) + return hue; + + return m_SkinHues[0]; + } + + public override int RandomSkinHue() + { + return m_SkinHues[Utility.Random( m_SkinHues.Length )] | 0x8000; + } + + public override int ClipHairHue( int hue ) + { + for( int i = 0; i < m_HairHues.Length; i++ ) + if( m_HairHues[i] == hue ) + return hue; + + return m_HairHues[0]; + } + + public override int RandomHairHue() + { + return m_HairHues[Utility.Random( m_HairHues.Length )]; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/RegenRates.cs b/Scripts/Misc/RegenRates.cs new file mode 100644 index 000000000..7156a9dc7 --- /dev/null +++ b/Scripts/Misc/RegenRates.cs @@ -0,0 +1,201 @@ +using System; +using Server; +using Server.Items; +using Server.Spells; +using Server.Spells.Necromancy; +using Server.Spells.Ninjitsu; +using Server.Mobiles; + +namespace Server.Misc +{ + public class RegenRates + { + [CallPriority( 10 )] + public static void Configure() + { + Mobile.DefaultHitsRate = TimeSpan.FromSeconds( 11.0 ); + Mobile.DefaultStamRate = TimeSpan.FromSeconds( 7.0 ); + Mobile.DefaultManaRate = TimeSpan.FromSeconds( 7.0 ); + + Mobile.ManaRegenRateHandler = new RegenRateHandler( Mobile_ManaRegenRate ); + + if ( Core.AOS ) + { + Mobile.StamRegenRateHandler = new RegenRateHandler( Mobile_StamRegenRate ); + Mobile.HitsRegenRateHandler = new RegenRateHandler( Mobile_HitsRegenRate ); + } + } + + private static void CheckBonusSkill( Mobile m, int cur, int max, SkillName skill ) + { + if ( !m.Alive ) + return; + + double n = (double)cur / max; + double v = Math.Sqrt( m.Skills[skill].Value * 0.005 ); + + n *= (1.0 - v); + n += v; + + m.CheckSkill( skill, n ); + } + + private static bool CheckTransform( Mobile m, Type type ) + { + return TransformationSpell.UnderTransformation( m, type ); + } + + private static bool CheckAnimal( Mobile m, Type type ) + { + return AnimalForm.UnderTransformation( m, type ); + } + + private static TimeSpan Mobile_HitsRegenRate( Mobile from ) + { + int points = AosAttributes.GetValue( from, AosAttribute.RegenHits ); + + if ( from is BaseCreature && !((BaseCreature)from).IsAnimatedDead ) + points += 4; + + if ( (from is BaseCreature && ((BaseCreature)from).IsParagon) || from is Leviathan ) + points += 40; + + if ( CheckTransform( from, typeof( HorrificBeastSpell ) ) ) + points += 20; + + if ( CheckAnimal( from, typeof( Dog ) ) || CheckAnimal( from, typeof( Cat ) ) ) + points += from.Skills[SkillName.Ninjitsu].Fixed / 300; + + if( Core.ML && from.Race == Race.Human ) + points += 2; + + if ( points < 0 ) + points = 0; + + return TimeSpan.FromSeconds( 1.0 / (0.1 * (1 + points)) ); + } + + private static TimeSpan Mobile_StamRegenRate( Mobile from ) + { + if ( from.Skills == null ) + return Mobile.DefaultStamRate; + + CheckBonusSkill( from, from.Stam, from.StamMax, SkillName.Focus ); + + int points = AosAttributes.GetValue( from, AosAttribute.RegenStam ) + + (int)(from.Skills[SkillName.Focus].Value * 0.1); + + if ( CheckTransform( from, typeof( VampiricEmbraceSpell ) ) ) + points += 15; + + if ( CheckAnimal( from, typeof( Kirin ) ) ) + points += 20; + + if ( (from is BaseCreature && ((BaseCreature)from).IsParagon) || from is Leviathan ) + points += 40; + + if ( points < -1 ) + points = -1; + + return TimeSpan.FromSeconds( 1.0 / (0.1 * (2 + points)) ); + } + + private static TimeSpan Mobile_ManaRegenRate( Mobile from ) + { + if ( from.Skills == null ) + return Mobile.DefaultManaRate; + + if ( !from.Meditating ) + CheckBonusSkill( from, from.Mana, from.ManaMax, SkillName.Meditation ); + + double rate; + double armorPenalty = GetArmorOffset( from ); + + if ( Core.AOS ) + { + double medPoints = from.Int + (from.Skills[SkillName.Meditation].Value * 3); + + medPoints *= ( from.Skills[SkillName.Meditation].Value < 100.0 ) ? 0.025 : 0.0275; + + CheckBonusSkill( from, from.Mana, from.ManaMax, SkillName.Focus ); + + double focusPoints = (int)(from.Skills[SkillName.Focus].Value * 0.05); + + if ( armorPenalty > 0 ) + medPoints = 0; // In AOS, wearing any meditation-blocking armor completely removes meditation bonus + + double totalPoints = AosAttributes.GetValue( from, AosAttribute.RegenMana ) + + focusPoints + medPoints + (from.Meditating ? (medPoints > 13.0 ? 13.0 : medPoints) : 0.0); + + if ( CheckTransform( from, typeof( VampiricEmbraceSpell ) ) ) + totalPoints += 3; + else if ( CheckTransform( from, typeof( LichFormSpell ) ) ) + totalPoints += 13; + + if ( (from is BaseCreature && ((BaseCreature)from).IsParagon) || from is Leviathan ) + totalPoints += 40; + + if ( totalPoints < -1 ) + totalPoints = -1; + + rate = 1.0 / (0.1 * (2 + (int)totalPoints)); + } + else + { + double medPoints = (from.Int + from.Skills[SkillName.Meditation].Value) * 0.5; + + if ( medPoints <= 0 ) + rate = 7.0; + else if ( medPoints <= 100 ) + rate = 7.0 - (239*medPoints/2400) + (19*medPoints*medPoints/48000); + else if ( medPoints < 120 ) + rate = 1.0; + else + rate = 0.75; + + rate += armorPenalty; + + if ( from.Meditating ) + rate *= 0.5; + + if ( rate < 0.5 ) + rate = 0.5; + else if ( rate > 7.0 ) + rate = 7.0; + } + + return TimeSpan.FromSeconds( rate ); + } + + public static double GetArmorOffset( Mobile from ) + { + double rating = 0.0; + + if ( !Core.AOS ) + rating += GetArmorMeditationValue( from.ShieldArmor as BaseArmor ); + + rating += GetArmorMeditationValue( from.NeckArmor as BaseArmor ); + rating += GetArmorMeditationValue( from.HandArmor as BaseArmor ); + rating += GetArmorMeditationValue( from.HeadArmor as BaseArmor ); + rating += GetArmorMeditationValue( from.ArmsArmor as BaseArmor ); + rating += GetArmorMeditationValue( from.LegsArmor as BaseArmor ); + rating += GetArmorMeditationValue( from.ChestArmor as BaseArmor ); + + return rating / 4; + } + + private static double GetArmorMeditationValue( BaseArmor ar ) + { + if ( ar == null || ar.ArmorAttributes.MageArmor != 0 || ar.Attributes.SpellChanneling != 0 ) + return 0.0; + + switch ( ar.MeditationAllowance ) + { + default: + case ArmorMeditationAllowance.None: return ar.BaseArmorRatingScaled; + case ArmorMeditationAllowance.Half: return ar.BaseArmorRatingScaled / 2.0; + case ArmorMeditationAllowance.All: return 0.0; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/RenameRequests.cs b/Scripts/Misc/RenameRequests.cs new file mode 100644 index 000000000..c6cb691a6 --- /dev/null +++ b/Scripts/Misc/RenameRequests.cs @@ -0,0 +1,51 @@ +using System; +using Server; + +namespace Server.Misc +{ + public class RenameRequests + { + public static void Initialize() + { + EventSink.RenameRequest += new RenameRequestEventHandler( EventSink_RenameRequest ); + } + + private static void EventSink_RenameRequest( RenameRequestEventArgs e ) + { + Mobile from = e.From; + Mobile targ = e.Target; + string name = e.Name; + + if ( from.CanSee( targ ) && from.InRange( targ, 12 ) && targ.CanBeRenamedBy( from ) ) + { + name = name.Trim(); + + if( NameVerification.Validate( name, 1, 16, true, false, true, 0, NameVerification.Empty, NameVerification.StartDisallowed, ( Core.ML ? NameVerification.Disallowed : new string[]{} ) ) ) + { + + if( Core.ML ) + { + string[] disallowed = ProfanityProtection.Disallowed; + + for( int i = 0; i < disallowed.Length; i++ ) + { + if( name.IndexOf( disallowed[i] ) != -1 ) + { + from.SendLocalizedMessage( 1072622 ); // That name isn't very polite. + return; + } + } + + from.SendLocalizedMessage( 1072623, String.Format( "{0}\t{1}", targ.Name, name ) ); // Pet ~1_OLDPETNAME~ renamed to ~2_NEWPETNAME~. + } + + targ.Name = name; + } + else + { + from.SendMessage( "That name is unacceptable." ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/ServerList.cs b/Scripts/Misc/ServerList.cs new file mode 100644 index 000000000..297442fad --- /dev/null +++ b/Scripts/Misc/ServerList.cs @@ -0,0 +1,94 @@ +using System; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Network; + +namespace Server.Misc +{ + public class ServerList + { + /* Address: + * + * The default setting, a value of 'null', will attempt to detect your IP address automatically: + * private const string Address = null; + * + * This detection, however, does not work for servers behind routers. If you're running behind a router, put in your IP: + * private const string Address = "12.34.56.78"; + * + * If you need to resolve a DNS host name, you can do that too: + * private const string Address = "shard.host.com"; + */ + + public static readonly string Address = null; + + public const string ServerName = "RunUO TC"; + + public static void Initialize() + { + Listener.Port = 2593; + + EventSink.ServerList += new ServerListEventHandler( EventSink_ServerList ); + } + + public static void EventSink_ServerList( ServerListEventArgs e ) + { + try + { + IPAddress ipAddr; + + if ( Resolve( Address != null && !IsLocalMachine( e.State ) ? Address : Dns.GetHostName(), out ipAddr ) ) + e.AddServer( ServerName, new IPEndPoint( ipAddr, Listener.Port ) ); + else + e.Rejected = true; + } + catch + { + e.Rejected = true; + } + } + + public static bool Resolve( string addr, out IPAddress outValue ) + { + + if ( IPAddress.TryParse( addr, out outValue ) ) + return true; + + try + { + IPHostEntry iphe = Dns.GetHostEntry( addr ); + + if ( iphe.AddressList.Length > 0 ) + { + outValue = iphe.AddressList[iphe.AddressList.Length - 1]; + return true; + } + } + catch + { + } + + outValue = IPAddress.None; + return false; + } + + private static bool IsLocalMachine( NetState state ) + { + Socket sock = state.Socket; + + IPAddress theirAddress = ((IPEndPoint)sock.RemoteEndPoint).Address; + + if ( IPAddress.IsLoopback( theirAddress ) ) + return true; + + bool contains = false; + + IPHostEntry iphe = Dns.GetHostEntry( Dns.GetHostName() ); + + for ( int i = 0; !contains && i < iphe.AddressList.Length; ++i ) + contains = theirAddress.Equals( iphe.AddressList[i] ); + + return contains; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/ShardPoller.cs b/Scripts/Misc/ShardPoller.cs new file mode 100644 index 000000000..aeb0905b2 --- /dev/null +++ b/Scripts/Misc/ShardPoller.cs @@ -0,0 +1,643 @@ +using System; +using System.Net; +using System.Collections; +using System.Text; +using System.Text.RegularExpressions; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Prompts; + +namespace Server.Misc +{ + public class ShardPoller : Item + { + private string m_Title; + + private ShardPollOption[] m_Options; + private IPAddress[] m_Addresses; + + private TimeSpan m_Duration; + private DateTime m_StartTime; + + private bool m_Active; + + public ShardPollOption[] Options + { + get{ return m_Options; } + set{ m_Options = value; } + } + + public IPAddress[] Addresses + { + get{ return m_Addresses; } + set{ m_Addresses = value; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public string Title + { + get{ return m_Title; } + set{ m_Title = ShardPollPrompt.UrlToHref( value ); } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public TimeSpan Duration + { + get{ return m_Duration; } + set{ m_Duration = value; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public DateTime StartTime + { + get{ return m_StartTime; } + set{ m_StartTime = value; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public TimeSpan TimeRemaining + { + get + { + if ( m_StartTime == DateTime.MinValue || !m_Active ) + return TimeSpan.Zero; + + try + { + TimeSpan ts = (m_StartTime + m_Duration) - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + return TimeSpan.Zero; + + return ts; + } + catch + { + return TimeSpan.Zero; + } + } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public bool Active + { + get{ return m_Active; } + set + { + if ( m_Active == value ) + return; + + m_Active = value; + + if ( m_Active ) + { + m_StartTime = DateTime.Now; + m_ActivePollers.Add( this ); + } + else + { + m_ActivePollers.Remove( this ); + } + } + } + + public bool HasAlreadyVoted( NetState ns ) + { + for ( int i = 0; i < m_Options.Length; ++i ) + { + if ( m_Options[i].HasAlreadyVoted( ns ) ) + return true; + } + + return false; + } + + public void AddVote( NetState ns, ShardPollOption option ) + { + option.AddVote( ns ); + } + + public void RemoveOption( ShardPollOption option ) + { + int index = Array.IndexOf( m_Options, option ); + + if ( index < 0 ) + return; + + ShardPollOption[] old = m_Options; + m_Options = new ShardPollOption[old.Length - 1]; + + for ( int i = 0; i < index; ++i ) + m_Options[i] = old[i]; + + for ( int i = index; i < m_Options.Length; ++i ) + m_Options[i] = old[i + 1]; + } + + public void AddOption( ShardPollOption option ) + { + ShardPollOption[] old = m_Options; + m_Options = new ShardPollOption[old.Length + 1]; + + for ( int i = 0; i < old.Length; ++i ) + m_Options[i] = old[i]; + + m_Options[old.Length] = option; + } + + public override string DefaultName + { + get { return "shard poller"; } + } + + [Constructable] + public ShardPoller() : base( 0x1047 ) + { + m_Duration = TimeSpan.FromHours( 24.0 ); + m_Options = new ShardPollOption[0]; + m_Addresses = new IPAddress[0]; + + Movable = false; + } + + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static ArrayList m_ActivePollers = new ArrayList(); + + private static void EventSink_Login( LoginEventArgs e ) + { + if ( m_ActivePollers.Count == 0 ) + return; + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( EventSink_Login_Callback ), e.Mobile ); + } + + private static void EventSink_Login_Callback( object state ) + { + Mobile from = (Mobile)state; + NetState ns = from.NetState; + + if ( ns == null ) + return; + + ShardPollGump spg = null; + + for ( int i = 0; i < m_ActivePollers.Count; ++i ) + { + ShardPoller poller = (ShardPoller)m_ActivePollers[i]; + + if ( poller.Deleted || !poller.Active ) + continue; + + if ( poller.TimeRemaining > TimeSpan.Zero ) + { + if ( poller.HasAlreadyVoted( ns ) ) + continue; + + if ( spg == null ) + { + spg = new ShardPollGump( from, poller, false, null ); + from.SendGump( spg ); + } + else + { + spg.QueuePoll( poller ); + } + } + else + { + poller.Active = false; + } + } + } + + public void SendQueuedPoll_Callback( object state ) + { + object[] states = (object[])state; + Mobile from = (Mobile)states[0]; + Queue queue = (Queue)states[1]; + + from.SendGump( new ShardPollGump( from, this, false, queue ) ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.Administrator ) + from.SendGump( new ShardPollGump( from, this, true, null ) ); + } + + public ShardPoller( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Title ); + writer.Write( m_Duration ); + writer.Write( m_StartTime ); + writer.Write( m_Active ); + + writer.Write( m_Options.Length ); + + for ( int i = 0; i < m_Options.Length; ++i ) + m_Options[i].Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Title = reader.ReadString(); + m_Duration = reader.ReadTimeSpan(); + m_StartTime = reader.ReadDateTime(); + m_Active = reader.ReadBool(); + + m_Options = new ShardPollOption[reader.ReadInt()]; + + for ( int i = 0; i < m_Options.Length; ++i ) + m_Options[i] = new ShardPollOption( reader ); + + if ( m_Active ) + m_ActivePollers.Add( this ); + + break; + } + } + } + + public override void OnDelete() + { + base.OnDelete(); + + Active = false; + } + } + + public class ShardPollOption + { + private string m_Title; + private int m_LineBreaks; + private IPAddress[] m_Voters; + + public string Title{ get{ return m_Title; } set{ m_Title = value; m_LineBreaks = GetBreaks( m_Title ); } } + public int LineBreaks{ get{ return m_LineBreaks; } } + + public int Votes{ get{ return m_Voters.Length; } } + public IPAddress[] Voters{ get{ return m_Voters; } set{ m_Voters = value; } } + + public ShardPollOption( string title ) + { + m_Title = title; + m_LineBreaks = GetBreaks( m_Title ); + m_Voters = new IPAddress[0]; + } + + public bool HasAlreadyVoted( NetState ns ) + { + if ( ns == null ) + return false; + + IPAddress ipAddress = ns.Address; + + for ( int i = 0; i < m_Voters.Length; ++i ) + { + if ( Utility.IPMatchClassC( m_Voters[i], ipAddress ) ) + return true; + } + + return false; + } + + public void AddVote( NetState ns ) + { + if ( ns == null ) + return; + + IPAddress[] old = m_Voters; + m_Voters = new IPAddress[old.Length + 1]; + + for ( int i = 0; i < old.Length; ++i ) + m_Voters[i] = old[i]; + + m_Voters[old.Length] = ns.Address; + } + + public int ComputeHeight() + { + int height = m_LineBreaks * 18; + + if ( height > 30 ) + return height; + + return 30; + } + + public int GetBreaks( string title ) + { + if ( title == null ) + return 1; + + int count = 0; + int index = -1; + + do + { + ++count; + index = title.IndexOf( "
", index + 1 ); + } while ( index >= 0 ); + + return count; + } + + public ShardPollOption( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Title = reader.ReadString(); + m_LineBreaks = GetBreaks( m_Title ); + + m_Voters = new IPAddress[reader.ReadInt()]; + + for ( int i = 0; i < m_Voters.Length; ++i ) + m_Voters[i] = reader.ReadIPAddress(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version + + writer.Write( m_Title ); + + writer.Write( m_Voters.Length ); + + for ( int i = 0; i < m_Voters.Length; ++i ) + writer.Write( m_Voters[i] ); + } + } + + public class ShardPollGump : Gump + { + private Mobile m_From; + private ShardPoller m_Poller; + private bool m_Editing; + private Queue m_Polls; + + public bool Editing{ get{ return m_Editing; } } + + public void QueuePoll( ShardPoller poller ) + { + if ( m_Polls == null ) + m_Polls = new Queue( 4 ); + + m_Polls.Enqueue( poller ); + } + + public string Center( string text ) + { + return String.Format( "
{0}
", text ); + } + + public string Color( string text, int color ) + { + return String.Format( "{1}", color, text ); + } + + private const int LabelColor32 = 0xFFFFFF; + + public ShardPollGump( Mobile from, ShardPoller poller, bool editing, Queue polls ) : base( 50, 50 ) + { + m_From = from; + m_Poller = poller; + m_Editing = editing; + m_Polls = polls; + + Closable = false; + + AddPage( 0 ); + + int totalVotes = 0; + int totalOptionHeight = 0; + + for ( int i = 0; i < poller.Options.Length; ++i ) + { + totalVotes += poller.Options[i].Votes; + totalOptionHeight += poller.Options[i].ComputeHeight() + 5; + } + + bool isViewingResults = editing && poller.Active; + bool isCompleted = totalVotes > 0 && !poller.Active; + + if ( editing && !isViewingResults ) + totalOptionHeight += 35; + + int height = 115 + totalOptionHeight; + + AddBackground( 1, 1, 398, height - 2, 3600 ); + AddAlphaRegion( 16, 15, 369, height - 31 ); + + AddItem( 308, 30, 0x1E5E ); + + string title; + + if ( editing ) + title = ( isCompleted ? "Poll Completed" : "Poll Editor" ); + else + title = "Shard Poll"; + + AddHtml( 22, 22, 294, 20, Color( Center( title ), LabelColor32 ), false, false ); + + if ( editing ) + { + AddHtml( 22, 22, 294, 20, Color( String.Format( "{0} total", totalVotes ), LabelColor32 ), false, false ); + AddButton( 287, 23, 0x2622, 0x2623, 2, GumpButtonType.Reply, 0 ); + } + + AddHtml( 22, 50, 294, 40, Color( poller.Title, 0x99CC66 ), false, false ); + + AddImageTiled( 32, 88, 264, 1, 9107 ); + AddImageTiled( 42, 90, 264, 1, 9157 ); + + int y = 100; + + for ( int i = 0; i < poller.Options.Length; ++i ) + { + ShardPollOption option = poller.Options[i]; + string text = option.Title; + + if ( editing && totalVotes > 0 ) + { + double perc = option.Votes / (double)totalVotes; + + text = String.Format( "[{1}: {2}%] {0}", text, option.Votes, (int)(perc*100) ); + } + + int optHeight = option.ComputeHeight(); + + y += optHeight/2; + + if ( isViewingResults ) + AddImage( 24, y - 15, 0x25FE ); + else + AddRadio( 24, y - 15, 0x25F9, 0x25FC, false, 1 + i ); + + AddHtml( 60, y - (9 * option.LineBreaks), 250, 18 * option.LineBreaks, Color( text, LabelColor32 ), false, false ); + + y += optHeight/2; + y += 5; + } + + if ( editing && !isViewingResults ) + { + AddRadio( 24, y + 15 - 15, 0x25F9, 0x25FC, false, 1 + poller.Options.Length ); + AddHtml( 60, y + 15 - 9, 250, 18, Color( "Create new option.", 0x99CC66 ), false, false ); + } + + AddButton( 314, height - 73, 247, 248, 1, GumpButtonType.Reply, 0 ); + AddButton( 314, height - 47, 242, 241, 0, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_Polls != null && m_Polls.Count > 0 ) + { + ShardPoller poller = (ShardPoller)m_Polls.Dequeue(); + + if ( poller != null ) + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( poller.SendQueuedPoll_Callback ), new object[]{ m_From, m_Polls } ); + } + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length == 0 ) + return; + + int switched = switches[0] - 1; + ShardPollOption opt = null; + + if ( switched >= 0 && switched < m_Poller.Options.Length ) + opt = m_Poller.Options[switched]; + + if ( opt == null && !m_Editing ) + return; + + if ( m_Editing ) + { + if ( !m_Poller.Active ) + { + m_From.SendMessage( "Enter a title for the option. Escape to cancel.{0}", opt == null ? "" : " Use \"DEL\" to delete." ); + m_From.Prompt = new ShardPollPrompt( m_Poller, opt ); + } + else + { + m_From.SendMessage( "You may not edit an active poll. Deactivate it first." ); + m_From.SendGump( new ShardPollGump( m_From, m_Poller, m_Editing, m_Polls ) ); + } + } + else + { + if ( !m_Poller.Active ) + m_From.SendMessage( "The poll has been deactivated." ); + else if ( m_Poller.HasAlreadyVoted( sender ) ) + m_From.SendMessage( "You have already voted on this poll." ); + else + m_Poller.AddVote( sender, opt ); + } + } + else if ( info.ButtonID == 2 && m_Editing ) + { + m_From.SendGump( new ShardPollGump( m_From, m_Poller, m_Editing, m_Polls ) ); + m_From.SendGump( new PropertiesGump( m_From, m_Poller ) ); + } + } + } + + public class ShardPollPrompt : Prompt + { + private ShardPoller m_Poller; + private ShardPollOption m_Option; + + public ShardPollPrompt( ShardPoller poller, ShardPollOption opt ) + { + m_Poller = poller; + m_Option = opt; + } + + public override void OnCancel( Mobile from ) + { + from.SendGump( new ShardPollGump( from, m_Poller, true, null ) ); + } + + private static Regex m_UrlRegex = new Regex( @"\[url(?:=(.*?))?\](.*?)\[/url\]", RegexOptions.IgnoreCase | RegexOptions.Compiled ); + + private static string UrlRegex_Match( Match m ) + { + if ( m.Groups[1].Success ) + { + if ( m.Groups[2].Success ) + return String.Format( "{1}", m.Groups[1].Value, m.Groups[2].Value ); + } + else if ( m.Groups[2].Success ) + { + return String.Format( "{0}", m.Groups[2].Value ); + } + + return m.Value; + } + + public static string UrlToHref( string text ) + { + if ( text == null ) + return null; + + return m_UrlRegex.Replace( text, new MatchEvaluator( UrlRegex_Match ) ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Poller.Active ) + { + from.SendMessage( "You may not edit an active poll. Deactivate it first." ); + } + else if ( text == "DEL" ) + { + if ( m_Option != null ) + m_Poller.RemoveOption( m_Option ); + } + else + { + text = UrlToHref( text ); + + if ( m_Option == null ) + m_Poller.AddOption( new ShardPollOption( text ) ); + else + m_Option.Title = text; + } + + from.SendGump( new ShardPollGump( from, m_Poller, true, null ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/ShrinkTable.cs b/Scripts/Misc/ShrinkTable.cs new file mode 100644 index 000000000..1cb6e59eb --- /dev/null +++ b/Scripts/Misc/ShrinkTable.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; + +namespace Server +{ + public class ShrinkTable + { + public const int DefaultItemID = 0x1870; // Yellow virtue stone + + private static int[] m_Table; + + public static int Lookup( Mobile m ) + { + return Lookup( m.Body.BodyID, DefaultItemID ); + } + + public static int Lookup( int body ) + { + return Lookup( body, DefaultItemID ); + } + + public static int Lookup( Mobile m, int defaultValue ) + { + return Lookup( m.Body.BodyID, defaultValue ); + } + + public static int Lookup( int body, int defaultValue ) + { + if ( m_Table == null ) + Load(); + + int val = 0; + + if ( body >= 0 && body < m_Table.Length ) + val = m_Table[body]; + + if ( val == 0 ) + val = defaultValue; + + return val; + } + + private static void Load() + { + string path = Path.Combine( Core.BaseDirectory, "Data/shrink.cfg" ); + + if ( !File.Exists( path ) ) + { + m_Table = new int[0]; + return; + } + + m_Table = new int[1000]; + + using ( StreamReader ip = new StreamReader( path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + line = line.Trim(); + + if ( line.Length == 0 || line.StartsWith( "#" ) ) + continue; + + try + { + string[] split = line.Split( '\t' ); + + if ( split.Length >= 2 ) + { + int body = Utility.ToInt32( split[0] ); + int item = Utility.ToInt32( split[1] ); + + if ( body >= 0 && body < m_Table.Length ) + m_Table[body] = item; + } + } + catch + { + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/SkillCheck.cs b/Scripts/Misc/SkillCheck.cs new file mode 100644 index 000000000..b883d1f76 --- /dev/null +++ b/Scripts/Misc/SkillCheck.cs @@ -0,0 +1,364 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Misc +{ + public class SkillCheck + { + private static readonly bool AntiMacroCode = true; //Change this to false to disable anti-macro code + + public static TimeSpan AntiMacroExpire = TimeSpan.FromMinutes( 5.0 ); //How long do we remember targets/locations? + public const int Allowance = 3; //How many times may we use the same location/target for gain + private const int LocationSize = 5; //The size of eeach location, make this smaller so players dont have to move as far + private static bool[] UseAntiMacro = new bool[] + { + // true if this skill uses the anti-macro code, false if it does not + false,// Alchemy = 0, + true,// Anatomy = 1, + true,// AnimalLore = 2, + true,// ItemID = 3, + true,// ArmsLore = 4, + false,// Parry = 5, + true,// Begging = 6, + false,// Blacksmith = 7, + false,// Fletching = 8, + true,// Peacemaking = 9, + true,// Camping = 10, + false,// Carpentry = 11, + false,// Cartography = 12, + false,// Cooking = 13, + true,// DetectHidden = 14, + true,// Discordance = 15, + true,// EvalInt = 16, + true,// Healing = 17, + true,// Fishing = 18, + true,// Forensics = 19, + true,// Herding = 20, + true,// Hiding = 21, + true,// Provocation = 22, + false,// Inscribe = 23, + true,// Lockpicking = 24, + true,// Magery = 25, + true,// MagicResist = 26, + false,// Tactics = 27, + true,// Snooping = 28, + true,// Musicianship = 29, + true,// Poisoning = 30, + false,// Archery = 31, + true,// SpiritSpeak = 32, + true,// Stealing = 33, + false,// Tailoring = 34, + true,// AnimalTaming = 35, + true,// TasteID = 36, + false,// Tinkering = 37, + true,// Tracking = 38, + true,// Veterinary = 39, + false,// Swords = 40, + false,// Macing = 41, + false,// Fencing = 42, + false,// Wrestling = 43, + true,// Lumberjacking = 44, + true,// Mining = 45, + true,// Meditation = 46, + true,// Stealth = 47, + true,// RemoveTrap = 48, + true,// Necromancy = 49, + false,// Focus = 50, + true,// Chivalry = 51 + true,// Bushido = 52 + true,//Ninjitsu = 53 + true // Spellweaving + }; + + public static void Initialize() + { + Mobile.SkillCheckLocationHandler = new SkillCheckLocationHandler( Mobile_SkillCheckLocation ); + Mobile.SkillCheckDirectLocationHandler = new SkillCheckDirectLocationHandler( Mobile_SkillCheckDirectLocation ); + + Mobile.SkillCheckTargetHandler = new SkillCheckTargetHandler( Mobile_SkillCheckTarget ); + Mobile.SkillCheckDirectTargetHandler = new SkillCheckDirectTargetHandler( Mobile_SkillCheckDirectTarget ); + } + + public static bool Mobile_SkillCheckLocation( Mobile from, SkillName skillName, double minSkill, double maxSkill ) + { + Skill skill = from.Skills[skillName]; + + if ( skill == null ) + return false; + + double value = skill.Value; + + if ( value < minSkill ) + return false; // Too difficult + else if ( value >= maxSkill ) + return true; // No challenge + + double chance = (value - minSkill) / (maxSkill - minSkill); + + Point2D loc = new Point2D( from.Location.X / LocationSize, from.Location.Y / LocationSize ); + return CheckSkill( from, skill, loc, chance ); + } + + public static bool Mobile_SkillCheckDirectLocation( Mobile from, SkillName skillName, double chance ) + { + Skill skill = from.Skills[skillName]; + + if ( skill == null ) + return false; + + if ( chance < 0.0 ) + return false; // Too difficult + else if ( chance >= 1.0 ) + return true; // No challenge + + Point2D loc = new Point2D( from.Location.X / LocationSize, from.Location.Y / LocationSize ); + return CheckSkill( from, skill, loc, chance ); + } + + public static bool CheckSkill( Mobile from, Skill skill, object amObj, double chance ) + { + if ( from.Skills.Cap == 0 ) + return false; + + bool success = ( chance >= Utility.RandomDouble() ); + double gc = (double)(from.Skills.Cap - from.Skills.Total) / from.Skills.Cap; + gc += ( skill.Cap - skill.Base ) / skill.Cap; + gc /= 2; + + gc += ( 1.0 - chance ) * ( success ? 0.5 : (Core.AOS ? 0.0 : 0.2) ); + gc /= 2; + + gc *= skill.Info.GainFactor; + + if ( gc < 0.01 ) + gc = 0.01; + + if ( from is BaseCreature && ((BaseCreature)from).Controlled ) + gc *= 2; + + if ( from.Alive && ( ( gc >= Utility.RandomDouble() && AllowGain( from, skill, amObj ) ) || skill.Base < 10.0 ) ) + Gain( from, skill ); + + return success; + } + + public static bool Mobile_SkillCheckTarget( Mobile from, SkillName skillName, object target, double minSkill, double maxSkill ) + { + Skill skill = from.Skills[skillName]; + + if ( skill == null ) + return false; + + double value = skill.Value; + + if ( value < minSkill ) + return false; // Too difficult + else if ( value >= maxSkill ) + return true; // No challenge + + double chance = (value - minSkill) / (maxSkill - minSkill); + + return CheckSkill( from, skill, target, chance ); + } + + public static bool Mobile_SkillCheckDirectTarget( Mobile from, SkillName skillName, object target, double chance ) + { + Skill skill = from.Skills[skillName]; + + if ( skill == null ) + return false; + + if ( chance < 0.0 ) + return false; // Too difficult + else if ( chance >= 1.0 ) + return true; // No challenge + + return CheckSkill( from, skill, target, chance ); + } + + private static bool AllowGain( Mobile from, Skill skill, object obj ) + { + if ( from is PlayerMobile && AntiMacroCode && UseAntiMacro[skill.Info.SkillID] ) + return ((PlayerMobile)from).AntiMacroCheck( skill, obj ); + else + return true; + } + + public enum Stat { Str, Dex, Int } + + public static void Gain( Mobile from, Skill skill ) + { + if ( from.Region.IsPartOf( typeof( Regions.Jail ) ) ) + return; + + if ( from is BaseCreature && ((BaseCreature)from).IsDeadPet ) + return; + + if ( skill.SkillName == SkillName.Focus && from is BaseCreature ) + return; + + if ( skill.Base < skill.Cap && skill.Lock == SkillLock.Up ) + { + int toGain = 1; + + if ( skill.Base <= 10.0 ) + toGain = Utility.Random( 4 ) + 1; + + Skills skills = from.Skills; + + if ( ( skills.Total / skills.Cap ) >= Utility.RandomDouble() )//( skills.Total >= skills.Cap ) + { + for ( int i = 0; i < skills.Length; ++i ) + { + Skill toLower = skills[i]; + + if ( toLower != skill && toLower.Lock == SkillLock.Down && toLower.BaseFixedPoint >= toGain ) + { + toLower.BaseFixedPoint -= toGain; + break; + } + } + } + + if ( (skills.Total + toGain) <= skills.Cap ) + { + skill.BaseFixedPoint += toGain; + } + } + + if ( skill.Lock == SkillLock.Up ) + { + SkillInfo info = skill.Info; + + if ( from.StrLock == StatLockType.Up && (info.StrGain / 33.3) > Utility.RandomDouble() ) + GainStat( from, Stat.Str ); + else if ( from.DexLock == StatLockType.Up && (info.DexGain / 33.3) > Utility.RandomDouble() ) + GainStat( from, Stat.Dex ); + else if ( from.IntLock == StatLockType.Up && (info.IntGain / 33.3) > Utility.RandomDouble() ) + GainStat( from, Stat.Int ); + } + } + + public static bool CanLower( Mobile from, Stat stat ) + { + switch ( stat ) + { + case Stat.Str: return ( from.StrLock == StatLockType.Down && from.RawStr > 10 ); + case Stat.Dex: return ( from.DexLock == StatLockType.Down && from.RawDex > 10 ); + case Stat.Int: return ( from.IntLock == StatLockType.Down && from.RawInt > 10 ); + } + + return false; + } + + public static bool CanRaise( Mobile from, Stat stat ) + { + if ( !(from is BaseCreature && ((BaseCreature)from).Controlled) ) + { + if ( from.RawStatTotal >= from.StatCap ) + return false; + } + + switch ( stat ) + { + case Stat.Str: return ( from.StrLock == StatLockType.Up && from.RawStr < 125 ); + case Stat.Dex: return ( from.DexLock == StatLockType.Up && from.RawDex < 125 ); + case Stat.Int: return ( from.IntLock == StatLockType.Up && from.RawInt < 125 ); + } + + return false; + } + + public static void IncreaseStat( Mobile from, Stat stat, bool atrophy ) + { + atrophy = atrophy || (from.RawStatTotal >= from.StatCap); + + switch ( stat ) + { + case Stat.Str: + { + if ( atrophy ) + { + if ( CanLower( from, Stat.Dex ) && (from.RawDex < from.RawInt || !CanLower( from, Stat.Int )) ) + --from.RawDex; + else if ( CanLower( from, Stat.Int ) ) + --from.RawInt; + } + + if ( CanRaise( from, Stat.Str ) ) + ++from.RawStr; + + break; + } + case Stat.Dex: + { + if ( atrophy ) + { + if ( CanLower( from, Stat.Str ) && (from.RawStr < from.RawInt || !CanLower( from, Stat.Int )) ) + --from.RawStr; + else if ( CanLower( from, Stat.Int ) ) + --from.RawInt; + } + + if ( CanRaise( from, Stat.Dex ) ) + ++from.RawDex; + + break; + } + case Stat.Int: + { + if ( atrophy ) + { + if ( CanLower( from, Stat.Str ) && (from.RawStr < from.RawDex || !CanLower( from, Stat.Dex )) ) + --from.RawStr; + else if ( CanLower( from, Stat.Dex ) ) + --from.RawDex; + } + + if ( CanRaise( from, Stat.Int ) ) + ++from.RawInt; + + break; + } + } + } + + private static TimeSpan m_StatGainDelay = TimeSpan.FromMinutes( 15.0 ); + + public static void GainStat( Mobile from, Stat stat ) + { + switch( stat ) + { + case Stat.Str: + { + if( (from.LastStrGain + m_StatGainDelay) >= DateTime.Now ) + return; + + from.LastStrGain = DateTime.Now; + break; + } + case Stat.Dex: + { + if( (from.LastDexGain + m_StatGainDelay) >= DateTime.Now ) + return; + + from.LastDexGain = DateTime.Now; + break; + } + case Stat.Int: + { + if( (from.LastIntGain + m_StatGainDelay) >= DateTime.Now ) + return; + + from.LastIntGain = DateTime.Now; + break; + } + } + + bool atrophy = ( (from.RawStatTotal / (double)from.StatCap) >= Utility.RandomDouble() ); + + IncreaseStat( from, stat, atrophy ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/SocketOptions.cs b/Scripts/Misc/SocketOptions.cs new file mode 100644 index 000000000..85a717bbd --- /dev/null +++ b/Scripts/Misc/SocketOptions.cs @@ -0,0 +1,43 @@ +using System; +using System.IO; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Misc; +using Server.Network; + +namespace Server +{ + public class SocketOptions + { + private const bool NagleEnabled = false; // Should the Nagle algorithm be enabled? This may reduce performance + private const int CoalesceBufferSize = 512; // MSS that the core will use when buffering packets + + private static int[] m_AdditionalPorts = new int[0]; + //private static int[] m_AdditionalPorts = new int[]{ 2594 }; + + public static void Initialize() + { + EventSink.SocketConnect += new SocketConnectEventHandler( EventSink_SocketConnect ); + SendQueue.CoalesceBufferSize = CoalesceBufferSize; + + if ( m_AdditionalPorts.Length > 0 ) + EventSink.ServerStarted += new ServerStartedEventHandler( EventSink_ServerStarted ); + } + + public static void EventSink_ServerStarted() + { + for ( int i = 0; i < m_AdditionalPorts.Length; ++i ) + Core.MessagePump.AddListener( new Listener( m_AdditionalPorts[i] ) ); + } + + private static void EventSink_SocketConnect( SocketConnectEventArgs e ) + { + if ( !e.AllowConnection ) + return; + + if ( !NagleEnabled ) + e.Socket.SetSocketOption( SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1 ); // RunUO uses its own algorithm + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Test Center/TestCenter.cs b/Scripts/Misc/Test Center/TestCenter.cs new file mode 100644 index 000000000..44d1cc07d --- /dev/null +++ b/Scripts/Misc/Test Center/TestCenter.cs @@ -0,0 +1,246 @@ +using System; +using System.Text; +using Server.Gumps; +using Server.Network; +using Server.Commands; + +namespace Server.Misc +{ + public class TestCenter + { + private const bool m_Enabled = false; + public static bool Enabled { get{ return m_Enabled; } } + + public static void Initialize() + { + // Register our speech handler + if( Enabled ) + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + } + + private static void EventSink_Speech( SpeechEventArgs args ) + { + if ( !args.Handled ) + { + if( Insensitive.StartsWith( args.Speech, "set" ) ) + { + Mobile from = args.Mobile; + + string[] split = args.Speech.Split( ' ' ); + + if ( split.Length == 3 ) + { + try + { + string name = split[1]; + double value = Convert.ToDouble( split[2] ); + + if ( Insensitive.Equals( name, "str" ) ) + ChangeStrength( from, (int)value ); + else if ( Insensitive.Equals( name, "dex" ) ) + ChangeDexterity( from, (int)value ); + else if ( Insensitive.Equals( name, "int" ) ) + ChangeIntelligence( from, (int)value ); + else + ChangeSkill( from, name, value ); + } + catch + { + } + } + } + else if( Insensitive.Equals( args.Speech, "help" ) ) + { + args.Mobile.SendGump( new TCHelpGump() ); + + args.Handled = true; + } + } + } + + private static void ChangeStrength( Mobile from, int value ) + { + if ( value < 10 || value > 125 ) + { + from.SendLocalizedMessage( 1005628 ); // Stats range between 10 and 125. + } + else + { + if ( (value + from.RawDex + from.RawInt) > from.StatCap ) + { + from.SendLocalizedMessage( 1005629 ); // You can not exceed the stat cap. Try setting another stat lower first. + } + else + { + from.RawStr = value; + from.SendLocalizedMessage( 1005630 ); // Your stats have been adjusted. + } + } + } + + private static void ChangeDexterity( Mobile from, int value ) + { + if ( value < 10 || value > 125 ) + { + from.SendLocalizedMessage( 1005628 ); // Stats range between 10 and 125. + } + else + { + if ( (from.RawStr + value + from.RawInt) > from.StatCap ) + { + from.SendLocalizedMessage( 1005629 ); // You can not exceed the stat cap. Try setting another stat lower first. + } + else + { + from.RawDex = value; + from.SendLocalizedMessage( 1005630 ); // Your stats have been adjusted. + } + } + } + + private static void ChangeIntelligence( Mobile from, int value ) + { + if ( value < 10 || value > 125 ) + { + from.SendLocalizedMessage( 1005628 ); // Stats range between 10 and 125. + } + else + { + if ( (from.RawStr + from.RawDex + value) > from.StatCap ) + { + from.SendLocalizedMessage( 1005629 ); // You can not exceed the stat cap. Try setting another stat lower first. + } + else + { + from.RawInt = value; + from.SendLocalizedMessage( 1005630 ); // Your stats have been adjusted. + } + } + } + + private static void ChangeSkill( Mobile from, string name, double value ) + { + SkillName index; + + try + { + index = (SkillName)Enum.Parse( typeof( SkillName ), name, true ); + } + catch + { + from.SendLocalizedMessage( 1005631 ); // You have specified an invalid skill to set. + return; + } + + if ( ( !Core.SE && (int)index > 51 ) || ( !Core.AOS && (int)index > 48 ) ) + { + from.SendLocalizedMessage( 1005631 ); // You have specified an invalid skill to set. + return; + } + + Skill skill = from.Skills[index]; + + if ( skill != null ) + { + if ( value < 0 || value > skill.Cap ) + { + from.SendMessage( String.Format( "Your skill in {0} is capped at {1:F1}.", skill.Info.Name, skill.Cap ) ); + } + else + { + int newFixedPoint = (int)(value * 10.0); + int oldFixedPoint = skill.BaseFixedPoint; + + if ( ((skill.Owner.Total - oldFixedPoint) + newFixedPoint) > skill.Owner.Cap ) + { + from.SendMessage( "You can not exceed the skill cap. Try setting another skill lower first." ); + } + else + { + skill.BaseFixedPoint = newFixedPoint; + } + } + } + else + { + from.SendLocalizedMessage( 1005631 ); // You have specified an invalid skill to set. + } + } + + + public class TCHelpGump : Gump + { + public TCHelpGump() : base( 40, 40 ) + { + AddPage( 0 ); + AddBackground( 0, 0, 160, 120, 5054 ); + + AddButton( 10, 10, 0xFB7, 0xFB9, 1, GumpButtonType.Reply, 0 ); + AddLabel( 45, 10, 0x34, "RunUO.com" ); + + AddButton( 10, 35, 0xFB7, 0xFB9, 2, GumpButtonType.Reply, 0 ); + AddLabel( 45, 35, 0x34, "List of skills" ); + + AddButton( 10, 60, 0xFB7, 0xFB9, 3, GumpButtonType.Reply, 0 ); + AddLabel( 45, 60, 0x34, "Command list" ); + + AddButton( 10, 85, 0xFB1, 0xFB3, 0, GumpButtonType.Reply, 0 ); + AddLabel( 45, 85, 0x34, "Close" ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + switch ( info.ButtonID ) + { + case 1: // RunUO.com + { + sender.LaunchBrowser( "http://www.RunUO.com" ); + break; + } + case 2: // List of skills + { + string[] strings = Enum.GetNames( typeof( SkillName ) ); + + Array.Sort( strings ); + + StringBuilder sb = new StringBuilder(); + + if ( strings.Length > 0 ) + sb.Append( strings[0] ); + + for ( int i = 1; i < strings.Length; ++i ) + { + string v = strings[i]; + + if ( (sb.Length + 1 + v.Length) >= 256 ) + { + sender.Send( new AsciiMessage( Server.Serial.MinusOne, -1, MessageType.Label, 0x35, 3, "System", sb.ToString() ) ); + sb = new StringBuilder(); + sb.Append( v ); + } + else + { + sb.Append( ' ' ); + sb.Append( v ); + } + } + + if ( sb.Length > 0 ) + { + sender.Send( new AsciiMessage( Server.Serial.MinusOne, -1, MessageType.Label, 0x35, 3, "System", sb.ToString() ) ); + } + + break; + } + case 3: // Command list + { + sender.Mobile.SendAsciiMessage( 0x482, "The command prefix is \"{0}\"", CommandSystem.Prefix ); + CommandHandlers.Help_OnCommand( new CommandEventArgs( sender.Mobile, "help", "", new string[0] ) ); + + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/TextDefinition.cs b/Scripts/Misc/TextDefinition.cs new file mode 100644 index 000000000..7c5d37d6d --- /dev/null +++ b/Scripts/Misc/TextDefinition.cs @@ -0,0 +1,97 @@ +using System; + +namespace Server +{ + public class TextDefinition + { + private int m_Number; + private string m_String; + + public int Number{ get{ return m_Number; } } + public string String{ get{ return m_String; } } + + public TextDefinition( int number ) : this( number, null ) + { + } + + public TextDefinition( string text ) : this( 0, text ) + { + } + + public TextDefinition( int number, string text ) + { + m_Number = number; + m_String = text; + } + + public override string ToString() + { + if ( m_Number > 0 ) + return "#" + m_Number.ToString(); + else if ( m_String != null ) + return m_String; + + return "(empty)"; + } + + public static void AddTo( ObjectPropertyList list, TextDefinition def ) + { + if ( def != null && def.m_Number > 0 ) + list.Add( def.m_Number ); + else if ( def != null && def.m_String != null ) + list.Add( def.m_String ); + } + + public static implicit operator TextDefinition ( int v ) + { + return new TextDefinition( v ); + } + + public static implicit operator TextDefinition ( string s ) + { + return new TextDefinition( s ); + } + + public static implicit operator int ( TextDefinition m ) + { + if ( m == null ) + return 0; + + return m.m_Number; + } + + public static implicit operator string ( TextDefinition m ) + { + if ( m == null ) + return null; + + return m.m_String; + } + + public static void AddHtmlText( Server.Gumps.Gump g, int x, int y, int width, int height, TextDefinition text, bool back, bool scroll, int numberColor, int stringColor ) + { + if( text != null && text.Number > 0 ) + if( numberColor >= 0 ) + g.AddHtmlLocalized( x, y, width, height, text.Number, numberColor, back, scroll ); + else + g.AddHtmlLocalized( x, y, width, height, text.Number, back, scroll ); + else if( text != null && text.String != null ) + if( stringColor >= 0 ) + g.AddHtml( x, y, width, height, String.Format( "{1}", stringColor, text.String ), back, scroll ); + else + g.AddHtml( x, y, width, height, text.String, back, scroll ); + } + public static void AddHtmlText( Server.Gumps.Gump g, int x, int y, int width, int height, TextDefinition text, bool back, bool scroll ) + { + AddHtmlText( g, x, y, width, height, text, back, scroll, -1, -1 ); + } + + public static void SendMessageTo( Mobile m, TextDefinition def ) + { + if( def.Number > 0 ) + m.SendLocalizedMessage( def.Number ); + else if( def.String != null ) + m.SendMessage( def.String ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Titles.cs b/Scripts/Misc/Titles.cs new file mode 100644 index 000000000..a9325af3b --- /dev/null +++ b/Scripts/Misc/Titles.cs @@ -0,0 +1,390 @@ +using System; +using System.Text; +using Server; +using Server.Mobiles; +using Server.Engines.CannedEvil; + +namespace Server.Misc +{ + public class Titles + { + public const int MinFame = 0; + public const int MaxFame = 15000; + + public static void AwardFame( Mobile m, int offset, bool message ) + { + if ( offset > 0 ) + { + if ( m.Fame >= MaxFame ) + return; + + offset -= m.Fame / 100; + + if ( offset < 0 ) + offset = 0; + } + else if ( offset < 0 ) + { + if ( m.Fame <= MinFame ) + return; + + offset -= m.Fame / 100; + + if ( offset > 0 ) + offset = 0; + } + + if ( (m.Fame + offset) > MaxFame ) + offset = MaxFame - m.Fame; + else if ( (m.Fame + offset) < MinFame ) + offset = MinFame - m.Fame; + + m.Fame += offset; + + if ( message ) + { + if ( offset > 40 ) + m.SendLocalizedMessage( 1019054 ); // You have gained a lot of fame. + else if ( offset > 20 ) + m.SendLocalizedMessage( 1019053 ); // You have gained a good amount of fame. + else if ( offset > 10 ) + m.SendLocalizedMessage( 1019052 ); // You have gained some fame. + else if ( offset > 0 ) + m.SendLocalizedMessage( 1019051 ); // You have gained a little fame. + else if ( offset < -40 ) + m.SendLocalizedMessage( 1019058 ); // You have lost a lot of fame. + else if ( offset < -20 ) + m.SendLocalizedMessage( 1019057 ); // You have lost a good amount of fame. + else if ( offset < -10 ) + m.SendLocalizedMessage( 1019056 ); // You have lost some fame. + else if ( offset < 0 ) + m.SendLocalizedMessage( 1019055 ); // You have lost a little fame. + } + } + + public const int MinKarma = -15000; + public const int MaxKarma = 15000; + + public static void AwardKarma( Mobile m, int offset, bool message ) + { + if ( offset > 0 ) + { + if ( m is PlayerMobile && ((PlayerMobile)m).KarmaLocked ) + return; + + if ( m.Karma >= MaxKarma ) + return; + + offset -= m.Karma / 100; + + if ( offset < 0 ) + offset = 0; + } + else if ( offset < 0 ) + { + if ( m.Karma <= MinKarma ) + return; + + offset -= m.Karma / 100; + + if ( offset > 0 ) + offset = 0; + } + + if ( (m.Karma + offset) > MaxKarma ) + offset = MaxKarma - m.Karma; + else if ( (m.Karma + offset) < MinKarma ) + offset = MinKarma - m.Karma; + + bool wasPositiveKarma = ( m.Karma >= 0 ); + + m.Karma += offset; + + if ( message ) + { + if ( offset > 40 ) + m.SendLocalizedMessage( 1019062 ); // You have gained a lot of karma. + else if ( offset > 20 ) + m.SendLocalizedMessage( 1019061 ); // You have gained a good amount of karma. + else if ( offset > 10 ) + m.SendLocalizedMessage( 1019060 ); // You have gained some karma. + else if ( offset > 0 ) + m.SendLocalizedMessage( 1019059 ); // You have gained a little karma. + else if ( offset < -40 ) + m.SendLocalizedMessage( 1019066 ); // You have lost a lot of karma. + else if ( offset < -20 ) + m.SendLocalizedMessage( 1019065 ); // You have lost a good amount of karma. + else if ( offset < -10 ) + m.SendLocalizedMessage( 1019064 ); // You have lost some karma. + else if ( offset < 0 ) + m.SendLocalizedMessage( 1019063 ); // You have lost a little karma. + } + + if ( wasPositiveKarma && m.Karma < 0 && m is PlayerMobile && !((PlayerMobile)m).KarmaLocked ) + { + ((PlayerMobile)m).KarmaLocked = true; + m.SendLocalizedMessage( 1042511, "", 0x22 ); // Karma is locked. A mantra spoken at a shrine will unlock it again. + } + } + + public static string[] HarrowerTitles = new string[] { "Spite", "Opponent", "Hunter", "Venom", "Executioner", "Annihilator", "Champion" }; + + public static string ComputeTitle( Mobile beholder, Mobile beheld ) + { + StringBuilder title = new StringBuilder(); + + int fame = beheld.Fame; + int karma = beheld.Karma; + + bool showSkillTitle = beheld.ShowFameTitle && ( (beholder == beheld) || (fame >= 5000) ); + + /*if ( beheld.Kills >= 5 ) + { + title.AppendFormat( beheld.Fame >= 10000 ? "The Murderer {1} {0}" : "The Murderer {0}", beheld.Name, beheld.Female ? "Lady" : "Lord" ); + } + else*/if ( beheld.ShowFameTitle || (beholder == beheld) ) + { + for ( int i = 0; i < m_FameEntries.Length; ++i ) + { + FameEntry fe = m_FameEntries[i]; + + if ( fame <= fe.m_Fame || i == (m_FameEntries.Length - 1) ) + { + KarmaEntry[] karmaEntries = fe.m_Karma; + + for ( int j = 0; j < karmaEntries.Length; ++j ) + { + KarmaEntry ke = karmaEntries[j]; + + if ( karma <= ke.m_Karma || j == (karmaEntries.Length - 1) ) + { + title.AppendFormat( ke.m_Title, beheld.Name, beheld.Female ? "Lady" : "Lord" ); + break; + } + } + + break; + } + } + } + else + { + title.Append( beheld.Name ); + } + + if( beheld is PlayerMobile && ((PlayerMobile)beheld).DisplayChampionTitle ) + { + PlayerMobile.ChampionTitleInfo info = ((PlayerMobile)beheld).ChampionTitles; + + if( info.Harrower > 0 ) + title.AppendFormat( ": {0} of Evil", HarrowerTitles[Math.Min( HarrowerTitles.Length, info.Harrower )-1] ); + else + { + int highestValue = 0, highestType = 0; + for( int i = 0; i < ChampionSpawnInfo.Table.Length; i++ ) + { + int v = info.GetValue( i ); + + if( v > highestValue ) + { + highestValue = v; + highestType = i; + } + } + + int offset = 0; + if( highestValue > 800 ) + offset = 3; + else if( highestValue > 300 ) + offset = (int)(highestValue/300); + + if( offset > 0 ) + { + ChampionSpawnInfo champInfo = ChampionSpawnInfo.GetInfo( (ChampionSpawnType)highestType ); + title.AppendFormat( ": {0} of the {1}", champInfo.LevelNames[Math.Min( offset, champInfo.LevelNames.Length ) -1], champInfo.Name ); + } + } + } + + string customTitle = beheld.Title; + + if ( customTitle != null && (customTitle = customTitle.Trim()).Length > 0 ) + { + title.AppendFormat( " {0}", customTitle ); + } + else if ( showSkillTitle && beheld.Player ) + { + Skill highest = GetHighestSkill( beheld );// beheld.Skills.Highest; + + if ( highest != null && highest.BaseFixedPoint >= 300 ) + { + string skillLevel = GetSkillLevel( highest ); + string skillTitle = highest.Info.Title; + + if ( beheld.Female && skillTitle.EndsWith( "man" ) ) + skillTitle = skillTitle.Substring( 0, skillTitle.Length - 3 ) + "woman"; + + title.AppendFormat( ", {0} {1}", skillLevel, skillTitle ); + } + } + + return title.ToString(); + } + + private static Skill GetHighestSkill( Mobile m ) + { + Skills skills = m.Skills; + + if ( !Core.AOS ) + return skills.Highest; + + Skill highest = null; + + for ( int i = 0; i < m.Skills.Length; ++i ) + { + Skill check = m.Skills[i]; + + if ( highest == null || check.BaseFixedPoint > highest.BaseFixedPoint ) + highest = check; + else if ( highest != null && highest.Lock != SkillLock.Up && check.Lock == SkillLock.Up && check.BaseFixedPoint == highest.BaseFixedPoint ) + highest = check; + } + + return highest; + } + + private static string[,] m_Levels = new string[,] + { + { "Neophyte", "Neophyte", "Neophyte" }, + { "Novice", "Novice", "Novice" }, + { "Apprentice", "Apprentice", "Apprentice" }, + { "Journeyman", "Journeyman", "Journeyman" }, + { "Expert", "Expert", "Expert" }, + { "Adept", "Adept", "Adept" }, + { "Master", "Master", "Master" }, + { "Grandmaster", "Grandmaster", "Grandmaster" }, + { "Elder", "Tatsujin", "Shinobi" }, + { "Legendary", "Kengo", "Ka-ge" } + }; + + private static string GetSkillLevel( Skill skill ) + { + return m_Levels[GetTableIndex( skill ), GetTableType( skill )]; + } + + private static int GetTableType( Skill skill ) + { + switch ( skill.SkillName ) + { + default: return 0; + case SkillName.Bushido: return 1; + case SkillName.Ninjitsu: return 2; + } + } + + private static int GetTableIndex( Skill skill ) + { + int fp = Math.Min( skill.BaseFixedPoint, 1200 ); + + return (fp - 300) / 100; + } + + private static FameEntry[] m_FameEntries = new FameEntry[] + { + new FameEntry( 1249, new KarmaEntry[] + { + new KarmaEntry( -10000, "The Outcast {0}" ), + new KarmaEntry( -5000, "The Despicable {0}" ), + new KarmaEntry( -2500, "The Scoundrel {0}" ), + new KarmaEntry( -1250, "The Unsavory {0}" ), + new KarmaEntry( -625, "The Rude {0}" ), + new KarmaEntry( 624, "{0}" ), + new KarmaEntry( 1249, "The Fair {0}" ), + new KarmaEntry( 2499, "The Kind {0}" ), + new KarmaEntry( 4999, "The Good {0}" ), + new KarmaEntry( 9999, "The Honest {0}" ), + new KarmaEntry( 10000, "The Trustworthy {0}" ) + } ), + new FameEntry( 2499, new KarmaEntry[] + { + new KarmaEntry( -10000, "The Wretched {0}" ), + new KarmaEntry( -5000, "The Dastardly {0}" ), + new KarmaEntry( -2500, "The Malicious {0}" ), + new KarmaEntry( -1250, "The Dishonorable {0}" ), + new KarmaEntry( -625, "The Disreputable {0}" ), + new KarmaEntry( 624, "The Notable {0}" ), + new KarmaEntry( 1249, "The Upstanding {0}" ), + new KarmaEntry( 2499, "The Respectable {0}" ), + new KarmaEntry( 4999, "The Honorable {0}" ), + new KarmaEntry( 9999, "The Commendable {0}" ), + new KarmaEntry( 10000, "The Estimable {0}" ) + } ), + new FameEntry( 4999, new KarmaEntry[] + { + new KarmaEntry( -10000, "The Nefarious {0}" ), + new KarmaEntry( -5000, "The Wicked {0}" ), + new KarmaEntry( -2500, "The Vile {0}" ), + new KarmaEntry( -1250, "The Ignoble {0}" ), + new KarmaEntry( -625, "The Notorious {0}" ), + new KarmaEntry( 624, "The Prominent {0}" ), + new KarmaEntry( 1249, "The Reputable {0}" ), + new KarmaEntry( 2499, "The Proper {0}" ), + new KarmaEntry( 4999, "The Admirable {0}" ), + new KarmaEntry( 9999, "The Famed {0}" ), + new KarmaEntry( 10000, "The Great {0}" ) + } ), + new FameEntry( 9999, new KarmaEntry[] + { + new KarmaEntry( -10000, "The Dread {0}" ), + new KarmaEntry( -5000, "The Evil {0}" ), + new KarmaEntry( -2500, "The Villainous {0}" ), + new KarmaEntry( -1250, "The Sinister {0}" ), + new KarmaEntry( -625, "The Infamous {0}" ), + new KarmaEntry( 624, "The Renowned {0}" ), + new KarmaEntry( 1249, "The Distinguished {0}" ), + new KarmaEntry( 2499, "The Eminent {0}" ), + new KarmaEntry( 4999, "The Noble {0}" ), + new KarmaEntry( 9999, "The Illustrious {0}" ), + new KarmaEntry( 10000, "The Glorious {0}" ) + } ), + new FameEntry( 10000, new KarmaEntry[] + { + new KarmaEntry( -10000, "The Dread {1} {0}" ), + new KarmaEntry( -5000, "The Evil {1} {0}" ), + new KarmaEntry( -2500, "The Dark {1} {0}" ), + new KarmaEntry( -1250, "The Sinister {1} {0}" ), + new KarmaEntry( -625, "The Dishonored {1} {0}" ), + new KarmaEntry( 624, "{1} {0}" ), + new KarmaEntry( 1249, "The Distinguished {1} {0}" ), + new KarmaEntry( 2499, "The Eminent {1} {0}" ), + new KarmaEntry( 4999, "The Noble {1} {0}" ), + new KarmaEntry( 9999, "The Illustrious {1} {0}" ), + new KarmaEntry( 10000, "The Glorious {1} {0}" ) + } ) + }; + } + + public class FameEntry + { + public int m_Fame; + public KarmaEntry[] m_Karma; + + public FameEntry( int fame, KarmaEntry[] karma ) + { + m_Fame = fame; + m_Karma = karma; + } + } + + public class KarmaEntry + { + public int m_Karma; + public string m_Title; + + public KarmaEntry( int karma, string title ) + { + m_Karma = karma; + m_Title = title; + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/TreasureMapProtection.cs b/Scripts/Misc/TreasureMapProtection.cs new file mode 100644 index 000000000..af4a018dd --- /dev/null +++ b/Scripts/Misc/TreasureMapProtection.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; +using System.Collections; +using Server; +using Server.Regions; + +namespace Server +{ + public class TreasureRegion : BaseRegion + { + private const int Range = 5; // No house may be placed within 5 tiles of the treasure + + public TreasureRegion( int x, int y, Map map ): base( null, map, Region.DefaultPriority, new Rectangle2D( x - Range, y - Range, 1 + (Range * 2), 1 + (Range * 2) ) ) + { + GoLocation = new Point3D( x, y, map.GetAverageZ( x, y ) ); + + Register(); + } + + public static void Initialize() + { + string filePath = Path.Combine( Core.BaseDirectory, "Data/treasure.cfg" ); + int i = 0, x = 0, y = 0; + + if ( File.Exists( filePath ) ) + { + using ( StreamReader ip = new StreamReader( filePath ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + i++; + + try + { + string[] split = line.Split( ' ' ); + + x = Convert.ToInt32( split[0] ); + y = Convert.ToInt32( split[1] ); + + try + { + new TreasureRegion( x, y, Map.Felucca ); + new TreasureRegion( x, y, Map.Trammel ); + } + catch ( Exception e ) + { + Console.WriteLine( "{0} {1} {2} {3}", i, x, y, e ); + } + } + catch + { + } + } + } + } + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return false; + } + + public override void OnEnter( Mobile m ) + { + if ( m.AccessLevel > AccessLevel.Player ) + m.SendMessage( "You have entered a protected treasure map area." ); + } + + public override void OnExit( Mobile m ) + { + if ( m.AccessLevel > AccessLevel.Player ) + m.SendMessage( "You have left a protected treasure map area." ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/VendorGenerator.cs b/Scripts/Misc/VendorGenerator.cs new file mode 100644 index 000000000..6f6a3a3c0 --- /dev/null +++ b/Scripts/Misc/VendorGenerator.cs @@ -0,0 +1,536 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Commands; +using Server.Mobiles; + +namespace Server +{ + public class VendorGenerator + { + public static void Initialize() + { + CommandSystem.Register( "VendorGen", AccessLevel.Administrator, new CommandEventHandler( VendorGenerator.VendorGen_OnCommand ) ); + } + + private static Rectangle2D[] m_BritRegions = new Rectangle2D[] + { + new Rectangle2D( new Point2D( 250, 750 ), new Point2D( 775, 1330 ) ), + new Rectangle2D( new Point2D( 525, 2095 ), new Point2D( 925, 2430 ) ), + new Rectangle2D( new Point2D( 1025, 2155 ), new Point2D( 1265, 2310 ) ), + new Rectangle2D( new Point2D( 1635, 2430 ), new Point2D( 1705, 2508 ) ), + new Rectangle2D( new Point2D( 1775, 2605 ), new Point2D( 2165, 2975 ) ), + new Rectangle2D( new Point2D( 1055, 3520 ), new Point2D( 1570, 4075 ) ), + new Rectangle2D( new Point2D( 2860, 3310 ), new Point2D( 3120, 3630 ) ), + new Rectangle2D( new Point2D( 2470, 1855 ), new Point2D( 3950, 3045 ) ), + new Rectangle2D( new Point2D( 3425, 990 ), new Point2D( 3900, 1455 ) ), + new Rectangle2D( new Point2D( 4175, 735 ), new Point2D( 4840, 1600 ) ), + new Rectangle2D( new Point2D( 2375, 330 ), new Point2D( 3100, 1045 ) ), + new Rectangle2D( new Point2D( 2100, 1090 ), new Point2D( 2310, 1450 ) ), + new Rectangle2D( new Point2D( 1495, 1400 ), new Point2D( 1550, 1475 ) ), + new Rectangle2D( new Point2D( 1085, 1520 ), new Point2D( 1415, 1910 ) ), + new Rectangle2D( new Point2D( 1410, 1500 ), new Point2D( 1745, 1795 ) ), + new Rectangle2D( new Point2D( 5120, 2300 ), new Point2D( 6143, 4095 ) ) + }; + + private static Rectangle2D[] m_IlshRegions = new Rectangle2D[] + { + new Rectangle2D( new Point2D( 0, 0 ), new Point2D( 288*8, 200*8 ) ) + }; + + [Usage( "VendorGen" )] + [Description( "Generates vendors based on display cases and floor plans. Analyzes the map files, slow." )] + private static void VendorGen_OnCommand( CommandEventArgs e ) + { + Process( Map.Trammel, m_BritRegions ); + Process( Map.Felucca, m_BritRegions ); + Process( Map.Ilshenar, m_IlshRegions ); + } + + private static bool GetFloorZ( Map map, int x, int y, out int z ) + { + Tile lt = map.Tiles.GetLandTile( x, y ); + + if ( IsFloor( lt.ID ) && map.CanFit( x, y, lt.Z, 16, false, false ) ) + { + z = lt.Z; + return true; + } + + Tile[] tiles = map.Tiles.GetStaticTiles( x, y ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile t = tiles[i]; + ItemData id = TileData.ItemTable[t.ID & 0x3FFF]; + + if ( IsStaticFloor( t.ID ) && map.CanFit( x, y, t.Z + (id.Surface ? id.CalcHeight : 0), 16, false, false ) ) + { + z = t.Z + (id.Surface ? id.CalcHeight : 0); + return true; + } + } + + z = 0; + return false; + } + + private static bool IsFloor( Map map, int x, int y, bool canFit ) + { + Tile lt = map.Tiles.GetLandTile( x, y ); + + if ( IsFloor( lt.ID ) && (canFit||CanFit( map, x, y, lt.Z )) ) + return true; + + Tile[] tiles = map.Tiles.GetStaticTiles( x, y ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile t = tiles[i]; + ItemData id = TileData.ItemTable[t.ID & 0x3FFF]; + + if ( IsStaticFloor( t.ID ) && (canFit||CanFit( map, x, y, t.Z + (id.Surface ? id.CalcHeight : 0) )) ) + return true; + } + + return false; + } + + private static bool IsFloor( int itemID ) + { + itemID &= 0x3FFF; + + return ( itemID >= 0x406 && itemID <= 0x51A ); + } + + private static bool IsStaticFloor( int itemID ) + { + itemID &= 0x3FFF; + + return ( itemID >= 0x495 && itemID <= 0x514 ) + || ( itemID >= 0x519 && itemID <= 0x53A ); + } + + private static bool IsDisplayCase( int itemID ) + { + itemID &= 0x3FFF; + + return ( itemID >= 0xB00 && itemID <= 0xB02 ) + || ( itemID >= 0xB06 && itemID <= 0xB0A ) + || ( itemID >= 0xB0D && itemID <= 0xB17 ); + } + + private static void Process( Map map, Rectangle2D[] regions ) + { + m_ShopTable = new Hashtable(); + m_ShopList = new ArrayList(); + + World.Broadcast( 0x35, true, "Generating vendor spawns for {0}, please wait.", map ); + + for ( int i = 0; i < regions.Length; ++i ) + for ( int x = 0; x < map.Width; ++x ) + for ( int y = 0; y < map.Height; ++y ) + CheckPoint( map, regions[i].X + x, regions[i].Y + y ); + + for ( int i = 0; i < m_ShopList.Count; ++i ) + { + ShopInfo si = (ShopInfo)m_ShopList[i]; + + int xTotal = 0; + int yTotal = 0; + + bool hasSpawner = false; + + for ( int j = 0; !hasSpawner && j < si.m_Floor.Count; ++j ) + { + Point2D fp = (Point2D)si.m_Floor[j]; + + xTotal += fp.X; + yTotal += fp.Y; + + IPooledEnumerable eable = map.GetItemsInRange( new Point3D( fp.X, fp.Y, 0 ), 0 ); + + foreach ( Item item in eable ) + { + if ( item is Spawner ) + { + hasSpawner = true; + break; + } + } + + eable.Free(); + + if ( hasSpawner ) + break; + } + + if ( hasSpawner ) + continue; + + int xAvg = xTotal / si.m_Floor.Count; + int yAvg = yTotal / si.m_Floor.Count; + + ArrayList names = new ArrayList(); + ShopFlags flags = si.m_Flags; + + if ( (flags & ShopFlags.Armor) != 0 ) + names.Add( "armorer" ); + + if ( (flags & ShopFlags.MetalWeapon) != 0 ) + names.Add( "weaponsmith" ); + + if ( (flags & ShopFlags.ArcheryWeapon) != 0 ) + names.Add( "bowyer" ); + + if ( (flags & ShopFlags.Scroll) != 0 ) + names.Add( "mage" ); + + if ( (flags & ShopFlags.Spellbook) != 0 ) + names.Add( "mage" ); + + if ( (flags & ShopFlags.Bread) != 0 ) + names.Add( "baker" ); + + if ( (flags & ShopFlags.Jewel) != 0 ) + names.Add( "jeweler" ); + + if ( (flags & ShopFlags.Potion) != 0 ) + { + names.Add( "herbalist" ); + names.Add( "alchemist" ); + names.Add( "mage" ); + } + + if ( (flags & ShopFlags.Reagent) != 0 ) + { + names.Add( "mage" ); + names.Add( "herbalist" ); + } + + if ( (flags & ShopFlags.Clothes) != 0 ) + { + names.Add( "tailor" ); + names.Add( "weaver" ); + } + + for ( int j = 0; j < names.Count; ++j ) + { + Point2D cp = Point2D.Zero; + int dist = 100000; + int tz; + + for ( int k = 0; k < si.m_Floor.Count; ++k ) + { + Point2D fp = (Point2D)si.m_Floor[k]; + + int rx = fp.X - xAvg; + int ry = fp.Y - yAvg; + int fd = (int)Math.Sqrt( rx*rx + ry*ry ); + + if ( fd > 0 && fd < 5 ) + fd -= Utility.Random( 10 ); + + if ( fd < dist && GetFloorZ( map, fp.X, fp.Y, out tz ) ) + { + dist = fd; + cp = fp; + } + } + + if ( cp == Point2D.Zero ) + continue; + + int z; + + if ( !GetFloorZ( map, cp.X, cp.Y, out z ) ) + continue; + + new Spawner( 1, 1, 1, 0, 4, (string)names[j] ).MoveToWorld( new Point3D( cp.X, cp.Y, z ), map ); + } + } + + World.Broadcast( 0x35, true, "Generation complete. {0} spawners generated.", m_ShopList.Count ); + } + + private static void CheckPoint( Map map, int x, int y ) + { + if ( IsFloor( map, x, y, true ) ) + CheckFloor( map, x, y ); + } + + private static void CheckFloor( Map map, int x, int y ) + { + Tile[] tiles = map.Tiles.GetStaticTiles( x, y ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + if ( IsDisplayCase( tiles[i].ID ) ) + { + ProcessDisplayCase( map, tiles, x, y ); + break; + } + } + } + + [Flags] + private enum ShopFlags + { + None = 0x000, + Armor = 0x001, + MetalWeapon = 0x002, + Jewel = 0x004, + Reagent = 0x008, + Potion = 0x010, + Bread = 0x020, + Clothes = 0x040, + ArcheryWeapon = 0x080, + Scroll = 0x100, + Spellbook = 0x200 + } + + private static bool IsClothes( int itemID ) + { + if ( itemID >= 0x1515 && itemID <= 0x1518 ) + return true; + + if ( itemID >= 0x152E && itemID <= 0x1531 ) + return true; + + if ( itemID >= 0x1537 && itemID <= 0x154C ) + return true; + + if ( itemID >= 0x1EFD && itemID <= 0x1F04 ) + return true; + + if ( itemID >= 0x170B && itemID <= 0x171C ) + return true; + + return false; + } + + private static bool IsArmor( int itemID ) + { + if ( itemID >= 0x13BB && itemID <= 0x13E2 ) + return true; + + if ( itemID >= 0x13E5 && itemID <= 0x13F2 ) + return true; + + if ( itemID >= 0x1408 && itemID <= 0x141A ) + return true; + + if ( itemID >= 0x144E && itemID <= 0x1457 ) + return true; + + return false; + } + + private static bool IsMetalWeapon( int itemID ) + { + if ( itemID >= 0xF43 && itemID <= 0xF4E ) + return true; + + if ( itemID >= 0xF51 && itemID <= 0xF52 ) + return true; + + if ( itemID >= 0xF5C && itemID <= 0xF63 ) + return true; + + if ( itemID >= 0x13AF && itemID <= 0x13B0 ) + return true; + + if ( itemID >= 0x13B5 && itemID <= 0x13BA ) + return true; + + if ( itemID >= 0x13FA && itemID <= 0x13FB ) + return true; + + if ( itemID >= 0x13FE && itemID <= 0x1407 ) + return true; + + if ( itemID >= 0x1438 && itemID <= 0x1443 ) + return true; + + return false; + } + + private static bool IsArcheryWeapon( int itemID ) + { + if ( itemID >= 0xF4F && itemID <= 0xF50 ) + return true; + + if ( itemID >= 0x13B1 && itemID <= 0x13B2 ) + return true; + + if ( itemID >= 0x13FC && itemID <= 0x13FD ) + return true; + + return false; + } + + private static ShopFlags ProcessDisplayedItem( int itemID ) + { + itemID &= 0x3FFF; + + ShopFlags res = ShopFlags.None; + + ItemData id = TileData.ItemTable[itemID]; + TileFlag flags = id.Flags; + + if ( (flags & TileFlag.Wearable) != 0 ) + { + if ( IsClothes( itemID ) ) + res |= ShopFlags.Clothes; + else if ( IsArmor( itemID ) ) + res |= ShopFlags.Armor; + else if ( IsMetalWeapon( itemID ) ) + res |= ShopFlags.MetalWeapon; + else if ( IsArcheryWeapon( itemID ) ) + res |= ShopFlags.ArcheryWeapon; + } + + if ( itemID == 0x98C || itemID == 0x103B || itemID == 0x103C ) + res |= ShopFlags.Bread; + + if ( itemID >= 0xF0F && itemID <= 0xF30 ) + res |= ShopFlags.Jewel; + + if ( itemID >= 0xEFB && itemID <= 0xF0D ) + res |= ShopFlags.Potion; + + if ( itemID >= 0xF78 && itemID <= 0xF91 ) + res |= ShopFlags.Reagent; + + if ( (itemID >= 0xE35 && itemID <= 0xE3A) || (itemID >= 0xEF4 && itemID <= 0xEF9) || (itemID >= 0x1F2D && itemID <= 0x1F72) ) + res |= ShopFlags.Scroll; + + if ( itemID == 0xE38 || itemID == 0xEFA ) + res |= ShopFlags.Spellbook; + + return res; + } + + private static void ProcessDisplayCase( Map map, Tile[] tiles, int x, int y ) + { + ShopFlags flags = ShopFlags.None; + + for ( int i = 0; i < tiles.Length; ++i ) + flags |= ProcessDisplayedItem( tiles[i].ID ); + + if ( flags != ShopFlags.None ) + { + Point2D p = new Point2D( x, y ); + ShopInfo si = (ShopInfo)m_ShopTable[p]; + + if ( si == null ) + { + ArrayList floor = new ArrayList(); + + RecurseFindFloor( map, x, y, floor ); + + if ( floor.Count == 0 ) + return; + + si = new ShopInfo(); + si.m_Flags = flags; + si.m_Floor = floor; + m_ShopList.Add( si ); + + for ( int i = 0; i < floor.Count; ++i ) + m_ShopTable[(Point2D)floor[i]] = si; + } + else + { + si.m_Flags |= flags; + } + } + } + + private static Hashtable m_ShopTable; + private static ArrayList m_ShopList; + + private class ShopInfo + { + public ShopFlags m_Flags; + public ArrayList m_Floor; + } + + private static bool CanFit( Map map, int x, int y, int z ) + { + bool hasSurface = false; + + Tile lt = map.Tiles.GetLandTile( x, y ); + int lowZ = 0, avgZ = 0, topZ = 0; + + map.GetAverageZ( x, y, ref lowZ, ref avgZ, ref topZ ); + TileFlag landFlags = TileData.LandTable[lt.ID & 0x3FFF].Flags; + + if ( (landFlags & TileFlag.Impassable) != 0 && topZ > z && (z + 16) > lowZ ) + return false; + else if ( (landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored ) + hasSurface = true; + + Tile[] staticTiles = map.Tiles.GetStaticTiles( x, y ); + + bool surface, impassable; + + for ( int i = 0; i < staticTiles.Length; ++i ) + { + if ( IsDisplayCase( staticTiles[i].ID ) ) + continue; + + ItemData id = TileData.ItemTable[staticTiles[i].ID & 0x3FFF]; + + surface = id.Surface; + impassable = id.Impassable; + + if ( (surface || impassable) && (staticTiles[i].Z + id.CalcHeight) > z && (z + 16) > staticTiles[i].Z ) + return false; + else if ( surface && !impassable && z == (staticTiles[i].Z + id.CalcHeight) ) + hasSurface = true; + } + + Sector sector = map.GetSector( x, y ); + List items = sector.Items; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + if ( item.AtWorldPoint( x, y ) ) + { + ItemData id = item.ItemData; + surface = id.Surface; + impassable = id.Impassable; + + if ( (surface || impassable) && (item.Z + id.CalcHeight) > z && (z + 16) > item.Z ) + return false; + else if ( surface && !impassable && z == (item.Z + id.CalcHeight) ) + hasSurface = true; + } + } + + return hasSurface; + } + + private static void RecurseFindFloor( Map map, int x, int y, ArrayList floor ) + { + Point2D p = new Point2D( x, y ); + + if ( floor.Contains( p ) ) + return; + + floor.Add( p ); + + for ( int xo = -1; xo <= 1; ++xo ) + { + for ( int yo = -1; yo <= 1; ++yo ) + { + if ( (xo != 0 || yo != 0) && IsFloor( map, x + xo, y + yo, false ) ) + RecurseFindFloor( map, x + xo, y + yo, floor ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/Weather.cs b/Scripts/Misc/Weather.cs new file mode 100644 index 000000000..155fea518 --- /dev/null +++ b/Scripts/Misc/Weather.cs @@ -0,0 +1,394 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Network; + +namespace Server.Misc +{ + public class Weather + { + private static Map[] m_Facets; + private static Dictionary> m_WeatherByFacet = new Dictionary>(); + + public static void Initialize() + { + m_Facets = new Map[]{ Map.Felucca, Map.Trammel }; + + /* Static weather: + * + * Format: + * AddWeather( temperature, chanceOfPercipitation, chanceOfExtremeTemperature, ); + */ + + // ice island + AddWeather( -15, 100, 5, new Rectangle2D( 3850, 160, 390, 320 ), new Rectangle2D( 3900, 480, 380, 180 ), new Rectangle2D( 4160, 660, 150, 110 ) ); + + // covetous entrance, around vesper and minoc + AddWeather( +15, 50, 5, new Rectangle2D( 2425, 725, 250, 250 ) ); + + // despise entrance, north of britain + AddWeather( +15, 50, 5, new Rectangle2D( 1245, 1045, 250, 250 ) ); + + + /* Dynamic weather: + * + * Format: + * AddDynamicWeather( temperature, chanceOfPercipitation, chanceOfExtremeTemperature, moveSpeed, width, height, bounds ); + */ + + for ( int i = 0; i < 15; ++i ) + AddDynamicWeather( +15, 100, 5, 8, 400, 400, new Rectangle2D( 0, 0, 5120, 4096 ) ); + } + + public static List GetWeatherList( Map facet ) + { + if ( facet == null ) + return null; + + List list = null; + m_WeatherByFacet.TryGetValue( facet, out list ); + + if ( list == null ) + m_WeatherByFacet[facet] = list = new List(); + + return list; + } + + public static void AddDynamicWeather( int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, int moveSpeed, int width, int height, Rectangle2D bounds ) + { + for ( int i = 0; i < m_Facets.Length; ++i ) + { + Rectangle2D area = new Rectangle2D(); + bool isValid = false; + + for ( int j = 0; j < 10; ++j ) + { + area = new Rectangle2D( bounds.X + Utility.Random( bounds.Width - width ), bounds.Y + Utility.Random( bounds.Height - height ), width, height ); + + if ( !CheckWeatherConflict( m_Facets[i], null, area ) ) + isValid = true; + + if ( isValid ) + break; + } + + if ( !isValid ) + continue; + + Weather w = new Weather( m_Facets[i], new Rectangle2D[]{ area }, temperature, chanceOfPercipitation, chanceOfExtremeTemperature, TimeSpan.FromSeconds( 30.0 ) ); + + w.m_Bounds = bounds; + w.m_MoveSpeed = moveSpeed; + } + } + + public static void AddWeather( int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, params Rectangle2D[] area ) + { + for ( int i = 0; i < m_Facets.Length; ++i ) + new Weather( m_Facets[i], area, temperature, chanceOfPercipitation, chanceOfExtremeTemperature, TimeSpan.FromSeconds( 30.0 ) ); + } + + public static bool CheckWeatherConflict( Map facet, Weather exclude, Rectangle2D area ) + { + List list = GetWeatherList( facet ); + + if ( list == null ) + return false; + + for ( int i = 0; i < list.Count; ++i ) + { + Weather w = list[i]; + + if ( w != exclude && w.IntersectsWith( area ) ) + return true; + } + + return false; + } + + private Map m_Facet; + private Rectangle2D[] m_Area; + private int m_Temperature; + private int m_ChanceOfPercipitation; + private int m_ChanceOfExtremeTemperature; + + public Map Facet{ get{ return m_Facet; } } + public Rectangle2D[] Area{ get{ return m_Area; } set{ m_Area = value; } } + public int Temperature{ get{ return m_Temperature; } set{ m_Temperature = value; } } + public int ChanceOfPercipitation{ get{ return m_ChanceOfPercipitation; } set{ m_ChanceOfPercipitation = value; } } + public int ChanceOfExtremeTemperature{ get{ return m_ChanceOfExtremeTemperature; } set{ m_ChanceOfExtremeTemperature = value; } } + + // For dynamic weather: + private Rectangle2D m_Bounds; + private int m_MoveSpeed; + private int m_MoveAngleX, m_MoveAngleY; + + public Rectangle2D Bounds{ get{ return m_Bounds; } set{ m_Bounds = value; } } + public int MoveSpeed{ get{ return m_MoveSpeed; } set{ m_MoveSpeed = value; } } + public int MoveAngleX{ get{ return m_MoveAngleX; } set{ m_MoveAngleX = value; } } + public int MoveAngleY{ get{ return m_MoveAngleY; } set{ m_MoveAngleY = value; } } + + public static bool CheckIntersection( Rectangle2D r1, Rectangle2D r2 ) + { + if ( r1.X >= (r2.X + r2.Width) ) + return false; + + if ( r2.X >= (r1.X + r1.Width) ) + return false; + + if ( r1.Y >= (r2.Y + r2.Height) ) + return false; + + if ( r2.Y >= (r1.Y + r1.Height) ) + return false; + + return true; + } + + public static bool CheckContains( Rectangle2D big, Rectangle2D small ) + { + if ( small.X < big.X ) + return false; + + if ( small.Y < big.Y ) + return false; + + if ( (small.X + small.Width) > (big.X + big.Width) ) + return false; + + if ( (small.Y + small.Height) > (big.Y + big.Height) ) + return false; + + return true; + } + + public virtual bool IntersectsWith( Rectangle2D area ) + { + for ( int i = 0; i < m_Area.Length; ++i ) + { + if ( CheckIntersection( area, m_Area[i] ) ) + return true; + } + + return false; + } + + public Weather( Map facet, Rectangle2D[] area, int temperature, int chanceOfPercipitation, int chanceOfExtremeTemperature, TimeSpan interval ) + { + m_Facet = facet; + m_Area = area; + m_Temperature = temperature; + m_ChanceOfPercipitation = chanceOfPercipitation; + m_ChanceOfExtremeTemperature = chanceOfExtremeTemperature; + + List list = GetWeatherList( facet ); + + if ( list != null ) + list.Add( this ); + + Timer.DelayCall( TimeSpan.FromSeconds( (0.2+(Utility.RandomDouble()*0.8)) * interval.TotalSeconds ), interval, new TimerCallback( OnTick ) ); + } + + public virtual void Reposition() + { + if ( m_Area.Length == 0 ) + return; + + int width = m_Area[0].Width; + int height = m_Area[0].Height; + + Rectangle2D area = new Rectangle2D(); + bool isValid = false; + + for ( int j = 0; j < 10; ++j ) + { + area = new Rectangle2D( m_Bounds.X + Utility.Random( m_Bounds.Width - width ), m_Bounds.Y + Utility.Random( m_Bounds.Height - height ), width, height ); + + if ( !CheckWeatherConflict( m_Facet, this, area ) ) + isValid = true; + + if ( isValid ) + break; + } + + if ( !isValid ) + return; + + m_Area[0] = area; + } + + public virtual void RecalculateMovementAngle() + { + double angle = Utility.RandomDouble() * Math.PI * 2.0; + + double cos = Math.Cos( angle ); + double sin = Math.Sin( angle ); + + m_MoveAngleX = (int)(100 * cos); + m_MoveAngleY = (int)(100 * sin); + } + + public virtual void MoveForward() + { + if ( m_Area.Length == 0 ) + return; + + for ( int i = 0; i < 5; ++i ) // try 5 times to find a valid spot + { + int xOffset = (m_MoveSpeed * m_MoveAngleX) / 100; + int yOffset = (m_MoveSpeed * m_MoveAngleY) / 100; + + Rectangle2D oldArea = m_Area[0]; + Rectangle2D newArea = new Rectangle2D( oldArea.X + xOffset, oldArea.Y + yOffset, oldArea.Width, oldArea.Height ); + + if ( !CheckWeatherConflict( m_Facet, this, newArea ) && CheckContains( m_Bounds, newArea ) ) + { + m_Area[0] = newArea; + break; + } + + RecalculateMovementAngle(); + } + } + + private int m_Stage; + private bool m_Active; + private bool m_ExtremeTemperature; + + public virtual void OnTick() + { + if ( m_Stage == 0 ) + { + m_Active = ( m_ChanceOfPercipitation > Utility.Random( 100 ) ); + m_ExtremeTemperature = ( m_ChanceOfExtremeTemperature > Utility.Random( 100 ) ); + + if ( m_MoveSpeed > 0 ) + { + Reposition(); + RecalculateMovementAngle(); + } + } + + if ( m_Active ) + { + if ( m_Stage > 0 && m_MoveSpeed > 0 ) + MoveForward(); + + int type, density, temperature; + + temperature = m_Temperature; + + if ( m_ExtremeTemperature ) + temperature *= -1; + + if ( m_Stage < 15 ) + { + density = m_Stage * 5; + } + else + { + density = 150 - (m_Stage * 5); + + if ( density < 10 ) + density = 10; + else if ( density > 70 ) + density = 70; + } + + if ( density == 0 ) + type = 0xFE; + else if ( temperature > 0 ) + type = 0; + else + type = 2; + + List states = NetState.Instances; + + Packet weatherPacket = null; + + for ( int i = 0; i < states.Count; ++i ) + { + NetState ns = states[i]; + Mobile mob = ns.Mobile; + + if ( mob == null || mob.Map != m_Facet ) + continue; + + bool contains = ( m_Area.Length == 0 ); + + for ( int j = 0; !contains && j < m_Area.Length; ++j ) + contains = m_Area[j].Contains( mob.Location ); + + if ( !contains ) + continue; + + if ( weatherPacket == null ) + weatherPacket = Packet.Acquire( new Server.Network.Weather( type, density, temperature ) ); + + ns.Send( weatherPacket ); + } + + Packet.Release( weatherPacket ); + } + + m_Stage++; + m_Stage %= 30; + } + } + + public class WeatherMap : MapItem + { + public override string DefaultName + { + get { return "weather map"; } + } + + [Constructable] + public WeatherMap() + { + SetDisplay( 0, 0, 5119, 4095, 400, 400 ); + } + + public override void OnDoubleClick( Mobile from ) + { + Map facet = from.Map; + + if ( facet == null ) + return; + + List list = Weather.GetWeatherList( facet ); + + ClearPins(); + + for ( int i = 0; i < list.Count; ++i ) + { + Weather w = list[i]; + + for ( int j = 0; j < w.Area.Length; ++j ) + AddWorldPin( w.Area[j].X + (w.Area[j].Width/2), w.Area[j].Y + (w.Area[j].Height/2) ); + } + + base.OnDoubleClick( from ); + } + + public WeatherMap( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/WebStatus.cs b/Scripts/Misc/WebStatus.cs new file mode 100644 index 000000000..4c22b384e --- /dev/null +++ b/Scripts/Misc/WebStatus.cs @@ -0,0 +1,118 @@ +using System; +using System.IO; +using System.Text; +using System.Collections; + +using Server; +using Server.Network; +using Server.Guilds; + +namespace Server.Misc +{ + public class StatusPage : Timer + { + public static void Initialize() + { + new StatusPage().Start(); + } + + public StatusPage() : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 60.0 ) ) + { + Priority = TimerPriority.FiveSeconds; + } + + private static string Encode( string input ) + { + StringBuilder sb = new StringBuilder( input ); + + sb.Replace( "&", "&" ); + sb.Replace( "<", "<" ); + sb.Replace( ">", ">" ); + sb.Replace( "\"", """ ); + sb.Replace( "'", "'" ); + + return sb.ToString(); + } + + protected override void OnTick() + { + if ( !Directory.Exists( "web" ) ) + Directory.CreateDirectory( "web" ); + + using ( StreamWriter op = new StreamWriter( "web/status.html" ) ) + { + op.WriteLine( "" ); + op.WriteLine( " " ); + op.WriteLine( " RunUO Server Status"); + op.WriteLine( " " ); + op.WriteLine( " " ); + op.WriteLine( "

RunUO Server Status

" ); + op.WriteLine( " Online clients:
" ); + op.WriteLine( " " ); + op.WriteLine( " " ); + op.WriteLine( " " ); + op.WriteLine( " " ); + + foreach ( NetState state in NetState.Instances ) + { + Mobile m = state.Mobile; + + if ( m != null ) + { + Guild g = m.Guild as Guild; + + op.Write( " " ); + } + } + + op.WriteLine( " " ); + op.WriteLine( "
NameLocationKillsKarma / Fame
" ); + + if ( g != null ) + { + op.Write( Encode( m.Name ) ); + op.Write( " [" ); + + string title = m.GuildTitle; + + if ( title != null ) + title = title.Trim(); + else + title = ""; + + if ( title.Length > 0 ) + { + op.Write( Encode( title ) ); + op.Write( ", " ); + } + + op.Write( Encode( g.Abbreviation ) ); + + op.Write( ']' ); + } + else + { + op.Write( Encode( m.Name ) ); + } + + op.Write( "" ); + op.Write( m.X ); + op.Write( ", " ); + op.Write( m.Y ); + op.Write( ", " ); + op.Write( m.Z ); + op.Write( " (" ); + op.Write( m.Map ); + op.Write( ")" ); + op.Write( m.Kills ); + op.Write( "" ); + op.Write( m.Karma ); + op.Write( " / " ); + op.Write( m.Fame ); + op.WriteLine( "
" ); + op.WriteLine( " " ); + op.WriteLine( "" ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/WeightOverloading.cs b/Scripts/Misc/WeightOverloading.cs new file mode 100644 index 000000000..1d182b8b0 --- /dev/null +++ b/Scripts/Misc/WeightOverloading.cs @@ -0,0 +1,132 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Misc +{ + public enum DFAlgorithm + { + Standard, + PainSpike + } + + public class WeightOverloading + { + public static void Initialize() + { + EventSink.Movement += new MovementEventHandler( EventSink_Movement ); + } + + private static DFAlgorithm m_DFA; + + public static DFAlgorithm DFA + { + get{ return m_DFA; } + set{ m_DFA = value; } + } + + public static void FatigueOnDamage( Mobile m, int damage ) + { + double fatigue = 0.0; + + switch ( m_DFA ) + { + case DFAlgorithm.Standard: + { + fatigue = (damage * (100.0 / m.Hits) * ((double)m.Stam / 100)) - 5.0; + break; + } + case DFAlgorithm.PainSpike: + { + fatigue = (damage * ((100.0 / m.Hits) + ((50.0 + m.Stam) / 100) - 1.0)) - 5.0; + break; + } + } + + if ( fatigue > 0 ) + m.Stam -= (int)fatigue; + } + + public const int OverloadAllowance = 4; // We can be four stones overweight without getting fatigued + + public static int GetMaxWeight( Mobile m ) + { + //return ((( Core.ML && m.Race == Race.Human) ? 100 : 40 ) + (int)(3.5 * m.Str)); + //Moved to core virtual method for use there + + return m.MaxWeight; + } + + public static void EventSink_Movement( MovementEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !from.Alive || from.AccessLevel > AccessLevel.Player ) + return; + + if ( !from.Player ) + { + // Else it won't work on monsters. + Spells.Ninjitsu.DeathStrike.AddStep( from ); + return; + } + + int maxWeight = GetMaxWeight( from ) + OverloadAllowance; + int overWeight = (Mobile.BodyWeight + from.TotalWeight) - maxWeight; + + if ( overWeight > 0 ) + { + from.Stam -= GetStamLoss( from, overWeight, (e.Direction & Direction.Running) != 0 ); + + if ( from.Stam == 0 ) + { + from.SendLocalizedMessage( 500109 ); // You are too fatigued to move, because you are carrying too much weight! + e.Blocked = true; + return; + } + } + + if ( ((from.Stam * 100) / Math.Max( from.StamMax, 1 )) < 10 ) + --from.Stam; + + if ( from.Stam == 0 ) + { + from.SendLocalizedMessage( 500110 ); // You are too fatigued to move. + e.Blocked = true; + return; + } + + if ( from is PlayerMobile ) + { + int amt = ( from.Mounted ? 48 : 16 ); + PlayerMobile pm = (PlayerMobile)from; + + if ( (++pm.StepsTaken % amt) == 0 ) + --from.Stam; + } + + Spells.Ninjitsu.DeathStrike.AddStep( from ); + } + + public static int GetStamLoss( Mobile from, int overWeight, bool running ) + { + int loss = 5 + (overWeight / 25); + + if ( from.Mounted ) + loss /= 3; + + if ( running ) + loss *= 2; + + return loss; + } + + public static bool IsOverloaded( Mobile m ) + { + if ( !m.Player || !m.Alive || m.AccessLevel > AccessLevel.Player ) + return false; + + return ( (Mobile.BodyWeight + m.TotalWeight) > (GetMaxWeight( m ) + OverloadAllowance) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/WelcomeTimer.cs b/Scripts/Misc/WelcomeTimer.cs new file mode 100644 index 000000000..3140621fd --- /dev/null +++ b/Scripts/Misc/WelcomeTimer.cs @@ -0,0 +1,53 @@ +using System; +using Server.Network; + +namespace Server.Misc +{ + /// + /// This timer spouts some welcome messages to a user at a set interval. It is used on character creation and login. + /// + public class WelcomeTimer : Timer + { + private Mobile m_Mobile; + private int m_State, m_Count; + + private static string[] m_Messages = ( TestCenter.Enabled ? + new string[] + { + "Welcome to this test shard. You are able to customize your character's stats and skills at anytime to anything you wish. To see the commands to do this just say 'help'.", + "You will find a bank check worth 1,000,000 gold in your bank!", + "A spellbook and a bag of reagents has been placed into your bank box.", + "Various tools have been placed into your bank.", + "Various raw materials like ingots, logs, feathers, hides, bottles, etc, have been placed into your bank.", + "5 unmarked recall runes, 5 Felucca moonstones and 5 Trammel moonstones have been placed into your bank box.", + "One of each level of treasure map has been placed in your bank box.", + "You will find 9000 silver pieces deposited into your bank box. Spend it as you see fit and enjoy yourself!", + "You will find 9000 gold pieces deposited into your bank box. Spend it as you see fit and enjoy yourself!", + "A bag of PowerScrolls has been placed in your bank box." + } : + new string[] + { //Yes, this message is a pathetic message, It's suggested that you change it. + "Welcome to this shard.", + "Please enjoy your stay." + } ); + + public WelcomeTimer( Mobile m ) : this( m, m_Messages.Length ) + { + } + + public WelcomeTimer( Mobile m, int count ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 10.0 ) ) + { + m_Mobile = m; + m_Count = count; + } + + protected override void OnTick() + { + if ( m_State < m_Count ) + m_Mobile.SendMessage( 0x35, m_Messages[m_State++] ); + + if ( m_State == m_Count ) + Stop(); + } + } +} \ No newline at end of file diff --git a/Scripts/Misc/uoamVendors.cs b/Scripts/Misc/uoamVendors.cs new file mode 100644 index 000000000..d9ace089b --- /dev/null +++ b/Scripts/Misc/uoamVendors.cs @@ -0,0 +1,353 @@ +using System; +using System.Collections; +using System.IO; +using Server.Mobiles; +using Server.Items; +using Server.Commands; + +// Version 0.8 + +namespace Server +{ + public class UOAMVendorGenerator + { + private static int m_Count; + + //configuration + private const int NPCCount = 2;//2 npcs per type (so a mage spawner will spawn 2 npcs, a alchemist and herbalist spawner will spawn 4 npcs total) + private const int HomeRange = 5;//How far should they wander? + private const bool TotalRespawn = true;//Should we spawn them up right away? + private static TimeSpan MinTime = TimeSpan.FromMinutes( 2.5 );//min spawn time + private static TimeSpan MaxTime = TimeSpan.FromMinutes( 10.0 );//max spawn time + private const int Team = 0;//"team" the npcs are on + + public static void Initialize() + { + CommandSystem.Register( "UOAMVendors", AccessLevel.Administrator, new CommandEventHandler( Generate_OnCommand ) ); + } + + [Usage( "UOAMVendors" )] + [Description( "Generates vendor spawners from Data/Common.MAP (taken from UOAutoMap)" )] + private static void Generate_OnCommand( CommandEventArgs e ) + { + Parse( e.Mobile ); + } + + public static void Parse( Mobile from ) + { + string vendor_path = Path.Combine( Core.BaseDirectory, "Data/Common.map" ); + m_Count = 0; + + if ( File.Exists( vendor_path ) ) + { + ArrayList list = new ArrayList(); + from.SendMessage( "Generating Vendors..." ); + + using ( StreamReader ip = new StreamReader( vendor_path ) ) + { + string line; + + while ( (line = ip.ReadLine()) != null ) + { + int indexOf = line.IndexOf( ':' ); + + if ( indexOf == -1 ) + continue; + + string type = line.Substring( 0, ++indexOf ).Trim(); + string sub = line.Substring( indexOf ).Trim(); + + string[] split = sub.Split( ' ' ); + + if ( split.Length < 3 ) + continue; + + split = new string[]{ type, split[0], split[1], split[2] }; + + switch(split[0].ToLower()) + { + case "-healer:": + PlaceNPC( split[1], split[2], split[3], "Healer", "HealerGuildmaster" ); + break; + case "-baker:": + PlaceNPC( split[1], split[2], split[3], "Baker" ); + break; + case "-vet:": + PlaceNPC( split[1], split[2], split[3], "Veterinarian" ); + break; + case "-gypsymaiden:": + PlaceNPC( split[1], split[2], split[3], "GypsyMaiden" ); + break; + case "-gypsybank:": + PlaceNPC( split[1], split[2], split[3], "GypsyBanker" ); + break; + case "-bank:": + PlaceNPC( split[1], split[2], split[3], "Banker", "Minter" ); + break; + case "-inn:": + PlaceNPC( split[1], split[2], split[3], "Innkeeper" ); + break; + case "-provisioner:": + PlaceNPC( split[1], split[2], split[3], "Provisioner", "Cobbler" ); + break; + case "-tailor:": + PlaceNPC( split[1], split[2], split[3], "Tailor", "Weaver", "TailorGuildmaster" ); + break; + case "-tavern:": + PlaceNPC( split[1], split[2], split[3], "Tavernkeeper", "Waiter", "Cook", "Barkeeper" ); + break; + case "-reagents:": + PlaceNPC( split[1], split[2], split[3], "Herbalist", "Alchemist", "CustomHairstylist" ); + break; + case "-fortuneteller:": + PlaceNPC( split[1], split[2], split[3], "FortuneTeller" ); + break; + case "-holymage:": + PlaceNPC( split[1], split[2], split[3], "HolyMage" ); + break; + case "-chivalrykeeper:": + PlaceNPC( split[1], split[2], split[3], "KeeperOfChivalry" ); + break; + case "-mage:": + PlaceNPC( split[1], split[2], split[3], "Mage", "Alchemist", "MageGuildmaster" ); + break; + case "-arms:": + PlaceNPC( split[1], split[2], split[3], "Armorer", "Weaponsmith" ); + break; + case "-tinker:": + PlaceNPC( split[1], split[2], split[3], "Tinker", "TinkerGuildmaster" ); + break; + case "-gypsystable:": + PlaceNPC( split[1], split[2], split[3], "GypsyAnimalTrainer" ); + break; + case "-stable:": + PlaceNPC( split[1], split[2], split[3], "AnimalTrainer" ); + break; + case "-blacksmith:": + PlaceNPC( split[1], split[2], split[3], "Blacksmith", "BlacksmithGuildmaster" ); + break; + case "-bowyer:": + case "-fletcher:": + PlaceNPC( split[1], split[2], split[3], "Bowyer" ); + break; + case "-carpenter:": + PlaceNPC( split[1], split[2], split[3], "Carpenter", "Architect", "RealEstateBroker" ); + break; + case "-butcher:": + PlaceNPC( split[1], split[2], split[3], "Butcher" ); + break; + case "-jeweler:": + PlaceNPC( split[1], split[2], split[3], "Jeweler" ); + break; + case "-tanner:": + PlaceNPC( split[1], split[2], split[3], "Tanner", "Furtrader" ); + break; + case "-bard:": + PlaceNPC( split[1], split[2], split[3], "Bard", "BardGuildmaster" ); + break; + case "-market:": + PlaceNPC( split[1], split[2], split[3], "Butcher", "Farmer" ); + break; + case "-library:": + PlaceNPC( split[1], split[2], split[3], "Scribe" ); + break; + case "-shipwright:": + PlaceNPC( split[1], split[2], split[3], "Shipwright", "Mapmaker" ); + break; + case "-docks:": + PlaceNPC( split[1], split[2], split[3], "Fisherman" ); + break; + + case "-beekeeper:": + PlaceNPC( split[1], split[2], split[3], "Beekeeper" ); + break; + + + // Guilds & Misc + case "-tinkers guild:": + PlaceNPC( split[1], split[2], split[3], "TinkerGuildmaster" ); + break; + case "-blacksmiths guild:": + PlaceNPC( split[1], split[2], split[3], "BlacksmithGuildmaster" ); + break; + case "-sorcerors guild:": + PlaceNPC( split[1], split[2], split[3], "MageGuildmaster" ); + break; + case "-customs:": break; + case "-painter:": break; + case "-theater:": break; + case "-warriors guild:": + PlaceNPC( split[1], split[2], split[3], "WarriorGuildmaster" ); + break; + case "-archers guild:": + PlaceNPC( split[1], split[2], split[3], "RangerGuildmaster" ); + break; + case "-thieves guild:": + PlaceNPC( split[1], split[2], split[3], "ThiefGuildmaster" ); + break; + case "-miners guild:": + PlaceNPC( split[1], split[2], split[3], "MinerGuildmaster" ); + break; + case "-fishermans guild:": + PlaceNPC( split[1], split[2], split[3], "FisherGuildmaster" ); + break; + case "-merchants guild:": + PlaceNPC( split[1], split[2], split[3], "MerchantGuildmaster" ); + break; + case "-illusionists guild:": break; + case "-armourers guild:": break; + case "-sorcerers guild:": break; + case "-mages guild:": + PlaceNPC( split[1], split[2], split[3], "MageGuildmaster" ); + break; + case "-weapons guild:": break; + case "-bardic guild:": + PlaceNPC( split[1], split[2], split[3], "BardGuildmaster" ); + break; + case "-rogues guild:": + break; + + // Skip + case "+landmark:": + case "-point of interest:": + case "+shrine:": + case "+moongate:": + case "+dungeon:": + case "+scenic:": + case "-gate:": + case "+Body of Water:": + case "+ruins:": + case "+teleporter:": + case "+Terrain:": + case "-exit:": + case "-bridge:": + case "-other:": + case "+champion:": + case "-stairs:": + case "-guild:": + case "+graveyard:": + case "+Island:": + case "+town:": + break; + /*default: + Console.WriteLine(split[0]); + break;*/ + } + } + } + from.SendMessage( "Done, added {0} spawners",m_Count ); + } + else + { + from.SendMessage( "{0} not found!", vendor_path ); + } + } + + public static void PlaceNPC( string sx, string sy, string sm, params string[] types ) + { + if ( types.Length == 0 ) + return; + + int x = Utility.ToInt32( sx ); + int y = Utility.ToInt32( sy ); + int map = Utility.ToInt32( sm ); + + switch ( map ) + { + case 0://Trammel and Felucca + MakeSpawner( types, x, y, Map.Felucca ); + MakeSpawner( types, x, y, Map.Trammel ); + break; + case 1://Felucca + MakeSpawner( types, x, y, Map.Felucca ); + break; + case 2: + MakeSpawner( types, x, y, Map.Trammel ); + break; + case 3: + MakeSpawner( types, x, y, Map.Ilshenar ); + break; + case 4: + MakeSpawner( types, x, y, Map.Malas ); + break; + default: + Console.WriteLine( "UOAM Vendor Parser: Warning, unknown map {0}", map ); + break; + } + } + + public static int GetSpawnerZ( int x, int y, Map map ) + { + int z = map.GetAverageZ( x, y ); + + if ( map.CanFit( x, y, z, 16, false, false, true ) ) + return z; + + for ( int i = 1; i <= 20; ++i ) + { + if ( map.CanFit( x, y, z + i, 16, false, false, true ) ) + return z + i; + + if ( map.CanFit( x, y, z - i, 16, false, false, true ) ) + return z - i; + } + + return z; + } + + private static Queue m_ToDelete = new Queue(); + + public static void ClearSpawners( int x, int y, int z, Map map ) + { + IPooledEnumerable eable = map.GetItemsInRange( new Point3D( x, y, z ), 0 ); + + foreach ( Item item in eable ) + { + if ( item is Spawner && item.Z == z ) + m_ToDelete.Enqueue( item ); + } + + eable.Free(); + + while ( m_ToDelete.Count > 0 ) + ((Item)m_ToDelete.Dequeue()).Delete(); + } + + private static void MakeSpawner( string[] types, int x, int y, Map map ) + { + if ( types.Length == 0 ) + return; + + int z = GetSpawnerZ( x, y, map ); + + ClearSpawners( x, y, z, map ); + + for ( int i = 0; i < types.Length; ++i ) + { + bool isGuildmaster = ( types[i].EndsWith( "Guildmaster" ) ); + + Spawner sp = new Spawner( types[i] ); + + if ( isGuildmaster ) + sp.Count = 1; + else + sp.Count = NPCCount; + + sp.MinDelay = MinTime; + sp.MaxDelay = MaxTime; + sp.Team = Team; + sp.HomeRange = HomeRange; + + sp.MoveToWorld( new Point3D( x, y, z ), map ); + + if ( TotalRespawn ) + { + sp.Respawn(); + sp.BringToHome(); + } + + ++m_Count; + } + } + } +} diff --git a/Scripts/Mobiles/Animals/Bears/BlackBear.cs b/Scripts/Mobiles/Animals/Bears/BlackBear.cs new file mode 100644 index 000000000..f10c556be --- /dev/null +++ b/Scripts/Mobiles/Animals/Bears/BlackBear.cs @@ -0,0 +1,69 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a bear corpse" )] + [TypeAlias( "Server.Mobiles.Bear" )] + public class BlackBear : BaseCreature + { + [Constructable] + public BlackBear() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a black bear"; + Body = 211; + BaseSoundID = 0xA3; + + SetStr( 76, 100 ); + SetDex( 56, 75 ); + SetInt( 11, 14 ); + + SetHits( 46, 60 ); + SetMana( 0 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 20.1, 40.0 ); + SetSkill( SkillName.Tactics, 40.1, 60.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 24; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 35.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.Meat | FoodType.FruitsAndVegies; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Bear; } } + + public BlackBear( Serial serial ) : base( serial ) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Bears/BrownBear.cs b/Scripts/Mobiles/Animals/Bears/BrownBear.cs new file mode 100644 index 000000000..aaeb456bd --- /dev/null +++ b/Scripts/Mobiles/Animals/Bears/BrownBear.cs @@ -0,0 +1,68 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a bear corpse" )] + public class BrownBear : BaseCreature + { + [Constructable] + public BrownBear() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a brown bear"; + Body = 167; + BaseSoundID = 0xA3; + + SetStr( 76, 100 ); + SetDex( 26, 45 ); + SetInt( 23, 47 ); + + SetHits( 46, 60 ); + SetMana( 0 ); + + SetDamage( 6, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 30 ); + SetResistance( ResistanceType.Cold, 15, 20 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + + SetSkill( SkillName.MagicResist, 25.1, 35.0 ); + SetSkill( SkillName.Tactics, 40.1, 60.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 24; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 41.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.FruitsAndVegies | FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Bear; } } + + public BrownBear( Serial serial ) : base( serial ) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Bears/GrizzlyBear.cs b/Scripts/Mobiles/Animals/Bears/GrizzlyBear.cs new file mode 100644 index 000000000..cc31bbf4e --- /dev/null +++ b/Scripts/Mobiles/Animals/Bears/GrizzlyBear.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a grizzly bear corpse" )] + [TypeAlias( "Server.Mobiles.Grizzlybear" )] + public class GrizzlyBear : BaseCreature + { + [Constructable] + public GrizzlyBear() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a grizzly bear"; + Body = 212; + BaseSoundID = 0xA3; + + SetStr( 126, 155 ); + SetDex( 81, 105 ); + SetInt( 16, 40 ); + + SetHits( 76, 93 ); + SetMana( 0 ); + + SetDamage( 8, 13 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 70.1, 100.0 ); + SetSkill( SkillName.Wrestling, 45.1, 70.0 ); + + Fame = 1000; + Karma = 0; + + VirtualArmor = 24; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 59.1; + } + + public override int Meat{ get{ return 2; } } + public override int Hides{ get{ return 16; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.FruitsAndVegies | FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Bear; } } + + public GrizzlyBear( Serial serial ) : base( serial ) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Bears/PolarBear.cs b/Scripts/Mobiles/Animals/Bears/PolarBear.cs new file mode 100644 index 000000000..7fa00abdf --- /dev/null +++ b/Scripts/Mobiles/Animals/Bears/PolarBear.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a polar bear corpse" )] + [TypeAlias( "Server.Mobiles.Polarbear" )] + public class PolarBear : BaseCreature + { + [Constructable] + public PolarBear() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a polar bear"; + Body = 213; + BaseSoundID = 0xA3; + + SetStr( 116, 140 ); + SetDex( 81, 105 ); + SetInt( 26, 50 ); + + SetHits( 70, 84 ); + SetMana( 0 ); + + SetDamage( 7, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Cold, 60, 80 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 60.1, 90.0 ); + SetSkill( SkillName.Wrestling, 45.1, 70.0 ); + + Fame = 1500; + Karma = 0; + + VirtualArmor = 18; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 35.1; + } + + public override int Meat{ get{ return 2; } } + public override int Hides{ get{ return 16; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.FruitsAndVegies | FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Bear; } } + + public PolarBear( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Birds/Chicken.cs b/Scripts/Mobiles/Animals/Birds/Chicken.cs new file mode 100644 index 000000000..a64849d98 --- /dev/null +++ b/Scripts/Mobiles/Animals/Birds/Chicken.cs @@ -0,0 +1,67 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a chicken corpse" )] + public class Chicken : BaseCreature + { + [Constructable] + public Chicken() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a chicken"; + Body = 0xD0; + BaseSoundID = 0x6E; + + SetStr( 5 ); + SetDex( 15 ); + SetInt( 5 ); + + SetHits( 3 ); + SetMana( 0 ); + + SetDamage( 1 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 1, 5 ); + + SetSkill( SkillName.MagicResist, 4.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 150; + Karma = 0; + + VirtualArmor = 2; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -0.9; + } + + public override int Meat{ get{ return 1; } } + public override MeatType MeatType{ get{ return MeatType.Bird; } } + public override FoodType FavoriteFood{ get{ return FoodType.GrainsAndHay; } } + + public override int Feathers{ get{ return 25; } } + + public Chicken(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Birds/Crane.cs b/Scripts/Mobiles/Animals/Birds/Crane.cs new file mode 100644 index 000000000..f8b46276b --- /dev/null +++ b/Scripts/Mobiles/Animals/Birds/Crane.cs @@ -0,0 +1,86 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a crane corpse" )] + public class Crane : BaseCreature + { + [Constructable] + public Crane() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a crane"; + Body = 254; + BaseSoundID = 0x4D7; + + SetStr( 26, 35 ); + SetDex( 16, 25 ); + SetInt( 11, 15 ); + + SetHits( 26, 35 ); + SetMana( 0 ); + + SetDamage( 1, 1 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 5 ); + + SetSkill( SkillName.MagicResist, 4.1, 5.0 ); + SetSkill( SkillName.Tactics, 10.1, 11.0 ); + SetSkill( SkillName.Wrestling, 10.1, 11.0 ); + + Fame = 0; + Karma = 200; + + VirtualArmor = 5; + } + + public override int Meat{ get{ return 1; } } + public override int Feathers{ get{ return 25; } } + + + public override int GetAngerSound() + { + return 0x4D9; + } + + public override int GetIdleSound() + { + return 0x4D8; + } + + public override int GetAttackSound() + { + return 0x4D7; + } + + public override int GetHurtSound() + { + return 0x4DA; + } + + public override int GetDeathSound() + { + return 0x4D6; + } + + public Crane(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Birds/Eagle.cs b/Scripts/Mobiles/Animals/Birds/Eagle.cs new file mode 100644 index 000000000..20f02948d --- /dev/null +++ b/Scripts/Mobiles/Animals/Birds/Eagle.cs @@ -0,0 +1,71 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an eagle corpse" )] + public class Eagle : BaseCreature + { + [Constructable] + public Eagle() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "an eagle"; + Body = 5; + BaseSoundID = 0x2EE; + + SetStr( 31, 47 ); + SetDex( 36, 60 ); + SetInt( 8, 20 ); + + SetHits( 20, 27 ); + SetMana( 0 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 20, 25 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 15.3, 30.0 ); + SetSkill( SkillName.Tactics, 18.1, 37.0 ); + SetSkill( SkillName.Wrestling, 20.1, 30.0 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 22; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 17.1; + } + + public override int Meat{ get{ return 1; } } + public override MeatType MeatType{ get{ return MeatType.Bird; } } + public override int Feathers{ get{ return 36; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish; } } + + public Eagle(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Birds/Phoenix.cs b/Scripts/Mobiles/Animals/Birds/Phoenix.cs new file mode 100644 index 000000000..80cbce407 --- /dev/null +++ b/Scripts/Mobiles/Animals/Birds/Phoenix.cs @@ -0,0 +1,73 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a phoenix corpse" )] + public class Phoenix : BaseCreature + { + [Constructable] + public Phoenix() : base( AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a phoenix"; + Body = 5; + Hue = 0x674; + BaseSoundID = 0x8F; + + SetStr( 504, 700 ); + SetDex( 202, 300 ); + SetInt( 504, 700 ); + + SetHits( 340, 383 ); + + SetDamage( 25 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Fire, 50 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 90.2, 100.0 ); + SetSkill( SkillName.Magery, 90.2, 100.0 ); + SetSkill( SkillName.Meditation, 75.1, 100.0 ); + SetSkill( SkillName.MagicResist, 86.0, 135.0 ); + SetSkill( SkillName.Tactics, 80.1, 90.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = 0; + + VirtualArmor = 60; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + } + + public override int Meat{ get{ return 1; } } + public override MeatType MeatType{ get{ return MeatType.Bird; } } + public override int Feathers{ get{ return 36; } } + + public Phoenix( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Canines/DireWolf.cs b/Scripts/Mobiles/Animals/Canines/DireWolf.cs new file mode 100644 index 000000000..f87a2ee3b --- /dev/null +++ b/Scripts/Mobiles/Animals/Canines/DireWolf.cs @@ -0,0 +1,72 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a dire wolf corpse" )] + [TypeAlias( "Server.Mobiles.Direwolf" )] + public class DireWolf : BaseCreature + { + [Constructable] + public DireWolf() : base( AIType.AI_Melee,FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a dire wolf"; + Body = 23; + BaseSoundID = 0xE5; + + SetStr( 96, 120 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + SetMana( 0 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 57.6, 75.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 22; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 83.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 7; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Canine; } } + + public DireWolf(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Canines/GreyWolf.cs b/Scripts/Mobiles/Animals/Canines/GreyWolf.cs new file mode 100644 index 000000000..e46a7c160 --- /dev/null +++ b/Scripts/Mobiles/Animals/Canines/GreyWolf.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a grey wolf corpse" )] + [TypeAlias( "Server.Mobiles.Greywolf" )] + public class GreyWolf : BaseCreature + { + [Constructable] + public GreyWolf() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a grey wolf"; + Body = Utility.RandomList( 25, 27 ); + BaseSoundID = 0xE5; + + SetStr( 56, 80 ); + SetDex( 56, 75 ); + SetInt( 31, 55 ); + + SetHits( 34, 48 ); + SetMana( 0 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 20, 25 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 20.1, 35.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 60.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 53.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 6; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Canine; } } + + public GreyWolf(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Canines/TimberWolf.cs b/Scripts/Mobiles/Animals/Canines/TimberWolf.cs new file mode 100644 index 000000000..f99165d62 --- /dev/null +++ b/Scripts/Mobiles/Animals/Canines/TimberWolf.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a timber wolf corpse" )] + [TypeAlias( "Server.Mobiles.Timberwolf" )] + public class TimberWolf : BaseCreature + { + [Constructable] + public TimberWolf() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a timber wolf"; + Body = 225; + BaseSoundID = 0xE5; + + SetStr( 56, 80 ); + SetDex( 56, 75 ); + SetInt( 11, 25 ); + + SetHits( 34, 48 ); + SetMana( 0 ); + + SetDamage( 5, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 27.6, 45.0 ); + SetSkill( SkillName.Tactics, 30.1, 50.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 23.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 5; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Canine; } } + + public TimberWolf(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Canines/WhiteWolf.cs b/Scripts/Mobiles/Animals/Canines/WhiteWolf.cs new file mode 100644 index 000000000..b74b1473e --- /dev/null +++ b/Scripts/Mobiles/Animals/Canines/WhiteWolf.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a white wolf corpse" )] + [TypeAlias( "Server.Mobiles.Whitewolf" )] + public class WhiteWolf : BaseCreature + { + [Constructable] + public WhiteWolf() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a white wolf"; + Body = Utility.RandomList( 34, 37 ); + BaseSoundID = 0xE5; + + SetStr( 56, 80 ); + SetDex( 56, 75 ); + SetInt( 31, 55 ); + + SetHits( 34, 48 ); + SetMana( 0 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 20, 25 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 20.1, 35.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 60.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 65.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 6; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Canine; } } + + public WhiteWolf( Serial serial ) : base( serial ) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Cows/Bull.cs b/Scripts/Mobiles/Animals/Cows/Bull.cs new file mode 100644 index 000000000..d3ff8b009 --- /dev/null +++ b/Scripts/Mobiles/Animals/Cows/Bull.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a bull corpse" )] + public class Bull : BaseCreature + { + [Constructable] + public Bull() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a bull"; + Body = Utility.RandomList( 0xE8, 0xE9 ); + BaseSoundID = 0x64; + + if ( 0.5 >= Utility.RandomDouble() ) + Hue = 0x901; + + SetStr( 77, 111 ); + SetDex( 56, 75 ); + SetInt( 47, 75 ); + + SetHits( 50, 64 ); + SetMana( 0 ); + + SetDamage( 4, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + + SetSkill( SkillName.MagicResist, 17.6, 25.0 ); + SetSkill( SkillName.Tactics, 67.6, 85.0 ); + SetSkill( SkillName.Wrestling, 40.1, 57.5 ); + + Fame = 600; + Karma = 0; + + VirtualArmor = 28; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 71.1; + } + + public override int Meat{ get{ return 10; } } + public override int Hides{ get{ return 15; } } + public override FoodType FavoriteFood{ get{ return FoodType.GrainsAndHay; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Bull; } } + + public Bull(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Cows/Cow.cs b/Scripts/Mobiles/Animals/Cows/Cow.cs new file mode 100644 index 000000000..12228c63e --- /dev/null +++ b/Scripts/Mobiles/Animals/Cows/Cow.cs @@ -0,0 +1,90 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a cow corpse" )] + public class Cow : BaseCreature + { + [Constructable] + public Cow() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a cow"; + Body = Utility.RandomList( 0xD8, 0xE7 ); + BaseSoundID = 0x78; + + SetStr( 30 ); + SetDex( 15 ); + SetInt( 5 ); + + SetHits( 18 ); + SetMana( 0 ); + + SetDamage( 1, 4 ); + + SetDamage( 1, 4 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 15 ); + + SetSkill( SkillName.MagicResist, 5.5 ); + SetSkill( SkillName.Tactics, 5.5 ); + SetSkill( SkillName.Wrestling, 5.5 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 10; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 11.1; + + if ( Core.AOS && Utility.Random( 1000 ) == 0 ) // 0.1% chance to have mad cows + FightMode = FightMode.Closest; + } + + public override int Meat{ get{ return 8; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public override void OnDoubleClick( Mobile from ) + { + base.OnDoubleClick( from ); + + int random = Utility.Random( 100 ); + + if ( random < 5 ) + Tip(); + else if ( random < 20 ) + PlaySound( 120 ); + else if ( random < 40 ) + PlaySound( 121 ); + } + + public void Tip() + { + PlaySound( 121 ); + Animate( 8, 0, 3, true, false, 0 ); + } + + public Cow(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Felines/Cougar.cs b/Scripts/Mobiles/Animals/Felines/Cougar.cs new file mode 100644 index 000000000..bf66a67af --- /dev/null +++ b/Scripts/Mobiles/Animals/Felines/Cougar.cs @@ -0,0 +1,69 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a cougar corpse" )] + public class Cougar : BaseCreature + { + [Constructable] + public Cougar() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a cougar"; + Body = 63; + BaseSoundID = 0x73; + + SetStr( 56, 80 ); + SetDex( 66, 85 ); + SetInt( 26, 50 ); + + SetHits( 34, 48 ); + SetMana( 0 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 15.1, 30.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 60.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 41.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 10; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Feline; } } + + public Cougar(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Felines/HellCat.cs b/Scripts/Mobiles/Animals/Felines/HellCat.cs new file mode 100644 index 000000000..54389af7f --- /dev/null +++ b/Scripts/Mobiles/Animals/Felines/HellCat.cs @@ -0,0 +1,77 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a hell cat corpse" )] + [TypeAlias( "Server.Mobiles.Hellcat" )] + public class HellCat : BaseCreature + { + [Constructable] + public HellCat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a hell cat"; + Body = 0xC9; + Hue = Utility.RandomList( 0x647, 0x650, 0x659, 0x662, 0x66B, 0x674); + BaseSoundID = 0x69; + + SetStr( 51, 100 ); + SetDex( 52, 150 ); + SetInt( 13, 85 ); + + SetHits( 48, 67 ); + + SetDamage( 6, 12 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Fire, 60 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 80, 90 ); + SetResistance( ResistanceType.Energy, 15, 20 ); + + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 40.1, 55.0 ); + SetSkill( SkillName.Wrestling, 30.1, 40.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 30; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 71.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Feline; } } + + public HellCat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Felines/Panther.cs b/Scripts/Mobiles/Animals/Felines/Panther.cs new file mode 100644 index 000000000..d6e9074de --- /dev/null +++ b/Scripts/Mobiles/Animals/Felines/Panther.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a panther corpse" )] + public class Panther : BaseCreature + { + [Constructable] + public Panther() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a panther"; + Body = 0xD6; + Hue = 0x901; + BaseSoundID = 0x462; + + SetStr( 61, 85 ); + SetDex( 86, 105 ); + SetInt( 26, 50 ); + + SetHits( 37, 51 ); + SetMana( 0 ); + + SetDamage( 4, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 15.1, 30.0 ); + SetSkill( SkillName.Tactics, 50.1, 65.0 ); + SetSkill( SkillName.Wrestling, 50.1, 65.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 53.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 10; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Feline; } } + + public Panther(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Felines/PredatorHellCat.cs b/Scripts/Mobiles/Animals/Felines/PredatorHellCat.cs new file mode 100644 index 000000000..8a4169991 --- /dev/null +++ b/Scripts/Mobiles/Animals/Felines/PredatorHellCat.cs @@ -0,0 +1,75 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a hell cat corpse" )] + [TypeAlias( "Server.Mobiles.Preditorhellcat" )] + public class PredatorHellCat : BaseCreature + { + [Constructable] + public PredatorHellCat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a hell cat"; + Body = 127; + BaseSoundID = 0xBA; + + SetStr( 161, 185 ); + SetDex( 96, 115 ); + SetInt( 76, 100 ); + + SetHits( 97, 131 ); + + SetDamage( 5, 17 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Fire, 25 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Energy, 5, 15 ); + + SetSkill( SkillName.MagicResist, 75.1, 90.0 ); + SetSkill( SkillName.Tactics, 50.1, 65.0 ); + SetSkill( SkillName.Wrestling, 50.1, 65.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 30; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 89.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Feline; } } + + public PredatorHellCat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Felines/SnowLeopard.cs b/Scripts/Mobiles/Animals/Felines/SnowLeopard.cs new file mode 100644 index 000000000..010ebf4be --- /dev/null +++ b/Scripts/Mobiles/Animals/Felines/SnowLeopard.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a leopard corpse" )] + [TypeAlias( "Server.Mobiles.Snowleopard" )] + public class SnowLeopard : BaseCreature + { + [Constructable] + public SnowLeopard() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a snow leopard"; + Body = Utility.RandomList( 64, 65 ); + BaseSoundID = 0x73; + + SetStr( 56, 80 ); + SetDex( 66, 85 ); + SetInt( 26, 50 ); + + SetHits( 34, 48 ); + SetMana( 0 ); + + SetDamage( 3, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 25.1, 35.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 40.1, 50.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 24; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 53.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 8; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Feline; } } + + public SnowLeopard(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Boar.cs b/Scripts/Mobiles/Animals/Misc/Boar.cs new file mode 100644 index 000000000..773a3047a --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Boar.cs @@ -0,0 +1,66 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a pig corpse" )] + public class Boar : BaseCreature + { + [Constructable] + public Boar() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a boar"; + Body = 0x122; + BaseSoundID = 0xC4; + + SetStr( 25 ); + SetDex( 15 ); + SetInt( 5 ); + + SetHits( 15 ); + SetMana( 0 ); + + SetDamage( 3, 6 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10, 15 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 9.0 ); + SetSkill( SkillName.Tactics, 9.0 ); + SetSkill( SkillName.Wrestling, 9.0 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 10; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override int Meat{ get{ return 2; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Boar(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/BullFrog.cs b/Scripts/Mobiles/Animals/Misc/BullFrog.cs new file mode 100644 index 000000000..db30b9e6d --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/BullFrog.cs @@ -0,0 +1,72 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a bull frog corpse" )] + [TypeAlias( "Server.Mobiles.Bullfrog" )] + public class BullFrog : BaseCreature + { + [Constructable] + public BullFrog() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a bull frog"; + Body = 81; + Hue = Utility.RandomList( 0x5AC,0x5A3,0x59A,0x591,0x588,0x57F ); + BaseSoundID = 0x266; + + SetStr( 46, 70 ); + SetDex( 6, 25 ); + SetInt( 11, 20 ); + + SetHits( 28, 42 ); + SetMana( 0 ); + + SetDamage( 1, 2 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 40.1, 60.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 350; + Karma = 0; + + VirtualArmor = 6; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 23.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 4; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.Meat; } } + + public BullFrog(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Dolphin.cs b/Scripts/Mobiles/Animals/Misc/Dolphin.cs new file mode 100644 index 000000000..cdf807a46 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Dolphin.cs @@ -0,0 +1,88 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a dolphin corpse" )] + public class Dolphin : BaseCreature + { + [Constructable] + public Dolphin() + : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a dolphin"; + Body = 0x97; + BaseSoundID = 0x8A; + + SetStr( 21, 49 ); + SetDex( 66, 85 ); + SetInt( 96, 110 ); + + SetHits( 15, 27 ); + + SetDamage( 3, 6 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Cold, 25, 30 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.2, 29.0 ); + SetSkill( SkillName.Wrestling, 19.2, 29.0 ); + + Fame = 500; + Karma = 2000; + + VirtualArmor = 16; + CanSwim = true; + CantWalk = true; + } + + public override int Meat { get { return 1; } } + + public Dolphin( Serial serial ) + : base( serial ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + if( from.AccessLevel >= AccessLevel.GameMaster ) + Jump(); + } + + public virtual void Jump() + { + if( Utility.RandomBool() ) + Animate( 3, 16, 1, true, false, 0 ); + else + Animate( 4, 20, 1, true, false, 0 ); + } + + public override void OnThink() + { + if( Utility.RandomDouble() < .005 ) // slim chance to jump + Jump(); + + base.OnThink(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Gaman.cs b/Scripts/Mobiles/Animals/Misc/Gaman.cs new file mode 100644 index 000000000..568776904 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Gaman.cs @@ -0,0 +1,90 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gaman corpse" )] + public class Gaman : BaseCreature + { + [Constructable] + public Gaman() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a gaman"; + Body = 248; + + SetStr( 146, 175 ); + SetDex( 111, 150 ); + SetInt( 46, 60 ); + + SetHits( 131, 160 ); + SetMana( 0 ); + + SetDamage( 6, 11 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 50, 70 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 30, 50 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + SetResistance( ResistanceType.Energy, 30, 50 ); + + SetSkill( SkillName.MagicResist, 37.6, 42.5 ); + SetSkill( SkillName.Tactics, 70.6, 83.0 ); + SetSkill( SkillName.Wrestling, 50.1, 57.5 ); + + Fame = 2000; + Karma = -2000; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 68.7; + } + + public override int GetAngerSound() + { + return 0x4F8; + } + + public override int GetIdleSound() + { + return 0x4F7; + } + + public override int GetAttackSound() + { + return 0x4F6; + } + + public override int GetHurtSound() + { + return 0x4F9; + } + + public override int GetDeathSound() + { + return 0x4F5; + } + + public override int Meat{ get{ return 10; } } + public override int Hides{ get{ return 15; } } + public override FoodType FavoriteFood{ get{ return FoodType.GrainsAndHay; } } + + public Gaman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Animals/Misc/GiantToad.cs b/Scripts/Mobiles/Animals/Misc/GiantToad.cs new file mode 100644 index 000000000..78378907c --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/GiantToad.cs @@ -0,0 +1,73 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a giant toad corpse" )] + [TypeAlias( "Server.Mobiles.Gianttoad" )] + public class GiantToad : BaseCreature + { + [Constructable] + public GiantToad() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a giant toad"; + Body = 80; + BaseSoundID = 0x26B; + + SetStr( 76, 100 ); + SetDex( 6, 25 ); + SetInt( 11, 20 ); + + SetHits( 46, 60 ); + SetMana( 0 ); + + SetDamage( 5, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 40.1, 60.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 750; + Karma = -750; + + VirtualArmor = 24; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 77.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Hides{ get{ return 12; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.Meat; } } + + public GiantToad(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Goat.cs b/Scripts/Mobiles/Animals/Misc/Goat.cs new file mode 100644 index 000000000..873e44ed2 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Goat.cs @@ -0,0 +1,65 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a goat corpse" )] + public class Goat : BaseCreature + { + [Constructable] + public Goat() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a goat"; + Body = 0xD1; + BaseSoundID = 0x99; + + SetStr( 19 ); + SetDex( 15 ); + SetInt( 5 ); + + SetHits( 12 ); + SetMana( 0 ); + + SetDamage( 3, 4 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 15 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 150; + Karma = 0; + + VirtualArmor = 10; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 11.1; + } + + public override int Meat{ get{ return 2; } } + public override int Hides{ get{ return 8; } } + public override FoodType FavoriteFood{ get{ return FoodType.GrainsAndHay | FoodType.FruitsAndVegies; } } + + public Goat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Gorilla.cs b/Scripts/Mobiles/Animals/Misc/Gorilla.cs new file mode 100644 index 000000000..27491fdfd --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Gorilla.cs @@ -0,0 +1,67 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a gorilla corpse" )] + public class Gorilla : BaseCreature + { + [Constructable] + public Gorilla() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a gorilla"; + Body = 0x1D; + BaseSoundID = 0x9E; + + SetStr( 53, 95 ); + SetDex( 36, 55 ); + SetInt( 36, 60 ); + + SetHits( 38, 51 ); + SetMana( 0 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 43.3, 58.0 ); + SetSkill( SkillName.Wrestling, 43.3, 58.0 ); + + Fame = 450; + Karma = 0; + + VirtualArmor = 20; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -18.9; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 6; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Gorilla(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/GreatHart.cs b/Scripts/Mobiles/Animals/Misc/GreatHart.cs new file mode 100644 index 000000000..6fde00d0a --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/GreatHart.cs @@ -0,0 +1,81 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a deer corpse" )] + [TypeAlias( "Server.Mobiles.Greathart" )] + public class GreatHart : BaseCreature + { + [Constructable] + public GreatHart() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a great hart"; + Body = 0xEA; + + SetStr( 41, 71 ); + SetDex( 47, 77 ); + SetInt( 27, 57 ); + + SetHits( 27, 41 ); + SetMana( 0 ); + + SetDamage( 5, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + + SetSkill( SkillName.MagicResist, 26.8, 44.5 ); + SetSkill( SkillName.Tactics, 29.8, 47.5 ); + SetSkill( SkillName.Wrestling, 29.8, 47.5 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 24; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 59.1; + } + + public override int Meat{ get{ return 6; } } + public override int Hides{ get{ return 15; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public GreatHart(Serial serial) : base(serial) + { + } + + public override int GetAttackSound() + { + return 0x82; + } + + public override int GetHurtSound() + { + return 0x83; + } + + public override int GetDeathSound() + { + return 0x84; + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Hind.cs b/Scripts/Mobiles/Animals/Misc/Hind.cs new file mode 100644 index 000000000..a30ed6869 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Hind.cs @@ -0,0 +1,80 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a deer corpse" )] + public class Hind : BaseCreature + { + [Constructable] + public Hind() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a hind"; + Body = 0xED; + + SetStr( 21, 51 ); + SetDex( 47, 77 ); + SetInt( 17, 47 ); + + SetHits( 15, 29 ); + SetMana( 0 ); + + SetDamage( 4 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 15 ); + SetResistance( ResistanceType.Cold, 5 ); + + SetSkill( SkillName.MagicResist, 15.0 ); + SetSkill( SkillName.Tactics, 19.0 ); + SetSkill( SkillName.Wrestling, 26.0 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 8; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 23.1; + } + + public override int Meat{ get{ return 5; } } + public override int Hides{ get{ return 8; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Hind(Serial serial) : base(serial) + { + } + + public override int GetAttackSound() + { + return 0x82; + } + + public override int GetHurtSound() + { + return 0x83; + } + + public override int GetDeathSound() + { + return 0x84; + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Llama.cs b/Scripts/Mobiles/Animals/Misc/Llama.cs new file mode 100644 index 000000000..56c51cc3c --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Llama.cs @@ -0,0 +1,65 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a llama corpse" )] + public class Llama : BaseCreature + { + [Constructable] + public Llama() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a llama"; + Body = 0xDC; + BaseSoundID = 0x3F3; + + SetStr( 21, 49 ); + SetDex( 36, 55 ); + SetInt( 16, 30 ); + + SetHits( 15, 27 ); + SetMana( 0 ); + + SetDamage( 3, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.2, 29.0 ); + SetSkill( SkillName.Wrestling, 19.2, 29.0 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 35.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Llama(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/MountainGoat.cs b/Scripts/Mobiles/Animals/Misc/MountainGoat.cs new file mode 100644 index 000000000..431dca6f6 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/MountainGoat.cs @@ -0,0 +1,69 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a mountain goat corpse" )] + public class MountainGoat : BaseCreature + { + [Constructable] + public MountainGoat() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a mountain goat"; + Body = 88; + BaseSoundID = 0x99; + + SetStr( 22, 64 ); + SetDex( 56, 75 ); + SetInt( 16, 30 ); + + SetHits( 20, 33 ); + SetMana( 0 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 10; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -0.9; + } + + public override int Meat{ get{ return 2; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.GrainsAndHay | FoodType.FruitsAndVegies; } } + + public MountainGoat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/PackHorse.cs b/Scripts/Mobiles/Animals/Misc/PackHorse.cs new file mode 100644 index 000000000..1a72ff53e --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/PackHorse.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Mobiles; +using Server.Items; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [CorpseName( "a horse corpse" )] + public class PackHorse : BaseCreature + { + [Constructable] + public PackHorse() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a pack horse"; + Body = 291; + BaseSoundID = 0xA8; + + SetStr( 44, 120 ); + SetDex( 36, 55 ); + SetInt( 6, 10 ); + + SetHits( 61, 80 ); + SetStam( 81, 100 ); + SetMana( 0 ); + + SetDamage( 5, 11 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 20, 25 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 0; + Karma = 200; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 11.1; + + Container pack = Backpack; + + if ( pack != null ) + pack.Delete(); + + pack = new StrongBackpack(); + pack.Movable = false; + + AddItem( pack ); + } + + public override int Meat{ get{ return 3; } } + public override int Hides{ get{ return 10; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public PackHorse( Serial serial ) : base( serial ) + { + } + + #region Pack Animal Methods + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + PackAnimal.CombineBackpacks( this ); + + return true; + } + + public override DeathMoveResult GetInventoryMoveResultFor( Item item ) + { + return DeathMoveResult.MoveToCorpse; + } + + public override bool IsSnoop( Mobile from ) + { + if ( PackAnimal.CheckAccess( this, from ) ) + return false; + + return base.IsSnoop( from ); + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( CheckFeed( from, item ) ) + return true; + + if ( PackAnimal.CheckAccess( this, from ) ) + { + AddToBackpack( item ); + return true; + } + + return base.OnDragDrop( from, item ); + } + + public override bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override bool CheckNonlocalLift( Mobile from, Item item ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override void OnDoubleClick( Mobile from ) + { + PackAnimal.TryPackOpen( this, from ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + PackAnimal.GetContextMenuEntries( this, from, list ); + } + #endregion + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PackAnimalBackpackEntry : ContextMenuEntry + { + private BaseCreature m_Animal; + private Mobile m_From; + + public PackAnimalBackpackEntry( BaseCreature animal, Mobile from ) : base( 6145, 3 ) + { + m_Animal = animal; + m_From = from; + + if ( animal.IsDeadPet ) + Enabled = false; + } + + public override void OnClick() + { + PackAnimal.TryPackOpen( m_Animal, m_From ); + } + } + + public class PackAnimal + { + public static void GetContextMenuEntries( BaseCreature animal, Mobile from, List list ) + { + if ( CheckAccess( animal, from ) ) + list.Add( new PackAnimalBackpackEntry( animal, from ) ); + } + + public static bool CheckAccess( BaseCreature animal, Mobile from ) + { + if ( from == animal || from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if ( from.Alive && animal.Controlled && !animal.IsDeadPet && (from == animal.ControlMaster || from == animal.SummonMaster) ) + return true; + + return false; + } + + public static void CombineBackpacks( BaseCreature animal ) + { + if ( Core.AOS ) + return; + + //if ( animal.IsBonded || animal.IsDeadPet ) + // return; + + Container pack = animal.Backpack; + + if ( pack != null ) + { + Container newPack = new Backpack(); + + for ( int i = pack.Items.Count - 1; i >= 0; --i ) + { + if ( i >= pack.Items.Count ) + continue; + + newPack.DropItem( pack.Items[i] ); + } + + pack.DropItem( newPack ); + } + } + + public static void TryPackOpen( BaseCreature animal, Mobile from ) + { + if ( animal.IsDeadPet ) + return; + + Container item = animal.Backpack; + + if ( item != null ) + from.Use( item ); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/PackLlama.cs b/Scripts/Mobiles/Animals/Misc/PackLlama.cs new file mode 100644 index 000000000..3867bb892 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/PackLlama.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using Server.Mobiles; +using Server.Items; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [CorpseName("a llama corpse")] + public class PackLlama : BaseCreature + { + [Constructable] + public PackLlama() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a pack llama"; + Body = 292; + BaseSoundID = 0x3F3; + + SetStr( 52, 80 ); + SetDex( 36, 55 ); + SetInt( 16, 30 ); + + SetHits( 50 ); + SetStam( 86, 105 ); + SetMana( 0 ); + + SetDamage( 2, 6 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.2, 29.0 ); + SetSkill( SkillName.Wrestling, 19.2, 29.0 ); + + Fame = 0; + Karma = 200; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 11.1; + + Container pack = Backpack; + + if ( pack != null ) + pack.Delete(); + + pack = new StrongBackpack(); + pack.Movable = false; + + AddItem( pack ); + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public PackLlama( Serial serial ) : base( serial ) + { + } + + #region Pack Animal Methods + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + PackAnimal.CombineBackpacks( this ); + + return true; + } + + public override DeathMoveResult GetInventoryMoveResultFor( Item item ) + { + return DeathMoveResult.MoveToCorpse; + } + + public override bool IsSnoop( Mobile from ) + { + if ( PackAnimal.CheckAccess( this, from ) ) + return false; + + return base.IsSnoop( from ); + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( CheckFeed( from, item ) ) + return true; + + if ( PackAnimal.CheckAccess( this, from ) ) + { + AddToBackpack( item ); + return true; + } + + return base.OnDragDrop( from, item ); + } + + public override bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override bool CheckNonlocalLift( Mobile from, Item item ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override void OnDoubleClick( Mobile from ) + { + PackAnimal.TryPackOpen( this, from ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + PackAnimal.GetContextMenuEntries( this, from, list ); + } + #endregion + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Pig.cs b/Scripts/Mobiles/Animals/Misc/Pig.cs new file mode 100644 index 000000000..f40a4834f --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Pig.cs @@ -0,0 +1,64 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a pig corpse" )] + public class Pig : BaseCreature + { + [Constructable] + public Pig() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a pig"; + Body = 0xCB; + BaseSoundID = 0xC4; + + SetStr( 20 ); + SetDex( 20 ); + SetInt( 5 ); + + SetHits( 12 ); + SetMana( 0 ); + + SetDamage( 2, 4 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10, 15 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 150; + Karma = 0; + + VirtualArmor = 12; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 11.1; + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Pig(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Sheep.cs b/Scripts/Mobiles/Animals/Misc/Sheep.cs new file mode 100644 index 000000000..90a8eb55b --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Sheep.cs @@ -0,0 +1,111 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a sheep corpse" )] + public class Sheep : BaseCreature, ICarvable + { + private DateTime m_NextWoolTime; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NextWoolTime + { + get{ return m_NextWoolTime; } + set{ m_NextWoolTime = value; Body = ( DateTime.Now >= m_NextWoolTime ) ? 0xCF : 0xDF; } + } + + public void Carve( Mobile from, Item item ) + { + if ( DateTime.Now < m_NextWoolTime ) + { + // This sheep is not yet ready to be shorn. + PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500449, from.NetState ); + return; + } + + from.SendLocalizedMessage( 500452 ); // You place the gathered wool into your backpack. + from.AddToBackpack( new Wool( Map == Map.Felucca ? 2 : 1 ) ); + + NextWoolTime = DateTime.Now + TimeSpan.FromHours( 3.0 ); // TODO: Proper time delay + } + + public override void OnThink() + { + base.OnThink(); + Body = ( DateTime.Now >= m_NextWoolTime ) ? 0xCF : 0xDF; + } + + [Constructable] + public Sheep() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a sheep"; + Body = 0xCF; + BaseSoundID = 0xD6; + + SetStr( 19 ); + SetDex( 25 ); + SetInt( 5 ); + + SetHits( 12 ); + SetMana( 0 ); + + SetDamage( 1, 2 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 6.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 300; + Karma = 0; + + VirtualArmor = 6; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 11.1; + } + + public override int Meat{ get{ return 3; } } + public override MeatType MeatType{ get{ return MeatType.LambLeg; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public override int Wool{ get{ return (Body == 0xCF ? 3 : 0); } } + + public Sheep( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); + + writer.WriteDeltaTime( m_NextWoolTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + NextWoolTime = reader.ReadDeltaTime(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Misc/Walrus.cs b/Scripts/Mobiles/Animals/Misc/Walrus.cs new file mode 100644 index 000000000..7102625d3 --- /dev/null +++ b/Scripts/Mobiles/Animals/Misc/Walrus.cs @@ -0,0 +1,69 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a walrus corpse" )] + public class Walrus : BaseCreature + { + [Constructable] + public Walrus() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a walrus"; + Body = 0xDD; + BaseSoundID = 0xE0; + + SetStr( 21, 29 ); + SetDex( 46, 55 ); + SetInt( 16, 20 ); + + SetHits( 14, 17 ); + SetMana( 0 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 20, 25 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.2, 29.0 ); + SetSkill( SkillName.Wrestling, 19.2, 29.0 ); + + Fame = 150; + Karma = 0; + + VirtualArmor = 18; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 35.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish; } } + + public Walrus(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/BaseMount.cs b/Scripts/Mobiles/Animals/Mounts/BaseMount.cs new file mode 100644 index 000000000..f72667f41 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/BaseMount.cs @@ -0,0 +1,458 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public abstract class BaseMount : BaseCreature, IMount + { + private Mobile m_Rider; + private Item m_InternalItem; + private DateTime m_NextMountAbility; + + public virtual TimeSpan MountAbilityDelay { get { return TimeSpan.Zero; } } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NextMountAbility + { + get { return m_NextMountAbility; } + set { m_NextMountAbility = value; } + } + + public virtual bool AllowMaleRider{ get{ return true; } } + public virtual bool AllowFemaleRider{ get{ return true; } } + + public BaseMount( string name, int bodyID, int itemID, AIType aiType, FightMode fightMode, int rangePerception, int rangeFight, double activeSpeed, double passiveSpeed ) : base ( aiType, fightMode, rangePerception, rangeFight, activeSpeed, passiveSpeed ) + { + Name = name; + Body = bodyID; + + m_InternalItem = new MountItem( this, itemID ); + } + + public BaseMount( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_NextMountAbility ); + + writer.Write( m_Rider ); + writer.Write( m_InternalItem ); + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public override int Hue + { + get + { + return base.Hue; + } + set + { + base.Hue = value; + + if ( m_InternalItem != null ) + m_InternalItem.Hue = value; + } + } + + public override bool OnBeforeDeath() + { + Rider = null; + + return base.OnBeforeDeath(); + } + + public override void OnAfterDelete() + { + if ( m_InternalItem != null ) + m_InternalItem.Delete(); + + m_InternalItem = null; + + base.OnAfterDelete(); + } + + public override void OnDelete() + { + Rider = null; + + base.OnDelete(); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_NextMountAbility = reader.ReadDateTime(); + goto case 0; + } + case 0: + { + m_Rider = reader.ReadMobile(); + m_InternalItem = reader.ReadItem(); + + if ( m_InternalItem == null ) + Delete(); + + break; + } + } + } + + public virtual void OnDisallowedRider( Mobile m ) + { + m.SendMessage( "You may not ride this creature." ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsDeadPet ) + return; + + if ( from.IsBodyMod && !from.Body.IsHuman ) + { + if ( Core.AOS ) // You cannot ride a mount in your current form. + PrivateOverheadMessage( Network.MessageType.Regular, 0x3B2, 1062061, from.NetState ); + else + from.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed. + + return; + } + + if ( !CheckMountAllowed( from, true ) ) + return; + + if ( from.Mounted ) + { + from.SendLocalizedMessage( 1005583 ); // Please dismount first. + return; + } + + if ( from.Female ? !AllowFemaleRider : !AllowMaleRider ) + { + OnDisallowedRider( from ); + return; + } + + if ( !Multis.DesignContext.Check( from ) ) + return; + + if ( from.HasTrade ) + { + from.SendLocalizedMessage( 1042317, "", 0x41 ); // You may not ride at this time + return; + } + + if ( from.InRange( this, 1 ) ) + { + bool canAccess = ( from.AccessLevel >= AccessLevel.GameMaster ) + || ( Controlled && ControlMaster == from ) + || ( Summoned && SummonMaster == from ); + + if ( canAccess ) + { + if ( this.Poisoned ) + PrivateOverheadMessage( Network.MessageType.Regular, 0x3B2, 1049692, from.NetState ); // This mount is too ill to ride. + else + Rider = from; + } + else if ( !Controlled && !Summoned ) + { + // That mount does not look broken! You would have to tame it to ride it. + PrivateOverheadMessage( Network.MessageType.Regular, 0x3B2, 501263, from.NetState ); + } + else + { + // This isn't your mount; it refuses to let you ride. + PrivateOverheadMessage( Network.MessageType.Regular, 0x3B2, 501264, from.NetState ); + } + } + else + { + from.SendLocalizedMessage( 500206 ); // That is too far away to ride. + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ItemID + { + get + { + if ( m_InternalItem != null ) + return m_InternalItem.ItemID; + else + return 0; + } + set + { + if ( m_InternalItem != null ) + m_InternalItem.ItemID = value; + } + } + + public static void Dismount( Mobile m ) + { + IMount mount = m.Mount; + + if ( mount != null ) + mount.Rider = null; + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Rider + { + get + { + return m_Rider; + } + set + { + if ( m_Rider != value ) + { + if ( value == null ) + { + Point3D loc = m_Rider.Location; + Map map = m_Rider.Map; + + if ( map == null || map == Map.Internal ) + { + loc = m_Rider.LogoutLocation; + map = m_Rider.LogoutMap; + } + + Direction = m_Rider.Direction; + Location = loc; + Map = map; + + if ( m_InternalItem != null ) + m_InternalItem.Internalize(); + } + else + { + if ( m_Rider != null ) + Dismount( m_Rider ); + + Dismount( value ); + + if ( m_InternalItem != null ) + value.AddItem( m_InternalItem ); + + value.Direction = this.Direction; + + Internalize(); + } + + m_Rider = value; + } + } + } + + private class BlockEntry + { + public BlockMountType m_Type; + public DateTime m_Expiration; + + public bool IsExpired{ get{ return ( DateTime.Now >= m_Expiration ); } } + + public BlockEntry( BlockMountType type, DateTime expiration ) + { + m_Type = type; + m_Expiration = expiration; + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static void SetMountPrevention( Mobile mob, BlockMountType type, TimeSpan duration ) + { + if ( mob == null ) + return; + + DateTime expiration = DateTime.Now + duration; + + BlockEntry entry = m_Table[mob] as BlockEntry; + + if ( entry != null ) + { + entry.m_Type = type; + entry.m_Expiration = expiration; + } + else + { + m_Table[mob] = entry = new BlockEntry( type, expiration ); + } + } + + public static void ClearMountPrevention( Mobile mob ) + { + if ( mob != null ) + m_Table.Remove( mob ); + } + + public static BlockMountType GetMountPrevention( Mobile mob ) + { + if ( mob == null ) + return BlockMountType.None; + + BlockEntry entry = m_Table[mob] as BlockEntry; + + if ( entry == null ) + return BlockMountType.None; + + if ( entry.IsExpired ) + { + m_Table.Remove( mob ); + return BlockMountType.None; + } + + return entry.m_Type; + } + + public static bool CheckMountAllowed( Mobile mob, bool message ) + { + BlockMountType type = GetMountPrevention( mob ); + + if ( type == BlockMountType.None ) + return true; + + if ( message ) + { + switch ( type ) + { + case BlockMountType.Dazed: + { + mob.SendLocalizedMessage( 1040024 ); // You are still too dazed from being knocked off your mount to ride! + break; + } + case BlockMountType.BolaRecovery: + { + mob.SendLocalizedMessage( 1062910 ); // You cannot mount while recovering from a bola throw. + break; + } + case BlockMountType.DismountRecovery: + { + mob.SendLocalizedMessage( 1070859 ); // You cannot mount while recovering from a dismount special maneuver. + break; + } + } + } + + return false; + } + + public virtual void OnRiderDamaged( int amount, Mobile from, bool willKill ) + { + if( m_Rider == null ) + return; + + Mobile attacker = from; + if( attacker == null ) + attacker = m_Rider.FindMostRecentDamager( true ); + + if( !(attacker == this || attacker == m_Rider || willKill || DateTime.Now < m_NextMountAbility) ) + { + if( DoMountAbility( amount, from ) ) + m_NextMountAbility = DateTime.Now + MountAbilityDelay; + + } + } + + public virtual bool DoMountAbility( int damage, Mobile attacker ) + { + return false; + } + } + + public class MountItem : Item, IMountItem + { + private BaseMount m_Mount; + + public MountItem( BaseMount mount, int itemID ) : base( itemID ) + { + Layer = Layer.Mount; + Movable = false; + + m_Mount = mount; + } + + public MountItem( Serial serial ) : base( serial ) + { + } + + public override void OnAfterDelete() + { + if ( m_Mount != null ) + m_Mount.Delete(); + + m_Mount = null; + + base.OnAfterDelete(); + } + + public override DeathMoveResult OnParentDeath(Mobile parent) + { + if ( m_Mount != null ) + m_Mount.Rider = null; + + return DeathMoveResult.RemainEquiped; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Mount ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Mount = reader.ReadMobile() as BaseMount; + + if ( m_Mount == null ) + Delete(); + + break; + } + } + } + + public IMount Mount + { + get + { + return m_Mount; + } + } + } + + public enum BlockMountType + { + None = -1, + Dazed, + BolaRecovery, + DismountRecovery + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/Beetle.cs b/Scripts/Mobiles/Animals/Mounts/Beetle.cs new file mode 100644 index 000000000..be2e45cc7 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Beetle.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [CorpseName( "a giant beetle corpse" )] + public class Beetle : BaseMount + { + [Constructable] + public Beetle() : this( "a giant beetle" ) + { + } + + public override bool SubdueBeforeTame{ get{ return true; } } // Must be beaten into submission + + [Constructable] + public Beetle( string name ) : base( name, 0x317, 0x3EBC, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + SetStr( 300 ); + SetDex( 100 ); + SetInt( 500 ); + + SetHits( 200 ); + + SetDamage( 7, 20 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 80.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 4000; + Karma = -4000; + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 29.1; + + Container pack = Backpack; + + if ( pack != null ) + pack.Delete(); + + pack = new StrongBackpack(); + pack.Movable = false; + + AddItem( pack ); + } + + public override int GetAngerSound() + { + return 0x21D; + } + + public override int GetIdleSound() + { + return 0x21D; + } + + public override int GetAttackSound() + { + return 0x162; + } + + public override int GetHurtSound() + { + return 0x163; + } + + public override int GetDeathSound() + { + return 0x21D; + } + + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + + // TODO: Speed boost when hit by magic. + + public Beetle( Serial serial ) : base( serial ) + { + } + + #region Pack Animal Methods + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + PackAnimal.CombineBackpacks( this ); + + return true; + } + + public override DeathMoveResult GetInventoryMoveResultFor( Item item ) + { + return DeathMoveResult.MoveToCorpse; + } + + public override bool IsSnoop( Mobile from ) + { + if ( PackAnimal.CheckAccess( this, from ) ) + return false; + + return base.IsSnoop( from ); + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( CheckFeed( from, item ) ) + return true; + + if ( PackAnimal.CheckAccess( this, from ) ) + { + AddToBackpack( item ); + return true; + } + + return base.OnDragDrop( from, item ); + } + + public override bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override bool CheckNonlocalLift( Mobile from, Item item ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + PackAnimal.GetContextMenuEntries( this, from, list ); + } + #endregion + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/DesertOstard.cs b/Scripts/Mobiles/Animals/Mounts/DesertOstard.cs new file mode 100644 index 000000000..c82cdc6d8 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/DesertOstard.cs @@ -0,0 +1,67 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an ostard corpse" )] + public class DesertOstard : BaseMount + { + [Constructable] + public DesertOstard() : this( "a desert ostard" ) + { + } + + [Constructable] + public DesertOstard( string name ) : base( name, 0xD2, 0x3EA3, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x270; + + SetStr( 94, 170 ); + SetDex( 56, 75 ); + SetInt( 6, 10 ); + + SetHits( 71, 88 ); + SetMana( 0 ); + + SetDamage( 5, 11 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 15 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 25.3, 40.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 450; + Karma = 0; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override int Meat{ get{ return 3; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Ostard; } } + + public DesertOstard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/Ethereals.cs b/Scripts/Mobiles/Animals/Mounts/Ethereals.cs new file mode 100644 index 000000000..78bbc98d5 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Ethereals.cs @@ -0,0 +1,677 @@ +using System; +using Server.Mobiles; +using Server.Items; +using Server.Spells; +using Server.Engines.VeteranRewards; + +namespace Server.Mobiles +{ + public class EtherealMount : Item, IMount, IMountItem, Engines.VeteranRewards.IRewardItem + { + private int m_MountedID; + private int m_RegularID; + private Mobile m_Rider; + private bool m_IsRewardItem; + private bool m_IsDonationItem; + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsRewardItem + { + get{ return m_IsRewardItem; } + set{ m_IsRewardItem = value; } + } + + public override double DefaultWeight + { + get { return 1.0; } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public bool IsDonationItem + { + get { return m_IsDonationItem; } + set { m_IsDonationItem = value; InvalidateProperties(); } + } + + [Constructable] + public EtherealMount( int itemID, int mountID ) : base( itemID ) + { + m_MountedID = mountID; + m_RegularID = itemID; + m_Rider = null; + + Layer = Layer.Invalid; + + LootType = LootType.Blessed; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if( m_IsDonationItem ) + { + list.Add( "Donation Ethereal" ); + list.Add( "7.5 sec slower cast time if not a 9mo. Veteran" ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MountedID + { + get + { + return m_MountedID; + } + set + { + if ( m_MountedID != value ) + { + m_MountedID = value; + + if ( m_Rider != null ) + ItemID = value; + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RegularID + { + get + { + return m_RegularID; + } + set + { + if ( m_RegularID != value ) + { + m_RegularID = value; + + if ( m_Rider == null ) + ItemID = value; + } + } + } + + public EtherealMount( Serial serial ) : base( serial ) + { + } + + public override bool DisplayLootType{ get{ return false; } } + + public virtual int FollowerSlots{ get{ return 1; } } + + public void RemoveFollowers() + { + if ( m_Rider != null ) + m_Rider.Followers -= FollowerSlots; + + if ( m_Rider != null && m_Rider.Followers < 0 ) + m_Rider.Followers = 0; + } + + public void AddFollowers() + { + if ( m_Rider != null ) + m_Rider.Followers += FollowerSlots; + } + + public virtual bool Validate( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + return false; + } + else if ( m_IsRewardItem && !Engines.VeteranRewards.RewardSystem.CheckIsUsableBy( from, this, null ) ) + { + // CheckIsUsableBy sends the message + return false; + } + else if ( !BaseMount.CheckMountAllowed( from, true ) ) + { + // CheckMountAllowed sends the message + return false; + } + else if ( from.Mounted ) + { + from.SendLocalizedMessage( 1005583 ); // Please dismount first. + return false; + } + else if ( from.IsBodyMod && !from.Body.IsHuman ) + { + from.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed. + return false; + } + else if ( from.HasTrade ) + { + from.SendLocalizedMessage( 1042317, "", 0x41 ); // You may not ride at this time + return false; + } + else if ( (from.Followers + FollowerSlots) > from.FollowersMax ) + { + from.SendLocalizedMessage( 1049679 ); // You have too many followers to summon your mount. + return false; + } + else if ( !Multis.DesignContext.Check( from ) ) + { + // Check sends the message + return false; + } + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( Validate( from ) ) + new EtherealSpell( this, from ).Cast(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 3 ); // version + + writer.Write( m_IsDonationItem ); + writer.Write( (bool) m_IsRewardItem ); + + writer.Write( (int)m_MountedID ); + writer.Write( (int)m_RegularID ); + writer.Write( m_Rider ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + LootType = LootType.Blessed; + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + { + m_IsDonationItem = reader.ReadBool(); + goto case 2; + } + case 2: + { + m_IsRewardItem = reader.ReadBool(); + goto case 0; + } + case 1: reader.ReadInt(); goto case 0; + case 0: + { + m_MountedID = reader.ReadInt(); + m_RegularID = reader.ReadInt(); + m_Rider = reader.ReadMobile(); + + if ( m_MountedID == 0x3EA2 ) + m_MountedID = 0x3EAA; + + break; + } + } + + AddFollowers(); + + if ( version < 3 && Weight == 0 ) + Weight = -1; + } + + public override DeathMoveResult OnParentDeath( Mobile parent ) + { + Rider = null;//get off, move to pack + + return DeathMoveResult.RemainEquiped; + } + + public static void Dismount( Mobile m ) + { + IMount mount = m.Mount; + + if ( mount != null ) + mount.Rider = null; + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Rider + { + get + { + return m_Rider; + } + set + { + if ( value != m_Rider ) + { + if ( value == null ) + { + Internalize(); + UnmountMe(); + + RemoveFollowers(); + m_Rider = value; + } + else + { + if ( m_Rider != null ) + Dismount( m_Rider ); + + Dismount( value ); + + RemoveFollowers(); + m_Rider = value; + AddFollowers(); + + MountMe(); + } + } + } + } + + public void UnmountMe() + { + Container bp = m_Rider.Backpack; + + ItemID = m_RegularID; + Layer = Layer.Invalid; + Movable = true; + + if ( Hue == 0x4001 ) + Hue = 0; + + if ( bp != null ) + { + bp.DropItem( this ); + } + else + { + Point3D loc = m_Rider.Location; + Map map = m_Rider.Map; + + if ( map == null || map == Map.Internal ) + { + loc = m_Rider.LogoutLocation; + map = m_Rider.LogoutMap; + } + + MoveToWorld( loc, map ); + } + } + + public void MountMe() + { + ItemID = m_MountedID; + Layer = Layer.Mount; + Movable = false; + + if ( Hue == 0 ) + Hue = 0x4001; + + ProcessDelta(); + m_Rider.ProcessDelta(); + m_Rider.EquipItem( this ); + m_Rider.ProcessDelta(); + ProcessDelta(); + } + + public IMount Mount + { + get + { + return this; + } + } + + public static void StopMounting( Mobile mob ) + { + if ( mob.Spell is EtherealSpell ) + ((EtherealSpell)mob.Spell).Stop(); + } + + public void OnRiderDamaged( int amount, Mobile from, bool willKill ) + { + } + + private class EtherealSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( "Ethereal Mount", "", SpellCircle.Second, 230 ); + + private EtherealMount m_Mount; + private Mobile m_Rider; + + public EtherealSpell( EtherealMount mount, Mobile rider ) : base( rider, null, m_Info ) + { + m_Rider = rider; + m_Mount = mount; + } + + public override bool ClearHandsOnCast{ get{ return false; } } + public override bool RevealOnCast{ get{ return false; } } + + public override TimeSpan GetCastRecovery() + { + return TimeSpan.Zero; + } + + public override TimeSpan GetCastDelay() + { + return TimeSpan.FromSeconds( ( (m_Mount.IsDonationItem && RewardSystem.GetRewardLevel( m_Rider ) < 3 )? 12.5 : 5.0 ) ); + } + + public override int GetMana() + { + return 0; + } + + public override bool ConsumeReagents() + { + return true; + } + + public override bool CheckFizzle() + { + return true; + } + + private bool m_Stop; + + public void Stop() + { + m_Stop = true; + Disturb( DisturbType.Hurt, false, false ); + } + + public override bool CheckDisturb( DisturbType type, bool checkFirst, bool resistable ) + { + if ( type == DisturbType.EquipRequest || type == DisturbType.UseRequest/* || type == DisturbType.Hurt*/ ) + return false; + + return true; + } + + public override void DoHurtFizzle() + { + if ( !m_Stop ) + base.DoHurtFizzle(); + } + + public override void DoFizzle() + { + if ( !m_Stop ) + base.DoFizzle(); + } + + public override void OnDisturb( DisturbType type, bool message ) + { + if ( message && !m_Stop ) + Caster.SendLocalizedMessage( 1049455 ); // You have been disrupted while attempting to summon your ethereal mount! + + //m_Mount.UnmountMe(); + } + + public override void OnCast() + { + if ( !m_Mount.Deleted && m_Mount.Rider == null && m_Mount.Validate( m_Rider ) ) + m_Mount.Rider = m_Rider; + + FinishSequence(); + } + } + } + + public class EtherealHorse : EtherealMount + { + public override int LabelNumber{ get{ return 1041298; } } // Ethereal Horse Statuette + + [Constructable] + public EtherealHorse() : base( 0x20DD, 0x3EAA ) + { + } + + public EtherealHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal horse" ) + Name = null; + + if ( ItemID == 0x2124 ) + ItemID = 0x20DD; + } + } + + public class EtherealLlama : EtherealMount + { + public override int LabelNumber{ get{ return 1041300; } } // Ethereal Llama Statuette + + [Constructable] + public EtherealLlama() : base( 0x20F6, 0x3EAB ) + { + } + + public EtherealLlama( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal llama" ) + Name = null; + } + } + + public class EtherealOstard : EtherealMount + { + public override int LabelNumber{ get{ return 1041299; } } // Ethereal Ostard Statuette + + [Constructable] + public EtherealOstard() : base( 0x2135, 0x3EAC ) + { + } + + public EtherealOstard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal ostard" ) + Name = null; + } + } + + public class EtherealRidgeback : EtherealMount + { + public override int LabelNumber{ get{ return 1049747; } } // Ethereal Ridgeback Statuette + + [Constructable] + public EtherealRidgeback() : base( 0x2615, 0x3E9A ) + { + } + + public EtherealRidgeback( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal ridgeback" ) + Name = null; + } + } + + public class EtherealUnicorn : EtherealMount + { + public override int LabelNumber{ get{ return 1049745; } } // Ethereal Unicorn Statuette + + [Constructable] + public EtherealUnicorn() : base( 0x25CE, 0x3E9B ) + { + } + + public EtherealUnicorn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal unicorn" ) + Name = null; + } + } + + public class EtherealBeetle : EtherealMount + { + public override int LabelNumber{ get{ return 1049748; } } // Ethereal Beetle Statuette + + [Constructable] + public EtherealBeetle() : base( 0x260F, 0x3E97 ) + { + } + + public EtherealBeetle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal beetle" ) + Name = null; + } + } + + public class EtherealKirin : EtherealMount + { + public override int LabelNumber{ get{ return 1049746; } } // Ethereal Ki-Rin Statuette + + [Constructable] + public EtherealKirin() : base( 0x25A0, 0x3E9C ) + { + } + + public EtherealKirin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal kirin" ) + Name = null; + } + } + + public class EtherealSwampDragon : EtherealMount + { + public override int LabelNumber{ get{ return 1049749; } } // Ethereal Swamp Dragon Statuette + + [Constructable] + public EtherealSwampDragon() : base( 0x2619, 0x3E98 ) + { + } + + public EtherealSwampDragon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Name == "an ethereal swamp dragon" ) + Name = null; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/FireSteed.cs b/Scripts/Mobiles/Animals/Mounts/FireSteed.cs new file mode 100644 index 000000000..7af136c05 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/FireSteed.cs @@ -0,0 +1,80 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a fire steed corpse" )] + public class FireSteed : BaseMount + { + [Constructable] + public FireSteed() : this( "a fire steed" ) + { + } + + [Constructable] + public FireSteed( string name ) : base( name, 0xBE, 0x3E9E, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0xA8; + + SetStr( 376, 400 ); + SetDex( 91, 120 ); + SetInt( 291, 300 ); + + SetHits( 226, 240 ); + + SetDamage( 11, 30 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Fire, 80 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 100.0, 120.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 20000; + Karma = -20000; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 106.0; + } + + public override void GenerateLoot() + { + PackItem( new SulfurousAsh( Utility.RandomMinMax( 151, 300 ) ) ); + PackItem( new Ruby( Utility.RandomMinMax( 16, 30 ) ) ); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Daemon | PackInstinct.Equine; } } + + public FireSteed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( BaseSoundID <= 0 ) + BaseSoundID = 0xA8; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/ForestOstard.cs b/Scripts/Mobiles/Animals/Mounts/ForestOstard.cs new file mode 100644 index 000000000..84a8b96b2 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/ForestOstard.cs @@ -0,0 +1,68 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an ostard corpse" )] + public class ForestOstard : BaseMount + { + [Constructable] + public ForestOstard() : this( "a forest ostard" ) + { + } + + [Constructable] + public ForestOstard( string name ) : base( name, 0xDB, 0x3EA5, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Hue = Utility.RandomSlimeHue() | 0x8000; + + BaseSoundID = 0x270; + + SetStr( 94, 170 ); + SetDex( 56, 75 ); + SetInt( 6, 10 ); + + SetHits( 71, 88 ); + SetMana( 0 ); + + SetDamage( 8, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + + SetSkill( SkillName.MagicResist, 27.1, 32.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 450; + Karma = 0; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override int Meat{ get{ return 3; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Ostard; } } + + public ForestOstard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/FrenziedOstard.cs b/Scripts/Mobiles/Animals/Mounts/FrenziedOstard.cs new file mode 100644 index 000000000..40f122b38 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/FrenziedOstard.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an ostard corpse" )] + public class FrenziedOstard : BaseMount + { + [Constructable] + public FrenziedOstard() : this( "a frenzied ostard" ) + { + } + + [Constructable] + public FrenziedOstard( string name ) : base( name, 0xDA, 0x3EA4, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Hue = Utility.RandomHairHue() | 0x8000; + + BaseSoundID = 0x275; + + SetStr( 94, 170 ); + SetDex( 96, 115 ); + SetInt( 6, 10 ); + + SetHits( 71, 110 ); + SetMana( 0 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Poison, 20, 25 ); + SetResistance( ResistanceType.Energy, 20, 25 ); + + SetSkill( SkillName.MagicResist, 75.1, 80.0 ); + SetSkill( SkillName.Tactics, 79.3, 94.0 ); + SetSkill( SkillName.Wrestling, 79.3, 94.0 ); + + Fame = 1500; + Karma = -1500; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 77.1; + } + + public override int Meat{ get{ return 3; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish | FoodType.Eggs | FoodType.FruitsAndVegies; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Ostard; } } + + public FrenziedOstard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/HellSteed.cs b/Scripts/Mobiles/Animals/Mounts/HellSteed.cs new file mode 100644 index 000000000..c4cfb2790 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/HellSteed.cs @@ -0,0 +1,61 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a hellsteed corpse" )] + public class HellSteed : BaseMount + { + [Constructable] + public HellSteed() : this( "a hellsteed" ) + { + } + + [Constructable] + public HellSteed( string name ) : base( name, 793, 0x3EBB, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + SetStr( 201, 210 ); + SetDex( 101, 110 ); + SetInt( 101, 115 ); + + SetHits( 201, 220 ); + + SetDamage( 20, 24 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Fire, 75 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 90 ); + SetResistance( ResistanceType.Poison, 100 ); + + SetSkill( SkillName.MagicResist, 90.1, 110.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + SetSkill( SkillName.Wrestling, 90.1, 110.0 ); + + Fame = 0; + Karma = 0; + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + // TODO: "This creature can breath chaos." + + public HellSteed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/Hiryu.cs b/Scripts/Mobiles/Animals/Mounts/Hiryu.cs new file mode 100644 index 000000000..dbfa17306 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Hiryu.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a hiryu corpse" )] + public class Hiryu : BaseMount + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.Dismount; + } + + public override bool StatLossAfterTame{ get{ return true; } } + + [Constructable] + public Hiryu() : base( "a hiryu", 243, 0x3E94, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + if ( 0.05 > Utility.RandomDouble() ) + Hue = Utility.RandomList( 0x32, 0x37, 0x3C, 0x3D, 0x123, 0x294, 0x295, 0x47F, 0x482, 0x487, 0x48D, 0x490, 0x495, 0x55C, 0x8A0, 0x899, 0x89F ) | 0x8000; + + SetStr( 1201, 1410 ); + SetDex( 171, 270 ); + SetInt( 301, 325 ); + + SetHits( 901, 1100 ); + SetMana( 60 ); + + SetDamage( 20, 30 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 55, 70 ); + SetResistance( ResistanceType.Fire, 70, 90 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Anatomy, 75.1, 80.0 ); + SetSkill( SkillName.MagicResist, 85.1, 100.0 ); + SetSkill( SkillName.Tactics, 100.1, 110.0 ); + SetSkill( SkillName.Wrestling, 100.1, 120.0 ); + + Fame = 18000; + Karma = -18000; + + Tamable = true; + ControlSlots = 4; + MinTameSkill = 98.7; + + if ( Utility.RandomDouble() < .33 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + + public override int GetAngerSound() + { + return 0x4FE; + } + + public override int GetIdleSound() + { + return 0x4FD; + } + + public override int GetAttackSound() + { + return 0x4FC; + } + + public override int GetHurtSound() + { + return 0x4FF; + } + + public override int GetDeathSound() + { + return 0x4FB; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 3 ); + AddLoot( LootPack.Gems, 4 ); + } + + public override int TreasureMapLevel{ get{ return 5; } } + public override int Meat{ get{ return 16; } } + public override int Hides{ get{ return 60; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override bool CanAngerOnTame { get { return true; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() ) + { + /* Grasping Claw + * Start cliloc: 1070836 + * Effect: Physical resistance -15% for 5 seconds + * End cliloc: 1070838 + * Effect: Type: "3" - From: "0x57D4F5B" (player) - To: "0x0" - ItemId: "0x37B9" - ItemIdName: "glow" - FromLocation: "(1149 808, 32)" - ToLocation: "(1149 808, 32)" - Speed: "10" - Duration: "5" - FixedDirection: "True" - Explode: "False" + */ + + ExpireTimer timer = (ExpireTimer)m_Table[defender]; + + if ( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070837 ); // The creature lands another blow in your weakened state. + } + else + defender.SendLocalizedMessage( 1070836 ); // The blow from the creature's claws has made you more susceptible to physical attacks. + + int effect = -(defender.PhysicalResistance * 15 / 100); + + ResistanceMod mod = new ResistanceMod( ResistanceType.Physical, effect ); + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.AddResistanceMod( mod ); + + timer = new ExpireTimer( defender, mod, TimeSpan.FromSeconds( 5.0 ) ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private ResistanceMod m_Mod; + + public ExpireTimer( Mobile m, ResistanceMod mod, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + m_Mod = mod; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + m_Mobile.RemoveResistanceMod( m_Mod ); + Stop(); + m_Table.Remove( m_Mobile ); + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage( 1070838 ); // Your resistance to physical attacks has returned. + DoExpire(); + } + } + + public Hiryu( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Animals/Mounts/Horse.cs b/Scripts/Mobiles/Animals/Mounts/Horse.cs new file mode 100644 index 000000000..219d5166e --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Horse.cs @@ -0,0 +1,79 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a horse corpse" )] + [TypeAlias( "Server.Mobiles.BrownHorse", "Server.Mobiles.DirtyHorse", "Server.Mobiles.GrayHorse", "Server.Mobiles.TanHorse" )] + public class Horse : BaseMount + { + private static int[] m_IDs = new int[] + { + 0xC8, 0x3E9F, + 0xE2, 0x3EA0, + 0xE4, 0x3EA1, + 0xCC, 0x3EA2 + }; + + [Constructable] + public Horse() : this( "a horse" ) + { + } + + [Constructable] + public Horse( string name ) : base( name, 0xE2, 0x3EA0, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + int random = Utility.Random( 4 ); + + Body = m_IDs[random * 2]; + ItemID = m_IDs[random * 2 + 1]; + BaseSoundID = 0xA8; + + SetStr( 22, 98 ); + SetDex( 56, 75 ); + SetInt( 6, 10 ); + + SetHits( 28, 45 ); + SetMana( 0 ); + + SetDamage( 3, 4 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 300; + Karma = 300; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override int Meat{ get{ return 3; } } + public override int Hides{ get{ return 10; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Horse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/Kirin.cs b/Scripts/Mobiles/Animals/Mounts/Kirin.cs new file mode 100644 index 000000000..a22632510 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Kirin.cs @@ -0,0 +1,124 @@ +using System; +using Server.Mobiles; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a ki-rin corpse" )] + public class Kirin : BaseMount + { + public override bool AllowFemaleRider{ get{ return false; } } + public override bool AllowFemaleTamer{ get{ return false; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override TimeSpan MountAbilityDelay { get { return TimeSpan.FromHours( 1.0 ); } } + + public override void OnDisallowedRider( Mobile m ) + { + m.SendLocalizedMessage( 1042319 ); // The Ki-Rin refuses your attempts to mount it. + } + + public override bool DoMountAbility( int damage, Mobile attacker ) + { + if( Rider == null || attacker == null ) //sanity + return false; + + if( (Rider.Hits - damage) < 30 && Rider.Map == attacker.Map && Rider.InRange( attacker, 18 ) ) //Range and map checked here instead of other base fuction because of abiliites that don't need to check this + { + attacker.BoltEffect( 0 ); + // 35~100 damage, unresistable, by the Ki-rin. + attacker.Damage( Utility.RandomMinMax( 35, 100 ), this, false ); //Don't inform mount about this damage, Still unsure wether or not it's flagged as the mount doing damage or the player. If changed to player, without the extra bool it'd be an infinite loop + + Rider.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1042534 ); // Your mount calls down the forces of nature on your opponent. + Rider.FixedParticles( 0, 0, 0, 0x13A7, EffectLayer.Waist ); + Rider.PlaySound( 0xA9 ); // Ki-rin's whinny. + return true; + } + + return false; + } + + [Constructable] + public Kirin() : this( "a ki-rin" ) + { + } + + [Constructable] + public Kirin( string name ) : base( name, 132, 0x3EAD, AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x3C5; + + SetStr( 296, 325 ); + SetDex( 86, 105 ); + SetInt( 186, 225 ); + + SetHits( 191, 210 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Fire, 10 ); + SetDamageType( ResistanceType.Cold, 10 ); + SetDamageType( ResistanceType.Energy, 10 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 35, 45 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.EvalInt, 80.1, 90.0 ); + SetSkill( SkillName.Magery, 60.4, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 85.3, 100.0 ); + SetSkill( SkillName.Tactics, 20.1, 22.5 ); + SetSkill( SkillName.Wrestling, 80.5, 92.5 ); + + Fame = 9000; + Karma = 9000; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 95.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.Potions ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override int Meat{ get{ return 3; } } + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Horned; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Kirin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version == 0 ) + AI = AIType.AI_Mage; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/LesserHiryu.cs b/Scripts/Mobiles/Animals/Mounts/LesserHiryu.cs new file mode 100644 index 000000000..529c6eefe --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/LesserHiryu.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a hiryu corpse" )] + public class LesserHiryu : BaseMount + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.Dismount; + } + + public override bool StatLossAfterTame{ get{ return true; } } + + [Constructable] + public LesserHiryu() : base( "a lesser hiryu", 243, 0x3E94, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + if ( 0.05 > Utility.RandomDouble() ) + Hue = Utility.RandomList( 0x32, 0x37, 0x3C, 0x3D, 0x123, 0x294, 0x295, 0x47F, 0x482, 0x487, 0x48D, 0x490, 0x495, 0x55C, 0x8A0, 0x899, 0x89F ) | 0x8000; + + SetStr( 301, 410 ); + SetDex( 171, 270 ); + SetInt( 301, 325 ); + + SetHits( 401, 600 ); + SetMana( 60 ); + + SetDamage( 18, 23 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 70 ); + SetResistance( ResistanceType.Fire, 60, 80 ); + SetResistance( ResistanceType.Cold, 5, 15 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.Anatomy, 75.1, 80.0 ); + SetSkill( SkillName.MagicResist, 85.1, 100.0 ); + SetSkill( SkillName.Tactics, 100.1, 110.0 ); + SetSkill( SkillName.Wrestling, 100.1, 120.0 ); + + Fame = 18000; + Karma = -18000; + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 98.7; + + if ( Utility.RandomDouble() < .33 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + + public override int GetAngerSound() + { + return 0x4FE; + } + + public override int GetIdleSound() + { + return 0x4FD; + } + + public override int GetAttackSound() + { + return 0x4FC; + } + + public override int GetHurtSound() + { + return 0x4FF; + } + + public override int GetDeathSound() + { + return 0x4FB; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.Gems, 4 ); + } + + public override double GetControlChance( Mobile m ) + { + if ( m.Skills.Bushido.Value >= 90.0 ) + return 1.0; + + return base.GetControlChance( m ); + } + + public override int TreasureMapLevel{ get{ return 3; } } + public override int Meat{ get{ return 16; } } + public override int Hides{ get{ return 60; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override bool CanAngerOnTame { get { return true; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() ) + { + /* Grasping Claw + * Start cliloc: 1070836 + * Effect: Physical resistance -15% for 5 seconds + * End cliloc: 1070838 + * Effect: Type: "3" - From: "0x57D4F5B" (player) - To: "0x0" - ItemId: "0x37B9" - ItemIdName: "glow" - FromLocation: "(1149 808, 32)" - ToLocation: "(1149 808, 32)" - Speed: "10" - Duration: "5" - FixedDirection: "True" - Explode: "False" + */ + + ExpireTimer timer = (ExpireTimer)m_Table[defender]; + + if ( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070837 ); // The creature lands another blow in your weakened state. + } + else + defender.SendLocalizedMessage( 1070836 ); // The blow from the creature's claws has made you more susceptible to physical attacks. + + int effect = -(defender.PhysicalResistance * 15 / 100); + + ResistanceMod mod = new ResistanceMod( ResistanceType.Physical, effect ); + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.AddResistanceMod( mod ); + + timer = new ExpireTimer( defender, mod, TimeSpan.FromSeconds( 5.0 ) ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private ResistanceMod m_Mod; + + public ExpireTimer( Mobile m, ResistanceMod mod, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + m_Mod = mod; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + m_Mobile.RemoveResistanceMod( m_Mod ); + Stop(); + m_Table.Remove( m_Mobile ); + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage( 1070838 ); // Your resistance to physical attacks has returned. + DoExpire(); + } + } + + public LesserHiryu( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Animals/Mounts/Nightmare.cs b/Scripts/Mobiles/Animals/Mounts/Nightmare.cs new file mode 100644 index 000000000..a29fb539e --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Nightmare.cs @@ -0,0 +1,123 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a nightmare corpse" )] + public class Nightmare : BaseMount + { + [Constructable] + public Nightmare() : this( "a nightmare" ) + { + } + + [Constructable] + public Nightmare( string name ) : base( name, 0x74, 0x3EA7, AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0xA8; + + SetStr( 496, 525 ); + SetDex( 86, 105 ); + SetInt( 86, 125 ); + + SetHits( 298, 315 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Fire, 40 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.EvalInt, 10.4, 50.0 ); + SetSkill( SkillName.Magery, 10.4, 50.0 ); + SetSkill( SkillName.MagicResist, 85.3, 100.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 80.5, 92.5 ); + + Fame = 14000; + Karma = -14000; + + VirtualArmor = 60; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 95.1; + + switch ( Utility.Random( 3 ) ) + { + case 0: + { + BodyValue = 116; + ItemID = 16039; + break; + } + case 1: + { + BodyValue = 178; + ItemID = 16041; + break; + } + case 2: + { + BodyValue = 179; + ItemID = 16055; + break; + } + } + + PackItem( new SulfurousAsh( Utility.RandomMinMax( 3, 5 ) ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.Potions ); + } + + public override int GetAngerSound() + { + if ( !Controlled ) + return 0x16A; + + return base.GetAngerSound(); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Meat{ get{ return 5; } } + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override bool CanAngerOnTame { get { return true; } } + + public Nightmare( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( BaseSoundID == 0x16A ) + BaseSoundID = 0xA8; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/RidableLlama.cs b/Scripts/Mobiles/Animals/Mounts/RidableLlama.cs new file mode 100644 index 000000000..71aedd57e --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/RidableLlama.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a llama corpse" )] + public class RidableLlama : BaseMount + { + [Constructable] + public RidableLlama() : this( "a ridable llama" ) + { + } + + [Constructable] + public RidableLlama( string name ) : base( name, 0xDC, 0x3EA6, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x3F3; + + SetStr( 21, 49 ); + SetDex( 56, 75 ); + SetInt( 16, 30 ); + + SetHits( 15, 27 ); + SetMana( 0 ); + + SetDamage( 3, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10, 15 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.2, 29.0 ); + SetSkill( SkillName.Wrestling, 19.2, 29.0 ); + + Fame = 300; + Karma = 0; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public RidableLlama( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/Ridgeback.cs b/Scripts/Mobiles/Animals/Mounts/Ridgeback.cs new file mode 100644 index 000000000..46dc8d221 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Ridgeback.cs @@ -0,0 +1,76 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a ridgeback corpse" )] + public class Ridgeback : BaseMount + { + [Constructable] + public Ridgeback() : this( "a ridgeback" ) + { + } + + [Constructable] + public Ridgeback( string name ) : base( name, 187, 0x3EBA, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x3F3; + + SetStr( 58, 100 ); + SetDex( 56, 75 ); + SetInt( 16, 30 ); + + SetHits( 41, 54 ); + SetMana( 0 ); + + SetDamage( 3, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 25.3, 40.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 35.1, 45.0 ); + + Fame = 300; + Karma = 0; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 83.1; + } + + public override double GetControlChance( Mobile m ) + { + return 1.0; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Ridgeback( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/SavageRidgeback.cs b/Scripts/Mobiles/Animals/Mounts/SavageRidgeback.cs new file mode 100644 index 000000000..e342be44d --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/SavageRidgeback.cs @@ -0,0 +1,76 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a savage ridgeback corpse" )] + public class SavageRidgeback : BaseMount + { + [Constructable] + public SavageRidgeback() : this( "a savage ridgeback" ) + { + } + + [Constructable] + public SavageRidgeback( string name ) : base( name, 188, 0x3EB8, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x3F3; + + SetStr( 58, 100 ); + SetDex( 56, 75 ); + SetInt( 16, 30 ); + + SetHits( 41, 54 ); + SetMana( 0 ); + + SetDamage( 3, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 15, 20 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 25.3, 40.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 35.1, 45.0 ); + + Fame = 300; + Karma = 0; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 83.1; + } + + public override double GetControlChance( Mobile m ) + { + return 1.0; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public SavageRidgeback( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/ScaledSwampDragon.cs b/Scripts/Mobiles/Animals/Mounts/ScaledSwampDragon.cs new file mode 100644 index 000000000..5b8cb590e --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/ScaledSwampDragon.cs @@ -0,0 +1,73 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a swamp dragon corpse" )] + public class ScaledSwampDragon : BaseMount + { + [Constructable] + public ScaledSwampDragon() : this( "a swamp dragon" ) + { + } + + [Constructable] + public ScaledSwampDragon( string name ) : base( name, 0x31F, 0x3EBE, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + SetStr( 201, 300 ); + SetDex( 66, 85 ); + SetInt( 61, 100 ); + + SetHits( 121, 180 ); + + SetDamage( 3, 4 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Poison, 25 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 20, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.Anatomy, 45.1, 55.0 ); + SetSkill( SkillName.MagicResist, 45.1, 55.0 ); + SetSkill( SkillName.Tactics, 45.1, 55.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 2000; + Karma = -2000; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 93.9; + } + + public override double GetControlChance( Mobile m ) + { + return 1.0; + } + + public override bool AutoDispel{ get{ return !Controlled; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + + public ScaledSwampDragon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/SeaHorse.cs b/Scripts/Mobiles/Animals/Mounts/SeaHorse.cs new file mode 100644 index 000000000..dbfa7d82d --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/SeaHorse.cs @@ -0,0 +1,41 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a sea horse corpse" )] + public class SeaHorse : BaseMount + { + [Constructable] + public SeaHorse() : this( "a sea horse" ) + { + } + + [Constructable] + public SeaHorse( string name ) : base( name, 0x90, 0x3EB3, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + InitStats( Utility.Random( 50, 30 ), Utility.Random( 50, 30 ), 10 ); + Skills[SkillName.MagicResist].Base = 25.0 + (Utility.RandomDouble() * 5.0); + Skills[SkillName.Wrestling].Base = 35.0 + (Utility.RandomDouble() * 10.0); + Skills[SkillName.Tactics].Base = 30.0 + (Utility.RandomDouble() * 15.0); + } + + public SeaHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/SilverSteed.cs b/Scripts/Mobiles/Animals/Mounts/SilverSteed.cs new file mode 100644 index 000000000..fb722953f --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/SilverSteed.cs @@ -0,0 +1,45 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a silver steed corpse" )] + public class SilverSteed : BaseMount + { + [Constructable] + public SilverSteed() : this( "a silver steed" ) + { + } + + [Constructable] + public SilverSteed( string name ) : base( name, 0x75, 0x3EA8, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + InitStats( Utility.Random( 50, 30 ), Utility.Random( 50, 30 ), 10 ); + Skills[SkillName.MagicResist].Base = 25.0 + (Utility.RandomDouble() * 5.0); + Skills[SkillName.Wrestling].Base = 35.0 + (Utility.RandomDouble() * 10.0); + Skills[SkillName.Tactics].Base = 30.0 + (Utility.RandomDouble() * 15.0); + + ControlSlots = 1; + Tamable = true; + MinTameSkill = 103.1; + } + + public SilverSteed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/SkeletalMount.cs b/Scripts/Mobiles/Animals/Mounts/SkeletalMount.cs new file mode 100644 index 000000000..95931d55e --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/SkeletalMount.cs @@ -0,0 +1,73 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an undead horse corpse" )] + public class SkeletalMount : BaseMount + { + [Constructable] + public SkeletalMount() : this( "a skeletal steed" ) + { + } + + [Constructable] + public SkeletalMount( string name ) : base( name, 793, 0x3EBB, AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + SetStr( 91, 100 ); + SetDex( 46, 55 ); + SetInt( 46, 60 ); + + SetHits( 41, 50 ); + + SetDamage( 5, 12 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Cold, 90, 95 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.MagicResist, 95.1, 100.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + SetSkill( SkillName.Wrestling, 70.1, 80.0 ); + + Fame = 0; + Karma = 0; + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public SkeletalMount( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + Name = "a skeletal steed"; + Tamable = false; + MinTameSkill = 0.0; + ControlSlots = 0; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/SwampDragon.cs b/Scripts/Mobiles/Animals/Mounts/SwampDragon.cs new file mode 100644 index 000000000..a5db3236b --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/SwampDragon.cs @@ -0,0 +1,210 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a swamp dragon corpse" )] + public class SwampDragon : BaseMount + { + private bool m_BardingExceptional; + private Mobile m_BardingCrafter; + private int m_BardingHP; + private bool m_HasBarding; + private CraftResource m_BardingResource; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile BardingCrafter + { + get{ return m_BardingCrafter; } + set{ m_BardingCrafter = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool BardingExceptional + { + get{ return m_BardingExceptional; } + set{ m_BardingExceptional = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int BardingHP + { + get{ return m_BardingHP; } + set{ m_BardingHP = value; InvalidateProperties(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool HasBarding + { + get{ return m_HasBarding; } + set + { + m_HasBarding = value; + + if ( m_HasBarding ) + { + Hue = CraftResources.GetHue( m_BardingResource ); + BodyValue = 0x31F; + ItemID = 0x3EBE; + } + else + { + Hue = 0x851; + BodyValue = 0x31A; + ItemID = 0x3EBD; + } + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public CraftResource BardingResource + { + get{ return m_BardingResource; } + set + { + m_BardingResource = value; + + if ( m_HasBarding ) + Hue = CraftResources.GetHue( value ); + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int BardingMaxHP + { + get{ return m_BardingExceptional ? 2500 : 1000; } + } + + [Constructable] + public SwampDragon() : this( "a swamp dragon" ) + { + } + + [Constructable] + public SwampDragon( string name ) : base( name, 0x31A, 0x3EBD, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x16A; + + SetStr( 201, 300 ); + SetDex( 66, 85 ); + SetInt( 61, 100 ); + + SetHits( 121, 180 ); + + SetDamage( 3, 4 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Poison, 25 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 20, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.Anatomy, 45.1, 55.0 ); + SetSkill( SkillName.MagicResist, 45.1, 55.0 ); + SetSkill( SkillName.Tactics, 45.1, 55.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 2000; + Karma = -2000; + + Hue = 0x851; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 93.9; + } + + public override int GetIdleSound() + { + return 0x2CE; + } + + public override int GetDeathSound() + { + return 0x2CC; + } + + public override int GetHurtSound() + { + return 0x2D1; + } + + public override int GetAttackSound() + { + return 0x2C8; + } + + public override double GetControlChance( Mobile m ) + { + return 1.0; + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override bool AutoDispel{ get{ return !Controlled; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override int Meat{ get{ return 19; } } + public override int Hides{ get{ return 20; } } + public override int Scales{ get{ return 5; } } + public override ScaleType ScaleType{ get{ return ScaleType.Green; } } + public override bool CanAngerOnTame { get { return true; } } + + public SwampDragon( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_HasBarding && m_BardingExceptional && m_BardingCrafter != null ) + list.Add( 1060853, m_BardingCrafter.Name ); // armor exceptionally crafted by ~1_val~ + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) m_BardingExceptional ); + writer.Write( (Mobile) m_BardingCrafter ); + writer.Write( (bool) m_HasBarding ); + writer.Write( (int) m_BardingHP ); + writer.Write( (int) m_BardingResource ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_BardingExceptional = reader.ReadBool(); + m_BardingCrafter = reader.ReadMobile(); + m_HasBarding = reader.ReadBool(); + m_BardingHP = reader.ReadInt(); + m_BardingResource = (CraftResource) reader.ReadInt(); + break; + } + } + + if ( Hue == 0 && !m_HasBarding ) + Hue = 0x851; + + if ( BaseSoundID == -1 ) + BaseSoundID = 0x16A; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/Unicorn.cs b/Scripts/Mobiles/Animals/Mounts/Unicorn.cs new file mode 100644 index 000000000..756e60213 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/Unicorn.cs @@ -0,0 +1,132 @@ +using System; +using Server.Mobiles; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a unicorn corpse" )] + public class Unicorn : BaseMount + { + public override bool AllowMaleRider{ get{ return false; } } + public override bool AllowMaleTamer{ get{ return false; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override TimeSpan MountAbilityDelay { get { return TimeSpan.FromHours( 1.0 ); } } + + public override void OnDisallowedRider( Mobile m ) + { + m.SendLocalizedMessage( 1042318 ); // The unicorn refuses to allow you to ride it. + } + + public override bool DoMountAbility( int damage, Mobile attacker ) + { + if( Rider == null || attacker == null ) //sanity + return false; + + if( Rider.Poisoned && ((Rider.Hits - damage) < 40) ) + { + Poison p = Rider.Poison; + + if( p != null ) + { + int chanceToCure = 10000 + (int)(this.Skills[SkillName.Magery].Value * 75) - ((p.Level + 1) * (Core.AOS ? (p.Level < 4 ? 3300 : 3100) : 1750)); + chanceToCure /= 100; + + if( chanceToCure > Utility.Random( 100 ) ) + { + if( Rider.CurePoison( this ) ) //TODO: Confirm if mount is the one flagged for curing it or the rider is + { + Rider.LocalOverheadMessage( Server.Network.MessageType.Regular, 0x3B2, true, "Your mount senses you are in danger and aids you with magic." ); + Rider.FixedParticles( 0x373A, 10, 15, 5012, EffectLayer.Waist ); + Rider.PlaySound( 0x1E0 ); // Cure spell effect. + Rider.PlaySound( 0xA9 ); // Unicorn's whinny. + + return true; + } + } + } + } + + return false; + } + + [Constructable] + public Unicorn() : this( "a unicorn" ) + { + } + + [Constructable] + public Unicorn( string name ) : base( name, 0x7A, 0x3EB4, AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + BaseSoundID = 0x4BC; + + SetStr( 296, 325 ); + SetDex( 96, 115 ); + SetInt( 186, 225 ); + + SetHits( 191, 210 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 25, 40 ); + SetResistance( ResistanceType.Cold, 25, 40 ); + SetResistance( ResistanceType.Poison, 55, 65 ); + SetResistance( ResistanceType.Energy, 25, 40 ); + + SetSkill( SkillName.EvalInt, 80.1, 90.0 ); + SetSkill( SkillName.Magery, 60.2, 80.0 ); + SetSkill( SkillName.Meditation, 50.1, 60.0 ); + SetSkill( SkillName.MagicResist, 75.3, 90.0 ); + SetSkill( SkillName.Tactics, 20.1, 22.5 ); + SetSkill( SkillName.Wrestling, 80.5, 92.5 ); + + Fame = 9000; + Karma = 9000; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 95.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.Potions ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int Meat{ get{ return 3; } } + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Horned; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Unicorn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs b/Scripts/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs new file mode 100644 index 000000000..d2bee37e8 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/War Horses/BaseWarHorse.cs @@ -0,0 +1,65 @@ +using System; +using Server; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a war horse corpse" )] + public abstract class BaseWarHorse : BaseMount + { + public BaseWarHorse( int bodyID, int itemID, AIType aiType, FightMode fightMode, int rangePerception, int rangeFight, double activeSpeed, double passiveSpeed ) : base ( "a war horse", bodyID, itemID, aiType, fightMode, rangePerception, rangeFight, activeSpeed, passiveSpeed ) + { + BaseSoundID = 0xA8; + + InitStats( Utility.Random( 300, 100 ), 125, 60 ); + + SetStr( 400 ); + SetDex( 125 ); + SetInt( 51, 55 ); + + SetHits( 240 ); + SetMana( 0 ); + + SetDamage( 5, 8 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 300; + Karma = 300; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public BaseWarHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs b/Scripts/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs new file mode 100644 index 000000000..5fdc281c3 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/War Horses/CoMWarHorse.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class CoMWarHorse : BaseWarHorse + { + [Constructable] + public CoMWarHorse() : base( 0x77, 0x3EB1, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + } + + public CoMWarHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs b/Scripts/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs new file mode 100644 index 000000000..6fcfaa5ba --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/War Horses/MinaxWarHorse.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class MinaxWarHorse : BaseWarHorse + { + [Constructable] + public MinaxWarHorse() : base( 0x78, 0x3EAF, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + } + + public MinaxWarHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs b/Scripts/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs new file mode 100644 index 000000000..28064feb9 --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/War Horses/SLWarHorse.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class SLWarHorse : BaseWarHorse + { + [Constructable] + public SLWarHorse() : base( 0x79, 0x3EB0, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + } + + public SLWarHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs b/Scripts/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs new file mode 100644 index 000000000..17908740a --- /dev/null +++ b/Scripts/Mobiles/Animals/Mounts/War Horses/TBWarHorse.cs @@ -0,0 +1,31 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class TBWarHorse : BaseWarHorse + { + [Constructable] + public TBWarHorse() : base( 0x76, 0x3EB2, AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + } + + public TBWarHorse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/Alligator.cs b/Scripts/Mobiles/Animals/Reptiles/Alligator.cs new file mode 100644 index 000000000..dc612e748 --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/Alligator.cs @@ -0,0 +1,72 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an alligator corpse" )] + public class Alligator : BaseCreature + { + [Constructable] + public Alligator() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an alligator"; + Body = 0xCA; + BaseSoundID = 660; + + SetStr( 76, 100 ); + SetDex( 6, 25 ); + SetInt( 11, 20 ); + + SetHits( 46, 60 ); + SetStam( 46, 65 ); + SetMana( 0 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 40.1, 60.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 600; + Karma = -600; + + VirtualArmor = 30; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 47.1; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish; } } + + public Alligator(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( BaseSoundID == 0x5A ) + BaseSoundID = 660; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/GiantSerpent.cs b/Scripts/Mobiles/Animals/Reptiles/GiantSerpent.cs new file mode 100644 index 000000000..ef42c1a0a --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/GiantSerpent.cs @@ -0,0 +1,86 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a giant serpent corpse" )] + [TypeAlias( "Server.Mobiles.Serpant" )] + public class GiantSerpent : BaseCreature + { + [Constructable] + public GiantSerpent() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a giant serpent"; + Body = 0x15; + Hue = Utility.RandomSnakeHue(); + BaseSoundID = 219; + + SetStr( 186, 215 ); + SetDex( 56, 80 ); + SetInt( 66, 85 ); + + SetHits( 112, 129 ); + SetMana( 0 ); + + SetDamage( 7, 17 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Poison, 60 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 70, 90 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Poisoning, 70.1, 100.0 ); + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 65.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 32; + + PackItem( new Bone() ); + // TODO: Body parts + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + public override Poison HitPoison{ get{ return (0.8 >= Utility.RandomDouble() ? Poison.Greater : Poison.Deadly); } } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override int Meat{ get{ return 4; } } + public override int Hides{ get{ return 15; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public GiantSerpent(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( BaseSoundID == -1 ) + BaseSoundID = 219; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/IceSerpent.cs b/Scripts/Mobiles/Animals/Reptiles/IceSerpent.cs new file mode 100644 index 000000000..380195f60 --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/IceSerpent.cs @@ -0,0 +1,97 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an ice serpent corpse" )] + [TypeAlias( "Server.Mobiles.Iceserpant" )] + public class IceSerpent : BaseCreature + { + [Constructable] + public IceSerpent() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a giant ice serpent"; + Body = 89; + BaseSoundID = 219; + + SetStr( 216, 245 ); + SetDex( 26, 50 ); + SetInt( 66, 85 ); + + SetHits( 130, 147 ); + SetMana( 0 ); + + SetDamage( 7, 17 ); + + SetDamageType( ResistanceType.Physical, 10 ); + SetDamageType( ResistanceType.Cold, 90 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Cold, 80, 90 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Anatomy, 27.5, 50.0 ); + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 75.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 32; + + PackItem( Loot.RandomArmorOrShieldOrWeapon() ); + + switch ( Utility.Random( 10 )) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + + if ( 0.025 > Utility.RandomDouble() ) + PackItem( new GlacialStaff() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override int Meat{ get{ return 4; } } + public override int Hides{ get{ return 15; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public IceSerpent(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( BaseSoundID == -1 ) + BaseSoundID = 219; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/IceSnake.cs b/Scripts/Mobiles/Animals/Reptiles/IceSnake.cs new file mode 100644 index 000000000..c45bfb547 --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/IceSnake.cs @@ -0,0 +1,72 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "an ice snake corpse" )] + [TypeAlias( "Server.Mobiles.Icesnake" )] + public class IceSnake : BaseCreature + { + [Constructable] + public IceSnake() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ice snake"; + Body = 52; + Hue = 0x480; + BaseSoundID = 0xDB; + + SetStr( 42, 54 ); + SetDex( 36, 45 ); + SetInt( 26, 30 ); + + SetMana( 0 ); + + SetDamage( 4, 12 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Cold, 25 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Cold, 80, 90 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 39.3, 54.0 ); + SetSkill( SkillName.Wrestling, 39.3, 54.0 ); + + Fame = 900; + Karma = -900; + + VirtualArmor = 30; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override int Meat{ get{ return 1; } } + + public IceSnake(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/LavaLizard.cs b/Scripts/Mobiles/Animals/Reptiles/LavaLizard.cs new file mode 100644 index 000000000..068295dfd --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/LavaLizard.cs @@ -0,0 +1,78 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a lava lizard corpse" )] + [TypeAlias( "Server.Mobiles.Lavalizard" )] + public class LavaLizard : BaseCreature + { + [Constructable] + public LavaLizard() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a lava lizard"; + Body = 0xCE; + Hue = Utility.RandomList( 0x647, 0x650, 0x659, 0x662, 0x66B, 0x674 ); + BaseSoundID = 0x5A; + + SetStr( 126, 150 ); + SetDex( 56, 75 ); + SetInt( 11, 20 ); + + SetHits( 76, 90 ); + SetMana( 0 ); + + SetDamage( 6, 24 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 30, 45 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 60.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 40; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 80.7; + + PackItem( new SulfurousAsh( Utility.Random( 4, 10 ) ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Hides{ get{ return 12; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public LavaLizard(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/LavaSerpent.cs b/Scripts/Mobiles/Animals/Reptiles/LavaSerpent.cs new file mode 100644 index 000000000..ec15a3925 --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/LavaSerpent.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a lava serpent corpse" )] + [TypeAlias( "Server.Mobiles.Lavaserpant" )] + public class LavaSerpent : BaseCreature + { + [Constructable] + public LavaSerpent() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a lava serpent"; + Body = 90; + BaseSoundID = 219; + + SetStr( 386, 415 ); + SetDex( 56, 80 ); + SetInt( 66, 85 ); + + SetHits( 232, 249 ); + SetMana( 0 ); + + SetDamage( 10, 22 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Fire, 80 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 25.3, 70.0 ); + SetSkill( SkillName.Tactics, 65.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 40; + + PackItem( new SulfurousAsh( 3 ) ); + PackItem( new Bone() ); + // TODO: body parts, armour + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Meat{ get{ return 4; } } + public override int Hides{ get{ return 15; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public LavaSerpent(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( BaseSoundID == -1 ) + BaseSoundID = 219; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/LavaSnake.cs b/Scripts/Mobiles/Animals/Reptiles/LavaSnake.cs new file mode 100644 index 000000000..5daa60c4d --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/LavaSnake.cs @@ -0,0 +1,75 @@ +using System; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName("a lava snake corpse")] + [TypeAlias( "Server.Mobiles.Lavasnake" )] + public class LavaSnake : BaseCreature + { + [Constructable] + public LavaSnake() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a lava snake"; + Body = 52; + Hue = Utility.RandomList( 0x647, 0x650, 0x659, 0x662, 0x66B, 0x674 ); + BaseSoundID = 0xDB; + + SetStr( 43, 55 ); + SetDex( 16, 25 ); + SetInt( 6, 10 ); + + SetHits( 28, 32 ); + SetMana( 0 ); + + SetDamage( 1, 8 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.3, 34.0 ); + SetSkill( SkillName.Wrestling, 19.3, 34.0 ); + + Fame = 600; + Karma = -600; + + VirtualArmor = 24; + + PackItem( new SulfurousAsh() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Meat{ get{ return 1; } } + + public LavaSnake(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/SilverSerpent.cs b/Scripts/Mobiles/Animals/Reptiles/SilverSerpent.cs new file mode 100644 index 000000000..d3c9fa3b3 --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/SilverSerpent.cs @@ -0,0 +1,82 @@ +using System; +using Server.Mobiles; +using Server.Factions; + +namespace Server.Mobiles +{ + [CorpseName("a silver serpent corpse")] + [TypeAlias( "Server.Mobiles.Silverserpant" )] + public class SilverSerpent : BaseCreature + { + public override Faction FactionAllegiance { get { return TrueBritannians.Instance; } } + public override Ethics.Ethic EthicAllegiance { get { return Ethics.Ethic.Hero; } } + + [Constructable] + public SilverSerpent() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 92; + Name = "a silver serpent"; + BaseSoundID = 219; + + SetStr( 161, 360 ); + SetDex( 151, 300 ); + SetInt( 21, 40 ); + + SetHits( 97, 216 ); + + SetDamage( 5, 21 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.Poisoning, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 95.1, 100.0 ); + SetSkill( SkillName.Tactics, 80.1, 95.0 ); + SetSkill( SkillName.Wrestling, 85.1, 100.0 ); + + Fame = 7000; + Karma = -7000; + + VirtualArmor = 40; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override int Meat{ get{ return 1; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + + public SilverSerpent(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + + if ( BaseSoundID == -1 ) + BaseSoundID = 219; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Reptiles/Snake.cs b/Scripts/Mobiles/Animals/Reptiles/Snake.cs new file mode 100644 index 000000000..9d64fb13a --- /dev/null +++ b/Scripts/Mobiles/Animals/Reptiles/Snake.cs @@ -0,0 +1,72 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a snake corpse" )] + public class Snake : BaseCreature + { + [Constructable] + public Snake() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a snake"; + Body = 52; + Hue = Utility.RandomSnakeHue(); + BaseSoundID = 0xDB; + + SetStr( 22, 34 ); + SetDex( 16, 25 ); + SetInt( 6, 10 ); + + SetHits( 15, 19 ); + SetMana( 0 ); + + SetDamage( 1, 4 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + + SetSkill( SkillName.Poisoning, 50.1, 70.0 ); + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.3, 34.0 ); + SetSkill( SkillName.Wrestling, 19.3, 34.0 ); + + Fame = 300; + Karma = -300; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 59.1; + } + + public override Poison PoisonImmune{ get{ return Poison.Lesser; } } + public override Poison HitPoison{ get{ return Poison.Lesser; } } + + public override bool DeathAdderCharmable{ get{ return true; } } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Eggs; } } + + public Snake(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Rodents/GiantRat.cs b/Scripts/Mobiles/Animals/Rodents/GiantRat.cs new file mode 100644 index 000000000..f7ea383e5 --- /dev/null +++ b/Scripts/Mobiles/Animals/Rodents/GiantRat.cs @@ -0,0 +1,73 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a giant rat corpse" )] + [TypeAlias( "Server.Mobiles.Giantrat" )] + public class GiantRat : BaseCreature + { + [Constructable] + public GiantRat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a giant rat"; + Body = 0xD7; + BaseSoundID = 0x188; + + SetStr( 32, 74 ); + SetDex( 46, 65 ); + SetInt( 16, 30 ); + + SetHits( 26, 39 ); + SetMana( 0 ); + + SetDamage( 4, 8 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + + SetSkill( SkillName.MagicResist, 25.1, 30.0 ); + SetSkill( SkillName.Tactics, 29.3, 44.0 ); + SetSkill( SkillName.Wrestling, 29.3, 44.0 ); + + Fame = 300; + Karma = -300; + + VirtualArmor = 18; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 29.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 6; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish | FoodType.Meat | FoodType.FruitsAndVegies | FoodType.Eggs; } } + + public GiantRat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Rodents/JackRabbit.cs b/Scripts/Mobiles/Animals/Rodents/JackRabbit.cs new file mode 100644 index 000000000..fc5e2fdb4 --- /dev/null +++ b/Scripts/Mobiles/Animals/Rodents/JackRabbit.cs @@ -0,0 +1,81 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a jack rabbit corpse" )] + [TypeAlias( "Server.Mobiles.Jackrabbit" )] + public class JackRabbit : BaseCreature + { + [Constructable] + public JackRabbit() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a jack rabbit"; + Body = 0xCD; + Hue = 0x1BB; + + SetStr( 15 ); + SetDex( 25 ); + SetInt( 5 ); + + SetHits( 9 ); + SetMana( 0 ); + + SetDamage( 1, 2 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 2, 5 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 150; + Karma = 0; + + VirtualArmor = 4; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -18.9; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies; } } + + public JackRabbit(Serial serial) : base(serial) + { + } + + public override int GetAttackSound() + { + return 0xC9; + } + + public override int GetHurtSound() + { + return 0xCA; + } + + public override int GetDeathSound() + { + return 0xCB; + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Rodents/Rabbit.cs b/Scripts/Mobiles/Animals/Rodents/Rabbit.cs new file mode 100644 index 000000000..f31a01684 --- /dev/null +++ b/Scripts/Mobiles/Animals/Rodents/Rabbit.cs @@ -0,0 +1,82 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a hare corpse" )] + public class Rabbit : BaseCreature + { + [Constructable] + public Rabbit() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a rabbit"; + Body = 205; + + if ( 0.5 >= Utility.RandomDouble() ) + Hue = Utility.RandomAnimalHue(); + + SetStr( 6, 10 ); + SetDex( 26, 38 ); + SetInt( 6, 14 ); + + SetHits( 4, 6 ); + SetMana( 0 ); + + SetDamage( 1 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 150; + Karma = 0; + + VirtualArmor = 6; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -18.9; + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies; } } + + public Rabbit(Serial serial) : base(serial) + { + } + + public override int GetAttackSound() + { + return 0xC9; + } + + public override int GetHurtSound() + { + return 0xCA; + } + + public override int GetDeathSound() + { + return 0xCB; + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Rodents/SewerRat.cs b/Scripts/Mobiles/Animals/Rodents/SewerRat.cs new file mode 100644 index 000000000..882dfc2b7 --- /dev/null +++ b/Scripts/Mobiles/Animals/Rodents/SewerRat.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a sewer rat corpse" )] + public class Sewerrat : BaseCreature + { + [Constructable] + public Sewerrat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a sewer rat"; + Body = 238; + BaseSoundID = 0xCC; + + SetStr( 9 ); + SetDex( 25 ); + SetInt( 6, 10 ); + + SetHits( 6 ); + SetMana( 0 ); + + SetDamage( 1, 2 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 300; + Karma = -300; + + VirtualArmor = 6; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -0.9; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Eggs | FoodType.FruitsAndVegies; } } + + public Sewerrat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Slimes/Jwilson.cs b/Scripts/Mobiles/Animals/Slimes/Jwilson.cs new file mode 100644 index 000000000..04b435b8b --- /dev/null +++ b/Scripts/Mobiles/Animals/Slimes/Jwilson.cs @@ -0,0 +1,71 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a jwilson corpse" )] + public class Jwilson : BaseCreature + { + [Constructable] + public Jwilson() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Hue = Utility.RandomList(0x89C,0x8A2,0x8A8,0x8AE); + this.Body = 0x33; + this.Name = ("a jwilson"); + this.VirtualArmor = 8; + + this.InitStats(Utility.Random(22,13),Utility.Random(16,6),Utility.Random(16,5)); + + this.Skills[SkillName.Wrestling].Base = Utility.Random(24,17); + this.Skills[SkillName.Tactics].Base = Utility.Random(18,14); + this.Skills[SkillName.MagicResist].Base = Utility.Random(15,6); + this.Skills[SkillName.Poisoning].Base = Utility.Random(31,20); + + this.Fame = Utility.Random(0,1249); + this.Karma = Utility.Random(0,-624); + } + + public Jwilson(Serial serial) : base(serial) + { + } + + public override int GetAngerSound() + { + return 0x1C8; + } + + public override int GetIdleSound() + { + return 0x1C9; + } + + public override int GetAttackSound() + { + return 0x1CA; + } + + public override int GetHurtSound() + { + return 0x1CB; + } + + public override int GetDeathSound() + { + return 0x1CC; + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs b/Scripts/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs new file mode 100644 index 000000000..16616924d --- /dev/null +++ b/Scripts/Mobiles/Animals/Town Critters/(UO 3D Only) Parrot.cs @@ -0,0 +1,73 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName("a parrot corpse")] + public class Parrot : BaseCreature + { + [Constructable] + public Parrot() : base(AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + this.Body = 831; + this.Name = ("a parrot"); + this.VirtualArmor = Utility.Random(0,6); + + this.InitStats((10),Utility.Random(25,16),(10)); + + this.Skills[SkillName.Wrestling].Base = (6); + this.Skills[SkillName.Tactics].Base = (6); + this.Skills[SkillName.MagicResist].Base = (5); + + this.Fame = Utility.Random(0,1249); + this.Karma = Utility.Random(0,-624); + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 0.0; + } + + public Parrot(Serial serial) : base(serial) + { + } + + public override int GetAngerSound() + { + return 0x1B; + } + + public override int GetIdleSound() + { + return 0x1C; + } + + public override int GetAttackSound() + { + return 0x1D; + } + + public override int GetHurtSound() + { + return 0x1E; + } + + public override int GetDeathSound() + { + return 0x1F; + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Town Critters/Bird.cs b/Scripts/Mobiles/Animals/Town Critters/Bird.cs new file mode 100644 index 000000000..cdabffa29 --- /dev/null +++ b/Scripts/Mobiles/Animals/Town Critters/Bird.cs @@ -0,0 +1,139 @@ +using System; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "a bird corpse" )] + public class Bird : BaseCreature + { + [Constructable] + public Bird() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + if ( Utility.RandomBool() ) + { + Hue = 0x901; + + switch ( Utility.Random( 3 ) ) + { + case 0: Name = "a crow"; break; + case 2: Name = "a raven"; break; + case 1: Name = "a magpie"; break; + } + } + else + { + Hue = Utility.RandomBirdHue(); + Name = NameList.RandomName( "bird" ); + } + + Body = 6; + BaseSoundID = 0x1B; + + VirtualArmor = Utility.RandomMinMax( 0, 6 ); + + SetStr( 10 ); + SetDex( 25, 35 ); + SetInt( 10 ); + + SetDamage( 0 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetSkill( SkillName.Wrestling, 4.2, 6.4 ); + SetSkill( SkillName.Tactics, 4.0, 6.0 ); + SetSkill( SkillName.MagicResist, 4.0, 5.0 ); + + SetFameLevel( 1 ); + SetKarmaLevel( 0 ); + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -6.9; + } + + public override MeatType MeatType{ get{ return MeatType.Bird; } } + public override int Meat{ get{ return 1; } } + public override int Feathers{ get{ return 25; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public Bird( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Hue == 0 ) + Hue = Utility.RandomBirdHue(); + } + } + + [CorpseName( "a bird corpse" )] + public class TropicalBird : BaseCreature + { + [Constructable] + public TropicalBird() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Hue = Utility.RandomBirdHue(); + Name = "a tropical bird"; + + Body = 6; + BaseSoundID = 0xBF; + + VirtualArmor = Utility.RandomMinMax( 0, 6 ); + + SetStr( 10 ); + SetDex( 25, 35 ); + SetInt( 10 ); + + SetDamage( 0 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetSkill( SkillName.Wrestling, 4.2, 6.4 ); + SetSkill( SkillName.Tactics, 4.0, 6.0 ); + SetSkill( SkillName.MagicResist, 4.0, 5.0 ); + + SetFameLevel( 1 ); + SetKarmaLevel( 0 ); + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -6.9; + } + + public override MeatType MeatType{ get{ return MeatType.Bird; } } + public override int Meat{ get{ return 1; } } + public override int Feathers{ get{ return 25; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public TropicalBird( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Town Critters/Cat.cs b/Scripts/Mobiles/Animals/Town Critters/Cat.cs new file mode 100644 index 000000000..b38ef6bd5 --- /dev/null +++ b/Scripts/Mobiles/Animals/Town Critters/Cat.cs @@ -0,0 +1,67 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a cat corpse" )] + [TypeAlias( "Server.Mobiles.Housecat" )] + public class Cat : BaseCreature + { + [Constructable] + public Cat() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a cat"; + Body = 0xC9; + Hue = Utility.RandomAnimalHue(); + BaseSoundID = 0x69; + + SetStr( 9 ); + SetDex( 35 ); + SetInt( 5 ); + + SetHits( 6 ); + SetMana( 0 ); + + SetDamage( 1 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + + SetSkill( SkillName.MagicResist, 5.0 ); + SetSkill( SkillName.Tactics, 4.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 0; + Karma = 150; + + VirtualArmor = 8; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -0.9; + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Feline; } } + + public Cat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Town Critters/Dog.cs b/Scripts/Mobiles/Animals/Town Critters/Dog.cs new file mode 100644 index 000000000..30a08a5a7 --- /dev/null +++ b/Scripts/Mobiles/Animals/Town Critters/Dog.cs @@ -0,0 +1,66 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a dog corpse" )] + public class Dog : BaseCreature + { + [Constructable] + public Dog() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a dog"; + Body = 0xD9; + Hue = Utility.RandomAnimalHue(); + BaseSoundID = 0x85; + + SetStr( 27, 37 ); + SetDex( 28, 43 ); + SetInt( 29, 37 ); + + SetHits( 17, 22 ); + SetMana( 0 ); + + SetDamage( 4, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10, 15 ); + + SetSkill( SkillName.MagicResist, 22.1, 47.0 ); + SetSkill( SkillName.Tactics, 19.2, 31.0 ); + SetSkill( SkillName.Wrestling, 19.2, 31.0 ); + + Fame = 0; + Karma = 300; + + VirtualArmor = 12; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -15.3; + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Canine; } } + + public Dog(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Animals/Town Critters/Rat.cs b/Scripts/Mobiles/Animals/Town Critters/Rat.cs new file mode 100644 index 000000000..745c27b84 --- /dev/null +++ b/Scripts/Mobiles/Animals/Town Critters/Rat.cs @@ -0,0 +1,70 @@ +using System; +using Server.Mobiles; + +namespace Server.Mobiles +{ + [CorpseName( "a rat corpse" )] + public class Rat : BaseCreature + { + [Constructable] + public Rat() : base( AIType.AI_Animal, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a rat"; + Body = 238; + BaseSoundID = 0xCC; + + SetStr( 9 ); + SetDex( 35 ); + SetInt( 5 ); + + SetHits( 6 ); + SetMana( 0 ); + + SetDamage( 1, 2 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 4.0 ); + SetSkill( SkillName.Tactics, 4.0 ); + SetSkill( SkillName.Wrestling, 4.0 ); + + Fame = 150; + Karma = -150; + + VirtualArmor = 6; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -0.9; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish | FoodType.Eggs | FoodType.GrainsAndHay; } } + + public Rat(Serial serial) : base(serial) + { + } + + public override void Serialize(GenericWriter writer) + { + base.Serialize(writer); + + writer.Write((int) 0); + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize(reader); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Familiars/BaseFamiliar.cs b/Scripts/Mobiles/Familiars/BaseFamiliar.cs new file mode 100644 index 000000000..5ebbad017 --- /dev/null +++ b/Scripts/Mobiles/Familiars/BaseFamiliar.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public abstract class BaseFamiliar : BaseCreature + { + public BaseFamiliar() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + } + + public override bool BardImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override bool Commandable{ get{ return false; } } + + private bool m_LastHidden; + + public override void OnThink() + { + base.OnThink(); + + Mobile master = ControlMaster; + + if ( master == null ) + return; + + if ( master.Deleted || master.Map != this.Map || !InRange( master.Location, 20 ) ) + { + DropPackContents(); + EndRelease( null ); + return; + } + + if ( m_LastHidden != master.Hidden ) + Hidden = m_LastHidden = master.Hidden; + + Mobile toAttack = null; + + if ( !Hidden ) + { + toAttack = master.Combatant; + + if ( toAttack == this ) + toAttack = master; + else if ( toAttack == null ) + toAttack = this.Combatant; + } + + if ( Combatant != toAttack ) + Combatant = null; + + if ( toAttack == null ) + { + if ( ControlTarget != master || ControlOrder != OrderType.Follow ) + { + ControlTarget = master; + ControlOrder = OrderType.Follow; + } + } + else if ( ControlTarget != toAttack || ControlOrder != OrderType.Attack ) + { + ControlTarget = toAttack; + ControlOrder = OrderType.Attack; + } + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from.Alive && Controlled && from == ControlMaster && from.InRange( this, 14 ) ) + list.Add( new ReleaseEntry( from, this ) ); + } + + public virtual void BeginRelease( Mobile from ) + { + if ( !Deleted && Controlled && from == ControlMaster && from.CheckAlive() ) + EndRelease( from ); + } + + public virtual void EndRelease( Mobile from ) + { + if ( from == null || (!Deleted && Controlled && from == ControlMaster && from.CheckAlive()) ) + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 1, 13, 2100, 3, 5042, 0 ); + PlaySound( 0x201 ); + Delete(); + } + } + + public virtual void DropPackContents() + { + Map map = this.Map; + Container pack = this.Backpack; + + if ( map != null && map != Map.Internal && pack != null ) + { + List list = new List( pack.Items ); + + for ( int i = 0; i < list.Count; ++i ) + list[i].MoveToWorld( Location, map ); + } + } + + public BaseFamiliar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + DropPackContents(); + Delete(); + } + + private class ReleaseEntry : ContextMenuEntry + { + private Mobile m_From; + private BaseFamiliar m_Familiar; + + public ReleaseEntry( Mobile from, BaseFamiliar familiar ) : base( 6118, 14 ) + { + m_From = from; + m_Familiar = familiar; + } + + public override void OnClick() + { + if ( !m_Familiar.Deleted && m_Familiar.Controlled && m_From == m_Familiar.ControlMaster && m_From.CheckAlive() ) + m_Familiar.BeginRelease( m_From ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Familiars/DarkWolf.cs b/Scripts/Mobiles/Familiars/DarkWolf.cs new file mode 100644 index 000000000..b94d42596 --- /dev/null +++ b/Scripts/Mobiles/Familiars/DarkWolf.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a dark wolf corpse" )] + public class DarkWolfFamiliar : BaseFamiliar + { + public DarkWolfFamiliar() + { + Name = "a dark wolf"; + Body = 99; + Hue = 0x901; + BaseSoundID = 0xE5; + + SetStr( 100 ); + SetDex( 90 ); + SetInt( 90 ); + + SetHits( 60 ); + SetStam( 90 ); + SetMana( 0 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 25, 40 ); + SetResistance( ResistanceType.Cold, 25, 40 ); + SetResistance( ResistanceType.Poison, 25, 40 ); + SetResistance( ResistanceType.Energy, 25, 40 ); + + SetSkill( SkillName.Wrestling, 85.1, 90.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + + ControlSlots = 1; + } + + private DateTime m_NextRestore; + + public override void OnThink() + { + base.OnThink(); + + if ( DateTime.Now < m_NextRestore ) + return; + + m_NextRestore = DateTime.Now + TimeSpan.FromSeconds( 2.0 ); + + Mobile caster = ControlMaster; + + if ( caster == null ) + caster = SummonMaster; + + if ( caster != null ) + ++caster.Stam; + } + + public DarkWolfFamiliar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Familiars/DeathAdder.cs b/Scripts/Mobiles/Familiars/DeathAdder.cs new file mode 100644 index 000000000..6415d72f2 --- /dev/null +++ b/Scripts/Mobiles/Familiars/DeathAdder.cs @@ -0,0 +1,58 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + [CorpseName( "a death adder corpse" )] + public class DeathAdder : BaseFamiliar + { + public DeathAdder() + { + Name = "a death adder"; + Body = 0x15; + Hue = 0x455; + BaseSoundID = 219; + + SetStr( 70 ); + SetDex( 150 ); + SetInt( 100 ); + + SetHits( 50 ); + SetStam( 150 ); + SetMana( 0 ); + + SetDamage( 1, 4 ); + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10 ); + SetResistance( ResistanceType.Poison, 100 ); + + SetSkill( SkillName.Wrestling, 90.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + SetSkill( SkillName.MagicResist, 100.0 ); + SetSkill( SkillName.Poisoning, 150.0 ); + + ControlSlots = 1; + } + + public override Poison HitPoison{ get{ return (0.8 >= Utility.RandomDouble() ? Poison.Greater : Poison.Deadly); } } + + public DeathAdder( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Familiars/HordeMinion.cs b/Scripts/Mobiles/Familiars/HordeMinion.cs new file mode 100644 index 000000000..a5c4d9610 --- /dev/null +++ b/Scripts/Mobiles/Familiars/HordeMinion.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [CorpseName( "a horde minion corpse" )] + public class HordeMinionFamiliar : BaseFamiliar + { + public HordeMinionFamiliar() + { + Name = "a horde minion"; + Body = 776; + BaseSoundID = 0x39D; + + SetStr( 100 ); + SetDex( 110 ); + SetInt( 100 ); + + SetHits( 70 ); + SetStam( 110 ); + SetMana( 0 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 50, 55 ); + SetResistance( ResistanceType.Poison, 25, 30 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.Wrestling, 70.1, 75.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + + ControlSlots = 1; + + Container pack = Backpack; + + if ( pack != null ) + pack.Delete(); + + pack = new StrongBackpack(); + pack.Movable = false; + + AddItem( pack ); + } + + private DateTime m_NextPickup; + + public override void OnThink() + { + base.OnThink(); + + if ( DateTime.Now < m_NextPickup ) + return; + + m_NextPickup = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 5, 10 ) ); + + Container pack = this.Backpack; + + if ( pack == null ) + return; + + ArrayList list = new ArrayList(); + + foreach ( Item item in this.GetItemsInRange( 2 ) ) + { + if ( item.Movable && item.Stackable ) + list.Add( item ); + } + + int pickedUp = 0; + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = (Item)list[i]; + + if ( !pack.CheckHold( this, item, false, true ) ) + return; + + bool rejected; + LRReason reject; + + NextActionTime = DateTime.Now; + + Lift( item, item.Amount, out rejected, out reject ); + + if ( rejected ) + continue; + + Drop( this, Point3D.Zero ); + + if ( ++pickedUp == 3 ) + break; + } + } + + private void ConfirmRelease_Callback( Mobile from, bool okay, object state ) + { + if ( okay ) + EndRelease( from ); + } + + public override void BeginRelease( Mobile from ) + { + Container pack = this.Backpack; + + if ( pack != null && pack.Items.Count > 0 ) + from.SendGump( new WarningGump( 1060635, 30720, 1061672, 32512, 420, 280, new WarningGumpCallback( ConfirmRelease_Callback ), null ) ); + else + EndRelease( from ); + } + + #region Pack Animal Methods + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + PackAnimal.CombineBackpacks( this ); + + return true; + } + + public override DeathMoveResult GetInventoryMoveResultFor( Item item ) + { + return DeathMoveResult.MoveToCorpse; + } + + public override bool IsSnoop( Mobile from ) + { + if ( PackAnimal.CheckAccess( this, from ) ) + return false; + + return base.IsSnoop( from ); + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( CheckFeed( from, item ) ) + return true; + + if ( PackAnimal.CheckAccess( this, from ) ) + { + AddToBackpack( item ); + return true; + } + + return base.OnDragDrop( from, item ); + } + + public override bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override bool CheckNonlocalLift( Mobile from, Item item ) + { + return PackAnimal.CheckAccess( this, from ); + } + + public override void OnDoubleClick( Mobile from ) + { + PackAnimal.TryPackOpen( this, from ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + PackAnimal.GetContextMenuEntries( this, from, list ); + } + #endregion + + public HordeMinionFamiliar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Familiars/ShadowWisp.cs b/Scripts/Mobiles/Familiars/ShadowWisp.cs new file mode 100644 index 000000000..582a30519 --- /dev/null +++ b/Scripts/Mobiles/Familiars/ShadowWisp.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a shadow wisp corpse" )] + public class ShadowWispFamiliar : BaseFamiliar + { + public ShadowWispFamiliar() + { + Name = "a shadow wisp"; + Body = 165; + Hue = 0x901; + BaseSoundID = 466; + + SetStr( 50 ); + SetDex( 60 ); + SetInt( 100 ); + + SetHits( 50 ); + SetStam( 60 ); + SetMana( 0 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Energy, 100 ); + + SetResistance( ResistanceType.Physical, 10, 15 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 99 ); + + SetSkill( SkillName.Wrestling, 40.0 ); + SetSkill( SkillName.Tactics, 40.0 ); + + ControlSlots = 1; + } + + private DateTime m_NextFlare; + + public override void OnThink() + { + base.OnThink(); + + if ( DateTime.Now < m_NextFlare ) + return; + + m_NextFlare = DateTime.Now + TimeSpan.FromSeconds( 5.0 + (25.0 * Utility.RandomDouble()) ); + + this.FixedEffect( 0x37C4, 1, 12, 1109, 6 ); + this.PlaySound( 0x1D3 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 0.5 ), new TimerCallback( Flare ) ); + } + + private void Flare() + { + Mobile caster = this.ControlMaster; + + if ( caster == null ) + caster = this.SummonMaster; + + if ( caster == null ) + return; + + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 5 ) ) + { + if ( m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 ) + list.Add( m ); + } + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + bool friendly = true; + + for ( int j = 0; friendly && j < caster.Aggressors.Count; ++j ) + friendly = ( caster.Aggressors[j].Attacker != m ); + + for ( int j = 0; friendly && j < caster.Aggressed.Count; ++j ) + friendly = ( caster.Aggressed[j].Defender != m ); + + if ( friendly ) + { + m.FixedEffect( 0x37C4, 1, 12, 1109, 3 ); // At player + m.Mana += 1 - (m.Karma / 1000); + } + } + } + + public ShadowWispFamiliar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Familiars/VampireBat.cs b/Scripts/Mobiles/Familiars/VampireBat.cs new file mode 100644 index 000000000..bea77825c --- /dev/null +++ b/Scripts/Mobiles/Familiars/VampireBat.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a vampire bat corpse" )] + public class VampireBatFamiliar : BaseFamiliar + { + public VampireBatFamiliar() + { + Name = "a vampire bat"; + Body = 317; + BaseSoundID = 0x270; + + SetStr( 120 ); + SetDex( 120 ); + SetInt( 100 ); + + SetHits( 90 ); + SetStam( 120 ); + SetMana( 0 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 10, 15 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 10, 15 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.Wrestling, 95.1, 100.0 ); + SetSkill( SkillName.Tactics, 50.0 ); + + ControlSlots = 1; + } + + public VampireBatFamiliar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Guards/ArcherGuard.cs b/Scripts/Mobiles/Guards/ArcherGuard.cs new file mode 100644 index 000000000..d6dc215dc --- /dev/null +++ b/Scripts/Mobiles/Guards/ArcherGuard.cs @@ -0,0 +1,397 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + public class ArcherGuard : BaseGuard + { + private Timer m_AttackTimer, m_IdleTimer; + + private Mobile m_Focus; + + [Constructable] + public ArcherGuard() : this( null ) + { + } + + public ArcherGuard( Mobile target ) : base( target ) + { + InitStats( 100, 125, 25 ); + Title = "the guard"; + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + new Horse().Rider = this; + + AddItem( new StuddedChest() ); + AddItem( new StuddedArms() ); + AddItem( new StuddedGloves() ); + AddItem( new StuddedGorget() ); + AddItem( new StuddedLegs() ); + AddItem( new Boots() ); + AddItem( new SkullCap() ); + + Bow bow = new Bow(); + + bow.Movable = false; + bow.Crafter = this; + bow.Quality = WeaponQuality.Exceptional; + + AddItem( bow ); + + Container pack = new Backpack(); + + pack.Movable = false; + + Arrow arrows = new Arrow( 250 ); + + arrows.LootType = LootType.Newbied; + + pack.DropItem( arrows ); + pack.DropItem( new Gold( 10, 25 ) ); + + AddItem( pack ); + + Skills[SkillName.Anatomy].Base = 120.0; + Skills[SkillName.Tactics].Base = 120.0; + Skills[SkillName.Archery].Base = 120.0; + Skills[SkillName.MagicResist].Base = 120.0; + Skills[SkillName.DetectHidden].Base = 100.0; + + this.NextCombatTime = DateTime.Now + TimeSpan.FromSeconds( 0.5 ); + this.Focus = target; + } + + public ArcherGuard( Serial serial ) : base( serial ) + { + } + + public override bool OnBeforeDeath() + { + if ( m_Focus != null && m_Focus.Alive ) + new AvengeTimer( m_Focus ).Start(); // If a guard dies, three more guards will spawn + + return base.OnBeforeDeath(); + } + + [CommandProperty( AccessLevel.GameMaster )] + public override Mobile Focus + { + get + { + return m_Focus; + } + set + { + if ( Deleted ) + return; + + Mobile oldFocus = m_Focus; + + if ( oldFocus != value ) + { + m_Focus = value; + + if ( value != null ) + this.AggressiveAction( value ); + + Combatant = value; + + if ( oldFocus != null && !oldFocus.Alive ) + Say( "Thou hast suffered thy punishment, scoundrel." ); + + if ( value != null ) + Say( 500131 ); // Thou wilt regret thine actions, swine! + + if ( m_AttackTimer != null ) + { + m_AttackTimer.Stop(); + m_AttackTimer = null; + } + + if ( m_IdleTimer != null ) + { + m_IdleTimer.Stop(); + m_IdleTimer = null; + } + + if ( m_Focus != null ) + { + m_AttackTimer = new AttackTimer( this ); + m_AttackTimer.Start(); + ((AttackTimer)m_AttackTimer).DoOnTick(); + } + else + { + m_IdleTimer = new IdleTimer( this ); + m_IdleTimer.Start(); + } + } + else if ( m_Focus == null && m_IdleTimer == null ) + { + m_IdleTimer = new IdleTimer( this ); + m_IdleTimer.Start(); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Focus ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Focus = reader.ReadMobile(); + + if ( m_Focus != null ) + { + m_AttackTimer = new AttackTimer( this ); + m_AttackTimer.Start(); + } + else + { + m_IdleTimer = new IdleTimer( this ); + m_IdleTimer.Start(); + } + + break; + } + } + } + + public override void OnAfterDelete() + { + if ( m_AttackTimer != null ) + { + m_AttackTimer.Stop(); + m_AttackTimer = null; + } + + if ( m_IdleTimer != null ) + { + m_IdleTimer.Stop(); + m_IdleTimer = null; + } + + base.OnAfterDelete(); + } + + private class AvengeTimer : Timer + { + private Mobile m_Focus; + + public AvengeTimer( Mobile focus ) : base( TimeSpan.FromSeconds( 2.5 ), TimeSpan.FromSeconds( 1.0 ), 3 ) // After 2.5 seconds, one guard will spawn every 1.0 second, three times + { + m_Focus = focus; + } + + protected override void OnTick() + { + BaseGuard.Spawn( m_Focus, m_Focus, 1, true ); + } + } + + private class AttackTimer : Timer + { + private ArcherGuard m_Owner; + // private bool m_Shooting; + + public AttackTimer( ArcherGuard owner ) : base( TimeSpan.FromSeconds( 0.25 ), TimeSpan.FromSeconds( 0.1 ) ) + { + m_Owner = owner; + } + + public void DoOnTick() + { + OnTick(); + } + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + m_Owner.Criminal = false; + m_Owner.Kills = 0; + m_Owner.Stam = m_Owner.StamMax; + + Mobile target = m_Owner.Focus; + + if ( target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful( target )) ) + { + m_Owner.Focus = null; + Stop(); + return; + } + else if ( m_Owner.Weapon is Fists ) + { + m_Owner.Kill(); + Stop(); + return; + } + + if ( target != null && m_Owner.Combatant != target ) + m_Owner.Combatant = target; + + if ( target == null ) + { + Stop(); + } + else + {// + TeleportTo( target ); + target.BoltEffect( 0 ); + + if ( target is BaseCreature ) + ((BaseCreature)target).NoKillAwards = true; + + target.Damage( target.HitsMax, m_Owner ); + target.Kill(); // just in case, maybe Damage is overriden on some shard + + if ( target.Corpse != null && !target.Player ) + target.Corpse.Delete(); + + m_Owner.Focus = null; + Stop(); + }// + /*else if ( !m_Owner.InRange( target, 20 ) ) + { + m_Shooting = false; + m_Owner.Focus = null; + } + else if ( !m_Owner.InLOS( target ) ) + { + m_Shooting = false; + TeleportTo( target ); + } + else if ( !m_Owner.CanSee( target ) ) + { + m_Shooting = false; + + if ( !m_Owner.InRange( target, 2 ) ) + { + if ( !m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target ) ) + TeleportTo( target ); + } + else + { + if ( !m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0 ) + m_Owner.Say( "Reveal!" ); + } + } + else + { + if ( m_Shooting && (TimeToSpare() || OutOfMaxDistance( target )) ) + m_Shooting = false; + else if ( !m_Shooting && InMinDistance( target ) ) + m_Shooting = true; + + if ( !m_Shooting ) + { + if ( m_Owner.InRange( target, 1 ) ) + { + if ( !m_Owner.Move( (Direction)(m_Owner.GetDirectionTo( target ) - 4) | Direction.Running ) && OutOfMaxDistance( target ) ) // Too close, move away + TeleportTo( target ); + } + else if ( !m_Owner.InRange( target, 2 ) ) + { + if ( !m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) && OutOfMaxDistance( target ) ) + TeleportTo( target ); + } + } + }*/ + } + + private bool TimeToSpare() + { + return (m_Owner.NextCombatTime - DateTime.Now) > TimeSpan.FromSeconds( 1.0 ); + } + + private bool OutOfMaxDistance( Mobile target ) + { + return !m_Owner.InRange( target, m_Owner.Weapon.MaxRange ); + } + + private bool InMinDistance( Mobile target ) + { + return m_Owner.InRange( target, 4 ); + } + + private void TeleportTo( Mobile target ) + { + Point3D from = m_Owner.Location; + Point3D to = target.Location; + + m_Owner.Location = to; + + Effects.SendLocationParticles( EffectItem.Create( from, m_Owner.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( to, m_Owner.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + + m_Owner.PlaySound( 0x1FE ); + } + } + + private class IdleTimer : Timer + { + private ArcherGuard m_Owner; + private int m_Stage; + + public IdleTimer( ArcherGuard owner ) : base( TimeSpan.FromSeconds( 2.0 ), TimeSpan.FromSeconds( 2.5 ) ) + { + m_Owner = owner; + } + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + if ( (m_Stage++ % 4) == 0 || !m_Owner.Move( m_Owner.Direction ) ) + m_Owner.Direction = (Direction)Utility.Random( 8 ); + + if ( m_Stage > 16 ) + { + Effects.SendLocationParticles( EffectItem.Create( m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + m_Owner.PlaySound( 0x1FE ); + + m_Owner.Delete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Guards/BaseGuard.cs b/Scripts/Mobiles/Guards/BaseGuard.cs new file mode 100644 index 000000000..bcca3093a --- /dev/null +++ b/Scripts/Mobiles/Guards/BaseGuard.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + public abstract class BaseGuard : Mobile + { + public static void Spawn( Mobile caller, Mobile target ) + { + Spawn( caller, target, 1, false ); + } + + public static void Spawn( Mobile caller, Mobile target, int amount, bool onlyAdditional ) + { + if ( target == null || target.Deleted ) + return; + + foreach ( Mobile m in target.GetMobilesInRange( 15 ) ) + { + if ( m is BaseGuard ) + { + BaseGuard g = (BaseGuard)m; + + if ( g.Focus == null ) // idling + { + g.Focus = target; + + --amount; + } + else if ( g.Focus == target && !onlyAdditional ) + { + --amount; + } + } + } + + while ( amount-- > 0 ) + caller.Region.MakeGuard( target ); + } + + public BaseGuard( Mobile target ) + { + if ( target != null ) + { + Location = target.Location; + Map = target.Map; + + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + } + } + + public BaseGuard( Serial serial ) : base( serial ) + { + } + + public override bool OnBeforeDeath() + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + + PlaySound( 0x1FE ); + + Delete(); + + return false; + } + + public abstract Mobile Focus{ get; set; } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Guards/WarriorGuard.cs b/Scripts/Mobiles/Guards/WarriorGuard.cs new file mode 100644 index 000000000..5f87ac7e9 --- /dev/null +++ b/Scripts/Mobiles/Guards/WarriorGuard.cs @@ -0,0 +1,369 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Mobiles +{ + public class WarriorGuard : BaseGuard + { + private Timer m_AttackTimer, m_IdleTimer; + + private Mobile m_Focus; + + [Constructable] + public WarriorGuard() : this( null ) + { + } + + public WarriorGuard( Mobile target ) : base( target ) + { + InitStats( 1000, 1000, 1000 ); + Title = "the guard"; + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + + switch( Utility.Random( 2 ) ) + { + case 0: AddItem( new LeatherSkirt() ); break; + case 1: AddItem( new LeatherShorts() ); break; + } + + switch( Utility.Random( 5 ) ) + { + case 0: AddItem( new FemaleLeatherChest() ); break; + case 1: AddItem( new FemaleStuddedChest() ); break; + case 2: AddItem( new LeatherBustierArms() ); break; + case 3: AddItem( new StuddedBustierArms() ); break; + case 4: AddItem( new FemalePlateChest() ); break; + } + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + + AddItem( new PlateChest() ); + AddItem( new PlateArms() ); + AddItem( new PlateLegs() ); + + switch( Utility.Random( 3 ) ) + { + case 0: AddItem( new Doublet( Utility.RandomNondyedHue() ) ); break; + case 1: AddItem( new Tunic( Utility.RandomNondyedHue() ) ); break; + case 2: AddItem( new BodySash( Utility.RandomNondyedHue() ) ); break; + } + } + Utility.AssignRandomHair( this ); + + if( Utility.RandomBool() ) + Utility.AssignRandomFacialHair( this, HairHue ); + + Halberd weapon = new Halberd(); + + weapon.Movable = false; + weapon.Crafter = this; + weapon.Quality = WeaponQuality.Exceptional; + + AddItem( weapon ); + + Container pack = new Backpack(); + + pack.Movable = false; + + pack.DropItem( new Gold( 10, 25 ) ); + + AddItem( pack ); + + Skills[SkillName.Anatomy].Base = 120.0; + Skills[SkillName.Tactics].Base = 120.0; + Skills[SkillName.Swords].Base = 120.0; + Skills[SkillName.MagicResist].Base = 120.0; + Skills[SkillName.DetectHidden].Base = 100.0; + + this.NextCombatTime = DateTime.Now + TimeSpan.FromSeconds( 0.5 ); + this.Focus = target; + } + + public WarriorGuard( Serial serial ) : base( serial ) + { + } + + public override bool OnBeforeDeath() + { + if ( m_Focus != null && m_Focus.Alive ) + new AvengeTimer( m_Focus ).Start(); // If a guard dies, three more guards will spawn + + return base.OnBeforeDeath(); + } + + [CommandProperty( AccessLevel.GameMaster )] + public override Mobile Focus + { + get + { + return m_Focus; + } + set + { + if ( Deleted ) + return; + + Mobile oldFocus = m_Focus; + + if ( oldFocus != value ) + { + m_Focus = value; + + if ( value != null ) + this.AggressiveAction( value ); + + Combatant = value; + + if ( oldFocus != null && !oldFocus.Alive ) + Say( "Thou hast suffered thy punishment, scoundrel." ); + + if ( value != null ) + Say( 500131 ); // Thou wilt regret thine actions, swine! + + if ( m_AttackTimer != null ) + { + m_AttackTimer.Stop(); + m_AttackTimer = null; + } + + if ( m_IdleTimer != null ) + { + m_IdleTimer.Stop(); + m_IdleTimer = null; + } + + if ( m_Focus != null ) + { + m_AttackTimer = new AttackTimer( this ); + m_AttackTimer.Start(); + ((AttackTimer)m_AttackTimer).DoOnTick(); + } + else + { + m_IdleTimer = new IdleTimer( this ); + m_IdleTimer.Start(); + } + } + else if ( m_Focus == null && m_IdleTimer == null ) + { + m_IdleTimer = new IdleTimer( this ); + m_IdleTimer.Start(); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Focus ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Focus = reader.ReadMobile(); + + if ( m_Focus != null ) + { + m_AttackTimer = new AttackTimer( this ); + m_AttackTimer.Start(); + } + else + { + m_IdleTimer = new IdleTimer( this ); + m_IdleTimer.Start(); + } + + break; + } + } + } + + public override void OnAfterDelete() + { + if ( m_AttackTimer != null ) + { + m_AttackTimer.Stop(); + m_AttackTimer = null; + } + + if ( m_IdleTimer != null ) + { + m_IdleTimer.Stop(); + m_IdleTimer = null; + } + + base.OnAfterDelete(); + } + + private class AvengeTimer : Timer + { + private Mobile m_Focus; + + public AvengeTimer( Mobile focus ) : base( TimeSpan.FromSeconds( 2.5 ), TimeSpan.FromSeconds( 1.0 ), 3 ) + { + m_Focus = focus; + } + + protected override void OnTick() + { + BaseGuard.Spawn( m_Focus, m_Focus, 1, true ); + } + } + + private class AttackTimer : Timer + { + private WarriorGuard m_Owner; + + public AttackTimer( WarriorGuard owner ) : base( TimeSpan.FromSeconds( 0.25 ), TimeSpan.FromSeconds( 0.1 ) ) + { + m_Owner = owner; + } + + public void DoOnTick() + { + OnTick(); + } + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + m_Owner.Criminal = false; + m_Owner.Kills = 0; + m_Owner.Stam = m_Owner.StamMax; + + Mobile target = m_Owner.Focus; + + if ( target != null && (target.Deleted || !target.Alive || !m_Owner.CanBeHarmful( target )) ) + { + m_Owner.Focus = null; + Stop(); + return; + } + else if ( m_Owner.Weapon is Fists ) + { + m_Owner.Kill(); + Stop(); + return; + } + + if ( target != null && m_Owner.Combatant != target ) + m_Owner.Combatant = target; + + if ( target == null ) + { + Stop(); + } + else + {// + TeleportTo( target ); + target.BoltEffect( 0 ); + + if ( target is BaseCreature ) + ((BaseCreature)target).NoKillAwards = true; + + target.Damage( target.HitsMax, m_Owner ); + target.Kill(); // just in case, maybe Damage is overriden on some shard + + if ( target.Corpse != null && !target.Player ) + target.Corpse.Delete(); + + m_Owner.Focus = null; + Stop(); + }// + /*else if ( !m_Owner.InRange( target, 20 ) ) + { + m_Owner.Focus = null; + } + else if ( !m_Owner.InRange( target, 10 ) || !m_Owner.InLOS( target ) ) + { + TeleportTo( target ); + } + else if ( !m_Owner.InRange( target, 1 ) ) + { + if ( !m_Owner.Move( m_Owner.GetDirectionTo( target ) | Direction.Running ) ) + TeleportTo( target ); + } + else if ( !m_Owner.CanSee( target ) ) + { + if ( !m_Owner.UseSkill( SkillName.DetectHidden ) && Utility.Random( 50 ) == 0 ) + m_Owner.Say( "Reveal!" ); + }*/ + } + + private void TeleportTo( Mobile target ) + { + Point3D from = m_Owner.Location; + Point3D to = target.Location; + + m_Owner.Location = to; + + Effects.SendLocationParticles( EffectItem.Create( from, m_Owner.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( to, m_Owner.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + + m_Owner.PlaySound( 0x1FE ); + } + } + + private class IdleTimer : Timer + { + private WarriorGuard m_Owner; + private int m_Stage; + + public IdleTimer( WarriorGuard owner ) : base( TimeSpan.FromSeconds( 2.0 ), TimeSpan.FromSeconds( 2.5 ) ) + { + m_Owner = owner; + } + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + if ( (m_Stage++ % 4) == 0 || !m_Owner.Move( m_Owner.Direction ) ) + m_Owner.Direction = (Direction)Utility.Random( 8 ); + + if ( m_Stage > 16 ) + { + Effects.SendLocationParticles( EffectItem.Create( m_Owner.Location, m_Owner.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + m_Owner.PlaySound( 0x1FE ); + + m_Owner.Delete(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/BaseHealer.cs b/Scripts/Mobiles/Healers/BaseHealer.cs new file mode 100644 index 000000000..81497b5aa --- /dev/null +++ b/Scripts/Mobiles/Healers/BaseHealer.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Gumps; + +namespace Server.Mobiles +{ + public abstract class BaseHealer : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override bool IsActiveVendor{ get{ return false; } } + public override bool IsInvulnerable{ get{ return false; } } + + public override void InitSBInfo() + { + } + + public BaseHealer() : base( null ) + { + if ( !IsInvulnerable ) + { + AI = AIType.AI_Mage; + ActiveSpeed = 0.2; + PassiveSpeed = 0.8; + RangePerception = BaseCreature.DefaultRangePerception; + FightMode = FightMode.Aggressor; + } + + SpeechHue = 0; + + SetStr( 304, 400 ); + SetDex( 102, 150 ); + SetInt( 204, 300 ); + + SetDamage( 10, 23 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Anatomy, 75.0, 97.5 ); + SetSkill( SkillName.EvalInt, 82.0, 100.0 ); + SetSkill( SkillName.Healing, 75.0, 97.5 ); + SetSkill( SkillName.Magery, 82.0, 100.0 ); + SetSkill( SkillName.MagicResist, 82.0, 100.0 ); + SetSkill( SkillName.Tactics, 82.0, 100.0 ); + + Fame = 1000; + Karma = 10000; + + PackItem( new Bandage( Utility.RandomMinMax( 5, 10 ) ) ); + PackItem( new HealPotion() ); + PackItem( new CurePotion() ); + } + + public override VendorShoeType ShoeType{ get{ return VendorShoeType.Sandals; } } + + public virtual int GetRobeColor() + { + return Utility.RandomYellowHue(); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Robe( GetRobeColor() ) ); + } + + public virtual bool HealsYoungPlayers{ get{ return true; } } + + public virtual bool CheckResurrect( Mobile m ) + { + return true; + } + + private DateTime m_NextResurrect; + private static TimeSpan ResurrectDelay = TimeSpan.FromSeconds( 2.0 ); + + public virtual void OfferResurrection( Mobile m ) + { + Direction = GetDirectionTo( m ); + Say( 501224 ); // Thou hast strayed from the path of virtue, but thou still deservest a second chance. + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, ResurrectMessage.Healer ) ); + } + + public virtual void OfferHeal( PlayerMobile m ) + { + Direction = GetDirectionTo( m ); + + if ( m.CheckYoungHealTime() ) + { + Say( 501229 ); // You look like you need some healing my child. + + m.PlaySound( 0x1F2 ); + m.FixedEffect( 0x376A, 9, 32 ); + + m.Hits = m.HitsMax; + } + else + { + Say( 501228 ); // I can do no more for you at this time. + } + } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( !m.Frozen && DateTime.Now >= m_NextResurrect && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && InLOS( m ) ) + { + if ( !m.Alive ) + { + m_NextResurrect = DateTime.Now + ResurrectDelay; + + if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else if ( CheckResurrect( m ) ) + { + OfferResurrection( m ); + } + } + else if ( this.HealsYoungPlayers && m.Hits < m.HitsMax && m is PlayerMobile && ((PlayerMobile)m).Young ) + { + OfferHeal( (PlayerMobile) m ); + } + } + } + + public BaseHealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + if ( !IsInvulnerable ) + { + AI = AIType.AI_Mage; + ActiveSpeed = 0.2; + PassiveSpeed = 0.8; + RangePerception = BaseCreature.DefaultRangePerception; + FightMode = FightMode.Aggressor; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/EvilHealer.cs b/Scripts/Mobiles/Healers/EvilHealer.cs new file mode 100644 index 000000000..906bdd2f5 --- /dev/null +++ b/Scripts/Mobiles/Healers/EvilHealer.cs @@ -0,0 +1,64 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class EvilHealer : BaseHealer + { + public override bool CanTeach{ get{ return true; } } + + public override bool CheckTeach( SkillName skill, Mobile from ) + { + if ( !base.CheckTeach( skill, from ) ) + return false; + + return ( skill == SkillName.Forensics ) + || ( skill == SkillName.Healing ) + || ( skill == SkillName.SpiritSpeak ) + || ( skill == SkillName.Swords ); + } + + [Constructable] + public EvilHealer() + { + Title = "the healer"; + + Karma = -10000; + + SetSkill( SkillName.Forensics, 80.0, 100.0 ); + SetSkill( SkillName.SpiritSpeak, 80.0, 100.0 ); + SetSkill( SkillName.Swords, 80.0, 100.0 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool IsActiveVendor{ get{ return true; } } + + public override void InitSBInfo() + { + SBInfos.Add( new SBHealer() ); + } + + public override bool CheckResurrect( Mobile m ) + { + return true; + } + + public EvilHealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/EvilWanderingHealer.cs b/Scripts/Mobiles/Healers/EvilWanderingHealer.cs new file mode 100644 index 000000000..673c5a43c --- /dev/null +++ b/Scripts/Mobiles/Healers/EvilWanderingHealer.cs @@ -0,0 +1,65 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class EvilWanderingHealer : BaseHealer + { + public override bool CanTeach{ get{ return true; } } + + public override bool CheckTeach( SkillName skill, Mobile from ) + { + if ( !base.CheckTeach( skill, from ) ) + return false; + + return ( skill == SkillName.Anatomy ) + || ( skill == SkillName.Camping ) + || ( skill == SkillName.Forensics ) + || ( skill == SkillName.Healing ) + || ( skill == SkillName.SpiritSpeak ); + } + + [Constructable] + public EvilWanderingHealer() + { + Title = ( Core.AOS ) ? "the Priest Of Mondain" : "the evil wandering healer"; + Karma = -10000; + + AddItem( new GnarledStaff() ); + + SetSkill( SkillName.Camping, 80.0, 100.0 ); + SetSkill( SkillName.Forensics, 80.0, 100.0 ); + SetSkill( SkillName.SpiritSpeak, 80.0, 100.0 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } // Do not display title in OnSingleClick + + public override bool CheckResurrect( Mobile m ) + { + return true; + } + + public EvilWanderingHealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( version < 1 && Title == "the wandering healer" && Core.AOS ) + Title = "the priest of Mondain"; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/FortuneTeller.cs b/Scripts/Mobiles/Healers/FortuneTeller.cs new file mode 100644 index 000000000..67487e387 --- /dev/null +++ b/Scripts/Mobiles/Healers/FortuneTeller.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class FortuneTeller : BaseHealer + { + public override bool CanTeach{ get{ return true; } } + + public override bool CheckTeach( SkillName skill, Mobile from ) + { + if ( !base.CheckTeach( skill, from ) ) + return false; + + return ( skill == SkillName.Anatomy ) + || ( skill == SkillName.Healing ) + || ( skill == SkillName.Forensics ) + || ( skill == SkillName.SpiritSpeak ); + } + + [Constructable] + public FortuneTeller() + { + Title = "the fortune teller"; + + SetSkill( SkillName.Anatomy, 85.0, 100.0 ); + SetSkill( SkillName.Healing, 90.0, 100.0 ); + SetSkill( SkillName.Forensics, 75.0, 98.0 ); + SetSkill( SkillName.SpiritSpeak, 65.0, 88.0 ); + } + + public override bool IsActiveVendor{ get{ return true; } } + public override bool IsInvulnerable{ get{ return true; } } + + public override void InitSBInfo() + { + SBInfos.Add( new SBMage() ); + SBInfos.Add( new SBFortuneTeller() ); + } + + public override int GetRobeColor() + { + return RandomBrightHue(); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new SkullCap( RandomBrightHue() ) ); break; + case 1: AddItem( new WizardsHat( RandomBrightHue() ) ); break; + case 2: AddItem( new Bandana( RandomBrightHue() ) ); break; + } + + AddItem( new Spellbook() ); + } + + public FortuneTeller( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/Healer.cs b/Scripts/Mobiles/Healers/Healer.cs new file mode 100644 index 000000000..028f6890a --- /dev/null +++ b/Scripts/Mobiles/Healers/Healer.cs @@ -0,0 +1,79 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class Healer : BaseHealer + { + public override bool CanTeach{ get{ return true; } } + + public override bool CheckTeach( SkillName skill, Mobile from ) + { + if ( !base.CheckTeach( skill, from ) ) + return false; + + return ( skill == SkillName.Forensics ) + || ( skill == SkillName.Healing ) + || ( skill == SkillName.SpiritSpeak ) + || ( skill == SkillName.Swords ); + } + + [Constructable] + public Healer() + { + Title = "the healer"; + + if ( !Core.AOS ) + NameHue = 0x35; + + SetSkill( SkillName.Forensics, 80.0, 100.0 ); + SetSkill( SkillName.SpiritSpeak, 80.0, 100.0 ); + SetSkill( SkillName.Swords, 80.0, 100.0 ); + } + + public override bool IsActiveVendor{ get{ return true; } } + public override bool IsInvulnerable{ get{ return true; } } + + public override void InitSBInfo() + { + SBInfos.Add( new SBHealer() ); + } + + public override bool CheckResurrect( Mobile m ) + { + if ( m.Criminal ) + { + Say( 501222 ); // Thou art a criminal. I shall not resurrect thee. + return false; + } + else if ( m.Kills >= 5 ) + { + Say( 501223 ); // Thou'rt not a decent and good person. I shall not resurrect thee. + return false; + } + + return true; + } + + public Healer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Core.AOS && NameHue == 0x35 ) + NameHue = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/PricedHealer.cs b/Scripts/Mobiles/Healers/PricedHealer.cs new file mode 100644 index 000000000..5ba63a363 --- /dev/null +++ b/Scripts/Mobiles/Healers/PricedHealer.cs @@ -0,0 +1,85 @@ +using System; +using Server; +using Server.Gumps; + +namespace Server.Mobiles +{ + public class PricedHealer : BaseHealer + { + private int m_Price; + + [CommandProperty( AccessLevel.GameMaster )] + public int Price + { + get{ return m_Price; } + set{ m_Price = value; } + } + + [Constructable] + public PricedHealer() : this( 5000 ) + { + } + + [Constructable] + public PricedHealer( int price ) + { + m_Price = price; + + if ( !Core.AOS ) + NameHue = 0x35; + } + + public override bool IsInvulnerable{ get{ return true; } } + + public override void InitSBInfo() + { + } + + public override bool HealsYoungPlayers{ get{ return false; } } + + public override void OfferResurrection( Mobile m ) + { + Direction = GetDirectionTo( m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, this, m_Price ) ); + } + + public override bool CheckResurrect( Mobile m ) + { + return true; + } + + public PricedHealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (int) m_Price ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Price = reader.ReadInt(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Healers/WanderingHealer.cs b/Scripts/Mobiles/Healers/WanderingHealer.cs new file mode 100644 index 000000000..f39ebbae8 --- /dev/null +++ b/Scripts/Mobiles/Healers/WanderingHealer.cs @@ -0,0 +1,71 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class WanderingHealer : BaseHealer + { + public override bool CanTeach{ get{ return true; } } + + public override bool CheckTeach( SkillName skill, Mobile from ) + { + if ( !base.CheckTeach( skill, from ) ) + return false; + + return ( skill == SkillName.Anatomy ) + || ( skill == SkillName.Camping ) + || ( skill == SkillName.Forensics ) + || ( skill == SkillName.Healing ) + || ( skill == SkillName.SpiritSpeak ); + } + + [Constructable] + public WanderingHealer() + { + Title = "the wandering healer"; + + AddItem( new GnarledStaff() ); + + SetSkill( SkillName.Camping, 80.0, 100.0 ); + SetSkill( SkillName.Forensics, 80.0, 100.0 ); + SetSkill( SkillName.SpiritSpeak, 80.0, 100.0 ); + } + + public override bool ClickTitle{ get{ return false; } } // Do not display title in OnSingleClick + + public override bool CheckResurrect( Mobile m ) + { + if ( m.Criminal ) + { + Say( 501222 ); // Thou art a criminal. I shall not resurrect thee. + return false; + } + else if ( m.Kills >= 5 ) + { + Say( 501223 ); // Thou'rt not a decent and good person. I shall not resurrect thee. + return false; + } + + return true; + } + + public WanderingHealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/AbysmalHorror.cs b/Scripts/Mobiles/Monsters/AOS/AbysmalHorror.cs new file mode 100644 index 000000000..2fa68ccbd --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/AbysmalHorror.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an abyssmal horror corpse" )] + public class AbysmalHorror : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return Utility.RandomBool() ? WeaponAbility.MortalStrike : WeaponAbility.WhirlwindAttack; + } + + public override bool IgnoreYoungProtection { get { return Core.ML; } } + + [Constructable] + public AbysmalHorror() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an abyssmal horror"; + Body = 312; + BaseSoundID = 0x451; + + SetStr( 401, 420 ); + SetDex( 81, 90 ); + SetInt( 401, 420 ); + + SetHits( 6000 ); + + SetDamage( 13, 17 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 100 ); + SetResistance( ResistanceType.Cold, 50, 55 ); + SetResistance( ResistanceType.Poison, 60, 65 ); + SetResistance( ResistanceType.Energy, 77, 80 ); + + SetSkill( SkillName.EvalInt, 200.0 ); + SetSkill( SkillName.Magery, 112.6, 117.5 ); + SetSkill( SkillName.Meditation, 200.0 ); + SetSkill( SkillName.MagicResist, 117.6, 120.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 84.1, 88.0 ); + + Fame = 26000; + Karma = -26000; + + VirtualArmor = 54; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 2 ); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( !Summoned && !NoKillAwards && DemonKnight.CheckArtifactChance( this ) ) + DemonKnight.DistributeArtifact( this ); + } + + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public AbysmalHorror( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 357 ) + BaseSoundID = 0x451; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/BoneDemon.cs b/Scripts/Mobiles/Monsters/AOS/BoneDemon.cs new file mode 100644 index 000000000..09e16959e --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/BoneDemon.cs @@ -0,0 +1,74 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a bone demon corpse" )] + public class BoneDemon : BaseCreature + { + [Constructable] + public BoneDemon() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a bone demon"; + Body = 308; + BaseSoundID = 0x48D; + + SetStr( 1000 ); + SetDex( 151, 175 ); + SetInt( 171, 220 ); + + SetHits( 3600 ); + + SetDamage( 34, 36 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetResistance( ResistanceType.Physical, 75 ); + SetResistance( ResistanceType.Fire, 60 ); + SetResistance( ResistanceType.Cold, 90 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 60 ); + + SetSkill( SkillName.DetectHidden, 80.0 ); + SetSkill( SkillName.EvalInt, 77.6, 87.5 ); + SetSkill( SkillName.Magery, 77.6, 87.5 ); + SetSkill( SkillName.Meditation, 100.0 ); + SetSkill( SkillName.MagicResist, 50.1, 75.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 20000; + Karma = -20000; + + VirtualArmor = 44; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 8 ); + } + + public override bool Unprovokable{ get{ return true; } } + public override bool Uncalmable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public BoneDemon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/CrystalElemental.cs b/Scripts/Mobiles/Monsters/AOS/CrystalElemental.cs new file mode 100644 index 000000000..30de1a5bb --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/CrystalElemental.cs @@ -0,0 +1,78 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a crystal elemental corpse" )] + public class CrystalElemental : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.BleedAttack; + } + + [Constructable] + public CrystalElemental() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a crystal elemental"; + Body = 300; + BaseSoundID = 278; + + SetStr( 136, 160 ); + SetDex( 51, 65 ); + SetInt( 86, 110 ); + + SetHits( 150 ); + + SetDamage( 10, 15 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 55, 70 ); + + SetSkill( SkillName.EvalInt, 70.1, 75.0 ); + SetSkill( SkillName.Magery, 70.1, 75.0 ); + SetSkill( SkillName.Meditation, 65.1, 75.0 ); + SetSkill( SkillName.MagicResist, 80.1, 90.0 ); + SetSkill( SkillName.Tactics, 75.1, 85.0 ); + SetSkill( SkillName.Wrestling, 65.1, 75.0 ); + + Fame = 6500; + Karma = -6500; + + VirtualArmor = 54; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public CrystalElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/DarknightCreeper.cs b/Scripts/Mobiles/Monsters/AOS/DarknightCreeper.cs new file mode 100644 index 000000000..8bc64bc81 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/DarknightCreeper.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a darknight creeper corpse" )] + public class DarknightCreeper : BaseCreature + { + public override bool IgnoreYoungProtection { get { return Core.ML; } } + + [Constructable] + public DarknightCreeper() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "darknight creeper" ); + Body = 313; + BaseSoundID = 0xE0; + + SetStr( 301, 330 ); + SetDex( 101, 110 ); + SetInt( 301, 330 ); + + SetHits( 4000 ); + + SetDamage( 22, 26 ); + + SetDamageType( ResistanceType.Physical, 85 ); + SetDamageType( ResistanceType.Poison, 15 ); + + SetResistance( ResistanceType.Physical, 60 ); + SetResistance( ResistanceType.Fire, 60 ); + SetResistance( ResistanceType.Cold, 100 ); + SetResistance( ResistanceType.Poison, 90 ); + SetResistance( ResistanceType.Energy, 75 ); + + SetSkill( SkillName.DetectHidden, 80.0 ); + SetSkill( SkillName.EvalInt, 118.1, 120.0 ); + SetSkill( SkillName.Magery, 112.6, 120.0 ); + SetSkill( SkillName.Meditation, 150.0 ); + SetSkill( SkillName.Poisoning, 120.0 ); + SetSkill( SkillName.MagicResist, 90.1, 90.9 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 90.9 ); + + Fame = 22000; + Karma = -22000; + + VirtualArmor = 34; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 2 ); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( !Summoned && !NoKillAwards && DemonKnight.CheckArtifactChance( this ) ) + DemonKnight.DistributeArtifact( this ); + } + + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + + public override int TreasureMapLevel{ get{ return 1; } } + + public DarknightCreeper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 471 ) + BaseSoundID = 0xE0; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/DemonKnight.cs b/Scripts/Mobiles/Monsters/AOS/DemonKnight.cs new file mode 100644 index 000000000..39c2ba6f4 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/DemonKnight.cs @@ -0,0 +1,290 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using System.Collections.Generic; + +namespace Server.Mobiles +{ + [CorpseName( "a daemon knight corpse" )] + public class DemonKnight : BaseCreature + { + public override bool IgnoreYoungProtection { get { return Core.ML; } } + + public static Type[] ArtifactRarity10 { get { return m_ArtifactRarity10; } } + public static Type[] ArtifactRarity11 { get { return m_ArtifactRarity11; } } + private static Type[] m_ArtifactRarity10 = new Type[] + { + typeof( TheTaskmaster ) + }; + + private static Type[] m_ArtifactRarity11 = new Type[] + { + typeof( LegacyOfTheDreadLord ), + typeof( TheDragonSlayer ), + typeof( ArmorOfFortune ), + typeof( GauntletsOfNobility ), + typeof( HelmOfInsight ), + typeof( HolyKnightsBreastplate ), + typeof( JackalsCollar ), + typeof( LeggingsOfBane ), + typeof( MidnightBracers ), + typeof( OrnateCrownOfTheHarrower ), + typeof( ShadowDancerLeggings ), + typeof( TunicOfFire ), + typeof( VoiceOfTheFallenKing ), + typeof( BraceletOfHealth ), + typeof( OrnamentOfTheMagician ), + typeof( RingOfTheElements ), + typeof( RingOfTheVile ), + typeof( Aegis ), + typeof( ArcaneShield ), + typeof( AxeOfTheHeavens ), + typeof( BladeOfInsanity ), + typeof( BoneCrusher ), + typeof( BreathOfTheDead ), + typeof( Frostbringer ), + typeof( SerpentsFang ), + typeof( StaffOfTheMagi ), + typeof( TheBeserkersMaul ), + typeof( TheDryadBow ), + typeof( DivineCountenance ), + typeof( HatOfTheMagi ), + typeof( HuntersHeaddress ), + typeof( SpiritOfTheTotem ) + }; + + public static Item CreateRandomArtifact() + { + if ( !Core.AOS ) + return null; + + int count = ( m_ArtifactRarity10.Length * 5 ) + ( m_ArtifactRarity11.Length * 4 ); + int random = Utility.Random( count ); + Type type; + + if ( random < ( m_ArtifactRarity10.Length * 5 ) ) + { + type = m_ArtifactRarity10[random / 5]; + } + else + { + random -= m_ArtifactRarity10.Length * 5; + type = m_ArtifactRarity11[random / 4]; + } + + return Loot.Construct( type ); + } + + public static Mobile FindRandomPlayer( BaseCreature creature ) + { + List rights = BaseCreature.GetLootingRights( creature.DamageEntries, creature.HitsMax ); + + for ( int i = rights.Count - 1; i >= 0; --i ) + { + DamageStore ds = rights[i]; + + if ( !ds.m_HasRight ) + rights.RemoveAt( i ); + } + + if ( rights.Count > 0 ) + return rights[Utility.Random( rights.Count )].m_Mobile; + + return null; + } + + public static void DistributeArtifact( BaseCreature creature ) + { + DistributeArtifact( creature, CreateRandomArtifact() ); + } + + public static void DistributeArtifact( BaseCreature creature, Item artifact ) + { + DistributeArtifact( FindRandomPlayer( creature ), artifact ); + } + + public static void DistributeArtifact( Mobile to ) + { + DistributeArtifact( to, CreateRandomArtifact() ); + } + + public static void DistributeArtifact( Mobile to, Item artifact ) + { + if ( to == null || artifact == null ) + return; + + Container pack = to.Backpack; + + if ( pack == null || !pack.TryDropItem( to, artifact, false ) ) + to.BankBox.DropItem( artifact ); + + to.SendLocalizedMessage( 1062317 ); // For your valor in combating the fallen beast, a special artifact has been bestowed on you. + } + + public static int GetArtifactChance( Mobile boss ) + { + if ( !Core.AOS ) + return 0; + + int luck = LootPack.GetLuckChanceForKiller( boss ); + int chance; + + if ( boss is DemonKnight ) + chance = 1500 + (luck / 5); + else + chance = 750 + (luck / 10); + + return chance; + } + + public static bool CheckArtifactChance( Mobile boss ) + { + return GetArtifactChance( boss ) > Utility.Random( 100000 ); + } + + public override WeaponAbility GetWeaponAbility() + { + switch ( Utility.Random( 3 ) ) + { + default: + case 0: return WeaponAbility.DoubleStrike; + case 1: return WeaponAbility.WhirlwindAttack; + case 2: return WeaponAbility.CrushingBlow; + } + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( !Summoned && !NoKillAwards && DemonKnight.CheckArtifactChance( this ) ) + DemonKnight.DistributeArtifact( this ); + } + + [Constructable] + public DemonKnight() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "demon knight" ); + Title = "the Dark Father"; + Body = 318; + BaseSoundID = 0x165; + + SetStr( 500 ); + SetDex( 100 ); + SetInt( 1000 ); + + SetHits( 30000 ); + SetMana( 5000 ); + + SetDamage( 17, 21 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Fire, 20 ); + SetDamageType( ResistanceType.Cold, 20 ); + SetDamageType( ResistanceType.Poison, 20 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 30 ); + SetResistance( ResistanceType.Fire, 30 ); + SetResistance( ResistanceType.Cold, 30 ); + SetResistance( ResistanceType.Poison, 30 ); + SetResistance( ResistanceType.Energy, 30 ); + + SetSkill( SkillName.DetectHidden, 80.0 ); + SetSkill( SkillName.EvalInt, 100.0 ); + SetSkill( SkillName.Magery, 100.0 ); + SetSkill( SkillName.Meditation, 120.0 ); + SetSkill( SkillName.MagicResist, 150.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 120.0 ); + + Fame = 28000; + Karma = -28000; + + VirtualArmor = 64; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.SuperBoss, 2 ); + AddLoot( LootPack.HighScrolls, Utility.RandomMinMax( 6, 60 ) ); + } + + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int TreasureMapLevel{ get{ return 1; } } + + private static bool m_InHere; + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + if ( from != null && from != this && !m_InHere ) + { + m_InHere = true; + AOS.Damage( from, this, Utility.RandomMinMax( 8, 20 ), 100, 0, 0, 0, 0 ); + + MovingEffect( from, 0xECA, 10, 0, false, false, 0, 0 ); + PlaySound( 0x491 ); + + if ( 0.05 > Utility.RandomDouble() ) + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( CreateBones_Callback ), from ); + + m_InHere = false; + } + } + + public virtual void CreateBones_Callback( object state ) + { + Mobile from = (Mobile)state; + Map map = from.Map; + + if ( map == null ) + return; + + int count = Utility.RandomMinMax( 1, 3 ); + + for ( int i = 0; i < count; ++i ) + { + int x = from.X + Utility.RandomMinMax( -1, 1 ); + int y = from.Y + Utility.RandomMinMax( -1, 1 ); + int z = from.Z; + + if ( !map.CanFit( x, y, z, 16, false, true ) ) + { + z = map.GetAverageZ( x, y ); + + if ( z == from.Z || !map.CanFit( x, y, z, 16, false, true ) ) + continue; + } + + UnholyBone bone = new UnholyBone(); + + bone.Hue = 0; + bone.Name = "unholy bones"; + bone.ItemID = Utility.Random( 0xECA, 9 ); + + bone.MoveToWorld( new Point3D( x, y, z ), map ); + } + } + + public DemonKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/Devourer.cs b/Scripts/Mobiles/Monsters/AOS/Devourer.cs new file mode 100644 index 000000000..32f266532 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/Devourer.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a devourer of souls corpse" )] + public class Devourer : BaseCreature + { + [Constructable] + public Devourer() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a devourer of souls"; + Body = 303; + BaseSoundID = 357; + + SetStr( 801, 950 ); + SetDex( 126, 175 ); + SetInt( 201, 250 ); + + SetHits( 650 ); + + SetDamage( 22, 26 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Cold, 20 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 90.1, 105.0 ); + SetSkill( SkillName.Tactics, 75.1, 85.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + Fame = 9500; + Karma = -9500; + + VirtualArmor = 44; + + PackNecroReg( 24, 45 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int Meat{ get{ return 3; } } + + public Devourer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/FleshGolem.cs b/Scripts/Mobiles/Monsters/AOS/FleshGolem.cs new file mode 100644 index 000000000..91f6dd51d --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/FleshGolem.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a flesh golem corpse" )] + public class FleshGolem : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.BleedAttack; + } + + [Constructable] + public FleshGolem() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a flesh golem"; + Body = 304; + BaseSoundID = 684; + + SetStr( 176, 200 ); + SetDex( 51, 75 ); + SetInt( 46, 70 ); + + SetHits( 106, 120 ); + + SetDamage( 18, 22 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 50.1, 75.0 ); + SetSkill( SkillName.Tactics, 55.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 70.0 ); + + Fame = 1000; + Karma = -1800; + + VirtualArmor = 34; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public FleshGolem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/FleshRenderer.cs b/Scripts/Mobiles/Monsters/AOS/FleshRenderer.cs new file mode 100644 index 000000000..86d7b6764 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/FleshRenderer.cs @@ -0,0 +1,116 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a fleshrenderer corpse" )] + public class FleshRenderer : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return Utility.RandomBool() ? WeaponAbility.Dismount : WeaponAbility.ParalyzingBlow; + } + + public override bool IgnoreYoungProtection { get { return Core.ML; } } + + [Constructable] + public FleshRenderer() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a fleshrenderer"; + Body = 315; + + SetStr( 401, 460 ); + SetDex( 201, 210 ); + SetInt( 221, 260 ); + + SetHits( 4500 ); + + SetDamage( 16, 20 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 80, 90 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 70, 80 ); + + SetSkill( SkillName.DetectHidden, 80.0 ); + SetSkill( SkillName.MagicResist, 155.1, 160.0 ); + SetSkill( SkillName.Meditation, 100.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 23000; + Karma = -23000; + + VirtualArmor = 24; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 2 ); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( !Summoned && !NoKillAwards && DemonKnight.CheckArtifactChance( this ) ) + DemonKnight.DistributeArtifact( this ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool Unprovokable{ get{ return true; } } + public override bool Uncalmable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int TreasureMapLevel{ get{ return 1; } } + + public override int GetAttackSound() + { + return 0x34C; + } + + public override int GetHurtSound() + { + return 0x354; + } + + public override int GetAngerSound() + { + return 0x34C; + } + + public override int GetIdleSound() + { + return 0x34C; + } + + public override int GetDeathSound() + { + return 0x354; + } + + public FleshRenderer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 660 ) + BaseSoundID = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/Gibberling.cs b/Scripts/Mobiles/Monsters/AOS/Gibberling.cs new file mode 100644 index 000000000..8683239c0 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/Gibberling.cs @@ -0,0 +1,73 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gibberling corpse" )] + public class Gibberling : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.Dismount; + } + + [Constructable] + public Gibberling() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a gibberling"; + Body = 307; + BaseSoundID = 422; + + SetStr( 141, 165 ); + SetDex( 101, 125 ); + SetInt( 56, 80 ); + + SetHits( 85, 99 ); + + SetDamage( 12, 17 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Fire, 40 ); + SetDamageType( ResistanceType.Energy, 60 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 45.1, 70.0 ); + SetSkill( SkillName.Tactics, 67.6, 92.5 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 27; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override int TreasureMapLevel{ get{ return 1; } } + + public Gibberling( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/GoreFiend.cs b/Scripts/Mobiles/Monsters/AOS/GoreFiend.cs new file mode 100644 index 000000000..b5e688456 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/GoreFiend.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gore fiend corpse" )] + public class GoreFiend : BaseCreature + { + [Constructable] + public GoreFiend() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a gore fiend"; + Body = 305; + BaseSoundID = 224; + + SetStr( 161, 185 ); + SetDex( 41, 65 ); + SetInt( 46, 70 ); + + SetHits( 97, 111 ); + + SetDamage( 15, 21 ); + + SetDamageType( ResistanceType.Physical, 85 ); + SetDamageType( ResistanceType.Poison, 15 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 5, 15 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 40.1, 55.0 ); + SetSkill( SkillName.Tactics, 45.1, 70.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 24; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override int GetDeathSound() + { + return 1218; + } + + public override bool BleedImmune{ get{ return true; } } + + public GoreFiend( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/Impaler.cs b/Scripts/Mobiles/Monsters/AOS/Impaler.cs new file mode 100644 index 000000000..5796a12f8 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/Impaler.cs @@ -0,0 +1,94 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an impaler corpse" )] + public class Impaler : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return Utility.RandomBool() ? WeaponAbility.MortalStrike : WeaponAbility.BleedAttack; + } + + public override bool IgnoreYoungProtection { get { return Core.ML; } } + + [Constructable] + public Impaler() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "impaler" ); + Body = 306; + BaseSoundID = 0x2A7; + + SetStr( 190 ); + SetDex( 45 ); + SetInt( 190 ); + + SetHits( 5000 ); + + SetDamage( 31, 35 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 90 ); + SetResistance( ResistanceType.Fire, 60 ); + SetResistance( ResistanceType.Cold, 75 ); + SetResistance( ResistanceType.Poison, 60 ); + SetResistance( ResistanceType.Energy, 100 ); + + SetSkill( SkillName.DetectHidden, 80.0 ); + SetSkill( SkillName.Meditation, 120.0 ); + SetSkill( SkillName.Poisoning, 160.0 ); + SetSkill( SkillName.MagicResist, 100.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 49; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 2 ); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( !Summoned && !NoKillAwards && DemonKnight.CheckArtifactChance( this ) ) + DemonKnight.DistributeArtifact( this ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return (0.8 >= Utility.RandomDouble() ? Poison.Greater : Poison.Deadly); } } + + public override int TreasureMapLevel{ get{ return 1; } } + + public Impaler( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 1200 ) + BaseSoundID = 0x2A7; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/MoundOfMaggots.cs b/Scripts/Mobiles/Monsters/AOS/MoundOfMaggots.cs new file mode 100644 index 000000000..200948871 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/MoundOfMaggots.cs @@ -0,0 +1,66 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a maggoty corpse" )] // TODO: Corpse name? + public class MoundOfMaggots : BaseCreature + { + [Constructable] + public MoundOfMaggots() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a mound of maggots"; + Body = 319; + BaseSoundID = 898; + + SetStr( 61, 70 ); + SetDex( 61, 70 ); + SetInt( 10 ); + + SetMana( 0 ); + + SetDamage( 3, 9 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 90 ); + SetResistance( ResistanceType.Poison, 100 ); + + SetSkill( SkillName.Tactics, 50.0 ); + SetSkill( SkillName.Wrestling, 50.1, 60.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 24; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Gems ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int TreasureMapLevel{ get{ return 1; } } + + public MoundOfMaggots( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/PatchworkSkeleton.cs b/Scripts/Mobiles/Monsters/AOS/PatchworkSkeleton.cs new file mode 100644 index 000000000..495ba8797 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/PatchworkSkeleton.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a patchwork skeletal corpse" )] + public class PatchworkSkeleton : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.Dismount; + } + + [Constructable] + public PatchworkSkeleton() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a patchwork skeleton"; + Body = 309; + BaseSoundID = 0x48D; + + SetStr( 96, 120 ); + SetDex( 71, 95 ); + SetInt( 16, 40 ); + + SetHits( 58, 72 ); + + SetDamage( 18, 22 ); + + SetDamageType( ResistanceType.Physical, 85 ); + SetDamageType( ResistanceType.Cold, 15 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 70, 80 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 70.1, 95.0 ); + SetSkill( SkillName.Tactics, 55.1, 80.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 500; + Karma = -500; + + VirtualArmor = 54; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int TreasureMapLevel{ get{ return 1; } } + + public PatchworkSkeleton( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/Ravager.cs b/Scripts/Mobiles/Monsters/AOS/Ravager.cs new file mode 100644 index 000000000..9bdd1297b --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/Ravager.cs @@ -0,0 +1,69 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a ravager corpse" )] + public class Ravager : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return Utility.RandomBool() ? WeaponAbility.Dismount : WeaponAbility.CrushingBlow; + } + + [Constructable] + public Ravager() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a ravager"; + Body = 314; + BaseSoundID = 357; + + SetStr( 251, 275 ); + SetDex( 101, 125 ); + SetInt( 66, 90 ); + + SetHits( 161, 175 ); + + SetDamage( 15, 20 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 50.1, 75.0 ); + SetSkill( SkillName.Tactics, 75.1, 100.0 ); + SetSkill( SkillName.Wrestling, 70.1, 90.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 54; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public Ravager( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/Revenant.cs b/Scripts/Mobiles/Monsters/AOS/Revenant.cs new file mode 100644 index 000000000..98b13418b --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/Revenant.cs @@ -0,0 +1,183 @@ +using System; +using Server; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + public class Revenant : BaseCreature + { + private Mobile m_Target; + private DateTime m_ExpireTime; + + public override void DisplayPaperdollTo( Mobile to ) + { + // Do nothing + } + + public override Mobile ConstantFocus{ get{ return m_Target; } } + public override bool NoHouseRestrictions{ get{ return true; } } + + public override double DispelDifficulty{ get{ return 80.0; } } + public override double DispelFocus{ get{ return 20.0; } } + + public Revenant( Mobile caster, Mobile target, TimeSpan duration ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.18, 0.36 ) + { + Name = "a revenant"; + Body = 400; + Hue = 1; + // TODO: Sound values? + + double scalar = caster.Skills[SkillName.SpiritSpeak].Value * 0.01; + + m_Target = target; + m_ExpireTime = DateTime.Now + duration; + + SetStr( 200 ); + SetDex( 150 ); + SetInt( 150 ); + + SetDamage( 16, 17 ); + + // Bestiary says 50 phys 50 cold, animal lore says differently + SetDamageType( ResistanceType.Physical, 100 ); + + SetSkill( SkillName.MagicResist, 100.0 * scalar ); // magic resist is absolute value of spiritspeak + SetSkill( SkillName.Tactics, 100.0 ); // always 100 + SetSkill( SkillName.Swords, 100.0 * scalar ); // not displayed in animal lore but tests clearly show this is influenced + SetSkill( SkillName.DetectHidden, 75.0 * scalar ); + + scalar /= 1.2; + + SetResistance( ResistanceType.Physical, 40 + (int)(20 * scalar), 50 + (int)(20 * scalar) ); + SetResistance( ResistanceType.Cold, 40 + (int)(20 * scalar), 50 + (int)(20 * scalar) ); + SetResistance( ResistanceType.Fire, (int)(20 * scalar) ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 40 + (int)(20 * scalar), 50 + (int)(20 * scalar) ); + + Fame = 0; + Karma = 0; + + ControlSlots = 3; + + VirtualArmor = 32; + + Item shroud = new DeathShroud(); + + shroud.Hue = 0x455; + + shroud.Movable = false; + + AddItem( shroud ); + + Halberd weapon = new Halberd(); + + weapon.Hue = 1; + weapon.Movable = false; + + AddItem( weapon ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public override bool BleedImmune{ get{ return true; } } + public override bool BardImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override void OnThink() + { + if ( !m_Target.Alive || DateTime.Now > m_ExpireTime ) + { + Kill(); + return; + } + else if ( Map != m_Target.Map || !InRange( m_Target, 15 ) ) + { + Map fromMap = Map; + Point3D from = Location; + + Map toMap = m_Target.Map; + Point3D to = m_Target.Location; + + if ( toMap != null ) + { + for ( int i = 0; i < 5; ++i ) + { + Point3D loc = new Point3D( to.X - 4 + Utility.Random( 9 ), to.Y - 4 + Utility.Random( 9 ), to.Z ); + + if ( toMap.CanSpawnMobile( loc ) ) + { + to = loc; + break; + } + else + { + loc.Z = toMap.GetAverageZ( loc.X, loc.Y ); + + if ( toMap.CanSpawnMobile( loc ) ) + { + to = loc; + break; + } + } + } + } + + Map = toMap; + Location = to; + + ProcessDelta(); + + Effects.SendLocationParticles( EffectItem.Create( from, fromMap, EffectItem.DefaultDuration ), 0x3728, 1, 13, 37, 7, 5023, 0 ); + FixedParticles( 0x3728, 1, 13, 5023, 37, 7, EffectLayer.Waist ); + + PlaySound( 0x37D ); + } + + if ( m_Target.Hidden && InRange( m_Target, 3 ) && DateTime.Now >= this.NextSkillTime && UseSkill( SkillName.DetectHidden ) ) + { + Target targ = this.Target; + + if ( targ != null ) + targ.Invoke( this, this ); + } + + Combatant = m_Target; + FocusMob = m_Target; + + if ( AIObject != null ) + AIObject.Action = ActionType.Combat; + + base.OnThink(); + } + + public override bool OnBeforeDeath() + { + Effects.PlaySound( Location, Map, 0x10B ); + Effects.SendLocationParticles( EffectItem.Create( Location, Map, TimeSpan.FromSeconds( 10.0 ) ), 0x37CC, 1, 50, 2101, 7, 9909, 0 ); + + Delete(); + return false; + } + + public Revenant( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/ShadowKnight.cs b/Scripts/Mobiles/Monsters/AOS/ShadowKnight.cs new file mode 100644 index 000000000..a7273a20f --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/ShadowKnight.cs @@ -0,0 +1,194 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a shadow knight corpse" )] + public class ShadowKnight : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return Utility.RandomBool() ? WeaponAbility.ConcussionBlow : WeaponAbility.CrushingBlow; + } + + public override bool IgnoreYoungProtection { get { return Core.ML; } } + + [Constructable] + public ShadowKnight() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "shadow knight" ); + Title = "the Shadow Knight"; + Body = 311; + + SetStr( 250 ); + SetDex( 100 ); + SetInt( 100 ); + + SetHits( 2000 ); + + SetDamage( 20, 30 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Cold, 40 ); + + SetResistance( ResistanceType.Physical, 90 ); + SetResistance( ResistanceType.Fire, 65 ); + SetResistance( ResistanceType.Cold, 75 ); + SetResistance( ResistanceType.Poison, 75 ); + SetResistance( ResistanceType.Energy, 55 ); + + SetSkill( SkillName.Chivalry, 120.0 ); + SetSkill( SkillName.DetectHidden, 80.0 ); + SetSkill( SkillName.EvalInt, 100.0 ); + SetSkill( SkillName.Magery, 100.0 ); + SetSkill( SkillName.Meditation, 100.0 ); + SetSkill( SkillName.MagicResist, 120.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 25000; + Karma = -25000; + + VirtualArmor = 54; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 2 ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( !Summoned && !NoKillAwards && DemonKnight.CheckArtifactChance( this ) ) + DemonKnight.DistributeArtifact( this ); + } + + public override int GetIdleSound() + { + return 0x2CE; + } + + public override int GetDeathSound() + { + return 0x2C1; + } + + public override int GetHurtSound() + { + return 0x2D1; + } + + public override int GetAttackSound() + { + return 0x2C8; + } + + private Timer m_SoundTimer; + private bool m_HasTeleportedAway; + + public override void OnCombatantChange() + { + base.OnCombatantChange(); + + if ( Hidden && Combatant != null ) + Combatant = null; + } + + public virtual void SendTrackingSound() + { + if ( Hidden ) + { + Effects.PlaySound( this.Location, this.Map, 0x2C8 ); + Combatant = null; + } + else + { + Frozen = false; + + if ( m_SoundTimer != null ) + m_SoundTimer.Stop(); + + m_SoundTimer = null; + } + } + + public override void OnThink() + { + if ( !m_HasTeleportedAway && Hits < (HitsMax / 2) ) + { + Map map = this.Map; + + if ( map != null ) + { + // try 10 times to find a teleport spot + for ( int i = 0; i < 10; ++i ) + { + int x = X + (Utility.RandomMinMax( 5, 10 ) * (Utility.RandomBool() ? 1 : -1)); + int y = Y + (Utility.RandomMinMax( 5, 10 ) * (Utility.RandomBool() ? 1 : -1)); + int z = Z; + + if ( !map.CanFit( x, y, z, 16, false, false ) ) + continue; + + Point3D from = this.Location; + Point3D to = new Point3D( x, y, z ); + + if ( !InLOS( to ) ) + continue; + + this.Location = to; + this.ProcessDelta(); + this.Hidden = true; + this.Combatant = null; + + Effects.SendLocationParticles( EffectItem.Create( from, map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( to, map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + + Effects.PlaySound( to, map, 0x1FE ); + + m_HasTeleportedAway = true; + m_SoundTimer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 2.5 ), new TimerCallback( SendTrackingSound ) ); + + Frozen = true; + + break; + } + } + } + + base.OnThink(); + } + + public override bool BardImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int TreasureMapLevel{ get{ return 1; } } + + public ShadowKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 357 ) + BaseSoundID = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/SkitteringHopper.cs b/Scripts/Mobiles/Monsters/AOS/SkitteringHopper.cs new file mode 100644 index 000000000..f882b5ad0 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/SkitteringHopper.cs @@ -0,0 +1,68 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a skittering hopper corpse" )] + public class SkitteringHopper : BaseCreature + { + [Constructable] + public SkitteringHopper() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a skittering hopper"; + Body = 302; + BaseSoundID = 959; + + SetStr( 41, 65 ); + SetDex( 91, 115 ); + SetInt( 26, 50 ); + + SetHits( 31, 45 ); + + SetDamage( 3, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 30.1, 45.0 ); + SetSkill( SkillName.Tactics, 45.1, 70.0 ); + SetSkill( SkillName.Wrestling, 40.1, 60.0 ); + + Fame = 300; + Karma = 0; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -12.9; + + VirtualArmor = 12; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override int TreasureMapLevel{ get{ return 1; } } + + public SkitteringHopper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/Treefellow.cs b/Scripts/Mobiles/Monsters/AOS/Treefellow.cs new file mode 100644 index 000000000..9c0456d8e --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/Treefellow.cs @@ -0,0 +1,93 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a treefellow corpse" )] + public class Treefellow : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.Dismount; + } + + [Constructable] + public Treefellow() : base( AIType.AI_Melee, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + Name = "a treefellow"; + Body = 301; + + SetStr( 196, 220 ); + SetDex( 31, 55 ); + SetInt( 66, 90 ); + + SetHits( 118, 132 ); + + SetDamage( 12, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 30, 35 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 40.1, 55.0 ); + SetSkill( SkillName.Tactics, 65.1, 90.0 ); + SetSkill( SkillName.Wrestling, 65.1, 85.0 ); + + Fame = 500; + Karma = 1500; + + VirtualArmor = 24; + PackItem( new Log( Utility.RandomMinMax( 23, 34 ) ) ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override int GetIdleSound() + { + return 443; + } + + public override int GetDeathSound() + { + return 31; + } + + public override int GetAttackSound() + { + return 672; + } + + public override bool BleedImmune{ get{ return true; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public Treefellow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 442 ) + BaseSoundID = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/VampireBat.cs b/Scripts/Mobiles/Monsters/AOS/VampireBat.cs new file mode 100644 index 000000000..cfcdd2a50 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/VampireBat.cs @@ -0,0 +1,70 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a vampire bat corpse" )] + public class VampireBat : BaseCreature + { + [Constructable] + public VampireBat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a vampire bat"; + Body = 317; + BaseSoundID = 0x270; + + SetStr( 91, 110 ); + SetDex( 91, 115 ); + SetInt( 26, 50 ); + + SetHits( 55, 66 ); + + SetDamage( 7, 9 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 70.1, 95.0 ); + SetSkill( SkillName.Tactics, 55.1, 80.0 ); + SetSkill( SkillName.Wrestling, 30.1, 55.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 14; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int GetIdleSound() + { + return 0x29B; + } + + public VampireBat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/WailingBanshee.cs b/Scripts/Mobiles/Monsters/AOS/WailingBanshee.cs new file mode 100644 index 000000000..95d8baf5d --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/WailingBanshee.cs @@ -0,0 +1,73 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a wailing banshee corpse" )] + public class WailingBanshee : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.MortalStrike; + } + + [Constructable] + public WailingBanshee() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a wailing banshee"; + Body = 310; + BaseSoundID = 0x482; + + SetStr( 126, 150 ); + SetDex( 76, 100 ); + SetInt( 86, 110 ); + + SetHits( 76, 90 ); + + SetDamage( 10, 14 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 60 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 70, 80 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 70.1, 95.0 ); + SetSkill( SkillName.Tactics, 45.1, 70.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 19; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + + public WailingBanshee( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/AOS/WandererOfTheVoid.cs b/Scripts/Mobiles/Monsters/AOS/WandererOfTheVoid.cs new file mode 100644 index 000000000..5b7d03557 --- /dev/null +++ b/Scripts/Mobiles/Monsters/AOS/WandererOfTheVoid.cs @@ -0,0 +1,78 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a wanderer of the void corpse" )] + public class WandererOfTheVoid : BaseCreature + { + [Constructable] + public WandererOfTheVoid() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a wanderer of the void"; + Body = 316; + BaseSoundID = 377; + + SetStr( 111, 200 ); + SetDex( 101, 125 ); + SetInt( 301, 390 ); + + SetHits( 351, 400 ); + + SetDamage( 11, 13 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Cold, 15 ); + SetDamageType( ResistanceType.Energy, 85 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 50, 75 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 60.1, 70.0 ); + SetSkill( SkillName.Magery, 60.1, 70.0 ); + SetSkill( SkillName.Meditation, 60.1, 70.0 ); + SetSkill( SkillName.MagicResist, 50.1, 75.0 ); + SetSkill( SkillName.Tactics, 60.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 70.0 ); + + Fame = 20000; + Karma = -20000; + + VirtualArmor = 44; + + int count = Utility.RandomMinMax( 2, 3 ); + + for ( int i = 0; i < count; ++i ) + PackItem( new TreasureMap( 3, Map.Trammel ) ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return Core.AOS ? 4 : 1; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + } + + public WandererOfTheVoid( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/AntLion.cs b/Scripts/Mobiles/Monsters/Ants/AntLion.cs new file mode 100644 index 000000000..5c123b336 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/AntLion.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an ant lion corpse" )] + public class AntLion : BaseCreature + { + [Constructable] + public AntLion() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ant lion"; + Body = 787; + BaseSoundID = 1006; + + SetStr( 296, 320 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 151, 162 ); + + SetDamage( 7, 21 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 30 ); + + SetResistance( ResistanceType.Physical, 45, 60 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 30, 35 ); + + SetSkill( SkillName.MagicResist, 70.0 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 45; + + PackItem( new Bone( 3 ) ); + PackItem( new FertileDirt( Utility.RandomMinMax( 1, 5 ) ) ); + + switch ( Utility.Random( 4 ) ) + { + case 0: PackItem( new DullCopperOre( Utility.RandomMinMax( 1, 10 ) ) ); break; + case 1: PackItem( new ShadowIronOre( Utility.RandomMinMax( 1, 10 ) ) ); break; + case 2: PackItem( new CopperOre( Utility.RandomMinMax( 1, 10 ) ) ); break; + case 3: PackItem( new BronzeOre( Utility.RandomMinMax( 1, 10 ) ) ); break; + } + + // TODO: skeleton + } + + public override int GetAngerSound() + { + return 0x5A; + } + + public override int GetIdleSound() + { + return 0x5A; + } + + public override int GetAttackSound() + { + return 0x164; + } + + public override int GetHurtSound() + { + return 0x187; + } + + public override int GetDeathSound() + { + return 0x1BA; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + } + + + public AntLion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs b/Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs new file mode 100644 index 000000000..44b1c8ffe --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorQueen.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a solen infiltrator corpse" )] // TODO: Corpse name? + public class BlackSolenInfiltratorQueen : BaseCreature + { + [Constructable] + public BlackSolenInfiltratorQueen() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a black solen infiltrator"; + Body = 807; + BaseSoundID = 959; + Hue = 0x453; + + SetStr( 326, 350 ); + SetDex( 141, 165 ); + SetInt( 96, 120 ); + + SetHits( 151, 162 ); + + SetDamage( 10, 15 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 30 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 35 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 35, 40 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.MagicResist, 90.0 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + Fame = 6500; + Karma = -6500; + + VirtualArmor = 50; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( 0.05 > Utility.RandomDouble() ) ? 16 : 4 ) ); + } + + public override int GetAngerSound() + { + return 0x259; + } + + public override int GetIdleSound() + { + return 0x259; + } + + public override int GetAttackSound() + { + return 0x195; + } + + public override int GetHurtSound() + { + return 0x250; + } + + public override int GetDeathSound() + { + return 0x25B; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckBlackFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnBlackDamage( from ); + + base.OnDamage( amount, from, willKill ); + } + + public BlackSolenInfiltratorQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs b/Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs new file mode 100644 index 000000000..911f86c58 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/BlackSolenInfiltratorWarrior.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a solen infiltrator corpse" )] + public class BlackSolenInfiltratorWarrior : BaseCreature + { + [Constructable] + public BlackSolenInfiltratorWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a black solen infiltrator"; + Body = 806; + BaseSoundID = 959; + Hue = 0x453; + + SetStr( 206, 230 ); + SetDex( 121, 145 ); + SetInt( 66, 90 ); + + SetHits( 96, 107 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 20, 35 ); + SetResistance( ResistanceType.Fire, 20, 35 ); + SetResistance( ResistanceType.Cold, 10, 25 ); + SetResistance( ResistanceType.Poison, 20, 35 ); + SetResistance( ResistanceType.Energy, 10, 25 ); + + SetSkill( SkillName.MagicResist, 80.0 ); + SetSkill( SkillName.Tactics, 80.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 40; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( 0.05 > Utility.RandomDouble() )? 13 : 3 ) ); + } + + public override int GetAngerSound() + { + return 0xB5; + } + + public override int GetIdleSound() + { + return 0xB5; + } + + public override int GetAttackSound() + { + return 0x289; + } + + public override int GetHurtSound() + { + return 0xBC; + } + + public override int GetDeathSound() + { + return 0xE4; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 4 ) ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckBlackFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnBlackDamage( from ); + + base.OnDamage( amount, from, willKill ); + } + + public BlackSolenInfiltratorWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/BlackSolenQueen.cs b/Scripts/Mobiles/Monsters/Ants/BlackSolenQueen.cs new file mode 100644 index 000000000..4bfad5767 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/BlackSolenQueen.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a solen queen corpse" )] + public class BlackSolenQueen : BaseCreature + { + private bool m_BurstSac; + public bool BurstSac{ get{ return m_BurstSac; } } + + [Constructable] + public BlackSolenQueen() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a black solen queen"; + Body = 807; + BaseSoundID = 959; + Hue = 0x453; + + SetStr( 296, 320 ); + SetDex( 121, 145 ); + SetInt( 76, 100 ); + + SetHits( 151, 162 ); + + SetDamage( 10, 15 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 30 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 35 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 35, 40 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.MagicResist, 70.0 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 45; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( Utility.RandomDouble() > 0.05 )? 5 : 25 ) ); + + if ( Utility.RandomDouble() < 0.05 ) + PackItem( new BallOfSummoning() ); + } + + public override int GetAngerSound() + { + return 0x259; + } + + public override int GetIdleSound() + { + return 0x259; + } + + public override int GetAttackSound() + { + return 0x195; + } + + public override int GetHurtSound() + { + return 0x250; + } + + public override int GetDeathSound() + { + return 0x25B; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckBlackFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnBlackDamage( from ); + + if ( !willKill ) + { + if ( !BurstSac ) + { + if ( Hits < 50 ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The solen's acid sac is burst open! *" ); + m_BurstSac = true; + } + } + else if ( from != null && from != this && InRange( from, 1 ) ) + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, from ); + } + } + + base.OnDamage( amount, from, willKill ); + } + + public override bool OnBeforeDeath() + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, 1, 4 ); + + return base.OnBeforeDeath(); + } + + public BlackSolenQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 1 ); + writer.Write( m_BurstSac ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + switch( version ) + { + case 1: + { + m_BurstSac = reader.ReadBool(); + break; + } + } + } + } +} diff --git a/Scripts/Mobiles/Monsters/Ants/BlackSolenWarrior.cs b/Scripts/Mobiles/Monsters/Ants/BlackSolenWarrior.cs new file mode 100644 index 000000000..06e7f15bf --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/BlackSolenWarrior.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a solen warrior corpse" )] + public class BlackSolenWarrior : BaseCreature + { + private bool m_BurstSac; + public bool BurstSac{ get{ return m_BurstSac; } } + + [Constructable] + public BlackSolenWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a black solen warrior"; + Body = 806; + BaseSoundID = 959; + Hue = 0x453; + + SetStr( 196, 220 ); + SetDex( 101, 125 ); + SetInt( 36, 60 ); + + SetHits( 96, 107 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 20, 35 ); + SetResistance( ResistanceType.Fire, 20, 35 ); + SetResistance( ResistanceType.Cold, 10, 25 ); + SetResistance( ResistanceType.Poison, 20, 35 ); + SetResistance( ResistanceType.Energy, 10, 25 ); + + SetSkill( SkillName.MagicResist, 60.0 ); + SetSkill( SkillName.Tactics, 80.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 35; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( 0.05 > Utility.RandomDouble() )? 13 : 3 ) ); + + if ( Utility.RandomDouble() < 0.05 ) + PackItem( new BraceletOfBinding() ); + } + + public override int GetAngerSound() + { + return 0xB5; + } + + public override int GetIdleSound() + { + return 0xB5; + } + + public override int GetAttackSound() + { + return 0x289; + } + + public override int GetHurtSound() + { + return 0xBC; + } + + public override int GetDeathSound() + { + return 0xE4; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 4 ) ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckBlackFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnBlackDamage( from ); + + if ( !willKill ) + { + if ( !BurstSac ) + { + if ( Hits < 50 ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The solen's acid sac is burst open! *" ); + m_BurstSac = true; + } + } + else if ( from != null && from != this && InRange( from, 1 ) ) + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, from ); + } + } + + base.OnDamage( amount, from, willKill ); + } + + public override bool OnBeforeDeath() + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, 1, 4 ); + + return base.OnBeforeDeath(); + } + + public BlackSolenWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 1 ); + writer.Write( m_BurstSac ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + switch( version ) + { + case 1: + { + m_BurstSac = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/BlackSolenWorker.cs b/Scripts/Mobiles/Monsters/Ants/BlackSolenWorker.cs new file mode 100644 index 000000000..bf358c97b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/BlackSolenWorker.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a solen worker corpse" )] + public class BlackSolenWorker : BaseCreature + { + [Constructable] + public BlackSolenWorker() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a black solen worker"; + Body = 805; + BaseSoundID = 959; + Hue = 0x453; + + SetStr( 96, 120 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 60.0 ); + SetSkill( SkillName.Tactics, 65.0 ); + SetSkill( SkillName.Wrestling, 60.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 28; + + PackGold( Utility.Random( 100, 180 ) ); + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus() ); + } + + public override int GetAngerSound() + { + return 0x269; + } + + public override int GetIdleSound() + { + return 0x269; + } + + public override int GetAttackSound() + { + return 0x186; + } + + public override int GetHurtSound() + { + return 0x1BE; + } + + public override int GetDeathSound() + { + return 0x8E; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 2 ) ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckBlackFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnBlackDamage( from ); + + base.OnDamage( amount, from, willKill ); + } + + public BlackSolenWorker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs b/Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs new file mode 100644 index 000000000..fdf58998f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorQueen.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a solen infiltrator corpse" )] // TODO: Corpse name? + public class RedSolenInfiltratorQueen : BaseCreature + { + [Constructable] + public RedSolenInfiltratorQueen() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a red solen infiltrator"; + Body = 783; + BaseSoundID = 959; + + SetStr( 326, 350 ); + SetDex( 141, 165 ); + SetInt( 96, 120 ); + + SetHits( 151, 162 ); + + SetDamage( 10, 15 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 30 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 35 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 35, 40 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.MagicResist, 90.0 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + Fame = 6500; + Karma = -6500; + + VirtualArmor = 50; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( 0.05 < Utility.RandomDouble() )? 4 : 16 ) ); + } + + public override int GetAngerSound() + { + return 0x259; + } + + public override int GetIdleSound() + { + return 0x259; + } + + public override int GetAttackSound() + { + return 0x195; + } + + public override int GetHurtSound() + { + return 0x250; + } + + public override int GetDeathSound() + { + return 0x25B; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckRedFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnRedDamage( from ); + + base.OnDamage( amount, from, willKill ); + } + + public RedSolenInfiltratorQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs b/Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs new file mode 100644 index 000000000..71c564607 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/RedSolenInfiltratorWarrior.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a solen infiltrator corpse" )] + public class RedSolenInfiltratorWarrior : BaseCreature + { + [Constructable] + public RedSolenInfiltratorWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a red solen infiltrator"; + Body = 782; + BaseSoundID = 959; + + SetStr( 206, 230 ); + SetDex( 121, 145 ); + SetInt( 66, 90 ); + + SetHits( 96, 107 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 20, 35 ); + SetResistance( ResistanceType.Fire, 20, 35 ); + SetResistance( ResistanceType.Cold, 10, 25 ); + SetResistance( ResistanceType.Poison, 20, 35 ); + SetResistance( ResistanceType.Energy, 10, 25 ); + + SetSkill( SkillName.MagicResist, 80.0 ); + SetSkill( SkillName.Tactics, 80.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 40; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( 0.05 < Utility.RandomDouble() )? 3 : 13 ) ); + + } + + public override int GetAngerSound() + { + return 0xB5; + } + + public override int GetIdleSound() + { + return 0xB5; + } + + public override int GetAttackSound() + { + return 0x289; + } + + public override int GetHurtSound() + { + return 0xBC; + } + + public override int GetDeathSound() + { + return 0xE4; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 4 ) ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckRedFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnRedDamage( from ); + + base.OnDamage( amount, from, willKill ); + } + + public RedSolenInfiltratorWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/RedSolenQueen.cs b/Scripts/Mobiles/Monsters/Ants/RedSolenQueen.cs new file mode 100644 index 000000000..b5a8641f6 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/RedSolenQueen.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a solen queen corpse" )] + public class RedSolenQueen : BaseCreature + { + private bool m_BurstSac; + public bool BurstSac{ get{ return m_BurstSac; } } + + [Constructable] + public RedSolenQueen() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a red solen queen"; + Body = 783; + BaseSoundID = 959; + + SetStr( 296, 320 ); + SetDex( 121, 145 ); + SetInt( 76, 100 ); + + SetHits( 151, 162 ); + + SetDamage( 10, 15 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 30 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 35 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 35, 40 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.MagicResist, 70.0 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 45; + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus( ( Utility.RandomDouble() > 0.05 )? 5 : 25 ) ); + + if( Utility.RandomDouble() < 0.05 ) + PackItem( new BallOfSummoning() ); + } + + public override int GetAngerSound() + { + return 0x259; + } + + public override int GetIdleSound() + { + return 0x259; + } + + public override int GetAttackSound() + { + return 0x195; + } + + public override int GetHurtSound() + { + return 0x250; + } + + public override int GetDeathSound() + { + return 0x25B; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckRedFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnRedDamage( from ); + + if ( !willKill ) + { + if ( !BurstSac ) + { + if ( Hits < 50 ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The solen's acid sac is burst open! *" ); + m_BurstSac = true; + } + } + else if ( from != null && from != this && InRange( from, 1 ) ) + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, from ); + } + } + + base.OnDamage( amount, from, willKill ); + } + + public override bool OnBeforeDeath() + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, 1, 4 ); + + return base.OnBeforeDeath(); + } + + public RedSolenQueen( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 1 ); + writer.Write( m_BurstSac ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + switch( version ) + { + case 1: + { + m_BurstSac = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/RedSolenWarrior.cs b/Scripts/Mobiles/Monsters/Ants/RedSolenWarrior.cs new file mode 100644 index 000000000..cf553bf8a --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/RedSolenWarrior.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a solen warrior corpse" )] + public class RedSolenWarrior : BaseCreature + { + private bool m_BurstSac; + public bool BurstSac{ get{ return m_BurstSac; } } + + [Constructable] + public RedSolenWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a red solen warrior"; + Body = 782; + BaseSoundID = 959; + + SetStr( 196, 220 ); + SetDex( 101, 125 ); + SetInt( 36, 60 ); + + SetHits( 96, 107 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 20, 35 ); + SetResistance( ResistanceType.Fire, 20, 35 ); + SetResistance( ResistanceType.Cold, 10, 25 ); + SetResistance( ResistanceType.Poison, 20, 35 ); + SetResistance( ResistanceType.Energy, 10, 25 ); + + SetSkill( SkillName.MagicResist, 60.0 ); + SetSkill( SkillName.Tactics, 80.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 35; + + SolenHelper.PackPicnicBasket( this ); + PackItem( new ZoogiFungus( ( 0.05 < Utility.RandomDouble() )? 3 : 13 ) ); + + if ( Utility.RandomDouble() < 0.05 ) + PackItem( new BraceletOfBinding() ); + + } + + public override int GetAngerSound() + { + return 0xB5; + } + + public override int GetIdleSound() + { + return 0xB5; + } + + public override int GetAttackSound() + { + return 0x289; + } + + public override int GetHurtSound() + { + return 0xBC; + } + + public override int GetDeathSound() + { + return 0xE4; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 4 ) ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckRedFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnRedDamage( from ); + + if ( !willKill ) + { + if ( !BurstSac ) + { + if ( Hits < 50 ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, true, "* The solen's acid sac is burst open! *" ); + m_BurstSac = true; + } + } + else if ( from != null && from != this && InRange( from, 1 ) ) + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, from ); + } + } + + base.OnDamage( amount, from, willKill ); + } + + public override bool OnBeforeDeath() + { + SpillAcid( TimeSpan.FromSeconds( 10 ), 30, 30, 1, 4 ); + + return base.OnBeforeDeath(); + } + + public RedSolenWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 1 ); + writer.Write( m_BurstSac ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + switch( version ) + { + case 1: + { + m_BurstSac = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/RedSolenWorker.cs b/Scripts/Mobiles/Monsters/Ants/RedSolenWorker.cs new file mode 100644 index 000000000..c3db08079 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/RedSolenWorker.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a solen worker corpse" )] + public class RedSolenWorker : BaseCreature + { + [Constructable] + public RedSolenWorker() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a red solen worker"; + Body = 781; + BaseSoundID = 959; + + SetStr( 96, 120 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 60.0 ); + SetSkill( SkillName.Tactics, 65.0 ); + SetSkill( SkillName.Wrestling, 60.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 28; + + PackGold( Utility.Random( 100, 180 ) ); + + SolenHelper.PackPicnicBasket( this ); + + PackItem( new ZoogiFungus() ); + } + + public override int GetAngerSound() + { + return 0x269; + } + + public override int GetIdleSound() + { + return 0x269; + } + + public override int GetAttackSound() + { + return 0x186; + } + + public override int GetHurtSound() + { + return 0x1BE; + } + + public override int GetDeathSound() + { + return 0x8E; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 2 ) ); + } + + public override bool IsEnemy( Mobile m ) + { + if ( SolenHelper.CheckRedFriendship( m ) ) + return false; + else + return base.IsEnemy( m ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + SolenHelper.OnRedDamage( from ); + + base.OnDamage( amount, from, willKill ); + } + + public RedSolenWorker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ants/SolenHelper.cs b/Scripts/Mobiles/Monsters/Ants/SolenHelper.cs new file mode 100644 index 000000000..308eb08f5 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ants/SolenHelper.cs @@ -0,0 +1,101 @@ +using System; +using Server; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + public class SolenHelper + { + public static void PackPicnicBasket( BaseCreature solen ) + { + if ( 1 > Utility.Random( 100 ) ) + { + PicnicBasket basket = new PicnicBasket(); + + basket.DropItem( new BeverageBottle( BeverageType.Wine ) ); + basket.DropItem( new CheeseWedge() ); + + solen.PackItem( basket ); + } + } + + public static bool CheckRedFriendship( Mobile m ) + { + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + if ( bc.Controlled && bc.ControlMaster is PlayerMobile ) + return CheckRedFriendship( bc.ControlMaster ); + else if ( bc.Summoned && bc.SummonMaster is PlayerMobile ) + return CheckRedFriendship( bc.SummonMaster ); + } + + PlayerMobile player = m as PlayerMobile; + + return player != null && player.SolenFriendship == SolenFriendship.Red; + } + + public static bool CheckBlackFriendship( Mobile m ) + { + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + if ( bc.Controlled && bc.ControlMaster is PlayerMobile ) + return CheckBlackFriendship( bc.ControlMaster ); + else if ( bc.Summoned && bc.SummonMaster is PlayerMobile ) + return CheckBlackFriendship( bc.SummonMaster ); + } + + PlayerMobile player = m as PlayerMobile; + + return player != null && player.SolenFriendship == SolenFriendship.Black; + } + + public static void OnRedDamage( Mobile from ) + { + if ( from is BaseCreature ) + { + BaseCreature bc = (BaseCreature)from; + + if ( bc.Controlled && bc.ControlMaster is PlayerMobile ) + OnRedDamage( bc.ControlMaster ); + else if ( bc.Summoned && bc.SummonMaster is PlayerMobile ) + OnRedDamage( bc.SummonMaster ); + } + + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.SolenFriendship == SolenFriendship.Red ) + { + player.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1054103 ); // The solen revoke their friendship. You will now be considered an intruder. + + player.SolenFriendship = SolenFriendship.None; + } + } + + public static void OnBlackDamage( Mobile from ) + { + if ( from is BaseCreature ) + { + BaseCreature bc = (BaseCreature)from; + + if ( bc.Controlled && bc.ControlMaster is PlayerMobile ) + OnBlackDamage( bc.ControlMaster ); + else if ( bc.Summoned && bc.SummonMaster is PlayerMobile ) + OnBlackDamage( bc.SummonMaster ); + } + + PlayerMobile player = from as PlayerMobile; + + if ( player != null && player.SolenFriendship == SolenFriendship.Black ) + { + player.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1054103 ); // The solen revoke their friendship. You will now be considered an intruder. + + player.SolenFriendship = SolenFriendship.None; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs b/Scripts/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs new file mode 100644 index 000000000..9c9dbc603 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Magic/DreadSpider.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a dread spider corpse" )] + public class DreadSpider : BaseCreature + { + [Constructable] + public DreadSpider () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a dread spider"; + Body = 11; + BaseSoundID = 1170; + + SetStr( 196, 220 ); + SetDex( 126, 145 ); + SetInt( 286, 310 ); + + SetHits( 118, 132 ); + + SetDamage( 5, 17 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Poison, 80 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 90, 100 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.EvalInt, 65.1, 80.0 ); + SetSkill( SkillName.Magery, 65.1, 80.0 ); + SetSkill( SkillName.Meditation, 65.1, 80.0 ); + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 55.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 75.0 ); + + Fame = 5000; + Karma = -5000; + + VirtualArmor = 36; + + PackItem( new SpidersSilk( 8 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 3; } } + + public DreadSpider( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 263 ) + BaseSoundID = 1170; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs b/Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs new file mode 100644 index 000000000..fff658c04 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanAvenger.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a terathan avenger corpse" )] + public class TerathanAvenger : BaseCreature + { + [Constructable] + public TerathanAvenger() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a terathan avenger"; + Body = 152; + BaseSoundID = 0x24D; + + SetStr( 467, 645 ); + SetDex( 77, 95 ); + SetInt( 126, 150 ); + + SetHits( 296, 372 ); + SetMana( 46, 70 ); + + SetDamage( 18, 22 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 90, 100 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.EvalInt, 70.3, 100.0 ); + SetSkill( SkillName.Magery, 70.3, 100.0 ); + SetSkill( SkillName.Poisoning, 60.1, 80.0 ); + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 50; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 2 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + public override Poison HitPoison{ get{ return Poison.Deadly; } } + public override int TreasureMapLevel{ get{ return 3; } } + public override int Meat{ get{ return 2; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public TerathanAvenger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 263 ) + BaseSoundID = 0x24D; + } + } +} diff --git a/Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs b/Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs new file mode 100644 index 000000000..3e1ca4c24 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Magic/TerathanMatriarch.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a terathan matriarch corpse" )] + public class TerathanMatriarch : BaseCreature + { + [Constructable] + public TerathanMatriarch() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a terathan matriarch"; + Body = 72; + BaseSoundID = 599; + + SetStr( 316, 405 ); + SetDex( 96, 115 ); + SetInt( 366, 455 ); + + SetHits( 190, 243 ); + + SetDamage( 11, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 10000; + Karma = -10000; + + PackItem( new SpidersSilk( 5 ) ); + PackNecroReg( Utility.RandomMinMax( 4, 10 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + AddLoot( LootPack.Potions ); + } + + public override int TreasureMapLevel{ get{ return 4; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public TerathanMatriarch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs b/Scripts/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs new file mode 100644 index 000000000..02507d19e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Melee/FrostSpider.cs @@ -0,0 +1,78 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a frost spider corpse" )] + public class FrostSpider : BaseCreature + { + [Constructable] + public FrostSpider() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a frost spider"; + Body = 20; + BaseSoundID = 0x388; + + SetStr( 76, 100 ); + SetDex( 126, 145 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + SetMana( 0 ); + + SetDamage( 6, 16 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 80 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 35.1, 50.0 ); + SetSkill( SkillName.Wrestling, 50.1, 65.0 ); + + Fame = 775; + Karma = -775; + + VirtualArmor = 28; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 74.7; + + PackItem( new SpidersSilk( 7 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Poor ); + } + + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Arachnid; } } + + public FrostSpider( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 387 ) + BaseSoundID = 0x388; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs b/Scripts/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs new file mode 100644 index 000000000..fe9464715 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Melee/GiantBlackWidow.cs @@ -0,0 +1,75 @@ +using System; +using Server.Items; +using Server.Targeting; +using System.Collections; + +namespace Server.Mobiles +{ + [CorpseName( "a giant black widow spider corpse" )] // stupid corpse name + public class GiantBlackWidow : BaseCreature + { + [Constructable] + public GiantBlackWidow() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a giant black widow"; + Body = 0x9D; + BaseSoundID = 0x388; // TODO: validate + + SetStr( 76, 100 ); + SetDex( 96, 115 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + + SetDamage( 5, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 30 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Anatomy, 30.3, 75.0 ); + SetSkill( SkillName.Poisoning, 60.1, 80.0 ); + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 65.1, 80.0 ); + SetSkill( SkillName.Wrestling, 70.1, 85.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 24; + + PackItem( new SpidersSilk( 5 ) ); + PackItem( new LesserPoisonPotion() ); + PackItem( new LesserPoisonPotion() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + public override Poison HitPoison{ get{ return Poison.Deadly; } } + + public GiantBlackWidow( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs b/Scripts/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs new file mode 100644 index 000000000..05ab0d8b1 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Melee/GiantSpider.cs @@ -0,0 +1,75 @@ +using System; +using Server.Items; +using Server.Targeting; +using System.Collections; + +namespace Server.Mobiles +{ + [CorpseName( "a giant spider corpse" )] + public class GiantSpider : BaseCreature + { + [Constructable] + public GiantSpider() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a giant spider"; + Body = 28; + BaseSoundID = 0x388; + + SetStr( 76, 100 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + SetMana( 0 ); + + SetDamage( 5, 13 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + + SetSkill( SkillName.Poisoning, 60.1, 80.0 ); + SetSkill( SkillName.MagicResist, 25.1, 40.0 ); + SetSkill( SkillName.Tactics, 35.1, 50.0 ); + SetSkill( SkillName.Wrestling, 50.1, 65.0 ); + + Fame = 600; + Karma = -600; + + VirtualArmor = 16; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 59.1; + + PackItem( new SpidersSilk( 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Arachnid; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override Poison HitPoison{ get{ return Poison.Regular; } } + + public GiantSpider( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs b/Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs new file mode 100644 index 000000000..33af58be2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanDrone.cs @@ -0,0 +1,80 @@ +using System; +using Server.Items; +using Server.Targeting; +using System.Collections; + +namespace Server.Mobiles +{ + [CorpseName( "a terathan drone corpse" )] + public class TerathanDrone : BaseCreature + { + [Constructable] + public TerathanDrone() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a terathan drone"; + Body = 71; + BaseSoundID = 594; + + SetStr( 36, 65 ); + SetDex( 96, 145 ); + SetInt( 21, 45 ); + + SetHits( 22, 39 ); + SetMana( 0 ); + + SetDamage( 6, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.Poisoning, 40.1, 60.0 ); + SetSkill( SkillName.MagicResist, 30.1, 45.0 ); + SetSkill( SkillName.Tactics, 30.1, 50.0 ); + SetSkill( SkillName.Wrestling, 40.1, 50.0 ); + + Fame = 2000; + Karma = -2000; + + VirtualArmor = 24; + + PackItem( new SpidersSilk( 2 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + // TODO: weapon? + } + + public override int Meat{ get{ return 4; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public TerathanDrone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 589 ) + BaseSoundID = 594; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs b/Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs new file mode 100644 index 000000000..8f572a60c --- /dev/null +++ b/Scripts/Mobiles/Monsters/Arachnid/Melee/TerathanWarrior.cs @@ -0,0 +1,75 @@ +using System; +using Server.Items; +using Server.Targeting; +using System.Collections; + +namespace Server.Mobiles +{ + [CorpseName( "a terathan warrior corpse" )] + public class TerathanWarrior : BaseCreature + { + [Constructable] + public TerathanWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a terathan warrior"; + Body = 70; + BaseSoundID = 589; + + SetStr( 166, 215 ); + SetDex( 96, 145 ); + SetInt( 41, 65 ); + + SetHits( 100, 129 ); + SetMana( 0 ); + + SetDamage( 7, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Poisoning, 60.1, 80.0 ); + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 30; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 4; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public TerathanWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/AirElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/AirElemental.cs new file mode 100644 index 000000000..805f6e3ab --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/AirElemental.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an air elemental corpse" )] + public class AirElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public AirElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an air elemental"; + Body = 13; + Hue = 0x4001; + BaseSoundID = 655; + + SetStr( 126, 155 ); + SetDex( 166, 185 ); + SetInt( 101, 125 ); + + SetHits( 76, 93 ); + + SetDamage( 8, 10 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 40 ); + SetDamageType( ResistanceType.Energy, 40 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.EvalInt, 60.1, 75.0 ); + SetSkill( SkillName.Magery, 60.1, 75.0 ); + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 60.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 40; + ControlSlots = 2; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.MedScrolls ); + } + + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 2; } } + + public AirElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 263 ) + BaseSoundID = 655; + } + } +} diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs new file mode 100644 index 000000000..16945fccf --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/BloodElemental.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a blood elemental corpse" )] + public class BloodElemental : BaseCreature + { + [Constructable] + public BloodElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a blood elemental"; + Body = 159; + BaseSoundID = 278; + + SetStr( 526, 615 ); + SetDex( 66, 85 ); + SetInt( 226, 350 ); + + SetHits( 316, 369 ); + + SetDamage( 17, 27 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Poison, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 85.1, 100.0 ); + SetSkill( SkillName.Magery, 85.1, 100.0 ); + SetSkill( SkillName.Meditation, 10.4, 50.0 ); + SetSkill( SkillName.MagicResist, 80.1, 95.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + Fame = 12500; + Karma = -12500; + + VirtualArmor = 60; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + } + + public override int TreasureMapLevel{ get{ return 5; } } + + public BloodElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/Efreet.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/Efreet.cs new file mode 100644 index 000000000..e7e54e357 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/Efreet.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an efreet corpse" )] + public class Efreet : BaseCreature + { + [Constructable] + public Efreet () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an efreet"; + Body = 131; + BaseSoundID = 768; + + SetStr( 326, 355 ); + SetDex( 266, 285 ); + SetInt( 171, 195 ); + + SetHits( 196, 213 ); + + SetDamage( 11, 13 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Fire, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 60.1, 75.0 ); + SetSkill( SkillName.Magery, 60.1, 75.0 ); + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 60.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 10000; + Karma = -10000; + + VirtualArmor = 56; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems ); + + if ( 0.02 > Utility.RandomDouble() ) + { + switch ( Utility.Random( 5 ) ) + { + case 0: PackItem( new DaemonArms() ); break; + case 1: PackItem( new DaemonChest() ); break; + case 2: PackItem( new DaemonGloves() ); break; + case 3: PackItem( new DaemonLegs() ); break; + case 4: PackItem( new DaemonHelm() ); break; + } + } + } + + public override int TreasureMapLevel{ get{ return Core.AOS ? 4 : 5; } } + + public Efreet( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/FireElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/FireElemental.cs new file mode 100644 index 000000000..433e10ecd --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/FireElemental.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a fire elemental corpse" )] + public class FireElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public FireElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a fire elemental"; + Body = 15; + BaseSoundID = 838; + + SetStr( 126, 155 ); + SetDex( 166, 185 ); + SetInt( 101, 125 ); + + SetHits( 76, 93 ); + + SetDamage( 7, 9 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Fire, 75 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 60, 80 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 60.1, 75.0 ); + SetSkill( SkillName.Magery, 60.1, 75.0 ); + SetSkill( SkillName.MagicResist, 75.2, 105.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 70.1, 100.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 40; + ControlSlots = 4; + + PackItem( new SulfurousAsh( 3 ) ); + + AddItem( new LightSource() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Gems ); + } + + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 2; } } + + public FireElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 274 ) + BaseSoundID = 838; + } + } +} diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/IceElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/IceElemental.cs new file mode 100644 index 000000000..6b9ed1b3f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/IceElemental.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ice elemental corpse" )] + public class IceElemental : BaseCreature + { + [Constructable] + public IceElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ice elemental"; + Body = 161; + BaseSoundID = 268; + + SetStr( 156, 185 ); + SetDex( 96, 115 ); + SetInt( 171, 192 ); + + SetHits( 94, 111 ); + + SetDamage( 10, 21 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Cold, 75 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.EvalInt, 10.5, 60.0 ); + SetSkill( SkillName.Magery, 10.5, 60.0 ); + SetSkill( SkillName.MagicResist, 30.1, 80.0 ); + SetSkill( SkillName.Tactics, 70.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 40; + + PackItem( new BlackPearl() ); + PackReg( 3 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.Gems, 2 ); + } + public override bool BleedImmune{ get{ return true; } } + + public IceElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs new file mode 100644 index 000000000..78aea1a63 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/PoisonElemental.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a poison elementals corpse" )] + public class PoisonElemental : BaseCreature + { + [Constructable] + public PoisonElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a poison elemental"; + Body = 162; + BaseSoundID = 263; + + SetStr( 426, 515 ); + SetDex( 166, 185 ); + SetInt( 361, 435 ); + + SetHits( 256, 309 ); + + SetDamage( 12, 18 ); + + SetDamageType( ResistanceType.Physical, 10 ); + SetDamageType( ResistanceType.Poison, 90 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 80.1, 95.0 ); + SetSkill( SkillName.Magery, 80.1, 95.0 ); + SetSkill( SkillName.Meditation, 80.2, 120.0 ); + SetSkill( SkillName.Poisoning, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 85.2, 115.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 70.1, 90.0 ); + + Fame = 12500; + Karma = -12500; + + VirtualArmor = 70; + + PackItem( new Nightshade( 4 ) ); + PackItem( new LesserPoisonPotion() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override double HitPoisonChance{ get{ return 0.75; } } + + public override int TreasureMapLevel{ get{ return 5; } } + + public PoisonElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/ToxicElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/ToxicElemental.cs new file mode 100644 index 000000000..a9d73b6b6 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/ToxicElemental.cs @@ -0,0 +1,85 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an acid elemental corpse" )] + public class ToxicElemental : BaseCreature + { + [Constructable] + public ToxicElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an acid elemental"; + Body = 0x9E; + BaseSoundID = 278; + + SetStr( 326, 355 ); + SetDex( 66, 85 ); + SetInt( 271, 295 ); + + SetHits( 196, 213 ); + + SetDamage( 9, 15 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Fire, 50 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.Anatomy, 30.3, 60.0 ); + SetSkill( SkillName.EvalInt, 70.1, 85.0 ); + SetSkill( SkillName.Magery, 70.1, 85.0 ); + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 80.1, 90.0 ); + SetSkill( SkillName.Wrestling, 70.1, 90.0 ); + + Fame = 10000; + Karma = -10000; + + VirtualArmor = 40; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override double HitPoisonChance{ get{ return 0.6; } } + + public override int TreasureMapLevel{ get{ return Core.AOS ? 2 : 3; } } + + public ToxicElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 263 ) + BaseSoundID = 278; + + if ( Body == 13 ) + Body = 0x9E; + + if ( Hue == 0x4001 ) + Hue = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs new file mode 100644 index 000000000..98c248eaf --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Magic/WaterElemental.cs @@ -0,0 +1,78 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a water elemental corpse" )] + public class WaterElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public WaterElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a water elemental"; + Body = 16; + BaseSoundID = 278; + + SetStr( 126, 155 ); + SetDex( 66, 85 ); + SetInt( 101, 125 ); + + SetHits( 76, 93 ); + + SetDamage( 7, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 10, 25 ); + SetResistance( ResistanceType.Cold, 10, 25 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.EvalInt, 60.1, 75.0 ); + SetSkill( SkillName.Magery, 60.1, 75.0 ); + SetSkill( SkillName.MagicResist, 100.1, 115.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 40; + ControlSlots = 3; + CanSwim = true; + + PackItem( new BlackPearl( 3 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Potions ); + } + + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 2; } } + + public WaterElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs new file mode 100644 index 000000000..44ff5ac6c --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Melee/EarthElemental.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an earth elemental corpse" )] + public class EarthElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public EarthElemental() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an earth elemental"; + Body = 14; + BaseSoundID = 268; + + SetStr( 126, 155 ); + SetDex( 66, 85 ); + SetInt( 71, 92 ); + + SetHits( 76, 93 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 34; + ControlSlots = 2; + + PackItem( new FertileDirt( Utility.RandomMinMax( 1, 4 ) ) ); + PackItem( new IronOre( 3 ) ); // TODO: Five small iron ore + PackItem( new MandrakeRoot() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Gems ); + } + + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public EarthElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs b/Scripts/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs new file mode 100644 index 000000000..7dd058ef3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Elemental/Melee/SnowElemental.cs @@ -0,0 +1,71 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a snow elemental corpse" )] + public class SnowElemental : BaseCreature + { + [Constructable] + public SnowElemental() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a snow elemental"; + Body = 163; + BaseSoundID = 263; + + SetStr( 326, 355 ); + SetDex( 166, 185 ); + SetInt( 71, 95 ); + + SetHits( 196, 213 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 80 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 50.1, 65.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + Fame = 5000; + Karma = -5000; + + VirtualArmor = 50; + + PackItem( new IronOre( 3 ) ); + PackItem( new BlackPearl( 3 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override bool BleedImmune{ get{ return true; } } + + public override int TreasureMapLevel{ get{ return Utility.RandomList( 2, 3 ); } } + + public SnowElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs new file mode 100644 index 000000000..3cd6b2b55 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/AncientLich.cs @@ -0,0 +1,108 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ancient liche's corpse" )] + public class AncientLich : BaseCreature + { + [Constructable] + public AncientLich() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "ancient lich" ); + Body = 78; + BaseSoundID = 412; + + SetStr( 216, 305 ); + SetDex( 96, 115 ); + SetInt( 966, 1045 ); + + SetHits( 560, 595 ); + + SetDamage( 15, 27 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 40 ); + SetDamageType( ResistanceType.Energy, 40 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.EvalInt, 120.1, 130.0 ); + SetSkill( SkillName.Magery, 120.1, 130.0 ); + SetSkill( SkillName.Meditation, 100.1, 101.0 ); + SetSkill( SkillName.Poisoning, 100.1, 101.0 ); + SetSkill( SkillName.MagicResist, 175.2, 200.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 75.1, 100.0 ); + + Fame = 23000; + Karma = -23000; + + VirtualArmor = 60; + PackNecroReg( 30, 275 ); + + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override int GetIdleSound() + { + return 0x19D; + } + + public override int GetAngerSound() + { + return 0x175; + } + + public override int GetDeathSound() + { + return 0x108; + } + + public override int GetAttackSound() + { + return 0xE2; + } + + public override int GetHurtSound() + { + return 0x28B; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 3 ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool Unprovokable{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public AncientLich( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs new file mode 100644 index 000000000..57cbf1ff1 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/ArcaneDaemon.cs @@ -0,0 +1,74 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an arcane daemon corpse" )] + public class ArcaneDaemon : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.ConcussionBlow; + } + + [Constructable] + public ArcaneDaemon() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an arcane daemon"; + Body = 0x310; + BaseSoundID = 0x47D; + + SetStr( 131, 150 ); + SetDex( 126, 145 ); + SetInt( 301, 350 ); + + SetHits( 101, 115 ); + + SetDamage( 12, 16 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Fire, 20 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 85.1, 95.0 ); + SetSkill( SkillName.Tactics, 70.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + SetSkill( SkillName.Magery, 80.1, 90.0 ); + SetSkill( SkillName.EvalInt, 70.1, 80.0 ); + SetSkill( SkillName.Meditation, 70.1, 80.0 ); + + Fame = 7000; + Karma = -10000; + + VirtualArmor = 55; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + + public ArcaneDaemon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Balron.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Balron.cs new file mode 100644 index 000000000..88064975e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Balron.cs @@ -0,0 +1,79 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a balron corpse" )] + public class Balron : BaseCreature + { + [Constructable] + public Balron () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "balron" ); + Body = 40; + BaseSoundID = 357; + + SetStr( 986, 1185 ); + SetDex( 177, 255 ); + SetInt( 151, 250 ); + + SetHits( 592, 711 ); + + SetDamage( 22, 29 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Fire, 25 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 65, 80 ); + SetResistance( ResistanceType.Fire, 60, 80 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Anatomy, 25.1, 50.0 ); + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 95.5, 100.0 ); + SetSkill( SkillName.Meditation, 25.1, 50.0 ); + SetSkill( SkillName.MagicResist, 100.5, 150.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 90; + + PackItem( new Longsword() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + public override int TreasureMapLevel{ get{ return 5; } } + public override int Meat{ get{ return 1; } } + + public Balron( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs new file mode 100644 index 000000000..703693920 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Betrayer.cs @@ -0,0 +1,169 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a betrayer corpse" )] + public class Betrayer : BaseCreature + { + private bool m_Stunning; + + [Constructable] + public Betrayer() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a betrayer"; + Body = 767; + + + SetStr( 401, 500 ); + SetDex( 81, 100 ); + SetInt( 151, 200 ); + + SetHits( 241, 300 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.Anatomy, 90.1, 100.0 ); + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 50.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 120.1, 130.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 65; + SpeechHue = Utility.RandomDyedHue(); + + PackItem( new PowerCrystal() ); + + if ( 0.02 > Utility.RandomDouble() ) + PackItem( new BlackthornWelcomeBook() ); + + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 5, 30 ) ); + } + + public override int GetDeathSound() + { + return 0x423; + } + + public override int GetAttackSound() + { + return 0x23B; + } + + public override int GetHurtSound() + { + return 0x140; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 1 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( !m_Stunning && 0.3 > Utility.RandomDouble() ) + { + m_Stunning = true; + + defender.Animate( 21, 6, 1, true, false, 0 ); + this.PlaySound( 0xEE ); + defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You have been stunned by a colossal blow!" ); + + BaseWeapon weapon = this.Weapon as BaseWeapon; + if ( weapon != null ) + weapon.OnHit( this, defender ); + + if ( defender.Alive ) + { + defender.Frozen = true; + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( Recover_Callback ), defender ); + } + } + } + + private void Recover_Callback( object state ) + { + Mobile defender = state as Mobile; + + if ( defender != null ) + { + defender.Frozen = false; + defender.Combatant = null; + defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You recover your senses." ); + } + + m_Stunning = false; + } + + private DateTime m_NextAbilityTime; + + public override void OnActionCombat() + { + Mobile combatant = Combatant; + + if ( DateTime.Now < m_NextAbilityTime || combatant == null || combatant.Deleted || combatant.Map != Map || !InRange( combatant, 3 ) || !CanBeHarmful( combatant ) || !InLOS( combatant ) ) + return; + + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 5, 30 ) ); + + if ( Utility.RandomBool() ) + { + this.FixedParticles( 0x376A, 9, 32, 0x2539, EffectLayer.LeftHand ); + this.PlaySound( 0x1DE ); + + foreach ( Mobile m in this.GetMobilesInRange( 2 ) ) + { + if ( m != this && IsEnemy( m ) ) + { + m.ApplyPoison( this, Poison.Deadly ); + } + } + } + } + + public Betrayer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Bogle.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Bogle.cs new file mode 100644 index 000000000..b3e6f6d26 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Bogle.cs @@ -0,0 +1,68 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a ghostly corpse" )] + public class Bogle : BaseCreature + { + [Constructable] + public Bogle() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a bogle"; + Body = 153; + BaseSoundID = 0x482; + + SetStr( 76, 100 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + + SetDamage( 7, 11 ); + + SetSkill( SkillName.EvalInt, 55.1, 70.0 ); + SetSkill( SkillName.Magery, 55.1, 70.0 ); + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 28; + PackItem( Loot.RandomWeapon() ); + PackItem( new Bone() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public Bogle( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs new file mode 100644 index 000000000..54abf6ceb --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/BoneMagi.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a skeletal corpse" )] + public class BoneMagi : BaseCreature + { + [Constructable] + public BoneMagi() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a bone magi"; + Body = 148; + BaseSoundID = 451; + + SetStr( 76, 100 ); + SetDex( 56, 75 ); + SetInt( 186, 210 ); + + SetHits( 46, 60 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 60.1, 70.0 ); + SetSkill( SkillName.Magery, 60.1, 70.0 ); + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 38; + + PackReg( 3 ); + PackNecroReg( 3, 10 ); + PackItem( new Bone() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.Potions ); + } + + public override bool BleedImmune{ get{ return true; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + + public BoneMagi( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Daemon.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Daemon.cs new file mode 100644 index 000000000..6d7eae1d5 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Daemon.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; +using Server.Factions; + +namespace Server.Mobiles +{ + [CorpseName( "a daemon corpse" )] + public class Daemon : BaseCreature + { + public override double DispelDifficulty{ get{ return 125.0; } } + public override double DispelFocus{ get{ return 45.0; } } + + public override Faction FactionAllegiance { get { return Shadowlords.Instance; } } + public override Ethics.Ethic EthicAllegiance { get { return Ethics.Ethic.Evil; } } + + [Constructable] + public Daemon () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "daemon" ); + Body = 9; + BaseSoundID = 357; + + SetStr( 476, 505 ); + SetDex( 76, 95 ); + SetInt( 301, 325 ); + + SetHits( 286, 303 ); + + SetDamage( 7, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 60 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 70.1, 80.0 ); + SetSkill( SkillName.Magery, 70.1, 80.0 ); + SetSkill( SkillName.MagicResist, 85.1, 95.0 ); + SetSkill( SkillName.Tactics, 70.1, 80.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 58; + ControlSlots = Core.SE ? 4 : 5; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override int TreasureMapLevel{ get{ return 4; } } + public override int Meat{ get{ return 1; } } + + public Daemon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs new file mode 100644 index 000000000..2c230177c --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/ElderGazer.cs @@ -0,0 +1,70 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an elder gazer corpse" )] + public class ElderGazer : BaseCreature + { + [Constructable] + public ElderGazer () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an elder gazer"; + Body = 22; + BaseSoundID = 377; + + SetStr( 296, 325 ); + SetDex( 86, 105 ); + SetInt( 291, 385 ); + + SetHits( 178, 195 ); + + SetDamage( 8, 19 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Anatomy, 62.0, 100.0 ); + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 115.1, 130.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + Fame = 12500; + Karma = -12500; + + VirtualArmor = 50; + } + + public override int TreasureMapLevel{ get{ return Core.AOS ? 4 : 0; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + } + + public ElderGazer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs new file mode 100644 index 000000000..a9ce61ce7 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMage.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an evil mage corpse" )] + public class EvilMage : BaseCreature + { + [Constructable] + public EvilMage() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "evil mage" ); + Title = "the evil mage"; + Body = 124; + + SetStr( 81, 105 ); + SetDex( 91, 115 ); + SetInt( 96, 120 ); + + SetHits( 49, 63 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.EvalInt, 75.1, 100.0 ); + SetSkill( SkillName.Magery, 75.1, 100.0 ); + SetSkill( SkillName.MagicResist, 75.0, 97.5 ); + SetSkill( SkillName.Tactics, 65.0, 87.5 ); + SetSkill( SkillName.Wrestling, 20.2, 60.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 16; + PackReg( 6 ); + PackItem( new Robe( Utility.RandomNeutralHue() ) ); // TODO: Proper hue + PackItem( new Sandals() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.MedScrolls ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override bool AlwaysMurderer{ get{ return true; } } + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return Core.AOS ? 1 : 0; } } + + public EvilMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs new file mode 100644 index 000000000..1f7c63323 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/EvilMageLord.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an evil mage lord corpse" )] + public class EvilMageLord : BaseCreature + { + [Constructable] + public EvilMageLord() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "evil mage lord" ); + Body = Utility.RandomList( 125, 126 ); + + PackItem( new Robe( Utility.RandomMetalHue() ) ); + PackItem( new WizardsHat( Utility.RandomMetalHue() ) ); + + SetStr( 81, 105 ); + SetDex( 191, 215 ); + SetInt( 126, 150 ); + + SetHits( 49, 63 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 80.2, 100.0 ); + SetSkill( SkillName.Magery, 95.1, 100.0 ); + SetSkill( SkillName.Meditation, 27.5, 50.0 ); + SetSkill( SkillName.MagicResist, 77.5, 100.0 ); + SetSkill( SkillName.Tactics, 65.0, 87.5 ); + SetSkill( SkillName.Wrestling, 20.3, 80.0 ); + + Fame = 10500; + Karma = -10500; + + VirtualArmor = 16; + PackReg( 23 ); + if ( Utility.RandomBool() ) + PackItem( new Shoes() ); + else + PackItem( new Sandals() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override bool AlwaysMurderer{ get{ return true; } } + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return Core.AOS ? 2 : 0; } } + + public EvilMageLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs new file mode 100644 index 000000000..516761139 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/FireGargoyle.cs @@ -0,0 +1,73 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a charred corpse" )] + public class FireGargoyle : BaseCreature + { + [Constructable] + public FireGargoyle() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "fire gargoyle" ); + Body = 130; + BaseSoundID = 0x174; + + SetStr( 351, 400 ); + SetDex( 126, 145 ); + SetInt( 226, 250 ); + + SetHits( 211, 240 ); + + SetDamage( 7, 14 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Fire, 80 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.Anatomy, 75.1, 85.0 ); + SetSkill( SkillName.EvalInt, 90.1, 105.0 ); + SetSkill( SkillName.Magery, 90.1, 105.0 ); + SetSkill( SkillName.Meditation, 90.1, 105.0 ); + SetSkill( SkillName.MagicResist, 90.1, 105.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 40.1, 80.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 32; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems ); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 1; } } + + public FireGargoyle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs new file mode 100644 index 000000000..e7e4f03af --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Gargoyle.cs @@ -0,0 +1,73 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gargoyle corpse" )] + public class Gargoyle : BaseCreature + { + [Constructable] + public Gargoyle() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a gargoyle"; + Body = 4; + BaseSoundID = 372; + + SetStr( 146, 175 ); + SetDex( 76, 95 ); + SetInt( 81, 105 ); + + SetHits( 88, 105 ); + + SetDamage( 7, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + + SetSkill( SkillName.EvalInt, 70.1, 85.0 ); + SetSkill( SkillName.Magery, 70.1, 85.0 ); + SetSkill( SkillName.MagicResist, 70.1, 85.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 40.1, 80.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 32; + + if ( 0.025 > Utility.RandomDouble() ) + PackItem( new GargoylesPickaxe() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.MedScrolls ); + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 4 ) ); + } + + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 1; } } + + public Gargoyle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs new file mode 100644 index 000000000..eeeb0b0bb --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleDestroyer.cs @@ -0,0 +1,103 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gargoyle corpse" )] + public class GargoyleDestroyer : BaseCreature + { + [Constructable] + public GargoyleDestroyer() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "Gargoyle Destroyer"; + Body = 0x2F3; + BaseSoundID = 0x174; + + SetStr( 760, 850 ); + SetDex( 102, 150 ); + SetInt( 152, 200 ); + + SetHits( 482, 485 ); + + SetDamage( 7, 14 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 120.4, 160.0 ); + SetSkill( SkillName.Anatomy, 50.5, 100.0 ); + SetSkill( SkillName.Swords, 90.1, 100.0 ); + SetSkill( SkillName.Macing, 90.1, 100.0 ); + SetSkill( SkillName.Fencing, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + + Fame = 10000; + Karma = -10000; + + VirtualArmor = 50; + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new GargoylesPickaxe() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool BardImmune{ get{ return !Core.AOS; } } + public override int Meat{ get{ return 1; } } + + public override void OnDamagedBySpell( Mobile from ) + { + if( from != null && from.Alive && 0.4 > Utility.RandomDouble() ) + { + ThrowHatchet( from ); + } + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if( attacker != null && attacker.Alive && attacker.Weapon is BaseRanged && 0.4 > Utility.RandomDouble() ) + { + ThrowHatchet( attacker ); + } + } + + public void ThrowHatchet( Mobile to ) + { + int damage = 50; + this.MovingEffect( to, 0xF43, 10, 0, false, false ); + this.DoHarmful( to ); + AOS.Damage( to, this, damage, 100, 0, 0, 0, 0 ); + } + + public GargoyleDestroyer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs new file mode 100644 index 000000000..2e2cb4861 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/GargoyleEnforcer.cs @@ -0,0 +1,77 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gargoyle corpse" )] + public class GargoyleEnforcer : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.WhirlwindAttack; + } + + [Constructable] + public GargoyleEnforcer() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "Gargoyle Enforcer"; + Body = 0x2F2; + BaseSoundID = 0x174; + + SetStr( 760, 850 ); + SetDex( 102, 150 ); + SetInt( 152, 200 ); + + SetHits( 482, 485 ); + + SetDamage( 7, 14 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.MagicResist, 120.1, 130.0 ); + SetSkill( SkillName.Tactics, 70.1, 80.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + SetSkill( SkillName.Swords, 80.1, 90.0 ); + SetSkill( SkillName.Anatomy, 70.1, 80.0 ); + SetSkill( SkillName.Magery, 80.1, 90.0 ); + SetSkill( SkillName.EvalInt, 70.3, 100.0 ); + SetSkill( SkillName.Meditation, 70.3, 100.0 ); + + Fame = 5000; + Karma = -5000; + + VirtualArmor = 50; + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new GargoylesPickaxe() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls ); + } + + public override int Meat{ get{ return 1; } } + + public GargoyleEnforcer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Gazer.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Gazer.cs new file mode 100644 index 000000000..dee78b84d --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Gazer.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gazer corpse" )] + public class Gazer : BaseCreature + { + [Constructable] + public Gazer () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a gazer"; + Body = 22; + BaseSoundID = 377; + + SetStr( 96, 125 ); + SetDex( 86, 105 ); + SetInt( 141, 165 ); + + SetHits( 58, 75 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.EvalInt, 50.1, 65.0 ); + SetSkill( SkillName.Magery, 50.1, 65.0 ); + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 36; + + PackItem( new Nightshade( 4 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Potions ); + } + + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 1; } } + + public Gazer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/GolemController.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/GolemController.cs new file mode 100644 index 000000000..ad729aed8 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/GolemController.cs @@ -0,0 +1,95 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a golem controller corpse" )] + public class GolemController : BaseCreature + { + [Constructable] + public GolemController() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "golem controller" ); + Title = "the controller"; + + Body = 400; + Hue = 0x455; + + AddArcane( new Robe() ); + AddArcane( new ThighBoots() ); + AddArcane( new LeatherGloves() ); + AddArcane( new Cloak() ); + + SetStr( 126, 150 ); + SetDex( 96, 120 ); + SetInt( 151, 175 ); + + SetHits( 76, 90 ); + + SetDamage( 6, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 5, 15 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.EvalInt, 95.1, 100.0 ); + SetSkill( SkillName.Magery, 95.1, 100.0 ); + SetSkill( SkillName.Meditation, 95.1, 100.0 ); + SetSkill( SkillName.MagicResist, 102.5, 125.0 ); + SetSkill( SkillName.Tactics, 65.0, 87.5 ); + SetSkill( SkillName.Wrestling, 65.0, 87.5 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 16; + + if ( 0.7 > Utility.RandomDouble() ) + PackItem( new ArcaneGem() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public void AddArcane( Item item ) + { + if ( item is IArcaneEquip ) + { + IArcaneEquip eq = (IArcaneEquip)item; + eq.CurArcaneCharges = eq.MaxArcaneCharges = 20; + } + + item.Hue = ArcaneGem.DefaultArcaneHue; + item.LootType = LootType.Newbied; + + AddItem( item ); + } + + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + public override bool AlwaysMurderer{ get{ return true; } } + + public GolemController( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs new file mode 100644 index 000000000..98a30f7c8 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/IceFiend.cs @@ -0,0 +1,69 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ice fiend corpse" )] + public class IceFiend : BaseCreature + { + [Constructable] + public IceFiend () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ice fiend"; + Body = 43; + BaseSoundID = 357; + + SetStr( 376, 405 ); + SetDex( 176, 195 ); + SetInt( 201, 225 ); + + SetHits( 226, 243 ); + + SetDamage( 8, 19 ); + + SetSkill( SkillName.EvalInt, 80.1, 90.0 ); + SetSkill( SkillName.Magery, 80.1, 90.0 ); + SetSkill( SkillName.MagicResist, 75.1, 85.0 ); + SetSkill( SkillName.Tactics, 80.1, 90.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + Fame = 18000; + Karma = -18000; + + VirtualArmor = 60; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override int TreasureMapLevel{ get{ return 4; } } + public override int Meat{ get{ return 1; } } + + public IceFiend( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Imp.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Imp.cs new file mode 100644 index 000000000..3ae8aa2ee --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Imp.cs @@ -0,0 +1,79 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an imp corpse" )] + public class Imp : BaseCreature + { + [Constructable] + public Imp() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an imp"; + Body = 74; + BaseSoundID = 422; + + SetStr( 91, 115 ); + SetDex( 61, 80 ); + SetInt( 86, 105 ); + + SetHits( 55, 70 ); + + SetDamage( 10, 14 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Fire, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 20.1, 30.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 30.1, 50.0 ); + SetSkill( SkillName.Tactics, 42.1, 50.0 ); + SetSkill( SkillName.Wrestling, 40.1, 44.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 30; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 83.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 6; } } + public override HideType HideType{ get{ return HideType.Spined; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Daemon; } } + + public Imp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Lich.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Lich.cs new file mode 100644 index 000000000..845921607 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Lich.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a liche's corpse" )] + public class Lich : BaseCreature + { + [Constructable] + public Lich() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a lich"; + Body = 24; + BaseSoundID = 0x3E9; + + SetStr( 171, 200 ); + SetDex( 126, 145 ); + SetInt( 276, 305 ); + + SetHits( 103, 120 ); + + SetDamage( 24, 26 ); + + SetDamageType( ResistanceType.Physical, 10 ); + SetDamageType( ResistanceType.Cold, 40 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 55, 65 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 100.0 ); + SetSkill( SkillName.Magery, 70.1, 80.0 ); + SetSkill( SkillName.Meditation, 85.1, 95.0 ); + SetSkill( SkillName.MagicResist, 80.1, 100.0 ); + SetSkill( SkillName.Tactics, 70.1, 90.0 ); + + Fame = 8000; + Karma = -8000; + + VirtualArmor = 50; + PackItem( new GnarledStaff() ); + PackNecroReg( 17, 24 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 3; } } + + public Lich( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/LichLord.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/LichLord.cs new file mode 100644 index 000000000..69a1ba76b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/LichLord.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a liche's corpse" )] + public class LichLord : BaseCreature + { + [Constructable] + public LichLord() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a lich lord"; + Body = 79; + BaseSoundID = 412; + + SetStr( 416, 505 ); + SetDex( 146, 165 ); + SetInt( 566, 655 ); + + SetHits( 250, 303 ); + + SetDamage( 11, 13 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Cold, 60 ); + SetDamageType( ResistanceType.Energy, 40 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 150.5, 200.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 18000; + Karma = -18000; + + VirtualArmor = 50; + PackItem( new GnarledStaff() ); + PackNecroReg( 12, 40 ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 4; } } + + public LichLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs new file mode 100644 index 000000000..08aa8cd9a --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/OrcishMage.cs @@ -0,0 +1,108 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a glowing orc corpse" )] + public class OrcishMage : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Orc; } } + + [Constructable] + public OrcishMage () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an orcish mage"; + Body = 140; + BaseSoundID = 0x45A; + + SetStr( 116, 150 ); + SetDex( 91, 115 ); + SetInt( 161, 185 ); + + SetHits( 70, 90 ); + + SetDamage( 4, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 60.1, 72.5 ); + SetSkill( SkillName.Magery, 60.1, 72.5 ); + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 50.1, 65.0 ); + SetSkill( SkillName.Wrestling, 40.1, 50.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 30; + + + PackReg( 6 ); + + if ( 0.05 > Utility.RandomDouble() ) + PackItem( new OrcishKinMask() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.LowScrolls ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 1; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player && m.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + Item item = aggressor.FindItemOnLayer( Layer.Helm ); + + if ( item is OrcishKinMask ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + item.Delete(); + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + } + } + + public OrcishMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs new file mode 100644 index 000000000..bf7ec9cf5 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/RatmanMage.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a glowing ratman corpse" )] + public class RatmanMage : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Ratman; } } + + [Constructable] + public RatmanMage() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "ratman" ); + Body = 0x8F; + BaseSoundID = 437; + + SetStr( 146, 180 ); + SetDex( 101, 130 ); + SetInt( 186, 210 ); + + SetHits( 88, 108 ); + + SetDamage( 7, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 45 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.EvalInt, 70.1, 80.0 ); + SetSkill( SkillName.Magery, 70.1, 80.0 ); + SetSkill( SkillName.MagicResist, 65.1, 90.0 ); + SetSkill( SkillName.Tactics, 50.1, 75.0 ); + SetSkill( SkillName.Wrestling, 50.1, 75.0 ); + + Fame = 7500; + Karma = -7500; + + VirtualArmor = 44; + + PackReg( 6 ); + + if ( 0.02 > Utility.RandomDouble() ) + PackStatue(); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.LowScrolls ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 8; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public RatmanMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Body == 42 ) + { + Body = 0x8F; + Hue = 0; + } + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs new file mode 100644 index 000000000..338999037 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Spells; + +namespace Server.Mobiles +{ + [CorpseName( "a savage corpse" )] + public class SavageShaman : BaseCreature + { + [Constructable] + public SavageShaman() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "savage shaman" ); + + if ( Utility.RandomBool() ) + Body = 184; + else + Body = 183; + + SetStr( 126, 145 ); + SetDex( 91, 110 ); + SetInt( 161, 185 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 77.5, 100.0 ); + SetSkill( SkillName.Fencing, 62.5, 85.0 ); + SetSkill( SkillName.Macing, 62.5, 85.0 ); + SetSkill( SkillName.Magery, 72.5, 95.0 ); + SetSkill( SkillName.Meditation, 77.5, 100.0 ); + SetSkill( SkillName.MagicResist, 77.5, 100.0 ); + SetSkill( SkillName.Swords, 62.5, 85.0 ); + SetSkill( SkillName.Tactics, 62.5, 85.0 ); + SetSkill( SkillName.Wrestling, 62.5, 85.0 ); + + Fame = 1000; + Karma = -1000; + + PackReg( 10, 15 ); + PackItem( new Bandage( Utility.RandomMinMax( 1, 15 ) ) ); + + if ( 0.1 > Utility.RandomDouble() ) + PackItem( new TribalBerry() ); + + AddItem( new BoneArms() ); + AddItem( new BoneLegs() ); + AddItem( new DeerMask() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + + } + + public override int Meat{ get{ return 1; } } + public override bool AlwaysMurderer{ get{ return true; } } + public override bool ShowFameTitle{ get{ return false; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.BodyMod == 183 || m.BodyMod == 184 ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + if ( aggressor.BodyMod == 183 || aggressor.BodyMod == 184 ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + aggressor.BodyMod = 0; + aggressor.HueMod = -1; + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + aggressor.SendLocalizedMessage( 1040008 ); // Your skin is scorched as the tribal paint burns away! + + if ( aggressor is PlayerMobile ) + ((PlayerMobile)aggressor).SavagePaintExpiration = TimeSpan.Zero; + } + } + + public override void AlterMeleeDamageTo( Mobile to, ref int damage ) + { + if ( to is Dragon || to is WhiteWyrm || to is SwampDragon || to is Drake || to is Nightmare || to is Hiryu || to is LesserHiryu || to is Daemon ) + damage *= 3; + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( 0.1 > Utility.RandomDouble() ) + BeginSavageDance(); + } + + public void BeginSavageDance() + { + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 8 ) ) + { + if ( m != this && m is SavageShaman ) + list.Add( m ); + } + + Animate( 111, 5, 1, true, false, 0 ); // Do a little dance... + + if ( AIObject != null ) + AIObject.NextMove = DateTime.Now + TimeSpan.FromSeconds( 1.0 ); + + if ( list.Count >= 3 ) + { + for ( int i = 0; i < list.Count; ++i ) + { + SavageShaman dancer = (SavageShaman)list[i]; + + dancer.Animate( 111, 5, 1, true, false, 0 ); // Get down tonight... + + if ( dancer.AIObject != null ) + dancer.AIObject.NextMove = DateTime.Now + TimeSpan.FromSeconds( 1.0 ); + } + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerCallback( EndSavageDance ) ); + } + } + + public void EndSavageDance() + { + if ( Deleted ) + return; + + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 8 ) ) + list.Add( m ); + + if ( list.Count > 0 ) + { + switch ( Utility.Random( 3 ) ) + { + case 0: /* greater heal */ + { + foreach ( Mobile m in list ) + { + bool isFriendly = ( m is Savage || m is SavageRider || m is SavageShaman || m is SavageRidgeback ); + + if ( !isFriendly ) + continue; + + if ( m.Poisoned || MortalStrike.IsWounded( m ) || !CanBeBeneficial( m ) ) + continue; + + DoBeneficial( m ); + + // Algorithm: (40% of magery) + (1-10) + + int toHeal = (int)(Skills[SkillName.Magery].Value * 0.4); + toHeal += Utility.Random( 1, 10 ); + + m.Heal( toHeal ); + + m.FixedParticles( 0x376A, 9, 32, 5030, EffectLayer.Waist ); + m.PlaySound( 0x202 ); + } + + break; + } + case 1: /* lightning */ + { + foreach ( Mobile m in list ) + { + bool isFriendly = ( m is Savage || m is SavageRider || m is SavageShaman || m is SavageRidgeback ); + + if ( isFriendly ) + continue; + + if ( !CanBeHarmful( m ) ) + continue; + + DoHarmful( m ); + + double damage; + + if ( Core.AOS ) + { + int baseDamage = 6 + (int)(Skills[SkillName.EvalInt].Value / 5.0); + + damage = Utility.RandomMinMax( baseDamage, baseDamage + 3 ); + } + else + { + damage = Utility.Random( 12, 9 ); + } + + m.BoltEffect( 0 ); + + SpellHelper.Damage( TimeSpan.FromSeconds( 0.25 ), m, this, damage, 0, 0, 0, 0, 100 ); + } + + break; + } + case 2: /* poison */ + { + foreach ( Mobile m in list ) + { + bool isFriendly = ( m is Savage || m is SavageRider || m is SavageShaman || m is SavageRidgeback ); + + if ( isFriendly ) + continue; + + if ( !CanBeHarmful( m ) ) + continue; + + DoHarmful( m ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + double total = Skills[SkillName.Magery].Value + Skills[SkillName.Poisoning].Value; + + double dist = GetDistanceToSqrt( m ); + + if ( dist >= 3.0 ) + total -= (dist - 3.0) * 10.0; + + int level; + + if ( total >= 200.0 && Utility.Random( 1, 100 ) <= 10 ) + level = 3; + else if ( total > 170.0 ) + level = 2; + else if ( total > 130.0 ) + level = 1; + else + level = 0; + + m.ApplyPoison( this, Poison.GetPoison( level ) ); + + m.FixedParticles( 0x374A, 10, 15, 5021, EffectLayer.Waist ); + m.PlaySound( 0x474 ); + } + + break; + } + } + } + } + + public SavageShaman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Shade.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Shade.cs new file mode 100644 index 000000000..4d9754a90 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Shade.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a ghostly corpse" )] + public class Shade : BaseCreature + { + [Constructable] + public Shade() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a shade"; + Body = 26; + Hue = 0x4001; + BaseSoundID = 0x482; + + SetStr( 76, 100 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + + SetDamage( 7, 11 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + + SetSkill( SkillName.EvalInt, 55.1, 70.0 ); + SetSkill( SkillName.Magery, 55.1, 70.0 ); + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 28; + + PackReg( 10 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public Shade( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs new file mode 100644 index 000000000..2ae41135b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/SkeletalMage.cs @@ -0,0 +1,80 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a skeletal corpse" )] + public class SkeletalMage : BaseCreature + { + [Constructable] + public SkeletalMage() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a skeletal mage"; + Body = 148; + BaseSoundID = 451; + + SetStr( 76, 100 ); + SetDex( 56, 75 ); + SetInt( 186, 210 ); + + SetHits( 46, 60 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 60.1, 70.0 ); + SetSkill( SkillName.Magery, 60.1, 70.0 ); + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 38; + PackReg( 3 ); + PackNecroReg( 3, 10 ); + PackItem( new Bone() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.Potions ); + } + + public override bool BleedImmune{ get{ return true; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + + public SkeletalMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Spectre.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Spectre.cs new file mode 100644 index 000000000..e1b267856 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Spectre.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a ghostly corpse" )] + public class Spectre : BaseCreature + { + [Constructable] + public Spectre() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a spectre"; + Body = 26; + Hue = 0x4001; + BaseSoundID = 0x482; + + SetStr( 76, 100 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + + SetDamage( 7, 11 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + + SetSkill( SkillName.EvalInt, 55.1, 70.0 ); + SetSkill( SkillName.Magery, 55.1, 70.0 ); + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 28; + + PackReg( 10 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public Spectre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Succubus.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Succubus.cs new file mode 100644 index 000000000..0a05f0424 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Succubus.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a succubus corpse" )] + public class Succubus : BaseCreature + { + [Constructable] + public Succubus () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a succubus"; + Body = 149; + BaseSoundID = 0x4B0; + + SetStr( 488, 620 ); + SetDex( 121, 170 ); + SetInt( 498, 657 ); + + SetHits( 312, 353 ); + + SetDamage( 18, 28 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 80, 90 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 50, 60 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 99.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 100.5, 150.0 ); + SetSkill( SkillName.Tactics, 80.1, 90.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 80; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public void DrainLife() + { + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 2 ) ) + { + if ( m == this || !CanBeHarmful( m ) ) + continue; + + if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != this.Team) ) + list.Add( m ); + else if ( m.Player ) + list.Add( m ); + } + + foreach ( Mobile m in list ) + { + DoHarmful( m ); + + m.FixedParticles( 0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist ); + m.PlaySound( 0x231 ); + + m.SendMessage( "You feel the life drain out of you!" ); + + int toDrain = Utility.RandomMinMax( 10, 40 ); + + Hits += toDrain; + m.Damage( toDrain, this ); + } + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 >= Utility.RandomDouble() ) + DrainLife(); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( 0.1 >= Utility.RandomDouble() ) + DrainLife(); + } + + public Succubus( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Titan.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Titan.cs new file mode 100644 index 000000000..bdd66acf2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Titan.cs @@ -0,0 +1,72 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a titans corpse" )] + public class Titan : BaseCreature + { + [Constructable] + public Titan() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a titan"; + Body = 76; + BaseSoundID = 609; + + SetStr( 536, 585 ); + SetDex( 126, 145 ); + SetInt( 281, 305 ); + + SetHits( 322, 351 ); + + SetDamage( 13, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 85.1, 100.0 ); + SetSkill( SkillName.Magery, 85.1, 100.0 ); + SetSkill( SkillName.MagicResist, 80.2, 110.0 ); + SetSkill( SkillName.Tactics, 60.1, 80.0 ); + SetSkill( SkillName.Wrestling, 40.1, 50.0 ); + + Fame = 11500; + Karma = -11500; + + VirtualArmor = 40; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.MedScrolls ); + } + + public override int Meat{ get{ return 4; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public Titan( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Magic/Wraith.cs b/Scripts/Mobiles/Monsters/Humanoid/Magic/Wraith.cs new file mode 100644 index 000000000..5325c1e59 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Magic/Wraith.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a ghostly corpse" )] + public class Wraith : BaseCreature + { + [Constructable] + public Wraith() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a wraith"; + Body = 26; + Hue = 0x4001; + BaseSoundID = 0x482; + + SetStr( 76, 100 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + + SetDamage( 7, 11 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + + SetSkill( SkillName.EvalInt, 55.1, 70.0 ); + SetSkill( SkillName.Magery, 55.1, 70.0 ); + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 28; + + PackReg( 10 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public Wraith( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs new file mode 100644 index 000000000..b95f5f30e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/ArcticOgreLord.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a frozen ogre lord's corpse" )] + [TypeAlias( "Server.Mobiles.ArticOgreLord" )] + public class ArcticOgreLord : BaseCreature + { + [Constructable] + public ArcticOgreLord() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an arctic ogre lord"; + Body = 135; + BaseSoundID = 427; + + SetStr( 767, 945 ); + SetDex( 66, 75 ); + SetInt( 46, 70 ); + + SetHits( 476, 552 ); + + SetDamage( 20, 25 ); + + SetDamageType( ResistanceType.Physical, 30 ); + SetDamageType( ResistanceType.Cold, 70 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 125.1, 140.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 50; + + PackItem( new Club() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + } + + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override int TreasureMapLevel{ get{ return 3; } } + + public ArcticOgreLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs new file mode 100644 index 000000000..e1367ff26 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/BoneKnight.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a skeletal corpse" )] + public class BoneKnight : BaseCreature + { + [Constructable] + public BoneKnight() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a bone knight"; + Body = 57; + BaseSoundID = 451; + + SetStr( 196, 250 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 118, 150 ); + + SetDamage( 8, 18 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Cold, 60 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 85.1, 100.0 ); + SetSkill( SkillName.Wrestling, 85.1, 95.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 40; + + switch ( Utility.Random( 6 ) ) + { + case 0: PackItem( new PlateArms() ); break; + case 1: PackItem( new PlateChest() ); break; + case 2: PackItem( new PlateGloves() ); break; + case 3: PackItem( new PlateGorget() ); break; + case 4: PackItem( new PlateLegs() ); break; + case 5: PackItem( new PlateHelm() ); break; + } + + PackSlayer(); + PackItem( new Scimitar() ); + PackItem( new WoodenShield() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + + public BoneKnight( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Brigand.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Brigand.cs new file mode 100644 index 000000000..57134fe6f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Brigand.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using Server.Items; +using Server.ContextMenus; +using Server.Misc; +using Server.Network; + +namespace Server.Mobiles +{ + public class Brigand : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + + [Constructable] + public Brigand() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + SpeechHue = Utility.RandomDyedHue(); + Title = "the brigand"; + Hue = Utility.RandomSkinHue(); + + if ( this.Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + AddItem( new Skirt( Utility.RandomNeutralHue() ) ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); + } + + SetStr( 86, 100 ); + SetDex( 81, 95 ); + SetInt( 61, 75 ); + + SetDamage( 10, 23 ); + + SetSkill( SkillName.Fencing, 66.0, 97.5 ); + SetSkill( SkillName.Macing, 65.0, 87.5 ); + SetSkill( SkillName.MagicResist, 25.0, 47.5 ); + SetSkill( SkillName.Swords, 65.0, 87.5 ); + SetSkill( SkillName.Tactics, 65.0, 87.5 ); + SetSkill( SkillName.Wrestling, 15.0, 37.5 ); + + Fame = 1000; + Karma = -1000; + + AddItem( new Boots( Utility.RandomNeutralHue() ) ); + AddItem( new FancyShirt()); + AddItem( new Bandana()); + + switch ( Utility.Random( 7 )) + { + case 0: AddItem( new Longsword() ); break; + case 1: AddItem( new Cutlass() ); break; + case 2: AddItem( new Broadsword() ); break; + case 3: AddItem( new Axe() ); break; + case 4: AddItem( new Club() ); break; + case 5: AddItem( new Dagger() ); break; + case 6: AddItem( new Spear() ); break; + } + + Utility.AssignRandomHair( this ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public Brigand( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs new file mode 100644 index 000000000..fb5571d71 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/ChaosDaemon.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a chaos daemon corpse" )] + public class ChaosDaemon : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.CrushingBlow; + } + + [Constructable] + public ChaosDaemon() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a chaos daemon"; + Body = 792; + BaseSoundID = 0x3E9; + + SetStr( 106, 130 ); + SetDex( 171, 200 ); + SetInt( 56, 80 ); + + SetHits( 91, 110 ); + + SetDamage( 12, 17 ); + + SetDamageType( ResistanceType.Physical, 85 ); + SetDamageType( ResistanceType.Fire, 15 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 85.1, 95.0 ); + SetSkill( SkillName.Tactics, 70.1, 80.0 ); + SetSkill( SkillName.Wrestling, 95.1, 100.0 ); + + Fame = 3000; + Karma = -4000; + + VirtualArmor = 15; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + } + + public ChaosDaemon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Cursed.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Cursed.cs new file mode 100644 index 000000000..919b82bd5 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Cursed.cs @@ -0,0 +1,85 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an inhuman corpse" )] + public class Cursed : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + + [Constructable] + public Cursed() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Title = "the Cursed"; + + Hue = Utility.RandomMinMax( 0x8596, 0x8599 ); + Body = 0x190; + Name = NameList.RandomName( "male" ); + BaseSoundID = 471; + + AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); + AddItem( new Shirt( Utility.RandomNeutralHue() ) ); + + BaseWeapon weapon = Loot.RandomWeapon(); + weapon.Movable = false; + AddItem( weapon ); + + SetStr( 91, 100 ); + SetDex( 86, 95 ); + SetInt( 61, 70 ); + + SetHits( 91, 120 ); + + SetDamage( 5, 13 ); + + SetResistance( ResistanceType.Physical, 15, 25 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.Fencing, 46.0, 77.5 ); + SetSkill( SkillName.Macing, 35.0, 57.5 ); + SetSkill( SkillName.MagicResist, 53.5, 62.5 ); + SetSkill( SkillName.Swords, 55.0, 77.5 ); + SetSkill( SkillName.Tactics, 60.0, 82.5 ); + SetSkill( SkillName.Poisoning, 60.0, 82.5 ); + + Fame = 1000; + Karma = -2000; + } + + public override int GetAttackSound() + { + return -1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + //AddLoot( LootPack.Miscellaneous ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public Cursed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs new file mode 100644 index 000000000..d932d9548 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Cyclops.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a cyclops corpse" )] + public class Cyclops : BaseCreature + { + [Constructable] + public Cyclops() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a cyclops"; + Body = 75; + BaseSoundID = 604; + + SetStr( 336, 385 ); + SetDex( 96, 115 ); + SetInt( 31, 55 ); + + SetHits( 202, 231 ); + SetMana( 0 ); + + SetDamage( 7, 23 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 60.3, 105.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 48; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + } + + public override int Meat{ get{ return 4; } } + public override int TreasureMapLevel{ get{ return 3; } } + + public Cyclops( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs new file mode 100644 index 000000000..98d3f83b7 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Doppleganger.cs @@ -0,0 +1,64 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a doppleganger corpse" )] + public class Doppleganger : BaseCreature + { + [Constructable] + public Doppleganger() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a doppleganger"; + Body = 0x309; + BaseSoundID = 0x451; + + SetStr( 81, 110 ); + SetDex( 56, 75 ); + SetInt( 81, 105 ); + + SetHits( 101, 120 ); + + SetDamage( 8, 12 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 75.1, 85.0 ); + SetSkill( SkillName.Tactics, 70.1, 80.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 55; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override int Hides{ get{ return 6; } } + public override int Meat{ get{ return 1; } } + + public Doppleganger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs new file mode 100644 index 000000000..8be834119 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/EnslavedGargoyle.cs @@ -0,0 +1,68 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an enslaved gargoyle corpse" )] + public class EnslavedGargoyle : BaseCreature + { + [Constructable] + public EnslavedGargoyle() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an enslaved gargoyle"; + Body = 0x2F1; + BaseSoundID = 0x174; + + SetStr( 302, 360 ); + SetDex( 76, 95 ); + SetInt( 81, 105 ); + + SetHits( 186,212 ); + + SetDamage( 7, 14 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 50, 70 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 25, 30 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.MagicResist, 70.1, 85.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 40.1, 80.0 ); + + Fame = 3500; + Karma = 0; + + VirtualArmor = 35; + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new GargoylesPickaxe() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.Gems ); + } + + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public EnslavedGargoyle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Ettin.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ettin.cs new file mode 100644 index 000000000..018ff685d --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ettin.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an ettins corpse" )] + public class Ettin : BaseCreature + { + [Constructable] + public Ettin() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ettin"; + Body = 18; + BaseSoundID = 367; + + SetStr( 136, 165 ); + SetDex( 56, 75 ); + SetInt( 31, 55 ); + + SetHits( 82, 99 ); + + SetDamage( 7, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.MagicResist, 40.1, 55.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 50.1, 60.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 38; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.Potions ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 4; } } + + public Ettin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Executioner.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Executioner.cs new file mode 100644 index 000000000..e3ea56706 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Executioner.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using Server.Items; +using Server.ContextMenus; +using Server.Misc; +using Server.Network; + +namespace Server.Mobiles +{ + public class Executioner : BaseCreature + { + [Constructable] + public Executioner() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + SpeechHue = Utility.RandomDyedHue(); + Title = "the executioner"; + Hue = Utility.RandomSkinHue(); + + if ( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + AddItem( new Skirt( Utility.RandomRedHue() ) ); + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + AddItem( new ShortPants( Utility.RandomRedHue() ) ); + } + + SetStr( 386, 400 ); + SetDex( 151, 165 ); + SetInt( 161, 175 ); + + SetDamage( 8, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 25, 30 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Anatomy, 125.0 ); + SetSkill( SkillName.Fencing, 46.0, 77.5 ); + SetSkill( SkillName.Macing, 35.0, 57.5 ); + SetSkill( SkillName.Poisoning, 60.0, 82.5 ); + SetSkill( SkillName.MagicResist, 83.5, 92.5 ); + SetSkill( SkillName.Swords, 125.0 ); + SetSkill( SkillName.Tactics, 125.0 ); + SetSkill( SkillName.Lumberjacking, 125.0 ); + + Fame = 5000; + Karma = -5000; + + VirtualArmor = 40; + + AddItem( new ThighBoots( Utility.RandomRedHue() ) ); + AddItem( new Surcoat( Utility.RandomRedHue() ) ); + AddItem( new ExecutionersAxe()); + + Utility.AssignRandomHair( this ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Meager ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public Executioner( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs new file mode 100644 index 000000000..47e8a9d87 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/FrostTroll.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a frost troll corpse" )] + public class FrostTroll : BaseCreature + { + [Constructable] + public FrostTroll() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a frost troll"; + Body = 55; + BaseSoundID = 461; + + SetStr( 227, 265 ); + SetDex( 66, 85 ); + SetInt( 46, 70 ); + + SetHits( 140, 156 ); + + SetDamage( 14, 20 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Cold, 25 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 50; + + PackItem( new DoubleAxe() ); // TODO: Weapon?? + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems ); + } + + public override int Meat{ get{ return 2; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public FrostTroll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs new file mode 100644 index 000000000..263e31544 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/GazerLarva.cs @@ -0,0 +1,64 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a gazer larva corpse" )] + public class GazerLarva : BaseCreature + { + [Constructable] + public GazerLarva () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a gazer larva"; + Body = 778; + BaseSoundID = 377; + + SetStr( 76, 100 ); + SetDex( 51, 75 ); + SetInt( 56, 80 ); + + SetHits( 36, 47 ); + + SetDamage( 2, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 25 ); + + SetSkill( SkillName.MagicResist, 70.0 ); + SetSkill( SkillName.Tactics, 70.0 ); + SetSkill( SkillName.Wrestling, 70.0 ); + + Fame = 900; + Karma = -900; + + VirtualArmor = 25; + + PackItem( new Nightshade( Utility.RandomMinMax( 2, 3 ) ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + + public GazerLarva( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs new file mode 100644 index 000000000..80ea94b50 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ghoul.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a ghostly corpse" )] + public class Ghoul : BaseCreature + { + [Constructable] + public Ghoul() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a ghoul"; + Body = 153; + BaseSoundID = 0x482; + + SetStr( 76, 100 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 46, 60 ); + SetMana( 0 ); + + SetDamage( 7, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 28; + + PackItem( Loot.RandomWeapon() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + + public Ghoul( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Guardian.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Guardian.cs new file mode 100644 index 000000000..7267e1dce --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Guardian.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Mobiles; + +namespace Server.Mobiles +{ + public class Guardian : BaseCreature + { + [Constructable] + public Guardian() : base( AIType.AI_Archer, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + InitStats( 100, 125, 25 ); + Title = "the guardian"; + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + new ForestOstard().Rider = this; + + PlateChest chest = new PlateChest(); + chest.Hue = 0x966; + AddItem( chest ); + PlateArms arms = new PlateArms(); + arms.Hue = 0x966; + AddItem( arms ); + PlateGloves gloves = new PlateGloves(); + gloves.Hue = 0x966; + AddItem( gloves ); + PlateGorget gorget = new PlateGorget(); + gorget.Hue = 0x966; + AddItem( gorget ); + PlateLegs legs = new PlateLegs(); + legs.Hue = 0x966; + AddItem( legs ); + PlateHelm helm = new PlateHelm(); + helm.Hue = 0x966; + AddItem( helm ); + + + Bow bow = new Bow(); + + bow.Movable = false; + bow.Crafter = this; + bow.Quality = WeaponQuality.Exceptional; + + AddItem( bow ); + + PackItem( new Arrow( 250 ) ); + PackGold( 250, 500 ); + + Skills[SkillName.Anatomy].Base = 120.0; + Skills[SkillName.Tactics].Base = 120.0; + Skills[SkillName.Archery].Base = 120.0; + Skills[SkillName.MagicResist].Base = 120.0; + Skills[SkillName.DetectHidden].Base = 100.0; + + } + + public Guardian( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs new file mode 100644 index 000000000..5387f7c03 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/HeadlessOne.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a headless corpse" )] + public class HeadlessOne : BaseCreature + { + [Constructable] + public HeadlessOne() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a headless one"; + Body = 31; + Hue = Utility.RandomSkinHue() & 0x7FFF; + BaseSoundID = 0x39D; + + SetStr( 26, 50 ); + SetDex( 36, 55 ); + SetInt( 16, 30 ); + + SetHits( 16, 30 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 25.1, 40.0 ); + SetSkill( SkillName.Wrestling, 25.1, 40.0 ); + + Fame = 450; + Karma = -450; + + VirtualArmor = 18; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + // TODO: body parts + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + + public HeadlessOne( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs new file mode 100644 index 000000000..c00267d40 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/HordeMinion.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a horde minion corpse" )] + public class HordeMinion : BaseCreature + { + [Constructable] + public HordeMinion () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a horde minion"; + Body = 776; + BaseSoundID = 357; + + SetStr( 16, 40 ); + SetDex( 31, 60 ); + SetInt( 11, 25 ); + + SetHits( 10, 24 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + + SetSkill( SkillName.MagicResist, 10.0 ); + SetSkill( SkillName.Tactics, 0.1, 15.0 ); + SetSkill( SkillName.Wrestling, 25.1, 40.0 ); + + Fame = 500; + Karma = -500; + + VirtualArmor = 18; + + AddItem( new LightSource() ); + + PackItem( new Bone( 3 ) ); + // TODO: Body parts + } + + public override int GetIdleSound() + { + return 338; + } + + public override int GetAngerSound() + { + return 338; + } + + public override int GetDeathSound() + { + return 338; + } + + public override int GetAttackSound() + { + return 406; + } + + public override int GetHurtSound() + { + return 194; + } + + public HordeMinion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs new file mode 100644 index 000000000..30a76cfc2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Juggernaut.cs @@ -0,0 +1,139 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a juggernaut corpse" )] + public class Juggernaut : BaseCreature + { + private bool m_Stunning; + + [Constructable] + public Juggernaut() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.3, 0.6 ) + { + Name = "a blackthorn juggernaut"; + Body = 768; + + + SetStr( 301, 400 ); + SetDex( 51, 70 ); + SetInt( 51, 100 ); + + SetHits( 181, 240 ); + + SetDamage( 12, 19 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Fire, 25 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 65, 75 ); + SetResistance( ResistanceType.Fire, 35, 45 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Anatomy, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 140.1, 150.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 12000; + Karma = -12000; + + VirtualArmor = 70; + + if ( 0.1 > Utility.RandomDouble() ) + PackItem( new PowerCrystal() ); + + if ( 0.4 > Utility.RandomDouble() ) + PackItem( new ClockworkAssembly() ); + } + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 1 ); + } + + public override int GetDeathSound() + { + return 0x423; + } + + public override int GetAttackSound() + { + return 0x23B; + } + + public override int GetHurtSound() + { + return 0x140; + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( !m_Stunning && 0.3 > Utility.RandomDouble() ) + { + m_Stunning = true; + + defender.Animate( 21, 6, 1, true, false, 0 ); + this.PlaySound( 0xEE ); + defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You have been stunned by a colossal blow!" ); + + BaseWeapon weapon = this.Weapon as BaseWeapon; + if ( weapon != null ) + weapon.OnHit( this, defender ); + + if ( defender.Alive ) + { + defender.Frozen = true; + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( Recover_Callback ), defender ); + } + } + } + + private void Recover_Callback( object state ) + { + Mobile defender = state as Mobile; + + if ( defender != null ) + { + defender.Frozen = false; + defender.Combatant = null; + defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You recover your senses." ); + } + + m_Stunning = false; + } + + public Juggernaut( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs new file mode 100644 index 000000000..f8417ba72 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunRevenant.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + public class KhaldunRevenant : BaseCreature + { + private static Hashtable m_Table = new Hashtable(); + + public static void Initialize() + { + EventSink.PlayerDeath += new PlayerDeathEventHandler( EventSink_PlayerDeath ); + } + + public static void EventSink_PlayerDeath( PlayerDeathEventArgs e ) + { + Mobile m = e.Mobile; + Mobile lastKiller = m.LastKiller; + + if ( lastKiller is BaseCreature ) + lastKiller = ((BaseCreature)lastKiller).GetMaster(); + + if ( IsInsideKhaldun( m ) && IsInsideKhaldun( lastKiller ) && lastKiller.Player && !m_Table.Contains( lastKiller ) ) + { + foreach ( AggressorInfo ai in m.Aggressors ) + { + if ( ai.Attacker == lastKiller && ai.CanReportMurder ) + { + SummonRevenant( m, lastKiller ); + break; + } + } + } + } + + public static void SummonRevenant( Mobile victim, Mobile killer ) + { + KhaldunRevenant revenant = new KhaldunRevenant( killer ); + + revenant.MoveToWorld( victim.Location, victim.Map ); + revenant.Combatant = killer; + revenant.FixedParticles( 0, 0, 0, 0x13A7, EffectLayer.Waist ); + Effects.PlaySound( revenant.Location, revenant.Map, 0x29 ); + + m_Table.Add( killer, null ); + } + + public static bool IsInsideKhaldun( Mobile from ) + { + return from != null && from.Region != null && from.Region.IsPartOf( "Khaldun" ); + } + + private Mobile m_Target; + private DateTime m_ExpireTime; + + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + public override void DisplayPaperdollTo( Mobile to ) + { + } + + public override Mobile ConstantFocus{ get{ return m_Target; } } + public override bool AlwaysAttackable{ get{ return true; } } + + public KhaldunRevenant( Mobile target ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.18, 0.36 ) + { + Name = "a revenant"; + Body = 0x3CA; + Hue = 0x41CE; + + m_Target = target; + m_ExpireTime = DateTime.Now + TimeSpan.FromMinutes( 10.0 ); + + SetStr( 401, 500 ); + SetDex( 296, 315 ); + SetInt( 101, 200 ); + + SetHits( 241, 300 ); + SetStam( 242, 280 ); + + SetDamage( 20, 30 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetSkill( SkillName.MagicResist, 100.1, 150.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Swords, 140.1, 150.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + Fame = 0; + Karma = 0; + + VirtualArmor = 60; + + Halberd weapon = new Halberd(); + weapon.Hue = 0x41CE; + weapon.Movable = false; + + AddItem( weapon ); + } + + public override int GetIdleSound() + { + return 0x1BF; + } + + public override int GetAngerSound() + { + return 0x107; + } + + public override int GetDeathSound() + { + return 0xFD; + } + + public override bool BardImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override void OnThink() + { + if ( !m_Target.Alive || DateTime.Now > m_ExpireTime ) + { + Delete(); + return; + } + + //Combatant = m_Target; + //FocusMob = m_Target; + + if ( AIObject != null ) + AIObject.Action = ActionType.Combat; + + base.OnThink(); + } + + public override bool OnBeforeDeath() + { + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public override void OnDelete() + { + if ( m_Target != null ) + m_Table.Remove( m_Target ); + + base.OnDelete(); + } + + public KhaldunRevenant( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs new file mode 100644 index 000000000..fcfa1e32e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunSummoner.cs @@ -0,0 +1,144 @@ +using System; +using Server.Misc; +using Server.Network; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + public class KhaldunSummoner : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + + [Constructable] + public KhaldunSummoner():base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 0x190; + Name = "Zealot of Khaldun"; + Title = "the Summoner"; + + SetStr( 351, 400 ); + SetDex( 101, 150 ); + SetInt( 502, 700 ); + + SetHits( 421, 480 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Cold, 25 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.EvalInt, 100.0 ); + SetSkill( SkillName.Meditation, 120.1, 130.0 ); + + VirtualArmor = 36; + Fame = 10000; + Karma = -10000; + + LeatherGloves gloves = new LeatherGloves(); + gloves.Hue = 0x66D; + AddItem( gloves ); + + BoneHelm helm = new BoneHelm(); + helm.Hue = 0x835; + AddItem( helm ); + + Necklace necklace = new Necklace(); + necklace.Hue = 0x66D; + AddItem( necklace ); + + Cloak cloak = new Cloak(); + cloak.Hue = 0x66D; + AddItem( cloak ); + + Kilt kilt = new Kilt(); + kilt.Hue = 0x66D; + AddItem( kilt ); + + Sandals sandals = new Sandals(); + sandals.Hue = 0x66D; + AddItem( sandals ); + } + + public override int GetIdleSound() + { + return 0x184; + } + + public override int GetAngerSound() + { + return 0x286; + } + + public override int GetDeathSound() + { + return 0x288; + } + + public override int GetHurtSound() + { + return 0x19F; + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool Unprovokable{ get{ return true; } } + + public KhaldunSummoner( Serial serial ) : base( serial ) + { + } + + public override bool OnBeforeDeath() + { + BoneMagi rm = new BoneMagi(); + rm.Team = this.Team; + rm.Combatant = this.Combatant; + rm.NoKillAwards = true; + + if ( rm.Backpack == null ) + { + Backpack pack = new Backpack(); + pack.Movable = false; + rm.AddItem( pack ); + } + + for ( int i = 0; i < 2; i++ ) + { + LootPack.FilthyRich.Generate( this, rm.Backpack, true, LootPack.GetLuckChanceForKiller( this ) ); + LootPack.FilthyRich.Generate( this, rm.Backpack, false, LootPack.GetLuckChanceForKiller( this ) ); + } + + Effects.PlaySound(this, Map, GetDeathSound()); + Effects.SendLocationEffect( Location, Map, 0x3709, 30, 10, 0x835, 0 ); + rm.MoveToWorld( Location, Map ); + + Delete(); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs new file mode 100644 index 000000000..f38bede46 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/KhaldunZealot.cs @@ -0,0 +1,153 @@ +using System; +using Server.Misc; +using Server.Network; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + public class KhaldunZealot : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + public override bool ShowFameTitle{ get{ return false; } } + + [Constructable] + public KhaldunZealot(): base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 0x190; + Name = "Zealot of Khaldun"; + Title = "the Knight"; + Hue = 0; + + SetStr( 351, 400 ); + SetDex( 151, 165 ); + SetInt( 76, 100 ); + + SetHits( 448, 470 ); + + SetDamage( 15, 25 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Cold, 25 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Wrestling, 70.1, 80.0 ); + SetSkill( SkillName.Swords, 120.1, 130.0 ); + SetSkill( SkillName.Anatomy, 120.1, 130.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + + Fame = 10000; + Karma = -10000; + VirtualArmor = 40; + + VikingSword weapon = new VikingSword(); + weapon.Hue = 0x835; + weapon.Movable = false; + AddItem( weapon ); + + MetalShield shield = new MetalShield(); + shield.Hue = 0x835; + shield.Movable = false; + AddItem( shield ); + + BoneHelm helm = new BoneHelm(); + helm.Hue = 0x835; + AddItem( helm ); + + BoneArms arms = new BoneArms(); + arms.Hue = 0x835; + AddItem( arms ); + + BoneGloves gloves = new BoneGloves(); + gloves.Hue = 0x835; + AddItem( gloves ); + + BoneChest tunic = new BoneChest(); + tunic.Hue = 0x835; + AddItem( tunic ); + + BoneLegs legs = new BoneLegs(); + legs.Hue = 0x835; + AddItem( legs ); + + AddItem( new Boots() ); + } + + public override int GetIdleSound() + { + return 0x184; + } + + public override int GetAngerSound() + { + return 0x286; + } + + public override int GetDeathSound() + { + return 0x288; + } + + public override int GetHurtSound() + { + return 0x19F; + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool Unprovokable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + + public KhaldunZealot( Serial serial ) : base( serial ) + { + } + + public override bool OnBeforeDeath() + { + BoneKnight rm = new BoneKnight(); + rm.Team = this.Team; + rm.Combatant = this.Combatant; + rm.NoKillAwards = true; + + if ( rm.Backpack == null ) + { + Backpack pack = new Backpack(); + pack.Movable = false; + rm.AddItem( pack ); + } + + for ( int i = 0; i < 2; i++ ) + { + LootPack.FilthyRich.Generate( this, rm.Backpack, true, LootPack.GetLuckChanceForKiller( this ) ); + LootPack.FilthyRich.Generate( this, rm.Backpack, false, LootPack.GetLuckChanceForKiller( this ) ); + } + + Effects.PlaySound(this, Map, GetDeathSound()); + Effects.SendLocationEffect( Location, Map, 0x3709, 30, 10, 0x835, 0 ); + rm.MoveToWorld( Location, Map ); + + Delete(); + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Moloch.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Moloch.cs new file mode 100644 index 000000000..7ea9aea10 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Moloch.cs @@ -0,0 +1,68 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a moloch corpse" )] + public class Moloch : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.ConcussionBlow; + } + + [Constructable] + public Moloch() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a moloch"; + Body = 0x311; + BaseSoundID = 0x300; + + SetStr( 331, 360 ); + SetDex( 66, 85 ); + SetInt( 41, 65 ); + + SetHits( 171, 200 ); + + SetDamage( 15, 23 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 65.1, 75.0 ); + SetSkill( SkillName.Tactics, 75.1, 90.0 ); + SetSkill( SkillName.Wrestling, 70.1, 90.0 ); + + Fame = 7500; + Karma = -7500; + + VirtualArmor = 32; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + + public Moloch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs new file mode 100644 index 000000000..1efca0eff --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Mongbat.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a mongbat corpse" )] + public class Mongbat : BaseCreature + { + [Constructable] + public Mongbat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a mongbat"; + Body = 39; + BaseSoundID = 422; + + SetStr( 6, 10 ); + SetDex( 26, 38 ); + SetInt( 6, 14 ); + + SetHits( 4, 6 ); + SetMana( 0 ); + + SetDamage( 1, 2 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + + SetSkill( SkillName.MagicResist, 5.1, 14.0 ); + SetSkill( SkillName.Tactics, 5.1, 10.0 ); + SetSkill( SkillName.Wrestling, 5.1, 10.0 ); + + Fame = 150; + Karma = -150; + + VirtualArmor = 10; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = -18.9; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + + public Mongbat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Mummy.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Mummy.cs new file mode 100644 index 000000000..8bcbf6226 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Mummy.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a mummy corpse" )] + public class Mummy : BaseCreature + { + [Constructable] + public Mummy() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8 ) + { + Name = "a mummy"; + Body = 154; + BaseSoundID = 471; + + SetStr( 346, 370 ); + SetDex( 71, 90 ); + SetInt( 26, 40 ); + + SetHits( 208, 222 ); + + SetDamage( 13, 23 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Cold, 60 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 15.1, 40.0 ); + SetSkill( SkillName.Tactics, 35.1, 50.0 ); + SetSkill( SkillName.Wrestling, 35.1, 50.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 50; + + PackItem( new Garlic( 5 ) ); + PackItem( new Bandage( 10 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems ); + AddLoot( LootPack.Potions ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lesser; } } + + public Mummy( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Ogre.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ogre.cs new file mode 100644 index 000000000..f8de39c25 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ogre.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an ogre corpse" )] + public class Ogre : BaseCreature + { + [Constructable] + public Ogre () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ogre"; + Body = 1; + BaseSoundID = 427; + + SetStr( 166, 195 ); + SetDex( 46, 65 ); + SetInt( 46, 70 ); + + SetHits( 100, 117 ); + SetMana( 0 ); + + SetDamage( 9, 11 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 25 ); + + SetSkill( SkillName.MagicResist, 55.1, 70.0 ); + SetSkill( SkillName.Tactics, 60.1, 70.0 ); + SetSkill( SkillName.Wrestling, 70.1, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 32; + + PackItem( new Club() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Potions ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 2; } } + + public Ogre( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs new file mode 100644 index 000000000..bc38b424e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/OgreLord.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Factions; + +namespace Server.Mobiles +{ + [CorpseName( "an ogre lords corpse" )] + public class OgreLord : BaseCreature + { + public override Faction FactionAllegiance { get { return Minax.Instance; } } + public override Ethics.Ethic EthicAllegiance { get { return Ethics.Ethic.Evil; } } + + [Constructable] + public OgreLord () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ogre lord"; + Body = 83; + BaseSoundID = 427; + + SetStr( 767, 945 ); + SetDex( 66, 75 ); + SetInt( 46, 70 ); + + SetHits( 476, 552 ); + + SetDamage( 20, 25 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 125.1, 140.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 50; + + PackItem( new Club() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override int TreasureMapLevel{ get{ return 3; } } + public override int Meat{ get{ return 2; } } + + public OgreLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Orc.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Orc.cs new file mode 100644 index 000000000..e1df682a3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Orc.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "an orcish corpse" )] + public class Orc : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Orc; } } + + [Constructable] + public Orc() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "orc" ); + Body = 17; + BaseSoundID = 0x45A; + + SetStr( 96, 120 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 50.1, 75.0 ); + SetSkill( SkillName.Tactics, 55.1, 80.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 28; + + switch ( Utility.Random( 20 ) ) + { + case 0: PackItem( new Scimitar() ); break; + case 1: PackItem( new Katana() ); break; + case 2: PackItem( new WarMace() ); break; + case 3: PackItem( new WarHammer() ); break; + case 4: PackItem( new Kryss() ); break; + case 5: PackItem( new Pitchfork() ); break; + } + + PackItem( new ThighBoots() ); + + switch ( Utility.Random( 3 ) ) + { + case 0: PackItem( new Ribs() ); break; + case 1: PackItem( new Shaft() ); break; + case 2: PackItem( new Candle() ); break; + } + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new BolaBall() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 1; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player && m.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + Item item = aggressor.FindItemOnLayer( Layer.Helm ); + + if ( item is OrcishKinMask ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + item.Delete(); + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + } + } + + public Orc( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs new file mode 100644 index 000000000..e0ad3c987 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBomber.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "an orcish corpse" )] + public class OrcBomber : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Orc; } } + + [Constructable] + public OrcBomber() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 182; + + Name = "an orc bomber"; + BaseSoundID = 0x45A; + + SetStr( 147, 215 ); + SetDex( 91, 115 ); + SetInt( 61, 85 ); + + SetHits( 95, 123 ); + + SetDamage( 1, 8 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Fire, 25 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 15, 20 ); + SetResistance( ResistanceType.Energy, 25, 30 ); + + SetSkill( SkillName.MagicResist, 70.1, 85.0 ); + SetSkill( SkillName.Swords, 60.1, 85.0 ); + SetSkill( SkillName.Tactics, 75.1, 90.0 ); + SetSkill( SkillName.Wrestling, 60.1, 85.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 30; + + PackItem( new SulfurousAsh( Utility.RandomMinMax( 6, 10 ) ) ); + PackItem( new MandrakeRoot( Utility.RandomMinMax( 6, 10 ) ) ); + PackItem( new BlackPearl( Utility.RandomMinMax( 6, 10 ) ) ); + PackItem( new MortarPestle() ); + PackItem( new LesserExplosionPotion() ); + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new BolaBall() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player && m.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + Item item = aggressor.FindItemOnLayer( Layer.Helm ); + + if ( item is OrcishKinMask ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + item.Delete(); + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + } + } + + private DateTime m_NextBomb; + private int m_Thrown; + + public override void OnActionCombat() + { + Mobile combatant = Combatant; + + if ( combatant == null || combatant.Deleted || combatant.Map != Map || !InRange( combatant, 12 ) || !CanBeHarmful( combatant ) || !InLOS( combatant ) ) + return; + + if ( DateTime.Now >= m_NextBomb ) + { + ThrowBomb( combatant ); + + m_Thrown++; + + if ( 0.75 >= Utility.RandomDouble() && (m_Thrown % 2) == 1 ) // 75% chance to quickly throw another bomb + m_NextBomb = DateTime.Now + TimeSpan.FromSeconds( 3.0 ); + else + m_NextBomb = DateTime.Now + TimeSpan.FromSeconds( 5.0 + (10.0 * Utility.RandomDouble()) ); // 5-15 seconds + } + } + + public void ThrowBomb( Mobile m ) + { + DoHarmful( m ); + + this.MovingParticles( m, 0x1C19, 1, 0, false, true, 0, 0, 9502, 6014, 0x11D, EffectLayer.Waist, 0 ); + + new InternalTimer( m, this ).Start(); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile, m_From; + + public InternalTimer( Mobile m, Mobile from ) : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_From = from; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Mobile.PlaySound( 0x11D ); + AOS.Damage( m_Mobile, m_From, Utility.RandomMinMax( 10, 20 ), 0, 100, 0, 0, 0 ); + } + } + + public OrcBomber( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs new file mode 100644 index 000000000..a3b299502 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "an orcish corpse" )] + public class OrcBrute : BaseCreature + { + [Constructable] + public OrcBrute() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 189; + + Name = "an orc brute"; + BaseSoundID = 0x45A; + + SetStr( 767, 945 ); + SetDex( 66, 75 ); + SetInt( 46, 70 ); + + SetHits( 476, 552 ); + + SetDamage( 20, 25 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.Macing, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 125.1, 140.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 50; + + PackItem( new ShadowIronOre( 25 ) ); + PackItem( new IronIngot( 10 ) ); + + if ( 0.05 > Utility.RandomDouble() ) + PackItem( new OrcishKinMask() ); + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new BolaBall() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + } + + public override bool BardImmune{ get{ return !Core.AOS; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int Meat{ get{ return 2; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player && m.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + Item item = aggressor.FindItemOnLayer( Layer.Helm ); + + if ( item is OrcishKinMask ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + item.Delete(); + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + } + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override bool AutoDispel{ get{ return true; } } + + public override void OnDamagedBySpell( Mobile caster ) + { + if ( caster == this ) + return; + + SpawnOrcLord( caster ); + } + + public void SpawnOrcLord( Mobile target ) + { + Map map = target.Map; + + if ( map == null ) + return; + + int orcs = 0; + + foreach ( Mobile m in this.GetMobilesInRange( 10 ) ) + { + if ( m is OrcishLord ) + ++orcs; + } + + if ( orcs < 10 ) + { + BaseCreature orc = new SpawnedOrcishLord(); + + orc.Team = this.Team; + + Point3D loc = target.Location; + bool validLocation = false; + + for ( int j = 0; !validLocation && j < 10; ++j ) + { + int x = target.X + Utility.Random( 3 ) - 1; + int y = target.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 ); + } + + orc.MoveToWorld( loc, map ); + + orc.Combatant = target; + } + } + + public OrcBrute( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs new file mode 100644 index 000000000..795e506dd --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcCaptain.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an orcish corpse" )] + public class OrcCaptain : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Orc; } } + + [Constructable] + public OrcCaptain() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "orc" ); + Body = 7; + BaseSoundID = 0x45A; + + SetStr( 111, 145 ); + SetDex( 101, 135 ); + SetInt( 86, 110 ); + + SetHits( 67, 87 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 35 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 70.1, 85.0 ); + SetSkill( SkillName.Swords, 70.1, 95.0 ); + SetSkill( SkillName.Tactics, 85.1, 100.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 34; + + // TODO: Skull? + switch ( Utility.Random( 7 ) ) + { + case 0: PackItem( new Arrow() ); break; + case 1: PackItem( new Lockpick() ); break; + case 2: PackItem( new Shaft() ); break; + case 3: PackItem( new Ribs() ); break; + case 4: PackItem( new Bandage() ); break; + case 5: PackItem( new BeverageBottle( BeverageType.Wine ) ); break; + case 6: PackItem( new Jug( BeverageType.Cider ) ); break; + } + + if ( Core.AOS ) + PackItem( Loot.RandomNecromancyReagent() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player && m.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + Item item = aggressor.FindItemOnLayer( Layer.Helm ); + + if ( item is OrcishKinMask ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + item.Delete(); + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + } + } + + public OrcCaptain( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs new file mode 100644 index 000000000..4cce58dfe --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/OrcishLord.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an orcish corpse" )] + public class OrcishLord : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Orc; } } + + [Constructable] + public OrcishLord() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an orcish lord"; + Body = 138; + BaseSoundID = 0x45A; + + SetStr( 147, 215 ); + SetDex( 91, 115 ); + SetInt( 61, 85 ); + + SetHits( 95, 123 ); + + SetDamage( 4, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 70.1, 85.0 ); + SetSkill( SkillName.Swords, 60.1, 85.0 ); + SetSkill( SkillName.Tactics, 75.1, 90.0 ); + SetSkill( SkillName.Wrestling, 60.1, 85.0 ); + + Fame = 2500; + Karma = -2500; + + switch ( Utility.Random( 5 ) ) + { + case 0: PackItem( new Lockpick() ); break; + case 1: PackItem( new MortarPestle() ); break; + case 2: PackItem( new Bottle() ); break; + case 3: PackItem( new RawRibs() ); break; + case 4: PackItem( new Shovel() ); break; + } + + PackItem( new RingmailChest() ); + + if ( 0.3 > Utility.RandomDouble() ) + PackItem( Loot.RandomPossibleReagent() ); + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new BolaBall() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Average ); + // TODO: evil orc helm + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 1; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.Player && m.FindItemOnLayer( Layer.Helm ) is OrcishKinMask ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + Item item = aggressor.FindItemOnLayer( Layer.Helm ); + + if ( item is OrcishKinMask ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + item.Delete(); + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + } + } + + public OrcishLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Ratman.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ratman.cs new file mode 100644 index 000000000..952aee408 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Ratman.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a ratman's corpse" )] + public class Ratman : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Ratman; } } + + [Constructable] + public Ratman() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "ratman" ); + Body = 42; + BaseSoundID = 437; + + SetStr( 96, 120 ); + SetDex( 81, 100 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + + SetDamage( 4, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 35.1, 60.0 ); + SetSkill( SkillName.Tactics, 50.1, 75.0 ); + SetSkill( SkillName.Wrestling, 50.1, 75.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 28; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + // TODO: weapon, misc + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Hides{ get{ return 8; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public Ratman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs new file mode 100644 index 000000000..1fc5cc031 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/RatmanArcher.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a ratman archer corpse" )] + public class RatmanArcher : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Ratman; } } + + [Constructable] + public RatmanArcher() : base( AIType.AI_Archer, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "ratman" ); + Body = 0x8E; + BaseSoundID = 437; + + SetStr( 146, 180 ); + SetDex( 101, 130 ); + SetInt( 116, 140 ); + + SetHits( 88, 108 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 55 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Anatomy, 60.2, 100.0 ); + SetSkill( SkillName.Archery, 80.1, 90.0 ); + SetSkill( SkillName.MagicResist, 65.1, 90.0 ); + SetSkill( SkillName.Tactics, 50.1, 75.0 ); + SetSkill( SkillName.Wrestling, 50.1, 75.0 ); + + Fame = 6500; + Karma = -6500; + + VirtualArmor = 56; + + AddItem( new Bow() ); + PackItem( new Arrow( Utility.RandomMinMax( 50, 70 ) ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Hides{ get{ return 8; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public RatmanArcher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Body == 42 ) + { + Body = 0x8E; + Hue = 0; + } + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs new file mode 100644 index 000000000..c715b42c7 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/RestlessSoul.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Targeting; +using Server.Engines.Quests; +using Server.Engines.Quests.Haven; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [CorpseName( "a ghostly corpse" )] + public class RestlessSoul : BaseCreature + { + [Constructable] + public RestlessSoul() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8 ) + { + Name = "a restless soul"; + Body = 0x3CA; + Hue = 0x453; + + SetStr( 26, 40 ); + SetDex( 26, 40 ); + SetInt( 26, 40 ); + + SetHits( 16, 24 ); + + SetDamage( 1, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 25 ); + SetResistance( ResistanceType.Fire, 5, 15 ); + SetResistance( ResistanceType.Cold, 25, 40 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 20.1, 30.0 ); + SetSkill( SkillName.Swords, 20.1, 30.0 ); + SetSkill( SkillName.Tactics, 20.1, 30.0 ); + SetSkill( SkillName.Wrestling, 20.1, 30.0 ); + + Fame = 500; + Karma = -500; + + VirtualArmor = 6; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override bool AlwaysAttackable{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + + public override void DisplayPaperdollTo(Mobile to) + { + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + for ( int i = 0; i < list.Count; ++i ) + { + if ( list[i] is ContextMenus.PaperdollEntry ) + list.RemoveAt( i-- ); + } + } + + public override int GetIdleSound() + { + return 0x1BF; + } + + public override int GetAngerSound() + { + return 0x107; + } + + public override int GetDeathSound() + { + return 0xFD; + } + + public override bool IsEnemy( Mobile m ) + { + PlayerMobile player = m as PlayerMobile; + + if ( player != null && Map == Map.Trammel && X >= 5199 && X <= 5271 && Y >= 1812 && Y <= 1865 ) // Schmendrick's cave + { + QuestSystem qs = player.Quest; + + if ( qs is UzeraanTurmoilQuest && qs.IsObjectiveInProgress( typeof( FindSchmendrickObjective ) ) ) + { + return false; + } + } + + return base.IsEnemy( m ); + } + + public RestlessSoul( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs new file mode 100644 index 000000000..d1fa265c3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/RottingCorpse.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a rotting corpse" )] + public class RottingCorpse : BaseCreature + { + [Constructable] + public RottingCorpse() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a rotting corpse"; + Body = 155; + BaseSoundID = 471; + + SetStr( 301, 350 ); + SetDex( 75 ); + SetInt( 151, 200 ); + + SetHits( 1200 ); + SetStam( 150 ); + SetMana( 0 ); + + SetDamage( 8, 10 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Cold, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 70 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.Poisoning, 120.0 ); + SetSkill( SkillName.MagicResist, 250.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 6000; + Karma = -6000; + + VirtualArmor = 40; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public RottingCorpse( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Savage.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Savage.cs new file mode 100644 index 000000000..98270de6f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Savage.cs @@ -0,0 +1,118 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a savage corpse" )] + public class Savage : BaseCreature + { + [Constructable] + public Savage() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "savage" ); + + if ( Female = Utility.RandomBool() ) + Body = 184; + else + Body = 183; + + SetStr( 96, 115 ); + SetDex( 86, 105 ); + SetInt( 51, 65 ); + + SetDamage( 23, 27 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetSkill( SkillName.Fencing, 60.0, 82.5 ); + SetSkill( SkillName.Macing, 60.0, 82.5 ); + SetSkill( SkillName.Poisoning, 60.0, 82.5 ); + SetSkill( SkillName.MagicResist, 57.5, 80.0 ); + SetSkill( SkillName.Swords, 60.0, 82.5 ); + SetSkill( SkillName.Tactics, 60.0, 82.5 ); + + Fame = 1000; + Karma = -1000; + + PackItem( new Bandage( Utility.RandomMinMax( 1, 15 ) ) ); + + if ( Female && 0.1 > Utility.RandomDouble() ) + PackItem( new TribalBerry() ); + else if ( !Female && 0.1 > Utility.RandomDouble() ) + PackItem( new BolaBall() ); + + AddItem( new Spear() ); + AddItem( new BoneArms() ); + AddItem( new BoneLegs() ); + + if ( 0.5 > Utility.RandomDouble() ) + AddItem( new SavageMask() ); + else if ( 0.1 > Utility.RandomDouble() ) + AddItem( new OrcishKinMask() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override int Meat{ get{ return 1; } } + public override bool AlwaysMurderer{ get{ return true; } } + public override bool ShowFameTitle{ get{ return false; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.BodyMod == 183 || m.BodyMod == 184 ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + if ( aggressor.BodyMod == 183 || aggressor.BodyMod == 184 ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + aggressor.BodyMod = 0; + aggressor.HueMod = -1; + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + aggressor.SendLocalizedMessage( 1040008 ); // Your skin is scorched as the tribal paint burns away! + + if ( aggressor is PlayerMobile ) + ((PlayerMobile)aggressor).SavagePaintExpiration = TimeSpan.Zero; + } + } + + public override void AlterMeleeDamageTo( Mobile to, ref int damage ) + { + if ( to is Dragon || to is WhiteWyrm || to is SwampDragon || to is Drake || to is Nightmare || to is Hiryu || to is LesserHiryu || to is Daemon ) + damage *= 3; + } + + public Savage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs new file mode 100644 index 000000000..04c8a647f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/SavageRider.cs @@ -0,0 +1,128 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a savage corpse" )] + public class SavageRider : BaseCreature + { + [Constructable] + public SavageRider() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.15, 0.4 ) + { + Name = NameList.RandomName( "savage rider" ); + + if ( Female = Utility.RandomBool() ) + Body = 186; + else + Body = 185; + + SetStr( 151, 170 ); + SetDex( 92, 130 ); + SetInt( 51, 65 ); + + SetDamage( 29, 34 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetSkill( SkillName.Fencing, 72.5, 95.0 ); + SetSkill( SkillName.Healing, 60.3, 90.0 ); + SetSkill( SkillName.Macing, 72.5, 95.0 ); + SetSkill( SkillName.Poisoning, 60.0, 82.5 ); + SetSkill( SkillName.MagicResist, 72.5, 95.0 ); + SetSkill( SkillName.Swords, 72.5, 95.0 ); + SetSkill( SkillName.Tactics, 72.5, 95.0 ); + + Fame = 1000; + Karma = -1000; + + PackItem( new Bandage( Utility.RandomMinMax( 1, 15 ) ) ); + + if ( 0.1 > Utility.RandomDouble() ) + PackItem( new BolaBall() ); + + AddItem( new TribalSpear() ); + AddItem( new BoneArms() ); + AddItem( new BoneLegs() ); + // TODO: BEAR MASK + + new SavageRidgeback().Rider = this; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override int Meat{ get{ return 1; } } + public override bool AlwaysMurderer{ get{ return true; } } + public override bool ShowFameTitle{ get{ return false; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.SavagesAndOrcs; } + } + + public override bool OnBeforeDeath() + { + IMount mount = this.Mount; + + if ( mount != null ) + mount.Rider = null; + + if ( mount is Mobile ) + ((Mobile)mount).Delete(); + + return base.OnBeforeDeath(); + } + + public override bool IsEnemy( Mobile m ) + { + if ( m.BodyMod == 183 || m.BodyMod == 184 ) + return false; + + return base.IsEnemy( m ); + } + + public override void AggressiveAction( Mobile aggressor, bool criminal ) + { + base.AggressiveAction( aggressor, criminal ); + + if ( aggressor.BodyMod == 183 || aggressor.BodyMod == 184 ) + { + AOS.Damage( aggressor, 50, 0, 100, 0, 0, 0 ); + aggressor.BodyMod = 0; + aggressor.HueMod = -1; + aggressor.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + aggressor.PlaySound( 0x307 ); + aggressor.SendLocalizedMessage( 1040008 ); // Your skin is scorched as the tribal paint burns away! + + if ( aggressor is PlayerMobile ) + ((PlayerMobile)aggressor).SavagePaintExpiration = TimeSpan.Zero; + } + } + + public override void AlterMeleeDamageTo( Mobile to, ref int damage ) + { + if ( to is Dragon || to is WhiteWyrm || to is SwampDragon || to is Drake || to is Nightmare || to is Hiryu || to is LesserHiryu || to is Daemon ) + damage *= 3; + } + + public SavageRider( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs new file mode 100644 index 000000000..22e4fa791 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/ShadowFiend.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class ShadowFiend : BaseCreature + { + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + private UnhideTimer m_Timer; + + [Constructable] + public ShadowFiend() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a shadow fiend"; + Body = 0xA8; + + // this to allow shadow fiend to loot from corpses + Backpack backpack = new Backpack(); + backpack.Movable = false; + AddItem( backpack ); + + SetStr( 46, 55 ); + SetDex( 121, 130 ); + SetInt( 46, 55 ); + + SetHits( 28, 33 ); + SetStam( 46, 55 ); + + SetDamage( 10, 22 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 80 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 20, 25 ); + SetResistance( ResistanceType.Cold, 40, 45 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 5, 10 ); + + SetSkill( SkillName.MagicResist, 20.1, 30.0 ); + SetSkill( SkillName.Tactics, 20.1, 30.0 ); + SetSkill( SkillName.Wrestling, 20.1, 30.0 ); + + Fame = 1000; + Karma = -1000; + + m_Timer = new UnhideTimer( this ); + m_Timer.Start(); + } + + public override int GetIdleSound() + { + return 0x37A; + } + + public override int GetAngerSound() + { + return 0x379; + } + + public override int GetDeathSound() + { + return 0x381; + } + + public override int GetAttackSound() + { + return 0x37F; + } + + public override int GetHurtSound() + { + return 0x380; + } + + public override bool CanRummageCorpses{ get{ return true; } } + + public override bool OnBeforeDeath() + { + if ( Backpack != null ) + Backpack.Destroy(); + + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public ShadowFiend( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + m_Timer = new UnhideTimer( this ); + m_Timer.Start(); + } + + public override void OnAfterDelete() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + base.OnAfterDelete(); + } + + private class UnhideTimer : Timer + { + private ShadowFiend m_Owner; + + public UnhideTimer( ShadowFiend owner ) : base( TimeSpan.FromSeconds( 30.0 ) ) + { + m_Owner = owner; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + foreach ( Mobile m in m_Owner.GetMobilesInRange( 3 ) ) + { + if ( m != m_Owner && m.Player && m.Hidden && m_Owner.CanBeHarmful( m ) && m.AccessLevel == AccessLevel.Player ) + m.Hidden = false; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs new file mode 100644 index 000000000..7e203e787 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/SkeletalKnight.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a skeletal corpse" )] + public class SkeletalKnight : BaseCreature + { + [Constructable] + public SkeletalKnight() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a skeletal knight"; + Body = 147; + BaseSoundID = 451; + + SetStr( 196, 250 ); + SetDex( 76, 95 ); + SetInt( 36, 60 ); + + SetHits( 118, 150 ); + + SetDamage( 8, 18 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Cold, 60 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 85.1, 100.0 ); + SetSkill( SkillName.Wrestling, 85.1, 95.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 40; + + switch ( Utility.Random( 6 ) ) + { + case 0: PackItem( new PlateArms() ); break; + case 1: PackItem( new PlateChest() ); break; + case 2: PackItem( new PlateGloves() ); break; + case 3: PackItem( new PlateGorget() ); break; + case 4: PackItem( new PlateLegs() ); break; + case 5: PackItem( new PlateHelm() ); break; + } + + PackItem( new Scimitar() ); + PackItem( new WoodenShield() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + + public SkeletalKnight( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs new file mode 100644 index 000000000..a0caa943c --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Skeleton.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a skeletal corpse" )] + public class Skeleton : BaseCreature + { + [Constructable] + public Skeleton() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a skeleton"; + Body = Utility.RandomList( 50, 56 ); + BaseSoundID = 0x48D; + + SetStr( 56, 80 ); + SetDex( 56, 75 ); + SetInt( 16, 40 ); + + SetHits( 34, 48 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 25, 40 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 5, 15 ); + + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 55.0 ); + + Fame = 450; + Karma = -450; + + VirtualArmor = 16; + + switch ( Utility.Random( 5 )) + { + case 0: PackItem( new BoneArms() ); break; + case 1: PackItem( new BoneChest() ); break; + case 2: PackItem( new BoneGloves() ); break; + case 3: PackItem( new BoneLegs() ); break; + case 4: PackItem( new BoneHelm() ); break; + } + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lesser; } } + + public Skeleton( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/SpawnedOrcishLord.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/SpawnedOrcishLord.cs new file mode 100644 index 000000000..08d74d624 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/SpawnedOrcishLord.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an orcish corpse" )] + public class SpawnedOrcishLord : OrcishLord + { + [Constructable] + public SpawnedOrcishLord() + { + Container pack = this.Backpack; + + if ( pack != null ) + pack.Delete(); + + NoKillAwards = true; + } + + public SpawnedOrcishLord( Serial serial ) : base( serial ) + { + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + c.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + NoKillAwards = true; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs new file mode 100644 index 000000000..977c94768 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/SpectralArmour.cs @@ -0,0 +1,97 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + + public class SpectralArmour : BaseCreature + { + public override bool DeleteCorpseOnDeath{ get{ return true; } } + + [Constructable] + public SpectralArmour() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = 637; + Hue = 0x8026; + Name = "spectral armour"; + + Buckler buckler = new Buckler(); + ChainCoif coif = new ChainCoif(); + PlateGloves gloves = new PlateGloves(); + + buckler.Hue = 0x835; buckler.Movable = false; + coif.Hue = 0x835; + gloves.Hue = 0x835; + + AddItem( buckler ); + AddItem( coif ); + AddItem( gloves ); + + SetStr( 101, 110 ); + SetDex( 101, 110 ); + SetInt( 101, 110 ); + + SetHits( 178, 201 ); + SetStam( 191, 200 ); + + SetDamage( 10, 22 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Cold, 25 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.Wrestling, 75.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100 ); + + VirtualArmor = 40; + Fame = 7000; + Karma = -7000; + } + + public override int GetIdleSound() + { + return 0x200; + } + + public override int GetAngerSound() + { + return 0x56; + } + + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + Gold gold = new Gold( Utility.RandomMinMax( 240, 375 ) ); + gold.MoveToWorld( Location, Map ); + + Effects.SendLocationEffect( Location, Map, 0x376A, 10, 1 ); + return true; + } + + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + + public SpectralArmour( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs new file mode 100644 index 000000000..f1336251f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/StoneGargoyle.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a gargoyle corpse" )] + public class StoneGargoyle : BaseCreature + { + [Constructable] + public StoneGargoyle() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a stone gargoyle"; + Body = 67; + BaseSoundID = 0x174; + + SetStr( 246, 275 ); + SetDex( 76, 95 ); + SetInt( 81, 105 ); + + SetHits( 148, 165 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 85.1, 100.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 50; + + PackItem( new IronIngot( 12 ) ); + + if ( 0.05 > Utility.RandomDouble() ) + PackItem( new GargoylesPickaxe() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.Gems, 1 ); + AddLoot( LootPack.Potions ); + } + + public override int TreasureMapLevel{ get{ return 2; } } + + public StoneGargoyle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs new file mode 100644 index 000000000..292c8bd53 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/StrongMongbat.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a mongbat corpse" )] + public class StrongMongbat : BaseCreature + { + [Constructable] + public StrongMongbat() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a mongbat"; + Body = 39; + BaseSoundID = 422; + + SetStr( 6, 10 ); + SetDex( 26, 38 ); + SetInt( 6, 14 ); + + SetHits( 4, 6 ); + SetMana( 0 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 25 ); + + SetSkill( SkillName.MagicResist, 15.1, 30.0 ); + SetSkill( SkillName.Tactics, 35.1, 50.0 ); + SetSkill( SkillName.Wrestling, 20.1, 35.0 ); + + Fame = 150; + Karma = -150; + + VirtualArmor = 10; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 71.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 6; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + + public StrongMongbat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Troll.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Troll.cs new file mode 100644 index 000000000..f511d5cc8 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Troll.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a troll corpse" )] + public class Troll : BaseCreature + { + [Constructable] + public Troll () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a troll"; + Body = Utility.RandomList( 53, 54 ); + BaseSoundID = 461; + + SetStr( 176, 205 ); + SetDex( 46, 65 ); + SetInt( 46, 70 ); + + SetHits( 106, 123 ); + + SetDamage( 8, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 5, 15 ); + SetResistance( ResistanceType.Energy, 5, 15 ); + + SetSkill( SkillName.MagicResist, 45.1, 60.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 40; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + public override int Meat{ get{ return 2; } } + + public Troll( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Humanoid/Melee/Zombie.cs b/Scripts/Mobiles/Monsters/Humanoid/Melee/Zombie.cs new file mode 100644 index 000000000..8abf1053c --- /dev/null +++ b/Scripts/Mobiles/Monsters/Humanoid/Melee/Zombie.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a rotting corpse" )] + public class Zombie : BaseCreature + { + [Constructable] + public Zombie() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a zombie"; + Body = 3; + BaseSoundID = 471; + + SetStr( 46, 70 ); + SetDex( 31, 50 ); + SetInt( 26, 40 ); + + SetHits( 28, 42 ); + + SetDamage( 3, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + + SetSkill( SkillName.MagicResist, 15.1, 40.0 ); + SetSkill( SkillName.Tactics, 35.1, 50.0 ); + SetSkill( SkillName.Wrestling, 35.1, 50.0 ); + + Fame = 600; + Karma = -600; + + VirtualArmor = 18; + + switch ( Utility.Random( 10 )) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + + public Zombie( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs b/Scripts/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs new file mode 100644 index 000000000..5980cd5c6 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Exodus/ExodusMinion.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a minion's corpse" )] + public class ExodusMinion : BaseCreature + { + private bool m_FieldActive; + public bool FieldActive{ get{ return m_FieldActive; } } + public bool CanUseField{ get{ return Hits >= HitsMax * 9 / 10; } } // TODO: an OSI bug prevents to verify this + + public override bool IsScaredOfScaryThings{ get{ return false; } } + public override bool IsScaryToPets{ get{ return true; } } + + [Constructable] + public ExodusMinion() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "exodus minion"; + Body = 0x2F5; + + SetStr( 851, 950 ); + SetDex( 71, 80 ); + SetInt( 61, 90 ); + + SetHits( 511, 570 ); + + SetDamage( 16, 22 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 15, 25); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 18000; + Karma = -18000; + VirtualArmor = 65; + + PackItem( new PowerCrystal() ); + PackItem( new ArcaneGem() ); + PackItem( new ClockworkAssembly() ); + + switch( Utility.Random( 3 ) ) + { + case 0: PackItem( new PowerCrystal() ); break; + case 1: PackItem( new ArcaneGem() ); break; + case 2: PackItem( new ClockworkAssembly() ); break; + } + + m_FieldActive = CanUseField; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Rich ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int GetIdleSound() + { + return 0x218; + } + + public override int GetAngerSound() + { + return 0x26C; + } + + public override int GetDeathSound() + { + return 0x211; + } + + public override int GetAttackSound() + { + return 0x232; + } + + public override int GetHurtSound() + { + return 0x140; + } + + public override void AlterMeleeDamageFrom( Mobile from, ref int damage ) + { + if ( m_FieldActive ) + damage = 0; // no melee damage when the field is up + } + + public override void AlterSpellDamageFrom( Mobile from, ref int damage ) + { + if ( !m_FieldActive ) + damage = 0; // no spell damage when the field is down + } + + public override void OnDamagedBySpell( Mobile from ) + { + if( from != null && from.Alive && 0.4 > Utility.RandomDouble() ) + { + SendEBolt( from ); + } + + if ( !m_FieldActive ) + { + // should there be an effect when spells nullifying is on? + this.FixedParticles( 0, 10, 0, 0x2522, EffectLayer.Waist ); + } + else if ( m_FieldActive && !CanUseField ) + { + m_FieldActive = false; + + // TODO: message and effect when field turns down; cannot be verified on OSI due to a bug + this.FixedParticles( 0x3735, 1, 30, 0x251F, EffectLayer.Waist ); + } + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( m_FieldActive ) + { + this.FixedParticles( 0x376A, 20, 10, 0x2530, EffectLayer.Waist ); + + PlaySound( 0x2F4 ); + + attacker.SendAsciiMessage( "Your weapon cannot penetrate the creature's magical barrier" ); + } + + if( attacker != null && attacker.Alive && attacker.Weapon is BaseRanged && 0.4 > Utility.RandomDouble() ) + { + SendEBolt( attacker ); + } + } + + public override void OnThink() + { + base.OnThink(); + + // TODO: an OSI bug prevents to verify if the field can regenerate or not + if ( !m_FieldActive && !IsHurt() ) + m_FieldActive = true; + } + + public override bool Move( Direction d ) + { + bool move = base.Move( d ); + + if ( move && m_FieldActive && this.Combatant != null ) + this.FixedParticles( 0, 10, 0, 0x2530, EffectLayer.Waist ); + + return move; + } + + public void SendEBolt( Mobile to ) + { + this.MovingParticles( to, 0x379F, 7, 0, false, true, 0xBE3, 0xFCB, 0x211 ); + to.PlaySound( 0x229 ); + this.DoHarmful( to ); + AOS.Damage( to, this, 50, 0, 0, 0, 0, 100 ); + } + + public ExodusMinion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + m_FieldActive = CanUseField; + + if( this.Name == "Exodus Minion" ) + this.Name = "exodus minion"; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs b/Scripts/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs new file mode 100644 index 000000000..6469c0900 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Exodus/ExodusOverseer.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "an overseer's corpse" )] + public class ExodusOverseer : BaseCreature + { + private bool m_FieldActive; + public bool FieldActive{ get{ return m_FieldActive; } } + public bool CanUseField{ get{ return Hits >= HitsMax * 9 / 10; } } // TODO: an OSI bug prevents to verify this + + public override bool IsScaredOfScaryThings{ get{ return false; } } + public override bool IsScaryToPets{ get{ return true; } } + + [Constructable] + public ExodusOverseer() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "exodus overseer"; + Body = 0x2F4; + + SetStr( 561, 650 ); + SetDex( 76, 95 ); + SetInt( 61, 90 ); + + SetHits( 331, 390 ); + + SetDamage( 13, 19 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 80.2, 98.0 ); + SetSkill( SkillName.Tactics, 80.2, 98.0 ); + SetSkill( SkillName.Wrestling, 80.2, 98.0 ); + + Fame = 10000; + Karma = -10000; + VirtualArmor = 50; + + if ( Utility.Random( 2 ) == 0 ) + PackItem( new PowerCrystal() ); + else + PackItem( new ArcaneGem() ); + + m_FieldActive = CanUseField; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override int GetIdleSound() + { + return 0xFD; + } + + public override int GetAngerSound() + { + return 0x26C; + } + + public override int GetDeathSound() + { + return 0x211; + } + + public override int GetAttackSound() + { + return 0x23B; + } + + public override int GetHurtSound() + { + return 0x140; + } + + public override void AlterMeleeDamageFrom( Mobile from, ref int damage ) + { + if ( m_FieldActive ) + damage = 0; // no melee damage when the field is up + } + + public override void AlterSpellDamageFrom( Mobile caster, ref int damage ) + { + if ( !m_FieldActive ) + damage = 0; // no spell damage when the field is down + } + + public override void OnDamagedBySpell( Mobile from ) + { + if( from != null && from.Alive && 0.4 > Utility.RandomDouble() ) + { + SendEBolt( from ); + } + + if ( !m_FieldActive ) + { + // should there be an effect when spells nullifying is on? + this.FixedParticles( 0, 10, 0, 0x2522, EffectLayer.Waist ); + } + else if ( m_FieldActive && !CanUseField ) + { + m_FieldActive = false; + + // TODO: message and effect when field turns down; cannot be verified on OSI due to a bug + this.FixedParticles( 0x3735, 1, 30, 0x251F, EffectLayer.Waist ); + } + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( m_FieldActive ) + { + this.FixedParticles( 0x376A, 20, 10, 0x2530, EffectLayer.Waist ); + + PlaySound( 0x2F4 ); + + attacker.SendAsciiMessage( "Your weapon cannot penetrate the creature's magical barrier" ); + } + + if( attacker != null && attacker.Alive && attacker.Weapon is BaseRanged && 0.4 > Utility.RandomDouble() ) + { + SendEBolt( attacker ); + } + } + + public override void OnThink() + { + base.OnThink(); + + // TODO: an OSI bug prevents to verify if the field can regenerate or not + if ( !m_FieldActive && !IsHurt() ) + m_FieldActive = true; + } + + public override bool Move( Direction d ) + { + bool move = base.Move( d ); + + if ( move && m_FieldActive && this.Combatant != null ) + this.FixedParticles( 0, 10, 0, 0x2530, EffectLayer.Waist ); + + return move; + } + + public void SendEBolt( Mobile to ) + { + this.MovingParticles( to, 0x379F, 7, 0, false, true, 0xBE3, 0xFCB, 0x211 ); + to.PlaySound( 0x229 ); + this.DoHarmful( to ); + AOS.Damage( to, this, 50, 0, 0, 0, 0, 100 ); + } + + public ExodusOverseer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + m_FieldActive = CanUseField; + + if( this.Name == "Exodus Overseer" ) + this.Name = "exodus overseer"; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/LBR/Jukas/JukaLord.cs b/Scripts/Mobiles/Monsters/LBR/Jukas/JukaLord.cs new file mode 100644 index 000000000..50513fd23 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Jukas/JukaLord.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "a juka corpse" )] // Why is this 'juka' and warriors 'jukan' ? :-( + public class JukaLord : BaseCreature + { + [Constructable] + public JukaLord() : base( AIType.AI_Archer, FightMode.Closest, 10, 3, 0.2, 0.4 ) + { + Name = "a juka lord"; + Body = 766; + + SetStr( 401, 500 ); + SetDex( 81, 100 ); + SetInt( 151, 200 ); + + SetHits( 241, 300 ); + + SetDamage( 10, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 45, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 20, 25 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Anatomy, 90.1, 100.0 ); + SetSkill( SkillName.Archery, 95.1, 100.0 ); + SetSkill( SkillName.Healing, 80.1, 100.0 ); + SetSkill( SkillName.MagicResist, 120.1, 130.0 ); + SetSkill( SkillName.Swords, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 95.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 28; + + Container pack = new Backpack(); + + pack.DropItem( new Arrow( Utility.RandomMinMax( 25, 35 ) ) ); + pack.DropItem( new Arrow( Utility.RandomMinMax( 25, 35 ) ) ); + pack.DropItem( new Bandage( Utility.RandomMinMax( 5, 15 ) ) ); + pack.DropItem( new Bandage( Utility.RandomMinMax( 5, 15 ) ) ); + pack.DropItem( Loot.RandomGem() ); + pack.DropItem( new ArcaneGem() ); + + PackItem( pack ); + + AddItem( new JukaBow() ); + + // TODO: Bandage self + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + if ( from != null && !willKill && amount > 5 && from.Player && 5 > Utility.Random( 100 ) ) + { + string[] toSay = new string[] + { + "{0}!! You will have to do better than that!", + "{0}!! Prepare to meet your doom!", + "{0}!! My armies will crush you!", + "{0}!! You will pay for that!" + }; + + this.Say( true, String.Format( toSay[Utility.Random( toSay.Length )], from.Name ) ); + } + + base.OnDamage( amount, from, willKill ); + } + + public override int GetIdleSound() + { + return 0x262; + } + + public override int GetAngerSound() + { + return 0x263; + } + + public override int GetHurtSound() + { + return 0x1D0; + } + + public override int GetDeathSound() + { + return 0x28D; + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + + public JukaLord( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/LBR/Jukas/JukaMage.cs b/Scripts/Mobiles/Monsters/LBR/Jukas/JukaMage.cs new file mode 100644 index 000000000..9cf2297d6 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Jukas/JukaMage.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; +using Server.Spells; + +namespace Server.Mobiles +{ + [CorpseName( "a juka corpse" )] // Why is this 'juka' and warriors 'jukan' ? :-( + public class JukaMage : BaseCreature + { + [Constructable] + public JukaMage() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a juka mage"; + Body = 765; + + SetStr( 201, 300 ); + SetDex( 71, 90 ); + SetInt( 451, 500 ); + + SetHits( 121, 180 ); + + SetDamage( 4, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 30 ); + SetResistance( ResistanceType.Fire, 35, 45 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.Anatomy, 80.1, 90.0 ); + SetSkill( SkillName.EvalInt, 80.2, 100.0 ); + SetSkill( SkillName.Magery, 99.1, 100.0 ); + SetSkill( SkillName.Meditation, 80.2, 100.0 ); + SetSkill( SkillName.MagicResist, 140.1, 150.0 ); + SetSkill( SkillName.Tactics, 80.1, 90.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 16; + + Container bag = new Bag(); + + int count = Utility.RandomMinMax( 10, 20 ); + + for ( int i = 0; i < count; ++i ) + { + Item item = Loot.RandomReagent(); + + if ( item == null ) + continue; + + if ( !bag.TryDropItem( this, item, false ) ) + item.Delete(); + } + + PackItem( bag ); + + PackItem( new ArcaneGem() ); + + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 2, 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override int GetIdleSound() + { + return 0x1AC; + } + + public override int GetAngerSound() + { + return 0x1CD; + } + + public override int GetHurtSound() + { + return 0x1D0; + } + + public override int GetDeathSound() + { + return 0x28D; + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + + private DateTime m_NextAbilityTime; + + public override void OnThink() + { + if ( DateTime.Now >= m_NextAbilityTime ) + { + JukaLord toBuff = null; + + foreach ( Mobile m in this.GetMobilesInRange( 8 ) ) + { + if ( m is JukaLord && IsFriend( m ) && m.Combatant != null && CanBeBeneficial( m ) && m.CanBeginAction( typeof( JukaMage ) ) && InLOS( m ) ) + { + toBuff = (JukaLord)m; + break; + } + } + + if ( toBuff != null ) + { + if ( CanBeBeneficial( toBuff ) && toBuff.BeginAction( typeof( JukaMage ) ) ) + { + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 30, 60 ) ); + + toBuff.Say( true, "Give me the power to destroy my enemies!" ); + this.Say( true, "Fight well my lord!" ); + + DoBeneficial( toBuff ); + + object[] state = new object[]{ toBuff, toBuff.HitsMaxSeed, toBuff.RawStr, toBuff.RawDex }; + + SpellHelper.Turn( this, toBuff ); + + int toScale = toBuff.HitsMaxSeed; + + if ( toScale > 0 ) + { + toBuff.HitsMaxSeed += AOS.Scale( toScale, 75 ); + toBuff.Hits += AOS.Scale( toScale, 75 ); + } + + toScale = toBuff.RawStr; + + if ( toScale > 0 ) + toBuff.RawStr += AOS.Scale( toScale, 50 ); + + toScale = toBuff.RawDex; + + if ( toScale > 0 ) + { + toBuff.RawDex += AOS.Scale( toScale, 50 ); + toBuff.Stam += AOS.Scale( toScale, 50 ); + } + + toBuff.Hits = toBuff.Hits; + toBuff.Stam = toBuff.Stam; + + toBuff.FixedParticles( 0x375A, 10, 15, 5017, EffectLayer.Waist ); + toBuff.PlaySound( 0x1EE ); + + Timer.DelayCall( TimeSpan.FromSeconds( 20.0 ), new TimerStateCallback( Unbuff ), state ); + } + } + else + { + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 2, 5 ) ); + } + } + + base.OnThink(); + } + + private void Unbuff( object state ) + { + object[] states = (object[])state; + + JukaLord toDebuff = (JukaLord)states[0]; + + toDebuff.EndAction( typeof( JukaMage ) ); + + if ( toDebuff.Deleted ) + return; + + toDebuff.HitsMaxSeed = (int)states[1]; + toDebuff.RawStr = (int)states[2]; + toDebuff.RawDex = (int)states[3]; + + toDebuff.Hits = toDebuff.Hits; + toDebuff.Stam = toDebuff.Stam; + } + + public JukaMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs b/Scripts/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs new file mode 100644 index 000000000..ae4aebda3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Jukas/JukaWarrior.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "a jukan corpse" )] + public class JukaWarrior : BaseCreature + { + [Constructable] + public JukaWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a juka warrior"; + Body = 764; + + SetStr( 251, 350 ); + SetDex( 61, 80 ); + SetInt( 101, 150 ); + + SetHits( 151, 210 ); + + SetDamage( 7, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.Anatomy, 80.1, 90.0 ); + SetSkill( SkillName.Fencing, 80.1, 90.0 ); + SetSkill( SkillName.Macing, 80.1, 90.0 ); + SetSkill( SkillName.MagicResist, 120.1, 130.0 ); + SetSkill( SkillName.Swords, 80.1, 90.0 ); + SetSkill( SkillName.Tactics, 80.1, 90.0 ); + SetSkill( SkillName.Wrestling, 80.1, 90.0 ); + + Fame = 10000; + Karma = -10000; + + VirtualArmor = 22; + + if ( Utility.RandomDouble() < 0.1 ) + PackItem( new ArcaneGem() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Gems, 1 ); + } + + public override int GetIdleSound() + { + return 0x1AC; + } + + public override int GetAngerSound() + { + return 0x1CD; + } + + public override int GetHurtSound() + { + return 0x1D0; + } + + public override int GetDeathSound() + { + return 0x28D; + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.2 < Utility.RandomDouble() ) + return; + + switch ( Utility.Random( 3 ) ) + { + case 0: + { + defender.SendLocalizedMessage( 1004014 ); // You have been stunned! + defender.Freeze( TimeSpan.FromSeconds( 4.0 ) ); + break; + } + case 1: + { + defender.SendAsciiMessage( "You have been hit by a paralyzing blow!" ); + defender.Freeze( TimeSpan.FromSeconds( 3.0 ) ); + break; + } + case 2: + { + AOS.Damage( defender, this, Utility.Random( 10, 5 ), 100, 0, 0, 0, 0 ); + defender.SendAsciiMessage( "You have been hit by a critical strike!" ); + break; + } + } + } + + public JukaWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs b/Scripts/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs new file mode 100644 index 000000000..da4d3e63f --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Meers/MeerCaptain.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Spells; + +namespace Server.Mobiles +{ + [CorpseName( "a meer corpse" )] + public class MeerCaptain : BaseCreature + { + [Constructable] + public MeerCaptain() : base( AIType.AI_Archer, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + Name = "a meer captain"; + Body = 773; + + SetStr( 96, 110 ); + SetDex( 186, 200 ); + SetInt( 96, 110 ); + + SetHits( 58, 66 ); + + SetDamage( 5, 15 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 35, 45 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.Archery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 91.0, 100.0 ); + SetSkill( SkillName.Swords, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 91.0, 100.0 ); + SetSkill( SkillName.Wrestling, 80.9, 89.9 ); + + Fame = 2000; + Karma = 5000; + + VirtualArmor = 28; + + Container pack = new Backpack(); + + pack.DropItem( new Bolt( Utility.RandomMinMax( 10, 20 ) ) ); + pack.DropItem( new Bolt( Utility.RandomMinMax( 10, 20 ) ) ); + + switch ( Utility.Random( 6 ) ) + { + case 0: pack.DropItem( new Broadsword() ); break; + case 1: pack.DropItem( new Cutlass() ); break; + case 2: pack.DropItem( new Katana() ); break; + case 3: pack.DropItem( new Longsword() ); break; + case 4: pack.DropItem( new Scimitar() ); break; + case 5: pack.DropItem( new VikingSword() ); break; + } + + Container bag = new Bag(); + + int count = Utility.RandomMinMax( 10, 20 ); + + for ( int i = 0; i < count; ++i ) + { + Item item = Loot.RandomReagent(); + + if ( item == null ) + continue; + + if ( !bag.TryDropItem( this, item, false ) ) + item.Delete(); + } + + pack.DropItem( bag ); + + AddItem( new Crossbow() ); + PackItem( pack ); + + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 2, 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BardImmune{ get{ return !Core.AOS; } } + public override bool CanRummageCorpses{ get{ return true; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override int GetHurtSound() + { + return 0x14D; + } + + public override int GetDeathSound() + { + return 0x314; + } + + public override int GetAttackSound() + { + return 0x75; + } + + private DateTime m_NextAbilityTime; + + public override void OnThink() + { + if ( Combatant != null && this.MagicDamageAbsorb < 1 ) + { + this.MagicDamageAbsorb = Utility.RandomMinMax( 5, 7 ); + this.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist ); + this.PlaySound( 0x1E9 ); + } + + if ( DateTime.Now >= m_NextAbilityTime ) + { + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 10, 15 ) ); + + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 8 ) ) + { + if ( m is MeerWarrior && IsFriend( m ) && CanBeBeneficial( m ) && m.Hits < m.HitsMax && !m.Poisoned && !MortalStrike.IsWounded( m ) ) + list.Add( m ); + } + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + DoBeneficial( m ); + + int toHeal = Utility.RandomMinMax( 20, 30 ); + + SpellHelper.Turn( this, m ); + + m.Heal( toHeal ); + + m.FixedParticles( 0x376A, 9, 32, 5030, EffectLayer.Waist ); + m.PlaySound( 0x202 ); + } + } + + base.OnThink(); + } + + public MeerCaptain( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/LBR/Meers/MeerEternal.cs b/Scripts/Mobiles/Monsters/LBR/Meers/MeerEternal.cs new file mode 100644 index 000000000..36affbec0 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Meers/MeerEternal.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Spells; + +namespace Server.Mobiles +{ + [CorpseName( "a meer's corpse" )] + public class MeerEternal : BaseCreature + { + [Constructable] + public MeerEternal() : base( AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a meer eternal"; + Body = 772; + + SetStr( 416, 505 ); + SetDex( 146, 165 ); + SetInt( 566, 655 ); + + SetHits( 250, 303 ); + + SetDamage( 11, 13 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 45, 55 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 150.5, 200.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 18000; + Karma = 18000; + + VirtualArmor = 34; + + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 2, 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + AddLoot( LootPack.HighScrolls, 2 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + public override bool CanRummageCorpses{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return Core.AOS ? 5 : 4; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override int GetHurtSound() + { + return 0x167; + } + + public override int GetDeathSound() + { + return 0xBC; + } + + public override int GetAttackSound() + { + return 0x28B; + } + + private DateTime m_NextAbilityTime; + + private void DoAreaLeech() + { + m_NextAbilityTime += TimeSpan.FromSeconds( 2.5 ); + + this.Say( true, "Beware, mortals! You have provoked my wrath!" ); + this.FixedParticles( 0x376A, 10, 10, 9537, 33, 0, EffectLayer.Waist ); + + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerCallback( DoAreaLeech_Finish ) ); + } + + private void DoAreaLeech_Finish() + { + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 6 ) ) + { + if ( this.CanBeHarmful( m ) && this.IsEnemy( m ) ) + list.Add( m ); + } + + if ( list.Count == 0 ) + { + this.Say( true, "Bah! You have escaped my grasp this time, mortal!" ); + } + else + { + double scalar; + + if ( list.Count == 1 ) + scalar = 0.75; + else if ( list.Count == 2 ) + scalar = 0.50; + else + scalar = 0.25; + + for ( int i = 0; i < list.Count; ++i ) + { + Mobile m = (Mobile)list[i]; + + int damage = (int)(m.Hits * scalar); + + damage += Utility.RandomMinMax( -5, 5 ); + + if ( damage < 1 ) + damage = 1; + + m.MovingParticles( this, 0x36F4, 1, 0, false, false, 32, 0, 9535, 1, 0, (EffectLayer)255, 0x100 ); + m.MovingParticles( this, 0x0001, 1, 0, false, true, 32, 0, 9535, 9536, 0, (EffectLayer)255, 0 ); + + this.DoHarmful( m ); + this.Hits += AOS.Damage( m, this, damage, 100, 0, 0, 0, 0 ); + } + + this.Say( true, "If I cannot cleanse thy soul, I will destroy it!" ); + } + } + + private void DoFocusedLeech( Mobile combatant, string message ) + { + this.Say( true, message ); + + Timer.DelayCall( TimeSpan.FromSeconds( 0.5 ), new TimerStateCallback( DoFocusedLeech_Stage1 ), combatant ); + } + + private void DoFocusedLeech_Stage1( object state ) + { + Mobile combatant = (Mobile)state; + + if ( this.CanBeHarmful( combatant ) ) + { + this.MovingParticles( combatant, 0x36FA, 1, 0, false, false, 1108, 0, 9533, 1, 0, (EffectLayer)255, 0x100 ); + this.MovingParticles( combatant, 0x0001, 1, 0, false, true, 1108, 0, 9533, 9534, 0, (EffectLayer)255, 0 ); + this.PlaySound( 0x1FB ); + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( DoFocusedLeech_Stage2 ), combatant ); + } + } + + private void DoFocusedLeech_Stage2( object state ) + { + Mobile combatant = (Mobile)state; + + if ( this.CanBeHarmful( combatant ) ) + { + combatant.MovingParticles( this, 0x36F4, 1, 0, false, false, 32, 0, 9535, 1, 0, (EffectLayer)255, 0x100 ); + combatant.MovingParticles( this, 0x0001, 1, 0, false, true, 32, 0, 9535, 9536, 0, (EffectLayer)255, 0 ); + + this.PlaySound( 0x209 ); + this.DoHarmful( combatant ); + this.Hits += AOS.Damage( combatant, this, Utility.RandomMinMax( 30, 40 ) - (Core.AOS ? 0 : 10), 100, 0, 0, 0, 0 ); + } + } + + public override void OnThink() + { + if ( DateTime.Now >= m_NextAbilityTime ) + { + Mobile combatant = this.Combatant; + + if ( combatant != null && combatant.Map == this.Map && combatant.InRange( this, 12 ) ) + { + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 10, 15 ) ); + + int ability = Utility.Random( 4 ); + + switch ( ability ) + { + case 0: DoFocusedLeech( combatant, "Thine essence will fill my withering body with strength!" ); break; + case 1: DoFocusedLeech( combatant, "I rebuke thee, worm, and cleanse thy vile spirit of its tainted blood!" ); break; + case 2: DoFocusedLeech( combatant, "I devour your life's essence to strengthen my resolve!" ); break; + case 3: DoAreaLeech(); break; + // TODO: Resurrect ability + } + } + } + + base.OnThink(); + } + + public MeerEternal( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/LBR/Meers/MeerMage.cs b/Scripts/Mobiles/Monsters/LBR/Meers/MeerMage.cs new file mode 100644 index 000000000..b553996c7 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Meers/MeerMage.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Spells; + +namespace Server.Mobiles +{ + [CorpseName( "a meer's corpse" )] + public class MeerMage : BaseCreature + { + [Constructable] + public MeerMage() : base( AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a meer mage"; + Body = 770; + + SetStr( 171, 200 ); + SetDex( 126, 145 ); + SetInt( 276, 305 ); + + SetHits( 103, 120 ); + + SetDamage( 24, 26 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 50 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.EvalInt, 100.0 ); + SetSkill( SkillName.Magery, 70.1, 80.0 ); + SetSkill( SkillName.Meditation, 85.1, 95.0 ); + SetSkill( SkillName.MagicResist, 80.1, 100.0 ); + SetSkill( SkillName.Tactics, 70.1, 90.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 8000; + Karma = 8000; + + VirtualArmor = 16; + + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 2, 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.MedScrolls, 2 ); + // TODO: Daemon bone ... + } + + public override bool AutoDispel{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 3; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override int GetHurtSound() + { + return 0x14D; + } + + public override int GetDeathSound() + { + return 0x314; + } + + public override int GetAttackSound() + { + return 0x75; + } + + private DateTime m_NextAbilityTime; + + public override void OnThink() + { + if ( DateTime.Now >= m_NextAbilityTime ) + { + Mobile combatant = this.Combatant; + + if ( combatant != null && combatant.Map == this.Map && combatant.InRange( this, 12 ) && IsEnemy( combatant ) && !UnderEffect( combatant ) ) + { + m_NextAbilityTime = DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 20, 30 ) ); + + // TODO: Forest summon ability + + this.Say( true, "I call a plague of insects to sting your flesh!" ); + + m_Table[combatant] = Timer.DelayCall( TimeSpan.FromSeconds( 0.5 ), TimeSpan.FromSeconds( 7.0 ), new TimerStateCallback( DoEffect ), new object[]{ combatant, 0 } ); + } + } + + base.OnThink(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool UnderEffect( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void StopEffect( Mobile m, bool message ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + { + if ( message ) + m.PublicOverheadMessage( Network.MessageType.Emote, m.SpeechHue, true, "* The open flame begins to scatter the swarm of insects *" ); + + t.Stop(); + m_Table.Remove( m ); + } + } + + public void DoEffect( object state ) + { + object[] states = (object[])state; + + Mobile m = (Mobile)states[0]; + int count = (int)states[1]; + + if ( !m.Alive ) + { + StopEffect( m, false ); + } + else + { + Torch torch = m.FindItemOnLayer( Layer.TwoHanded ) as Torch; + + if ( torch != null && torch.Burning ) + { + StopEffect( m, true ); + } + else + { + if ( (count % 4) == 0 ) + { + m.LocalOverheadMessage( Network.MessageType.Emote, m.SpeechHue, true, "* The swarm of insects bites and stings your flesh! *" ); + m.NonlocalOverheadMessage( Network.MessageType.Emote, m.SpeechHue, true, String.Format( "* {0} is stung by a swarm of insects *", m.Name ) ); + } + + m.FixedParticles( 0x91C, 10, 180, 9539, EffectLayer.Waist ); + m.PlaySound( 0x00E ); + m.PlaySound( 0x1BC ); + + AOS.Damage( m, this, Utility.RandomMinMax( 30, 40 ) - (Core.AOS ? 0 : 10), 100, 0, 0, 0, 0 ); + + states[1] = count + 1; + + if ( !m.Alive ) + StopEffect( m, false ); + } + } + } + + public MeerMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs b/Scripts/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs new file mode 100644 index 000000000..8f2d6bba1 --- /dev/null +++ b/Scripts/Mobiles/Monsters/LBR/Meers/MeerWarrior.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using Server; +using Server.Misc; +using Server.Items; +using Server.Spells; + +namespace Server.Mobiles +{ + [CorpseName( "a meer corpse" )] + public class MeerWarrior : BaseCreature + { + [Constructable] + public MeerWarrior() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a meer warrior"; + Body = 771; + + SetStr( 86, 100 ); + SetDex( 186, 200 ); + SetInt( 86, 100 ); + + SetHits( 52, 60 ); + + SetDamage( 12, 19 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 5, 15 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 91.0, 100.0 ); + SetSkill( SkillName.Tactics, 91.0, 100.0 ); + SetSkill( SkillName.Wrestling, 91.0, 100.0 ); + + VirtualArmor = 22; + + Fame = 2000; + Karma = 5000; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool BardImmune{ get{ return !Core.AOS; } } + public override bool CanRummageCorpses{ get{ return true; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + if ( from != null && !willKill && amount > 3 && from != null && !InRange( from, 7 ) ) + { + this.MovingEffect( from, 0xF51, 10, 0, false, false ); + SpellHelper.Damage( TimeSpan.FromSeconds( 1.0 ), from, this, Utility.RandomMinMax( 30, 40 ) - (Core.AOS ? 0 : 10), 100, 0, 0, 0, 0 ); + } + + base.OnDamage( amount, from, willKill ); + } + + public override int GetHurtSound() + { + return 0x156; + } + + public override int GetDeathSound() + { + return 0x15C; + } + + public MeerWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Mammal/Melee/HellHound.cs b/Scripts/Mobiles/Monsters/Mammal/Melee/HellHound.cs new file mode 100644 index 000000000..b44eac130 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Mammal/Melee/HellHound.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a hell hound corpse" )] + public class HellHound : BaseCreature + { + [Constructable] + public HellHound() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a hell hound"; + Body = 98; + BaseSoundID = 229; + + SetStr( 102, 150 ); + SetDex( 81, 105 ); + SetInt( 36, 60 ); + + SetHits( 66, 125 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Fire, 80 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + Fame = 3400; + Karma = -3400; + + VirtualArmor = 30; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 85.5; + + PackItem( new SulfurousAsh( 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + } + + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Canine; } } + + public HellHound( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs b/Scripts/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs new file mode 100644 index 000000000..1f3a7a19a --- /dev/null +++ b/Scripts/Mobiles/Monsters/Mammal/Melee/VorpalBunny.cs @@ -0,0 +1,145 @@ +using System; +using Server.Mobiles; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a vorpal bunny corpse" )] + public class VorpalBunny : BaseCreature + { + [Constructable] + public VorpalBunny() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a vorpal bunny"; + Body = 205; + Hue = 0x480; + + SetStr( 15 ); + SetDex( 2000 ); + SetInt( 1000 ); + + SetHits( 2000 ); + SetStam( 500 ); + SetMana( 0 ); + + SetDamage( 1 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetSkill( SkillName.MagicResist, 200.0 ); + SetSkill( SkillName.Tactics, 5.0 ); + SetSkill( SkillName.Wrestling, 5.0 ); + + Fame = 1000; + Karma = 0; + + VirtualArmor = 4; + + int carrots = Utility.RandomMinMax( 5, 10 ); + PackItem( new Carrot( carrots ) ); + + if ( Utility.Random( 5 ) == 0 ) + PackItem( new BrightlyColoredEggs() ); + + PackStatue(); + + DelayBeginTunnel(); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich, 2 ); + } + + public class BunnyHole : Item + { + public BunnyHole() : base( 0x913 ) + { + Movable = false; + Hue = 1; + Name = "a mysterious rabbit hole"; + + Timer.DelayCall( TimeSpan.FromSeconds( 40.0 ), new TimerCallback( Delete ) ); + } + + public BunnyHole( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize(writer); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + } + + public virtual void DelayBeginTunnel() + { + Timer.DelayCall( TimeSpan.FromMinutes( 3.0 ), new TimerCallback( BeginTunnel ) ); + } + + public virtual void BeginTunnel() + { + if ( Deleted ) + return; + + new BunnyHole().MoveToWorld( Location, Map ); + + Frozen = true; + Say( "* The bunny begins to dig a tunnel back to its underground lair *" ); + PlaySound( 0x247 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerCallback( Delete ) ); + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 1; } } + public override bool BardImmune{ get{ return !Core.AOS; } } + + public VorpalBunny( Serial serial ) : base( serial ) + { + } + + public override int GetAttackSound() + { + return 0xC9; + } + + public override int GetHurtSound() + { + return 0xCA; + } + + public override int GetDeathSound() + { + return 0xCB; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize(writer); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + DelayBeginTunnel(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Magic/DarkWisp.cs b/Scripts/Mobiles/Monsters/Misc/Magic/DarkWisp.cs new file mode 100644 index 000000000..4e932ad14 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Magic/DarkWisp.cs @@ -0,0 +1,84 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Factions; + +namespace Server.Mobiles +{ + [CorpseName( "a wisp corpse" )] + public class DarkWisp : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Wisp; } } + + public override Ethics.Ethic EthicAllegiance { get { return Ethics.Ethic.Evil; } } + + public override TimeSpan ReacquireDelay { get { return TimeSpan.FromSeconds( 1.0 ); } } + + [Constructable] + public DarkWisp() : base( AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a wisp"; + Body = 165; + BaseSoundID = 466; + + SetStr( 196, 225 ); + SetDex( 196, 225 ); + SetInt( 196, 225 ); + + SetHits( 118, 135 ); + + SetDamage( 17, 18 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 20, 40 ); + SetResistance( ResistanceType.Cold, 10, 30 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 50, 70 ); + + SetSkill( SkillName.EvalInt, 80.0 ); + SetSkill( SkillName.Magery, 80.0 ); + SetSkill( SkillName.MagicResist, 80.0 ); + SetSkill( SkillName.Tactics, 80.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 40; + + AddItem( new LightSource() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public DarkWisp( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs b/Scripts/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs new file mode 100644 index 000000000..cc90ccae2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Magic/EtherealWarrior.cs @@ -0,0 +1,123 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ethereal warrior corpse" )] + public class EtherealWarrior : BaseCreature + { + public override bool InitialInnocent{ get{ return true; } } + + [Constructable] + public EtherealWarrior() : base( AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "ethereal warrior" ); + Body = 123; + + SetStr( 586, 785 ); + SetDex( 177, 255 ); + SetInt( 351, 450 ); + + SetHits( 352, 471 ); + + SetDamage( 13, 19 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 80, 90 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Anatomy, 50.1, 75.0 ); + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 99.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 7000; + Karma = 7000; + + VirtualArmor = 120; + } + + public override int TreasureMapLevel{ get{ return Core.AOS ? 5 : 0; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 3 ); + AddLoot( LootPack.Gems ); + } + // TODO: Resurrect good players. + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override int Feathers{ get{ return 100; } } + + public override int GetAngerSound() + { + return 0x2F8; + } + + public override int GetIdleSound() + { + return 0x2F8; + } + + public override int GetAttackSound() + { + return Utility.Random( 0x2F5, 2 ); + } + + public override int GetHurtSound() + { + return 0x2F9; + } + + public override int GetDeathSound() + { + return 0x2F7; + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + defender.Damage( Utility.Random( 10, 10 ), this ); + defender.Stam -= Utility.Random( 10, 10 ); + defender.Mana -= Utility.Random( 10, 10 ); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + attacker.Damage( Utility.Random( 10, 10 ), this ); + attacker.Stam -= Utility.Random( 10, 10 ); + attacker.Mana -= Utility.Random( 10, 10 ); + } + + public EtherealWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Magic/Pixie.cs b/Scripts/Mobiles/Monsters/Misc/Magic/Pixie.cs new file mode 100644 index 000000000..40ec33f2e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Magic/Pixie.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "a pixie corpse" )] + public class Pixie : BaseCreature + { + public override bool InitialInnocent{ get{ return true; } } + + [Constructable] + public Pixie() : base( AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "pixie" ); + Body = 128; + BaseSoundID = 0x467; + + SetStr( 21, 30 ); + SetDex( 301, 400 ); + SetInt( 201, 250 ); + + SetHits( 13, 18 ); + + SetDamage( 9, 15 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 80, 90 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 100.5, 150.0 ); + SetSkill( SkillName.Tactics, 10.1, 20.0 ); + SetSkill( SkillName.Wrestling, 10.1, 12.5 ); + + Fame = 7000; + Karma = 7000; + + VirtualArmor = 100; + if ( 0.02 > Utility.RandomDouble() ) + PackStatue(); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.Gems, 2 ); + } + + public override HideType HideType{ get{ return HideType.Spined; } } + public override int Hides{ get{ return 5; } } + public override int Meat{ get{ return 1; } } + + public Pixie( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs b/Scripts/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs new file mode 100644 index 000000000..78a17294e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Magic/ShadowWisp.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a wisp corpse" )] + public class ShadowWisp : BaseCreature + { + [Constructable] + public ShadowWisp() : base( AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.3, 0.6 ) + { + Name = "a shadow wisp"; + Body = 165; + BaseSoundID = 466; + + SetStr( 16, 40 ); + SetDex( 16, 45 ); + SetInt( 11, 25 ); + + SetHits( 10, 24 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 15, 20 ); + + SetSkill( SkillName.EvalInt, 40.0 ); + SetSkill( SkillName.Magery, 50.0 ); + SetSkill( SkillName.Meditation, 40.0 ); + SetSkill( SkillName.MagicResist, 10.0 ); + SetSkill( SkillName.Tactics, 0.1, 15.0 ); + SetSkill( SkillName.Wrestling, 25.1, 40.0 ); + + Fame = 500; + Karma = -500; + + VirtualArmor = 18; + + AddItem( new LightSource() ); + + switch ( Utility.Random( 10 )) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + } + + public ShadowWisp( Serial serial ) : base( serial ) + { + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Magic/Wisp.cs b/Scripts/Mobiles/Monsters/Misc/Magic/Wisp.cs new file mode 100644 index 000000000..9485caaf4 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Magic/Wisp.cs @@ -0,0 +1,84 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Factions; + +namespace Server.Mobiles +{ + [CorpseName( "a wisp corpse" )] + public class Wisp : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Wisp; } } + + public override Faction FactionAllegiance{ get{ return CouncilOfMages.Instance; } } + public override Ethics.Ethic EthicAllegiance { get { return Ethics.Ethic.Hero; } } + + public override TimeSpan ReacquireDelay { get { return TimeSpan.FromSeconds( 1.0 ); } } + + [Constructable] + public Wisp() : base( AIType.AI_Mage, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = "a wisp"; + Body = 58; + BaseSoundID = 466; + + SetStr( 196, 225 ); + SetDex( 196, 225 ); + SetInt( 196, 225 ); + + SetHits( 118, 135 ); + + SetDamage( 17, 18 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 20, 40 ); + SetResistance( ResistanceType.Cold, 10, 30 ); + SetResistance( ResistanceType.Poison, 5, 10 ); + SetResistance( ResistanceType.Energy, 50, 70 ); + + SetSkill( SkillName.EvalInt, 80.0 ); + SetSkill( SkillName.Magery, 80.0 ); + SetSkill( SkillName.MagicResist, 80.0 ); + SetSkill( SkillName.Tactics, 80.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 4000; + Karma = 0; + + VirtualArmor = 40; + + AddItem( new LightSource() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public Wisp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs b/Scripts/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs new file mode 100644 index 000000000..a158ed690 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a blade spirit corpse" )] + public class BladeSpirits : BaseCreature + { + public override bool DeleteCorpseOnDeath { get { return Core.AOS; } } + public override bool IsHouseSummonable { get { return true; } } + + public override double DispelDifficulty { get { return 0.0; } } + public override double DispelFocus { get { return 20.0; } } + + public override double GetValueFrom( Mobile m, FightMode acqType, bool bPlayerOnly ) + { + return ( m.Str + m.Skills[SkillName.Tactics].Value ) / Math.Max( GetDistanceToSqrt( m ), 1.0 ); + } + + [Constructable] + public BladeSpirits() + : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.3, 0.6 ) + { + Name = "a blade spirit"; + Body = 574; + + SetStr( 150 ); + SetDex( 150 ); + SetInt( 100 ); + + SetHits( ( Core.SE ) ? 160 : 80 ); + SetStam( 250 ); + SetMana( 0 ); + + SetDamage( 10, 14 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Poison, 20 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 70.0 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + Fame = 0; + Karma = 0; + + VirtualArmor = 40; + ControlSlots = ( Core.SE ) ? 2 : 1; + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune { get { return Poison.Lethal; } } + + public override int GetAngerSound() + { + return 0x23A; + } + + public override int GetAttackSound() + { + return 0x3B8; + } + + public override int GetHurtSound() + { + return 0x23A; + } + + public override void OnThink() + { + if ( Core.SE && Summoned ) + { + ArrayList spirtsOrVortexes = new ArrayList(); + + foreach ( Mobile m in GetMobilesInRange( 5 ) ) + { + if ( m is EnergyVortex || m is BladeSpirits ) + { + if ( ( (BaseCreature) m ).Summoned ) + spirtsOrVortexes.Add( m ); + } + } + + while ( spirtsOrVortexes.Count > 6 ) + { + int index = Utility.Random( spirtsOrVortexes.Count ); + Dispel( ( (Mobile) spirtsOrVortexes[index] ) ); + spirtsOrVortexes.RemoveAt( index ); + } + } + + base.OnThink(); + } + + public BladeSpirits( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/Centaur.cs b/Scripts/Mobiles/Monsters/Misc/Melee/Centaur.cs new file mode 100644 index 000000000..14ade4fe2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/Centaur.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a centaur corpse" )] + public class Centaur : BaseCreature + { + [Constructable] + public Centaur() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "centaur" ); + Body = 101; + BaseSoundID = 679; + + SetStr( 202, 300 ); + SetDex( 104, 260 ); + SetInt( 91, 100 ); + + SetHits( 130, 172 ); + + SetDamage( 13, 24 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 35, 45 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 45, 55 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.Anatomy, 95.1, 115.0 ); + SetSkill( SkillName.Archery, 95.1, 100.0 ); + SetSkill( SkillName.MagicResist, 50.3, 80.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 95.1, 100.0 ); + + Fame = 6500; + Karma = 0; + + VirtualArmor = 50; + AddItem( new Bow() ); + PackItem( new Arrow( Utility.RandomMinMax( 80, 90 ) ) ); // OSI it is different: in a sub backpack, this is probably just a limitation of their engine + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 8; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public Centaur( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 678 ) + BaseSoundID = 679; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs b/Scripts/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs new file mode 100644 index 000000000..4ae771d2b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs @@ -0,0 +1,130 @@ +using System; +using Server; +using Server.Items; +using System.Collections; + +namespace Server.Mobiles +{ + [CorpseName( "an energy vortex corpse" )] + public class EnergyVortex : BaseCreature + { + public override bool DeleteCorpseOnDeath { get { return Summoned; } } + public override bool AlwaysMurderer{ get{ return true; } } // Or Llama vortices will appear gray. + + public override double DispelDifficulty { get { return 80.0; } } + public override double DispelFocus { get { return 20.0; } } + + public override double GetValueFrom( Mobile m, FightMode acqType, bool bPlayerOnly ) + { + return ( m.Int + m.Skills[SkillName.Magery].Value ) / Math.Max( GetDistanceToSqrt( m ), 1.0 ); + } + + [Constructable] + public EnergyVortex() + : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an energy vortex"; + + if ( Core.SE && 0.02 > Utility.RandomDouble() ) // Tested on OSI, but is this right? Who knows. + { + // Llama vortex! + Body = 0xDC; + Hue = 0x76; + } + else + Body = 164; + + SetStr( 200 ); + SetDex( 200 ); + SetInt( 100 ); + + SetHits( ( Core.SE ) ? 140 : 70 ); + SetStam( 250 ); + SetMana( 0 ); + + SetDamage( 14, 17 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Energy, 100 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 90, 100 ); + + SetSkill( SkillName.MagicResist, 99.9 ); + SetSkill( SkillName.Tactics, 90.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 0; + Karma = 0; + + VirtualArmor = 40; + ControlSlots = ( Core.SE ) ? 2 : 1; + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune { get { return Poison.Lethal; } } + + public override int GetAngerSound() + { + return 0x15; + } + + public override int GetAttackSound() + { + return 0x28; + } + + public override void OnThink() + { + if ( Core.SE && Summoned ) + { + ArrayList spirtsOrVortexes = new ArrayList(); + + foreach ( Mobile m in GetMobilesInRange( 5 ) ) + { + if ( m is EnergyVortex || m is BladeSpirits ) + { + if ( ( (BaseCreature) m ).Summoned ) + spirtsOrVortexes.Add( m ); + } + } + + while ( spirtsOrVortexes.Count > 6 ) + { + int index = Utility.Random( spirtsOrVortexes.Count ); + //TODO: Confim if it's the dispel with all the pretty effects or just a Deletion of it. + Dispel( ( (Mobile) spirtsOrVortexes[index] ) ); + spirtsOrVortexes.RemoveAt( index ); + } + } + + base.OnThink(); + } + + + public EnergyVortex( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( BaseSoundID == 263 ) + BaseSoundID = 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/FrostOoze.cs b/Scripts/Mobiles/Monsters/Misc/Melee/FrostOoze.cs new file mode 100644 index 000000000..8bcf1c702 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/FrostOoze.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a frost ooze corpse" )] + public class FrostOoze : BaseCreature + { + [Constructable] + public FrostOoze() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a frost ooze"; + Body = 94; + BaseSoundID = 456; + + SetStr( 18, 30 ); + SetDex( 16, 21 ); + SetInt( 16, 20 ); + + SetHits( 13, 17 ); + + SetDamage( 3, 9 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 5.1, 10.0 ); + SetSkill( SkillName.Tactics, 19.3, 34.0 ); + SetSkill( SkillName.Wrestling, 25.3, 40.0 ); + + Fame = 450; + Karma = -450; + + VirtualArmor = 38; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Gems, Utility.RandomMinMax( 1, 2 ) ); + } + + public FrostOoze( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/Golem.cs b/Scripts/Mobiles/Monsters/Misc/Melee/Golem.cs new file mode 100644 index 000000000..3dc9b2860 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/Golem.cs @@ -0,0 +1,210 @@ +using System; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + [CorpseName( "a golem corpse" )] + public class Golem : BaseCreature + { + private bool m_Stunning; + + public override bool IsScaredOfScaryThings{ get{ return false; } } + public override bool IsScaryToPets{ get{ return true; } } + + public override bool IsBondable{ get{ return false; } } + + [Constructable] + public Golem() : this( false, 1.0 ) + { + } + + [Constructable] + public Golem( bool summoned, double scalar ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8 ) + { + Name = "a golem"; + Body = 752; + + if ( summoned ) + Hue = 2101; + + SetStr( (int)(251*scalar), (int)(350*scalar) ); + SetDex( (int)(76*scalar), (int)(100*scalar) ); + SetInt( (int)(101*scalar), (int)(150*scalar) ); + + SetHits( (int)(151*scalar), (int)(210*scalar) ); + + SetDamage( (int)(13*scalar), (int)(24*scalar) ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, (int)(35*scalar), (int)(55*scalar) ); + + if ( summoned ) + SetResistance( ResistanceType.Fire, (int)(50*scalar), (int)(60*scalar) ); + else + SetResistance( ResistanceType.Fire, (int)(100*scalar) ); + + SetResistance( ResistanceType.Cold, (int)(10*scalar), (int)(30*scalar) ); + SetResistance( ResistanceType.Poison, (int)(10*scalar), (int)(25*scalar) ); + SetResistance( ResistanceType.Energy, (int)(30*scalar), (int)(40*scalar) ); + + SetSkill( SkillName.MagicResist, (150.1*scalar), (190.0*scalar) ); + SetSkill( SkillName.Tactics, (60.1*scalar), (100.0*scalar) ); + SetSkill( SkillName.Wrestling, (60.1*scalar), (100.0*scalar) ); + + if ( summoned ) + { + Fame = 10; + Karma = 10; + } + else + { + Fame = 3500; + Karma = -3500; + } + + if ( !summoned ) + { + PackItem( new IronIngot( Utility.RandomMinMax( 13, 21 ) ) ); + + if ( 0.1 > Utility.RandomDouble() ) + PackItem( new PowerCrystal() ); + + if ( 0.15 > Utility.RandomDouble() ) + PackItem( new ClockworkAssembly() ); + + if ( 0.2 > Utility.RandomDouble() ) + PackItem( new ArcaneGem() ); + + if ( 0.25 > Utility.RandomDouble() ) + PackItem( new Gears() ); + } + + ControlSlots = 4; + } + + public override bool DeleteOnRelease{ get{ return true; } } + + public override int GetAngerSound() + { + return 541; + } + + public override int GetIdleSound() + { + if ( !Controlled ) + return 542; + + return base.GetIdleSound(); + } + + public override int GetDeathSound() + { + if ( !Controlled ) + return 545; + + return base.GetDeathSound(); + } + + public override int GetAttackSound() + { + return 562; + } + + public override int GetHurtSound() + { + if ( Controlled ) + return 320; + + return base.GetHurtSound(); + } + + public override bool AutoDispel{ get{ return !Controlled; } } + public override bool BleedImmune{ get{ return true; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( !m_Stunning && 0.3 > Utility.RandomDouble() ) + { + m_Stunning = true; + + defender.Animate( 21, 6, 1, true, false, 0 ); + this.PlaySound( 0xEE ); + defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You have been stunned by a colossal blow!" ); + + BaseWeapon weapon = this.Weapon as BaseWeapon; + if ( weapon != null ) + weapon.OnHit( this, defender ); + + if ( defender.Alive ) + { + defender.Frozen = true; + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( Recover_Callback ), defender ); + } + } + } + + private void Recover_Callback( object state ) + { + Mobile defender = state as Mobile; + + if ( defender != null ) + { + defender.Frozen = false; + defender.Combatant = null; + defender.LocalOverheadMessage( MessageType.Regular, 0x3B2, false, "You recover your senses." ); + } + + m_Stunning = false; + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + if ( Controlled || Summoned ) + { + Mobile master = ( this.ControlMaster ); + + if ( master == null ) + master = this.SummonMaster; + + if ( master != null && master.Player && master.Map == this.Map && master.InRange( Location, 20 ) ) + { + if ( master.Mana >= amount ) + { + master.Mana -= amount; + } + else + { + amount -= master.Mana; + master.Mana = 0; + master.Damage( amount ); + } + } + } + + base.OnDamage( amount, from, willKill ); + } + + public override bool BardImmune{ get{ return !Core.AOS || Controlled; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public Golem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs b/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs new file mode 100644 index 000000000..1742cd630 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/PlagueBeast.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a plague beast corpse" )] + public class PlagueBeast : BaseCreature + { + [Constructable] + public PlagueBeast() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a plague beast"; + Body = 775; + + SetStr( 302, 500 ); + SetDex( 80 ); + SetInt( 16, 20 ); + + SetHits( 318, 404 ); + + SetDamage( 20, 24 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Poison, 40 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 65, 75 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 35.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 13000; + Karma = -13000; + + VirtualArmor = 30; + PackArmor( 1, 5 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Gems, Utility.Random( 1, 3 ) ); + // TODO: dungeon chest, healthy gland + } + + // TODO: Poison attack + + public override void OnDamagedBySpell( Mobile caster ) + { + if ( caster != this && 0.25 > Utility.RandomDouble() ) + { + BaseCreature spawn = new PlagueSpawn( this ); + + spawn.Team = this.Team; + spawn.MoveToWorld( this.Location, this.Map ); + spawn.Combatant = caster; + + Say( 1053034 ); // * The plague beast creates another beast from its flesh! * + } + + base.OnDamagedBySpell( caster ); + } + + public override bool AutoDispel{ get{ return true; } } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + if ( attacker != this && 0.25 > Utility.RandomDouble() ) + { + BaseCreature spawn = new PlagueSpawn( this ); + + spawn.Team = this.Team; + spawn.MoveToWorld( this.Location, this.Map ); + spawn.Combatant = attacker; + + Say( 1053034 ); // * The plague beast creates another beast from its flesh! * + } + + base.OnGotMeleeAttack( attacker ); + } + + public PlagueBeast( Serial serial ) : base( serial ) + { + } + + public override int GetIdleSound() + { + return 0x1BF; + } + + public override int GetAttackSound() + { + return 0x1C0; + } + + public override int GetHurtSound() + { + return 0x1C1; + } + + public override int GetDeathSound() + { + return 0x1C2; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs b/Scripts/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs new file mode 100644 index 000000000..6bd944254 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/PlagueSpawn.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [CorpseName( "a plague spawn corpse" )] + public class PlagueSpawn : BaseCreature + { + private Mobile m_Owner; + private DateTime m_ExpireTime; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime ExpireTime + { + get{ return m_ExpireTime; } + set{ m_ExpireTime = value; } + } + + [Constructable] + public PlagueSpawn() : this( null ) + { + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public override void DisplayPaperdollTo(Mobile to) + { + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + for ( int i = 0; i < list.Count; ++i ) + { + if ( list[i] is ContextMenus.PaperdollEntry ) + list.RemoveAt( i-- ); + } + } + + public override void OnThink() + { + bool expired; + + expired = ( DateTime.Now >= m_ExpireTime ); + + if ( !expired && m_Owner != null ) + expired = m_Owner.Deleted || Map != m_Owner.Map || !InRange( m_Owner, 16 ); + + if ( expired ) + { + PlaySound( GetIdleSound() ); + Delete(); + } + else + { + base.OnThink(); + } + } + + public PlagueSpawn( Mobile owner ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + m_Owner = owner; + m_ExpireTime = DateTime.Now + TimeSpan.FromMinutes( 1.0 ); + + Name = "a plague spawn"; + Hue = Utility.Random( 0x11, 15 ); + + switch ( Utility.Random( 12 ) ) + { + case 0: // earth elemental + Body = 14; + BaseSoundID = 268; + break; + case 1: // headless one + Body = 31; + BaseSoundID = 0x39D; + break; + case 2: // person + Body = Utility.RandomList( 400, 401 ); + break; + case 3: // gorilla + Body = 0x1D; + BaseSoundID = 0x9E; + break; + case 4: // serpent + Body = 0x15; + BaseSoundID = 0xDB; + break; + default: + case 5: // slime + Body = 51; + BaseSoundID = 456; + break; + } + + SetStr( 201, 300 ); + SetDex( 80 ); + SetInt( 16, 20 ); + + SetHits( 121, 180 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 65, 75 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 25.0 ); + SetSkill( SkillName.Tactics, 25.0 ); + SetSkill( SkillName.Wrestling, 50.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 20; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + AddLoot( LootPack.Gems ); + } + + public PlagueSpawn( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/SandVortex.cs b/Scripts/Mobiles/Monsters/Misc/Melee/SandVortex.cs new file mode 100644 index 000000000..0bb27f9ab --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/SandVortex.cs @@ -0,0 +1,111 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a sand vortex corpse" )] + public class SandVortex : BaseCreature + { + [Constructable] + public SandVortex() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a sand vortex"; + Body = 790; + BaseSoundID = 263; + + SetStr( 96, 120 ); + SetDex( 171, 195 ); + SetInt( 76, 100 ); + + SetHits( 51, 62 ); + + SetDamage( 3, 16 ); + + SetDamageType( ResistanceType.Physical, 90 ); + SetDamageType( ResistanceType.Fire, 10 ); + + SetResistance( ResistanceType.Physical, 80, 90 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 60, 70 ); + + SetSkill( SkillName.MagicResist, 150.0 ); + SetSkill( SkillName.Tactics, 70.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 28; + PackItem( new Bone() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager, 2 ); + } + + private DateTime m_NextAttack; + + public override void OnActionCombat() + { + Mobile combatant = Combatant; + + if ( combatant == null || combatant.Deleted || combatant.Map != Map || !InRange( combatant, 12 ) || !CanBeHarmful( combatant ) || !InLOS( combatant ) ) + return; + + if ( DateTime.Now >= m_NextAttack ) + { + SandAttack( combatant ); + m_NextAttack = DateTime.Now + TimeSpan.FromSeconds( 10.0 + (10.0 * Utility.RandomDouble()) ); + } + } + + public void SandAttack( Mobile m ) + { + DoHarmful( m ); + + m.FixedParticles( 0x36B0, 10, 25, 9540, 2413, 0, EffectLayer.Waist ); + + new InternalTimer( m, this ).Start(); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile, m_From; + + public InternalTimer( Mobile m, Mobile from ) : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_From = from; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Mobile.PlaySound( 0x4CF ); + AOS.Damage( m_Mobile, m_From, Utility.RandomMinMax( 1, 40 ), 90, 10, 0, 0, 0 ); + } + } + + public SandVortex( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Misc/Melee/Slime.cs b/Scripts/Mobiles/Monsters/Misc/Melee/Slime.cs new file mode 100644 index 000000000..76b19ec13 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Misc/Melee/Slime.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a slimey corpse" )] + public class Slime : BaseCreature + { + [Constructable] + public Slime() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a slime"; + Body = 51; + BaseSoundID = 456; + + Hue = Utility.RandomSlimeHue(); + + SetStr( 22, 34 ); + SetDex( 16, 21 ); + SetInt( 16, 20 ); + + SetHits( 15, 19 ); + + SetDamage( 1, 5 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 5, 10 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + + SetSkill( SkillName.Poisoning, 30.1, 50.0 ); + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 19.3, 34.0 ); + SetSkill( SkillName.Wrestling, 19.3, 34.0 ); + + Fame = 300; + Karma = -300; + + VirtualArmor = 8; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 23.1; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Poor ); + AddLoot( LootPack.Gems ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lesser; } } + public override Poison HitPoison{ get{ return Poison.Lesser; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish | FoodType.FruitsAndVegies | FoodType.GrainsAndHay | FoodType.Eggs; } } + + public Slime( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs new file mode 100644 index 000000000..be8c1936d --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/AgapiteElemental.cs @@ -0,0 +1,76 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class AgapiteElemental : BaseCreature + { + [Constructable] + public AgapiteElemental() : this( 2 ) + { + } + + [Constructable] + public AgapiteElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an agapite elemental"; + Body = 107; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 28 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 32; + + PackItem( new AgapiteOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool BleedImmune{ get{ return true; } } + public override bool AutoDispel{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public AgapiteElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs new file mode 100644 index 000000000..5f45b7c7e --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/BronzeElemental.cs @@ -0,0 +1,78 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class BronzeElemental : BaseCreature + { + [Constructable] + public BronzeElemental() : this( 2 ) + { + } + + [Constructable] + public BronzeElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + // TODO: Gas attack + Name = "a bronze elemental"; + Body = 108; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 30 ); + SetDamageType( ResistanceType.Fire, 70 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 70, 80 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 5000; + Karma = -5000; + + VirtualArmor = 29; + + PackItem( new BronzeOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool BleedImmune{ get{ return true; } } + public override bool AutoDispel{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public BronzeElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/CopperElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/CopperElemental.cs new file mode 100644 index 000000000..dc931ccea --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/CopperElemental.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class CopperElemental : BaseCreature + { + [Constructable] + public CopperElemental() : this( 2 ) + { + } + + [Constructable] + public CopperElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a copper elemental"; + Body = 109; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 4800; + Karma = -4800; + + VirtualArmor = 26; + + PackItem( new CopperOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool BleedImmune{ get{ return true; } } + public override bool AutoDispel{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public override void CheckReflect( Mobile caster, ref bool reflect ) + { + reflect = true; // Every spell is reflected back to the caster + } + + public CopperElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs new file mode 100644 index 000000000..b2d5d19b5 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/DullCopperElemental.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class DullCopperElemental : BaseCreature + { + [Constructable] + public DullCopperElemental() : this( 2 ) + { + } + + [Constructable] + public DullCopperElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a dull copper elemental"; + Body = 110; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 20; + + PackItem( new DullCopperOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + + public DullCopperElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs new file mode 100644 index 000000000..14cb8345f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/GoldenElemental.cs @@ -0,0 +1,76 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class GoldenElemental : BaseCreature + { + [Constructable] + public GoldenElemental() : this( 2 ) + { + } + + [Constructable] + public GoldenElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a golden elemental"; + Body = 166; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 60, 75 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 60; + + PackItem( new GoldOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public GoldenElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs new file mode 100644 index 000000000..e340eb9a8 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/ShadowIronElemental.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class ShadowIronElemental : BaseCreature + { + [Constructable] + public ShadowIronElemental() : this( 2 ) + { + } + + [Constructable] + public ShadowIronElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a shadow iron elemental"; + Body = 111; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 23; + + PackItem( new ShadowIronOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public override void AlterMeleeDamageFrom( Mobile from, ref int damage ) + { + if ( from is BaseCreature ) + { + BaseCreature bc = (BaseCreature)from; + + if ( bc.Controlled || bc.BardTarget == this ) + damage = 0; // Immune to pets and provoked creatures + } + } + + public override void AlterDamageScalarFrom( Mobile caster, ref double scalar ) + { + scalar = 0.0; // Immune to magic + } + + public ShadowIronElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs new file mode 100644 index 000000000..16b775d5b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/ValoriteElemental.cs @@ -0,0 +1,96 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class ValoriteElemental : BaseCreature + { + [Constructable] + public ValoriteElemental() : this( 2 ) + { + } + + [Constructable] + public ValoriteElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + // TODO: Gas attack + Name = "a valorite elemental"; + Body = 112; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 28 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Fire, 25 ); + SetDamageType( ResistanceType.Cold, 25 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 65, 75 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 38; + + PackItem( new ValoriteOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Gems, 4 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public override void AlterMeleeDamageFrom( Mobile from, ref int damage ) + { + if ( from is BaseCreature ) + { + BaseCreature bc = (BaseCreature)from; + + if ( bc.Controlled || bc.BardTarget == this ) + damage = 0; // Immune to pets and provoked creatures + } + } + + public override void CheckReflect( Mobile caster, ref bool reflect ) + { + reflect = true; // Every spell is reflected back to the caster + } + + public ValoriteElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs b/Scripts/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs new file mode 100644 index 000000000..2d521f7b7 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Ore Elementals/VeriteElemental.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ore elemental corpse" )] + public class VeriteElemental : BaseCreature + { + [Constructable] + public VeriteElemental() : this( 2 ) + { + } + + [Constructable] + public VeriteElemental( int oreAmount ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a verite elemental"; + Body = 113; + BaseSoundID = 268; + + SetStr( 226, 255 ); + SetDex( 126, 145 ); + SetInt( 71, 92 ); + + SetHits( 136, 153 ); + + SetDamage( 9, 16 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 50, 60 ); + + SetSkill( SkillName.MagicResist, 50.1, 95.0 ); + SetSkill( SkillName.Tactics, 60.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 100.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 35; + + PackItem( new VeriteOre( oreAmount ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool BleedImmune{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public VeriteElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Plant/Magic/Reaper.cs b/Scripts/Mobiles/Monsters/Plant/Magic/Reaper.cs new file mode 100644 index 000000000..338766144 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Magic/Reaper.cs @@ -0,0 +1,75 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a reapers corpse" )] + public class Reaper : BaseCreature + { + [Constructable] + public Reaper() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a reaper"; + Body = 47; + BaseSoundID = 442; + + SetStr( 66, 215 ); + SetDex( 66, 75 ); + SetInt( 101, 250 ); + + SetHits( 40, 129 ); + SetStam( 0 ); + + SetDamage( 9, 11 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Poison, 20 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 100.1, 125.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 50.1, 60.0 ); + + Fame = 3500; + Karma = -3500; + + VirtualArmor = 40; + + PackItem( new Log( 10 ) ); + PackItem( new MandrakeRoot( 5 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + public override int TreasureMapLevel{ get{ return 2; } } + public override bool DisallowAllMoves{ get{ return true; } } + + public Reaper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Plant/Melee/BogThing.cs b/Scripts/Mobiles/Monsters/Plant/Melee/BogThing.cs new file mode 100644 index 000000000..059a883f3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Melee/BogThing.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a plant corpse" )] + public class BogThing : BaseCreature + { + [Constructable] + public BogThing() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.6, 1.2 ) + { + Name = "a bog thing"; + Body = 780; + + SetStr( 801, 900 ); + SetDex( 46, 65 ); + SetInt( 36, 50 ); + + SetHits( 481, 540 ); + SetMana( 0 ); + + SetDamage( 10, 23 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Poison, 40 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 20, 25 ); + SetResistance( ResistanceType.Cold, 10, 15 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 20, 25 ); + + SetSkill( SkillName.MagicResist, 90.1, 95.0 ); + SetSkill( SkillName.Tactics, 70.1, 85.0 ); + SetSkill( SkillName.Wrestling, 65.1, 80.0 ); + + Fame = 8000; + Karma = -8000; + + VirtualArmor = 28; + + if ( 0.25 > Utility.RandomDouble() ) + PackItem( new Board( 10 ) ); + else + PackItem( new Log( 10 ) ); + + PackReg( 3 ); + PackItem( new Engines.Plants.Seed() ); + PackItem( new Engines.Plants.Seed() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + } + + public override bool BardImmune{ get{ return !Core.AOS; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public BogThing( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + public void SpawnBogling( Mobile m ) + { + Map map = this.Map; + + if ( map == null ) + return; + + Bogling spawned = new Bogling(); + + spawned.Team = this.Team; + + bool validLocation = false; + Point3D loc = this.Location; + + 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 ); + } + + spawned.MoveToWorld( loc, map ); + spawned.Combatant = m; + } + + public void EatBoglings() + { + ArrayList toEat = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 2 ) ) + { + if ( m is Bogling ) + toEat.Add( m ); + } + + if ( toEat.Count > 0 ) + { + PlaySound( Utility.Random( 0x3B, 2 ) ); // Eat sound + + foreach ( Mobile m in toEat ) + { + Hits += (m.Hits / 2); + m.Delete(); + } + } + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( this.Hits > (this.HitsMax / 4) ) + { + if ( 0.25 >= Utility.RandomDouble() ) + SpawnBogling( attacker ); + } + else if ( 0.25 >= Utility.RandomDouble() ) + { + EatBoglings(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Plant/Melee/Bogling.cs b/Scripts/Mobiles/Monsters/Plant/Melee/Bogling.cs new file mode 100644 index 000000000..93366711c --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Melee/Bogling.cs @@ -0,0 +1,69 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a plant corpse" )] + public class Bogling : BaseCreature + { + [Constructable] + public Bogling() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a bogling"; + Body = 779; + BaseSoundID = 422; + + SetStr( 96, 120 ); + SetDex( 91, 115 ); + SetInt( 21, 45 ); + + SetHits( 58, 72 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 15, 25 ); + SetResistance( ResistanceType.Energy, 15, 25 ); + + SetSkill( SkillName.MagicResist, 75.1, 100.0 ); + SetSkill( SkillName.Tactics, 55.1, 80.0 ); + SetSkill( SkillName.Wrestling, 55.1, 75.0 ); + + Fame = 450; + Karma = -450; + + VirtualArmor = 28; + + PackItem( new Log( 4 ) ); + PackItem( new Engines.Plants.Seed() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override int Hides{ get{ return 6; } } + public override int Meat{ get{ return 1; } } + + public Bogling( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Plant/Melee/Corpser.cs b/Scripts/Mobiles/Monsters/Plant/Melee/Corpser.cs new file mode 100644 index 000000000..a33fedf70 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Melee/Corpser.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a corpser corpse" )] + public class Corpser : BaseCreature + { + [Constructable] + public Corpser() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a corpser"; + Body = 8; + BaseSoundID = 684; + + SetStr( 156, 180 ); + SetDex( 26, 45 ); + SetInt( 26, 40 ); + + SetHits( 94, 108 ); + SetMana( 0 ); + + SetDamage( 10, 23 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Poison, 40 ); + + SetResistance( ResistanceType.Physical, 15, 20 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 60.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 18; + + if ( 0.25 > Utility.RandomDouble() ) + PackItem( new Board( 10 ) ); + else + PackItem( new Log( 10 ) ); + + PackItem( new MandrakeRoot( 3 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lesser; } } + public override bool DisallowAllMoves{ get{ return true; } } + + public Corpser( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 352 ) + BaseSoundID = 684; + } + } +} diff --git a/Scripts/Mobiles/Monsters/Plant/Melee/Quagmire.cs b/Scripts/Mobiles/Monsters/Plant/Melee/Quagmire.cs new file mode 100644 index 000000000..76fadfb91 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Melee/Quagmire.cs @@ -0,0 +1,76 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a quagmire corpse" )] + public class Quagmire : BaseCreature + { + [Constructable] + public Quagmire() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.4, 0.8 ) + { + Name = "a quagmire"; + Body = 789; + BaseSoundID = 352; + + SetStr( 101, 130 ); + SetDex( 66, 85 ); + SetInt( 31, 55 ); + + SetHits( 91, 105 ); + + SetDamage( 10, 14 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Poison, 40 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 65.1, 75.0 ); + SetSkill( SkillName.Tactics, 50.1, 60.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 32; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override int GetAngerSound() + { + return 353; + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override double HitPoisonChance{ get{ return 0.1; } } + + public Quagmire( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == -1 ) + BaseSoundID = 352; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs b/Scripts/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs new file mode 100644 index 000000000..7ae709765 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Melee/SwampTentacle.cs @@ -0,0 +1,69 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a swamp tentacle corpse" )] + public class SwampTentacle : BaseCreature + { + [Constructable] + public SwampTentacle() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a swamp tentacle"; + Body = 66; + BaseSoundID = 352; + + SetStr( 96, 120 ); + SetDex( 66, 85 ); + SetInt( 16, 30 ); + + SetHits( 58, 72 ); + SetMana( 0 ); + + SetDamage( 6, 12 ); + + SetDamageType( ResistanceType.Physical, 40 ); + SetDamageType( ResistanceType.Poison, 60 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 60, 80 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 65.1, 80.0 ); + SetSkill( SkillName.Wrestling, 65.1, 80.0 ); + + Fame = 3000; + Karma = -3000; + + VirtualArmor = 28; + + PackReg( 3 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + } + + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + + public SwampTentacle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Plant/Melee/WhippingVine.cs b/Scripts/Mobiles/Monsters/Plant/Melee/WhippingVine.cs new file mode 100644 index 000000000..444e06574 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Plant/Melee/WhippingVine.cs @@ -0,0 +1,71 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a whipping vine corpse" )] + public class WhippingVine : BaseCreature + { + [Constructable] + public WhippingVine() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a whipping vine"; + Body = 8; + Hue = 0x851; + BaseSoundID = 352; + + SetStr( 251, 300 ); + SetDex( 76, 100 ); + SetInt( 26, 40 ); + + SetMana( 0 ); + + SetDamage( 7, 25 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 30 ); + + SetResistance( ResistanceType.Physical, 75, 85 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 15, 25 ); + SetResistance( ResistanceType.Poison, 75, 85 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.MagicResist, 70.0 ); + SetSkill( SkillName.Tactics, 70.0 ); + SetSkill( SkillName.Wrestling, 70.0 ); + + Fame = 1000; + Karma = -1000; + + VirtualArmor = 45; + + PackReg( 3 ); + PackItem( new FertileDirt( Utility.RandomMinMax( 1, 10 ) ) ); + + if ( 0.2 >= Utility.RandomDouble() ) + PackItem( new ExecutionersCap() ); + + PackItem( new Vines() ); + } + + public override bool BardImmune{ get{ return !Core.AOS; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public WhippingVine( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs new file mode 100644 index 000000000..4d49c57d6 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/AncientWyrm.cs @@ -0,0 +1,91 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a dragon corpse" )] + public class AncientWyrm : BaseCreature + { + [Constructable] + public AncientWyrm () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ancient wyrm"; + Body = 46; + BaseSoundID = 362; + + SetStr( 1096, 1185 ); + SetDex( 86, 175 ); + SetInt( 686, 775 ); + + SetHits( 658, 711 ); + + SetDamage( 29, 35 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Fire, 25 ); + + SetResistance( ResistanceType.Physical, 65, 75 ); + SetResistance( ResistanceType.Fire, 80, 90 ); + SetResistance( ResistanceType.Cold, 70, 80 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 60, 70 ); + + SetSkill( SkillName.EvalInt, 80.1, 100.0 ); + SetSkill( SkillName.Magery, 80.1, 100.0 ); + SetSkill( SkillName.Meditation, 52.5, 75.0 ); + SetSkill( SkillName.MagicResist, 100.5, 150.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 70; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 3 ); + AddLoot( LootPack.Gems, 5 ); + } + + public override int GetIdleSound() + { + return 0x2D3; + } + + public override int GetHurtSound() + { + return 0x2D1; + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override bool AutoDispel{ get{ return true; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + public override int Hides{ get{ return 40; } } + public override int Meat{ get{ return 19; } } + public override int Scales{ get{ return 12; } } + public override ScaleType ScaleType{ get{ return (ScaleType)Utility.Random( 4 ); } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override Poison HitPoison{ get{ return Utility.RandomBool() ? Poison.Lesser : Poison.Regular; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public AncientWyrm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs new file mode 100644 index 000000000..06b4a81e9 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/DeepSeaSerpent.cs @@ -0,0 +1,80 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a deep sea serpents corpse" )] + public class DeepSeaSerpent : BaseCreature + { + [Constructable] + public DeepSeaSerpent() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a deep sea serpent"; + Body = 150; + BaseSoundID = 447; + + Hue = Utility.Random( 0x8A0, 5 ); + + SetStr( 251, 425 ); + SetDex( 87, 135 ); + SetInt( 87, 155 ); + + SetHits( 151, 255 ); + + SetDamage( 6, 14 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 30, 40 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 15, 20 ); + + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 60.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 70.0 ); + + Fame = 6000; + Karma = -6000; + + VirtualArmor = 60; + CanSwim = true; + CantWalk = true; + + if ( Utility.RandomBool() ) + PackItem( new SulfurousAsh( 4 ) ); + else + PackItem( new BlackPearl( 4 ) ); + + //PackItem( new SpecialFishingNet() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool HasBreath{ get{ return true; } } + public override int Meat{ get{ return 1; } } + public override int Scales{ get{ return 8; } } + public override ScaleType ScaleType{ get{ return ScaleType.Blue; } } + + public DeepSeaSerpent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/Dragon.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/Dragon.cs new file mode 100644 index 000000000..63fa113b9 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/Dragon.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a dragon corpse" )] + public class Dragon : BaseCreature + { + [Constructable] + public Dragon () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a dragon"; + Body = Utility.RandomList( 12, 59 ); + BaseSoundID = 362; + + SetStr( 796, 825 ); + SetDex( 86, 105 ); + SetInt( 436, 475 ); + + SetHits( 478, 495 ); + + SetDamage( 16, 22 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.EvalInt, 30.1, 40.0 ); + SetSkill( SkillName.Magery, 30.1, 40.0 ); + SetSkill( SkillName.MagicResist, 99.1, 100.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 92.5 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 60; + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 93.9; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.Gems, 8 ); + } + + public override bool ReacquireOnMovement{ get{ return !Controlled; } } + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override bool AutoDispel{ get{ return !Controlled; } } + public override int TreasureMapLevel{ get{ return 4; } } + public override int Meat{ get{ return 19; } } + public override int Hides{ get{ return 20; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + public override int Scales{ get{ return 7; } } + public override ScaleType ScaleType{ get{ return ( Body == 12 ? ScaleType.Yellow : ScaleType.Red ); } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override bool CanAngerOnTame { get { return true; } } + + public Dragon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/Leviathan.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/Leviathan.cs new file mode 100644 index 000000000..60f590dc4 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/Leviathan.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a leviathan corpse" )] + public class Leviathan : BaseCreature + { + private Mobile m_Fisher; + + public Mobile Fisher + { + get{ return m_Fisher; } + set{ m_Fisher = value; } + } + + [Constructable] + public Leviathan() : this( null ) + { + } + + [Constructable] + public Leviathan( Mobile fisher ) : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + m_Fisher = fisher; + + // May not be OSI accurate; mostly copied from krakens + Name = "a leviathan"; + Body = 77; + BaseSoundID = 353; + + Hue = 0x481; + + SetStr( 1000 ); + SetDex( 501, 520 ); + SetInt( 501, 515 ); + + SetHits( 1500 ); + + SetDamage( 25, 33 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Cold, 30 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 45, 55 ); + SetResistance( ResistanceType.Cold, 45, 55 ); + SetResistance( ResistanceType.Poison, 35, 45 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.EvalInt, 97.6, 107.5 ); + SetSkill( SkillName.Magery, 97.6, 107.5 ); + SetSkill( SkillName.MagicResist, 97.6, 107.5 ); + SetSkill( SkillName.Meditation, 97.6, 107.5 ); + SetSkill( SkillName.Tactics, 97.6, 107.5 ); + SetSkill( SkillName.Wrestling, 97.6, 107.5 ); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 50; + + CanSwim = true; + CantWalk = true; + + PackItem( new MessageInABottle() ); + + Rope rope = new Rope(); + rope.ItemID = 0x14F8; + PackItem( rope ); + + rope = new Rope(); + rope.ItemID = 0x14FA; + PackItem( rope ); + } + + public override bool HasBreath{ get{ return true; } } + public override int BreathPhysicalDamage{ get{ return 70; } } // TODO: Verify damage type + public override int BreathColdDamage{ get{ return 30; } } + public override int BreathFireDamage{ get{ return 0; } } + public override int BreathEffectHue{ get{ return 0x1ED; } } + public override double BreathDamageScalar{ get{ return 0.05; } } + public override double BreathMinDelay{ get{ return 5.0; } } + public override double BreathMaxDelay{ get{ return 7.5; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 5 ); + } + + public override double TreasureMapChance{ get{ return 0.25; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public Leviathan( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public static Type[] Artifacts { get { return m_Artifacts; } } + + private static Type[] m_Artifacts = new Type[] + { + // Decorations + typeof( CandelabraOfSouls ), + typeof( GhostShipAnchor ), + typeof( GoldBricks ), + typeof( PhillipsWoodenSteed ), + typeof( SeahorseStatuette ), + typeof( ShipModelOfTheHMSCape ), + typeof( AdmiralsHeartyRum ), + + // Equipment + typeof( AlchemistsBauble ), + typeof( ArcticDeathDealer ), + typeof( BlazeOfDeath ), + typeof( BurglarsBandana ), + typeof( CaptainQuacklebushsCutlass ), + typeof( CavortingClub ), + typeof( DreadPirateHat ), + typeof( EnchantedTitanLegBone ), + typeof( GwennosHarp ), + typeof( IolosLute ), + typeof( LunaLance ), + typeof( NightsKiss ), + typeof( NoxRangersHeavyCrossbow ), + typeof( PolarBearMask ), + typeof( VioletCourage ) + }; + + public static void GiveArtifactTo( Mobile m ) + { + Item item = Loot.Construct( m_Artifacts ); + + if ( item == null ) + return; + + // TODO: Confirm messages + if ( m.AddToBackpack( item ) ) + m.SendMessage( "As a reward for slaying the mighty leviathan, an artifact has been placed in your backpack." ); + else + m.SendMessage( "As your backpack is full, your reward for destroying the legendary leviathan has been placed at your feet." ); + } + + public override void OnKilledBy( Mobile mob ) + { + base.OnKilledBy( mob ); + + if ( Paragon.CheckArtifactChance( mob, this ) ) + { + GiveArtifactTo( mob ); + + if ( mob == m_Fisher ) + m_Fisher = null; + } + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + if ( m_Fisher != null && 25 > Utility.Random( 100 ) ) + GiveArtifactTo( m_Fisher ); + + m_Fisher = null; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs new file mode 100644 index 000000000..2dec70e3d --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianArchmage.cs @@ -0,0 +1,85 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ophidian corpse" )] + [TypeAlias( "Server.Mobiles.OphidianJusticar", "Server.Mobiles.OphidianZealot" )] + public class OphidianArchmage : BaseCreature + { + private static string[] m_Names = new string[] + { + "an ophidian justicar", + "an ophidian zealot" + }; + + [Constructable] + public OphidianArchmage() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = m_Names[Utility.Random( m_Names.Length )]; + Body = 85; + BaseSoundID = 639; + + SetStr( 281, 305 ); + SetDex( 191, 215 ); + SetInt( 226, 250 ); + + SetHits( 169, 183 ); + SetStam( 36, 45 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 40, 45 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 35, 40 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.EvalInt, 95.1, 100.0 ); + SetSkill( SkillName.Magery, 95.1, 100.0 ); + SetSkill( SkillName.MagicResist, 75.0, 97.5 ); + SetSkill( SkillName.Tactics, 65.0, 87.5 ); + SetSkill( SkillName.Wrestling, 20.2, 60.0 ); + + Fame = 11500; + Karma = -11500; + + VirtualArmor = 44; + + PackReg( 5, 15 ); + PackNecroReg( 5, 15 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override int Meat{ get{ return 1; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public OphidianArchmage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs new file mode 100644 index 000000000..2121dce99 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMage.cs @@ -0,0 +1,86 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ophidian corpse" )] + [TypeAlias( "Server.Mobiles.OphidianShaman" )] + public class OphidianMage : BaseCreature + { + private static string[] m_Names = new string[] + { + "an ophidian apprentice mage", + "an ophidian shaman" + }; + + [Constructable] + public OphidianMage() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = m_Names[Utility.Random( m_Names.Length )]; + Body = 85; + BaseSoundID = 639; + + SetStr( 181, 205 ); + SetDex( 191, 215 ); + SetInt( 96, 120 ); + + SetHits( 109, 123 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.EvalInt, 85.1, 100.0 ); + SetSkill( SkillName.Magery, 85.1, 100.0 ); + SetSkill( SkillName.MagicResist, 75.0, 97.5 ); + SetSkill( SkillName.Tactics, 65.0, 87.5 ); + SetSkill( SkillName.Wrestling, 20.2, 60.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 30; + + PackReg( 10 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.LowScrolls ); + AddLoot( LootPack.MedScrolls ); + AddLoot( LootPack.Potions ); + } + + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return 2; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public OphidianMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs new file mode 100644 index 000000000..f1af85f4b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/OphidianMatriarch.cs @@ -0,0 +1,77 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an ophidian corpse" )] + public class OphidianMatriarch : BaseCreature + { + [Constructable] + public OphidianMatriarch() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an ophidian matriarch"; + Body = 87; + BaseSoundID = 644; + + SetStr( 416, 505 ); + SetDex( 96, 115 ); + SetInt( 366, 455 ); + + SetHits( 250, 303 ); + + SetDamage( 11, 13 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.Meditation, 5.4, 25.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 50.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 80.0 ); + + Fame = 16000; + Karma = -16000; + + VirtualArmor = 50; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + public override int TreasureMapLevel{ get{ return 4; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public OphidianMatriarch( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs new file mode 100644 index 000000000..10941ed1d --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/SeaSerpent.cs @@ -0,0 +1,86 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a sea serpents corpse" )] + [TypeAlias( "Server.Mobiles.Seaserpant" )] + public class SeaSerpent : BaseCreature + { + [Constructable] + public SeaSerpent() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a sea serpent"; + Body = 150; + BaseSoundID = 447; + + Hue = Utility.Random( 0x530, 9 ); + + SetStr( 168, 225 ); + SetDex( 58, 85 ); + SetInt( 53, 95 ); + + SetHits( 110, 127 ); + + SetDamage( 7, 13 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 35 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 15, 20 ); + + SetSkill( SkillName.MagicResist, 60.1, 75.0 ); + SetSkill( SkillName.Tactics, 60.1, 70.0 ); + SetSkill( SkillName.Wrestling, 60.1, 70.0 ); + + Fame = 6000; + Karma = -6000; + + VirtualArmor = 30; + CanSwim = true; + CantWalk = true; + + if ( Utility.RandomBool() ) + PackItem( new SulfurousAsh( 4 ) ); + else + PackItem( new BlackPearl( 4 ) ); + + PackItem( new RawFishSteak() ); + + //PackItem( new SpecialFishingNet() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override bool HasBreath{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 2; } } + + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Horned; } } + public override int Scales{ get{ return 8; } } + public override ScaleType ScaleType{ get{ return ScaleType.Blue; } } + + public SeaSerpent( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs new file mode 100644 index 000000000..ad8da53db --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/SerpentineDragon.cs @@ -0,0 +1,123 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a dragon corpse" )] + public class SerpentineDragon : BaseCreature + { + [Constructable] + public SerpentineDragon() : base( AIType.AI_Mage, FightMode.Evil, 10, 1, 0.2, 0.4 ) + { + Name = "a serpentine dragon"; + Body = 103; + BaseSoundID = 362; + + SetStr( 111, 140 ); + SetDex( 201, 220 ); + SetInt( 1001, 1040 ); + + SetHits( 480 ); + + SetDamage( 5, 12 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Poison, 25 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 25, 35 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 25, 35 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.EvalInt, 100.1, 110.0 ); + SetSkill( SkillName.Magery, 110.1, 120.0 ); + SetSkill( SkillName.Meditation, 100.0 ); + SetSkill( SkillName.MagicResist, 100.0 ); + SetSkill( SkillName.Tactics, 50.1, 60.0 ); + SetSkill( SkillName.Wrestling, 30.1, 100.0 ); + + Fame = 15000; + Karma = 15000; + + VirtualArmor = 36; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.Gems, 2 ); + } + + public override int GetIdleSound() + { + return 0x2C4; + } + + public override int GetAttackSound() + { + return 0x2C0; + } + + public override int GetDeathSound() + { + return 0x2C1; + } + + public override int GetAngerSound() + { + return 0x2C4; + } + + public override int GetHurtSound() + { + return 0x2C3; + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override double BonusPetDamageScalar{ get{ return (Core.SE) ? 3.0 : 1.0; } } + + public override bool AutoDispel{ get{ return true; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + public override int Hides{ get{ return 20; } } + public override int Meat{ get{ return 19; } } + public override int Scales{ get{ return 6; } } + public override ScaleType ScaleType{ get{ return ( Utility.RandomBool() ? ScaleType.Black : ScaleType.White ); } } + public override int TreasureMapLevel{ get{ return 4; } } + + public SerpentineDragon( Serial serial ) : base( serial ) + { + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( !Core.SE && 0.2 > Utility.RandomDouble() && attacker is BaseCreature ) + { + BaseCreature c = (BaseCreature)attacker; + + if ( c.Controlled && c.ControlMaster != null ) + { + c.ControlTarget = c.ControlMaster; + c.ControlOrder = OrderType.Attack; + c.Combatant = c.ControlMaster; + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs new file mode 100644 index 000000000..9be1bb806 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/ShadowWyrm.cs @@ -0,0 +1,92 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a shadow wyrm corpse" )] + public class ShadowWyrm : BaseCreature + { + [Constructable] + public ShadowWyrm() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a shadow wyrm"; + Body = 106; + BaseSoundID = 362; + + SetStr( 898, 1030 ); + SetDex( 68, 200 ); + SetInt( 488, 620 ); + + SetHits( 558, 599 ); + + SetDamage( 29, 35 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Cold, 25 ); + + SetResistance( ResistanceType.Physical, 65, 75 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 45, 55 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 50, 60 ); + + SetSkill( SkillName.EvalInt, 80.1, 100.0 ); + SetSkill( SkillName.Magery, 80.1, 100.0 ); + SetSkill( SkillName.Meditation, 52.5, 75.0 ); + SetSkill( SkillName.MagicResist, 100.3, 130.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 70; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 3 ); + AddLoot( LootPack.Gems, 5 ); + } + + public override int GetIdleSound() + { + return 0x2D5; + } + + public override int GetHurtSound() + { + return 0x2D1; + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override bool AutoDispel{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + public override Poison HitPoison{ get{ return Poison.Deadly; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public override int Meat{ get{ return 19; } } + public override int Hides{ get{ return 20; } } + public override int Scales{ get{ return 10; } } + public override ScaleType ScaleType{ get{ return ScaleType.Black; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + + public ShadowWyrm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs new file mode 100644 index 000000000..135e66554 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/SkeletalDragon.cs @@ -0,0 +1,83 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a skeletal dragon corpse" )] + public class SkeletalDragon : BaseCreature + { + [Constructable] + public SkeletalDragon () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a skeletal dragon"; + Body = 104; + BaseSoundID = 0x488; + + SetStr( 898, 1030 ); + SetDex( 68, 200 ); + SetInt( 488, 620 ); + + SetHits( 558, 599 ); + + SetDamage( 29, 35 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Fire, 25 ); + + SetResistance( ResistanceType.Physical, 75, 80 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 40, 60 ); + SetResistance( ResistanceType.Poison, 70, 80 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + + SetSkill( SkillName.EvalInt, 80.1, 100.0 ); + SetSkill( SkillName.Magery, 80.1, 100.0 ); + SetSkill( SkillName.MagicResist, 100.3, 130.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 80; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 4 ); + AddLoot( LootPack.Gems, 5 ); + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int BreathFireDamage{ get{ return 0; } } + public override int BreathColdDamage{ get{ return 100; } } + public override int BreathEffectHue{ get{ return 0x480; } } + public override double BonusPetDamageScalar{ get{ return (Core.SE)? 3.0 : 1.0; } } + // TODO: Undead summoning? + + public override bool AutoDispel{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override bool BleedImmune{ get{ return true; } } + public override int Meat{ get{ return 19; } } // where's it hiding these? :) + public override int Hides{ get{ return 20; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + + public SkeletalDragon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs b/Scripts/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs new file mode 100644 index 000000000..cf26295d2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Magic/WhiteWyrm.cs @@ -0,0 +1,86 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a white wyrm corpse" )] + public class WhiteWyrm : BaseCreature + { + [Constructable] + public WhiteWyrm () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Body = Core.AOS ? 180 : 49; + Name = "a white wyrm"; + BaseSoundID = 362; + + SetStr( 721, 760 ); + SetDex( 101, 130 ); + SetInt( 386, 425 ); + + SetHits( 433, 456 ); + + SetDamage( 17, 25 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Cold, 50 ); + + SetResistance( ResistanceType.Physical, 55, 70 ); + SetResistance( ResistanceType.Fire, 15, 25 ); + SetResistance( ResistanceType.Cold, 80, 90 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 99.1, 100.0 ); + SetSkill( SkillName.Magery, 99.1, 100.0 ); + SetSkill( SkillName.MagicResist, 99.1, 100.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 18000; + Karma = -18000; + + VirtualArmor = 64; + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 96.3; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems, Utility.Random( 1, 5 ) ); + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 4; } } + public override int Meat{ get{ return 19; } } + public override int Hides{ get{ return 20; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + public override int Scales{ get{ return 9; } } + public override ScaleType ScaleType{ get{ return ScaleType.White; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Gold; } } + public override bool CanAngerOnTame { get { return true; } } + + public WhiteWyrm( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( Core.AOS && Body == 49 ) + Body = 180; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Drake.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Drake.cs new file mode 100644 index 000000000..18b1c72be --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Drake.cs @@ -0,0 +1,82 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a drake corpse" )] + public class Drake : BaseCreature + { + [Constructable] + public Drake () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a drake"; + Body = Utility.RandomList( 60, 61 ); + BaseSoundID = 362; + + SetStr( 401, 430 ); + SetDex( 133, 152 ); + SetInt( 101, 140 ); + + SetHits( 241, 258 ); + + SetDamage( 11, 17 ); + + SetDamageType( ResistanceType.Physical, 80 ); + SetDamageType( ResistanceType.Fire, 20 ); + + SetResistance( ResistanceType.Physical, 45, 50 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 65.1, 90.0 ); + SetSkill( SkillName.Wrestling, 65.1, 80.0 ); + + Fame = 5500; + Karma = -5500; + + VirtualArmor = 46; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 84.3; + + PackReg( 3 ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override bool HasBreath{ get{ return true; } } // fire breath enabled + public override int TreasureMapLevel{ get{ return 2; } } + public override int Meat{ get{ return 10; } } + public override int Hides{ get{ return 20; } } + public override HideType HideType{ get{ return HideType.Horned; } } + public override int Scales{ get{ return 2; } } + public override ScaleType ScaleType{ get{ return ( Body == 60 ? ScaleType.Yellow : ScaleType.Red ); } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat | FoodType.Fish; } } + + public Drake( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Harpy.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Harpy.cs new file mode 100644 index 000000000..ca06ab0aa --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Harpy.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a harpy corpse" )] + public class Harpy : BaseCreature + { + [Constructable] + public Harpy() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a harpy"; + Body = 30; + BaseSoundID = 402; + + SetStr( 96, 120 ); + SetDex( 86, 110 ); + SetInt( 51, 75 ); + + SetHits( 58, 72 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 10, 20 ); + SetResistance( ResistanceType.Cold, 10, 30 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 50.1, 65.0 ); + SetSkill( SkillName.Tactics, 70.1, 100.0 ); + SetSkill( SkillName.Wrestling, 60.1, 90.0 ); + + Fame = 2500; + Karma = -2500; + + VirtualArmor = 28; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager, 2 ); + } + + public override int GetAttackSound() + { + return 916; + } + + public override int GetAngerSound() + { + return 916; + } + + public override int GetDeathSound() + { + return 917; + } + + public override int GetHurtSound() + { + return 919; + } + + public override int GetIdleSound() + { + return 918; + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 4; } } + public override MeatType MeatType{ get{ return MeatType.Bird; } } + public override int Feathers{ get{ return 50; } } + + public Harpy( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs new file mode 100644 index 000000000..5ad696198 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Kraken.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a krakens corpse" )] + public class Kraken : BaseCreature + { + [Constructable] + public Kraken() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a kraken"; + Body = 77; + BaseSoundID = 353; + + SetStr( 756, 780 ); + SetDex( 226, 245 ); + SetInt( 26, 40 ); + + SetHits( 454, 468 ); + SetMana( 0 ); + + SetDamage( 19, 33 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Cold, 30 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.MagicResist, 15.1, 20.0 ); + SetSkill( SkillName.Tactics, 45.1, 60.0 ); + SetSkill( SkillName.Wrestling, 45.1, 60.0 ); + + Fame = 11000; + Karma = -11000; + + VirtualArmor = 50; + + CanSwim = true; + CantWalk = true; + + Rope rope = new Rope(); + rope.ItemID = 0x14F8; + PackItem( rope ); + + if( Utility.RandomDouble() < .05 ) + PackItem( new MessageInABottle() ); + + PackItem( new SpecialFishingNet() ); //Confirm? + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich ); + } + + public override int TreasureMapLevel{ get{ return 4; } } + + public Kraken( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Lizardman.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Lizardman.cs new file mode 100644 index 000000000..ac9a445f3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Lizardman.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "a lizardman corpse" )] + public class Lizardman : BaseCreature + { + public override InhumanSpeech SpeechType{ get{ return InhumanSpeech.Lizardman; } } + + [Constructable] + public Lizardman() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "lizardman" ); + Body = Utility.RandomList( 35, 36 ); + BaseSoundID = 417; + + SetStr( 96, 120 ); + SetDex( 86, 105 ); + SetInt( 36, 60 ); + + SetHits( 58, 72 ); + + SetDamage( 5, 7 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 5, 10 ); + SetResistance( ResistanceType.Cold, 5, 10 ); + SetResistance( ResistanceType.Poison, 10, 20 ); + + SetSkill( SkillName.MagicResist, 35.1, 60.0 ); + SetSkill( SkillName.Tactics, 55.1, 80.0 ); + SetSkill( SkillName.Wrestling, 50.1, 70.0 ); + + Fame = 1500; + Karma = -1500; + + VirtualArmor = 28; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + // TODO: weapon + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 12; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + public Lizardman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs new file mode 100644 index 000000000..dd28c953f --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/OphidianKnight.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an ophidian corpse" )] + [TypeAlias( "Server.Mobiles.OphidianAvenger" )] + public class OphidianKnight : BaseCreature + { + private static string[] m_Names = new string[] + { + "an ophidian knight-errant", + "an ophidian avenger" + }; + + [Constructable] + public OphidianKnight() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = m_Names[Utility.Random( m_Names.Length )]; + Body = 86; + BaseSoundID = 634; + + SetStr( 417, 595 ); + SetDex( 166, 175 ); + SetInt( 46, 70 ); + + SetHits( 266, 342 ); + SetMana( 0 ); + + SetDamage( 16, 19 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 90, 100 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.Poisoning, 60.1, 80.0 ); + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 10000; + Karma = -10000; + + VirtualArmor = 40; + + PackItem( new LesserPoisonPotion() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 2 ); + } + + public override int Meat{ get{ return 2; } } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 3; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public OphidianKnight( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs new file mode 100644 index 000000000..d2931e2ae --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/OphidianWarrior.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an ophidian corpse" )] + public class OphidianWarrior : BaseCreature + { + private static string[] m_Names = new string[] + { + "an ophidian warrior", + "an ophidian enforcer" + }; + + [Constructable] + public OphidianWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = m_Names[Utility.Random( m_Names.Length )]; + Body = 86; + BaseSoundID = 634; + + SetStr( 150, 320 ); + SetDex( 94, 190 ); + SetInt( 64, 160 ); + + SetHits( 128, 155 ); + SetMana( 0 ); + + SetDamage( 5, 11 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 25, 35 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 25, 35 ); + + SetSkill( SkillName.MagicResist, 70.1, 85.0 ); + SetSkill( SkillName.Swords, 60.1, 85.0 ); + SetSkill( SkillName.Tactics, 75.1, 90.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 36; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Average ); + AddLoot( LootPack.Gems ); + } + + public override int Meat{ get{ return 1; } } + public override int TreasureMapLevel{ get{ return 1; } } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.TerathansAndOphidians; } + } + + public OphidianWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Scorpion.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Scorpion.cs new file mode 100644 index 000000000..e7f6385fc --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Scorpion.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a scorpion corpse" )] + public class Scorpion : BaseCreature + { + [Constructable] + public Scorpion() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a scorpion"; + Body = 48; + BaseSoundID = 397; + + SetStr( 73, 115 ); + SetDex( 76, 95 ); + SetInt( 16, 30 ); + + SetHits( 50, 63 ); + SetMana( 0 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 60 ); + SetDamageType( ResistanceType.Poison, 40 ); + + SetResistance( ResistanceType.Physical, 20, 25 ); + SetResistance( ResistanceType.Fire, 10, 15 ); + SetResistance( ResistanceType.Cold, 20, 25 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 10, 15 ); + + SetSkill( SkillName.Poisoning, 80.1, 100.0 ); + SetSkill( SkillName.MagicResist, 30.1, 35.0 ); + SetSkill( SkillName.Tactics, 60.3, 75.0 ); + SetSkill( SkillName.Wrestling, 50.3, 65.0 ); + + Fame = 2000; + Karma = -2000; + + VirtualArmor = 28; + + Tamable = true; + ControlSlots = 1; + MinTameSkill = 47.1; + + PackItem( new LesserPoisonPotion() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + } + + public override int Meat{ get{ return 1; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + public override PackInstinct PackInstinct{ get{ return PackInstinct.Arachnid; } } + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + public override Poison HitPoison{ get{ return (0.8 >= Utility.RandomDouble() ? Poison.Greater : Poison.Deadly); } } + + public Scorpion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs new file mode 100644 index 000000000..34a8258c9 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/StoneHarpy.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a stone harpy corpse" )] + public class StoneHarpy : BaseCreature + { + [Constructable] + public StoneHarpy() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a stone harpy"; + Body = 73; + BaseSoundID = 402; + + SetStr( 296, 320 ); + SetDex( 86, 110 ); + SetInt( 51, 75 ); + + SetHits( 178, 192 ); + SetMana( 0 ); + + SetDamage( 8, 16 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Poison, 25 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 10, 20 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.MagicResist, 50.1, 65.0 ); + SetSkill( SkillName.Tactics, 70.1, 100.0 ); + SetSkill( SkillName.Wrestling, 70.1, 100.0 ); + + Fame = 4500; + Karma = -4500; + + VirtualArmor = 50; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average, 2 ); + AddLoot( LootPack.Gems, 2 ); + } + + public override int GetAttackSound() + { + return 916; + } + + public override int GetAngerSound() + { + return 916; + } + + public override int GetDeathSound() + { + return 917; + } + + public override int GetHurtSound() + { + return 919; + } + + public override int GetIdleSound() + { + return 918; + } + + public override int Meat{ get{ return 1; } } + public override int Feathers{ get{ return 50; } } + + public StoneHarpy( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Reptile/Melee/Wyvern.cs b/Scripts/Mobiles/Monsters/Reptile/Melee/Wyvern.cs new file mode 100644 index 000000000..810654023 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Reptile/Melee/Wyvern.cs @@ -0,0 +1,104 @@ +using System; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a wyvern corpse" )] + public class Wyvern : BaseCreature + { + [Constructable] + public Wyvern () : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a wyvern"; + Body = 62; + BaseSoundID = 362; + + SetStr( 202, 240 ); + SetDex( 153, 172 ); + SetInt( 51, 90 ); + + SetHits( 125, 141 ); + + SetDamage( 8, 19 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 35, 45 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 90, 100 ); + SetResistance( ResistanceType.Energy, 30, 40 ); + + SetSkill( SkillName.Poisoning, 60.1, 80.0 ); + SetSkill( SkillName.MagicResist, 65.1, 80.0 ); + SetSkill( SkillName.Tactics, 65.1, 90.0 ); + SetSkill( SkillName.Wrestling, 65.1, 80.0 ); + + Fame = 4000; + Karma = -4000; + + VirtualArmor = 40; + + PackItem( new LesserPoisonPotion() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Meager ); + AddLoot( LootPack.MedScrolls ); + } + + public override bool ReacquireOnMovement{ get{ return true; } } + + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + public override Poison HitPoison{ get{ return Poison.Deadly; } } + public override int TreasureMapLevel{ get{ return 2; } } + + public override int Meat{ get{ return 10; } } + public override int Hides{ get{ return 20; } } + public override HideType HideType{ get{ return HideType.Horned; } } + + public override int GetAttackSound() + { + return 713; + } + + public override int GetAngerSound() + { + return 718; + } + + public override int GetDeathSound() + { + return 716; + } + + public override int GetHurtSound() + { + return 721; + } + + public override int GetIdleSound() + { + return 725; + } + + public Wyvern( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/BakeKitsune.cs b/Scripts/Mobiles/Monsters/SE/BakeKitsune.cs new file mode 100644 index 000000000..36258a4bd --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/BakeKitsune.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a bake kitsune corpse" )] + public class BakeKitsune : BaseCreature + { + + [Constructable] + public BakeKitsune() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a bake kitsune"; + Body = 246; + + SetStr( 171, 220 ); + SetDex( 126, 145 ); + SetInt( 376, 425 ); + + SetHits( 301, 350 ); + + SetDamage( 15, 22 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Energy, 30 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 70, 90 ); + SetResistance( ResistanceType.Cold, 40, 60 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + + SetSkill( SkillName.EvalInt, 80.1, 90.0 ); + SetSkill( SkillName.Magery, 80.1, 90.0 ); + SetSkill( SkillName.MagicResist, 80.1, 100.0 ); + SetSkill( SkillName.Tactics, 70.1, 90.0 ); + SetSkill( SkillName.Wrestling, 50.1, 55.0 ); + + Fame = 8000; + Karma = -8000; + + Tamable = true; + ControlSlots = 2; + MinTameSkill = 80.7; + + + if ( Utility.RandomDouble() < .25 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.MedScrolls, 2 ); + } + + public override int Meat{ get{ return 5; } } + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Barbed; } } + public override FoodType FavoriteFood{ get{ return FoodType.Fish; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() ) + { + /* Blood Bath + * Start cliloc 1070826 + * Sound: 0x52B + * 2-3 blood spots + * Damage: 2 hps per second for 5 seconds + * End cliloc: 1070824 + */ + + ExpireTimer timer = (ExpireTimer)m_Table[defender]; + + if ( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070825 ); // The creature continues to rage! + } + else + defender.SendLocalizedMessage( 1070826 ); // The creature goes into a rage, inflicting heavy damage! + + timer = new ExpireTimer( defender, this ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private Mobile m_From; + private int m_Count; + + public ExpireTimer( Mobile m, Mobile from ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_From = from; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + Stop(); + m_Table.Remove( m_Mobile ); + } + + public void DrainLife() + { + if ( m_Mobile.Alive ) + m_Mobile.Damage( 2, m_From ); + else + DoExpire(); + } + + protected override void OnTick() + { + DrainLife(); + + if ( ++m_Count >= 5 ) + { + DoExpire(); + m_Mobile.SendLocalizedMessage( 1070824 ); // The creature's rage subsides. + } + } + } + + public override int GetAngerSound() + { + return 0x4DE; + } + + public override int GetIdleSound() + { + return 0x4DD; + } + + public override int GetAttackSound() + { + return 0x4DC; + } + + public override int GetHurtSound() + { + return 0x4DF; + } + + public override int GetDeathSound() + { + return 0x4DB; + } + + public BakeKitsune( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/DeathWatchBeetle.cs b/Scripts/Mobiles/Monsters/SE/DeathWatchBeetle.cs new file mode 100644 index 000000000..7df747751 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/DeathWatchBeetle.cs @@ -0,0 +1,123 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a deathwatchbeetle corpse" )] + [TypeAlias( "Server.Mobiles.DeathWatchBeetle" )] + public class DeathwatchBeetle : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.CrushingBlow; + } + + [Constructable] + public DeathwatchBeetle() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a deathwatch beetle"; + Body = 242; + + SetStr( 136, 160 ); + SetDex( 41, 52 ); + SetInt( 31, 40 ); + + SetHits( 121, 145 ); + SetMana( 20 ); + + SetDamage( 5, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 15, 30 ); + SetResistance( ResistanceType.Cold, 15, 30 ); + SetResistance( ResistanceType.Poison, 50, 80 ); + SetResistance( ResistanceType.Energy, 20, 35 ); + + SetSkill( SkillName.MagicResist, 50.1, 58.0 ); + SetSkill( SkillName.Tactics, 67.1, 77.0 ); + SetSkill( SkillName.Wrestling, 50.1, 60.0 ); + SetSkill( SkillName.Anatomy, 30.1, 34.0 ); + + Fame = 1400; + Karma = -1400; + + switch ( Utility.Random( 12 ) ) + { + case 0: PackItem( new LeatherGorget() ); break; + case 1: PackItem( new LeatherGloves() ); break; + case 2: PackItem( new LeatherArms() ); break; + case 3: PackItem( new LeatherLegs() ); break; + case 4: PackItem( new LeatherCap() ); break; + case 5: PackItem( new LeatherChest() ); break; + + } + + if ( Utility.RandomDouble() < .5 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + + Tamable = true; + MinTameSkill = 41.1; + ControlSlots = 1; + } + + public override int GetAngerSound() + { + return 0x4F3; + } + + public override int GetIdleSound() + { + return 0x4F2; + } + + public override int GetAttackSound() + { + return 0x4F1; + } + + public override int GetHurtSound() + { + return 0x4F4; + } + + public override int GetDeathSound() + { + return 0x4F0; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.LowScrolls, 1 ); + AddLoot( LootPack.Potions, 1 ); + } + + public override int Feathers{ get{ return 50; } } + public override int Meat{ get{ return 1; } } + + /* + * Death spit: 0x118 + * Effect: source.MovingParticles( m, 0x36D4, 1, 0, false, false, 0x3F, 0, 0x1F73, 1, 0, 255, 0x100 ); + * Message: 1070821 + */ + + public DeathwatchBeetle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs b/Scripts/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs new file mode 100644 index 000000000..37e8194cb --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/DeathWatchBeetleHatchling.cs @@ -0,0 +1,108 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a deathwatchbeetle hatchling corpse" )] + [TypeAlias( "Server.Mobiles.DeathWatchBeetleHatchling" )] + public class DeathwatchBeetleHatchling : BaseCreature + { + [Constructable] + public DeathwatchBeetleHatchling() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a deathwatch beetle hatchling"; + Body = 242; + + SetStr( 26, 50 ); + SetDex( 41, 52 ); + SetInt( 21, 30 ); + + SetHits( 51, 60 ); + SetMana( 20 ); + + SetDamage( 2, 8 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 40 ); + SetResistance( ResistanceType.Fire, 15, 30 ); + SetResistance( ResistanceType.Cold, 15, 30 ); + SetResistance( ResistanceType.Poison, 20, 40 ); + SetResistance( ResistanceType.Energy, 20, 35 ); + + SetSkill( SkillName.Wrestling, 30.1, 40.0 ); + SetSkill( SkillName.Tactics, 47.1, 57.0 ); + SetSkill( SkillName.MagicResist, 30.1, 38.0 ); + SetSkill( SkillName.Anatomy, 20.1, 24.0 ); + + Fame = 700; + Karma = -700; + + + if( Utility.RandomBool() ) + { + Item i = Loot.RandomReagent(); + i.Amount = 3; + PackItem( i ); + } + + switch ( Utility.Random( 12 ) ) + { + case 0: PackItem( new LeatherGorget() ); break; + case 1: PackItem( new LeatherGloves() ); break; + case 2: PackItem( new LeatherArms() ); break; + case 3: PackItem( new LeatherLegs() ); break; + case 4: PackItem( new LeatherCap() ); break; + case 5: PackItem( new LeatherChest() ); break; + } + } + + public override int GetAngerSound() + { + return 0x4F3; + } + + public override int GetIdleSound() + { + return 0x4F2; + } + + public override int GetAttackSound() + { + return 0x4F1; + } + + public override int GetHurtSound() + { + return 0x4F4; + } + + public override int GetDeathSound() + { + return 0x4F0; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.LowScrolls, 1 ); + AddLoot( LootPack.Potions, 1 ); + } + + public DeathwatchBeetleHatchling( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/EliteNinja.cs b/Scripts/Mobiles/Monsters/SE/EliteNinja.cs new file mode 100644 index 000000000..90e1a68a4 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/EliteNinja.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections; +using Server.Items; +using Server.ContextMenus; +using Server.Misc; +using Server.Network; + +namespace Server.Mobiles +{ + public class EliteNinja : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + + [Constructable] + public EliteNinja() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Utility.RandomSkinHue(); + Name = "an elite ninja"; + + Body = ( this.Female = Utility.RandomBool() ) ? 0x191 : 0x190; + + SetHits( 251, 350 ); + + SetStr( 126, 225 ); + SetDex( 81, 95 ); + SetInt( 151, 165 ); + + SetDamage( 12, 20 ); + + SetDamageType( ResistanceType.Physical, 65 ); + SetDamageType( ResistanceType.Fire, 15 ); + SetDamageType( ResistanceType.Poison, 15 ); + SetDamageType( ResistanceType.Energy, 5 ); + + SetResistance( ResistanceType.Physical, 35, 65 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 25, 45 ); + SetResistance( ResistanceType.Poison, 40, 60 ); + SetResistance( ResistanceType.Energy, 35, 55 ); + + SetSkill( SkillName.Anatomy, 105.0, 120.0 ); + SetSkill( SkillName.MagicResist, 80.0, 100.0 ); + SetSkill( SkillName.Tactics, 115.0, 130.0 ); + SetSkill( SkillName.Wrestling, 95.0, 120.0 ); + SetSkill( SkillName.Fencing, 95.0, 120.0 ); + SetSkill( SkillName.Macing, 95.0, 120.0 ); + SetSkill( SkillName.Swords, 95.0, 120.0 ); + SetSkill( SkillName.Ninjitsu, 95.0, 120.0 ); + + + Fame = 8500; + Karma = -8500; + + /* TODO: + Uses Smokebombs + Hides + Stealths + Can use Ninjitsu Abilities + Can change weapons during a fight + */ + + + AddItem( new NinjaTabi() ); + AddItem( new LeatherNinjaJacket()); + AddItem( new LeatherNinjaHood()); + AddItem( new LeatherNinjaPants()); + AddItem( new LeatherNinjaMitts()); + + if( Utility.RandomDouble() < 0.33 ) + AddItem( new SmokeBomb() ); + + switch ( Utility.Random( 8 )) + { + case 0: AddItem( new Tessen() ); break; + case 1: AddItem( new Wakizashi() ); break; + case 2: AddItem( new Nunchaku() ); break; + case 3: AddItem( new Daisho() ); break; + case 4: AddItem( new Sai() ); break; + case 5: AddItem( new Tekagi() ); break; + case 6: AddItem( new Kama() ); break; + case 7: AddItem( new Katana() ); break; + } + + Utility.AssignRandomHair( this ); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + c.DropItem( new BookOfNinjitsu() ); + } + + public override bool BardImmune{ get{ return true; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + + public EliteNinja( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/FanDancer.cs b/Scripts/Mobiles/Monsters/SE/FanDancer.cs new file mode 100644 index 000000000..e66e74a60 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/FanDancer.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using Server; +using Server.Network; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a fan dancer corpse" )] + public class FanDancer : BaseCreature + { + [Constructable] + public FanDancer() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a fan dancer"; + Body = 247; + BaseSoundID = 0x372; + + SetStr( 301, 375 ); + SetDex( 201, 255 ); + SetInt( 21, 25 ); + + SetHits( 351, 430 ); + + SetDamage( 12, 17 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Fire, 10 ); + SetDamageType( ResistanceType.Cold, 10 ); + SetDamageType( ResistanceType.Poison, 10 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 50, 70 ); + SetResistance( ResistanceType.Cold, 50, 70 ); + SetResistance( ResistanceType.Poison, 50, 70 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + + SetSkill( SkillName.MagicResist, 100.1, 110.0 ); + SetSkill( SkillName.Tactics, 85.1, 95.0 ); + SetSkill( SkillName.Wrestling, 85.1, 95.0 ); + SetSkill( SkillName.Anatomy, 85.1, 95.0 ); + + Fame = 9000; + Karma = -9000; + + if ( Utility.RandomDouble() < .33 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + + AddItem( new Tessen() ); + + if ( 0.02 >= Utility.RandomDouble() ) + PackItem( new OrigamiPaper() ); + } + + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool Uncalmable{ get{ return true; } } + + /* TODO: Repel Magic + * 10% chance of repelling a melee attack (why did they call it repel magic anyway?) + * Cliloc: 1070844 + * Effect: damage is dealt to the attacker, no damage is taken by the fan dancer + */ + + public override void OnDamagedBySpell( Mobile attacker ) + { + base.OnDamagedBySpell( attacker ); + + if ( 0.8 > Utility.RandomDouble() && !attacker.InRange( this, 1 ) ) + { + /* Fan Throw + * Effect: - To: "0x57D4F5B" - ItemId: "0x27A3" - ItemIdName: "Tessen" - FromLocation: "(992 299, 24)" - ToLocation: "(992 308, 22)" - Speed: "10" - Duration: "0" - FixedDirection: "False" - Explode: "False" - Hue: "0x0" - Render: "0x0" + * Damage: 50-65 + */ + Effects.SendPacket( attacker, attacker.Map, new HuedEffect( EffectType.Moving, Serial.Zero, Serial.Zero, 0x27A3, this.Location, attacker.Location, 10, 0, false, false, 0, 0 ) ); + AOS.Damage( attacker, this, Utility.RandomMinMax( 50, 65 ), 100, 0, 0, 0, 0 ); + } + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( 0.8 > Utility.RandomDouble() && !attacker.InRange( this, 1 ) ) + { + /* Fan Throw + * Effect: - To: "0x57D4F5B" - ItemId: "0x27A3" - ItemIdName: "Tessen" - FromLocation: "(992 299, 24)" - ToLocation: "(992 308, 22)" - Speed: "10" - Duration: "0" - FixedDirection: "False" - Explode: "False" - Hue: "0x0" - Render: "0x0" + * Damage: 50-65 + */ + Effects.SendPacket( attacker, attacker.Map, new HuedEffect( EffectType.Moving, Serial.Zero, Serial.Zero, 0x27A3, this.Location, attacker.Location, 10, 0, false, false, 0, 0 ) ); + AOS.Damage( attacker, this, Utility.RandomMinMax( 50, 65 ), 100, 0, 0, 0, 0 ); + } + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( !IsFanned( defender ) && 0.05 > Utility.RandomDouble() ) + { + /* Fanning Fire + * Graphic: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x3709" ItemIdName: "fire column" FromLocation: "(994 325, 16)" ToLocation: "(994 325, 16)" Speed: "10" Duration: "30" FixedDirection: "True" Explode: "False" Hue: "0x0" RenderMode: "0x0" Effect: "0x34" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x57D4F5B" Layer: "5" Unknown: "0x0" + * Sound: 0x208 + * Start cliloc: 1070833 + * Effect: Fire res -10% for 10 seconds + * Damage: 35-45, 100% fire + * End cliloc: 1070834 + * Effect does not stack + */ + + defender.SendLocalizedMessage( 1070833 ); // The creature fans you with fire, reducing your resistance to fire attacks. + + int effect = -(defender.FireResistance / 10); + + ResistanceMod mod = new ResistanceMod( ResistanceType.Fire, effect ); + + defender.FixedParticles( 0x37B9, 10, 30, 0x34, EffectLayer.RightFoot ); + defender.PlaySound( 0x208 ); + + // This should be done in place of the normal attack damage. + //AOS.Damage( defender, this, Utility.RandomMinMax( 35, 45 ), 0, 100, 0, 0, 0 ); + + defender.AddResistanceMod( mod ); + + ExpireTimer timer = new ExpireTimer( defender, mod, TimeSpan.FromSeconds( 10.0 ) ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + public bool IsFanned( Mobile m ) + { + return m_Table.Contains( m ); + } + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private ResistanceMod m_Mod; + + public ExpireTimer( Mobile m, ResistanceMod mod, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + m_Mod = mod; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage( 1070834 ); // Your resistance to fire attacks has returned. + m_Mobile.RemoveResistanceMod( m_Mod ); + Stop(); + m_Table.Remove( m_Mobile ); + } + } + + public FanDancer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/FireBeetle.cs b/Scripts/Mobiles/Monsters/SE/FireBeetle.cs new file mode 100644 index 000000000..92947da0e --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/FireBeetle.cs @@ -0,0 +1,106 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a fire beetle corpse" )] + [Server.Engines.Craft.Forge] + public class FireBeetle : BaseMount + { + public override bool SubdueBeforeTame{ get{ return true; } } // Must be beaten into submission + public override bool StatLossAfterTame{ get{ return true; } } + + [Constructable] + public FireBeetle() : base( "a fire beetle", 0xA9, 0x3E95, AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + SetStr( 300 ); + SetDex( 100 ); + SetInt( 500 ); + + SetHits( 200 ); + + SetDamage( 7, 20 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Fire, 100 ); + + SetResistance( ResistanceType.Physical, 40 ); + SetResistance( ResistanceType.Fire, 70, 75 ); + SetResistance( ResistanceType.Cold, 10 ); + SetResistance( ResistanceType.Poison, 30 ); + SetResistance( ResistanceType.Energy, 30 ); + + SetSkill( SkillName.MagicResist, 90.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 4000; + Karma = -4000; + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 93.9; + + PackItem( new SulfurousAsh( Utility.RandomMinMax( 16, 25 ) ) ); + PackItem( new IronIngot( 2 ) ); + + Hue = 0x489; + } + + public override int GetAngerSound() + { + return 0x21D; + } + + public override int GetIdleSound() + { + return 0x21D; + } + + public override int GetAttackSound() + { + return 0x162; + } + + public override int GetHurtSound() + { + return 0x163; + } + + public override int GetDeathSound() + { + return 0x21D; + } + + + public override int Meat{ get{ return 16; } } + public override FoodType FavoriteFood{ get{ return FoodType.Meat; } } + + public override double GetControlChance( Mobile m ) + { + return 1.0; + } + + public FireBeetle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if( version == 0 ) + Hue = 0x489; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/Kappa.cs b/Scripts/Mobiles/Monsters/SE/Kappa.cs new file mode 100644 index 000000000..3f23def43 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/Kappa.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a kappa corpse" )] + public class Kappa : BaseCreature + { + + [Constructable] + public Kappa() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a kappa"; + Body = 240; + + SetStr( 186, 230 ); + SetDex( 51, 75 ); + SetInt( 41, 55 ); + + SetHits( 151, 180 ); + + SetDamage( 6, 12 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 35, 50 ); + SetResistance( ResistanceType.Fire, 35, 50 ); + SetResistance( ResistanceType.Cold, 25, 50 ); + SetResistance( ResistanceType.Poison, 35, 50 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.MagicResist, 60.1, 70.0 ); + SetSkill( SkillName.Tactics, 79.1, 89.0 ); + SetSkill( SkillName.Wrestling, 60.1, 70.0 ); + + Fame = 1700; + Karma = -1700; + + PackItem( new RawFishSteak( 3 ) ); + for( int i = 0; i < 2; i++ ) + { + switch ( Utility.Random( 6 ) ) + { + case 0: PackItem( new Gears() ); break; + case 1: PackItem( new Hinge() ); break; + case 2: PackItem( new Axle() ); break; + } + } + + + } + + + + public override void GenerateLoot() + { + AddLoot( LootPack.Meager ); + AddLoot( LootPack.Average ); + } + + public override int Meat{ get{ return 1; } } + public override int Hides{ get{ return 10; } } + public override HideType HideType{ get{ return HideType.Spined; } } + + // TODO: Soul Sap + /* + * Message: 1070820 + * Spits pool of acid (blood, hue 0x3F), hits lost 6-10 per second/step + * Damage is resistable (physical) + * Acid last 10 seconds + */ + + public override int GetAngerSound() + { + return 0x50B; + } + + public override int GetIdleSound() + { + return 0x50A; + } + + public override int GetAttackSound() + { + return 0x509; + } + + public override int GetHurtSound() + { + return 0x50C; + } + + public override int GetDeathSound() + { + return 0x508; + } + + public override void OnGaveMeleeAttack(Mobile defender) + { + base.OnGaveMeleeAttack (defender); + + if ( 0.1 > Utility.RandomDouble() ) + { + if ( !IsBeingDrained( defender ) ) + { + defender.SendLocalizedMessage( 1070848 ); // You feel your life force being stolen away. + BeginLifeDrain( defender, this ); + } + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsBeingDrained( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void BeginLifeDrain( Mobile m, Mobile from ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( from, m ); + m_Table[m] = t; + + t.Start(); + } + + public static void DrainLife( Mobile m, Mobile from ) + { + if ( m.Alive ) + { + int damageGiven = AOS.Damage( m, from, 5, 100, 0, 0, 0, 0 ); + + from.Hits += damageGiven; + } + else + { + EndLifeDrain( m ); + } + } + + public static void EndLifeDrain( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table.Remove( m ); + + m.SendLocalizedMessage( 1070849 ); // The drain on your life force is gone. + } + + /* + public override bool OnBeforeDeath() + { + SpillAcid( 1, 4, 10, 6, 10 ); + + return base.OnBeforeDeath(); + } + */ + + public Kappa( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private Mobile m_Mobile; + private int m_Count; + + public InternalTimer( Mobile from, Mobile m ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_From = from; + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + DrainLife( m_Mobile, m_From ); + + if ( ++m_Count == 5 ) + EndLifeDrain( m_Mobile ); + } + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/KazeKemono.cs b/Scripts/Mobiles/Monsters/SE/KazeKemono.cs new file mode 100644 index 000000000..20f2223f0 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/KazeKemono.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a kaze kemono corpse" )] + public class KazeKemono : BaseCreature + { + + [Constructable] + public KazeKemono() + : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a kaze kemono"; + Body = 196; + BaseSoundID = 655; + + SetStr( 201, 275 ); + SetDex( 101, 155 ); + SetInt( 101, 105 ); + + SetHits( 251, 330 ); + + SetDamage( 15, 20 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Fire, 10 ); + SetDamageType( ResistanceType.Cold, 10 ); + SetDamageType( ResistanceType.Poison, 10 ); + + SetResistance( ResistanceType.Physical, 50, 70 ); + SetResistance( ResistanceType.Fire, 30, 60 ); + SetResistance( ResistanceType.Cold, 30, 60 ); + SetResistance( ResistanceType.Poison, 50, 70 ); + SetResistance( ResistanceType.Energy, 60, 80 ); + + SetSkill( SkillName.MagicResist, 110.1, 125.0 ); + SetSkill( SkillName.Tactics, 55.1, 65.0 ); + SetSkill( SkillName.Wrestling, 85.1, 95.0 ); + SetSkill( SkillName.Anatomy, 25.1, 35.0 ); + SetSkill( SkillName.Magery, 95.1, 105.0 ); + + Fame = 8000; + Karma = -8000; + } + + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 3 ); + } + + public override bool BleedImmune { get { return true; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if( 0.1 > Utility.RandomDouble() ) + { + /* Flurry of Twigs + * Start cliloc: 1070850 + * Effect: Physical resistance -15% for 5 seconds + * End cliloc: 1070852 + * Effect: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(1048 779, 6)" ToLocation: "(1048 779, 6)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + */ + + ExpireTimer timer = (ExpireTimer)m_FlurryOfTwigsTable[defender]; + + if( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070851 ); // The creature lands another blow in your weakened state. + } + else + defender.SendLocalizedMessage( 1070850 ); // The creature's flurry of twigs has made you more susceptible to physical attacks! + + int effect = -(defender.PhysicalResistance * 15 / 100); + + ResistanceMod mod = new ResistanceMod( ResistanceType.Physical, effect ); + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.AddResistanceMod( mod ); + + timer = new ExpireTimer( defender, mod, m_FlurryOfTwigsTable, TimeSpan.FromSeconds( 5.0 ) ); + timer.Start(); + m_FlurryOfTwigsTable[defender] = timer; + } + else if( 0.05 > Utility.RandomDouble() ) + { + /* Chlorophyl Blast + * Start cliloc: 1070827 + * Effect: Energy resistance -50% for 10 seconds + * End cliloc: 1070829 + * Effect: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(1048 779, 6)" ToLocation: "(1048 779, 6)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + */ + + ExpireTimer timer = (ExpireTimer)m_ChlorophylBlastTable[defender]; + + if( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070828 ); // The creature continues to hinder your energy resistance! + } + else + defender.SendLocalizedMessage( 1070827 ); // The creature's attack has made you more susceptible to energy attacks! + + int effect = -(defender.EnergyResistance / 2); + + ResistanceMod mod = new ResistanceMod( ResistanceType.Energy, effect ); + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.AddResistanceMod( mod ); + + timer = new ExpireTimer( defender, mod, m_ChlorophylBlastTable, TimeSpan.FromSeconds( 10.0 ) ); + timer.Start(); + m_ChlorophylBlastTable[defender] = timer; + } + } + + private static Hashtable m_FlurryOfTwigsTable = new Hashtable(); + private static Hashtable m_ChlorophylBlastTable = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private ResistanceMod m_Mod; + private Hashtable m_Table; + + public ExpireTimer( Mobile m, ResistanceMod mod, Hashtable table, TimeSpan delay ) + : base( delay ) + { + m_Mobile = m; + m_Mod = mod; + m_Table = table; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + m_Mobile.RemoveResistanceMod( m_Mod ); + Stop(); + m_Table.Remove( m_Mobile ); + } + + protected override void OnTick() + { + if( m_Mod.Type == ResistanceType.Physical ) + m_Mobile.SendLocalizedMessage( 1070852 ); // Your resistance to physical attacks has returned. + else + m_Mobile.SendLocalizedMessage( 1070829 ); // Your resistance to energy attacks has returned. + + DoExpire(); + } + } + + public KazeKemono( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/LadyOfTheSnow.cs b/Scripts/Mobiles/Monsters/SE/LadyOfTheSnow.cs new file mode 100644 index 000000000..40d84d118 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/LadyOfTheSnow.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a lady of the snow corpse" )] + public class LadyOfTheSnow : BaseCreature + { + [Constructable] + public LadyOfTheSnow() + : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a lady of the snow"; + Body = 252; + BaseSoundID = 0x482; + + SetStr( 276, 305 ); + SetDex( 106, 125 ); + SetInt( 471, 495 ); + + SetHits( 596, 625 ); + + SetDamage( 13, 20 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Cold, 80 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 40, 55 ); + SetResistance( ResistanceType.Cold, 70, 90 ); + SetResistance( ResistanceType.Poison, 60, 70 ); + SetResistance( ResistanceType.Energy, 65, 85 ); + + SetSkill( SkillName.Magery, 95.1, 110.0 ); + SetSkill( SkillName.MagicResist, 90.1, 105.0 ); + SetSkill( SkillName.Tactics, 80.1, 100.0 ); + SetSkill( SkillName.Wrestling, 80.1, 100.0 ); + + Fame = 15200; + Karma = -15200; + + + PackReg( 3 ); + PackItem( new Necklace() ); + + if ( 0.25 > Utility.RandomDouble() ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + public override int GetDeathSound() + { + return 0x370; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + } + + public override bool BleedImmune { get { return true; } } + public override bool CanRummageCorpses { get { return true; } } + public override int TreasureMapLevel { get { return 4; } } + + // TODO: Snowball + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if( 0.1 > Utility.RandomDouble() ) + { + /* Cold Wind + * Graphics: Message - Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(928 164, 34)" ToLocation: "(928 164, 34)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + * Start cliloc: 1070832 + * Damage: 1hp per second for 5 seconds + * End cliloc: 1070830 + * Reset cliloc: 1070831 + */ + + ExpireTimer timer = (ExpireTimer)m_Table[defender]; + + if( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070831 ); // The freezing wind continues to blow! + } + else + defender.SendLocalizedMessage( 1070832 ); // An icy wind surrounds you, freezing your lungs as you breathe! + + timer = new ExpireTimer( defender, this ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private Mobile m_From; + private int m_Count; + + public ExpireTimer( Mobile m, Mobile from ) + : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_From = from; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + Stop(); + m_Table.Remove( m_Mobile ); + } + + public void DrainLife() + { + if( m_Mobile.Alive ) + m_Mobile.Damage( 2, m_From ); + else + DoExpire(); + } + + protected override void OnTick() + { + DrainLife(); + + if( ++m_Count >= 5 ) + { + DoExpire(); + m_Mobile.SendLocalizedMessage( 1070830 ); // The icy wind dissipates. + } + } + } + + public LadyOfTheSnow( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/Oni.cs b/Scripts/Mobiles/Monsters/SE/Oni.cs new file mode 100644 index 000000000..163c0fcd2 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/Oni.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an oni corpse" )] + public class Oni : BaseCreature + { + [Constructable] + public Oni() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an oni"; + Body = 241; + + SetStr( 801, 910 ); + SetDex( 151, 300 ); + SetInt( 171, 195 ); + + SetHits( 401, 530 ); + + SetDamage( 14, 20 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Fire, 10 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 65, 80 ); + SetResistance( ResistanceType.Fire, 50, 70 ); + SetResistance( ResistanceType.Cold, 35, 50 ); + SetResistance( ResistanceType.Poison, 45, 70 ); + SetResistance( ResistanceType.Energy, 45, 65 ); + + SetSkill( SkillName.EvalInt, 100.1, 125.0 ); + SetSkill( SkillName.Magery, 96.1, 106.0 ); + SetSkill( SkillName.Anatomy, 85.1, 95.0 ); + SetSkill( SkillName.MagicResist, 85.1, 100.0 ); + SetSkill( SkillName.Tactics, 86.1, 101.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 12000; + Karma = -12000; + + if ( Utility.RandomDouble() < .33 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + + // TODO: Brain (0x1CF0) or Skull (0x1AE3) or Body Part (0x1CE3) + } + + public override int GetAngerSound() + { + return 0x4E3; + } + + public override int GetIdleSound() + { + return 0x4E2; + } + + public override int GetAttackSound() + { + return 0x4E1; + } + + public override int GetHurtSound() + { + return 0x4E4; + } + + public override int GetDeathSound() + { + return 0x4E0; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 3 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 4; } } + + /* TODO: Angry Fire + * cliloc 1070823 + * Action: 4 4 1 true false 1 + * Damage: 50-85, 60 phys, 20 fire, 20 nrgy according to the guide + * With 45/49/70 res I got 48 + * 50: 30/10/10 -> 16 + 5 + 3 = 24 + * 85: 51/17/17 -> 28 + 8 + 5 = 41 + */ + + public Oni( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/RaiJu.cs b/Scripts/Mobiles/Monsters/SE/RaiJu.cs new file mode 100644 index 000000000..3c4704a04 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/RaiJu.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a rai-ju corpse" )] + public class RaiJu : BaseCreature + { + [Constructable] + public RaiJu() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a Rai-Ju"; + Body = 199; + BaseSoundID = 0x346; + + SetStr( 151, 225 ); + SetDex( 81, 135 ); + SetInt( 176, 180 ); + + SetHits( 201, 280 ); + + SetDamage( 12, 15 ); + + SetDamageType( ResistanceType.Physical, 10 ); + SetDamageType( ResistanceType.Fire, 10 ); + SetDamageType( ResistanceType.Cold, 10 ); + SetDamageType( ResistanceType.Poison, 10 ); + SetDamageType( ResistanceType.Energy, 60 ); + + SetResistance( ResistanceType.Physical, 45, 65 ); + SetResistance( ResistanceType.Fire, 70, 85 ); + SetResistance( ResistanceType.Cold, 30, 60 ); + SetResistance( ResistanceType.Poison, 50, 70 ); + SetResistance( ResistanceType.Energy, 60, 80 ); + + SetSkill( SkillName.Wrestling, 85.1, 95.0 ); + SetSkill( SkillName.Tactics, 55.1, 65.0 ); + SetSkill( SkillName.MagicResist, 110.1, 125.0 ); + SetSkill( SkillName.Anatomy, 25.1, 35.0 ); + + Fame = 8000; + Karma = -8000; + + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 2 ); + AddLoot( LootPack.Gems, 2 ); + } + public override bool BleedImmune{ get{ return true; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() && !IsStunned( defender ) ) + { + /* Lightning Fist + * Cliloc: 1070839 + * Effect: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(884 715, 10)" ToLocation: "(884 715, 10)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + * Damage: 35-65, 100% energy, resistable + * Freezes for 4 seconds + * Effect cannot stack + */ + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.SendLocalizedMessage( 1070839 ); // The creature attacks with stunning force! + + // This should be done in place of the normal attack damage. + //AOS.Damage( defender, this, Utility.RandomMinMax( 35, 65 ), 0, 0, 0, 0, 100 ); + + defender.Frozen = true; + + ExpireTimer timer = new ExpireTimer( defender, TimeSpan.FromSeconds( 4.0 ) ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + public bool IsStunned( Mobile m ) + { + return m_Table.Contains( m ); + } + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + + public ExpireTimer( Mobile m, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + m_Mobile.Frozen = false; + Stop(); + m_Table.Remove( m_Mobile ); + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage( 1005603 ); // You can move again! + DoExpire(); + } + } + + public RaiJu( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/RevenantLion.cs b/Scripts/Mobiles/Monsters/SE/RevenantLion.cs new file mode 100644 index 000000000..c7f13eefe --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/RevenantLion.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a revenant lion corpse" )] + public class RevenantLion : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.BleedAttack; + } + + [Constructable] + public RevenantLion() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a Revenant Lion"; + Body = 251; + + SetStr( 276, 325 ); + SetDex( 156, 175 ); + SetInt( 76, 105 ); + + SetHits( 251, 280 ); + + SetDamage( 18, 24 ); + + SetDamageType( ResistanceType.Physical, 30 ); + SetDamageType( ResistanceType.Cold, 30 ); + SetDamageType( ResistanceType.Poison, 10 ); + SetDamageType( ResistanceType.Energy, 30 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 55, 65 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 80.1, 90.0 ); + SetSkill( SkillName.Magery, 80.1, 90.0 ); + SetSkill( SkillName.Poisoning, 120.1, 130.0 ); + SetSkill( SkillName.MagicResist, 70.1, 90.0 ); + SetSkill( SkillName.Tactics, 60.1, 80.0 ); + SetSkill( SkillName.Wrestling, 80.1, 88.0 ); + + Fame = 4000; + Karma = -4000; + PackNecroReg( 6, 8 ); + + switch ( Utility.Random( 10 )) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + } + + public override int GetAngerSound() + { + return 0x518; + } + + public override int GetIdleSound() + { + return 0x517; + } + + public override int GetAttackSound() + { + return 0x516; + } + + public override int GetHurtSound() + { + return 0x519; + } + + public override int GetDeathSound() + { + return 0x515; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 2 ); + AddLoot( LootPack.MedScrolls, 2 ); + + // TODO: Bone Pile + } + + public override bool BleedImmune{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + public override Poison HitPoison{ get{ return Poison.Greater; } } + + public RevenantLion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/Ronin.cs b/Scripts/Mobiles/Monsters/SE/Ronin.cs new file mode 100644 index 000000000..43279764e --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/Ronin.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a ronin corpse" )] + public class Ronin : BaseCreature + { + public override bool ClickTitle{ get{ return false; } } + + [Constructable] + public Ronin() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + SpeechHue = Utility.RandomDyedHue(); + Hue = Utility.RandomSkinHue(); + Name = "a ronin"; + Body = (( this.Female = Utility.RandomBool() ) ? Body = 0x191 : Body = 0x190); + + Hue = Utility.RandomSkinHue(); + + SetStr( 326, 375 ); + SetDex( 31, 45 ); + SetInt( 101, 110 ); + + SetHits( 301, 400 ); + SetMana( 101, 110 ); + + SetDamage( 17, 25 ); + + SetDamageType( ResistanceType.Physical, 90 ); + SetDamageType( ResistanceType.Poison, 10 ); + + SetResistance( ResistanceType.Physical, 55, 75 ); + SetResistance( ResistanceType.Fire, 40, 60 ); + SetResistance( ResistanceType.Cold, 35, 55 ); + SetResistance( ResistanceType.Poison, 50, 70 ); + SetResistance( ResistanceType.Energy, 55, 75 ); + + SetSkill( SkillName.MagicResist, 42.6, 57.5 ); + SetSkill( SkillName.Tactics, 115.1, 130.0 ); + SetSkill( SkillName.Wrestling, 92.6, 107.5 ); + SetSkill( SkillName.Anatomy, 110.1, 125.0 ); + + SetSkill( SkillName.Fencing, 92.6, 107.5 ); + SetSkill( SkillName.Macing, 92.6, 107.5 ); + SetSkill( SkillName.Swords, 92.6, 107.5 ); + + Fame = 8500; + Karma = -8500; + + AddItem( new SamuraiTabi() ); + AddItem( new LeatherHiroSode()); + AddItem( new LeatherDo()); + + switch ( Utility.Random( 4 )) + { + case 0: AddItem( new LightPlateJingasa()); break; + case 1: AddItem( new ChainHatsuburi() ); break; + case 2: AddItem( new DecorativePlateKabuto() ); break; + case 3: AddItem( new LeatherJingasa()); break; + } + + switch ( Utility.Random( 3 )) + { + case 0: AddItem( new StuddedHaidate()); break; + case 1: AddItem( new LeatherSuneate() ); break; + case 2: AddItem( new PlateSuneate() ); break; + } + + + + if( Utility.RandomDouble() > .2 ) + AddItem( new NoDachi() ); + else + AddItem( new Halberd() ); + + PackItem( new Wakizashi() ); + PackItem( new Longsword() ); + + Utility.AssignRandomHair( this ); + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + c.DropItem( new BookOfBushido() ); + } + + // TODO: Bushido abilities + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool BardImmune{ get{ return true; } } + public override bool CanRummageCorpses{ get{ return true; } } + + public Ronin( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/RuneBeetle.cs b/Scripts/Mobiles/Monsters/SE/RuneBeetle.cs new file mode 100644 index 000000000..ab4fe35f1 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/RuneBeetle.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a rune beetle corpse" )] + public class RuneBeetle : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.BleedAttack; + } + + [Constructable] + public RuneBeetle() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a rune beetle"; + Body = 244; + + SetStr( 401, 460 ); + SetDex( 121, 170 ); + SetInt( 376, 450 ); + + SetHits( 301, 360 ); + + SetDamage( 15, 22 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Poison, 10 ); + SetDamageType( ResistanceType.Energy, 70 ); + + SetResistance( ResistanceType.Physical, 40, 65 ); + SetResistance( ResistanceType.Fire, 35, 50 ); + SetResistance( ResistanceType.Cold, 35, 50 ); + SetResistance( ResistanceType.Poison, 75, 95 ); + SetResistance( ResistanceType.Energy, 40, 60 ); + + SetSkill( SkillName.EvalInt, 100.1, 125.0 ); + SetSkill( SkillName.Magery, 100.1, 110.0 ); + SetSkill( SkillName.Poisoning, 120.1, 140.0 ); + SetSkill( SkillName.MagicResist, 95.1, 110.0 ); + SetSkill( SkillName.Tactics, 78.1, 93.0 ); + SetSkill( SkillName.Wrestling, 70.1, 77.5 ); + + Fame = 15000; + Karma = -15000; + + + if ( Utility.RandomDouble() < .25 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + + switch ( Utility.Random( 10 )) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + + Tamable = true; + ControlSlots = 3; + MinTameSkill = 93.9; + + } + + public override int GetAngerSound() + { + return 0x4E8; + } + + public override int GetIdleSound() + { + return 0x4E7; + } + + public override int GetAttackSound() + { + return 0x4E6; + } + + public override int GetHurtSound() + { + return 0x4E9; + } + + public override int GetDeathSound() + { + return 0x4E5; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.MedScrolls, 1 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Greater; } } + public override Poison HitPoison{ get{ return Poison.Greater; } } + public override FoodType FavoriteFood{ get{ return FoodType.FruitsAndVegies | FoodType.GrainsAndHay; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.05 > Utility.RandomDouble() ) + { + /* Rune Corruption + * Start cliloc: 1070846 "The creature magically corrupts your armor!" + * Effect: All resistances -70 (lowest 0) for 5 seconds + * End ASCII: "The corruption of your armor has worn off" + */ + + ExpireTimer timer = (ExpireTimer)m_Table[defender]; + + if ( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070845 ); // The creature continues to corrupt your armor! + } + else + defender.SendLocalizedMessage( 1070846 ); // The creature magically corrupts your armor! + + List mods = new List(); + + if ( defender.PhysicalResistance > 0 ) + mods.Add( new ResistanceMod( ResistanceType.Physical, (defender.PhysicalResistance > 70) ? -70 : -defender.PhysicalResistance ) ); + + if ( defender.FireResistance > 0 ) + mods.Add( new ResistanceMod( ResistanceType.Fire, (defender.FireResistance > 70) ? -70 : -defender.FireResistance ) ); + + if ( defender.ColdResistance > 0 ) + mods.Add( new ResistanceMod( ResistanceType.Cold, (defender.ColdResistance > 70) ? -70 : -defender.ColdResistance ) ); + + if ( defender.PoisonResistance > 0 ) + mods.Add( new ResistanceMod( ResistanceType.Poison, (defender.PoisonResistance > 70) ? -70 : -defender.PoisonResistance ) ); + + if ( defender.EnergyResistance > 0 ) + mods.Add( new ResistanceMod( ResistanceType.Energy, (defender.EnergyResistance > 70) ? -70 : -defender.EnergyResistance ) ); + + for ( int i = 0; i < mods.Count; ++i ) + defender.AddResistanceMod( mods[i] ); + + defender.FixedEffect( 0x37B9, 10, 5 ); + + timer = new ExpireTimer( defender, mods, TimeSpan.FromSeconds( 5.0 ) ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private List m_Mods; + + public ExpireTimer( Mobile m, List mods, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + m_Mods = mods; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + for ( int i = 0; i < m_Mods.Count; ++i ) + m_Mobile.RemoveResistanceMod( m_Mods[i] ); + + Stop(); + m_Table.Remove( m_Mobile ); + } + + protected override void OnTick() + { + m_Mobile.SendMessage( "The corruption of your armor has worn off" ); + DoExpire(); + } + } + + public RuneBeetle( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/TsukiWolf.cs b/Scripts/Mobiles/Monsters/SE/TsukiWolf.cs new file mode 100644 index 000000000..ca39d13af --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/TsukiWolf.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "a tsuki wolf corpse" )] + public class TsukiWolf : BaseCreature + { + [Constructable] + public TsukiWolf() + : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a tsuki wolf"; + Body = 250; + + switch( Utility.Random( 3 ) ) + { + case 0: Hue = Utility.RandomNeutralHue(); break; //No, this really isn't accurate ;-> + } + + SetStr( 401, 450 ); + SetDex( 151, 200 ); + SetInt( 66, 76 ); + + SetHits( 376, 450 ); + SetMana( 40 ); + + SetDamage( 14, 18 ); + + SetDamageType( ResistanceType.Physical, 90 ); + SetDamageType( ResistanceType.Cold, 5 ); + SetDamageType( ResistanceType.Energy, 5 ); + + SetResistance( ResistanceType.Physical, 40, 60 ); + SetResistance( ResistanceType.Fire, 50, 70 ); + SetResistance( ResistanceType.Cold, 50, 70 ); + SetResistance( ResistanceType.Poison, 50, 70 ); + SetResistance( ResistanceType.Energy, 50, 70 ); + + SetSkill( SkillName.Anatomy, 65.1, 72.0 ); + SetSkill( SkillName.MagicResist, 65.1, 70.0 ); + SetSkill( SkillName.Tactics, 95.1, 110.0 ); + SetSkill( SkillName.Wrestling, 97.6, 107.5 ); + + Fame = 8500; + Karma = -8500; + + + + switch( Utility.Random( 10 ) ) + { + case 0: PackItem( new LeftArm() ); break; + case 1: PackItem( new RightArm() ); break; + case 2: PackItem( new Torso() ); break; + case 3: PackItem( new Bone() ); break; + case 4: PackItem( new RibCage() ); break; + case 5: PackItem( new RibCage() ); break; + case 6: PackItem( new BonePile() ); break; + case 7: PackItem( new BonePile() ); break; + case 8: PackItem( new BonePile() ); break; + case 9: PackItem( new BonePile() ); break; + } + } + + public override void GenerateLoot() + { + AddLoot( LootPack.Average ); + AddLoot( LootPack.Rich ); + } + public override int Meat { get { return 4; } } + public override int Hides { get { return 25; } } + public override FoodType FavoriteFood { get { return FoodType.Meat; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if( 0.1 > Utility.RandomDouble() ) + { + /* Blood Bath + * Start cliloc 1070826 + * Sound: 0x52B + * 2-3 blood spots + * Damage: 2 hps per second for 5 seconds + * End cliloc: 1070824 + */ + + ExpireTimer timer = (ExpireTimer)m_Table[defender]; + + if( timer != null ) + { + timer.DoExpire(); + defender.SendLocalizedMessage( 1070825 ); // The creature continues to rage! + } + else + defender.SendLocalizedMessage( 1070826 ); // The creature goes into a rage, inflicting heavy damage! + + timer = new ExpireTimer( defender, this ); + timer.Start(); + m_Table[defender] = timer; + } + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private Mobile m_From; + private int m_Count; + + public ExpireTimer( Mobile m, Mobile from ) + : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_From = from; + Priority = TimerPriority.TwoFiftyMS; + } + + public void DoExpire() + { + Stop(); + m_Table.Remove( m_Mobile ); + } + + public void DrainLife() + { + if( m_Mobile.Alive ) + m_Mobile.Damage( 2, m_From ); + else + DoExpire(); + } + + protected override void OnTick() + { + DrainLife(); + + if( ++m_Count >= 5 ) + { + DoExpire(); + m_Mobile.SendLocalizedMessage( 1070824 ); // The creature's rage subsides. + } + } + } + + public TsukiWolf( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override int GetAngerSound() + { + return 0x52D; + } + + public override int GetIdleSound() + { + return 0x52C; + } + + public override int GetAttackSound() + { + return 0x52B; + } + + public override int GetHurtSound() + { + return 0x52E; + } + + public override int GetDeathSound() + { + return 0x52A; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/SE/Yamandon.cs b/Scripts/Mobiles/Monsters/SE/Yamandon.cs new file mode 100644 index 000000000..fa98d964c --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/Yamandon.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + [TypeAlias( "Server.Mobiles.Yamadon" )] + [CorpseName( "a yamandon corpse" )] + public class Yamandon : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.DoubleStrike; + } + + [Constructable] + public Yamandon() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a yamandon"; + Body = 249; + + SetStr( 786, 930 ); + SetDex( 251, 365 ); + SetInt( 101, 115 ); + + SetHits( 1601, 1800 ); + + SetDamage( 19, 35 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 20 ); + SetDamageType( ResistanceType.Energy, 10 ); + + SetResistance( ResistanceType.Physical, 65, 85 ); + SetResistance( ResistanceType.Fire, 70, 90 ); + SetResistance( ResistanceType.Cold, 50, 70 ); + SetResistance( ResistanceType.Poison, 50, 70 ); + SetResistance( ResistanceType.Energy, 50, 70 ); + + SetSkill( SkillName.Anatomy, 115.1, 130.0 ); + SetSkill( SkillName.MagicResist, 117.6, 132.5 ); + SetSkill( SkillName.Poisoning, 120.1, 140.0 ); + SetSkill( SkillName.Tactics, 117.1, 132.0 ); + SetSkill( SkillName.Wrestling, 112.6, 132.5 ); + + Fame = 22000; + Karma = -22000; + + if ( Utility.RandomDouble() < .50 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + + PackItem( new Eggs( 2 ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich ); + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.Gems, 6 ); + } + + public override bool ReacquireOnMovement{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Utility.RandomBool() ? Poison.Deadly : Poison.Lethal; } } + public override int TreasureMapLevel{ get{ return 5; } } + public override int Hides{ get{ return 20; } } + + public override void OnDamagedBySpell( Mobile attacker ) + { + base.OnDamagedBySpell( attacker ); + + DoCounter( attacker ); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + DoCounter( attacker ); + } + + private void DoCounter( Mobile attacker ) + { + if ( attacker is BaseCreature && ((BaseCreature)attacker).BardProvoked ) + return; + + if ( 0.2 > Utility.RandomDouble() ) + { + /* Counterattack with Hit Poison Area + * 20-25 damage, unresistable + * Lethal poison, 100% of the time + * Particle effect: Type: "2" From: "0x4061A107" To: "0x0" ItemId: "0x36BD" ItemIdName: "explosion" FromLocation: "(296 615, 17)" ToLocation: "(296 615, 17)" Speed: "1" Duration: "10" FixedDirection: "True" Explode: "False" Hue: "0xA6" RenderMode: "0x0" Effect: "0x1F78" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x4061A107" Layer: "255" Unknown: "0x0" + * Doesn't work on provoked monsters + */ + + Mobile target = null; + + if ( attacker is BaseCreature ) + { + Mobile m = ((BaseCreature)attacker).GetMaster(); + + if( m != null ) + target = m; + } + + if ( target == null || !target.InRange( this, 18 ) ) + target = attacker; + + this.Animate( 10, 4, 1, true, false, 0 ); + + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in target.GetMobilesInRange( 8 ) ) + { + if ( m == this || !CanBeHarmful( m ) ) + continue; + + if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != this.Team) ) + targets.Add( m ); + else if ( m.Player && m.Alive ) + targets.Add( m ); + } + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + DoHarmful( m ); + + AOS.Damage( m, this, Utility.RandomMinMax( 20, 25 ), true, 0, 0, 0, 100, 0 ); + + m.FixedParticles( 0x36BD, 1, 10, 0x1F78, 0xA6, 0, (EffectLayer)255 ); + m.ApplyPoison( this, Poison.Lethal ); + } + } + } + + public override int GetAttackSound() + { + return 1260; + } + + public override int GetAngerSound() + { + return 1262; + } + + public override int GetDeathSound() + { + return 1259; //Other Death sound is 1258... One for Yamadon, one for Serado? + } + + public override int GetHurtSound() + { + return 1263; + } + + public override int GetIdleSound() + { + return 1261; + } + + + public Yamandon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/YomotsuElder.cs b/Scripts/Mobiles/Monsters/SE/YomotsuElder.cs new file mode 100644 index 000000000..feb00be98 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/YomotsuElder.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Misc; + +namespace Server.Mobiles +{ + [CorpseName( "a wrinkly yomotsu corpse" )] + public class YomotsuElder : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.DoubleStrike; + } + + [Constructable] + public YomotsuElder() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a yomotsu elder"; + Body = 255; + BaseSoundID = 0x452; + + SetStr( 686, 830 ); + SetDex( 251, 365 ); + SetInt( 17, 31 ); + + SetHits( 801, 900 ); + + SetDamage( 19, 27 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 65, 85 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 45, 65 ); + SetResistance( ResistanceType.Poison, 35, 55 ); + SetResistance( ResistanceType.Energy, 25, 50 ); + + SetSkill( SkillName.Anatomy, 115.1, 130.0 ); + SetSkill( SkillName.MagicResist, 100.1, 120.0 ); + SetSkill( SkillName.Tactics, 115.1, 130.0 ); + SetSkill( SkillName.Wrestling, 110.1, 130.0 ); + + Fame = 12000; + Karma = -12000; + + + PackItem( new GreenGourd() ); + PackItem( new ExecutionersAxe() ); + + switch ( Utility.Random( 3 ) ) + { + case 0: PackItem( new LongPants() ); break; + case 1: PackItem( new ShortPants() ); break; + } + + switch ( Utility.Random( 6 ) ) + { + case 0: PackItem( new Shoes() ); break; + case 1: PackItem( new Sandals() ); break; + case 2: PackItem( new Boots() ); break; + case 3: PackItem( new ThighBoots() ); break; + } + + if ( Utility.RandomDouble() < .25 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + public override FoodType FavoriteFood{ get{ return FoodType.Fish; } } + + public override int Meat{ get{ return 1; } } + + + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 3 ); + AddLoot( LootPack.Gems, 2 ); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 5; } } + + // TODO: Axe Throw + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() ) + { + /* Maniacal laugh + * Cliloc: 1070840 + * Effect: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(884 715, 10)" ToLocation: "(884 715, 10)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + * Paralyzes for 4 seconds, or until hit + */ + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.SendLocalizedMessage( 1070840 ); // You are frozen as the creature laughs maniacally. + + defender.Paralyze( TimeSpan.FromSeconds( 4.0 ) ); + } + } + + public YomotsuElder( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + public override int GetIdleSound() + { + return 0x42A; + } + + public override int GetAttackSound() + { + return 0x435; + } + + public override int GetHurtSound() + { + return 0x436; + } + + public override int GetDeathSound() + { + return 0x43A; + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/YomotsuPriest.cs b/Scripts/Mobiles/Monsters/SE/YomotsuPriest.cs new file mode 100644 index 000000000..f25bd774f --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/YomotsuPriest.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a glowing yomotsu corpse" )] + public class YomotsuPriest : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.DoubleStrike; + } + + [Constructable] + public YomotsuPriest() : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a yomotsu priest"; + Body = 253; + BaseSoundID = 0x452; + + SetStr( 486, 530 ); + SetDex( 101, 115 ); + SetInt( 601, 670 ); + + SetHits( 486, 530 ); + + SetDamage( 8, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 65, 85 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 45, 65 ); + SetResistance( ResistanceType.Poison, 35, 55 ); + SetResistance( ResistanceType.Energy, 25, 50 ); + + SetSkill( SkillName.EvalInt, 92.6, 107.5 ); + SetSkill( SkillName.Magery, 105.1, 115.0 ); + SetSkill( SkillName.Meditation, 100.1, 110.0 ); + SetSkill( SkillName.MagicResist, 112.6, 122.5 ); + SetSkill( SkillName.Tactics, 55.1, 105.0 ); + SetSkill( SkillName.Wrestling, 47.6, 57.5 ); + + Fame = 9000; + Karma = -9000; + + + PackItem( new GreenGourd() ); + PackItem( new ExecutionersAxe() ); + + switch ( Utility.Random( 3 ) ) + { + case 0: PackItem( new LongPants() ); break; + case 1: PackItem( new ShortPants() ); break; + } + + switch ( Utility.Random( 6 ) ) + { + case 0: PackItem( new Shoes() ); break; + case 1: PackItem( new Sandals() ); break; + case 2: PackItem( new Boots() ); break; + case 3: PackItem( new ThighBoots() ); break; + } + + if ( Utility.RandomDouble() < .25 ) PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + public override FoodType FavoriteFood{ get{ return FoodType.Fish; } } + + public override int Meat{ get{ return 1; } } + + + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Rich ); + AddLoot( LootPack.Gems, 4); + } + + public override bool CanRummageCorpses{ get{ return true; } } + + // TODO: Body Transformation + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() ) + { + /* Maniacal laugh + * Cliloc: 1070840 + * Effect: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(884 715, 10)" ToLocation: "(884 715, 10)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + * Paralyzes for 4 seconds, or until hit + */ + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.SendLocalizedMessage( 1070840 ); // You are frozen as the creature laughs maniacally. + + defender.Paralyze( TimeSpan.FromSeconds( 4.0 ) ); + } + } + + public YomotsuPriest( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + public override int GetIdleSound() + { + return 0x42A; + } + + public override int GetAttackSound() + { + return 0x435; + } + + public override int GetHurtSound() + { + return 0x436; + } + + public override int GetDeathSound() + { + return 0x43A; + } + } +} diff --git a/Scripts/Mobiles/Monsters/SE/YomotsuWarrior.cs b/Scripts/Mobiles/Monsters/SE/YomotsuWarrior.cs new file mode 100644 index 000000000..eef53bc17 --- /dev/null +++ b/Scripts/Mobiles/Monsters/SE/YomotsuWarrior.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a yomotsu corpse" )] + public class YomotsuWarrior : BaseCreature + { + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.DoubleStrike; + } + + [Constructable] + public YomotsuWarrior() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a yomotsu warrior"; + Body = 245; + BaseSoundID = 0x452; + + SetStr( 486, 530 ); + SetDex( 151, 165 ); + SetInt( 17, 31 ); + + SetHits( 486, 530 ); + SetMana( 17, 31 ); + + SetDamage( 8, 10 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 65, 85 ); + SetResistance( ResistanceType.Fire, 30, 50 ); + SetResistance( ResistanceType.Cold, 45, 65 ); + SetResistance( ResistanceType.Poison, 35, 55 ); + SetResistance( ResistanceType.Energy, 25, 50 ); + + SetSkill( SkillName.Anatomy, 85.1, 95.0 ); + SetSkill( SkillName.MagicResist, 82.6, 90.5 ); + SetSkill( SkillName.Tactics, 95.1, 105.0 ); + SetSkill( SkillName.Wrestling, 97.6, 107.5 ); + + Fame = 4200; + Karma = -4200; + + PackItem( new GreenGourd() ); + PackItem( new ExecutionersAxe() ); + + if( Utility.RandomBool() ) + PackItem( new LongPants() ); + else + PackItem( new ShortPants() ); + + switch ( Utility.Random( 4 ) ) + { + case 0: PackItem( new Shoes() ); break; + case 1: PackItem( new Sandals() ); break; + case 2: PackItem( new Boots() ); break; + case 3: PackItem( new ThighBoots() ); break; + } + + if ( Utility.RandomDouble() < .25 ) + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + + } + + public override FoodType FavoriteFood{ get{ return FoodType.Fish; } } + + public override int Meat{ get{ return 1; } } + + public override void GenerateLoot() + { + AddLoot( LootPack.Rich, 2 ); + AddLoot( LootPack.Gems,2); + } + + public override bool CanRummageCorpses{ get{ return true; } } + public override int TreasureMapLevel{ get{ return 3; } } + + // TODO: Throwing Dagger + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 > Utility.RandomDouble() ) + { + /* Maniacal laugh + * Cliloc: 1070840 + * Effect: Type: "3" From: "0x57D4F5B" To: "0x0" ItemId: "0x37B9" ItemIdName: "glow" FromLocation: "(884 715, 10)" ToLocation: "(884 715, 10)" Speed: "10" Duration: "5" FixedDirection: "True" Explode: "False" + * Paralyzes for 4 seconds, or until hit + */ + + defender.FixedEffect( 0x37B9, 10, 5 ); + defender.SendLocalizedMessage( 1070840 ); // You are frozen as the creature laughs maniacally. + + defender.Paralyze( TimeSpan.FromSeconds( 4.0 ) ); + } + } + + public YomotsuWarrior( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + + public override int GetIdleSound() + { + return 0x42A; + } + + public override int GetAttackSound() + { + return 0x435; + } + + public override int GetHurtSound() + { + return 0x436; + } + + public override int GetDeathSound() + { + return 0x43A; + } + } +} diff --git a/Scripts/Mobiles/Monsters/Summons/SummonedAirElemental.cs b/Scripts/Mobiles/Monsters/Summons/SummonedAirElemental.cs new file mode 100644 index 000000000..4f96408b9 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Summons/SummonedAirElemental.cs @@ -0,0 +1,69 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "an air elemental corpse" )] + public class SummonedAirElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public SummonedAirElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an air elemental"; + Body = 13; + Hue = 0x4001; + BaseSoundID = 655; + + SetStr( 200 ); + SetDex( 200 ); + SetInt( 100 ); + + SetHits( 150 ); + SetStam( 50 ); + + SetDamage( 6, 9 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 40, 50 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 70, 80 ); + + SetSkill( SkillName.Meditation, 90.0 ); + SetSkill( SkillName.EvalInt, 70.0 ); + SetSkill( SkillName.Magery, 70.0 ); + SetSkill( SkillName.MagicResist, 60.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 80.0 ); + + VirtualArmor = 40; + ControlSlots = 2; + } + + public SummonedAirElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + if ( BaseSoundID == 263 ) + BaseSoundID = 655; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs b/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs new file mode 100644 index 000000000..d2c145bf3 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Summons/SummonedDaemon.cs @@ -0,0 +1,64 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a daemon corpse" )] + public class SummonedDaemon : BaseCreature + { + public override double DispelDifficulty{ get{ return 125.0; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public SummonedDaemon () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = NameList.RandomName( "daemon" ); + Body = 9; + BaseSoundID = 357; + + SetStr( 200 ); + SetDex( 110 ); + SetInt( 150 ); + + SetDamage( 14, 21 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Poison, 100 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 70, 80 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 90.1, 100.0 ); + SetSkill( SkillName.Meditation, 90.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 100.0 ); + SetSkill( SkillName.MagicResist, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 98.1, 99.0 ); + + VirtualArmor = 58; + ControlSlots = Core.SE ? 4 : 5; + } + + public override Poison PoisonImmune{ get{ return Poison.Regular; } } // TODO: Immune to poison? + + public SummonedDaemon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Summons/SummonedEarthElemental.cs b/Scripts/Mobiles/Monsters/Summons/SummonedEarthElemental.cs new file mode 100644 index 000000000..95ca9871b --- /dev/null +++ b/Scripts/Mobiles/Monsters/Summons/SummonedEarthElemental.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; + +namespace Server.Mobiles +{ + [CorpseName( "an earth elemental corpse" )] + public class SummonedEarthElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public SummonedEarthElemental() : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "an earth elemental"; + Body = 14; + BaseSoundID = 268; + + SetStr( 200 ); + SetDex( 70 ); + SetInt( 70 ); + + SetHits( 180 ); + + SetDamage( 14, 21 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 65, 75 ); + SetResistance( ResistanceType.Fire, 40, 50 ); + SetResistance( ResistanceType.Cold, 40, 50 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 65.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 90.0 ); + + VirtualArmor = 34; + ControlSlots = 2; + } + + public SummonedEarthElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Monsters/Summons/SummonedFireElemental.cs b/Scripts/Mobiles/Monsters/Summons/SummonedFireElemental.cs new file mode 100644 index 000000000..8bd6623bf --- /dev/null +++ b/Scripts/Mobiles/Monsters/Summons/SummonedFireElemental.cs @@ -0,0 +1,63 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a fire elemental corpse" )] + public class SummonedFireElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public SummonedFireElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a fire elemental"; + Body = 15; + BaseSoundID = 838; + + SetStr( 200 ); + SetDex( 200 ); + SetInt( 100 ); + + SetDamage( 9, 14 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Fire, 100 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 70, 80 ); + SetResistance( ResistanceType.Cold, 0, 10 ); + SetResistance( ResistanceType.Poison, 50, 60 ); + SetResistance( ResistanceType.Energy, 50, 60 ); + + SetSkill( SkillName.EvalInt, 90.0 ); + SetSkill( SkillName.Magery, 90.0 ); + SetSkill( SkillName.MagicResist, 85.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 92.0 ); + + VirtualArmor = 40; + ControlSlots = 4; + + AddItem( new LightSource() ); + } + + public SummonedFireElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Monsters/Summons/SummonedWaterElemental.cs b/Scripts/Mobiles/Monsters/Summons/SummonedWaterElemental.cs new file mode 100644 index 000000000..079fd04f9 --- /dev/null +++ b/Scripts/Mobiles/Monsters/Summons/SummonedWaterElemental.cs @@ -0,0 +1,65 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a water elemental corpse" )] + public class SummonedWaterElemental : BaseCreature + { + public override double DispelDifficulty{ get{ return 117.5; } } + public override double DispelFocus{ get{ return 45.0; } } + + [Constructable] + public SummonedWaterElemental () : base( AIType.AI_Mage, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + Name = "a water elemental"; + Body = 16; + BaseSoundID = 278; + + SetStr( 200 ); + SetDex( 70 ); + SetInt( 100 ); + + SetHits( 165 ); + + SetDamage( 12, 16 ); + + SetDamageType( ResistanceType.Physical, 0 ); + SetDamageType( ResistanceType.Cold, 100 ); + + SetResistance( ResistanceType.Physical, 50, 60 ); + SetResistance( ResistanceType.Fire, 20, 30 ); + SetResistance( ResistanceType.Cold, 70, 80 ); + SetResistance( ResistanceType.Poison, 45, 55 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.Meditation, 90.0 ); + SetSkill( SkillName.EvalInt, 80.0 ); + SetSkill( SkillName.Magery, 80.0 ); + SetSkill( SkillName.MagicResist, 75.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 85.0 ); + + VirtualArmor = 40; + ControlSlots = 3; + CanSwim = true; + } + + public SummonedWaterElemental( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/PlayerMobile.cs b/Scripts/Mobiles/PlayerMobile.cs new file mode 100644 index 000000000..112c28a98 --- /dev/null +++ b/Scripts/Mobiles/PlayerMobile.cs @@ -0,0 +1,3847 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Misc; +using Server.Items; +using Server.Gumps; +using Server.Multis; +using Server.Engines.Help; +using Server.ContextMenus; +using Server.Network; +using Server.Spells; +using Server.Spells.Fifth; +using Server.Spells.Sixth; +using Server.Spells.Seventh; +using Server.Spells.Necromancy; +using Server.Spells.Ninjitsu; +using Server.Spells.Bushido; +using Server.Targeting; +using Server.Engines.Quests; +using Server.Factions; +using Server.Regions; +using Server.Accounting; +using Server.Engines.CannedEvil; +using Server.Engines.Craft; + +namespace Server.Mobiles +{ + #region Enums + [Flags] + public enum PlayerFlag // First 16 bits are reserved for default-distro use, start custom flags at 0x00010000 + { + None = 0x00000000, + Glassblowing = 0x00000001, + Masonry = 0x00000002, + SandMining = 0x00000004, + StoneMining = 0x00000008, + ToggleMiningStone = 0x00000010, + KarmaLocked = 0x00000020, + AutoRenewInsurance = 0x00000040, + UseOwnFilter = 0x00000080, + PublicMyRunUO = 0x00000100, + PagingSquelched = 0x00000200, + Young = 0x00000400, + AcceptGuildInvites = 0x00000800, + DisplayChampionTitle= 0x00001000 + } + + public enum NpcGuild + { + None, + MagesGuild, + WarriorsGuild, + ThievesGuild, + RangersGuild, + HealersGuild, + MinersGuild, + MerchantsGuild, + TinkersGuild, + TailorsGuild, + FishermensGuild, + BardsGuild, + BlacksmithsGuild + } + + public enum SolenFriendship + { + None, + Red, + Black + } + #endregion + + public class PlayerMobile : Mobile, IHonorTarget + { + private class CountAndTimeStamp + { + private int m_Count; + private DateTime m_Stamp; + + public CountAndTimeStamp() + { + } + + public DateTime TimeStamp { get{ return m_Stamp; } } + public int Count + { + get { return m_Count; } + set { m_Count = value; m_Stamp = DateTime.Now; } + } + } + + private DesignContext m_DesignContext; + + private NpcGuild m_NpcGuild; + private DateTime m_NpcGuildJoinTime; + private TimeSpan m_NpcGuildGameTime; + private PlayerFlag m_Flags; + private int m_StepsTaken; + private int m_Profession; + + private DateTime m_LastOnline; + private Server.Guilds.RankDefinition m_GuildRank; + + private int m_GuildMessageHue, m_AllianceMessageHue; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Owner )] + public new Account Account + { + get { return base.Account as Account; } + set { base.Account = value; } + } + + #region Getters & Setters + public Server.Guilds.RankDefinition GuildRank + { + get + { + if( this.AccessLevel >= AccessLevel.GameMaster ) + return Server.Guilds.RankDefinition.Leader; + else + return m_GuildRank; + } + set{ m_GuildRank = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int GuildMessageHue + { + get{ return m_GuildMessageHue; } + set{ m_GuildMessageHue = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int AllianceMessageHue + { + get { return m_AllianceMessageHue; } + set { m_AllianceMessageHue = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Profession + { + get{ return m_Profession; } + set{ m_Profession = value; } + } + + public int StepsTaken + { + get{ return m_StepsTaken; } + set{ m_StepsTaken = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public NpcGuild NpcGuild + { + get{ return m_NpcGuild; } + set{ m_NpcGuild = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NpcGuildJoinTime + { + get{ return m_NpcGuildJoinTime; } + set{ m_NpcGuildJoinTime = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastOnline + { + get{ return m_LastOnline; } + set{ m_LastOnline = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan NpcGuildGameTime + { + get{ return m_NpcGuildGameTime; } + set{ m_NpcGuildGameTime = value; } + } + + private int m_ToTItemsTurnedIn; + + [CommandProperty( AccessLevel.GameMaster )] + public int ToTItemsTurnedIn + { + get { return m_ToTItemsTurnedIn; } + set { m_ToTItemsTurnedIn = value; } + } + + private int m_ToTTotalMonsterFame; + + [CommandProperty( AccessLevel.GameMaster )] + public int ToTTotalMonsterFame + { + get { return m_ToTTotalMonsterFame; } + set { m_ToTTotalMonsterFame = value; } + } + + #endregion + + #region PlayerFlags + public PlayerFlag Flags + { + get{ return m_Flags; } + set{ m_Flags = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool PagingSquelched + { + get{ return GetFlag( PlayerFlag.PagingSquelched ); } + set{ SetFlag( PlayerFlag.PagingSquelched, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Glassblowing + { + get{ return GetFlag( PlayerFlag.Glassblowing ); } + set{ SetFlag( PlayerFlag.Glassblowing, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Masonry + { + get{ return GetFlag( PlayerFlag.Masonry ); } + set{ SetFlag( PlayerFlag.Masonry, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool SandMining + { + get{ return GetFlag( PlayerFlag.SandMining ); } + set{ SetFlag( PlayerFlag.SandMining, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool StoneMining + { + get{ return GetFlag( PlayerFlag.StoneMining ); } + set{ SetFlag( PlayerFlag.StoneMining, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool ToggleMiningStone + { + get{ return GetFlag( PlayerFlag.ToggleMiningStone ); } + set{ SetFlag( PlayerFlag.ToggleMiningStone, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool KarmaLocked + { + get{ return GetFlag( PlayerFlag.KarmaLocked ); } + set{ SetFlag( PlayerFlag.KarmaLocked, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool AutoRenewInsurance + { + get{ return GetFlag( PlayerFlag.AutoRenewInsurance ); } + set{ SetFlag( PlayerFlag.AutoRenewInsurance, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool UseOwnFilter + { + get{ return GetFlag( PlayerFlag.UseOwnFilter ); } + set{ SetFlag( PlayerFlag.UseOwnFilter, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool PublicMyRunUO + { + get{ return GetFlag( PlayerFlag.PublicMyRunUO ); } + set{ SetFlag( PlayerFlag.PublicMyRunUO, value ); InvalidateMyRunUO(); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool AcceptGuildInvites + { + get{ return GetFlag( PlayerFlag.AcceptGuildInvites ); } + set{ SetFlag( PlayerFlag.AcceptGuildInvites, value ); } + } + #endregion + + + public static Direction GetDirection4( Point3D from, Point3D to ) + { + int dx = from.X - to.X; + int dy = from.Y - to.Y; + + int rx = dx - dy; + int ry = dx + dy; + + Direction ret; + + if ( rx >= 0 && ry >= 0 ) + ret = Direction.West; + else if ( rx >= 0 && ry < 0 ) + ret = Direction.South; + else if ( rx < 0 && ry < 0 ) + ret = Direction.East; + else + ret = Direction.North; + + return ret; + } + + public override bool OnDroppedItemToWorld( Item item, Point3D location ) + { + if ( !base.OnDroppedItemToWorld( item, location ) ) + return false; + + BounceInfo bi = item.GetBounce(); + + if ( bi != null ) + { + Type type = item.GetType(); + + if ( type.IsDefined( typeof( FurnitureAttribute ), true ) || type.IsDefined( typeof( DynamicFlipingAttribute ), true ) ) + { + object[] objs = type.GetCustomAttributes( typeof( FlipableAttribute ), true ); + + if ( objs != null && objs.Length > 0 ) + { + FlipableAttribute fp = objs[0] as FlipableAttribute; + + if ( fp != null ) + { + int[] itemIDs = fp.ItemIDs; + + Point3D oldWorldLoc = bi.m_WorldLoc; + Point3D newWorldLoc = location; + + if ( oldWorldLoc.X != newWorldLoc.X || oldWorldLoc.Y != newWorldLoc.Y ) + { + Direction dir = GetDirection4( oldWorldLoc, newWorldLoc ); + + if ( itemIDs.Length == 2 ) + { + switch ( dir ) + { + case Direction.North: + case Direction.South: item.ItemID = itemIDs[0]; break; + case Direction.East: + case Direction.West: item.ItemID = itemIDs[1]; break; + } + } + else if ( itemIDs.Length == 4 ) + { + switch ( dir ) + { + case Direction.South: item.ItemID = itemIDs[0]; break; + case Direction.East: item.ItemID = itemIDs[1]; break; + case Direction.North: item.ItemID = itemIDs[2]; break; + case Direction.West: item.ItemID = itemIDs[3]; break; + } + } + } + } + } + } + } + + return true; + } + + public bool GetFlag( PlayerFlag flag ) + { + return ( (m_Flags & flag) != 0 ); + } + + public void SetFlag( PlayerFlag flag, bool value ) + { + if ( value ) + m_Flags |= flag; + else + m_Flags &= ~flag; + } + + public DesignContext DesignContext + { + get{ return m_DesignContext; } + set{ m_DesignContext = value; } + } + + public static void Initialize() + { + if ( FastwalkPrevention ) + { + PacketHandler ph = PacketHandlers.GetHandler( 0x02 ); + + ph.ThrottleCallback = new ThrottlePacketCallback( MovementThrottle_Callback ); + } + + EventSink.Login += new LoginEventHandler( OnLogin ); + EventSink.Logout += new LogoutEventHandler( OnLogout ); + EventSink.Connected += new ConnectedEventHandler( EventSink_Connected ); + EventSink.Disconnected += new DisconnectedEventHandler( EventSink_Disconnected ); + } + + public override void OnSkillInvalidated( Skill skill ) + { + if ( Core.AOS && skill.SkillName == SkillName.MagicResist ) + UpdateResistances(); + } + + public override int GetMaxResistance( ResistanceType type ) + { + int max = base.GetMaxResistance( type ); + + if ( type != ResistanceType.Physical && 60 < max && Spells.Fourth.CurseSpell.UnderEffect( this ) ) + max = 60; + + if( Core.ML && this.Race == Race.Elf && type == ResistanceType.Energy ) + max += 5; //Intended to go after the 60 max from curse + + return max; + } + + protected override void OnRaceChange( Race oldRace ) + { + ValidateEquipment(); + UpdateResistances(); + } + + public override int MaxWeight { get { return (((Core.ML && this.Race == Race.Human) ? 100 : 40) + (int)(3.5 * this.Str)); } } + + private int m_LastGlobalLight = -1, m_LastPersonalLight = -1; + + public override void OnNetStateChanged() + { + m_LastGlobalLight = -1; + m_LastPersonalLight = -1; + } + + public override void ComputeBaseLightLevels( out int global, out int personal ) + { + global = LightCycle.ComputeLevelFor( this ); + + if ( this.LightLevel < 21 && AosAttributes.GetValue( this, AosAttribute.NightSight ) > 0 ) + personal = 21; + else + personal = this.LightLevel; + } + + public override void CheckLightLevels( bool forceResend ) + { + NetState ns = this.NetState; + + if ( ns == null ) + return; + + int global, personal; + + ComputeLightLevels( out global, out personal ); + + if ( !forceResend ) + forceResend = ( global != m_LastGlobalLight || personal != m_LastPersonalLight ); + + if ( !forceResend ) + return; + + m_LastGlobalLight = global; + m_LastPersonalLight = personal; + + ns.Send( GlobalLightLevel.Instantiate( global ) ); + ns.Send( new PersonalLightLevel( this, personal ) ); + } + + public override int GetMinResistance( ResistanceType type ) + { + int magicResist = (int)(Skills[SkillName.MagicResist].Value * 10); + int min = int.MinValue; + + if ( magicResist >= 1000 ) + min = 40 + ((magicResist - 1000) / 50); + else if ( magicResist >= 400 ) + min = (magicResist - 400) / 15; + + if ( min > MaxPlayerResistance ) + min = MaxPlayerResistance; + + int baseMin = base.GetMinResistance( type ); + + if ( min < baseMin ) + min = baseMin; + + return min; + } + + private static void OnLogin( LoginEventArgs e ) + { + Mobile from = e.Mobile; + + CheckAtrophies( from ); + + if ( AccountHandler.LockdownLevel > AccessLevel.Player ) + { + string notice; + + Accounting.Account acct = from.Account as Accounting.Account; + + if ( acct == null || !acct.HasAccess( from.NetState ) ) + { + if ( from.AccessLevel == AccessLevel.Player ) + notice = "The server is currently under lockdown. No players are allowed to log in at this time."; + else + notice = "The server is currently under lockdown. You do not have sufficient access level to connect."; + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), new TimerStateCallback( Disconnect ), from ); + } + else if ( from.AccessLevel >= AccessLevel.Administrator ) + { + notice = "The server is currently under lockdown. As you are an administrator, you may change this from the [Admin gump."; + } + else + { + notice = "The server is currently under lockdown. You have sufficient access level to connect."; + } + + from.SendGump( new NoticeGump( 1060637, 30720, notice, 0xFFC000, 300, 140, null, null ) ); + } + } + + private bool m_NoDeltaRecursion; + + public void ValidateEquipment() + { + if ( m_NoDeltaRecursion || Map == null || Map == Map.Internal ) + return; + + if ( this.Items == null ) + return; + + m_NoDeltaRecursion = true; + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ValidateEquipment_Sandbox ) ); + } + + private void ValidateEquipment_Sandbox() + { + try + { + if ( Map == null || Map == Map.Internal ) + return; + + List items = this.Items; + + if ( items == null ) + return; + + bool moved = false; + + int str = this.Str; + int dex = this.Dex; + int intel = this.Int; + + #region Factions + int factionItemCount = 0; + #endregion + + Mobile from = this; + + #region Ethics + Ethics.Ethic ethic = Ethics.Ethic.Find( from ); + #endregion + + for ( int i = items.Count - 1; i >= 0; --i ) + { + if ( i >= items.Count ) + continue; + + Item item = items[i]; + + #region Ethics + if ( ( item.SavedFlags & 0x100 ) != 0 ) + { + if ( item.Hue != Ethics.Ethic.Hero.Definition.PrimaryHue ) + { + item.SavedFlags &= ~0x100; + } + else if ( ethic != Ethics.Ethic.Hero ) + { + from.AddToBackpack( item ); + moved = true; + continue; + } + } + else if ( ( item.SavedFlags & 0x200 ) != 0 ) + { + if ( item.Hue != Ethics.Ethic.Evil.Definition.PrimaryHue ) + { + item.SavedFlags &= ~0x200; + } + else if ( ethic != Ethics.Ethic.Evil ) + { + from.AddToBackpack( item ); + moved = true; + continue; + } + } + #endregion + + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + bool drop = false; + + if( dex < weapon.DexRequirement ) + drop = true; + else if( str < AOS.Scale( weapon.StrRequirement, 100 - weapon.GetLowerStatReq() ) ) + drop = true; + else if( intel < weapon.IntRequirement ) + drop = true; + else if( weapon.RequiredRace != null && weapon.RequiredRace != this.Race ) + drop = true; + + if ( drop ) + { + string name = weapon.Name; + + if ( name == null ) + name = String.Format( "#{0}", weapon.LabelNumber ); + + from.SendLocalizedMessage( 1062001, name ); // You can no longer wield your ~1_WEAPON~ + from.AddToBackpack( weapon ); + moved = true; + } + } + else if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + bool drop = false; + + if ( !armor.AllowMaleWearer && !from.Female && from.AccessLevel < AccessLevel.GameMaster ) + { + drop = true; + } + else if ( !armor.AllowFemaleWearer && from.Female && from.AccessLevel < AccessLevel.GameMaster ) + { + drop = true; + } + else if( armor.RequiredRace != null && armor.RequiredRace != this.Race ) + { + drop = true; + } + else + { + int strBonus = armor.ComputeStatBonus( StatType.Str ), strReq = armor.ComputeStatReq( StatType.Str ); + int dexBonus = armor.ComputeStatBonus( StatType.Dex ), dexReq = armor.ComputeStatReq( StatType.Dex ); + int intBonus = armor.ComputeStatBonus( StatType.Int ), intReq = armor.ComputeStatReq( StatType.Int ); + + if( dex < dexReq || (dex + dexBonus) < 1 ) + drop = true; + else if( str < strReq || (str + strBonus) < 1 ) + drop = true; + else if( intel < intReq || (intel + intBonus) < 1 ) + drop = true; + } + + if ( drop ) + { + string name = armor.Name; + + if ( name == null ) + name = String.Format( "#{0}", armor.LabelNumber ); + + if ( armor is BaseShield ) + from.SendLocalizedMessage( 1062003, name ); // You can no longer equip your ~1_SHIELD~ + else + from.SendLocalizedMessage( 1062002, name ); // You can no longer wear your ~1_ARMOR~ + + from.AddToBackpack( armor ); + moved = true; + } + } + else if ( item is BaseClothing ) + { + BaseClothing clothing = (BaseClothing)item; + + bool drop = false; + + if ( !clothing.AllowMaleWearer && !from.Female && from.AccessLevel < AccessLevel.GameMaster ) + { + drop = true; + } + else if ( !clothing.AllowFemaleWearer && from.Female && from.AccessLevel < AccessLevel.GameMaster ) + { + drop = true; + } + else if( clothing.RequiredRace != null && clothing.RequiredRace != this.Race ) + { + drop = true; + } + else + { + int strBonus = clothing.ComputeStatBonus( StatType.Str ); + int strReq = clothing.ComputeStatReq( StatType.Str ); + + if( str < strReq || (str + strBonus) < 1 ) + drop = true; + } + + if ( drop ) + { + string name = clothing.Name; + + if ( name == null ) + name = String.Format( "#{0}", clothing.LabelNumber ); + + from.SendLocalizedMessage( 1062002, name ); // You can no longer wear your ~1_ARMOR~ + + from.AddToBackpack( clothing ); + moved = true; + } + } + + FactionItem factionItem = FactionItem.Find( item ); + + if ( factionItem != null ) + { + bool drop = false; + + Faction ourFaction = Faction.Find( this ); + + if ( ourFaction == null || ourFaction != factionItem.Faction ) + drop = true; + else if ( ++factionItemCount > FactionItem.GetMaxWearables( this ) ) + drop = true; + + if ( drop ) + { + from.AddToBackpack( item ); + moved = true; + } + } + } + + if ( moved ) + from.SendLocalizedMessage( 500647 ); // Some equipment has been moved to your backpack. + } + catch ( Exception e ) + { + Console.WriteLine( e ); + } + finally + { + m_NoDeltaRecursion = false; + } + } + + public override void Delta( MobileDelta flag ) + { + base.Delta( flag ); + + if ( (flag & MobileDelta.Stat) != 0 ) + ValidateEquipment(); + + if ( (flag & (MobileDelta.Name | MobileDelta.Hue)) != 0 ) + InvalidateMyRunUO(); + } + + private static void Disconnect( object state ) + { + NetState ns = ((Mobile)state).NetState; + + if ( ns != null ) + ns.Dispose(); + } + + private static void OnLogout( LogoutEventArgs e ) + { + } + + private static void EventSink_Connected( ConnectedEventArgs e ) + { + PlayerMobile pm = e.Mobile as PlayerMobile; + + if ( pm != null ) + { + pm.m_SessionStart = DateTime.Now; + + if ( pm.m_Quest != null ) + pm.m_Quest.StartTimer(); + + pm.BedrollLogout = false; + pm.LastOnline = DateTime.Now; + } + + Timer.DelayCall( TimeSpan.Zero, new TimerStateCallback( ClearSpecialMovesCallback ), e.Mobile ); + } + + private static void ClearSpecialMovesCallback( object state ) + { + Mobile from = (Mobile)state; + + SpecialMove.ClearAllMoves( from ); + } + + private static void EventSink_Disconnected( DisconnectedEventArgs e ) + { + Mobile from = e.Mobile; + DesignContext context = DesignContext.Find( from ); + + if ( context != null ) + { + /* Client disconnected + * - Remove design context + * - Eject all from house + * - Restore relocated entities + */ + + // Remove design context + DesignContext.Remove( from ); + + // Eject all from house + from.RevealingAction(); + + foreach ( Item item in context.Foundation.GetItems() ) + item.Location = context.Foundation.BanLocation; + + foreach ( Mobile mobile in context.Foundation.GetMobiles() ) + mobile.Location = context.Foundation.BanLocation; + + // Restore relocated entities + context.Foundation.RestoreRelocatedEntities(); + } + + PlayerMobile pm = e.Mobile as PlayerMobile; + + if ( pm != null ) + { + pm.m_GameTime += (DateTime.Now - pm.m_SessionStart); + + if ( pm.m_Quest != null ) + pm.m_Quest.StopTimer(); + + pm.m_SpeechLog = null; + pm.LastOnline = DateTime.Now; + } + } + + public override void RevealingAction() + { + if ( m_DesignContext != null ) + return; + + Spells.Sixth.InvisibilitySpell.RemoveTimer( this ); + + base.RevealingAction(); + } + + [CommandProperty( AccessLevel.GameMaster )] + public override bool Hidden + { + get + { + return base.Hidden; + } + set + { + base.Hidden = value; + + RemoveBuff( BuffIcon.Invisibility ); //Always remove, default to the hiding icon EXCEPT in the invis spell where it's explicitly set + + if( !Hidden ) + { + RemoveBuff( BuffIcon.HidingAndOrStealth ); + } + else// if( !InvisibilitySpell.HasTimer( this ) ) + { + BuffInfo.AddBuff( this, new BuffInfo( BuffIcon.HidingAndOrStealth, 1075655 ) ); //Hidden/Stealthing & You Are Hidden + } + } + } + + public override void OnSubItemAdded( Item item ) + { + if ( AccessLevel < AccessLevel.GameMaster && item.IsChildOf( this.Backpack ) ) + { + int maxWeight = WeightOverloading.GetMaxWeight( this ); + int curWeight = Mobile.BodyWeight + this.TotalWeight; + + if ( curWeight > maxWeight ) + this.SendLocalizedMessage( 1019035, true, String.Format( " : {0} / {1}", curWeight, maxWeight ) ); + } + } + + public override bool CanBeHarmful( Mobile target, bool message, bool ignoreOurBlessedness ) + { + if ( m_DesignContext != null || (target is PlayerMobile && ((PlayerMobile)target).m_DesignContext != null) ) + 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 CanBeBeneficial( Mobile target, bool message, bool allowDead ) + { + if ( m_DesignContext != null || (target is PlayerMobile && ((PlayerMobile)target).m_DesignContext != null) ) + return false; + + return base.CanBeBeneficial( target, message, allowDead ); + } + + public override bool CheckContextMenuDisplay( IEntity target ) + { + return ( m_DesignContext == null ); + } + + public override void OnItemAdded( Item item ) + { + base.OnItemAdded( item ); + + if ( item is BaseArmor || item is BaseWeapon ) + { + Hits=Hits; Stam=Stam; Mana=Mana; + } + + if ( this.NetState != null ) + CheckLightLevels( false ); + + InvalidateMyRunUO(); + } + + public override void OnItemRemoved( Item item ) + { + base.OnItemRemoved( item ); + + if ( item is BaseArmor || item is BaseWeapon ) + { + Hits=Hits; Stam=Stam; Mana=Mana; + } + + if ( this.NetState != null ) + CheckLightLevels( false ); + + InvalidateMyRunUO(); + } + + public override double ArmorRating + { + get + { + //BaseArmor ar; + double rating = 0.0; + + AddArmorRating( ref rating, NeckArmor ); + AddArmorRating( ref rating, HandArmor ); + AddArmorRating( ref rating, HeadArmor ); + AddArmorRating( ref rating, ArmsArmor ); + AddArmorRating( ref rating, LegsArmor ); + AddArmorRating( ref rating, ChestArmor ); + AddArmorRating( ref rating, ShieldArmor ); + + return VirtualArmor + VirtualArmorMod + rating; + } + } + + private void AddArmorRating( ref double rating, Item armor ) + { + BaseArmor ar = armor as BaseArmor; + + if( ar != null && ( !Core.AOS || ar.ArmorAttributes.MageArmor == 0 )) + rating += ar.ArmorRatingScaled; + } + + #region [Stats]Max + [CommandProperty( AccessLevel.GameMaster )] + public override int HitsMax + { + get + { + int strBase; + int strOffs = GetStatOffset( StatType.Str ); + + if ( Core.AOS ) + { + strBase = this.Str; //this.Str already includes GetStatOffset/str + strOffs = AosAttributes.GetValue( this, AosAttribute.BonusHits ); + + if ( AnimalForm.UnderTransformation( this, typeof( BakeKitsune ) ) || AnimalForm.UnderTransformation( this, typeof( GreyWolf ) ) ) + strOffs += 20; + } + else + { + strBase = this.RawStr; + } + + return (strBase / 2) + 50 + strOffs; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override int StamMax + { + get{ return base.StamMax + AosAttributes.GetValue( this, AosAttribute.BonusStam ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override int ManaMax + { + get{ return base.ManaMax + AosAttributes.GetValue( this, AosAttribute.BonusMana ) + ((Core.ML && Race == Race.Elf) ? 20 : 0); } + } + #endregion + + #region Stat Getters/Setters + + [CommandProperty( AccessLevel.GameMaster )] + public override int Str + { + get + { + if( Core.ML && this.AccessLevel == AccessLevel.Player ) + return Math.Min( base.Str, 150 ); + + return base.Str; + } + set + { + base.Str = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override int Int + { + get + { + if( Core.ML && this.AccessLevel == AccessLevel.Player ) + return Math.Min( base.Int, 150 ); + + return base.Int; + } + set + { + base.Int = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override int Dex + { + get + { + if( Core.ML && this.AccessLevel == AccessLevel.Player ) + return Math.Min( base.Dex, 150 ); + + return base.Dex; + } + set + { + base.Dex = value; + } + } + + #endregion + + public override bool Move( Direction d ) + { + NetState ns = this.NetState; + + if ( ns != null ) + { + List gumps = ns.Gumps; + + for ( int i = 0; i < gumps.Count; ++i ) + { + if ( gumps[i] is ResurrectGump ) + { + if ( Alive ) + { + CloseGump( typeof( ResurrectGump ) ); + } + else + { + SendLocalizedMessage( 500111 ); // You are frozen and cannot move. + return false; + } + } + } + } + + TimeSpan speed = ComputeMovementSpeed( d ); + + bool res; + + if ( !Alive ) + Server.Movement.MovementImpl.IgnoreMovableImpassables = true; + + res = base.Move( d ); + + Server.Movement.MovementImpl.IgnoreMovableImpassables = false; + + if ( !res ) + return false; + + m_NextMovementTime += speed; + + return true; + } + + public override bool CheckMovement( Direction d, out int newZ ) + { + DesignContext context = m_DesignContext; + + if ( context == null ) + return base.CheckMovement( d, out newZ ); + + HouseFoundation foundation = context.Foundation; + + newZ = foundation.Z + HouseFoundation.GetLevelZ( context.Level, context.Foundation ); + + int newX = this.X, newY = this.Y; + Movement.Movement.Offset( d, ref newX, ref newY ); + + int startX = foundation.X + foundation.Components.Min.X + 1; + int startY = foundation.Y + foundation.Components.Min.Y + 1; + int endX = startX + foundation.Components.Width - 1; + int endY = startY + foundation.Components.Height - 2; + + return ( newX >= startX && newY >= startY && newX < endX && newY < endY && Map == foundation.Map ); + } + + public override bool AllowItemUse( Item item ) + { + return DesignContext.Check( this ); + } + + public SkillName[] AnimalFormRestrictedSkills{ get{ return m_AnimalFormRestrictedSkills; } } + + private SkillName[] m_AnimalFormRestrictedSkills = new SkillName[] + { + SkillName.ArmsLore, SkillName.Begging, SkillName.Discordance, SkillName.Forensics, + SkillName.Inscribe, SkillName.ItemID, SkillName.Meditation, SkillName.Peacemaking, + SkillName.Provocation, SkillName.RemoveTrap, SkillName.SpiritSpeak, SkillName.Stealing, + SkillName.TasteID + }; + + public override bool AllowSkillUse( SkillName skill ) + { + if ( AnimalForm.UnderTransformation( this ) ) + { + for( int i = 0; i < m_AnimalFormRestrictedSkills.Length; i++ ) + { + if( m_AnimalFormRestrictedSkills[i] == skill ) + { + SendLocalizedMessage( 1070771 ); // You cannot use that skill in this form. + return false; + } + } + } + + return DesignContext.Check( this ); + } + + private bool m_LastProtectedMessage; + private int m_NextProtectionCheck = 10; + + public virtual void RecheckTownProtection() + { + m_NextProtectionCheck = 10; + + Regions.GuardedRegion reg = (Regions.GuardedRegion) this.Region.GetRegion( typeof( Regions.GuardedRegion ) ); + bool isProtected = ( reg != null && !reg.IsDisabled() ); + + if ( isProtected != m_LastProtectedMessage ) + { + if ( isProtected ) + SendLocalizedMessage( 500112 ); // You are now under the protection of the town guards. + else + SendLocalizedMessage( 500113 ); // You have left the protection of the town guards. + + m_LastProtectedMessage = isProtected; + } + } + + public override void MoveToWorld( Point3D loc, Map map ) + { + base.MoveToWorld( loc, map ); + + RecheckTownProtection(); + } + + public override void SetLocation( Point3D loc, bool isTeleport ) + { + if ( !isTeleport && AccessLevel == AccessLevel.Player ) + { + // moving, not teleporting + int zDrop = ( this.Location.Z - loc.Z ); + + if ( zDrop > 20 ) // we fell more than one story + Hits -= ((zDrop / 20) * 10) - 5; // deal some damage; does not kill, disrupt, etc + } + + base.SetLocation( loc, isTeleport ); + + if ( isTeleport || --m_NextProtectionCheck == 0 ) + RecheckTownProtection(); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( from == this ) + { + if ( m_Quest != null ) + m_Quest.GetContextMenuEntries( list ); + + if ( Alive && InsuranceEnabled ) + { + list.Add( new CallbackEntry( 6201, new ContextCallback( ToggleItemInsurance ) ) ); + + if ( AutoRenewInsurance ) + list.Add( new CallbackEntry( 6202, new ContextCallback( CancelRenewInventoryInsurance ) ) ); + else + list.Add( new CallbackEntry( 6200, new ContextCallback( AutoRenewInventoryInsurance ) ) ); + } + + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null ) + { + if ( Alive && house.InternalizedVendors.Count > 0 && house.IsOwner( this ) ) + list.Add( new CallbackEntry( 6204, new ContextCallback( GetVendor ) ) ); + + if ( house.IsAosRules ) + list.Add( new CallbackEntry( 6207, new ContextCallback( LeaveHouse ) ) ); + } + + if ( m_JusticeProtectors.Count > 0 ) + list.Add( new CallbackEntry( 6157, new ContextCallback( CancelProtection ) ) ); + + if( Alive ) + list.Add( new CallbackEntry( 6210, new ContextCallback( ToggleChampionTitleDisplay ) ) ); + } + } + + private void CancelProtection() + { + for ( int i = 0; i < m_JusticeProtectors.Count; ++i ) + { + Mobile prot = m_JusticeProtectors[i]; + + string args = String.Format( "{0}\t{1}", this.Name, prot.Name ); + + prot.SendLocalizedMessage( 1049371, args ); // The protective relationship between ~1_PLAYER1~ and ~2_PLAYER2~ has been ended. + this.SendLocalizedMessage( 1049371, args ); // The protective relationship between ~1_PLAYER1~ and ~2_PLAYER2~ has been ended. + } + + m_JusticeProtectors.Clear(); + } + + #region Insurance + + private void ToggleItemInsurance() + { + if ( !CheckAlive() ) + return; + + BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ToggleItemInsurance_Callback ) ); + SendLocalizedMessage( 1060868 ); // Target the item you wish to toggle insurance status on to cancel + } + + private bool CanInsure( Item item ) + { + if ( item is Container || item is BagOfSending || item is KeyRing ) + return false; + + if ( (item is Spellbook && item.LootType == LootType.Blessed)|| item is Runebook || item is PotionKeg || item is Sigil ) + return false; + + if ( item.Stackable ) + return false; + + if ( item.LootType == LootType.Cursed ) + return false; + + if ( item.ItemID == 0x204E ) // death shroud + return false; + + return true; + } + + private void ToggleItemInsurance_Callback( Mobile from, object obj ) + { + if ( !CheckAlive() ) + return; + + Item item = obj as Item; + + if ( item == null || !item.IsChildOf( this ) ) + { + BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ToggleItemInsurance_Callback ) ); + SendLocalizedMessage( 1060871, "", 0x23 ); // You can only insure items that you have equipped or that are in your backpack + } + else if ( item.Insured ) + { + item.Insured = false; + + SendLocalizedMessage( 1060874, "", 0x35 ); // You cancel the insurance on the item + + BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ToggleItemInsurance_Callback ) ); + SendLocalizedMessage( 1060868, "", 0x23 ); // Target the item you wish to toggle insurance status on to cancel + } + else if ( !CanInsure( item ) ) + { + BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ToggleItemInsurance_Callback ) ); + SendLocalizedMessage( 1060869, "", 0x23 ); // You cannot insure that + } + else if ( item.LootType == LootType.Blessed || item.LootType == LootType.Newbied || item.BlessedFor == from ) + { + BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ToggleItemInsurance_Callback ) ); + SendLocalizedMessage( 1060870, "", 0x23 ); // That item is blessed and does not need to be insured + SendLocalizedMessage( 1060869, "", 0x23 ); // You cannot insure that + } + else + { + if ( !item.PayedInsurance ) + { + if ( Banker.Withdraw( from, 600 ) ) + { + SendLocalizedMessage( 1060398, "600" ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + item.PayedInsurance = true; + } + else + { + SendLocalizedMessage( 1061079, "", 0x23 ); // You lack the funds to purchase the insurance + return; + } + } + + item.Insured = true; + + SendLocalizedMessage( 1060873, "", 0x23 ); // You have insured the item + + BeginTarget( -1, false, TargetFlags.None, new TargetCallback( ToggleItemInsurance_Callback ) ); + SendLocalizedMessage( 1060868, "", 0x23 ); // Target the item you wish to toggle insurance status on to cancel + } + } + + private void AutoRenewInventoryInsurance() + { + if ( !CheckAlive() ) + return; + + SendLocalizedMessage( 1060881, "", 0x23 ); // You have selected to automatically reinsure all insured items upon death + AutoRenewInsurance = true; + } + + private void CancelRenewInventoryInsurance() + { + if ( !CheckAlive() ) + return; + + if( Core.SE ) + { + if( !HasGump( typeof( CancelRenewInventoryInsuranceGump ) ) ) + SendGump( new CancelRenewInventoryInsuranceGump( this ) ); + } + else + { + SendLocalizedMessage( 1061075, "", 0x23 ); // You have cancelled automatically reinsuring all insured items upon death + AutoRenewInsurance = false; + } + } + + private class CancelRenewInventoryInsuranceGump : Gump + { + private PlayerMobile m_Player; + + public CancelRenewInventoryInsuranceGump( PlayerMobile player ) : base( 250, 200 ) + { + m_Player = player; + + AddBackground( 0, 0, 240, 142, 0x13BE ); + AddImageTiled( 6, 6, 228, 100, 0xA40 ); + AddImageTiled( 6, 116, 228, 20, 0xA40 ); + AddAlphaRegion( 6, 6, 228, 142 ); + + AddHtmlLocalized( 8, 8, 228, 100, 1071021, 0x7FFF, false, false ); // You are about to disable inventory insurance auto-renewal. + + AddButton( 6, 116, 0xFB1, 0xFB2, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 40, 118, 450, 20, 1060051, 0x7FFF, false, false ); // CANCEL + + AddButton( 114, 116, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 148, 118, 450, 20, 1071022, 0x7FFF, false, false ); // DISABLE IT! + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( !m_Player.CheckAlive() ) + return; + + if ( info.ButtonID == 1 ) + { + m_Player.SendLocalizedMessage( 1061075, "", 0x23 ); // You have cancelled automatically reinsuring all insured items upon death + m_Player.AutoRenewInsurance = false; + } + else + { + m_Player.SendLocalizedMessage( 1042021 ); // Cancelled. + } + } + } + #endregion + + private void GetVendor() + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( CheckAlive() && house != null && house.IsOwner( this ) && house.InternalizedVendors.Count > 0 ) + { + CloseGump( typeof( ReclaimVendorGump ) ); + SendGump( new ReclaimVendorGump( house ) ); + } + } + + private void LeaveHouse() + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null ) + this.Location = house.BanLocation; + } + + private delegate void ContextCallback(); + + private class CallbackEntry : ContextMenuEntry + { + private ContextCallback m_Callback; + + public CallbackEntry( int number, ContextCallback callback ) : this( number, -1, callback ) + { + } + + public CallbackEntry( int number, int range, ContextCallback callback ) : base( number, range ) + { + m_Callback = callback; + } + + public override void OnClick() + { + if ( m_Callback != null ) + m_Callback(); + } + } + + public override void DisruptiveAction() + { + if( Meditating ) + { + RemoveBuff( BuffIcon.ActiveMeditation ); + } + + base.DisruptiveAction(); + } + public override void OnDoubleClick( Mobile from ) + { + if ( this == from && !Warmode ) + { + IMount mount = Mount; + + if ( mount != null && !DesignContext.Check( this ) ) + return; + } + + base.OnDoubleClick( from ); + } + + public override void DisplayPaperdollTo( Mobile to ) + { + if ( DesignContext.Check( this ) ) + base.DisplayPaperdollTo( to ); + } + + private static bool m_NoRecursion; + + public override bool CheckEquip( Item item ) + { + if ( !base.CheckEquip( item ) ) + return false; + + #region Factions + FactionItem factionItem = FactionItem.Find( item ); + + if ( factionItem != null ) + { + Faction faction = Faction.Find( this ); + + if ( faction == null ) + { + SendLocalizedMessage( 1010371 ); // You cannot equip a faction item! + return false; + } + else if ( faction != factionItem.Faction ) + { + SendLocalizedMessage( 1010372 ); // You cannot equip an opposing faction's item! + return false; + } + else + { + int maxWearables = FactionItem.GetMaxWearables( this ); + + for ( int i = 0; i < Items.Count; ++i ) + { + Item equiped = Items[i]; + + if ( item != equiped && FactionItem.Find( equiped ) != null ) + { + if ( --maxWearables == 0 ) + { + SendLocalizedMessage( 1010373 ); // You do not have enough rank to equip more faction items! + return false; + } + } + } + } + } + #endregion + + if ( this.AccessLevel < AccessLevel.GameMaster && item.Layer != Layer.Mount && this.HasTrade ) + { + BounceInfo bounce = item.GetBounce(); + + if ( bounce != null ) + { + if ( bounce.m_Parent is Item ) + { + Item parent = (Item) bounce.m_Parent; + + if ( parent == this.Backpack || parent.IsChildOf( this.Backpack ) ) + return true; + } + else if ( bounce.m_Parent == this ) + { + return true; + } + } + + SendLocalizedMessage( 1004042 ); // You can only equip what you are already carrying while you have a trade pending. + return false; + } + + return true; + } + + public override bool CheckTrade( Mobile to, Item item, SecureTradeContainer cont, bool message, bool checkItems, int plusItems, int plusWeight ) + { + int msgNum = 0; + + if ( cont == null ) + { + if ( to.Holding != null ) + msgNum = 1062727; // You cannot trade with someone who is dragging something. + else if ( this.HasTrade ) + msgNum = 1062781; // You are already trading with someone else! + else if ( to.HasTrade ) + msgNum = 1062779; // That person is already involved in a trade + } + + if ( msgNum == 0 ) + { + if ( cont != null ) + { + plusItems += cont.TotalItems; + plusWeight += cont.TotalWeight; + } + + if ( this.Backpack == null || !this.Backpack.CheckHold( this, item, false, checkItems, plusItems, plusWeight ) ) + msgNum = 1004040; // You would not be able to hold this if the trade failed. + else if ( to.Backpack == null || !to.Backpack.CheckHold( to, item, false, checkItems, plusItems, plusWeight ) ) + msgNum = 1004039; // The recipient of this trade would not be able to carry this. + else + msgNum = CheckContentForTrade( item ); + } + + if ( msgNum != 0 ) + { + if ( message ) + this.SendLocalizedMessage( msgNum ); + + return false; + } + + return true; + } + + private static int CheckContentForTrade( Item item ) + { + if ( item is TrapableContainer && ((TrapableContainer)item).TrapType != TrapType.None ) + return 1004044; // You may not trade trapped items. + + if ( SkillHandlers.StolenItem.IsStolen( item ) ) + return 1004043; // You may not trade recently stolen items. + + if ( item is Container ) + { + foreach ( Item subItem in item.Items ) + { + int msg = CheckContentForTrade( subItem ); + + if ( msg != 0 ) + return msg; + } + } + + return 0; + } + + public override bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + if ( !base.CheckNonlocalDrop( from, item, target ) ) + return false; + + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + Container pack = this.Backpack; + if ( from == this && this.HasTrade && ( target == pack || target.IsChildOf( pack ) ) ) + { + BounceInfo bounce = item.GetBounce(); + + if ( bounce != null && bounce.m_Parent is Item ) + { + Item parent = (Item) bounce.m_Parent; + + if ( parent == pack || parent.IsChildOf( pack ) ) + return true; + } + + SendLocalizedMessage( 1004041 ); // You can't do that while you have a trade pending. + return false; + } + + return true; + } + + protected override void OnLocationChange( Point3D oldLocation ) + { + CheckLightLevels( false ); + + DesignContext context = m_DesignContext; + + if ( context == null || m_NoRecursion ) + return; + + m_NoRecursion = true; + + HouseFoundation foundation = context.Foundation; + + int newX = this.X, newY = this.Y; + int newZ = foundation.Z + HouseFoundation.GetLevelZ( context.Level, context.Foundation ); + + int startX = foundation.X + foundation.Components.Min.X + 1; + int startY = foundation.Y + foundation.Components.Min.Y + 1; + int endX = startX + foundation.Components.Width - 1; + int endY = startY + foundation.Components.Height - 2; + + if ( newX >= startX && newY >= startY && newX < endX && newY < endY && Map == foundation.Map ) + { + if ( Z != newZ ) + Location = new Point3D( X, Y, newZ ); + + m_NoRecursion = false; + return; + } + + Location = new Point3D( foundation.X, foundation.Y, newZ ); + Map = foundation.Map; + + m_NoRecursion = false; + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m is BaseCreature && !((BaseCreature)m).Controlled ) + return false; + + return base.OnMoveOver( m ); + } + + public override bool CheckShove( Mobile shoved ) + { + if( TransformationSpell.UnderTransformation( this, typeof( WraithFormSpell ) ) ) + return true; + else + return base.CheckShove( shoved ); + } + + + protected override void OnMapChange( Map oldMap ) + { + if ( (Map != Faction.Facet && oldMap == Faction.Facet) || (Map == Faction.Facet && oldMap != Faction.Facet) ) + InvalidateProperties(); + + DesignContext context = m_DesignContext; + + if ( context == null || m_NoRecursion ) + return; + + m_NoRecursion = true; + + HouseFoundation foundation = context.Foundation; + + if ( Map != foundation.Map ) + Map = foundation.Map; + + m_NoRecursion = false; + } + + public override void OnBeneficialAction( Mobile target, bool isCriminal ) + { + if ( m_SentHonorContext != null ) + m_SentHonorContext.OnSourceBeneficialAction( target ); + + base.OnBeneficialAction( target, isCriminal ); + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + int disruptThreshold; + + 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 ); + + if ( m_ReceivedHonorContext != null ) + m_ReceivedHonorContext.OnTargetDamaged( from, amount ); + if ( m_SentHonorContext != null ) + m_SentHonorContext.OnSourceDamaged( from, amount ); + + base.OnDamage( amount, from, willKill ); + } + + public override void Resurrect() + { + bool wasAlive = this.Alive; + + base.Resurrect(); + + if ( this.Alive && !wasAlive ) + { + Item deathRobe = new DeathRobe(); + + if ( !EquipItem( deathRobe ) ) + deathRobe.Delete(); + } + } + + public override double RacialSkillBonus + { + get + { + if( Core.ML && this.Race == Race.Human ) + return 20.0; + + return 0; + } + } + + private Mobile m_InsuranceAward; + private int m_InsuranceCost; + private int m_InsuranceBonus; + + public override bool OnBeforeDeath() + { + m_InsuranceCost = 0; + m_InsuranceAward = base.FindMostRecentDamager( false ); + + if ( m_InsuranceAward is BaseCreature ) + { + Mobile master = ((BaseCreature)m_InsuranceAward).GetMaster(); + + if ( master != null ) + m_InsuranceAward = master; + } + + if ( m_InsuranceAward != null && (!m_InsuranceAward.Player || m_InsuranceAward == this) ) + m_InsuranceAward = null; + + if ( m_InsuranceAward is PlayerMobile ) + ((PlayerMobile)m_InsuranceAward).m_InsuranceBonus = 0; + + if ( m_ReceivedHonorContext != null ) + m_ReceivedHonorContext.OnTargetKilled(); + if ( m_SentHonorContext != null ) + m_SentHonorContext.OnSourceKilled(); + + return base.OnBeforeDeath(); + } + + private bool CheckInsuranceOnDeath( Item item ) + { + if ( InsuranceEnabled && item.Insured ) + { + if ( AutoRenewInsurance ) + { + int cost = ( m_InsuranceAward == null ? 600 : 300 ); + + if ( Banker.Withdraw( this, cost ) ) + { + m_InsuranceCost += cost; + item.PayedInsurance = true; + } + else + { + SendLocalizedMessage( 1061079, "", 0x23 ); // You lack the funds to purchase the insurance + item.PayedInsurance = false; + item.Insured = false; + } + } + else + { + item.PayedInsurance = false; + item.Insured = false; + } + + if ( m_InsuranceAward != null ) + { + if ( Banker.Deposit( m_InsuranceAward, 300 ) ) + { + if ( m_InsuranceAward is PlayerMobile ) + ((PlayerMobile)m_InsuranceAward).m_InsuranceBonus += 300; + } + } + + return true; + } + + return false; + } + + public override DeathMoveResult GetParentMoveResultFor( Item item ) + { + if ( CheckInsuranceOnDeath( item ) ) + return DeathMoveResult.MoveToBackpack; + + DeathMoveResult res = base.GetParentMoveResultFor( item ); + + if ( res == DeathMoveResult.MoveToCorpse && item.Movable && this.Young ) + res = DeathMoveResult.MoveToBackpack; + + return res; + } + + public override DeathMoveResult GetInventoryMoveResultFor( Item item ) + { + if ( CheckInsuranceOnDeath( item ) ) + return DeathMoveResult.MoveToBackpack; + + DeathMoveResult res = base.GetInventoryMoveResultFor( item ); + + if ( res == DeathMoveResult.MoveToCorpse && item.Movable && this.Young ) + res = DeathMoveResult.MoveToBackpack; + + return res; + } + + public override void OnDeath( Container c ) + { + base.OnDeath( c ); + + HueMod = -1; + NameMod = null; + SavagePaintExpiration = TimeSpan.Zero; + + SetHairMods( -1, -1 ); + + PolymorphSpell.StopTimer( this ); + IncognitoSpell.StopTimer( this ); + DisguiseGump.StopTimer( this ); + + EndAction( typeof( PolymorphSpell ) ); + EndAction( typeof( IncognitoSpell ) ); + + MeerMage.StopEffect( this, false ); + + SkillHandlers.StolenItem.ReturnOnDeath( this, c ); + + if ( m_PermaFlags.Count > 0 ) + { + m_PermaFlags.Clear(); + + if ( c is Corpse ) + ((Corpse)c).Criminal = true; + + if ( SkillHandlers.Stealing.ClassicMode ) + Criminal = true; + } + + if ( this.Kills >= 5 && DateTime.Now >= m_NextJustAward ) + { + Mobile m = FindMostRecentDamager( false ); + + if( m is BaseCreature ) + m = ((BaseCreature)m).GetMaster(); + + if ( m != null && m is PlayerMobile && m != this ) + { + bool gainedPath = false; + + int pointsToGain = 0; + + pointsToGain += (int) Math.Sqrt( this.GameTime.TotalSeconds * 4 ); + pointsToGain *= 5; + pointsToGain += (int) Math.Pow( this.Skills.Total / 250, 2 ); + + if ( VirtueHelper.Award( m, VirtueName.Justice, pointsToGain, ref gainedPath ) ) + { + if ( gainedPath ) + m.SendLocalizedMessage( 1049367 ); // You have gained a path in Justice! + else + m.SendLocalizedMessage( 1049363 ); // You have gained in Justice. + + m.FixedParticles( 0x375A, 9, 20, 5027, EffectLayer.Waist ); + m.PlaySound( 0x1F7 ); + + m_NextJustAward = DateTime.Now + TimeSpan.FromMinutes( pointsToGain / 3 ); + } + } + } + + if ( m_InsuranceCost > 0 ) + SendLocalizedMessage( 1060398, m_InsuranceCost.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + + if ( m_InsuranceAward is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m_InsuranceAward; + + if ( pm.m_InsuranceBonus > 0 ) + pm.SendLocalizedMessage( 1060397, pm.m_InsuranceBonus.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + } + + Mobile killer = this.FindMostRecentDamager( true ); + + if ( killer is BaseCreature ) + { + BaseCreature bc = (BaseCreature)killer; + + Mobile master = bc.GetMaster(); + if( master != null ) + killer = master; + } + + if ( this.Young ) + { + if ( YoungDeathTeleport() ) + Timer.DelayCall( TimeSpan.FromSeconds( 2.5 ), new TimerCallback( SendYoungDeathNotice ) ); + } + + Faction.HandleDeath( this, killer ); + + Server.Guilds.Guild.HandleDeath( this, killer ); + + if( m_BuffTable != null ) + { + List list = new List(); + + foreach( BuffInfo buff in m_BuffTable.Values ) + { + if( !buff.RetainThroughDeath ) + { + list.Add( buff ); + } + } + + for( int i = 0; i < list.Count; i++ ) + { + RemoveBuff( list[i] ); + } + } + } + + private List m_PermaFlags; + private List m_VisList; + private Hashtable m_AntiMacroTable; + private TimeSpan m_GameTime; + private TimeSpan m_ShortTermElapse; + private TimeSpan m_LongTermElapse; + private DateTime m_SessionStart; + private DateTime m_LastEscortTime; + private DateTime m_NextSmithBulkOrder; + private DateTime m_NextTailorBulkOrder; + private DateTime m_SavagePaintExpiration; + private SkillName m_Learning = (SkillName)(-1); + + public SkillName Learning + { + get{ return m_Learning; } + set{ m_Learning = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan SavagePaintExpiration + { + get + { + TimeSpan ts = m_SavagePaintExpiration - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + return ts; + } + set + { + m_SavagePaintExpiration = DateTime.Now + value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan NextSmithBulkOrder + { + get + { + TimeSpan ts = m_NextSmithBulkOrder - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + return ts; + } + set + { + try{ m_NextSmithBulkOrder = DateTime.Now + value; } + catch{} + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan NextTailorBulkOrder + { + get + { + TimeSpan ts = m_NextTailorBulkOrder - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + return ts; + } + set + { + try{ m_NextTailorBulkOrder = DateTime.Now + value; } + catch{} + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastEscortTime + { + get{ return m_LastEscortTime; } + set{ m_LastEscortTime = value; } + } + + public PlayerMobile() + { + m_VisList = new List(); + m_PermaFlags = new List(); + m_AntiMacroTable = new Hashtable(); + + m_BOBFilter = new Engines.BulkOrders.BOBFilter(); + + m_GameTime = TimeSpan.Zero; + m_ShortTermElapse = TimeSpan.FromHours( 8.0 ); + m_LongTermElapse = TimeSpan.FromHours( 40.0 ); + + m_JusticeProtectors = new List(); + m_GuildRank = Guilds.RankDefinition.Lowest; + + m_ChampionTitles = new ChampionTitleInfo(); + + InvalidateMyRunUO(); + } + + public override bool MutateSpeech( List hears, ref string text, ref object context ) + { + if ( Alive ) + return false; + + if ( Core.AOS ) + { + for ( int i = 0; i < hears.Count; ++i ) + { + Mobile m = hears[i]; + + if ( m != this && m.Skills[SkillName.SpiritSpeak].Value >= 100.0 ) + return false; + } + } + + return base.MutateSpeech( hears, ref text, ref context ); + } + + public override void DoSpeech( string text, int[] keywords, MessageType type, int hue ) + { + if( Guilds.Guild.NewGuildSystem && (type == MessageType.Guild || type == MessageType.Alliance) ) + { + Guilds.Guild g = this.Guild as Guilds.Guild; + if( g == null ) + { + SendLocalizedMessage( 1063142 ); // You are not in a guild! + } + else if( type == MessageType.Alliance ) + { + if( g.Alliance != null && g.Alliance.IsMember( g ) ) + { + //g.Alliance.AllianceTextMessage( hue, "[Alliance][{0}]: {1}", this.Name, text ); + g.Alliance.AllianceChat( this, text ); + SendToStaffMessage( this, "[Alliance]: {0}", this.Name, text ); + + m_AllianceMessageHue = hue; + } + else + { + SendLocalizedMessage( 1071020 ); // You are not in an alliance! + } + } + else //Type == MessageType.Guild + { + m_GuildMessageHue = hue; + + g.GuildChat( this, text ); + SendToStaffMessage( this, "[Guild]: {0}", text ); + } + } + else + { + base.DoSpeech( text, keywords, type, hue ); + } + } + + private static void SendToStaffMessage( Mobile from, string text ) + { + Packet p = null; + + foreach( NetState ns in from.GetClientsInRange( 8 ) ) + { + Mobile mob = ns.Mobile; + + if( mob != null && mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > from.AccessLevel ) + { + if( p == null ) + p = Packet.Acquire( new UnicodeMessage( from.Serial, from.Body, MessageType.Regular, from.SpeechHue, 3, from.Language, from.Name, text ) ); + + ns.Send( p ); + } + } + + Packet.Release( p ); + } + private static void SendToStaffMessage( Mobile from, string format, params object[] args ) + { + SendToStaffMessage( from, String.Format( format, args ) ); + } + + public override void Damage( int amount, Mobile from ) + { + 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 ); + } + + #region Poison + public override ApplyPoisonResult ApplyPoison( Mobile from, Poison poison ) + { + if ( !Alive ) + 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 ( this.Young ) + return true; + + return base.CheckPoisonImmunity( from, poison ); + } + + public override void OnPoisonImmunity( Mobile from, Poison poison ) + { + if ( this.Young ) + SendLocalizedMessage( 502808 ); // You would have been poisoned, were you not new to the land of Britannia. Be careful in the future. + else + base.OnPoisonImmunity( from, poison ); + } + #endregion + + public PlayerMobile( Serial s ) : base( s ) + { + m_VisList = new List(); + m_AntiMacroTable = new Hashtable(); + InvalidateMyRunUO(); + } + + public List VisibilityList + { + get{ return m_VisList; } + } + + public List PermaFlags + { + get{ return m_PermaFlags; } + } + + public override int Luck{ get{ return AosAttributes.GetValue( this, AosAttribute.Luck ); } } + + public override bool IsHarmfulCriminal( Mobile target ) + { + if ( SkillHandlers.Stealing.ClassicMode && target is PlayerMobile && ((PlayerMobile)target).m_PermaFlags.Count > 0 ) + { + int noto = Notoriety.Compute( this, target ); + + if ( noto == Notoriety.Innocent ) + target.Delta( MobileDelta.Noto ); + + return false; + } + + if ( target is BaseCreature && ((BaseCreature)target).InitialInnocent && !((BaseCreature)target).Controlled ) + return false; + + return base.IsHarmfulCriminal( target ); + } + + public bool AntiMacroCheck( Skill skill, object obj ) + { + if ( obj == null || m_AntiMacroTable == null || this.AccessLevel != AccessLevel.Player ) + return true; + + Hashtable tbl = (Hashtable)m_AntiMacroTable[skill]; + if ( tbl == null ) + m_AntiMacroTable[skill] = tbl = new Hashtable(); + + CountAndTimeStamp count = (CountAndTimeStamp)tbl[obj]; + if ( count != null ) + { + if ( count.TimeStamp + SkillCheck.AntiMacroExpire <= DateTime.Now ) + { + count.Count = 1; + return true; + } + else + { + ++count.Count; + if ( count.Count <= SkillCheck.Allowance ) + return true; + else + return false; + } + } + else + { + tbl[obj] = count = new CountAndTimeStamp(); + count.Count = 1; + + return true; + } + } + + private void RevertHair() + { + SetHairMods( -1, -1 ); + } + + private Engines.BulkOrders.BOBFilter m_BOBFilter; + + public Engines.BulkOrders.BOBFilter BOBFilter + { + get{ return m_BOBFilter; } + } + + + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + + switch ( version ) + { + case 25: + { + int recipeCount = reader.ReadInt(); + + if( recipeCount > 0 ) + { + m_AcquiredRecipes = new Dictionary(); + + for( int i = 0; i < recipeCount; i++ ) + { + int r = reader.ReadInt(); + if( reader.ReadBool() ) //Don't add in recipies which we haven't gotten or have been removed + m_AcquiredRecipes.Add( r, true ); + } + } + goto case 24; + } + case 24: + { + m_LastHonorLoss = reader.ReadDeltaTime(); + goto case 23; + } + case 23: + { + m_ChampionTitles = new ChampionTitleInfo( reader ); + goto case 22; + } + case 22: + { + m_LastValorLoss = reader.ReadDateTime(); + goto case 21; + } + case 21: + { + m_ToTItemsTurnedIn = reader.ReadEncodedInt(); + m_ToTTotalMonsterFame = reader.ReadInt(); + goto case 20; + } + case 20: + { + m_AllianceMessageHue = reader.ReadEncodedInt(); + m_GuildMessageHue = reader.ReadEncodedInt(); + + goto case 19; + } + case 19: + { + int rank = reader.ReadEncodedInt(); + int maxRank = Guilds.RankDefinition.Ranks.Length -1; + if( rank > maxRank ) + rank = maxRank; + + m_GuildRank = Guilds.RankDefinition.Ranks[rank]; + m_LastOnline = reader.ReadDateTime(); + goto case 18; + } + case 18: + { + m_SolenFriendship = (SolenFriendship) reader.ReadEncodedInt(); + + goto case 17; + } + case 17: // changed how DoneQuests is serialized + case 16: + { + m_Quest = QuestSerializer.DeserializeQuest( reader ); + + if ( m_Quest != null ) + m_Quest.From = this; + + int count = reader.ReadEncodedInt(); + + if ( count > 0 ) + { + m_DoneQuests = new List(); + + for ( int i = 0; i < count; ++i ) + { + Type questType = QuestSerializer.ReadType( QuestSystem.QuestTypes, reader ); + DateTime restartTime; + + if ( version < 17 ) + restartTime = DateTime.MaxValue; + else + restartTime = reader.ReadDateTime(); + + m_DoneQuests.Add( new QuestRestartInfo( questType, restartTime ) ); + } + } + + m_Profession = reader.ReadEncodedInt(); + goto case 15; + } + case 15: + { + m_LastCompassionLoss = reader.ReadDeltaTime(); + goto case 14; + } + case 14: + { + m_CompassionGains = reader.ReadEncodedInt(); + + if ( m_CompassionGains > 0 ) + m_NextCompassionDay = reader.ReadDeltaTime(); + + goto case 13; + } + case 13: // just removed m_PayedInsurance list + case 12: + { + m_BOBFilter = new Engines.BulkOrders.BOBFilter( reader ); + goto case 11; + } + case 11: + { + if ( version < 13 ) + { + List payed = reader.ReadStrongItemList(); + + for ( int i = 0; i < payed.Count; ++i ) + payed[i].PayedInsurance = true; + } + + goto case 10; + } + case 10: + { + if ( reader.ReadBool() ) + { + m_HairModID = reader.ReadInt(); + m_HairModHue = reader.ReadInt(); + m_BeardModID = reader.ReadInt(); + m_BeardModHue = reader.ReadInt(); + + // We cannot call SetHairMods( -1, -1 ) here because the items have not yet loaded + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( RevertHair ) ); + } + + goto case 9; + } + case 9: + { + SavagePaintExpiration = reader.ReadTimeSpan(); + + if ( SavagePaintExpiration > TimeSpan.Zero ) + { + BodyMod = ( Female ? 184 : 183 ); + HueMod = 0; + } + + goto case 8; + } + case 8: + { + m_NpcGuild = (NpcGuild)reader.ReadInt(); + m_NpcGuildJoinTime = reader.ReadDateTime(); + m_NpcGuildGameTime = reader.ReadTimeSpan(); + goto case 7; + } + case 7: + { + m_PermaFlags = reader.ReadStrongMobileList(); + goto case 6; + } + case 6: + { + NextTailorBulkOrder = reader.ReadTimeSpan(); + goto case 5; + } + case 5: + { + NextSmithBulkOrder = reader.ReadTimeSpan(); + goto case 4; + } + case 4: + { + m_LastJusticeLoss = reader.ReadDeltaTime(); + m_JusticeProtectors = reader.ReadStrongMobileList(); + goto case 3; + } + case 3: + { + m_LastSacrificeGain = reader.ReadDeltaTime(); + m_LastSacrificeLoss = reader.ReadDeltaTime(); + m_AvailableResurrects = reader.ReadInt(); + goto case 2; + } + case 2: + { + m_Flags = (PlayerFlag)reader.ReadInt(); + goto case 1; + } + case 1: + { + m_LongTermElapse = reader.ReadTimeSpan(); + m_ShortTermElapse = reader.ReadTimeSpan(); + m_GameTime = reader.ReadTimeSpan(); + goto case 0; + } + case 0: + { + break; + } + } + + // Professions weren't verified on 1.0 RC0 + if ( !CharacterCreation.VerifyProfession( m_Profession ) ) + m_Profession = 0; + + if ( m_PermaFlags == null ) + m_PermaFlags = new List(); + + if ( m_JusticeProtectors == null ) + m_JusticeProtectors = new List(); + + if ( m_BOBFilter == null ) + m_BOBFilter = new Engines.BulkOrders.BOBFilter(); + + if( m_GuildRank == null ) + m_GuildRank = Guilds.RankDefinition.Member; //Default to member if going from older verstion to new version (only time it should be null) + + if( m_LastOnline == DateTime.MinValue && Account != null ) + m_LastOnline = ((Account)Account).LastLogin; + + if( m_ChampionTitles == null ) + m_ChampionTitles = new ChampionTitleInfo(); + + List list = this.Stabled; + + for ( int i = 0; i < list.Count; ++i ) + { + BaseCreature bc = list[i] as BaseCreature; + + if ( bc != null ) + bc.IsStabled = true; + } + + CheckAtrophies( this ); + + + if( Hidden ) //Hiding is the only buff where it has an effect that's serialized. + AddBuff( new BuffInfo( BuffIcon.HidingAndOrStealth, 1075655 ) ); + } + + public override void Serialize( GenericWriter writer ) + { + //cleanup our anti-macro table + foreach ( Hashtable t in m_AntiMacroTable.Values ) + { + ArrayList remove = new ArrayList(); + foreach ( CountAndTimeStamp time in t.Values ) + { + if ( time.TimeStamp + SkillCheck.AntiMacroExpire <= DateTime.Now ) + remove.Add( time ); + } + + for (int i=0;i 0 ) + --ShortTermMurders; + } + + if ( m_LongTermElapse < this.GameTime ) + { + m_LongTermElapse += TimeSpan.FromHours( 40 ); + if ( Kills > 0 ) + --Kills; + } + + CheckAtrophies( this ); + + base.Serialize( writer ); + + writer.Write( (int) 25 ); // version + + if( m_AcquiredRecipes == null ) + { + writer.Write( (int)0 ); + } + else + { + writer.Write( m_AcquiredRecipes.Count ); + + foreach( KeyValuePair kvp in m_AcquiredRecipes ) + { + writer.Write( kvp.Key ); + writer.Write( kvp.Value ); + } + } + + writer.WriteDeltaTime( m_LastHonorLoss ); + + ChampionTitleInfo.Serialize( writer, m_ChampionTitles ); + + writer.Write( m_LastValorLoss ); + writer.WriteEncodedInt( m_ToTItemsTurnedIn ); + writer.Write( m_ToTTotalMonsterFame ); //This ain't going to be a small #. + + writer.WriteEncodedInt( m_AllianceMessageHue ); + writer.WriteEncodedInt( m_GuildMessageHue ); + + writer.WriteEncodedInt( m_GuildRank.Rank ); + writer.Write( m_LastOnline ); + + writer.WriteEncodedInt( (int) m_SolenFriendship ); + + QuestSerializer.Serialize( m_Quest, writer ); + + if ( m_DoneQuests == null ) + { + writer.WriteEncodedInt( (int) 0 ); + } + else + { + writer.WriteEncodedInt( (int) m_DoneQuests.Count ); + + for ( int i = 0; i < m_DoneQuests.Count; ++i ) + { + QuestRestartInfo restartInfo = m_DoneQuests[i]; + + QuestSerializer.Write( (Type) restartInfo.QuestType, QuestSystem.QuestTypes, writer ); + writer.Write( (DateTime) restartInfo.RestartTime ); + } + } + + writer.WriteEncodedInt( (int) m_Profession ); + + writer.WriteDeltaTime( m_LastCompassionLoss ); + + writer.WriteEncodedInt( m_CompassionGains ); + + if ( m_CompassionGains > 0 ) + writer.WriteDeltaTime( m_NextCompassionDay ); + + m_BOBFilter.Serialize( writer ); + + bool useMods = ( m_HairModID != -1 || m_BeardModID != -1 ); + + writer.Write( useMods ); + + if ( useMods ) + { + writer.Write( (int) m_HairModID ); + writer.Write( (int) m_HairModHue ); + writer.Write( (int) m_BeardModID ); + writer.Write( (int) m_BeardModHue ); + } + + writer.Write( SavagePaintExpiration ); + + writer.Write( (int) m_NpcGuild ); + writer.Write( (DateTime) m_NpcGuildJoinTime ); + writer.Write( (TimeSpan) m_NpcGuildGameTime ); + + writer.Write( m_PermaFlags, true ); + + writer.Write( NextTailorBulkOrder ); + + writer.Write( NextSmithBulkOrder ); + + writer.WriteDeltaTime( m_LastJusticeLoss ); + writer.Write( m_JusticeProtectors, true ); + + writer.WriteDeltaTime( m_LastSacrificeGain ); + writer.WriteDeltaTime( m_LastSacrificeLoss ); + writer.Write( m_AvailableResurrects ); + + writer.Write( (int) m_Flags ); + + writer.Write( m_LongTermElapse ); + writer.Write( m_ShortTermElapse ); + writer.Write( this.GameTime ); + } + + + + public static void CheckAtrophies( Mobile m ) + { + SacrificeVirtue.CheckAtrophy( m ); + JusticeVirtue.CheckAtrophy( m ); + CompassionVirtue.CheckAtrophy( m ); + ValorVirtue.CheckAtrophy( m ); + HonorVirtue.CheckAtrophy( m ); + + if( m is PlayerMobile ) + ChampionTitleInfo.CheckAtrophy( (PlayerMobile)m ); + } + + public void ResetKillTime() + { + m_ShortTermElapse = this.GameTime + TimeSpan.FromHours( 8 ); + m_LongTermElapse = this.GameTime + TimeSpan.FromHours( 40 ); + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime SessionStart + { + get{ return m_SessionStart; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public TimeSpan GameTime + { + get + { + if ( NetState != null ) + return m_GameTime + (DateTime.Now - m_SessionStart); + else + return m_GameTime; + } + } + + public override bool CanSee( Mobile m ) + { + if ( m is PlayerMobile && ((PlayerMobile)m).m_VisList.Contains( this ) ) + return true; + + return base.CanSee( m ); + } + + public override bool CanSee( Item item ) + { + if ( m_DesignContext != null && m_DesignContext.Foundation.IsHiddenToCustomizer( item ) ) + return false; + + return base.CanSee( item ); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + Faction faction = Faction.Find( this ); + + if ( faction != null ) + faction.RemoveMember( this ); + + BaseHouse.HandleDeletion( this ); + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( Map == Faction.Facet ) + { + PlayerState pl = PlayerState.Find( this ); + + if ( pl != null ) + { + Faction faction = pl.Faction; + + if ( faction.Commander == this ) + list.Add( 1042733, faction.Definition.PropName ); // Commanding Lord of the ~1_FACTION_NAME~ + else if ( pl.Sheriff != null ) + list.Add( 1042734, "{0}\t{1}", pl.Sheriff.Definition.FriendlyName, faction.Definition.PropName ); // The Sheriff of ~1_CITY~, ~2_FACTION_NAME~ + else if ( pl.Finance != null ) + list.Add( 1042735, "{0}\t{1}", pl.Finance.Definition.FriendlyName, faction.Definition.PropName ); // The Finance Minister of ~1_CITY~, ~2_FACTION_NAME~ + else if ( pl.MerchantTitle != MerchantTitle.None ) + list.Add( 1060776, "{0}\t{1}", MerchantTitles.GetInfo( pl.MerchantTitle ).Title, faction.Definition.PropName ); // ~1_val~, ~2_val~ + else + list.Add( 1060776, "{0}\t{1}", pl.Rank.Title, faction.Definition.PropName ); // ~1_val~, ~2_val~ + } + } + } + + public override void OnSingleClick( Mobile from ) + { + if ( Map == Faction.Facet ) + { + PlayerState pl = PlayerState.Find( this ); + + if ( pl != null ) + { + string text; + bool ascii = false; + + Faction faction = pl.Faction; + + if ( faction.Commander == this ) + text = String.Concat( this.Female ? "(Commanding Lady of the " : "(Commanding Lord of the ", faction.Definition.FriendlyName, ")" ); + else if ( pl.Sheriff != null ) + text = String.Concat( "(The Sheriff of ", pl.Sheriff.Definition.FriendlyName, ", ", faction.Definition.FriendlyName, ")" ); + else if ( pl.Finance != null ) + text = String.Concat( "(The Finance Minister of ", pl.Finance.Definition.FriendlyName, ", ", faction.Definition.FriendlyName, ")" ); + else + { + ascii = true; + + if ( pl.MerchantTitle != MerchantTitle.None ) + text = String.Concat( "(", MerchantTitles.GetInfo( pl.MerchantTitle ).Title.String, ", ", faction.Definition.FriendlyName, ")" ); + else + text = String.Concat( "(", pl.Rank.Title.String, ", ", faction.Definition.FriendlyName, ")" ); + } + + int hue = ( Faction.Find( from ) == faction ? 98 : 38 ); + + PrivateOverheadMessage( MessageType.Label, hue, ascii, text, from.NetState ); + } + } + + base.OnSingleClick( from ); + } + + protected override bool OnMove( Direction d ) + { + if( !Core.SE ) + return base.OnMove( d ); + + if( AccessLevel != AccessLevel.Player ) + return true; + + if( Hidden && DesignContext.Find( this ) == null ) //Hidden & NOT customizing a house + { + if( !Mounted && Skills.Stealth.Value >= 25.0 ) + { + bool running = (d & Direction.Running) != 0; + + if( running ) + { + if( (AllowedStealthSteps -= 2) <= 0 ) + RevealingAction(); + } + else if( AllowedStealthSteps-- <= 0 ) + { + Server.SkillHandlers.Stealth.OnUse( this ); + } + } + else + { + RevealingAction(); + } + } + + return true; + } + + private bool m_BedrollLogout; + + public bool BedrollLogout + { + get{ return m_BedrollLogout; } + set{ m_BedrollLogout = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override bool Paralyzed + { + get + { + return base.Paralyzed; + } + set + { + base.Paralyzed = value; + + if( value ) + AddBuff( new BuffInfo( BuffIcon.Paralyze, 1075827 ) ); //Paralyze/You are frozen and can not move + else + RemoveBuff( BuffIcon.Paralyze ); + } + } + + #region Ethics + private Ethics.Player m_EthicPlayer; + + [CommandProperty( AccessLevel.GameMaster )] + public Ethics.Player EthicPlayer + { + get { return m_EthicPlayer; } + set { m_EthicPlayer = value; } + } + #endregion + + #region Factions + private PlayerState m_FactionPlayerState; + + public PlayerState FactionPlayerState + { + get{ return m_FactionPlayerState; } + set{ m_FactionPlayerState = value; } + } + #endregion + + #region Quests + private QuestSystem m_Quest; + private List m_DoneQuests; + private SolenFriendship m_SolenFriendship; + + public QuestSystem Quest + { + get{ return m_Quest; } + set{ m_Quest = value; } + } + + public List DoneQuests + { + get{ return m_DoneQuests; } + set{ m_DoneQuests = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SolenFriendship SolenFriendship + { + get{ return m_SolenFriendship; } + set{ m_SolenFriendship = value; } + } + #endregion + + #region MyRunUO Invalidation + private bool m_ChangedMyRunUO; + + public bool ChangedMyRunUO + { + get{ return m_ChangedMyRunUO; } + set{ m_ChangedMyRunUO = value; } + } + + public void InvalidateMyRunUO() + { + if ( !Deleted && !m_ChangedMyRunUO ) + { + m_ChangedMyRunUO = true; + Engines.MyRunUO.MyRunUO.QueueMobileUpdate( this ); + } + } + + public override void OnKillsChange( int oldValue ) + { + if ( this.Young && this.Kills > oldValue ) + { + Account acc = this.Account as Account; + + if ( acc != null ) + acc.RemoveYoungStatus( 0 ); + } + + InvalidateMyRunUO(); + } + + public override void OnGenderChanged( bool oldFemale ) + { + InvalidateMyRunUO(); + } + + public override void OnGuildChange( Server.Guilds.BaseGuild oldGuild ) + { + InvalidateMyRunUO(); + } + + public override void OnGuildTitleChange( string oldTitle ) + { + InvalidateMyRunUO(); + } + + public override void OnKarmaChange( int oldValue ) + { + InvalidateMyRunUO(); + } + + public override void OnFameChange( int oldValue ) + { + InvalidateMyRunUO(); + } + + public override void OnSkillChange( SkillName skill, double oldBase ) + { + if ( this.Young && this.SkillsTotal >= 4500 ) + { + Account acc = this.Account as Account; + + if ( acc != null ) + acc.RemoveYoungStatus( 1019036 ); // You have successfully obtained a respectable skill level, and have outgrown your status as a young player! + } + + InvalidateMyRunUO(); + } + + public override void OnAccessLevelChanged( AccessLevel oldLevel ) + { + InvalidateMyRunUO(); + } + + public override void OnRawStatChange( StatType stat, int oldValue ) + { + InvalidateMyRunUO(); + } + + public override void OnDelete() + { + if ( m_ReceivedHonorContext != null ) + m_ReceivedHonorContext.Cancel(); + if ( m_SentHonorContext != null ) + m_SentHonorContext.Cancel(); + + InvalidateMyRunUO(); + } + #endregion + + #region Fastwalk Prevention + private static bool FastwalkPrevention = true; // Is fastwalk prevention enabled? + private static TimeSpan FastwalkThreshold = TimeSpan.FromSeconds( 0.4 ); // Fastwalk prevention will become active after 0.4 seconds + + private DateTime m_NextMovementTime; + + public virtual bool UsesFastwalkPrevention{ get{ return ( AccessLevel < AccessLevel.Counselor ); } } + + public override TimeSpan ComputeMovementSpeed( Direction dir, bool checkTurning ) + { + if ( checkTurning && (dir & Direction.Mask) != (this.Direction & Direction.Mask) ) + return TimeSpan.FromSeconds( 0.1 ); // We are NOT actually moving (just a direction change) + + bool running = ( (dir & Direction.Running) != 0 ); + + bool onHorse = ( this.Mount != null ); + + AnimalFormContext context = AnimalForm.GetContext( this ); + + if ( onHorse || (context != null && context.SpeedBoost) ) + return ( running ? Mobile.RunMount : Mobile.WalkMount ); + + return ( running ? Mobile.RunFoot : Mobile.WalkFoot ); + } + + public static bool MovementThrottle_Callback( NetState ns ) + { + PlayerMobile pm = ns.Mobile as PlayerMobile; + + if ( pm == null || !pm.UsesFastwalkPrevention ) + return true; + + if ( pm.m_NextMovementTime == DateTime.MinValue ) + { + // has not yet moved + pm.m_NextMovementTime = DateTime.Now; + return true; + } + + TimeSpan ts = pm.m_NextMovementTime - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + { + // been a while since we've last moved + pm.m_NextMovementTime = DateTime.Now; + return true; + } + + return ( ts < FastwalkThreshold ); + } + #endregion + + #region Enemy of One + private Type m_EnemyOfOneType; + private bool m_WaitingForEnemy; + + public Type EnemyOfOneType + { + get{ return m_EnemyOfOneType; } + set + { + Type oldType = m_EnemyOfOneType; + Type newType = value; + + if ( oldType == newType ) + return; + + m_EnemyOfOneType = value; + + DeltaEnemies( oldType, newType ); + } + } + + public bool WaitingForEnemy + { + get{ return m_WaitingForEnemy; } + set{ m_WaitingForEnemy = value; } + } + + private void DeltaEnemies( Type oldType, Type newType ) + { + foreach ( Mobile m in this.GetMobilesInRange( 18 ) ) + { + Type t = m.GetType(); + + if ( t == oldType || t == newType ) + Send( new MobileMoving( m, Notoriety.Compute( this, m ) ) ); + } + } + #endregion + + #region Hair and beard mods + private int m_HairModID = -1, m_HairModHue; + private int m_BeardModID = -1, m_BeardModHue; + + public void SetHairMods( int hairID, int beardID ) + { + if ( hairID == -1 ) + InternalRestoreHair( true, ref m_HairModID, ref m_HairModHue ); + else if ( hairID != -2 ) + InternalChangeHair( true, hairID, ref m_HairModID, ref m_HairModHue ); + + if ( beardID == -1 ) + InternalRestoreHair( false, ref m_BeardModID, ref m_BeardModHue ); + else if ( beardID != -2 ) + InternalChangeHair( false, beardID, ref m_BeardModID, ref m_BeardModHue ); + } + + private void CreateHair( bool hair, int id, int hue ) + { + if( hair ) + { + //TODO Verification? + HairItemID = id; + HairHue = hue; + } + else + { + FacialHairItemID = id; + FacialHairHue = hue; + } + } + + private void InternalRestoreHair( bool hair, ref int id, ref int hue ) + { + if ( id == -1 ) + return; + + if ( hair ) + HairItemID = 0; + else + FacialHairItemID = 0; + + //if( id != 0 ) + CreateHair( hair, id, hue ); + + id = -1; + hue = 0; + } + + private void InternalChangeHair( bool hair, int id, ref int storeID, ref int storeHue ) + { +     if ( storeID == -1 ) +     { +         storeID = hair ? HairItemID : FacialHairItemID; +         storeHue = hair ? HairHue : FacialHairHue; +     } +     CreateHair( hair, id, 0 ); + } + #endregion + + #region Virtues + private DateTime m_LastSacrificeGain; + private DateTime m_LastSacrificeLoss; + private int m_AvailableResurrects; + + public DateTime LastSacrificeGain{ get{ return m_LastSacrificeGain; } set{ m_LastSacrificeGain = value; } } + public DateTime LastSacrificeLoss{ get{ return m_LastSacrificeLoss; } set{ m_LastSacrificeLoss = value; } } + public int AvailableResurrects{ get{ return m_AvailableResurrects; } set{ m_AvailableResurrects = value; } } + + private DateTime m_NextJustAward; + private DateTime m_LastJusticeLoss; + private List m_JusticeProtectors; + + public DateTime LastJusticeLoss{ get{ return m_LastJusticeLoss; } set{ m_LastJusticeLoss = value; } } + public List JusticeProtectors { get { return m_JusticeProtectors; } set { m_JusticeProtectors = value; } } + + private DateTime m_LastCompassionLoss; + private DateTime m_NextCompassionDay; + private int m_CompassionGains; + + public DateTime LastCompassionLoss{ get{ return m_LastCompassionLoss; } set{ m_LastCompassionLoss = value; } } + public DateTime NextCompassionDay{ get{ return m_NextCompassionDay; } set{ m_NextCompassionDay = value; } } + public int CompassionGains{ get{ return m_CompassionGains; } set{ m_CompassionGains = value; } } + + private DateTime m_LastValorLoss; + + public DateTime LastValorLoss { get { return m_LastValorLoss; } set { m_LastValorLoss = value; } } + + private DateTime m_LastHonorLoss; + private DateTime m_LastHonorUse; + private bool m_HonorActive; + private HonorContext m_ReceivedHonorContext; + private HonorContext m_SentHonorContext; + + public DateTime LastHonorLoss{ get{ return m_LastHonorLoss; } set{ m_LastHonorLoss = value; } } + public DateTime LastHonorUse{ get{ return m_LastHonorUse; } set{ m_LastHonorUse = value; } } + public bool HonorActive{ get{ return m_HonorActive; } set{ m_HonorActive = value; } } + public HonorContext ReceivedHonorContext{ get{ return m_ReceivedHonorContext; } set{ m_ReceivedHonorContext = value; } } + public HonorContext SentHonorContext{ get{ return m_SentHonorContext; } set{ m_SentHonorContext = value; } } + #endregion + + #region Young system + [CommandProperty( AccessLevel.GameMaster )] + public bool Young + { + get{ return GetFlag( PlayerFlag.Young ); } + set{ SetFlag( PlayerFlag.Young, value ); InvalidateProperties(); } + } + + public override string ApplyNameSuffix( string suffix ) + { + if ( Young ) + { + if ( suffix.Length == 0 ) + suffix = "(Young)"; + else + suffix = String.Concat( suffix, " (Young)" ); + } + + #region Ethics + if ( m_EthicPlayer != null ) + { + if ( suffix.Length == 0 ) + suffix = m_EthicPlayer.Ethic.Definition.Adjunct.String; + else + suffix = String.Concat( suffix, " ", m_EthicPlayer.Ethic.Definition.Adjunct.String ); + } + #endregion + + return base.ApplyNameSuffix( suffix ); + } + + + public override TimeSpan GetLogoutDelay() + { + if ( Young || BedrollLogout || TestCenter.Enabled ) + return TimeSpan.Zero; + + return base.GetLogoutDelay(); + } + + private DateTime m_LastYoungMessage = DateTime.MinValue; + + public bool CheckYoungProtection( Mobile from ) + { + if ( !this.Young ) + return false; + + if ( Region.IsPartOf( typeof( DungeonRegion ) ) ) + return false; + + if( from is BaseCreature && ((BaseCreature)from).IgnoreYoungProtection ) + return false; + + if ( this.Quest != null && this.Quest.IgnoreYoungProtection( from ) ) + return false; + + if ( DateTime.Now - m_LastYoungMessage > TimeSpan.FromMinutes( 1.0 ) ) + { + m_LastYoungMessage = DateTime.Now; + SendLocalizedMessage( 1019067 ); // A monster looks at you menacingly but does not attack. You would be under attack now if not for your status as a new citizen of Britannia. + } + + return true; + } + + private DateTime m_LastYoungHeal = DateTime.MinValue; + + public bool CheckYoungHealTime() + { + if ( DateTime.Now - m_LastYoungHeal > TimeSpan.FromMinutes( 5.0 ) ) + { + m_LastYoungHeal = DateTime.Now; + return true; + } + + return false; + } + + private static Point3D[] m_TrammelDeathDestinations = new Point3D[] + { + new Point3D( 1481, 1612, 20 ), + new Point3D( 2708, 2153, 0 ), + new Point3D( 2249, 1230, 0 ), + new Point3D( 5197, 3994, 37 ), + new Point3D( 1412, 3793, 0 ), + new Point3D( 3688, 2232, 20 ), + new Point3D( 2578, 604, 0 ), + new Point3D( 4397, 1089, 0 ), + new Point3D( 5741, 3218, -2 ), + new Point3D( 2996, 3441, 15 ), + new Point3D( 624, 2225, 0 ), + new Point3D( 1916, 2814, 0 ), + new Point3D( 2929, 854, 0 ), + new Point3D( 545, 967, 0 ), + new Point3D( 3665, 2587, 0 ) + }; + + private static Point3D[] m_IlshenarDeathDestinations = new Point3D[] + { + new Point3D( 1216, 468, -13 ), + new Point3D( 723, 1367, -60 ), + new Point3D( 745, 725, -28 ), + new Point3D( 281, 1017, 0 ), + new Point3D( 986, 1011, -32 ), + new Point3D( 1175, 1287, -30 ), + new Point3D( 1533, 1341, -3 ), + new Point3D( 529, 217, -44 ), + new Point3D( 1722, 219, 96 ) + }; + + private static Point3D[] m_MalasDeathDestinations = new Point3D[] + { + new Point3D( 2079, 1376, -70 ), + new Point3D( 944, 519, -71 ) + }; + + private static Point3D[] m_TokunoDeathDestinations = new Point3D[] + { + new Point3D( 1166, 801, 27 ), + new Point3D( 782, 1228, 25 ), + new Point3D( 268, 624, 15 ) + }; + + public bool YoungDeathTeleport() + { + if ( this.Region.IsPartOf( typeof( Jail ) ) + || this.Region.IsPartOf( "Samurai start location" ) + || this.Region.IsPartOf( "Ninja start location" ) + || this.Region.IsPartOf( "Ninja cave" ) ) + return false; + + Point3D loc; + Map map; + + DungeonRegion dungeon = (DungeonRegion) this.Region.GetRegion( typeof( DungeonRegion ) ); + if ( dungeon != null && dungeon.EntranceLocation != Point3D.Zero ) + { + loc = dungeon.EntranceLocation; + map = dungeon.EntranceMap; + } + else + { + loc = this.Location; + map = this.Map; + } + + Point3D[] list; + + if ( map == Map.Trammel ) + list = m_TrammelDeathDestinations; + else if ( map == Map.Ilshenar ) + list = m_IlshenarDeathDestinations; + else if ( map == Map.Malas ) + list = m_MalasDeathDestinations; + else if ( map == Map.Tokuno ) + list = m_TokunoDeathDestinations; + else + return false; + + Point3D dest = Point3D.Zero; + int sqDistance = int.MaxValue; + + for ( int i = 0; i < list.Length; i++ ) + { + Point3D curDest = list[i]; + + int width = loc.X - curDest.X; + int height = loc.Y - curDest.Y; + int curSqDistance = width * width + height * height; + + if ( curSqDistance < sqDistance ) + { + dest = curDest; + sqDistance = curSqDistance; + } + } + + this.MoveToWorld( dest, map ); + return true; + } + + private void SendYoungDeathNotice() + { + this.SendGump( new YoungDeathNotice() ); + } + #endregion + + #region Speech log + private SpeechLog m_SpeechLog; + + public SpeechLog SpeechLog{ get{ return m_SpeechLog; } } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( SpeechLog.Enabled && this.NetState != null ) + { + if ( m_SpeechLog == null ) + m_SpeechLog = new SpeechLog(); + + m_SpeechLog.Add( e.Mobile, e.Speech ); + } + } + #endregion + + #region Champion Titles + [CommandProperty( AccessLevel.GameMaster )] + public bool DisplayChampionTitle + { + get { return GetFlag( PlayerFlag.DisplayChampionTitle ); } + set { SetFlag( PlayerFlag.DisplayChampionTitle, value ); } + } + + private ChampionTitleInfo m_ChampionTitles; + + [CommandProperty( AccessLevel.GameMaster )] + public ChampionTitleInfo ChampionTitles { get { return m_ChampionTitles; } set { } } + + private void ToggleChampionTitleDisplay() + { + if( !CheckAlive() ) + return; + + if( DisplayChampionTitle ) + SendLocalizedMessage( 1062419, "", 0x23 ); // You have chosen to hide your monster kill title. + else + SendLocalizedMessage( 1062418, "", 0x23 ); // You have chosen to display your monster kill title. + + DisplayChampionTitle = !DisplayChampionTitle; + } + + [PropertyObject] + public class ChampionTitleInfo + { + public static TimeSpan LossDelay = TimeSpan.FromDays( 1.0 ); + public const int LossAmount = 90; + + private class TitleInfo + { + private int m_Value; + private DateTime m_LastDecay; + + public int Value { get { return m_Value; } set { m_Value = value; } } + public DateTime LastDecay { get { return m_LastDecay; } set { m_LastDecay = value; } } + + public TitleInfo() + { + } + + public TitleInfo( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch( version ) + { + case 0: + { + m_Value = reader.ReadEncodedInt(); + m_LastDecay = reader.ReadDateTime(); + break; + } + } + } + + public static void Serialize( GenericWriter writer, TitleInfo info ) + { + writer.WriteEncodedInt( (int)0 ); // version + + writer.WriteEncodedInt( info.m_Value ); + writer.Write( info.m_LastDecay ); + } + + } + private TitleInfo[] m_Values; + + private int m_Harrower; //Harrower titles do NOT decay + + + public int GetValue( ChampionSpawnType type ) + { + return GetValue( (int)type ); + } + + public void SetValue( ChampionSpawnType type, int value ) + { + SetValue( (int)type, value ); + } + + public void Award( ChampionSpawnType type, int value ) + { + Award( (int)type, value ); + } + + public int GetValue( int index ) + { + if( m_Values == null || index < 0 || index >= m_Values.Length ) + return 0; + + if( m_Values[index] == null ) + m_Values[index] = new TitleInfo(); + + return m_Values[index].Value; + } + + public DateTime GetLastDecay( int index ) + { + if( m_Values == null || index < 0 || index >= m_Values.Length ) + return DateTime.MinValue; + + if( m_Values[index] == null ) + m_Values[index] = new TitleInfo(); + + return m_Values[index].LastDecay; + } + + public void SetValue( int index, int value ) + { + if( m_Values == null ) + m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length]; + + if( value < 0 ) + value = 0; + + if( index < 0 || index >= m_Values.Length ) + return; + + if( m_Values[index] == null ) + m_Values[index] = new TitleInfo(); + + m_Values[index].Value = value; + } + + public void Award( int index, int value ) + { + if( m_Values == null ) + m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length]; + + if( index < 0 || index >= m_Values.Length || value <= 0 ) + return; + + if( m_Values[index] == null ) + m_Values[index] = new TitleInfo(); + + m_Values[index].Value += value; + } + + public void Atrophy( int index, int value ) + { + if( m_Values == null ) + m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length]; + + if( index < 0 || index >= m_Values.Length || value <= 0 ) + return; + + if( m_Values[index] == null ) + m_Values[index] = new TitleInfo(); + + int before = m_Values[index].Value; + + if( (m_Values[index].Value - value) < 0 ) + m_Values[index].Value = 0; + else + m_Values[index].Value -= value; + + if( before != m_Values[index].Value ) + m_Values[index].LastDecay = DateTime.Now; + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Abyss { get { return GetValue( ChampionSpawnType.Abyss ); } set { SetValue( ChampionSpawnType.Abyss, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Arachnid { get { return GetValue( ChampionSpawnType.Arachnid ); } set { SetValue( ChampionSpawnType.Arachnid, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ColdBlood { get { return GetValue( ChampionSpawnType.ColdBlood ); } set { SetValue( ChampionSpawnType.ColdBlood, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int ForestLord { get { return GetValue( ChampionSpawnType.ForestLord ); } set { SetValue( ChampionSpawnType.ForestLord, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int SleepingDragon { get { return GetValue( ChampionSpawnType.SleepingDragon ); } set { SetValue( ChampionSpawnType.SleepingDragon, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int UnholyTerror { get { return GetValue( ChampionSpawnType.UnholyTerror ); } set { SetValue( ChampionSpawnType.UnholyTerror, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int VerminHorde { get { return GetValue( ChampionSpawnType.VerminHorde ); } set { SetValue( ChampionSpawnType.VerminHorde, value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Harrower { get { return m_Harrower; } set { m_Harrower = value; } } + + public ChampionTitleInfo() + { + } + + public ChampionTitleInfo( GenericReader reader ) + { + int version = reader.ReadEncodedInt(); + + switch( version ) + { + case 0: + { + m_Harrower = reader.ReadEncodedInt(); + + int length = reader.ReadEncodedInt(); + m_Values = new TitleInfo[length]; + + for( int i = 0; i < length; i++ ) + { + m_Values[i] = new TitleInfo( reader ); + } + + if( m_Values.Length != ChampionSpawnInfo.Table.Length ) + { + TitleInfo[] oldValues = m_Values; + m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length]; + + for( int i = 0; i < m_Values.Length && i < oldValues.Length; i++ ) + { + m_Values[i] = oldValues[i]; + } + } + break; + } + } + } + + public static void Serialize( GenericWriter writer, ChampionTitleInfo titles ) + { + writer.WriteEncodedInt( (int)0 ); // version + + writer.WriteEncodedInt( titles.m_Harrower ); + + int length = titles.m_Values.Length; + writer.WriteEncodedInt( length ); + + for( int i = 0; i < length; i++ ) + { + if( titles.m_Values[i] == null ) + titles.m_Values[i] = new TitleInfo(); + + TitleInfo.Serialize( writer, titles.m_Values[i] ); + } + } + + public static void CheckAtrophy( PlayerMobile pm ) + { + ChampionTitleInfo t = pm.m_ChampionTitles; + if( t == null ) + return; + + if( t.m_Values == null ) + t.m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length]; + + for( int i = 0; i < t.m_Values.Length; i++ ) + { + if( (t.GetLastDecay( i ) + LossDelay) < DateTime.Now ) + { + t.Atrophy( i, LossAmount ); + } + } + } + + public static void AwardHarrowerTitle( PlayerMobile pm ) //Called when killing a harrower. Will give a minimum of 1 point. + { + ChampionTitleInfo t = pm.m_ChampionTitles; + if( t == null ) + return; + + if( t.m_Values == null ) + t.m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length]; + + int count = 1; + + for( int i = 0; i < t.m_Values.Length; i++ ) + { + if( t.m_Values[i].Value > 900 ) + count++; + } + + t.m_Harrower = Math.Max( count, t.m_Harrower ); //Harrower titles never decay. + } + } + #endregion + + #region Recipes + + private Dictionary m_AcquiredRecipes; + + public virtual bool HasRecipe( Recipe r ) + { + if( r == null ) + return false; + + return HasRecipe( r.ID ); + } + + public virtual bool HasRecipe( int recipeID ) + { + if( m_AcquiredRecipes != null && m_AcquiredRecipes.ContainsKey( recipeID ) ) + return m_AcquiredRecipes[recipeID]; + + return false; + } + + public virtual void AcquireRecipe( int recipeID ) + { + if( m_AcquiredRecipes == null ) + m_AcquiredRecipes = new Dictionary(); + + m_AcquiredRecipes.Add( recipeID, true ); + } + + public virtual void AcquireRecipe( Recipe r ) + { + if( r != null ) + AcquireRecipe( r.ID ); + } + + public virtual void ResetRecipes() + { + m_AcquiredRecipes = null; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int KnownRecipes + { + get + { + if( m_AcquiredRecipes == null ) + return 0; + + return m_AcquiredRecipes.Count; + } + } + + + #endregion + + #region Buff Icons + + public void ResendBuffs() + { + if( !BuffInfo.Enabled || m_BuffTable == null ) + return; + + NetState state = this.NetState; + + if( state != null && state.Version >= BuffInfo.RequiredClient ) + { + foreach( BuffInfo info in m_BuffTable.Values ) + { + state.Send( new AddBuffPacket( this, info ) ); + } + } + } + + private Dictionary m_BuffTable; + + public void AddBuff( BuffInfo b ) + { + if( !BuffInfo.Enabled || b == null ) + return; + + RemoveBuff( b ); //Check & subsequently remove the old one. + + if( m_BuffTable == null ) + m_BuffTable = new Dictionary(); + + m_BuffTable.Add( b.ID, b ); + + NetState state = this.NetState; + + if( state != null && state.Version >= BuffInfo.RequiredClient ) + { + state.Send( new AddBuffPacket( this, b ) ); + } + } + + public void RemoveBuff( BuffInfo b ) + { + if( b == null ) + return; + + RemoveBuff( b.ID ); + } + + public void RemoveBuff( BuffIcon b ) + { + if( m_BuffTable == null || !m_BuffTable.ContainsKey( b ) ) + return; + + BuffInfo info = m_BuffTable[b]; + + if( info.Timer != null && info.Timer.Running ) + info.Timer.Stop(); + + m_BuffTable.Remove( b ); + + NetState state = this.NetState; + + if( state != null && state.Version >= BuffInfo.RequiredClient ) + { + state.Send( new RemoveBuffPacket( this, b ) ); + } + + if( m_BuffTable.Count <= 0 ) + m_BuffTable = null; + } + #endregion + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Barracoon.cs b/Scripts/Mobiles/Special/Barracoon.cs new file mode 100644 index 000000000..83087f840 --- /dev/null +++ b/Scripts/Mobiles/Special/Barracoon.cs @@ -0,0 +1,232 @@ +using System; +using Server; +using Server.Items; +using Server.Spells; +using Server.Spells.Seventh; +using Server.Spells.Fifth; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class Barracoon : BaseChampion + { + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Greed; } } + + [Constructable] + public Barracoon() : base( AIType.AI_Melee ) + { + Name = "Barracoon"; + Title = "the piper"; + Body = 0x190; + Hue = 0x83EC; + + SetStr( 305, 425 ); + SetDex( 72, 150 ); + SetInt( 505, 750 ); + + SetHits( 4200 ); + SetStam( 102, 300 ); + + SetDamage( 25, 35 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 60, 70 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.MagicResist, 100.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 70; + + AddItem( new FancyShirt( Utility.RandomGreenHue() ) ); + AddItem( new LongPants( Utility.RandomYellowHue() ) ); + AddItem( new JesterHat( Utility.RandomPinkHue() ) ); + AddItem( new Cloak( Utility.RandomPinkHue() ) ); + AddItem( new Sandals() ); + + HairItemID = 0x203B; // Short Hair + HairHue = 0x94; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 3 ); + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool AutoDispel{ get{ return true; } } + public override double AutoDispelChance{ get{ return 1.0; } } + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + + public override bool ShowFameTitle{ get{ return false; } } + public override bool ClickTitle{ get{ return false; } } + + public void Polymorph( Mobile m ) + { + if ( !m.CanBeginAction( typeof( PolymorphSpell ) ) || !m.CanBeginAction( typeof( IncognitoSpell ) ) || m.IsBodyMod ) + return; + + IMount mount = m.Mount; + + if ( mount != null ) + mount.Rider = null; + + if ( m.Mounted ) + return; + + if ( m.BeginAction( typeof( PolymorphSpell ) ) ) + { + Item disarm = m.FindItemOnLayer( Layer.OneHanded ); + + if ( disarm != null && disarm.Movable ) + m.AddToBackpack( disarm ); + + disarm = m.FindItemOnLayer( Layer.TwoHanded ); + + if ( disarm != null && disarm.Movable ) + m.AddToBackpack( disarm ); + + m.BodyMod = 42; + m.HueMod = 0; + + new ExpirePolymorphTimer( m ).Start(); + } + } + + private class ExpirePolymorphTimer : Timer + { + private Mobile m_Owner; + + public ExpirePolymorphTimer( Mobile owner ) : base( TimeSpan.FromMinutes( 3.0 ) ) + { + m_Owner = owner; + + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + if ( !m_Owner.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + m_Owner.BodyMod = 0; + m_Owner.HueMod = -1; + m_Owner.EndAction( typeof( PolymorphSpell ) ); + } + } + } + + public void SpawnRatmen( Mobile target ) + { + Map map = this.Map; + + if ( map == null ) + return; + + int rats = 0; + + foreach ( Mobile m in this.GetMobilesInRange( 10 ) ) + { + if ( m is Ratman || m is RatmanArcher || m is RatmanMage ) + ++rats; + } + + if ( rats < 16 ) + { + PlaySound( 0x3D ); + + int newRats = Utility.RandomMinMax( 3, 6 ); + + for ( int i = 0; i < newRats; ++i ) + { + BaseCreature rat; + + switch ( Utility.Random( 5 ) ) + { + default: + case 0: case 1: rat = new Ratman(); break; + case 2: case 3: rat = new RatmanArcher(); break; + case 4: rat = new RatmanMage(); break; + } + + rat.Team = this.Team; + + bool validLocation = false; + Point3D loc = this.Location; + + 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 ); + } + + rat.MoveToWorld( loc, map ); + rat.Combatant = target; + } + } + } + + public void DoSpecialAbility( Mobile target ) + { + if ( target == null || target.Deleted ) //sanity + return; + if ( 0.6 >= Utility.RandomDouble() ) // 60% chance to polymorph attacker into a ratman + Polymorph( target ); + + if ( 0.2 >= Utility.RandomDouble() ) // 20% chance to more ratmen + SpawnRatmen( target ); + + if ( Hits < 500 && !IsBodyMod ) // Baracoon is low on life, polymorph into a ratman + Polymorph( this ); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + DoSpecialAbility( attacker ); + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + DoSpecialAbility( defender ); + } + + public Barracoon( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/BaseChampion.cs b/Scripts/Mobiles/Special/BaseChampion.cs new file mode 100644 index 000000000..c33d9858c --- /dev/null +++ b/Scripts/Mobiles/Special/BaseChampion.cs @@ -0,0 +1,276 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.CannedEvil; +using System.Collections.Generic; + +namespace Server.Mobiles +{ + public abstract class BaseChampion : BaseCreature + { + public BaseChampion( AIType aiType ) : this( aiType, FightMode.Closest ) + { + } + + public BaseChampion( AIType aiType, FightMode mode ) : base( aiType, mode, 18, 1, 0.1, 0.2 ) + { + } + + public BaseChampion( Serial serial ) : base( serial ) + { + } + + public abstract ChampionSkullType SkullType{ get; } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private PowerScroll CreateRandomPowerScroll() + { + int level; + double random = Utility.RandomDouble(); + + if ( 0.1 >= random ) + level = 20; + else if ( 0.4 >= random ) + level = 15; + else + level = 10; + + return PowerScroll.CreateRandomNoCraft( level, level ); + } + + public void GivePowerScrolls() + { + if ( Map != Map.Felucca ) + return; + + ArrayList toGive = new ArrayList(); + List rights = BaseCreature.GetLootingRights( this.DamageEntries, this.HitsMax ); + + for ( int i = rights.Count - 1; i >= 0; --i ) + { + DamageStore ds = rights[i]; + + if ( ds.m_HasRight ) + toGive.Add( ds.m_Mobile ); + } + + if ( toGive.Count == 0 ) + return; + + for( int i = 0; i < toGive.Count; i++ ) + { + Mobile m = (Mobile)toGive[i]; + + if( !(m is PlayerMobile) ) + continue; + + bool gainedPath = false; + + int pointsToGain = 800; + + if( VirtueHelper.Award( m, VirtueName.Valor, pointsToGain, ref gainedPath ) ) + { + if( gainedPath ) + m.SendLocalizedMessage( 1054032 ); // You have gained a path in Valor! + else + m.SendLocalizedMessage( 1054030 ); // You have gained in Valor! + + //No delay on Valor gains + } + } + + // Randomize + for ( int i = 0; i < toGive.Count; ++i ) + { + int rand = Utility.Random( toGive.Count ); + object hold = toGive[i]; + toGive[i] = toGive[rand]; + toGive[rand] = hold; + } + + for ( int i = 0; i < 6; ++i ) + { + Mobile m = (Mobile)toGive[i % toGive.Count]; + + PowerScroll ps = CreateRandomPowerScroll(); + + m.SendLocalizedMessage( 1049524 ); // You have received a scroll of power! + + if ( !Core.SE || m.Alive ) + m.AddToBackpack( ps ); + else + { + if ( m.Corpse != null && !m.Corpse.Deleted ) + ((Container)m.Corpse).DropItem( ps ); + else + m.AddToBackpack( ps ); + } + + if ( m is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m; + + for ( int j = 0; j < pm.JusticeProtectors.Count; ++j ) + { + Mobile prot = (Mobile)pm.JusticeProtectors[j]; + + if ( prot.Map != m.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion( m, prot ) ) + continue; + + int chance = 0; + + switch ( VirtueHelper.GetLevel( prot, VirtueName.Justice ) ) + { + case VirtueLevel.Seeker: chance = 60; break; + case VirtueLevel.Follower: chance = 80; break; + case VirtueLevel.Knight: chance = 100; break; + } + + if ( chance > Utility.Random( 100 ) ) + { + ps = CreateRandomPowerScroll(); + + prot.SendLocalizedMessage( 1049368 ); // You have been rewarded for your dedication to Justice! + + if ( !Core.SE || prot.Alive ) + prot.AddToBackpack( ps ); + else + { + if ( prot.Corpse != null && !prot.Corpse.Deleted ) + ((Container)prot.Corpse).DropItem( ps ); + else + prot.AddToBackpack( ps ); + } + } + } + } + } + } + + public override bool OnBeforeDeath() + { + if ( !NoKillAwards ) + { + GivePowerScrolls(); + + Map map = this.Map; + + if ( map != null ) + { + for ( int x = -12; x <= 12; ++x ) + { + for ( int y = -12; y <= 12; ++y ) + { + double dist = Math.Sqrt(x*x+y*y); + + if ( dist <= 12 ) + new GoodiesTimer( map, X + x, Y + y ).Start(); + } + } + } + } + + return base.OnBeforeDeath(); + } + + public override void OnDeath( Container c ) + { + if ( Map == Map.Felucca ) + { + //TODO: Confirm SE change or AoS one too? + List rights = BaseCreature.GetLootingRights( this.DamageEntries, this.HitsMax ); + List toGive = new List(); + + for ( int i = rights.Count - 1; i >= 0; --i ) + { + DamageStore ds = rights[i]; + + if ( ds.m_HasRight ) + toGive.Add( ds.m_Mobile ); + } + + if ( toGive.Count > 0 ) + toGive[Utility.Random( toGive.Count )].AddToBackpack( new ChampionSkull( SkullType ) ); + else + c.DropItem( new ChampionSkull( SkullType ) ); + } + + base.OnDeath( c ); + } + + private class GoodiesTimer : Timer + { + private Map m_Map; + private int m_X, m_Y; + + public GoodiesTimer( Map map, int x, int y ) : base( TimeSpan.FromSeconds( Utility.RandomDouble() * 10.0 ) ) + { + m_Map = map; + m_X = x; + m_Y = y; + } + + protected override void OnTick() + { + int z = m_Map.GetAverageZ( m_X, m_Y ); + bool canFit = m_Map.CanFit( m_X, m_Y, z, 6, false, false ); + + for ( int i = -3; !canFit && i <= 3; ++i ) + { + canFit = m_Map.CanFit( m_X, m_Y, z + i, 6, false, false ); + + if ( canFit ) + z += i; + } + + if ( !canFit ) + return; + + Gold g = new Gold( 500, 1000 ); + + g.MoveToWorld( new Point3D( m_X, m_Y, z ), m_Map ); + + if ( 0.5 >= Utility.RandomDouble() ) + { + switch ( Utility.Random( 3 ) ) + { + case 0: // Fire column + { + Effects.SendLocationParticles( EffectItem.Create( g.Location, g.Map, EffectItem.DefaultDuration ), 0x3709, 10, 30, 5052 ); + Effects.PlaySound( g, g.Map, 0x208 ); + + break; + } + case 1: // Explosion + { + Effects.SendLocationParticles( EffectItem.Create( g.Location, g.Map, EffectItem.DefaultDuration ), 0x36BD, 20, 10, 5044 ); + Effects.PlaySound( g, g.Map, 0x307 ); + + break; + } + case 2: // Ball of fire + { + Effects.SendLocationParticles( EffectItem.Create( g.Location, g.Map, EffectItem.DefaultDuration ), 0x36FE, 10, 10, 5052 ); + + break; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/BaseShieldGuard.cs b/Scripts/Mobiles/Special/BaseShieldGuard.cs new file mode 100644 index 000000000..1280fe65b --- /dev/null +++ b/Scripts/Mobiles/Special/BaseShieldGuard.cs @@ -0,0 +1,151 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Guilds; + +namespace Server.Mobiles +{ + public abstract class BaseShieldGuard : BaseCreature + { + public BaseShieldGuard() : base( AIType.AI_Melee, FightMode.Aggressor, 14, 1, 0.8, 1.6 ) + { + InitStats( 1000, 1000, 1000 ); + Title = "the guard"; + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + + AddItem( new FemalePlateChest() ); + AddItem( new PlateArms() ); + AddItem( new PlateLegs() ); + + switch( Utility.Random( 2 ) ) + { + case 0: AddItem( new Doublet( Utility.RandomNondyedHue() ) ); break; + case 1: AddItem( new BodySash( Utility.RandomNondyedHue() ) ); break; + } + + switch( Utility.Random( 2 ) ) + { + case 0: AddItem( new Skirt( Utility.RandomNondyedHue() ) ); break; + case 1: AddItem( new Kilt( Utility.RandomNondyedHue() ) ); break; + } + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + + AddItem( new PlateChest() ); + AddItem( new PlateArms() ); + AddItem( new PlateLegs() ); + + switch( Utility.Random( 3 ) ) + { + case 0: AddItem( new Doublet( Utility.RandomNondyedHue() ) ); break; + case 1: AddItem( new Tunic( Utility.RandomNondyedHue() ) ); break; + case 2: AddItem( new BodySash( Utility.RandomNondyedHue() ) ); break; + } + } + + Utility.AssignRandomHair( this ); + if( Utility.RandomBool() ) + Utility.AssignRandomFacialHair( this, HairHue ); + + VikingSword weapon = new VikingSword(); + weapon.Movable = false; + AddItem( weapon ); + + BaseShield shield = Shield; + shield.Movable = false; + AddItem( shield ); + + PackGold( 250, 500 ); + + Skills[SkillName.Anatomy].Base = 120.0; + Skills[SkillName.Tactics].Base = 120.0; + Skills[SkillName.Swords].Base = 120.0; + Skills[SkillName.MagicResist].Base = 120.0; + Skills[SkillName.DetectHidden].Base = 100.0; + } + + public abstract int Keyword{ get; } + public abstract BaseShield Shield{ get; } + public abstract int SignupNumber{ get; } + public abstract GuildType Type{ get; } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.InRange( this.Location, 2 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( !e.Handled && e.HasKeyword( Keyword ) && e.Mobile.InRange( this.Location, 2 ) ) + { + e.Handled = true; + + Mobile from = e.Mobile; + Guild g = from.Guild as Guild; + + if ( g == null || g.Type != Type ) + { + Say( SignupNumber ); + } + else + { + Container pack = from.Backpack; + BaseShield shield = Shield; + Item twoHanded = from.FindItemOnLayer( Layer.TwoHanded ); + + if ( (pack != null && pack.FindItemByType( shield.GetType() ) != null) || ( twoHanded != null && shield.GetType().IsAssignableFrom( twoHanded.GetType() ) ) ) + { + Say( 1007110 ); // Why dost thou ask about virtue guards when thou art one? + shield.Delete(); + } + else if ( from.PlaceInBackpack( shield ) ) + { + Say( Utility.Random( 1007101, 5 ) ); + Say( 1007139 ); // I see you are in need of our shield, Here you go. + from.AddToBackpack( shield ); + } + else + { + from.SendLocalizedMessage( 502868 ); // Your backpack is too full. + shield.Delete(); + } + } + } + + base.OnSpeech( e ); + } + + public BaseShieldGuard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/CapturedHordeMinion.cs b/Scripts/Mobiles/Special/CapturedHordeMinion.cs new file mode 100644 index 000000000..54a55d2b4 --- /dev/null +++ b/Scripts/Mobiles/Special/CapturedHordeMinion.cs @@ -0,0 +1,39 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class CapturedHordeMinion : HordeMinion + { + [Constructable] + public CapturedHordeMinion() + { + this.FightMode = FightMode.None; + } + + public override bool InitialInnocent{ get{ return true; } } + + public override bool CanBeDamaged() + { + return false; + } + + public CapturedHordeMinion( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/ChaosGuard.cs b/Scripts/Mobiles/Special/ChaosGuard.cs new file mode 100644 index 000000000..591f30a85 --- /dev/null +++ b/Scripts/Mobiles/Special/ChaosGuard.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Guilds; + +namespace Server.Mobiles +{ + public class ChaosGuard : BaseShieldGuard + { + public override int Keyword{ get{ return 0x22; } } // *chaos shield* + public override BaseShield Shield{ get{ return new ChaosShield(); } } + public override int SignupNumber{ get{ return 1007140; } } // Sign up with a guild of chaos if thou art interested. + public override GuildType Type{ get{ return GuildType.Chaos; } } + + public override bool BardImmune{ get{ return true; } } + + [Constructable] + public ChaosGuard() + { + } + + public ChaosGuard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Harrower.cs b/Scripts/Mobiles/Special/Harrower.cs new file mode 100644 index 000000000..2864b7905 --- /dev/null +++ b/Scripts/Mobiles/Special/Harrower.cs @@ -0,0 +1,545 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.CannedEvil; +using System.Collections.Generic; + +namespace Server.Mobiles +{ + public class Harrower : BaseCreature + { + private bool m_TrueForm; + private Item m_GateItem; + private ArrayList m_Tentacles; + private Timer m_Timer; + + private class SpawnEntry + { + public Point3D m_Location; + public Point3D m_Entrance; + + public SpawnEntry( Point3D loc, Point3D ent ) + { + m_Location = loc; + m_Entrance = ent; + } + } + + private static SpawnEntry[] m_Entries = new SpawnEntry[] + { + new SpawnEntry( new Point3D( 5242, 945, -40 ), new Point3D( 1176, 2638, 0 ) ), // Destard + new SpawnEntry( new Point3D( 5225, 798, 0 ), new Point3D( 1176, 2638, 0 ) ), // Destard + new SpawnEntry( new Point3D( 5556, 886, 30 ), new Point3D( 1298, 1080, 0 ) ), // Despise + new SpawnEntry( new Point3D( 5187, 615, 0 ), new Point3D( 4111, 432, 5 ) ), // Deceit + new SpawnEntry( new Point3D( 5319, 583, 0 ), new Point3D( 4111, 432, 5 ) ), // Deceit + new SpawnEntry( new Point3D( 5713, 1334, -1 ), new Point3D( 2923, 3407, 8 ) ), // Fire + new SpawnEntry( new Point3D( 5860, 1460, -2 ), new Point3D( 2923, 3407, 8 ) ), // Fire + new SpawnEntry( new Point3D( 5328, 1620, 0 ), new Point3D( 5451, 3143, -60 ) ), // Terathan Keep + new SpawnEntry( new Point3D( 5690, 538, 0 ), new Point3D( 2042, 224, 14 ) ), // Wrong + new SpawnEntry( new Point3D( 5609, 195, 0 ), new Point3D( 514, 1561, 0 ) ), // Shame + new SpawnEntry( new Point3D( 5475, 187, 0 ), new Point3D( 514, 1561, 0 ) ), // Shame + new SpawnEntry( new Point3D( 6085, 179, 0 ), new Point3D( 4721, 3822, 0 ) ), // Hythloth + new SpawnEntry( new Point3D( 6084, 66, 0 ), new Point3D( 4721, 3822, 0 ) ), // Hythloth + new SpawnEntry( new Point3D( 5499, 2003, 0 ), new Point3D( 2499, 919, 0 ) ), // Covetous + new SpawnEntry( new Point3D( 5579, 1858, 0 ), new Point3D( 2499, 919, 0 ) ) // Covetous + }; + + private static ArrayList m_Instances = new ArrayList(); + + public static ArrayList Instances{ get{ return m_Instances; } } + + public static Harrower Spawn( Point3D platLoc, Map platMap ) + { + if ( m_Instances.Count > 0 ) + return null; + + SpawnEntry entry = m_Entries[Utility.Random( m_Entries.Length )]; + + Harrower harrower = new Harrower(); + + harrower.MoveToWorld( entry.m_Location, Map.Felucca ); + + harrower.m_GateItem = new HarrowerGate( harrower, platLoc, platMap, entry.m_Entrance, Map.Felucca ); + + return harrower; + } + + public static bool CanSpawn + { + get + { + return ( m_Instances.Count == 0 ); + } + } + + [Constructable] + public Harrower() : base( AIType.AI_Mage, FightMode.Closest, 18, 1, 0.2, 0.4 ) + { + m_Instances.Add( this ); + + Name = "the harrower"; + BodyValue = 146; + + SetStr( 900, 1000 ); + SetDex( 125, 135 ); + SetInt( 1000, 1200 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 60; + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Energy, 50 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 60, 80 ); + SetResistance( ResistanceType.Cold, 60, 80 ); + SetResistance( ResistanceType.Poison, 60, 80 ); + SetResistance( ResistanceType.Energy, 60, 80 ); + + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.2, 110.0 ); + SetSkill( SkillName.MagicResist, 120.2, 160.0 ); + SetSkill( SkillName.Magery, 120.0 ); + SetSkill( SkillName.EvalInt, 120.0 ); + SetSkill( SkillName.Meditation, 120.0 ); + + m_Tentacles = new ArrayList(); + + m_Timer = new TeleportTimer( this ); + m_Timer.Start(); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.SuperBoss, 2 ); + AddLoot( LootPack.Meager ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool Unprovokable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + private static readonly double[] m_Offsets = new double[] + { + Math.Cos( 000.0 / 180.0 * Math.PI ), Math.Sin( 000.0 / 180.0 * Math.PI ), + Math.Cos( 040.0 / 180.0 * Math.PI ), Math.Sin( 040.0 / 180.0 * Math.PI ), + Math.Cos( 080.0 / 180.0 * Math.PI ), Math.Sin( 080.0 / 180.0 * Math.PI ), + Math.Cos( 120.0 / 180.0 * Math.PI ), Math.Sin( 120.0 / 180.0 * Math.PI ), + Math.Cos( 160.0 / 180.0 * Math.PI ), Math.Sin( 160.0 / 180.0 * Math.PI ), + Math.Cos( 200.0 / 180.0 * Math.PI ), Math.Sin( 200.0 / 180.0 * Math.PI ), + Math.Cos( 240.0 / 180.0 * Math.PI ), Math.Sin( 240.0 / 180.0 * Math.PI ), + Math.Cos( 280.0 / 180.0 * Math.PI ), Math.Sin( 280.0 / 180.0 * Math.PI ), + Math.Cos( 320.0 / 180.0 * Math.PI ), Math.Sin( 320.0 / 180.0 * Math.PI ), + }; + + public void Morph() + { + if ( m_TrueForm ) + return; + + m_TrueForm = true; + + Name = "the true harrower"; + BodyValue = 780; + Hue = 0x497; + + Hits = HitsMax; + Stam = StamMax; + Mana = ManaMax; + + ProcessDelta(); + + Say( 1049499 ); // Behold my true form! + + Map map = this.Map; + + if ( map != null ) + { + for ( int i = 0; i < m_Offsets.Length; i += 2 ) + { + double rx = m_Offsets[i]; + double ry = m_Offsets[i + 1]; + + int dist = 0; + bool ok = false; + int x = 0, y = 0, z = 0; + + while ( !ok && dist < 10 ) + { + int rdist = 10 + dist; + + x = this.X + (int)(rx * rdist); + y = this.Y + (int)(ry * rdist); + z = map.GetAverageZ( x, y ); + + if ( !(ok = map.CanFit( x, y, this.Z, 16, false, false ) ) ) + ok = map.CanFit( x, y, z, 16, false, false ); + + if ( dist >= 0 ) + dist = -(dist + 1); + else + dist = -(dist - 1); + } + + if ( !ok ) + continue; + + BaseCreature spawn = new HarrowerTentacles( this ); + + spawn.Team = this.Team; + + spawn.MoveToWorld( new Point3D( x, y, z ), map ); + + m_Tentacles.Add( spawn ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override int HitsMax{ get{ return m_TrueForm ? 65000 : 30000; } } + + [CommandProperty( AccessLevel.GameMaster )] + public override int ManaMax{ get{ return 5000; } } + + public Harrower( Serial serial ) : base( serial ) + { + m_Instances.Add( this ); + } + + public override void OnAfterDelete() + { + m_Instances.Remove( this ); + + base.OnAfterDelete(); + } + + public override bool DisallowAllMoves{ get{ return m_TrueForm; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_TrueForm ); + writer.Write( m_GateItem ); + writer.WriteMobileList( m_Tentacles ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_TrueForm = reader.ReadBool(); + m_GateItem = reader.ReadItem(); + m_Tentacles = reader.ReadMobileList(); + + m_Timer = new TeleportTimer( this ); + m_Timer.Start(); + + break; + } + } + } + + public void GivePowerScrolls() + { + List toGive = new List(); + List rights = BaseCreature.GetLootingRights( this.DamageEntries, this.HitsMax ); + + for ( int i = rights.Count - 1; i >= 0; --i ) + { + DamageStore ds = rights[i]; + + if ( ds.m_HasRight ) + toGive.Add( ds.m_Mobile ); + } + + if ( toGive.Count == 0 ) + return; + + // Randomize + for ( int i = 0; i < toGive.Count; ++i ) + { + int rand = Utility.Random( toGive.Count ); + Mobile hold = toGive[i]; + toGive[i] = toGive[rand]; + toGive[rand] = hold; + } + + for ( int i = 0; i < 16; ++i ) + { + int level; + double random = Utility.RandomDouble(); + + if ( 0.1 >= random ) + level = 25; + else if ( 0.25 >= random ) + level = 20; + else if ( 0.45 >= random ) + level = 15; + else if ( 0.70 >= random ) + level = 10; + else + level = 5; + + Mobile m = toGive[i % toGive.Count]; + + m.SendLocalizedMessage( 1049524 ); // You have received a scroll of power! + m.AddToBackpack( new StatCapScroll( 225 + level ) ); + + if ( m is PlayerMobile ) + { + PlayerMobile pm = (PlayerMobile)m; + + for ( int j = 0; j < pm.JusticeProtectors.Count; ++j ) + { + Mobile prot = (Mobile)pm.JusticeProtectors[j]; + + if ( prot.Map != m.Map || prot.Kills >= 5 || prot.Criminal || !JusticeVirtue.CheckMapRegion( m, prot ) ) + continue; + + int chance = 0; + + switch ( VirtueHelper.GetLevel( prot, VirtueName.Justice ) ) + { + case VirtueLevel.Seeker: chance = 60; break; + case VirtueLevel.Follower: chance = 80; break; + case VirtueLevel.Knight: chance = 100; break; + } + + if ( chance > Utility.Random( 100 ) ) + { + prot.SendLocalizedMessage( 1049368 ); // You have been rewarded for your dedication to Justice! + prot.AddToBackpack( new StatCapScroll( 225 + level ) ); + } + } + } + } + } + + public override bool OnBeforeDeath() + { + if ( m_TrueForm ) + { + List rights = BaseCreature.GetLootingRights( this.DamageEntries, this.HitsMax ); + + for ( int i = rights.Count - 1; i >= 0; --i ) + { + DamageStore ds = rights[i]; + + if ( ds.m_HasRight && ds.m_Mobile is PlayerMobile ) + PlayerMobile.ChampionTitleInfo.AwardHarrowerTitle( (PlayerMobile)ds.m_Mobile ); + } + + if ( !NoKillAwards ) + { + GivePowerScrolls(); + + Map map = this.Map; + + if ( map != null ) + { + for ( int x = -16; x <= 16; ++x ) + { + for ( int y = -16; y <= 16; ++y ) + { + double dist = Math.Sqrt(x*x+y*y); + + if ( dist <= 16 ) + new GoodiesTimer( map, X + x, Y + y ).Start(); + } + } + } + + for ( int i = 0; i < m_Tentacles.Count; ++i ) + { + Mobile m = (Mobile)m_Tentacles[i]; + + if ( !m.Deleted ) + m.Kill(); + } + + m_Tentacles.Clear(); + + if ( m_GateItem != null ) + m_GateItem.Delete(); + } + + return base.OnBeforeDeath(); + } + else + { + Morph(); + return false; + } + } + + private class TeleportTimer : Timer + { + private Mobile m_Owner; + + private static int[] m_Offsets = new int[] + { + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1 + }; + + public TeleportTimer( Mobile owner ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Owner = owner; + } + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + Map map = m_Owner.Map; + + if ( map == null ) + return; + + if ( 0.25 < Utility.RandomDouble() ) + return; + + Mobile toTeleport = null; + + foreach ( Mobile m in m_Owner.GetMobilesInRange( 16 ) ) + { + if ( m != m_Owner && m.Player && m_Owner.CanBeHarmful( m ) && m_Owner.CanSee( m ) ) + { + toTeleport = m; + break; + } + } + + if ( toTeleport != null ) + { + int offset = Utility.Random( 8 ) * 2; + + Point3D to = m_Owner.Location; + + for ( int i = 0; i < m_Offsets.Length; i += 2 ) + { + int x = m_Owner.X + m_Offsets[(offset + i) % m_Offsets.Length]; + int y = m_Owner.Y + m_Offsets[(offset + i + 1) % m_Offsets.Length]; + + if ( map.CanSpawnMobile( x, y, m_Owner.Z ) ) + { + to = new Point3D( x, y, m_Owner.Z ); + break; + } + else + { + int z = map.GetAverageZ( x, y ); + + if ( map.CanSpawnMobile( x, y, z ) ) + { + to = new Point3D( x, y, z ); + break; + } + } + } + + Mobile m = toTeleport; + + Point3D from = m.Location; + + m.Location = to; + + Server.Spells.SpellHelper.Turn( m_Owner, toTeleport ); + Server.Spells.SpellHelper.Turn( toTeleport, m_Owner ); + + m.ProcessDelta(); + + Effects.SendLocationParticles( EffectItem.Create( from, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( to, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + + m.PlaySound( 0x1FE ); + + m_Owner.Combatant = toTeleport; + } + } + } + + private class GoodiesTimer : Timer + { + private Map m_Map; + private int m_X, m_Y; + + public GoodiesTimer( Map map, int x, int y ) : base( TimeSpan.FromSeconds( Utility.RandomDouble() * 10.0 ) ) + { + m_Map = map; + m_X = x; + m_Y = y; + } + + protected override void OnTick() + { + int z = m_Map.GetAverageZ( m_X, m_Y ); + bool canFit = m_Map.CanFit( m_X, m_Y, z, 6, false, false ); + + for ( int i = -3; !canFit && i <= 3; ++i ) + { + canFit = m_Map.CanFit( m_X, m_Y, z + i, 6, false, false ); + + if ( canFit ) + z += i; + } + + if ( !canFit ) + return; + + Gold g = new Gold( 750, 1250 ); + + g.MoveToWorld( new Point3D( m_X, m_Y, z ), m_Map ); + + if ( 0.5 >= Utility.RandomDouble() ) + { + switch ( Utility.Random( 3 ) ) + { + case 0: // Fire column + { + Effects.SendLocationParticles( EffectItem.Create( g.Location, g.Map, EffectItem.DefaultDuration ), 0x3709, 10, 30, 5052 ); + Effects.PlaySound( g, g.Map, 0x208 ); + + break; + } + case 1: // Explosion + { + Effects.SendLocationParticles( EffectItem.Create( g.Location, g.Map, EffectItem.DefaultDuration ), 0x36BD, 20, 10, 5044 ); + Effects.PlaySound( g, g.Map, 0x307 ); + + break; + } + case 2: // Ball of fire + { + Effects.SendLocationParticles( EffectItem.Create( g.Location, g.Map, EffectItem.DefaultDuration ), 0x36FE, 10, 10, 5052 ); + + break; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/HarrowerTentacles.cs b/Scripts/Mobiles/Special/HarrowerTentacles.cs new file mode 100644 index 000000000..fe2bff1c8 --- /dev/null +++ b/Scripts/Mobiles/Special/HarrowerTentacles.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + [CorpseName( "a tentacles corpse" )] + public class HarrowerTentacles : BaseCreature + { + private Mobile m_Harrower; + + private DrainTimer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Harrower + { + get + { + return m_Harrower; + } + set + { + m_Harrower = value; + } + } + + [Constructable] + public HarrowerTentacles() : this( null ) + { + } + + public HarrowerTentacles( Mobile harrower ) : base( AIType.AI_Melee, FightMode.Closest, 10, 1, 0.2, 0.4 ) + { + m_Harrower = harrower; + + Name = "tentacles of the harrower"; + Body = 129; + + SetStr( 901, 1000 ); + SetDex( 126, 140 ); + SetInt( 1001, 1200 ); + + SetHits( 541, 600 ); + + SetDamage( 13, 20 ); + + SetDamageType( ResistanceType.Physical, 20 ); + SetDamageType( ResistanceType.Fire, 20 ); + SetDamageType( ResistanceType.Cold, 20 ); + SetDamageType( ResistanceType.Poison, 20 ); + SetDamageType( ResistanceType.Energy, 20 ); + + SetResistance( ResistanceType.Physical, 55, 65 ); + SetResistance( ResistanceType.Fire, 35, 45 ); + SetResistance( ResistanceType.Cold, 35, 45 ); + SetResistance( ResistanceType.Poison, 35, 45 ); + SetResistance( ResistanceType.Energy, 35, 45 ); + + SetSkill( SkillName.Meditation, 100.0 ); + SetSkill( SkillName.MagicResist, 120.1, 140.0 ); + SetSkill( SkillName.Swords, 90.1, 100.0 ); + SetSkill( SkillName.Tactics, 90.1, 100.0 ); + SetSkill( SkillName.Wrestling, 90.1, 100.0 ); + + Fame = 15000; + Karma = -15000; + + VirtualArmor = 60; + + m_Timer = new DrainTimer( this ); + m_Timer.Start(); + + PackReg( 50 ); + PackNecroReg( 15, 75 ); + } + + public override void CheckReflect( Mobile caster, ref bool reflect ) + { + reflect = true; + } + + public override int GetIdleSound() + { + return 0x101; + } + + public override int GetAngerSound() + { + return 0x5E; + } + + public override int GetDeathSound() + { + return 0x1C2; + } + + public override int GetAttackSound() + { + return -1; // unknown + } + + public override int GetHurtSound() + { + return 0x289; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.FilthyRich, 2 ); + AddLoot( LootPack.MedScrolls, 3 ); + AddLoot( LootPack.HighScrolls, 2 ); + } + + public override bool AutoDispel{ get{ return true; } } + public override bool Unprovokable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override bool DisallowAllMoves{ get{ return true; } } + + public HarrowerTentacles( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Harrower ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Harrower = reader.ReadMobile(); + + m_Timer = new DrainTimer( this ); + m_Timer.Start(); + + break; + } + } + } + + public override void OnAfterDelete() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + base.OnAfterDelete(); + } + + private class DrainTimer : Timer + { + private HarrowerTentacles m_Owner; + + public DrainTimer( HarrowerTentacles owner ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Owner = owner; + Priority = TimerPriority.TwoFiftyMS; + } + + private static ArrayList m_ToDrain = new ArrayList(); + + protected override void OnTick() + { + if ( m_Owner.Deleted ) + { + Stop(); + return; + } + + foreach ( Mobile m in m_Owner.GetMobilesInRange( 9 ) ) + { + if ( m == m_Owner || m == m_Owner.Harrower || !m_Owner.CanBeHarmful( m ) ) + continue; + + if ( m is BaseCreature ) + { + BaseCreature bc = m as BaseCreature; + + if ( bc.Controlled || bc.Summoned ) + m_ToDrain.Add( m ); + } + else if ( m.Player ) + { + m_ToDrain.Add( m ); + } + } + + foreach ( Mobile m in m_ToDrain ) + { + m_Owner.DoHarmful( m ); + + m.FixedParticles( 0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist ); + m.PlaySound( 0x1F1 ); + + int drain = Utility.RandomMinMax( 14, 30 ); + + m_Owner.Hits += drain; + + if ( m_Owner.Harrower != null ) + m_Owner.Harrower.Hits += drain; + + m.Damage( drain, m_Owner ); + } + + m_ToDrain.Clear(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/LordOaks.cs b/Scripts/Mobiles/Special/LordOaks.cs new file mode 100644 index 000000000..b39df3d65 --- /dev/null +++ b/Scripts/Mobiles/Special/LordOaks.cs @@ -0,0 +1,221 @@ +using System; +using Server; +using Server.Items; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class LordOaks : BaseChampion + { + private Mobile m_Queen; + private bool m_SpawnedQueen; + + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Enlightenment; } } + + [Constructable] + public LordOaks() : base( AIType.AI_Mage, FightMode.Evil ) + { + Body = 175; + Name = "Lord Oaks"; + + SetStr( 403, 850 ); + SetDex( 101, 150 ); + SetInt( 503, 800 ); + + SetHits( 3000 ); + SetStam( 202, 400 ); + + SetDamage( 21, 33 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Fire, 25 ); + + SetResistance( ResistanceType.Physical, 85, 90 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 80, 90 ); + SetResistance( ResistanceType.Energy, 80, 90 ); + + SetSkill( SkillName.Anatomy, 75.1, 100.0 ); + SetSkill( SkillName.EvalInt, 120.1, 130.0 ); + SetSkill( SkillName.Magery, 120.0 ); + SetSkill( SkillName.Meditation, 120.1, 130.0 ); + SetSkill( SkillName.MagicResist, 100.5, 150.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + SetSkill( SkillName.Wrestling, 100.0 ); + + Fame = 22500; + Karma = 22500; + + VirtualArmor = 100; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 5 ); + } + + public override bool AutoDispel{ get{ return true; } } + + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + + public void SpawnPixies( Mobile target ) + { + Map map = this.Map; + + if ( map == null ) + return; + + this.Say( 1042154 ); // You shall never defeat me as long as I have my queen! + + int newPixies = Utility.RandomMinMax( 3, 6 ); + + for ( int i = 0; i < newPixies; ++i ) + { + Pixie pixie = new Pixie(); + + pixie.Team = this.Team; + pixie.FightMode = FightMode.Closest; + + bool validLocation = false; + Point3D loc = this.Location; + + 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 ); + } + + pixie.MoveToWorld( loc, map ); + pixie.Combatant = target; + } + } + + public override int GetAngerSound() + { + return 0x2F8; + } + + public override int GetIdleSound() + { + return 0x2F8; + } + + public override int GetAttackSound() + { + return Utility.Random( 0x2F5, 2 ); + } + + public override int GetHurtSound() + { + return 0x2F9; + } + + public override int GetDeathSound() + { + return 0x2F7; + } + + public void CheckQueen() + { + if ( !m_SpawnedQueen ) + { + this.Say( 1042153 ); // Come forth my queen! + + m_Queen = new Silvani(); + + ((BaseCreature)m_Queen).Team = this.Team; + + m_Queen.MoveToWorld( this.Location, this.Map ); + + m_SpawnedQueen = true; + } + else if ( m_Queen != null && m_Queen.Deleted ) + { + m_Queen = null; + } + } + + public override void AlterDamageScalarFrom( Mobile caster, ref double scalar ) + { + CheckQueen(); + + if ( m_Queen != null ) + { + scalar *= 0.1; + + if ( 0.1 >= Utility.RandomDouble() ) + SpawnPixies( caster ); + } + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + defender.Damage( Utility.Random( 20, 10 ), this ); + defender.Stam -= Utility.Random( 20, 10 ); + defender.Mana -= Utility.Random( 20, 10 ); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + CheckQueen(); + + if ( m_Queen != null && 0.1 >= Utility.RandomDouble() ) + SpawnPixies( attacker ); + + attacker.Damage( Utility.Random( 20, 10 ), this ); + attacker.Stam -= Utility.Random( 20, 10 ); + attacker.Mana -= Utility.Random( 20, 10 ); + } + + public LordOaks( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Queen ); + writer.Write( m_SpawnedQueen ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Queen = reader.ReadMobile(); + m_SpawnedQueen = reader.ReadBool(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Mephitis.cs b/Scripts/Mobiles/Special/Mephitis.cs new file mode 100644 index 000000000..e14286254 --- /dev/null +++ b/Scripts/Mobiles/Special/Mephitis.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class Mephitis : BaseChampion + { + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Venom; } } + + [Constructable] + public Mephitis() : base( AIType.AI_Melee ) + { + Body = 173; + Name = "Mephitis"; + + BaseSoundID = 0x183; + + SetStr( 505, 1000 ); + SetDex( 102, 300 ); + SetInt( 402, 600 ); + + SetHits( 3000 ); + SetStam( 105, 600 ); + + SetDamage( 21, 33 ); + + SetDamageType( ResistanceType.Physical, 50 ); + SetDamageType( ResistanceType.Poison, 50 ); + + SetResistance( ResistanceType.Physical, 75, 80 ); + SetResistance( ResistanceType.Fire, 60, 70 ); + SetResistance( ResistanceType.Cold, 60, 70 ); + SetResistance( ResistanceType.Poison, 100 ); + SetResistance( ResistanceType.Energy, 60, 70 ); + + SetSkill( SkillName.MagicResist, 70.7, 140.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 80; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 4 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override Poison HitPoison{ get{ return Poison.Lethal; } } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + // TODO: Web ability + } + + public Mephitis( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Neira.cs b/Scripts/Mobiles/Special/Neira.cs new file mode 100644 index 000000000..41ed86639 --- /dev/null +++ b/Scripts/Mobiles/Special/Neira.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class Neira : BaseChampion + { + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Death; } } + + [Constructable] + public Neira() : base( AIType.AI_Mage ) + { + Name = "Neira"; + Title = "the necromancer"; + Body = 401; + Hue = 0x83EC; + + SetStr( 305, 425 ); + SetDex( 72, 150 ); + SetInt( 505, 750 ); + + SetHits( 4800 ); + SetStam( 102, 300 ); + + SetDamage( 25, 35 ); + + SetDamageType( ResistanceType.Physical, 100 ); + + SetResistance( ResistanceType.Physical, 25, 30 ); + SetResistance( ResistanceType.Fire, 35, 45 ); + SetResistance( ResistanceType.Cold, 50, 60 ); + SetResistance( ResistanceType.Poison, 30, 40 ); + SetResistance( ResistanceType.Energy, 20, 30 ); + + SetSkill( SkillName.EvalInt, 120.0 ); + SetSkill( SkillName.Magery, 120.0 ); + SetSkill( SkillName.Meditation, 120.0 ); + SetSkill( SkillName.MagicResist, 150.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 30; + Female = true; + + Item shroud = new HoodedShroudOfShadows(); + + shroud.Movable = false; + + AddItem( shroud ); + + Scimitar weapon = new Scimitar(); + + weapon.Skill = SkillName.Wrestling; + weapon.Hue = 38; + weapon.Movable = false; + + AddItem( weapon ); + + //new SkeletalMount().Rider = this; + AddItem( new VirtualMountItem( this ) ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 3 ); + AddLoot( LootPack.Meager ); + } + + public override bool OnBeforeDeath() + { + IMount mount = this.Mount; + + if ( mount != null ) + mount.Rider = null; + + if ( mount is Mobile ) + ((Mobile)mount).Delete(); + + return base.OnBeforeDeath(); + } + + private bool m_SpeedBoost; + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + CheckSpeedBoost(); + base.OnDamage( amount, from, willKill ); + } + + private const double SpeedBoostScalar = 1.2; + + private void CheckSpeedBoost() + { + if( Hits < (HitsMax / 4 ) ) + { + if( !m_SpeedBoost ) + { + ActiveSpeed /= SpeedBoostScalar; + PassiveSpeed /= SpeedBoostScalar; + m_SpeedBoost = true; + } + } + else if( m_SpeedBoost ) + { + ActiveSpeed *= SpeedBoostScalar; + PassiveSpeed *= SpeedBoostScalar; + m_SpeedBoost = false; + } + } + + private class VirtualMount : IMount + { + private VirtualMountItem m_Item; + + public Mobile Rider + { + get { return m_Item.Rider; } + set { } + } + + public VirtualMount( VirtualMountItem item ) + { + m_Item = item; + } + + public virtual void OnRiderDamaged( int amount, Mobile from, bool willKill ) + { + } + } + + private class VirtualMountItem : Item, IMountItem + { + private Mobile m_Rider; + private VirtualMount m_Mount; + + public Mobile Rider { get { return m_Rider; } } + + public VirtualMountItem( Mobile mob ) + : base( 0x3EBB ) + { + Layer = Layer.Mount; + + Movable = false; + + m_Rider = mob; + m_Mount = new VirtualMount( this ); + } + + public IMount Mount + { + get { return m_Mount; } + } + + public VirtualMountItem( Serial serial ) + : base( serial ) + { + m_Mount = new VirtualMount( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + + writer.Write( (Mobile)m_Rider ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Rider = reader.ReadMobile(); + + if( m_Rider == null ) + Delete(); + } + } + + public override bool AlwaysMurderer{ get{ return true; } } + public override bool BardImmune{ get{ return !Core.SE; } } + public override bool Unprovokable{ get{ return Core.SE; } } + public override bool Uncalmable{ get{ return Core.SE; } } + public override Poison PoisonImmune{ get{ return Poison.Deadly; } } + + public override bool ShowFameTitle{ get{ return false; } } + public override bool ClickTitle{ get{ return false; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.1 >= Utility.RandomDouble() ) // 10% chance to drop or throw an unholy bone + AddUnholyBone( defender, 0.25 ); + + CheckSpeedBoost(); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( 0.1 >= Utility.RandomDouble() ) // 10% chance to drop or throw an unholy bone + AddUnholyBone( attacker, 0.25 ); + } + + public override void AlterDamageScalarFrom( Mobile caster, ref double scalar ) + { + base.AlterDamageScalarFrom( caster, ref scalar ); + + if ( 0.1 >= Utility.RandomDouble() ) // 10% chance to throw an unholy bone + AddUnholyBone( caster, 1.0 ); + } + + public void AddUnholyBone( Mobile target, double chanceToThrow ) + { + if ( chanceToThrow >= Utility.RandomDouble() ) + { + Direction = GetDirectionTo( target ); + MovingEffect( target, 0xF7E, 10, 1, true, false, 0x496, 0 ); + new DelayTimer( this, target ).Start(); + } + else + { + new UnholyBone().MoveToWorld( Location, Map ); + } + } + + private class DelayTimer : Timer + { + private Mobile m_Mobile; + private Mobile m_Target; + + public DelayTimer( Mobile m, Mobile target ) : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_Target = target; + } + + protected override void OnTick() + { + if ( m_Mobile.CanBeHarmful( m_Target ) ) + { + m_Mobile.DoHarmful( m_Target ); + AOS.Damage( m_Target, m_Mobile, Utility.RandomMinMax( 10, 20 ), 100, 0, 0, 0, 0 ); + new UnholyBone().MoveToWorld( m_Target.Location, m_Target.Map ); + } + } + } + + public Neira( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + writer.Write( m_SpeedBoost ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch( version ) + { + case 1: + { + m_SpeedBoost = reader.ReadBool(); + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/OrderGuard.cs b/Scripts/Mobiles/Special/OrderGuard.cs new file mode 100644 index 000000000..a152c04d7 --- /dev/null +++ b/Scripts/Mobiles/Special/OrderGuard.cs @@ -0,0 +1,41 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Guilds; + +namespace Server.Mobiles +{ + public class OrderGuard : BaseShieldGuard + { + public override int Keyword{ get{ return 0x21; } } // *order shield* + public override BaseShield Shield{ get{ return new OrderShield(); } } + public override int SignupNumber{ get{ return 1007141; } } // Sign up with a guild of order if thou art interested. + public override GuildType Type{ get{ return GuildType.Order; } } + + public override bool BardImmune{ get{ return true; } } + + [Constructable] + public OrderGuard() + { + } + + public OrderGuard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Rikktor.cs b/Scripts/Mobiles/Special/Rikktor.cs new file mode 100644 index 000000000..78991b8cc --- /dev/null +++ b/Scripts/Mobiles/Special/Rikktor.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Spells; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class Rikktor : BaseChampion + { + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Power; } } + + [Constructable] + public Rikktor() : base( AIType.AI_Melee ) + { + Body = 172; + Name = "Rikktor"; + + SetStr( 701, 900 ); + SetDex( 201, 350 ); + SetInt( 51, 100 ); + + SetHits( 3000 ); + SetStam( 203, 650 ); + + SetDamage( 28, 55 ); + + SetDamageType( ResistanceType.Physical, 25 ); + SetDamageType( ResistanceType.Fire, 50 ); + SetDamageType( ResistanceType.Energy, 25 ); + + SetResistance( ResistanceType.Physical, 80, 90 ); + SetResistance( ResistanceType.Fire, 80, 90 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 80, 90 ); + SetResistance( ResistanceType.Energy, 80, 90 ); + + SetSkill( SkillName.Anatomy, 100.0 ); + SetSkill( SkillName.MagicResist, 140.2, 160.0 ); + SetSkill( SkillName.Tactics, 100.0 ); + + Fame = 22500; + Karma = -22500; + + VirtualArmor = 130; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 4 ); + } + + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override ScaleType ScaleType{ get{ return ScaleType.All; } } + public override int Scales{ get{ return 20; } } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.2 >= Utility.RandomDouble() ) + Earthquake(); + } + + public void Earthquake() + { + Map map = this.Map; + + if ( map == null ) + return; + + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 8 ) ) + { + if ( m == this || !CanBeHarmful( m ) ) + continue; + + if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != this.Team) ) + targets.Add( m ); + else if ( m.Player ) + targets.Add( m ); + } + + PlaySound( 0x2F3 ); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + double damage = m.Hits * 0.6; + + if ( damage < 10.0 ) + damage = 10.0; + else if ( damage > 75.0 ) + damage = 75.0; + + DoHarmful( m ); + + AOS.Damage( m, this, (int)damage, 100, 0, 0, 0, 0 ); + + if ( m.Alive && m.Body.IsHuman && !m.Mounted ) + m.Animate( 20, 7, 1, true, false, 0 ); // take hit + } + } + + public override int GetAngerSound() + { + return Utility.Random( 0x2CE, 2 ); + } + + public override int GetIdleSound() + { + return 0x2D2; + } + + public override int GetAttackSound() + { + return Utility.Random( 0x2C7, 5 ); + } + + public override int GetHurtSound() + { + return 0x2D1; + } + + public override int GetDeathSound() + { + return 0x2CC; + } + + public Rikktor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Semidar.cs b/Scripts/Mobiles/Special/Semidar.cs new file mode 100644 index 000000000..8e7b94274 --- /dev/null +++ b/Scripts/Mobiles/Special/Semidar.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class Semidar : BaseChampion + { + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Pain; } } + + [Constructable] + public Semidar() : base( AIType.AI_Mage ) + { + Name = "Semidar"; + Body = 174; + BaseSoundID = 0x4B0; + + SetStr( 502, 600 ); + SetDex( 102, 200 ); + SetInt( 601, 750 ); + + SetHits( 1500 ); + SetStam( 103, 250 ); + + SetDamage( 29, 35 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Fire, 25 ); + + SetResistance( ResistanceType.Physical, 20, 30 ); + SetResistance( ResistanceType.Fire, 50, 60 ); + SetResistance( ResistanceType.Cold, 20, 30 ); + SetResistance( ResistanceType.Poison, 20, 30 ); + SetResistance( ResistanceType.Energy, 10, 20 ); + + SetSkill( SkillName.EvalInt, 95.1, 100.0 ); + SetSkill( SkillName.Magery, 90.1, 105.0 ); + SetSkill( SkillName.Meditation, 95.1, 100.0 ); + SetSkill( SkillName.MagicResist, 120.2, 140.0 ); + SetSkill( SkillName.Tactics, 90.1, 105.0 ); + SetSkill( SkillName.Wrestling, 90.1, 105.0 ); + + Fame = 24000; + Karma = -24000; + + VirtualArmor = 20; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 4 ); + AddLoot( LootPack.FilthyRich ); + } + + public override bool Unprovokable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + + public override void CheckReflect( Mobile caster, ref bool reflect ) + { + if ( caster.Body.IsMale ) + reflect = true; // Always reflect if caster isn't female + } + + public override void AlterDamageScalarFrom( Mobile caster, ref double scalar ) + { + if ( caster.Body.IsMale ) + scalar = 20; // Male bodies always reflect.. damage scaled 20x + } + + public void DrainLife() + { + ArrayList list = new ArrayList(); + + foreach ( Mobile m in this.GetMobilesInRange( 2 ) ) + { + if ( m == this || !CanBeHarmful( m ) ) + continue; + + if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != this.Team) ) + list.Add( m ); + else if ( m.Player ) + list.Add( m ); + } + + foreach ( Mobile m in list ) + { + DoHarmful( m ); + + m.FixedParticles( 0x374A, 10, 15, 5013, 0x496, 0, EffectLayer.Waist ); + m.PlaySound( 0x231 ); + + m.SendMessage( "You feel the life drain out of you!" ); + + int toDrain = Utility.RandomMinMax( 10, 40 ); + + Hits += toDrain; + m.Damage( toDrain, this ); + } + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + if ( 0.25 >= Utility.RandomDouble() ) + DrainLife(); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( 0.25 >= Utility.RandomDouble() ) + DrainLife(); + } + + public Semidar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int) 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Special/Serado.cs b/Scripts/Mobiles/Special/Serado.cs new file mode 100644 index 000000000..4b014fda6 --- /dev/null +++ b/Scripts/Mobiles/Special/Serado.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Engines.CannedEvil; + +namespace Server.Mobiles +{ + public class Serado : BaseChampion + { + public override ChampionSkullType SkullType{ get{ return ChampionSkullType.Power; } } + + public override WeaponAbility GetWeaponAbility() + { + return WeaponAbility.DoubleStrike; + } + + [Constructable] + public Serado() : base( AIType.AI_Melee ) + { + Name = "Serado"; + Title = "the awakened"; + + Body = 249; + Hue = 0x96C; + + SetStr( 1000 ); + SetDex( 150 ); + SetInt( 300 ); + + SetHits( 9000 ); + SetMana( 300 ); + + SetDamage( 29, 35 ); + + SetDamageType( ResistanceType.Physical, 70 ); + SetDamageType( ResistanceType.Poison, 20 ); + SetDamageType( ResistanceType.Energy, 10 ); + + SetResistance( ResistanceType.Physical, 30 ); + SetResistance( ResistanceType.Fire, 60 ); + SetResistance( ResistanceType.Cold, 60 ); + SetResistance( ResistanceType.Poison, 90 ); + SetResistance( ResistanceType.Energy, 50 ); + + SetSkill( SkillName.MagicResist, 120.0 ); + SetSkill( SkillName.Tactics, 120.0 ); + SetSkill( SkillName.Wrestling, 70.0 ); + SetSkill( SkillName.Poisoning, 150.0 ); + + Fame = 22500; + Karma = -22500; + + PackItem( Engines.Plants.Seed.RandomBonsaiSeed() ); + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 4 ); + AddLoot( LootPack.FilthyRich ); + AddLoot( LootPack.Gems, 6 ); + } + + public override int TreasureMapLevel{ get{ return 5; } } + + public override Poison HitPoison{ get{ return Poison.Lethal; } } + public override Poison PoisonImmune{ get{ return Poison.Lethal; } } + public override double HitPoisonChance{ get{ return 0.8; } } + + public override int Feathers{ get{ return 30; } } + + public override bool ShowFameTitle{ get{ return false; } } + public override bool ClickTitle{ get{ return false; } } + + // TODO: Hit Lightning Area + + public override void OnDamagedBySpell( Mobile attacker ) + { + base.OnDamagedBySpell( attacker ); + + ScaleResistances(); + DoCounter( attacker ); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + ScaleResistances(); + DoCounter( attacker ); + } + + private void ScaleResistances() + { + double hitsLost = (HitsMax - Hits) / (double)HitsMax; + + SetResistance( ResistanceType.Physical, 30 + (int)(hitsLost * ( 95 - 30 )) ); + SetResistance( ResistanceType.Fire, 60 + (int)(hitsLost * ( 95 - 60 )) ); + SetResistance( ResistanceType.Cold, 60 + (int)(hitsLost * ( 95 - 60 )) ); + SetResistance( ResistanceType.Poison, 90 + (int)(hitsLost * ( 95 - 90 )) ); + SetResistance( ResistanceType.Energy, 50 + (int)(hitsLost * ( 95 - 50 )) ); + } + + private void DoCounter( Mobile attacker ) + { + if ( attacker is BaseCreature && ((BaseCreature)attacker).BardProvoked ) + return; + + if ( 0.2 > Utility.RandomDouble() ) + { + /* Counterattack with Hit Poison Area + * 20-25 damage, unresistable + * Lethal poison, 100% of the time + * Particle effect: Type: "2" From: "0x4061A107" To: "0x0" ItemId: "0x36BD" ItemIdName: "explosion" FromLocation: "(296 615, 17)" ToLocation: "(296 615, 17)" Speed: "1" Duration: "10" FixedDirection: "True" Explode: "False" Hue: "0xA6" RenderMode: "0x0" Effect: "0x1F78" ExplodeEffect: "0x1" ExplodeSound: "0x0" Serial: "0x4061A107" Layer: "255" Unknown: "0x0" + * Doesn't work on provoked monsters + */ + + Mobile target = null; + + if ( attacker is BaseCreature ) + { + Mobile m = ((BaseCreature)attacker).GetMaster(); + + if ( m != null ) + target = m; + } + + if ( target == null || !target.InRange( this, 25 ) ) + target = attacker; + + this.Animate( 10, 4, 1, true, false, 0 ); + + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in target.GetMobilesInRange( 8 ) ) + { + if ( m == this || !CanBeHarmful( m ) ) + continue; + + if ( m is BaseCreature && (((BaseCreature)m).Controlled || ((BaseCreature)m).Summoned || ((BaseCreature)m).Team != this.Team) ) + targets.Add( m ); + else if ( m.Player ) + targets.Add( m ); + } + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + DoHarmful( m ); + + AOS.Damage( m, this, Utility.RandomMinMax( 20, 25 ), true, 0, 0, 0, 100, 0 ); + + m.FixedParticles( 0x36BD, 1, 10, 0x1F78, 0xA6, 0, (EffectLayer)255 ); + m.ApplyPoison( this, Poison.Lethal ); + } + } + } + + public Serado( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Special/ServantOfSemidar.cs b/Scripts/Mobiles/Special/ServantOfSemidar.cs new file mode 100644 index 000000000..2eb381309 --- /dev/null +++ b/Scripts/Mobiles/Special/ServantOfSemidar.cs @@ -0,0 +1,49 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class ServantOfSemidar : BaseCreature + { + [Constructable] + public ServantOfSemidar() : base( AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4 ) + { + Name = "a servant of Semidar"; + Body = 0x26; + } + + public override bool DisallowAllMoves{ get{ return true; } } + + public override bool InitialInnocent{ get{ return true; } } + + public override bool CanBeDamaged() + { + return false; + } + + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + + list.Add( 1005494 ); // enslaved + } + + public ServantOfSemidar( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Silvani.cs b/Scripts/Mobiles/Special/Silvani.cs new file mode 100644 index 000000000..3cc360002 --- /dev/null +++ b/Scripts/Mobiles/Special/Silvani.cs @@ -0,0 +1,137 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class Silvani : BaseCreature + { + [Constructable] + public Silvani() : base( AIType.AI_Mage, FightMode.Evil, 18, 1, 0.1, 0.2 ) + { + Name = "Silvani"; + Body = 176; + BaseSoundID = 0x467; + + SetStr( 253, 400 ); + SetDex( 157, 850 ); + SetInt( 503, 800 ); + + SetHits( 600 ); + + SetDamage( 27, 38 ); + + SetDamageType( ResistanceType.Physical, 75 ); + SetDamageType( ResistanceType.Cold, 25 ); + + SetResistance( ResistanceType.Physical, 45, 55 ); + SetResistance( ResistanceType.Fire, 30, 40 ); + SetResistance( ResistanceType.Cold, 30, 40 ); + SetResistance( ResistanceType.Poison, 40, 50 ); + SetResistance( ResistanceType.Energy, 40, 50 ); + + SetSkill( SkillName.EvalInt, 100.0 ); + SetSkill( SkillName.Magery, 97.6, 107.5 ); + SetSkill( SkillName.Meditation, 100.0 ); + SetSkill( SkillName.MagicResist, 100.5, 150.0 ); + SetSkill( SkillName.Tactics, 97.6, 100.0 ); + SetSkill( SkillName.Wrestling, 97.6, 100.0 ); + + Fame = 20000; + Karma = 20000; + + VirtualArmor = 50; + } + + public override void GenerateLoot() + { + AddLoot( LootPack.UltraRich, 2 ); + } + + public override OppositionGroup OppositionGroup + { + get{ return OppositionGroup.FeyAndUndead; } + } + + public override bool Unprovokable{ get{ return true; } } + public override Poison PoisonImmune{ get{ return Poison.Regular; } } + public override int TreasureMapLevel{ get{ return 5; } } + + public void SpawnPixies( Mobile target ) + { + Map map = this.Map; + + if ( map == null ) + return; + + int newPixies = Utility.RandomMinMax( 3, 6 ); + + for ( int i = 0; i < newPixies; ++i ) + { + Pixie pixie = new Pixie(); + + pixie.Team = this.Team; + pixie.FightMode = FightMode.Closest; + + bool validLocation = false; + Point3D loc = this.Location; + + 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 ); + } + + pixie.MoveToWorld( loc, map ); + pixie.Combatant = target; + } + } + + public override void AlterDamageScalarFrom( Mobile caster, ref double scalar ) + { + if ( 0.1 >= Utility.RandomDouble() ) + SpawnPixies( caster ); + } + + public override void OnGaveMeleeAttack( Mobile defender ) + { + base.OnGaveMeleeAttack( defender ); + + defender.Damage( Utility.Random( 20, 10 ), this ); + defender.Stam -= Utility.Random( 20, 10 ); + defender.Mana -= Utility.Random( 20, 10 ); + } + + public override void OnGotMeleeAttack( Mobile attacker ) + { + base.OnGotMeleeAttack( attacker ); + + if ( 0.1 >= Utility.RandomDouble() ) + SpawnPixies( attacker ); + } + + public Silvani( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Special/Wanderer.cs b/Scripts/Mobiles/Special/Wanderer.cs new file mode 100644 index 000000000..ef7a7c969 --- /dev/null +++ b/Scripts/Mobiles/Special/Wanderer.cs @@ -0,0 +1,69 @@ +using System; +using Server; + +namespace Server.Mobiles +{ + public class Wanderer : Mobile + { + private Timer m_Timer; + + [Constructable] + public Wanderer() + { + this.Name = "Me"; + this.Body = 0x1; + this.AccessLevel = AccessLevel.Counselor; + + m_Timer = new InternalTimer( this ); + m_Timer.Start(); + } + + public Wanderer( Serial serial ) : base( serial ) + { + m_Timer = new InternalTimer( this ); + m_Timer.Start(); + } + + public override void OnDelete() + { + m_Timer.Stop(); + + base.OnDelete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + private class InternalTimer : Timer + { + private Wanderer m_Owner; + private int m_Count = 0; + + public InternalTimer( Wanderer owner ) : base( TimeSpan.FromSeconds( 0.1 ), TimeSpan.FromSeconds( 0.1 ) ) + { + m_Owner = owner; + } + + protected override void OnTick() + { + if ( (m_Count++ & 0x3) == 0 ) + { + m_Owner.Direction = (Direction)(Utility.Random( 8 ) | 0x80); + } + + m_Owner.Move( m_Owner.Direction ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Actor.cs b/Scripts/Mobiles/Townfolk/Actor.cs new file mode 100644 index 000000000..b7529cb83 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Actor.cs @@ -0,0 +1,69 @@ +using System; +using Server.Items; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + public class Actor : BaseCreature + { + [Constructable] + public Actor () : base( AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4 ) + { + InitStats( 31, 41, 51 ); + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + AddItem( new FancyDress( Utility.RandomDyedHue() ) ); + Title = "the actress"; + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + AddItem( new LongPants( Utility.RandomNeutralHue() ) ); + AddItem( new FancyShirt( Utility.RandomDyedHue() ) ); + Title = "the actor"; + } + + AddItem( new Boots( Utility.RandomNeutralHue() ) ); + + Utility.AssignRandomHair( this ); + + Container pack = new Backpack(); + + pack.DropItem( new Gold( 250, 300 ) ); + + pack.Movable = false; + + AddItem( pack ); + } + + + public override bool ClickTitle{ get{ return false; } } + + public Actor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/Artist.cs b/Scripts/Mobiles/Townfolk/Artist.cs new file mode 100644 index 000000000..ca0307916 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Artist.cs @@ -0,0 +1,74 @@ +using System; +using Server.Items; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + public class Artist : BaseCreature + { + public override bool CanTeach { get { return true; } } + + [Constructable] + public Artist() + : base( AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4 ) + { + InitStats( 31, 41, 51 ); + + SetSkill( SkillName.Healing, 36, 68 ); + + + SpeechHue = Utility.RandomDyedHue(); + Title = "the artist"; + Hue = Utility.RandomSkinHue(); + + + if( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + } + AddItem( new Doublet( Utility.RandomDyedHue() ) ); + AddItem( new Sandals( Utility.RandomNeutralHue() ) ); + AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); + AddItem( new HalfApron( Utility.RandomDyedHue() ) ); + + Utility.AssignRandomHair( this ); + + Container pack = new Backpack(); + + pack.DropItem( new Gold( 250, 300 ) ); + + pack.Movable = false; + + AddItem( pack ); + } + + public override bool ClickTitle { get { return false; } } + + + public Artist( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/Banker.cs b/Scripts/Mobiles/Townfolk/Banker.cs new file mode 100644 index 000000000..4cb607990 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Banker.cs @@ -0,0 +1,408 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.ContextMenus; +using Server.Misc; +using Server.Network; + +namespace Server.Mobiles +{ + public class Banker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.MerchantsGuild; } } + + [Constructable] + public Banker() : base( "the banker" ) + { + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBBanker() ); + } + + public static int GetBalance( Mobile from ) + { + Item[] gold, checks; + + return GetBalance( from, out gold, out checks ); + } + + public static int GetBalance( Mobile from, out Item[] gold, out Item[] checks ) + { + int balance = 0; + + Container bank = from.BankBox; + + if ( bank != null ) + { + gold = bank.FindItemsByType( typeof( Gold ) ); + checks = bank.FindItemsByType( typeof( BankCheck ) ); + + for ( int i = 0; i < gold.Length; ++i ) + balance += gold[i].Amount; + + for ( int i = 0; i < checks.Length; ++i ) + balance += ((BankCheck)checks[i]).Worth; + } + else + { + gold = checks = new Item[0]; + } + + return balance; + } + + public static bool Withdraw( Mobile from, int amount ) + { + Item[] gold, checks; + int balance = GetBalance( from, out gold, out checks ); + + if ( balance < amount ) + return false; + + for ( int i = 0; amount > 0 && i < gold.Length; ++i ) + { + if ( gold[i].Amount <= amount ) + { + amount -= gold[i].Amount; + gold[i].Delete(); + } + else + { + gold[i].Amount -= amount; + amount = 0; + } + } + + for ( int i = 0; amount > 0 && i < checks.Length; ++i ) + { + BankCheck check = (BankCheck)checks[i]; + + if ( check.Worth <= amount ) + { + amount -= check.Worth; + check.Delete(); + } + else + { + check.Worth -= amount; + amount = 0; + } + } + + return true; + } + + public static bool Deposit( Mobile from, int amount ) + { + BankBox box = from.BankBox; + if ( box == null ) + return false; + + ArrayList items = new ArrayList(); + + while ( amount > 0 ) + { + Item item; + if ( amount < 5000 ) + { + item = new Gold( amount ); + amount = 0; + } + else if ( amount <= 1000000 ) + { + item = new BankCheck( amount ); + amount = 0; + } + else + { + item = new BankCheck( 1000000 ); + amount -= 1000000; + } + + if ( box.TryDropItem( from, item, false ) ) + { + items.Add( item ); + } + else + { + item.Delete(); + foreach ( Item curItem in items ) + { + curItem.Delete(); + } + + return false; + } + } + + return true; + } + + public static int DepositUpTo( Mobile from, int amount ) + { + BankBox box = from.BankBox; + if ( box == null ) + return 0; + + int amountLeft = amount; + while ( amountLeft > 0 ) + { + Item item; + int amountGiven; + + if ( amountLeft < 5000 ) + { + item = new Gold( amountLeft ); + amountGiven = amountLeft; + } + else if ( amountLeft <= 1000000 ) + { + item = new BankCheck( amountLeft ); + amountGiven = amountLeft; + } + else + { + item = new BankCheck( 1000000 ); + amountGiven = 1000000; + } + + if ( box.TryDropItem( from, item, false ) ) + { + amountLeft -= amountGiven; + } + else + { + item.Delete(); + break; + } + } + + return amount - amountLeft; + } + + public static void Deposit( Container cont, int amount ) + { + while ( amount > 0 ) + { + Item item; + + if ( amount < 5000 ) + { + item = new Gold( amount ); + amount = 0; + } + else if ( amount <= 1000000 ) + { + item = new BankCheck( amount ); + amount = 0; + } + else + { + item = new BankCheck( 1000000 ); + amount -= 1000000; + } + + cont.DropItem( item ); + } + } + + public Banker( Serial serial ) : base( serial ) + { + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.InRange( this.Location, 12 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( !e.Handled && e.Mobile.InRange( this.Location, 12 ) ) + { + for ( int i = 0; i < e.Keywords.Length; ++i ) + { + int keyword = e.Keywords[i]; + + switch ( keyword ) + { + case 0x0000: // *withdraw* + { + e.Handled = true; + + if ( e.Mobile.Criminal ) + { + this.Say( 500389 ); // I will not do business with a criminal! + break; + } + + string[] split = e.Speech.Split( ' ' ); + + if ( split.Length >= 2 ) + { + int amount; + + try + { + amount = Convert.ToInt32( split[1] ); + } + catch + { + break; + } + + if ( amount > 5000 ) + { + this.Say( 500381 ); // Thou canst not withdraw so much at one time! + } + else if ( amount > 0 ) + { + BankBox box = e.Mobile.BankBox; + + if ( box == null || !box.ConsumeTotal( typeof( Gold ), amount ) ) + { + this.Say( 500384 ); // Ah, art thou trying to fool me? Thou hast not so much gold! + } + else + { + e.Mobile.AddToBackpack( new Gold( amount ) ); + + this.Say( 1010005 ); // Thou hast withdrawn gold from thy account. + } + } + } + + break; + } + case 0x0001: // *balance* + { + e.Handled = true; + + if ( e.Mobile.Criminal ) + { + this.Say( 500389 ); // I will not do business with a criminal! + break; + } + + BankBox box = e.Mobile.BankBox; + + if ( box != null ) + { + this.Say( 1042759, box.TotalGold.ToString() ); // Thy current bank balance is ~1_AMOUNT~ gold. + } + + break; + } + case 0x0002: // *bank* + { + e.Handled = true; + + if ( e.Mobile.Criminal ) + { + this.Say( 500378 ); // Thou art a criminal and cannot access thy bank box. + break; + } + + BankBox box = e.Mobile.BankBox; + + if ( box != null ) + box.Open(); + + break; + } + case 0x0003: // *check* + { + e.Handled = true; + + if ( e.Mobile.Criminal ) + { + this.Say( 500389 ); // I will not do business with a criminal! + break; + } + + string[] split = e.Speech.Split( ' ' ); + + if ( split.Length >= 2 ) + { + int amount; + + try + { + amount = Convert.ToInt32( split[1] ); + } + catch + { + break; + } + + if ( amount < 5000 ) + { + this.Say( 1010006 ); // We cannot create checks for such a paltry amount of gold! + } + else if ( amount > 1000000 ) + { + this.Say( 1010007 ); // Our policies prevent us from creating checks worth that much! + } + else + { + BankCheck check = new BankCheck( amount ); + + BankBox box = e.Mobile.BankBox; + + if ( box == null || !box.TryDropItem( e.Mobile, check, false ) ) + { + this.Say( 500386 ); // There's not enough room in your bankbox for the check! + check.Delete(); + } + else if ( !box.ConsumeTotal( typeof( Gold ), amount ) ) + { + this.Say( 500384 ); // Ah, art thou trying to fool me? Thou hast not so much gold! + check.Delete(); + } + else + { + this.Say( 1042673, AffixType.Append, amount.ToString(), "" ); // Into your bank box I have placed a check in the amount of: + } + } + } + + break; + } + } + } + } + + base.OnSpeech( e ); + } + + public override void AddCustomContextEntries( Mobile from, List list ) + { + if ( from.Alive ) + list.Add( new OpenBankEntry( from, this ) ); + + base.AddCustomContextEntries( from, list ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/BaseEscortable.cs b/Scripts/Mobiles/Townfolk/BaseEscortable.cs new file mode 100644 index 000000000..de97cbed0 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/BaseEscortable.cs @@ -0,0 +1,594 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Network; +using Server.ContextMenus; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class BaseEscortable : BaseCreature + { + private EDI m_Destination; + private string m_DestinationString; + + private DateTime m_DeleteTime; + private Timer m_DeleteTimer; + + public override bool Commandable{ get{ return false; } } // Our master cannot boss us around! + + [CommandProperty( AccessLevel.GameMaster )] + public string Destination + { + get{ return m_Destination == null ? null : m_Destination.Name; } + set{ m_DestinationString = value; m_Destination = EDI.Find( value ); } + } + + private static string[] m_TownNames = new string[] + { + "Cove", "Britain", "Jhelom", + "Minoc", "Ocllo", "Trinsic", + "Vesper", "Yew", "Skara Brae", + "Nujel'm", "Moonglow", "Magincia" + }; + + [Constructable] + public BaseEscortable() : base( AIType.AI_Melee, FightMode.Aggressor, 22, 1, 0.2, 1.0 ) + { + InitBody(); + InitOutfit(); + } + + public virtual void InitBody() + { + SetStr( 90, 100 ); + SetDex( 90, 100 ); + SetInt( 15, 25 ); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 401; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 400; + Name = NameList.RandomName( "male" ); + } + } + + public virtual void InitOutfit() + { + AddItem( new FancyShirt( Utility.RandomNeutralHue() ) ); + AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); + AddItem( new Boots( Utility.RandomNeutralHue() ) ); + + Utility.AssignRandomHair( this ); + + PackGold( 200, 250 ); + } + + public virtual bool SayDestinationTo( Mobile m ) + { + EDI dest = GetDestination(); + + if ( dest == null || !m.Alive ) + return false; + + Mobile escorter = GetEscorter(); + + if ( escorter == null ) + { + Say( "I am looking to go to {0}, will you take me?", (dest.Name == "Ocllo" && m.Map == Map.Trammel) ? "Haven" : dest.Name ); + return true; + } + else if ( escorter == m ) + { + Say( "Lead on! Payment will be made when we arrive in {0}.", (dest.Name == "Ocllo" && m.Map == Map.Trammel) ? "Haven" : dest.Name ); + return true; + } + + return false; + } + + private static Hashtable m_EscortTable = new Hashtable(); + + public static Hashtable EscortTable + { + get{ return m_EscortTable; } + } + + private static TimeSpan m_EscortDelay = TimeSpan.FromMinutes( 5.0 ); + + public virtual bool AcceptEscorter( Mobile m ) + { + EDI dest = GetDestination(); + + if ( dest == null ) + return false; + + Mobile escorter = GetEscorter(); + + if ( escorter != null || !m.Alive ) + return false; + + BaseEscortable escortable = (BaseEscortable)m_EscortTable[m]; + + if ( escortable != null && !escortable.Deleted && escortable.GetEscorter() == m ) + { + Say( "I see you already have an escort." ); + return false; + } + else if ( m is PlayerMobile && (((PlayerMobile)m).LastEscortTime + m_EscortDelay) >= DateTime.Now ) + { + int minutes = (int)Math.Ceiling( ((((PlayerMobile)m).LastEscortTime + m_EscortDelay) - DateTime.Now).TotalMinutes ); + + Say( "You must rest {0} minute{1} before we set out on this journey.", minutes, minutes == 1 ? "" : "s" ); + return false; + } + else if ( SetControlMaster( m ) ) + { + m_LastSeenEscorter = DateTime.Now; + + if ( m is PlayerMobile ) + ((PlayerMobile)m).LastEscortTime = DateTime.Now; + + Say( "Lead on! Payment will be made when we arrive in {0}.", (dest.Name == "Ocllo" && m.Map == Map.Trammel) ? "Haven" : dest.Name ); + m_EscortTable[m] = this; + StartFollow(); + return true; + } + + return false; + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.InRange( this.Location, 3 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + base.OnSpeech( e ); + + EDI dest = GetDestination(); + + if ( dest != null && !e.Handled && e.Mobile.InRange( this.Location, 3 ) ) + { + if ( e.HasKeyword( 0x1D ) ) // *destination* + e.Handled = SayDestinationTo( e.Mobile ); + else if ( e.HasKeyword( 0x1E ) ) // *i will take thee* + e.Handled = AcceptEscorter( e.Mobile ); + } + } + + public override void OnAfterDelete() + { + if ( m_DeleteTimer != null ) + m_DeleteTimer.Stop(); + + m_DeleteTimer = null; + + base.OnAfterDelete(); + } + + public override void OnThink() + { + base.OnThink(); + CheckAtDestination(); + } + + protected override bool OnMove( Direction d ) + { + if ( !base.OnMove( d ) ) + return false; + + CheckAtDestination(); + + return true; + } + + public virtual void StartFollow() + { + StartFollow( GetEscorter() ); + } + + public virtual void StartFollow( Mobile escorter ) + { + if ( escorter == null ) + return; + + ActiveSpeed = 0.1; + PassiveSpeed = 0.2; + + ControlOrder = OrderType.Follow; + ControlTarget = escorter; + + CurrentSpeed = 0.1; + } + + public virtual void StopFollow() + { + ActiveSpeed = 0.2; + PassiveSpeed = 1.0; + + ControlOrder = OrderType.None; + ControlTarget = null; + + CurrentSpeed = 1.0; + } + + private DateTime m_LastSeenEscorter; + + public virtual Mobile GetEscorter() + { + if ( !Controlled ) + return null; + + Mobile master = ControlMaster; + + if ( master == null ) + return null; + + if ( master.Deleted || master.Map != this.Map || !master.InRange( Location, 30 ) || !master.Alive ) + { + StopFollow(); + + TimeSpan lastSeenDelay = DateTime.Now - m_LastSeenEscorter; + + if ( lastSeenDelay >= TimeSpan.FromMinutes( 2.0 ) ) + { + master.SendLocalizedMessage( 1042473 ); // You have lost the person you were escorting. + Say( 1005653 ); // Hmmm. I seem to have lost my master. + + SetControlMaster( null ); + m_EscortTable.Remove( master ); + + Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerCallback( Delete ) ); + return null; + } + else + { + ControlOrder = OrderType.Stay; + return master; + } + } + + if ( ControlOrder != OrderType.Follow ) + StartFollow( master ); + + m_LastSeenEscorter = DateTime.Now; + return master; + } + + public virtual void BeginDelete() + { + if ( m_DeleteTimer != null ) + m_DeleteTimer.Stop(); + + m_DeleteTime = DateTime.Now + TimeSpan.FromSeconds( 30.0 ); + + m_DeleteTimer = new DeleteTimer( this, m_DeleteTime - DateTime.Now ); + m_DeleteTimer.Start(); + } + + public virtual bool CheckAtDestination() + { + EDI dest = GetDestination(); + + if ( dest == null ) + return false; + + Mobile escorter = GetEscorter(); + + if ( escorter == null ) + return false; + + if ( dest.Contains( Location ) ) + { + Say( 1042809, escorter.Name ); // We have arrived! I thank thee, ~1_PLAYER_NAME~! I have no further need of thy services. Here is thy pay. + + // not going anywhere + m_Destination = null; + m_DestinationString = null; + + Container cont = escorter.Backpack; + + if ( cont == null ) + cont = escorter.BankBox; + + Gold gold = new Gold( 500, 1000 ); + + if ( cont == null || !cont.TryDropItem( escorter, gold, false ) ) + gold.MoveToWorld( escorter.Location, escorter.Map ); + + StopFollow(); + SetControlMaster( null ); + m_EscortTable.Remove( escorter ); + BeginDelete(); + + Misc.Titles.AwardFame( escorter, 10, true ); + + bool gainedPath = false; + + PlayerMobile pm = escorter as PlayerMobile; + + if ( pm != null ) + { + if ( pm.CompassionGains > 0 && DateTime.Now > pm.NextCompassionDay ) + { + pm.NextCompassionDay = DateTime.MinValue; + pm.CompassionGains = 0; + } + + if ( pm.CompassionGains >= 5 ) // have already gained 5 times in one day, can gain no more + { + pm.SendLocalizedMessage( 1053004 ); // You must wait about a day before you can gain in compassion again. + } + else if ( VirtueHelper.Award( pm, VirtueName.Compassion, 200, ref gainedPath ) ) + { + if ( gainedPath ) + pm.SendLocalizedMessage( 1053005 ); // You have achieved a path in compassion! + else + pm.SendLocalizedMessage( 1053002 ); // You have gained in compassion. + + pm.NextCompassionDay = DateTime.Now + TimeSpan.FromDays( 1.0 ); // in one day CompassionGains gets reset to 0 + ++pm.CompassionGains; + } + else + { + pm.SendLocalizedMessage( 1053003 ); // You have achieved the highest path of compassion and can no longer gain any further. + } + } + + return true; + } + + return false; + } + + public BaseEscortable( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + EDI dest = GetDestination(); + + writer.Write( dest != null ); + + if ( dest != null ) + writer.Write( dest.Name ); + + writer.Write( m_DeleteTimer != null ); + + if ( m_DeleteTimer != null ) + writer.WriteDeltaTime( m_DeleteTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( reader.ReadBool() ) + m_DestinationString = reader.ReadString(); // NOTE: We cannot EDI.Find here, regions have not yet been loaded :-( + + if ( reader.ReadBool() ) + { + m_DeleteTime = reader.ReadDeltaTime(); + m_DeleteTimer = new DeleteTimer( this, m_DeleteTime - DateTime.Now ); + m_DeleteTimer.Start(); + } + } + + public override bool CanBeRenamedBy( Mobile from ) + { + return ( from.AccessLevel >= AccessLevel.GameMaster ); + } + + public override void AddCustomContextEntries( Mobile from, List list ) + { + EDI dest = GetDestination(); + + if ( dest != null && from.Alive ) + { + Mobile escorter = GetEscorter(); + + if ( escorter == null || escorter == from ) + list.Add( new AskDestinationEntry( this, from ) ); + + if ( escorter == null ) + list.Add( new AcceptEscortEntry( this, from ) ); + else if ( escorter == from ) + list.Add( new AbandonEscortEntry( this, from ) ); + } + + base.AddCustomContextEntries( from, list ); + } + + public virtual string[] GetPossibleDestinations() + { + return m_TownNames; + } + + public virtual string PickRandomDestination() + { + if ( Map.Felucca.Regions.Count == 0 || Map == null || Map == Map.Internal || Location == Point3D.Zero ) + return null; // Not yet fully initialized + + string[] possible = GetPossibleDestinations(); + string picked = null; + + while ( picked == null ) + { + picked = possible[Utility.Random( possible.Length )]; + EDI test = EDI.Find( picked ); + + if ( test != null && test.Contains( Location ) ) + picked = null; + } + + return picked; + } + + public EDI GetDestination() + { + if ( m_DestinationString == null && m_DeleteTimer == null ) + m_DestinationString = PickRandomDestination(); + + if ( m_Destination != null && m_Destination.Name == m_DestinationString ) + return m_Destination; + + if ( Map.Felucca.Regions.Count > 0 ) + return ( m_Destination = EDI.Find( m_DestinationString ) ); + + return ( m_Destination = null ); + } + + private class DeleteTimer : Timer + { + private Mobile m_Mobile; + + public DeleteTimer( Mobile m, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Mobile.Delete(); + } + } + } + + public class EscortDestinationInfo + { + private string m_Name; + private Region m_Region; + //private Rectangle2D[] m_Bounds; + + public string Name + { + get{ return m_Name; } + } + + public Region Region + { + get{ return m_Region; } + } + + /*public Rectangle2D[] Bounds + { + get{ return m_Bounds; } + }*/ + + public bool Contains( Point3D p ) + { + return m_Region.Contains( p ); + } + + public EscortDestinationInfo( string name, Region region ) + { + m_Name = name; + m_Region = region; + } + + private static Hashtable m_Table; + + public static void LoadTable() + { + ICollection list = Map.Felucca.Regions.Values; + + if ( list.Count == 0 ) + return; + + m_Table = new Hashtable(); + + foreach ( Region r in list ) + { + if ( r.Name == null ) + continue; + + if ( r is Regions.DungeonRegion || r is Regions.TownRegion ) + m_Table[r.Name] = new EscortDestinationInfo( r.Name, r ); + } + } + + public static EDI Find( string name ) + { + if ( m_Table == null ) + LoadTable(); + + if ( name == null || m_Table == null ) + return null; + + return (EscortDestinationInfo)m_Table[name]; + } + } + + public class AskDestinationEntry : ContextMenuEntry + { + private BaseEscortable m_Mobile; + private Mobile m_From; + + public AskDestinationEntry( BaseEscortable m, Mobile from ) : base( 6100, 3 ) + { + m_Mobile = m; + m_From = from; + } + + public override void OnClick() + { + m_Mobile.SayDestinationTo( m_From ); + } + } + + public class AcceptEscortEntry : ContextMenuEntry + { + private BaseEscortable m_Mobile; + private Mobile m_From; + + public AcceptEscortEntry( BaseEscortable m, Mobile from ) : base( 6101, 3 ) + { + m_Mobile = m; + m_From = from; + } + + public override void OnClick() + { + m_Mobile.AcceptEscorter( m_From ); + } + } + + public class AbandonEscortEntry : ContextMenuEntry + { + private BaseEscortable m_Mobile; + private Mobile m_From; + + public AbandonEscortEntry( BaseEscortable m, Mobile from ) : base( 6102, 3 ) + { + m_Mobile = m; + m_From = from; + } + + public override void OnClick() + { + m_Mobile.Delete(); // OSI just seems to delete instantly + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/BrideGroom.cs b/Scripts/Mobiles/Townfolk/BrideGroom.cs new file mode 100644 index 000000000..acc537c22 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/BrideGroom.cs @@ -0,0 +1,84 @@ +using System; +using Server; +using Server.Items; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class BrideGroom : BaseEscortable + { + [Constructable] + public BrideGroom() + { + if ( Female ) + Title = "the bride"; + else + Title = "the groom"; + + } + + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } // Do not display 'the groom' when single-clicking + + private static int GetRandomHue() + { + switch ( Utility.Random( 6 ) ) + { + default: + case 0: return 0; + case 1: return Utility.RandomBlueHue(); + case 2: return Utility.RandomGreenHue(); + case 3: return Utility.RandomRedHue(); + case 4: return Utility.RandomYellowHue(); + case 5: return Utility.RandomNeutralHue(); + } + } + + public override void InitOutfit() + { + if ( Female ) + + AddItem( new FancyDress() ); + else + AddItem( new FancyShirt() ); + + int lowHue = GetRandomHue(); + + AddItem( new LongPants( lowHue ) ); + + if ( Female ) + AddItem( new Shoes( lowHue ) ); + else + AddItem( new Boots( lowHue ) ); + + + + if( Utility.RandomBool() ) + HairItemID = 0x203B; + else + HairItemID = 0x203C; + + HairHue = this.Race.RandomHairHue(); + + PackGold( 200, 250 ); + } + + public BrideGroom( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/EscortableMage.cs b/Scripts/Mobiles/Townfolk/EscortableMage.cs new file mode 100644 index 000000000..ebf8c33c9 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/EscortableMage.cs @@ -0,0 +1,74 @@ +using System; +using Server; +using Server.Items; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class EscortableMage : BaseEscortable + { + [Constructable] + public EscortableMage() + { + Title = "the mage"; + + SetSkill( SkillName.EvalInt, 80.0, 100.0 ); + SetSkill( SkillName.Inscribe, 80.0, 100.0 ); + SetSkill( SkillName.Magery, 80.0, 100.0 ); + SetSkill( SkillName.Meditation, 80.0, 100.0 ); + SetSkill( SkillName.MagicResist, 80.0, 100.0 ); + } + + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } // Do not display 'the mage' when single-clicking + + private static int GetRandomHue() + { + switch ( Utility.Random( 5 ) ) + { + default: + case 0: return Utility.RandomBlueHue(); + case 1: return Utility.RandomGreenHue(); + case 2: return Utility.RandomRedHue(); + case 3: return Utility.RandomYellowHue(); + case 4: return Utility.RandomNeutralHue(); + } + } + + public override void InitOutfit() + { + AddItem( new Robe( GetRandomHue() ) ); + + int lowHue = GetRandomHue(); + + AddItem( new ShortPants( lowHue ) ); + + if ( Female ) + AddItem( new ThighBoots( lowHue ) ); + else + AddItem( new Boots( lowHue ) ); + + Utility.AssignRandomHair( this ); + + PackGold( 200, 250 ); + } + + public EscortableMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Gypsy.cs b/Scripts/Mobiles/Townfolk/Gypsy.cs new file mode 100644 index 000000000..a7c012fe9 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Gypsy.cs @@ -0,0 +1,82 @@ +using System; +using Server.Items; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + public class Gypsy : BaseCreature + { + + + [Constructable] + public Gypsy() + : base( AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4 ) + { + InitStats( 31, 41, 51 ); + + SpeechHue = Utility.RandomDyedHue(); + + SetSkill( SkillName.Cooking, 65, 88 ); + SetSkill( SkillName.Snooping, 65, 88 ); + SetSkill( SkillName.Stealing, 65, 88 ); + + Hue = Utility.RandomSkinHue(); + + if( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + AddItem( new Kilt( Utility.RandomDyedHue() ) ); + AddItem( new Shirt( Utility.RandomDyedHue() ) ); + AddItem( new ThighBoots() ); + Title = "the gypsy"; + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); + AddItem( new Shirt( Utility.RandomDyedHue() ) ); + AddItem( new Sandals() ); + Title = "the gypsy"; + } + + AddItem( new Bandana( Utility.RandomDyedHue() ) ); + AddItem( new Dagger() ); + + Utility.AssignRandomHair( this ); + + Container pack = new Backpack(); + + pack.DropItem( new Gold( 250, 300 ) ); + + pack.Movable = false; + + AddItem( pack ); + } + + + public override bool CanTeach { get { return true; } } + public override bool ClickTitle { get { return false; } } + + public Gypsy( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/HarborMaster.cs b/Scripts/Mobiles/Townfolk/HarborMaster.cs new file mode 100644 index 000000000..c7d9bd821 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/HarborMaster.cs @@ -0,0 +1,76 @@ +using System; +using Server.Items; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + public class HarborMaster : BaseCreature + { + public override bool CanTeach { get { return false; } } + + [Constructable] + public HarborMaster() + : base( AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4 ) + { + InitStats( 31, 41, 51 ); + + SetSkill( SkillName.Mining, 36, 68 ); + + + SpeechHue = Utility.RandomDyedHue(); + Hue = Utility.RandomSkinHue(); + Blessed = true; + + + if( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + Title = "the Harbor Mistress"; + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + Title = "the Harbor Master"; + } + AddItem( new Shirt( Utility.RandomDyedHue() ) ); + AddItem( new Boots() ); + AddItem( new LongPants( Utility.RandomNeutralHue() ) ); + AddItem( new QuarterStaff() ); + + Utility.AssignRandomHair( this ); + + Container pack = new Backpack(); + + pack.DropItem( new Gold( 250, 300 ) ); + + pack.Movable = false; + + AddItem( pack ); + } + + public override bool ClickTitle { get { return false; } } + + + public HarborMaster( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/Merchant.cs b/Scripts/Mobiles/Townfolk/Merchant.cs new file mode 100644 index 000000000..90548ec9f --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Merchant.cs @@ -0,0 +1,85 @@ +using System; +using Server; +using Server.Items; +using EDI=Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class Merchant : BaseEscortable + { + [Constructable] + public Merchant() + { + Title = "the merchant"; + SetSkill( SkillName.ItemID, 55.0, 78.0 ); + SetSkill( SkillName.ArmsLore, 55, 78 ); + } + + public override bool CanTeach { get { return true; } } + public override bool ClickTitle { get { return false; } } // Do not display 'the merchant' when single-clicking + + private static int GetRandomHue() + { + switch( Utility.Random( 6 ) ) + { + default: + case 0: return 0; + case 1: return Utility.RandomBlueHue(); + case 2: return Utility.RandomGreenHue(); + case 3: return Utility.RandomRedHue(); + case 4: return Utility.RandomYellowHue(); + case 5: return Utility.RandomNeutralHue(); + } + + } + + public override void InitOutfit() + { + if( Female ) + AddItem( new PlainDress() ); + else + AddItem( new Shirt( GetRandomHue() ) ); + + int lowHue = GetRandomHue(); + + AddItem( new ThighBoots() ); + + if( Female ) + AddItem( new FancyDress( lowHue ) ); + else + AddItem( new FancyShirt( lowHue ) ); + AddItem( new LongPants( lowHue ) ); + + if( !Female ) + AddItem( new BodySash( lowHue ) ); + + + + //if ( !Female ) + //AddItem( new Longsword() ); + + Utility.AssignRandomHair( this ); + + PackGold( 200, 250 ); + } + + public Merchant( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Messenger.cs b/Scripts/Mobiles/Townfolk/Messenger.cs new file mode 100644 index 000000000..456a02c46 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Messenger.cs @@ -0,0 +1,87 @@ +using System; +using Server; +using Server.Items; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class Messenger : BaseEscortable + { + [Constructable] + public Messenger() + { + Title = "the messenger"; + + } + + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } // Do not display 'the messenger' when single-clicking + + private static int GetRandomHue() + { + switch ( Utility.Random( 6 ) ) + { + default: + case 0: return 0; + case 1: return Utility.RandomBlueHue(); + case 2: return Utility.RandomGreenHue(); + case 3: return Utility.RandomRedHue(); + case 4: return Utility.RandomYellowHue(); + case 5: return Utility.RandomNeutralHue(); + } + } + + public override void InitOutfit() + { + if ( Female ) + AddItem( new PlainDress() ); + else + AddItem( new Shirt( GetRandomHue() ) ); + + int lowHue = GetRandomHue(); + + AddItem( new ShortPants( lowHue ) ); + + if ( Female ) + AddItem( new Boots( lowHue ) ); + else + AddItem( new Shoes( lowHue ) ); + + //if ( !Female ) + //AddItem( new BodySash( lowHue ) ); + + //AddItem( new Cloak( GetRandomHue() ) ); + + //if ( !Female ) + //AddItem( new Longsword() ); + + switch ( Utility.Random( 4 ) ) + { + case 0: AddItem( new ShortHair( Utility.RandomHairHue() ) ); break; + case 1: AddItem( new TwoPigTails( Utility.RandomHairHue() ) ); break; + case 2: AddItem( new ReceedingHair( Utility.RandomHairHue() ) ); break; + case 3: AddItem( new KrisnaHair( Utility.RandomHairHue() ) ); break; + } + + PackGold( 200, 250 ); + } + + public Messenger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Minter.cs b/Scripts/Mobiles/Townfolk/Minter.cs new file mode 100644 index 000000000..f727d9518 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Minter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using Server.Items; +using Server.ContextMenus; +using Server.Misc; +using Server.Network; + +namespace Server.Mobiles +{ + public class Minter : Banker + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.MerchantsGuild; } } + + [Constructable] + public Minter() + { + Title = "the minter"; + } + + public Minter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Ninja.cs b/Scripts/Mobiles/Townfolk/Ninja.cs new file mode 100644 index 000000000..0768d7ae2 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Ninja.cs @@ -0,0 +1,78 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class Ninja : BaseCreature + { + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } + + [Constructable] + public Ninja() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Title = "the ninja"; + + InitStats( 100, 100, 25 ); + + SetSkill( SkillName.Fencing, 64.0, 80.0 ); + SetSkill( SkillName.Macing, 64.0, 80.0 ); + SetSkill( SkillName.Ninjitsu, 60.0, 80.0 ); + SetSkill( SkillName.Parry, 64.0, 80.0 ); + SetSkill( SkillName.Tactics, 64.0, 85.0 ); + SetSkill( SkillName.Swords, 64.0, 85.0 ); + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + if ( !Female ) + AddItem( new LeatherNinjaHood() ); + + AddItem( new LeatherNinjaPants() ); + AddItem( new LeatherNinjaBelt() ); + AddItem( new LeatherNinjaJacket() ); + AddItem( new NinjaTabi() ); + + int hairHue = Utility.RandomNondyedHue(); + + + Utility.AssignRandomHair( this, hairHue ); + + if( Utility.Random( 7 ) != 0 ) + Utility.AssignRandomFacialHair( this, hairHue ); + + PackGold( 250, 300 ); + } + + public Ninja( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/Noble.cs b/Scripts/Mobiles/Townfolk/Noble.cs new file mode 100644 index 000000000..393a0de77 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Noble.cs @@ -0,0 +1,84 @@ +using System; +using Server; +using Server.Items; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class Noble : BaseEscortable + { + [Constructable] + public Noble() + { + Title = "the noble"; + + SetSkill( SkillName.Parry, 80.0, 100.0 ); + SetSkill( SkillName.Swords, 80.0, 100.0 ); + SetSkill( SkillName.Tactics, 80.0, 100.0 ); + } + + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } // Do not display 'the noble' when single-clicking + + private static int GetRandomHue() + { + switch ( Utility.Random( 6 ) ) + { + default: + case 0: return 0; + case 1: return Utility.RandomBlueHue(); + case 2: return Utility.RandomGreenHue(); + case 3: return Utility.RandomRedHue(); + case 4: return Utility.RandomYellowHue(); + case 5: return Utility.RandomNeutralHue(); + } + } + + public override void InitOutfit() + { + if ( Female ) + AddItem( new FancyDress() ); + else + AddItem( new FancyShirt( GetRandomHue() ) ); + + int lowHue = GetRandomHue(); + + AddItem( new ShortPants( lowHue ) ); + + if ( Female ) + AddItem( new ThighBoots( lowHue ) ); + else + AddItem( new Boots( lowHue ) ); + + if ( !Female ) + AddItem( new BodySash( lowHue ) ); + + AddItem( new Cloak( GetRandomHue() ) ); + + if ( !Female ) + AddItem( new Longsword() ); + + Utility.AssignRandomHair( this ); + + PackGold( 200, 250 ); + } + + public Noble( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Peasant.cs b/Scripts/Mobiles/Townfolk/Peasant.cs new file mode 100644 index 000000000..04673c9ec --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Peasant.cs @@ -0,0 +1,81 @@ +using System; +using Server; +using Server.Items; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class Peasant : BaseEscortable + { + [Constructable] + public Peasant() + { + Title = "the peasant"; + + } + + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } // Do not display 'the peasant' when single-clicking + + private static int GetRandomHue() + { + switch ( Utility.Random( 6 ) ) + { + default: + case 0: return 0; + case 1: return Utility.RandomBlueHue(); + case 2: return Utility.RandomGreenHue(); + case 3: return Utility.RandomRedHue(); + case 4: return Utility.RandomYellowHue(); + case 5: return Utility.RandomNeutralHue(); + } + } + + public override void InitOutfit() + { + if ( Female ) + AddItem( new PlainDress() ); + else + AddItem( new Shirt( GetRandomHue() ) ); + + int lowHue = GetRandomHue(); + + AddItem( new ShortPants( lowHue ) ); + + if ( Female ) + AddItem( new Boots( lowHue ) ); + else + AddItem( new Shoes( lowHue ) ); + + //if ( !Female ) + //AddItem( new BodySash( lowHue ) ); + + //AddItem( new Cloak( GetRandomHue() ) ); + + //if ( !Female ) + //AddItem( new Longsword() ); + + Utility.AssignRandomHair( this ); + + PackGold( 200, 250 ); + } + + public Peasant( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/Samurai.cs b/Scripts/Mobiles/Townfolk/Samurai.cs new file mode 100644 index 000000000..932dad8c6 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Samurai.cs @@ -0,0 +1,94 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class Samurai : BaseCreature + { + public override bool CanTeach{ get{ return true; } } + public override bool ClickTitle{ get{ return false; } } + + [Constructable] + public Samurai() : base( AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.2, 0.4 ) + { + Title = "the samurai"; + + InitStats( 100, 100, 25 ); + + SetSkill( SkillName.ArmsLore, 64.0, 80.0 ); + SetSkill( SkillName.Bushido, 64.0, 85.0 ); + SetSkill( SkillName.Parry, 64.0, 80.0 ); + SetSkill( SkillName.Swords, 64.0, 85.0 ); + + SpeechHue = Utility.RandomDyedHue(); + + Hue = Utility.RandomSkinHue(); + + if ( Female = Utility.RandomBool() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new Lajatang() ); break; + case 1: AddItem( new Wakizashi() ); break; + case 2: AddItem( new NoDachi() ); break; + } + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new LeatherSuneate() ); break; + case 1: AddItem( new PlateSuneate() ); break; + case 2: AddItem( new StuddedHaidate() ); break; + } + + switch ( Utility.Random( 4 ) ) + { + case 0: AddItem( new LeatherJingasa() ); break; + case 1: AddItem( new ChainHatsuburi() ); break; + case 2: AddItem( new HeavyPlateJingasa() ); break; + case 3: AddItem( new DecorativePlateKabuto() ); break; + } + + AddItem( new LeatherDo() ); + AddItem( new LeatherHiroSode() ); + AddItem( new SamuraiTabi( Utility.RandomNondyedHue() ) ); // TODO: Hue + + int hairHue = Utility.RandomNondyedHue(); + + + Utility.AssignRandomHair( this, hairHue ); + + if( Utility.Random( 7 ) != 0 ) + Utility.AssignRandomFacialHair( this, hairHue ); + + PackGold( 250, 300 ); + } + + public Samurai( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/Sculptor.cs b/Scripts/Mobiles/Townfolk/Sculptor.cs new file mode 100644 index 000000000..a6c99c645 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/Sculptor.cs @@ -0,0 +1,70 @@ +using System; +using Server.Items; +using Server; +using Server.Misc; + +namespace Server.Mobiles +{ + public class Sculptor : BaseCreature + { + [Constructable] + public Sculptor() + : base( AIType.AI_Animal, FightMode.None, 10, 1, 0.2, 0.4 ) + { + InitStats( 31, 41, 51 ); + + SpeechHue = Utility.RandomDyedHue(); + Title = "the sculptor"; + Hue = Utility.RandomSkinHue(); + + if( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + AddItem( new Kilt( Utility.RandomNeutralHue() ) ); + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + AddItem( new LongPants( Utility.RandomNeutralHue() ) ); + } + + + + AddItem( new Doublet( Utility.RandomNeutralHue() ) ); + AddItem( new HalfApron() ); + + Utility.AssignRandomHair( this ); + + Container pack = new Backpack(); + + pack.DropItem( new Gold( 250, 300 ) ); + + pack.Movable = false; + + AddItem( pack ); + } + + public override bool ClickTitle { get { return false; } } + + public Sculptor( Serial serial ) + : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Townfolk/SeekerOfAdventure.cs b/Scripts/Mobiles/Townfolk/SeekerOfAdventure.cs new file mode 100644 index 000000000..91735d1a1 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/SeekerOfAdventure.cs @@ -0,0 +1,90 @@ +using System; +using Server; +using Server.Items; +using EDI = Server.Mobiles.EscortDestinationInfo; + +namespace Server.Mobiles +{ + public class SeekerOfAdventure : BaseEscortable + { + private static string[] m_Dungeons = new string[] + { + "Covetous", "Deceit", "Despise", + "Destard", "Hythloth", "Shame", + "Wrong" + }; + + public override string[] GetPossibleDestinations() + { + return m_Dungeons; + } + + [Constructable] + public SeekerOfAdventure() + { + Title = "the seeker of adventure"; + } + + public override bool ClickTitle{ get{ return false; } } // Do not display 'the seeker of adventure' when single-clicking + + private static int GetRandomHue() + { + switch ( Utility.Random( 6 ) ) + { + default: + case 0: return 0; + case 1: return Utility.RandomBlueHue(); + case 2: return Utility.RandomGreenHue(); + case 3: return Utility.RandomRedHue(); + case 4: return Utility.RandomYellowHue(); + case 5: return Utility.RandomNeutralHue(); + } + } + + public override void InitOutfit() + { + if ( Female ) + AddItem( new FancyDress( GetRandomHue() ) ); + else + AddItem( new FancyShirt( GetRandomHue() ) ); + + int lowHue = GetRandomHue(); + + AddItem( new ShortPants( lowHue ) ); + + if ( Female ) + AddItem( new ThighBoots( lowHue ) ); + else + AddItem( new Boots( lowHue ) ); + + if ( !Female ) + AddItem( new BodySash( lowHue ) ); + + AddItem( new Cloak( GetRandomHue() ) ); + + AddItem( new Longsword() ); + + Utility.AssignRandomHair( this ); + + PackGold( 100, 150 ); + } + + public SeekerOfAdventure( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Townfolk/TownCrier.cs b/Scripts/Mobiles/Townfolk/TownCrier.cs new file mode 100644 index 000000000..4ba677299 --- /dev/null +++ b/Scripts/Mobiles/Townfolk/TownCrier.cs @@ -0,0 +1,568 @@ +using System; +using System.Text; +using System.Collections; +using Server; +using Server.Prompts; +using Server.Gumps; +using Server.Network; +using Server.Items; +using Server.Misc; +using Server.Commands; + +namespace Server.Mobiles +{ + public interface ITownCrierEntryList + { + ArrayList Entries{ get; } + TownCrierEntry GetRandomEntry(); + TownCrierEntry AddEntry( string[] lines, TimeSpan duration ); + void RemoveEntry( TownCrierEntry entry ); + } + + public class GlobalTownCrierEntryList : ITownCrierEntryList + { + private static GlobalTownCrierEntryList m_Instance; + + public static void Initialize() + { + CommandSystem.Register( "TownCriers", AccessLevel.GameMaster, new CommandEventHandler( TownCriers_OnCommand ) ); + } + + [Usage( "TownCriers" )] + [Description( "Manages the global town crier list." )] + public static void TownCriers_OnCommand( CommandEventArgs e ) + { + e.Mobile.SendGump( new TownCrierGump( e.Mobile, Instance ) ); + } + + public static GlobalTownCrierEntryList Instance + { + get + { + if ( m_Instance == null ) + m_Instance = new GlobalTownCrierEntryList(); + + return m_Instance; + } + } + + public bool IsEmpty{ get{ return ( m_Entries == null || m_Entries.Count == 0 ); } } + + public GlobalTownCrierEntryList() + { + } + + private ArrayList m_Entries; + + public ArrayList Entries + { + get{ return m_Entries; } + } + + public TownCrierEntry GetRandomEntry() + { + if ( m_Entries == null || m_Entries.Count == 0 ) + return null; + + for ( int i = m_Entries.Count - 1; m_Entries != null && i >= 0; --i ) + { + if ( i >= m_Entries.Count ) + continue; + + TownCrierEntry tce = (TownCrierEntry)m_Entries[i]; + + if ( tce.Expired ) + RemoveEntry( tce ); + } + + if ( m_Entries == null || m_Entries.Count == 0 ) + return null; + + return (TownCrierEntry)m_Entries[Utility.Random( m_Entries.Count )]; + } + + public TownCrierEntry AddEntry( string[] lines, TimeSpan duration ) + { + if ( m_Entries == null ) + m_Entries = new ArrayList(); + + TownCrierEntry tce = new TownCrierEntry( lines, duration ); + + m_Entries.Add( tce ); + + ArrayList instances = TownCrier.Instances; + + for ( int i = 0; i < instances.Count; ++i ) + ((TownCrier)instances[i]).ForceBeginAutoShout(); + + return tce; + } + + public void RemoveEntry( TownCrierEntry tce ) + { + if ( m_Entries == null ) + return; + + m_Entries.Remove( tce ); + + if ( m_Entries.Count == 0 ) + m_Entries = null; + } + } + + public class TownCrierEntry + { + private string[] m_Lines; + private DateTime m_ExpireTime; + + public string[] Lines{ get{ return m_Lines; } } + public DateTime ExpireTime{ get{ return m_ExpireTime; } } + public bool Expired{ get{ return ( DateTime.Now >= m_ExpireTime ); } } + + public TownCrierEntry( string[] lines, TimeSpan duration ) + { + m_Lines = lines; + + if ( duration < TimeSpan.Zero ) + duration = TimeSpan.Zero; + else if ( duration > TimeSpan.FromDays( 365.0 ) ) + duration = TimeSpan.FromDays( 365.0 ); + + m_ExpireTime = DateTime.Now + duration; + } + } + + public class TownCrierDurationPrompt : Prompt + { + private ITownCrierEntryList m_Owner; + + public TownCrierDurationPrompt( ITownCrierEntryList owner ) + { + m_Owner = owner; + } + + public override void OnResponse( Mobile from, string text ) + { + TimeSpan ts; + + if( !TimeSpan.TryParse( text, out ts ) ) + { + from.SendMessage( "Value was not properly formatted. Use: " ); + from.SendGump( new TownCrierGump( from, m_Owner ) ); + return; + } + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + from.SendMessage( "Duration set to: {0}", ts ); + from.SendMessage( "Enter the first line to shout:" ); + + from.Prompt = new TownCrierLinesPrompt( m_Owner, null, new ArrayList(), ts ); + } + + public override void OnCancel( Mobile from ) + { + from.SendLocalizedMessage( 502980 ); // Message entry cancelled. + from.SendGump( new TownCrierGump( from, m_Owner ) ); + } + } + + public class TownCrierLinesPrompt : Prompt + { + private ITownCrierEntryList m_Owner; + private TownCrierEntry m_Entry; + private ArrayList m_Lines; + private TimeSpan m_Duration; + + public TownCrierLinesPrompt( ITownCrierEntryList owner, TownCrierEntry entry, ArrayList lines, TimeSpan duration ) + { + m_Owner = owner; + m_Entry = entry; + m_Lines = lines; + m_Duration = duration; + } + + public override void OnResponse( Mobile from, string text ) + { + m_Lines.Add( text ); + + from.SendMessage( "Enter the next line to shout, or press if the message is finished." ); + from.Prompt = new TownCrierLinesPrompt( m_Owner, m_Entry, m_Lines, m_Duration ); + } + + public override void OnCancel( Mobile from ) + { + if ( m_Entry != null ) + m_Owner.RemoveEntry( m_Entry ); + + if ( m_Lines.Count > 0 ) + { + m_Owner.AddEntry( (string[])m_Lines.ToArray( typeof( string ) ), m_Duration ); + from.SendMessage( "Message has been set." ); + } + else + { + if ( m_Entry != null ) + from.SendMessage( "Message deleted." ); + else + from.SendLocalizedMessage( 502980 ); // Message entry cancelled. + } + + from.SendGump( new TownCrierGump( from, m_Owner ) ); + } + } + + public class TownCrierGump : Gump + { + private Mobile m_From; + private ITownCrierEntryList m_Owner; + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + { + m_From.SendMessage( "Enter the duration for the new message. Format: " ); + m_From.Prompt = new TownCrierDurationPrompt( m_Owner ); + } + else if ( info.ButtonID > 1 ) + { + ArrayList entries = m_Owner.Entries; + int index = info.ButtonID - 2; + + if ( entries != null && index < entries.Count ) + { + TownCrierEntry tce = (TownCrierEntry)entries[index]; + TimeSpan ts = tce.ExpireTime - DateTime.Now; + + if ( ts < TimeSpan.Zero ) + ts = TimeSpan.Zero; + + m_From.SendMessage( "Editing entry #{0}.", index + 1 ); + m_From.SendMessage( "Enter the first line to shout:" ); + m_From.Prompt = new TownCrierLinesPrompt( m_Owner, tce, new ArrayList(), ts ); + } + } + } + + public TownCrierGump( Mobile from, ITownCrierEntryList owner ) : base( 50, 50 ) + { + m_From = from; + m_Owner = owner; + + from.CloseGump( typeof( TownCrierGump ) ); + + AddPage( 0 ); + + ArrayList entries = owner.Entries; + + owner.GetRandomEntry(); // force expiration checks + + int count = 0; + + if ( entries != null ) + count = entries.Count; + + AddImageTiled( 0, 0, 300, 38 + (count == 0 ? 20 : (count * 85)), 0xA40 ); + AddAlphaRegion( 1, 1, 298, 36 + (count == 0 ? 20 : (count * 85)) ); + + AddHtml( 8, 8, 300 - 8 - 30, 20, "
TOWN CRIER MESSAGES
", false, false ); + + AddButton( 300 - 8 - 30, 8, 0xFAB, 0xFAD, 1, GumpButtonType.Reply, 0 ); + + if ( count == 0 ) + { + AddHtml( 8, 30, 284, 20, "The crier has no news.", false, false ); + } + else + { + for ( int i = 0; i < entries.Count; ++i ) + { + TownCrierEntry tce = (TownCrierEntry)entries[i]; + + TimeSpan toExpire = tce.ExpireTime - DateTime.Now; + + if ( toExpire < TimeSpan.Zero ) + toExpire = TimeSpan.Zero; + + StringBuilder sb = new StringBuilder(); + + sb.Append( "[Expires: " ); + + if ( toExpire.TotalHours >= 1 ) + { + sb.Append( (int)toExpire.TotalHours ); + sb.Append( ':' ); + sb.Append( toExpire.Minutes.ToString( "D2" ) ); + } + else + { + sb.Append( toExpire.Minutes ); + } + + sb.Append( ':' ); + sb.Append( toExpire.Seconds.ToString( "D2" ) ); + + sb.Append( "] " ); + + for ( int j = 0; j < tce.Lines.Length; ++j ) + { + if ( j > 0 ) + sb.Append( "
" ); + + sb.Append( tce.Lines[j] ); + } + + AddHtml( 8, 35 + (i * 85), 254, 80, sb.ToString(), true, true ); + + AddButton( 300 - 8 - 26, 35 + (i * 85), 0x15E1, 0x15E5, 2 + i, GumpButtonType.Reply, 0 ); + } + } + } + } + + public class TownCrier : Mobile, ITownCrierEntryList + { + private ArrayList m_Entries; + private Timer m_NewsTimer; + private Timer m_AutoShoutTimer; + + public ArrayList Entries + { + get{ return m_Entries; } + } + + public TownCrierEntry GetRandomEntry() + { + if ( m_Entries == null || m_Entries.Count == 0 ) + return GlobalTownCrierEntryList.Instance.GetRandomEntry(); + + for ( int i = m_Entries.Count - 1; m_Entries != null && i >= 0; --i ) + { + if ( i >= m_Entries.Count ) + continue; + + TownCrierEntry tce = (TownCrierEntry)m_Entries[i]; + + if ( tce.Expired ) + RemoveEntry( tce ); + } + + if ( m_Entries == null || m_Entries.Count == 0 ) + return GlobalTownCrierEntryList.Instance.GetRandomEntry(); + + TownCrierEntry entry = GlobalTownCrierEntryList.Instance.GetRandomEntry(); + + if ( entry == null || Utility.RandomBool() ) + entry = (TownCrierEntry)m_Entries[Utility.Random( m_Entries.Count )]; + + return entry; + } + + public void ForceBeginAutoShout() + { + if ( m_AutoShoutTimer == null ) + m_AutoShoutTimer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromMinutes( 1.0 ), new TimerCallback( AutoShout_Callback ) ); + } + + public TownCrierEntry AddEntry( string[] lines, TimeSpan duration ) + { + if ( m_Entries == null ) + m_Entries = new ArrayList(); + + TownCrierEntry tce = new TownCrierEntry( lines, duration ); + + m_Entries.Add( tce ); + + if ( m_AutoShoutTimer == null ) + m_AutoShoutTimer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromMinutes( 1.0 ), new TimerCallback( AutoShout_Callback ) ); + + return tce; + } + + public void RemoveEntry( TownCrierEntry tce ) + { + if ( m_Entries == null ) + return; + + m_Entries.Remove( tce ); + + if ( m_Entries.Count == 0 ) + m_Entries = null; + + if ( m_Entries == null && GlobalTownCrierEntryList.Instance.IsEmpty ) + { + if ( m_AutoShoutTimer != null ) + m_AutoShoutTimer.Stop(); + + m_AutoShoutTimer = null; + } + } + + private void AutoShout_Callback() + { + TownCrierEntry tce = GetRandomEntry(); + + if ( tce == null ) + { + if ( m_AutoShoutTimer != null ) + m_AutoShoutTimer.Stop(); + + m_AutoShoutTimer = null; + } + else if ( m_NewsTimer == null ) + { + m_NewsTimer = Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 3.0 ), new TimerStateCallback( ShoutNews_Callback ), new object[]{ tce, 0 } ); + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 502976 ); // Hear ye! Hear ye! + } + } + + private void ShoutNews_Callback( object state ) + { + object[] states = (object[])state; + TownCrierEntry tce = (TownCrierEntry)states[0]; + int index = (int)states[1]; + + if ( index < 0 || index >= tce.Lines.Length ) + { + if ( m_NewsTimer != null ) + m_NewsTimer.Stop(); + + m_NewsTimer = null; + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, false, tce.Lines[index] ); + states[1] = index + 1; + } + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster ) + from.SendGump( new TownCrierGump( from, this ) ); + else + base.OnDoubleClick( from ); + } + + public override bool HandlesOnSpeech( Mobile from ) + { + return ( m_NewsTimer == null && from.Alive && InRange( from, 12 ) ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( m_NewsTimer == null && e.HasKeyword( 0x30 ) && e.Mobile.Alive && InRange( e.Mobile, 12 ) ) // *news* + { + Direction = GetDirectionTo( e.Mobile ); + + TownCrierEntry tce = GetRandomEntry(); + + if ( tce == null ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1005643 ); // I have no news at this time. + } + else + { + m_NewsTimer = Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 3.0 ), new TimerStateCallback( ShoutNews_Callback ), new object[]{ tce, 0 } ); + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 502978 ); // Some of the latest news! + } + } + } + + private static ArrayList m_Instances = new ArrayList(); + + public static ArrayList Instances + { + get{ return m_Instances; } + } + + [Constructable] + public TownCrier() + { + m_Instances.Add( this ); + + InitStats( 100, 100, 25 ); + + Title = "the town crier"; + Hue = Utility.RandomSkinHue(); + + if ( !Core.AOS ) + NameHue = 0x35; + + if ( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + } + + AddItem( new FancyShirt( Utility.RandomBlueHue() ) ); + + Item skirt; + + switch ( Utility.Random( 2 ) ) + { + case 0: skirt = new Skirt(); break; + default: case 1: skirt = new Kilt(); break; + } + + skirt.Hue = Utility.RandomGreenHue(); + + AddItem( skirt ); + + AddItem( new FeatheredHat( Utility.RandomGreenHue() ) ); + + Item boots; + + switch ( Utility.Random( 2 ) ) + { + case 0: boots = new Boots(); break; + default: case 1: boots = new ThighBoots(); break; + } + + AddItem( boots ); + + Utility.AssignRandomHair( this ); + } + + public override bool CanBeDamaged() + { + return false; + } + + public override void OnDelete() + { + m_Instances.Remove( this ); + base.OnDelete(); + } + + public TownCrier( Serial serial ) : base( serial ) + { + m_Instances.Add( this ); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Core.AOS && NameHue == 0x35 ) + NameHue = -1; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/AnimalBuy.cs b/Scripts/Mobiles/Vendors/AnimalBuy.cs new file mode 100644 index 000000000..b666331be --- /dev/null +++ b/Scripts/Mobiles/Vendors/AnimalBuy.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class AnimalBuyInfo : GenericBuyInfo + { + private int m_ControlSlots; + + public AnimalBuyInfo( int controlSlots, Type type, int price, int amount, int itemID, int hue ) : this( controlSlots, null, type, price, amount, itemID, hue ) + { + } + + public AnimalBuyInfo( int controlSlots, string name, Type type, int price, int amount, int itemID, int hue ) : base( name, type, price, amount, itemID, hue ) + { + m_ControlSlots = controlSlots; + } + + public override int ControlSlots + { + get + { + return m_ControlSlots; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/BaseVendor.cs b/Scripts/Mobiles/Vendors/BaseVendor.cs new file mode 100644 index 000000000..3078dee2e --- /dev/null +++ b/Scripts/Mobiles/Vendors/BaseVendor.cs @@ -0,0 +1,1484 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Network; +using Server.ContextMenus; +using Server.Mobiles; +using Server.Misc; +using Server.Engines.BulkOrders; +using Server.Regions; +using Server.Factions; + +namespace Server.Mobiles +{ + public enum VendorShoeType + { + None, + Shoes, + Boots, + Sandals, + ThighBoots + } + + public abstract class BaseVendor : BaseCreature, IVendor + { + private const int MaxSell = 500; + + protected abstract ArrayList SBInfos{ get; } + + private ArrayList m_ArmorBuyInfo = new ArrayList(); + private ArrayList m_ArmorSellInfo = new ArrayList(); + + private DateTime m_LastRestock; + + public override bool CanTeach{ get{ return true; } } + + public override bool PlayerRangeSensitive{ get{ return true; } } + + public virtual bool IsActiveVendor{ get{ return true; } } + public virtual bool IsActiveBuyer{ get{ return IsActiveVendor; } } // response to vendor SELL + public virtual bool IsActiveSeller{ get{ return IsActiveVendor; } } // repsonse to vendor BUY + + public virtual NpcGuild NpcGuild{ get{ return NpcGuild.None; } } + + public virtual bool IsInvulnerable{ get{ return true; } } + + public override bool ShowFameTitle{ get{ return false; } } + + public virtual bool IsValidBulkOrder( Item item ) + { + return false; + } + + public virtual Item CreateBulkOrder( Mobile from, bool fromContextMenu ) + { + return null; + } + + public virtual bool SupportsBulkOrders( Mobile from ) + { + return false; + } + + public virtual TimeSpan GetNextBulkOrder( Mobile from ) + { + return TimeSpan.Zero; + } + + public virtual void OnSuccessfulBulkOrderRecieve( Mobile from ) + { + } + + #region Faction + public virtual int GetPriceScalar() + { + Town town = Town.FromRegion( this.Region ); + + if ( town != null ) + return (100 + town.Tax); + + return 100; + } + + public void UpdateBuyInfo() + { + int priceScalar = GetPriceScalar(); + + IBuyItemInfo[] buyinfo = (IBuyItemInfo[])m_ArmorBuyInfo.ToArray( typeof( IBuyItemInfo ) ); + + if ( buyinfo != null ) + { + foreach ( IBuyItemInfo info in buyinfo ) + info.PriceScalar = priceScalar; + } + } + #endregion + + private class BulkOrderInfoEntry : ContextMenuEntry + { + private Mobile m_From; + private BaseVendor m_Vendor; + + public BulkOrderInfoEntry( Mobile from, BaseVendor vendor ) : base( 6152, 6 ) + { + m_From = from; + m_Vendor = vendor; + } + + public override void OnClick() + { + if ( m_Vendor.SupportsBulkOrders( m_From ) ) + { + TimeSpan ts = m_Vendor.GetNextBulkOrder( m_From ); + + int totalSeconds = (int)ts.TotalSeconds; + int totalHours = (totalSeconds + 3599) / 3600; + int totalMinutes = (totalSeconds + 59) / 60; + + if ( ((Core.SE ) ? totalMinutes == 0 : totalHours == 0) ) + { + m_From.SendLocalizedMessage( 1049038 ); // You can get an order now. + + if ( Core.AOS ) + { + Item bulkOrder = m_Vendor.CreateBulkOrder( m_From, true ); + + if ( bulkOrder is LargeBOD ) + m_From.SendGump( new LargeBODAcceptGump( m_From, (LargeBOD)bulkOrder ) ); + else if ( bulkOrder is SmallBOD ) + m_From.SendGump( new SmallBODAcceptGump( m_From, (SmallBOD)bulkOrder ) ); + } + } + else + { + int oldSpeechHue = m_Vendor.SpeechHue; + m_Vendor.SpeechHue = 0x3B2; + + if( Core.SE ) + m_Vendor.SayTo( m_From, 1072058, totalMinutes.ToString() ); // An offer may be available in about ~1_minutes~ minutes. + else + m_Vendor.SayTo( m_From, 1049039, totalHours.ToString() ); // An offer may be available in about ~1_hours~ hours. + + m_Vendor.SpeechHue = oldSpeechHue; + } + } + } + } + + public BaseVendor( string title ) : base( AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2 ) + { + LoadSBInfo(); + + this.Title = title; + InitBody(); + InitOutfit(); + + Container pack; + //these packs MUST exist, or the client will crash when the packets are sent + pack = new Backpack(); + pack.Layer = Layer.ShopBuy; + pack.Movable = false; + pack.Visible = false; + AddItem( pack ); + + pack = new Backpack(); + pack.Layer = Layer.ShopResale; + pack.Movable = false; + pack.Visible = false; + AddItem( pack ); + + m_LastRestock = DateTime.Now; + } + + public BaseVendor( Serial serial ) : base( serial ) + { + } + + public DateTime LastRestock + { + get + { + return m_LastRestock; + } + set + { + m_LastRestock = value; + } + } + + public virtual TimeSpan RestockDelay + { + get + { + return TimeSpan.FromHours( 1 ); + } + } + + public Container BuyPack + { + get + { + Container pack = FindItemOnLayer( Layer.ShopBuy ) as Container; + + if ( pack == null ) + { + pack = new Backpack(); + pack.Layer = Layer.ShopBuy; + pack.Visible = false; + AddItem( pack ); + } + + return pack; + } + } + + public abstract void InitSBInfo(); + + public virtual bool IsTokunoVendor{ get{ return ( Map == Map.Tokuno ); } } + + protected void LoadSBInfo() + { + m_LastRestock = DateTime.Now; + + for ( int i = 0; i < m_ArmorBuyInfo.Count; ++i ) + { + GenericBuyInfo buy = m_ArmorBuyInfo[i] as GenericBuyInfo; + + if ( buy != null ) + buy.DeleteDisplayObject(); + } + + SBInfos.Clear(); + + InitSBInfo(); + + m_ArmorBuyInfo.Clear(); + m_ArmorSellInfo.Clear(); + + for ( int i = 0; i < SBInfos.Count; i++ ) + { + SBInfo sbInfo = (SBInfo)SBInfos[i]; + m_ArmorBuyInfo.AddRange( sbInfo.BuyInfo ); + m_ArmorSellInfo.Add( sbInfo.SellInfo ); + } + } + + public virtual bool GetGender() + { + return Utility.RandomBool(); + } + + public virtual void InitBody() + { + InitStats( 100, 100, 25 ); + + SpeechHue = Utility.RandomDyedHue(); + Hue = Utility.RandomSkinHue(); + + if ( IsInvulnerable && !Core.AOS ) + NameHue = 0x35; + + if ( Female = GetGender() ) + { + Body = 0x191; + Name = NameList.RandomName( "female" ); + } + else + { + Body = 0x190; + Name = NameList.RandomName( "male" ); + } + } + + public virtual int GetRandomHue() + { + switch ( Utility.Random( 5 ) ) + { + default: + case 0: return Utility.RandomBlueHue(); + case 1: return Utility.RandomGreenHue(); + case 2: return Utility.RandomRedHue(); + case 3: return Utility.RandomYellowHue(); + case 4: return Utility.RandomNeutralHue(); + } + } + + public virtual int GetShoeHue() + { + if ( 0.1 > Utility.RandomDouble() ) + return 0; + + return Utility.RandomNeutralHue(); + } + + public virtual VendorShoeType ShoeType + { + get{ return VendorShoeType.Shoes; } + } + + public virtual int RandomBrightHue() + { + if ( 0.1 > Utility.RandomDouble() ) + return Utility.RandomList( 0x62, 0x71 ); + + return Utility.RandomList( 0x03, 0x0D, 0x13, 0x1C, 0x21, 0x30, 0x37, 0x3A, 0x44, 0x59 ); + } + + public virtual void CheckMorph() + { + if ( CheckGargoyle() ) + return; + + if ( CheckNecromancer() ) + return; + + CheckTokuno(); + } + + public virtual bool CheckTokuno() + { + if ( this.Map != Map.Tokuno ) + return false; + + NameList n; + + if ( Female ) + n = NameList.GetNameList( "tokuno female" ); + else + n = NameList.GetNameList( "tokuno male" ); + + if ( !n.ContainsName( this.Name ) ) + TurnToTokuno(); + + return true; + } + + public virtual void TurnToTokuno() + { + if ( Female ) + this.Name = NameList.RandomName( "tokuno female" ); + else + this.Name = NameList.RandomName( "tokuno male" ); + } + + public virtual bool CheckGargoyle() + { + Map map = this.Map; + + if ( map != Map.Ilshenar ) + return false; + + if ( !Region.IsPartOf( "Gargoyle City" ) ) + return false; + + if ( Body != 0x2F6 || (Hue & 0x8000) == 0 ) + TurnToGargoyle(); + + return true; + } + + public virtual bool CheckNecromancer() + { + Map map = this.Map; + + if ( map != Map.Malas ) + return false; + + if ( !Region.IsPartOf( "Umbra" ) ) + return false; + + if ( Hue != 0x83E8 ) + TurnToNecromancer(); + + return true; + } + + public override void OnAfterSpawn() + { + CheckMorph(); + } + + protected override void OnMapChange( Map oldMap ) + { + base.OnMapChange( oldMap ); + + CheckMorph(); + + LoadSBInfo(); + } + + public virtual int GetRandomNecromancerHue() + { + switch ( Utility.Random( 20 ) ) + { + case 0: return 0; + case 1: return 0x4E9; + default: return Utility.RandomList( 0x485, 0x497 ); + } + } + + public virtual void TurnToNecromancer() + { + for ( int i = 0; i < this.Items.Count; ++i ) + { + Item item = this.Items[i]; + + if ( item is Hair || item is Beard ) + item.Hue = 0; + else if ( item is BaseClothing || item is BaseWeapon || item is BaseArmor || item is BaseTool ) + item.Hue = GetRandomNecromancerHue(); + } + + HairHue = 0; + FacialHairHue = 0; + + Hue = 0x83E8; + } + + public virtual void TurnToGargoyle() + { + for ( int i = 0; i < this.Items.Count; ++i ) + { + Item item = this.Items[i]; + + if ( item is BaseClothing || item is Hair || item is Beard ) + item.Delete(); + } + + HairItemID = 0; + FacialHairItemID = 0; + + Body = 0x2F6; + Hue = RandomBrightHue() | 0x8000; + Name = NameList.RandomName( "gargoyle vendor" ); + + CapitalizeTitle(); + } + + public virtual void CapitalizeTitle() + { + string title = this.Title; + + if ( title == null ) + return; + + string[] split = title.Split( ' ' ); + + for ( int i = 0; i < split.Length; ++i ) + { + if ( Insensitive.Equals( split[i], "the" ) ) + continue; + + if ( split[i].Length > 1 ) + split[i] = Char.ToUpper( split[i][0] ) + split[i].Substring( 1 ); + else if ( split[i].Length > 0 ) + split[i] = Char.ToUpper( split[i][0] ).ToString(); + } + + this.Title = String.Join( " ", split ); + } + + public virtual int GetHairHue() + { + return Utility.RandomHairHue(); + } + + public virtual void InitOutfit() + { + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new FancyShirt( GetRandomHue() ) ); break; + case 1: AddItem( new Doublet( GetRandomHue() ) ); break; + case 2: AddItem( new Shirt( GetRandomHue() ) ); break; + } + + switch ( ShoeType ) + { + case VendorShoeType.Shoes: AddItem( new Shoes( GetShoeHue() ) ); break; + case VendorShoeType.Boots: AddItem( new Boots( GetShoeHue() ) ); break; + case VendorShoeType.Sandals: AddItem( new Sandals( GetShoeHue() ) ); break; + case VendorShoeType.ThighBoots: AddItem( new ThighBoots( GetShoeHue() ) ); break; + } + + int hairHue = GetHairHue(); + + Utility.AssignRandomHair( this, hairHue ); + Utility.AssignRandomFacialHair( this, hairHue ); + + if ( Female ) + { + switch ( Utility.Random( 6 ) ) + { + case 0: AddItem( new ShortPants( GetRandomHue() ) ); break; + case 1: + case 2: AddItem( new Kilt( GetRandomHue() ) ); break; + case 3: + case 4: + case 5: AddItem( new Skirt( GetRandomHue() ) ); break; + } + } + else + { + switch ( Utility.Random( 2 ) ) + { + case 0: AddItem( new LongPants( GetRandomHue() ) ); break; + case 1: AddItem( new ShortPants( GetRandomHue() ) ); break; + } + } + + PackGold( 100, 200 ); + } + + public virtual void Restock() + { + m_LastRestock = DateTime.Now; + + IBuyItemInfo[] buyInfo = this.GetBuyInfo(); + + foreach ( IBuyItemInfo bii in buyInfo ) + bii.OnRestock(); + } + + private static TimeSpan InventoryDecayTime = TimeSpan.FromHours( 1.0 ); + + public virtual void VendorBuy( Mobile from ) + { + if ( !IsActiveSeller ) + return; + + if ( !from.CheckAlive() ) + return; + + if ( !CheckVendorAccess( from ) ) + { + Say( 501522 ); // I shall not treat with scum like thee! + return; + } + + if ( DateTime.Now - m_LastRestock > RestockDelay ) + Restock(); + + UpdateBuyInfo(); + + int count = 0; + ArrayList list; + IBuyItemInfo[] buyInfo = this.GetBuyInfo(); + IShopSellInfo[] sellInfo = this.GetSellInfo(); + + list = new ArrayList( buyInfo.Length ); + Container cont = this.BuyPack; + + ArrayList opls = new ArrayList(); + + for (int idx=0;idx= 250 ) + continue; + + // NOTE: Only GBI supported; if you use another implementation of IBuyItemInfo, this will crash + GenericBuyInfo gbi = (GenericBuyInfo) buyItem; + IEntity disp = gbi.GetDisplayObject() as IEntity; + + list.Add( new BuyItemState( buyItem.Name, cont.Serial, disp == null ? (Serial) 0x7FC0FFEE : disp.Serial, buyItem.Price, buyItem.Amount, buyItem.ItemID, buyItem.Hue ) ); + count++; + + if ( disp is Item ) + opls.Add( (disp as Item).PropertyList ); + else if ( disp is Mobile ) + opls.Add( (disp as Mobile).PropertyList ); + } + + List playerItems = cont.Items; + + for ( int i = playerItems.Count - 1; i >= 0; --i ) + { + if ( i >= playerItems.Count ) + continue; + + Item item = playerItems[i]; + + if ( (item.LastMoved + InventoryDecayTime) <= DateTime.Now ) + item.Delete(); + } + + for ( int i = 0; i < playerItems.Count; ++i ) + { + Item item = playerItems[i]; + + int price = 0; + string name = null; + + foreach( IShopSellInfo ssi in sellInfo ) + { + if ( ssi.IsSellable( item ) ) + { + price = ssi.GetBuyPriceFor( item ); + name = ssi.GetNameFor( item ); + break; + } + } + + if ( name != null && list.Count < 250 ) + { + list.Add( new BuyItemState( name, cont.Serial, item.Serial, price, item.Amount, item.ItemID, item.Hue ) ); + count++; + + opls.Add( item.PropertyList ); + } + } + + //one (not all) of the packets uses a byte to describe number of items in the list. Osi = dumb. + //if ( list.Count > 255 ) + // Console.WriteLine( "Vendor Warning: Vendor {0} has more than 255 buy items, may cause client errors!", this ); + + if ( list.Count > 0 ) + { + list.Sort( new BuyItemStateComparer() ); + + SendPacksTo( from ); + + from.Send( new VendorBuyContent( list ) ); + from.Send( new VendorBuyList( this, list ) ); + from.Send( new DisplayBuyList( this ) ); + from.Send( new MobileStatusExtended( from ) );//make sure their gold amount is sent + + for ( int i = 0; i < opls.Count; ++i ) + from.Send( opls[i] as Packet ); + + SayTo( from, 500186 ); // Greetings. Have a look around. + } + } + + public virtual void SendPacksTo( Mobile from ) + { + Item pack = FindItemOnLayer( Layer.ShopBuy ); + + if ( pack == null ) + { + pack = new Backpack(); + pack.Layer = Layer.ShopBuy; + pack.Movable = false; + pack.Visible = false; + AddItem( pack ); + } + + from.Send( new EquipUpdate( pack ) ); + + pack = FindItemOnLayer( Layer.ShopSell ); + + if ( pack != null ) + from.Send( new EquipUpdate( pack ) ); + + pack = FindItemOnLayer( Layer.ShopResale ); + + if ( pack == null ) + { + pack = new Backpack(); + pack.Layer = Layer.ShopResale; + pack.Movable = false; + pack.Visible = false; + AddItem( pack ); + } + + from.Send( new EquipUpdate( pack ) ); + } + + public virtual void VendorSell( Mobile from ) + { + if ( !IsActiveBuyer ) + return; + + if ( !from.CheckAlive() ) + return; + + if ( !CheckVendorAccess( from ) ) + { + Say( 501522 ); // I shall not treat with scum like thee! + return; + } + + Container pack = from.Backpack; + + if ( pack != null ) + { + IShopSellInfo[] info = GetSellInfo(); + + Hashtable table = new Hashtable(); + + foreach ( IShopSellInfo ssi in info ) + { + Item[] items = pack.FindItemsByType( ssi.Types ); + + foreach ( Item item in items ) + { + if ( item is Container && ((Container)item).Items.Count != 0 ) + continue; + + if ( item.IsStandardLoot() && item.Movable && ssi.IsSellable( item ) ) + table[item] = new SellItemState( item, ssi.GetSellPriceFor( item ), ssi.GetNameFor( item ) ); + } + } + + if ( table.Count > 0 ) + { + SendPacksTo( from ); + + from.Send( new VendorSellList( this, table ) ); + } + else + { + Say( true, "You have nothing I would be interested in." ); + } + } + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is SmallBOD || dropped is LargeBOD ) + { + if ( !IsValidBulkOrder( dropped ) || !SupportsBulkOrders( from ) ) + { + SayTo( from, 1045130 ); // That order is for some other shopkeeper. + return false; + } + else if ( (dropped is SmallBOD && !((SmallBOD)dropped).Complete) || (dropped is LargeBOD && !((LargeBOD)dropped).Complete) ) + { + SayTo( from, 1045131 ); // You have not completed the order yet. + return false; + } + + Item reward; + int gold, fame; + + if ( dropped is SmallBOD ) + ((SmallBOD)dropped).GetRewards( out reward, out gold, out fame ); + else + ((LargeBOD)dropped).GetRewards( out reward, out gold, out fame ); + + from.SendSound( 0x3D ); + + SayTo( from, 1045132 ); // Thank you so much! Here is a reward for your effort. + + if ( reward != null ) + from.AddToBackpack( reward ); + + if ( gold > 1000 ) + from.AddToBackpack( new BankCheck( gold ) ); + else if ( gold > 0 ) + from.AddToBackpack( new Gold( gold ) ); + + Titles.AwardFame( from, fame, true ); + + OnSuccessfulBulkOrderRecieve( from ); + + dropped.Delete(); + return true; + } + + return base.OnDragDrop( from, dropped ); + } + + private GenericBuyInfo LookupDisplayObject( object obj ) + { + IBuyItemInfo[] buyInfo = this.GetBuyInfo(); + + for ( int i = 0; i < buyInfo.Length; ++i ) + { + GenericBuyInfo gbi = buyInfo[i] as GenericBuyInfo; + + if ( gbi.GetDisplayObject() == obj ) + return gbi; + } + + return null; + } + + private void ProcessSinglePurchase( BuyItemResponse buy, IBuyItemInfo bii, ArrayList validBuy, ref int controlSlots, ref bool fullPurchase, ref int totalCost ) + { + int amount = buy.Amount; + + if ( amount > bii.Amount ) + amount = bii.Amount; + + if ( amount <= 0 ) + return; + + int slots = bii.ControlSlots * amount; + + if ( controlSlots >= slots ) + { + controlSlots -= slots; + } + else + { + fullPurchase = false; + return; + } + + totalCost += bii.Price * amount; + validBuy.Add( buy ); + } + + private void ProcessValidPurchase( int amount, IBuyItemInfo bii, Mobile buyer, Container cont ) + { + if ( amount > bii.Amount ) + amount = bii.Amount; + + if ( amount < 1 ) + return; + + bii.Amount -= amount; + + object o = bii.GetObject(); + + if ( o is Item ) + { + Item item = (Item)o; + + if ( item.Stackable ) + { + item.Amount = amount; + + if ( cont == null || !cont.TryDropItem( buyer, item, false ) ) + item.MoveToWorld( buyer.Location, buyer.Map ); + } + else + { + item.Amount = 1; + + if ( cont == null || !cont.TryDropItem( buyer, item, false ) ) + item.MoveToWorld( buyer.Location, buyer.Map ); + + for (int i=1;i item.Amount ) + amount = item.Amount; + + if ( amount <= 0 ) + continue; + + foreach ( IShopSellInfo ssi in info ) + { + if ( ssi.IsSellable( item ) ) + { + if ( ssi.IsResellable( item ) ) + { + totalCost += ssi.GetBuyPriceFor( item ) * amount; + validBuy.Add( buy ); + break; + } + } + } + } + } + else if ( ser.IsMobile ) + { + Mobile mob = World.FindMobile( ser ); + + if ( mob == null ) + continue; + + GenericBuyInfo gbi = LookupDisplayObject( mob ); + + if ( gbi != null ) + ProcessSinglePurchase( buy, gbi, validBuy, ref controlSlots, ref fullPurchase, ref totalCost ); + } + }//foreach + + if ( fullPurchase && validBuy.Count == 0 ) + SayTo( buyer, 500190 ); // Thou hast bought nothing! + else if ( validBuy.Count == 0 ) + SayTo( buyer, 500187 ); // Your order cannot be fulfilled, please try again. + + if ( validBuy.Count == 0 ) + return false; + + bought = ( buyer.AccessLevel >= AccessLevel.GameMaster ); + + cont = buyer.Backpack; + if ( !bought && cont != null ) + { + if ( cont.ConsumeTotal( typeof( Gold ), totalCost ) ) + bought = true; + else if ( totalCost < 2000 ) + SayTo( buyer, 500192 );//Begging thy pardon, but thou casnt afford that. + } + + if ( !bought && totalCost >= 2000 ) + { + cont = buyer.BankBox; + if ( cont != null && cont.ConsumeTotal( typeof( Gold ), totalCost ) ) + { + bought = true; + fromBank = true; + } + else + { + SayTo( buyer, 500191 ); //Begging thy pardon, but thy bank account lacks these funds. + } + } + + if ( !bought ) + return false; + else + buyer.PlaySound( 0x32 ); + + cont = buyer.Backpack; + if ( cont == null ) + cont = buyer.BankBox; + + foreach ( BuyItemResponse buy in validBuy ) + { + Serial ser = buy.Serial; + int amount = buy.Amount; + + if ( amount < 1 ) + continue; + + if ( ser.IsItem ) + { + Item item = World.FindItem( ser ); + + if ( item == null ) + continue; + + GenericBuyInfo gbi = LookupDisplayObject( item ); + + if ( gbi != null ) + { + ProcessValidPurchase( amount, gbi, buyer, cont ); + } + else + { + if ( amount > item.Amount ) + amount = item.Amount; + + foreach ( IShopSellInfo ssi in info ) + { + if ( ssi.IsSellable( item ) ) + { + if ( ssi.IsResellable( item ) ) + { + Item buyItem; + if ( amount >= item.Amount ) + { + buyItem = item; + } + else + { + buyItem = Mobile.LiftItemDupe( item, item.Amount - amount ); + + if ( buyItem == null ) + buyItem = item; + } + + if ( cont == null || !cont.TryDropItem( buyer, buyItem, false ) ) + buyItem.MoveToWorld( buyer.Location, buyer.Map ); + + break; + } + } + } + } + } + else if ( ser.IsMobile ) + { + Mobile mob = World.FindMobile( ser ); + + if ( mob == null ) + continue; + + GenericBuyInfo gbi = LookupDisplayObject( mob ); + + if ( gbi != null ) + ProcessValidPurchase( amount, gbi, buyer, cont ); + } + }//foreach + + if ( fullPurchase ) + { + if ( buyer.AccessLevel >= AccessLevel.GameMaster ) + SayTo( buyer, true, "I would not presume to charge thee anything. Here are the goods you requested." ); + else if ( fromBank ) + SayTo( buyer, true, "The total of thy purchase is {0} gold, which has been withdrawn from your bank account. My thanks for the patronage.", totalCost ); + else + SayTo( buyer, true, "The total of thy purchase is {0} gold. My thanks for the patronage.", totalCost ); + } + else + { + if ( buyer.AccessLevel >= AccessLevel.GameMaster ) + SayTo( buyer, true, "I would not presume to charge thee anything. Unfortunately, I could not sell you all the goods you requested." ); + else if ( fromBank ) + SayTo( buyer, true, "The total of thy purchase is {0} gold, which has been withdrawn from your bank account. My thanks for the patronage. Unfortunately, I could not sell you all the goods you requested.", totalCost ); + else + SayTo( buyer, true, "The total of thy purchase is {0} gold. My thanks for the patronage. Unfortunately, I could not sell you all the goods you requested.", totalCost ); + } + + return true; + } + + public virtual bool CheckVendorAccess( Mobile from ) + { + GuardedRegion reg = (GuardedRegion) this.Region.GetRegion( typeof( GuardedRegion ) ); + + if ( reg != null && !reg.CheckVendorAccess( this, from ) ) + return false; + + if ( this.Region != from.Region ) + { + reg = (GuardedRegion) from.Region.GetRegion( typeof( GuardedRegion ) ); + + if ( reg != null && !reg.CheckVendorAccess( this, from ) ) + return false; + } + + return true; + } + + public virtual bool OnSellItems( Mobile seller, ArrayList list ) + { + if ( !IsActiveBuyer ) + return false; + + if ( !seller.CheckAlive() ) + return false; + + if ( !CheckVendorAccess( seller ) ) + { + Say( 501522 ); // I shall not treat with scum like thee! + return false; + } + + seller.PlaySound( 0x32 ); + + IShopSellInfo[] info = GetSellInfo(); + IBuyItemInfo[] buyInfo = this.GetBuyInfo(); + int GiveGold = 0; + int Sold = 0; + Container cont; + ArrayList delete = new ArrayList(); + ArrayList drop = new ArrayList(); + + foreach ( SellItemResponse resp in list ) + { + if ( resp.Item.RootParent != seller || resp.Amount <= 0 || !resp.Item.IsStandardLoot() || !resp.Item.Movable || (resp.Item is Container && ((Container)resp.Item).Items.Count != 0) ) + continue; + + foreach( IShopSellInfo ssi in info ) + { + if ( ssi.IsSellable( resp.Item ) ) + { + Sold++; + break; + } + } + } + + if ( Sold > MaxSell ) + { + SayTo( seller, true, "You may only sell {0} items at a time!", MaxSell ); + return false; + } + else if ( Sold == 0 ) + { + return true; + } + + foreach ( SellItemResponse resp in list ) + { + if ( resp.Item.RootParent != seller || resp.Amount <= 0 || !resp.Item.IsStandardLoot() || !resp.Item.Movable || (resp.Item is Container && ((Container)resp.Item).Items.Count != 0) ) + continue; + + foreach( IShopSellInfo ssi in info ) + { + if ( ssi.IsSellable( resp.Item ) ) + { + int amount = resp.Amount; + + if ( amount > resp.Item.Amount ) + amount = resp.Item.Amount; + + if ( ssi.IsResellable( resp.Item ) ) + { + bool found = false; + + foreach ( IBuyItemInfo bii in buyInfo ) + { + if ( bii.Restock( resp.Item, amount ) ) + { + resp.Item.Consume( amount ); + found = true; + + break; + } + } + + if ( !found ) + { + cont = this.BuyPack; + + if ( amount < resp.Item.Amount ) + { + Item item = Mobile.LiftItemDupe( resp.Item, resp.Item.Amount - amount ); + + if ( item != null ) + { + item.SetLastMoved(); + cont.DropItem( item ); + } + else + { + resp.Item.SetLastMoved(); + cont.DropItem( resp.Item ); + } + } + else + { + resp.Item.SetLastMoved(); + cont.DropItem( resp.Item ); + } + } + } + else + { + if ( amount < resp.Item.Amount ) + resp.Item.Amount -= amount; + else + resp.Item.Delete(); + } + + GiveGold += ssi.GetSellPriceFor( resp.Item )*amount; + break; + } + } + } + + if ( GiveGold > 0 ) + { + while ( GiveGold > 60000 ) + { + seller.AddToBackpack( new Gold( 60000 ) ); + GiveGold -= 60000; + } + + seller.AddToBackpack( new Gold( GiveGold ) ); + + seller.PlaySound( 0x0037 );//Gold dropping sound + + if ( SupportsBulkOrders( seller ) ) + { + Item bulkOrder = CreateBulkOrder( seller, false ); + + if ( bulkOrder is LargeBOD ) + seller.SendGump( new LargeBODAcceptGump( seller, (LargeBOD)bulkOrder ) ); + else if ( bulkOrder is SmallBOD ) + seller.SendGump( new SmallBODAcceptGump( seller, (SmallBOD)bulkOrder ) ); + } + } + //no cliloc for this? + //SayTo( seller, true, "Thank you! I bought {0} item{1}. Here is your {2}gp.", Sold, (Sold > 1 ? "s" : ""), GiveGold ); + + return true; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + ArrayList sbInfos = this.SBInfos; + + for ( int i = 0; sbInfos != null && i < sbInfos.Count; ++i ) + { + SBInfo sbInfo = (SBInfo)sbInfos[i]; + ArrayList buyInfo = sbInfo.BuyInfo; + + for ( int j = 0; buyInfo != null && j < buyInfo.Count; ++j ) + { + GenericBuyInfo gbi = (GenericBuyInfo)buyInfo[j]; + + int maxAmount = gbi.MaxAmount; + int doubled = 0; + + switch ( maxAmount ) + { + case 40: doubled = 1; break; + case 80: doubled = 2; break; + case 160: doubled = 3; break; + case 320: doubled = 4; break; + case 640: doubled = 5; break; + case 999: doubled = 6; break; + } + + if ( doubled > 0 ) + { + writer.WriteEncodedInt( 1 + ((j * sbInfos.Count) + i) ); + writer.WriteEncodedInt( doubled ); + } + } + } + + writer.WriteEncodedInt( 0 ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + LoadSBInfo(); + + ArrayList sbInfos = this.SBInfos; + + switch ( version ) + { + case 1: + { + int index; + + while ( (index = reader.ReadEncodedInt()) > 0 ) + { + int doubled = reader.ReadEncodedInt(); + + if ( sbInfos != null ) + { + index -= 1; + int sbInfoIndex = index % sbInfos.Count; + int buyInfoIndex = index / sbInfos.Count; + + if ( sbInfoIndex >= 0 && sbInfoIndex < sbInfos.Count ) + { + SBInfo sbInfo = (SBInfo)sbInfos[sbInfoIndex]; + ArrayList buyInfo = sbInfo.BuyInfo; + + if ( buyInfo != null && buyInfoIndex >= 0 && buyInfoIndex < buyInfo.Count ) + { + GenericBuyInfo gbi = (GenericBuyInfo)buyInfo[buyInfoIndex]; + + int amount = 20; + + switch ( doubled ) + { + case 1: amount = 40; break; + case 2: amount = 80; break; + case 3: amount = 160; break; + case 4: amount = 320; break; + case 5: amount = 640; break; + case 6: amount = 999; break; + } + + gbi.Amount = gbi.MaxAmount = amount; + } + } + } + } + + break; + } + } + + if ( IsParagon ) + IsParagon = false; + + if ( Core.AOS && NameHue == 0x35 ) + NameHue = -1; + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( CheckMorph ) ); + } + + public override void AddCustomContextEntries( Mobile from, List list ) + { + if ( from.Alive && IsActiveVendor ) + { + if ( IsActiveSeller ) + list.Add( new VendorBuyEntry( from, this ) ); + + if ( IsActiveBuyer ) + list.Add( new VendorSellEntry( from, this ) ); + + if ( SupportsBulkOrders( from ) ) + list.Add( new BulkOrderInfoEntry( from, this ) ); + } + + base.AddCustomContextEntries( from, list ); + } + + public virtual IShopSellInfo[] GetSellInfo() + { + return (IShopSellInfo[])m_ArmorSellInfo.ToArray( typeof( IShopSellInfo ) ); + } + + public virtual IBuyItemInfo[] GetBuyInfo() + { + return (IBuyItemInfo[])m_ArmorBuyInfo.ToArray( typeof( IBuyItemInfo ) ); + } + + public override bool CanBeDamaged() + { + return !IsInvulnerable; + } + } +} + +namespace Server.ContextMenus +{ + public class VendorBuyEntry : ContextMenuEntry + { + private BaseVendor m_Vendor; + + public VendorBuyEntry( Mobile from, BaseVendor vendor ) : base( 6103, 8 ) + { + m_Vendor = vendor; + Enabled = vendor.CheckVendorAccess( from ); + } + + public override void OnClick() + { + m_Vendor.VendorBuy( this.Owner.From ); + } + } + + public class VendorSellEntry : ContextMenuEntry + { + private BaseVendor m_Vendor; + + public VendorSellEntry( Mobile from, BaseVendor vendor ) : base( 6104, 8 ) + { + m_Vendor = vendor; + Enabled = vendor.CheckVendorAccess( from ); + } + + public override void OnClick() + { + m_Vendor.VendorSell( this.Owner.From ); + } + } +} + +namespace Server +{ + public interface IShopSellInfo + { + //get display name for an item + string GetNameFor( Item item ); + + //get price for an item which the player is selling + int GetSellPriceFor( Item item ); + + //get price for an item which the player is buying + int GetBuyPriceFor( Item item ); + + //can we sell this item to this vendor? + bool IsSellable( Item item ); + + //What do we sell? + Type[] Types{ get; } + + //does the vendor resell this item? + bool IsResellable( Item item ); + } + + public interface IBuyItemInfo + { + //get a new instance of an object (we just bought it) + object GetObject(); + + int ControlSlots{ get; } + + int PriceScalar{ get; set; } + + //display price of the item + int Price{ get; } + + //display name of the item + string Name{ get; } + + //display hue + int Hue{ get; } + + //display id + int ItemID{ get; } + + //amount in stock + int Amount{ get; set; } + + //max amount in stock + int MaxAmount{ get; } + + //Attempt to restock with item, (return true if restock sucessful) + bool Restock( Item item, int amount ); + + //called when its time for the whole shop to restock + void OnRestock(); + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/BeverageBuy.cs b/Scripts/Mobiles/Vendors/BeverageBuy.cs new file mode 100644 index 000000000..defc2fd6a --- /dev/null +++ b/Scripts/Mobiles/Vendors/BeverageBuy.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class BeverageBuyInfo : GenericBuyInfo + { + private BeverageType m_Content; + + public override bool CanCacheDisplay{ get{ return false; } } + + public BeverageBuyInfo( Type type, BeverageType content, int price, int amount, int itemID, int hue ) : this( null, type, content, price, amount, itemID, hue ) + { + } + + public BeverageBuyInfo( string name, Type type, BeverageType content, int price, int amount, int itemID, int hue ) : base( name, type, price, amount, itemID, hue ) + { + m_Content = content; + + if ( type == typeof( Pitcher ) ) + Name = (1048128 + (int)content).ToString(); + else if ( type == typeof( BeverageBottle ) ) + Name = (1042959 + (int)content).ToString(); + else if ( type == typeof( Jug ) ) + Name = (1042965 + (int)content).ToString(); + } + + public override object GetObject() + { + return Activator.CreateInstance( Type, new object[]{ m_Content } ); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/GenericBuy.cs b/Scripts/Mobiles/Vendors/GenericBuy.cs new file mode 100644 index 000000000..94aadc2e6 --- /dev/null +++ b/Scripts/Mobiles/Vendors/GenericBuy.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + public class GenericBuyInfo : IBuyItemInfo + { + private class DisplayCache : Container + { + private static DisplayCache m_Cache; + + public static DisplayCache Cache + { + get + { + if ( m_Cache == null || m_Cache.Deleted ) + m_Cache = new DisplayCache(); + + return m_Cache; + } + } + + private Hashtable m_Table; + private ArrayList m_Mobiles; + + public DisplayCache() : base( 0 ) + { + m_Table = new Hashtable(); + m_Mobiles = new ArrayList(); + } + + public object Lookup( Type key ) + { + return m_Table[key]; + } + + public void Store( Type key, object obj, bool cache ) + { + if ( cache ) + m_Table[key] = obj; + + if ( obj is Item ) + AddItem( (Item) obj ); + else if ( obj is Mobile ) + m_Mobiles.Add( obj ); + } + + public DisplayCache( Serial serial ) : base( serial ) + { + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + for ( int i = 0; i < m_Mobiles.Count; ++i ) + ((Mobile)m_Mobiles[i]).Delete(); + + m_Mobiles.Clear(); + + for ( int i = Items.Count - 1; i >= 0; --i ) + { + if ( i < Items.Count ) + ((Item)Items[i]).Delete(); + } + + if ( m_Cache == this ) + m_Cache = null; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteMobileList( m_Mobiles, true ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + m_Mobiles = reader.ReadMobileList(); + + for ( int i = 0; i < m_Mobiles.Count; ++i ) + ((Mobile)m_Mobiles[i]).Delete(); + + m_Mobiles.Clear(); + + for ( int i = Items.Count - 1; i >= 0; --i ) + { + if ( i < Items.Count ) + ((Item)Items[i]).Delete(); + } + + if ( m_Cache == null ) + m_Cache = this; + else + Delete(); + + m_Table = new Hashtable(); + } + } + + private Type m_Type; + private string m_Name; + private int m_Price; + private int m_MaxAmount, m_Amount; + private int m_ItemID; + private int m_Hue; + private object[] m_Args; + private object m_DisplayObject; + + public virtual int ControlSlots{ get{ return 0; } } + + public virtual bool CanCacheDisplay{ get{ return false; } } //return ( m_Args == null || m_Args.Length == 0 ); } + + private bool IsDeleted( object obj ) + { + if ( obj is Item ) + return (obj as Item).Deleted; + else if ( obj is Mobile ) + return (obj as Mobile).Deleted; + + return false; + } + + public void DeleteDisplayObject() + { + if ( m_DisplayObject is Item ) + (m_DisplayObject as Item).Delete(); + else if ( m_DisplayObject is Mobile ) + (m_DisplayObject as Mobile).Delete(); + + m_DisplayObject = null; + } + + public object GetDisplayObject() + { + if ( m_DisplayObject != null && !IsDeleted( m_DisplayObject ) ) + return m_DisplayObject; + + bool canCache = this.CanCacheDisplay; + + if ( canCache ) + m_DisplayObject = DisplayCache.Cache.Lookup( m_Type ); + + if ( m_DisplayObject == null || IsDeleted( m_DisplayObject ) ) + m_DisplayObject = GetObject(); + + DisplayCache.Cache.Store( m_Type, m_DisplayObject, canCache ); + + return m_DisplayObject; + } + + public Type Type + { + get{ return m_Type; } + set{ m_Type = value; } + } + + public string Name + { + get{ return m_Name; } + set{ m_Name = value; } + } + + public int DefaultPrice{ get{ return m_PriceScalar; } } + + private int m_PriceScalar; + + public int PriceScalar + { + get{ return m_PriceScalar; } + set{ m_PriceScalar = value; } + } + + public int Price + { + get + { + if ( m_PriceScalar != 0 ) + { + if ( m_Price > 5000000 ) + { + long price = m_Price; + + price *= m_PriceScalar; + price += 50; + price /= 100; + + if ( price > int.MaxValue ) + price = int.MaxValue; + + return (int)price; + } + + return ( ((m_Price * m_PriceScalar) + 50) / 100 ); + } + + return m_Price; + } + set{ m_Price = value; } + } + + public int ItemID + { + get{ return m_ItemID; } + set{ m_ItemID = value; } + } + + public int Hue + { + get{ return m_Hue; } + set{ m_Hue = value; } + } + + public int Amount + { + get{ return m_Amount; } + set{ if ( value < 0 ) value = 0; m_Amount = value; } + } + + public int MaxAmount + { + get{ return m_MaxAmount; } + set{ m_MaxAmount = value; } + } + + public object[] Args + { + get{ return m_Args; } + set{ m_Args = value; } + } + + public GenericBuyInfo( Type type, int price, int amount, int itemID, int hue ) : this( null, type, price, amount, itemID, hue, null ) + { + } + + public GenericBuyInfo( string name, Type type, int price, int amount, int itemID, int hue ) : this( name, type, price, amount, itemID, hue, null ) + { + } + + public GenericBuyInfo( Type type, int price, int amount, int itemID, int hue, object[] args ) : this( null, type, price, amount, itemID, hue, args ) + { + } + + public GenericBuyInfo( string name, Type type, int price, int amount, int itemID, int hue, object[] args ) + { + amount = 20; + + m_Type = type; + m_Price = price; + m_MaxAmount = m_Amount = amount; + m_ItemID = itemID; + m_Hue = hue; + m_Args = args; + + if ( name == null ) + m_Name = (1020000 + (itemID & 0x3FFF)).ToString(); + else + m_Name = name; + } + + //get a new instance of an object (we just bought it) + public virtual object GetObject() + { + if ( m_Args == null || m_Args.Length == 0 ) + return Activator.CreateInstance( m_Type ); + + return Activator.CreateInstance( m_Type, m_Args ); + //return (Item)Activator.CreateInstance( m_Type ); + } + + //Attempt to restock with item, (return true if restock sucessful) + public bool Restock( Item item, int amount ) + { + return false; + /*if ( item.GetType() == m_Type ) + { + if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( weapon.Quality == WeaponQuality.Low || weapon.Quality == WeaponQuality.Exceptional || (int)weapon.DurabilityLevel > 0 || (int)weapon.DamageLevel > 0 || (int)weapon.AccuracyLevel > 0 ) + return false; + } + + if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( armor.Quality == ArmorQuality.Low || armor.Quality == ArmorQuality.Exceptional || (int)armor.Durability > 0 || (int)armor.ProtectionLevel > 0 ) + return false; + } + + m_Amount += amount; + + return true; + } + else + { + return false; + }*/ + } + + public void OnRestock() + { + if ( m_Amount <= 0 ) + { + m_MaxAmount *= 2; + + if ( m_MaxAmount >= 999 ) + m_MaxAmount = 999; + } + else + { + /* NOTE: According to UO.com, the quantity is halved if the item does not reach 0 + * Here we implement differently: the quantity is halved only if less than half + * of the maximum quantity was bought. That is, if more than half is sold, then + * there's clearly a demand and we should not cut down on the stock. + */ + + int halfQuantity = m_MaxAmount; + + if ( halfQuantity >= 999 ) + halfQuantity = 640; + else if ( halfQuantity > 20 ) + halfQuantity /= 2; + + if ( m_Amount >= halfQuantity ) + m_MaxAmount = halfQuantity; + } + + m_Amount = m_MaxAmount; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/GenericSell.cs b/Scripts/Mobiles/Vendors/GenericSell.cs new file mode 100644 index 000000000..3ffc19d1f --- /dev/null +++ b/Scripts/Mobiles/Vendors/GenericSell.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class GenericSellInfo : IShopSellInfo + { + private Hashtable m_Table = new Hashtable(); + private Type[] m_Types; + + public GenericSellInfo() + { + } + + public void Add( Type type, int price ) + { + m_Table[type] = price; + m_Types = null; + } + + public int GetSellPriceFor( Item item ) + { + int price = (int)m_Table[item.GetType()]; + + if ( item is BaseArmor ) + { + BaseArmor armor = (BaseArmor)item; + + if ( armor.Quality == ArmorQuality.Low ) + price = (int)( price * 0.60 ); + else if ( armor.Quality == ArmorQuality.Exceptional ) + price = (int)( price * 1.25 ); + + price += 100 * (int)armor.Durability; + + price += 100 * (int)armor.ProtectionLevel; + + if ( price < 1 ) + price = 1; + } + + else if ( item is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)item; + + if ( weapon.Quality == WeaponQuality.Low ) + price = (int)( price * 0.60 ); + else if ( weapon.Quality == WeaponQuality.Exceptional ) + price = (int)( price * 1.25 ); + + price += 100 * (int)weapon.DurabilityLevel; + + price += 100 * (int)weapon.DamageLevel; + + if ( price < 1 ) + price = 1; + } + else if ( item is BaseBeverage ) + { + int price1 = price, price2 = price; + + if ( item is Pitcher ) + { price1 = 3; price2 = 5; } + else if ( item is BeverageBottle ) + { price1 = 3; price2 = 3; } + else if ( item is Jug ) + { price1 = 6; price2 = 6; } + + BaseBeverage bev = (BaseBeverage)item; + + if ( bev.IsEmpty || bev.Content == BeverageType.Milk ) + price = price1; + else + price = price2; + } + + return price; + } + + public int GetBuyPriceFor( Item item ) + { + return (int)( 1.90 * GetSellPriceFor( item ) ); + } + + public Type[] Types + { + get + { + if ( m_Types == null ) + { + m_Types = new Type[m_Table.Keys.Count]; + m_Table.Keys.CopyTo( m_Types, 0 ); + } + + return m_Types; + } + } + + public string GetNameFor( Item item ) + { + if ( item.Name != null ) + return item.Name; + else + return item.LabelNumber.ToString(); + } + + public bool IsSellable( Item item ) + { + //if ( item.Hue != 0 ) + //return false; + + return IsInList( item.GetType() ); + } + + public bool IsResellable( Item item ) + { + //if ( item.Hue != 0 ) + //return false; + + return IsInList( item.GetType() ); + } + + public bool IsInList( Type type ) + { + Object o = m_Table[type]; + + if ( o == null ) + return false; + else + return true; + } + } +} diff --git a/Scripts/Mobiles/Vendors/NPC/Alchemist.cs b/Scripts/Mobiles/Vendors/NPC/Alchemist.cs new file mode 100644 index 000000000..373f3a1aa --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Alchemist.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Alchemist : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.MagesGuild; } } + + [Constructable] + public Alchemist() : base( "the alchemist" ) + { + SetSkill( SkillName.Alchemy, 85.0, 100.0 ); + SetSkill( SkillName.TasteID, 65.0, 88.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBAlchemist() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Robe( Utility.RandomPinkHue() ) ); + } + + public Alchemist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/AnimalTrainer.cs b/Scripts/Mobiles/Vendors/NPC/AnimalTrainer.cs new file mode 100644 index 000000000..fc8ec2e34 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/AnimalTrainer.cs @@ -0,0 +1,426 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.ContextMenus; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Targeting; + +namespace Server.Mobiles +{ + public class AnimalTrainer : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public AnimalTrainer() : base( "the animal trainer" ) + { + SetSkill( SkillName.AnimalLore, 64.0, 100.0 ); + SetSkill( SkillName.AnimalTaming, 90.0, 100.0 ); + SetSkill( SkillName.Veterinary, 65.0, 88.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBAnimalTrainer() ); + } + + public override VendorShoeType ShoeType + { + get{ return Female ? VendorShoeType.ThighBoots : VendorShoeType.Boots; } + } + + public override int GetShoeHue() + { + return 0; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( Utility.RandomBool() ? (Item)new QuarterStaff() : (Item)new ShepherdsCrook() ); + } + + private class StableEntry : ContextMenuEntry + { + private AnimalTrainer m_Trainer; + private Mobile m_From; + + public StableEntry( AnimalTrainer trainer, Mobile from ) : base( 6126, 12 ) + { + m_Trainer = trainer; + m_From = from; + } + + public override void OnClick() + { + m_Trainer.BeginStable( m_From ); + } + } + + private class ClaimListGump : Gump + { + private AnimalTrainer m_Trainer; + private Mobile m_From; + private List m_List; + + public ClaimListGump( AnimalTrainer trainer, Mobile from, List list ) : base( 50, 50 ) + { + m_Trainer = trainer; + m_From = from; + m_List = list; + + from.CloseGump( typeof( ClaimListGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 325, 50 + (list.Count * 20), 9250 ); + AddAlphaRegion( 5, 5, 315, 40 + (list.Count * 20) ); + + AddHtml( 15, 15, 275, 20, "Select a pet to retrieve from the stables:", false, false ); + + for ( int i = 0; i < list.Count; ++i ) + { + BaseCreature pet = list[i]; + + if ( pet == null || pet.Deleted ) + continue; + + AddButton( 15, 39 + (i * 20), 10006, 10006, i + 1, GumpButtonType.Reply, 0 ); + AddHtml( 32, 35 + (i * 20), 275, 18, String.Format( "{0}", pet.Name ), false, false ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_List.Count ) + m_Trainer.EndClaimList( m_From, m_List[index] ); + } + } + + private class ClaimAllEntry : ContextMenuEntry + { + private AnimalTrainer m_Trainer; + private Mobile m_From; + + public ClaimAllEntry( AnimalTrainer trainer, Mobile from ) : base( 6127, 12 ) + { + m_Trainer = trainer; + m_From = from; + } + + public override void OnClick() + { + m_Trainer.Claim( m_From ); + } + } + + public override void AddCustomContextEntries( Mobile from, List list ) + { + if ( from.Alive ) + { + list.Add( new StableEntry( this, from ) ); + + if ( from.Stabled.Count > 0 ) + list.Add( new ClaimAllEntry( this, from ) ); + } + + base.AddCustomContextEntries( from, list ); + } + + public static int GetMaxStabled( Mobile from ) + { + double taming = from.Skills[SkillName.AnimalTaming].Value; + double anlore = from.Skills[SkillName.AnimalLore].Value; + double vetern = from.Skills[SkillName.Veterinary].Value; + double sklsum = taming + anlore + vetern; + + int max; + + if ( sklsum >= 240.0 ) + max = 5; + else if ( sklsum >= 200.0 ) + max = 4; + else if ( sklsum >= 160.0 ) + max = 3; + else + max = 2; + + if ( taming >= 100.0 ) + max += (int)((taming - 90.0) / 10); + + if ( anlore >= 100.0 ) + max += (int)((anlore - 90.0) / 10); + + if ( vetern >= 100.0 ) + max += (int)((vetern - 90.0) / 10); + + return max; + } + + private class StableTarget : Target + { + private AnimalTrainer m_Trainer; + + public StableTarget( AnimalTrainer trainer ) : base( 12, false, TargetFlags.None ) + { + m_Trainer = trainer; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is BaseCreature ) + m_Trainer.EndStable( from, (BaseCreature)targeted ); + else if ( targeted == from ) + m_Trainer.SayTo( from, 502672 ); // HA HA HA! Sorry, I am not an inn. + else + m_Trainer.SayTo( from, 1048053 ); // You can't stable that! + } + } + + public void BeginClaimList( Mobile from ) + { + if ( Deleted || !from.CheckAlive() ) + return; + + List list = new List(); + + for ( int i = 0; i < from.Stabled.Count; ++i ) + { + BaseCreature pet = from.Stabled[i] as BaseCreature; + + if ( pet == null || pet.Deleted ) + { + pet.IsStabled = false; + from.Stabled.RemoveAt( i ); + --i; + continue; + } + + list.Add( pet ); + } + + if ( list.Count > 0 ) + from.SendGump( new ClaimListGump( this, from, list ) ); + else + SayTo( from, 502671 ); // But I have no animals stabled with me at the moment! + } + + public void EndClaimList( Mobile from, BaseCreature pet ) + { + if ( pet == null || pet.Deleted || from.Map != this.Map || !from.InRange( this, 14 ) || !from.Stabled.Contains( pet ) || !from.CheckAlive() ) + return; + + if ( (from.Followers + pet.ControlSlots) <= from.FollowersMax ) + { + pet.SetControlMaster( from ); + + if ( pet.Summoned ) + pet.SummonMaster = from; + + pet.ControlTarget = from; + pet.ControlOrder = OrderType.Follow; + + pet.MoveToWorld( from.Location, from.Map ); + + pet.IsStabled = false; + from.Stabled.Remove( pet ); + + SayTo( from, 1042559 ); // Here you go... and good day to you! + } + else + { + SayTo( from, 1049612, pet.Name ); // ~1_NAME~ remained in the stables because you have too many followers. + } + } + + public void BeginStable( Mobile from ) + { + if ( Deleted || !from.CheckAlive() ) + return; + + if ( from.Stabled.Count >= GetMaxStabled( from ) ) + { + SayTo( from, 1042565 ); // You have too many pets in the stables! + } + else + { + SayTo( from, 1042558 ); /* I charge 30 gold per pet for a real week's stable time. + * I will withdraw it from thy bank account. + * Which animal wouldst thou like to stable here? + */ + + from.Target = new StableTarget( this ); + } + } + + public void EndStable( Mobile from, BaseCreature pet ) + { + if ( Deleted || !from.CheckAlive() ) + return; + + if ( !pet.Controlled || pet.ControlMaster != from ) + { + SayTo( from, 1042562 ); // You do not own that pet! + } + else if ( pet.IsDeadPet ) + { + SayTo( from, 1049668 ); // Living pets only, please. + } + else if ( pet.Summoned ) + { + SayTo( from, 502673 ); // I can not stable summoned creatures. + } + else if ( pet.Body.IsHuman ) + { + SayTo( from, 502672 ); // HA HA HA! Sorry, I am not an inn. + } + else if ( (pet is PackLlama || pet is PackHorse || pet is Beetle) && (pet.Backpack != null && pet.Backpack.Items.Count > 0) ) + { + SayTo( from, 1042563 ); // You need to unload your pet. + } + else if ( pet.Combatant != null && pet.InRange( pet.Combatant, 12 ) && pet.Map == pet.Combatant.Map ) + { + SayTo( from, 1042564 ); // I'm sorry. Your pet seems to be busy. + } + else if ( from.Stabled.Count >= GetMaxStabled( from ) ) + { + SayTo( from, 1042565 ); // You have too many pets in the stables! + } + else + { + Container bank = from.BankBox; + + if ( bank != null && bank.ConsumeTotal( typeof( Gold ), 30 ) ) + { + pet.ControlTarget = null; + pet.ControlOrder = OrderType.Stay; + pet.Internalize(); + + pet.SetControlMaster( null ); + pet.SummonMaster = null; + + pet.IsStabled = true; + + if ( Core.SE ) + pet.Loyalty = BaseCreature.MaxLoyalty; // Wonderfully happy + + from.Stabled.Add( pet ); + + SayTo( from, 502679 ); // Very well, thy pet is stabled. Thou mayst recover it by saying 'claim' to me. In one real world week, I shall sell it off if it is not claimed! + } + else + { + SayTo( from, 502677 ); // But thou hast not the funds in thy bank account! + } + } + } + + public void Claim( Mobile from ) + { + if ( Deleted || !from.CheckAlive() ) + return; + + bool claimed = false; + int stabled = 0; + + for ( int i = 0; i < from.Stabled.Count; ++i ) + { + BaseCreature pet = from.Stabled[i] as BaseCreature; + + if ( pet == null || pet.Deleted ) + { + pet.IsStabled = false; + from.Stabled.RemoveAt( i ); + --i; + continue; + } + + ++stabled; + + if ( (from.Followers + pet.ControlSlots) <= from.FollowersMax ) + { + pet.SetControlMaster( from ); + + if ( pet.Summoned ) + pet.SummonMaster = from; + + pet.ControlTarget = from; + pet.ControlOrder = OrderType.Follow; + + pet.MoveToWorld( from.Location, from.Map ); + + pet.IsStabled = false; + + if ( Core.SE ) + pet.Loyalty = BaseCreature.MaxLoyalty; // Wonderfully Happy + + from.Stabled.RemoveAt( i ); + --i; + + claimed = true; + } + else + { + SayTo( from, 1049612, pet.Name ); // ~1_NAME~ remained in the stables because you have too many followers. + } + } + + if ( claimed ) + SayTo( from, 1042559 ); // Here you go... and good day to you! + else if ( stabled == 0 ) + SayTo( from, 502671 ); // But I have no animals stabled with me at the moment! + } + + public override bool HandlesOnSpeech( Mobile from ) + { + return true; + } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( !e.Handled && e.HasKeyword( 0x0008 ) ) + { + e.Handled = true; + BeginStable( e.Mobile ); + } + else if ( !e.Handled && e.HasKeyword( 0x0009 ) ) + { + e.Handled = true; + + if ( !Insensitive.Equals( e.Speech, "claim" ) ) + BeginClaimList( e.Mobile ); + else + Claim( e.Mobile ); + } + else + { + base.OnSpeech( e ); + } + } + + public AnimalTrainer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Architect.cs b/Scripts/Mobiles/Vendors/NPC/Architect.cs new file mode 100644 index 000000000..cae03ed6d --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Architect.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Architect : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.TinkersGuild; } } + + [Constructable] + public Architect() : base( "the architect" ) + { + } + + public override void InitSBInfo() + { + if ( !Core.AOS ) + m_SBInfos.Add( new SBHouseDeed() ); + + m_SBInfos.Add( new SBArchitect() ); + } + + public Architect( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Armorer.cs b/Scripts/Mobiles/Vendors/NPC/Armorer.cs new file mode 100644 index 000000000..1a4edc1af --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Armorer.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Armorer : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Armorer() : base( "the armorer" ) + { + SetSkill( SkillName.ArmsLore, 64.0, 100.0 ); + SetSkill( SkillName.Blacksmith, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + switch ( Utility.Random( 4 )) + { + case 0: + { + m_SBInfos.Add( new SBLeatherArmor() ); + m_SBInfos.Add( new SBStuddedArmor() ); + m_SBInfos.Add( new SBMetalShields() ); + m_SBInfos.Add( new SBPlateArmor() ); + m_SBInfos.Add( new SBHelmetArmor() ); + m_SBInfos.Add( new SBChainmailArmor() ); + m_SBInfos.Add( new SBRingmailArmor() ); + break; + } + case 1: + { + m_SBInfos.Add( new SBStuddedArmor() ); + m_SBInfos.Add( new SBLeatherArmor() ); + m_SBInfos.Add( new SBMetalShields() ); + m_SBInfos.Add( new SBHelmetArmor() ); + break; + } + case 2: + { + m_SBInfos.Add( new SBMetalShields() ); + m_SBInfos.Add( new SBPlateArmor() ); + m_SBInfos.Add( new SBHelmetArmor() ); + m_SBInfos.Add( new SBChainmailArmor() ); + m_SBInfos.Add( new SBRingmailArmor() ); + break; + } + case 3: + { + m_SBInfos.Add( new SBMetalShields() ); + m_SBInfos.Add( new SBHelmetArmor() ); + break; + } + } + if ( IsTokunoVendor ) + { + m_SBInfos.Add( new SBSELeatherArmor() ); + m_SBInfos.Add( new SBSEArmor() ); + } + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.Boots; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron( Utility.RandomYellowHue() ) ); + AddItem( new Server.Items.Bascinet() ); + } + + public Armorer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Baker.cs b/Scripts/Mobiles/Vendors/NPC/Baker.cs new file mode 100644 index 000000000..76d0af6ba --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Baker.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Baker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Baker() : base( "the baker" ) + { + SetSkill( SkillName.Cooking, 75.0, 98.0 ); + SetSkill( SkillName.TasteID, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBBaker() ); + } + + public Baker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Bard.cs b/Scripts/Mobiles/Vendors/NPC/Bard.cs new file mode 100644 index 000000000..6b9289df8 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Bard.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Bard : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.BardsGuild; } } + + [Constructable] + public Bard() : base( "the bard" ) + { + SetSkill( SkillName.Discordance, 64.0, 100.0 ); + SetSkill( SkillName.Musicianship, 64.0, 100.0 ); + SetSkill( SkillName.Peacemaking, 65.0, 88.0 ); + SetSkill( SkillName.Provocation, 60.0, 83.0 ); + SetSkill( SkillName.Archery, 36.0, 68.0 ); + SetSkill( SkillName.Swords, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBBard() ); + } + + public Bard( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Barkeeper.cs b/Scripts/Mobiles/Vendors/NPC/Barkeeper.cs new file mode 100644 index 000000000..03450a09f --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Barkeeper.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Prompts; +using Server.Network; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public class Barkeeper : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBBarkeeper() ); + } + + public override VendorShoeType ShoeType{ get{ return Utility.RandomBool() ? VendorShoeType.ThighBoots : VendorShoeType.Boots; } } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new HalfApron( RandomBrightHue() ) ); + } + + [Constructable] + public Barkeeper() : base( "the barkeeper" ) + { + } + + public Barkeeper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Beekeeper.cs b/Scripts/Mobiles/Vendors/NPC/Beekeeper.cs new file mode 100644 index 000000000..81a2f875e --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Beekeeper.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Beekeeper : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Beekeeper() : base( "the beekeeper" ) + { + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBBeekeeper() ); + } + + public override VendorShoeType ShoeType{ get{ return VendorShoeType.Boots; } } + + public Beekeeper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Blacksmith.cs b/Scripts/Mobiles/Vendors/NPC/Blacksmith.cs new file mode 100644 index 000000000..28e47c295 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Blacksmith.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using Server; +using Server.Engines.BulkOrders; + +namespace Server.Mobiles +{ + public class Blacksmith : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.BlacksmithsGuild; } } + + [Constructable] + public Blacksmith() : base( "the blacksmith" ) + { + SetSkill( SkillName.ArmsLore, 36.0, 68.0 ); + SetSkill( SkillName.Blacksmith, 65.0, 88.0 ); + SetSkill( SkillName.Fencing, 60.0, 83.0 ); + SetSkill( SkillName.Macing, 61.0, 93.0 ); + SetSkill( SkillName.Swords, 60.0, 83.0 ); + SetSkill( SkillName.Tactics, 60.0, 83.0 ); + SetSkill( SkillName.Parry, 61.0, 93.0 ); + } + + public override void InitSBInfo() + { + /*m_SBInfos.Add( new SBSmithTools() ); + + m_SBInfos.Add( new SBMetalShields() ); + m_SBInfos.Add( new SBWoodenShields() ); + + m_SBInfos.Add( new SBPlateArmor() ); + + m_SBInfos.Add( new SBHelmetArmor() ); + m_SBInfos.Add( new SBChainmailArmor() ); + m_SBInfos.Add( new SBRingmailArmor() ); + m_SBInfos.Add( new SBAxeWeapon() ); + m_SBInfos.Add( new SBPoleArmWeapon() ); + m_SBInfos.Add( new SBRangedWeapon() ); + + m_SBInfos.Add( new SBKnifeWeapon() ); + m_SBInfos.Add( new SBMaceWeapon() ); + m_SBInfos.Add( new SBSpearForkWeapon() ); + m_SBInfos.Add( new SBSwordWeapon() );*/ + + m_SBInfos.Add( new SBBlacksmith() ); + if ( IsTokunoVendor ) + { + m_SBInfos.Add( new SBSEArmor() ); + m_SBInfos.Add( new SBSEWeapons() ); + } + + + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.None; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + Item item = ( Utility.RandomBool() ? null : new Server.Items.RingmailChest() ); + + if ( item != null && !EquipItem( item ) ) + { + item.Delete(); + item = null; + } + + if ( item == null ) + AddItem( new Server.Items.FullApron() ); + + AddItem( new Server.Items.Bascinet() ); + AddItem( new Server.Items.SmithHammer() ); + } + + #region Bulk Orders + public override Item CreateBulkOrder( Mobile from, bool fromContextMenu ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null && pm.NextSmithBulkOrder == TimeSpan.Zero && (fromContextMenu || 0.2 > Utility.RandomDouble()) ) + { + double theirSkill = pm.Skills[SkillName.Blacksmith].Base; + + if ( theirSkill >= 70.1 ) + pm.NextSmithBulkOrder = TimeSpan.FromHours( 6.0 ); + else if ( theirSkill >= 50.1 ) + pm.NextSmithBulkOrder = TimeSpan.FromHours( 2.0 ); + else + pm.NextSmithBulkOrder = TimeSpan.FromHours( 1.0 ); + + if ( theirSkill >= 70.1 && ((theirSkill - 40.0) / 300.0) > Utility.RandomDouble() ) + return new LargeSmithBOD(); + + return SmallSmithBOD.CreateRandomFor( from ); + } + + return null; + } + + public override bool IsValidBulkOrder( Item item ) + { + return ( item is SmallSmithBOD || item is LargeSmithBOD ); + } + + public override bool SupportsBulkOrders( Mobile from ) + { + return ( from is PlayerMobile && from.Skills[SkillName.Blacksmith].Base > 0 ); + } + + public override TimeSpan GetNextBulkOrder( Mobile from ) + { + if ( from is PlayerMobile ) + return ((PlayerMobile)from).NextSmithBulkOrder; + + return TimeSpan.Zero; + } + + public override void OnSuccessfulBulkOrderRecieve( Mobile from ) + { + if( Core.SE && from is PlayerMobile ) + ((PlayerMobile)from).NextSmithBulkOrder = TimeSpan.Zero; + } + #endregion + + public Blacksmith( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Bowyer.cs b/Scripts/Mobiles/Vendors/NPC/Bowyer.cs new file mode 100644 index 000000000..8ccfb89db --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Bowyer.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + [TypeAlias( "Server.Mobiles.Bower" )] + public class Bowyer : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Bowyer() : base( "the bowyer" ) + { + SetSkill( SkillName.Fletching, 80.0, 100.0 ); + SetSkill( SkillName.Archery, 80.0, 100.0 ); + } + + public override VendorShoeType ShoeType + { + get{ return Female ? VendorShoeType.ThighBoots : VendorShoeType.Boots; } + } + + public override int GetShoeHue() + { + return 0; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Bow() ); + AddItem( new Server.Items.LeatherGorget() ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBBowyer() ); + m_SBInfos.Add( new SBRangedWeapon() ); + + if ( IsTokunoVendor ) + m_SBInfos.Add( new SBSEBowyer() ); + } + + public Bowyer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Butcher.cs b/Scripts/Mobiles/Vendors/NPC/Butcher.cs new file mode 100644 index 000000000..38715195e --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Butcher.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Butcher : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Butcher() : base( "the butcher" ) + { + SetSkill( SkillName.Anatomy, 45.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBButcher() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron() ); + AddItem( new Server.Items.Cleaver() ); + } + + public Butcher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Carpenter.cs b/Scripts/Mobiles/Vendors/NPC/Carpenter.cs new file mode 100644 index 000000000..c93ec2b09 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Carpenter.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Carpenter : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.TinkersGuild; } } + + [Constructable] + public Carpenter() : base( "the carpenter" ) + { + SetSkill( SkillName.Carpentry, 85.0, 100.0 ); + SetSkill( SkillName.Lumberjacking, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBStavesWeapon() ); + m_SBInfos.Add( new SBCarpenter() ); + m_SBInfos.Add( new SBWoodenShields() ); + + if ( IsTokunoVendor ) + m_SBInfos.Add( new SBSECarpenter() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron() ); + } + + public Carpenter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Cobbler.cs b/Scripts/Mobiles/Vendors/NPC/Cobbler.cs new file mode 100644 index 000000000..f95c08912 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Cobbler.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Cobbler : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Cobbler() : base( "the cobbler" ) + { + SetSkill( SkillName.Tailoring, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBCobbler() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Sandals : VendorShoeType.Shoes; } + } + + public Cobbler( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Cook.cs b/Scripts/Mobiles/Vendors/NPC/Cook.cs new file mode 100644 index 000000000..586e53404 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Cook.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Cook : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Cook() : base( "the cook" ) + { + SetSkill( SkillName.Cooking, 90.0, 100.0 ); + SetSkill( SkillName.TasteID, 75.0, 98.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBCook() ); + + if ( IsTokunoVendor ) + m_SBInfos.Add( new SBSECook() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Sandals : VendorShoeType.Shoes; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron() ); + } + + public Cook( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/CustomHairstylist.cs b/Scripts/Mobiles/Vendors/NPC/CustomHairstylist.cs new file mode 100644 index 000000000..f75fd0cea --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/CustomHairstylist.cs @@ -0,0 +1,587 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Network; + +namespace Server.Mobiles +{ + public class CustomHairstylist : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override bool ClickTitle{ get{ return false; } } + + public override bool IsActiveBuyer{ get{ return false; } } + public override bool IsActiveSeller{ get{ return true; } } + + public override bool OnBuyItems( Mobile buyer, ArrayList list ) + { + return false; + } + + public static readonly object From = new object(); + public static readonly object Vendor = new object(); + public static readonly object Price = new object(); + + private static HairstylistBuyInfo[] m_SellList = new HairstylistBuyInfo[] + { + new HairstylistBuyInfo( 1018357, 50000, false, typeof( ChangeHairstyleGump ), new object[] + { From, Vendor, Price, false, ChangeHairstyleEntry.HairEntries } ), + new HairstylistBuyInfo( 1018358, 50000, true, typeof( ChangeHairstyleGump ), new object[] + { From, Vendor, Price, true, ChangeHairstyleEntry.BeardEntries } ), + new HairstylistBuyInfo( 1018359, 50, false, typeof( ChangeHairHueGump ), new object[] + { From, Vendor, Price, true, true, ChangeHairHueEntry.RegularEntries } ), + new HairstylistBuyInfo( 1018360, 500000, false, typeof( ChangeHairHueGump ), new object[] + { From, Vendor, Price, true, true, ChangeHairHueEntry.BrightEntries } ), + new HairstylistBuyInfo( 1018361, 30000, false, typeof( ChangeHairHueGump ), new object[] + { From, Vendor, Price, true, false, ChangeHairHueEntry.RegularEntries } ), + new HairstylistBuyInfo( 1018362, 30000, true, typeof( ChangeHairHueGump ), new object[] + { From, Vendor, Price, false, true, ChangeHairHueEntry.RegularEntries } ), + new HairstylistBuyInfo( 1018363, 500000, false, typeof( ChangeHairHueGump ), new object[] + { From, Vendor, Price, true, false, ChangeHairHueEntry.BrightEntries } ), + new HairstylistBuyInfo( 1018364, 500000, true, typeof( ChangeHairHueGump ), new object[] + { From, Vendor, Price, false, true, ChangeHairHueEntry.BrightEntries } ) + }; + + public override void VendorBuy( Mobile from ) + { + from.SendGump( new HairstylistBuyGump( from, this, m_SellList ) ); + } + + [Constructable] + public CustomHairstylist() : base( "the hairstylist" ) + { + } + + public override int GetHairHue() + { + return RandomBrightHue(); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Robe( Utility.RandomPinkHue() ) ); + } + + public override void InitSBInfo() + { + } + + public CustomHairstylist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class HairstylistBuyInfo + { + private int m_Title; + private string m_TitleString; + private int m_Price; + private bool m_FacialHair; + private Type m_GumpType; + private object[] m_GumpArgs; + + public int Title{ get{ return m_Title; } } + public string TitleString{ get{ return m_TitleString; } } + public int Price{ get{ return m_Price; } } + public bool FacialHair{ get{ return m_FacialHair; } } + public Type GumpType{ get{ return m_GumpType; } } + public object[] GumpArgs{ get{ return m_GumpArgs; } } + + public HairstylistBuyInfo( int title, int price, bool facialHair, Type gumpType, object[] args ) + { + m_Title = title; + m_Price = price; + m_FacialHair = facialHair; + m_GumpType = gumpType; + m_GumpArgs = args; + } + + public HairstylistBuyInfo( string title, int price, bool facialHair, Type gumpType, object[] args ) + { + m_TitleString = title; + m_Price = price; + m_FacialHair = facialHair; + m_GumpType = gumpType; + m_GumpArgs = args; + } + } + + public class HairstylistBuyGump : Gump + { + private Mobile m_From; + private Mobile m_Vendor; + private HairstylistBuyInfo[] m_SellList; + + public HairstylistBuyGump( Mobile from, Mobile vendor, HairstylistBuyInfo[] sellList ) : base( 50, 50 ) + { + m_From = from; + m_Vendor = vendor; + m_SellList = sellList; + + from.CloseGump( typeof( HairstylistBuyGump ) ); + from.CloseGump( typeof( ChangeHairHueGump ) ); + from.CloseGump( typeof( ChangeHairstyleGump ) ); + + bool isFemale = ( from.Female || from.Body.IsFemale ); + + int balance = Banker.GetBalance( from ); + int canAfford = 0; + + for ( int i = 0; i < sellList.Length; ++i ) + { + if ( balance >= sellList[i].Price && (!sellList[i].FacialHair || !isFemale) ) + ++canAfford; + } + + AddPage( 0 ); + + AddBackground( 50, 10, 450, 100 + (canAfford * 25), 2600 ); + + AddHtmlLocalized( 100, 40, 350, 20, 1018356, false, false ); // Choose your hairstyle change: + + int index = 0; + + for ( int i = 0; i < sellList.Length; ++i ) + { + if ( balance >= sellList[i].Price && (!sellList[i].FacialHair || !isFemale) ) + { + if ( sellList[i].TitleString != null ) + AddHtml( 140, 75 + (index * 25), 300, 20, sellList[i].TitleString, false, false ); + else + AddHtmlLocalized( 140, 75 + (index * 25), 300, 20, sellList[i].Title, false, false ); + + AddButton( 100, 75 + (index++ * 25), 4005, 4007, 1 + i, GumpButtonType.Reply, 0 ); + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_SellList.Length ) + { + HairstylistBuyInfo buyInfo = m_SellList[index]; + + int balance = Banker.GetBalance( m_From ); + + bool isFemale = ( m_From.Female || m_From.Body.IsFemale ); + + if ( buyInfo.FacialHair && isFemale ) + { + // You cannot place facial hair on a woman! + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1010639, m_From.NetState ); + } + else if ( balance >= buyInfo.Price ) + { + try + { + object[] origArgs = buyInfo.GumpArgs; + object[] args = new object[origArgs.Length]; + + for ( int i = 0; i < args.Length; ++i ) + { + if ( origArgs[i] == CustomHairstylist.Price ) + args[i] = m_SellList[index].Price; + else if ( origArgs[i] == CustomHairstylist.From ) + args[i] = m_From; + else if ( origArgs[i] == CustomHairstylist.Vendor ) + args[i] = m_Vendor; + else + args[i] = origArgs[i]; + } + + Gump g = Activator.CreateInstance( buyInfo.GumpType, args ) as Gump; + + m_From.SendGump( g ); + } + catch + { + } + } + else + { + // You cannot afford my services for that style. + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042293, m_From.NetState ); + } + } + } + } + + public class ChangeHairHueEntry + { + private string m_Name; + private int[] m_Hues; + + public string Name{ get{ return m_Name; } } + public int[] Hues{ get{ return m_Hues; } } + + public ChangeHairHueEntry( string name, int[] hues ) + { + m_Name = name; + m_Hues = hues; + } + + public ChangeHairHueEntry( string name, int start, int count ) + { + m_Name = name; + + m_Hues = new int[count]; + + for ( int i = 0; i < count; ++i ) + m_Hues[i] = start + i; + } + + public static readonly ChangeHairHueEntry[] BrightEntries = new ChangeHairHueEntry[] + { + new ChangeHairHueEntry( "*****", 12, 10 ), + new ChangeHairHueEntry( "*****", 32, 5 ), + new ChangeHairHueEntry( "*****", 38, 8 ), + new ChangeHairHueEntry( "*****", 54, 3 ), + new ChangeHairHueEntry( "*****", 62, 10 ), + new ChangeHairHueEntry( "*****", 81, 2 ), + new ChangeHairHueEntry( "*****", 89, 2 ), + new ChangeHairHueEntry( "*****", 1153, 2 ) + }; + + public static readonly ChangeHairHueEntry[] RegularEntries = new ChangeHairHueEntry[] + { + new ChangeHairHueEntry( "*****", 1602, 26 ), + new ChangeHairHueEntry( "*****", 1628, 27 ), + new ChangeHairHueEntry( "*****", 1502, 32 ), + new ChangeHairHueEntry( "*****", 1302, 32 ), + new ChangeHairHueEntry( "*****", 1402, 32 ), + new ChangeHairHueEntry( "*****", 1202, 24 ), + new ChangeHairHueEntry( "*****", 2402, 29 ), + new ChangeHairHueEntry( "*****", 2213, 6 ), + new ChangeHairHueEntry( "*****", 1102, 8 ), + new ChangeHairHueEntry( "*****", 1110, 8 ), + new ChangeHairHueEntry( "*****", 1118, 16 ), + new ChangeHairHueEntry( "*****", 1134, 16 ) + }; + } + + public class ChangeHairHueGump : Gump + { + private Mobile m_From; + private Mobile m_Vendor; + private int m_Price; + private bool m_Hair; + private bool m_FacialHair; + private ChangeHairHueEntry[] m_Entries; + + public ChangeHairHueGump( Mobile from, Mobile vendor, int price, bool hair, bool facialHair, ChangeHairHueEntry[] entries ) : base( 50, 50 ) + { + m_From = from; + m_Vendor = vendor; + m_Price = price; + m_Hair = hair; + m_FacialHair = facialHair; + m_Entries = entries; + + from.CloseGump( typeof( HairstylistBuyGump ) ); + from.CloseGump( typeof( ChangeHairHueGump ) ); + from.CloseGump( typeof( ChangeHairstyleGump ) ); + + AddPage( 0 ); + + AddBackground( 100, 10, 350, 370, 2600 ); + AddBackground( 120, 54, 110, 270, 5100 ); + + AddHtmlLocalized( 155, 25, 240, 30, 1011013, false, false ); //
Hair Color Selection Menu
+ + AddHtmlLocalized( 150, 330, 220, 35, 1011014, false, false ); // Dye my hair this color! + AddButton( 380, 330, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + + for ( int i = 0; i < entries.Length; ++i ) + { + ChangeHairHueEntry entry = entries[i]; + + AddLabel( 130, 59 + (i * 22), entry.Hues[0] - 1, entry.Name ); + AddButton( 207, 60 + (i * 22), 5224, 5224, 0, GumpButtonType.Page, 1 + i ); + } + + for ( int i = 0; i < entries.Length; ++i ) + { + ChangeHairHueEntry entry = entries[i]; + int[] hues = entry.Hues; + string name = entry.Name; + + AddPage( 1 + i ); + + for ( int j = 0; j < hues.Length; ++j ) + { + AddLabel( 278 + ((j / 16) * 80), 52 + ((j % 16) * 17), hues[j] - 1, name ); + AddRadio( 260 + ((j / 16) * 80), 52 + ((j % 16) * 17), 210, 211, false, (j * entries.Length) + i ); + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0] % m_Entries.Length; + int offset = switches[0] / m_Entries.Length; + + if ( index >= 0 && index < m_Entries.Length ) + { + if ( offset >= 0 && offset < m_Entries[index].Hues.Length ) + { + if ( m_Hair && m_From.HairItemID > 0 || m_FacialHair && m_From.FacialHairItemID > 0 ) + { + if ( !Banker.Withdraw( m_From, m_Price ) ) + { + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042293, m_From.NetState ); // You cannot afford my services for that style. + return; + } + + int hue = m_Entries[index].Hues[offset]; + + if ( m_Hair ) + m_From.HairHue = hue; + + if ( m_FacialHair ) + m_From.FacialHairHue = hue; + } + else + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502623, m_From.NetState ); // You have no hair to dye and you cannot use this. + } + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1013009, m_From.NetState ); + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1013009, m_From.NetState ); + } + } + } + + public class ChangeHairstyleEntry + { + private int m_ItemID; + private int m_GumpID; + private int m_X, m_Y; + + public int ItemID{ get{ return m_ItemID; } } + public int GumpID{ get{ return m_GumpID; } } + public int X{ get{ return m_X; } } + public int Y{ get{ return m_Y; } } + + public ChangeHairstyleEntry( int gumpID, int x, int y, int itemID ) + { + m_GumpID = gumpID; + m_X = x; + m_Y = y; + m_ItemID = itemID; + } + + public static readonly ChangeHairstyleEntry[] HairEntries = new ChangeHairstyleEntry[] + { + new ChangeHairstyleEntry( 50700, 70 - 137, 20 - 60, 0x203B ), + new ChangeHairstyleEntry( 60710, 193 - 260, 18 - 60, 0x2045 ), + new ChangeHairstyleEntry( 50703, 316 - 383, 25 - 60, 0x2044 ), + new ChangeHairstyleEntry( 60708, 70 - 137, 75 - 125, 0x203C ), + new ChangeHairstyleEntry( 60900, 193 - 260, 85 - 125, 0x2047 ), + new ChangeHairstyleEntry( 60713, 320 - 383, 85 - 125, 0x204A ), + new ChangeHairstyleEntry( 60702, 70 - 137, 140 - 190, 0x203D ), + new ChangeHairstyleEntry( 60707, 193 - 260, 140 - 190, 0x2049 ), + new ChangeHairstyleEntry( 60901, 315 - 383, 150 - 190, 0x2048 ), + new ChangeHairstyleEntry( 0, 0, 0, 0 ) + }; + + public static readonly ChangeHairstyleEntry[] BeardEntries = new ChangeHairstyleEntry[] + { + new ChangeHairstyleEntry( 50800, 120 - 187, 30 - 80, 0x2040 ), + new ChangeHairstyleEntry( 50904, 243 - 310, 33 - 80, 0x204B ), + new ChangeHairstyleEntry( 50906, 120 - 187, 100 - 150, 0x204D ), + new ChangeHairstyleEntry( 50801, 243 - 310, 95 - 150, 0x203E ), + new ChangeHairstyleEntry( 50802, 120 - 187, 173 - 220, 0x203F ), + new ChangeHairstyleEntry( 50905, 243 - 310, 165 - 220, 0x204C ), + new ChangeHairstyleEntry( 50808, 120 - 187, 242 - 290, 0x2041 ), + new ChangeHairstyleEntry( 0, 0, 0, 0 ) + }; + } + + public class ChangeHairstyleGump : Gump + { + private Mobile m_From; + private Mobile m_Vendor; + private int m_Price; + private bool m_FacialHair; + private ChangeHairstyleEntry[] m_Entries; + + public ChangeHairstyleGump( Mobile from, Mobile vendor, int price, bool facialHair, ChangeHairstyleEntry[] entries ) : base( 50, 50 ) + { + m_From = from; + m_Vendor = vendor; + m_Price = price; + m_FacialHair = facialHair; + m_Entries = entries; + + from.CloseGump( typeof( HairstylistBuyGump ) ); + from.CloseGump( typeof( ChangeHairHueGump ) ); + from.CloseGump( typeof( ChangeHairstyleGump ) ); + + int tableWidth = ( m_FacialHair ? 2 : 3 ); + int tableHeight = ( (entries.Length + tableWidth - ( m_FacialHair ? 1 : 2 )) / tableWidth ); + int offsetWidth = 123; + int offsetHeight = ( m_FacialHair ? 70 : 65 ); + + AddPage( 0 ); + + AddBackground( 0, 0, 81 + (tableWidth * offsetWidth), 105 + (tableHeight * offsetHeight), 2600 ); + + AddButton( 45, 45 + (tableHeight * offsetHeight), 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 77, 45 + (tableHeight * offsetHeight), 90, 35, 1006044, false, false ); // Ok + + AddButton( 81 + (tableWidth * offsetWidth) - 180, 45 + (tableHeight * offsetHeight), 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 81 + (tableWidth * offsetWidth) - 148, 45 + (tableHeight * offsetHeight), 90, 35, 1006045, false, false ); // Cancel + + if ( !facialHair ) + AddHtmlLocalized( 50, 15, 350, 20, 1018353, false, false ); //
New Hairstyle
+ else + AddHtmlLocalized( 55, 15, 200, 20, 1018354, false, false ); //
New Beard
+ + for ( int i = 0; i < entries.Length; ++i ) + { + int xTable = i % tableWidth; + int yTable = i / tableWidth; + + if ( entries[i].GumpID != 0 ) + { + AddRadio( 40 + (xTable * offsetWidth), 70 + (yTable * offsetHeight), 208, 209, false, i ); + AddBackground( 87 + (xTable * offsetWidth), 50 + (yTable * offsetHeight), 50, 50, 2620 ); + AddImage( 87 + (xTable * offsetWidth) + entries[i].X, 50 + (yTable * offsetHeight) + entries[i].Y, entries[i].GumpID ); + } + else if ( !facialHair ) + { + AddRadio( 40 + ((xTable + 1) * offsetWidth), 240, 208, 209, false, i ); + AddHtmlLocalized( 60 + ((xTable + 1) * offsetWidth), 240, 85, 35, 1011064, false, false ); // Bald + } + else + { + AddRadio( 40 + (xTable * offsetWidth), 70 + (yTable * offsetHeight), 208, 209, false, i ); + AddHtmlLocalized( 60 + (xTable * offsetWidth), 70 + (yTable * offsetHeight), 85, 35, 1011064, false, false ); // Bald + } + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( m_FacialHair && (m_From.Female || m_From.Body.IsFemale) ) + return; + + if ( m_From.Race == Race.Elf ) + { + m_From.SendMessage( "This isn't implemented for elves yet. Sorry!" ); + return; + } + + if ( info.ButtonID == 1 ) + { + int[] switches = info.Switches; + + if ( switches.Length > 0 ) + { + int index = switches[0]; + + if ( index >= 0 && index < m_Entries.Length ) + { + ChangeHairstyleEntry entry = m_Entries[index]; + + if ( m_From is PlayerMobile ) + ((PlayerMobile)m_From).SetHairMods( -1, -1 ); + + int hairID = m_From.HairItemID; + int facialHairID = m_From.FacialHairItemID; + + if ( entry.ItemID == 0 ) + { + if ( m_FacialHair ? (facialHairID == 0) : (hairID == 0) ) + return; + + if ( Banker.Withdraw( m_From, m_Price ) ) + { + if ( m_FacialHair ) + m_From.FacialHairItemID = 0; + else + m_From.HairItemID = 0; + } + else + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042293, m_From.NetState ); // You cannot afford my services for that style. + } + else + { + if ( m_FacialHair ) + { + if ( facialHairID > 0 && facialHairID == entry.ItemID ) + return; + } + else + { + if ( hairID > 0 && hairID == entry.ItemID ) + return; + } + + if ( Banker.Withdraw( m_From, m_Price ) ) + { + if ( m_FacialHair ) + m_From.FacialHairItemID = entry.ItemID; + else + m_From.HairItemID = entry.ItemID; + } + else + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042293, m_From.NetState ); // You cannot afford my services for that style. + } + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1013009, m_From.NetState ); + } + } + else + { + // You decide not to change your hairstyle. + m_Vendor.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1013009, m_From.NetState ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Farmer.cs b/Scripts/Mobiles/Vendors/NPC/Farmer.cs new file mode 100644 index 000000000..e72bb25be --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Farmer.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Farmer : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Farmer() : base( "the farmer" ) + { + SetSkill( SkillName.Lumberjacking, 36.0, 68.0 ); + SetSkill( SkillName.TasteID, 36.0, 68.0 ); + SetSkill( SkillName.Cooking, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBFarmer() ); + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.ThighBoots; } + } + + public override int GetShoeHue() + { + return 0; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.WideBrimHat( Utility.RandomNeutralHue() ) ); + } + + public Farmer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Fisherman.cs b/Scripts/Mobiles/Vendors/NPC/Fisherman.cs new file mode 100644 index 000000000..ddfb0d1a8 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Fisherman.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Fisherman : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.FishermensGuild; } } + + [Constructable] + public Fisherman() : base( "the fisher" ) + { + SetSkill( SkillName.Fishing, 75.0, 98.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBFisherman() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.FishingPole() ); + } + + public Fisherman( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Furtrader.cs b/Scripts/Mobiles/Vendors/NPC/Furtrader.cs new file mode 100644 index 000000000..a7b524747 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Furtrader.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Furtrader : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Furtrader() : base( "the furtrader" ) + { + SetSkill( SkillName.Camping, 55.0, 78.0 ); + //SetSkill( SkillName.Alchemy, 60.0, 83.0 ); + SetSkill( SkillName.AnimalLore, 85.0, 100.0 ); + SetSkill( SkillName.Cooking, 45.0, 68.0 ); + SetSkill( SkillName.Tracking, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBFurtrader() ); + } + + public Furtrader( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Glassblower.cs b/Scripts/Mobiles/Vendors/NPC/Glassblower.cs new file mode 100644 index 000000000..940ac1629 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Glassblower.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + [TypeAlias( "Server.Mobiles.GargoyleAlchemist" )] + public class Glassblower : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.MagesGuild; } } + + [Constructable] + public Glassblower() : base( "the alchemist" ) + { + SetSkill( SkillName.Alchemy, 85.0, 100.0 ); + SetSkill( SkillName.TasteID, 85.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBGlassblower() ); + m_SBInfos.Add( new SBAlchemist() ); + } + + public Glassblower( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Body == 0x2F2 ) + Body = 0x2F6; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/GolemCrafter.cs b/Scripts/Mobiles/Vendors/NPC/GolemCrafter.cs new file mode 100644 index 000000000..fe7706960 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/GolemCrafter.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class GolemCrafter : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public GolemCrafter() : base( "the golem crafter" ) + { + SetSkill( SkillName.Lockpicking, 60.0, 83.0 ); + SetSkill( SkillName.RemoveTrap, 75.0, 98.0 ); + SetSkill( SkillName.Tinkering, 64.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBTinker() ); + m_SBInfos.Add( new SBVagabond() ); + } + + public GolemCrafter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/BardGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/BardGuildmaster.cs new file mode 100644 index 000000000..515524aae --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/BardGuildmaster.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class BardGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.BardsGuild; } } + + [Constructable] + public BardGuildmaster() : base( "bard" ) + { + SetSkill( SkillName.Archery, 80.0, 100.0 ); + SetSkill( SkillName.Discordance, 80.0, 100.0 ); + SetSkill( SkillName.Musicianship, 80.0, 100.0 ); + SetSkill( SkillName.Peacemaking, 80.0, 100.0 ); + SetSkill( SkillName.Provocation, 80.0, 100.0 ); + SetSkill( SkillName.Swords, 80.0, 100.0 ); + } + + public BardGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs new file mode 100644 index 000000000..76a2973a1 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/BaseGuildmaster.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Network; + +namespace Server.Mobiles +{ + public abstract class BaseGuildmaster : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override bool IsActiveVendor{ get{ return false; } } + + public override bool ClickTitle{ get{ return false; } } + + public virtual int JoinCost{ get{ return 500; } } + + public virtual TimeSpan JoinAge{ get{ return TimeSpan.FromDays( 0.0 ); } } + public virtual TimeSpan JoinGameAge{ get{ return TimeSpan.FromDays( 2.0 ); } } + public virtual TimeSpan QuitAge{ get{ return TimeSpan.FromDays( 7.0 ); } } + public virtual TimeSpan QuitGameAge{ get{ return TimeSpan.FromDays( 4.0 ); } } + + public override void InitSBInfo() + { + } + + public virtual bool CheckCustomReqs( PlayerMobile pm ) + { + return true; + } + + public virtual void SayGuildTo( Mobile m ) + { + SayTo( m, 1008055 + (int)NpcGuild ); + } + + public virtual void SayWelcomeTo( Mobile m ) + { + SayTo( m, 1008054 ); // Welcome to the guild! Thou shalt find that fellow members shall grant thee lower prices in shops. + } + + public virtual void SayPriceTo( Mobile m ) + { + m.Send( new MessageLocalizedAffix( Serial, Body, MessageType.Regular, SpeechHue, 3, 1008052, Name, AffixType.Append, JoinCost.ToString(), "" ) ); + } + + public virtual bool WasNamed( string speech ) + { + string name = this.Name; + + return ( name != null && Insensitive.StartsWith( speech, name ) ); + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.InRange( this.Location, 2 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !e.Handled && from is PlayerMobile && from.InRange( this.Location, 2 ) && WasNamed( e.Speech ) ) + { + PlayerMobile pm = (PlayerMobile)from; + + if ( e.HasKeyword( 0x0004 ) ) // *join* | *member* + { + if ( pm.NpcGuild == this.NpcGuild ) + SayTo( from, 501047 ); // Thou art already a member of our guild. + else if ( pm.NpcGuild != NpcGuild.None ) + SayTo( from, 501046 ); // Thou must resign from thy other guild first. + else if ( pm.GameTime < JoinGameAge || (pm.CreationTime + JoinAge) > DateTime.Now ) + SayTo( from, 501048 ); // You are too young to join my guild... + else if ( CheckCustomReqs( pm ) ) + SayPriceTo( from ); + + e.Handled = true; + } + else if ( e.HasKeyword( 0x0005 ) ) // *resign* | *quit* + { + if ( pm.NpcGuild != this.NpcGuild ) + { + SayTo( from, 501052 ); // Thou dost not belong to my guild! + } + else if ( (pm.NpcGuildJoinTime + QuitAge) > DateTime.Now || (pm.NpcGuildGameTime + QuitGameAge) > pm.GameTime ) + { + SayTo( from, 501053 ); // You just joined my guild! You must wait a week to resign. + } + else + { + SayTo( from, 501054 ); // I accept thy resignation. + pm.NpcGuild = NpcGuild.None; + } + + e.Handled = true; + } + } + + base.OnSpeech( e ); + } + + public override bool OnGoldGiven( Mobile from, Gold dropped ) + { + if ( from is PlayerMobile && dropped.Amount == JoinCost ) + { + PlayerMobile pm = (PlayerMobile)from; + + if ( pm.NpcGuild == this.NpcGuild ) + { + SayTo( from, 501047 ); // Thou art already a member of our guild. + } + else if ( pm.NpcGuild != NpcGuild.None ) + { + SayTo( from, 501046 ); // Thou must resign from thy other guild first. + } + else if ( pm.GameTime < JoinGameAge || (pm.CreationTime + JoinAge) > DateTime.Now ) + { + SayTo( from, 501048 ); // You are too young to join my guild... + } + else if ( CheckCustomReqs( pm ) ) + { + SayWelcomeTo( from ); + + pm.NpcGuild = this.NpcGuild; + pm.NpcGuildJoinTime = DateTime.Now; + pm.NpcGuildGameTime = pm.GameTime; + + dropped.Delete(); + return true; + } + + return false; + } + + return base.OnGoldGiven( from, dropped ); + } + + public BaseGuildmaster( string title ) : base( title ) + { + Title = String.Format( "the {0} {1}", title, Female ? "guildmistress" : "guildmaster" ); + } + + public BaseGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs new file mode 100644 index 000000000..1544cca7e --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/BlacksmithGuildmaster.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class BlacksmithGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.BlacksmithsGuild; } } + + public override bool IsActiveVendor{ get{ return true; } } + + public override bool ClickTitle{ get{ return true; } } + + [Constructable] + public BlacksmithGuildmaster() : base( "blacksmith" ) + { + SetSkill( SkillName.ArmsLore, 65.0, 88.0 ); + SetSkill( SkillName.Blacksmith, 90.0, 100.0 ); + SetSkill( SkillName.Macing, 36.0, 68.0 ); + SetSkill( SkillName.Parry, 36.0, 68.0 ); + } + public override void InitSBInfo() + { + SBInfos.Add( new SBBlacksmith() ); + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.ThighBoots; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + Item item = ( Utility.RandomBool() ? null : new Server.Items.RingmailChest() ); + + if ( item != null && !EquipItem( item ) ) + { + item.Delete(); + item = null; + } + + if ( item == null ) + AddItem( new Server.Items.FullApron() ); + + AddItem( new Server.Items.Bascinet() ); + AddItem( new Server.Items.SmithHammer() ); + } + + public BlacksmithGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/FisherGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/FisherGuildmaster.cs new file mode 100644 index 000000000..e36ae4081 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/FisherGuildmaster.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class FisherGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.FishermensGuild; } } + + [Constructable] + public FisherGuildmaster() : base( "fisher" ) + { + SetSkill( SkillName.Fishing, 80.0, 100.0 ); + } + + public FisherGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/HealerGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/HealerGuildmaster.cs new file mode 100644 index 000000000..c50cc0287 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/HealerGuildmaster.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class HealerGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.HealersGuild; } } + + [Constructable] + public HealerGuildmaster() : base( "healer" ) + { + SetSkill( SkillName.Anatomy, 85.0, 100.0 ); + SetSkill( SkillName.Healing, 90.0, 100.0 ); + SetSkill( SkillName.Forensics, 75.0, 98.0 ); + SetSkill( SkillName.MagicResist, 75.0, 98.0 ); + SetSkill( SkillName.SpiritSpeak, 65.0, 88.0 ); + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Robe( Utility.RandomYellowHue() ) ); + } + + public HealerGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/MageGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/MageGuildmaster.cs new file mode 100644 index 000000000..2dd93a394 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/MageGuildmaster.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class MageGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.MagesGuild; } } + + [Constructable] + public MageGuildmaster() : base( "mage" ) + { + SetSkill( SkillName.EvalInt, 85.0, 100.0 ); + SetSkill( SkillName.Inscribe, 65.0, 88.0 ); + SetSkill( SkillName.MagicResist, 64.0, 100.0 ); + SetSkill( SkillName.Magery, 90.0, 100.0 ); + SetSkill( SkillName.Wrestling, 60.0, 83.0 ); + SetSkill( SkillName.Meditation, 85.0, 100.0 ); + SetSkill( SkillName.Macing, 36.0, 68.0 ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Robe( Utility.RandomBlueHue() ) ); + AddItem( new Server.Items.GnarledStaff() ); + } + + public MageGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/MerchantGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/MerchantGuildmaster.cs new file mode 100644 index 000000000..5681299c8 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/MerchantGuildmaster.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class MerchantGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.MerchantsGuild; } } + + [Constructable] + public MerchantGuildmaster() : base( "merchant" ) + { + SetSkill( SkillName.ItemID, 85.0, 100.0 ); + SetSkill( SkillName.ArmsLore, 85.0, 100.0 ); + } + + public MerchantGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/MinerGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/MinerGuildmaster.cs new file mode 100644 index 000000000..4344ccd3d --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/MinerGuildmaster.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class MinerGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.MinersGuild; } } + + [Constructable] + public MinerGuildmaster() : base( "miner" ) + { + SetSkill( SkillName.ItemID, 60.0, 83.0 ); + SetSkill( SkillName.Mining, 90.0, 100.0 ); + } + + public MinerGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/RangerGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/RangerGuildmaster.cs new file mode 100644 index 000000000..d35ef9325 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/RangerGuildmaster.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class RangerGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.RangersGuild; } } + + [Constructable] + public RangerGuildmaster() : base( "ranger" ) + { + SetSkill( SkillName.AnimalLore, 64.0, 100.0 ); + SetSkill( SkillName.Camping, 75.0, 98.0 ); + SetSkill( SkillName.Hiding, 75.0, 98.0 ); + SetSkill( SkillName.MagicResist, 75.0, 98.0 ); + SetSkill( SkillName.Tactics, 65.0, 88.0 ); + SetSkill( SkillName.Archery, 90.0, 100.0 ); + SetSkill( SkillName.Tracking, 90.0, 100.0 ); + SetSkill( SkillName.Stealth, 60.0, 83.0 ); + SetSkill( SkillName.Fencing, 36.0, 68.0 ); + SetSkill( SkillName.Herding, 36.0, 68.0 ); + SetSkill( SkillName.Swords, 45.0, 68.0 ); + } + + public RangerGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/TailorGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/TailorGuildmaster.cs new file mode 100644 index 000000000..672a6c266 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/TailorGuildmaster.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class TailorGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.TailorsGuild; } } + + [Constructable] + public TailorGuildmaster() : base( "tailor" ) + { + SetSkill( SkillName.Tailoring, 90.0, 100.0 ); + } + + public TailorGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/ThiefGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/ThiefGuildmaster.cs new file mode 100644 index 000000000..89e2d0b37 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/ThiefGuildmaster.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class ThiefGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.ThievesGuild; } } + + public override TimeSpan JoinAge{ get{ return TimeSpan.FromDays( 7.0 ); } } + + [Constructable] + public ThiefGuildmaster() : base( "thief" ) + { + SetSkill( SkillName.DetectHidden, 75.0, 98.0 ); + SetSkill( SkillName.Hiding, 65.0, 88.0 ); + SetSkill( SkillName.Lockpicking, 85.0, 100.0 ); + SetSkill( SkillName.Snooping, 90.0, 100.0 ); + SetSkill( SkillName.Poisoning, 60.0, 83.0 ); + SetSkill( SkillName.Stealing, 90.0, 100.0 ); + SetSkill( SkillName.Fencing, 75.0, 98.0 ); + SetSkill( SkillName.Stealth, 85.0, 100.0 ); + SetSkill( SkillName.RemoveTrap, 85.0, 100.0 ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + if ( Utility.RandomBool() ) + AddItem( new Server.Items.Kryss() ); + else + AddItem( new Server.Items.Dagger() ); + } + + public override bool CheckCustomReqs( PlayerMobile pm ) + { + if ( pm.Young ) + { + SayTo( pm, 502089 ); // You cannot be a member of the Thieves' Guild while you are Young. + return false; + } + else if ( pm.Kills > 0 ) + { + SayTo( pm, 501050 ); // This guild is for cunning thieves, not oafish cutthroats. + return false; + } + else if ( pm.Skills[SkillName.Stealing].Base < 60.0 ) + { + SayTo( pm, 501051 ); // You must be at least a journeyman pickpocket to join this elite organization. + return false; + } + + return true; + } + + public override void SayWelcomeTo( Mobile m ) + { + SayTo( m, 1008053 ); // Welcome to the guild! Stay to the shadows, friend. + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.InRange( this.Location, 2 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !e.Handled && from is PlayerMobile && from.InRange( this.Location, 2 ) && e.HasKeyword( 0x1F ) ) // *disguise* + { + PlayerMobile pm = (PlayerMobile)from; + + if ( pm.NpcGuild == NpcGuild.ThievesGuild ) + SayTo( from, 501839 ); // That particular item costs 700 gold pieces. + else + SayTo( from, 501838 ); // I don't know what you're talking about. + + e.Handled = true; + } + + base.OnSpeech( e ); + } + + public override bool OnGoldGiven( Mobile from, Gold dropped ) + { + if ( from is PlayerMobile && dropped.Amount == 700 ) + { + PlayerMobile pm = (PlayerMobile)from; + + if ( pm.NpcGuild == NpcGuild.ThievesGuild ) + { + from.AddToBackpack( new DisguiseKit() ); + + dropped.Delete(); + return true; + } + } + + return base.OnGoldGiven( from, dropped ); + } + + public ThiefGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs new file mode 100644 index 000000000..0a20f59e1 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/TinkerGuildmaster.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class TinkerGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.TinkersGuild; } } + + [Constructable] + public TinkerGuildmaster() : base( "tinker" ) + { + SetSkill( SkillName.Lockpicking, 65.0, 88.0 ); + SetSkill( SkillName.Tinkering, 90.0, 100.0 ); + SetSkill( SkillName.RemoveTrap, 85.0, 100.0 ); + } + + public TinkerGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Guildmasters/WarriorGuildmaster.cs b/Scripts/Mobiles/Vendors/NPC/Guildmasters/WarriorGuildmaster.cs new file mode 100644 index 000000000..4db13deca --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Guildmasters/WarriorGuildmaster.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class WarriorGuildmaster : BaseGuildmaster + { + public override NpcGuild NpcGuild{ get{ return NpcGuild.WarriorsGuild; } } + + [Constructable] + public WarriorGuildmaster() : base( "warrior" ) + { + SetSkill( SkillName.ArmsLore, 75.0, 98.0 ); + SetSkill( SkillName.Parry, 85.0, 100.0 ); + SetSkill( SkillName.MagicResist, 60.0, 83.0 ); + SetSkill( SkillName.Tactics, 85.0, 100.0 ); + SetSkill( SkillName.Swords, 90.0, 100.0 ); + SetSkill( SkillName.Macing, 60.0, 83.0 ); + SetSkill( SkillName.Fencing, 60.0, 83.0 ); + } + + public WarriorGuildmaster( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/GypsyAnimalTrainer.cs b/Scripts/Mobiles/Vendors/NPC/GypsyAnimalTrainer.cs new file mode 100644 index 000000000..99f21d916 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/GypsyAnimalTrainer.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public class GypsyAnimalTrainer : AnimalTrainer + { + [Constructable] + public GypsyAnimalTrainer() + { + if ( Utility.RandomBool() ) + Title = "the gypsy animal trainer"; + else + Title = "the gypsy animal herder"; + } + + public override VendorShoeType ShoeType + { + get{ return Female ? VendorShoeType.ThighBoots : VendorShoeType.Boots; } + } + + public override int GetShoeHue() + { + return 0; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( Utility.RandomBool() ? (Item)new QuarterStaff() : (Item)new ShepherdsCrook() ); + + Item item = FindItemOnLayer( Layer.Pants ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterTorso ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerTorso ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.Shirt ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + } + + public GypsyAnimalTrainer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/GypsyBanker.cs b/Scripts/Mobiles/Vendors/NPC/GypsyBanker.cs new file mode 100644 index 000000000..c7f523b44 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/GypsyBanker.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public class GypsyBanker : Banker + { + public override bool IsActiveVendor{ get{ return false; } } + public override NpcGuild NpcGuild{ get{ return NpcGuild.None; } } + public override bool ClickTitle{ get{ return false; } } + + [Constructable] + public GypsyBanker() + { + Title = "the gypsy banker"; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + switch ( Utility.Random( 4 ) ) + { + case 0: AddItem( new JesterHat( RandomBrightHue() ) ); break; + case 1: AddItem( new Bandana( RandomBrightHue() ) ); break; + case 2: AddItem( new SkullCap( RandomBrightHue() ) ); break; + } + + Item item = FindItemOnLayer( Layer.Pants ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.Shoes ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterTorso ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerTorso ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.Shirt ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + } + + public GypsyBanker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/GypsyMaiden.cs b/Scripts/Mobiles/Vendors/NPC/GypsyMaiden.cs new file mode 100644 index 000000000..bfe2a1180 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/GypsyMaiden.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public class GypsyMaiden : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public GypsyMaiden() : base( "the gypsy maiden" ) + { + } + + public override bool GetGender() + { + return true; // always female + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBProvisioner() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + switch ( Utility.Random( 4 ) ) + { + case 0: AddItem( new JesterHat( RandomBrightHue() ) ); break; + case 1: AddItem( new Bandana( RandomBrightHue() ) ); break; + case 2: AddItem( new SkullCap( RandomBrightHue() ) ); break; + } + + if ( Utility.RandomBool() ) + AddItem( new HalfApron( RandomBrightHue() ) ); + + Item item = FindItemOnLayer( Layer.Pants ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + } + + public GypsyMaiden( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/HairStylist.cs b/Scripts/Mobiles/Vendors/NPC/HairStylist.cs new file mode 100644 index 000000000..726c619dd --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/HairStylist.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class HairStylist : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public HairStylist() : base( "the hair stylist" ) + { + SetSkill( SkillName.Alchemy, 80.0, 100.0 ); + SetSkill( SkillName.Magery, 90.0, 110.0 ); + SetSkill( SkillName.TasteID, 85.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBHairStylist() ); + } + + public HairStylist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Herbalist.cs b/Scripts/Mobiles/Vendors/NPC/Herbalist.cs new file mode 100644 index 000000000..7213b0310 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Herbalist.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Herbalist : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.MagesGuild; } } + + [Constructable] + public Herbalist() : base( "the herbalist" ) + { + SetSkill( SkillName.Alchemy, 80.0, 100.0 ); + SetSkill( SkillName.Cooking, 80.0, 100.0 ); + SetSkill( SkillName.TasteID, 80.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBHerbalist() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public Herbalist( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/HolyMage.cs b/Scripts/Mobiles/Vendors/NPC/HolyMage.cs new file mode 100644 index 000000000..278118c48 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/HolyMage.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class HolyMage : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public HolyMage() : base( "the Holy Mage" ) + { + SetSkill( SkillName.EvalInt, 65.0, 88.0 ); + SetSkill( SkillName.Inscribe, 60.0, 83.0 ); + SetSkill( SkillName.Magery, 64.0, 100.0 ); + SetSkill( SkillName.Meditation, 60.0, 83.0 ); + SetSkill( SkillName.MagicResist, 65.0, 88.0 ); + SetSkill( SkillName.Wrestling, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBHolyMage() ); + } + + public Item ApplyHue( Item item, int hue ) + { + item.Hue = hue; + + return item; + } + + public override void InitOutfit() + { + AddItem( ApplyHue( new Robe(), 0x47E ) ); + AddItem( ApplyHue( new ThighBoots(), 0x47E ) ); + AddItem( ApplyHue( new BlackStaff(), 0x47E ) ); + + if ( Female ) + { + AddItem( ApplyHue( new LeatherGloves(), 0x47E ) ); + AddItem( ApplyHue( new GoldNecklace(), 0x47E ) ); + } + else + { + AddItem( ApplyHue( new PlateGloves(), 0x47E ) ); + AddItem( ApplyHue( new PlateGorget(), 0x47E ) ); + } + + switch ( Utility.Random( Female ? 2 : 1 ) ) + { + case 0: HairItemID = 0x203C; break; + case 1: HairItemID = 0x203D; break; + } + + HairHue = 0x47E; + + PackGold( 100, 200 ); + } + + public HolyMage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/InnKeeper.cs b/Scripts/Mobiles/Vendors/NPC/InnKeeper.cs new file mode 100644 index 000000000..31c66556d --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/InnKeeper.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class InnKeeper : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public InnKeeper() : base( "the innkeeper" ) + { + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBInnKeeper() ); + + if ( IsTokunoVendor ) + m_SBInfos.Add( new SBSEFood() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Sandals : VendorShoeType.Shoes; } + } + + public InnKeeper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/IronWorker.cs b/Scripts/Mobiles/Vendors/NPC/IronWorker.cs new file mode 100644 index 000000000..43c7724c7 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/IronWorker.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class IronWorker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public IronWorker() : base( "the iron worker" ) + { + SetSkill( SkillName.ArmsLore, 36.0, 68.0 ); + SetSkill( SkillName.Blacksmith, 65.0, 88.0 ); + SetSkill( SkillName.Fencing, 60.0, 83.0 ); + SetSkill( SkillName.Macing, 61.0, 93.0 ); + SetSkill( SkillName.Swords, 60.0, 83.0 ); + SetSkill( SkillName.Tactics, 60.0, 83.0 ); + SetSkill( SkillName.Parry, 61.0, 93.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBAxeWeapon() ); + m_SBInfos.Add( new SBKnifeWeapon() ); + m_SBInfos.Add( new SBMaceWeapon() ); + m_SBInfos.Add( new SBSmithTools() ); + m_SBInfos.Add( new SBPoleArmWeapon() ); + m_SBInfos.Add( new SBSpearForkWeapon() ); + m_SBInfos.Add( new SBSwordWeapon() ); + + m_SBInfos.Add( new SBMetalShields() ); + + m_SBInfos.Add( new SBHelmetArmor() ); + m_SBInfos.Add( new SBPlateArmor() ); + m_SBInfos.Add( new SBChainmailArmor() ); + m_SBInfos.Add( new SBRingmailArmor() ); + m_SBInfos.Add( new SBStuddedArmor() ); + m_SBInfos.Add( new SBLeatherArmor() ); + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.None; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + Item item = ( Utility.RandomBool() ? null : new Server.Items.RingmailChest() ); + + if ( item != null && !EquipItem( item ) ) + { + item.Delete(); + item = null; + } + + switch ( Utility.Random( 3 ) ) + { + case 0: + case 1: AddItem( new JesterHat( RandomBrightHue() ) ); break; + case 2: AddItem( new Bandana( RandomBrightHue() ) ); break; + } + + if ( item == null ) + AddItem( new FullApron( RandomBrightHue() ) ); + + AddItem( new Bascinet() ); + AddItem( new SmithHammer() ); + + item = FindItemOnLayer( Layer.Pants ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerLegs ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.OuterTorso ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.InnerTorso ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + + item = FindItemOnLayer( Layer.Shirt ); + + if ( item != null ) + item.Hue = RandomBrightHue(); + } + + public IronWorker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Jeweler.cs b/Scripts/Mobiles/Vendors/NPC/Jeweler.cs new file mode 100644 index 000000000..4a1668998 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Jeweler.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Jeweler : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Jeweler() : base( "the jeweler" ) + { + SetSkill( SkillName.ItemID, 64.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBJewel() ); + } + + public Jeweler( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/KeeperOfChivalry.cs b/Scripts/Mobiles/Vendors/NPC/KeeperOfChivalry.cs new file mode 100644 index 000000000..ffcf608c0 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/KeeperOfChivalry.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class KeeperOfChivalry : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public KeeperOfChivalry() : base( "the Keeper of Chivalry" ) + { + SetSkill( SkillName.Fencing, 75.0, 85.0 ); + SetSkill( SkillName.Macing, 75.0, 85.0 ); + SetSkill( SkillName.Swords, 75.0, 85.0 ); + SetSkill( SkillName.Chivalry, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBKeeperOfChivalry() ); + } + + public override void InitOutfit() + { + AddItem( new PlateArms() ); + AddItem( new PlateChest() ); + AddItem( new PlateGloves() ); + AddItem( new StuddedGorget() ); + AddItem( new PlateLegs() ); + + switch ( Utility.Random( 4 ) ) + { + case 0: AddItem( new PlateHelm() ); break; + case 1: AddItem( new NorseHelm() ); break; + case 2: AddItem( new CloseHelm() ); break; + case 3: AddItem( new Helmet() ); break; + } + + switch ( Utility.Random( 3 ) ) + { + case 0: AddItem( new BodySash( 0x482 ) ); break; + case 1: AddItem( new Doublet( 0x482 ) ); break; + case 2: AddItem( new Tunic( 0x482 ) ); break; + } + + AddItem( new Broadsword() ); + + Item shield = new MetalKiteShield(); + + shield.Hue = Utility.RandomNondyedHue(); + + AddItem( shield ); + + switch ( Utility.Random( 2 ) ) + { + case 0: AddItem( new Boots() ); break; + case 1: AddItem( new ThighBoots() ); break; + } + + PackGold( 100, 200 ); + } + + public KeeperOfChivalry( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/LeatherWorker.cs b/Scripts/Mobiles/Vendors/NPC/LeatherWorker.cs new file mode 100644 index 000000000..313268cfc --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/LeatherWorker.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class LeatherWorker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public LeatherWorker() : base( "the leather worker" ) + { + } + public override void InitSBInfo() + { + m_SBInfos.Add( new SBLeatherArmor() ); + m_SBInfos.Add( new SBStuddedArmor() ); + m_SBInfos.Add( new SBLeatherWorker() ); + } + public LeatherWorker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Vendors/NPC/Mage.cs b/Scripts/Mobiles/Vendors/NPC/Mage.cs new file mode 100644 index 000000000..761f9dcb5 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Mage.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Mage : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.MagesGuild; } } + + [Constructable] + public Mage() : base( "the mage" ) + { + SetSkill( SkillName.EvalInt, 65.0, 88.0 ); + SetSkill( SkillName.Inscribe, 60.0, 83.0 ); + SetSkill( SkillName.Magery, 64.0, 100.0 ); + SetSkill( SkillName.Meditation, 60.0, 83.0 ); + SetSkill( SkillName.MagicResist, 65.0, 88.0 ); + SetSkill( SkillName.Wrestling, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBMage() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Robe( Utility.RandomBlueHue() ) ); + } + + public Mage( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Mapmaker.cs b/Scripts/Mobiles/Vendors/NPC/Mapmaker.cs new file mode 100644 index 000000000..7c86d763e --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Mapmaker.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Mapmaker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Mapmaker() : base( "the mapmaker" ) + { + SetSkill( SkillName.Cartography, 90.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBMapmaker() ); + } + + public Mapmaker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Miller.cs b/Scripts/Mobiles/Vendors/NPC/Miller.cs new file mode 100644 index 000000000..596778e01 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Miller.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Miller : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Miller() : base( "the miller" ) + { + + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBMiller() ); + } + + public Miller( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Miner.cs b/Scripts/Mobiles/Vendors/NPC/Miner.cs new file mode 100644 index 000000000..a8fa81aad --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Miner.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Miner : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Miner() : base( "the miner" ) + { + SetSkill( SkillName.Mining, 65.0, 88.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBMiner() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.FancyShirt( 0x3E4 ) ); + AddItem( new Server.Items.LongPants( 0x192 ) ); + AddItem( new Server.Items.Pickaxe() ); + AddItem( new Server.Items.ThighBoots( 0x283 ) ); + } + + public Miner( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Provisioner.cs b/Scripts/Mobiles/Vendors/NPC/Provisioner.cs new file mode 100644 index 000000000..dbece4d45 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Provisioner.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Provisioner : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Provisioner() : base( "the provisioner" ) + { + SetSkill( SkillName.Camping, 45.0, 68.0 ); + SetSkill( SkillName.Tactics, 45.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBProvisioner() ); + + if ( IsTokunoVendor ) + m_SBInfos.Add( new SBSEHats() ); + } + + public Provisioner( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Rancher.cs b/Scripts/Mobiles/Vendors/NPC/Rancher.cs new file mode 100644 index 000000000..7a3748ccd --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Rancher.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public class Rancher : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Rancher() : base( "the rancher" ) + { + SetSkill( SkillName.AnimalLore, 55.0, 78.0 ); + SetSkill( SkillName.AnimalTaming, 55.0, 78.0 ); + SetSkill( SkillName.Herding, 64.0, 100.0 ); + SetSkill( SkillName.Veterinary, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBRancher() ); + } + + public Rancher( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Ranger.cs b/Scripts/Mobiles/Vendors/NPC/Ranger.cs new file mode 100644 index 000000000..bedf15ae2 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Ranger.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Ranger : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Ranger() : base( "the ranger" ) + { + SetSkill( SkillName.Camping, 55.0, 78.0 ); + SetSkill( SkillName.DetectHidden, 65.0, 88.0 ); + SetSkill( SkillName.Hiding, 45.0, 68.0 ); + SetSkill( SkillName.Archery, 65.0, 88.0 ); + SetSkill( SkillName.Tracking, 65.0, 88.0 ); + SetSkill( SkillName.Veterinary, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBRanger() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Shirt( Utility.RandomNeutralHue() ) ); + AddItem( new Server.Items.LongPants( Utility.RandomNeutralHue() ) ); + AddItem( new Server.Items.Bow() ); + AddItem( new Server.Items.ThighBoots( Utility.RandomNeutralHue() ) ); + } + + public Ranger( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/RealEstateBroker.cs b/Scripts/Mobiles/Vendors/NPC/RealEstateBroker.cs new file mode 100644 index 000000000..2dae8b25c --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/RealEstateBroker.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Multis; +using Server.Multis.Deeds; +using Server.Targeting; +using Server.Network; + +namespace Server.Mobiles +{ + public class RealEstateBroker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public RealEstateBroker() : base( "the real estate broker" ) + { + } + + public override bool HandlesOnSpeech( Mobile from ) + { + if ( from.Alive && from.InRange( this, 3 ) ) + return true; + + return base.HandlesOnSpeech( from ); + } + + private DateTime m_NextCheckPack; + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + if ( DateTime.Now > m_NextCheckPack && InRange( m, 4 ) && !InRange( oldLocation, 4 ) && m.Player ) + { + Container pack = m.Backpack; + + if ( pack != null ) + { + m_NextCheckPack = DateTime.Now + TimeSpan.FromSeconds( 2.0 ); + + Item deed = pack.FindItemByType( typeof( HouseDeed ), false ); + + if ( deed != null ) + { + // If you have a deed, I can appraise it or buy it from you... + PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500605, m.NetState ); + + // Simply hand me a deed to sell it. + PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500606, m.NetState ); + } + } + } + + base.OnMovement( m, oldLocation ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( !e.Handled && e.Mobile.Alive && e.HasKeyword( 0x38 ) ) // *appraise* + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500608 ); // Which deed would you like appraised? + e.Mobile.BeginTarget( 12, false, TargetFlags.None, new TargetCallback( Appraise_OnTarget ) ); + e.Handled = true; + } + + base.OnSpeech( e ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is HouseDeed ) + { + HouseDeed deed = (HouseDeed)dropped; + int price = ComputePriceFor( deed ); + + if ( price > 0 ) + { + if ( Banker.Deposit( from, price ) ) + { + // For the deed I have placed gold in your bankbox : + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1008000, AffixType.Append, price.ToString(), "" ); + + deed.Delete(); + return true; + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500390 ); // Your bank box is full. + return false; + } + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500607 ); // I'm not interested in that. + return false; + } + } + + return base.OnDragDrop (from, dropped); + } + + public void Appraise_OnTarget( Mobile from, object obj ) + { + if ( obj is HouseDeed ) + { + HouseDeed deed = (HouseDeed)obj; + int price = ComputePriceFor( deed ); + + if ( price > 0 ) + { + // I will pay you gold for this deed : + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1008001, AffixType.Append, price.ToString(), "" ); + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500610 ); // Simply hand me the deed if you wish to sell it. + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500607 ); // I'm not interested in that. + } + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 500609 ); // I can't appraise things I know nothing about... + } + } + + public int ComputePriceFor( HouseDeed deed ) + { + int price = 0; + + if ( deed is SmallBrickHouseDeed || deed is StonePlasterHouseDeed || deed is FieldStoneHouseDeed || deed is SmallBrickHouseDeed || deed is WoodHouseDeed || deed is WoodPlasterHouseDeed || deed is ThatchedRoofCottageDeed ) + price = 43800; + else if ( deed is BrickHouseDeed ) + price = 144500; + else if ( deed is TwoStoryWoodPlasterHouseDeed || deed is TwoStoryStonePlasterHouseDeed ) + price = 192400; + else if ( deed is TowerDeed ) + price = 433200; + else if ( deed is KeepDeed ) + price = 665200; + else if ( deed is CastleDeed ) + price = 1022800; + else if ( deed is LargePatioDeed ) + price = 152800; + else if ( deed is LargeMarbleDeed ) + price = 192800; + else if ( deed is SmallTowerDeed ) + price = 88500; + else if ( deed is LogCabinDeed ) + price = 97800; + else if ( deed is SandstonePatioDeed ) + price = 90900; + else if ( deed is VillaDeed ) + price = 136500; + else if ( deed is StoneWorkshopDeed ) + price = 60600; + else if ( deed is MarbleWorkshopDeed ) + price = 60300; + + return AOS.Scale( price, 80 ); // refunds 80% of the purchase price + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBRealEstateBroker() ); + } + + public RealEstateBroker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Scribe.cs b/Scripts/Mobiles/Vendors/NPC/Scribe.cs new file mode 100644 index 000000000..903b131ec --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Scribe.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Scribe : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.MagesGuild; } } + + [Constructable] + public Scribe() : base( "the scribe" ) + { + SetSkill( SkillName.EvalInt, 60.0, 83.0 ); + SetSkill( SkillName.Inscribe, 90.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBScribe() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Shoes : VendorShoeType.Sandals; } + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Robe( Utility.RandomNeutralHue() ) ); + } + + public Scribe( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Shipwright.cs b/Scripts/Mobiles/Vendors/NPC/Shipwright.cs new file mode 100644 index 000000000..eacdc7ac5 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Shipwright.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Shipwright : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Shipwright() : base( "the shipwright" ) + { + SetSkill( SkillName.Carpentry, 60.0, 83.0 ); + SetSkill( SkillName.Macing, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBShipwright() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.SmithHammer() ); + } + + public Shipwright( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/StoneCrafter.cs b/Scripts/Mobiles/Vendors/NPC/StoneCrafter.cs new file mode 100644 index 000000000..09bea2b0e --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/StoneCrafter.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + [TypeAlias( "Server.Mobiles.GargoyleStonecrafter" )] + public class StoneCrafter : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.TinkersGuild; } } + + [Constructable] + public StoneCrafter() : base( "the stone crafter" ) + { + SetSkill( SkillName.Carpentry, 85.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBStoneCrafter() ); + m_SBInfos.Add( new SBStavesWeapon() ); + m_SBInfos.Add( new SBCarpenter() ); + m_SBInfos.Add( new SBWoodenShields() ); + } + + public StoneCrafter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Title == "the stonecrafter" ) + Title = "the stone crafter"; + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Tailor.cs b/Scripts/Mobiles/Vendors/NPC/Tailor.cs new file mode 100644 index 000000000..240da1e58 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Tailor.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; +using Server; +using Server.Engines.BulkOrders; + +namespace Server.Mobiles +{ + public class Tailor : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.TailorsGuild; } } + + [Constructable] + public Tailor() : base( "the tailor" ) + { + SetSkill( SkillName.Tailoring, 64.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBTailor() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Sandals : VendorShoeType.Shoes; } + } + + #region Bulk Orders + public override Item CreateBulkOrder( Mobile from, bool fromContextMenu ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null && pm.NextTailorBulkOrder == TimeSpan.Zero && (fromContextMenu || 0.2 > Utility.RandomDouble()) ) + { + double theirSkill = pm.Skills[SkillName.Tailoring].Base; + + if ( theirSkill >= 70.1 ) + pm.NextTailorBulkOrder = TimeSpan.FromHours( 6.0 ); + else if ( theirSkill >= 50.1 ) + pm.NextTailorBulkOrder = TimeSpan.FromHours( 2.0 ); + else + pm.NextTailorBulkOrder = TimeSpan.FromHours( 1.0 ); + + if ( theirSkill >= 70.1 && ((theirSkill - 40.0) / 300.0) > Utility.RandomDouble() ) + return new LargeTailorBOD(); + + return SmallTailorBOD.CreateRandomFor( from ); + } + + return null; + } + + public override bool IsValidBulkOrder( Item item ) + { + return ( item is SmallTailorBOD || item is LargeTailorBOD ); + } + + public override bool SupportsBulkOrders( Mobile from ) + { + return ( from is PlayerMobile && from.Skills[SkillName.Tailoring].Base > 0 ); + } + + public override TimeSpan GetNextBulkOrder( Mobile from ) + { + if ( from is PlayerMobile ) + return ((PlayerMobile)from).NextTailorBulkOrder; + + return TimeSpan.Zero; + } + + public override void OnSuccessfulBulkOrderRecieve( Mobile from ) + { + if( Core.SE && from is PlayerMobile ) + ((PlayerMobile)from).NextTailorBulkOrder = TimeSpan.Zero; + } + #endregion + + public Tailor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Vendors/NPC/Tanner.cs b/Scripts/Mobiles/Vendors/NPC/Tanner.cs new file mode 100644 index 000000000..6efcca6cb --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Tanner.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Tanner : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Tanner() : base( "the tanner" ) + { + SetSkill( SkillName.Tailoring, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBTanner() ); + } + + public Tanner( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Mobiles/Vendors/NPC/TavernKeeper.cs b/Scripts/Mobiles/Vendors/NPC/TavernKeeper.cs new file mode 100644 index 000000000..92d17f04b --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/TavernKeeper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class TavernKeeper : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public TavernKeeper() : base( "the tavern keeper" ) + { + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBTavernKeeper() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron() ); + } + + public TavernKeeper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Thief.cs b/Scripts/Mobiles/Vendors/NPC/Thief.cs new file mode 100644 index 000000000..cc57fb5bf --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Thief.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Thief : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Thief() : base( "the thief" ) + { + SetSkill( SkillName.Camping, 55.0, 78.0 ); + SetSkill( SkillName.DetectHidden, 65.0, 88.0 ); + SetSkill( SkillName.Hiding, 45.0, 68.0 ); + SetSkill( SkillName.Archery, 65.0, 88.0 ); + SetSkill( SkillName.Tracking, 65.0, 88.0 ); + SetSkill( SkillName.Veterinary, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBThief() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.Shirt( Utility.RandomNeutralHue() ) ); + AddItem( new Server.Items.LongPants( Utility.RandomNeutralHue() ) ); + AddItem( new Server.Items.Dagger() ); + AddItem( new Server.Items.ThighBoots( Utility.RandomNeutralHue() ) ); + } + + public Thief( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Tinker.cs b/Scripts/Mobiles/Vendors/NPC/Tinker.cs new file mode 100644 index 000000000..6d9d8b7a8 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Tinker.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Tinker : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.TinkersGuild; } } + + [Constructable] + public Tinker() : base( "the tinker" ) + { + SetSkill( SkillName.Lockpicking, 60.0, 83.0 ); + SetSkill( SkillName.RemoveTrap, 75.0, 98.0 ); + SetSkill( SkillName.Tinkering, 64.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBTinker() ); + } + + public Tinker( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Vagabond.cs b/Scripts/Mobiles/Vendors/NPC/Vagabond.cs new file mode 100644 index 000000000..8f485445f --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Vagabond.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Mobiles +{ + public class Vagabond : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Vagabond() : base( "the vagabond" ) + { + SetSkill( SkillName.ItemID, 60.0, 83.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBTinker() ); + m_SBInfos.Add( new SBVagabond() ); + } + + public override void InitOutfit() + { + AddItem( new FancyShirt( RandomBrightHue() ) ); + AddItem( new Shoes( GetShoeHue() ) ); + AddItem( new LongPants( GetRandomHue() ) ); + + if ( Utility.RandomBool() ) + AddItem( new Cloak( RandomBrightHue() ) ); + + switch ( Utility.Random( 2 ) ) + { + case 0: AddItem( new SkullCap( Utility.RandomNeutralHue() ) ); break; + case 1: AddItem( new Bandana( Utility.RandomNeutralHue() ) ); break; + } + + + Utility.AssignRandomHair( this ); + Utility.AssignRandomFacialHair( this, HairHue ); + + PackGold( 100, 200 ); + } + + public Vagabond( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/VarietyDealer.cs b/Scripts/Mobiles/Vendors/NPC/VarietyDealer.cs new file mode 100644 index 000000000..717589f70 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/VarietyDealer.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class VarietyDealer : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public VarietyDealer() : base( "the variety dealer" ) + { + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBVarietyDealer() ); + } + + public VarietyDealer( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Veterinarian.cs b/Scripts/Mobiles/Vendors/NPC/Veterinarian.cs new file mode 100644 index 000000000..8fb2157a8 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Veterinarian.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + public class Veterinarian : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Veterinarian() : base( "the vet" ) + { + SetSkill( SkillName.AnimalLore, 85.0, 100.0 ); + SetSkill( SkillName.Veterinary, 90.0, 100.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBVeterinarian() ); + } + + public Veterinarian( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Waiter.cs b/Scripts/Mobiles/Vendors/NPC/Waiter.cs new file mode 100644 index 000000000..99ff46cbb --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Waiter.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Mobiles +{ + public class Waiter : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Waiter() : base( "the waiter" ) + { + SetSkill( SkillName.Discordance, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBWaiter() ); + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron() ); + } + + public Waiter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Weaponsmith.cs b/Scripts/Mobiles/Vendors/NPC/Weaponsmith.cs new file mode 100644 index 000000000..3fc53224e --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Weaponsmith.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; +using Server; +using Server.Engines.BulkOrders; + +namespace Server.Mobiles +{ + public class Weaponsmith : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + [Constructable] + public Weaponsmith() : base( "the weaponsmith" ) + { + SetSkill( SkillName.ArmsLore, 64.0, 100.0 ); + SetSkill( SkillName.Blacksmith, 65.0, 88.0 ); + SetSkill( SkillName.Fencing, 45.0, 68.0 ); + SetSkill( SkillName.Macing, 45.0, 68.0 ); + SetSkill( SkillName.Swords, 45.0, 68.0 ); + SetSkill( SkillName.Tactics, 36.0, 68.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBWeaponSmith() ); + + if ( IsTokunoVendor ) + m_SBInfos.Add( new SBSEWeapons() ); + } + + public override VendorShoeType ShoeType + { + get{ return Utility.RandomBool() ? VendorShoeType.Boots : VendorShoeType.ThighBoots; } + } + + public override int GetShoeHue() + { + return 0; + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new Server.Items.HalfApron() ); + } + + #region Bulk Orders + public override Item CreateBulkOrder( Mobile from, bool fromContextMenu ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null && pm.NextSmithBulkOrder == TimeSpan.Zero && (fromContextMenu || 0.2 > Utility.RandomDouble()) ) + { + double theirSkill = pm.Skills[SkillName.Blacksmith].Base; + + if ( theirSkill >= 70.1 ) + pm.NextSmithBulkOrder = TimeSpan.FromHours( 6.0 ); + else if ( theirSkill >= 50.1 ) + pm.NextSmithBulkOrder = TimeSpan.FromHours( 2.0 ); + else + pm.NextSmithBulkOrder = TimeSpan.FromHours( 1.0 ); + + if ( theirSkill >= 70.1 && ((theirSkill - 40.0) / 300.0) > Utility.RandomDouble() ) + return new LargeSmithBOD(); + + return SmallSmithBOD.CreateRandomFor( from ); + } + + return null; + } + + public override bool IsValidBulkOrder( Item item ) + { + return ( item is SmallSmithBOD || item is LargeSmithBOD ); + } + + public override bool SupportsBulkOrders( Mobile from ) + { + return ( from is PlayerMobile && Core.AOS && from.Skills[SkillName.Blacksmith].Base > 0 ); + } + + public override TimeSpan GetNextBulkOrder( Mobile from ) + { + if ( from is PlayerMobile ) + return ((PlayerMobile)from).NextSmithBulkOrder; + + return TimeSpan.Zero; + } + + public override void OnSuccessfulBulkOrderRecieve( Mobile from ) + { + if( Core.SE && from is PlayerMobile ) + ((PlayerMobile)from).NextSmithBulkOrder = TimeSpan.Zero; + } + #endregion + + public Weaponsmith( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/NPC/Weaver.cs b/Scripts/Mobiles/Vendors/NPC/Weaver.cs new file mode 100644 index 000000000..09c66b5c7 --- /dev/null +++ b/Scripts/Mobiles/Vendors/NPC/Weaver.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections; +using Server; +using Server.Engines.BulkOrders; + +namespace Server.Mobiles +{ + public class Weaver : BaseVendor + { + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override NpcGuild NpcGuild{ get{ return NpcGuild.TailorsGuild; } } + + [Constructable] + public Weaver() : base( "the weaver" ) + { + SetSkill( SkillName.Tailoring, 65.0, 88.0 ); + } + + public override void InitSBInfo() + { + m_SBInfos.Add( new SBWeaver() ); + } + + public override VendorShoeType ShoeType + { + get{ return VendorShoeType.Sandals; } + } + + #region Bulk Orders + public override Item CreateBulkOrder( Mobile from, bool fromContextMenu ) + { + PlayerMobile pm = from as PlayerMobile; + + if ( pm != null && pm.NextTailorBulkOrder == TimeSpan.Zero && (fromContextMenu || 0.2 > Utility.RandomDouble()) ) + { + double theirSkill = pm.Skills[SkillName.Tailoring].Base; + + if ( theirSkill >= 70.1 ) + pm.NextTailorBulkOrder = TimeSpan.FromHours( 6.0 ); + else if ( theirSkill >= 50.1 ) + pm.NextTailorBulkOrder = TimeSpan.FromHours( 2.0 ); + else + pm.NextTailorBulkOrder = TimeSpan.FromHours( 1.0 ); + + if ( theirSkill >= 70.1 && ((theirSkill - 40.0) / 300.0) > Utility.RandomDouble() ) + return new LargeTailorBOD(); + + return SmallTailorBOD.CreateRandomFor( from ); + } + + return null; + } + + public override bool IsValidBulkOrder( Item item ) + { + return ( item is SmallTailorBOD || item is LargeTailorBOD ); + } + + public override bool SupportsBulkOrders( Mobile from ) + { + return ( from is PlayerMobile && from.Skills[SkillName.Tailoring].Base > 0 ); + } + + public override TimeSpan GetNextBulkOrder( Mobile from ) + { + if ( from is PlayerMobile ) + return ((PlayerMobile)from).NextTailorBulkOrder; + + return TimeSpan.Zero; + } + + public override void OnSuccessfulBulkOrderRecieve( Mobile from ) + { + if( Core.SE && from is PlayerMobile ) + ((PlayerMobile)from).NextTailorBulkOrder = TimeSpan.Zero; + } + #endregion + + public Weaver( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/PlayerBarkeeper.cs b/Scripts/Mobiles/Vendors/PlayerBarkeeper.cs new file mode 100644 index 000000000..671f6dd37 --- /dev/null +++ b/Scripts/Mobiles/Vendors/PlayerBarkeeper.cs @@ -0,0 +1,1068 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Prompts; +using Server.Network; +using Server.ContextMenus; +using Server.Multis; + +namespace Server.Mobiles +{ + public class ChangeRumorMessagePrompt : Prompt + { + private PlayerBarkeeper m_Barkeeper; + private int m_RumorIndex; + + public ChangeRumorMessagePrompt( PlayerBarkeeper barkeeper, int rumorIndex ) + { + m_Barkeeper = barkeeper; + m_RumorIndex = rumorIndex; + } + + public override void OnCancel( Mobile from ) + { + OnResponse( from, "" ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( text.Length > 50 ) + text = text.Substring( 0, 50 ); + + m_Barkeeper.EndChangeRumor( from, m_RumorIndex, text ); + } + } + + public class ChangeRumorKeywordPrompt : Prompt + { + private PlayerBarkeeper m_Barkeeper; + private int m_RumorIndex; + + public ChangeRumorKeywordPrompt( PlayerBarkeeper barkeeper, int rumorIndex ) + { + m_Barkeeper = barkeeper; + m_RumorIndex = rumorIndex; + } + + public override void OnCancel( Mobile from ) + { + OnResponse( from, "" ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( text.Length > 50 ) + text = text.Substring( 0, 50 ); + + m_Barkeeper.EndChangeKeyword( from, m_RumorIndex, text ); + } + } + + public class ChangeTipMessagePrompt : Prompt + { + private PlayerBarkeeper m_Barkeeper; + + public ChangeTipMessagePrompt( PlayerBarkeeper barkeeper ) + { + m_Barkeeper = barkeeper; + } + + public override void OnCancel( Mobile from ) + { + OnResponse( from, "" ); + } + + public override void OnResponse( Mobile from, string text ) + { + if ( text.Length > 50 ) + text = text.Substring( 0, 50 ); + + m_Barkeeper.EndChangeTip( from, text ); + } + } + + public class BarkeeperRumor + { + private string m_Message; + private string m_Keyword; + + public string Message{ get{ return m_Message; } set{ m_Message = value; } } + public string Keyword{ get{ return m_Keyword; } set{ m_Keyword = value; } } + + public BarkeeperRumor( string message, string keyword ) + { + m_Message = message; + m_Keyword = keyword; + } + + public static BarkeeperRumor Deserialize( GenericReader reader ) + { + if ( !reader.ReadBool() ) + return null; + + return new BarkeeperRumor( reader.ReadString(), reader.ReadString() ); + } + + public static void Serialize( GenericWriter writer, BarkeeperRumor rumor ) + { + if ( rumor == null ) + { + writer.Write( false ); + } + else + { + writer.Write( true ); + writer.Write( rumor.m_Message ); + writer.Write( rumor.m_Keyword ); + } + } + } + + public class ManageBarkeeperEntry : ContextMenuEntry + { + private Mobile m_From; + private PlayerBarkeeper m_Barkeeper; + + public ManageBarkeeperEntry( Mobile from, PlayerBarkeeper barkeeper ) : base( 6151, 12 ) + { + m_From = from; + m_Barkeeper = barkeeper; + } + + public override void OnClick() + { + m_Barkeeper.BeginManagement( m_From ); + } + } + + public class PlayerBarkeeper : BaseVendor + { + private Mobile m_Owner; + private BaseHouse m_House; + private string m_TipMessage; + + private BarkeeperRumor[] m_Rumors; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + public BaseHouse House + { + get{ return m_House; } + set + { + if ( m_House != null ) + m_House.PlayerBarkeepers.Remove( this ); + + if ( value != null ) + value.PlayerBarkeepers.Add( this ); + + m_House = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string TipMessage + { + get{ return m_TipMessage; } + set{ m_TipMessage = value; } + } + + public override bool IsActiveBuyer{ get{ return false; } } + public override bool IsActiveSeller{ get{ return ( m_SBInfos.Count > 0 ); } } + + public override bool DisallowAllMoves{ get{ return true; } } + public override bool NoHouseRestrictions{ get{ return true; } } + + public BarkeeperRumor[] Rumors{ get{ return m_Rumors; } } + + public override VendorShoeType ShoeType{ get{ return Utility.RandomBool() ? VendorShoeType.ThighBoots : VendorShoeType.Boots; } } + + public override bool GetGender() + { + return false; // always starts as male + } + + public override void InitOutfit() + { + base.InitOutfit(); + + AddItem( new HalfApron( RandomBrightHue() ) ); + + Container pack = this.Backpack; + + if ( pack != null ) + pack.Delete(); + } + + public override void InitBody() + { + base.InitBody(); + + Hue = 0x83F4; // hue is not random + + Container pack = this.Backpack; + + if ( pack != null ) + pack.Delete(); + } + + public PlayerBarkeeper( Mobile owner, BaseHouse house ) : base( "the barkeeper" ) + { + m_Owner = owner; + House = house; + m_Rumors = new BarkeeperRumor[3]; + + LoadSBInfo(); + } + + public override bool HandlesOnSpeech(Mobile from) + { + if ( InRange( from, 3 ) ) + return true; + + return base.HandlesOnSpeech (from); + } + + private Timer m_NewsTimer; + + private void ShoutNews_Callback( object state ) + { + object[] states = (object[])state; + TownCrierEntry tce = (TownCrierEntry)states[0]; + int index = (int)states[1]; + + if ( index < 0 || index >= tce.Lines.Length ) + { + if ( m_NewsTimer != null ) + m_NewsTimer.Stop(); + + m_NewsTimer = null; + } + else + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, false, tce.Lines[index] ); + states[1] = index + 1; + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + House = null; + } + + public override bool OnBeforeDeath() + { + if ( !base.OnBeforeDeath() ) + return false; + + Item shoes = this.FindItemOnLayer( Layer.Shoes ); + + if ( shoes is Sandals ) + shoes.Hue = 0; + + return true; + } + + public override void OnSpeech( SpeechEventArgs e ) + { + base.OnSpeech( e ); + + if ( !e.Handled && InRange( e.Mobile, 3 ) ) + { + if ( m_NewsTimer == null && e.HasKeyword( 0x30 ) ) // *news* + { + TownCrierEntry tce = GlobalTownCrierEntryList.Instance.GetRandomEntry(); + + if ( tce == null ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, 1005643 ); // I have no news at this time. + } + else + { + m_NewsTimer = Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 3.0 ), new TimerStateCallback( ShoutNews_Callback ), new object[]{ tce, 0 } ); + + PublicOverheadMessage( MessageType.Regular, 0x3B2, 502978 ); // Some of the latest news! + } + } + + for ( int i = 0; i < m_Rumors.Length; ++i ) + { + BarkeeperRumor rumor = m_Rumors[i]; + + if ( rumor == null ) + continue; + + string keyword = rumor.Keyword; + + if ( keyword == null || (keyword = keyword.Trim()).Length == 0 ) + continue; + + if ( Insensitive.Equals( keyword, e.Speech ) ) + { + string message = rumor.Message; + + if ( message == null || (message = message.Trim()).Length == 0 ) + continue; + + PublicOverheadMessage( MessageType.Regular, 0x3B2, false, message ); + } + } + } + } + + public override bool CheckGold( Mobile from, Item dropped ) + { + if ( dropped is Gold ) + { + Gold g = (Gold)dropped; + + if ( g.Amount > 50 ) + { + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "I cannot accept so large a tip!", from.NetState ); + } + else + { + string tip = m_TipMessage; + + if ( tip == null || (tip = tip.Trim()).Length == 0 ) + { + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "It would not be fair of me to take your money and not offer you information in return.", from.NetState ); + } + else + { + Direction = GetDirectionTo( from ); + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, tip, from.NetState ); + + g.Delete(); + return true; + } + } + } + + return false; + } + + public bool IsOwner( Mobile from ) + { + if ( from == null || from.Deleted || this.Deleted ) + return false; + + if ( from.AccessLevel > AccessLevel.GameMaster ) + return true; + + return ( m_Owner == from ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( IsOwner( from ) && from.InLOS( this ) ) + list.Add( new ManageBarkeeperEntry( from, this ) ); + } + + public void BeginManagement( Mobile from ) + { + if ( !IsOwner( from ) ) + return; + + from.SendGump( new BarkeeperGump( from, this ) ); + } + + public void Dismiss() + { + Delete(); + } + + public void BeginChangeRumor( Mobile from, int index ) + { + if ( index < 0 || index >= m_Rumors.Length ) + return; + + from.Prompt = new ChangeRumorMessagePrompt( this, index ); + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "Say what news you would like me to tell our guests.", from.NetState ); + } + + public void EndChangeRumor( Mobile from, int index, string text ) + { + if ( index < 0 || index >= m_Rumors.Length ) + return; + + if ( m_Rumors[index] == null ) + m_Rumors[index] = new BarkeeperRumor( text, null ); + else + m_Rumors[index].Message = text; + + from.Prompt = new ChangeRumorKeywordPrompt( this, index ); + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "What keyword should a guest say to me to get this news?", from.NetState ); + } + + public void EndChangeKeyword( Mobile from, int index, string text ) + { + if ( index < 0 || index >= m_Rumors.Length ) + return; + + if ( m_Rumors[index] == null ) + m_Rumors[index] = new BarkeeperRumor( null, text ); + else + m_Rumors[index].Keyword = text; + + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "I'll pass on the message.", from.NetState ); + } + + public void RemoveRumor( Mobile from, int index ) + { + if ( index < 0 || index >= m_Rumors.Length ) + return; + + m_Rumors[index] = null; + } + + public void BeginChangeTip( Mobile from ) + { + from.Prompt = new ChangeTipMessagePrompt( this ); + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "Say what you want me to tell guests when they give me a good tip.", from.NetState ); + } + + public void EndChangeTip( Mobile from, string text ) + { + m_TipMessage = text; + PrivateOverheadMessage( MessageType.Regular, 0x3B2, false, "I'll say that to anyone who gives me a good tip.", from.NetState ); + } + + public void RemoveTip( Mobile from ) + { + m_TipMessage = null; + } + + public void BeginChangeTitle( Mobile from ) + { + from.SendGump( new BarkeeperTitleGump( from, this ) ); + } + + public void EndChangeTitle( Mobile from, string title, bool vendor ) + { + this.Title = title; + + LoadSBInfo(); + } + + public void CancelChangeTitle( Mobile from ) + { + from.SendGump( new BarkeeperGump( from, this ) ); + } + + public void BeginChangeAppearance( Mobile from ) + { + from.CloseGump( typeof( PlayerVendorCustomizeGump ) ); + from.SendGump( new PlayerVendorCustomizeGump( this, from ) ); + } + + public void ChangeGender( Mobile from ) + { + Female = !Female; + + if ( Female ) + { + Body = 401; + Name = NameList.RandomName( "female" ); + + FacialHairItemID = 0; + } + else + { + Body = 400; + Name = NameList.RandomName( "male" ); + } + } + + private ArrayList m_SBInfos = new ArrayList(); + protected override ArrayList SBInfos{ get { return m_SBInfos; } } + + public override void InitSBInfo() + { + if ( Title == "the waiter" || Title == "the barkeeper" || Title == "the baker" || Title == "the innkeeper" || Title == "the chef" ) + { + if ( m_SBInfos.Count == 0 ) + m_SBInfos.Add( new SBPlayerBarkeeper() ); + } + else + { + m_SBInfos.Clear(); + } + } + + public PlayerBarkeeper( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version; + + writer.Write( (Item) m_House ); + + writer.Write( (Mobile) m_Owner ); + + writer.WriteEncodedInt( (int) m_Rumors.Length ); + + for ( int i = 0; i < m_Rumors.Length; ++i ) + BarkeeperRumor.Serialize( writer, m_Rumors[i] ); + + writer.Write( (string) m_TipMessage ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + House = (BaseHouse) reader.ReadItem(); + + goto case 0; + } + case 0: + { + m_Owner = reader.ReadMobile(); + + m_Rumors = new BarkeeperRumor[reader.ReadEncodedInt()]; + + for ( int i = 0; i < m_Rumors.Length; ++i ) + m_Rumors[i] = BarkeeperRumor.Deserialize( reader ); + + m_TipMessage = reader.ReadString(); + + break; + } + } + + if ( version < 1 ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( UpgradeFromVersion0 ) ); + } + + private void UpgradeFromVersion0() + { + House = BaseHouse.FindHouseAt( this ); + } + } + + public class BarkeeperTitleGump : Gump + { + private Mobile m_From; + private PlayerBarkeeper m_Barkeeper; + + private class Entry + { + public string m_Description; + public string m_Title; + public bool m_Vendor; + + public Entry( string desc ) : this( desc, String.Format( "the {0}", desc.ToLower() ), false ) + { + } + + public Entry( string desc, bool vendor ) : this( desc, String.Format( "the {0}", desc.ToLower() ), vendor ) + { + } + + public Entry( string desc, string title, bool vendor ) + { + m_Description = desc; + m_Title = title; + m_Vendor = vendor; + } + } + + private static Entry[] m_Entries = new Entry[] + { + new Entry( "Alchemist" ), + new Entry( "Animal Tamer" ), + new Entry( "Apothecary" ), + new Entry( "Artist" ), + new Entry( "Baker", true ), + new Entry( "Bard" ), + new Entry( "Barkeep", "the barkeeper", true ), + new Entry( "Beggar" ), + new Entry( "Blacksmith" ), + new Entry( "Bounty Hunter" ), + new Entry( "Brigand" ), + new Entry( "Butler" ), + new Entry( "Carpenter" ), + new Entry( "Chef", true ), + new Entry( "Commander" ), + new Entry( "Curator" ), + new Entry( "Drunkard" ), + new Entry( "Farmer" ), + new Entry( "Fisherman" ), + new Entry( "Gambler" ), + new Entry( "Gypsy" ), + new Entry( "Herald" ), + new Entry( "Herbalist" ), + new Entry( "Hermit" ), + new Entry( "Innkeeper", true ), + new Entry( "Jailor" ), + new Entry( "Jester" ), + new Entry( "Librarian" ), + new Entry( "Mage" ), + new Entry( "Mercenary" ), + new Entry( "Merchant" ), + new Entry( "Messenger" ), + new Entry( "Miner" ), + new Entry( "Monk" ), + new Entry( "Noble" ), + new Entry( "Paladin" ), + new Entry( "Peasant" ), + new Entry( "Pirate" ), + new Entry( "Prisoner" ), + new Entry( "Prophet" ), + new Entry( "Ranger" ), + new Entry( "Sage" ), + new Entry( "Sailor" ), + new Entry( "Scholar" ), + new Entry( "Scribe" ), + new Entry( "Sentry" ), + new Entry( "Servant" ), + new Entry( "Shepherd" ), + new Entry( "Soothsayer" ), + new Entry( "Stoic" ), + new Entry( "Storyteller" ), + new Entry( "Tailor" ), + new Entry( "Thief" ), + new Entry( "Tinker" ), + new Entry( "Town Crier" ), + new Entry( "Treasure Hunter" ), + new Entry( "Waiter", true ), + new Entry( "Warrior" ), + new Entry( "Watchman" ), + new Entry( "No Title", null, false ) + }; + + private void RenderBackground() + { + AddPage( 0 ); + + AddBackground( 30, 40, 585, 410, 5054 ); + + AddImage( 30, 40, 9251 ); + AddImage( 180, 40, 9251 ); + AddImage( 30, 40, 9253 ); + AddImage( 30, 130, 9253 ); + AddImage( 598, 40, 9255 ); + AddImage( 598, 130, 9255 ); + AddImage( 30, 433, 9257 ); + AddImage( 180, 433, 9257 ); + AddImage( 30, 40, 9250 ); + AddImage( 598, 40, 9252 ); + AddImage( 598, 433, 9258 ); + AddImage( 30, 433, 9256 ); + + AddItem( 30, 40, 6816 ); + AddItem( 30, 125, 6817 ); + AddItem( 30, 233, 6817 ); + AddItem( 30, 341, 6817 ); + AddItem( 580, 40, 6814 ); + AddItem( 588, 125, 6815 ); + AddItem( 588, 233, 6815 ); + AddItem( 588, 341, 6815 ); + + AddImage( 560, 20, 1417 ); + AddItem( 580, 44, 4033 ); + + AddBackground( 183, 25, 280, 30, 5054 ); + + AddImage( 180, 25, 10460 ); + AddImage( 434, 25, 10460 ); + + AddHtml( 223, 32, 200, 40, "BARKEEP CUSTOMIZATION MENU", false, false ); + AddBackground( 243, 433, 150, 30, 5054 ); + + AddImage( 240, 433, 10460 ); + AddImage( 375, 433, 10460 ); + + AddImage( 80, 398, 2151 ); + AddItem( 72, 406, 2543 ); + + AddHtml( 110, 412, 180, 25, "sells food and drink", false, false ); + } + + private void RenderPage( Entry[] entries, int page ) + { + AddPage( 1 + page ); + + AddHtml( 430, 70, 180, 25, String.Format( "Page {0} of {1}", page + 1, (entries.Length + 19) / 20 ), false, false ); + + for ( int count = 0, i = (page * 20); count < 20 && i < entries.Length; ++count, ++i ) + { + Entry entry = entries[i]; + + AddButton( 80 + ((count / 10) * 260), 100 + ((count % 10) * 30), 4005, 4007, 2 + i, GumpButtonType.Reply, 0 ); + AddHtml( 120 + ((count / 10) * 260), 100 + ((count % 10) * 30), entry.m_Vendor ? 148 : 180, 25, entry.m_Description, true, false ); + + if ( entry.m_Vendor ) + { + AddImage( 270 + ((count / 10) * 260), 98 + ((count % 10) * 30), 2151 ); + AddItem( 262 + ((count / 10) * 260), 106 + ((count % 10) * 30), 2543 ); + } + } + + AddButton( 340, 400, 4005, 4007, 0, GumpButtonType.Page, 1 + ((page + 1) % ((entries.Length + 19) / 20)) ); + AddHtml( 380, 400, 180, 25, "More Job Titles", false, false ); + + AddButton( 338, 437, 4014, 4016, 1, GumpButtonType.Reply, 0 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + } + + public BarkeeperTitleGump( Mobile from, PlayerBarkeeper barkeeper ) : base( 0, 0 ) + { + m_From = from; + m_Barkeeper = barkeeper; + + from.CloseGump( typeof( BarkeeperGump ) ); + from.CloseGump( typeof( BarkeeperTitleGump ) ); + + Entry[] entries = m_Entries; + + RenderBackground(); + + int pageCount = (entries.Length + 19) / 20; + + for ( int i = 0; i < pageCount; ++i ) + RenderPage( entries, i ); + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int buttonID = info.ButtonID; + + if ( buttonID > 0 ) + { + --buttonID; + + if ( buttonID > 0 ) + { + --buttonID; + + if ( buttonID >= 0 && buttonID < m_Entries.Length ) + m_Barkeeper.EndChangeTitle( m_From, m_Entries[buttonID].m_Title, m_Entries[buttonID].m_Vendor ); + } + else + { + m_Barkeeper.CancelChangeTitle( m_From ); + } + } + } + } + + public class BarkeeperGump : Gump + { + private Mobile m_From; + private PlayerBarkeeper m_Barkeeper; + + public void RenderBackground() + { + AddPage( 0 ); + + AddBackground( 30, 40, 585, 410, 5054 ); + + AddImage( 30, 40, 9251 ); + AddImage( 180, 40, 9251 ); + AddImage( 30, 40, 9253 ); + AddImage( 30, 130, 9253 ); + AddImage( 598, 40, 9255 ); + AddImage( 598, 130, 9255 ); + AddImage( 30, 433, 9257 ); + AddImage( 180, 433, 9257 ); + AddImage( 30, 40, 9250 ); + AddImage( 598, 40, 9252 ); + AddImage( 598, 433, 9258 ); + AddImage( 30, 433, 9256 ); + + AddItem( 30, 40, 6816 ); + AddItem( 30, 125, 6817 ); + AddItem( 30, 233, 6817 ); + AddItem( 30, 341, 6817 ); + AddItem( 580, 40, 6814 ); + AddItem( 588, 125, 6815 ); + AddItem( 588, 233, 6815 ); + AddItem( 588, 341, 6815 ); + + AddBackground( 183, 25, 280, 30, 5054 ); + + AddImage( 180, 25, 10460 ); + AddImage( 434, 25, 10460 ); + AddImage( 560, 20, 1417 ); + + AddHtml( 223, 32, 200, 40, "BARKEEP CUSTOMIZATION MENU", false, false ); + AddBackground( 243, 433, 150, 30, 5054 ); + + AddImage( 240, 433, 10460 ); + AddImage( 375, 433, 10460 ); + } + + public void RenderCategories() + { + AddPage( 1 ); + + AddButton( 130, 120, 4005, 4007, 0, GumpButtonType.Page, 2 ); + AddHtml( 170, 120, 200, 40, "Message Control", false, false ); + + AddButton( 130, 200, 4005, 4007, 0, GumpButtonType.Page, 8 ); + AddHtml( 170, 200, 200, 40, "Customize your barkeep", false, false ); + + AddButton( 130, 280, 4005, 4007, 0, GumpButtonType.Page, 3 ); + AddHtml( 170, 280, 200, 40, "Dismiss your barkeep", false, false ); + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Reply, 0 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 574, 43, 5360 ); + } + + public void RenderMessageManagement() + { + AddPage( 2 ); + + AddButton( 130, 120, 4005, 4007, 0, GumpButtonType.Page, 4 ); + AddHtml( 170, 120, 380, 20, "Add or change a message and keyword", false, false ); + + AddButton( 130, 200, 4005, 4007, 0, GumpButtonType.Page, 5 ); + AddHtml( 170, 200, 380, 20, "Remove a message and keyword from your barkeep", false, false ); + + AddButton( 130, 280, 4005, 4007, 0, GumpButtonType.Page, 6 ); + AddHtml( 170, 280, 380, 20, "Add or change your barkeeper's tip message", false, false ); + + AddButton( 130, 360, 4005, 4007, 0, GumpButtonType.Page, 7 ); + AddHtml( 170, 360, 380, 20, "Delete your barkeepers tip message", false, false ); + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 1 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 580, 46, 4030 ); + } + + public void RenderDismissConfirmation() + { + AddPage( 3 ); + + AddHtml( 170, 160, 380, 20, "Are you sure you want to dismiss your barkeeper?", false, false ); + + AddButton( 205, 280, 4005, 4007, GetButtonID( 0, 0 ), GumpButtonType.Reply, 0 ); + AddHtml( 240, 280, 100, 20,@"Yes", false, false ); + + AddButton( 395, 280, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtml( 430, 280, 100, 20, "No", false, false ); + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 1 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 574, 43, 5360 ); + AddItem( 584, 34, 6579 ); + } + + public void RenderMessageManagement_Message_AddOrChange() + { + AddPage( 4 ); + + AddHtml( 250, 60, 500, 25, "Add or change a message", false, false ); + + BarkeeperRumor[] rumors = m_Barkeeper.Rumors; + + for ( int i = 0; i < rumors.Length; ++i ) + { + BarkeeperRumor rumor = rumors[i]; + + AddHtml( 100, 70 + (i * 120), 50, 20, "Message", false, false ); + AddHtml( 100, 90 + (i * 120), 450, 40, rumor == null ? "No current message" : rumor.Message, true, false ); + AddHtml( 100, 130 + (i * 120), 50, 20, "Keyword", false, false ); + AddHtml( 100, 150 + (i * 120), 450, 40, rumor == null ? "None" : rumor.Keyword, true, false ); + + AddButton( 60, 90 + (i * 120), 4005, 4007, GetButtonID( 1, i ), GumpButtonType.Reply, 0 ); + } + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 2 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 580, 46, 4030 ); + } + + public void RenderMessageManagement_Message_Remove() + { + AddPage( 5 ); + + AddHtml( 190, 60, 500, 25, "Choose the message you would like to remove", false, false ); + + BarkeeperRumor[] rumors = m_Barkeeper.Rumors; + + for ( int i = 0; i < rumors.Length; ++i ) + { + BarkeeperRumor rumor = rumors[i]; + + AddHtml( 100, 70 + (i * 120), 50, 20, "Message", false, false ); + AddHtml( 100, 90 + (i * 120), 450, 40, rumor == null ? "No current message" : rumor.Message, true, false ); + AddHtml( 100, 130 + (i * 120), 50, 20, "Keyword", false, false ); + AddHtml( 100, 150 + (i * 120), 450, 40, rumor == null ? "None" : rumor.Keyword, true, false ); + + AddButton( 60, 90 + (i * 120), 4005, 4007, GetButtonID( 2, i ), GumpButtonType.Reply, 0 ); + } + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 2 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 580, 46, 4030 ); + } + + private int GetButtonID( int type, int index ) + { + return 1 + (index * 6) + type; + } + + private void RenderMessageManagement_Tip_AddOrChange() + { + AddPage( 6 ); + + AddHtml( 250, 95, 500, 20, "Change this tip message", false, false ); + AddHtml( 100, 190, 50, 20, "Message", false, false ); + AddHtml( 100, 210, 450, 40, m_Barkeeper.TipMessage == null ? "No current message" : m_Barkeeper.TipMessage, true, false ); + + AddButton( 60, 210, 4005, 4007, GetButtonID( 3, 0 ), GumpButtonType.Reply, 0 ); + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 2 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 580, 46, 4030 ); + } + + private void RenderMessageManagement_Tip_Remove() + { + AddPage( 7 ); + + AddHtml( 250, 95, 500, 20, "Remove this tip message", false, false ); + AddHtml( 100, 190, 50, 20, "Message", false, false ); + AddHtml( 100, 210, 450, 40, m_Barkeeper.TipMessage == null ? "No current message" : m_Barkeeper.TipMessage, true, false ); + + AddButton( 60, 210, 4005, 4007, GetButtonID( 4, 0 ), GumpButtonType.Reply, 0 ); + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 2 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 580, 46, 4030 ); + } + + private void RenderAppearanceCategories() + { + AddPage( 8 ); + + AddButton( 130, 120, 4005, 4007, GetButtonID( 5, 0 ), GumpButtonType.Reply, 0 ); + AddHtml( 170, 120, 120, 20, "Title", false, false ); + + AddButton( 130, 200, 4005, 4007, GetButtonID( 5, 1 ), GumpButtonType.Reply, 0 ); + AddHtml( 170, 200, 120, 20, "Appearance", false, false ); + + AddButton( 130, 280, 4005, 4007, GetButtonID( 5, 2 ), GumpButtonType.Reply, 0 ); + AddHtml( 170, 280, 120, 20, "Male / Female", false, false ); + + AddButton( 338, 437, 4014, 4016, 0, GumpButtonType.Page, 1 ); + AddHtml( 290, 440, 35, 40, "Back", false, false ); + + AddItem( 580, 44, 4033 ); + } + + public BarkeeperGump( Mobile from, PlayerBarkeeper barkeeper ) : base( 0, 0 ) + { + m_From = from; + m_Barkeeper = barkeeper; + + from.CloseGump( typeof( BarkeeperGump ) ); + from.CloseGump( typeof( BarkeeperTitleGump ) ); + + RenderBackground(); + RenderCategories(); + RenderMessageManagement(); + RenderDismissConfirmation(); + RenderMessageManagement_Message_AddOrChange(); + RenderMessageManagement_Message_Remove(); + RenderMessageManagement_Tip_AddOrChange(); + RenderMessageManagement_Tip_Remove(); + RenderAppearanceCategories(); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( !m_Barkeeper.IsOwner( m_From ) ) + return; + + int index = info.ButtonID - 1; + + if ( index < 0 ) + return; + + int type = index % 6; + index /= 6; + + switch ( type ) + { + case 0: // Controls + { + switch ( index ) + { + case 0: // Dismiss + { + m_Barkeeper.Dismiss(); + break; + } + } + + break; + } + case 1: // Change message + { + m_Barkeeper.BeginChangeRumor( m_From, index ); + break; + } + case 2: // Remove message + { + m_Barkeeper.RemoveRumor( m_From, index ); + break; + } + case 3: // Change tip + { + m_Barkeeper.BeginChangeTip( m_From ); + break; + } + case 4: // Remove tip + { + m_Barkeeper.RemoveTip( m_From ); + break; + } + case 5: // Appearance category selection + { + switch ( index ) + { + case 0: m_Barkeeper.BeginChangeTitle( m_From ); break; + case 1: m_Barkeeper.BeginChangeAppearance( m_From ); break; + case 2: m_Barkeeper.ChangeGender( m_From ); break; + } + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/PlayerVendor.cs b/Scripts/Mobiles/Vendors/PlayerVendor.cs new file mode 100644 index 000000000..8794c8235 --- /dev/null +++ b/Scripts/Mobiles/Vendors/PlayerVendor.cs @@ -0,0 +1,1730 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Gumps; +using Server.Prompts; +using Server.Targeting; +using Server.Misc; +using Server.Multis; +using Server.ContextMenus; + +namespace Server.Mobiles +{ + [AttributeUsage( AttributeTargets.Class )] + public class PlayerVendorTargetAttribute : Attribute + { + public PlayerVendorTargetAttribute() + { + } + } + + public class VendorItem + { + private Item m_Item; + private int m_Price; + private string m_Description; + private DateTime m_Created; + + private bool m_Valid; + + public Item Item{ get{ return m_Item; } } + public int Price{ get{ return m_Price; } } + + public string Description + { + get{ return m_Description; } + set + { + if ( value != null ) + m_Description = value; + else + m_Description = ""; + + if ( Valid ) + Item.InvalidateProperties(); + } + } + + public DateTime Created{ get{ return m_Created; } } + + public bool IsForSale{ get{ return Price >= 0; } } + public bool IsForFree{ get{ return Price == 0; } } + + public bool Valid{ get{ return m_Valid; } } + + public VendorItem( Item item, int price, string description, DateTime created ) + { + m_Item = item; + m_Price = price; + + if ( description != null ) + m_Description = description; + else + m_Description = ""; + + m_Created = created; + + m_Valid = true; + } + + public void Invalidate() + { + m_Valid = false; + } + } + + public class VendorBackpack : Backpack + { + public VendorBackpack() + { + Layer = Layer.Backpack; + Weight = 1.0; + } + + public override int DefaultMaxWeight{ get{ return 0; } } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + if ( !base.CheckHold( m, item, message, checkItems, plusItems, plusWeight ) ) + return false; + + if ( Ethics.Ethic.IsImbued( item, true ) ) + { + if ( message ) + m.SendMessage( "Imbued items may not be sold here." ); + + return false; + } + + if ( !BaseHouse.NewVendorSystem && Parent is PlayerVendor ) + { + BaseHouse house = ((PlayerVendor)Parent).House; + + if ( house != null && house.IsAosRules && !house.CheckAosStorage( 1 + item.TotalItems + plusItems ) ) + { + if ( message ) + m.SendLocalizedMessage( 1061839 ); // This action would exceed the secure storage limit of the house. + + return false; + } + } + + return true; + } + + public override bool IsAccessibleTo( Mobile m ) + { + return true; + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + if ( !base.CheckItemUse( from, item ) ) + return false; + + if ( item is Container || item is Engines.BulkOrders.BulkOrderBook ) + return true; + + from.SendLocalizedMessage( 500447 ); // That is not accessible. + return false; + } + + public override bool CheckTarget( Mobile from, Target targ, object targeted ) + { + if ( !base.CheckTarget( from, targ, targeted ) ) + return false; + + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + + return targ.GetType().IsDefined( typeof( PlayerVendorTargetAttribute ), false ); + } + + public override void GetChildContextMenuEntries( Mobile from, List list, Item item ) + { + base.GetChildContextMenuEntries( from, list, item ); + + PlayerVendor pv = RootParent as PlayerVendor; + + if ( pv == null || pv.IsOwner( from ) ) + return; + + VendorItem vi = pv.GetVendorItem( item ); + + if ( vi != null ) + list.Add( new BuyEntry( item ) ); + } + + private class BuyEntry : ContextMenuEntry + { + private Item m_Item; + + public BuyEntry( Item item ) : base( 6103 ) + { + m_Item = item; + } + + public override bool NonLocalUse{ get{ return true; } } + + public override void OnClick() + { + if ( m_Item.Deleted ) + return; + + PlayerVendor.TryToBuy( m_Item, Owner.From ); + } + } + + public override void GetChildNameProperties( ObjectPropertyList list, Item item ) + { + base.GetChildNameProperties( list, item ); + + PlayerVendor pv = RootParent as PlayerVendor; + + if ( pv == null ) + return; + + VendorItem vi = pv.GetVendorItem( item ); + + if ( vi == null ) + return; + + if ( !vi.IsForSale ) + list.Add( 1043307 ); // Price: Not for sale. + else if ( vi.IsForFree ) + list.Add( 1043306 ); // Price: FREE! + else + list.Add( 1043304, vi.Price.ToString() ); // Price: ~1_COST~ + } + + public override void GetChildProperties( ObjectPropertyList list, Item item ) + { + base.GetChildProperties( list, item ); + + PlayerVendor pv = RootParent as PlayerVendor; + + if ( pv == null ) + return; + + VendorItem vi = pv.GetVendorItem( item ); + + if ( vi != null && vi.Description != null && vi.Description.Length > 0 ) + list.Add( 1043305, vi.Description ); //
Seller's Description:
"~1_DESC~" + } + + public override void OnSingleClickContained( Mobile from, Item item ) + { + if ( RootParent is PlayerVendor ) + { + PlayerVendor vendor = (PlayerVendor)RootParent; + + VendorItem vi = vendor.GetVendorItem( item ); + + if ( vi != null ) + { + if ( !vi.IsForSale ) + item.LabelTo( from, 1043307 ); // Price: Not for sale. + else if ( vi.IsForFree ) + item.LabelTo( from, 1043306 ); // Price: FREE! + else + item.LabelTo( from, 1043304, vi.Price.ToString() ); // Price: ~1_COST~ + + if ( vi.Description != null && vi.Description != "" ) + { + // The localized message (1043305) is no longer valid -
Seller's Description:
"~1_DESC~" + item.LabelTo( from, "Description: {0}", vi.Description ); + } + } + } + + base.OnSingleClickContained( from, item ); + } + + public VendorBackpack( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class PlayerVendor : Mobile + { + private Hashtable m_SellItems; + + private Mobile m_Owner; + private BaseHouse m_House; + + private int m_BankAccount; + private int m_HoldGold; + + private string m_ShopName; + + private Timer m_PayTimer; + private DateTime m_NextPayTime; + + private PlayerVendorPlaceholder m_Placeholder; + + public PlayerVendor( Mobile owner, BaseHouse house ) + { + Owner = owner; + House = house; + + if ( BaseHouse.NewVendorSystem ) + { + m_BankAccount = 0; + m_HoldGold = 4; + } + else + { + m_BankAccount = 1000; + m_HoldGold = 0; + } + + ShopName = "Shop Not Yet Named"; + + m_SellItems = new Hashtable(); + + CantWalk = true; + + if ( !Core.AOS ) + NameHue = 0x35; + + InitStats( 75, 75, 75 ); + InitBody(); + InitOutfit(); + + TimeSpan delay = PayTimer.GetInterval(); + + m_PayTimer = new PayTimer( this, delay ); + m_PayTimer.Start(); + + m_NextPayTime = DateTime.Now + delay; + } + + public PlayerVendor( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (bool) BaseHouse.NewVendorSystem ); + writer.Write( (string) m_ShopName ); + writer.WriteDeltaTime( (DateTime) m_NextPayTime ); + writer.Write( (Item) House ); + + writer.Write( (Mobile) m_Owner ); + writer.Write( (int) m_BankAccount ); + writer.Write( (int) m_HoldGold ); + + writer.Write( (int) m_SellItems.Count ); + foreach ( VendorItem vi in m_SellItems.Values ) + { + writer.Write( (Item) vi.Item ); + writer.Write( (int) vi.Price ); + writer.Write( (string) vi.Description ); + + writer.Write( (DateTime) vi.Created ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + bool newVendorSystem = false; + + switch ( version ) + { + case 1: + { + newVendorSystem = reader.ReadBool(); + m_ShopName = reader.ReadString(); + m_NextPayTime = reader.ReadDeltaTime(); + House = (BaseHouse) reader.ReadItem(); + + goto case 0; + } + case 0: + { + m_Owner = reader.ReadMobile(); + m_BankAccount = reader.ReadInt(); + m_HoldGold = reader.ReadInt(); + + m_SellItems = new Hashtable(); + + int count = reader.ReadInt(); + for ( int i = 0; i < count; i++ ) + { + Item item = reader.ReadItem(); + + int price = reader.ReadInt(); + if ( price > 100000000 ) + price = 100000000; + + string description = reader.ReadString(); + + DateTime created = version < 1 ? DateTime.Now : reader.ReadDateTime(); + + if ( item != null ) + { + SetVendorItem( item, version < 1 && price <= 0 ? -1 : price, description, created ); + } + } + + break; + } + } + + bool newVendorSystemActivated = BaseHouse.NewVendorSystem && !newVendorSystem; + + if ( version < 1 || newVendorSystemActivated ) + { + if ( version < 1 ) + { + m_ShopName = "Shop Not Yet Named"; + Timer.DelayCall( TimeSpan.Zero, new TimerStateCallback( UpgradeFromVersion0 ), newVendorSystemActivated ); + } + else + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( FixDresswear ) ); + } + + m_NextPayTime = DateTime.Now + PayTimer.GetInterval(); + + if ( newVendorSystemActivated ) + { + m_HoldGold += m_BankAccount; + m_BankAccount = 0; + } + } + + TimeSpan delay = m_NextPayTime - DateTime.Now; + + m_PayTimer = new PayTimer( this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero ); + m_PayTimer.Start(); + + Blessed = false; + + if ( Core.AOS && NameHue == 0x35 ) + NameHue = -1; + } + + private void UpgradeFromVersion0( object newVendorSystem ) + { + ArrayList toRemove = new ArrayList(); + + foreach ( VendorItem vi in m_SellItems.Values ) + { + if ( !CanBeVendorItem( vi.Item ) ) + toRemove.Add( vi.Item ); + else + vi.Description = Utility.FixHtml( vi.Description ); + } + + foreach ( Item item in toRemove ) + { + RemoveVendorItem( item ); + } + + House = BaseHouse.FindHouseAt( this ); + + if ( (bool) newVendorSystem ) + ActivateNewVendorSystem(); + } + + private void ActivateNewVendorSystem() + { + FixDresswear(); + + if ( House != null && !House.IsOwner( Owner ) ) + Destroy( false ); + } + + public void InitBody() + { + Hue = Utility.RandomSkinHue(); + SpeechHue = 0x3B2; + + if ( !Core.AOS ) + NameHue = 0x35; + + if ( this.Female = Utility.RandomBool() ) + { + this.Body = 0x191; + this.Name = NameList.RandomName( "female" ); + } + else + { + this.Body = 0x190; + this.Name = NameList.RandomName( "male" ); + } + } + + public virtual void InitOutfit() + { + Item item = new FancyShirt( Utility.RandomNeutralHue() ); + item.Layer = Layer.InnerTorso; + AddItem( item ); + AddItem( new LongPants( Utility.RandomNeutralHue() ) ); + AddItem( new BodySash( Utility.RandomNeutralHue() ) ); + AddItem( new Boots( Utility.RandomNeutralHue() ) ); + AddItem( new Cloak( Utility.RandomNeutralHue() ) ); + + Utility.AssignRandomHair( this ); + + Container pack = new VendorBackpack(); + pack.Movable = false; + AddItem( pack ); + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int BankAccount + { + get{ return m_BankAccount; } + set{ m_BankAccount = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int HoldGold + { + get{ return m_HoldGold; } + set{ m_HoldGold = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string ShopName + { + get{ return m_ShopName; } + set + { + if ( value == null ) + m_ShopName = ""; + else + m_ShopName = value; + + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime NextPayTime + { + get{ return m_NextPayTime; } + } + + public PlayerVendorPlaceholder Placeholder + { + get{ return m_Placeholder; } + set{ m_Placeholder = value; } + } + + public BaseHouse House + { + get{ return m_House; } + set + { + if ( m_House != null ) + m_House.PlayerVendors.Remove( this ); + + if ( value != null ) + value.PlayerVendors.Add( this ); + + m_House = value; + } + } + + public int ChargePerDay + { + get + { + if ( BaseHouse.NewVendorSystem ) + { + return ChargePerRealWorldDay / 12; + } + else + { + long total = 0; + foreach ( VendorItem vi in m_SellItems.Values ) + { + total += vi.Price; + } + + total -= 500; + + if ( total < 0 ) + total = 0; + + return (int)( 20 + (total / 500) ); + } + } + } + + public int ChargePerRealWorldDay + { + get + { + if ( BaseHouse.NewVendorSystem ) + { + long total = 0; + foreach ( VendorItem vi in m_SellItems.Values ) + { + total += vi.Price; + } + + return (int)( 60 + (total / 500) * 3 ); + } + else + { + return ChargePerDay * 12; + } + } + } + + public virtual bool IsOwner( Mobile m ) + { + if ( m.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if ( BaseHouse.NewVendorSystem && House != null ) + { + return House.IsOwner( m ); + } + else + { + return m == Owner; + } + } + + protected ArrayList GetItems() + { + ArrayList list = new ArrayList(); + + foreach ( Item item in this.Items ) + { + if ( item.Movable && item != this.Backpack && item.Layer != Layer.Hair && item.Layer != Layer.FacialHair ) + list.Add( item ); + } + + if ( this.Backpack != null ) + { + list.AddRange( this.Backpack.Items ); + } + + return list; + } + + public virtual void Destroy( bool toBackpack ) + { + Return(); + + if ( !BaseHouse.NewVendorSystem ) + FixDresswear(); + + /* Possible cases regarding item return: + * + * 1. No item must be returned + * -> do nothing. + * 2. ( toBackpack is false OR the vendor is in the internal map ) AND the vendor is associated with a AOS house + * -> put the items into the moving crate or a vendor inventory, + * depending on whether the vendor owner is also the house owner. + * 3. ( toBackpack is true OR the vendor isn't associated with any AOS house ) AND the vendor isn't in the internal map + * -> put the items into a backpack. + * 4. The vendor isn't associated with any house AND it's in the internal map + * -> do nothing (we can't do anything). + */ + + ArrayList list = GetItems(); + + if ( list.Count > 0 || HoldGold > 0 ) // No case 1 + { + if ( ( !toBackpack || this.Map == Map.Internal ) && House != null && House.IsAosRules ) // Case 2 + { + if ( House.IsOwner( Owner ) ) // Move to moving crate + { + if ( House.MovingCrate == null ) + House.MovingCrate = new MovingCrate( House ); + + if ( HoldGold > 0 ) + Banker.Deposit( House.MovingCrate, HoldGold ); + + foreach ( Item item in list ) + { + House.MovingCrate.DropItem( item ); + } + } + else // Move to vendor inventory + { + VendorInventory inventory = new VendorInventory( House, Owner, Name, ShopName ); + inventory.Gold = HoldGold; + + foreach ( Item item in list ) + { + inventory.AddItem( item ); + } + + House.VendorInventories.Add( inventory ); + } + } + else if ( ( toBackpack || House == null || !House.IsAosRules ) && this.Map != Map.Internal ) // Case 3 - Move to backpack + { + Container backpack = new Backpack(); + + if ( HoldGold > 0 ) + Banker.Deposit( backpack, HoldGold ); + + foreach ( Item item in list ) + { + backpack.DropItem( item ); + } + + backpack.MoveToWorld( this.Location, this.Map ); + } + } + + Delete(); + } + + private void FixDresswear() + { + for ( int i = 0; i < Items.Count; ++i ) + { + Item item = Items[i] as Item; + + if ( item is BaseHat ) + item.Layer = Layer.Helm; + else if ( item is BaseMiddleTorso ) + item.Layer = Layer.MiddleTorso; + else if ( item is BaseOuterLegs ) + item.Layer = Layer.OuterLegs; + else if ( item is BaseOuterTorso ) + item.Layer = Layer.OuterTorso; + else if ( item is BasePants ) + item.Layer = Layer.Pants; + else if ( item is BaseShirt ) + item.Layer = Layer.Shirt; + else if ( item is BaseWaist ) + item.Layer = Layer.Waist; + else if ( item is BaseShoes ) + { + if ( item is Sandals ) + item.Hue = 0; + + item.Layer = Layer.Shoes; + } + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + m_PayTimer.Stop(); + + House = null; + + if ( Placeholder != null ) + Placeholder.Delete(); + } + + public override bool IsSnoop( Mobile from ) + { + return false; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( BaseHouse.NewVendorSystem ) + { + list.Add( 1062449, ShopName ); // Shop Name: ~1_NAME~ + } + } + + public VendorItem GetVendorItem( Item item ) + { + return (VendorItem) m_SellItems[item]; + } + + private VendorItem SetVendorItem( Item item, int price, string description ) + { + return SetVendorItem( item, price, description, DateTime.Now ); + } + + private VendorItem SetVendorItem( Item item, int price, string description, DateTime created ) + { + RemoveVendorItem( item ); + + VendorItem vi = new VendorItem( item, price, description, created ); + m_SellItems[item] = vi; + + item.InvalidateProperties(); + + return vi; + } + + private void RemoveVendorItem( Item item ) + { + VendorItem vi = GetVendorItem( item ); + + if ( vi != null ) + { + vi.Invalidate(); + m_SellItems.Remove( item ); + + foreach ( Item subItem in item.Items ) + { + RemoveVendorItem( subItem ); + } + + item.InvalidateProperties(); + } + } + + private bool CanBeVendorItem( Item item ) + { + Item parent = item.Parent as Item; + + if ( parent == this.Backpack ) + return true; + + if ( parent is Container ) + { + VendorItem parentVI = GetVendorItem( parent ); + + if ( parentVI != null ) + return !parentVI.IsForSale; + } + + return false; + } + + public override void OnSubItemAdded( Item item ) + { + base.OnSubItemAdded( item ); + + if ( GetVendorItem( item ) == null && CanBeVendorItem( item ) ) + { + // TODO: default price should be dependent to the type of object + SetVendorItem( item, 999, "" ); + } + } + + public override void OnSubItemRemoved( Item item ) + { + base.OnSubItemRemoved( item ); + + if ( item.GetBounce() == null ) + RemoveVendorItem( item ); + } + + public override void OnSubItemBounceCleared( Item item ) + { + base.OnSubItemBounceCleared( item ); + + if ( !CanBeVendorItem( item ) ) + RemoveVendorItem( item ); + } + + public override void OnItemRemoved( Item item ) + { + base.OnItemRemoved( item ); + + if ( item == this.Backpack ) + { + foreach ( Item subItem in item.Items ) + { + RemoveVendorItem( subItem ); + } + } + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( !IsOwner( from ) ) + { + SayTo( from, 503209 ); // I can only take item from the shop owner. + return false; + } + + if ( item is Gold ) + { + if ( BaseHouse.NewVendorSystem ) + { + if ( this.HoldGold < 1000000 ) + { + SayTo( from, 503210 ); // I'll take that to fund my services. + + this.HoldGold += item.Amount; + item.Delete(); + + return true; + } + else + { + from.SendLocalizedMessage( 1062493 ); // Your vendor has sufficient funds for operation and cannot accept this gold. + + return false; + } + } + else + { + if ( this.BankAccount < 1000000 ) + { + SayTo( from, 503210 ); // I'll take that to fund my services. + + this.BankAccount += item.Amount; + item.Delete(); + + return true; + } + else + { + from.SendLocalizedMessage( 1062493 ); // Your vendor has sufficient funds for operation and cannot accept this gold. + + return false; + } + } + } + else + { + bool newItem = ( GetVendorItem( item ) == null ); + + if ( this.Backpack != null && this.Backpack.TryDropItem( from, item, false ) ) + { + if ( newItem ) + OnItemGiven( from, item ); + + return true; + } + else + { + SayTo( from, 503211 ); // I can't carry any more. + return false; + } + } + } + + public override bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + if ( IsOwner( from ) ) + { + if ( GetVendorItem( item ) == null ) + { + // We must wait until the item is added + Timer.DelayCall( TimeSpan.Zero, new TimerStateCallback( NonLocalDropCallback ), new object[] { from, item } ); + } + + return true; + } + else + { + SayTo( from, 503209 ); // I can only take item from the shop owner. + return false; + } + } + + private void NonLocalDropCallback( object state ) + { + object[] aState = (object[]) state; + + Mobile from = (Mobile) aState[0]; + Item item = (Item) aState[1]; + + OnItemGiven( from, item ); + } + + private void OnItemGiven( Mobile from, Item item ) + { + VendorItem vi = GetVendorItem( item ); + + if ( vi != null ) + { + string name; + if ( item.Name != null && item.Name != "" ) + name = item.Name; + else + name = "#" + item.LabelNumber.ToString(); + + from.SendLocalizedMessage( 1043303, name ); // Type in a price and description for ~1_ITEM~ (ESC=not for sale) + from.Prompt = new VendorPricePrompt( this, vi ); + } + } + + public override bool AllowEquipFrom( Mobile from ) + { + if ( BaseHouse.NewVendorSystem && IsOwner( from ) ) + return true; + + return base.AllowEquipFrom( from ); + } + + public override bool CheckNonlocalLift( Mobile from, Item item ) + { + if ( item.IsChildOf( this.Backpack ) ) + { + if ( IsOwner( from ) ) + { + return true; + } + else + { + SayTo( from, 503223 ); // If you'd like to purchase an item, just ask. + return false; + } + } + else if ( BaseHouse.NewVendorSystem && IsOwner( from ) ) + { + return true; + } + + return base.CheckNonlocalLift( from, item ); + } + + public bool CanInteractWith( Mobile from, bool ownerOnly ) + { + if ( !from.CanSee( this ) || !Utility.InUpdateRange( from, this ) || !from.CheckAlive() ) + return false; + + if ( ownerOnly ) + return IsOwner( from ); + + if ( House != null && House.IsBanned( from ) && !IsOwner( from ) ) + { + from.SendLocalizedMessage( 1062674 ); // You can't shop from this home as you have been banned from this establishment. + return false; + } + + return true; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsOwner( from ) ) + { + SendOwnerGump( from ); + } + else if ( CanInteractWith( from, false ) ) + { + OpenBackpack( from ); + } + } + + public override void DisplayPaperdollTo( Mobile m ) + { + if ( BaseHouse.NewVendorSystem ) + { + base.DisplayPaperdollTo( m ); + } + else if ( CanInteractWith( m, false ) ) + { + OpenBackpack( m ); + } + } + + public void SendOwnerGump( Mobile to ) + { + if ( BaseHouse.NewVendorSystem ) + { + to.CloseGump( typeof( NewPlayerVendorOwnerGump ) ); + to.CloseGump( typeof( NewPlayerVendorCustomizeGump ) ); + + to.SendGump( new NewPlayerVendorOwnerGump( this ) ); + } + else + { + to.CloseGump( typeof( PlayerVendorOwnerGump ) ); + to.CloseGump( typeof( PlayerVendorCustomizeGump ) ); + + to.SendGump( new PlayerVendorOwnerGump( this ) ); + } + } + + public void OpenBackpack( Mobile from ) + { + if ( this.Backpack != null ) + { + SayTo( from, IsOwner( from ) ? 1010642 : 503208 ); // Take a look at my/your goods. + + this.Backpack.DisplayTo( from ); + } + } + + public static void TryToBuy( Item item, Mobile from ) + { + PlayerVendor vendor = item.RootParent as PlayerVendor; + + if ( vendor == null || !vendor.CanInteractWith( from, false ) ) + return; + + if ( vendor.IsOwner( from ) ) + { + vendor.SayTo( from, 503212 ); // You own this shop, just take what you want. + return; + } + + VendorItem vi = vendor.GetVendorItem( item ); + + if ( vi == null ) + { + vendor.SayTo( from, 503216 ); // You can't buy that. + } + else if ( !vi.IsForSale ) + { + vendor.SayTo( from, 503202 ); // This item is not for sale. + } + else if ( vi.Created + TimeSpan.FromMinutes( 1.0 ) > DateTime.Now ) + { + from.SendMessage( "You cannot buy this item right now. Please wait one minute and try again." ); + } + else + { + from.CloseGump( typeof( PlayerVendorBuyGump ) ); + from.SendGump( new PlayerVendorBuyGump( vendor, vi ) ); + } + } + + public void CollectGold( Mobile to ) + { + if ( HoldGold > 0 ) + { + SayTo( to, "How much of the {0} that I'm holding would you like?", HoldGold.ToString() ); + to.SendMessage( "Enter the amount of gold you wish to withdraw (ESC = CANCEL):" ); + + to.Prompt = new CollectGoldPrompt( this ); + } + else + { + SayTo( to, 503215 ); // I am holding no gold for you. + } + } + + public int GiveGold( Mobile to, int amount ) + { + if ( amount <= 0 ) + return 0; + + if ( amount > HoldGold ) + { + SayTo( to, "I'm sorry, but I'm only holding {0} gold for you.", HoldGold.ToString() ); + return 0; + } + + int amountGiven = Banker.DepositUpTo( to, amount ); + HoldGold -= amountGiven; + + if ( amountGiven > 0 ) + { + to.SendLocalizedMessage( 1060397, amountGiven.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + } + + if ( amountGiven == 0 ) + { + SayTo( to, 1070755 ); // Your bank box cannot hold the gold you are requesting. I will keep the gold until you can take it. + } + else if ( amount > amountGiven ) + { + SayTo( to, 1070756 ); // I can only give you part of the gold now, as your bank box is too full to hold the full amount. + } + else if ( HoldGold > 0 ) + { + SayTo( to, 1042639 ); // Your gold has been transferred. + } + else + { + SayTo( to, 503234 ); // All the gold I have been carrying for you has been deposited into your bank account. + } + + return amountGiven; + } + + public void Dismiss( Mobile from ) + { + Container pack = this.Backpack; + + if ( pack != null && pack.Items.Count > 0 ) + { + SayTo( from, 1038325 ); // You cannot dismiss me while I am holding your goods. + return; + } + + if ( HoldGold > 0 ) + { + GiveGold( from, HoldGold ); + + if ( HoldGold > 0 ) + return; + } + + Destroy( true ); + } + + public void Rename( Mobile from ) + { + from.SendLocalizedMessage( 1062494 ); // Enter a new name for your vendor (20 characters max): + + from.Prompt = new VendorNamePrompt( this ); + } + + public void RenameShop( Mobile from ) + { + from.SendLocalizedMessage( 1062433 ); // Enter a new name for your shop (20 chars max): + + from.Prompt = new ShopNamePrompt( this ); + } + + public bool CheckTeleport( Mobile to ) + { + if ( Deleted || !IsOwner( to ) || House == null || this.Map == Map.Internal ) + return false; + + if ( House.IsInside( to ) || to.Map != House.Map || !House.InRange( to, 5 ) ) + return false; + + if ( Placeholder == null ) + { + Placeholder = new PlayerVendorPlaceholder( this ); + Placeholder.MoveToWorld( this.Location, this.Map ); + + this.MoveToWorld( to.Location, to.Map ); + + to.SendLocalizedMessage( 1062431 ); // This vendor has been moved out of the house to your current location temporarily. The vendor will return home automatically after two minutes have passed once you are done managing its inventory or customizing it. + } + else + { + Placeholder.RestartTimer(); + + to.SendLocalizedMessage( 1062430 ); // This vendor is currently temporarily in a location outside its house. The vendor will return home automatically after two minutes have passed once you are done managing its inventory or customizing it. + } + + return true; + } + + public void Return() + { + if ( Placeholder != null ) + Placeholder.Delete(); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + if ( from.Alive && Placeholder != null && IsOwner( from ) ) + { + list.Add( new ReturnVendorEntry( this ) ); + } + + base.GetContextMenuEntries( from, list ); + } + + private class ReturnVendorEntry : ContextMenuEntry + { + private PlayerVendor m_Vendor; + + public ReturnVendorEntry( PlayerVendor vendor ) : base( 6214 ) + { + m_Vendor = vendor; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( !m_Vendor.Deleted && m_Vendor.IsOwner( from ) && from.CheckAlive() ) + m_Vendor.Return(); + } + } + + public override bool HandlesOnSpeech( Mobile from ) + { + return ( from.Alive && from.GetDistanceToSqrt( this ) <= 3 ); + } + + public bool WasNamed( string speech ) + { + return this.Name != null && Insensitive.StartsWith( speech, this.Name ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + Mobile from = e.Mobile; + + if ( e.Handled || !from.Alive || from.GetDistanceToSqrt( this ) > 3 ) + return; + + if ( e.HasKeyword( 0x3C ) || (e.HasKeyword( 0x171 ) && WasNamed( e.Speech )) ) // vendor buy, *buy* + { + if ( IsOwner( from ) ) + { + SayTo( from, 503212 ); // You own this shop, just take what you want. + } + else if ( House == null || !House.IsBanned( from ) ) + { + from.SendLocalizedMessage( 503213 ); // Select the item you wish to buy. + from.Target = new PVBuyTarget(); + + e.Handled = true; + } + } + else if ( e.HasKeyword( 0x3D ) || (e.HasKeyword( 0x172 ) && WasNamed( e.Speech )) ) // vendor browse, *browse + { + if ( House != null && House.IsBanned( from ) && !IsOwner( from ) ) + { + SayTo( from, 1062674 ); // You can't shop from this home as you have been banned from this establishment. + } + else + { + OpenBackpack( from ); + + e.Handled = true; + } + } + else if ( e.HasKeyword( 0x3E ) || (e.HasKeyword( 0x173 ) && WasNamed( e.Speech )) ) // vendor collect, *collect + { + if ( IsOwner( from ) ) + { + CollectGold( from ); + + e.Handled = true; + } + } + else if ( e.HasKeyword( 0x3F ) || (e.HasKeyword( 0x174 ) && WasNamed( e.Speech )) ) // vendor status, *status + { + if ( IsOwner( from ) ) + { + SendOwnerGump( from ); + + e.Handled = true; + } + else + { + SayTo( from, 503226 ); // What do you care? You don't run this shop. + } + } + else if ( e.HasKeyword( 0x40 ) || (e.HasKeyword( 0x175 ) && WasNamed( e.Speech )) ) // vendor dismiss, *dismiss + { + if ( IsOwner( from ) ) + { + Dismiss( from ); + + e.Handled = true; + } + } + else if ( e.HasKeyword( 0x41 ) || (e.HasKeyword( 0x176 ) && WasNamed( e.Speech )) ) // vendor cycle, *cycle + { + if ( IsOwner( from ) ) + { + this.Direction = this.GetDirectionTo( from ); + + e.Handled = true; + } + } + } + + private class PayTimer : Timer + { + public static TimeSpan GetInterval() + { + if ( BaseHouse.NewVendorSystem ) + return TimeSpan.FromDays( 1.0 ); + else + return TimeSpan.FromMinutes( Clock.MinutesPerUODay ); + } + + private PlayerVendor m_Vendor; + + public PayTimer( PlayerVendor vendor, TimeSpan delay ) : base( delay, GetInterval() ) + { + m_Vendor = vendor; + + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + m_Vendor.m_NextPayTime = DateTime.Now + this.Interval; + + int pay; + int totalGold; + if ( BaseHouse.NewVendorSystem ) + { + pay = m_Vendor.ChargePerRealWorldDay; + totalGold = m_Vendor.HoldGold; + } + else + { + pay = m_Vendor.ChargePerDay; + totalGold = m_Vendor.BankAccount + m_Vendor.HoldGold; + } + + if ( pay > totalGold ) + { + m_Vendor.Destroy( !BaseHouse.NewVendorSystem ); + } + else + { + if ( !BaseHouse.NewVendorSystem ) + { + if ( m_Vendor.BankAccount >= pay ) + { + m_Vendor.BankAccount -= pay; + pay = 0; + } + else + { + pay -= m_Vendor.BankAccount; + m_Vendor.BankAccount = 0; + } + } + + m_Vendor.HoldGold -= pay; + } + } + } + + [PlayerVendorTarget] + private class PVBuyTarget : Target + { + public PVBuyTarget() : base( 3, false, TargetFlags.None ) + { + AllowNonlocal = true; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Item ) + { + TryToBuy( (Item) targeted, from ); + } + } + } + + private class VendorPricePrompt : Prompt + { + private PlayerVendor m_Vendor; + private VendorItem m_VI; + + public VendorPricePrompt( PlayerVendor vendor, VendorItem vi ) + { + m_Vendor = vendor; + m_VI = vi; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_VI.Valid || !m_Vendor.CanInteractWith( from, true ) ) + return; + + string firstWord; + + int sep = text.IndexOfAny( new char[] { ' ', ',' } ); + if ( sep >= 0 ) + firstWord = text.Substring( 0, sep ); + else + firstWord = text; + + int price; + string description; + + try + { + price = Convert.ToInt32( firstWord ); + + if ( sep >= 0 ) + description = text.Substring( sep + 1 ).Trim(); + else + description = ""; + } + catch + { + price = -1; + description = text.Trim(); + } + + SetInfo( from, price, Utility.FixHtml( description ) ); + } + + public override void OnCancel( Mobile from ) + { + if ( !m_VI.Valid || !m_Vendor.CanInteractWith( from, true ) ) + return; + + SetInfo( from, -1, "" ); + } + + private void SetInfo( Mobile from, int price, string description ) + { + Item item = m_VI.Item; + + bool setPrice = false; + + if ( price < 0 ) // Not for sale + { + price = -1; + + if ( item is Container ) + { + if ( item is LockableContainer && ((LockableContainer)item).Locked ) + m_Vendor.SayTo( from, 1043298 ); // Locked items may not be made not-for-sale. + else if ( item.Items.Count > 0 ) + m_Vendor.SayTo( from, 1043299 ); // To be not for sale, all items in a container must be for sale. + else + setPrice = true; + } + else if ( item is BaseBook || item is Engines.BulkOrders.BulkOrderBook ) + { + setPrice = true; + } + else + { + m_Vendor.SayTo( from, 1043301 ); // Only the following may be made not-for-sale: books, containers, keyrings, and items in for-sale containers. + } + } + else + { + if ( price > 100000000 ) + { + price = 100000000; + from.SendMessage( "You cannot price items above 100,000,000 gold. The price has been adjusted." ); + } + + setPrice = true; + } + + if ( setPrice ) + { + m_Vendor.SetVendorItem( item, price, description ); + } + else + { + m_VI.Description = description; + } + } + } + + private class CollectGoldPrompt : Prompt + { + private PlayerVendor m_Vendor; + + public CollectGoldPrompt( PlayerVendor vendor ) + { + m_Vendor = vendor; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + text = text.Trim(); + + int amount; + try + { + amount = Convert.ToInt32( text ); + } + catch + { + amount = 0; + } + + GiveGold( from, amount ); + } + + public override void OnCancel( Mobile from ) + { + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + GiveGold( from, 0 ); + } + + private void GiveGold( Mobile to, int amount ) + { + if ( amount <= 0 ) + { + m_Vendor.SayTo( to, "Very well. I will hold on to the money for now then." ); + } + else + { + m_Vendor.GiveGold( to, amount ); + } + } + } + + private class VendorNamePrompt : Prompt + { + private PlayerVendor m_Vendor; + + public VendorNamePrompt( PlayerVendor vendor ) + { + m_Vendor = vendor; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + string name = text.Trim(); + + if ( !NameVerification.Validate( name, 1, 20, true, true, true, 0, NameVerification.Empty ) ) + { + m_Vendor.SayTo( from, "That name is unacceptable." ); + return; + } + + m_Vendor.Name = Utility.FixHtml( name ); + + from.SendLocalizedMessage( 1062496 ); // Your vendor has been renamed. + + from.SendGump( new NewPlayerVendorOwnerGump( m_Vendor ) ); + } + } + + private class ShopNamePrompt : Prompt + { + private PlayerVendor m_Vendor; + + public ShopNamePrompt( PlayerVendor vendor ) + { + m_Vendor = vendor; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_Vendor.CanInteractWith( from, true ) ) + return; + + string name = text.Trim(); + + if ( !NameVerification.Validate( name, 1, 20, true, true, true, 0, NameVerification.Empty ) ) + { + m_Vendor.SayTo( from, "That name is unacceptable." ); + return; + } + + m_Vendor.ShopName = Utility.FixHtml( name ); + + from.SendGump( new NewPlayerVendorOwnerGump( m_Vendor ) ); + } + } + + public override bool CanBeDamaged() + { + return false; + } + + } + + public class PlayerVendorPlaceholder : Item + { + private PlayerVendor m_Vendor; + private ExpireTimer m_Timer; + + [CommandProperty( AccessLevel.GameMaster )] + public PlayerVendor Vendor{ get{ return m_Vendor; } } + + public PlayerVendorPlaceholder( PlayerVendor vendor ) : base( 0x1F28 ) + { + Hue = 0x672; + Movable = false; + + m_Vendor = vendor; + + m_Timer = new ExpireTimer( this ); + m_Timer.Start(); + } + + public PlayerVendorPlaceholder( Serial serial ) : base( serial ) + { + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( m_Vendor != null ) + list.Add( 1062498, m_Vendor.Name ); // reserved for vendor ~1_NAME~ + } + + public void RestartTimer() + { + m_Timer.Stop(); + m_Timer.Start(); + } + + private class ExpireTimer : Timer + { + private PlayerVendorPlaceholder m_Placeholder; + + public ExpireTimer( PlayerVendorPlaceholder placeholder ) : base( TimeSpan.FromMinutes( 2.0 ) ) + { + m_Placeholder = placeholder; + + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Placeholder.Delete(); + } + } + + public override void OnDelete() + { + if ( m_Vendor != null && !m_Vendor.Deleted ) + { + m_Vendor.MoveToWorld( this.Location, this.Map ); + m_Vendor.Placeholder = null; + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( (int) 0 ); + + writer.Write( (Mobile) m_Vendor ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Vendor = (PlayerVendor) reader.ReadMobile(); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Delete ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/PresetMapBuy.cs b/Scripts/Mobiles/Vendors/PresetMapBuy.cs new file mode 100644 index 000000000..1222b05fb --- /dev/null +++ b/Scripts/Mobiles/Vendors/PresetMapBuy.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class PresetMapBuyInfo : GenericBuyInfo + { + private PresetMapEntry m_Entry; + + public override bool CanCacheDisplay{ get{ return false; } } + + public PresetMapBuyInfo( PresetMapEntry entry, int price, int amount ) : base( entry.Name.ToString(), null, price, amount, 0x14EC, 0 ) + { + m_Entry = entry; + } + + public override object GetObject() + { + return new PresetMap( m_Entry ); + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/RentedVendor.cs b/Scripts/Mobiles/Vendors/RentedVendor.cs new file mode 100644 index 000000000..0dc443787 --- /dev/null +++ b/Scripts/Mobiles/Vendors/RentedVendor.cs @@ -0,0 +1,431 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.ContextMenus; +using Server.Prompts; + +namespace Server.Mobiles +{ + public class VendorRentalDuration + { + public static readonly VendorRentalDuration[] Instances = new VendorRentalDuration[] + { + new VendorRentalDuration( TimeSpan.FromDays( 7.0 ), 1062361 ), // 1 Week + new VendorRentalDuration( TimeSpan.FromDays( 14.0 ), 1062362 ), // 2 Weeks + new VendorRentalDuration( TimeSpan.FromDays( 21.0 ), 1062363 ), // 3 Weeks + new VendorRentalDuration( TimeSpan.FromDays( 28.0 ), 1062364 ) // 1 Month + }; + + private TimeSpan m_Duration; + private int m_Name; + + public TimeSpan Duration{ get{ return m_Duration; } } + public int Name{ get{ return m_Name; } } + + public int ID + { + get + { + for ( int i = 0; i < Instances.Length; i++ ) + { + if ( Instances[i] == this ) + return i; + } + + return 0; + } + } + + private VendorRentalDuration( TimeSpan duration, int name ) + { + m_Duration = duration; + m_Name = name; + } + } + + public class RentedVendor : PlayerVendor + { + private VendorRentalDuration m_RentalDuration; + private int m_RentalPrice; + private bool m_LandlordRenew; + private bool m_RenterRenew; + private int m_RenewalPrice; + + private int m_RentalGold; + + private DateTime m_RentalExpireTime; + private Timer m_RentalExpireTimer; + + public RentedVendor( Mobile owner, BaseHouse house, VendorRentalDuration duration, int rentalPrice, bool landlordRenew, int rentalGold ) : base( owner, house ) + { + m_RentalDuration = duration; + m_RentalPrice = m_RenewalPrice = rentalPrice; + m_LandlordRenew = landlordRenew; + m_RenterRenew = false; + + m_RentalGold = rentalGold; + + m_RentalExpireTime = DateTime.Now + duration.Duration; + m_RentalExpireTimer = new RentalExpireTimer( this, duration.Duration ); + m_RentalExpireTimer.Start(); + } + + public RentedVendor( Serial serial ) : base( serial ) + { + } + + public VendorRentalDuration RentalDuration + { + get{ return m_RentalDuration; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RentalPrice + { + get{ return m_RentalPrice; } + set{ m_RentalPrice = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool LandlordRenew + { + get{ return m_LandlordRenew; } + set{ m_LandlordRenew = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool RenterRenew + { + get{ return m_RenterRenew; } + set{ m_RenterRenew = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Renew + { + get{ return LandlordRenew && RenterRenew && House != null && House.DecayType != DecayType.Condemned; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RenewalPrice + { + get{ return m_RenewalPrice; } + set{ m_RenewalPrice = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RentalGold + { + get{ return m_RentalGold; } + set{ m_RentalGold = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime RentalExpireTime + { + get{ return m_RentalExpireTime; } + } + + public override bool IsOwner( Mobile m ) + { + return m == Owner || m.AccessLevel >= AccessLevel.GameMaster; + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Landlord + { + get + { + if ( House != null ) + return House.Owner; + + return null; + } + } + + public bool IsLandlord( Mobile m ) + { + return House != null && House.IsOwner( m ); + } + + public void ComputeRentalExpireDelay( out int days, out int hours ) + { + TimeSpan delay = RentalExpireTime - DateTime.Now; + + if ( delay <= TimeSpan.Zero ) + { + days = 0; + hours = 0; + } + else + { + days = delay.Days; + hours = delay.Hours; + } + } + + public void SendRentalExpireMessage( Mobile to ) + { + int days, hours; + ComputeRentalExpireDelay( out days, out hours ); + + to.SendLocalizedMessage( 1062464, days.ToString() + "\t" + hours.ToString() ); // The rental contract on this vendor will expire in ~1_DAY~ day(s) and ~2_HOUR~ hour(s). + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + m_RentalExpireTimer.Stop(); + } + + public override void Destroy( bool toBackpack ) + { + if ( RentalGold > 0 && House != null && House.IsAosRules ) + { + if ( House.MovingCrate == null ) + House.MovingCrate = new MovingCrate( House ); + + Banker.Deposit( House.MovingCrate, RentalGold ); + RentalGold = 0; + } + + base.Destroy( toBackpack ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + if ( from.Alive ) + { + if ( IsOwner( from ) ) + { + list.Add( new ContractOptionsEntry( this ) ); + } + else if ( IsLandlord( from ) ) + { + if ( RentalGold > 0 ) + list.Add( new CollectRentEntry( this ) ); + + list.Add( new TerminateContractEntry( this ) ); + list.Add( new ContractOptionsEntry( this ) ); + } + } + + base.GetContextMenuEntries( from, list ); + } + + private class ContractOptionsEntry : ContextMenuEntry + { + private RentedVendor m_Vendor; + + public ContractOptionsEntry( RentedVendor vendor ) : base( 6209 ) + { + m_Vendor = vendor; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( m_Vendor.Deleted || !from.CheckAlive() ) + return; + + if ( m_Vendor.IsOwner( from ) ) + { + from.CloseGump( typeof( RenterVendorRentalGump ) ); + from.SendGump( new RenterVendorRentalGump( m_Vendor ) ); + + m_Vendor.SendRentalExpireMessage( from ); + } + else if ( m_Vendor.IsLandlord( from ) ) + { + from.CloseGump( typeof( LandlordVendorRentalGump ) ); + from.SendGump( new LandlordVendorRentalGump( m_Vendor ) ); + + m_Vendor.SendRentalExpireMessage( from ); + } + } + } + + private class CollectRentEntry : ContextMenuEntry + { + private RentedVendor m_Vendor; + + public CollectRentEntry( RentedVendor vendor ) : base( 6212 ) + { + m_Vendor = vendor; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord( from ) ) + return; + + if ( m_Vendor.RentalGold > 0 ) + { + int depositedGold = Banker.DepositUpTo( from, m_Vendor.RentalGold ); + m_Vendor.RentalGold -= depositedGold; + + if ( depositedGold > 0 ) + from.SendLocalizedMessage( 1060397, depositedGold.ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + + if ( m_Vendor.RentalGold > 0 ) + from.SendLocalizedMessage( 500390 ); // Your bank box is full. + } + } + } + + private class TerminateContractEntry : ContextMenuEntry + { + private RentedVendor m_Vendor; + + public TerminateContractEntry( RentedVendor vendor ) : base( 6218 ) + { + m_Vendor = vendor; + } + + public override void OnClick() + { + Mobile from = Owner.From; + + if ( m_Vendor.Deleted || !from.CheckAlive() || !m_Vendor.IsLandlord( from ) ) + return; + + from.SendLocalizedMessage( 1062503 ); // Enter the amount of gold you wish to offer the renter in exchange for immediate termination of this contract? + from.Prompt = new RefundOfferPrompt( m_Vendor ); + } + } + + private class RefundOfferPrompt : Prompt + { + private RentedVendor m_Vendor; + + public RefundOfferPrompt( RentedVendor vendor ) + { + m_Vendor = vendor; + } + + public override void OnResponse( Mobile from, string text ) + { + if ( !m_Vendor.CanInteractWith( from, false ) || !m_Vendor.IsLandlord( from ) ) + return; + + text = text.Trim(); + + int amount; + try + { + amount = Convert.ToInt32( text ); + } + catch + { + amount = -1; + } + + Mobile owner = m_Vendor.Owner; + if ( owner == null ) + return; + + if ( amount < 0 ) + { + from.SendLocalizedMessage( 1062506 ); // You did not enter a valid amount. Offer canceled. + } + else if ( Banker.GetBalance( from ) < amount ) + { + from.SendLocalizedMessage( 1062507 ); // You do not have that much money in your bank account. + } + else if ( owner.Map != m_Vendor.Map || !owner.InRange( m_Vendor, 5 ) ) + { + from.SendLocalizedMessage( 1062505 ); // The renter must be closer to the vendor in order for you to make this offer. + } + else + { + from.SendLocalizedMessage( 1062504 ); // Please wait while the renter considers your offer. + + owner.CloseGump( typeof( VendorRentalRefundGump ) ); + owner.SendGump( new VendorRentalRefundGump( m_Vendor, from, amount ) ); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.WriteEncodedInt( m_RentalDuration.ID ); + + writer.Write( (int) m_RentalPrice ); + writer.Write( (bool) m_LandlordRenew ); + writer.Write( (bool) m_RenterRenew ); + writer.Write( (int) m_RenewalPrice ); + + writer.Write( (int) m_RentalGold ); + + writer.WriteDeltaTime( (DateTime) m_RentalExpireTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + int durationID = reader.ReadEncodedInt(); + if ( durationID < VendorRentalDuration.Instances.Length ) + m_RentalDuration = VendorRentalDuration.Instances[durationID]; + else + m_RentalDuration = VendorRentalDuration.Instances[0]; + + m_RentalPrice = reader.ReadInt(); + m_LandlordRenew = reader.ReadBool(); + m_RenterRenew = reader.ReadBool(); + m_RenewalPrice = reader.ReadInt(); + + m_RentalGold = reader.ReadInt(); + + m_RentalExpireTime = reader.ReadDeltaTime(); + + TimeSpan delay = m_RentalExpireTime - DateTime.Now; + m_RentalExpireTimer = new RentalExpireTimer( this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero ); + m_RentalExpireTimer.Start(); + } + + private class RentalExpireTimer : Timer + { + private RentedVendor m_Vendor; + + public RentalExpireTimer( RentedVendor vendor, TimeSpan delay ) : base( delay, vendor.RentalDuration.Duration ) + { + m_Vendor = vendor; + + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + int renewalPrice = m_Vendor.RenewalPrice; + + if ( m_Vendor.Renew && m_Vendor.HoldGold >= renewalPrice ) + { + m_Vendor.HoldGold -= renewalPrice; + m_Vendor.RentalGold += renewalPrice; + + m_Vendor.RentalPrice = renewalPrice; + + m_Vendor.m_RentalExpireTime = DateTime.Now + m_Vendor.RentalDuration.Duration; + } + else + { + m_Vendor.Destroy( false ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBChainmailArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBChainmailArmor.cs new file mode 100644 index 000000000..2762d0e9e --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBChainmailArmor.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBChainmailArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBChainmailArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( ChainCoif ), 17, 20, 0x13BB, 0 ) ); + Add( new GenericBuyInfo( typeof( ChainChest ), 143, 20, 0x13BF, 0 ) ); + Add( new GenericBuyInfo( typeof( ChainLegs ), 149, 20, 0x13BE, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( ChainCoif ), 6 ); + Add( typeof( ChainChest ), 71 ); + Add( typeof( ChainLegs ), 74 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBHelmetArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBHelmetArmor.cs new file mode 100644 index 000000000..cfa7247c1 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBHelmetArmor.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBHelmetArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBHelmetArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( PlateHelm ), 21, 20, 0x1412, 0 ) ); + Add( new GenericBuyInfo( typeof( CloseHelm ), 18, 20, 0x1408, 0 ) ); + Add( new GenericBuyInfo( typeof( CloseHelm ), 18, 20, 0x1409, 0 ) ); + Add( new GenericBuyInfo( typeof( Helmet ), 31, 20, 0x140A, 0 ) ); + Add( new GenericBuyInfo( typeof( Helmet ), 18, 20, 0x140B, 0 ) ); + Add( new GenericBuyInfo( typeof( NorseHelm ), 18, 20, 0x140E, 0 ) ); + Add( new GenericBuyInfo( typeof( NorseHelm ), 18, 20, 0x140F, 0 ) ); + Add( new GenericBuyInfo( typeof( Bascinet ), 18, 20, 0x140C, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateHelm ), 21, 20, 0x1419, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bascinet ), 9 ); + Add( typeof( CloseHelm ), 9 ); + Add( typeof( Helmet ), 9 ); + Add( typeof( NorseHelm ), 9 ); + Add( typeof( PlateHelm ), 10 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBLeatherArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBLeatherArmor.cs new file mode 100644 index 000000000..77562a01f --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBLeatherArmor.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBLeatherArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBLeatherArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( LeatherArms ), 80, 20, 0x13CD, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherChest ), 101, 20, 0x13CC, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherGloves ), 60, 20, 0x13C6, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherGorget ), 74, 20, 0x13C7, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherLegs ), 80, 20, 0x13cb, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherCap ), 10, 20, 0x1DB9, 0 ) ); + Add( new GenericBuyInfo( typeof( FemaleLeatherChest ), 116, 20, 0x1C06, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherBustierArms ), 97, 20, 0x1C0A, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherShorts ), 86, 20, 0x1C00, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherSkirt ), 87, 20, 0x1C08, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( LeatherArms ), 40 ); + Add( typeof( LeatherChest ), 52 ); + Add( typeof( LeatherGloves ), 30 ); + Add( typeof( LeatherGorget ), 37 ); + Add( typeof( LeatherLegs ), 40 ); + Add( typeof( LeatherCap ), 5 ); + + + Add( typeof( FemaleLeatherChest ), 18 ); + Add( typeof( FemaleStuddedChest ), 25 ); + Add( typeof( LeatherShorts ), 14 ); + Add( typeof( LeatherSkirt ), 11 ); + Add( typeof( LeatherBustierArms ), 11 ); + Add( typeof( StuddedBustierArms ), 27 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBMetalShields.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBMetalShields.cs new file mode 100644 index 000000000..9a2f4a070 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBMetalShields.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBMetalShields : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBMetalShields() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BronzeShield ), 66, 20, 0x1B72, 0 ) ); + Add( new GenericBuyInfo( typeof( Buckler ), 50, 20, 0x1B73, 0 ) ); + Add( new GenericBuyInfo( typeof( MetalKiteShield ), 123, 20, 0x1B74, 0 ) ); + Add( new GenericBuyInfo( typeof( HeaterShield ), 231, 20, 0x1B76, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenKiteShield ), 70, 20, 0x1B78, 0 ) ); + Add( new GenericBuyInfo( typeof( MetalShield ), 121, 20, 0x1B7B, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Buckler ), 25 ); + Add( typeof( BronzeShield ), 33 ); + Add( typeof( MetalShield ), 60 ); + Add( typeof( MetalKiteShield ), 62 ); + Add( typeof( HeaterShield ), 115 ); + Add( typeof( WoodenKiteShield ), 35 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBPlateArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBPlateArmor.cs new file mode 100644 index 000000000..7b9dee0fa --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBPlateArmor.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBPlateArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBPlateArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( PlateGorget ), 104, 20, 0x1413, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateChest ), 243, 20, 0x1415, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateLegs ), 218, 20, 0x1411, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateArms ), 188, 20, 0x1410, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateGloves ), 155, 20, 0x1414, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( PlateArms ), 94 ); + Add( typeof( PlateChest ), 121 ); + Add( typeof( PlateGloves ), 72 ); + Add( typeof( PlateGorget ), 52 ); + Add( typeof( PlateLegs ), 109 ); + + Add( typeof( FemalePlateChest ), 113 ); + + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBRingmailArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBRingmailArmor.cs new file mode 100644 index 000000000..ed22bc20c --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBRingmailArmor.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBRingmailArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBRingmailArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( RingmailChest ), 121, 20, 0x13ec, 0 ) ); + Add( new GenericBuyInfo( typeof( RingmailLegs ), 90, 20, 0x13F0, 0 ) ); + Add( new GenericBuyInfo( typeof( RingmailArms ), 85, 20, 0x13EE, 0 ) ); + Add( new GenericBuyInfo( typeof( RingmailGloves ), 93, 20, 0x13eb, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( RingmailArms ), 42 ); + Add( typeof( RingmailChest ), 60 ); + Add( typeof( RingmailGloves ), 26 ); + Add( typeof( RingmailLegs ), 45 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBStuddedArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBStuddedArmor.cs new file mode 100644 index 000000000..2db40207b --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBStuddedArmor.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBStuddedArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBStuddedArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( StuddedArms ), 87, 20, 0x13DC, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedChest ), 128, 20, 0x13DB, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedGloves ), 79, 20, 0x13D5, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedGorget ), 73, 20, 0x13D6, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedLegs ), 103, 20, 0x13DA, 0 ) ); + Add( new GenericBuyInfo( typeof( FemaleStuddedChest ), 142, 20, 0x1C02, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedBustierArms ), 120, 20, 0x1c0c, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( StuddedArms ), 43 ); + Add( typeof( StuddedChest ), 64 ); + Add( typeof( StuddedGloves ), 39 ); + Add( typeof( StuddedGorget ), 36 ); + Add( typeof( StuddedLegs ), 51 ); + Add( typeof( FemaleStuddedChest ), 71 ); + Add( typeof( StuddedBustierArms ), 60 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Armors/SBWoodenShields.cs b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBWoodenShields.cs new file mode 100644 index 000000000..e9733eedf --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Armors/SBWoodenShields.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBWoodenShields: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBWoodenShields() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( WoodenShield ), 30, 20, 0x1B7A, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( WoodenShield ), 15 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBAlchemist.cs b/Scripts/Mobiles/Vendors/SBInfo/SBAlchemist.cs new file mode 100644 index 000000000..0f1240f94 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBAlchemist.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBAlchemist : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBAlchemist() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( RefreshPotion ), 15, 10, 0xF0B, 0 ) ); + Add( new GenericBuyInfo( typeof( AgilityPotion ), 15, 10, 0xF08, 0 ) ); + Add( new GenericBuyInfo( typeof( NightSightPotion ), 15, 10, 0xF06, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserHealPotion ), 15, 10, 0xF0C, 0 ) ); + Add( new GenericBuyInfo( typeof( StrengthPotion ), 15, 10, 0xF09, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserPoisonPotion ), 15, 10, 0xF0A, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserCurePotion ), 15, 10, 0xF07, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserExplosionPotion ), 21, 10, 0xF0D, 0 ) ); + Add( new GenericBuyInfo( typeof( MortarPestle ), 8, 10, 0xE9B, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlackPearl ), 5, 20, 0xF7A, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 20, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( SulfurousAsh ), 3, 20, 0xF8C, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bottle ), 5, 100, 0xF0E, 0 ) ); + Add( new GenericBuyInfo( typeof( HeatingStand ), 2, 100, 0x1849, 0 ) ); + + Add( new GenericBuyInfo( "1041060", typeof( HairDye ), 37, 10, 0xEFF, 0 ) ); + + Add( new GenericBuyInfo( typeof( HeatingStand ), 2, 100, 0x1849, 0 ) ); // This is on OSI :-P + + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BlackPearl ), 3 ); + Add( typeof( Bloodmoss ), 3 ); + Add( typeof( MandrakeRoot ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( SpidersSilk ), 2 ); + Add( typeof( SulfurousAsh ), 2 ); + Add( typeof( Bottle ), 3 ); + Add( typeof( MortarPestle ), 4 ); + Add( typeof( HairDye ), 19 ); + + Add( typeof( NightSightPotion ), 7 ); + Add( typeof( AgilityPotion ), 7 ); + Add( typeof( StrengthPotion ), 7 ); + Add( typeof( RefreshPotion ), 7 ); + Add( typeof( LesserCurePotion ), 7 ); + Add( typeof( LesserHealPotion ), 7 ); + Add( typeof( LesserPoisonPotion ), 7 ); + Add( typeof( LesserExplosionPotion ), 10 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBAnimalTrainer.cs b/Scripts/Mobiles/Vendors/SBInfo/SBAnimalTrainer.cs new file mode 100644 index 000000000..646d5318e --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBAnimalTrainer.cs @@ -0,0 +1,51 @@ + +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBAnimalTrainer : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBAnimalTrainer() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new AnimalBuyInfo( 1, typeof( Cat ), 132, 10, 201, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Dog ), 170, 10, 217, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Horse ), 550, 10, 204, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( PackHorse ), 631, 10, 291, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( PackLlama ), 565, 10, 292, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Rabbit ), 106, 10, 205, 0 ) ); + + if( !Core.AOS ) + { + Add( new AnimalBuyInfo( 1, typeof( Eagle ), 402, 10, 5, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( BrownBear ), 855, 10, 167, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( GrizzlyBear ), 1767, 10, 212, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Panther ), 1271, 10, 214, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( TimberWolf ), 768, 10, 225, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Rat ), 107, 10, 238, 0 ) ); + } + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBArchitect.cs b/Scripts/Mobiles/Vendors/SBInfo/SBArchitect.cs new file mode 100644 index 000000000..0b3b59ac7 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBArchitect.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBArchitect : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBArchitect() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( "1041280", typeof( InteriorDecorator ), 10001, 20, 0xFC1, 0 ) ); + if ( Core.AOS ) + Add( new GenericBuyInfo( "1060651", typeof( HousePlacementTool ), 627, 20, 0x14F6, 0 )); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( InteriorDecorator ), 5000 ); + + if ( Core.AOS ) + Add( typeof( HousePlacementTool ), 301 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBaker.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBaker.cs new file mode 100644 index 000000000..e0fd55548 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBaker.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBaker : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBaker() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BreadLoaf ), 6, 20, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( BreadLoaf ), 5, 20, 0x103C, 0 ) ); + Add( new GenericBuyInfo( typeof( ApplePie ), 7, 20, 0x1041, 0 ) ); //OSI just has Pie, not Apple/Fruit/Meat + Add( new GenericBuyInfo( typeof( Cake ), 13, 20, 0x9E9, 0 ) ); + Add( new GenericBuyInfo( typeof( Muffins ), 3, 20, 0x9EA, 0 ) ); + Add( new GenericBuyInfo( typeof( SackFlour ), 3, 20, 0x1039, 0 ) ); + Add( new GenericBuyInfo( typeof( FrenchBread ), 5, 20, 0x98C, 0 ) ); + Add( new GenericBuyInfo( typeof( Cookies ), 3, 20, 0x160b, 0 ) ); + Add( new GenericBuyInfo( typeof( CheesePizza ), 8, 10, 0x1040, 0 ) ); // OSI just has Pizza + Add( new GenericBuyInfo( typeof( JarHoney ), 3, 20, 0x9ec, 0 ) ); + Add (new GenericBuyInfo( typeof( BowlFlour ), 7, 20, 0xA1E, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BreadLoaf ), 3 ); + Add( typeof( FrenchBread ), 1 ); + Add( typeof( Cake ), 5 ); + Add( typeof( Cookies ), 3 ); + Add( typeof( Muffins ), 2 ); + Add( typeof( CheesePizza ), 4 ); + Add( typeof( ApplePie ), 5 ); + Add( typeof( PeachCobbler ), 5 ); + Add( typeof( Quiche ), 6 ); + Add( typeof( Dough ), 4 ); + Add( typeof( JarHoney ), 1 ); + Add( typeof( Pitcher ), 5 ); + Add( typeof( SackFlour ), 1 ); + Add( typeof( Eggs ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBanker.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBanker.cs new file mode 100644 index 000000000..078bd5f10 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBanker.cs @@ -0,0 +1,39 @@ + +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBanker : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBanker() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( "1041243", typeof( ContractOfEmployment ), 1252, 20, 0x14F0, 0 ) ); + + if ( Multis.BaseHouse.NewVendorSystem ) + Add( new GenericBuyInfo( "1062332", typeof( VendorRentalContract ), 1252, 20, 0x14F0, 0x672 ) ); + Add( new GenericBuyInfo( "1047016", typeof( CommodityDeed ), 5, 20, 0x14F0, 0x47 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBard.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBard.cs new file mode 100644 index 000000000..2d5c85ddd --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBard.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBard: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBard() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Drums ), 21, ( 10 ), 0x0E9C, 0 ) ); + Add( new GenericBuyInfo( typeof( Tambourine ), 21, ( 10 ), 0x0E9E, 0 ) ); + Add( new GenericBuyInfo( typeof( LapHarp ), 21, ( 10 ), 0x0EB2, 0 ) ); + Add( new GenericBuyInfo( typeof( Lute ), 21, ( 10 ), 0x0EB3, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( LapHarp ), 10 ); + Add( typeof( Lute ), 10 ); + Add( typeof( Drums ), 10 ); + Add( typeof( Harp ), 10 ); + Add( typeof( Tambourine ), 10 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBarkeeper.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBarkeeper.cs new file mode 100644 index 000000000..8263cc6fe --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBarkeeper.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBarkeeper : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBarkeeper() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Ale, 7, 20, 0x99F, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Wine, 7, 20, 0x9C7, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Liquor, 7, 20, 0x99B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Jug ), BeverageType.Cider, 13, 20, 0x9C8, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Milk, 7, 20, 0x9F0, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Ale, 11, 20, 0x1F95, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Cider, 11, 20, 0x1F97, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Liquor, 11, 20, 0x1F99, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Wine, 11, 20, 0x1F9B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Water, 11, 20, 0x1F9D, 0 ) ); + + Add( new GenericBuyInfo( typeof( BreadLoaf ), 6, 10, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( CheeseWheel ), 21, 10, 0x97E, 0 ) ); + Add( new GenericBuyInfo( typeof( CookedBird ), 17, 20, 0x9B7, 0 ) ); + Add( new GenericBuyInfo( typeof( LambLeg ), 8, 20, 0x160A, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenBowlOfCarrots ), 3, 20, 0x15F9, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfCorn ), 3, 20, 0x15FA, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfLettuce ), 3, 20, 0x15FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfPeas ), 3, 20, 0x15FC, 0 ) ); + Add( new GenericBuyInfo( typeof( EmptyPewterBowl ), 2, 20, 0x15FD, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfCorn ), 3, 20, 0x15FE, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfLettuce ), 3, 20, 0x15FF, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPeas ), 3, 20, 0x1600, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPotatos ), 3, 20, 0x1601, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfStew ), 3, 20, 0x1604, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfTomatoSoup ), 3, 20, 0x1606, 0 ) ); + + Add( new GenericBuyInfo( typeof( ApplePie ), 7, 20, 0x1041, 0 ) ); //OSI just has Pie, not Apple/Fruit/Meat + + Add( new GenericBuyInfo( "1016450", typeof( Chessboard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( "1016449", typeof( CheckerBoard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( typeof( Backgammon ), 2, 20, 0xE1C, 0 ) ); + Add( new GenericBuyInfo( typeof( Dices ), 2, 20, 0xFA7, 0 ) ); + Add( new GenericBuyInfo( "1041243", typeof( ContractOfEmployment ), 1252, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "a barkeep contract", typeof( BarkeepContract ), 1252, 20, 0x14F0, 0 ) ); + if ( Multis.BaseHouse.NewVendorSystem ) + Add( new GenericBuyInfo( "1062332", typeof( VendorRentalContract ), 1252, 20, 0x14F0, 0x672 ) ); + + /*if ( Map == Tokuno ) + { + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E8, 0 ) ); + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E9, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2836, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2837, 0 ) ); + Add( new GenericBuyInfo( typeof( GreenTeaBasket ), 2, 20, 0x284B, 0 ) ); + }*/ + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( WoodenBowlOfCarrots ), 1 ); + Add( typeof( WoodenBowlOfCorn ), 1 ); + Add( typeof( WoodenBowlOfLettuce ), 1 ); + Add( typeof( WoodenBowlOfPeas ), 1 ); + Add( typeof( EmptyPewterBowl ), 1 ); + Add( typeof( PewterBowlOfCorn ), 1 ); + Add( typeof( PewterBowlOfLettuce ), 1 ); + Add( typeof( PewterBowlOfPeas ), 1 ); + Add( typeof( PewterBowlOfPotatos ), 1 ); + Add( typeof( WoodenBowlOfStew ), 1 ); + Add( typeof( WoodenBowlOfTomatoSoup ), 1 ); + Add( typeof( BeverageBottle ), 3 ); + Add( typeof( Jug ), 6 ); + Add( typeof( Pitcher ), 5 ); + Add( typeof( GlassMug ), 1 ); + Add( typeof( BreadLoaf ), 3 ); + Add( typeof( CheeseWheel ), 12 ); + Add( typeof( Ribs ), 6 ); + Add( typeof( Peach ), 1 ); + Add( typeof( Pear ), 1 ); + Add( typeof( Grapes ), 1 ); + Add( typeof( Apple ), 1 ); + Add( typeof( Banana ), 1 ); + Add( typeof( Candle ), 3 ); + Add( typeof( Chessboard ), 1 ); + Add( typeof( CheckerBoard ), 1 ); + Add( typeof( Backgammon ), 1 ); + Add( typeof( Dices ), 1 ); + Add( typeof( ContractOfEmployment ), 626 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBeekeeper.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBeekeeper.cs new file mode 100644 index 000000000..e9ab151cc --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBeekeeper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBeekeeper : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBeekeeper() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( JarHoney ), 3, 20, 0x9EC, 0 ) ); + Add( new GenericBuyInfo( typeof( Beeswax ), 2, 20, 0x1422, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( JarHoney ), 1 ); + Add( typeof( Beeswax ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBlacksmith.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBlacksmith.cs new file mode 100644 index 000000000..2bfa474b6 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBlacksmith.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBlacksmith : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBlacksmith() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( IronIngot ), 5, 16, 0x1BF2, 0 ) ); + Add( new GenericBuyInfo( typeof( Tongs ), 13, 14, 0xFBB, 0 ) ); + + Add( new GenericBuyInfo( typeof( BronzeShield ), 66, 20, 0x1B72, 0 ) ); + Add( new GenericBuyInfo( typeof( Buckler ), 50, 20, 0x1B73, 0 ) ); + Add( new GenericBuyInfo( typeof( MetalKiteShield ), 123, 20, 0x1B74, 0 ) ); + Add( new GenericBuyInfo( typeof( HeaterShield ), 231, 20, 0x1B76, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenKiteShield ), 70, 20, 0x1B78, 0 ) ); + Add( new GenericBuyInfo( typeof( MetalShield ), 121, 20, 0x1B7B, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenShield ), 30, 20, 0x1B7A, 0 ) ); + + Add( new GenericBuyInfo( typeof( PlateGorget ), 104, 20, 0x1413, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateChest ), 243, 20, 0x1415, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateLegs ), 218, 20, 0x1411, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateArms ), 188, 20, 0x1410, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateGloves ), 155, 20, 0x1414, 0 ) ); + + Add( new GenericBuyInfo( typeof( PlateHelm ), 21, 20, 0x1412, 0 ) ); + Add( new GenericBuyInfo( typeof( CloseHelm ), 18, 20, 0x1408, 0 ) ); + Add( new GenericBuyInfo( typeof( CloseHelm ), 18, 20, 0x1409, 0 ) ); + Add( new GenericBuyInfo( typeof( Helmet ), 31, 20, 0x140A, 0 ) ); + Add( new GenericBuyInfo( typeof( Helmet ), 18, 20, 0x140B, 0 ) ); + Add( new GenericBuyInfo( typeof( NorseHelm ), 18, 20, 0x140E, 0 ) ); + Add( new GenericBuyInfo( typeof( NorseHelm ), 18, 20, 0x140F, 0 ) ); + Add( new GenericBuyInfo( typeof( Bascinet ), 18, 20, 0x140C, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateHelm ), 21, 20, 0x1419, 0 ) ); + + Add( new GenericBuyInfo( typeof( ChainCoif ), 17, 20, 0x13BB, 0 ) ); + Add( new GenericBuyInfo( typeof( ChainChest ), 143, 20, 0x13BF, 0 ) ); + Add( new GenericBuyInfo( typeof( ChainLegs ), 149, 20, 0x13BE, 0 ) ); + + Add( new GenericBuyInfo( typeof( RingmailChest ), 121, 20, 0x13ec, 0 ) ); + Add( new GenericBuyInfo( typeof( RingmailLegs ), 90, 20, 0x13F0, 0 ) ); + Add( new GenericBuyInfo( typeof( RingmailArms ), 85, 20, 0x13EE, 0 ) ); + Add( new GenericBuyInfo( typeof( RingmailGloves ), 93, 20, 0x13eb, 0 ) ); + + Add( new GenericBuyInfo( typeof( ExecutionersAxe ), 30, 20, 0xF45, 0 ) ); + Add( new GenericBuyInfo( typeof( Bardiche ), 60, 20, 0xF4D, 0 ) ); + Add( new GenericBuyInfo( typeof( BattleAxe ), 26, 20, 0xF47, 0 ) ); + Add( new GenericBuyInfo( typeof( TwoHandedAxe ), 32, 20, 0x1443, 0 ) ); + Add( new GenericBuyInfo( typeof( Bow ), 35, 20, 0x13B2, 0 ) ); + Add( new GenericBuyInfo( typeof( ButcherKnife ), 14, 20, 0x13F6, 0 ) ); + Add( new GenericBuyInfo( typeof( Crossbow ), 46, 20, 0xF50, 0 ) ); + Add( new GenericBuyInfo( typeof( HeavyCrossbow ), 55, 20, 0x13FD, 0 ) ); + Add( new GenericBuyInfo( typeof( Cutlass ), 24, 20, 0x1441, 0 ) ); + Add( new GenericBuyInfo( typeof( Dagger ), 21, 20, 0xF52, 0 ) ); + Add( new GenericBuyInfo( typeof( Halberd ), 42, 20, 0x143E, 0 ) ); + Add( new GenericBuyInfo( typeof( HammerPick ), 26, 20, 0x143D, 0 ) ); + Add( new GenericBuyInfo( typeof( Katana ), 33, 20, 0x13FF, 0 ) ); + Add( new GenericBuyInfo( typeof( Kryss ), 32, 20, 0x1401, 0 ) ); + Add( new GenericBuyInfo( typeof( Broadsword ), 35, 20, 0xF5E, 0 ) ); + Add( new GenericBuyInfo( typeof( Longsword ), 55, 20, 0xF61, 0 ) ); + Add( new GenericBuyInfo( typeof( ThinLongsword ), 27, 20, 0x13B8, 0 ) ); + Add( new GenericBuyInfo( typeof( VikingSword ), 55, 20, 0x13B9, 0 ) ); + Add( new GenericBuyInfo( typeof( Cleaver ), 15, 20, 0xEC3, 0 ) ); + Add( new GenericBuyInfo( typeof( Axe ), 40, 20, 0xF49, 0 ) ); + Add( new GenericBuyInfo( typeof( DoubleAxe ), 52, 20, 0xF4B, 0 ) ); + Add( new GenericBuyInfo( typeof( Pickaxe ), 22, 20, 0xE86, 0 ) ); + Add( new GenericBuyInfo( typeof( Pitchfork ), 19, 20, 0xE87, 0 ) ); + Add( new GenericBuyInfo( typeof( Scimitar ), 36, 20, 0x13B6, 0 ) ); + Add( new GenericBuyInfo( typeof( SkinningKnife ), 14, 20, 0xEC4, 0 ) ); + Add( new GenericBuyInfo( typeof( LargeBattleAxe ), 33, 20, 0x13FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WarAxe ), 29, 20, 0x13B0, 0 ) ); + + if ( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( BoneHarvester ), 35, 20, 0x26BB, 0 ) ); + Add( new GenericBuyInfo( typeof( CrescentBlade ), 37, 20, 0x26C1, 0 ) ); + Add( new GenericBuyInfo( typeof( DoubleBladedStaff ), 35, 20, 0x26BF, 0 ) ); + Add( new GenericBuyInfo( typeof( Lance ), 34, 20, 0x26C0, 0 ) ); + Add( new GenericBuyInfo( typeof( Pike ), 39, 20, 0x26BE, 0 ) ); + Add( new GenericBuyInfo( typeof( Scythe ), 39, 20, 0x26BA, 0 ) ); + Add( new GenericBuyInfo( typeof( CompositeBow ), 50, 20, 0x26C2, 0 ) ); + Add( new GenericBuyInfo( typeof( RepeatingCrossbow ), 57, 20, 0x26C3, 0 ) ); + } + + Add( new GenericBuyInfo( typeof( BlackStaff ), 22, 20, 0xDF1, 0 ) ); + Add( new GenericBuyInfo( typeof( Club ), 16, 20, 0x13B4, 0 ) ); + Add( new GenericBuyInfo( typeof( GnarledStaff ), 16, 20, 0x13F8, 0 ) ); + Add( new GenericBuyInfo( typeof( Mace ), 28, 20, 0xF5C, 0 ) ); + Add( new GenericBuyInfo( typeof( Maul ), 21, 20, 0x143B, 0 ) ); + Add( new GenericBuyInfo( typeof( QuarterStaff ), 19, 20, 0xE89, 0 ) ); + Add( new GenericBuyInfo( typeof( ShepherdsCrook ), 20, 20, 0xE81, 0 ) ); + Add( new GenericBuyInfo( typeof( SmithHammer ), 21, 20, 0x13E3, 0 ) ); + Add( new GenericBuyInfo( typeof( ShortSpear ), 23, 20, 0x1403, 0 ) ); + Add( new GenericBuyInfo( typeof( Spear ), 31, 20, 0xF62, 0 ) ); + Add( new GenericBuyInfo( typeof( WarHammer ), 25, 20, 0x1439, 0 ) ); + Add( new GenericBuyInfo( typeof( WarMace ), 31, 20, 0x1407, 0 ) ); + + if( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( Scepter ), 39, 20, 0x26BC, 0 ) ); + Add( new GenericBuyInfo( typeof( BladedStaff ), 40, 20, 0x26BD, 0 ) ); + } + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Tongs ), 7 ); + Add( typeof( IronIngot ), 4 ); + + Add( typeof( Buckler ), 25 ); + Add( typeof( BronzeShield ), 33 ); + Add( typeof( MetalShield ), 60 ); + Add( typeof( MetalKiteShield ), 62 ); + Add( typeof( HeaterShield ), 115 ); + Add( typeof( WoodenKiteShield ), 35 ); + + Add( typeof( WoodenShield ), 15 ); + + Add( typeof( PlateArms ), 94 ); + Add( typeof( PlateChest ), 121 ); + Add( typeof( PlateGloves ), 72 ); + Add( typeof( PlateGorget ), 52 ); + Add( typeof( PlateLegs ), 109 ); + + Add( typeof( FemalePlateChest ), 113 ); + Add( typeof( FemaleLeatherChest ), 18 ); + Add( typeof( FemaleStuddedChest ), 25 ); + Add( typeof( LeatherShorts ), 14 ); + Add( typeof( LeatherSkirt ), 11 ); + Add( typeof( LeatherBustierArms ), 11 ); + Add( typeof( StuddedBustierArms ), 27 ); + + Add( typeof( Bascinet ), 9 ); + Add( typeof( CloseHelm ), 9 ); + Add( typeof( Helmet ), 9 ); + Add( typeof( NorseHelm ), 9 ); + Add( typeof( PlateHelm ), 10 ); + + Add( typeof( ChainCoif ), 6 ); + Add( typeof( ChainChest ), 71 ); + Add( typeof( ChainLegs ), 74 ); + + Add( typeof( RingmailArms ), 42 ); + Add( typeof( RingmailChest ), 60 ); + Add( typeof( RingmailGloves ), 26 ); + Add( typeof( RingmailLegs ), 45 ); + + Add( typeof( BattleAxe ), 13 ); + Add( typeof( DoubleAxe ), 26 ); + Add( typeof( ExecutionersAxe ), 15 ); + Add( typeof( LargeBattleAxe ),16 ); + Add( typeof( Pickaxe ), 11 ); + Add( typeof( TwoHandedAxe ), 16 ); + Add( typeof( WarAxe ), 14 ); + Add( typeof( Axe ), 20 ); + + Add( typeof( Bardiche ), 30 ); + Add( typeof( Halberd ), 21 ); + + Add( typeof( ButcherKnife ), 7 ); + Add( typeof( Cleaver ), 7 ); + Add( typeof( Dagger ), 10 ); + Add( typeof( SkinningKnife ), 7 ); + + Add( typeof( Club ), 8 ); + Add( typeof( HammerPick ), 13 ); + Add( typeof( Mace ), 14 ); + Add( typeof( Maul ), 10 ); + Add( typeof( WarHammer ), 12 ); + Add( typeof( WarMace ), 15 ); + + Add( typeof( HeavyCrossbow ), 27 ); + Add( typeof( Bow ), 17 ); + Add( typeof( Crossbow ), 23 ); + + if( Core.AOS ) + { + Add( typeof( CompositeBow ), 25 ); + Add( typeof( RepeatingCrossbow ), 28 ); + Add( typeof( Scepter ), 20 ); + Add( typeof( BladedStaff ), 20 ); + Add( typeof( Scythe ), 19 ); + Add( typeof( BoneHarvester ), 17 ); + Add( typeof( Scepter ), 18 ); + Add( typeof( BladedStaff ), 16 ); + Add( typeof( Pike ), 19 ); + Add( typeof( DoubleBladedStaff ), 17 ); + Add( typeof( Lance ), 17 ); + Add( typeof( CrescentBlade ), 18 ); + } + + Add( typeof( Spear ), 15 ); + Add( typeof( Pitchfork ), 9 ); + Add( typeof( ShortSpear ), 11 ); + + Add( typeof( BlackStaff ), 11 ); + Add( typeof( GnarledStaff ), 8 ); + Add( typeof( QuarterStaff ), 9 ); + Add( typeof( ShepherdsCrook ), 10 ); + + Add( typeof( SmithHammer ), 10 ); + + Add( typeof( Broadsword ), 17 ); + Add( typeof( Cutlass ), 12 ); + Add( typeof( Katana ), 16 ); + Add( typeof( Kryss ), 16 ); + Add( typeof( Longsword ), 27 ); + Add( typeof( Scimitar ), 18 ); + Add( typeof( ThinLongsword ), 13 ); + Add( typeof( VikingSword ), 27 ); + + + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBBowyer.cs b/Scripts/Mobiles/Vendors/SBInfo/SBBowyer.cs new file mode 100644 index 000000000..8db3f8033 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBBowyer.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBBowyer : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBBowyer() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( FletcherTools ), 2, 20, 0x1022, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( FletcherTools ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBButcher.cs b/Scripts/Mobiles/Vendors/SBInfo/SBButcher.cs new file mode 100644 index 000000000..210de81e9 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBButcher.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBButcher : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBButcher() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bacon ), 7, 20, 0x979, 0 ) ); + Add( new GenericBuyInfo( typeof( Ham ), 26, 20, 0x9C9, 0 ) ); + Add( new GenericBuyInfo( typeof( Sausage ), 18, 20, 0x9C0, 0 ) ); + Add( new GenericBuyInfo( typeof( RawChickenLeg ), 6, 20, 0x1607, 0 ) ); + Add( new GenericBuyInfo( typeof( RawBird ), 9, 20, 0x9B9, 0 ) ); + Add( new GenericBuyInfo( typeof( RawLambLeg ), 9, 20, 0x1609, 0 ) ); + Add( new GenericBuyInfo( typeof( RawRibs ), 16, 20, 0x9F1, 0 ) ); + Add( new GenericBuyInfo( typeof( ButcherKnife ), 13, 20, 0x13F6, 0 ) ); + Add( new GenericBuyInfo( typeof( Cleaver ), 13, 20, 0xEC3, 0 ) ); + Add( new GenericBuyInfo( typeof( SkinningKnife ), 13, 20, 0xEC4, 0 ) ); + + + + + + + + + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( RawRibs ), 8 ); + Add( typeof( RawLambLeg ), 4 ); + Add( typeof( RawChickenLeg ), 3 ); + Add( typeof( RawBird ), 4 ); + Add( typeof( Bacon ), 3 ); + Add( typeof( Sausage ), 9 ); + Add( typeof( Ham ), 13 ); + Add( typeof( ButcherKnife ), 7 ); + Add( typeof( Cleaver ), 7 ); + Add( typeof( SkinningKnife ), 7 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBCarpenter.cs b/Scripts/Mobiles/Vendors/SBInfo/SBCarpenter.cs new file mode 100644 index 000000000..79875c59a --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBCarpenter.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBCarpenter: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBCarpenter() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Nails ), 3, 20, 0x102E, 0 ) ); + Add( new GenericBuyInfo( typeof( Axle ), 2, 20, 0x105B, 0 ) ); + Add( new GenericBuyInfo( typeof( Board ), 3, 20, 0x1BD7, 0 ) ); + Add( new GenericBuyInfo( typeof( DrawKnife ), 10, 20, 0x10E4, 0 ) ); + Add( new GenericBuyInfo( typeof( Froe ), 10, 20, 0x10E5, 0 ) ); + Add( new GenericBuyInfo( typeof( Scorp ), 10, 20, 0x10E7, 0 ) ); + Add( new GenericBuyInfo( typeof( Inshave ), 10, 20, 0x10E6, 0 ) ); + Add( new GenericBuyInfo( typeof( DovetailSaw ), 12, 20, 0x1028, 0 ) ); + Add( new GenericBuyInfo( typeof( Saw ), 15, 20, 0x1034, 0 ) ); + Add( new GenericBuyInfo( typeof( Hammer ), 17, 20, 0x102A, 0 ) ); + Add( new GenericBuyInfo( typeof( MouldingPlane ), 11, 20, 0x102C, 0 ) ); + Add( new GenericBuyInfo( typeof( SmoothingPlane ), 10, 20, 0x1032, 0 ) ); + Add( new GenericBuyInfo( typeof( JointingPlane ), 11, 20, 0x1030, 0 ) ); + Add( new GenericBuyInfo( typeof( Drums ), 21, 20, 0xE9C, 0 ) ); + Add( new GenericBuyInfo( typeof( Tambourine ), 21, 20, 0xE9D, 0 ) ); + Add( new GenericBuyInfo( typeof( LapHarp ), 21, 20, 0xEB2, 0 ) ); + Add( new GenericBuyInfo( typeof( Lute ), 21, 20, 0xEB3, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( WoodenBox ), 7 ); + Add( typeof( SmallCrate ), 5 ); + Add( typeof( MediumCrate ), 6 ); + Add( typeof( LargeCrate ), 7 ); + Add( typeof( WoodenChest ), 15 ); + + Add( typeof( LargeTable ), 10 ); + Add( typeof( Nightstand ), 7 ); + Add( typeof( YewWoodTable ), 10 ); + + Add( typeof( Throne ), 24 ); + Add( typeof( WoodenThrone ), 6 ); + Add( typeof( Stool ), 6 ); + Add( typeof( FootStool ), 6 ); + + Add( typeof( FancyWoodenChairCushion ), 12 ); + Add( typeof( WoodenChairCushion ), 10 ); + Add( typeof( WoodenChair ), 8 ); + Add( typeof( BambooChair ), 6 ); + Add( typeof( WoodenBench ), 6 ); + + Add( typeof( Saw ), 9 ); + Add( typeof( Scorp ), 6 ); + Add( typeof( SmoothingPlane ), 6 ); + Add( typeof( DrawKnife ), 6 ); + Add( typeof( Froe ), 6 ); + Add( typeof( Hammer ), 14 ); + Add( typeof( Inshave ), 6 ); + Add( typeof( JointingPlane ), 6 ); + Add( typeof( MouldingPlane ), 6 ); + Add( typeof( DovetailSaw ), 7 ); + Add( typeof( Board ), 2 ); + Add( typeof( Axle ), 1 ); + + Add( typeof( Club ), 13 ); + + Add( typeof( Lute ), 10 ); + Add( typeof( LapHarp ), 10 ); + Add( typeof( Tambourine ), 10 ); + Add( typeof( Drums ), 10 ); + + Add( typeof( Log ), 1 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBCobbler.cs b/Scripts/Mobiles/Vendors/SBInfo/SBCobbler.cs new file mode 100644 index 000000000..cb8bbe86f --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBCobbler.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBCobbler : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBCobbler() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( ThighBoots ), 15, 20, 0x1711, Utility.RandomNeutralHue() ) ); + Add( new GenericBuyInfo( typeof( Shoes ), 8, 20, 0x170f, Utility.RandomNeutralHue() ) ); + Add( new GenericBuyInfo( typeof( Boots ), 10, 20, 0x170b, Utility.RandomNeutralHue() ) ); + Add( new GenericBuyInfo( typeof( Sandals ), 5, 20, 0x170d, Utility.RandomNeutralHue() ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Shoes ), 4 ); + Add( typeof( Boots ), 5 ); + Add( typeof( ThighBoots ), 7 ); + Add( typeof( Sandals ), 2 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBCook.cs b/Scripts/Mobiles/Vendors/SBInfo/SBCook.cs new file mode 100644 index 000000000..51be7e255 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBCook.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBCook : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBCook() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BreadLoaf ), 5, 20, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( BreadLoaf ), 5, 20, 0x103C, 0 ) ); + Add( new GenericBuyInfo( typeof( ApplePie ), 7, 20, 0x1041, 0 ) ); //OSI just has Pie, not Apple/Fruit/Meat + Add( new GenericBuyInfo( typeof( Cake ), 13, 20, 0x9E9, 0 ) ); + Add( new GenericBuyInfo( typeof( Muffins ), 3, 20, 0x9EA, 0 ) ); + + Add( new GenericBuyInfo( typeof( CheeseWheel ), 21, 10, 0x97E, 0 ) ); + Add( new GenericBuyInfo( typeof( CookedBird ), 17, 20, 0x9B7, 0 ) ); + Add( new GenericBuyInfo( typeof( LambLeg ), 8, 20, 0x160A, 0 ) ); + Add( new GenericBuyInfo( typeof( ChickenLeg ), 5, 20, 0x1608, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenBowlOfCarrots ), 3, 20, 0x15F9, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfCorn ), 3, 20, 0x15FA, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfLettuce ), 3, 20, 0x15FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfPeas ), 3, 20, 0x15FC, 0 ) ); + Add( new GenericBuyInfo( typeof( EmptyPewterBowl ), 2, 20, 0x15FD, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfCorn ), 3, 20, 0x15FE, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfLettuce ), 3, 20, 0x15FF, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPeas ), 3, 20, 0x1600, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPotatos ), 3, 20, 0x1601, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfStew ), 3, 20, 0x1604, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfTomatoSoup ), 3, 20, 0x1606, 0 ) ); + + Add( new GenericBuyInfo( typeof( RoastPig ), 106, 20, 0x9BB, 0 ) ); + Add( new GenericBuyInfo( typeof( SackFlour ), 3, 20, 0x1039, 0 ) ); + Add( new GenericBuyInfo( typeof( JarHoney ), 3, 20, 0x9EC, 0 ) ); + Add( new GenericBuyInfo( typeof( RollingPin ), 2, 20, 0x1043, 0 ) ); + Add( new GenericBuyInfo( typeof( FlourSifter ), 2, 20, 0x103E, 0 ) ); + Add( new GenericBuyInfo( "1044567", typeof( Skillet ), 3, 20, 0x97F, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( CheeseWheel ), 12 ); + Add( typeof( CookedBird ), 8 ); + Add( typeof( RoastPig ), 53 ); + Add( typeof( Cake ), 5 ); + Add( typeof( JarHoney ), 1 ); + Add( typeof( SackFlour ), 1 ); + Add( typeof( BreadLoaf ), 2 ); + Add( typeof( ChickenLeg ), 3 ); + Add( typeof( LambLeg ), 4 ); + Add( typeof( Skillet ), 1 ); + Add( typeof( FlourSifter ), 1 ); + Add( typeof( RollingPin ), 1 ); + Add( typeof( Muffins ), 1 ); + Add( typeof( ApplePie ), 3 ); + + Add( typeof( WoodenBowlOfCarrots ), 1 ); + Add( typeof( WoodenBowlOfCorn ), 1 ); + Add( typeof( WoodenBowlOfLettuce ), 1 ); + Add( typeof( WoodenBowlOfPeas ), 1 ); + Add( typeof( EmptyPewterBowl ), 1 ); + Add( typeof( PewterBowlOfCorn ), 1 ); + Add( typeof( PewterBowlOfLettuce ), 1 ); + Add( typeof( PewterBowlOfPeas ), 1 ); + Add( typeof( PewterBowlOfPotatos ), 1 ); + Add( typeof( WoodenBowlOfStew ), 1 ); + Add( typeof( WoodenBowlOfTomatoSoup ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBFarmer.cs b/Scripts/Mobiles/Vendors/SBInfo/SBFarmer.cs new file mode 100644 index 000000000..a923809bf --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBFarmer.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBFarmer : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFarmer() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Cabbage ), 5, 20, 0xC7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Cantaloupe ), 6, 20, 0xC79, 0 ) ); + Add( new GenericBuyInfo( typeof( Carrot ), 3, 20, 0xC78, 0 ) ); + Add( new GenericBuyInfo( typeof( HoneydewMelon ), 7, 20, 0xC74, 0 ) ); + Add( new GenericBuyInfo( typeof( Squash ), 3, 20, 0xC72, 0 ) ); + Add( new GenericBuyInfo( typeof( Lettuce ), 5, 20, 0xC70, 0 ) ); + Add( new GenericBuyInfo( typeof( Onion ), 3, 20, 0xC6D, 0 ) ); + Add( new GenericBuyInfo( typeof( Pumpkin ), 11, 20, 0xC6A, 0 ) ); + Add( new GenericBuyInfo( typeof( GreenGourd ), 3, 20, 0xC66, 0 ) ); + Add( new GenericBuyInfo( typeof( YellowGourd ), 3, 20, 0xC64, 0 ) ); + //Add( new GenericBuyInfo( typeof( Turnip ), 6, 20, XXXXXX, 0 ) ); + Add( new GenericBuyInfo( typeof( Watermelon ), 7, 20, 0xC5C, 0 ) ); + //Add( new GenericBuyInfo( typeof( EarOfCorn ), 3, 20, XXXXXX, 0 ) ); + Add( new GenericBuyInfo( typeof( Eggs ), 3, 20, 0x9B5, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Milk, 7, 20, 0x9AD, 0 ) ); + Add( new GenericBuyInfo( typeof( Peach ), 3, 20, 0x9D2, 0 ) ); + Add( new GenericBuyInfo( typeof( Pear ), 3, 20, 0x994, 0 ) ); + Add( new GenericBuyInfo( typeof( Lemon ), 3, 20, 0x1728, 0 ) ); + Add( new GenericBuyInfo( typeof( Lime ), 3, 20, 0x172A, 0 ) ); + Add( new GenericBuyInfo( typeof( Grapes ), 3, 20, 0x9D1, 0 ) ); + Add( new GenericBuyInfo( typeof( Apple ), 3, 20, 0x9D0, 0 ) ); + Add( new GenericBuyInfo( typeof( SheafOfHay ), 2, 20, 0xF36, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Pitcher ), 5 ); + Add( typeof( Eggs ), 1 ); + Add( typeof( Apple ), 1 ); + Add( typeof( Grapes ), 1 ); + Add( typeof( Watermelon ), 3 ); + Add( typeof( YellowGourd ), 1 ); + Add( typeof( GreenGourd ), 1 ); + Add( typeof( Pumpkin ), 5 ); + Add( typeof( Onion ), 1 ); + Add( typeof( Lettuce ), 2 ); + Add( typeof( Squash ), 1 ); + Add( typeof( Carrot ), 1 ); + Add( typeof( HoneydewMelon ), 3 ); + Add( typeof( Cantaloupe ), 3 ); + Add( typeof( Cabbage ), 2 ); + Add( typeof( Lemon ), 1 ); + Add( typeof( Lime ), 1 ); + Add( typeof( Peach ), 1 ); + Add( typeof( Pear ), 1 ); + Add( typeof( SheafOfHay ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBFisherman.cs b/Scripts/Mobiles/Vendors/SBInfo/SBFisherman.cs new file mode 100644 index 000000000..1acc31211 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBFisherman.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBFisherman : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFisherman() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( RawFishSteak ), 3, 20, 0x97A, 0 ) ); + //TODO: Add( new GenericBuyInfo( typeof( SmallFish ), 3, 20, 0xDD6, 0 ) ); + //TODO: Add( new GenericBuyInfo( typeof( SmallFish ), 3, 20, 0xDD7, 0 ) ); + Add( new GenericBuyInfo( typeof( Fish ), 3, 80, 0x9CC, 0 ) ); + Add( new GenericBuyInfo( typeof( Fish ), 3, 80, 0x9CD, 0 ) ); + Add( new GenericBuyInfo( typeof( Fish ), 3, 80, 0x9CE, 0 ) ); + Add( new GenericBuyInfo( typeof( Fish ), 3, 80, 0x9CF, 0 ) ); + Add( new GenericBuyInfo( typeof( FishingPole ), 15, 20, 0xDC0, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( RawFishSteak ), 1 ); + Add( typeof( Fish ), 1 ); + //TODO: Add( typeof( SmallFish ), 1 ); + Add( typeof( FishingPole ), 7 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBFortuneTeller.cs b/Scripts/Mobiles/Vendors/SBInfo/SBFortuneTeller.cs new file mode 100644 index 000000000..c353d089c --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBFortuneTeller.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBFortuneTeller : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFortuneTeller() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bandage ), 5, 20, 0xE21, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bandage ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBFurtrader.cs b/Scripts/Mobiles/Vendors/SBInfo/SBFurtrader.cs new file mode 100644 index 000000000..169c2dbdb --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBFurtrader.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBFurtrader : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBFurtrader() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Hides ), 3, 40, 0x1079, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Hides ), 2 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBGlassblower.cs b/Scripts/Mobiles/Vendors/SBInfo/SBGlassblower.cs new file mode 100644 index 000000000..5fce85aaa --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBGlassblower.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBGlassblower : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBGlassblower() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( RefreshPotion ), 15, 10, 0xF0B, 0 ) ); + Add( new GenericBuyInfo( typeof( AgilityPotion ), 15, 10, 0xF08, 0 ) ); + Add( new GenericBuyInfo( typeof( NightSightPotion ), 15, 10, 0xF06, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserHealPotion ), 15, 10, 0xF0C, 0 ) ); + Add( new GenericBuyInfo( typeof( StrengthPotion ), 15, 10, 0xF09, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserPoisonPotion ), 15, 10, 0xF0A, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserCurePotion ), 15, 10, 0xF07, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserExplosionPotion ), 21, 10, 0xF0D, 0 ) ); + + Add( new GenericBuyInfo( typeof( MortarPestle ), 8, 10, 0xE9B, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlackPearl ), 5, 20, 0xF7A, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 20, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( SulfurousAsh ), 3, 20, 0xF8C, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bottle ), 5, 100, 0xF0E, 0 ) ); + + Add( new GenericBuyInfo( typeof( HeatingStand ), 2, 100, 0x1849, 0 ) ); + + Add( new GenericBuyInfo( "Crafting Glass With Glassblowing", typeof( GlassblowingBook ), 10637, 30, 0xFF4, 0 ) ); + Add( new GenericBuyInfo( "Finding Glass-Quality Sand", typeof( SandMiningBook ), 10637, 30, 0xFF4, 0 ) ); + Add( new GenericBuyInfo( "1044608", typeof( Blowpipe ), 21, 100, 0xE8A, 0x3B9 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BlackPearl ), 3 ); + Add( typeof( Bloodmoss ), 3 ); + Add( typeof( MandrakeRoot ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( SpidersSilk ), 2 ); + Add( typeof( SulfurousAsh ), 2 ); + Add( typeof( Bottle ), 3 ); + Add( typeof( MortarPestle ), 4 ); + + Add( typeof( NightSightPotion ), 7 ); + Add( typeof( AgilityPotion ), 7 ); + Add( typeof( StrengthPotion ), 7 ); + Add( typeof( RefreshPotion ), 7 ); + Add( typeof( LesserCurePotion ), 7 ); + Add( typeof( LesserHealPotion ), 7 ); + Add( typeof( LesserPoisonPotion ), 7 ); + Add( typeof( LesserExplosionPotion ), 10 ); + + Add( typeof( GlassblowingBook ), 5000 ); + Add( typeof( SandMiningBook ), 5000 ); + Add( typeof( Blowpipe ), 10 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBHairStylist.cs b/Scripts/Mobiles/Vendors/SBInfo/SBHairStylist.cs new file mode 100644 index 000000000..2c0b51910 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBHairStylist.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBHairStylist : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBHairStylist() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( "special beard dye", typeof( SpecialBeardDye ), 500000, 20, 0xE26, 0 ) ); + Add( new GenericBuyInfo( "special hair dye", typeof( SpecialHairDye ), 500000, 20, 0xE26, 0 ) ); + Add( new GenericBuyInfo( "1041060", typeof( HairDye ), 60, 20, 0xEFF, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( HairDye ), 30 ); + Add( typeof( SpecialBeardDye ), 250000 ); + Add( typeof( SpecialHairDye ), 250000 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBHealer.cs b/Scripts/Mobiles/Vendors/SBInfo/SBHealer.cs new file mode 100644 index 000000000..fd8b29e0b --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBHealer.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBHealer : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBHealer() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bandage ), 5, 20, 0xE21, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserHealPotion ), 15, 20, 0xF0C, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( RefreshPotion ), 15, 20, 0xF0B, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bandage ), 1 ); + Add( typeof( LesserHealPotion ), 7 ); + Add( typeof( RefreshPotion ), 7 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBHerbalist.cs b/Scripts/Mobiles/Vendors/SBInfo/SBHerbalist.cs new file mode 100644 index 000000000..f9dc4e075 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBHerbalist.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBHerbalist : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBHerbalist() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( MortarPestle ), 8, 20, 0xE9B, 0 ) ); + Add( new GenericBuyInfo( typeof( Bottle ), 5, 20, 0xF0E, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bloodmoss ), 3 ); + Add( typeof( MandrakeRoot ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( Bottle ), 3 ); + Add( typeof( MortarPestle ), 4 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBHolyMage.cs b/Scripts/Mobiles/Vendors/SBInfo/SBHolyMage.cs new file mode 100644 index 000000000..95f75e4e3 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBHolyMage.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBHolyMage : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBHolyMage() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Spellbook ), 18, 10, 0xEFA, 0 ) ); + Add( new GenericBuyInfo( typeof( ScribesPen ), 8, 10, 0xFBF, 0 ) ); + Add( new GenericBuyInfo( typeof( BlankScroll ), 5, 20, 0x0E34, 0 ) ); + + Add( new GenericBuyInfo( "1041072", typeof( MagicWizardsHat ), 11, 10, 0x1718, Utility.RandomDyedHue() ) ); + + Add( new GenericBuyInfo( typeof( RecallRune ), 15, 10, 0x1f14, 0 ) ); + + Add( new GenericBuyInfo( typeof( RefreshPotion ), 15, 20, 0xF0B, 0 ) ); + Add( new GenericBuyInfo( typeof( AgilityPotion ), 15, 20, 0xF08, 0 ) ); + Add( new GenericBuyInfo( typeof( NightSightPotion ), 15, 20, 0xF06, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserHealPotion ), 15, 20, 0xF0C, 0 ) ); + Add( new GenericBuyInfo( typeof( StrengthPotion ), 15, 20, 0xF09, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserCurePotion ), 15, 20, 0xF07, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlackPearl ), 5, 20, 0xF7A, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 20, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( SulfurousAsh ), 3, 20, 0xF8C, 0 ) ); + + Type[] types = Loot.RegularScrollTypes; + + for ( int i = 0; i < types.Length && i < 8; ++i ) + { + int itemID = 0x1F2E + i; + + if ( i == 6 ) + itemID = 0x1F2D; + else if ( i > 6 ) + --itemID; + + Add( new GenericBuyInfo( types[i], 12 + ((i / 8) * 10), 20, itemID, 0 ) ); + } + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BlackPearl ), 3 ); + Add( typeof( Bloodmoss ), 3 ); + Add( typeof( MandrakeRoot ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( SpidersSilk ), 2 ); + Add( typeof( SulfurousAsh ), 2 ); + Add( typeof( RecallRune ), 8 ); + Add( typeof( Spellbook ), 9 ); + Add( typeof( BlankScroll ), 3 ); + + Add( typeof( NightSightPotion ), 7 ); + Add( typeof( AgilityPotion ), 7 ); + Add( typeof( StrengthPotion ), 7 ); + Add( typeof( RefreshPotion ), 7 ); + Add( typeof( LesserCurePotion ), 7 ); + Add( typeof( LesserHealPotion ), 7 ); + + Type[] types = Loot.RegularScrollTypes; + + for ( int i = 0; i < types.Length; ++i ) + Add( types[i], ((i / 8) + 2) * 5 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBHouseDeed.cs b/Scripts/Mobiles/Vendors/SBInfo/SBHouseDeed.cs new file mode 100644 index 000000000..0a2ebbb3e --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBHouseDeed.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections; +using Server.Multis.Deeds; + +namespace Server.Mobiles +{ + public class SBHouseDeed: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBHouseDeed() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( "deed to a stone-and-plaster house", typeof( StonePlasterHouseDeed ), 43800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a field stone house", typeof( FieldStoneHouseDeed ), 43800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a small brick house", typeof( SmallBrickHouseDeed), 43800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a wooden house", typeof( WoodHouseDeed ), 43800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a wood-and-plaster house", typeof( WoodPlasterHouseDeed ), 43800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a thatched-roof cottage", typeof( ThatchedRoofCottageDeed ), 43800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a brick house", typeof( BrickHouseDeed ), 144500, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a two-story wood-and-plaster house", typeof( TwoStoryWoodPlasterHouseDeed ), 192400, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a tower", typeof( TowerDeed ), 433200, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a small stone keep", typeof( KeepDeed ), 665200, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a castle", typeof( CastleDeed ), 1022800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a large house with patio", typeof( LargePatioDeed ), 152800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a marble house with patio", typeof( LargeMarbleDeed ), 192000, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a small stone tower", typeof( SmallTowerDeed ), 88500, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a two story log cabin", typeof( LogCabinDeed ), 97800, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a sandstone house with patio", typeof( SandstonePatioDeed ), 90900, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a two story villa", typeof( VillaDeed ), 136500, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a small stone workshop", typeof( StoneWorkshopDeed ), 60600, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "deed to a small marble workshop", typeof( MarbleWorkshopDeed ), 63000, 20, 0x14F0, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + /*Add( typeof( StonePlasterHouseDeed ), 43800 ); + Add( typeof( FieldStoneHouseDeed ), 43800 ); + Add( typeof( SmallBrickHouseDeed ), 43800 ); + Add( typeof( WoodHouseDeed ), 43800 ); + Add( typeof( WoodPlasterHouseDeed ), 43800 ); + Add( typeof( ThatchedRoofCottageDeed ), 43800 ); + Add( typeof( BrickHouseDeed ), 144500 ); + Add( typeof( TwoStoryWoodPlasterHouseDeed ), 192400 ); + Add( typeof( TowerDeed ), 433200 ); + Add( typeof( KeepDeed ), 665200 ); + Add( typeof( CastleDeed ), 1022800 ); + Add( typeof( LargePatioDeed ), 152800 ); + Add( typeof( LargeMarbleDeed ), 192800 ); + Add( typeof( SmallTowerDeed ), 88500 ); + Add( typeof( LogCabinDeed ), 97800 ); + Add( typeof( SandstonePatioDeed ), 90900 ); + Add( typeof( VillaDeed ), 136500 ); + Add( typeof( StoneWorkshopDeed ), 60600 ); + Add( typeof( MarbleWorkshopDeed ), 60300 ); + Add( typeof( SmallBrickHouseDeed ), 43800 );*/ + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBInfo.cs b/Scripts/Mobiles/Vendors/SBInfo/SBInfo.cs new file mode 100644 index 000000000..9c5252479 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBInfo.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public abstract class SBInfo + { + public SBInfo() + { + } + + public abstract IShopSellInfo SellInfo { get; } + public abstract ArrayList BuyInfo { get; } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBInnKeeper.cs b/Scripts/Mobiles/Vendors/SBInfo/SBInnKeeper.cs new file mode 100644 index 000000000..cd1397ac3 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBInnKeeper.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBInnKeeper : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBInnKeeper() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Ale, 7, 20, 0x99F, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Wine, 7, 20, 0x9C7, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Liquor, 7, 20, 0x99B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Jug ), BeverageType.Cider, 13, 20, 0x9C8, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Milk, 7, 20, 0x9F0, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Ale, 11, 20, 0x1F95, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Cider, 11, 20, 0x1F97, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Liquor, 11, 20, 0x1F99, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Wine, 11, 20, 0x1F9B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Water, 11, 20, 0x1F9D, 0 ) ); + + Add( new GenericBuyInfo( typeof( BreadLoaf ), 6, 10, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( CheeseWheel ), 21, 10, 0x97E, 0 ) ); + Add( new GenericBuyInfo( typeof( CookedBird ), 17, 20, 0x9B7, 0 ) ); + Add( new GenericBuyInfo( typeof( LambLeg ), 8, 20, 0x160A, 0 ) ); + Add( new GenericBuyInfo( typeof( ChickenLeg ), 5, 20, 0x1608, 0 ) ); + Add( new GenericBuyInfo( typeof( Ribs ), 7, 20, 0x9F2, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenBowlOfCarrots ), 3, 20, 0x15F9, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfCorn ), 3, 20, 0x15FA, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfLettuce ), 3, 20, 0x15FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfPeas ), 3, 20, 0x15FC, 0 ) ); + Add( new GenericBuyInfo( typeof( EmptyPewterBowl ), 2, 20, 0x15FD, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfCorn ), 3, 20, 0x15FE, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfLettuce ), 3, 20, 0x15FF, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPeas ), 3, 20, 0x1600, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPotatos ), 3, 20, 0x1601, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfStew ), 3, 20, 0x1604, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfTomatoSoup ), 3, 20, 0x1606, 0 ) ); + + Add( new GenericBuyInfo( typeof( ApplePie ), 7, 20, 0x1041, 0 ) ); //OSI just has Pie, not Apple/Fruit/Meat + + Add( new GenericBuyInfo( typeof( Peach ), 3, 20, 0x9D2, 0 ) ); + Add( new GenericBuyInfo( typeof( Pear ), 3, 20, 0x994, 0 ) ); + Add( new GenericBuyInfo( typeof( Grapes ), 3, 20, 0x9D1, 0 ) ); + Add( new GenericBuyInfo( typeof( Apple ), 3, 20, 0x9D0, 0 ) ); + Add( new GenericBuyInfo( typeof( Banana ), 2, 20, 0x171F, 0 ) ); + Add( new GenericBuyInfo( typeof( Torch ), 7, 20, 0xF6B, 0 ) ); + Add( new GenericBuyInfo( typeof( Candle ), 6, 20, 0xA28, 0 ) ); + Add( new GenericBuyInfo( typeof( Beeswax ), 1, 20, 0x1422, 0 ) ); + + Add( new GenericBuyInfo( typeof( Backpack ), 15, 20, 0x9B2, 0 ) ); + Add( new GenericBuyInfo( "1016450", typeof( Chessboard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( "1016449", typeof( CheckerBoard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( typeof( Backgammon ), 2, 20, 0xE1C, 0 ) ); + Add( new GenericBuyInfo( typeof( Dices ), 2, 20, 0xFA7, 0 ) ); + Add( new GenericBuyInfo( "1041243", typeof( ContractOfEmployment ), 1252, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "a barkeep contract", typeof( BarkeepContract ), 1252, 20, 0x14F0, 0 ) ); + + if ( Multis.BaseHouse.NewVendorSystem ) + Add( new GenericBuyInfo( "1062332", typeof( VendorRentalContract ), 1252, 20, 0x14F0, 0x672 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BeverageBottle ), 3 ); + Add( typeof( Jug ), 6 ); + Add( typeof( Pitcher ), 5 ); + Add( typeof( GlassMug ), 1 ); + Add( typeof( BreadLoaf ), 3 ); + Add( typeof( CheeseWheel ), 12 ); + Add( typeof( Ribs ), 6 ); + Add( typeof( Peach ), 1 ); + Add( typeof( Pear ), 1 ); + Add( typeof( Grapes ), 1 ); + Add( typeof( Apple ), 1 ); + Add( typeof( Banana ), 1 ); + Add( typeof( Torch ), 3 ); + Add( typeof( Candle ), 3 ); + Add( typeof( Chessboard ), 1 ); + Add( typeof( CheckerBoard ), 1 ); + Add( typeof( Backgammon ), 1 ); + Add( typeof( Dices ), 1 ); + Add( typeof( ContractOfEmployment ), 626 ); + Add( typeof( Beeswax ), 1 ); + Add( typeof( WoodenBowlOfCarrots ), 1 ); + Add( typeof( WoodenBowlOfCorn ), 1 ); + Add( typeof( WoodenBowlOfLettuce ), 1 ); + Add( typeof( WoodenBowlOfPeas ), 1 ); + Add( typeof( EmptyPewterBowl ), 1 ); + Add( typeof( PewterBowlOfCorn ), 1 ); + Add( typeof( PewterBowlOfLettuce ), 1 ); + Add( typeof( PewterBowlOfPeas ), 1 ); + Add( typeof( PewterBowlOfPotatos ), 1 ); + Add( typeof( WoodenBowlOfStew ), 1 ); + Add( typeof( WoodenBowlOfTomatoSoup ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBJewel.cs b/Scripts/Mobiles/Vendors/SBInfo/SBJewel.cs new file mode 100644 index 000000000..a34d44ba0 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBJewel.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBJewel: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBJewel() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( GoldRing ), 27, 20, 0x108A, 0 ) ); + Add( new GenericBuyInfo( typeof( Necklace ), 26, 20, 0x1085, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldNecklace ), 27, 20, 0x1088, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldBeadNecklace ), 27, 20, 0x1089, 0 ) ); + Add( new GenericBuyInfo( typeof( Beads ), 27, 20, 0x108B, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldBracelet ), 27, 20, 0x1086, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldEarrings ), 27, 20, 0x1087, 0 ) ); + + Add( new GenericBuyInfo( "1060740", typeof( BroadcastCrystal ), 68, 20, 0x1ED0, 0, new object[] { 500 } ) ); // 500 charges + Add( new GenericBuyInfo( "1060740", typeof( BroadcastCrystal ), 131, 20, 0x1ED0, 0, new object[] { 1000 } ) ); // 1000 charges + Add( new GenericBuyInfo( "1060740", typeof( BroadcastCrystal ), 256, 20, 0x1ED0, 0, new object[] { 2000 } ) ); // 2000 charges + + Add( new GenericBuyInfo( "1060740", typeof( ReceiverCrystal ), 6, 20, 0x1ED0, 0 ) ); + + Add( new GenericBuyInfo( typeof( StarSapphire ), 125, 20, 0xF21, 0 ) ); + Add( new GenericBuyInfo( typeof( Emerald ), 100, 20, 0xF10, 0 ) ); + Add( new GenericBuyInfo( typeof( Sapphire ), 100, 20, 0xF19, 0 ) ); + Add( new GenericBuyInfo( typeof( Ruby ), 75, 20, 0xF13, 0 ) ); + Add( new GenericBuyInfo( typeof( Citrine ), 50, 20, 0xF15, 0 ) ); + Add( new GenericBuyInfo( typeof( Amethyst ), 100, 20, 0xF16, 0 ) ); + Add( new GenericBuyInfo( typeof( Tourmaline ), 75, 20, 0xF2D, 0 ) ); + Add( new GenericBuyInfo( typeof( Amber ), 50, 20, 0xF25, 0 ) ); + Add( new GenericBuyInfo( typeof( Diamond ), 200, 20, 0xF26, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Amber ), 25 ); + Add( typeof( Amethyst ), 50 ); + Add( typeof( Citrine ), 25 ); + Add( typeof( Diamond ), 100 ); + Add( typeof( Emerald ), 50 ); + Add( typeof( Ruby ), 37 ); + Add( typeof( Sapphire ), 50 ); + Add( typeof( StarSapphire ), 62 ); + Add( typeof( Tourmaline ), 47 ); + Add( typeof( GoldRing ), 13 ); + Add( typeof( SilverRing ), 10 ); + Add( typeof( Necklace ), 13 ); + Add( typeof( GoldNecklace ), 13 ); + Add( typeof( GoldBeadNecklace ), 13 ); + Add( typeof( SilverNecklace ), 10 ); + Add( typeof( SilverBeadNecklace ), 10 ); + Add( typeof( Beads ), 13 ); + Add( typeof( GoldBracelet ), 13 ); + Add( typeof( SilverBracelet ), 10 ); + Add( typeof( GoldEarrings ), 13 ); + Add( typeof( SilverEarrings ), 10 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBKeeperOfChivalry.cs b/Scripts/Mobiles/Vendors/SBInfo/SBKeeperOfChivalry.cs new file mode 100644 index 000000000..0e31ba9d3 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBKeeperOfChivalry.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBKeeperOfChivalry : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBKeeperOfChivalry() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BookOfChivalry ), 140, 20, 0x2252, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBLeatherWorker.cs b/Scripts/Mobiles/Vendors/SBInfo/SBLeatherWorker.cs new file mode 100644 index 000000000..299bbdfdf --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBLeatherWorker.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBLeatherWorker: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBLeatherWorker() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Hides ), 4, 999, 0x1078, 0 ) ); + Add( new GenericBuyInfo( typeof( ThighBoots ), 56, 10, 0x1711, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Hides ), 2 ); + Add( typeof( ThighBoots ), 28 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBMage.cs b/Scripts/Mobiles/Vendors/SBInfo/SBMage.cs new file mode 100644 index 000000000..51bb72d0c --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBMage.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBMage : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBMage() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Spellbook ), 18, 10, 0xEFA, 0 ) ); + + if ( Core.AOS ) + Add( new GenericBuyInfo( typeof( NecromancerSpellbook ), 115, 10, 0x2253, 0 ) ); + + Add( new GenericBuyInfo( typeof( ScribesPen ), 8, 10, 0xFBF, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlankScroll ), 5, 20, 0x0E34, 0 ) ); + + Add( new GenericBuyInfo( "1041072", typeof( MagicWizardsHat ), 11, 10, 0x1718, Utility.RandomDyedHue() ) ); + + Add( new GenericBuyInfo( typeof( RecallRune ), 15, 10, 0x1F14, 0 ) ); + + Add( new GenericBuyInfo( typeof( RefreshPotion ), 15, 10, 0xF0B, 0 ) ); + Add( new GenericBuyInfo( typeof( AgilityPotion ), 15, 10, 0xF08, 0 ) ); + Add( new GenericBuyInfo( typeof( NightSightPotion ), 15, 10, 0xF06, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserHealPotion ), 15, 10, 0xF0C, 0 ) ); + Add( new GenericBuyInfo( typeof( StrengthPotion ), 15, 10, 0xF09, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserPoisonPotion ), 15, 10, 0xF0A, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserCurePotion ), 15, 10, 0xF07, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserExplosionPotion ), 21, 10, 0xF0D, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlackPearl ), 5, 20, 0xF7A, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 20, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 20, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 20, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 20, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( SulfurousAsh ), 3, 20, 0xF8C, 0 ) ); + + if ( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( BatWing ), 3, 999, 0xF78, 0 ) ); + Add( new GenericBuyInfo( typeof( DaemonBlood ), 6, 999, 0xF7D, 0 ) ); + Add( new GenericBuyInfo( typeof( PigIron ), 5, 999, 0xF8A, 0 ) ); + Add( new GenericBuyInfo( typeof( NoxCrystal ), 6, 999, 0xF8E, 0 ) ); + Add( new GenericBuyInfo( typeof( GraveDust ), 3, 999, 0xF8F, 0 ) ); + } + + Type[] types = Loot.RegularScrollTypes; + + int circles = 3; + + for ( int i = 0; i < circles*8 && i < types.Length; ++i ) + { + int itemID = 0x1F2E + i; + + if ( i == 6 ) + itemID = 0x1F2D; + else if ( i > 6 ) + --itemID; + + Add( new GenericBuyInfo( types[i], 12 + ((i / 8) * 10), 20, itemID, 0 ) ); + } + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( WizardsHat ), 15 ); + Add( typeof( BlackPearl ), 3 ); + Add( typeof( Bloodmoss ),4 ); + Add( typeof( MandrakeRoot ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( SpidersSilk ), 2 ); + Add( typeof( SulfurousAsh ), 2 ); + Add( typeof( RecallRune ), 13 ); + Add( typeof( Spellbook ), 25 ); + + Type[] types = Loot.RegularScrollTypes; + + for ( int i = 0; i < types.Length; ++i ) + Add( types[i], ((i / 8) + 2) * 5 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBMapmaker.cs b/Scripts/Mobiles/Vendors/SBInfo/SBMapmaker.cs new file mode 100644 index 000000000..1e5083596 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBMapmaker.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBMapmaker : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBMapmaker() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BlankMap ), 5, 40, 0x14EC, 0 ) ); + Add( new GenericBuyInfo( typeof( MapmakersPen ), 8, 20, 0x0FBF, 0 ) ); + Add( new GenericBuyInfo( typeof( BlankScroll ), 12, 40, 0xEF3, 0 ) ); + + for ( int i = 0; i < PresetMapEntry.Table.Length; ++i ) + Add( new PresetMapBuyInfo( PresetMapEntry.Table[i], Utility.RandomMinMax( 7, 10 ), 20 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BlankScroll ), 6 ); + Add( typeof( MapmakersPen ), 4 ); + Add( typeof( BlankMap ), 2 ); + Add( typeof( CityMap ), 3 ); + Add( typeof( LocalMap ), 3 ); + Add( typeof( WorldMap ), 3 ); + Add( typeof( PresetMapEntry ), 3 ); + //TODO: Buy back maps that the mapmaker sells!!! + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBMiller.cs b/Scripts/Mobiles/Vendors/SBInfo/SBMiller.cs new file mode 100644 index 000000000..6289993db --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBMiller.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBMiller : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBMiller() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( SackFlour ), 3, 20, 0x1039, 0 ) ); + Add( new GenericBuyInfo( typeof( SheafOfHay ), 2, 20, 0xF36, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( SackFlour ), 1 ); + Add( typeof( SheafOfHay ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBMiner.cs b/Scripts/Mobiles/Vendors/SBInfo/SBMiner.cs new file mode 100644 index 000000000..7cfa6da98 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBMiner.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBMiner: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBMiner() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bag ), 6, 20, 0xE76, 0 ) ); + Add( new GenericBuyInfo( typeof( Candle ), 6, 10, 0xA28, 0 ) ); + Add( new GenericBuyInfo( typeof( Torch ), 8, 10, 0xF6B, 0 ) ); + Add( new GenericBuyInfo( typeof( Lantern ), 2, 10, 0xA25, 0 ) ); + //Add( new GenericBuyInfo( typeof( OilFlask ), 8, 10, 0x####, 0 ) ); + Add( new GenericBuyInfo( typeof( Pickaxe ), 25, 10, 0xE86, 0 ) ); + Add( new GenericBuyInfo( typeof( Shovel ), 12, 10, 0xF39, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Pickaxe ), 12 ); + Add( typeof( Shovel ), 6 ); + Add( typeof( Lantern ), 1 ); + //Add( typeof( OilFlask ), 4 ); + Add( typeof( Torch ), 3 ); + Add( typeof( Bag ), 3 ); + Add( typeof( Candle ), 3 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBPlayerBarkeeper.cs b/Scripts/Mobiles/Vendors/SBInfo/SBPlayerBarkeeper.cs new file mode 100644 index 000000000..cc5b3eb6d --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBPlayerBarkeeper.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBPlayerBarkeeper : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBPlayerBarkeeper() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Ale, 7, 20, 0x99F, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Wine, 7, 20, 0x9C7, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Liquor, 7, 20, 0x99B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Jug ), BeverageType.Cider, 13, 20, 0x9C8, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Milk, 7, 20, 0x9F0, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Ale, 11, 20, 0x1F95, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Cider, 11, 20, 0x1F97, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Liquor, 11, 20, 0x1F99, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Wine, 11, 20, 0x1F9B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Water, 11, 20, 0x1F9D, 0 ) ); + // TODO: pizza + // TODO: bowl of *, tomato soup + Add( new GenericBuyInfo( "1016450", typeof( Chessboard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( "1016449", typeof( CheckerBoard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( typeof( Backgammon ), 2, 20, 0xE1C, 0 ) ); + Add( new GenericBuyInfo( typeof( Dices ), 2, 20, 0xFA7, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBProvisioner.cs b/Scripts/Mobiles/Vendors/SBInfo/SBProvisioner.cs new file mode 100644 index 000000000..bc7230fb2 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBProvisioner.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Guilds; + +namespace Server.Mobiles +{ + public class SBProvisioner : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBProvisioner() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( "1060834", typeof( Engines.Plants.PlantBowl ), 2, 20, 0x15FD, 0 ) ); + + Add( new GenericBuyInfo( typeof( Arrow ), 2, 20, 0xF3F, 0 ) ); + Add( new GenericBuyInfo( typeof( Bolt ), 5, 20, 0x1BFB, 0 ) ); + + Add( new GenericBuyInfo( typeof( Backpack ), 15, 20, 0x9B2, 0 ) ); + Add( new GenericBuyInfo( typeof( Pouch ), 6, 20, 0xE79, 0 ) ); + Add( new GenericBuyInfo( typeof( Bag ), 6, 20, 0xE76, 0 ) ); + + Add( new GenericBuyInfo( typeof( Candle ), 6, 20, 0xA28, 0 ) ); + Add( new GenericBuyInfo( typeof( Torch ), 8, 20, 0xF6B, 0 ) ); + Add( new GenericBuyInfo( typeof( Lantern ), 2, 20, 0xA25, 0 ) ); + + //TODO: Oil Flask @ 8GP + + Add( new GenericBuyInfo( typeof( Lockpick ), 12, 20, 0x14FC, 0 ) ); + + Add( new GenericBuyInfo( typeof( FloppyHat ), 7, 20, 0x1713, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( WideBrimHat ), 8, 20, 0x1714, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( Cap ), 10, 20, 0x1715, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( TallStrawHat ), 8, 20, 0x1716, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( StrawHat ), 7, 20, 0x1717, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( WizardsHat ), 11, 20, 0x1718, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( LeatherCap ), 10, 20, 0x1DB9, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( FeatheredHat ), 10, 20, 0x171A, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( TricorneHat ), 8, 20, 0x171B, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( Bandana ), 6, 20, 0x1540, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( SkullCap ), 7, 20, 0x1544, Utility.RandomDyedHue() ) ); + + Add( new GenericBuyInfo( typeof( BreadLoaf ), 6, 10, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( LambLeg ), 8, 20, 0x160A, 0 ) ); + Add( new GenericBuyInfo( typeof( ChickenLeg ), 5, 20, 0x1608, 0 ) ); + Add( new GenericBuyInfo( typeof( CookedBird ), 17, 20, 0x9B7, 0 ) ); + + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Ale, 7, 20, 0x99F, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Wine, 7, 20, 0x9C7, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Liquor, 7, 20, 0x99B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Jug ), BeverageType.Cider, 13, 20, 0x9C8, 0 ) ); + + Add( new GenericBuyInfo( typeof( Pear ), 3, 20, 0x994, 0 ) ); + Add( new GenericBuyInfo( typeof( Apple ), 3, 20, 0x9D0, 0 ) ); + + Add( new GenericBuyInfo( typeof( Beeswax ), 1, 20, 0x1422, 0 ) ); + + Add( new GenericBuyInfo( typeof( Garlic ), 3, 20, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 20, 0xF85, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bottle ), 5, 20, 0xF0E, 0 ) ); + + Add( new GenericBuyInfo( typeof( RedBook ), 15, 20, 0xFF1, 0 ) ); + Add( new GenericBuyInfo( typeof( BlueBook ), 15, 20, 0xFF2, 0 ) ); + Add( new GenericBuyInfo( typeof( TanBook ), 15, 20, 0xFF0, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenBox ), 14, 20, 0xE7D, 0 ) ); + Add( new GenericBuyInfo( typeof( Key ), 2, 20, 0x100E, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bedroll ), 5, 20, 0xA59, 0 ) ); + Add( new GenericBuyInfo( typeof( Kindling ), 2, 20, 0xDE1, 0 ) ); + + Add( new GenericBuyInfo( "1041205", typeof( Multis.SmallBoatDeed ), 10177, 20, 0x14F2, 0 ) ); + + Add( new GenericBuyInfo( "1041060", typeof( HairDye ), 60, 20, 0xEFF, 0 ) ); + + Add( new GenericBuyInfo( "1016450", typeof( Chessboard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( "1016449", typeof( CheckerBoard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( typeof( Backgammon ), 2, 20, 0xE1C, 0 ) ); + if ( Core.AOS ) + Add( new GenericBuyInfo( typeof( Engines.Mahjong.MahjongGame ), 6, 20, 0xFAA, 0 ) ); + Add( new GenericBuyInfo( typeof( Dices ), 2, 20, 0xFA7, 0 ) ); + + if ( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( SmallBagBall ), 3, 20, 0x2256, 0 ) ); + Add( new GenericBuyInfo( typeof( LargeBagBall ), 3, 20, 0x2257, 0 ) ); + } + + if( !Guild.NewGuildSystem ) + Add( new GenericBuyInfo( "1041055", typeof( GuildDeed ), 12450, 20, 0x14F0, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Arrow ), 1 ); + Add( typeof( Bolt ), 2 ); + Add( typeof( Backpack ), 7 ); + Add( typeof( Pouch ), 3 ); + Add( typeof( Bag ), 3 ); + Add( typeof( Candle ), 3 ); + Add( typeof( Torch ), 4 ); + Add( typeof( Lantern ), 1 ); + Add( typeof( Lockpick ), 6 ); + Add( typeof( FloppyHat ), 3 ); + Add( typeof( WideBrimHat ), 4 ); + Add( typeof( Cap ), 5 ); + Add( typeof( TallStrawHat ), 4 ); + Add( typeof( StrawHat ), 3 ); + Add( typeof( WizardsHat ), 5 ); + Add( typeof( LeatherCap ), 5 ); + Add( typeof( FeatheredHat ), 5 ); + Add( typeof( TricorneHat ), 4 ); + Add( typeof( Bandana ), 3 ); + Add( typeof( SkullCap ), 3 ); + Add( typeof( Bottle ), 3 ); + Add( typeof( RedBook ), 7 ); + Add( typeof( BlueBook ), 7 ); + Add( typeof( TanBook ), 7 ); + Add( typeof( WoodenBox ), 7 ); + Add( typeof( Kindling ), 1 ); + Add( typeof( HairDye ), 30 ); + Add( typeof( Chessboard ), 1 ); + Add( typeof( CheckerBoard ), 1 ); + Add( typeof( Backgammon ), 1 ); + Add( typeof( Dices ), 1 ); + + Add( typeof( Beeswax ), 1 ); + + Add( typeof( Amber ), 25 ); + Add( typeof( Amethyst ), 50 ); + Add( typeof( Citrine ), 25 ); + Add( typeof( Diamond ), 100 ); + Add( typeof( Emerald ), 50 ); + Add( typeof( Ruby ), 37 ); + Add( typeof( Sapphire ), 50 ); + Add( typeof( StarSapphire ), 62 ); + Add( typeof( Tourmaline ), 47 ); + Add( typeof( GoldRing ), 13 ); + Add( typeof( SilverRing ), 10 ); + Add( typeof( Necklace ), 13 ); + Add( typeof( GoldNecklace ), 13 ); + Add( typeof( GoldBeadNecklace ), 13 ); + Add( typeof( SilverNecklace ), 10 ); + Add( typeof( SilverBeadNecklace ), 10 ); + Add( typeof( Beads ), 13 ); + Add( typeof( GoldBracelet ), 13 ); + Add( typeof( SilverBracelet ), 10 ); + Add( typeof( GoldEarrings ), 13 ); + Add( typeof( SilverEarrings ), 10 ); + + if( !Guild.NewGuildSystem ) + Add( typeof( GuildDeed ), 6225 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBRancher.cs b/Scripts/Mobiles/Vendors/SBInfo/SBRancher.cs new file mode 100644 index 000000000..266383568 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBRancher.cs @@ -0,0 +1,35 @@ + +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBRancher : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBRancher() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new AnimalBuyInfo( 1, typeof( PackHorse ), 631, 10, 291, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBRanger.cs b/Scripts/Mobiles/Vendors/SBInfo/SBRanger.cs new file mode 100644 index 000000000..c9e86eec9 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBRanger.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBRanger : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBRanger() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new AnimalBuyInfo( 1, typeof( Cat ), 138, 20, 201, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Dog ), 181, 20, 217, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( PackLlama ), 491, 20, 292, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( PackHorse ), 606, 20, 291, 0 ) ); + Add( new GenericBuyInfo( typeof( Bandage ), 5, 20, 0xE21, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBRealEstateBroker.cs b/Scripts/Mobiles/Vendors/SBInfo/SBRealEstateBroker.cs new file mode 100644 index 000000000..2dbbc5313 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBRealEstateBroker.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBRealEstateBroker : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBRealEstateBroker() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BlankScroll ), 5, 20, 0x0E34, 0 ) ); + Add( new GenericBuyInfo( typeof( ScribesPen ), 8, 20, 0xFBF, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( ScribesPen ), 4 ); + Add( typeof( BlankScroll ), 2 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBSECook.cs b/Scripts/Mobiles/Vendors/SBInfo/SBSECook.cs new file mode 100644 index 000000000..f695e89d2 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBSECook.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSECook: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSECook() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E8, 0 ) ); + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E9, 0 ) ); + Add( new GenericBuyInfo( typeof( SushiRolls ), 3, 20, 0x283E, 0 ) ); + Add( new GenericBuyInfo( typeof( SushiPlatter ), 3, 20, 0x2840, 0 ) ); + Add( new GenericBuyInfo( typeof( GreenTea ), 3, 20, 0x284C, 0 ) ); + Add( new GenericBuyInfo( typeof( MisoSoup ), 3, 20, 0x284D, 0 ) ); + Add( new GenericBuyInfo( typeof( WhiteMisoSoup ), 3, 20, 0x284E, 0 ) ); + Add( new GenericBuyInfo( typeof( RedMisoSoup ), 3, 20, 0x284F, 0 ) ); + Add( new GenericBuyInfo( typeof( AwaseMisoSoup ), 3, 20, 0x2850, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2836, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2837, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Wasabi ), 1 ); + Add( typeof( BentoBox ), 3 ); + Add( typeof( GreenTea ), 1 ); + Add( typeof( SushiRolls ), 1 ); + Add( typeof( SushiPlatter ), 2 ); + Add( typeof( MisoSoup ), 1 ); + Add( typeof( RedMisoSoup ), 1 ); + Add( typeof( WhiteMisoSoup ), 1 ); + Add( typeof( AwaseMisoSoup ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBSEHats.cs b/Scripts/Mobiles/Vendors/SBInfo/SBSEHats.cs new file mode 100644 index 000000000..51680a696 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBSEHats.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSEHats: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSEHats() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Kasa ), 31, 20, 0x2798, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherJingasa ), 11, 20, 0x2776, 0 ) ); + Add( new GenericBuyInfo( typeof( ClothNinjaHood ), 33, 20, 0x278F, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Kasa ), 15 ); + Add( typeof( LeatherJingasa ), 5 ); + Add( typeof( ClothNinjaHood ), 16 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBScribe.cs b/Scripts/Mobiles/Vendors/SBInfo/SBScribe.cs new file mode 100644 index 000000000..9e150468b --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBScribe.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBScribe: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBScribe() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( ScribesPen ), 8, 20, 0xFBF, 0 ) ); + Add( new GenericBuyInfo( typeof( BlankScroll ), 5, 999, 0x0E34, 0 ) ); + Add( new GenericBuyInfo( typeof( ScribesPen ), 8, 20, 0xFC0, 0 ) ); + Add( new GenericBuyInfo( typeof( BrownBook ), 15, 10, 0xFEF, 0 ) ); + Add( new GenericBuyInfo( typeof( TanBook ), 15, 10, 0xFF0, 0 ) ); + Add( new GenericBuyInfo( typeof( BlueBook ), 15, 10, 0xFF2, 0 ) ); + //Add( new GenericBuyInfo( "1041267", typeof( Runebook ), 3500, 10, 0xEFA, 0x461 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( ScribesPen ), 4 ); + Add( typeof( BrownBook ), 7 ); + Add( typeof( TanBook ), 7 ); + Add( typeof( BlueBook ), 7 ); + Add( typeof( BlankScroll ), 3 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBShipwright.cs b/Scripts/Mobiles/Vendors/SBInfo/SBShipwright.cs new file mode 100644 index 000000000..653808d42 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBShipwright.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Multis; + +namespace Server.Mobiles +{ + public class SBShipwright : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBShipwright() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( "1041205", typeof( SmallBoatDeed ), 10177, 20, 0x14F2, 0 ) ); + Add( new GenericBuyInfo( "1041206", typeof( SmallDragonBoatDeed ), 10177, 20, 0x14F2, 0 ) ); + Add( new GenericBuyInfo( "1041207", typeof( MediumBoatDeed ), 11552, 20, 0x14F2, 0 ) ); + Add( new GenericBuyInfo( "1041208", typeof( MediumDragonBoatDeed ), 11552, 20, 0x14F2, 0 ) ); + Add( new GenericBuyInfo( "1041209", typeof( LargeBoatDeed ), 12927, 20, 0x14F2, 0 ) ); + Add( new GenericBuyInfo( "1041210", typeof( LargeDragonBoatDeed ), 12927, 20, 0x14F2, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + //You technically CAN sell them back, *BUT* the vendors do not carry enough money to buy with + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBSmithTools.cs b/Scripts/Mobiles/Vendors/SBInfo/SBSmithTools.cs new file mode 100644 index 000000000..b617a961f --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBSmithTools.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSmithTools: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSmithTools() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( IronIngot ), 5, 16, 0x1BF2, 0 ) ); + Add( new GenericBuyInfo( typeof( Tongs ), 13, 14, 0xFBB, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Tongs ), 7 ); + Add( typeof( IronIngot ), 4 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBStoneCrafter.cs b/Scripts/Mobiles/Vendors/SBInfo/SBStoneCrafter.cs new file mode 100644 index 000000000..e068c7ee6 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBStoneCrafter.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBStoneCrafter : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBStoneCrafter() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Nails ), 3, 20, 0x102E, 0 ) ); + Add( new GenericBuyInfo( typeof( Axle ), 2, 20, 0x105B, 0 ) ); + Add( new GenericBuyInfo( typeof( Board ), 3, 20, 0x1BD7, 0 ) ); + Add( new GenericBuyInfo( typeof( DrawKnife ), 10, 20, 0x10E4, 0 ) ); + Add( new GenericBuyInfo( typeof( Froe ), 10, 20, 0x10E5, 0 ) ); + Add( new GenericBuyInfo( typeof( Scorp ), 10, 20, 0x10E7, 0 ) ); + Add( new GenericBuyInfo( typeof( Inshave ), 10, 20, 0x10E6, 0 ) ); + Add( new GenericBuyInfo( typeof( DovetailSaw ), 12, 20, 0x1028, 0 ) ); + Add( new GenericBuyInfo( typeof( Saw ), 15, 20, 0x1034, 0 ) ); + Add( new GenericBuyInfo( typeof( Hammer ), 17, 20, 0x102A, 0 ) ); + Add( new GenericBuyInfo( typeof( MouldingPlane ), 11, 20, 0x102C, 0 ) ); + Add( new GenericBuyInfo( typeof( SmoothingPlane ), 10, 20, 0x1032, 0 ) ); + Add( new GenericBuyInfo( typeof( JointingPlane ), 11, 20, 0x1030, 0 ) ); + + Add( new GenericBuyInfo( "Making Valuables With Stonecrafting", typeof( MasonryBook ), 10625, 10, 0xFBE, 0 ) ); + Add( new GenericBuyInfo( "Mining For Quality Stone", typeof( StoneMiningBook ), 10625, 10, 0xFBE, 0 ) ); + Add( new GenericBuyInfo( "1044515", typeof( MalletAndChisel ), 3, 50, 0x12B3, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( MasonryBook ), 5000 ); + Add( typeof( StoneMiningBook ), 5000 ); + Add( typeof( MalletAndChisel ), 1 ); + + Add( typeof( WoodenBox ), 7 ); + Add( typeof( SmallCrate ), 5 ); + Add( typeof( MediumCrate ), 6 ); + Add( typeof( LargeCrate ), 7 ); + Add( typeof( WoodenChest ), 15 ); + + Add( typeof( LargeTable ), 10 ); + Add( typeof( Nightstand ), 7 ); + Add( typeof( YewWoodTable ), 10 ); + + Add( typeof( Throne ), 24 ); + Add( typeof( WoodenThrone ), 6 ); + Add( typeof( Stool ), 6 ); + Add( typeof( FootStool ), 6 ); + + Add( typeof( FancyWoodenChairCushion ), 12 ); + Add( typeof( WoodenChairCushion ), 10 ); + Add( typeof( WoodenChair ), 8 ); + Add( typeof( BambooChair ), 6 ); + Add( typeof( WoodenBench ), 6 ); + + Add( typeof( Saw ), 9 ); + Add( typeof( Scorp ), 6 ); + Add( typeof( SmoothingPlane ), 6 ); + Add( typeof( DrawKnife ), 6 ); + Add( typeof( Froe ), 6 ); + Add( typeof( Hammer ), 14 ); + Add( typeof( Inshave ), 6 ); + Add( typeof( JointingPlane ), 6 ); + Add( typeof( MouldingPlane ), 6 ); + Add( typeof( DovetailSaw ), 7 ); + Add( typeof( Board ), 2 ); + Add( typeof( Axle ), 1 ); + + Add( typeof( WoodenShield ), 31 ); + Add( typeof( BlackStaff ), 24 ); + Add( typeof( GnarledStaff ), 12 ); + Add( typeof( QuarterStaff ), 15 ); + Add( typeof( ShepherdsCrook ), 12 ); + Add( typeof( Club ), 13 ); + + Add( typeof( Log ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBTailor.cs b/Scripts/Mobiles/Vendors/SBInfo/SBTailor.cs new file mode 100644 index 000000000..2cd225510 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBTailor.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBTailor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBTailor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + + Add( new GenericBuyInfo( typeof( SewingKit ), 3, 20, 0xF9D, 0 ) ); + Add( new GenericBuyInfo( typeof( Scissors ), 11, 20, 0xF9F, 0 ) ); + Add( new GenericBuyInfo( typeof( DyeTub ), 8, 20, 0xFAB, 0 ) ); + Add( new GenericBuyInfo( typeof( Dyes ), 8, 20, 0xFA9, 0 ) ); + + Add( new GenericBuyInfo( typeof( Shirt ), 12, 20, 0x1517, 0 ) ); + Add( new GenericBuyInfo( typeof( ShortPants ), 7, 20, 0x152E, 0 ) ); + Add( new GenericBuyInfo( typeof( FancyShirt ), 21, 20, 0x1EFD, 0 ) ); + Add( new GenericBuyInfo( typeof( LongPants ), 10, 20, 0x1539, 0 ) ); + Add( new GenericBuyInfo( typeof( FancyDress ), 26, 20, 0x1EFF, 0 ) ); + Add( new GenericBuyInfo( typeof( PlainDress ), 13, 20, 0x1F01, 0 ) ); + Add( new GenericBuyInfo( typeof( Kilt ), 11, 20, 0x1537, 0 ) ); + Add( new GenericBuyInfo( typeof( Kilt ), 11, 20, 0x1537, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( HalfApron ), 10, 20, 0x153b, 0 ) ); + Add( new GenericBuyInfo( typeof( Robe ), 18, 20, 0x1F03, 0 ) ); + Add( new GenericBuyInfo( typeof( Cloak ), 8, 20, 0x1515, 0 ) ); + Add( new GenericBuyInfo( typeof( Cloak ), 8, 20, 0x1515, 0 ) ); + Add( new GenericBuyInfo( typeof( Doublet ), 13, 20, 0x1F7B, 0 ) ); + Add( new GenericBuyInfo( typeof( Tunic ), 18, 20, 0x1FA1, 0 ) ); + Add( new GenericBuyInfo( typeof( JesterSuit ), 26, 20, 0x1F9F, 0 ) ); + + Add( new GenericBuyInfo( typeof( JesterHat ), 12, 20, 0x171C, 0 ) ); + Add( new GenericBuyInfo( typeof( FloppyHat ), 7, 20, 0x1713, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( WideBrimHat ), 8, 20, 0x1714, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( Cap ), 10, 20, 0x1715, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( TallStrawHat ), 8, 20, 0x1716, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( StrawHat ), 7, 20, 0x1717, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( WizardsHat ), 11, 20, 0x1718, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( LeatherCap ), 10, 20, 0x1DB9, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( FeatheredHat ), 10, 20, 0x171A, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( TricorneHat ), 8, 20, 0x171B, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( Bandana ), 6, 20, 0x1540, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( SkullCap ), 7, 20, 0x1544, Utility.RandomDyedHue() ) ); + + Add( new GenericBuyInfo( typeof( BoltOfCloth ), 100, 20, 0xf95, Utility.RandomDyedHue() ) ); + + Add( new GenericBuyInfo( typeof( Cloth ), 2, 20, 0x1766, Utility.RandomDyedHue() ) ); + Add( new GenericBuyInfo( typeof( UncutCloth ), 2, 20, 0x1767, Utility.RandomDyedHue() ) ); + + Add( new GenericBuyInfo( typeof( Cotton ), 102, 20, 0xDF9, 0 ) ); + Add( new GenericBuyInfo( typeof( Wool ), 62, 20, 0xDF8, 0 ) ); + Add( new GenericBuyInfo( typeof( Flax ), 102, 20, 0x1A9C, 0 ) ); + Add( new GenericBuyInfo( typeof( SpoolOfThread ), 18, 20, 0xFA0, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Scissors ), 6 ); + Add( typeof( SewingKit ), 1 ); + Add( typeof( Dyes ), 4 ); + Add( typeof( DyeTub ), 4 ); + + Add( typeof( BoltOfCloth ), 50 ); + + Add( typeof( FancyShirt ), 10 ); + Add( typeof( Shirt ), 6 ); + + Add( typeof( ShortPants ), 3 ); + Add( typeof( LongPants ), 5 ); + + Add( typeof( Cloak ), 4 ); + Add( typeof( FancyDress ), 12 ); + Add( typeof( Robe ), 9 ); + Add( typeof( PlainDress ), 7 ); + + Add( typeof( Skirt ), 5 ); + Add( typeof( Kilt ), 5 ); + + Add( typeof( Doublet ), 7 ); + Add( typeof( Tunic ), 9 ); + Add( typeof( JesterSuit ), 13 ); + + Add( typeof( FullApron ), 5 ); + Add( typeof( HalfApron ), 5 ); + + Add( typeof( JesterHat ), 6 ); + Add( typeof( FloppyHat ), 3 ); + Add( typeof( WideBrimHat ), 13 ); + Add( typeof( Cap ), 5 ); + Add( typeof( SkullCap ), 3 ); + Add( typeof( Bandana ), 6 ); + Add( typeof( TallStrawHat ), 4 ); + Add( typeof( StrawHat ), 4 ); + Add( typeof( WizardsHat ), 5 ); + Add( typeof( Bonnet ), 4 ); + Add( typeof( FeatheredHat ), 5 ); + Add( typeof( TricorneHat ), 13 ); + + Add( typeof( SpoolOfThread ), 9 ); + + Add( typeof( Flax ), 51 ); + Add( typeof( Cotton ), 51 ); + Add( typeof( Wool ), 31 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBTanner.cs b/Scripts/Mobiles/Vendors/SBInfo/SBTanner.cs new file mode 100644 index 000000000..db931222a --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBTanner.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBTanner : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBTanner() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( LeatherGorget ), 31, 20, 0x13C7, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherCap ), 10, 20, 0x1DB9, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherArms ), 37, 20, 0x13CD, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherChest ), 47, 20, 0x13CC, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherLegs ), 36, 20, 0x13CB, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherGloves ), 31, 20, 0x13C6, 0 ) ); + + Add( new GenericBuyInfo( typeof( StuddedGorget ), 50, 20, 0x13D6, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedArms ), 57, 20, 0x13DC, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedChest ), 75, 20, 0x13DB, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedLegs ), 67, 20, 0x13DA, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedGloves ), 45, 20, 0x13D5, 0 ) ); + + Add( new GenericBuyInfo( typeof( FemaleStuddedChest ), 62, 20, 0x1C02, 0 ) ); + Add( new GenericBuyInfo( typeof( FemalePlateChest ), 207, 20, 0x1C04, 0 ) ); + Add( new GenericBuyInfo( typeof( FemaleLeatherChest ), 36, 20, 0x1C06, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherShorts ), 28, 20, 0x1C00, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherSkirt ), 25, 20, 0x1C08, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherBustierArms ), 25, 20, 0x1C0A, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherBustierArms ), 30, 20, 0x1C0B, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedBustierArms ), 50, 20, 0x1C0C, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedBustierArms ), 47, 20, 0x1C0D, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bag ), 6, 20, 0xE76, 0 ) ); + Add( new GenericBuyInfo( typeof( Pouch ), 6, 20, 0xE79, 0 ) ); + Add( new GenericBuyInfo( typeof( Backpack ), 15, 20, 0x9B2, 0 ) ); + Add( new GenericBuyInfo( typeof( Leather ), 6, 20, 0x1081, 0 ) ); + + Add( new GenericBuyInfo( typeof( SkinningKnife ), 15, 20, 0xEC4, 0 ) ); + + Add( new GenericBuyInfo( "1041279", typeof( TaxidermyKit ), 100000, 20, 0x1EBA, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bag ), 3 ); + Add( typeof( Pouch ), 3 ); + Add( typeof( Backpack ), 7 ); + + Add( typeof( Leather ), 5 ); + + Add( typeof( SkinningKnife ), 7 ); + + Add( typeof( LeatherArms ), 18 ); + Add( typeof( LeatherChest ), 23 ); + Add( typeof( LeatherGloves ), 15 ); + Add( typeof( LeatherGorget ), 15 ); + Add( typeof( LeatherLegs ), 18 ); + Add( typeof( LeatherCap ), 5 ); + + Add( typeof( StuddedArms ), 43 ); + Add( typeof( StuddedChest ), 37 ); + Add( typeof( StuddedGloves ), 39 ); + Add( typeof( StuddedGorget ), 22 ); + Add( typeof( StuddedLegs ), 33 ); + + Add( typeof( FemaleStuddedChest ), 31 ); + Add( typeof( StuddedBustierArms ), 23 ); + Add( typeof( FemalePlateChest), 103 ); + Add( typeof( FemaleLeatherChest ), 18 ); + Add( typeof( LeatherBustierArms ), 12 ); + Add( typeof( LeatherShorts ), 14 ); + Add( typeof( LeatherSkirt ), 12 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBTavernKeeper.cs b/Scripts/Mobiles/Vendors/SBInfo/SBTavernKeeper.cs new file mode 100644 index 000000000..957e7e9da --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBTavernKeeper.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBTavernKeeper : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBTavernKeeper() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Ale, 7, 20, 0x99F, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Wine, 7, 20, 0x9C7, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Liquor, 7, 20, 0x99B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Jug ), BeverageType.Cider, 13, 20, 0x9C8, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Milk, 7, 20, 0x9F0, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Ale, 11, 20, 0x1F95, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Cider, 11, 20, 0x1F97, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Liquor, 11, 20, 0x1F99, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Wine, 11, 20, 0x1F9B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Water, 11, 20, 0x1F9D, 0 ) ); + + Add( new GenericBuyInfo( typeof( BreadLoaf ), 6, 10, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( CheeseWheel ), 21, 10, 0x97E, 0 ) ); + Add( new GenericBuyInfo( typeof( CookedBird ), 17, 20, 0x9B7, 0 ) ); + Add( new GenericBuyInfo( typeof( LambLeg ), 8, 20, 0x160A, 0 ) ); + Add( new GenericBuyInfo( typeof( ChickenLeg ), 5, 20, 0x1608, 0 ) ); + Add( new GenericBuyInfo( typeof( Ribs ), 7, 20, 0x9F2, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenBowlOfCarrots ), 3, 20, 0x15F9, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfCorn ), 3, 20, 0x15FA, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfLettuce ), 3, 20, 0x15FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfPeas ), 3, 20, 0x15FC, 0 ) ); + Add( new GenericBuyInfo( typeof( EmptyPewterBowl ), 2, 20, 0x15FD, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfCorn ), 3, 20, 0x15FE, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfLettuce ), 3, 20, 0x15FF, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPeas ), 3, 20, 0x1600, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPotatos ), 3, 20, 0x1601, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfStew ), 3, 20, 0x1604, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfTomatoSoup ), 3, 20, 0x1606, 0 ) ); + + Add( new GenericBuyInfo( typeof( ApplePie ), 7, 20, 0x1041, 0 ) ); //OSI just has Pie, not Apple/Fruit/Meat + + Add( new GenericBuyInfo( "1016450", typeof( Chessboard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( "1016449", typeof( CheckerBoard ), 2, 20, 0xFA6, 0 ) ); + Add( new GenericBuyInfo( typeof( Backgammon ), 2, 20, 0xE1C, 0 ) ); + Add( new GenericBuyInfo( typeof( Dices ), 2, 20, 0xFA7, 0 ) ); + Add( new GenericBuyInfo( "1041243", typeof( ContractOfEmployment ), 1252, 20, 0x14F0, 0 ) ); + Add( new GenericBuyInfo( "a barkeep contract", typeof( BarkeepContract ), 1252, 20, 0x14F0, 0 ) ); + + if ( Multis.BaseHouse.NewVendorSystem ) + Add( new GenericBuyInfo( "1062332", typeof( VendorRentalContract ), 1252, 20, 0x14F0, 0x672 ) ); + + /*if ( Map == Tokuno ) + { + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E8, 0 ) ); + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E9, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2836, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2837, 0 ) ); + Add( new GenericBuyInfo( typeof( GreenTeaBasket ), 2, 20, 0x284B, 0 ) ); + }*/ + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( WoodenBowlOfCarrots ), 1 ); + Add( typeof( WoodenBowlOfCorn ), 1 ); + Add( typeof( WoodenBowlOfLettuce ), 1 ); + Add( typeof( WoodenBowlOfPeas ), 1 ); + Add( typeof( EmptyPewterBowl ), 1 ); + Add( typeof( PewterBowlOfCorn ), 1 ); + Add( typeof( PewterBowlOfLettuce ), 1 ); + Add( typeof( PewterBowlOfPeas ), 1 ); + Add( typeof( PewterBowlOfPotatos ), 1 ); + Add( typeof( WoodenBowlOfStew ), 1 ); + Add( typeof( WoodenBowlOfTomatoSoup ), 1 ); + Add( typeof( BeverageBottle ), 3 ); + Add( typeof( Jug ), 6 ); + Add( typeof( Pitcher ), 5 ); + Add( typeof( GlassMug ), 1 ); + Add( typeof( BreadLoaf ), 3 ); + Add( typeof( CheeseWheel ), 12 ); + Add( typeof( Ribs ), 6 ); + Add( typeof( Peach ), 1 ); + Add( typeof( Pear ), 1 ); + Add( typeof( Grapes ), 1 ); + Add( typeof( Apple ), 1 ); + Add( typeof( Banana ), 1 ); + Add( typeof( Candle ), 3 ); + Add( typeof( Chessboard ), 1 ); + Add( typeof( CheckerBoard ), 1 ); + Add( typeof( Backgammon ), 1 ); + Add( typeof( Dices ), 1 ); + Add( typeof( ContractOfEmployment ), 626 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBThief.cs b/Scripts/Mobiles/Vendors/SBInfo/SBThief.cs new file mode 100644 index 000000000..1436f93be --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBThief.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBThief : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBThief() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Backpack ), 15, 20, 0x9B2, 0 ) ); + Add( new GenericBuyInfo( typeof( Pouch ), 6, 20, 0xE79, 0 ) ); + Add( new GenericBuyInfo( typeof( Torch ), 8, 20, 0xF6B, 0 ) ); + Add( new GenericBuyInfo( typeof( Lantern ), 2, 20, 0xA25, 0 ) ); + //Add( new GenericBuyInfo( typeof( OilFlask ), 8, 20, 0x####, 0 ) ); + Add( new GenericBuyInfo( typeof( Lockpick ), 12, 20, 0x14FC, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBox ), 14, 20, 0x9AA, 0 ) ); + Add( new GenericBuyInfo( typeof( Key ), 2, 20, 0x100E, 0 ) ); + Add( new GenericBuyInfo( typeof( HairDye ), 37, 20, 0xEFF, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Backpack ), 7 ); + Add( typeof( Pouch ), 3 ); + Add( typeof( Torch ), 3 ); + Add( typeof( Lantern ), 1 ); + //Add( typeof( OilFlask ), 4 ); + Add( typeof( Lockpick ), 6 ); + Add( typeof( WoodenBox ), 7 ); + Add( typeof( HairDye ), 19 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBTinker.cs b/Scripts/Mobiles/Vendors/SBInfo/SBTinker.cs new file mode 100644 index 000000000..c710f56c4 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBTinker.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBTinker: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBTinker() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + + Add( new GenericBuyInfo( typeof( Clock ), 22, 20, 0x104B, 0 ) ); + Add( new GenericBuyInfo( typeof( Nails ), 3, 20, 0x102E, 0 ) ); + Add( new GenericBuyInfo( typeof( ClockParts ), 3, 20, 0x104F, 0 ) ); + Add( new GenericBuyInfo( typeof( AxleGears ), 3, 20, 0x1051, 0 ) ); + Add( new GenericBuyInfo( typeof( Gears ), 2, 20, 0x1053, 0 ) ); + Add( new GenericBuyInfo( typeof( Hinge ), 2, 20, 0x1055, 0 ) ); + + Add( new GenericBuyInfo( typeof( Sextant ), 13, 20, 0x1057, 0 ) ); + Add( new GenericBuyInfo( typeof( SextantParts ), 5, 20, 0x1059, 0 ) ); + Add( new GenericBuyInfo( typeof( Axle ), 2, 20, 0x105B, 0 ) ); + Add( new GenericBuyInfo( typeof( Springs ), 3, 20, 0x105D, 0 ) ); + + Add( new GenericBuyInfo( "1024111", typeof( Key ), 8, 20, 0x100F, 0 ) ); + Add( new GenericBuyInfo( "1024112", typeof( Key ), 8, 20, 0x1010, 0 ) ); + Add( new GenericBuyInfo( "1024115", typeof( Key ), 8, 20, 0x1013, 0 ) ); + Add( new GenericBuyInfo( typeof( KeyRing ), 8, 20, 0x1010, 0 ) ); + Add( new GenericBuyInfo( typeof( Lockpick ), 12, 20, 0x14FC, 0 ) ); + + Add( new GenericBuyInfo( typeof( TinkerTools ), 7, 20, 0x1EB8, 0 ) ); + Add( new GenericBuyInfo( typeof( Board ), 3, 20, 0x1BD7, 0 ) ); + Add( new GenericBuyInfo( typeof( IronIngot ), 5, 16, 0x1BF2, 0 ) ); + Add( new GenericBuyInfo( typeof( SewingKit ), 3, 20, 0xF9D, 0 ) ); + + Add( new GenericBuyInfo( typeof( DrawKnife ), 10, 20, 0x10E4, 0 ) ); + Add( new GenericBuyInfo( typeof( Froe ), 10, 20, 0x10E5, 0 ) ); + Add( new GenericBuyInfo( typeof( Scorp ), 10, 20, 0x10E7, 0 ) ); + Add( new GenericBuyInfo( typeof( Inshave ), 10, 20, 0x10E6, 0 ) ); + + Add( new GenericBuyInfo( typeof( ButcherKnife ), 13, 20, 0x13F6, 0 ) ); + + Add( new GenericBuyInfo( typeof( Scissors ), 11, 20, 0xF9F, 0 ) ); + + Add( new GenericBuyInfo( typeof( Tongs ), 13, 14, 0xFBB, 0 ) ); + + Add( new GenericBuyInfo( typeof( DovetailSaw ), 12, 20, 0x1028, 0 ) ); + Add( new GenericBuyInfo( typeof( Saw ), 15, 20, 0x1034, 0 ) ); + + Add( new GenericBuyInfo( typeof( Hammer ), 17, 20, 0x102A, 0 ) ); + Add( new GenericBuyInfo( typeof( SmithHammer ), 23, 20, 0x13E3, 0 ) ); + // TODO: Sledgehammer + + Add( new GenericBuyInfo( typeof( Shovel ), 12, 20, 0xF39, 0 ) ); + + Add( new GenericBuyInfo( typeof( MouldingPlane ), 11, 20, 0x102C, 0 ) ); + Add( new GenericBuyInfo( typeof( JointingPlane ), 10, 20, 0x1030, 0 ) ); + Add( new GenericBuyInfo( typeof( SmoothingPlane ), 11, 20, 0x1032, 0 ) ); + + Add( new GenericBuyInfo( typeof( Pickaxe ), 25, 20, 0xE86, 0 ) ); + + + Add( new GenericBuyInfo( typeof( Drums ), 21, 20, 0x0E9C, 0 ) ); + Add( new GenericBuyInfo( typeof( Tambourine ), 21, 20, 0x0E9E, 0 ) ); + Add( new GenericBuyInfo( typeof( LapHarp ), 21, 20, 0x0EB2, 0 ) ); + Add( new GenericBuyInfo( typeof( Lute ), 21, 20, 0x0EB3, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Drums ), 10 ); + Add( typeof( Tambourine ), 10 ); + Add( typeof( LapHarp ), 10 ); + Add( typeof( Lute ), 10 ); + + Add( typeof( Shovel ), 6 ); + Add( typeof( SewingKit ), 1 ); + Add( typeof( Scissors ), 6 ); + Add( typeof( Tongs ), 7 ); + Add( typeof( Key ), 1 ); + + Add( typeof( DovetailSaw ), 6 ); + Add( typeof( MouldingPlane ), 6 ); + Add( typeof( Nails ), 1 ); + Add( typeof( JointingPlane ), 6 ); + Add( typeof( SmoothingPlane ), 6 ); + Add( typeof( Saw ), 7 ); + + Add( typeof( Clock ), 11 ); + Add( typeof( ClockParts ), 1 ); + Add( typeof( AxleGears ), 1 ); + Add( typeof( Gears ), 1 ); + Add( typeof( Hinge ), 1 ); + Add( typeof( Sextant ), 6 ); + Add( typeof( SextantParts ), 2 ); + Add( typeof( Axle ), 1 ); + Add( typeof( Springs ), 1 ); + + Add( typeof( DrawKnife ), 5 ); + Add( typeof( Froe ), 5 ); + Add( typeof( Inshave ), 5 ); + Add( typeof( Scorp ), 5 ); + + Add( typeof( Lockpick ), 6 ); + Add( typeof( TinkerTools ), 3 ); + + Add( typeof( Board ), 1 ); + Add( typeof( Log ), 1 ); + + Add( typeof( Pickaxe ), 16 ); + Add( typeof( Hammer ), 3 ); + Add( typeof( SmithHammer ), 11 ); + Add( typeof( ButcherKnife ), 6 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBVagabond.cs b/Scripts/Mobiles/Vendors/SBInfo/SBVagabond.cs new file mode 100644 index 000000000..a4686dd77 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBVagabond.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBVagabond : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBVagabond() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( GoldRing ), 27, 20, 0x108A, 0 ) ); + Add( new GenericBuyInfo( typeof( Necklace ), 26, 20, 0x1085, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldNecklace ), 27, 20, 0x1088, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldBeadNecklace ), 27, 20, 0x1089, 0 ) ); + Add( new GenericBuyInfo( typeof( Beads ), 27, 20, 0x108B, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldBracelet ), 27, 20, 0x1086, 0 ) ); + Add( new GenericBuyInfo( typeof( GoldEarrings ), 27, 20, 0x1087, 0 ) ); + + Add( new GenericBuyInfo( typeof( Board ), 3, 20, 0x1BD7, 0 ) ); + Add( new GenericBuyInfo( typeof( IronIngot ), 6, 20, 0x1BF2, 0 ) ); + + Add( new GenericBuyInfo( typeof( StarSapphire ), 125, 20, 0xF21, 0 ) ); + Add( new GenericBuyInfo( typeof( Emerald ), 100, 20, 0xF10, 0 ) ); + Add( new GenericBuyInfo( typeof( Sapphire ), 100, 20, 0xF19, 0 ) ); + Add( new GenericBuyInfo( typeof( Ruby ), 75, 20, 0xF13, 0 ) ); + Add( new GenericBuyInfo( typeof( Citrine ), 50, 20, 0xF15, 0 ) ); + Add( new GenericBuyInfo( typeof( Amethyst ), 100, 20, 0xF16, 0 ) ); + Add( new GenericBuyInfo( typeof( Tourmaline ), 75, 20, 0xF2D, 0 ) ); + Add( new GenericBuyInfo( typeof( Amber ), 50, 20, 0xF25, 0 ) ); + Add( new GenericBuyInfo( typeof( Diamond ), 200, 20, 0xF26, 0 ) ); + + Add( new GenericBuyInfo( typeof( Board ), 3, 20, 0x1BD7, 0 ) ); + Add( new GenericBuyInfo( typeof( IronIngot ), 6, 20, 0x1BF2, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Board ), 1 ); + Add( typeof( IronIngot ), 3 ); + + Add( typeof( Amber ), 25 ); + Add( typeof( Amethyst ), 50 ); + Add( typeof( Citrine ), 25 ); + Add( typeof( Diamond ), 100 ); + Add( typeof( Emerald ), 50 ); + Add( typeof( Ruby ), 37 ); + Add( typeof( Sapphire ), 50 ); + Add( typeof( StarSapphire ), 62 ); + Add( typeof( Tourmaline ), 47 ); + Add( typeof( GoldRing ), 13 ); + Add( typeof( SilverRing ), 10 ); + Add( typeof( Necklace ), 13 ); + Add( typeof( GoldNecklace ), 13 ); + Add( typeof( GoldBeadNecklace ), 13 ); + Add( typeof( SilverNecklace ), 10 ); + Add( typeof( SilverBeadNecklace ), 10 ); + Add( typeof( Beads ), 13 ); + Add( typeof( GoldBracelet ), 13 ); + Add( typeof( SilverBracelet ), 10 ); + Add( typeof( GoldEarrings ), 13 ); + Add( typeof( SilverEarrings ), 10 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBVarietyDealer.cs b/Scripts/Mobiles/Vendors/SBInfo/SBVarietyDealer.cs new file mode 100644 index 000000000..994e8e1b3 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBVarietyDealer.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBVarietyDealer : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBVarietyDealer() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bandage ), 5, 20, 0xE21, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlankScroll ), 5, 999, 0x0E34, 0 ) ); + + Add( new GenericBuyInfo( typeof( NightSightPotion ), 15, 10, 0xF06, 0 ) ); + Add( new GenericBuyInfo( typeof( AgilityPotion ), 15, 10, 0xF08, 0 ) ); + Add( new GenericBuyInfo( typeof( StrengthPotion ), 15, 10, 0xF09, 0 ) ); + Add( new GenericBuyInfo( typeof( RefreshPotion ), 15, 10, 0xF0B, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserCurePotion ), 15, 10, 0xF07, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserHealPotion ), 15, 10, 0xF0C, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserPoisonPotion ), 15, 10, 0xF0A, 0 ) ); + Add( new GenericBuyInfo( typeof( LesserExplosionPotion ), 21, 10, 0xF0D, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bolt ), 6, Utility.Random( 30, 60 ), 0x1BFB, 0 ) ); + Add( new GenericBuyInfo( typeof( Arrow ), 3, Utility.Random( 30, 60 ), 0xF3F, 0 ) ); + + Add( new GenericBuyInfo( typeof( BlackPearl ), 5, 999, 0xF7A, 0 ) ); + Add( new GenericBuyInfo( typeof( Bloodmoss ), 5, 999, 0xF7B, 0 ) ); + Add( new GenericBuyInfo( typeof( MandrakeRoot ), 3, 999, 0xF86, 0 ) ); + Add( new GenericBuyInfo( typeof( Garlic ), 3, 999, 0xF84, 0 ) ); + Add( new GenericBuyInfo( typeof( Ginseng ), 3, 999, 0xF85, 0 ) ); + Add( new GenericBuyInfo( typeof( Nightshade ), 3, 999, 0xF88, 0 ) ); + Add( new GenericBuyInfo( typeof( SpidersSilk ), 3, 999, 0xF8D, 0 ) ); + Add( new GenericBuyInfo( typeof( SulfurousAsh ), 3, 999, 0xF8C, 0 ) ); + + Add( new GenericBuyInfo( typeof( BreadLoaf ), 7, 10, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( Backpack ), 15, 20, 0x9B2, 0 ) ); + + Type[] types = Loot.RegularScrollTypes; + + int circles = 3; + + for ( int i = 0; i < circles*8 && i < types.Length; ++i ) + { + int itemID = 0x1F2E + i; + + if ( i == 6 ) + itemID = 0x1F2D; + else if ( i > 6 ) + --itemID; + + Add( new GenericBuyInfo( types[i], 12 + ((i / 8) * 10), 20, itemID, 0 ) ); + } + + if ( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( BatWing ), 3, 999, 0xF78, 0 ) ); + Add( new GenericBuyInfo( typeof( GraveDust ), 3, 999, 0xF8F, 0 ) ); + Add( new GenericBuyInfo( typeof( DaemonBlood ), 6, 999, 0xF7D, 0 ) ); + Add( new GenericBuyInfo( typeof( NoxCrystal ), 6, 999, 0xF8E, 0 ) ); + Add( new GenericBuyInfo( typeof( PigIron ), 5, 999, 0xF8A, 0 ) ); + + Add( new GenericBuyInfo( typeof( NecromancerSpellbook ), 115, 10, 0x2253, 0 ) ); + } + + Add( new GenericBuyInfo( typeof( RecallRune ), 15, 10, 0x1f14, 0 ) ); + Add( new GenericBuyInfo( typeof( Spellbook ), 18, 10, 0xEFA, 0 ) ); + + Add( new GenericBuyInfo( "1041072", typeof( MagicWizardsHat ), 11, 10, 0x1718, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bandage ), 1 ); + + Add( typeof( BlankScroll ), 3 ); + + Add( typeof( NightSightPotion ), 7 ); + Add( typeof( AgilityPotion ), 7 ); + Add( typeof( StrengthPotion ), 7 ); + Add( typeof( RefreshPotion ), 7 ); + Add( typeof( LesserCurePotion ), 7 ); + Add( typeof( LesserHealPotion ), 7 ); + Add( typeof( LesserPoisonPotion ), 7 ); + Add( typeof( LesserExplosionPotion ), 10 ); + + Add( typeof( Bolt ), 3 ); + Add( typeof( Arrow ), 2 ); + + Add( typeof( BlackPearl ), 3 ); + Add( typeof( Bloodmoss ), 3 ); + Add( typeof( MandrakeRoot ), 2 ); + Add( typeof( Garlic ), 2 ); + Add( typeof( Ginseng ), 2 ); + Add( typeof( Nightshade ), 2 ); + Add( typeof( SpidersSilk ), 2 ); + Add( typeof( SulfurousAsh ), 2 ); + + Add( typeof( BreadLoaf ), 3 ); + Add( typeof( Backpack ), 7 ); + Add( typeof( RecallRune ), 8 ); + Add( typeof( Spellbook ), 9 ); + Add( typeof( BlankScroll ), 3 ); + + if ( Core.AOS ) + { + Add( typeof( BatWing ), 2 ); + Add( typeof( GraveDust ), 2 ); + Add( typeof( DaemonBlood ), 3 ); + Add( typeof( NoxCrystal ), 3 ); + Add( typeof( PigIron ), 3 ); + } + + Type[] types = Loot.RegularScrollTypes; + + for ( int i = 0; i < types.Length; ++i ) + Add( types[i], ((i / 8) + 2) * 5 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBVeterinarian.cs b/Scripts/Mobiles/Vendors/SBInfo/SBVeterinarian.cs new file mode 100644 index 000000000..fd3632d08 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBVeterinarian.cs @@ -0,0 +1,40 @@ + +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBVeterinarian : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBVeterinarian() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bandage ), 6, 20, 0xE21, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( PackHorse ), 616, 10, 291, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( PackLlama ), 523, 10, 292, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Dog ), 158, 10, 217, 0 ) ); + Add( new AnimalBuyInfo( 1, typeof( Cat ), 131, 10, 201, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bandage ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBWaiter.cs b/Scripts/Mobiles/Vendors/SBInfo/SBWaiter.cs new file mode 100644 index 000000000..7d2a1333b --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBWaiter.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBWaiter : SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBWaiter() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Ale, 7, 20, 0x99F, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Wine, 7, 20, 0x9C7, 0 ) ); + Add( new BeverageBuyInfo( typeof( BeverageBottle ), BeverageType.Liquor, 7, 20, 0x99B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Jug ), BeverageType.Cider, 13, 20, 0x9C8, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Milk, 7, 20, 0x9F0, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Ale, 11, 20, 0x1F95, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Cider, 11, 20, 0x1F97, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Liquor, 11, 20, 0x1F99, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Wine, 11, 20, 0x1F9B, 0 ) ); + Add( new BeverageBuyInfo( typeof( Pitcher ), BeverageType.Water, 11, 20, 0x1F9D, 0 ) ); + + Add( new GenericBuyInfo( typeof( BreadLoaf ), 6, 10, 0x103B, 0 ) ); + Add( new GenericBuyInfo( typeof( CheeseWheel ), 21, 10, 0x97E, 0 ) ); + Add( new GenericBuyInfo( typeof( CookedBird ), 17, 20, 0x9B7, 0 ) ); + Add( new GenericBuyInfo( typeof( LambLeg ), 8, 20, 0x160A, 0 ) ); + + Add( new GenericBuyInfo( typeof( WoodenBowlOfCarrots ), 3, 20, 0x15F9, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfCorn ), 3, 20, 0x15FA, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfLettuce ), 3, 20, 0x15FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfPeas ), 3, 20, 0x15FC, 0 ) ); + Add( new GenericBuyInfo( typeof( EmptyPewterBowl ), 2, 20, 0x15FD, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfCorn ), 3, 20, 0x15FE, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfLettuce ), 3, 20, 0x15FF, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPeas ), 3, 20, 0x1600, 0 ) ); + Add( new GenericBuyInfo( typeof( PewterBowlOfPotatos ), 3, 20, 0x1601, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfStew ), 3, 20, 0x1604, 0 ) ); + Add( new GenericBuyInfo( typeof( WoodenBowlOfTomatoSoup ), 3, 20, 0x1606, 0 ) ); + + Add( new GenericBuyInfo( typeof( ApplePie ), 7, 20, 0x1041, 0 ) ); //OSI just has Pie, not Apple/Fruit/Meat + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBWeaponSmith.cs b/Scripts/Mobiles/Vendors/SBInfo/SBWeaponSmith.cs new file mode 100644 index 000000000..6649e248d --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBWeaponSmith.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBWeaponSmith: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBWeaponSmith() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BlackStaff ), 22, 20, 0xDF1, 0 ) ); + Add( new GenericBuyInfo( typeof( Club ), 16, 20, 0x13B4, 0 ) ); + Add( new GenericBuyInfo( typeof( GnarledStaff ), 16, 20, 0x13F8, 0 ) ); + Add( new GenericBuyInfo( typeof( Mace ), 28, 20, 0xF5C, 0 ) ); + Add( new GenericBuyInfo( typeof( Maul ), 21, 20, 0x143B, 0 ) ); + Add( new GenericBuyInfo( typeof( QuarterStaff ), 19, 20, 0xE89, 0 ) ); + Add( new GenericBuyInfo( typeof( ShepherdsCrook ), 20, 20, 0xE81, 0 ) ); + Add( new GenericBuyInfo( typeof( SmithHammer ), 21, 20, 0x13E3, 0 ) ); + Add( new GenericBuyInfo( typeof( ShortSpear ), 23, 20, 0x1403, 0 ) ); + Add( new GenericBuyInfo( typeof( Spear ), 31, 20, 0xF62, 0 ) ); + Add( new GenericBuyInfo( typeof( WarHammer ), 25, 20, 0x1439, 0 ) ); + Add( new GenericBuyInfo( typeof( WarMace ), 31, 20, 0x1407, 0 ) ); + + if( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( Scepter ), 39, 20, 0x26BC, 0 ) ); + Add( new GenericBuyInfo( typeof( BladedStaff ), 40, 20, 0x26BD, 0 ) ); + } + + Add( new GenericBuyInfo( typeof( Hatchet ), 25, 20, 0xF44, 0 ) ); + Add( new GenericBuyInfo( typeof( Hatchet ), 27, 20, 0xF43, 0 ) ); + Add( new GenericBuyInfo( typeof( WarFork ), 32, 20, 0x1405, 0 ) ); + + switch ( Utility.Random( 3 )) + { + case 0: + { + Add( new GenericBuyInfo( typeof( ExecutionersAxe ), 30, 20, 0xF45, 0 ) ); + Add( new GenericBuyInfo( typeof( Bardiche ), 60, 20, 0xF4D, 0 ) ); + Add( new GenericBuyInfo( typeof( BattleAxe ), 26, 20, 0xF47, 0 ) ); + Add( new GenericBuyInfo( typeof( TwoHandedAxe ), 32, 20, 0x1443, 0 ) ); + + Add( new GenericBuyInfo( typeof( Bow ), 35, 20, 0x13B2, 0 ) ); + + Add( new GenericBuyInfo( typeof( ButcherKnife ), 14, 20, 0x13F6, 0 ) ); + + Add( new GenericBuyInfo( typeof( Crossbow ), 46, 20, 0xF50, 0 ) ); + Add( new GenericBuyInfo( typeof( HeavyCrossbow ), 55, 20, 0x13FD, 0 ) ); + + Add( new GenericBuyInfo( typeof( Cutlass ), 24, 20, 0x1441, 0 ) ); + Add( new GenericBuyInfo( typeof( Dagger ), 21, 20, 0xF52, 0 ) ); + Add( new GenericBuyInfo( typeof( Halberd ), 42, 20, 0x143E, 0 ) ); + + Add( new GenericBuyInfo( typeof( HammerPick ), 26, 20, 0x143D, 0 ) ); + + Add( new GenericBuyInfo( typeof( Katana ), 33, 20, 0x13FF, 0 ) ); + Add( new GenericBuyInfo( typeof( Kryss ), 32, 20, 0x1401, 0 ) ); + Add( new GenericBuyInfo( typeof( Broadsword ), 35, 20, 0xF5E, 0 ) ); + Add( new GenericBuyInfo( typeof( Longsword ), 55, 20, 0xF61, 0 ) ); + Add( new GenericBuyInfo( typeof( ThinLongsword ), 27, 20, 0x13B8, 0 ) ); + Add( new GenericBuyInfo( typeof( VikingSword ), 55, 20, 0x13B9, 0 ) ); + + Add( new GenericBuyInfo( typeof( Cleaver ), 15, 20, 0xEC3, 0 ) ); + Add( new GenericBuyInfo( typeof( Axe ), 40, 20, 0xF49, 0 ) ); + Add( new GenericBuyInfo( typeof( DoubleAxe ), 52, 20, 0xF4B, 0 ) ); + Add( new GenericBuyInfo( typeof( Pickaxe ), 22, 20, 0xE86, 0 ) ); + + Add( new GenericBuyInfo( typeof( Pitchfork ), 19, 20, 0xE87, 0 ) ); + + Add( new GenericBuyInfo( typeof( Scimitar ), 36, 20, 0x13B6, 0 ) ); + + Add( new GenericBuyInfo( typeof( SkinningKnife ), 14, 20, 0xEC4, 0 ) ); + + Add( new GenericBuyInfo( typeof( LargeBattleAxe ), 33, 20, 0x13FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WarAxe ), 29, 20, 0x13B0, 0 ) ); + + if ( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( BoneHarvester ), 35, 20, 0x26BB, 0 ) ); + Add( new GenericBuyInfo( typeof( CrescentBlade ), 37, 20, 0x26C1, 0 ) ); + Add( new GenericBuyInfo( typeof( DoubleBladedStaff ), 35, 20, 0x26BF, 0 ) ); + Add( new GenericBuyInfo( typeof( Lance ), 34, 20, 0x26C0, 0 ) ); + Add( new GenericBuyInfo( typeof( Pike ), 39, 20, 0x26BE, 0 ) ); + Add( new GenericBuyInfo( typeof( Scythe ), 39, 20, 0x26BA, 0 ) ); + Add( new GenericBuyInfo( typeof( CompositeBow ), 50, 20, 0x26C2, 0 ) ); + Add( new GenericBuyInfo( typeof( RepeatingCrossbow ), 57, 20, 0x26C3, 0 ) ); + } + + break; + } + + } + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BattleAxe ), 13 ); + Add( typeof( DoubleAxe ), 26 ); + Add( typeof( ExecutionersAxe ), 15 ); + Add( typeof( LargeBattleAxe ),16 ); + Add( typeof( Pickaxe ), 11 ); + Add( typeof( TwoHandedAxe ), 16 ); + Add( typeof( WarAxe ), 14 ); + Add( typeof( Axe ), 20 ); + + Add( typeof( Bardiche ), 30 ); + Add( typeof( Halberd ), 21 ); + + Add( typeof( ButcherKnife ), 7 ); + Add( typeof( Cleaver ), 7 ); + Add( typeof( Dagger ), 10 ); + Add( typeof( SkinningKnife ), 7 ); + + Add( typeof( Club ), 8 ); + Add( typeof( HammerPick ), 13 ); + Add( typeof( Mace ), 14 ); + Add( typeof( Maul ), 10 ); + Add( typeof( WarHammer ), 12 ); + Add( typeof( WarMace ), 15 ); + + Add( typeof( HeavyCrossbow ), 27 ); + Add( typeof( Bow ), 17 ); + Add( typeof( Crossbow ), 23 ); + + if( Core.AOS ) + { + Add( typeof( CompositeBow ), 25 ); + Add( typeof( RepeatingCrossbow ), 28 ); + Add( typeof( Scepter ), 20 ); + Add( typeof( BladedStaff ), 20 ); + Add( typeof( Scythe ), 19 ); + Add( typeof( BoneHarvester ), 17 ); + Add( typeof( Scepter ), 18 ); + Add( typeof( BladedStaff ), 16 ); + Add( typeof( Pike ), 19 ); + Add( typeof( DoubleBladedStaff ), 17 ); + Add( typeof( Lance ), 17 ); + Add( typeof( CrescentBlade ), 18 ); + } + + Add( typeof( Spear ), 15 ); + Add( typeof( Pitchfork ), 9 ); + Add( typeof( ShortSpear ), 11 ); + + Add( typeof( BlackStaff ), 11 ); + Add( typeof( GnarledStaff ), 8 ); + Add( typeof( QuarterStaff ), 9 ); + Add( typeof( ShepherdsCrook ), 10 ); + + Add( typeof( SmithHammer ), 10 ); + + Add( typeof( Broadsword ), 17 ); + Add( typeof( Cutlass ), 12 ); + Add( typeof( Katana ), 16 ); + Add( typeof( Kryss ), 16 ); + Add( typeof( Longsword ), 27 ); + Add( typeof( Scimitar ), 18 ); + Add( typeof( ThinLongsword ), 13 ); + Add( typeof( VikingSword ), 27 ); + + Add( typeof( Hatchet ), 13 ); + Add( typeof( WarFork ), 16 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/SBWeaver.cs b/Scripts/Mobiles/Vendors/SBInfo/SBWeaver.cs new file mode 100644 index 000000000..c5026a87b --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SBWeaver.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBWeaver: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBWeaver() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Dyes ), 8, 20, 0xFA9, 0 ) ); + Add( new GenericBuyInfo( typeof( DyeTub ), 8, 20, 0xFAB, 0 ) ); + + Add( new GenericBuyInfo( typeof( UncutCloth ), 3, 20, 0x1761, 0 ) ); + Add( new GenericBuyInfo( typeof( UncutCloth ), 3, 20, 0x1762, 0 ) ); + Add( new GenericBuyInfo( typeof( UncutCloth ), 3, 20, 0x1763, 0 ) ); + Add( new GenericBuyInfo( typeof( UncutCloth ), 3, 20, 0x1764, 0 ) ); + + Add( new GenericBuyInfo( typeof( BoltOfCloth ), 100, 20, 0xf9B, 0 ) ); + Add( new GenericBuyInfo( typeof( BoltOfCloth ), 100, 20, 0xf9C, 0 ) ); + Add( new GenericBuyInfo( typeof( BoltOfCloth ), 100, 20, 0xf96, 0 ) ); + Add( new GenericBuyInfo( typeof( BoltOfCloth ), 100, 20, 0xf97, 0 ) ); + + Add( new GenericBuyInfo( typeof( DarkYarn ), 18, 20, 0xE1D, 0 ) ); + Add( new GenericBuyInfo( typeof( LightYarn ), 18, 20, 0xE1E, 0 ) ); + Add( new GenericBuyInfo( typeof( LightYarnUnraveled ), 18, 20, 0xE1F, 0 ) ); + + Add( new GenericBuyInfo( typeof( Scissors ), 11, 20, 0xF9F, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Scissors ), 6 ); + Add( typeof( Dyes ), 4 ); + Add( typeof( DyeTub ), 4 ); + Add( typeof( UncutCloth ), 1 ); + Add( typeof( BoltOfCloth ), 50 ); + Add( typeof( LightYarnUnraveled ), 9 ); + Add( typeof( LightYarn ), 9 ); + Add( typeof( DarkYarn ), 9 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEArmor.cs new file mode 100644 index 000000000..efdaff96b --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEArmor.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSEArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSEArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( PlateHatsuburi ), 76, 20, 0x2775, 0 ) ); + Add( new GenericBuyInfo( typeof( HeavyPlateJingasa ), 76, 20, 0x2777, 0 ) ); + Add( new GenericBuyInfo( typeof( DecorativePlateKabuto ), 95, 20, 0x2778, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateDo ), 310, 20, 0x277D, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateHiroSode ), 222, 20, 0x2780, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateSuneate ), 224, 20, 0x2788, 0 ) ); + Add( new GenericBuyInfo( typeof( PlateHaidate ), 235, 20, 0x278D, 0 ) ); + Add( new GenericBuyInfo( typeof( ChainHatsuburi ), 76, 20, 0x2774, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( PlateHatsuburi ), 38 ); + Add( typeof( HeavyPlateJingasa ), 38 ); + Add( typeof( DecorativePlateKabuto ), 47 ); + Add( typeof( PlateDo ), 155 ); + Add( typeof( PlateHiroSode ), 111 ); + Add( typeof( PlateSuneate ), 112 ); + Add( typeof( PlateHaidate), 117 ); + Add( typeof( ChainHatsuburi ), 38 ); + + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEBowyer.cs b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEBowyer.cs new file mode 100644 index 000000000..598d733f0 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEBowyer.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSEBowyer: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSEBowyer() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Yumi ), 53, 20, 0x27A5, 0 ) ); + Add( new GenericBuyInfo( typeof( Fukiya ), 20, 20, 0x27AA, 0 ) ); + Add( new GenericBuyInfo( typeof( Nunchaku ), 35, 20, 0x27AE, 0 ) ); + Add( new GenericBuyInfo( typeof( FukiyaDarts ), 3, 20, 0x2806, 0 ) ); + Add( new GenericBuyInfo( typeof( Bokuto ), 21, 20, 0x27A8, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Yumi ), 26 ); + Add( typeof( Fukiya ), 10 ); + Add( typeof( Nunchaku ), 17 ); + Add( typeof( FukiyaDarts ), 1 ); + Add( typeof( Bokuto ), 10 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SE/SBSECarpenter.cs b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSECarpenter.cs new file mode 100644 index 000000000..c4ee27529 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSECarpenter.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSECarpenter: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSECarpenter() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bokuto ), 21, 20, 0x27A8, 0 ) ); + Add( new GenericBuyInfo( typeof( Tetsubo ), 43, 20, 0x27A6, 0 ) ); + Add( new GenericBuyInfo( typeof( Fukiya ), 20, 20, 0x27AA, 0 ) ); + Add( new GenericBuyInfo( typeof( BambooFlute ), 21, 20, 0x2805, 0 ) ); + Add( new GenericBuyInfo( typeof( BambooFlute ), 21, 20, 0x2805, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Tetsubo ), 21 ); + Add( typeof( Fukiya ), 10 ); + Add( typeof( BambooFlute ), 10 ); + Add( typeof( Bokuto ), 10 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEFood.cs b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEFood.cs new file mode 100644 index 000000000..511755c24 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEFood.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSEFood: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSEFood() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E8, 0 ) ); + Add( new GenericBuyInfo( typeof( Wasabi ), 2, 20, 0x24E9, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2836, 0 ) ); + Add( new GenericBuyInfo( typeof( BentoBox ), 6, 20, 0x2837, 0 ) ); + Add( new GenericBuyInfo( typeof( GreenTeaBasket ), 2, 20, 0x284B, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Wasabi ), 1 ); + Add( typeof( BentoBox ), 3 ); + Add( typeof( GreenTeaBasket ), 1 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SE/SBSELeatherArmor.cs b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSELeatherArmor.cs new file mode 100644 index 000000000..1781fcced --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSELeatherArmor.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSELeatherArmor: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSELeatherArmor() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( LeatherJingasa ), 11, 20, 0x2776, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherDo ), 87, 20, 0x277B, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherHiroSode ), 49, 20, 0x277E, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherSuneate ), 55, 20, 0x2786, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherHaidate), 54, 20, 0x278A, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherNinjaPants ), 49, 20, 0x2791, 0 ) ); + Add( new GenericBuyInfo( typeof( LeatherNinjaJacket ), 51, 20, 0x2793, 0 ) ); + + Add( new GenericBuyInfo( typeof( StuddedMempo ), 61, 20, 0x279D, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedDo ), 130, 20, 0x277C, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedHiroSode ), 73, 20, 0x277F, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedSuneate ), 78, 20, 0x2787, 0 ) ); + Add( new GenericBuyInfo( typeof( StuddedHaidate ), 76, 20, 0x278B, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( LeatherJingasa ), 5 ); + Add( typeof( LeatherDo ), 42 ); + Add( typeof( LeatherHiroSode ), 23 ); + Add( typeof( LeatherSuneate ), 26 ); + Add( typeof( LeatherHaidate), 28 ); + Add( typeof( LeatherNinjaPants ), 25 ); + Add( typeof( LeatherNinjaJacket ), 26 ); + Add( typeof( StuddedMempo ), 28 ); + Add( typeof( StuddedDo ), 66 ); + Add( typeof( StuddedHiroSode ), 32 ); + Add( typeof( StuddedSuneate ), 40 ); + Add( typeof( StuddedHaidate ), 37 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEWeapons.cs b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEWeapons.cs new file mode 100644 index 000000000..9b362ba9c --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/SE/SBSEWeapons.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSEWeapons: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSEWeapons() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( NoDachi ), 82, 20, 0x27A2, 0 ) ); + Add( new GenericBuyInfo( typeof( Tessen ), 83, 20, 0x27A3, 0 ) ); + Add( new GenericBuyInfo( typeof( Wakizashi ), 38, 20, 0x27A4, 0 ) ); + Add( new GenericBuyInfo( typeof( Tetsubo ), 43, 20, 0x27A6, 0 ) ); + Add( new GenericBuyInfo( typeof( Lajatang ), 108, 20, 0x27A7, 0 ) ); + Add( new GenericBuyInfo( typeof( Daisho ), 66, 20, 0x27A9, 0 ) ); + Add( new GenericBuyInfo( typeof( Tekagi ), 55, 20, 0x27AB, 0 ) ); + Add( new GenericBuyInfo( typeof( Shuriken ), 18, 20, 0x27AC, 0 ) ); + Add( new GenericBuyInfo( typeof( Kama ), 61, 20, 0x27AD, 0 ) ); + Add( new GenericBuyInfo( typeof( Sai ), 56, 20, 0x27AF, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( NoDachi ), 41 ); + Add( typeof( Tessen ), 41 ); + Add( typeof( Wakizashi ), 19 ); + Add( typeof( Tetsubo ), 21 ); + Add( typeof( Lajatang ), 54 ); + Add( typeof( Daisho ), 33 ); + Add( typeof( Tekagi ), 22 ); + Add( typeof( Shuriken), 9 ); + Add( typeof( Kama ), 30 ); + Add( typeof( Sai ), 28 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBAxeWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBAxeWeapon.cs new file mode 100644 index 000000000..d36594e0d --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBAxeWeapon.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBAxeWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBAxeWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( ExecutionersAxe ), 30, 20, 0xF45, 0 ) ); + Add( new GenericBuyInfo( typeof( BattleAxe ), 26, 20, 0xF47, 0 ) ); + Add( new GenericBuyInfo( typeof( TwoHandedAxe ), 32, 20, 0x1443, 0 ) ); + Add( new GenericBuyInfo( typeof( Axe ), 40, 20, 0xF49, 0 ) ); + Add( new GenericBuyInfo( typeof( DoubleAxe ), 52, 20, 0xF4B, 0 ) ); + Add( new GenericBuyInfo( typeof( Pickaxe ), 22, 20, 0xE86, 0 ) ); + Add( new GenericBuyInfo( typeof( LargeBattleAxe ), 33, 20, 0x13FB, 0 ) ); + Add( new GenericBuyInfo( typeof( WarAxe ), 29, 20, 0x13B0, 0 ) ); + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BattleAxe ), 13 ); + Add( typeof( DoubleAxe ), 26 ); + Add( typeof( ExecutionersAxe ), 15 ); + Add( typeof( LargeBattleAxe ),16 ); + Add( typeof( Pickaxe ), 11 ); + Add( typeof( TwoHandedAxe ), 16 ); + Add( typeof( WarAxe ), 14 ); + Add( typeof( Axe ), 20 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBKnifeWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBKnifeWeapon.cs new file mode 100644 index 000000000..222c0bd08 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBKnifeWeapon.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBKnifeWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBKnifeWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( ButcherKnife ), 14, 20, 0x13F6, 0 ) ); + Add( new GenericBuyInfo( typeof( Dagger ), 21, 20, 0xF52, 0 ) ); + Add( new GenericBuyInfo( typeof( Cleaver ), 15, 20, 0xEC3, 0 ) ); + Add( new GenericBuyInfo( typeof( SkinningKnife ), 14, 20, 0xEC4, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( ButcherKnife ), 7 ); + Add( typeof( Cleaver ), 7 ); + Add( typeof( Dagger ), 10 ); + Add( typeof( SkinningKnife ), 7 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBMaceWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBMaceWeapon.cs new file mode 100644 index 000000000..2c4a939f3 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBMaceWeapon.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBMaceWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBMaceWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( HammerPick ), 26, 20, 0x143D, 0 ) ); + Add( new GenericBuyInfo( typeof( Club ), 16, 20, 0x13B4, 0 ) ); + Add( new GenericBuyInfo( typeof( Mace ), 28, 20, 0xF5C, 0 ) ); + Add( new GenericBuyInfo( typeof( Maul ), 21, 20, 0x143B, 0 ) ); + Add( new GenericBuyInfo( typeof( WarHammer ), 25, 20, 0x1439, 0 ) ); + Add( new GenericBuyInfo( typeof( WarMace ), 31, 20, 0x1407, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Club ), 8 ); + Add( typeof( HammerPick ), 13 ); + Add( typeof( Mace ), 14 ); + Add( typeof( Maul ), 10 ); + Add( typeof( WarHammer ), 12 ); + Add( typeof( WarMace ), 15 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBPoleArmWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBPoleArmWeapon.cs new file mode 100644 index 000000000..3b9eb56a8 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBPoleArmWeapon.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBPoleArmWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBPoleArmWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Bardiche ), 60, 20, 0xF4D, 0 ) ); + Add( new GenericBuyInfo( typeof( Halberd ), 42, 20, 0x143E, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bardiche ), 30 ); + Add( typeof( Halberd ), 21 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBRangedWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBRangedWeapon.cs new file mode 100644 index 000000000..24ba26d68 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBRangedWeapon.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBRangedWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBRangedWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Crossbow ), 55, 20, 0xF50, 0 ) ); + Add( new GenericBuyInfo( typeof( HeavyCrossbow ), 55, 20, 0x13FD, 0 ) ); + if( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( RepeatingCrossbow ), 46, 20, 0x26C3, 0 ) ); + Add( new GenericBuyInfo( typeof( CompositeBow ), 45, 20, 0x26C2, 0 ) ); + } + Add( new GenericBuyInfo( typeof( Bolt ), 2, Utility.Random( 30, 60 ), 0x1BFB, 0 ) ); + Add( new GenericBuyInfo( typeof( Bow ), 40, 20, 0x13B2, 0 ) ); + Add( new GenericBuyInfo( typeof( Arrow ), 2, Utility.Random( 30, 60 ), 0xF3F, 0 ) ); + Add( new GenericBuyInfo( typeof( Feather ), 2, Utility.Random( 30, 60 ), 0x1BD1, 0 ) ); + Add( new GenericBuyInfo( typeof( Shaft ), 3, Utility.Random( 30, 60 ), 0x1BD4, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Bolt ), 1 ); + Add( typeof( Arrow ), 1 ); + Add( typeof( Shaft ), 1 ); + Add( typeof( Feather ), 1 ); + + Add( typeof( HeavyCrossbow ), 27 ); + Add( typeof( Bow ), 17 ); + Add( typeof( Crossbow ), 25 ); + + if( Core.AOS ) + { + Add( typeof( CompositeBow ), 23 ); + Add( typeof( RepeatingCrossbow ), 22 ); + } + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSpearForkWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSpearForkWeapon.cs new file mode 100644 index 000000000..489bee905 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSpearForkWeapon.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSpearForkWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSpearForkWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Pitchfork ), 19, 20, 0xE87, 0 ) ); + Add( new GenericBuyInfo( typeof( ShortSpear ), 23, 20, 0x1403, 0 ) ); + Add( new GenericBuyInfo( typeof( Spear ), 31, 20, 0xF62, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Spear ), 15 ); + Add( typeof( Pitchfork ), 9 ); + Add( typeof( ShortSpear ), 11 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBStavesWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBStavesWeapon.cs new file mode 100644 index 000000000..3acc18ed0 --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBStavesWeapon.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBStavesWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBStavesWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( BlackStaff ), 22, 20, 0xDF1, 0 ) ); + Add( new GenericBuyInfo( typeof( GnarledStaff ), 16, 20, 0x13F8, 0 ) ); + Add( new GenericBuyInfo( typeof( QuarterStaff ), 19, 20, 0xE89, 0 ) ); + Add( new GenericBuyInfo( typeof( ShepherdsCrook ), 20, 20, 0xE81, 0 ) ); + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( BlackStaff ), 11 ); + Add( typeof( GnarledStaff ), 8 ); + Add( typeof( QuarterStaff ), 9 ); + Add( typeof( ShepherdsCrook ), 10 ); + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSwordWeapon.cs b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSwordWeapon.cs new file mode 100644 index 000000000..3ffe9609d --- /dev/null +++ b/Scripts/Mobiles/Vendors/SBInfo/Weapons/SBSwordWeapon.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server.Items; + +namespace Server.Mobiles +{ + public class SBSwordWeapon: SBInfo + { + private ArrayList m_BuyInfo = new InternalBuyInfo(); + private IShopSellInfo m_SellInfo = new InternalSellInfo(); + + public SBSwordWeapon() + { + } + + public override IShopSellInfo SellInfo { get { return m_SellInfo; } } + public override ArrayList BuyInfo { get { return m_BuyInfo; } } + + public class InternalBuyInfo : ArrayList + { + public InternalBuyInfo() + { + Add( new GenericBuyInfo( typeof( Cutlass ), 24, 20, 0x1441, 0 ) ); + Add( new GenericBuyInfo( typeof( Katana ), 33, 20, 0x13FF, 0 ) ); + Add( new GenericBuyInfo( typeof( Kryss ), 32, 20, 0x1401, 0 ) ); + Add( new GenericBuyInfo( typeof( Broadsword ), 35, 20, 0xF5E, 0 ) ); + Add( new GenericBuyInfo( typeof( Longsword ), 55, 20, 0xF61, 0 ) ); + Add( new GenericBuyInfo( typeof( ThinLongsword ), 27, 20, 0x13B8, 0 ) ); + Add( new GenericBuyInfo( typeof( VikingSword ), 55, 20, 0x13B9, 0 ) ); + Add( new GenericBuyInfo( typeof( Scimitar ), 36, 20, 0x13B6, 0 ) ); + + if ( Core.AOS ) + { + Add( new GenericBuyInfo( typeof( BoneHarvester ), 35, 20, 0x26BB, 0 ) ); + Add( new GenericBuyInfo( typeof( CrescentBlade ), 37, 20, 0x26C1, 0 ) ); + Add( new GenericBuyInfo( typeof( DoubleBladedStaff ), 35, 20, 0x26BF, 0 ) ); + Add( new GenericBuyInfo( typeof( Lance ), 34, 20, 0x26C0, 0 ) ); + Add( new GenericBuyInfo( typeof( Pike ), 39, 20, 0x26BE, 0 ) ); + Add( new GenericBuyInfo( typeof( Scythe ), 39, 20, 0x26BA, 0 ) ); + } + + + } + } + + public class InternalSellInfo : GenericSellInfo + { + public InternalSellInfo() + { + Add( typeof( Broadsword ), 17 ); + Add( typeof( Cutlass ), 12 ); + Add( typeof( Katana ), 16 ); + Add( typeof( Kryss ), 16 ); + Add( typeof( Longsword ), 27 ); + Add( typeof( Scimitar ), 18 ); + Add( typeof( ThinLongsword ), 13 ); + Add( typeof( VikingSword ), 27 ); + + if ( Core.AOS ) + { + Add( typeof( Scythe ), 19 ); + Add( typeof( BoneHarvester ), 17 ); + Add( typeof( Scepter ), 18 ); + Add( typeof( BladedStaff ), 16 ); + Add( typeof( Pike ), 19 ); + Add( typeof( DoubleBladedStaff ), 17 ); + Add( typeof( Lance ), 17 ); + Add( typeof( CrescentBlade ), 18 ); + } + } + } + } +} diff --git a/Scripts/Mobiles/Vendors/VendorInventory.cs b/Scripts/Mobiles/Vendors/VendorInventory.cs new file mode 100644 index 000000000..9bf000ef6 --- /dev/null +++ b/Scripts/Mobiles/Vendors/VendorInventory.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections; +using Server; +using Server.Multis; + +namespace Server.Mobiles +{ + public class VendorInventory + { + public static readonly TimeSpan GracePeriod = TimeSpan.FromDays( 7.0 ); + + private BaseHouse m_House; + private string m_VendorName; + private string m_ShopName; + private Mobile m_Owner; + + private ArrayList m_Items; + private int m_Gold; + + private DateTime m_ExpireTime; + private Timer m_ExpireTimer; + + public VendorInventory( BaseHouse house, Mobile owner, string vendorName, string shopName ) + { + m_House = house; + m_Owner = owner; + m_VendorName = vendorName; + m_ShopName = shopName; + + m_Items = new ArrayList(); + + m_ExpireTime = DateTime.Now + GracePeriod; + m_ExpireTimer = new ExpireTimer( this, GracePeriod ); + m_ExpireTimer.Start(); + } + + public BaseHouse House + { + get{ return m_House; } + set{ m_House = value; } + } + + public string VendorName + { + get{ return m_VendorName; } + set{ m_VendorName = value; } + } + + public string ShopName + { + get{ return m_ShopName; } + set{ m_ShopName = value; } + } + + public Mobile Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + public ArrayList Items + { + get{ return m_Items; } + } + + public int Gold + { + get{ return m_Gold; } + set{ m_Gold = value; } + } + + public DateTime ExpireTime + { + get{ return m_ExpireTime; } + } + + public void AddItem( Item item ) + { + item.Internalize(); + m_Items.Add( item ); + } + + public void Delete() + { + foreach ( Item item in Items ) + { + item.Delete(); + } + + Items.Clear(); + Gold = 0; + + if ( House != null ) + House.VendorInventories.Remove( this ); + + m_ExpireTimer.Stop(); + } + + public void Serialize( GenericWriter writer ) + { + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (Mobile) m_Owner ); + writer.Write( (string) m_VendorName ); + writer.Write( (string) m_ShopName ); + + writer.WriteItemList( m_Items, true ); + writer.Write( (int) m_Gold ); + + writer.WriteDeltaTime( m_ExpireTime ); + } + + public VendorInventory( BaseHouse house, GenericReader reader ) + { + m_House = house; + + int version = reader.ReadEncodedInt(); + + m_Owner = reader.ReadMobile(); + m_VendorName = reader.ReadString(); + m_ShopName = reader.ReadString(); + + m_Items = reader.ReadItemList(); + m_Gold = reader.ReadInt(); + + m_ExpireTime = reader.ReadDeltaTime(); + + if ( m_Items.Count == 0 && m_Gold == 0 ) + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Delete ) ); + } + else + { + TimeSpan delay = m_ExpireTime - DateTime.Now; + m_ExpireTimer = new ExpireTimer( this, delay > TimeSpan.Zero ? delay : TimeSpan.Zero ); + m_ExpireTimer.Start(); + } + } + + private class ExpireTimer : Timer + { + private VendorInventory m_Inventory; + + public ExpireTimer( VendorInventory inventory, TimeSpan delay ) : base( delay ) + { + m_Inventory = inventory; + + Priority = TimerPriority.OneMinute; + } + + protected override void OnTick() + { + BaseHouse house = m_Inventory.House; + + if ( house != null ) + { + if ( m_Inventory.Gold > 0 ) + { + if ( house.MovingCrate == null ) + house.MovingCrate = new MovingCrate( house ); + + Banker.Deposit( house.MovingCrate, m_Inventory.Gold ); + } + + foreach ( Item item in m_Inventory.Items ) + { + if ( !item.Deleted ) + house.DropToMovingCrate( item ); + } + + m_Inventory.Gold = 0; + m_Inventory.Items.Clear(); + } + + m_Inventory.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/BaseHouse.cs b/Scripts/Multis/BaseHouse.cs new file mode 100644 index 000000000..dfa50725c --- /dev/null +++ b/Scripts/Multis/BaseHouse.cs @@ -0,0 +1,3644 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Multis.Deeds; +using Server.Regions; +using Server.Network; +using Server.Targeting; +using Server.Accounting; +using Server.ContextMenus; +using Server.Gumps; +using Server.Guilds; +using Server.Engines.BulkOrders; + +namespace Server.Multis +{ + public abstract class BaseHouse : BaseMulti + { + public static bool NewVendorSystem{ get{ return Core.AOS; } } // Is new player vendor system enabled? + + public const int MaxCoOwners = 15; + public const int MaxFriends = 50; + public const int MaxBans = 50; + + public const bool DecayEnabled = true; + + public static void Decay_OnTick() + { + for ( int i = 0; i < m_AllHouses.Count; ++i ) + ((BaseHouse)m_AllHouses[i]).CheckDecay(); + } + + private DateTime m_LastRefreshed; + private bool m_RestrictDecay; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastRefreshed + { + get{ return m_LastRefreshed; } + set{ m_LastRefreshed = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool RestrictDecay + { + get{ return m_RestrictDecay; } + set{ m_RestrictDecay = value; } + } + + public virtual TimeSpan DecayPeriod{ get{ return TimeSpan.FromDays( 5.0 ); } } + + public virtual DecayType DecayType + { + get + { + if ( m_RestrictDecay || !DecayEnabled || DecayPeriod == TimeSpan.Zero ) + return DecayType.Ageless; + + if ( m_Owner == null ) + return Core.AOS ? DecayType.Condemned : DecayType.ManualRefresh; + + Account acct = m_Owner.Account as Account; + + if ( acct == null ) + return Core.AOS ? DecayType.Condemned : DecayType.ManualRefresh; + + if ( acct.AccessLevel >= AccessLevel.GameMaster ) + return DecayType.Ageless; + + for ( int i = 0; i < acct.Length; ++i ) + { + Mobile mob = acct[i]; + + if ( mob != null && mob.AccessLevel >= AccessLevel.GameMaster ) + return DecayType.Ageless; + } + + if ( !Core.AOS ) + return DecayType.ManualRefresh; + + if ( (acct.LastLogin + TimeSpan.FromDays( 90.0 )) < DateTime.Now ) + return DecayType.Condemned; + + ArrayList allHouses = new ArrayList( 2 ); + + for ( int i = 0; i < acct.Length; ++i ) + { + Mobile mob = acct[i]; + + if ( mob != null ) + allHouses.AddRange( GetHouses( mob ) ); + } + + BaseHouse newest = null; + + for ( int i = 0; i < allHouses.Count; ++i ) + { + BaseHouse check = (BaseHouse)allHouses[i]; + + if ( newest == null || IsNewer( check, newest ) ) + newest = check; + } + + if ( this == newest ) + return DecayType.AutoRefresh; + + return DecayType.ManualRefresh; + } + } + + public bool IsNewer( BaseHouse check, BaseHouse house ) + { + DateTime checkTime = ( check.LastTraded > check.BuiltOn ? check.LastTraded : check.BuiltOn ); + DateTime houseTime = ( house.LastTraded > house.BuiltOn ? house.LastTraded : house.BuiltOn ); + + return ( checkTime > houseTime ); + } + + public virtual bool CanDecay + { + get + { + DecayType type = this.DecayType; + + return ( type == DecayType.Condemned || type == DecayType.ManualRefresh ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual DecayLevel DecayLevel + { + get + { + if ( !CanDecay ) + { + m_LastRefreshed = DateTime.Now; + return DecayLevel.Ageless; + } + + TimeSpan timeAfterRefresh = DateTime.Now - m_LastRefreshed; + int percent = (int) ((timeAfterRefresh.Ticks * 1000) / DecayPeriod.Ticks); + + if ( percent >= 1000 ) // 100.0% + return ( HasRentedVendors || VendorInventories.Count > 0 ) ? DecayLevel.DemolitionPending : DecayLevel.Collapsed; + else if ( percent >= 950 ) // 95.0% - 99.9% + return DecayLevel.IDOC; + else if ( percent >= 750 ) // 75.0% - 94.9% + return DecayLevel.Greatly; + else if ( percent >= 500 ) // 50.0% - 74.9% + return DecayLevel.Fairly; + else if ( percent >= 250 ) // 25.0% - 49.9% + return DecayLevel.Somewhat; + else if ( percent >= 005 ) // 00.5% - 24.9% + return DecayLevel.Slightly; + + return DecayLevel.LikeNew; + } + } + + public virtual bool RefreshDecay() + { + if ( DecayType == DecayType.Condemned ) + return false; + + DecayLevel oldLevel = this.DecayLevel; + + m_LastRefreshed = DateTime.Now; + + return ( oldLevel > DecayLevel.LikeNew ); + } + + public virtual bool CheckDecay() + { + if ( !Deleted && this.DecayLevel == DecayLevel.Collapsed ) + { + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Decay_Sandbox ) ); + return true; + } + + return false; + } + + public virtual void KillVendors() + { + ArrayList list = new ArrayList( PlayerVendors ); + + foreach ( PlayerVendor vendor in list ) + vendor.Destroy( true ); + + list = new ArrayList( PlayerBarkeepers ); + + foreach ( PlayerBarkeeper barkeeper in list ) + barkeeper.Delete(); + } + + public virtual void Decay_Sandbox() + { + if ( Deleted ) + return; + + KillVendors(); + Delete(); + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual double BonusStorageScalar { get { return (Core.ML ? 1.2 : 1.0); } } + + private bool m_Public; + + private HouseRegion m_Region; + private HouseSign m_Sign; + private TrashBarrel m_Trash; + private ArrayList m_Doors; + + private Mobile m_Owner; + + private ArrayList m_Access; + private ArrayList m_Bans; + private ArrayList m_CoOwners; + private ArrayList m_Friends; + + private ArrayList m_PlayerVendors = new ArrayList(); + private ArrayList m_PlayerBarkeepers = new ArrayList(); + + private ArrayList m_LockDowns; + private ArrayList m_VendorRentalContracts; + private ArrayList m_Secures; + + private ArrayList m_Addons; + + private ArrayList m_VendorInventories = new ArrayList(); + private ArrayList m_RelocatedEntities = new ArrayList(); + + private MovingCrate m_MovingCrate; + private ArrayList m_InternalizedVendors; + + private int m_MaxLockDowns; + private int m_MaxSecures; + private int m_Price; + + private int m_Visits; + + private DateTime m_BuiltOn, m_LastTraded; + + private Point3D m_RelativeBanLocation; + + private static Hashtable m_Table = new Hashtable(); + + public virtual bool IsAosRules{ get{ return Core.AOS; } } + + public virtual bool IsActive{ get{ return true; } } + + public virtual HousePlacementEntry GetAosEntry() + { + return HousePlacementEntry.Find( this ); + } + + public virtual int GetAosMaxSecures() + { + HousePlacementEntry hpe = GetAosEntry(); + + if ( hpe == null ) + return 0; + + return (int)(hpe.Storage * BonusStorageScalar); + } + + public virtual int GetAosMaxLockdowns() + { + HousePlacementEntry hpe = GetAosEntry(); + + if ( hpe == null ) + return 0; + + return (int)(hpe.Lockdowns * BonusStorageScalar); + } + + public virtual int GetAosCurSecures( out int fromSecures, out int fromVendors, out int fromLockdowns, out int fromMovingCrate ) + { + fromSecures = 0; + fromVendors = 0; + fromLockdowns = 0; + fromMovingCrate = 0; + + ArrayList list = m_Secures; + + for ( int i = 0; list != null && i < list.Count; ++i ) + { + SecureInfo si = (SecureInfo)list[i]; + + fromSecures += si.Item.TotalItems; + } + + if ( m_LockDowns != null ) + fromLockdowns += m_LockDowns.Count; + + if ( !NewVendorSystem ) + { + foreach ( PlayerVendor vendor in PlayerVendors ) + { + if ( vendor.Backpack != null ) + { + fromVendors += vendor.Backpack.TotalItems; + } + } + } + + if ( MovingCrate != null ) + { + fromMovingCrate += MovingCrate.TotalItems; + + foreach ( Item item in MovingCrate.Items ) + { + if ( item is PackingBox ) + fromMovingCrate--; + } + } + + return fromSecures + fromVendors + fromLockdowns + fromMovingCrate; + } + + public bool InRange( IPoint2D from, int range ) + { + if ( Region == null ) + return false; + + foreach ( Rectangle3D rect in Region.Area ) + { + if ( from.X >= rect.Start.X - range && from.Y >= rect.Start.Y - range && from.X < rect.End.X + range && from.Y < rect.End.Y + range ) + return true; + } + + return false; + } + + public virtual int GetNewVendorSystemMaxVendors() + { + HousePlacementEntry hpe = GetAosEntry(); + + if ( hpe == null ) + return 0; + + return (int)(hpe.Vendors * BonusStorageScalar); + } + + public virtual bool CanPlaceNewVendor() + { + if ( !IsAosRules ) + return true; + + if ( !NewVendorSystem ) + return CheckAosLockdowns( 10 ); + + return ( (PlayerVendors.Count + VendorRentalContracts.Count) < GetNewVendorSystemMaxVendors() ); + } + + public const int MaximumBarkeepCount = 2; + + public virtual bool CanPlaceNewBarkeep() + { + return ( PlayerBarkeepers.Count < MaximumBarkeepCount ); + } + + public static void IsThereVendor( Point3D location, Map map, out bool vendor, out bool rentalContract ) + { + vendor = false; + rentalContract = false; + + IPooledEnumerable eable = map.GetObjectsInRange( location, 0 ); + + foreach ( IEntity entity in eable ) + { + if ( Math.Abs( location.Z - entity.Z ) <= 16 ) + { + if ( entity is PlayerVendor || entity is PlayerBarkeeper || entity is PlayerVendorPlaceholder ) + { + vendor = true; + break; + } + + if ( entity is VendorRentalContract ) + { + rentalContract = true; + break; + } + } + } + + eable.Free(); + } + + public bool HasPersonalVendors + { + get + { + foreach ( PlayerVendor vendor in PlayerVendors ) + { + if ( !(vendor is RentedVendor) ) + return true; + } + + return false; + } + } + + public bool HasRentedVendors + { + get + { + foreach ( PlayerVendor vendor in PlayerVendors ) + { + if ( vendor is RentedVendor ) + return true; + } + + return false; + } + } + + public ArrayList AvailableVendorsFor( Mobile m ) + { + ArrayList list = new ArrayList(); + + foreach ( PlayerVendor vendor in PlayerVendors ) + { + if ( vendor.CanInteractWith( m, false ) ) + list.Add( vendor ); + } + + return list; + } + + public bool AreThereAvailableVendorsFor( Mobile m ) + { + foreach ( PlayerVendor vendor in PlayerVendors ) + { + if ( vendor.CanInteractWith( m, false ) ) + return true; + } + + return false; + } + + public void MoveAllToCrate() + { + RelocatedEntities.Clear(); + + if ( MovingCrate != null ) + MovingCrate.Hide(); + + if ( m_Trash != null ) + { + m_Trash.Delete(); + m_Trash = null; + } + + foreach ( Item item in LockDowns ) + { + if ( !item.Deleted ) + { + item.IsLockedDown = false; + item.IsSecure = false; + item.Movable = true; + + if ( item.Parent == null ) + DropToMovingCrate( item ); + } + } + + LockDowns.Clear(); + + foreach ( Item item in VendorRentalContracts ) + { + if ( !item.Deleted ) + { + item.IsLockedDown = false; + item.IsSecure = false; + item.Movable = true; + + if ( item.Parent == null ) + DropToMovingCrate( item ); + } + } + + VendorRentalContracts.Clear(); + + foreach ( SecureInfo info in Secures ) + { + Item item = info.Item; + + if ( !item.Deleted ) + { + if ( item is StrongBox ) + item = ((StrongBox)item).ConvertToStandardContainer(); + + item.IsLockedDown = false; + item.IsSecure = false; + item.Movable = true; + + if ( item.Parent == null ) + DropToMovingCrate( item ); + } + } + + Secures.Clear(); + + foreach ( Item addon in Addons ) + { + if ( !addon.Deleted ) + { + Item deed = null; + + if ( addon is IAddon ) + deed = ((IAddon)addon).Deed; + + if ( deed != null ) + { + addon.Delete(); + DropToMovingCrate( deed ); + } + else + { + DropToMovingCrate( addon ); + } + } + } + + Addons.Clear(); + + foreach ( PlayerVendor mobile in PlayerVendors ) + { + mobile.Return(); + mobile.Internalize(); + InternalizedVendors.Add( mobile ); + } + + foreach ( Mobile mobile in PlayerBarkeepers ) + { + mobile.Internalize(); + InternalizedVendors.Add( mobile ); + } + } + + public ArrayList GetHouseEntities() + { + ArrayList list = new ArrayList(); + + if ( MovingCrate != null ) + MovingCrate.Hide(); + + if ( m_Trash != null && m_Trash.Map != Map.Internal ) + list.Add( m_Trash ); + + foreach ( Item item in LockDowns ) + { + if ( item.Parent == null && item.Map != Map.Internal ) + list.Add( item ); + } + + foreach ( Item item in VendorRentalContracts ) + { + if ( item.Parent == null && item.Map != Map.Internal ) + list.Add( item ); + } + + foreach ( SecureInfo info in Secures ) + { + Item item = info.Item; + + if ( item.Parent == null && item.Map != Map.Internal ) + list.Add( item ); + } + + foreach ( Item item in Addons ) + { + if ( item.Parent == null && item.Map != Map.Internal ) + list.Add( item ); + } + + foreach ( PlayerVendor mobile in PlayerVendors ) + { + mobile.Return(); + + if ( mobile.Map != Map.Internal ) + list.Add( mobile ); + } + + foreach ( Mobile mobile in PlayerBarkeepers ) + { + if ( mobile.Map != Map.Internal ) + list.Add( mobile ); + } + + return list; + } + + public void RelocateEntities() + { + foreach ( IEntity entity in GetHouseEntities() ) + { + Point3D relLoc = new Point3D( entity.X - this.X, entity.Y - this.Y, entity.Z - this.Z ); + RelocatedEntity relocEntity = new RelocatedEntity( entity, relLoc ); + + RelocatedEntities.Add( relocEntity ); + + if ( entity is Item ) + ((Item)entity).Internalize(); + else + ((Mobile)entity).Internalize(); + } + } + + public void RestoreRelocatedEntities() + { + foreach ( RelocatedEntity relocEntity in RelocatedEntities ) + { + Point3D relLoc = relocEntity.RelativeLocation; + Point3D location = new Point3D( relLoc.X + this.X, relLoc.Y + this.Y, relLoc.Z + this.Z ); + + IEntity entity = relocEntity.Entity; + if ( entity is Item ) + { + Item item = (Item) entity; + + if ( !item.Deleted ) + { + if ( item is IAddon ) + { + if ( ((IAddon)item).CouldFit( location, this.Map ) ) + { + item.MoveToWorld( location, this.Map ); + continue; + } + } + else + { + int height; + bool requireSurface; + if ( item is VendorRentalContract ) + { + height = 16; + requireSurface = true; + } + else + { + height = item.ItemData.Height; + requireSurface = false; + } + + if ( this.Map.CanFit( location.X, location.Y, location.Z, height, false, false, requireSurface ) ) + { + item.MoveToWorld( location, this.Map ); + continue; + } + } + + // The item can't fit + + if ( item is TrashBarrel ) + { + item.Delete(); // Trash barrels don't go to the moving crate + } + else + { + SetLockdown( item, false ); + item.IsSecure = false; + item.Movable = true; + + Item relocateItem = item; + + if ( item is StrongBox ) + relocateItem = ((StrongBox)item).ConvertToStandardContainer(); + + if ( item is IAddon ) + { + relocateItem = ((IAddon)item).Deed; + item.Delete(); + } + + if ( relocateItem != null ) + DropToMovingCrate( relocateItem ); + } + } + + if ( m_Trash == item ) + m_Trash = null; + + LockDowns.Remove( item ); + VendorRentalContracts.Remove( item ); + Addons.Remove( item ); + for ( int i = Secures.Count - 1; i >= 0; i-- ) + { + if ( ((SecureInfo)Secures[i]).Item == item ) + Secures.RemoveAt( i ); + } + } + else + { + Mobile mobile = (Mobile) entity; + + if ( !mobile.Deleted ) + { + if ( this.Map.CanFit( location, 16, false, false ) ) + { + mobile.MoveToWorld( location, this.Map ); + } + else + { + InternalizedVendors.Add( mobile ); + } + } + } + } + + RelocatedEntities.Clear(); + } + + public void DropToMovingCrate( Item item ) + { + if ( MovingCrate == null ) + MovingCrate = new MovingCrate( this ); + + MovingCrate.DropItem( item ); + } + + public ArrayList GetItems() + { + if( this.Map == null || this.Map == Map.Internal ) + return new ArrayList(); + + Point2D start = new Point2D( this.X + Components.Min.X, this.Y + Components.Min.Y ); + Point2D end = new Point2D( this.X + Components.Max.X + 1, this.Y + Components.Max.Y + 1 ); + Rectangle2D rect = new Rectangle2D( start, end ); + + ArrayList list = new ArrayList(); + + IPooledEnumerable eable = this.Map.GetItemsInBounds( rect ); + foreach ( Item item in eable ) + { + if ( item.Movable && IsInside( item ) ) + list.Add( item ); + } + eable.Free(); + + return list; + } + + public ArrayList GetMobiles() + { + if( this.Map == null || this.Map == Map.Internal ) + return new ArrayList(); + + ArrayList list = new ArrayList(); + + foreach ( Mobile mobile in Region.GetMobiles() ) + { + if ( IsInside( mobile ) ) + list.Add( mobile ); + } + + return list; + } + + public virtual bool CheckAosLockdowns( int need ) + { + return ( (GetAosCurLockdowns() + need) <= GetAosMaxLockdowns() ); + } + + public virtual bool CheckAosStorage( int need ) + { + int fromSecures, fromVendors, fromLockdowns, fromMovingCrate; + + return ( (GetAosCurSecures( out fromSecures, out fromVendors, out fromLockdowns, out fromMovingCrate ) + need) <= GetAosMaxSecures() ); + } + + public static void Configure() + { + Item.LockedDownFlag = 1; + Item.SecureFlag = 2; + + Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.0 ), new TimerCallback( Decay_OnTick ) ); + } + + public virtual int GetAosCurLockdowns() + { + int v = 0; + + if ( m_LockDowns != null ) + v += m_LockDowns.Count; + + if ( m_Secures != null ) + v += m_Secures.Count; + + if ( !NewVendorSystem ) + v += PlayerVendors.Count * 10; + + return v; + } + + public static bool CheckLockedDown( Item item ) + { + BaseHouse house = FindHouseAt( item ); + + return ( house != null && house.IsLockedDown( item ) ); + } + + public static bool CheckSecured( Item item ) + { + BaseHouse house = FindHouseAt( item ); + + return ( house != null && house.IsSecure( item ) ); + } + + public static bool CheckLockedDownOrSecured( Item item ) + { + BaseHouse house = FindHouseAt( item ); + + return ( house != null && (house.IsSecure( item ) || house.IsLockedDown( item )) ); + } + + public static ArrayList GetHouses( Mobile m ) + { + ArrayList list = new ArrayList(); + + if ( m != null ) + { + ArrayList exists = (ArrayList)m_Table[m]; + + if ( exists != null ) + { + for ( int i = 0; i < exists.Count; ++i ) + { + BaseHouse house = exists[i] as BaseHouse; + + if ( house != null && !house.Deleted && house.Owner == m ) + list.Add( house ); + } + } + } + + return list; + } + + public static bool CheckHold( Mobile m, Container cont, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + BaseHouse house = FindHouseAt( cont ); + + if ( house == null || !house.IsAosRules ) + return true; + + if ( house.IsSecure( cont ) && !house.CheckAosStorage( 1 + item.TotalItems + plusItems ) ) + { + if ( message ) + m.SendLocalizedMessage( 1061839 ); // This action would exceed the secure storage limit of the house. + + return false; + } + + return true; + } + + public static bool CheckAccessible( Mobile m, Item item ) + { + if ( m.AccessLevel >= AccessLevel.GameMaster ) + return true; // Staff can access anything + + BaseHouse house = FindHouseAt( item ); + + if ( house == null ) + return true; + + SecureAccessResult res = house.CheckSecureAccess( m, item ); + + switch ( res ) + { + case SecureAccessResult.Insecure: break; + case SecureAccessResult.Accessible: return true; + case SecureAccessResult.Inaccessible: return false; + } + + if ( house.IsLockedDown( item ) ) + return house.IsCoOwner( m ) && (item is Container); + + return true; + } + + public static BaseHouse FindHouseAt( Mobile m ) + { + if ( m == null || m.Deleted ) + return null; + + return FindHouseAt( m.Location, m.Map, 16 ); + } + + public static BaseHouse FindHouseAt( Item item ) + { + if ( item == null || item.Deleted ) + return null; + + return FindHouseAt( item.GetWorldLocation(), item.Map, item.ItemData.Height ); + } + + public static BaseHouse FindHouseAt( Point3D loc, Map map, int height ) + { + if ( map == null || map == Map.Internal ) + return null; + + Sector sector = map.GetSector( loc ); + + for ( int i = 0; i < sector.Multis.Count; ++i ) + { + BaseHouse house = sector.Multis[i] as BaseHouse; + + if ( house != null && house.IsInside( loc, height ) ) + return house; + } + + return null; + } + + public bool IsInside( Mobile m ) + { + if ( m == null || m.Deleted || m.Map != this.Map ) + return false; + + return IsInside( m.Location, 16 ); + } + + public bool IsInside( Item item ) + { + if ( item == null || item.Deleted || item.Map != this.Map ) + return false; + + return IsInside( item.Location, item.ItemData.Height ); + } + + public bool CheckAccessibility( Item item, Mobile from ) + { + SecureAccessResult res = CheckSecureAccess( from, item ); + + switch ( res ) + { + case SecureAccessResult.Insecure: break; + case SecureAccessResult.Accessible: return true; + case SecureAccessResult.Inaccessible: return false; + } + + if ( !IsLockedDown( item ) ) + return true; + else if ( from.AccessLevel >= AccessLevel.GameMaster ) + return true; + else if ( item is Runebook ) + return true; + else if ( item is ISecurable ) + return HasSecureAccess( from, ((ISecurable)item).Level ); + else if ( item is Container ) + return IsCoOwner( from ); + else if ( item is BaseLight ) + return IsFriend( from ); + else if ( item is PotionKeg ) + return IsFriend( from ); + else if ( item is BaseBoard ) + return true; + else if ( item is Dices ) + return true; + else if ( item is RecallRune ) + return true; + else if ( item is TreasureMap ) + return true; + else if ( item is Clock ) + return true; + else if ( item is BaseBook ) + return true; + else if ( item is BaseInstrument ) + return true; + else if ( item is Dyes || item is DyeTub ) + return true; + else if ( item is VendorRentalContract ) + return true; + + return false; + } + + public virtual bool IsInside( Point3D p, int height ) + { + if ( Deleted ) + return false; + + MultiComponentList mcl = Components; + + int x = p.X - (X + mcl.Min.X); + int y = p.Y - (Y + mcl.Min.Y); + + if ( x < 0 || x >= mcl.Width || y < 0 || y >= mcl.Height ) + return false; + + if ( this is HouseFoundation && y < (mcl.Height-1) && p.Z >= this.Z ) + return true; + + Tile[] tiles = mcl.Tiles[x][y]; + + for ( int j = 0; j < tiles.Length; ++j ) + { + Tile tile = tiles[j]; + int id = tile.ID & 0x3FFF; + ItemData data = TileData.ItemTable[id]; + + // Slanted roofs do not count; they overhang blocking south and east sides of the multi + if ( (data.Flags & TileFlag.Roof) != 0 ) + continue; + + // Signs and signposts are not considered part of the multi + if ( (id >= 0xB95 && id <= 0xC0E) || (id >= 0xC43 && id <= 0xC44) ) + continue; + + int tileZ = tile.Z + this.Z; + + if ( p.Z == tileZ || (p.Z + height) > tileZ ) + return true; + } + + return false; + } + + public SecureAccessResult CheckSecureAccess( Mobile m, Item item ) + { + if ( m_Secures == null || !(item is Container) ) + return SecureAccessResult.Insecure; + + for ( int i = 0; i < m_Secures.Count; ++i ) + { + SecureInfo info = (SecureInfo)m_Secures[i]; + + if ( info.Item == item ) + return HasSecureAccess( m, info.Level ) ? SecureAccessResult.Accessible : SecureAccessResult.Inaccessible; + } + + return SecureAccessResult.Insecure; + } + + private static ArrayList m_AllHouses = new ArrayList(); + + public BaseHouse( int multiID, Mobile owner, int MaxLockDown, int MaxSecure ) : base( multiID | 0x4000 ) + { + m_AllHouses.Add( this ); + + m_LastRefreshed = DateTime.Now; + + m_BuiltOn = DateTime.Now; + m_LastTraded = DateTime.MinValue; + + m_Doors = new ArrayList(); + m_LockDowns = new ArrayList(); + m_Secures = new ArrayList(); + m_Addons = new ArrayList(); + + m_CoOwners = new ArrayList(); + m_Friends = new ArrayList(); + m_Bans = new ArrayList(); + m_Access = new ArrayList(); + + m_VendorRentalContracts = new ArrayList(); + m_InternalizedVendors = new ArrayList(); + + m_Owner = owner; + + m_MaxLockDowns = MaxLockDown; + m_MaxSecures = MaxSecure; + + m_RelativeBanLocation = this.BaseBanLocation; + + UpdateRegion(); + + if ( owner != null ) + { + ArrayList list = (ArrayList)m_Table[owner]; + + if ( list == null ) + m_Table[owner] = list = new ArrayList(); + + list.Add( this ); + } + + Movable = false; + } + + public BaseHouse( Serial serial ) : base( serial ) + { + m_AllHouses.Add( this ); + } + + public override void OnMapChange() + { + if ( m_LockDowns == null ) + return; + + UpdateRegion(); + + if ( m_Sign != null && !m_Sign.Deleted ) + m_Sign.Map = this.Map; + + if ( m_Doors != null ) + { + foreach ( Item item in m_Doors ) + item.Map = this.Map; + } + + foreach ( IEntity entity in GetHouseEntities() ) + { + if ( entity is Item ) + ((Item)entity).Map = this.Map; + else + ((Mobile)entity).Map = this.Map; + } + } + + public virtual void ChangeSignType( int itemID ) + { + if ( m_Sign != null ) + m_Sign.ItemID = itemID; + } + + public abstract Rectangle2D[] Area{ get; } + public abstract Point3D BaseBanLocation{ get; } + + public virtual void UpdateRegion() + { + if ( m_Region != null ) + m_Region.Unregister(); + + if ( this.Map != null ) + { + m_Region = new HouseRegion( this ); + m_Region.Register(); + } + else + { + m_Region = null; + } + } + + public override void OnLocationChange( Point3D oldLocation ) + { + if ( m_LockDowns == null ) + return; + + int x = base.Location.X - oldLocation.X; + int y = base.Location.Y - oldLocation.Y; + int z = base.Location.Z - oldLocation.Z; + + if ( m_Sign != null && !m_Sign.Deleted ) + m_Sign.Location = new Point3D( m_Sign.X + x, m_Sign.Y + y, m_Sign.Z + z ); + + UpdateRegion(); + + if ( m_Doors != null ) + { + foreach ( Item item in m_Doors ) + { + if ( !item.Deleted ) + item.Location = new Point3D( item.X + x, item.Y + y, item.Z + z ); + } + } + + foreach ( IEntity entity in GetHouseEntities() ) + { + Point3D newLocation = new Point3D( entity.X + x, entity.Y + y, entity.Z + z ); + + if ( entity is Item ) + ((Item)entity).Location = newLocation; + else + ((Mobile)entity).Location = newLocation; + } + } + + public BaseDoor AddEastDoor( int x, int y, int z ) + { + return AddEastDoor( true, x, y, z ); + } + + public BaseDoor AddEastDoor( bool wood, int x, int y, int z ) + { + BaseDoor door = MakeDoor( wood, DoorFacing.SouthCW ); + + AddDoor( door, x, y, z ); + + return door; + } + + public BaseDoor AddSouthDoor( int x, int y, int z ) + { + return AddSouthDoor( true, x, y, z ); + } + + public BaseDoor AddSouthDoor( bool wood, int x, int y, int z ) + { + BaseDoor door = MakeDoor( wood, DoorFacing.WestCW ); + + AddDoor( door, x, y, z ); + + return door; + } + + public BaseDoor AddEastDoor( int x, int y, int z, uint k ) + { + return AddEastDoor( true, x, y, z, k ); + } + + public BaseDoor AddEastDoor( bool wood, int x, int y, int z, uint k ) + { + BaseDoor door = MakeDoor( wood, DoorFacing.SouthCW ); + + door.Locked = true; + door.KeyValue = k; + + AddDoor( door, x, y, z ); + + return door; + } + + public BaseDoor AddSouthDoor( int x, int y, int z, uint k ) + { + return AddSouthDoor( true, x, y, z, k ); + } + + public BaseDoor AddSouthDoor( bool wood, int x, int y, int z, uint k ) + { + BaseDoor door = MakeDoor( wood, DoorFacing.WestCW ); + + door.Locked = true; + door.KeyValue = k; + + AddDoor( door, x, y, z ); + + return door; + } + + public BaseDoor[] AddSouthDoors( int x, int y, int z, uint k ) + { + return AddSouthDoors( true, x, y, z, k ); + } + + public BaseDoor[] AddSouthDoors( bool wood, int x, int y, int z, uint k ) + { + BaseDoor westDoor = MakeDoor( wood, DoorFacing.WestCW ); + BaseDoor eastDoor = MakeDoor( wood, DoorFacing.EastCCW ); + + westDoor.Locked = true; + eastDoor.Locked = true; + + westDoor.KeyValue = k; + eastDoor.KeyValue = k; + + westDoor.Link = eastDoor; + eastDoor.Link = westDoor; + + AddDoor( westDoor, x, y, z ); + AddDoor( eastDoor, x + 1, y, z ); + + return new BaseDoor[2]{ westDoor, eastDoor }; + } + + public uint CreateKeys( Mobile m ) + { + uint value = Key.RandomValue(); + + if ( !IsAosRules ) + { + Key packKey = new Key( KeyType.Gold ); + Key bankKey = new Key( KeyType.Gold ); + + packKey.KeyValue = value; + bankKey.KeyValue = value; + + packKey.LootType = LootType.Newbied; + bankKey.LootType = LootType.Newbied; + + BankBox box = m.BankBox; + + if ( box == null || !box.TryDropItem( m, bankKey, false ) ) + bankKey.Delete(); + + m.AddToBackpack( packKey ); + } + + return value; + } + + public BaseDoor[] AddSouthDoors( int x, int y, int z ) + { + return AddSouthDoors( true, x, y, z, false ); + } + + public BaseDoor[] AddSouthDoors( bool wood, int x, int y, int z, bool inv ) + { + BaseDoor westDoor = MakeDoor( wood, inv ? DoorFacing.WestCCW : DoorFacing.WestCW ); + BaseDoor eastDoor = MakeDoor( wood, inv ? DoorFacing.EastCW : DoorFacing.EastCCW ); + + westDoor.Link = eastDoor; + eastDoor.Link = westDoor; + + AddDoor( westDoor, x, y, z ); + AddDoor( eastDoor, x + 1, y, z ); + + return new BaseDoor[2]{ westDoor, eastDoor }; + } + + public BaseDoor MakeDoor( bool wood, DoorFacing facing ) + { + if ( wood ) + return new DarkWoodHouseDoor( facing ); + else + return new MetalHouseDoor( facing ); + } + + public void AddDoor( BaseDoor door, int xoff, int yoff, int zoff ) + { + door.MoveToWorld( new Point3D( xoff+this.X, yoff+this.Y, zoff+this.Z ), this.Map ); + m_Doors.Add( door ); + } + + public void AddTrashBarrel( Mobile from ) + { + if ( !IsActive ) + return; + + for ( int i = 0; m_Doors != null && i < m_Doors.Count; ++i ) + { + BaseDoor door = m_Doors[i] as BaseDoor; + Point3D p = door.Location; + + if ( door.Open ) + p = new Point3D( p.X - door.Offset.X, p.Y - door.Offset.Y, p.Z - door.Offset.Z ); + + if ( (from.Z + 16) >= p.Z && (p.Z + 16) >= from.Z ) + { + if ( from.InRange( p, 1 ) ) + { + from.SendLocalizedMessage( 502120 ); // You cannot place a trash barrel near a door or near steps. + return; + } + } + } + + if ( m_Trash == null || m_Trash.Deleted ) + { + m_Trash = new TrashBarrel(); + + m_Trash.Movable = false; + m_Trash.MoveToWorld( from.Location, from.Map ); + + from.SendLocalizedMessage( 502121 ); /* You have a new trash barrel. + * Three minutes after you put something in the barrel, the trash will be emptied. + * Be forewarned, this is permanent! */ + } + else + { + m_Trash.MoveToWorld( from.Location, from.Map ); + } + } + + public void SetSign( int xoff, int yoff, int zoff ) + { + m_Sign = new HouseSign( this ); + m_Sign.MoveToWorld( new Point3D( this.X + xoff, this.Y + yoff, this.Z + zoff ), this.Map ); + } + + private void SetLockdown( Item i, bool locked ) + { + SetLockdown( i, locked, false ); + } + + private void SetLockdown( Item i, bool locked, bool checkContains ) + { + if ( m_LockDowns == null ) + return; + + i.Movable = !locked; + i.IsLockedDown = locked; + + if ( locked ) + { + if ( i is VendorRentalContract ) + { + if ( !VendorRentalContracts.Contains( i ) ) + VendorRentalContracts.Add( i ); + } + else + { + if ( !checkContains || !m_LockDowns.Contains( i ) ) + m_LockDowns.Add( i ); + } + } + else + { + VendorRentalContracts.Remove( i ); + m_LockDowns.Remove( i ); + } + + if ( !locked ) + i.SetLastMoved(); + + if ( (i is Container) && (!locked || !(i is BaseBoard)) ) + { + foreach ( Item c in i.Items ) + SetLockdown( c, locked, checkContains ); + } + } + + public bool LockDown( Mobile m, Item item ) + { + return LockDown( m, item, true ); + } + + public bool LockDown( Mobile m, Item item, bool checkIsInside ) + { + if ( !IsCoOwner( m ) || !IsActive ) + return false; + + if ( item.Movable && !IsSecure( item ) ) + { + int amt = 1 + item.TotalItems; + + Item rootItem = item.RootParent as Item; + + if ( checkIsInside && item.RootParent is Mobile ) + { + m.SendLocalizedMessage( 1005525 );//That is not in your house + } + else if ( checkIsInside && !IsInside( item.GetWorldLocation(), item.ItemData.Height ) ) + { + m.SendLocalizedMessage( 1005525 );//That is not in your house + } + else if ( Ethics.Ethic.IsImbued( item ) ) + { + m.SendLocalizedMessage( 1005377 );//You cannot lock that down + } + else if ( IsSecure( rootItem ) ) + { + m.SendLocalizedMessage( 501737 ); // You need not lock down items in a secure container. + } + else if ( rootItem != null && !IsLockedDown( rootItem ) ) + { + m.SendLocalizedMessage( 501736 ); // You must lockdown the container first! + } + else if ( !(item is VendorRentalContract) && ( IsAosRules ? (!CheckAosLockdowns( amt ) || !CheckAosStorage( amt )) : (this.LockDownCount + amt) > m_MaxLockDowns ) ) + { + m.SendLocalizedMessage( 1005379 );//That would exceed the maximum lock down limit for this house + } + else + { + SetLockdown( item, true ); + return true; + } + } + else if ( m_LockDowns.IndexOf( item ) != -1 ) + { + m.SendLocalizedMessage( 1005526 );//That is already locked down + return true; + } + else + { + m.SendLocalizedMessage( 1005377 );//You cannot lock that down + } + + return false; + } + + private class TransferItem : Item + { + private BaseHouse m_House; + + public override string DefaultName + { + get { return "a house transfer contract"; } + } + + public TransferItem( BaseHouse house ) : base( 0x14F0 ) + { + m_House = house; + + Hue = 0x480; + Movable = false; + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + string houseName, owner, location; + + houseName = ( m_House == null ? "an unnamed house" : m_House.Sign.GetName() ); + + Mobile houseOwner = ( m_House == null ? null : m_House.Owner ); + + if ( houseOwner == null ) + owner = "nobody"; + else + owner = houseOwner.Name; + + int xLong = 0, yLat = 0, xMins = 0, yMins = 0; + bool xEast = false, ySouth = false; + + bool valid = m_House != null && Sextant.Format( m_House.Location, m_House.Map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth ); + + if ( valid ) + location = String.Format( "{0}° {1}'{2}, {3}° {4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W" ); + else + location = "unknown"; + + list.Add( 1061112, Utility.FixHtml( houseName ) ); // House Name: ~1_val~ + list.Add( 1061113, owner ); // Owner: ~1_val~ + list.Add( 1061114, location ); // Location: ~1_val~ + } + + public TransferItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + Delete(); + } + + public override bool AllowSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) + { + if ( !base.AllowSecureTrade( from, to, newOwner, accepted ) ) + return false; + else if ( !accepted ) + return true; + + if ( Deleted || m_House == null || m_House.Deleted || !m_House.IsOwner( from ) || !from.CheckAlive() || !to.CheckAlive() ) + return false; + + if ( BaseHouse.HasAccountHouse( to ) ) + { + from.SendLocalizedMessage( 501388 ); // You cannot transfer ownership to another house owner or co-owner! + return false; + } + + return m_House.CheckTransferPosition( from, to ); + } + + public override void OnSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) + { + if ( Deleted || m_House == null || m_House.Deleted || !m_House.IsOwner( from ) || !from.CheckAlive() || !to.CheckAlive() ) + return; + + Delete(); + + if ( !accepted ) + return; + + from.SendLocalizedMessage( 501338 ); // You have transferred ownership of the house. + to.SendLocalizedMessage( 501339 ); /* You are now the owner of this house. + * The house's co-owner, friend, ban, and access lists have been cleared. + * You should double-check the security settings on any doors and teleporters in the house. + */ + + m_House.RemoveKeys( from ); + m_House.Owner = to; + m_House.Bans.Clear(); + m_House.Friends.Clear(); + m_House.CoOwners.Clear(); + m_House.ChangeLocks( to ); + m_House.LastTraded = DateTime.Now; + } + } + + public bool CheckTransferPosition( Mobile from, Mobile to ) + { + bool isValid = true; + Item sign = m_Sign; + Point3D p = ( sign == null ? Point3D.Zero : sign.GetWorldLocation() ); + + if ( from.Map != Map || to.Map != Map ) + isValid = false; + else if ( sign == null ) + isValid = false; + else if ( from.Map != sign.Map || to.Map != sign.Map ) + isValid = false; + else if ( IsInside( from ) ) + isValid = false; + else if ( IsInside( to ) ) + isValid = false; + else if ( !from.InRange( p, 2 ) ) + isValid = false; + else if ( !to.InRange( p, 2 ) ) + isValid = false; + + if ( !isValid ) + from.SendLocalizedMessage( 1062067 ); // In order to transfer the house, you and the recipient must both be outside the building and within two paces of the house sign. + + return isValid; + } + + public void BeginConfirmTransfer( Mobile from, Mobile to ) + { + if ( Deleted || !from.CheckAlive() || !IsOwner( from ) ) + return; + + if ( NewVendorSystem && HasPersonalVendors ) + { + from.SendLocalizedMessage( 1062467 ); // You cannot trade this house while you still have personal vendors inside. + } + else if ( DecayLevel == DecayLevel.DemolitionPending ) + { + from.SendLocalizedMessage( 1005321 ); // This house has been marked for demolition, and it cannot be transferred. + } + else if ( from == to ) + { + from.SendLocalizedMessage( 1005330 ); // You cannot transfer a house to yourself, silly. + } + else if ( to.Player ) + { + if ( BaseHouse.HasAccountHouse( to ) ) + { + from.SendLocalizedMessage( 501388 ); // You cannot transfer ownership to another house owner or co-owner! + } + else if ( CheckTransferPosition( from, to ) ) + { + from.SendLocalizedMessage( 1005326 ); // Please wait while the other player verifies the transfer. + + if ( HasRentedVendors ) + { + /* You are about to be traded a home that has active vendor contracts. + * While there are active vendor contracts in this house, you + * cannot demolish OR customize the home. + * When you accept this house, you also accept landlordship for every + * contract vendor in the house. + */ + to.SendGump( new WarningGump( 1060635, 30720, 1062487, 32512, 420, 280, new WarningGumpCallback( ConfirmTransfer_Callback ), from ) ); + } + else + { + to.CloseGump( typeof( Gumps.HouseTransferGump ) ); + to.SendGump( new Gumps.HouseTransferGump( from, to, this ) ); + } + } + } + else + { + from.SendLocalizedMessage( 501384 ); // Only a player can own a house! + } + } + + private void ConfirmTransfer_Callback( Mobile to, bool ok, object state ) + { + Mobile from = (Mobile) state; + + if ( !ok || Deleted || !from.CheckAlive() || !IsOwner( from ) ) + return; + + if ( CheckTransferPosition( from, to ) ) + { + to.CloseGump( typeof( Gumps.HouseTransferGump ) ); + to.SendGump( new Gumps.HouseTransferGump( from, to, this ) ); + } + } + + public void EndConfirmTransfer( Mobile from, Mobile to ) + { + if ( Deleted || !from.CheckAlive() || !IsOwner( from ) ) + return; + + if ( NewVendorSystem && HasPersonalVendors ) + { + from.SendLocalizedMessage( 1062467 ); // You cannot trade this house while you still have personal vendors inside. + } + else if ( DecayLevel == DecayLevel.DemolitionPending ) + { + from.SendLocalizedMessage( 1005321 ); // This house has been marked for demolition, and it cannot be transferred. + } + else if ( from == to ) + { + from.SendLocalizedMessage( 1005330 ); // You cannot transfer a house to yourself, silly. + } + else if ( to.Player ) + { + if ( BaseHouse.HasAccountHouse( to ) ) + { + from.SendLocalizedMessage( 501388 ); // You cannot transfer ownership to another house owner or co-owner! + } + else if ( CheckTransferPosition( from, to ) ) + { + NetState fromState = from.NetState, toState = to.NetState; + + if ( fromState != null && toState != null ) + { + if ( from.HasTrade ) + { + from.SendLocalizedMessage( 1062071 ); // You cannot trade a house while you have other trades pending. + } + else if ( to.HasTrade ) + { + to.SendLocalizedMessage( 1062071 ); // You cannot trade a house while you have other trades pending. + } + else if( !to.Alive ) + { + // TODO: Check if the message is correct. + from.SendLocalizedMessage( 1062069 ); // You cannot transfer this house to that person. + } + else + { + Container c = fromState.AddTrade( toState ); + + c.DropItem( new TransferItem( this ) ); + } + } + } + } + else + { + from.SendLocalizedMessage( 501384 ); // Only a player can own a house! + } + } + + public void Release( Mobile m, Item item ) + { + if ( !IsCoOwner( m ) || !IsActive ) + return; + + if ( IsLockedDown( item ) ) + { + item.PublicOverheadMessage( Server.Network.MessageType.Label, 0x3B2, 501657 );//[no longer locked down] + SetLockdown( item, false ); + //TidyItemList( m_LockDowns ); + } + else if ( IsSecure( item ) ) + { + ReleaseSecure( m, item ); + } + else + { + m.SendLocalizedMessage( 501722 );//That isn't locked down... + } + } + + public void AddSecure( Mobile m, Item item ) + { + if ( m_Secures == null || !IsOwner( m ) || !IsActive ) + return; + + if ( !IsInside( item ) ) + { + m.SendLocalizedMessage( 1005525 ); // That is not in your house + } + else if ( IsLockedDown( item ) ) + { + m.SendLocalizedMessage( 1010550 ); // This is already locked down and cannot be secured. + } + else if ( !(item is Container) ) + { + LockDown( m, item ); + } + else + { + SecureInfo info = null; + + for ( int i = 0; info == null && i < m_Secures.Count; ++i ) + if ( ((SecureInfo)m_Secures[i]).Item == item ) + info = (SecureInfo)m_Secures[i]; + + if ( info != null ) + { + m.SendGump( new Gumps.SetSecureLevelGump( m_Owner, info, this ) ); + } + else if ( item.Parent != null ) + { + m.SendLocalizedMessage( 1010423 ); // You cannot secure this, place it on the ground first. + } + else if ( !item.Movable ) + { + m.SendLocalizedMessage( 1010424 ); // You cannot secure this. + } + else if ( !IsAosRules && SecureCount >= MaxSecures ) + { + // The maximum number of secure items has been reached : + m.SendLocalizedMessage( 1008142, true, MaxSecures.ToString() ); + } + else if ( IsAosRules ? !CheckAosLockdowns( 1 ) : ((LockDownCount + 125) >= MaxLockDowns) ) + { + m.SendLocalizedMessage( 1005379 ); // That would exceed the maximum lock down limit for this house + } + else if ( IsAosRules && !CheckAosStorage( item.TotalItems ) ) + { + m.SendLocalizedMessage( 1061839 ); // This action would exceed the secure storage limit of the house. + } + else + { + info = new SecureInfo( (Container)item, SecureLevel.CoOwners ); + + item.IsLockedDown = false; + item.IsSecure = true; + + m_Secures.Add( info ); + m_LockDowns.Remove( item ); + item.Movable = false; + + m.SendGump( new Gumps.SetSecureLevelGump( m_Owner, info, this ) ); + } + } + } + + public virtual bool IsCombatRestricted( Mobile m ) + { + if ( m == null || !m.Player || m.AccessLevel >= AccessLevel.GameMaster || !IsAosRules || ( m_Owner != null && m_Owner.AccessLevel >= AccessLevel.GameMaster )) + return false; + + for ( int i = 0; i < m.Aggressed.Count; ++i ) + { + AggressorInfo info = m.Aggressed[i]; + + Guild attackerGuild = m.Guild as Guild; + Guild defenderGuild = info.Defender.Guild as Guild; + + if ( info.Defender.Player && info.Defender.Alive && (DateTime.Now - info.LastCombatTime) < HouseRegion.CombatHeatDelay && (attackerGuild == null || defenderGuild == null || defenderGuild != attackerGuild && !defenderGuild.IsEnemy( attackerGuild )) ) + return true; + } + + return false; + } + + public bool HasSecureAccess( Mobile m, SecureLevel level ) + { + if ( m.AccessLevel >= AccessLevel.GameMaster ) + return true; + + if ( IsCombatRestricted( m ) ) + return false; + + switch ( level ) + { + case SecureLevel.Owner: return IsOwner( m ); + case SecureLevel.CoOwners: return IsCoOwner( m ); + case SecureLevel.Friends: return IsFriend( m ); + case SecureLevel.Anyone: return true; + case SecureLevel.Guild: return IsGuildMember( m ); + } + + return false; + } + + public void ReleaseSecure( Mobile m, Item item ) + { + if ( m_Secures == null || !IsOwner( m ) || item is StrongBox || !IsActive ) + return; + + for ( int i = 0; i < m_Secures.Count; ++i ) + { + SecureInfo info = (SecureInfo)m_Secures[i]; + + if ( info.Item == item && HasSecureAccess( m, info.Level ) ) + { + item.IsLockedDown = false; + item.IsSecure = false; + item.Movable = true; + item.SetLastMoved(); + item.PublicOverheadMessage( Server.Network.MessageType.Label, 0x3B2, 501656 );//[no longer secure] + m_Secures.RemoveAt( i ); + return; + } + } + + m.SendLocalizedMessage( 501717 );//This isn't secure... + } + + public override bool Decays + { + get + { + return false; + } + } + + public void AddStrongBox( Mobile from ) + { + if ( !IsCoOwner( from ) || !IsActive ) + return; + + if ( from == Owner ) + { + from.SendLocalizedMessage( 502109 ); // Owners don't get a strong box + return; + } + + if ( IsAosRules ? !CheckAosLockdowns( 1 ) : ((LockDownCount + 1) > m_MaxLockDowns) ) + { + from.SendLocalizedMessage( 1005379 );//That would exceed the maximum lock down limit for this house + return; + } + + foreach ( SecureInfo info in m_Secures ) + { + Container c = info.Item; + + if ( !c.Deleted && c is StrongBox && ((StrongBox)c).Owner == from ) + { + from.SendLocalizedMessage( 502112 );//You already have a strong box + return; + } + } + + for ( int i = 0; m_Doors != null && i < m_Doors.Count; ++i ) + { + BaseDoor door = m_Doors[i] as BaseDoor; + Point3D p = door.Location; + + if ( door.Open ) + p = new Point3D( p.X - door.Offset.X, p.Y - door.Offset.Y, p.Z - door.Offset.Z ); + + if ( (from.Z + 16) >= p.Z && (p.Z + 16) >= from.Z ) + { + if ( from.InRange( p, 1 ) ) + { + from.SendLocalizedMessage( 502113 ); // You cannot place a strongbox near a door or near steps. + return; + } + } + } + + StrongBox sb = new StrongBox( from, this ); + sb.Movable = false; + sb.IsLockedDown = false; + sb.IsSecure = true; + m_Secures.Add( new SecureInfo( sb, SecureLevel.CoOwners ) ); + sb.MoveToWorld( from.Location, from.Map ); + } + + public void Kick( Mobile from, Mobile targ ) + { + if ( !IsFriend( from ) || m_Friends == null ) + return; + + if ( targ.AccessLevel > AccessLevel.Player && from.AccessLevel <= targ.AccessLevel ) + { + from.SendLocalizedMessage( 501346 ); // Uh oh...a bigger boot may be required! + } + else if ( IsFriend( targ ) ) + { + from.SendLocalizedMessage( 501348 ); // You cannot eject a friend of the house! + } + else if ( targ is PlayerVendor ) + { + from.SendLocalizedMessage( 501351 ); // You cannot eject a vendor. + } + else if ( !IsInside( targ ) ) + { + from.SendLocalizedMessage( 501352 ); // You may not eject someone who is not in your house! + } + else if ( targ is BaseCreature && ((BaseCreature)targ).NoHouseRestrictions ) + { + from.SendLocalizedMessage( 501347 ); // You cannot eject that from the house! + } + else + { + targ.MoveToWorld( BanLocation, Map ); + + from.SendLocalizedMessage( 1042840, targ.Name ); // ~1_PLAYER NAME~ has been ejected from this house. + targ.SendLocalizedMessage( 501341 ); /* You have been ejected from this house. + * If you persist in entering, you may be banned from the house. + */ + } + } + + public void RemoveAccess( Mobile from, Mobile targ ) + { + if ( !IsFriend( from ) || m_Access == null ) + return; + + if ( m_Access.Contains( targ ) ) + { + m_Access.Remove( targ ); + + if ( !HasAccess( targ ) && IsInside( targ ) ) + { + targ.Location = BanLocation; + targ.SendLocalizedMessage( 1060734 ); // Your access to this house has been revoked. + } + + from.SendLocalizedMessage( 1050051 ); // The invitation has been revoked. + } + } + + public void RemoveBan( Mobile from, Mobile targ ) + { + if ( !IsCoOwner( from ) || m_Bans == null ) + return; + + if ( m_Bans.Contains( targ ) ) + { + m_Bans.Remove( targ ); + + from.SendLocalizedMessage( 501297 ); // The ban is lifted. + } + } + + public void Ban( Mobile from, Mobile targ ) + { + if ( !IsFriend( from ) || m_Bans == null ) + return; + + if ( targ.AccessLevel > AccessLevel.Player && from.AccessLevel <= targ.AccessLevel ) + { + from.SendLocalizedMessage( 501354 ); // Uh oh...a bigger boot may be required. + } + else if ( IsFriend( targ ) ) + { + from.SendLocalizedMessage( 501348 ); // You cannot eject a friend of the house! + } + else if ( targ is PlayerVendor ) + { + from.SendLocalizedMessage( 501351 ); // You cannot eject a vendor. + } + else if ( m_Bans.Count >= MaxBans ) + { + from.SendLocalizedMessage( 501355 ); // The ban limit for this house has been reached! + } + else if ( IsBanned( targ ) ) + { + from.SendLocalizedMessage( 501356 ); // This person is already banned! + } + else if ( !IsInside( targ ) ) + { + from.SendLocalizedMessage( 501352 ); // You may not eject someone who is not in your house! + } + else if ( !Public && IsAosRules ) + { + from.SendLocalizedMessage( 1062521 ); // You cannot ban someone from a private house. Revoke their access instead. + } + else if ( targ is BaseCreature && ((BaseCreature)targ).NoHouseRestrictions ) + { + from.SendLocalizedMessage( 1062040 ); // You cannot ban that. + } + else + { + m_Bans.Add( targ ); + + from.SendLocalizedMessage( 1042839, targ.Name ); // ~1_PLAYER_NAME~ has been banned from this house. + targ.SendLocalizedMessage( 501340 ); // You have been banned from this house. + + targ.MoveToWorld( BanLocation, Map ); + } + } + + public void GrantAccess( Mobile from, Mobile targ ) + { + if ( !IsFriend( from ) || m_Access == null ) + return; + + if ( HasAccess( targ ) ) + { + from.SendLocalizedMessage( 1060729 ); // That person already has access to this house. + } + else if ( !targ.Player ) + { + from.SendLocalizedMessage( 1060712 ); // That is not a player. + } + else if ( IsBanned( targ ) ) + { + from.SendLocalizedMessage( 501367 ); // This person is banned! Unban them first. + } + else + { + m_Access.Add( targ ); + + targ.SendLocalizedMessage( 1060735 ); // You have been granted access to this house. + } + } + + public void AddCoOwner( Mobile from, Mobile targ ) + { + if ( !IsOwner( from ) || m_CoOwners == null || m_Friends == null ) + return; + + if ( IsOwner( targ ) ) + { + from.SendLocalizedMessage( 501360 ); // This person is already the house owner! + } + else if ( m_Friends.Contains( targ ) ) + { + from.SendLocalizedMessage( 501361 ); // This person is a friend of the house. Remove them first. + } + else if ( !targ.Player ) + { + from.SendLocalizedMessage( 501362 ); // That can't be a co-owner of the house. + } + else if ( HasAccountHouse( targ ) ) + { + from.SendLocalizedMessage( 501364 ); // That person is already a house owner. + } + else if ( IsBanned( targ ) ) + { + from.SendLocalizedMessage( 501367 ); // This person is banned! Unban them first. + } + else if ( m_CoOwners.Count >= MaxCoOwners ) + { + from.SendLocalizedMessage( 501368 ); // Your co-owner list is full! + } + else if ( m_CoOwners.Contains( targ ) ) + { + from.SendLocalizedMessage( 501369 ); // This person is already on your co-owner list! + } + else + { + m_CoOwners.Add( targ ); + + targ.Delta( MobileDelta.Noto ); + targ.SendLocalizedMessage( 501343 ); // You have been made a co-owner of this house. + } + } + + public void RemoveCoOwner( Mobile from, Mobile targ ) + { + if ( !IsOwner( from ) || m_CoOwners == null ) + return; + + if ( m_CoOwners.Contains( targ ) ) + { + m_CoOwners.Remove( targ ); + + targ.Delta( MobileDelta.Noto ); + + from.SendLocalizedMessage( 501299 ); // Co-owner removed from list. + targ.SendLocalizedMessage( 501300 ); // You have been removed as a house co-owner. + + foreach ( SecureInfo info in m_Secures ) + { + Container c = info.Item; + + if ( c is StrongBox && ((StrongBox)c).Owner == targ ) + { + c.IsLockedDown = false; + c.IsSecure = false; + m_Secures.Remove( c ); + c.Destroy(); + break; + } + } + } + } + + public void AddFriend( Mobile from, Mobile targ ) + { + if ( !IsCoOwner( from ) || m_Friends == null || m_CoOwners == null ) + return; + + if ( IsOwner( targ ) ) + { + from.SendLocalizedMessage( 501370 ); // This person is already an owner of the house! + } + else if ( m_CoOwners.Contains( targ ) ) + { + from.SendLocalizedMessage( 501369 ); // This person is already on your co-owner list! + } + else if ( !targ.Player ) + { + from.SendLocalizedMessage( 501371 ); // That can't be a friend of the house. + } + else if ( IsBanned( targ ) ) + { + from.SendLocalizedMessage( 501374 ); // This person is banned! Unban them first. + } + else if ( m_Friends.Count >= MaxFriends ) + { + from.SendLocalizedMessage( 501375 ); // Your friends list is full! + } + else if ( m_Friends.Contains( targ ) ) + { + from.SendLocalizedMessage( 501376 ); // This person is already on your friends list! + } + else + { + m_Friends.Add( targ ); + + targ.Delta( MobileDelta.Noto ); + targ.SendLocalizedMessage( 501337 ); // You have been made a friend of this house. + } + } + + public void RemoveFriend( Mobile from, Mobile targ ) + { + if ( !IsCoOwner( from ) || m_Friends == null ) + return; + + if ( m_Friends.Contains( targ ) ) + { + m_Friends.Remove( targ ); + + targ.Delta( MobileDelta.Noto ); + + from.SendLocalizedMessage( 501298 ); // Friend removed from list. + targ.SendLocalizedMessage( 1060751 ); // You are no longer a friend of this house. + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 14 ); // version + + writer.Write( (Point3D) m_RelativeBanLocation ); + + writer.WriteItemList( m_VendorRentalContracts, true ); + writer.WriteMobileList( m_InternalizedVendors, true ); + + writer.WriteEncodedInt( m_RelocatedEntities.Count ); + foreach ( RelocatedEntity relEntity in m_RelocatedEntities ) + { + writer.Write( (Point3D) relEntity.RelativeLocation ); + + if ( ( relEntity.Entity is Item && ((Item)relEntity.Entity).Deleted ) || ( relEntity.Entity is Mobile && ((Mobile)relEntity.Entity).Deleted ) ) + writer.Write( (int) Serial.MinusOne ); + else + writer.Write( (int) relEntity.Entity.Serial ); + } + + writer.WriteEncodedInt( m_VendorInventories.Count ); + for ( int i = 0; i < m_VendorInventories.Count; i++ ) + { + VendorInventory inventory = (VendorInventory) m_VendorInventories[i]; + inventory.Serialize( writer ); + } + + writer.Write( (DateTime) m_LastRefreshed ); + writer.Write( (bool) m_RestrictDecay ); + + writer.Write( (int) m_Visits ); + + writer.Write( (int) m_Price ); + + writer.WriteMobileList( m_Access ); + + writer.Write( m_BuiltOn ); + writer.Write( m_LastTraded ); + + writer.WriteItemList( m_Addons, true ); + + writer.Write( m_Secures.Count ); + + for ( int i = 0; i < m_Secures.Count; ++i ) + ((SecureInfo)m_Secures[i]).Serialize( writer ); + + writer.Write( m_Public ); + + //writer.Write( BanLocation ); + + writer.Write( m_Owner ); + + // Version 5 no longer serializes region coords + /*writer.Write( (int)m_Region.Coords.Count ); + foreach( Rectangle2D rect in m_Region.Coords ) + { + writer.Write( rect ); + }*/ + + writer.WriteMobileList( m_CoOwners, true ); + writer.WriteMobileList( m_Friends, true ); + writer.WriteMobileList( m_Bans, true ); + + writer.Write( m_Sign ); + writer.Write( m_Trash ); + + writer.WriteItemList( m_Doors, true ); + writer.WriteItemList( m_LockDowns, true ); + //writer.WriteItemList( m_Secures, true ); + + writer.Write( (int) m_MaxLockDowns ); + writer.Write( (int) m_MaxSecures ); + + // Items in locked down containers that aren't locked down themselves must decay! + for ( int i = 0; i < m_LockDowns.Count; ++i ) + { + Item item = (Item)m_LockDowns[i]; + + if ( item is Container && !(item is BaseBoard) ) + { + Container cont = (Container)item; + List children = cont.Items; + + for ( int j = 0; j < children.Count; ++j ) + { + Item child = children[j]; + + if ( child.Decays && !child.IsLockedDown && !child.IsSecure && (child.LastMoved + child.DecayTime) <= DateTime.Now ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( child.Delete ) ); + } + } + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + int count; + + switch ( version ) + { + case 14: + { + m_RelativeBanLocation = reader.ReadPoint3D(); + goto case 13; + } + case 13: // removed ban location serialization + case 12: + { + m_VendorRentalContracts = reader.ReadItemList(); + m_InternalizedVendors = reader.ReadMobileList(); + + int relocatedCount = reader.ReadEncodedInt(); + for ( int i = 0; i < relocatedCount; i++ ) + { + Point3D relLocation = reader.ReadPoint3D(); + IEntity entity = World.FindEntity( reader.ReadInt() ); + + if ( entity != null ) + m_RelocatedEntities.Add( new RelocatedEntity( entity, relLocation ) ); + } + + int inventoryCount = reader.ReadEncodedInt(); + for ( int i = 0; i < inventoryCount; i++ ) + { + VendorInventory inventory = new VendorInventory( this, reader ); + m_VendorInventories.Add( inventory ); + } + + goto case 11; + } + case 11: + { + m_LastRefreshed = reader.ReadDateTime(); + m_RestrictDecay = reader.ReadBool(); + goto case 10; + } + case 10: // just a signal for updates + case 9: + { + m_Visits = reader.ReadInt(); + goto case 8; + } + case 8: + { + m_Price = reader.ReadInt(); + goto case 7; + } + case 7: + { + m_Access = reader.ReadMobileList(); + goto case 6; + } + case 6: + { + m_BuiltOn = reader.ReadDateTime(); + m_LastTraded = reader.ReadDateTime(); + goto case 5; + } + case 5: // just removed fields + case 4: + { + m_Addons = reader.ReadItemList(); + goto case 3; + } + case 3: + { + count = reader.ReadInt(); + m_Secures = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + SecureInfo info = new SecureInfo( reader ); + + if ( info.Item != null ) + { + info.Item.IsSecure = true; + m_Secures.Add( info ); + } + } + + goto case 2; + } + case 2: + { + m_Public = reader.ReadBool(); + goto case 1; + } + case 1: + { + if ( version < 13 ) + reader.ReadPoint3D(); // house ban location + goto case 0; + } + case 0: + { + if ( version < 14 ) + m_RelativeBanLocation = this.BaseBanLocation; + + if ( version < 12 ) + { + m_VendorRentalContracts = new ArrayList(); + m_InternalizedVendors = new ArrayList(); + } + + if ( version < 4 ) + m_Addons = new ArrayList(); + + if ( version < 7 ) + m_Access = new ArrayList(); + + if ( version < 8 ) + m_Price = DefaultPrice; + + m_Owner = reader.ReadMobile(); + + if ( version < 5 ) + { + count = reader.ReadInt(); + + for(int i=0;i 0 ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( RestoreRelocatedEntities ) ); + + if ( m_Owner == null && m_Friends.Count == 0 && m_CoOwners.Count == 0 ) + Timer.DelayCall( TimeSpan.FromSeconds( 10.0 ), new TimerCallback( Delete ) ); + } + } + + private void FixLockdowns_Sandbox() + { + ArrayList lockDowns = new ArrayList(); + + for ( int i = 0; m_LockDowns != null && i < m_LockDowns.Count; ++i ) + { + Item item = (Item)m_LockDowns[i]; + + if ( item is Container ) + lockDowns.Add( item ); + } + + for ( int i = 0; i < lockDowns.Count; ++i ) + SetLockdown( (Item)lockDowns[i], true, true ); + } + + public static void HandleDeletion( Mobile mob ) + { + ArrayList houses = GetHouses( mob ); + + if ( houses.Count == 0 ) + return; + + Account acct = mob.Account as Account; + Mobile trans = null; + + for ( int i = 0; i < acct.Length; ++i ) + { + if ( acct[i] != null && acct[i] != mob ) + trans = acct[i]; + } + + for ( int i = 0; i < houses.Count; ++i ) + { + BaseHouse house = houses[i] as BaseHouse; + + bool canClaim = false; + + if ( trans == null ) + canClaim = ( house.CoOwners.Count > 0 ); + /*{ + for ( int j = 0; j < house.CoOwners.Count; ++j ) + { + Mobile check = house.CoOwners[j] as Mobile; + + if ( check != null && !check.Deleted && !HasAccountHouse( check ) ) + { + canClaim = true; + break; + } + } + }*/ + + if ( trans == null && !canClaim ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( house.Delete ) ); + else + house.Owner = trans; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner + { + get + { + return m_Owner; + } + set + { + if ( m_Owner != null ) + { + ArrayList list = (ArrayList)m_Table[m_Owner]; + + if ( list == null ) + m_Table[m_Owner] = list = new ArrayList(); + + list.Remove( this ); + m_Owner.Delta( MobileDelta.Noto ); + } + + m_Owner = value; + + if ( m_Owner != null ) + { + ArrayList list = (ArrayList)m_Table[m_Owner]; + + if ( list == null ) + m_Table[m_Owner] = list = new ArrayList(); + + list.Add( this ); + m_Owner.Delta( MobileDelta.Noto ); + } + + if ( m_Sign != null ) + m_Sign.InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Visits + { + get{ return m_Visits; } + set{ m_Visits = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Public + { + get + { + return m_Public; + } + set + { + if ( m_Public != value ) + { + m_Public = value; + + if ( !m_Public ) // Privatizing the house, change to brass sign + ChangeSignType( 0xBD2 ); + + if ( m_Sign != null ) + m_Sign.InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxSecures + { + get + { + return m_MaxSecures; + } + set + { + m_MaxSecures = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D BanLocation + { + get + { + return m_Region.GoLocation; + } + set + { + this.RelativeBanLocation = new Point3D( value.X - this.X, value.Y - this.Y, value.Z - this.Z ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D RelativeBanLocation + { + get + { + return m_RelativeBanLocation; + } + set + { + m_RelativeBanLocation = value; + m_Region.GoLocation = new Point3D( this.X + value.X, this.Y + value.Y, this.Z + value.Z ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxLockDowns + { + get + { + return m_MaxLockDowns; + } + set + { + m_MaxLockDowns = value; + } + } + + public Region Region{ get{ return m_Region; } } + public ArrayList CoOwners{ get{ return m_CoOwners; } set{ m_CoOwners = value; } } + public ArrayList Friends{ get{ return m_Friends; } set{ m_Friends = value; } } + public ArrayList Access{ get{ return m_Access; } set{ m_Access = value; } } + public ArrayList Bans{ get{ return m_Bans; } set{ m_Bans = value; } } + public ArrayList Doors{ get{ return m_Doors; } set{ m_Doors = value; } } + + public int LockDownCount + { + get + { + int count = 0; + + if ( m_LockDowns != null ) + count += m_LockDowns.Count; + + if ( m_Secures != null ) + { + for ( int i = 0; i < m_Secures.Count; ++i ) + { + SecureInfo info = (SecureInfo)m_Secures[i]; + + if ( info.Item.Deleted ) + continue; + else if ( info.Item is StrongBox ) + count += 1; + else + count += 125; + } + } + + return count; + } + } + + public int SecureCount + { + get + { + int count = 0; + + if ( m_Secures != null ) + { + for ( int i = 0; i < m_Secures.Count; i++ ) + { + SecureInfo info = (SecureInfo)m_Secures[i]; + + if ( info.Item.Deleted ) + continue; + else if ( !(info.Item is StrongBox) ) + count += 1; + } + } + + return count; + } + } + + public ArrayList Addons{ get{ return m_Addons; } set{ m_Addons = value; } } + public ArrayList LockDowns{ get{ return m_LockDowns; } } + public ArrayList Secures{ get{ return m_Secures; } } + public HouseSign Sign{ get{ return m_Sign; } set{ m_Sign = value; } } + public ArrayList PlayerVendors{ get{ return m_PlayerVendors; } } + public ArrayList PlayerBarkeepers{ get{ return m_PlayerBarkeepers; } } + public ArrayList VendorRentalContracts{ get{ return m_VendorRentalContracts; } } + public ArrayList VendorInventories{ get{ return m_VendorInventories; } } + public ArrayList RelocatedEntities{ get{ return m_RelocatedEntities; } } + public MovingCrate MovingCrate{ get{ return m_MovingCrate; } set{ m_MovingCrate = value; } } + public ArrayList InternalizedVendors{ get{ return m_InternalizedVendors; } } + + public DateTime BuiltOn + { + get{ return m_BuiltOn; } + set{ m_BuiltOn = value; } + } + + public DateTime LastTraded + { + get{ return m_LastTraded; } + set{ m_LastTraded = value; } + } + + public override void OnDelete() + { + /*Map map = this.Map; + + if ( map != null ) + { + MultiComponentList mcl = Components; + IPooledEnumerable eable = map.GetItemsInBounds( new Rectangle2D( X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height ) ); + + foreach ( Item item in eable ) + if ( item is Guildstone && Contains( item ) ) + item.Delete(); + + eable.Free(); + }*/ + + RestoreRelocatedEntities(); + + new FixColumnTimer( this ).Start(); + + base.OnDelete(); + } + + private class FixColumnTimer : Timer + { + private Map m_Map; + private int m_StartX, m_StartY, m_EndX, m_EndY; + + public FixColumnTimer( BaseMulti multi ) : base( TimeSpan.Zero ) + { + m_Map = multi.Map; + + MultiComponentList mcl = multi.Components; + + m_StartX = multi.X + mcl.Min.X; + m_StartY = multi.Y + mcl.Min.Y; + m_EndX = multi.X + mcl.Max.X; + m_EndY = multi.Y + mcl.Max.Y; + } + + protected override void OnTick() + { + if ( m_Map == null ) + return; + + for ( int x = m_StartX; x <= m_EndX; ++x ) + for ( int y = m_StartY; y <= m_EndY; ++y ) + m_Map.FixColumn( x, y ); + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Owner != null ) + { + ArrayList list = (ArrayList)m_Table[m_Owner]; + + if ( list == null ) + m_Table[m_Owner] = list = new ArrayList(); + + list.Remove( this ); + } + + if ( m_Region != null ) + { + m_Region.Unregister(); + m_Region = null; + } + + if ( m_Sign != null ) + m_Sign.Delete(); + + if ( m_Trash != null ) + m_Trash.Delete(); + + if ( m_Doors != null ) + { + for ( int i = 0; i < m_Doors.Count; ++i ) + { + Item item = (Item)m_Doors[i]; + + if ( item != null ) + item.Delete(); + } + + m_Doors.Clear(); + } + + if ( m_LockDowns != null ) + { + for ( int i = 0; i < m_LockDowns.Count; ++i ) + { + Item item = (Item)m_LockDowns[i]; + + if ( item != null ) + { + item.IsLockedDown = false; + item.IsSecure = false; + item.Movable = true; + item.SetLastMoved(); + } + } + + m_LockDowns.Clear(); + } + + if ( VendorRentalContracts != null ) + { + for ( int i = 0; i < VendorRentalContracts.Count; ++i ) + { + Item item = (Item)VendorRentalContracts[i]; + + if ( item != null ) + { + item.IsLockedDown = false; + item.IsSecure = false; + item.Movable = true; + item.SetLastMoved(); + } + } + + VendorRentalContracts.Clear(); + } + + if ( m_Secures != null ) + { + for ( int i = 0; i < m_Secures.Count; ++i ) + { + SecureInfo info = (SecureInfo)m_Secures[i]; + + if ( info.Item is StrongBox ) + { + info.Item.Destroy(); + } + else + { + info.Item.IsLockedDown = false; + info.Item.IsSecure = false; + info.Item.Movable = true; + info.Item.SetLastMoved(); + } + } + + m_Secures.Clear(); + } + + if ( m_Addons != null ) + { + for ( int i = 0; i < m_Addons.Count; ++i ) + { + Item item = (Item)m_Addons[i]; + + if ( item != null ) + { + if ( !item.Deleted && item is IAddon ) + { + Item deed = ((IAddon)item).Deed; + + if ( deed != null ) + deed.MoveToWorld( item.Location, item.Map ); + } + + item.Delete(); + } + } + + m_Addons.Clear(); + } + + ArrayList inventories = new ArrayList( VendorInventories ); + + foreach ( VendorInventory inventory in inventories ) + inventory.Delete(); + + if ( MovingCrate != null ) + MovingCrate.Delete(); + + KillVendors(); + + m_AllHouses.Remove( this ); + } + + public static bool HasHouse( Mobile m ) + { + if ( m == null ) + return false; + + ArrayList list = (ArrayList)m_Table[m]; + + if ( list == null ) + return false; + + for ( int i = 0; i < list.Count; ++i ) + { + BaseHouse h = (BaseHouse)list[i]; + + if ( !h.Deleted ) + return true; + } + + return false; + } + + public static bool HasAccountHouse( Mobile m ) + { + Account a = m.Account as Account; + + if ( a == null ) + return false; + + for ( int i = 0; i < a.Length; ++i ) + if ( a[i] != null && HasHouse( a[i] ) ) + return true; + + return false; + } + + public bool CheckAccount( Mobile mobCheck, Mobile accCheck ) + { + if ( accCheck != null ) + { + Account a = accCheck.Account as Account; + + if ( a != null ) + { + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] == mobCheck ) + return true; + } + } + } + + return false; + } + + public bool IsOwner( Mobile m ) + { + if ( m == null ) + return false; + + if ( m == m_Owner || m.AccessLevel >= AccessLevel.GameMaster ) + return true; + + return IsAosRules && CheckAccount( m, m_Owner ); + } + + public bool IsCoOwner( Mobile m ) + { + if ( m == null || m_CoOwners == null ) + return false; + + if ( IsOwner( m ) || m_CoOwners.Contains( m ) ) + return true; + + return !IsAosRules && CheckAccount( m, m_Owner ); + } + + public bool IsGuildMember( Mobile m ) + { + if( m == null || Owner == null || Owner.Guild == null ) + return false; + + return ( m.Guild == Owner.Guild ); + } + + public void RemoveKeys( Mobile m ) + { + if ( m_Doors != null ) + { + uint keyValue = 0; + + for ( int i = 0; keyValue == 0 && i < m_Doors.Count; ++i ) + { + BaseDoor door = m_Doors[i] as BaseDoor; + + if ( door != null ) + keyValue = door.KeyValue; + } + + Key.RemoveKeys( m, keyValue ); + } + } + + public void ChangeLocks( Mobile m ) + { + uint keyValue = CreateKeys( m ); + + if ( m_Doors != null ) + { + for ( int i = 0; i < m_Doors.Count; ++i ) + { + BaseDoor door = m_Doors[i] as BaseDoor; + + if ( door != null ) + door.KeyValue = keyValue; + } + } + } + + public void RemoveLocks() + { + if ( m_Doors != null ) + { + for (int i=0;i AccessLevel.Player || m_Bans == null ) + return false; + + Account theirAccount = m.Account as Account; + + for ( int i = 0; i < m_Bans.Count; ++i ) + { + Mobile c = (Mobile)m_Bans[i]; + + if ( c == m ) + return true; + + Account bannedAccount = c.Account as Account; + + if ( bannedAccount != null && bannedAccount == theirAccount ) + return true; + } + + return false; + } + + public bool HasAccess( Mobile m ) + { + if ( m == null ) + return false; + + if ( m.AccessLevel > AccessLevel.Player || IsFriend( m ) || ( m_Access != null && m_Access.Contains( m ) ) ) + return true; + + if ( m is BaseCreature ) + { + BaseCreature bc = (BaseCreature)m; + + if ( bc.NoHouseRestrictions ) + return true; + + if ( bc.Controlled || bc.Summoned ) + { + m = bc.ControlMaster; + + if ( m == null ) + m = bc.SummonMaster; + + if ( m == null ) + return false; + + if ( m.AccessLevel > AccessLevel.Player || IsFriend( m ) || ( m_Access != null && m_Access.Contains( m ) ) ) + return true; + } + } + + return false; + } + + public new bool IsLockedDown( Item check ) + { + if ( check == null ) + return false; + + if ( m_LockDowns == null ) + return false; + + return ( m_LockDowns.Contains( check ) || VendorRentalContracts.Contains( check ) ); + } + + public new bool IsSecure( Item item ) + { + if ( item == null ) + return false; + + if ( m_Secures == null ) + return false; + + bool contains = false; + + for ( int i = 0; !contains && i < m_Secures.Count; ++i ) + contains = ( ((SecureInfo)m_Secures[i]).Item == item ); + + return contains; + } + + public virtual Guildstone FindGuildstone() + { + Map map = this.Map; + + if ( map == null ) + return null; + + MultiComponentList mcl = Components; + IPooledEnumerable eable = map.GetItemsInBounds( new Rectangle2D( X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height ) ); + + foreach ( Item item in eable ) + { + if ( item is Guildstone && Contains( item ) ) + { + eable.Free(); + return (Guildstone)item; + } + } + + eable.Free(); + return null; + } + } + + public enum DecayType + { + Ageless, + AutoRefresh, + ManualRefresh, + Condemned + } + + public enum DecayLevel + { + Ageless, + LikeNew, + Slightly, + Somewhat, + Fairly, + Greatly, + IDOC, + Collapsed, + DemolitionPending + } + + public enum SecureAccessResult + { + Insecure, + Accessible, + Inaccessible + } + + public enum SecureLevel + { + Owner, + CoOwners, + Friends, + Anyone, + Guild + } + + public class SecureInfo : ISecurable + { + private Container m_Item; + private SecureLevel m_Level; + + public Container Item{ get{ return m_Item; } } + public SecureLevel Level{ get{ return m_Level; } set{ m_Level = value; } } + + public SecureInfo( Container item, SecureLevel level ) + { + m_Item = item; + m_Level = level; + } + + public SecureInfo( GenericReader reader ) + { + m_Item = reader.ReadItem() as Container; + m_Level = (SecureLevel)reader.ReadByte(); + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( m_Item ); + writer.Write( (byte) m_Level ); + } + } + + public class RelocatedEntity + { + private IEntity m_Entity; + private Point3D m_RelativeLocation; + + public IEntity Entity + { + get{ return m_Entity; } + } + + public Point3D RelativeLocation + { + get{ return m_RelativeLocation; } + } + + public RelocatedEntity( IEntity entity, Point3D relativeLocation ) + { + m_Entity = entity; + m_RelativeLocation = relativeLocation; + } + } + + public class LockdownTarget : Target + { + private bool m_Release; + private BaseHouse m_House; + + public LockdownTarget( bool release, BaseHouse house ) : base( 12, false, TargetFlags.None ) + { + CheckLOS = false; + + m_Release = release; + m_House = house; + } + + protected override void OnTargetNotAccessible( Mobile from, object targeted ) + { + OnTarget( from, targeted ); + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsCoOwner( from ) ) + return; + + if ( targeted is Item ) + { + if ( m_Release ) + { + m_House.Release( from, (Item)targeted ); + } + else + { + if ( targeted is VendorRentalContract || ( targeted is Container && ((Container)targeted).FindItemByType( typeof( VendorRentalContract ) ) != null ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1062392 ); // You must double click the contract in your pack to lock it down. + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501732 ); // I cannot lock this down! + } + else + { + m_House.LockDown( from, (Item)targeted ); + } + } + } + else + { + from.SendLocalizedMessage( 1005377 );//You cannot lock that down + } + } + } + + public class SecureTarget : Target + { + private bool m_Release; + private BaseHouse m_House; + + public SecureTarget( bool release, BaseHouse house ) : base( 12, false, TargetFlags.None ) + { + CheckLOS = false; + + m_Release = release; + m_House = house; + } + + protected override void OnTargetNotAccessible( Mobile from, object targeted ) + { + OnTarget( from, targeted ); + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsCoOwner( from ) ) + return; + + if ( targeted is Item ) + { + if ( m_Release ) + { + m_House.ReleaseSecure( from, (Item)targeted ); + } + else + { + if ( targeted is VendorRentalContract || ( targeted is Container && ((Container)targeted).FindItemByType( typeof( VendorRentalContract ) ) != null ) ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1062392 ); // You must double click the contract in your pack to lock it down. + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501732 ); // I cannot lock this down! + } + else + { + m_House.AddSecure( from, (Item)targeted ); + } + } + } + else + { + from.SendLocalizedMessage( 1010424 );//You cannot secure this + } + } + } + + public class HouseKickTarget : Target + { + private BaseHouse m_House; + + public HouseKickTarget( BaseHouse house ) : base( -1, false, TargetFlags.None ) + { + CheckLOS = false; + + m_House = house; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsFriend( from ) ) + return; + + if ( targeted is Mobile ) + { + m_House.Kick( from, (Mobile)targeted ); + } + else + { + from.SendLocalizedMessage( 501347 );//You cannot eject that from the house! + } + } + } + + public class HouseBanTarget : Target + { + private BaseHouse m_House; + private bool m_Banning; + + public HouseBanTarget( bool ban, BaseHouse house ) : base( -1, false, TargetFlags.None ) + { + CheckLOS = false; + + m_House = house; + m_Banning = ban; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsFriend( from ) ) + return; + + if ( targeted is Mobile ) + { + if ( m_Banning ) + m_House.Ban( from, (Mobile)targeted ); + else + m_House.RemoveBan( from, (Mobile)targeted ); + } + else + { + from.SendLocalizedMessage( 501347 );//You cannot eject that from the house! + } + } + } + + public class HouseAccessTarget : Target + { + private BaseHouse m_House; + + public HouseAccessTarget( BaseHouse house ) : base( -1, false, TargetFlags.None ) + { + CheckLOS = false; + + m_House = house; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsFriend( from ) ) + return; + + if ( targeted is Mobile ) + m_House.GrantAccess( from, (Mobile)targeted ); + else + from.SendLocalizedMessage( 1060712 ); // That is not a player. + } + } + + public class CoOwnerTarget : Target + { + private BaseHouse m_House; + private bool m_Add; + + public CoOwnerTarget( bool add, BaseHouse house ) : base( 12, false, TargetFlags.None ) + { + CheckLOS = false; + + m_House = house; + m_Add = add; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsOwner( from ) ) + return; + + if ( targeted is Mobile ) + { + if ( m_Add ) + m_House.AddCoOwner( from, (Mobile)targeted ); + else + m_House.RemoveCoOwner( from, (Mobile)targeted ); + } + else + { + from.SendLocalizedMessage( 501362 );//That can't be a coowner + } + } + } + + public class HouseFriendTarget : Target + { + private BaseHouse m_House; + private bool m_Add; + + public HouseFriendTarget( bool add, BaseHouse house ) : base( 12, false, TargetFlags.None ) + { + CheckLOS = false; + + m_House = house; + m_Add = add; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive || m_House.Deleted || !m_House.IsCoOwner( from ) ) + return; + + if ( targeted is Mobile ) + { + if ( m_Add ) + m_House.AddFriend( from, (Mobile)targeted ); + else + m_House.RemoveFriend( from, (Mobile)targeted ); + } + else + { + from.SendLocalizedMessage( 501371 ); // That can't be a friend + } + } + } + + public class HouseOwnerTarget : Target + { + private BaseHouse m_House; + + public HouseOwnerTarget( BaseHouse house ) : base( 12, false, TargetFlags.None ) + { + CheckLOS = false; + + m_House = house; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + m_House.BeginConfirmTransfer( from, (Mobile)targeted ); + else + from.SendLocalizedMessage( 501384 ); // Only a player can own a house! + } + } + + public class SetSecureLevelEntry : ContextMenuEntry + { + private Item m_Item; + private ISecurable m_Securable; + + public SetSecureLevelEntry( Item item, ISecurable securable ) : base( 6203, 6 ) + { + m_Item = item; + m_Securable = securable; + } + + public static ISecurable GetSecurable( Mobile from, Item item ) + { + BaseHouse house = BaseHouse.FindHouseAt( item ); + + if ( house == null || !house.IsOwner( from ) || !house.IsAosRules ) + return null; + + ISecurable sec = null; + + if ( item is ISecurable ) + { + bool isOwned = house.Doors.Contains( item ); + + if ( !isOwned ) + isOwned = ( house is HouseFoundation && ((HouseFoundation)house).IsFixture( item ) ); + + if ( !isOwned ) + isOwned = house.IsLockedDown( item ); + + if ( isOwned ) + sec = (ISecurable)item; + } + else + { + ArrayList list = house.Secures; + + for ( int i = 0; sec == null && list != null && i < list.Count; ++i ) + { + SecureInfo si = (SecureInfo)list[i]; + + if ( si.Item == item ) + sec = si; + } + } + + return sec; + } + + public static void AddTo( Mobile from, Item item, List list ) + { + ISecurable sec = GetSecurable( from, item ); + + if ( sec != null ) + list.Add( new SetSecureLevelEntry( item, sec ) ); + } + + public override void OnClick() + { + ISecurable sec = GetSecurable( Owner.From, m_Item ); + + if ( sec != null ) + Owner.From.SendGump( new SetSecureLevelGump( Owner.From, sec, BaseHouse.FindHouseAt( m_Item ) ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/BaseBoat.cs b/Scripts/Multis/Boats/BaseBoat.cs new file mode 100644 index 000000000..761bb4d5e --- /dev/null +++ b/Scripts/Multis/Boats/BaseBoat.cs @@ -0,0 +1,1671 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Movement; +using Server.Network; + +namespace Server.Multis +{ + public enum BoatOrder + { + Move, + Course, + Single + } + + public abstract class BaseBoat : BaseMulti + { + private static Rectangle2D[] m_BritWrap = new Rectangle2D[]{ new Rectangle2D( 16, 16, 5120 - 32, 4096 - 32 ), new Rectangle2D( 5136, 2320, 992, 1760 ) }; + private static Rectangle2D[] m_IlshWrap = new Rectangle2D[]{ new Rectangle2D( 16, 16, 2304 - 32, 1600 - 32 ) }; + private static Rectangle2D[] m_TokunoWrap = new Rectangle2D[] { new Rectangle2D( 16, 16, 1448 - 32, 1448 - 32 ) }; + + private static TimeSpan BoatDecayDelay = TimeSpan.FromDays( 9.0 ); + + public static BaseBoat FindBoatAt( IPoint2D loc, Map map ) + { + Sector sector = map.GetSector( loc ); + + for ( int i = 0; i < sector.Multis.Count; i++ ) + { + BaseBoat boat = sector.Multis[i] as BaseBoat; + + if ( boat != null && boat.Contains( loc.X, loc.Y ) ) + return boat; + } + + return null; + } + + private Hold m_Hold; + private TillerMan m_TillerMan; + private Mobile m_Owner; + + private Direction m_Facing; + + private Direction m_Moving; + private int m_Speed; + + private bool m_Anchored; + private string m_ShipName; + + private BoatOrder m_Order; + + private MapItem m_MapItem; + private int m_NextNavPoint; + + private Plank m_PPlank, m_SPlank; + + private DateTime m_DecayTime; + + private Timer m_TurnTimer; + private Timer m_MoveTimer; + + [CommandProperty( AccessLevel.GameMaster )] + public Hold Hold{ get{ return m_Hold; } set{ m_Hold = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public TillerMan TillerMan{ get{ return m_TillerMan; } set{ m_TillerMan = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Plank PPlank{ get{ return m_PPlank; } set{ m_PPlank = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Plank SPlank{ get{ return m_SPlank; } set{ m_SPlank = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile Owner{ get{ return m_Owner; } set{ m_Owner = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Direction Facing{ get{ return m_Facing; } set{ SetFacing( value ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public Direction Moving{ get{ return m_Moving; } set{ m_Moving = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsMoving{ get{ return ( m_MoveTimer != null ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Speed{ get{ return m_Speed; } set{ m_Speed = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Anchored{ get{ return m_Anchored; } set{ m_Anchored = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public string ShipName{ get{ return m_ShipName; } set{ m_ShipName = value; if ( m_TillerMan != null ) m_TillerMan.InvalidateProperties(); } } + + [CommandProperty( AccessLevel.GameMaster )] + public BoatOrder Order{ get{ return m_Order; } set{ m_Order = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public MapItem MapItem{ get{ return m_MapItem; } set{ m_MapItem = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int NextNavPoint{ get{ return m_NextNavPoint; } set{ m_NextNavPoint = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime TimeOfDecay{ get{ return m_DecayTime; } set{ m_DecayTime = value; if ( m_TillerMan != null ) m_TillerMan.InvalidateProperties(); } } + + public int Status + { + get + { + DateTime start = TimeOfDecay - BoatDecayDelay; + + if ( DateTime.Now - start < TimeSpan.FromHours( 1.0 ) ) + return 1043010; // This structure is like new. + + if ( DateTime.Now - start < TimeSpan.FromDays( 2.0 ) ) + return 1043011; // This structure is slightly worn. + + if ( DateTime.Now - start < TimeSpan.FromDays( 3.0 ) ) + return 1043012; // This structure is somewhat worn. + + if ( DateTime.Now - start < TimeSpan.FromDays( 4.0 ) ) + return 1043013; // This structure is fairly worn. + + if ( DateTime.Now - start < TimeSpan.FromDays( 5.0 ) ) + return 1043014; // This structure is greatly worn. + + return 1043015; // This structure is in danger of collapsing. + } + } + + public virtual int NorthID{ get{ return 0; } } + public virtual int EastID{ get{ return 0; } } + public virtual int SouthID{ get{ return 0; } } + public virtual int WestID{ get{ return 0; } } + + public virtual int HoldDistance{ get{ return 0; } } + public virtual int TillerManDistance{ get{ return 0; } } + public virtual Point2D StarboardOffset{ get{ return Point2D.Zero; } } + public virtual Point2D PortOffset{ get{ return Point2D.Zero; } } + public virtual Point3D MarkOffset{ get{ return Point3D.Zero; } } + + public virtual BaseDockedBoat DockedBoat{ get{ return null; } } + + private static ArrayList m_Instances = new ArrayList(); + + public static ArrayList Boats{ get{ return m_Instances; } } + + public BaseBoat() : base( 0x4000 ) + { + m_DecayTime = DateTime.Now + BoatDecayDelay; + + m_TillerMan = new TillerMan( this ); + m_Hold = new Hold( this ); + + m_PPlank = new Plank( this, PlankSide.Port, 0 ); + m_SPlank = new Plank( this, PlankSide.Starboard, 0 ); + + m_PPlank.MoveToWorld( new Point3D( X + PortOffset.X, Y + PortOffset.Y, Z ), Map ); + m_SPlank.MoveToWorld( new Point3D( X + StarboardOffset.X, Y + StarboardOffset.Y, Z ), Map ); + + Facing = Direction.North; + + m_NextNavPoint = -1; + + Movable = false; + + m_Instances.Add( this ); + } + + public BaseBoat( Serial serial ) : base( serial ) + { + } + + public Point3D GetRotatedLocation( int x, int y ) + { + Point3D p = new Point3D( X + x, Y + y, Z ); + + return Rotate( p, (int)m_Facing / 2 ); + } + + public void UpdateComponents() + { + if ( m_PPlank != null ) + { + m_PPlank.MoveToWorld( GetRotatedLocation( PortOffset.X, PortOffset.Y ), Map ); + m_PPlank.SetFacing( m_Facing ); + } + + if ( m_SPlank != null ) + { + m_SPlank.MoveToWorld( GetRotatedLocation( StarboardOffset.X, StarboardOffset.Y ), Map ); + m_SPlank.SetFacing( m_Facing ); + } + + int xOffset = 0, yOffset = 0; + Movement.Movement.Offset( m_Facing, ref xOffset, ref yOffset ); + + if ( m_TillerMan != null ) + { + m_TillerMan.Location = new Point3D( X + (xOffset * TillerManDistance) + (m_Facing == Direction.North ? 1 : 0), Y + (yOffset * TillerManDistance), m_TillerMan.Z ); + m_TillerMan.SetFacing( m_Facing ); + m_TillerMan.InvalidateProperties(); + } + + if ( m_Hold != null ) + { + m_Hold.Location = new Point3D( X + (xOffset * HoldDistance), Y + (yOffset * HoldDistance), m_Hold.Z ); + m_Hold.SetFacing( m_Facing ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 3 ); + + writer.Write( (Item) m_MapItem ); + writer.Write( (int) m_NextNavPoint ); + + writer.Write( (int) m_Facing ); + + writer.WriteDeltaTime( m_DecayTime ); + + writer.Write( m_Owner ); + writer.Write( m_PPlank ); + writer.Write( m_SPlank ); + writer.Write( m_TillerMan ); + writer.Write( m_Hold ); + writer.Write( m_Anchored ); + writer.Write( m_ShipName ); + + CheckDecay(); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + { + m_MapItem = (MapItem) reader.ReadItem(); + m_NextNavPoint = reader.ReadInt(); + + goto case 2; + } + case 2: + { + m_Facing = (Direction)reader.ReadInt(); + + goto case 1; + } + case 1: + { + m_DecayTime = reader.ReadDeltaTime(); + + goto case 0; + } + case 0: + { + if ( version < 3 ) + m_NextNavPoint = -1; + + if ( version < 2 ) + { + if ( ItemID == NorthID ) + m_Facing = Direction.North; + else if ( ItemID == SouthID ) + m_Facing = Direction.South; + else if ( ItemID == EastID ) + m_Facing = Direction.East; + else if ( ItemID == WestID ) + m_Facing = Direction.West; + } + + m_Owner = reader.ReadMobile(); + m_PPlank = reader.ReadItem() as Plank; + m_SPlank = reader.ReadItem() as Plank; + m_TillerMan = reader.ReadItem() as TillerMan; + m_Hold = reader.ReadItem() as Hold; + m_Anchored = reader.ReadBool(); + m_ShipName = reader.ReadString(); + + if ( version < 1) + Refresh(); + + break; + } + } + + m_Instances.Add( this ); + } + + public void RemoveKeys( Mobile m ) + { + uint keyValue = 0; + + if ( m_PPlank != null ) + keyValue = m_PPlank.KeyValue; + + if ( keyValue == 0 && m_SPlank != null ) + keyValue = m_SPlank.KeyValue; + + Key.RemoveKeys( m, keyValue ); + } + + public uint CreateKeys( Mobile m ) + { + uint value = Key.RandomValue(); + + Key packKey = new Key( KeyType.Gold, value, this ); + Key bankKey = new Key( KeyType.Gold, value, this ); + + packKey.MaxRange = 10; + bankKey.MaxRange = 10; + + packKey.Name = "a ship key"; + bankKey.Name = "a ship key"; + + BankBox box = m.BankBox; + + if ( box == null || !box.TryDropItem( m, bankKey, false ) ) + bankKey.Delete(); + else + m.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502484 ); // A ship's key is now in my safety deposit box. + + if ( m.AddToBackpack( packKey ) ) + m.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502485 ); // A ship's key is now in my backpack. + else + m.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502483 ); // A ship's key is now at my feet. + + return value; + } + + public override void OnAfterDelete() + { + if ( m_TillerMan != null ) + m_TillerMan.Delete(); + + if ( m_Hold != null ) + m_Hold.Delete(); + + if ( m_PPlank != null ) + m_PPlank.Delete(); + + if ( m_SPlank != null ) + m_SPlank.Delete(); + + if ( m_TurnTimer != null ) + m_TurnTimer.Stop(); + + if ( m_MoveTimer != null ) + m_MoveTimer.Stop(); + + m_Instances.Remove( this ); + } + + public override void OnLocationChange( Point3D old ) + { + if ( m_TillerMan != null ) + m_TillerMan.Location = new Point3D( X + (m_TillerMan.X - old.X), Y + (m_TillerMan.Y - old.Y), Z + (m_TillerMan.Z - old.Z ) ); + + if ( m_Hold != null ) + m_Hold.Location = new Point3D( X + (m_Hold.X - old.X), Y + (m_Hold.Y - old.Y), Z + (m_Hold.Z - old.Z ) ); + + if ( m_PPlank != null ) + m_PPlank.Location = new Point3D( X + (m_PPlank.X - old.X), Y + (m_PPlank.Y - old.Y), Z + (m_PPlank.Z - old.Z ) ); + + if ( m_SPlank != null ) + m_SPlank.Location = new Point3D( X + (m_SPlank.X - old.X), Y + (m_SPlank.Y - old.Y), Z + (m_SPlank.Z - old.Z ) ); + } + + public override void OnMapChange() + { + if ( m_TillerMan != null ) + m_TillerMan.Map = Map; + + if ( m_Hold != null ) + m_Hold.Map = Map; + + if ( m_PPlank != null ) + m_PPlank.Map = Map; + + if ( m_SPlank != null ) + m_SPlank.Map = Map; + } + + public bool CanCommand( Mobile m ) + { + return true; + } + + public Point3D GetMarkedLocation() + { + Point3D p = new Point3D( X + MarkOffset.X, Y + MarkOffset.Y, Z + MarkOffset.Z ); + + return Rotate( p, (int)m_Facing / 2 ); + } + + public bool CheckKey( uint keyValue ) + { + if ( m_SPlank != null && m_SPlank.KeyValue == keyValue ) + return true; + + if ( m_PPlank != null && m_PPlank.KeyValue == keyValue ) + return true; + + return false; + } + + private static TimeSpan SlowInterval = TimeSpan.FromSeconds( 0.75 ); + private static TimeSpan FastInterval = TimeSpan.FromSeconds( 0.75 ); + + private static int SlowSpeed = 1; + private static int FastSpeed = 3; + + private static TimeSpan SlowDriftInterval = TimeSpan.FromSeconds( 1.50 ); + private static TimeSpan FastDriftInterval = TimeSpan.FromSeconds( 0.75 ); + + private static int SlowDriftSpeed = 1; + private static int FastDriftSpeed = 1; + + private static Direction Forward = Direction.North; + private static Direction ForwardLeft = Direction.Up; + private static Direction ForwardRight = Direction.Right; + private static Direction Backward = Direction.South; + private static Direction BackwardLeft = Direction.Left; + private static Direction BackwardRight = Direction.Down; + private static Direction Left = Direction.West; + private static Direction Right = Direction.East; + private static Direction Port = Left; + private static Direction Starboard = Right; + + private bool m_Decaying; + + public void Refresh() + { + m_DecayTime = DateTime.Now + BoatDecayDelay; + } + + private class DecayTimer : Timer + { + private BaseBoat m_Boat; + private int m_Count; + + public DecayTimer( BaseBoat boat ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Boat = boat; + + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + if ( m_Count == 5 ) + { + m_Boat.Delete(); + Stop(); + } + else + { + m_Boat.Location = new Point3D( m_Boat.X, m_Boat.Y, m_Boat.Z - 1 ); + + if ( m_Boat.TillerMan != null ) + m_Boat.TillerMan.Say( 1007168 + m_Count ); + + ++m_Count; + } + } + } + + public bool CheckDecay() + { + if ( m_Decaying ) + return true; + + if ( !IsMoving && DateTime.Now >= m_DecayTime ) + { + new DecayTimer( this ).Start(); + + m_Decaying = true; + + return true; + } + + return false; + } + + public bool LowerAnchor( bool message ) + { + if ( CheckDecay() ) + return false; + + if ( m_Anchored ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501445 ); // Ar, the anchor was already dropped sir. + + return false; + } + + StopMove( false ); + + m_Anchored = true; + + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501444 ); // Ar, anchor dropped sir. + + return true; + } + + public bool RaiseAnchor( bool message ) + { + if ( CheckDecay() ) + return false; + + if ( !m_Anchored ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501447 ); // Ar, the anchor has not been dropped sir. + + return false; + } + + m_Anchored = false; + + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501446 ); // Ar, anchor raised sir. + + return true; + } + + public bool StartMove( Direction dir, bool fast ) + { + if ( CheckDecay() ) + return false; + + bool drift = ( dir != Forward && dir != ForwardLeft && dir != ForwardRight ); + TimeSpan interval = (fast ? (drift ? FastDriftInterval : FastInterval) : (drift ? SlowDriftInterval : SlowInterval)); + int speed = (fast ? (drift ? FastDriftSpeed : FastSpeed) : (drift ? SlowDriftSpeed : SlowSpeed)); + + if ( StartMove( dir, speed, interval, false, true ) ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 501429 ); // Aye aye sir. + + return true; + } + + return false; + } + + public bool OneMove( Direction dir ) + { + if ( CheckDecay() ) + return false; + + bool drift = ( dir != Forward ); + TimeSpan interval = drift ? FastDriftInterval : FastInterval; + int speed = drift ? FastDriftSpeed : FastSpeed; + + if ( StartMove( dir, speed, interval, true, true ) ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 501429 ); // Aye aye sir. + + return true; + } + + return false; + } + + public void BeginRename( Mobile from ) + { + if ( CheckDecay() ) + return; + + if ( from.AccessLevel < AccessLevel.GameMaster && from != m_Owner ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( Utility.Random( 1042876, 4 ) ); // Arr, don't do that! | Arr, leave me alone! | Arr, watch what thour'rt doing, matey! | Arr! Do that again and I’ll throw ye overhead! + + return; + } + + if ( m_TillerMan != null ) + m_TillerMan.Say( 502580 ); // What dost thou wish to name thy ship? + + from.Prompt = new RenameBoatPrompt( this ); + } + + public void EndRename( Mobile from, string newName ) + { + if ( Deleted || CheckDecay() ) + return; + + if ( from.AccessLevel < AccessLevel.GameMaster && from != m_Owner ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 1042880 ); // Arr! Only the owner of the ship may change its name! + + return; + } + else if ( !from.Alive ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 502582 ); // You appear to be dead. + + return; + } + + newName = newName.Trim(); + + if ( newName.Length == 0 ) + newName = null; + + Rename( newName ); + } + + public enum DryDockResult{ Valid, Dead, NoKey, NotAnchored, Mobiles, Items, Hold, Decaying } + + public DryDockResult CheckDryDock( Mobile from ) + { + if ( CheckDecay() ) + return DryDockResult.Decaying; + + if ( !from.Alive ) + return DryDockResult.Dead; + + Container pack = from.Backpack; + if ( (m_SPlank == null || !Key.ContainsKey( pack, m_SPlank.KeyValue )) && (m_PPlank == null || !Key.ContainsKey( pack, m_PPlank.KeyValue )) ) + return DryDockResult.NoKey; + + if ( !m_Anchored ) + return DryDockResult.NotAnchored; + + if ( m_Hold != null && m_Hold.Items.Count > 0 ) + return DryDockResult.Hold; + + Map map = Map; + + if ( map == null || map == Map.Internal ) + return DryDockResult.Items; + + MultiComponentList mcl = Components; + + IPooledEnumerable eable = map.GetObjectsInBounds( new Rectangle2D( X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height ) ); + + foreach ( object o in eable ) + { + if ( o == this || o == m_Hold || o == m_SPlank || o == m_PPlank || o == m_TillerMan ) + continue; + + if ( o is Item && Contains( (Item)o ) ) + { + eable.Free(); + return DryDockResult.Items; + } + else if ( o is Mobile && Contains( (Mobile)o ) ) + { + eable.Free(); + return DryDockResult.Mobiles; + } + } + + eable.Free(); + return DryDockResult.Valid; + } + + public void BeginDryDock( Mobile from ) + { + if ( CheckDecay() ) + return; + + DryDockResult result = CheckDryDock( from ); + + if ( result == DryDockResult.Dead ) + from.SendLocalizedMessage( 502493 ); // You appear to be dead. + else if ( result == DryDockResult.NoKey ) + from.SendLocalizedMessage( 502494 ); // You must have a key to the ship to dock the boat. + else if ( result == DryDockResult.NotAnchored ) + from.SendLocalizedMessage( 1010570 ); // You must lower the anchor to dock the boat. + else if ( result == DryDockResult.Mobiles ) + from.SendLocalizedMessage( 502495 ); // You cannot dock the ship with beings on board! + else if ( result == DryDockResult.Items ) + from.SendLocalizedMessage( 502496 ); // You cannot dock the ship with a cluttered deck. + else if ( result == DryDockResult.Hold ) + from.SendLocalizedMessage( 502497 ); // Make sure your hold is empty, and try again! + else if ( result == DryDockResult.Valid ) + from.SendGump( new ConfirmDryDockGump( from, this ) ); + } + + public void EndDryDock( Mobile from ) + { + if ( Deleted || CheckDecay() ) + return; + + DryDockResult result = CheckDryDock( from ); + + if ( result == DryDockResult.Dead ) + from.SendLocalizedMessage( 502493 ); // You appear to be dead. + else if ( result == DryDockResult.NoKey ) + from.SendLocalizedMessage( 502494 ); // You must have a key to the ship to dock the boat. + else if ( result == DryDockResult.NotAnchored ) + from.SendLocalizedMessage( 1010570 ); // You must lower the anchor to dock the boat. + else if ( result == DryDockResult.Mobiles ) + from.SendLocalizedMessage( 502495 ); // You cannot dock the ship with beings on board! + else if ( result == DryDockResult.Items ) + from.SendLocalizedMessage( 502496 ); // You cannot dock the ship with a cluttered deck. + else if ( result == DryDockResult.Hold ) + from.SendLocalizedMessage( 502497 ); // Make sure your hold is empty, and try again! + + if ( result != DryDockResult.Valid ) + return; + + BaseDockedBoat boat = DockedBoat; + + if ( boat == null ) + return; + + RemoveKeys( from ); + + from.AddToBackpack( boat ); + Delete(); + } + + public void SetName( SpeechEventArgs e ) + { + if ( CheckDecay() ) + return; + + if ( e.Mobile.AccessLevel < AccessLevel.GameMaster && e.Mobile != m_Owner ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 1042880 ); // Arr! Only the owner of the ship may change its name! + + return; + } + else if ( !e.Mobile.Alive ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 502582 ); // You appear to be dead. + + return; + } + + if ( e.Speech.Length > 8 ) + { + string newName = e.Speech.Substring( 8 ).Trim(); + + if ( newName.Length == 0 ) + newName = null; + + Rename( newName ); + } + } + + public void Rename( string newName ) + { + if ( CheckDecay() ) + return; + + if ( newName != null && newName.Length > 40 ) + newName = newName.Substring( 0, 40 ); + + if ( m_ShipName == newName ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 502531 ); // Yes, sir. + + return; + } + + ShipName = newName; + + if ( m_TillerMan != null && m_ShipName != null ) + m_TillerMan.Say( 1042885, m_ShipName ); // This ship is now called the ~1_NEW_SHIP_NAME~. + else if ( m_TillerMan != null ) + m_TillerMan.Say( 502534 ); // This ship now has no name. + } + + public void RemoveName( Mobile m ) + { + if ( CheckDecay() ) + return; + + if ( m.AccessLevel < AccessLevel.GameMaster && m != m_Owner ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 1042880 ); // Arr! Only the owner of the ship may change its name! + + return; + } + else if ( !m.Alive ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 502582 ); // You appear to be dead. + + return; + } + + if ( m_ShipName == null ) + { + if ( m_TillerMan != null ) + m_TillerMan.Say( 502526 ); // Ar, this ship has no name. + + return; + } + + ShipName = null; + + if ( m_TillerMan != null ) + m_TillerMan.Say( 502534 ); // This ship now has no name. + } + + public void GiveName( Mobile m ) + { + if ( m_TillerMan == null || CheckDecay() ) + return; + + if ( m_ShipName == null ) + m_TillerMan.Say( 502526 ); // Ar, this ship has no name. + else + m_TillerMan.Say( 1042881, m_ShipName ); // This is the ~1_BOAT_NAME~. + } + + public void GiveNavPoint() + { + if ( TillerMan == null || CheckDecay() ) + return; + + if ( NextNavPoint < 0 ) + TillerMan.Say( 1042882 ); // I have no current nav point. + else + TillerMan.Say( 1042883, (NextNavPoint + 1).ToString() ); // My current destination navpoint is nav ~1_NAV_POINT_NUM~. + } + + public void AssociateMap( MapItem map ) + { + if ( CheckDecay() ) + return; + + if ( map is BlankMap ) + { + if ( TillerMan != null ) + TillerMan.Say( 502575 ); // Ar, that is not a map, tis but a blank piece of paper! + } + else if ( map.Pins.Count == 0 ) + { + if ( TillerMan != null ) + TillerMan.Say( 502576 ); // Arrrr, this map has no course on it! + } + else + { + StopMove( false ); + + MapItem = map; + NextNavPoint = -1; + + if ( TillerMan != null ) + TillerMan.Say( 502577 ); // A map! + } + } + + public bool StartCourse( string navPoint, bool single, bool message ) + { + int number = -1; + + int start = -1; + for ( int i = 0; i < navPoint.Length; i++ ) + { + if ( Char.IsDigit( navPoint[i] ) ) + { + start = i; + break; + } + } + + if ( start != -1 ) + { + string sNumber = navPoint.Substring( start ); + + try + { + number = Convert.ToInt32( sNumber ); + } + catch + { + number = -1; + } + + if ( number != -1 ) + { + number--; + + if ( MapItem == null || number < 0 || number >= MapItem.Pins.Count ) + { + number = -1; + } + } + } + + if ( number == -1 ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 1042551 ); // I don't see that navpoint, sir. + + return false; + } + + NextNavPoint = number; + return StartCourse( single, message ); + } + + public bool StartCourse( bool single, bool message ) + { + if ( CheckDecay() ) + return false; + + if ( Anchored ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 501419 ); // Ar, the anchor is down sir! + + return false; + } + else if ( MapItem == null || MapItem.Deleted ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 502513 ); // I have seen no map, sir. + + return false; + } + else if ( this.Map != MapItem.Map || !this.Contains( MapItem.GetWorldLocation() ) ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 502514 ); // The map is too far away from me, sir. + + return false; + } + else if ( ( this.Map != Map.Trammel && this.Map != Map.Felucca ) || NextNavPoint < 0 || NextNavPoint >= MapItem.Pins.Count ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 1042551 ); // I don't see that navpoint, sir. + + return false; + } + + Speed = FastSpeed; + Order = single ? BoatOrder.Single : BoatOrder.Course; + + if ( m_MoveTimer != null ) + m_MoveTimer.Stop(); + + m_MoveTimer = new MoveTimer( this, FastInterval, false ); + m_MoveTimer.Start(); + + if ( message && TillerMan != null ) + TillerMan.Say( 501429 ); // Aye aye sir. + + return true; + } + + public override bool HandlesOnSpeech{ get{ return true; } } + + public override void OnSpeech( SpeechEventArgs e ) + { + if ( CheckDecay() ) + return; + + Mobile from = e.Mobile; + + if ( CanCommand( from ) && Contains( from ) ) + { + for ( int i = 0; i < e.Keywords.Length; ++i ) + { + int keyword = e.Keywords[i]; + + if ( keyword >= 0x42 && keyword <= 0x6B ) + { + switch ( keyword ) + { + case 0x42: SetName( e ); break; + case 0x43: RemoveName( e.Mobile ); break; + case 0x44: GiveName( e.Mobile ); break; + case 0x45: StartMove( Forward, true ); break; + case 0x46: StartMove( Backward, true ); break; + case 0x47: StartMove( Left, true ); break; + case 0x48: StartMove( Right, true ); break; + case 0x4B: StartMove( ForwardLeft, true ); break; + case 0x4C: StartMove( ForwardRight, true ); break; + case 0x4D: StartMove( BackwardLeft, true ); break; + case 0x4E: StartMove( BackwardRight, true ); break; + case 0x4F: StopMove( true ); break; + case 0x50: StartMove( Left, false ); break; + case 0x51: StartMove( Right, false ); break; + case 0x52: StartMove( Forward, false ); break; + case 0x53: StartMove( Backward, false ); break; + case 0x54: StartMove( ForwardLeft, false ); break; + case 0x55: StartMove( ForwardRight, false ); break; + case 0x56: StartMove( BackwardRight, false ); break; + case 0x57: StartMove( BackwardLeft, false ); break; + case 0x58: OneMove( Left ); break; + case 0x59: OneMove( Right ); break; + case 0x5A: OneMove( Forward ); break; + case 0x5B: OneMove( Backward ); break; + case 0x5C: OneMove( ForwardLeft ); break; + case 0x5D: OneMove( ForwardRight ); break; + case 0x5E: OneMove( BackwardRight ); break; + case 0x5F: OneMove( BackwardLeft ); break; + case 0x49: case 0x65: StartTurn( 2, true ); break; // turn right + case 0x4A: case 0x66: StartTurn( -2, true ); break; // turn left + case 0x67: StartTurn( -4, true ); break; // turn around, come about + case 0x68: StartMove( Forward, true ); break; + case 0x69: StopMove( true ); break; + case 0x6A: LowerAnchor( true ); break; + case 0x6B: RaiseAnchor( true ); break; + case 0x60: GiveNavPoint(); break; // nav + case 0x61: NextNavPoint = 0; StartCourse( false, true ); break; // start + case 0x62: StartCourse( false, true ); break; // continue + case 0x63: StartCourse( e.Speech, false, true ); break; // goto* + case 0x64: StartCourse( e.Speech, true, true ); break; // single* + } + + break; + } + } + } + } + + public bool StartTurn( int offset, bool message ) + { + if ( CheckDecay() ) + return false; + + if ( m_Anchored ) + { + if ( message ) + m_TillerMan.Say( 501419 ); // Ar, the anchor is down sir! + + return false; + } + else + { + if ( m_MoveTimer != null && this.Order != BoatOrder.Move ) + { + m_MoveTimer.Stop(); + m_MoveTimer = null; + } + + if ( m_TurnTimer != null ) + m_TurnTimer.Stop(); + + m_TurnTimer = new TurnTimer( this, offset ); + m_TurnTimer.Start(); + + if ( message && TillerMan != null ) + TillerMan.Say( 501429 ); // Aye aye sir. + + return true; + } + } + + public bool Turn( int offset, bool message ) + { + if ( m_TurnTimer != null ) + { + m_TurnTimer.Stop(); + m_TurnTimer = null; + } + + if ( CheckDecay() ) + return false; + + if ( m_Anchored ) + { + if ( message ) + m_TillerMan.Say( 501419 ); // Ar, the anchor is down sir! + + return false; + } + else if ( SetFacing( (Direction)(((int)m_Facing + offset) & 0x7) ) ) + { + return true; + } + else + { + if ( message ) + m_TillerMan.Say( 501423 ); // Ar, can't turn sir. + + return false; + } + } + + private class TurnTimer : Timer + { + private BaseBoat m_Boat; + private int m_Offset; + + public TurnTimer( BaseBoat boat, int offset ) : base( TimeSpan.FromSeconds( 0.5 ) ) + { + m_Boat = boat; + m_Offset = offset; + + Priority = TimerPriority.TenMS; + } + + protected override void OnTick() + { + if ( !m_Boat.Deleted ) + m_Boat.Turn( m_Offset, true ); + } + } + + public bool StartMove( Direction dir, int speed, TimeSpan interval, bool single, bool message ) + { + if ( CheckDecay() ) + return false; + + if ( m_Anchored ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501419 ); // Ar, the anchor is down sir! + + return false; + } + + m_Moving = dir; + m_Speed = speed; + m_Order = BoatOrder.Move; + + if ( m_MoveTimer != null ) + m_MoveTimer.Stop(); + + m_MoveTimer = new MoveTimer( this, interval, single ); + m_MoveTimer.Start(); + + return true; + } + + public bool StopMove( bool message ) + { + if ( CheckDecay() ) + return false; + + if ( m_MoveTimer == null ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501443 ); // Er, the ship is not moving sir. + + return false; + } + + m_Moving = Direction.North; + m_Speed = 0; + m_MoveTimer.Stop(); + m_MoveTimer = null; + + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501429 ); // Aye aye sir. + + return true; + } + + public bool CanFit( Point3D p, Map map, int itemID ) + { + if ( map == null || map == Map.Internal || Deleted || CheckDecay() ) + return false; + + MultiComponentList newComponents = MultiData.GetComponents( itemID ); + + for ( int x = 0; x < newComponents.Width; ++x ) + { + for ( int y = 0; y < newComponents.Height; ++y ) + { + int tx = p.X + newComponents.Min.X + x; + int ty = p.Y + newComponents.Min.Y + y; + + if ( newComponents.Tiles[x][y].Length == 0 || Contains( tx, ty ) ) + continue; + + Tile landTile = map.Tiles.GetLandTile( tx, ty ); + Tile[] tiles = map.Tiles.GetStaticTiles( tx, ty, true ); + + bool hasWater = false; + + if ( landTile.Z == p.Z && ((landTile.ID >= 168 && landTile.ID <= 171) || (landTile.ID >= 310 && landTile.ID <= 311)) ) + hasWater = true; + + int z = p.Z; + + //int landZ = 0, landAvg = 0, landTop = 0; + + //map.GetAverageZ( tx, ty, ref landZ, ref landAvg, ref landTop ); + + //if ( !landTile.Ignored && top > landZ && landTop > z ) + // return false; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + bool isWater = ( tile.ID >= 0x5796 && tile.ID <= 0x57B2 ); + + if ( tile.Z == p.Z && isWater ) + hasWater = true; + else if ( tile.Z >= p.Z && !isWater ) + return false; + } + + if ( !hasWater ) + return false; + } + } + + IPooledEnumerable eable = map.GetItemsInBounds( new Rectangle2D( p.X + newComponents.Min.X, p.Y + newComponents.Min.Y, newComponents.Width, newComponents.Height ) ); + + foreach ( Item item in eable ) + { + if ( item.ItemID >= 0x4000 || item.Z < p.Z || !item.Visible ) + continue; + + int x = item.X - p.X + newComponents.Min.X; + int y = item.Y - p.Y + newComponents.Min.Y; + + if ( x >= 0 && x < newComponents.Width && y >= 0 && y < newComponents.Height && newComponents.Tiles[x][y].Length == 0 ) + continue; + else if ( Contains( item ) ) + continue; + + eable.Free(); + return false; + } + + eable.Free(); + + return true; + } + + public Point3D Rotate( Point3D p, int count ) + { + int rx = p.X - Location.X; + int ry = p.Y - Location.Y; + + for ( int i = 0; i < count; ++i ) + { + int temp = rx; + rx = -ry; + ry = temp; + } + + return new Point3D( Location.X + rx, Location.Y + ry, p.Z ); + } + + public override bool Contains( int x, int y ) + { + if ( base.Contains( x, y ) ) + return true; + + if ( m_TillerMan != null && x == m_TillerMan.X && y == m_TillerMan.Y ) + return true; + + if ( m_Hold != null && x == m_Hold.X && y == m_Hold.Y ) + return true; + + if ( m_PPlank != null && x == m_PPlank.X && y == m_PPlank.Y ) + return true; + + if ( m_SPlank != null && x == m_SPlank.X && y == m_SPlank.Y ) + return true; + + return false; + } + + public static bool IsValidLocation( Point3D p, Map map ) + { + Rectangle2D[] wrap = GetWrapFor( map ); + + for ( int i = 0; i < wrap.Length; ++i ) + { + if ( wrap[i].Contains( p ) ) + return true; + } + + return false; + } + + public static Rectangle2D[] GetWrapFor( Map m ) + { + if( m == Map.Ilshenar ) + return m_IlshWrap; + else if( m == Map.Tokuno ) + return m_TokunoWrap; + else + return m_BritWrap; + } + + public Direction GetMovementFor( int x, int y, out int maxSpeed ) + { + int dx = x - this.X; + int dy = y - this.Y; + + int adx = Math.Abs( dx ); + int ady = Math.Abs( dy ); + + Direction dir = Utility.GetDirection( this, new Point2D( x, y ) ); + int iDir = (int) dir; + + // Compute the maximum distance we can travel without going too far away + if ( iDir % 2 == 0 ) // North, East, South and West + maxSpeed = Math.Abs( adx - ady ); + else // Right, Down, Left and Up + maxSpeed = Math.Min( adx, ady ); + + return (Direction) ((iDir - (int)Facing) & 0x7); + } + + public bool DoMovement( bool message ) + { + Direction dir; + int speed; + + if ( this.Order == BoatOrder.Move ) + { + dir = this.Moving; + speed = this.Speed; + } + else if ( MapItem == null || MapItem.Deleted ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 502513 ); // I have seen no map, sir. + + return false; + } + else if ( this.Map != MapItem.Map || !this.Contains( MapItem.GetWorldLocation() ) ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 502514 ); // The map is too far away from me, sir. + + return false; + } + else if ( ( this.Map != Map.Trammel && this.Map != Map.Felucca ) || NextNavPoint < 0 || NextNavPoint >= MapItem.Pins.Count ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 1042551 ); // I don't see that navpoint, sir. + + return false; + } + else + { + Point2D dest = (Point2D) MapItem.Pins[NextNavPoint]; + + int x, y; + MapItem.ConvertToWorld( dest.X, dest.Y, out x, out y ); + + int maxSpeed; + dir = GetMovementFor( x, y, out maxSpeed ); + + if ( maxSpeed == 0 ) + { + if ( message && this.Order == BoatOrder.Single && TillerMan != null ) + TillerMan.Say( 1042874, (NextNavPoint + 1).ToString() ); // We have arrived at nav point ~1_POINT_NUM~ , sir. + + if ( NextNavPoint + 1 < MapItem.Pins.Count ) + { + NextNavPoint++; + + if ( this.Order == BoatOrder.Course ) + { + if ( message && TillerMan != null ) + TillerMan.Say( 1042875, (NextNavPoint + 1).ToString() ); // Heading to nav point ~1_POINT_NUM~, sir. + + return true; + } + + return false; + } + else + { + NextNavPoint = -1; + + if ( message && this.Order == BoatOrder.Course && TillerMan != null ) + TillerMan.Say( 502515 ); // The course is completed, sir. + + return false; + } + } + + if ( dir == Left || dir == BackwardLeft || dir == Backward ) + return Turn( -2, true ); + else if ( dir == Right || dir == BackwardRight ) + return Turn( 2, true ); + + speed = Math.Min( this.Speed, maxSpeed ); + } + + return Move( dir, speed, true ); + } + + public bool Move( Direction dir, int speed, bool message ) + { + Map map = Map; + + if ( map == null || Deleted || CheckDecay() ) + return false; + + if ( m_Anchored ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501419 ); // Ar, the anchor is down sir! + + return false; + } + + int rx = 0, ry = 0; + Movement.Movement.Offset( (Direction)(((int)m_Facing + (int)dir) & 0x7), ref rx, ref ry ); + + for ( int i = 1; i <= speed; ++i ) + { + if ( !CanFit( new Point3D( X + (i * rx), Y + (i * ry), Z ), Map, ItemID ) ) + { + if ( i == 1 ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501424 ); // Ar, we've stopped sir. + + return false; + } + + speed = i - 1; + break; + } + } + + int xOffset = speed*rx; + int yOffset = speed*ry; + + int newX = X + xOffset; + int newY = Y + yOffset; + + Rectangle2D[] wrap = GetWrapFor( map ); + + for ( int i = 0; i < wrap.Length; ++i ) + { + Rectangle2D rect = wrap[i]; + + if ( rect.Contains( new Point2D( X, Y ) ) && !rect.Contains( new Point2D( newX, newY ) ) ) + { + if ( newX < rect.X ) + newX = rect.X + rect.Width - 1; + else if ( newX >= rect.X + rect.Width ) + newX = rect.X; + + if ( newY < rect.Y ) + newY = rect.Y + rect.Height - 1; + else if ( newY >= rect.Y + rect.Height ) + newY = rect.Y; + + for ( int j = 1; j <= speed; ++j ) + { + if ( !CanFit( new Point3D( newX + (j * rx), newY + (j * ry), Z ), Map, ItemID ) ) + { + if ( message && m_TillerMan != null ) + m_TillerMan.Say( 501424 ); // Ar, we've stopped sir. + + return false; + } + } + + xOffset = newX - X; + yOffset = newY - Y; + } + } + + Teleport( xOffset, yOffset, 0 ); + + return true; + } + + public void Teleport( int xOffset, int yOffset, int zOffset ) + { + MultiComponentList mcl = Components; + + ArrayList toMove = new ArrayList(); + + IPooledEnumerable eable = this.Map.GetObjectsInBounds( new Rectangle2D( X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height ) ); + + foreach ( object o in eable ) + { + if ( o != this && !(o is TillerMan || o is Hold || o is Plank) ) + toMove.Add( o ); + } + + eable.Free(); + + for ( int i = 0; i < toMove.Count; ++i ) + { + object o = toMove[i]; + + if ( o is Item ) + { + Item item = (Item)o; + + if ( Contains( item ) && item.Visible && item.Z >= Z ) + item.Location = new Point3D( item.X + xOffset, item.Y + yOffset, item.Z + zOffset ); + } + else if ( o is Mobile ) + { + Mobile m = (Mobile)o; + + if ( Contains( m ) ) + m.Location = new Point3D( m.X + xOffset, m.Y + yOffset, m.Z + zOffset ); + } + } + + Location = new Point3D( X + xOffset, Y + yOffset, Z + zOffset ); + } + + public bool SetFacing( Direction facing ) + { + if ( Parent != null || this.Map == null ) + return false; + + if ( CheckDecay() ) + return false; + + if ( Map != Map.Internal ) + { + switch ( facing ) + { + case Direction.North: if ( !CanFit( Location, Map, NorthID ) ) return false; break; + case Direction.East: if ( !CanFit( Location, Map, EastID ) ) return false; break; + case Direction.South: if ( !CanFit( Location, Map, SouthID ) ) return false; break; + case Direction.West: if ( !CanFit( Location, Map, WestID ) ) return false; break; + } + } + + this.Map.OnLeave( this ); + + Direction old = m_Facing; + + m_Facing = facing; + + if ( m_TillerMan != null ) + m_TillerMan.SetFacing( facing ); + + if ( m_Hold != null ) + m_Hold.SetFacing( facing ); + + if ( m_PPlank != null ) + m_PPlank.SetFacing( facing ); + + if ( m_SPlank != null ) + m_SPlank.SetFacing( facing ); + + MultiComponentList mcl = Components; + + ArrayList toMove = new ArrayList(); + + toMove.Add( m_PPlank ); + toMove.Add( m_SPlank ); + + IPooledEnumerable eable = Map.GetObjectsInBounds( new Rectangle2D( X + mcl.Min.X, Y + mcl.Min.Y, mcl.Width, mcl.Height ) ); + + foreach ( object o in eable ) + { + if ( o is Item ) + { + Item item = (Item)o; + + if ( item != this && Contains( item ) && item.Visible && item.Z >= Z && !(item is TillerMan || item is Hold || item is Plank) ) + toMove.Add( item ); + } + else if ( o is Mobile && Contains( (Mobile)o ) ) + { + toMove.Add( o ); + + ((Mobile)o).Direction = (Direction)((int)((Mobile)o).Direction - (int)old + (int)facing); + } + } + + eable.Free(); + + int xOffset = 0, yOffset = 0; + Movement.Movement.Offset( facing, ref xOffset, ref yOffset ); + + if ( m_TillerMan != null ) + m_TillerMan.Location = new Point3D( X + (xOffset * TillerManDistance) + (facing == Direction.North ? 1 : 0), Y + (yOffset * TillerManDistance), m_TillerMan.Z ); + + if ( m_Hold != null ) + m_Hold.Location = new Point3D( X + (xOffset * HoldDistance), Y + (yOffset * HoldDistance), m_Hold.Z ); + + int count = (int)(m_Facing - old) & 0x7; + count /= 2; + + for ( int i = 0; i < toMove.Count; ++i ) + { + object o = toMove[i]; + + if ( o is Item ) + ((Item)o).Location = Rotate( ((Item)o).Location, count ); + else if ( o is Mobile ) + ((Mobile)o).Location = Rotate( ((Mobile)o).Location, count ); + } + + switch ( facing ) + { + case Direction.North: ItemID = NorthID; break; + case Direction.East: ItemID = EastID; break; + case Direction.South: ItemID = SouthID; break; + case Direction.West: ItemID = WestID; break; + } + + this.Map.OnEnter( this ); + + return true; + } + + private class MoveTimer : Timer + { + private BaseBoat m_Boat; + + public MoveTimer( BaseBoat boat, TimeSpan interval, bool single ) : base( interval, interval, single ? 1 : 0 ) + { + m_Boat = boat; + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + if ( !m_Boat.DoMovement( true ) ) + m_Boat.StopMove( false ); + } + } + + public static void UpdateAllComponents() + { + for ( int i = m_Instances.Count - 1; i >= 0; --i ) + ((BaseBoat)m_Instances[i]).UpdateComponents(); + } + + public static void Initialize() + { + new UpdateAllTimer().Start(); + EventSink.WorldSave += new WorldSaveEventHandler( EventSink_WorldSave ); + } + + private static void EventSink_WorldSave( WorldSaveEventArgs e ) + { + new UpdateAllTimer().Start(); + } + + public class UpdateAllTimer : Timer + { + public UpdateAllTimer() : base( TimeSpan.FromSeconds( 1.0 ) ) + { + } + + protected override void OnTick() + { + UpdateAllComponents(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/BaseBoatDeed.cs b/Scripts/Multis/Boats/BaseBoatDeed.cs new file mode 100644 index 000000000..7e612d5e2 --- /dev/null +++ b/Scripts/Multis/Boats/BaseBoatDeed.cs @@ -0,0 +1,183 @@ +using System; +using Server; +using Server.Regions; +using Server.Targeting; +using Server.Engines.CannedEvil; +using Server.Network; + +namespace Server.Multis +{ + public abstract class BaseBoatDeed : Item + { + private int m_MultiID; + private Point3D m_Offset; + + [CommandProperty( AccessLevel.GameMaster )] + public int MultiID{ get{ return m_MultiID; } set{ m_MultiID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Offset{ get{ return m_Offset; } set{ m_Offset = value; } } + + public BaseBoatDeed( int id, Point3D offset ) : base( 0x14F2 ) + { + Weight = 1.0; + + if ( !Core.AOS ) + LootType = LootType.Newbied; + + m_MultiID = id & 0x3FFF; + m_Offset = offset; + } + + public BaseBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_MultiID ); + writer.Write( m_Offset ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_MultiID = reader.ReadInt(); + m_Offset = reader.ReadPoint3D(); + + break; + } + } + + if ( Weight == 0.0 ) + Weight = 1.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.AccessLevel < AccessLevel.GameMaster && (from.Map == Map.Ilshenar || from.Map == Map.Malas) ) + { + from.SendLocalizedMessage( 1010567, null, 0x25 ); // You may not place a boat from this location. + } + else + { + if ( Core.SE ) + from.SendLocalizedMessage( 502482 ); // Where do you wish to place the ship? + else + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502482 ); // Where do you wish to place the ship? + + from.Target = new InternalTarget( this ); + } + } + + public abstract BaseBoat Boat{ get; } + + public void OnPlacement( Mobile from, Point3D p ) + { + if ( Deleted ) + { + return; + } + else if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + Map map = from.Map; + + if ( map == null ) + return; + + if ( from.AccessLevel < AccessLevel.GameMaster && (map == Map.Ilshenar || map == Map.Malas) ) + { + from.SendLocalizedMessage( 1043284 ); // A ship can not be created here. + return; + } + + if ( from.Region.IsPartOf( typeof( HouseRegion ) ) || Server.Multis.BaseBoat.FindBoatAt( from, from.Map ) != null ) + { + from.SendLocalizedMessage( 1010568, null, 0x25 ); // You may not place a ship while on another ship or inside a house. + return; + } + + BaseBoat boat = Boat; + + if ( boat == null ) + return; + + p = new Point3D( p.X - m_Offset.X, p.Y - m_Offset.Y, p.Z - m_Offset.Z ); + + if ( BaseBoat.IsValidLocation( p, map ) && boat.CanFit( p, map, boat.ItemID ) ) + { + Delete(); + + boat.Owner = from; + boat.Anchored = true; + + uint keyValue = boat.CreateKeys( from ); + + if ( boat.PPlank != null ) + boat.PPlank.KeyValue = keyValue; + + if ( boat.SPlank != null ) + boat.SPlank.KeyValue = keyValue; + + boat.MoveToWorld( p, map ); + } + else + { + boat.Delete(); + from.SendLocalizedMessage( 1043284 ); // A ship can not be created here. + } + } + } + + private class InternalTarget : MultiTarget + { + private BaseBoatDeed m_Deed; + + public InternalTarget( BaseBoatDeed deed ) : base( deed.MultiID, deed.Offset ) + { + m_Deed = deed; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D ip = o as IPoint3D; + + if ( ip != null ) + { + if ( ip is Item ) + ip = ((Item)ip).GetWorldTop(); + + Point3D p = new Point3D( ip ); + + Region region = Region.Find( p, from.Map ); + + if ( region.IsPartOf( typeof( DungeonRegion ) ) ) + from.SendLocalizedMessage( 502488 ); // You can not place a ship inside a dungeon. + else if ( region.IsPartOf( typeof( HouseRegion ) ) || region.IsPartOf( typeof( ChampionSpawnRegion ) ) ) + from.SendLocalizedMessage( 1042549 ); // A boat may not be placed in this area. + else + m_Deed.OnPlacement( from, p ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/BaseDockedBoat.cs b/Scripts/Multis/Boats/BaseDockedBoat.cs new file mode 100644 index 000000000..2eea84597 --- /dev/null +++ b/Scripts/Multis/Boats/BaseDockedBoat.cs @@ -0,0 +1,193 @@ +using System; +using Server; +using Server.Regions; +using Server.Targeting; +using Server.Engines.CannedEvil; + +namespace Server.Multis +{ + public abstract class BaseDockedBoat : Item + { + private int m_MultiID; + private Point3D m_Offset; + private string m_ShipName; + + [CommandProperty( AccessLevel.GameMaster )] + public int MultiID{ get{ return m_MultiID; } set{ m_MultiID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Offset{ get{ return m_Offset; } set{ m_Offset = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public string ShipName{ get{ return m_ShipName; } set{ m_ShipName = value; InvalidateProperties(); } } + + public BaseDockedBoat( int id, Point3D offset, BaseBoat boat ) : base( 0x14F4 ) + { + Weight = 1.0; + LootType = LootType.Blessed; + + m_MultiID = id & 0x3FFF; + m_Offset = offset; + + m_ShipName = boat.ShipName; + } + + public BaseDockedBoat( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_MultiID ); + writer.Write( m_Offset ); + writer.Write( m_ShipName ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + case 0: + { + m_MultiID = reader.ReadInt(); + m_Offset = reader.ReadPoint3D(); + m_ShipName = reader.ReadString(); + + if ( version == 0 ) + reader.ReadUInt(); + + break; + } + } + + if ( LootType == LootType.Newbied ) + LootType = LootType.Blessed; + + if ( Weight == 0.0 ) + Weight = 1.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + from.SendLocalizedMessage( 502482 ); // Where do you wish to place the ship? + + from.Target = new InternalTarget( this ); + } + } + + public abstract BaseBoat Boat{ get; } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( m_ShipName != null ) + list.Add( m_ShipName ); + else + base.AddNameProperty( list ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_ShipName != null ) + LabelTo( from, m_ShipName ); + else + base.OnSingleClick( from ); + } + + public void OnPlacement( Mobile from, Point3D p ) + { + if ( Deleted ) + { + return; + } + else if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else + { + Map map = from.Map; + + if ( map == null ) + return; + + BaseBoat boat = Boat; + + if ( boat == null ) + return; + + p = new Point3D( p.X - m_Offset.X, p.Y - m_Offset.Y, p.Z - m_Offset.Z ); + + if ( BaseBoat.IsValidLocation( p, map ) && boat.CanFit( p, map, boat.ItemID ) && map != Map.Ilshenar && map != Map.Malas ) + { + Delete(); + + boat.Owner = from; + boat.Anchored = true; + boat.ShipName = m_ShipName; + + uint keyValue = boat.CreateKeys( from ); + + if ( boat.PPlank != null ) + boat.PPlank.KeyValue = keyValue; + + if ( boat.SPlank != null ) + boat.SPlank.KeyValue = keyValue; + + boat.MoveToWorld( p, map ); + } + else + { + boat.Delete(); + from.SendLocalizedMessage( 1043284 ); // A ship can not be created here. + } + } + } + + private class InternalTarget : MultiTarget + { + private BaseDockedBoat m_Model; + + public InternalTarget( BaseDockedBoat model ) : base( model.MultiID, model.Offset ) + { + m_Model = model; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D ip = o as IPoint3D; + + if ( ip != null ) + { + if ( ip is Item ) + ip = ((Item)ip).GetWorldTop(); + + Point3D p = new Point3D( ip ); + + Region region = Region.Find( p, from.Map ); + + if ( region.IsPartOf( typeof( DungeonRegion ) ) ) + from.SendLocalizedMessage( 502488 ); // You can not place a ship inside a dungeon. + else if ( region.IsPartOf( typeof( HouseRegion ) ) || region.IsPartOf( typeof( ChampionSpawnRegion ) ) ) + from.SendLocalizedMessage( 1042549 ); // A boat may not be placed in this area. + else + m_Model.OnPlacement( from, p ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/ConfirmDryDockGump.cs b/Scripts/Multis/Boats/ConfirmDryDockGump.cs new file mode 100644 index 000000000..51b845a33 --- /dev/null +++ b/Scripts/Multis/Boats/ConfirmDryDockGump.cs @@ -0,0 +1,40 @@ +using System; +using Server; +using Server.Gumps; +using Server.Network; + +namespace Server.Multis +{ + public class ConfirmDryDockGump : Gump + { + private Mobile m_From; + private BaseBoat m_Boat; + + public ConfirmDryDockGump( Mobile from, BaseBoat boat ) : base( 150, 200 ) + { + m_From = from; + m_Boat = boat; + + m_From.CloseGump( typeof( ConfirmDryDockGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 220, 170, 5054 ); + AddBackground( 10, 10, 200, 150, 3000 ); + + AddHtmlLocalized( 20, 20, 180, 80, 1018319, true, false ); // Do you wish to dry dock this boat? + + AddHtmlLocalized( 55, 100, 140, 25, 1011011, false, false ); // CONTINUE + AddButton( 20, 100, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + + AddHtmlLocalized( 55, 125, 140, 25, 1011012, false, false ); // CANCEL + AddButton( 20, 125, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID == 2 ) + m_Boat.EndDryDock( m_From ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/Hold.cs b/Scripts/Multis/Boats/Hold.cs new file mode 100644 index 000000000..768d20393 --- /dev/null +++ b/Scripts/Multis/Boats/Hold.cs @@ -0,0 +1,125 @@ +using System; +using Server; +using Server.Multis; +using Server.Network; + +namespace Server.Items +{ + public class Hold : Container + { + private BaseBoat m_Boat; + + public Hold( BaseBoat boat ) : base( 0x3EAE ) + { + m_Boat = boat; + Movable = false; + } + + public Hold( Serial serial ) : base( serial ) + { + } + + public void SetFacing( Direction dir ) + { + switch ( dir ) + { + case Direction.East: ItemID = 0x3E65; break; + case Direction.West: ItemID = 0x3E93; break; + case Direction.North: ItemID = 0x3EAE; break; + case Direction.South: ItemID = 0x3EB9; break; + } + } + + public override bool OnDragDrop( Mobile from, Item item ) + { + if ( m_Boat == null || !m_Boat.Contains( from ) || m_Boat.IsMoving ) + return false; + + return base.OnDragDrop( from, item ); + } + + public override bool OnDragDropInto( Mobile from, Item item, Point3D p ) + { + if ( m_Boat == null || !m_Boat.Contains( from ) || m_Boat.IsMoving ) + return false; + + return base.OnDragDropInto( from, item, p ); + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + if ( item != this && (m_Boat == null || !m_Boat.Contains( from ) || m_Boat.IsMoving) ) + return false; + + return base.CheckItemUse( from, item ); + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( m_Boat == null || !m_Boat.Contains( from ) || m_Boat.IsMoving ) + return false; + + return base.CheckLift( from, item, ref reject ); + } + + public override void OnAfterDelete() + { + if ( m_Boat != null ) + m_Boat.Delete(); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Boat == null || !m_Boat.Contains( from ) ) + { + if ( m_Boat.TillerMan != null ) + m_Boat.TillerMan.Say( 502490 ); // You must be on the ship to open the hold. + } + else if ( m_Boat.IsMoving ) + { + if ( m_Boat.TillerMan != null ) + m_Boat.TillerMan.Say( 502491 ); // I can not open the hold while the ship is moving. + } + else + { + base.OnDoubleClick( from ); + } + } + + public override bool IsDecoContainer + { + get{ return false; } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); + + writer.Write( m_Boat ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Boat = reader.ReadItem() as BaseBoat; + + if ( m_Boat == null || Parent != null ) + Delete(); + + Movable = false; + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/LargeBoat.cs b/Scripts/Multis/Boats/LargeBoat.cs new file mode 100644 index 000000000..0308a57ec --- /dev/null +++ b/Scripts/Multis/Boats/LargeBoat.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class LargeBoat : BaseBoat + { + public override int NorthID{ get{ return 0x4010; } } + public override int EastID{ get{ return 0x4011; } } + public override int SouthID{ get{ return 0x4012; } } + public override int WestID{ get{ return 0x4013; } } + + public override int HoldDistance{ get{ return 5; } } + public override int TillerManDistance{ get{ return -5; } } + + public override Point2D StarboardOffset{ get{ return new Point2D( 2, -1 ); } } + public override Point2D PortOffset{ get{ return new Point2D( -2, -1 ); } } + + public override Point3D MarkOffset{ get{ return new Point3D( 0, 0, 3 ); } } + + public override BaseDockedBoat DockedBoat{ get{ return new LargeDockedBoat( this ); } } + + [Constructable] + public LargeBoat() + { + } + + public LargeBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class LargeBoatDeed : BaseBoatDeed + { + public override int LabelNumber{ get{ return 1041209; } } // large ship deed + public override BaseBoat Boat{ get{ return new LargeBoat(); } } + + [Constructable] + public LargeBoatDeed() : base( 0x4010, new Point3D( 0, -1, 0 ) ) + { + } + + public LargeBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class LargeDockedBoat : BaseDockedBoat + { + public override BaseBoat Boat{ get{ return new LargeBoat(); } } + + public LargeDockedBoat( BaseBoat boat ) : base( 0x4010, new Point3D( 0, -1, 0 ), boat ) + { + } + + public LargeDockedBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/LargeDragonBoat.cs b/Scripts/Multis/Boats/LargeDragonBoat.cs new file mode 100644 index 000000000..6c08f579e --- /dev/null +++ b/Scripts/Multis/Boats/LargeDragonBoat.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class LargeDragonBoat : BaseBoat + { + public override int NorthID{ get{ return 0x4014; } } + public override int EastID{ get{ return 0x4015; } } + public override int SouthID{ get{ return 0x4016; } } + public override int WestID{ get{ return 0x4017; } } + + public override int HoldDistance{ get{ return 5; } } + public override int TillerManDistance{ get{ return -5; } } + + public override Point2D StarboardOffset{ get{ return new Point2D( 2, -1 ); } } + public override Point2D PortOffset{ get{ return new Point2D( -2, -1 ); } } + + public override Point3D MarkOffset{ get{ return new Point3D( 0, 0, 3 ); } } + + public override BaseDockedBoat DockedBoat{ get{ return new LargeDockedDragonBoat( this ); } } + + [Constructable] + public LargeDragonBoat() + { + } + + public LargeDragonBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class LargeDragonBoatDeed : BaseBoatDeed + { + public override int LabelNumber{ get{ return 1041210; } }// large dragon ship deed + public override BaseBoat Boat{ get{ return new LargeDragonBoat(); } } + + [Constructable] + public LargeDragonBoatDeed() : base( 0x4014, new Point3D( 0, -1, 0 ) ) + { + } + + public LargeDragonBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class LargeDockedDragonBoat : BaseDockedBoat + { + public override BaseBoat Boat{ get{ return new LargeDragonBoat(); } } + + public LargeDockedDragonBoat( BaseBoat boat ) : base( 0x4014, new Point3D( 0, -1, 0 ), boat ) + { + } + + public LargeDockedDragonBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/MediumBoat.cs b/Scripts/Multis/Boats/MediumBoat.cs new file mode 100644 index 000000000..124cbd4f4 --- /dev/null +++ b/Scripts/Multis/Boats/MediumBoat.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class MediumBoat : BaseBoat + { + public override int NorthID{ get{ return 0x4008; } } + public override int EastID{ get{ return 0x4009; } } + public override int SouthID{ get{ return 0x400A; } } + public override int WestID{ get{ return 0x400B; } } + + public override int HoldDistance{ get{ return 4; } } + public override int TillerManDistance{ get{ return -5; } } + + public override Point2D StarboardOffset{ get{ return new Point2D( 2, 0 ); } } + public override Point2D PortOffset{ get{ return new Point2D( -2, 0 ); } } + + public override Point3D MarkOffset{ get{ return new Point3D( 0, 1, 3 ); } } + + public override BaseDockedBoat DockedBoat{ get{ return new MediumDockedBoat( this ); } } + + [Constructable] + public MediumBoat() + { + } + + public MediumBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class MediumBoatDeed : BaseBoatDeed + { + public override int LabelNumber{ get{ return 1041207; } } // medium ship deed + public override BaseBoat Boat{ get{ return new MediumBoat(); } } + + [Constructable] + public MediumBoatDeed() : base( 0x4008, Point3D.Zero ) + { + } + + public MediumBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class MediumDockedBoat : BaseDockedBoat + { + public override BaseBoat Boat{ get{ return new MediumBoat(); } } + + public MediumDockedBoat( BaseBoat boat ) : base( 0x4008, Point3D.Zero, boat ) + { + } + + public MediumDockedBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/MediumDragonBoat.cs b/Scripts/Multis/Boats/MediumDragonBoat.cs new file mode 100644 index 000000000..5b624e370 --- /dev/null +++ b/Scripts/Multis/Boats/MediumDragonBoat.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class MediumDragonBoat : BaseBoat + { + public override int NorthID{ get{ return 0x400C; } } + public override int EastID{ get{ return 0x400D; } } + public override int SouthID{ get{ return 0x400E; } } + public override int WestID{ get{ return 0x400F; } } + + public override int HoldDistance{ get{ return 4; } } + public override int TillerManDistance{ get{ return -5; } } + + public override Point2D StarboardOffset{ get{ return new Point2D( 2, 0 ); } } + public override Point2D PortOffset{ get{ return new Point2D( -2, 0 ); } } + + public override Point3D MarkOffset{ get{ return new Point3D( 0, 1, 3 ); } } + + public override BaseDockedBoat DockedBoat{ get{ return new MediumDockedDragonBoat( this ); } } + + [Constructable] + public MediumDragonBoat() + { + } + + public MediumDragonBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class MediumDragonBoatDeed : BaseBoatDeed + { + public override int LabelNumber{ get{ return 1041208; } } // medium dragon ship deed + public override BaseBoat Boat{ get{ return new MediumDragonBoat(); } } + + [Constructable] + public MediumDragonBoatDeed() : base( 0x400C, Point3D.Zero ) + { + } + + public MediumDragonBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class MediumDockedDragonBoat : BaseDockedBoat + { + public override BaseBoat Boat{ get{ return new MediumDragonBoat(); } } + + public MediumDockedDragonBoat( BaseBoat boat ) : base( 0x400C, Point3D.Zero, boat ) + { + } + + public MediumDockedDragonBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/Plank.cs b/Scripts/Multis/Boats/Plank.cs new file mode 100644 index 000000000..168413e6a --- /dev/null +++ b/Scripts/Multis/Boats/Plank.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections; +using Server; +using Server.Multis; + +namespace Server.Items +{ + public enum PlankSide{ Port, Starboard } + + public class Plank : Item, ILockable + { + private BaseBoat m_Boat; + private PlankSide m_Side; + private bool m_Locked; + private uint m_KeyValue; + + private Timer m_CloseTimer; + + public Plank( BaseBoat boat, PlankSide side, uint keyValue ) : base( 0x3EB1 + (int)side ) + { + m_Boat = boat; + m_Side = side; + m_KeyValue = keyValue; + m_Locked = true; + + Movable = false; + } + + public Plank( Serial serial ) : base(serial) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 );//version + + writer.Write( m_Boat ); + writer.Write( (int) m_Side ); + writer.Write( m_Locked ); + writer.Write( m_KeyValue ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Boat = reader.ReadItem() as BaseBoat; + m_Side = (PlankSide) reader.ReadInt(); + m_Locked = reader.ReadBool(); + m_KeyValue = reader.ReadUInt(); + + if ( m_Boat == null ) + Delete(); + + break; + } + } + + if ( IsOpen ) + { + m_CloseTimer = new CloseTimer( this ); + m_CloseTimer.Start(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public BaseBoat Boat{ get{ return m_Boat; } set{ m_Boat = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public PlankSide Side{ get{ return m_Side; } set{ m_Side = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Locked{ get{ return m_Locked; } set{ m_Locked = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public uint KeyValue{ get{ return m_KeyValue; } set{ m_KeyValue = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsOpen{ get{ return ( ItemID == 0x3ED5 || ItemID == 0x3ED4 || ItemID == 0x3E84 || ItemID == 0x3E89 ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Starboard{ get{ return ( m_Side == PlankSide.Starboard ); } } + + public void SetFacing( Direction dir ) + { + if ( IsOpen ) + { + switch ( dir ) + { + case Direction.North: ItemID = Starboard ? 0x3ED4 : 0x3ED5; break; + case Direction.East: ItemID = Starboard ? 0x3E84 : 0x3E89; break; + case Direction.South: ItemID = Starboard ? 0x3ED5 : 0x3ED4; break; + case Direction.West: ItemID = Starboard ? 0x3E89 : 0x3E84; break; + } + } + else + { + switch ( dir ) + { + case Direction.North: ItemID = Starboard ? 0x3EB2 : 0x3EB1; break; + case Direction.East: ItemID = Starboard ? 0x3E85 : 0x3E8A; break; + case Direction.South: ItemID = Starboard ? 0x3EB1 : 0x3EB2; break; + case Direction.West: ItemID = Starboard ? 0x3E8A : 0x3E85; break; + } + } + } + + public void Open() + { + if ( IsOpen || Deleted ) + return; + + if ( m_CloseTimer != null ) + m_CloseTimer.Stop(); + + m_CloseTimer = new CloseTimer( this ); + m_CloseTimer.Start(); + + switch ( ItemID ) + { + case 0x3EB1: ItemID = 0x3ED5; break; + case 0x3E8A: ItemID = 0x3E89; break; + case 0x3EB2: ItemID = 0x3ED4; break; + case 0x3E85: ItemID = 0x3E84; break; + } + + if ( m_Boat != null ) + m_Boat.Refresh(); + } + + public override bool OnMoveOver( Mobile from ) + { + if ( IsOpen ) + { + if ( (from.Direction & Direction.Running) != 0 || (m_Boat != null && !m_Boat.Contains( from )) ) + return true; + + Map map = Map; + + if ( map == null ) + return false; + + int rx = 0, ry = 0; + + if ( ItemID == 0x3ED4 ) + rx = 1; + else if ( ItemID == 0x3ED5 ) + rx = -1; + else if ( ItemID == 0x3E84 ) + ry = 1; + else if ( ItemID == 0x3E89 ) + ry = -1; + + for ( int i = 1; i <= 6; ++i ) + { + int x = X + (i*rx); + int y = Y + (i*ry); + int z; + + for ( int j = -8; j <= 8; ++j ) + { + z = from.Z + j; + + if ( map.CanFit( x, y, z, 16, false, false ) && !Server.Spells.SpellHelper.CheckMulti( new Point3D( x, y, z ), map ) && !Region.Find( new Point3D( x, y, z ), map ).IsPartOf( typeof( Factions.StrongholdRegion ) ) ) + { + if ( i == 1 && j >= -2 && j <= 2 ) + return true; + + from.Location = new Point3D( x, y, z ); + return false; + } + } + + z = map.GetAverageZ( x, y ); + + if ( map.CanFit( x, y, z, 16, false, false ) && !Server.Spells.SpellHelper.CheckMulti( new Point3D( x, y, z ), map ) && !Region.Find( new Point3D( x, y, z ), map ).IsPartOf( typeof( Factions.StrongholdRegion ) ) ) + { + if ( i == 1 ) + return true; + + from.Location = new Point3D( x, y, z ); + return false; + } + } + + return true; + } + else + { + return false; + } + } + + public bool CanClose() + { + Map map = Map; + + if ( map == null || Deleted ) + return false; + + foreach ( object o in this.GetObjectsInRange( 0 ) ) + { + if ( o != this ) + return false; + } + + return true; + } + + public void Close() + { + if ( !IsOpen || !CanClose() || Deleted ) + return; + + if ( m_CloseTimer != null ) + m_CloseTimer.Stop(); + + m_CloseTimer = null; + + switch ( ItemID ) + { + case 0x3ED5: ItemID = 0x3EB1; break; + case 0x3E89: ItemID = 0x3E8A; break; + case 0x3ED4: ItemID = 0x3EB2; break; + case 0x3E84: ItemID = 0x3E85; break; + } + + if ( m_Boat != null ) + m_Boat.Refresh(); + } + + public override void OnDoubleClickDead( Mobile from ) + { + OnDoubleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Boat == null ) + return; + + if ( from.InRange( GetWorldLocation(), 8 ) ) + { + if ( m_Boat.Contains( from ) ) + { + if ( IsOpen ) + Close(); + else + Open(); + } + else + { + if ( !IsOpen ) + { + if ( !Locked ) + { + Open(); + } + else if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x00, 502502 ); // That is locked but your godly powers allow access + Open(); + } + else + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x00, 502503 ); // That is locked. + } + } + else if ( !Locked ) + { + from.Location = new Point3D( this.X, this.Y, this.Z + 3 ); + } + else if ( from.AccessLevel >= AccessLevel.GameMaster ) + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x00, 502502 ); // That is locked but your godly powers allow access + from.Location = new Point3D( this.X, this.Y, this.Z + 3 ); + } + else + { + from.LocalOverheadMessage( Network.MessageType.Regular, 0x00, 502503 ); // That is locked. + } + } + } + } + + private class CloseTimer : Timer + { + private Plank m_Plank; + + public CloseTimer( Plank plank ) : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Plank = plank; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Plank.Close(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/RenameBoatPrompt.cs b/Scripts/Multis/Boats/RenameBoatPrompt.cs new file mode 100644 index 000000000..952639f92 --- /dev/null +++ b/Scripts/Multis/Boats/RenameBoatPrompt.cs @@ -0,0 +1,21 @@ +using System; +using Server; +using Server.Prompts; + +namespace Server.Multis +{ + public class RenameBoatPrompt : Prompt + { + private BaseBoat m_Boat; + + public RenameBoatPrompt( BaseBoat boat ) + { + m_Boat = boat; + } + + public override void OnResponse( Mobile from, string text ) + { + m_Boat.EndRename( from, text ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/SmallBoat.cs b/Scripts/Multis/Boats/SmallBoat.cs new file mode 100644 index 000000000..87463b765 --- /dev/null +++ b/Scripts/Multis/Boats/SmallBoat.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class SmallBoat : BaseBoat + { + public override int NorthID{ get{ return 0x4000; } } + public override int EastID{ get{ return 0x4001; } } + public override int SouthID{ get{ return 0x4002; } } + public override int WestID{ get{ return 0x4003; } } + + public override int HoldDistance{ get{ return 4; } } + public override int TillerManDistance{ get{ return -4; } } + + public override Point2D StarboardOffset{ get{ return new Point2D( 2, 0 ); } } + public override Point2D PortOffset{ get{ return new Point2D( -2, 0 ); } } + + public override Point3D MarkOffset{ get{ return new Point3D( 0, 1, 3 ); } } + + public override BaseDockedBoat DockedBoat{ get{ return new SmallDockedBoat( this ); } } + + [Constructable] + public SmallBoat() + { + } + + public SmallBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class SmallBoatDeed : BaseBoatDeed + { + public override int LabelNumber{ get{ return 1041205; } } // small ship deed + public override BaseBoat Boat{ get{ return new SmallBoat(); } } + + [Constructable] + public SmallBoatDeed() : base( 0x4000, Point3D.Zero ) + { + } + + public SmallBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class SmallDockedBoat : BaseDockedBoat + { + public override BaseBoat Boat{ get{ return new SmallBoat(); } } + + public SmallDockedBoat( BaseBoat boat ) : base( 0x4000, Point3D.Zero, boat ) + { + } + + public SmallDockedBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/SmallDragonBoat.cs b/Scripts/Multis/Boats/SmallDragonBoat.cs new file mode 100644 index 000000000..eb2d377ff --- /dev/null +++ b/Scripts/Multis/Boats/SmallDragonBoat.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class SmallDragonBoat : BaseBoat + { + public override int NorthID{ get{ return 0x4004; } } + public override int EastID{ get{ return 0x4005; } } + public override int SouthID{ get{ return 0x4006; } } + public override int WestID{ get{ return 0x4007; } } + + public override int HoldDistance{ get{ return 4; } } + public override int TillerManDistance{ get{ return -4; } } + + public override Point2D StarboardOffset{ get{ return new Point2D( 2, 0 ); } } + public override Point2D PortOffset{ get{ return new Point2D( -2, 0 ); } } + + public override Point3D MarkOffset{ get{ return new Point3D( 0, 1, 3 ); } } + + public override BaseDockedBoat DockedBoat{ get{ return new SmallDockedDragonBoat( this ); } } + + [Constructable] + public SmallDragonBoat() + { + } + + public SmallDragonBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class SmallDragonBoatDeed : BaseBoatDeed + { + public override int LabelNumber{ get{ return 1041206; } } // small dragon ship deed + public override BaseBoat Boat{ get{ return new SmallDragonBoat(); } } + + [Constructable] + public SmallDragonBoatDeed() : base( 0x4004, Point3D.Zero ) + { + } + + public SmallDragonBoatDeed( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } + + public class SmallDockedDragonBoat : BaseDockedBoat + { + public override BaseBoat Boat{ get{ return new SmallDragonBoat(); } } + + public SmallDockedDragonBoat( BaseBoat boat ) : base( 0x4004, Point3D.Zero, boat ) + { + } + + public SmallDockedDragonBoat( Serial serial ) : base( serial ) + { + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int)0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/Strandedness.cs b/Scripts/Multis/Boats/Strandedness.cs new file mode 100644 index 000000000..2efc1e21a --- /dev/null +++ b/Scripts/Multis/Boats/Strandedness.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using Server; + +namespace Server.Misc +{ + public class Strandedness + { + private static Point2D[] m_Felucca = new Point2D[] + { + new Point2D( 2528, 3568 ), new Point2D( 2376, 3400 ), new Point2D( 2528, 3896 ), + new Point2D( 2168, 3904 ), new Point2D( 1136, 3416 ), new Point2D( 1432, 3648 ), + new Point2D( 1416, 4000 ), new Point2D( 4512, 3936 ), new Point2D( 4440, 3120 ), + new Point2D( 4192, 3672 ), new Point2D( 4720, 3472 ), new Point2D( 3744, 2768 ), + new Point2D( 3480, 2432 ), new Point2D( 3560, 2136 ), new Point2D( 3792, 2112 ), + new Point2D( 2800, 2296 ), new Point2D( 2736, 2016 ), new Point2D( 4576, 1456 ), + new Point2D( 4680, 1152 ), new Point2D( 4304, 1104 ), new Point2D( 4496, 984 ), + new Point2D( 4248, 696 ), new Point2D( 4040, 616 ), new Point2D( 3896, 248 ), + new Point2D( 4176, 384 ), new Point2D( 3672, 1104 ), new Point2D( 3520, 1152 ), + new Point2D( 3720, 1360 ), new Point2D( 2184, 2152 ), new Point2D( 1952, 2088 ), + new Point2D( 2056, 1936 ), new Point2D( 1720, 1992 ), new Point2D( 472, 2064 ), + new Point2D( 656, 2096 ), new Point2D( 3008, 3592 ), new Point2D( 2784, 3472 ), + new Point2D( 5456, 2400 ), new Point2D( 5976, 2424 ), new Point2D( 5328, 3112 ), + new Point2D( 5792, 3152 ), new Point2D( 2120, 3616 ), new Point2D( 2136, 3128 ), + new Point2D( 1632, 3528 ), new Point2D( 1328, 3160 ), new Point2D( 1072, 3136 ), + new Point2D( 1128, 2976 ), new Point2D( 960, 2576 ), new Point2D( 752, 1832 ), + new Point2D( 184, 1488 ), new Point2D( 592, 1440 ), new Point2D( 368, 1216 ), + new Point2D( 232, 752 ), new Point2D( 696, 744 ), new Point2D( 304, 1000 ), + new Point2D( 840, 376 ), new Point2D( 1192, 624 ), new Point2D( 1200, 192 ), + new Point2D( 1512, 240 ), new Point2D( 1336, 456 ), new Point2D( 1536, 648 ), + new Point2D( 1104, 952 ), new Point2D( 1864, 264 ), new Point2D( 2136, 200 ), + new Point2D( 2160, 528 ), new Point2D( 1904, 512 ), new Point2D( 2240, 784 ), + new Point2D( 2536, 776 ), new Point2D( 2488, 216 ), new Point2D( 2336, 72 ), + new Point2D( 2648, 288 ), new Point2D( 2680, 576 ), new Point2D( 2896, 88 ), + new Point2D( 2840, 344 ), new Point2D( 3136, 72 ), new Point2D( 2968, 520 ), + new Point2D( 3192, 328 ), new Point2D( 3448, 208 ), new Point2D( 3432, 608 ), + new Point2D( 3184, 752 ), new Point2D( 2800, 704 ), new Point2D( 2768, 1016 ), + new Point2D( 2448, 1232 ), new Point2D( 2272, 920 ), new Point2D( 2072, 1080 ), + new Point2D( 2048, 1264 ), new Point2D( 1808, 1528 ), new Point2D( 1496, 1880 ), + new Point2D( 1656, 2168 ), new Point2D( 2096, 2320 ), new Point2D( 1816, 2528 ), + new Point2D( 1840, 2640 ), new Point2D( 1928, 2952 ), new Point2D( 2120, 2712 ) + }; + + private static Point2D[] m_Trammel = m_Felucca; + + private static Point2D[] m_Ilshenar = new Point2D[] + { + new Point2D( 1252, 1180 ), new Point2D( 1562, 1090 ), new Point2D( 1444, 1016 ), + new Point2D( 1324, 968 ), new Point2D( 1418, 806 ), new Point2D( 1722, 874 ), + new Point2D( 1456, 684 ), new Point2D( 1036, 866 ), new Point2D( 612, 476 ), + new Point2D( 1476, 372 ), new Point2D( 762, 472 ), new Point2D( 812, 1162 ), + new Point2D( 1422, 1144 ), new Point2D( 1254, 1066 ), new Point2D( 1598, 870 ), + new Point2D( 1358, 866 ), new Point2D( 510, 302 ), new Point2D( 510, 392 ) + }; + + private static Point2D[] m_Tokuno = new Point2D[] + { + //Makoto-Jima + new Point2D( 837, 1351 ), new Point2D( 941, 1241 ), new Point2D( 959, 1185 ), + new Point2D( 923, 1091 ), new Point2D( 904, 983 ), new Point2D( 845, 944 ), + new Point2D( 829, 896 ), new Point2D( 794, 852 ), new Point2D( 766, 821 ), + new Point2D( 695, 814 ), new Point2D( 576, 835 ), new Point2D( 518, 840 ), + new Point2D( 519, 902 ), new Point2D( 502, 950 ), new Point2D( 503, 1045 ), + new Point2D( 547, 1131 ), new Point2D( 518, 1204 ), new Point2D( 506, 1243 ), + new Point2D( 526, 1271 ), new Point2D( 562, 1295 ), new Point2D( 616, 1335 ), + new Point2D( 789, 1347 ), new Point2D( 712, 1359 ), + + //Homare-Jima + new Point2D( 202, 498 ), new Point2D( 116, 600 ), new Point2D( 107, 699 ), + new Point2D( 162, 799 ), new Point2D( 158, 889 ), new Point2D( 169, 989 ), + new Point2D( 194, 1101 ), new Point2D( 250, 1163 ), new Point2D( 295, 1176 ), + new Point2D( 280, 1194 ), new Point2D( 286, 1102 ), new Point2D( 250, 1000 ), + new Point2D( 260, 906 ), new Point2D( 360, 838 ), new Point2D( 389, 763 ), + new Point2D( 415, 662 ), new Point2D( 500, 597 ), new Point2D( 570, 572 ), + new Point2D( 631, 577 ), new Point2D( 692, 500 ), new Point2D( 723, 445 ), + new Point2D( 672, 379 ), new Point2D( 626, 332 ), new Point2D( 494, 291 ), + new Point2D( 371, 336 ), new Point2D( 324, 334 ), new Point2D( 270, 362 ), + + //Isamu-Jima + new Point2D( 1240, 1076 ), new Point2D( 1189, 1115 ), new Point2D( 1046, 1039 ), + new Point2D( 1025, 885 ), new Point2D( 907, 809 ), new Point2D( 840, 506 ), + new Point2D( 799, 396 ), new Point2D( 720, 258 ), new Point2D( 744, 158 ), + new Point2D( 904, 37 ), new Point2D( 974, 91 ), new Point2D( 1020, 187 ), + new Point2D( 1035, 288 ), new Point2D( 1104, 395 ), new Point2D( 1215, 462 ), + new Point2D( 1275, 488 ), new Point2D( 1348, 611 ), new Point2D( 1363, 739 ), + new Point2D( 1364, 765 ), new Point2D( 1364, 876 ), new Point2D( 1300, 936 ), + new Point2D( 1240, 1003 ) + + + }; + + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( EventSink_Login ); + } + + private static bool IsStranded( Mobile from ) + { + Map map = from.Map; + + if ( map == null ) + return false; + + object surface = map.GetTopSurface( from.Location ); + + if ( surface is Tile ) + { + int id = ((Tile)surface).ID; + + return (id >= 168 && id <= 171) + || (id >= 310 && id <= 311) + || (id >= 0x5796 && id <= 0x57B2); + } + + return false; + } + + public static void EventSink_Login( LoginEventArgs e ) + { + Mobile from = e.Mobile; + + if ( !IsStranded( from ) ) + return; + + Map map = from.Map; + + Point2D[] list; + + if( map == Map.Felucca ) + list = m_Felucca; + else if( map == Map.Trammel ) + list = m_Trammel; + else if( map == Map.Ilshenar ) + list = m_Ilshenar; + else if( map == Map.Tokuno ) + list = m_Tokuno; + else + return; + + Point2D p = Point2D.Zero; + double pdist = double.MaxValue; + + for ( int i = 0; i < list.Length; ++i ) + { + double dist = from.GetDistanceToSqrt( list[i] ); + + if ( dist < pdist ) + { + p = list[i]; + pdist = dist; + } + } + + int x = p.X, y = p.Y; + int z; + bool canFit = false; + + z = map.GetAverageZ( x, y ); + canFit = map.CanSpawnMobile( x, y, z ); + + for ( int i = 1; !canFit && i <= 40; i += 2 ) + { + for ( int xo = -1; !canFit && xo <= 1; ++xo ) + { + for ( int yo = -1; !canFit && yo <= 1; ++yo ) + { + if ( xo == 0 && yo == 0 ) + continue; + + x = p.X + (xo * i); + y = p.Y + (yo * i); + z = map.GetAverageZ( x, y ); + canFit = map.CanSpawnMobile( x, y, z ); + } + } + } + + if ( canFit ) + from.Location = new Point3D( x, y, z ); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Boats/TillerMan.cs b/Scripts/Multis/Boats/TillerMan.cs new file mode 100644 index 000000000..d45f9f75d --- /dev/null +++ b/Scripts/Multis/Boats/TillerMan.cs @@ -0,0 +1,119 @@ +using System; +using Server; +using Server.Multis; +using Server.Network; + +namespace Server.Items +{ + public class TillerMan : Item + { + private BaseBoat m_Boat; + + public TillerMan( BaseBoat boat ) : base( 0x3E4E ) + { + m_Boat = boat; + Movable = false; + } + + public TillerMan( Serial serial ) : base(serial) + { + } + + public void SetFacing( Direction dir ) + { + switch ( dir ) + { + case Direction.South: ItemID = 0x3E4B; break; + case Direction.North: ItemID = 0x3E4E; break; + case Direction.West: ItemID = 0x3E50; break; + case Direction.East: ItemID = 0x3E53; break; + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( m_Boat.Status ); + } + + public void Say( int number ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, number ); + } + + public void Say( int number, string args ) + { + PublicOverheadMessage( MessageType.Regular, 0x3B2, number, args ); + } + + public override void AddNameProperty( ObjectPropertyList list ) + { + if ( m_Boat != null && m_Boat.ShipName != null ) + list.Add( 1042884, m_Boat.ShipName ); // the tiller man of the ~1_SHIP_NAME~ + else + base.AddNameProperty( list ); + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Boat != null && m_Boat.ShipName != null ) + LabelTo( from, 1042884, m_Boat.ShipName ); // the tiller man of the ~1_SHIP_NAME~ + else + base.OnSingleClick( from ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( m_Boat != null && m_Boat.Contains( from ) ) + m_Boat.BeginRename( from ); + else if ( m_Boat != null ) + m_Boat.BeginDryDock( from ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( dropped is MapItem && m_Boat != null && m_Boat.CanCommand( from ) && m_Boat.Contains( from ) ) + { + m_Boat.AssociateMap( (MapItem) dropped ); + } + + return false; + } + + public override void OnAfterDelete() + { + if ( m_Boat != null ) + m_Boat.Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 );//version + + writer.Write( m_Boat ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Boat = reader.ReadItem() as BaseBoat; + + if ( m_Boat == null ) + Delete(); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Camps/BankerCamp.cs b/Scripts/Multis/Camps/BankerCamp.cs new file mode 100644 index 000000000..44e59861d --- /dev/null +++ b/Scripts/Multis/Camps/BankerCamp.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Multis +{ + public class BankerCamp : BaseCamp + { + [Constructable] + public BankerCamp() : base( 0x1F6 ) + { + } + + public override void AddComponents() + { + BaseDoor west, east; + + AddItem( west = new LightWoodGate( DoorFacing.WestCW ), -4, 4, 7 ); + AddItem( east = new LightWoodGate( DoorFacing.EastCCW ), -3, 4, 7 ); + + west.Link = east; + east.Link = west; + + AddItem( new Sign( SignType.Bank, SignFacing.West ), -5, 5, -4 ); + + AddMobile( new Banker(), 4, -4, 3, 7 ); + AddMobile( new Banker(), 5, 4, -2, 0 ); + } + + public BankerCamp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Camps/BaseCamp.cs b/Scripts/Multis/Camps/BaseCamp.cs new file mode 100644 index 000000000..f938927f8 --- /dev/null +++ b/Scripts/Multis/Camps/BaseCamp.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Multis +{ + public abstract class BaseCamp : BaseMulti + { + private ArrayList m_Items, m_Mobiles; + private DateTime m_DecayTime; + private Timer m_DecayTimer; + + public virtual int EventRange{ get{ return 10; } } + public virtual TimeSpan DecayDelay{ get{ return TimeSpan.FromMinutes( 30.0 ); } } + + public BaseCamp( int multiID ) : base( multiID | 0x4000 ) + { + m_Items = new ArrayList(); + m_Mobiles = new ArrayList(); + RefreshDecay( true ); + + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( CheckAddComponents ) ); + } + + public void CheckAddComponents() + { + if ( Deleted ) + return; + + AddComponents(); + } + + public virtual void AddComponents() + { + } + + public virtual void RefreshDecay( bool setDecayTime ) + { + if ( Deleted ) + return; + + if ( m_DecayTimer != null ) + m_DecayTimer.Stop(); + + if ( setDecayTime ) + m_DecayTime = DateTime.Now + DecayDelay; + + m_DecayTimer = Timer.DelayCall( DecayDelay, new TimerCallback( Delete ) ); + } + + public virtual void AddItem( Item item, int xOffset, int yOffset, int zOffset ) + { + m_Items.Add( item ); + + item.MoveToWorld( new Point3D( X + xOffset, Y + yOffset, Z + zOffset ), Map ); + } + + public virtual void AddMobile( Mobile m, int wanderRange, int xOffset, int yOffset, int zOffset ) + { + m_Mobiles.Add( m ); + + Point3D loc = new Point3D( X + xOffset, Y + yOffset, Z + zOffset ); + BaseCreature bc = m as BaseCreature; + + if ( bc != null ) + { + bc.RangeHome = wanderRange; + bc.Home = loc; + } + + if ( m is BaseVendor || m is Banker ) + m.Direction = Direction.South; + + m.MoveToWorld( loc, this.Map ); + } + + public virtual void OnEnter( Mobile m ) + { + RefreshDecay( true ); + } + + public virtual void OnExit( Mobile m ) + { + RefreshDecay( true ); + } + + public override bool HandlesOnMovement{ get{ return true; } } + + public override void OnMovement( Mobile m, Point3D oldLocation ) + { + bool inOldRange = Utility.InRange( oldLocation, Location, EventRange ); + bool inNewRange = Utility.InRange( m.Location, Location, EventRange ); + + if ( inNewRange && !inOldRange ) + OnEnter( m ); + else if ( inOldRange && !inNewRange ) + OnExit( m ); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + for ( int i = 0; i < m_Items.Count; ++i ) + ((Item)m_Items[i]).Delete(); + + for ( int i = 0; i < m_Mobiles.Count; ++i ) + ((Mobile)m_Mobiles[i]).Delete(); + + m_Items.Clear(); + m_Mobiles.Clear(); + } + + public BaseCamp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteItemList( m_Items, true ); + writer.WriteMobileList( m_Mobiles, true ); + writer.WriteDeltaTime( m_DecayTime ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Items = reader.ReadItemList(); + m_Mobiles = reader.ReadMobileList(); + m_DecayTime = reader.ReadDeltaTime(); + + RefreshDecay( false ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Camps/HealerCamp.cs b/Scripts/Multis/Camps/HealerCamp.cs new file mode 100644 index 000000000..6fbce8db2 --- /dev/null +++ b/Scripts/Multis/Camps/HealerCamp.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Multis +{ + public class HealerCamp : BaseCamp + { + [Constructable] + public HealerCamp() : base( 0x1F4 ) + { + } + + public override void AddComponents() + { + BaseDoor west, east; + + AddItem( west = new LightWoodGate( DoorFacing.WestCW ), -4, 4, 7 ); + AddItem( east = new LightWoodGate( DoorFacing.EastCCW ), -3, 4, 7 ); + + west.Link = east; + east.Link = west; + + AddItem( new Sign( SignType.Healer, SignFacing.West ), -5, 5, -4 ); + + AddMobile( new Healer(), 4, -4, 3, 7 ); + AddMobile( new Healer(), 5, 4, -2, 0 ); + } + + public HealerCamp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Camps/MageCamp.cs b/Scripts/Multis/Camps/MageCamp.cs new file mode 100644 index 000000000..4f30e3dcf --- /dev/null +++ b/Scripts/Multis/Camps/MageCamp.cs @@ -0,0 +1,49 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Multis +{ + public class MageCamp : BaseCamp + { + [Constructable] + public MageCamp() : base( 0x1F5 ) + { + } + + public override void AddComponents() + { + BaseDoor west, east; + + AddItem( west = new LightWoodGate( DoorFacing.WestCW ), -4, 4, 7 ); + AddItem( east = new LightWoodGate( DoorFacing.EastCCW ), -3, 4, 7 ); + + west.Link = east; + east.Link = west; + + AddItem( new Sign( SignType.Mage, SignFacing.West ), -5, 5, -4 ); + + AddMobile( new Mage(), 4, -4, 3, 7 ); + AddMobile( new Mage(), 5, 4, -2, 0 ); + } + + public MageCamp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Camps/OrcCamp.cs b/Scripts/Multis/Camps/OrcCamp.cs new file mode 100644 index 000000000..c872eb87e --- /dev/null +++ b/Scripts/Multis/Camps/OrcCamp.cs @@ -0,0 +1,105 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Multis +{ + public class OrcCamp : BaseCamp + { + private Mobile m_Prisoner; + private BaseDoor m_Gate; + + [Constructable] + public OrcCamp() : base( 0x1D4C ) + { + } + + public override void AddComponents() + { + IronGate gate = new IronGate( DoorFacing.EastCCW ); + m_Gate = gate; + + gate.KeyValue = Key.RandomValue(); + gate.Locked = true; + + AddItem( gate, -2, 1, 0 ); + + MetalChest chest = new MetalChest(); + + chest.ItemID = 0xE7C; + chest.DropItem( new Key( KeyType.Iron, gate.KeyValue ) ); + + TreasureMapChest.Fill( chest, 2 ); + + AddItem( chest, 4, 4, 0 ); + + AddMobile( new Orc(), 15, 0, -2, 0 ); + AddMobile( new Orc(), 15, 0, 1, 0 ); + AddMobile( new OrcishMage(), 15, 0, -1, 0 ); + AddMobile( new OrcCaptain(), 15, 0, 0, 0 ); + + switch ( Utility.Random( 2 ) ) + { + case 0: m_Prisoner = new Noble(); break; + case 1: m_Prisoner = new SeekerOfAdventure(); break; + } + + m_Prisoner.YellHue = Utility.RandomList( 0x57, 0x67, 0x77, 0x87, 0x117 ); + + AddMobile( m_Prisoner, 2, -2, 0, 0 ); + } + + public override void OnEnter( Mobile m ) + { + base.OnEnter( m ); + + if ( m.Player && m_Prisoner != null && m_Gate != null && m_Gate.Locked ) + { + int number; + + switch ( Utility.Random( 4 ) ) + { + default: + case 0: number = 502264; break; // Help a poor prisoner! + case 1: number = 502266; break; // Aaah! Help me! + case 2: number = 1046000; break; // Help! These savages wish to end my life! + case 3: number = 1046003; break; // Quickly! Kill them for me! HELP!! + } + + m_Prisoner.Yell( number ); + } + } + + public OrcCamp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Prisoner ); + writer.Write( m_Gate ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Prisoner = reader.ReadMobile(); + m_Gate = reader.ReadItem() as BaseDoor; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Camps/RatCamp.cs b/Scripts/Multis/Camps/RatCamp.cs new file mode 100644 index 000000000..8dbf1da76 --- /dev/null +++ b/Scripts/Multis/Camps/RatCamp.cs @@ -0,0 +1,105 @@ +using System; +using Server; +using Server.Items; +using Server.Mobiles; + +namespace Server.Multis +{ + public class RatCamp : BaseCamp + { + private Mobile m_Prisoner; + private BaseDoor m_Gate; + + [Constructable] + public RatCamp() : base( 0x1D4C ) + { + } + + public override void AddComponents() + { + IronGate gate = new IronGate( DoorFacing.EastCCW ); + m_Gate = gate; + + gate.KeyValue = Key.RandomValue(); + gate.Locked = true; + + AddItem( gate, -2, 1, 0 ); + + MetalChest chest = new MetalChest(); + + chest.ItemID = 0xE7C; + chest.DropItem( new Key( KeyType.Iron, gate.KeyValue ) ); + + TreasureMapChest.Fill( chest, 2 ); + + AddItem( chest, 4, 4, 0 ); + + AddMobile( new Ratman(), 15, 0, -2, 0 ); + AddMobile( new Ratman(), 15, 0, 1, 0 ); + AddMobile( new RatmanMage(), 15, 0, -1, 0 ); + AddMobile( new RatmanArcher(), 15, 0, 0, 0 ); + + switch ( Utility.Random( 2 ) ) + { + case 0: m_Prisoner = new Noble(); break; + case 1: m_Prisoner = new SeekerOfAdventure(); break; + } + + m_Prisoner.YellHue = Utility.RandomList( 0x57, 0x67, 0x77, 0x87, 0x117 ); + + AddMobile( m_Prisoner, 2, -2, 0, 0 ); + } + + public override void OnEnter( Mobile m ) + { + base.OnEnter( m ); + + if ( m.Player && m_Prisoner != null && m_Gate != null && m_Gate.Locked ) + { + int number; + + switch ( Utility.Random( 4 ) ) + { + default: + case 0: number = 502264; break; // Help a poor prisoner! + case 1: number = 502266; break; // Aaah! Help me! + case 2: number = 1046000; break; // Help! These savages wish to end my life! + case 3: number = 1046003; break; // Quickly! Kill them for me! HELP!! + } + + m_Prisoner.Yell( number ); + } + } + + public RatCamp( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Prisoner ); + writer.Write( m_Gate ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Prisoner = reader.ReadMobile(); + m_Gate = reader.ReadItem() as BaseDoor; + break; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/ComponentVerification.cs b/Scripts/Multis/ComponentVerification.cs new file mode 100644 index 000000000..58fd6e987 --- /dev/null +++ b/Scripts/Multis/ComponentVerification.cs @@ -0,0 +1,251 @@ +using System; +using System.IO; +using System.Collections; + +namespace Server.Multis +{ + public class ComponentVerification + { + private byte[] m_ItemTable; + private byte[] m_MultiTable; + + public bool IsItemValid( int itemID ) + { + if ( itemID <= 0 || itemID >= m_ItemTable.Length ) + return false; + + return CheckValidity( m_ItemTable[itemID] ); + } + + public bool IsMultiValid( int multiID ) + { + if ( multiID <= 0 || multiID >= m_MultiTable.Length ) + return false; + + return CheckValidity( m_MultiTable[multiID] ); + } + + public bool CheckValidity( byte val ) + { + return ( val == 0 || (ExpansionInfo.CurrentExpansion.CustomHousingFlag & val) != 0 ); + } + + public ComponentVerification() + { + m_ItemTable = CreateTable( 0x4000 ); + m_MultiTable = CreateTable( 0x4000 ); + + LoadItems( "Data/Components/walls.txt", "South1", "South2", "South3", "Corner", "East1", "East2", "East3", "Post", "WindowS", "AltWindowS", "WindowE", "AltWindowE", "SecondAltWindowS", "SecondAltWindowE" ); + LoadItems( "Data/Components/teleprts.txt", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16" ); + LoadItems( "Data/Components/stairs.txt", "Block", "North", "East", "South", "West", "Squared1", "Squared2", "Rounded1", "Rounded2" ); + LoadItems( "Data/Components/roof.txt", "North", "East", "South", "West", "NSCrosspiece", "EWCrosspiece", "NDent", "EDent", "SDent", "WDent", "NTPiece", "ETPiece", "STPiece", "WTPiece", "XPiece", "Extra Piece" ); + LoadItems( "Data/Components/floors.txt", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16" ); + LoadItems( "Data/Components/misc.txt", "Piece1", "Piece2", "Piece3", "Piece4", "Piece5", "Piece6", "Piece7", "Piece8" ); + LoadItems( "Data/Components/doors.txt", "Piece1", "Piece2", "Piece3", "Piece4", "Piece5", "Piece6", "Piece7", "Piece8" ); + + LoadMultis( "Data/Components/stairs.txt", "MultiNorth", "MultiEast", "MultiSouth", "MultiWest" ); + } + + private byte[] CreateTable( int length ) + { + byte[] table = new byte[length]; + + for ( int i = 0; i < table.Length; ++i ) + table[i] = 0xFF; + + return table; + } + + private void LoadItems( string path, params string[] itemColumns ) + { + LoadSpreadsheet( m_ItemTable, path, itemColumns ); + } + + private void LoadMultis( string path, params string[] multiColumns ) + { + LoadSpreadsheet( m_MultiTable, path, multiColumns ); + } + + private void LoadSpreadsheet( byte[] table, string path, params string[] tileColumns ) + { + Spreadsheet ss = new Spreadsheet( path ); + + int[] tileCIDs = new int[tileColumns.Length]; + + for ( int i = 0; i < tileColumns.Length; ++i ) + tileCIDs[i] = ss.GetColumnID( tileColumns[i] ); + + int featureCID = ss.GetColumnID( "FeatureMask" ); + + for ( int i = 0; i < ss.Records.Length; ++i ) + { + DataRecord record = ss.Records[i]; + + byte fid = (byte) record.GetInt32( featureCID ); + + for ( int j = 0; j < tileCIDs.Length; ++j ) + { + int itemID = record.GetInt32( tileCIDs[j] ); + + if ( itemID <= 0 || itemID >= table.Length ) + continue; + + table[itemID] = fid; + } + } + } + } + + public class Spreadsheet + { + private class ColumnInfo + { + public int m_DataIndex; + + public string m_Type; + public string m_Name; + + public ColumnInfo( int dataIndex, string type, string name ) + { + m_DataIndex = dataIndex; + + m_Type = type; + m_Name = name; + } + } + + private ColumnInfo[] m_Columns; + private DataRecord[] m_Records; + + public DataRecord[] Records { get { return m_Records; } } + + public int GetColumnID( string name ) + { + for ( int i = 0; i < m_Columns.Length; ++i ) + { + if ( m_Columns[i].m_Name == name ) + return i; + } + + return -1; + } + + public Spreadsheet( string path ) + { + using ( StreamReader ip = new StreamReader( path ) ) + { + string[] types = ReadLine( ip ); + string[] names = ReadLine( ip ); + + m_Columns = new ColumnInfo[types.Length]; + + for ( int i = 0; i < m_Columns.Length; ++i ) + m_Columns[i] = new ColumnInfo( i, types[i], names[i] ); + + ArrayList records = new ArrayList(); + + string[] values; + + while ( ( values = ReadLine( ip ) ) != null ) + { + object[] data = new object[m_Columns.Length]; + + for ( int i = 0; i < m_Columns.Length; ++i ) + { + ColumnInfo ci = m_Columns[i]; + + switch ( ci.m_Type ) + { + case "int": + { + data[i] = Utility.ToInt32( values[ci.m_DataIndex] ); + break; + } + case "string": + { + data[i] = values[ci.m_DataIndex]; + break; + } + } + } + + records.Add( new DataRecord( this, data ) ); + } + + m_Records = (DataRecord[]) records.ToArray( typeof( DataRecord ) ); + } + } + + private string[] ReadLine( StreamReader ip ) + { + string line; + + while ( ( line = ip.ReadLine() ) != null ) + { + if ( line.Length == 0 ) + continue; + + return line.Split( '\t' ); + } + + return null; + } + } + + public class DataRecord + { + private Spreadsheet m_Spreadsheet; + private object[] m_Data; + + public Spreadsheet Spreadsheet { get { return m_Spreadsheet; } } + public object[] Data { get { return m_Data; } } + + public DataRecord( Spreadsheet ss, object[] data ) + { + m_Spreadsheet = ss; + m_Data = data; + } + + public int GetInt32( string name ) + { + return GetInt32( this[name] ); + } + + public int GetInt32( int id ) + { + return GetInt32( this[id] ); + } + + public int GetInt32( object obj ) + { + if ( obj is int ) + return (int) obj; + + return 0; + } + + public string GetString( string name ) + { + return this[name] as string; + } + + public object this[string name] + { + get + { + return this[m_Spreadsheet.GetColumnID( name )]; + } + } + + public object this[int id] + { + get + { + if ( id < 0 ) + return null; + + return m_Data[id]; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Deeds.cs b/Scripts/Multis/Deeds.cs new file mode 100644 index 000000000..451ed104f --- /dev/null +++ b/Scripts/Multis/Deeds.cs @@ -0,0 +1,887 @@ +using Server; +using System; +using System.Collections; +using Server.Multis; +using Server.Targeting; +using Server.Items; + +namespace Server.Multis.Deeds +{ + public class HousePlacementTarget : MultiTarget + { + private HouseDeed m_Deed; + + public HousePlacementTarget( HouseDeed deed ) : base( deed.MultiID, deed.Offset ) + { + m_Deed = deed; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D ip = o as IPoint3D; + + if ( ip != null ) + { + if ( ip is Item ) + ip = ((Item)ip).GetWorldTop(); + + Point3D p = new Point3D( ip ); + + Region reg = Region.Find( new Point3D( p ), from.Map ); + + if ( from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing( from, p ) ) + m_Deed.OnPlacement( from, p ); + else if ( reg.IsPartOf( typeof( TreasureRegion ) ) ) + from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + else + from.SendLocalizedMessage( 501265 ); // Housing can not be created in this area. + } + } + } + + public abstract class HouseDeed : Item + { + private int m_MultiID; + private Point3D m_Offset; + + [CommandProperty( AccessLevel.GameMaster )] + public int MultiID + { + get + { + return m_MultiID; + } + set + { + m_MultiID = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Point3D Offset + { + get + { + return m_Offset; + } + set + { + m_Offset = value; + } + } + + public HouseDeed( int id, Point3D offset ) : base( 0x14F0 ) + { + Weight = 1.0; + LootType = LootType.Newbied; + + m_MultiID = id & 0x3FFF; + m_Offset = offset; + } + + public HouseDeed( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_Offset ); + + writer.Write( m_MultiID ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Offset = reader.ReadPoint3D(); + + goto case 0; + } + case 0: + { + m_MultiID = reader.ReadInt(); + + break; + } + } + + if ( Weight == 0.0 ) + Weight = 1.0; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse( from ) ) + { + from.SendLocalizedMessage( 501271 ); // You already own a house, you may not place another! + } + else + { + from.SendLocalizedMessage( 1010433 ); /* House placement cancellation could result in a + * 60 second delay in the return of your deed. + */ + + from.Target = new HousePlacementTarget( this ); + } + } + + public abstract BaseHouse GetHouse( Mobile owner ); + public abstract Rectangle2D[] Area{ get; } + + public void OnPlacement( Mobile from, Point3D p ) + { + if ( Deleted ) + return; + + if ( !IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + else if ( from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse( from ) ) + { + from.SendLocalizedMessage( 501271 ); // You already own a house, you may not place another! + } + else + { + ArrayList toMove; + Point3D center = new Point3D( p.X - m_Offset.X, p.Y - m_Offset.Y, p.Z - m_Offset.Z ); + HousePlacementResult res = HousePlacement.Check( from, m_MultiID, center, out toMove ); + + switch ( res ) + { + case HousePlacementResult.Valid: + { + BaseHouse house = GetHouse( from ); + house.MoveToWorld( center, from.Map ); + Delete(); + + for ( int i = 0; i < toMove.Count; ++i ) + { + object o = toMove[i]; + + if ( o is Mobile ) + ((Mobile)o).Location = house.BanLocation; + else if ( o is Item ) + ((Item)o).Location = house.BanLocation; + } + + break; + } + case HousePlacementResult.BadItem: + case HousePlacementResult.BadLand: + case HousePlacementResult.BadStatic: + case HousePlacementResult.BadRegionHidden: + { + from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + break; + } + case HousePlacementResult.NoSurface: + { + from.SendMessage( "The house could not be created here. Part of the foundation would not be on any surface." ); + break; + } + case HousePlacementResult.BadRegion: + { + from.SendLocalizedMessage( 501265 ); // Housing cannot be created in this area. + break; + } + } + } + } + } + + public class StonePlasterHouseDeed : HouseDeed + { + [Constructable] + public StonePlasterHouseDeed() : base( 0x64, new Point3D( 0, 4, 0 ) ) + { + } + + public StonePlasterHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallOldHouse( owner, 0x64 ); + } + + public override int LabelNumber{ get{ return 1041211; } } + public override Rectangle2D[] Area{ get{ return SmallOldHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class FieldStoneHouseDeed : HouseDeed + { + [Constructable] + public FieldStoneHouseDeed() : base( 0x66, new Point3D( 0, 4, 0 ) ) + { + } + + public FieldStoneHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallOldHouse( owner, 0x66 ); + } + + public override int LabelNumber{ get{ return 1041212; } } + public override Rectangle2D[] Area{ get{ return SmallOldHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallBrickHouseDeed : HouseDeed + { + [Constructable] + public SmallBrickHouseDeed() : base( 0x68, new Point3D( 0, 4, 0 ) ) + { + } + + public SmallBrickHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallOldHouse( owner, 0x68 ); + } + + public override int LabelNumber{ get{ return 1041213; } } + public override Rectangle2D[] Area{ get{ return SmallOldHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodHouseDeed : HouseDeed + { + [Constructable] + public WoodHouseDeed() : base( 0x6A, new Point3D( 0, 4, 0 ) ) + { + } + + public WoodHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallOldHouse( owner, 0x6A ); + } + + public override int LabelNumber{ get{ return 1041214; } } + public override Rectangle2D[] Area{ get{ return SmallOldHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class WoodPlasterHouseDeed : HouseDeed + { + [Constructable] + public WoodPlasterHouseDeed() : base( 0x6C, new Point3D( 0, 4, 0 ) ) + { + } + + public WoodPlasterHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallOldHouse( owner, 0x6C ); + } + + public override int LabelNumber{ get{ return 1041215; } } + public override Rectangle2D[] Area{ get{ return SmallOldHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class ThatchedRoofCottageDeed : HouseDeed + { + [Constructable] + public ThatchedRoofCottageDeed() : base( 0x6E, new Point3D( 0, 4, 0 ) ) + { + } + + public ThatchedRoofCottageDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallOldHouse( owner, 0x6E ); + } + + public override int LabelNumber{ get{ return 1041216; } } + public override Rectangle2D[] Area{ get{ return SmallOldHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class BrickHouseDeed : HouseDeed + { + [Constructable] + public BrickHouseDeed() : base( 0x74, new Point3D( -1, 7, 0 ) ) + { + } + + public BrickHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new GuildHouse( owner ); + } + + public override int LabelNumber{ get{ return 1041219; } } + public override Rectangle2D[] Area{ get{ return GuildHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TwoStoryWoodPlasterHouseDeed : HouseDeed + { + [Constructable] + public TwoStoryWoodPlasterHouseDeed() : base( 0x76, new Point3D( -3, 7, 0 ) ) + { + } + + public TwoStoryWoodPlasterHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new TwoStoryHouse( owner, 0x76 ); + } + + public override int LabelNumber{ get{ return 1041220; } } + public override Rectangle2D[] Area{ get{ return TwoStoryHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TwoStoryStonePlasterHouseDeed : HouseDeed + { + [Constructable] + public TwoStoryStonePlasterHouseDeed() : base( 0x78, new Point3D( -3, 7, 0 ) ) + { + } + + public TwoStoryStonePlasterHouseDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new TwoStoryHouse( owner, 0x78 ); + } + + public override int LabelNumber{ get{ return 1041221; } } + public override Rectangle2D[] Area{ get{ return TwoStoryHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class TowerDeed : HouseDeed + { + [Constructable] + public TowerDeed() : base( 0x7A, new Point3D( 0, 7, 0 ) ) + { + } + + public TowerDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new Tower( owner ); + } + + public override int LabelNumber{ get{ return 1041222; } } + public override Rectangle2D[] Area{ get{ return Tower.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class KeepDeed : HouseDeed + { + [Constructable] + public KeepDeed() : base( 0x7C, new Point3D( 0, 11, 0 ) ) + { + } + + public KeepDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new Keep( owner ); + } + + public override int LabelNumber{ get{ return 1041223; } } + public override Rectangle2D[] Area{ get{ return Keep.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class CastleDeed : HouseDeed + { + [Constructable] + public CastleDeed() : base( 0x7E, new Point3D( 0, 16, 0 ) ) + { + } + + public CastleDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new Castle( owner ); + } + + public override int LabelNumber{ get{ return 1041224; } } + public override Rectangle2D[] Area{ get{ return Castle.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargePatioDeed : HouseDeed + { + [Constructable] + public LargePatioDeed() : base( 0x8C, new Point3D( -4, 7, 0 ) ) + { + } + + public LargePatioDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new LargePatioHouse( owner ); + } + + public override int LabelNumber{ get{ return 1041231; } } + public override Rectangle2D[] Area{ get{ return LargePatioHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LargeMarbleDeed : HouseDeed + { + [Constructable] + public LargeMarbleDeed() : base( 0x96, new Point3D( -4, 7, 0 ) ) + { + } + + public LargeMarbleDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new LargeMarbleHouse( owner ); + } + + public override int LabelNumber{ get{ return 1041236; } } + public override Rectangle2D[] Area{ get{ return LargeMarbleHouse.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SmallTowerDeed : HouseDeed + { + [Constructable] + public SmallTowerDeed() : base( 0x98, new Point3D( 3, 4, 0 ) ) + { + } + + public SmallTowerDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallTower( owner ); + } + + public override int LabelNumber{ get{ return 1041237; } } + public override Rectangle2D[] Area{ get{ return SmallTower.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class LogCabinDeed : HouseDeed + { + [Constructable] + public LogCabinDeed() : base( 0x9A, new Point3D( 1, 6, 0 ) ) + { + } + + public LogCabinDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new LogCabin( owner ); + } + + public override int LabelNumber{ get{ return 1041238; } } + public override Rectangle2D[] Area{ get{ return LogCabin.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class SandstonePatioDeed : HouseDeed + { + [Constructable] + public SandstonePatioDeed() : base( 0x9C, new Point3D( -1, 4, 0 ) ) + { + } + + public SandstonePatioDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SandStonePatio( owner ); + } + + public override int LabelNumber{ get{ return 1041239; } } + public override Rectangle2D[] Area{ get{ return SandStonePatio.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class VillaDeed : HouseDeed + { + [Constructable] + public VillaDeed() : base( 0x9E, new Point3D( 3, 6, 0 ) ) + { + } + + public VillaDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new TwoStoryVilla( owner ); + } + + public override int LabelNumber{ get{ return 1041240; } } + public override Rectangle2D[] Area{ get{ return TwoStoryVilla.AreaArray; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class StoneWorkshopDeed : HouseDeed + { + [Constructable] + public StoneWorkshopDeed() : base( 0xA0, new Point3D( -1, 4, 0 ) ) + { + } + + public StoneWorkshopDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallShop( owner, 0xA0 ); + } + + public override int LabelNumber{ get{ return 1041241; } } + public override Rectangle2D[] Area{ get{ return SmallShop.AreaArray2; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } + + public class MarbleWorkshopDeed : HouseDeed + { + [Constructable] + public MarbleWorkshopDeed() : base( 0xA2, new Point3D( -1, 4, 0 ) ) + { + } + + public MarbleWorkshopDeed( Serial serial ) : base( serial ) + { + } + + public override BaseHouse GetHouse( Mobile owner ) + { + return new SmallShop( owner, 0xA2 ); + } + + public override int LabelNumber{ get{ return 1041242; } } + public override Rectangle2D[] Area{ get{ return SmallShop.AreaArray1; } } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/HouseFoundation.cs b/Scripts/Multis/HouseFoundation.cs new file mode 100644 index 000000000..8c9753294 --- /dev/null +++ b/Scripts/Multis/HouseFoundation.cs @@ -0,0 +1,2458 @@ +using System; +using System.IO; +using System.Threading; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; +using Server.Commands; + +namespace Server.Multis +{ + public enum FoundationType + { + Stone, + DarkWood, + LightWood, + Dungeon, + Brick, + ElvenGrey, + ElvenNatural + } + + public class HouseFoundation : BaseHouse + { + private DesignState m_Current; // State which is currently visible. + private DesignState m_Design; // State of current design. + private DesignState m_Backup; // State at last user backup. + private Item m_SignHanger; // Item hanging the sign. + private Item m_Signpost; // Item supporting the hanger. + private int m_SignpostGraphic; // ItemID number of the chosen signpost. + private int m_LastRevision; // Latest revision number. + private ArrayList m_Fixtures; // List of fixtures (teleporters and doors) associated with this house. + private FoundationType m_Type; // Graphic type of this foundation. + private Mobile m_Customizer; // Who is currently customizing this -or- null if not customizing. + + public FoundationType Type { get { return m_Type; } set { m_Type = value; } } + public int LastRevision { get { return m_LastRevision; } set { m_LastRevision = value; } } + public ArrayList Fixtures { get { return m_Fixtures; } } + public Item SignHanger { get { return m_SignHanger; } } + public Item Signpost { get { return m_Signpost; } } + public int SignpostGraphic { get { return m_SignpostGraphic; } set { m_SignpostGraphic = value; } } + public Mobile Customizer { get { return m_Customizer; } set { m_Customizer = value; } } + + public override bool IsAosRules { get { return true; } } + + public override bool IsActive { get { return Customizer == null; } } + + public virtual int CustomizationCost { get { return (Core.SE ? 0 : 10000); } } + + public bool IsFixture( Item item ) + { + return (m_Fixtures != null && m_Fixtures.Contains( item )); + } + + public override MultiComponentList Components + { + get + { + if( m_Current == null ) + SetInitialState(); + + return m_Current.Components; + } + } + + public override int GetMaxUpdateRange() + { + return 24; + } + + public override int GetUpdateRange( Mobile m ) + { + int w = CurrentState.Components.Width; + int h = CurrentState.Components.Height-1; + int v = 18 + ((w > h ? w : h) / 2); + + if( v > 24 ) + v = 24; + else if( v < 18 ) + v = 18; + + return v; + } + + public DesignState CurrentState + { + get { if( m_Current == null ) SetInitialState(); return m_Current; } + set { m_Current = value; } + } + + public DesignState DesignState + { + get { if( m_Design == null ) SetInitialState(); return m_Design; } + set { m_Design = value; } + } + + public DesignState BackupState + { + get { if( m_Backup == null ) SetInitialState(); return m_Backup; } + set { m_Backup = value; } + } + + public void SetInitialState() + { + // This is a new house, it has not yet loaded a design state + m_Current = new DesignState( this, GetEmptyFoundation() ); + m_Design = new DesignState( m_Current ); + m_Backup = new DesignState( m_Current ); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if( m_SignHanger != null ) + m_SignHanger.Delete(); + + if( m_Signpost != null ) + m_Signpost.Delete(); + + if( m_Fixtures == null ) + return; + + for( int i = 0; i < m_Fixtures.Count; ++i ) + { + Item item = (Item)m_Fixtures[i]; + + if( item != null ) + item.Delete(); + } + + m_Fixtures.Clear(); + } + + public override void OnLocationChange( Point3D oldLocation ) + { + base.OnLocationChange( oldLocation ); + + int x = Location.X - oldLocation.X; + int y = Location.Y - oldLocation.Y; + int z = Location.Z - oldLocation.Z; + + if( m_SignHanger != null ) + m_SignHanger.MoveToWorld( new Point3D( m_SignHanger.X + x, m_SignHanger.Y + y, m_SignHanger.Z + z ), Map ); + + if( m_Signpost != null ) + m_Signpost.MoveToWorld( new Point3D( m_Signpost.X + x, m_Signpost.Y + y, m_Signpost.Z + z ), Map ); + + if( m_Fixtures == null ) + return; + + for( int i = 0; i < m_Fixtures.Count; ++i ) + { + Item item = (Item)m_Fixtures[i]; + + if( Doors.Contains( item ) ) + continue; + + item.MoveToWorld( new Point3D( item.X + x, item.Y + y, item.Z + z ), Map ); + } + } + + public override void OnMapChange() + { + base.OnMapChange(); + + if( m_SignHanger != null ) + m_SignHanger.Map = this.Map; + + if( m_Signpost != null ) + m_Signpost.Map = this.Map; + + if( m_Fixtures == null ) + return; + + for( int i = 0; i < m_Fixtures.Count; ++i ) + ((Item)m_Fixtures[i]).Map = this.Map; + } + + public void ClearFixtures( Mobile from ) + { + if( m_Fixtures == null ) + return; + + RemoveKeys( from ); + + for( int i = 0; i < m_Fixtures.Count; ++i ) + { + ((Item)m_Fixtures[i]).Delete(); + Doors.Remove( m_Fixtures[i] ); + } + + m_Fixtures.Clear(); + } + + public void AddFixtures( Mobile from, MultiTileEntry[] list ) + { + if( m_Fixtures == null ) + m_Fixtures = new ArrayList(); + + uint keyValue = 0; + + for( int i = 0; i < list.Length; ++i ) + { + MultiTileEntry mte = list[i]; + int itemID = mte.m_ItemID & 0x3FFF; + + if( itemID >= 0x181D && itemID < 0x1829 ) + { + HouseTeleporter tp = new HouseTeleporter( itemID ); + + AddFixture( tp, mte ); + } + else + { + BaseDoor door = null; + + if( itemID >= 0x675 && itemID < 0x6F5 ) + { + int type = (itemID - 0x675) / 16; + DoorFacing facing = (DoorFacing)(((itemID - 0x675) / 2) % 8); + + switch( type ) + { + case 0: door = new GenericHouseDoor( facing, 0x675, 0xEC, 0xF3 ); break; + case 1: door = new GenericHouseDoor( facing, 0x685, 0xEC, 0xF3 ); break; + case 2: door = new GenericHouseDoor( facing, 0x695, 0xEB, 0xF2 ); break; + case 3: door = new GenericHouseDoor( facing, 0x6A5, 0xEA, 0xF1 ); break; + case 4: door = new GenericHouseDoor( facing, 0x6B5, 0xEA, 0xF1 ); break; + case 5: door = new GenericHouseDoor( facing, 0x6C5, 0xEC, 0xF3 ); break; + case 6: door = new GenericHouseDoor( facing, 0x6D5, 0xEA, 0xF1 ); break; + case 7: door = new GenericHouseDoor( facing, 0x6E5, 0xEA, 0xF1 ); break; + } + } + else if( itemID >= 0x314 && itemID < 0x364 ) + { + int type = (itemID - 0x314) / 16; + DoorFacing facing = (DoorFacing)(((itemID - 0x314) / 2) % 8); + + //TODO: Change this to just do a 0x314 + type*16 + switch( type ) + { + case 0: door = new GenericHouseDoor( facing, 0x314, 0xED, 0xF4 ); break; + case 1: door = new GenericHouseDoor( facing, 0x324, 0xED, 0xF4 ); break; + case 2: door = new GenericHouseDoor( facing, 0x334, 0xED, 0xF4 ); break; + case 3: door = new GenericHouseDoor( facing, 0x344, 0xED, 0xF4 ); break; + case 4: door = new GenericHouseDoor( facing, 0x354, 0xED, 0xF4 ); break; + } + } + else if( itemID >= 0x824 && itemID < 0x834 ) + { + DoorFacing facing = (DoorFacing)(((itemID - 0x824) / 2) % 8); + door = new GenericHouseDoor( facing, 0x824, 0xEC, 0xF3 ); + } + else if( itemID >= 0x839 && itemID < 0x849 ) + { + DoorFacing facing = (DoorFacing)(((itemID - 0x839) / 2) % 8); + door = new GenericHouseDoor( facing, 0x839, 0xEB, 0xF2 ); + } + else if( itemID >= 0x84C && itemID < 0x85C ) + { + DoorFacing facing = (DoorFacing)(((itemID - 0x84C) / 2) % 8); + door = new GenericHouseDoor( facing, 0x84C, 0xEC, 0xF3 ); + } + else if( itemID >= 0x866 && itemID < 0x876 ) + { + DoorFacing facing = (DoorFacing)(((itemID - 0x866) / 2) % 8); + door = new GenericHouseDoor( facing, 0x866, 0xEB, 0xF2 ); + } + else if( itemID >= 0xE8 && itemID < 0xF8 ) + { + DoorFacing facing = (DoorFacing)(((itemID - 0xE8) / 2) % 8); + door = new GenericHouseDoor( facing, 0xE8, 0xED, 0xF4 ); + } + else if( itemID >= 0x1FED && itemID < 0x1FFD ) + { + DoorFacing facing = (DoorFacing)(((itemID - 0x1FED) / 2) % 8); + door = new GenericHouseDoor( facing, 0x1FED, 0xEC, 0xF3 ); + } + else if( itemID >= 0x241F && itemID < 0x2421 ) + { + //DoorFacing facing = (DoorFacing)(((itemID - 0x241F) / 2) % 8); + door = new GenericHouseDoor( DoorFacing.NorthCCW, 0x2415, -1, -1 ); + } + else if( itemID >= 0x2423 && itemID < 0x2425 ) + { + //DoorFacing facing = (DoorFacing)(((itemID - 0x241F) / 2) % 8); + //This one and the above one are 'special' cases, ie: OSI had the ItemID pattern discombobulated for these + door = new GenericHouseDoor( DoorFacing.WestCW, 0x2423, -1, -1 ); + } + else if( itemID >= 0x2A05 && itemID < 0x2A1D ) + { + DoorFacing facing = (DoorFacing)((((itemID - 0x2A05) / 2) % 4) + 8); + + int sound = (itemID >= 0x2A0D && itemID < 0x2a15) ? 0x539 : -1; + + door = new GenericHouseDoor( facing, 0x29F5 + (8 * ((itemID - 0x2A05) / 8)), sound, sound ); + } + else if( itemID == 0x2D46 ) + { + door = new GenericHouseDoor( DoorFacing.NorthCW, 0x2D46, 0xEA, 0xF1, false ); + } + else if( itemID == 0x2D48 || itemID == 0x2FE2 ) + { + door = new GenericHouseDoor( DoorFacing.SouthCCW, itemID, 0xEA, 0xF1, false ); + } + else if( itemID >= 0x2D63 && itemID < 0x2D70 ) + { + int mod = (itemID - 0x2D63)/2%2; + DoorFacing facing = ( ( mod == 0 ) ? DoorFacing.SouthCCW : DoorFacing.WestCCW ); + + int type = (itemID - 0x2D63) / 4; + + door = new GenericHouseDoor( facing, 0x2D63 + 4*type + mod*2, 0xEA, 0xF1, false ); + } + else if( itemID == 0x2FE4 || itemID == 0x31AE ) + { + door = new GenericHouseDoor( DoorFacing.WestCCW, itemID, 0xEA, 0xF1, false ); + } + else if( itemID >= 0x319C && itemID < 0x31AE ) + { + //special case for 0x31aa <-> 0x31a8 (a9) + + int mod = (itemID - 0x319C) / 2 % 2; + + bool specialCase = (itemID == 0x31AA || itemID == 0x31A8); + + DoorFacing facing; + + if ( itemID == 0x31AA || itemID == 0x31A8 ) + facing = ((mod == 0) ? DoorFacing.NorthCW : DoorFacing.EastCW); + else + facing = ((mod == 0) ? DoorFacing.EastCW : DoorFacing.NorthCW); + + int type = (itemID - 0x319C) / 4; + + door = new GenericHouseDoor( facing, 0x319C + 4 * type + mod * 2, 0xEA, 0xF1, false ); + } + + /* + * ASayre HATE doors. ASayre SMASH + WestCCW EastCW SoutCCW NorthCW + 0x31AE* 0x31AC 0x2D48* 0x2D46* Elvan Wood Door + 0x2d65* 0x31a0 0x2d63* 0x31a2 Elvan White Wooden Door 1 + 0x2d69* 0x31a4 0x2d67* 0x31a6 Elvan Ornate Door + 0x2d6d* 0x31aa 0x2d6b* 0x31a8 Elvan Kia Wood Door 2 + 0x2fe4* 0x319c 0x2fe2* 0x319e Elvan Moon Door + * + * */ + + if( door != null ) + { + if( keyValue == 0 ) + keyValue = CreateKeys( from ); + + door.Locked = true; + door.KeyValue = keyValue; + + AddDoor( door, mte.m_OffsetX, mte.m_OffsetY, mte.m_OffsetZ ); + m_Fixtures.Add( door ); + } + } + } + + for( int i = 0; i < m_Fixtures.Count; ++i ) + { + Item fixture = (Item)m_Fixtures[i]; + + if( fixture is HouseTeleporter ) + { + HouseTeleporter tp = (HouseTeleporter)fixture; + + for( int j = 1; j <= m_Fixtures.Count; ++j ) + { + HouseTeleporter check = m_Fixtures[(i + j) % m_Fixtures.Count] as HouseTeleporter; + + if( check != null && check.ItemID == tp.ItemID ) + { + tp.Target = check; + break; + } + } + } + else if( fixture is BaseHouseDoor ) + { + BaseHouseDoor door = (BaseHouseDoor)fixture; + + if( door.Link != null ) + continue; + + DoorFacing linkFacing; + int xOffset, yOffset; + + switch( door.Facing ) + { + default: + case DoorFacing.WestCW: linkFacing = DoorFacing.EastCCW; xOffset = 1; yOffset = 0; break; + case DoorFacing.EastCCW: linkFacing = DoorFacing.WestCW; xOffset = -1; yOffset = 0; break; + case DoorFacing.WestCCW: linkFacing = DoorFacing.EastCW; xOffset = 1; yOffset = 0; break; + case DoorFacing.EastCW: linkFacing = DoorFacing.WestCCW; xOffset = -1; yOffset = 0; break; + case DoorFacing.SouthCW: linkFacing = DoorFacing.NorthCCW; xOffset = 0; yOffset = -1; break; + case DoorFacing.NorthCCW: linkFacing = DoorFacing.SouthCW; xOffset = 0; yOffset = 1; break; + case DoorFacing.SouthCCW: linkFacing = DoorFacing.NorthCW; xOffset = 0; yOffset = -1; break; + case DoorFacing.NorthCW: linkFacing = DoorFacing.SouthCCW; xOffset = 0; yOffset = 1; break; + case DoorFacing.SouthSW: linkFacing = DoorFacing.SouthSE; xOffset = 1; yOffset = 0; break; + case DoorFacing.SouthSE: linkFacing = DoorFacing.SouthSW; xOffset = -1; yOffset = 0; break; + case DoorFacing.WestSN: linkFacing = DoorFacing.WestSS; xOffset = 0; yOffset = 1; break; + case DoorFacing.WestSS: linkFacing = DoorFacing.WestSN; xOffset = 0; yOffset = -1; break; + } + + for( int j = i + 1; j < m_Fixtures.Count; ++j ) + { + BaseHouseDoor check = m_Fixtures[j] as BaseHouseDoor; + + if( check != null && check.Link == null && check.Facing == linkFacing && (check.X - door.X) == xOffset && (check.Y - door.Y) == yOffset && (check.Z == door.Z) ) + { + check.Link = door; + door.Link = check; + break; + } + } + } + } + } + + public void AddFixture( Item item, MultiTileEntry mte ) + { + m_Fixtures.Add( item ); + item.MoveToWorld( new Point3D( X + mte.m_OffsetX, Y + mte.m_OffsetY, Z + mte.m_OffsetZ ), Map ); + } + + public static void GetFoundationGraphics( FoundationType type, out int east, out int south, out int post, out int corner ) + { + switch( type ) + { + default: + case FoundationType.DarkWood: corner = 0x0014; east = 0x0015; south = 0x0016; post = 0x0017; break; + case FoundationType.LightWood: corner = 0x00BD; east = 0x00BE; south = 0x00BF; post = 0x00C0; break; + case FoundationType.Dungeon: corner = 0x02FD; east = 0x02FF; south = 0x02FE; post = 0x0300; break; + case FoundationType.Brick: corner = 0x0041; east = 0x0043; south = 0x0042; post = 0x0044; break; + case FoundationType.Stone: corner = 0x0065; east = 0x0064; south = 0x0063; post = 0x0066; break; + + case FoundationType.ElvenGrey: corner = 0x2DF7; east = 0x2DF9; south = 0x2DFA; post = 0x2DF8; break; + case FoundationType.ElvenNatural: corner = 0x2DFB; east = 0x2DFD; south = 0x2DFE; post = 0x2DFC; break; + } + } + + public static void ApplyFoundation( FoundationType type, MultiComponentList mcl ) + { + int east, south, post, corner; + + GetFoundationGraphics( type, out east, out south, out post, out corner ); + + int xCenter = mcl.Center.X; + int yCenter = mcl.Center.Y; + + mcl.Add( post, 0 - xCenter, 0 - yCenter, 0 ); + mcl.Add( corner, mcl.Width - 1 - xCenter, mcl.Height - 2 - yCenter, 0 ); + + for( int x = 1; x < mcl.Width; ++x ) + { + mcl.Add( south, x - xCenter, 0 - yCenter, 0 ); + + if( x < mcl.Width-1 ) + mcl.Add( south, x - xCenter, mcl.Height - 2 - yCenter, 0 ); + } + + for( int y = 1; y < mcl.Height - 1; ++y ) + { + mcl.Add( east, 0 - xCenter, y - yCenter, 0 ); + + if( y < mcl.Height - 2 ) + mcl.Add( east, mcl.Width - 1 - xCenter, y - yCenter, 0 ); + } + } + + public static void AddStairsTo( ref MultiComponentList mcl ) + { + // copy the original.. + mcl = new MultiComponentList( mcl ); + + mcl.Resize( mcl.Width, mcl.Height + 1 ); + + int xCenter = mcl.Center.X; + int yCenter = mcl.Center.Y; + int y = mcl.Height - 1; + + for( int x = 0; x < mcl.Width; ++x ) + mcl.Add( 0x63, x - xCenter, y - yCenter, 0 ); + } + + public MultiComponentList GetEmptyFoundation() + { + // Copy original foundation layout + MultiComponentList mcl = new MultiComponentList( MultiData.GetComponents( ItemID ) ); + + mcl.Resize( mcl.Width, mcl.Height + 1 ); + + int xCenter = mcl.Center.X; + int yCenter = mcl.Center.Y; + int y = mcl.Height - 1; + + ApplyFoundation( m_Type, mcl ); + + for( int x = 1; x < mcl.Width; ++x ) + mcl.Add( 0x751, x - xCenter, y - yCenter, 0 ); + + return mcl; + } + + public override Rectangle2D[] Area + { + get + { + MultiComponentList mcl = Components; + + return new Rectangle2D[] { new Rectangle2D( mcl.Min.X, mcl.Min.Y, mcl.Width, mcl.Height ) }; + } + } + + public override Point3D BaseBanLocation { get { return new Point3D( Components.Min.X, Components.Height - 1 - Components.Center.Y, 0 ); } } + + public void CheckSignpost() + { + MultiComponentList mcl = this.Components; + + int x = mcl.Min.X; + int y = mcl.Height - 2 - mcl.Center.Y; + + if( CheckWall( mcl, x, y ) ) + { + if( m_Signpost != null ) + m_Signpost.Delete(); + + m_Signpost = null; + } + else if( m_Signpost == null ) + { + m_Signpost = new Static( m_SignpostGraphic ); + m_Signpost.MoveToWorld( new Point3D( X + x, Y + y, Z + 7 ), Map ); + } + else + { + m_Signpost.ItemID = m_SignpostGraphic; + m_Signpost.MoveToWorld( new Point3D( X + x, Y + y, Z + 7 ), Map ); + } + } + + public bool CheckWall( MultiComponentList mcl, int x, int y ) + { + x += mcl.Center.X; + y += mcl.Center.Y; + + if( x >= 0 && x < mcl.Width && y >= 0 && y < mcl.Height ) + { + Tile[] tiles = mcl.Tiles[x][y]; + + for( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + if( tile.Z == 7 && tile.Height == 20 ) + return true; + } + } + + return false; + } + + public HouseFoundation( Mobile owner, int multiID, int maxLockdowns, int maxSecures ) + : base( multiID, owner, maxLockdowns, maxSecures ) + { + m_SignpostGraphic = 9; + + m_Fixtures = new ArrayList(); + + int x = Components.Min.X; + int y = Components.Height - 1 - Components.Center.Y; + + m_SignHanger = new Static( 0xB98 ); + m_SignHanger.MoveToWorld( new Point3D( X + x, Y + y, Z + 7 ), Map ); + + CheckSignpost(); + + SetSign( x, y, 7 ); + } + + public HouseFoundation( Serial serial ) + : base( serial ) + { + } + + public void BeginCustomize( Mobile m ) + { + if( !m.CheckAlive() ) + return; + + RelocateEntities(); + + foreach( Item item in GetItems() ) + { + item.Location = BanLocation; + } + + foreach( Mobile mobile in GetMobiles() ) + { + if( mobile != m ) + mobile.Location = BanLocation; + } + + DesignContext.Add( m, this ); + m.Send( new BeginHouseCustomization( this ) ); + + if( m.NetState != null ) + SendInfoTo( m.NetState ); + + DesignState.SendDetailedInfoTo( m.NetState ); + } + + public override void SendInfoTo( NetState state ) + { + base.SendInfoTo( state ); + + DesignContext context = DesignContext.Find( state.Mobile ); + DesignState stateToSend; + + if( context != null && context.Foundation == this ) + stateToSend = DesignState; + else + stateToSend = CurrentState; + + stateToSend.SendGeneralInfoTo( state ); + } + + public override void Serialize( GenericWriter writer ) + { + writer.Write( (int)5 ); // version + + writer.Write( m_Signpost ); + writer.Write( (int)m_SignpostGraphic ); + + writer.Write( (int)m_Type ); + + writer.Write( m_SignHanger ); + + writer.Write( (int)m_LastRevision ); + writer.WriteItemList( m_Fixtures, true ); + + CurrentState.Serialize( writer ); + DesignState.Serialize( writer ); + BackupState.Serialize( writer ); + + base.Serialize( writer ); + } + + private int m_DefaultPrice; + + public override int DefaultPrice { get { return m_DefaultPrice; } } + + public override void Deserialize( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch( version ) + { + case 5: + case 4: + { + m_Signpost = reader.ReadItem(); + m_SignpostGraphic = reader.ReadInt(); + + goto case 3; + } + case 3: + { + m_Type = (FoundationType)reader.ReadInt(); + + goto case 2; + } + case 2: + { + m_SignHanger = reader.ReadItem(); + + goto case 1; + } + case 1: + { + if( version < 5 ) + m_DefaultPrice = reader.ReadInt(); + + goto case 0; + } + case 0: + { + if( version < 3 ) + m_Type = FoundationType.Stone; + + if( version < 4 ) + m_SignpostGraphic = 9; + + m_LastRevision = reader.ReadInt(); + m_Fixtures = reader.ReadItemList(); + + m_Current = new DesignState( this, reader ); + m_Design = new DesignState( this, reader ); + m_Backup = new DesignState( this, reader ); + + break; + } + } + + base.Deserialize( reader ); + } + + public bool IsHiddenToCustomizer( Item item ) + { + return (item == m_Signpost || (m_Fixtures != null && m_Fixtures.Contains( item ))); + } + + public static void Initialize() + { + PacketHandlers.RegisterExtended( 0x1E, true, new OnPacketReceive( QueryDesignDetails ) ); + + PacketHandlers.RegisterEncoded( 0x02, true, new OnEncodedPacketReceive( Designer_Backup ) ); + PacketHandlers.RegisterEncoded( 0x03, true, new OnEncodedPacketReceive( Designer_Restore ) ); + PacketHandlers.RegisterEncoded( 0x04, true, new OnEncodedPacketReceive( Designer_Commit ) ); + PacketHandlers.RegisterEncoded( 0x05, true, new OnEncodedPacketReceive( Designer_Delete ) ); + PacketHandlers.RegisterEncoded( 0x06, true, new OnEncodedPacketReceive( Designer_Build ) ); + PacketHandlers.RegisterEncoded( 0x0C, true, new OnEncodedPacketReceive( Designer_Close ) ); + PacketHandlers.RegisterEncoded( 0x0D, true, new OnEncodedPacketReceive( Designer_Stairs ) ); + PacketHandlers.RegisterEncoded( 0x0E, true, new OnEncodedPacketReceive( Designer_Sync ) ); + PacketHandlers.RegisterEncoded( 0x10, true, new OnEncodedPacketReceive( Designer_Clear ) ); + PacketHandlers.RegisterEncoded( 0x12, true, new OnEncodedPacketReceive( Designer_Level ) ); + + PacketHandlers.RegisterEncoded( 0x13, true, new OnEncodedPacketReceive( Designer_Roof ) ); // Samurai Empire roof + PacketHandlers.RegisterEncoded( 0x14, true, new OnEncodedPacketReceive( Designer_RoofDelete ) ); // Samurai Empire roof + + PacketHandlers.RegisterEncoded( 0x1A, true, new OnEncodedPacketReceive( Designer_Revert ) ); + + CommandSystem.Register( "DesignInsert", AccessLevel.GameMaster, new CommandEventHandler( DesignInsert_OnCommand ) ); + + EventSink.Speech += new SpeechEventHandler( EventSink_Speech ); + } + + private static void EventSink_Speech( SpeechEventArgs e ) + { + if( DesignContext.Find( e.Mobile ) != null ) + { + e.Mobile.SendLocalizedMessage( 1061925 ); // You cannot speak while customizing your house. + e.Blocked = true; + } + } + + public static void Designer_Sync( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client requested state synchronization + * - Resend full house state + */ + + DesignState design = context.Foundation.DesignState; + + // Resend full house state + design.SendDetailedInfoTo( state ); + } + } + + public static void Designer_Clear( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to clear the design + * - Restore empty foundation + * - Construct new design state from empty foundation + * - Assign constructed state to foundation + * - Update revision + * - Update client with new state + */ + + // Restore empty foundation : Construct new design state from empty foundation + DesignState newDesign = new DesignState( context.Foundation, context.Foundation.GetEmptyFoundation() ); + + // Restore empty foundation : Assign constructed state to foundation + context.Foundation.DesignState = newDesign; + + // Update revision + newDesign.OnRevised(); + + // Update client with new state + context.Foundation.SendInfoTo( state ); + newDesign.SendDetailedInfoTo( state ); + } + } + + public static void Designer_Restore( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to restore design to the last backup state + * - Restore backup + * - Construct new design state from backup state + * - Assign constructed state to foundation + * - Update revision + * - Update client with new state + */ + + // Restore backup : Construct new design state from backup state + DesignState backupDesign = new DesignState( context.Foundation.BackupState ); + + // Restore backup : Assign constructed state to foundation + context.Foundation.DesignState = backupDesign; + + // Update revision; + backupDesign.OnRevised(); + + // Update client with new state + context.Foundation.SendInfoTo( state ); + backupDesign.SendDetailedInfoTo( state ); + } + } + + public static void Designer_Backup( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to backup design state + * - Construct a copy of the current design state + * - Assign constructed state to backup state field + */ + + // Construct a copy of the current design state + DesignState copyState = new DesignState( context.Foundation.DesignState ); + + // Assign constructed state to backup state field + context.Foundation.BackupState = copyState; + } + } + + public static void Designer_Revert( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to revert design state to currently visible state + * - Revert design state + * - Construct a copy of the current visible state + * - Freeze fixtures in constructed state + * - Assign constructed state to foundation + * - If a signpost is needed, add it + * - Update revision + * - Update client with new state + */ + + // Revert design state : Construct a copy of the current visible state + DesignState copyState = new DesignState( context.Foundation.CurrentState ); + + // Revert design state : Freeze fixtures in constructed state + copyState.FreezeFixtures(); + + // Revert design state : Assign constructed state to foundation + context.Foundation.DesignState = copyState; + + // Revert design state : If a signpost is needed, add it + context.Foundation.CheckSignpost(); + + // Update revision + copyState.OnRevised(); + + // Update client with new state + context.Foundation.SendInfoTo( state ); + copyState.SendDetailedInfoTo( state ); + } + } + + public void EndConfirmCommit( Mobile from ) + { + int oldPrice = Price; + int newPrice = oldPrice + CustomizationCost + ((DesignState.Components.List.Length - CurrentState.Components.List.Length) * 500); + int cost = newPrice - oldPrice; + + if( cost > 0 ) + { + if( Banker.Withdraw( from, cost ) ) + { + from.SendLocalizedMessage( 1060398, cost.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + } + else + { + from.SendLocalizedMessage( 1061903 ); // You cannot commit this house design, because you do not have the necessary funds in your bank box to pay for the upgrade. Please back up your design, obtain the required funds, and commit your design again. + return; + } + } + else if( cost < 0 ) + { + if( Banker.Deposit( from, -cost ) ) + from.SendLocalizedMessage( 1060397, (-cost).ToString() ); // ~1_AMOUNT~ gold has been deposited into your bank box. + else + return; + } + + /* Client chose to commit current design state + * - Commit design state + * - Construct a copy of the current design state + * - Clear visible fixtures + * - Melt fixtures from constructed state + * - Add melted fixtures from constructed state + * - Assign constructed state to foundation + * - Update house price + * - Remove design context + * - Notify the client that customization has ended + * - Notify the core that the foundation has changed and should be resent to all clients + * - If a signpost is needed, add it + * - Eject all from house + * - Restore relocated entities + */ + + // Commit design state : Construct a copy of the current design state + DesignState copyState = new DesignState( DesignState ); + + // Commit design state : Clear visible fixtures + ClearFixtures( from ); + + // Commit design state : Melt fixtures from constructed state + copyState.MeltFixtures(); + + // Commit design state : Add melted fixtures from constructed state + AddFixtures( from, copyState.Fixtures ); + + // Commit design state : Assign constructed state to foundation + CurrentState = copyState; + + // Update house price + Price = newPrice - CustomizationCost; + + // Remove design context + DesignContext.Remove( from ); + + // Notify the client that customization has ended + from.Send( new EndHouseCustomization( this ) ); + + // Notify the core that the foundation has changed and should be resent to all clients + Delta( ItemDelta.Update ); + ProcessDelta(); + CurrentState.SendDetailedInfoTo( from.NetState ); + + // If a signpost is needed, add it + CheckSignpost(); + + // Eject all from house + from.RevealingAction(); + + foreach( Item item in GetItems() ) + item.Location = BanLocation; + + foreach( Mobile mobile in GetMobiles() ) + mobile.Location = BanLocation; + + // Restore relocated entities + RestoreRelocatedEntities(); + } + + public static void Designer_Commit( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + int oldPrice = context.Foundation.Price; + int newPrice = oldPrice + context.Foundation.CustomizationCost + ((context.Foundation.DesignState.Components.List.Length - context.Foundation.CurrentState.Components.List.Length) * 500); + int bankBalance = Banker.GetBalance( from ); + + from.SendGump( new ConfirmCommitGump( from, context.Foundation, bankBalance, oldPrice, newPrice ) ); + } + } + + public static bool IsSignHanger( int itemID ) + { + itemID &= 0x3FFF; + + return (itemID >= 0xB97 && itemID < 0xBA3); + } + + public int MaxLevels + { + get + { + MultiComponentList mcl = this.Components; + + if( mcl.Width >= 14 || mcl.Height >= 14 ) + return 4; + else + return 3; + } + } + + public static int GetLevelZ( int level, HouseFoundation house ) + { + if( level < 1 || level > house.MaxLevels ) + level = 1; + + return (level-1)*20 + 7; + + /* + switch( level ) + { + default: + case 1: return 07; + case 2: return 27; + case 3: return 47; + case 4: return 67; + } + * */ + } + + public static int GetZLevel( int z, HouseFoundation house ) + { + int level = (z - 7)/20 +1; + + if( level < 1 || level > house.MaxLevels ) + level = 1; + + return level; + } + + private static ComponentVerification m_Verification; + + public static ComponentVerification Verification + { + get + { + if ( m_Verification == null ) + m_Verification = new ComponentVerification(); + + return m_Verification; + } + } + + public static bool ValidPiece( int itemID ) + { + return ValidPiece( itemID, false ); + } + + public static bool ValidPiece( int itemID, bool roof ) + { + itemID &= 0x3FFF; + + if ( !roof && ( TileData.ItemTable[itemID & 0x3FFF].Flags & TileFlag.Roof ) != 0 ) + return false; + else if ( roof && ( TileData.ItemTable[itemID & 0x3FFF].Flags & TileFlag.Roof ) == 0 ) + return false; + + return Verification.IsItemValid( itemID ); + } + + private static int[] m_BlockIDs = new int[] + { + 0x3EE, 0x709, 0x71E, 0x721, + 0x738, 0x750, 0x76C, 0x788, + 0x7A3, 0x7BA + }; + + private static int[] m_StairSeqs = new int[] + { + 0x3EF, 0x70A, 0x722, 0x739, + 0x751, 0x76D, 0x789, 0x7A4 + }; + + private static int[] m_StairIDs = new int[] + { + 0x71F, 0x736, 0x737, 0x749, + 0x7BB, 0x7BC + }; + + public static bool IsStairBlock( int id ) + { + id &= 0x3FFF; + int delta = -1; + + for( int i = 0; delta < 0 && i < m_BlockIDs.Length; ++i ) + delta = (m_BlockIDs[i] - id); + + return (delta == 0); + } + + public static bool IsStair( int id, ref int dir ) + { + id &= 0x3FFF; + int delta = -4; + + for( int i = 0; delta < -3 && i < m_StairSeqs.Length; ++i ) + delta = (m_StairSeqs[i] - id); + + if( delta >= -3 && delta <= 0 ) + { + dir = -delta; + return true; + } + + delta = -1; + + for( int i = 0; delta < 0 && i < m_StairIDs.Length; ++i ) + { + delta = (m_StairIDs[i] - id); + dir = i % 4; + } + + return (delta == 0); + } + + public static bool DeleteStairs( MultiComponentList mcl, int id, int x, int y, int z ) + { + int ax = x + mcl.Center.X; + int ay = y + mcl.Center.Y; + + if( ax < 0 || ay < 0 || ax >= mcl.Width || ay >= (mcl.Height - 1) || z < 7 || ((z - 7) % 5) != 0 ) + return false; + + if( IsStairBlock( id ) ) + { + Tile[] tiles = mcl.Tiles[ax][ay]; + + for( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + if( tile.Z == (z + 5) ) + { + id = tile.ID; + z = tile.Z; + + if( !IsStairBlock( id ) ) + break; + } + } + } + + int dir = 0; + + if( !IsStair( id, ref dir ) ) + return false; + + int height = ((z - 7) % 20) / 5; + + int xStart, yStart; + int xInc, yInc; + + switch( dir ) + { + default: + case 0: // North + { + xStart = x; + yStart = y + height; + xInc = 0; + yInc = -1; + break; + } + case 1: // West + { + xStart = x + height; + yStart = y; + xInc = -1; + yInc = 0; + break; + } + case 2: // South + { + xStart = x; + yStart = y - height; + xInc = 0; + yInc = 1; + break; + } + case 3: // East + { + xStart = x - height; + yStart = y; + xInc = 1; + yInc = 0; + break; + } + } + + int zStart = z - (height * 5); + + for( int i = 0; i < 4; ++i ) + { + x = xStart + (i * xInc); + y = yStart + (i * yInc); + + for( int j = 0; j <= i; ++j ) + mcl.RemoveXYZH( x, y, zStart + (j * 5), 5 ); + + ax = x + mcl.Center.X; + ay = y + mcl.Center.Y; + + if( ax >= 1 && ax < mcl.Width && ay >= 1 && ay < mcl.Height - 1 ) + { + Tile[] tiles = mcl.Tiles[ax][ay]; + + bool hasBaseFloor = false; + + for( int j = 0; !hasBaseFloor && j < tiles.Length; ++j ) + hasBaseFloor = (tiles[j].Z == 7 && (tiles[j].ID & 0x3FFF) != 1); + + if( !hasBaseFloor ) + mcl.Add( 0x31F4, x, y, 7 ); + } + } + + return true; + } + + public static void Designer_Delete( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to delete a component + * - Read data detailing which component to delete + * - Verify component is deletable + * - Remove the component + * - If needed, replace removed component with a dirt tile + * - Update revision + */ + + // Read data detailing which component to delete + int itemID = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt32(); + int y = pvSrc.ReadInt32(); + int z = pvSrc.ReadInt32(); + + // Verify component is deletable + DesignState design = context.Foundation.DesignState; + MultiComponentList mcl = design.Components; + + int ax = x + mcl.Center.X; + int ay = y + mcl.Center.Y; + + if( IsSignHanger( itemID ) || (z == 0 && ax >= 0 && ax < mcl.Width && ay >= 0 && ay < (mcl.Height - 1)) ) + { + /* Component is not deletable + * - Resend design state + * - Return without further processing + */ + + design.SendDetailedInfoTo( state ); + return; + } + + // Remove the component + if( !DeleteStairs( mcl, itemID, x, y, z ) ) + mcl.Remove( itemID, x, y, z ); + + // If needed, replace removed component with a dirt tile + if( ax >= 1 && ax < mcl.Width && ay >= 1 && ay < mcl.Height - 1 ) + { + Tile[] tiles = mcl.Tiles[ax][ay]; + + bool hasBaseFloor = false; + + for( int i = 0; !hasBaseFloor && i < tiles.Length; ++i ) + hasBaseFloor = (tiles[i].Z == 7 && (tiles[i].ID & 0x3FFF) != 1); + + if( !hasBaseFloor ) + { + // Replace with a dirt tile + mcl.Add( 0x31F4, x, y, 7 ); + } + } + + // Update revision + design.OnRevised(); + } + } + + public static void Designer_Stairs( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to add stairs + * - Read data detailing stair type and location + * - Validate stair multi ID + * - Add the stairs + * - Load data describing the stair components + * - Insert described components + * - Update revision + */ + + // Read data detailing stair type and location + int itemID = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt32(); + int y = pvSrc.ReadInt32(); + + // Validate stair multi ID + DesignState design = context.Foundation.DesignState; + + if ( !Verification.IsMultiValid( itemID ) ) + { + /* Specified multi ID is not a stair + * - Resend design state + * - Return without further processing + */ + + TraceValidity( state, itemID ); + design.SendDetailedInfoTo( state ); + return; + } + + // Add the stairs + MultiComponentList mcl = design.Components; + + // Add the stairs : Load data describing stair components + MultiComponentList stairs = MultiData.GetComponents( itemID ); + + // Add the stairs : Insert described components + int z = GetLevelZ( context.Level, context.Foundation ); + + for( int i = 0; i < stairs.List.Length; ++i ) + { + MultiTileEntry entry = stairs.List[i]; + + if( (entry.m_ItemID & 0x3FFF) != 1 ) + mcl.Add( entry.m_ItemID, x + entry.m_OffsetX, y + entry.m_OffsetY, z + entry.m_OffsetZ ); + } + + // Update revision + design.OnRevised(); + } + } + + [Usage( "DesignInsert" )] + [Description( "Inserts multiple targeted items into a customizable houses design." )] + public static void DesignInsert_OnCommand( CommandEventArgs e ) + { + e.Mobile.Target = new DesignInsertTarget( null ); + e.Mobile.SendMessage( "Target an item to insert it into the house design." ); + } + + private class DesignInsertTarget : Target + { + private HouseFoundation m_Foundation; + + public DesignInsertTarget( HouseFoundation foundation ) + : base( -1, false, TargetFlags.None ) + { + m_Foundation = foundation; + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + if( m_Foundation != null ) + { + from.SendMessage( "Your changes have been committed. Updating..." ); + + m_Foundation.Delta( ItemDelta.Update ); + } + } + + protected override void OnTarget( Mobile from, object obj ) + { + Item item = obj as Item; + + if( item == null ) + { + from.Target = new DesignInsertTarget( m_Foundation ); + from.SendMessage( "That is not an item. Try again." ); + } + else + { + HouseFoundation house = BaseHouse.FindHouseAt( item ) as HouseFoundation; + + if( house == null ) + { + from.Target = new DesignInsertTarget( m_Foundation ); + from.SendMessage( "That item is not inside a customizable house. Try again." ); + } + else if( m_Foundation != null && house != m_Foundation ) + { + from.Target = new DesignInsertTarget( m_Foundation ); + from.SendMessage( "That item is not inside the current house; all targeted items must reside in the same house. You may cancel this target and repeat the command." ); + } + else + { + DesignState state = house.CurrentState; + MultiComponentList mcl = state.Components; + + int x = item.X - house.X; + int y = item.Y - house.Y; + int z = item.Z - house.Z; + + if( x >= mcl.Min.X && y >= mcl.Min.Y && x <= mcl.Max.X && y <= mcl.Max.Y ) + { + mcl.Add( item.ItemID, x, y, z ); + item.Delete(); + + state.OnRevised(); + + state = house.DesignState; + mcl = state.Components; + + if( x >= mcl.Min.X && y >= mcl.Min.Y && x <= mcl.Max.X && y <= mcl.Max.Y ) + { + mcl.Add( item.ItemID, x, y, z ); + state.OnRevised(); + } + + from.Target = new DesignInsertTarget( house ); + + if( m_Foundation == null ) + from.SendMessage( "The item has been inserted into the house design. Press ESC when you are finished." ); + else + from.SendMessage( "The item has been inserted into the house design." ); + + m_Foundation = house; + } + else + { + from.Target = new DesignInsertTarget( m_Foundation ); + from.SendMessage( "That item is not inside a customizable house. Try again." ); + } + } + } + } + } + + private static void TraceValidity( NetState state, int itemID ) + { + try + { + using ( StreamWriter op = new StreamWriter( "comp_val.log", true ) ) + op.WriteLine( "{0}\t{1}\tInvalid ItemID 0x{2:X4}", state, state.Mobile, itemID ); + } + catch + { + } + } + + public static void Designer_Build( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client chose to add a component + * - Read data detailing component graphic and location + * - Add component + * - Update revision + */ + + // Read data detailing component graphic and location + int itemID = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt32(); + int y = pvSrc.ReadInt32(); + + // Add component + DesignState design = context.Foundation.DesignState; + + if( from.AccessLevel < AccessLevel.GameMaster && !ValidPiece( itemID ) ) + { + TraceValidity( state, itemID ); + design.SendDetailedInfoTo( state ); + return; + } + + MultiComponentList mcl = design.Components; + + int z = GetLevelZ( context.Level, context.Foundation ); + + if( (y + mcl.Center.Y) == (mcl.Height - 1) ) + z = 0; // Tiles placed on the far-south of the house are at 0 Z + + mcl.Add( itemID, x, y, z ); + + // Update revision + design.OnRevised(); + } + } + + public static void Designer_Close( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client closed his house design window + * - Remove design context + * - Notify the client that customization has ended + * - Refresh client with current visable design state + * - If a signpost is needed, add it + * - Eject all from house + * - Restore relocated entities + */ + + // Remove design context + DesignContext.Remove( from ); + + // Notify the client that customization has ended + from.Send( new EndHouseCustomization( context.Foundation ) ); + + // Refresh client with current visible design state + context.Foundation.SendInfoTo( state ); + context.Foundation.CurrentState.SendDetailedInfoTo( state ); + + // If a signpost is needed, add it + context.Foundation.CheckSignpost(); + + // Eject all from house + from.RevealingAction(); + + foreach( Item item in context.Foundation.GetItems() ) + item.Location = context.Foundation.BanLocation; + + foreach( Mobile mobile in context.Foundation.GetMobiles() ) + mobile.Location = context.Foundation.BanLocation; + + // Restore relocated entities + context.Foundation.RestoreRelocatedEntities(); + } + } + + public static void Designer_Level( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) + { + /* Client is moving to a new floor level + * - Read data detailing the target level + * - Validate target level + * - Update design context with new level + * - Teleport mobile to new level + * - Update client + * + */ + + // Read data detailing the target level + int newLevel = pvSrc.ReadInt32(); + + // Validate target level + if( newLevel < 1 || newLevel > context.MaxLevels ) + newLevel = 1; + + // Update design context with new level + context.Level = newLevel; + + // Teleport mobile to new level + from.Location = new Point3D( from.X, from.Y, context.Foundation.Z + GetLevelZ( newLevel, context.Foundation ) ); + + // Update client + context.Foundation.SendInfoTo( state ); + } + } + + public static void QueryDesignDetails( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + HouseFoundation foundation = World.FindItem( pvSrc.ReadInt32() ) as HouseFoundation; + + if( foundation != null && from.Map == foundation.Map && from.InRange( foundation.GetWorldLocation(), 24 ) && from.CanSee( foundation ) ) + { + DesignState stateToSend; + + if( context != null && context.Foundation == foundation ) + stateToSend = foundation.DesignState; + else + stateToSend = foundation.CurrentState; + + stateToSend.SendDetailedInfoTo( state ); + } + } + + public static void Designer_Roof( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null && (Core.SE || from.AccessLevel >= AccessLevel.GameMaster) ) + { + // Read data detailing component graphic and location + int itemID = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt32(); + int y = pvSrc.ReadInt32(); + int z = pvSrc.ReadInt32(); + + // Add component + DesignState design = context.Foundation.DesignState; + + if( from.AccessLevel < AccessLevel.GameMaster && !ValidPiece( itemID, true ) ) + { + TraceValidity( state, itemID ); + design.SendDetailedInfoTo( state ); + return; + } + + MultiComponentList mcl = design.Components; + + if( z < -3 || z > 12 || z % 3 != 0 ) + z = -3; + z += GetLevelZ( context.Level, context.Foundation ); + + MultiTileEntry[] list = mcl.List; + for( int i = 0; i < list.Length; i++ ) + { + MultiTileEntry mte = list[i]; + + if( mte.m_OffsetX == x && mte.m_OffsetY == y && GetZLevel( mte.m_OffsetZ, context.Foundation ) == context.Level && (TileData.ItemTable[mte.m_ItemID & 0x3FFF].Flags & TileFlag.Roof) != 0 ) + mcl.Remove( mte.m_ItemID, x, y, mte.m_OffsetZ ); + } + + mcl.Add( itemID, x, y, z ); + + // Update revision + design.OnRevised(); + } + } + + public static void Designer_RoofDelete( NetState state, IEntity e, EncodedReader pvSrc ) + { + Mobile from = state.Mobile; + DesignContext context = DesignContext.Find( from ); + + if( context != null ) //No need to check if core.SE if trying to remvoe something that shouldn't be able to be placed anyways + { + // Read data detailing which component to delete + int itemID = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt32(); + int y = pvSrc.ReadInt32(); + int z = pvSrc.ReadInt32(); + + // Verify component is deletable + DesignState design = context.Foundation.DesignState; + MultiComponentList mcl = design.Components; + + if( (TileData.ItemTable[itemID & 0x3FFF].Flags & TileFlag.Roof) == 0 ) + { + design.SendDetailedInfoTo( state ); + return; + } + + mcl.Remove( itemID, x, y, z ); + + design.OnRevised(); + } + } + + + } + + public class DesignState + { + private HouseFoundation m_Foundation; + private MultiComponentList m_Components; + private MultiTileEntry[] m_Fixtures; + private int m_Revision; + private Packet m_PacketCache; + + public Packet PacketCache + { + get { return m_PacketCache; } + set + { + if( m_PacketCache == value ) + return; + + if( m_PacketCache != null ) + m_PacketCache.Release(); + + m_PacketCache = value; + } + } + + public HouseFoundation Foundation { get { return m_Foundation; } } + public MultiComponentList Components { get { return m_Components; } } + public MultiTileEntry[] Fixtures { get { return m_Fixtures; } } + public int Revision { get { return m_Revision; } set { m_Revision = value; } } + + public DesignState( HouseFoundation foundation, MultiComponentList components ) + { + m_Foundation = foundation; + m_Components = components; + m_Fixtures = new MultiTileEntry[0]; + } + + public DesignState( DesignState toCopy ) + { + m_Foundation = toCopy.m_Foundation; + m_Components = new MultiComponentList( toCopy.m_Components ); + m_Revision = toCopy.m_Revision; + m_Fixtures = new MultiTileEntry[toCopy.m_Fixtures.Length]; + + for( int i = 0; i < m_Fixtures.Length; ++i ) + m_Fixtures[i] = toCopy.m_Fixtures[i]; + } + + public DesignState( HouseFoundation foundation, GenericReader reader ) + { + m_Foundation = foundation; + + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + m_Components = new MultiComponentList( reader ); + + int length = reader.ReadInt(); + + m_Fixtures = new MultiTileEntry[length]; + + for( int i = 0; i < length; ++i ) + { + m_Fixtures[i].m_ItemID = reader.ReadShort(); + m_Fixtures[i].m_OffsetX = reader.ReadShort(); + m_Fixtures[i].m_OffsetY = reader.ReadShort(); + m_Fixtures[i].m_OffsetZ = reader.ReadShort(); + m_Fixtures[i].m_Flags = reader.ReadInt(); + } + + m_Revision = reader.ReadInt(); + + break; + } + } + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int)0 ); // version + + m_Components.Serialize( writer ); + + writer.Write( (int)m_Fixtures.Length ); + + for( int i = 0; i < m_Fixtures.Length; ++i ) + { + MultiTileEntry ent = m_Fixtures[i]; + + writer.Write( (short)ent.m_ItemID ); + writer.Write( (short)ent.m_OffsetX ); + writer.Write( (short)ent.m_OffsetY ); + writer.Write( (short)ent.m_OffsetZ ); + writer.Write( (int)ent.m_Flags ); + } + + writer.Write( (int)m_Revision ); + } + + public void OnRevised() + { + lock( this ) + { + m_Revision = ++m_Foundation.LastRevision; + + if( m_PacketCache != null ) + m_PacketCache.Release(); + + m_PacketCache = null; + } + } + + public void SendGeneralInfoTo( NetState state ) + { + if( state != null ) + state.Send( new DesignStateGeneral( m_Foundation, this ) ); + } + + public void SendDetailedInfoTo( NetState state ) + { + if( state != null ) + { + lock( this ) + { + if( m_PacketCache == null ) + DesignStateDetailed.SendDetails( state, m_Foundation, this ); + else + state.Send( m_PacketCache ); + } + } + } + + public void FreezeFixtures() + { + OnRevised(); + + for( int i = 0; i < m_Fixtures.Length; ++i ) + { + MultiTileEntry mte = m_Fixtures[i]; + + m_Components.Add( mte.m_ItemID, mte.m_OffsetX, mte.m_OffsetY, mte.m_OffsetZ ); + } + + m_Fixtures = new MultiTileEntry[0]; + } + + public void MeltFixtures() + { + OnRevised(); + + MultiTileEntry[] list = m_Components.List; + int length = 0; + + for( int i = list.Length - 1; i >= 0; --i ) + { + MultiTileEntry mte = list[i]; + + if( IsFixture( mte.m_ItemID ) ) + ++length; + } + + m_Fixtures = new MultiTileEntry[length]; + + for( int i = list.Length - 1; i >= 0; --i ) + { + MultiTileEntry mte = list[i]; + + if( IsFixture( mte.m_ItemID ) ) + { + m_Fixtures[--length] = mte; + m_Components.Remove( mte.m_ItemID, mte.m_OffsetX, mte.m_OffsetY, mte.m_OffsetZ ); + } + } + } + + public static bool IsFixture( int itemID ) + { + itemID &= 0x3FFF; + + if( itemID >= 0x675 && itemID < 0x6F5 ) + return true; + else if( itemID >= 0x314 && itemID < 0x364 ) + return true; + else if( itemID >= 0x824 && itemID < 0x834 ) + return true; + else if( itemID >= 0x839 && itemID < 0x849 ) + return true; + else if( itemID >= 0x84C && itemID < 0x85C ) + return true; + else if( itemID >= 0x866 && itemID < 0x876 ) + return true; + else if( itemID >= 0x0E8 && itemID < 0x0F8 ) + return true; + else if( itemID >= 0x1FED && itemID < 0x1FFD ) + return true; + else if( itemID >= 0x181D && itemID < 0x1829 ) + return true; + else if( itemID >= 0x241F && itemID < 0x2421 ) + return true; + else if( itemID >= 0x2423 && itemID < 0x2425 ) + return true; + else if( itemID >= 0x2A05 && itemID < 0x2A1D ) + return true; + else if( itemID >= 0x319C && itemID < 0x31B0 ) + return true; + else if( itemID == 0x2D46 ||itemID == 0x2D48 || itemID == 0x2FE2 || itemID == 0x2FE4 ) //ML doors begin here. Note funkyness. + return true; + else if( itemID >= 0x2D63 && itemID < 0x2D70 ) + return true; + else if( itemID >= 0x319C && itemID < 0x31AF ) + return true; + + return false; + } + } + + public class ConfirmCommitGump : Gump + { + private HouseFoundation m_Foundation; + + public ConfirmCommitGump( Mobile from, HouseFoundation foundation, int bankBalance, int oldPrice, int newPrice ) + : base( 50, 50 ) + { + m_Foundation = foundation; + + AddPage( 0 ); + + AddBackground( 0, 0, 320, 320, 5054 ); + + AddImageTiled( 10, 10, 300, 20, 2624 ); + AddImageTiled( 10, 40, 300, 240, 2624 ); + AddImageTiled( 10, 290, 300, 20, 2624 ); + + AddAlphaRegion( 10, 10, 300, 300 ); + + AddHtmlLocalized( 10, 10, 300, 20, 1062060, 32736, false, false ); //
COMMIT DESIGN
+ + AddHtmlLocalized( 10, 40, 300, 140, (newPrice - oldPrice) <= bankBalance ? 1061898 : 1061903, 1023, false, true ); + + AddHtmlLocalized( 10, 190, 150, 20, 1061902, 32736, false, false ); // Bank Balance: + AddLabel( 170, 190, 55, bankBalance.ToString() ); + + AddHtmlLocalized( 10, 215, 150, 20, 1061899, 1023, false, false ); // Old Value: + AddLabel( 170, 215, 90, oldPrice.ToString() ); + + AddHtmlLocalized( 10, 235, 150, 20, 1061900, 1023, false, false ); // Cost To Commit: + AddLabel( 170, 235, 90, newPrice.ToString() ); + + AddHtmlLocalized( 10, 260, 150, 20, 1061901, 31744, false, false ); // Your Cost: + AddLabel( 170, 260, 40, (newPrice - oldPrice).ToString() ); + + AddButton( 10, 290, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 290, 55, 20, 1011036, 32767, false, false ); // OKAY + + AddButton( 170, 290, 4005, 4007, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 195, 290, 55, 20, 1011012, 32767, false, false ); // CANCEL + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if( info.ButtonID == 1 ) + m_Foundation.EndConfirmCommit( sender.Mobile ); + } + } + + public class DesignContext + { + private HouseFoundation m_Foundation; + private int m_Level; + + public HouseFoundation Foundation { get { return m_Foundation; } } + public int Level { get { return m_Level; } set { m_Level = value; } } + public int MaxLevels { get { return m_Foundation.MaxLevels; } } + + public DesignContext( HouseFoundation foundation ) + { + m_Foundation = foundation; + m_Level = 1; + } + + private static Hashtable m_Table = new Hashtable(); + + public static Hashtable Table { get { return m_Table; } } + + public static DesignContext Find( Mobile from ) + { + if( from == null ) + return null; + + return (DesignContext)m_Table[from]; + } + + public static bool Check( Mobile m ) + { + if( Find( m ) != null ) + { + m.SendLocalizedMessage( 1062206 ); // You cannot do that while customizing a house. + return false; + } + + return true; + } + + public static void Add( Mobile from, HouseFoundation foundation ) + { + if( from == null ) + return; + + DesignContext c = new DesignContext( foundation ); + + m_Table[from] = c; + + if( from is PlayerMobile ) + ((PlayerMobile)from).DesignContext = c; + + foundation.Customizer = from; + + from.Hidden = true; + from.Location = new Point3D( foundation.X, foundation.Y, foundation.Z + 7 ); + + NetState state = from.NetState; + + if( state == null ) + return; + + ArrayList fixtures = foundation.Fixtures; + + for( int i = 0; fixtures != null && i < fixtures.Count; ++i ) + { + Item item = (Item)fixtures[i]; + + state.Send( item.RemovePacket ); + } + + if( foundation.Signpost != null ) + state.Send( foundation.Signpost.RemovePacket ); + } + + public static void Remove( Mobile from ) + { + if( from == null ) + return; + + DesignContext context = (DesignContext)m_Table[from]; + + m_Table.Remove( from ); + + if( from is PlayerMobile ) + ((PlayerMobile)from).DesignContext = null; + + if( context == null ) + return; + + context.Foundation.Customizer = null; + + NetState state = from.NetState; + + if( state == null ) + return; + + ArrayList fixtures = context.Foundation.Fixtures; + + for( int i = 0; fixtures != null && i < fixtures.Count; ++i ) + { + Item item = (Item)fixtures[i]; + + item.SendInfoTo( state ); + } + + if( context.Foundation.Signpost != null ) + context.Foundation.Signpost.SendInfoTo( state ); + } + } + + public class BeginHouseCustomization : Packet + { + public BeginHouseCustomization( HouseFoundation house ) + : base( 0xBF ) + { + EnsureCapacity( 17 ); + + m_Stream.Write( (short)0x20 ); + m_Stream.Write( (int)house.Serial ); + m_Stream.Write( (byte)0x04 ); + m_Stream.Write( (ushort)0x0000 ); + m_Stream.Write( (ushort)0xFFFF ); + m_Stream.Write( (ushort)0xFFFF ); + m_Stream.Write( (byte)0xFF ); + } + } + + public class EndHouseCustomization : Packet + { + public EndHouseCustomization( HouseFoundation house ) + : base( 0xBF ) + { + EnsureCapacity( 17 ); + + m_Stream.Write( (short)0x20 ); + m_Stream.Write( (int)house.Serial ); + m_Stream.Write( (byte)0x05 ); + m_Stream.Write( (ushort)0x0000 ); + m_Stream.Write( (ushort)0xFFFF ); + m_Stream.Write( (ushort)0xFFFF ); + m_Stream.Write( (byte)0xFF ); + } + } + + public class DesignStateGeneral : Packet + { + public DesignStateGeneral( HouseFoundation house, DesignState state ) + : base( 0xBF ) + { + EnsureCapacity( 13 ); + + m_Stream.Write( (short)0x1D ); + m_Stream.Write( (int)house.Serial ); + m_Stream.Write( (int)state.Revision ); + } + } + + public class DesignStateDetailed : Packet + { + public const int MaxItemsPerStairBuffer = 750; + + private static byte[][] m_PlaneBuffers; + private static bool[] m_PlaneUsed; + + private static byte[][] m_StairBuffers; + + private static byte[] m_PrimBuffer = new byte[4]; + + public void Write( int value ) + { + m_PrimBuffer[0] = (byte)(value >> 24); + m_PrimBuffer[1] = (byte)(value >> 16); + m_PrimBuffer[2] = (byte)(value >> 8); + m_PrimBuffer[3] = (byte)value; + + m_Stream.UnderlyingStream.Write( m_PrimBuffer, 0, 4 ); + } + + public void Write( short value ) + { + m_PrimBuffer[0] = (byte)(value >> 8); + m_PrimBuffer[1] = (byte)value; + + m_Stream.UnderlyingStream.Write( m_PrimBuffer, 0, 2 ); + } + + public void Write( byte value ) + { + m_Stream.UnderlyingStream.WriteByte( value ); + } + + public void Write( byte[] buffer, int offset, int size ) + { + m_Stream.UnderlyingStream.Write( buffer, offset, size ); + } + + public static void Clear( byte[] buffer, int size ) + { + for( int i = 0; i < size; ++i ) + buffer[i] = 0; + } + + public DesignStateDetailed( int serial, int revision, int xMin, int yMin, int xMax, int yMax, MultiTileEntry[] tiles ) + : base( 0xD8 ) + { + EnsureCapacity( 17 + (tiles.Length * 5) ); + + Write( (byte)0x03 ); // Compression Type + Write( (byte)0x00 ); // Unknown + Write( (int)serial ); + Write( (int)revision ); + Write( (short)tiles.Length ); + Write( (short)0 ); // Buffer length : reserved + Write( (byte)0 ); // Plane count : reserved + + int totalLength = 1; // includes plane count + + int width = (xMax - xMin) + 1; + int height = (yMax - yMin) + 1; + + if( m_PlaneBuffers == null ) + { + m_PlaneBuffers = new byte[9][]; + m_PlaneUsed = new bool[9]; + + for( int i = 0; i < m_PlaneBuffers.Length; ++i ) + m_PlaneBuffers[i] = new byte[0x400]; + + m_StairBuffers = new byte[6][]; + + for( int i = 0; i < m_StairBuffers.Length; ++i ) + m_StairBuffers[i] = new byte[MaxItemsPerStairBuffer * 5]; + } + else + { + for( int i = 0; i < m_PlaneUsed.Length; ++i ) + m_PlaneUsed[i] = false; + + Clear( m_PlaneBuffers[0], width * height * 2 ); + + for( int i = 0; i < 4; ++i ) + { + Clear( m_PlaneBuffers[1 + i], (width - 1) * (height - 2) * 2 ); + Clear( m_PlaneBuffers[5 + i], width * (height - 1) * 2 ); + } + } + + int totalStairsUsed = 0; + + for( int i = 0; i < tiles.Length; ++i ) + { + MultiTileEntry mte = tiles[i]; + int x = mte.m_OffsetX - xMin; + int y = mte.m_OffsetY - yMin; + int z = mte.m_OffsetZ; + bool floor = (TileData.ItemTable[mte.m_ItemID & 0x3FFF].Height <= 0); + int plane, size; + + switch( z ) + { + case 0: plane = 0; break; + case 7: plane = 1; break; + case 27: plane = 2; break; + case 47: plane = 3; break; + case 67: plane = 4; break; + default: + { + int stairBufferIndex = (totalStairsUsed / MaxItemsPerStairBuffer); + byte[] stairBuffer = m_StairBuffers[stairBufferIndex]; + + int byteIndex = (totalStairsUsed % MaxItemsPerStairBuffer) * 5; + + stairBuffer[byteIndex++] = (byte)((mte.m_ItemID >> 8) & 0x3F); + stairBuffer[byteIndex++] = (byte)mte.m_ItemID; + + stairBuffer[byteIndex++] = (byte)mte.m_OffsetX; + stairBuffer[byteIndex++] = (byte)mte.m_OffsetY; + stairBuffer[byteIndex++] = (byte)mte.m_OffsetZ; + + ++totalStairsUsed; + + continue; + } + } + + if( plane == 0 ) + { + size = height; + } + else if( floor ) + { + size = height - 2; + x -= 1; + y -= 1; + } + else + { + size = height - 1; + plane += 4; + } + + int index = ((x * size) + y) * 2; + + if( x < 0 || y < 0 || y >= size || (index + 1) >= 0x400 ) + { + int stairBufferIndex = (totalStairsUsed / MaxItemsPerStairBuffer); + byte[] stairBuffer = m_StairBuffers[stairBufferIndex]; + + int byteIndex = (totalStairsUsed % MaxItemsPerStairBuffer) * 5; + + stairBuffer[byteIndex++] = (byte)((mte.m_ItemID >> 8) & 0x3F); + stairBuffer[byteIndex++] = (byte)mte.m_ItemID; + + stairBuffer[byteIndex++] = (byte)mte.m_OffsetX; + stairBuffer[byteIndex++] = (byte)mte.m_OffsetY; + stairBuffer[byteIndex++] = (byte)mte.m_OffsetZ; + + ++totalStairsUsed; + } + else + { + m_PlaneUsed[plane] = true; + m_PlaneBuffers[plane][index] = (byte)((mte.m_ItemID >> 8) & 0x3F); + m_PlaneBuffers[plane][index + 1] = (byte)mte.m_ItemID; + } + } + + int planeCount = 0; + + for( int i = 0; i < m_PlaneBuffers.Length; ++i ) + { + if( !m_PlaneUsed[i] ) + continue; + + ++planeCount; + + int size = 0; + + if( i == 0 ) + size = width * height * 2; + else if( i < 5 ) + size = (width - 1) * (height - 2) * 2; + else + size = width * (height - 1) * 2; + + byte[] inflatedBuffer = m_PlaneBuffers[i]; + + int deflatedLength = m_DeflatedBuffer.Length; + ZLibError ce = Compression.Pack( m_DeflatedBuffer, ref deflatedLength, inflatedBuffer, size, ZLibQuality.Default ); + + if( ce != ZLibError.Okay ) + { + Console.WriteLine( "ZLib error: {0} (#{1})", ce, (int)ce ); + deflatedLength = 0; + size = 0; + } + + Write( (byte)(0x20 | i) ); + Write( (byte)size ); + Write( (byte)deflatedLength ); + Write( (byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)) ); + Write( m_DeflatedBuffer, 0, deflatedLength ); + + totalLength += 4 + deflatedLength; + } + + int totalStairBuffersUsed = (totalStairsUsed + (MaxItemsPerStairBuffer - 1)) / MaxItemsPerStairBuffer; + + for( int i = 0; i < totalStairBuffersUsed; ++i ) + { + ++planeCount; + + int count = (totalStairsUsed - (i * MaxItemsPerStairBuffer)); + + if( count > MaxItemsPerStairBuffer ) + count = MaxItemsPerStairBuffer; + + int size = count * 5; + + byte[] inflatedBuffer = m_StairBuffers[i]; + + int deflatedLength = m_DeflatedBuffer.Length; + ZLibError ce = Compression.Pack( m_DeflatedBuffer, ref deflatedLength, inflatedBuffer, size, ZLibQuality.Default ); + + if( ce != ZLibError.Okay ) + { + Console.WriteLine( "ZLib error: {0} (#{1})", ce, (int)ce ); + deflatedLength = 0; + size = 0; + } + + Write( (byte)(9 + i) ); + Write( (byte)size ); + Write( (byte)deflatedLength ); + Write( (byte)(((size >> 4) & 0xF0) | ((deflatedLength >> 8) & 0xF)) ); + Write( m_DeflatedBuffer, 0, deflatedLength ); + + totalLength += 4 + deflatedLength; + } + + m_Stream.Seek( 15, System.IO.SeekOrigin.Begin ); + + Write( (short)totalLength ); // Buffer length + Write( (byte)planeCount ); // Plane count + } + + private static byte[] m_InflatedBuffer = new byte[0x2000]; + private static byte[] m_DeflatedBuffer = new byte[0x2000]; + + private class SendQueueEntry + { + public NetState m_NetState; + public int m_Serial, m_Revision; + public int m_xMin, m_yMin, m_xMax, m_yMax; + public DesignState m_Root; + public MultiTileEntry[] m_Tiles; + + public SendQueueEntry( NetState ns, HouseFoundation foundation, DesignState state ) + { + m_NetState = ns; + m_Serial = foundation.Serial; + m_Revision = state.Revision; + m_Root = state; + + MultiComponentList mcl = state.Components; + + m_xMin = mcl.Min.X; + m_yMin = mcl.Min.Y; + m_xMax = mcl.Max.X; + m_yMax = mcl.Max.Y; + + m_Tiles = mcl.List; + } + } + + private static Queue m_SendQueue; + private static AutoResetEvent m_Sync; + private static Thread m_Thread; + + static DesignStateDetailed() + { + m_SendQueue = Queue.Synchronized( new Queue() ); + m_Sync = new AutoResetEvent( false ); + + m_Thread = new Thread( new ThreadStart( CompressionThread ) ); + m_Thread.Name = "AOS Compression Thread"; + m_Thread.Start(); + } + + public static void CompressionThread() + { + while( !Core.Closing ) + { + m_Sync.WaitOne(); + + while( m_SendQueue.Count > 0 ) + { + SendQueueEntry sqe = (SendQueueEntry)m_SendQueue.Dequeue(); + + try + { + Packet p = null; + + lock( sqe.m_Root ) + p = sqe.m_Root.PacketCache; + + if( p == null ) + { + p = new DesignStateDetailed( sqe.m_Serial, sqe.m_Revision, sqe.m_xMin, sqe.m_yMin, sqe.m_xMax, sqe.m_yMax, sqe.m_Tiles ); + p.SetStatic(); + + lock( sqe.m_Root ) + { + if( sqe.m_Revision == sqe.m_Root.Revision ) + sqe.m_Root.PacketCache = p; + } + } + + Timer.DelayCall( TimeSpan.Zero, new TimerStateCallback( SendPacket_Sandbox ), new object[] { sqe.m_NetState, p } ); + } + catch( Exception e ) + { + Console.WriteLine( e ); + + try + { + using( StreamWriter op = new StreamWriter( "dsd_exceptions.txt", true ) ) + op.WriteLine( e ); + } + catch + { + } + } + + //sqe.m_NetState.Send( new DesignStateDetailed( sqe.m_Serial, sqe.m_Revision, sqe.m_xMin, sqe.m_yMin, sqe.m_xMax, sqe.m_yMax, sqe.m_Tiles ) ); + } + } + } + + public static void SendPacket_Sandbox( object state ) + { + object[] states = (object[])state; + NetState ns = (NetState)states[0]; + Packet p = (Packet)states[1]; + + ns.Send( p ); + } + + public static void SendDetails( NetState ns, HouseFoundation house, DesignState state ) + { + m_SendQueue.Enqueue( new SendQueueEntry( ns, house, state ) ); + m_Sync.Set(); + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/HousePlacement.cs b/Scripts/Multis/HousePlacement.cs new file mode 100644 index 000000000..c8b834541 --- /dev/null +++ b/Scripts/Multis/HousePlacement.cs @@ -0,0 +1,342 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Regions; + +namespace Server.Multis +{ + public enum HousePlacementResult + { + Valid, + BadRegion, + BadLand, + BadStatic, + BadItem, + NoSurface, + BadRegionHidden + } + + public class HousePlacement + { + private const int YardSize = 5; + + // Any land tile which matches one of these ID numbers is considered a road and cannot be placed over. + private static int[] m_RoadIDs = new int[] + { + 0x071, 0x08C, + 0x0E8, 0x0EB, + 0x14C, 0x14F, + 0x161, 0x174, + 0x1F0, 0x1F3, + 0x26E, 0x279, + 0x27E, 0x281, + 0x324, 0x3AC, + 0x547, 0x556, + 0x597, 0x5A6, + 0x637, 0x63A, + 0x7AE, 0x7B1, + 0x442, 0x479, // Sand stones + 0x501, 0x510, // Sand stones + 0x009, 0x015, // Furrows + 0x150, 0x15C // Furrows + }; + + public static HousePlacementResult Check( Mobile from, int multiID, Point3D center, out ArrayList toMove ) + { + // If this spot is considered valid, every item and mobile in this list will be moved under the house sign + toMove = new ArrayList(); + + Map map = from.Map; + + if ( map == null || map == Map.Internal ) + return HousePlacementResult.BadLand; // A house cannot go here + + if ( from.AccessLevel >= AccessLevel.GameMaster ) + return HousePlacementResult.Valid; // Staff can place anywhere + + if ( map == Map.Ilshenar ) + return HousePlacementResult.BadRegion; // No houses in Ilshenar + + NoHousingRegion noHousingRegion = (NoHousingRegion) Region.Find( center, map ).GetRegion( typeof( NoHousingRegion ) ); + + if ( noHousingRegion != null ) + return HousePlacementResult.BadRegion; + + // This holds data describing the internal structure of the house + MultiComponentList mcl = MultiData.GetComponents( multiID ); + + if ( multiID >= 0x13EC && multiID < 0x1D00 ) + HouseFoundation.AddStairsTo( ref mcl ); // this is a AOS house, add the stairs + + // Location of the nortwest-most corner of the house + Point3D start = new Point3D( center.X + mcl.Min.X, center.Y + mcl.Min.Y, center.Z ); + + // These are storage lists. They hold items and mobiles found in the map for further processing + ArrayList items = new ArrayList(), mobiles = new ArrayList(); + + // These are also storage lists. They hold location values indicating the yard and border locations. + ArrayList yard = new ArrayList(), borders = new ArrayList(); + + /* RULES: + * + * 1) All tiles which are around the -outside- of the foundation must not have anything impassable. + * 2) No impassable object or land tile may come in direct contact with any part of the house. + * 3) Five tiles from the front and back of the house must be completely clear of all house tiles. + * 4) The foundation must rest flatly on a surface. Any bumps around the foundation are not allowed. + * 5) No foundation tile may reside over terrain which is viewed as a road. + */ + + for ( int x = 0; x < mcl.Width; ++x ) + { + for ( int y = 0; y < mcl.Height; ++y ) + { + int tileX = start.X + x; + int tileY = start.Y + y; + + Tile[] addTiles = mcl.Tiles[x][y]; + + if ( addTiles.Length == 0 ) + continue; // There are no tiles here, continue checking somewhere else + + Point3D testPoint = new Point3D( tileX, tileY, center.Z ); + + Region reg = Region.Find( testPoint, map ); + + if ( !reg.AllowHousing( from, testPoint ) ) // Cannot place houses in dungeons, towns, treasure map areas etc + { + if ( reg.IsPartOf( typeof( TreasureRegion ) ) ) + return HousePlacementResult.BadRegionHidden; + + return HousePlacementResult.BadRegion; + } + + Tile landTile = map.Tiles.GetLandTile( tileX, tileY ); + int landID = landTile.ID & 0x3FFF; + + Tile[] oldTiles = map.Tiles.GetStaticTiles( tileX, tileY, true ); + + Sector sector = map.GetSector( tileX, tileY ); + + items.Clear(); + + for ( int i = 0; i < sector.Items.Count; ++i ) + { + Item item = sector.Items[i]; + + if ( item.Visible && item.X == tileX && item.Y == tileY ) + items.Add( item ); + } + + mobiles.Clear(); + + for ( int i = 0; i < sector.Mobiles.Count; ++i ) + { + Mobile m = (Mobile)sector.Mobiles[i]; + + if ( m.X == tileX && m.Y == tileY ) + mobiles.Add( m ); + } + + int landStartZ = 0, landAvgZ = 0, landTopZ = 0; + + map.GetAverageZ( tileX, tileY, ref landStartZ, ref landAvgZ, ref landTopZ ); + + bool hasFoundation = false; + + for ( int i = 0; i < addTiles.Length; ++i ) + { + Tile addTile = addTiles[i]; + + if ( addTile.ID == 0x4001 ) // Nodraw + continue; + + TileFlag addTileFlags = TileData.ItemTable[addTile.ID & 0x3FFF].Flags; + + bool isFoundation = ( addTile.Z == 0 && (addTileFlags & TileFlag.Wall) != 0 ); + bool hasSurface = false; + + if ( isFoundation ) + hasFoundation = true; + + int addTileZ = center.Z + addTile.Z; + int addTileTop = addTileZ + addTile.Height; + + if ( (addTileFlags & TileFlag.Surface) != 0 ) + addTileTop += 16; + + if ( addTileTop > landStartZ && landAvgZ > addTileZ ) + return HousePlacementResult.BadLand; // Broke rule #2 + + if ( isFoundation && ((TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) == 0) && landAvgZ == center.Z ) + hasSurface = true; + + for ( int j = 0; j < oldTiles.Length; ++j ) + { + Tile oldTile = oldTiles[j]; + ItemData id = TileData.ItemTable[oldTile.ID & 0x3FFF]; + + if ( (id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0)) && addTileTop > oldTile.Z && (oldTile.Z + id.CalcHeight) > addTileZ ) + return HousePlacementResult.BadStatic; // Broke rule #2 + /*else if ( isFoundation && !hasSurface && (id.Flags & TileFlag.Surface) != 0 && (oldTile.Z + id.CalcHeight) == center.Z ) + hasSurface = true;*/ + } + + for ( int j = 0; j < items.Count; ++j ) + { + Item item = (Item)items[j]; + ItemData id = item.ItemData; + + if ( addTileTop > item.Z && (item.Z + id.CalcHeight) > addTileZ ) + { + if ( item.Movable ) + toMove.Add( item ); + else if ( (id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0)) ) + return HousePlacementResult.BadItem; // Broke rule #2 + } + /*else if ( isFoundation && !hasSurface && (id.Flags & TileFlag.Surface) != 0 && (item.Z + id.CalcHeight) == center.Z ) + { + hasSurface = true; + }*/ + } + + if ( isFoundation && !hasSurface ) + return HousePlacementResult.NoSurface; // Broke rule #4 + + for ( int j = 0; j < mobiles.Count; ++j ) + { + Mobile m = (Mobile)mobiles[j]; + + if ( addTileTop > m.Z && (m.Z + 16) > addTileZ ) + toMove.Add( m ); + } + } + + for ( int i = 0; i < m_RoadIDs.Length; i += 2 ) + { + if ( landID >= m_RoadIDs[i] && landID <= m_RoadIDs[i + 1] ) + return HousePlacementResult.BadLand; // Broke rule #5 + } + + if ( hasFoundation ) + { + for ( int xOffset = -1; xOffset <= 1; ++xOffset ) + { + for ( int yOffset = -YardSize; yOffset <= YardSize; ++yOffset ) + { + Point2D yardPoint = new Point2D( tileX + xOffset, tileY + yOffset ); + + if ( !yard.Contains( yardPoint ) ) + yard.Add( yardPoint ); + } + } + + for ( int xOffset = -1; xOffset <= 1; ++xOffset ) + { + for ( int yOffset = -1; yOffset <= 1; ++yOffset ) + { + if ( xOffset == 0 && yOffset == 0 ) + continue; + + // To ease this rule, we will not add to the border list if the tile here is under a base floor (z<=8) + + int vx = x + xOffset; + int vy = y + yOffset; + + if ( vx >= 0 && vx < mcl.Width && vy >= 0 && vy < mcl.Height ) + { + Tile[] breakTiles = mcl.Tiles[vx][vy]; + bool shouldBreak = false; + + for ( int i = 0; !shouldBreak && i < breakTiles.Length; ++i ) + { + Tile breakTile = breakTiles[i]; + + if ( breakTile.Height == 0 && breakTile.Z <= 8 && TileData.ItemTable[breakTile.ID & 0x3FFF].Surface ) + shouldBreak = true; + } + + if ( shouldBreak ) + continue; + } + + Point2D borderPoint = new Point2D( tileX + xOffset, tileY + yOffset ); + + if ( !borders.Contains( borderPoint ) ) + borders.Add( borderPoint ); + } + } + } + } + } + + for ( int i = 0; i < borders.Count; ++i ) + { + Point2D borderPoint = (Point2D)borders[i]; + + Tile landTile = map.Tiles.GetLandTile( borderPoint.X, borderPoint.Y ); + int landID = landTile.ID & 0x3FFF; + + if ( (TileData.LandTable[landID].Flags & TileFlag.Impassable) != 0 ) + return HousePlacementResult.BadLand; + + for ( int j = 0; j < m_RoadIDs.Length; j += 2 ) + { + if ( landID >= m_RoadIDs[j] && landID <= m_RoadIDs[j + 1] ) + return HousePlacementResult.BadLand; // Broke rule #5 + } + + Tile[] tiles = map.Tiles.GetStaticTiles( borderPoint.X, borderPoint.Y, true ); + + for ( int j = 0; j < tiles.Length; ++j ) + { + Tile tile = tiles[j]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + if ( id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0 && (tile.Z + id.CalcHeight) > (center.Z + 2)) ) + return HousePlacementResult.BadStatic; // Broke rule #1 + } + + Sector sector = map.GetSector( borderPoint.X, borderPoint.Y ); + List sectorItems = sector.Items; + + for ( int j = 0; j < sectorItems.Count; ++j ) + { + Item item = sectorItems[j]; + + if ( item.X != borderPoint.X || item.Y != borderPoint.Y || item.Movable ) + continue; + + ItemData id = item.ItemData; + + if ( id.Impassable || (id.Surface && (id.Flags & TileFlag.Background) == 0 && (item.Z + id.CalcHeight) > (center.Z + 2)) ) + return HousePlacementResult.BadItem; // Broke rule #1 + } + } + + for ( int i = 0; i < yard.Count; ++i ) + { + Point2D yardPoint = (Point2D)yard[i]; + + IPooledEnumerable eable = map.GetMultiTilesAt( yardPoint.X, yardPoint.Y ); + + foreach ( Tile[] tile in eable ) + { + for ( int j = 0; j < tile.Length; ++j ) + { + if ( (TileData.ItemTable[tile[j].ID & 0x3FFF].Flags & (TileFlag.Impassable | TileFlag.Surface)) != 0 ) + { + eable.Free(); + return HousePlacementResult.BadStatic; // Broke rule #3 + } + } + } + + eable.Free(); + } + + return HousePlacementResult.Valid; + + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/HousePlacementTool.cs b/Scripts/Multis/HousePlacementTool.cs new file mode 100644 index 000000000..84f0c201f --- /dev/null +++ b/Scripts/Multis/HousePlacementTool.cs @@ -0,0 +1,752 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Items +{ + public class HousePlacementTool : Item + { + public override int LabelNumber{ get{ return 1060651; } } // a house placement tool + + [Constructable] + public HousePlacementTool() : base( 0x14F6 ) + { + Weight = 3.0; + LootType = LootType.Blessed; + } + + public override void OnDoubleClick( Mobile from ) + { + if ( IsChildOf( from.Backpack ) ) + from.SendGump( new HousePlacementCategoryGump( from ) ); + else + from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it. + } + + public HousePlacementTool( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + if ( Weight == 0.0 ) + Weight = 3.0; + } + } + + public class HousePlacementCategoryGump : Gump + { + private Mobile m_From; + + private const int LabelColor = 0x7FFF; + private const int LabelColorDisabled = 0x4210; + + public HousePlacementCategoryGump( Mobile from ) : base( 50, 50 ) + { + m_From = from; + + from.CloseGump( typeof( HousePlacementCategoryGump ) ); + from.CloseGump( typeof( HousePlacementListGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 270, 145, 5054 ); + + AddImageTiled( 10, 10, 250, 125, 2624 ); + AddAlphaRegion( 10, 10, 250, 125 ); + + AddHtmlLocalized( 10, 10, 250, 20, 1060239, LabelColor, false, false ); //
HOUSE PLACEMENT TOOL
+ + AddButton( 10, 110, 4017, 4019, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 110, 150, 20, 3000363, LabelColor, false, false ); // Close + + AddButton( 10, 40, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 40, 200, 20, 1060390, LabelColor, false, false ); // Classic Houses + + AddButton( 10, 60, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 60, 200, 20, 1060391, LabelColor, false, false ); // 2-Story Customizable Houses + + AddButton( 10, 80, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 45, 80, 200, 20, 1060392, LabelColor, false, false ); // 3-Story Customizable Houses + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( !m_From.CheckAlive() || m_From.Backpack == null || m_From.Backpack.FindItemByType( typeof( HousePlacementTool ) ) == null ) + return; + + switch ( info.ButtonID ) + { + case 1: // Classic Houses + { + m_From.SendGump( new HousePlacementListGump( m_From, HousePlacementEntry.ClassicHouses ) ); + break; + } + case 2: // 2-Story Customizable Houses + { + m_From.SendGump( new HousePlacementListGump( m_From, HousePlacementEntry.TwoStoryFoundations ) ); + break; + } + case 3: // 3-Story Customizable Houses + { + m_From.SendGump( new HousePlacementListGump( m_From, HousePlacementEntry.ThreeStoryFoundations ) ); + break; + } + } + } + } + + public class HousePlacementListGump : Gump + { + private Mobile m_From; + private HousePlacementEntry[] m_Entries; + + private const int LabelColor = 0x7FFF; + private const int LabelHue = 0x480; + + public HousePlacementListGump( Mobile from, HousePlacementEntry[] entries ) : base( 50, 50 ) + { + m_From = from; + m_Entries = entries; + + from.CloseGump( typeof( HousePlacementCategoryGump ) ); + from.CloseGump( typeof( HousePlacementListGump ) ); + + AddPage( 0 ); + + AddBackground( 0, 0, 520, 420, 5054 ); + + AddImageTiled( 10, 10, 500, 20, 2624 ); + AddAlphaRegion( 10, 10, 500, 20 ); + + AddHtmlLocalized( 10, 10, 500, 20, 1060239, LabelColor, false, false ); //
HOUSE PLACEMENT TOOL
+ + AddImageTiled( 10, 40, 500, 20, 2624 ); + AddAlphaRegion( 10, 40, 500, 20 ); + + AddHtmlLocalized( 50, 40, 225, 20, 1060235, LabelColor, false, false ); // House Description + AddHtmlLocalized( 275, 40, 75, 20, 1060236, LabelColor, false, false ); // Storage + AddHtmlLocalized( 350, 40, 75, 20, 1060237, LabelColor, false, false ); // Lockdowns + AddHtmlLocalized( 425, 40, 75, 20, 1060034, LabelColor, false, false ); // Cost + + AddImageTiled( 10, 70, 500, 280, 2624 ); + AddAlphaRegion( 10, 70, 500, 280 ); + + AddImageTiled( 10, 360, 500, 20, 2624 ); + AddAlphaRegion( 10, 360, 500, 20 ); + + AddHtmlLocalized( 10, 360, 250, 20, 1060645, LabelColor, false, false ); // Bank Balance: + AddLabel( 250, 360, LabelHue, Banker.GetBalance( from ).ToString() ); + + AddImageTiled( 10, 390, 500, 20, 2624 ); + AddAlphaRegion( 10, 390, 500, 20 ); + + AddButton( 10, 390, 4017, 4019, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, 390, 100, 20, 3000363, LabelColor, false, false ); // Close + + for ( int i = 0; i < entries.Length; ++i ) + { + int page = 1 + (i / 14); + int index = i % 14; + + if ( index == 0 ) + { + if ( page > 1 ) + { + AddButton( 450, 390, 4005, 4007, 0, GumpButtonType.Page, page ); + AddHtmlLocalized( 400, 390, 100, 20, 3000406, LabelColor, false, false ); // Next + } + + AddPage( page ); + + if ( page > 1 ) + { + AddButton( 200, 390, 4014, 4016, 0, GumpButtonType.Page, page - 1 ); + AddHtmlLocalized( 250, 390, 100, 20, 3000405, LabelColor, false, false ); // Previous + } + } + + HousePlacementEntry entry = entries[i]; + + int y = 70 + (index * 20); + + AddButton( 10, y, 4005, 4007, 1 + i, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 50, y, 225, 20, entry.Description, LabelColor, false, false ); + AddLabel( 275, y, LabelHue, entry.Storage.ToString() ); + AddLabel( 350, y, LabelHue, entry.Lockdowns.ToString() ); + AddLabel( 425, y, LabelHue, entry.Cost.ToString() ); + } + } + + public override void OnResponse( Server.Network.NetState sender, RelayInfo info ) + { + if ( !m_From.CheckAlive() || m_From.Backpack == null || m_From.Backpack.FindItemByType( typeof( HousePlacementTool ) ) == null ) + return; + + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_Entries.Length ) + { + if ( m_From.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse( m_From ) ) + m_From.SendLocalizedMessage( 501271 ); // You already own a house, you may not place another! + else + m_From.Target = new NewHousePlacementTarget( m_Entries, m_Entries[index] ); + } + else + { + m_From.SendGump( new HousePlacementCategoryGump( m_From ) ); + } + } + } + + public class NewHousePlacementTarget : MultiTarget + { + private HousePlacementEntry m_Entry; + private HousePlacementEntry[] m_Entries; + + private bool m_Placed; + + public NewHousePlacementTarget( HousePlacementEntry[] entries, HousePlacementEntry entry ) : base( entry.MultiID, entry.Offset ) + { + Range = 14; + + m_Entries = entries; + m_Entry = entry; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( !from.CheckAlive() || from.Backpack == null || from.Backpack.FindItemByType( typeof( HousePlacementTool ) ) == null ) + return; + + IPoint3D ip = o as IPoint3D; + + if ( ip != null ) + { + if ( ip is Item ) + ip = ((Item)ip).GetWorldTop(); + + Point3D p = new Point3D( ip ); + + Region reg = Region.Find( new Point3D( p ), from.Map ); + + if ( from.AccessLevel >= AccessLevel.GameMaster || reg.AllowHousing( from, p ) ) + m_Placed = m_Entry.OnPlacement( from, p ); + else if ( reg.IsPartOf( typeof( TreasureRegion ) ) ) + from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + else + from.SendLocalizedMessage( 501265 ); // Housing can not be created in this area. + } + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( !from.CheckAlive() || from.Backpack == null || from.Backpack.FindItemByType( typeof( HousePlacementTool ) ) == null ) + return; + + if ( !m_Placed ) + from.SendGump( new HousePlacementListGump( from, m_Entries ) ); + } + } + + public class HousePlacementEntry + { + private Type m_Type; + private int m_Description; + private int m_Storage; + private int m_Lockdowns; + private int m_NewStorage; + private int m_NewLockdowns; + private int m_Vendors; + private int m_Cost; + private int m_MultiID; + private Point3D m_Offset; + + public Type Type{ get{ return m_Type; } } + + public int Description{ get{ return m_Description; } } + public int Storage{ get{ return BaseHouse.NewVendorSystem ? m_NewStorage : m_Storage; } } + public int Lockdowns{ get{ return BaseHouse.NewVendorSystem ? m_NewLockdowns : m_Lockdowns; } } + public int Vendors{ get{ return m_Vendors; } } + public int Cost{ get{ return m_Cost; } } + + public int MultiID{ get{ return m_MultiID; } } + public Point3D Offset{ get{ return m_Offset; } } + + public HousePlacementEntry( Type type, int description, int storage, int lockdowns, int newStorage, int newLockdowns, int vendors, int cost, int xOffset, int yOffset, int zOffset, int multiID ) + { + m_Type = type; + m_Description = description; + m_Storage = storage; + m_Lockdowns = lockdowns; + m_NewStorage = newStorage; + m_NewLockdowns = newLockdowns; + m_Vendors = vendors; + m_Cost = cost; + + m_Offset = new Point3D( xOffset, yOffset, zOffset ); + + m_MultiID = multiID; + } + + public BaseHouse ConstructHouse( Mobile from ) + { + try + { + object[] args; + + if ( m_Type == typeof( HouseFoundation ) ) + args = new object[4]{ from, m_MultiID, m_Storage, m_Lockdowns }; + else if ( m_Type == typeof( SmallOldHouse ) || m_Type == typeof( SmallShop ) || m_Type == typeof( TwoStoryHouse ) ) + args = new object[2]{ from, m_MultiID }; + else + args = new object[1]{ from }; + + return Activator.CreateInstance( m_Type, args ) as BaseHouse; + } + catch + { + } + + return null; + } + + public void PlacementWarning_Callback( Mobile from, bool okay, object state ) + { + if ( !from.CheckAlive() || from.Backpack == null || from.Backpack.FindItemByType( typeof( HousePlacementTool ) ) == null ) + return; + + PreviewHouse prevHouse = (PreviewHouse)state; + + if ( !okay ) + { + prevHouse.Delete(); + return; + } + + if ( prevHouse.Deleted ) + { + /* Too much time has passed and the test house you created has been deleted. + * Please try again! + */ + from.SendGump( new NoticeGump( 1060637, 30720, 1060647, 32512, 320, 180, null, null ) ); + + return; + } + + Point3D center = prevHouse.Location; + Map map = prevHouse.Map; + + prevHouse.Delete(); + + ArrayList toMove; + //Point3D center = new Point3D( p.X - m_Offset.X, p.Y - m_Offset.Y, p.Z - m_Offset.Z ); + HousePlacementResult res = HousePlacement.Check( from, m_MultiID, center, out toMove ); + + switch ( res ) + { + case HousePlacementResult.Valid: + { + if ( from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse( from ) ) + { + from.SendLocalizedMessage( 501271 ); // You already own a house, you may not place another! + } + else + { + BaseHouse house = ConstructHouse( from ); + + if ( house == null ) + return; + + house.Price = m_Cost; + + if ( Banker.Withdraw( from, m_Cost ) ) + { + from.SendLocalizedMessage( 1060398, m_Cost.ToString() ); // ~1_AMOUNT~ gold has been withdrawn from your bank box. + } + else + { + house.RemoveKeys( from ); + house.Delete(); + from.SendLocalizedMessage( 1060646 ); // You do not have the funds available in your bank box to purchase this house. Try placing a smaller house, or adding gold or checks to your bank box. + return; + } + + house.MoveToWorld( center, from.Map ); + + for ( int i = 0; i < toMove.Count; ++i ) + { + object o = toMove[i]; + + if ( o is Mobile ) + ((Mobile)o).Location = house.BanLocation; + else if ( o is Item ) + ((Item)o).Location = house.BanLocation; + } + } + + break; + } + case HousePlacementResult.BadItem: + case HousePlacementResult.BadLand: + case HousePlacementResult.BadStatic: + case HousePlacementResult.BadRegionHidden: + case HousePlacementResult.NoSurface: + { + from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + break; + } + case HousePlacementResult.BadRegion: + { + from.SendLocalizedMessage( 501265 ); // Housing cannot be created in this area. + break; + } + } + } + + public bool OnPlacement( Mobile from, Point3D p ) + { + if ( !from.CheckAlive() || from.Backpack == null || from.Backpack.FindItemByType( typeof( HousePlacementTool ) ) == null ) + return false; + + ArrayList toMove; + Point3D center = new Point3D( p.X - m_Offset.X, p.Y - m_Offset.Y, p.Z - m_Offset.Z ); + HousePlacementResult res = HousePlacement.Check( from, m_MultiID, center, out toMove ); + + switch ( res ) + { + case HousePlacementResult.Valid: + { + if ( from.AccessLevel < AccessLevel.GameMaster && BaseHouse.HasAccountHouse( from ) ) + { + from.SendLocalizedMessage( 501271 ); // You already own a house, you may not place another! + } + else + { + from.SendLocalizedMessage( 1011576 ); // This is a valid location. + + PreviewHouse prev = new PreviewHouse( m_MultiID ); + + MultiComponentList mcl = prev.Components; + + Point3D banLoc = new Point3D( center.X + mcl.Min.X, center.Y + mcl.Max.Y + 1, center.Z ); + + for ( int i = 0; i < mcl.List.Length; ++i ) + { + MultiTileEntry entry = mcl.List[i]; + + int itemID = entry.m_ItemID & 0x3FFF; + + if ( itemID >= 0xBA3 && itemID <= 0xC0E ) + { + banLoc = new Point3D( center.X + entry.m_OffsetX, center.Y + entry.m_OffsetY, center.Z ); + break; + } + } + + for ( int i = 0; i < toMove.Count; ++i ) + { + object o = toMove[i]; + + if ( o is Mobile ) + ((Mobile)o).Location = banLoc; + else if ( o is Item ) + ((Item)o).Location = banLoc; + } + + prev.MoveToWorld( center, from.Map ); + + /* You are about to place a new house. + * Placing this house will condemn any and all of your other houses that you may have. + * All of your houses on all shards will be affected. + * + * In addition, you will not be able to place another house or have one transferred to you for one (1) real-life week. + * + * Once you accept these terms, these effects cannot be reversed. + * Re-deeding or transferring your new house will not uncondemn your other house(s) nor will the one week timer be removed. + * + * If you are absolutely certain you wish to proceed, click the button next to OKAY below. + * If you do not wish to trade for this house, click CANCEL. + */ + from.SendGump( new WarningGump( 1060635, 30720, 1049583, 32512, 420, 280, new WarningGumpCallback( PlacementWarning_Callback ), prev ) ); + + return true; + } + + break; + } + case HousePlacementResult.BadItem: + case HousePlacementResult.BadLand: + case HousePlacementResult.BadStatic: + case HousePlacementResult.BadRegionHidden: + case HousePlacementResult.NoSurface: + { + from.SendLocalizedMessage( 1043287 ); // The house could not be created here. Either something is blocking the house, or the house would not be on valid terrain. + break; + } + case HousePlacementResult.BadRegion: + { + from.SendLocalizedMessage( 501265 ); // Housing cannot be created in this area. + break; + } + } + + return false; + } + + private static Hashtable m_Table; + + static HousePlacementEntry() + { + m_Table = new Hashtable(); + + FillTable( m_ClassicHouses ); + FillTable( m_TwoStoryFoundations ); + FillTable( m_ThreeStoryFoundations ); + } + + public static HousePlacementEntry Find( BaseHouse house ) + { + object obj = m_Table[house.GetType()]; + + if ( obj is HousePlacementEntry ) + { + return ((HousePlacementEntry)obj); + } + else if ( obj is ArrayList ) + { + ArrayList list = (ArrayList)obj; + + for ( int i = 0; i < list.Count; ++i ) + { + HousePlacementEntry e = (HousePlacementEntry)list[i]; + + if ( e.m_MultiID == (house.ItemID & 0x3FFF) ) + return e; + } + } + else if ( obj is Hashtable ) + { + Hashtable table = (Hashtable)obj; + + obj = table[house.ItemID & 0x3FFF]; + + if ( obj is HousePlacementEntry ) + return (HousePlacementEntry)obj; + } + + return null; + } + + private static void FillTable( HousePlacementEntry[] entries ) + { + for ( int i = 0; i < entries.Length; ++i ) + { + HousePlacementEntry e = (HousePlacementEntry)entries[i]; + + object obj = m_Table[e.m_Type]; + + if ( obj == null ) + { + m_Table[e.m_Type] = e; + } + else if ( obj is HousePlacementEntry ) + { + ArrayList list = new ArrayList(); + + list.Add( obj ); + list.Add( e ); + + m_Table[e.m_Type] = list; + } + else if ( obj is ArrayList ) + { + ArrayList list = (ArrayList)obj; + + if ( list.Count == 8 ) + { + Hashtable table = new Hashtable(); + + for ( int j = 0; j < list.Count; ++j ) + table[((HousePlacementEntry)list[j]).m_MultiID] = list[j]; + + table[e.m_MultiID] = e; + + m_Table[e.m_Type] = table; + } + else + { + list.Add( e ); + } + } + else if ( obj is Hashtable ) + { + ((Hashtable)obj)[e.m_MultiID] = e; + } + } + } + + private static HousePlacementEntry[] m_ClassicHouses = new HousePlacementEntry[] + { + new HousePlacementEntry( typeof( SmallOldHouse ), 1011303, 425, 212, 489, 244, 10, 37000, 0, 4, 0, 0x0064 ), + new HousePlacementEntry( typeof( SmallOldHouse ), 1011304, 425, 212, 489, 244, 10, 37000, 0, 4, 0, 0x0066 ), + new HousePlacementEntry( typeof( SmallOldHouse ), 1011305, 425, 212, 489, 244, 10, 36750, 0, 4, 0, 0x0068 ), + new HousePlacementEntry( typeof( SmallOldHouse ), 1011306, 425, 212, 489, 244, 10, 35250, 0, 4, 0, 0x006A ), + new HousePlacementEntry( typeof( SmallOldHouse ), 1011307, 425, 212, 489, 244, 10, 36750, 0, 4, 0, 0x006C ), + new HousePlacementEntry( typeof( SmallOldHouse ), 1011308, 425, 212, 489, 244, 10, 36750, 0, 4, 0, 0x006E ), + new HousePlacementEntry( typeof( SmallShop ), 1011321, 425, 212, 489, 244, 10, 50500, -1, 4, 0, 0x00A0 ), + new HousePlacementEntry( typeof( SmallShop ), 1011322, 425, 212, 489, 244, 10, 52500, 0, 4, 0, 0x00A2 ), + new HousePlacementEntry( typeof( SmallTower ), 1011317, 580, 290, 667, 333, 14, 73500, 3, 4, 0, 0x0098 ), + new HousePlacementEntry( typeof( TwoStoryVilla ), 1011319, 1100, 550, 1265, 632, 24, 113750, 3, 6, 0, 0x009E ), + new HousePlacementEntry( typeof( SandStonePatio ), 1011320, 850, 425, 1265, 632, 24, 76500, -1, 4, 0, 0x009C ), + new HousePlacementEntry( typeof( LogCabin ), 1011318, 1100, 550, 1265, 632, 24, 81750, 1, 6, 0, 0x009A ), + new HousePlacementEntry( typeof( GuildHouse ), 1011309, 1370, 685, 1576, 788, 28, 131500, -1, 7, 0, 0x0074 ), + new HousePlacementEntry( typeof( TwoStoryHouse ), 1011310, 1370, 685, 1576, 788, 28, 162750, -3, 7, 0, 0x0076 ), + new HousePlacementEntry( typeof( TwoStoryHouse ), 1011311, 1370, 685, 1576, 788, 28, 162000, -3, 7, 0, 0x0078 ), + new HousePlacementEntry( typeof( LargePatioHouse ), 1011315, 1370, 685, 1576, 788, 28, 129250, -4, 7, 0, 0x008C ), + new HousePlacementEntry( typeof( LargeMarbleHouse ), 1011316, 1370, 685, 1576, 788, 28, 160500, -4, 7, 0, 0x0096 ), + new HousePlacementEntry( typeof( Tower ), 1011312, 2119, 1059, 2437, 1218, 42, 366500, 0, 7, 0, 0x007A ), + new HousePlacementEntry( typeof( Keep ), 1011313, 2625, 1312, 3019, 1509, 52, 572750, 0, 11, 0, 0x007C ), + new HousePlacementEntry( typeof( Castle ), 1011314, 4076, 2038, 4688, 2344, 78, 865250, 0, 16, 0, 0x007E ) + }; + + public static HousePlacementEntry[] ClassicHouses{ get{ return m_ClassicHouses; } } + + + + + + private static HousePlacementEntry[] m_TwoStoryFoundations = new HousePlacementEntry[] + { + new HousePlacementEntry( typeof( HouseFoundation ), 1060241, 425, 212, 489, 244, 10, 30500, 0, 4, 0, 0x13EC ), // 7x7 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060242, 580, 290, 667, 333, 14, 34500, 0, 5, 0, 0x13ED ), // 7x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060243, 650, 325, 748, 374, 16, 38500, 0, 5, 0, 0x13EE ), // 7x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060244, 700, 350, 805, 402, 16, 42500, 0, 6, 0, 0x13EF ), // 7x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060245, 750, 375, 863, 431, 16, 46500, 0, 6, 0, 0x13F0 ), // 7x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060246, 800, 400, 920, 460, 18, 50500, 0, 7, 0, 0x13F1 ), // 7x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060253, 580, 290, 667, 333, 14, 34500, 0, 4, 0, 0x13F8 ), // 8x7 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060254, 650, 325, 748, 374, 16, 39000, 0, 5, 0, 0x13F9 ), // 8x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060255, 700, 350, 805, 402, 16, 43500, 0, 5, 0, 0x13FA ), // 8x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060256, 750, 375, 863, 431, 16, 48000, 0, 6, 0, 0x13FB ), // 8x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060257, 800, 400, 920, 460, 18, 52500, 0, 6, 0, 0x13FC ), // 8x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060258, 850, 425, 1265, 632, 24, 57000, 0, 7, 0, 0x13FD ), // 8x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060259, 1100, 550, 1265, 632, 24, 61500, 0, 7, 0, 0x13FE ), // 8x13 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060265, 650, 325, 748, 374, 16, 38500, 0, 4, 0, 0x1404 ), // 9x7 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060266, 700, 350, 805, 402, 16, 43500, 0, 5, 0, 0x1405 ), // 9x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060267, 750, 375, 863, 431, 16, 48500, 0, 5, 0, 0x1406 ), // 9x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060268, 800, 400, 920, 460, 18, 53500, 0, 6, 0, 0x1407 ), // 9x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060269, 850, 425, 1265, 632, 24, 58500, 0, 6, 0, 0x1408 ), // 9x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060270, 1100, 550, 1265, 632, 24, 63500, 0, 7, 0, 0x1409 ), // 9x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060271, 1100, 550, 1265, 632, 24, 68500, 0, 7, 0, 0x140A ), // 9x13 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060277, 700, 350, 805, 402, 16, 42500, 0, 4, 0, 0x1410 ), // 10x7 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060278, 750, 375, 863, 431, 16, 48000, 0, 5, 0, 0x1411 ), // 10x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060279, 800, 400, 920, 460, 18, 53500, 0, 5, 0, 0x1412 ), // 10x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060280, 850, 425, 1265, 632, 24, 59000, 0, 6, 0, 0x1413 ), // 10x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060281, 1100, 550, 1265, 632, 24, 64500, 0, 6, 0, 0x1414 ), // 10x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060282, 1100, 550, 1265, 632, 24, 70000, 0, 7, 0, 0x1415 ), // 10x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060283, 1150, 575, 1323, 661, 24, 75500, 0, 7, 0, 0x1416 ), // 10x13 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060289, 750, 375, 863, 431, 16, 46500, 0, 4, 0, 0x141C ), // 11x7 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060290, 800, 400, 920, 460, 18, 52500, 0, 5, 0, 0x141D ), // 11x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060291, 850, 425, 1265, 632, 24, 58500, 0, 5, 0, 0x141E ), // 11x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060292, 1100, 550, 1265, 632, 24, 64500, 0, 6, 0, 0x141F ), // 11x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060293, 1100, 550, 1265, 632, 24, 70500, 0, 6, 0, 0x1420 ), // 11x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060294, 1150, 575, 1323, 661, 24, 76500, 0, 7, 0, 0x1421 ), // 11x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060295, 1200, 600, 1380, 690, 26, 82500, 0, 7, 0, 0x1422 ), // 11x13 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060301, 800, 400, 920, 460, 18, 50500, 0, 4, 0, 0x1428 ), // 12x7 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060302, 850, 425, 1265, 632, 24, 57000, 0, 5, 0, 0x1429 ), // 12x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060303, 1100, 550, 1265, 632, 24, 63500, 0, 5, 0, 0x142A ), // 12x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060304, 1100, 550, 1265, 632, 24, 70000, 0, 6, 0, 0x142B ), // 12x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060305, 1150, 575, 1323, 661, 24, 76500, 0, 6, 0, 0x142C ), // 12x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060306, 1200, 600, 1380, 690, 26, 83000, 0, 7, 0, 0x142D ), // 12x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060307, 1250, 625, 1438, 719, 26, 89500, 0, 7, 0, 0x142E ), // 12x13 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060314, 1100, 550, 1265, 632, 24, 61500, 0, 5, 0, 0x1435 ), // 13x8 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060315, 1100, 550, 1265, 632, 24, 68500, 0, 5, 0, 0x1436 ), // 13x9 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060316, 1150, 575, 1323, 661, 24, 75500, 0, 6, 0, 0x1437 ), // 13x10 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060317, 1200, 600, 1380, 690, 26, 82500, 0, 6, 0, 0x1438 ), // 13x11 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060318, 1250, 625, 1438, 719, 26, 89500, 0, 7, 0, 0x1439 ), // 13x12 2-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060319, 1300, 650, 1495, 747, 28, 96500, 0, 7, 0, 0x143A ) // 13x13 2-Story Customizable House + }; + + public static HousePlacementEntry[] TwoStoryFoundations{ get{ return m_TwoStoryFoundations; } } + + + + + + private static HousePlacementEntry[] m_ThreeStoryFoundations = new HousePlacementEntry[] + { + new HousePlacementEntry( typeof( HouseFoundation ), 1060272, 1150, 575, 1323, 661, 24, 73500, 0, 8, 0, 0x140B ), // 9x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060284, 1200, 600, 1380, 690, 26, 81000, 0, 8, 0, 0x1417 ), // 10x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060285, 1250, 625, 1438, 719, 26, 86500, 0, 8, 0, 0x1418 ), // 10x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060296, 1250, 625, 1438, 719, 26, 88500, 0, 8, 0, 0x1423 ), // 11x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060297, 1300, 650, 1495, 747, 28, 94500, 0, 8, 0, 0x1424 ), // 11x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060298, 1350, 675, 1553, 776, 28, 100500, 0, 9, 0, 0x1425 ), // 11x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060308, 1300, 650, 1495, 747, 28, 96000, 0, 8, 0, 0x142F ), // 12x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060309, 1350, 675, 1553, 776, 28, 102500, 0, 8, 0, 0x1430 ), // 12x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060310, 1370, 685, 1576, 788, 28, 109000, 0, 9, 0, 0x1431 ), // 12x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060311, 1370, 685, 1576, 788, 28, 115500, 0, 9, 0, 0x1432 ), // 12x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060320, 1350, 675, 1553, 776, 28, 103500, 0, 8, 0, 0x143B ), // 13x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060321, 1370, 685, 1576, 788, 28, 110500, 0, 8, 0, 0x143C ), // 13x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060322, 1370, 685, 1576, 788, 28, 117500, 0, 9, 0, 0x143D ), // 13x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060323, 2119, 1059, 2437, 1218, 42, 124500, 0, 9, 0, 0x143E ), // 13x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060324, 2119, 1059, 2437, 1218, 42, 131500, 0, 10, 0, 0x143F ), // 13x18 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060327, 1150, 575, 1323, 661, 24, 73500, 0, 5, 0, 0x1442 ), // 14x9 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060328, 1200, 600, 1380, 690, 26, 81000, 0, 6, 0, 0x1443 ), // 14x10 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060329, 1250, 625, 1438, 719, 26, 88500, 0, 6, 0, 0x1444 ), // 14x11 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060330, 1300, 650, 1495, 747, 28, 96000, 0, 7, 0, 0x1445 ), // 14x12 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060331, 1350, 675, 1553, 776, 28, 103500, 0, 7, 0, 0x1446 ), // 14x13 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060332, 1370, 685, 1576, 788, 28, 111000, 0, 8, 0, 0x1447 ), // 14x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060333, 1370, 685, 1576, 788, 28, 118500, 0, 8, 0, 0x1448 ), // 14x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060334, 2119, 1059, 2437, 1218, 42, 126000, 0, 9, 0, 0x1449 ), // 14x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060335, 2119, 1059, 2437, 1218, 42, 133500, 0, 9, 0, 0x144A ), // 14x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060336, 2119, 1059, 2437, 1218, 42, 141000, 0, 10, 0, 0x144B ), // 14x18 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060340, 1250, 625, 1438, 719, 26, 86500, 0, 6, 0, 0x144F ), // 15x10 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060341, 1300, 650, 1495, 747, 28, 94500, 0, 6, 0, 0x1450 ), // 15x11 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060342, 1350, 675, 1553, 776, 28, 102500, 0, 7, 0, 0x1451 ), // 15x12 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060343, 1370, 685, 1576, 788, 28, 110500, 0, 7, 0, 0x1452 ), // 15x13 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060344, 1370, 685, 1576, 788, 28, 118500, 0, 8, 0, 0x1453 ), // 15x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060345, 2119, 1059, 2437, 1218, 42, 126500, 0, 8, 0, 0x1454 ), // 15x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060346, 2119, 1059, 2437, 1218, 42, 134500, 0, 9, 0, 0x1455 ), // 15x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060347, 2119, 1059, 2437, 1218, 42, 142500, 0, 9, 0, 0x1456 ), // 15x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060348, 2119, 1059, 2437, 1218, 42, 150500, 0, 10, 0, 0x1457 ), // 15x18 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060353, 1350, 675, 1553, 776, 28, 100500, 0, 6, 0, 0x145C ), // 16x11 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060354, 1370, 685, 1576, 788, 28, 109000, 0, 7, 0, 0x145D ), // 16x12 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060355, 1370, 685, 1576, 788, 28, 117500, 0, 7, 0, 0x145E ), // 16x13 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060356, 2119, 1059, 2437, 1218, 42, 126000, 0, 8, 0, 0x145F ), // 16x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060357, 2119, 1059, 2437, 1218, 42, 134500, 0, 8, 0, 0x1460 ), // 16x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060358, 2119, 1059, 2437, 1218, 42, 143000, 0, 9, 0, 0x1461 ), // 16x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060359, 2119, 1059, 2437, 1218, 42, 151500, 0, 9, 0, 0x1462 ), // 16x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060360, 2119, 1059, 2437, 1218, 42, 160000, 0, 10, 0, 0x1463 ), // 16x18 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060366, 1370, 685, 1576, 788, 28, 115500, 0, 7, 0, 0x1469 ), // 17x12 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060367, 2119, 1059, 2437, 1218, 42, 124500, 0, 7, 0, 0x146A ), // 17x13 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060368, 2119, 1059, 2437, 1218, 42, 133500, 0, 8, 0, 0x146B ), // 17x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060369, 2119, 1059, 2437, 1218, 42, 142500, 0, 8, 0, 0x146C ), // 17x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060370, 2119, 1059, 2437, 1218, 42, 151500, 0, 9, 0, 0x146D ), // 17x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060371, 2119, 1059, 2437, 1218, 42, 160500, 0, 9, 0, 0x146E ), // 17x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060372, 2119, 1059, 2437, 1218, 42, 169500, 0, 10, 0, 0x146F ), // 17x18 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060379, 2119, 1059, 2437, 1218, 42, 131500, 0, 7, 0, 0x1476 ), // 18x13 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060380, 2119, 1059, 2437, 1218, 42, 141000, 0, 8, 0, 0x1477 ), // 18x14 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060381, 2119, 1059, 2437, 1218, 42, 150500, 0, 8, 0, 0x1478 ), // 18x15 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060382, 2119, 1059, 2437, 1218, 42, 160000, 0, 9, 0, 0x1479 ), // 18x16 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060383, 2119, 1059, 2437, 1218, 42, 169500, 0, 9, 0, 0x147A ), // 18x17 3-Story Customizable House + new HousePlacementEntry( typeof( HouseFoundation ), 1060384, 2119, 1059, 2437, 1218, 42, 179000, 0, 10, 0, 0x147B ) // 18x18 3-Story Customizable House + }; + + public static HousePlacementEntry[] ThreeStoryFoundations{ get{ return m_ThreeStoryFoundations; } } + } +} \ No newline at end of file diff --git a/Scripts/Multis/HouseSign.cs b/Scripts/Multis/HouseSign.cs new file mode 100644 index 000000000..1a3480222 --- /dev/null +++ b/Scripts/Multis/HouseSign.cs @@ -0,0 +1,293 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Multis; +using Server.Gumps; +using Server.ContextMenus; + +namespace Server.Multis +{ + public class HouseSign : Item + { + private BaseHouse m_Owner; + private Mobile m_OrgOwner; + + public HouseSign( BaseHouse owner ) : base( 0xBD2 ) + { + m_Owner = owner; + m_OrgOwner = m_Owner.Owner; + Movable = false; + } + + public HouseSign( Serial serial ) : base( serial ) + { + } + + public string GetName() + { + if ( Name == null ) + return "An Unnamed House"; + + return Name; + } + + public BaseHouse Owner + { + get + { + return m_Owner; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool RestrictDecay + { + get{ return ( m_Owner != null && m_Owner.RestrictDecay ); } + set{ if ( m_Owner != null ) m_Owner.RestrictDecay = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile OriginalOwner + { + get + { + return m_OrgOwner; + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Owner != null && !m_Owner.Deleted ) + m_Owner.Delete(); + } + + public override void AddNameProperty(ObjectPropertyList list) + { + list.Add( 1061638 ); // A House Sign + } + + public override bool ForceShowProperties{ get{ return ObjectPropertyList.Enabled; } } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + list.Add( 1061639, Utility.FixHtml( GetName() ) ); // Name: ~1_NAME~ + list.Add( 1061640, (m_Owner == null || m_Owner.Owner == null) ? "nobody" : m_Owner.Owner.Name ); // Owner: ~1_OWNER~ + + if ( m_Owner != null ) + { + list.Add( m_Owner.Public ? 1061641 : 1061642 ); // This House is Open to the Public : This is a Private Home + + DecayLevel level = m_Owner.DecayLevel; + + if ( level == DecayLevel.DemolitionPending ) + { + list.Add( 1062497 ); // Demolition Pending + } + else if ( level != DecayLevel.Ageless ) + { + if ( level == DecayLevel.Collapsed ) + level = DecayLevel.IDOC; + + list.Add( 1062028, String.Format( "#{0}", 1043009 + (int)level ) ); // Condition: This structure is ... + } + } + } + + public override void OnSingleClick( Mobile from ) + { + if ( m_Owner != null && BaseHouse.DecayEnabled && m_Owner.DecayPeriod != TimeSpan.Zero ) + { + string message; + + switch ( m_Owner.DecayLevel ) + { + case DecayLevel.Ageless: message = "ageless"; break; + case DecayLevel.Fairly: message = "fairly worn"; break; + case DecayLevel.Greatly: message = "greatly worn"; break; + case DecayLevel.LikeNew: message = "like new"; break; + case DecayLevel.Slightly: message = "slightly worn"; break; + case DecayLevel.Somewhat: message = "somewhat worn"; break; + default: message = "in danger of collapsing"; break; + } + + LabelTo( from, "This house is {0}.", message ); + } + + base.OnSingleClick( from ); + } + + public void ShowSign( Mobile m ) + { + if ( m_Owner != null ) + { + if ( m_Owner.IsFriend( m ) && m.AccessLevel < AccessLevel.GameMaster ) + { + m_Owner.RefreshDecay(); + m.SendLocalizedMessage( 501293 ); // Welcome back to the house, friend! + } + + if ( m_Owner.IsAosRules ) + m.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Information, m, m_Owner ) ); + else + m.SendGump( new HouseGump( m, m_Owner ) ); + } + } + + public void ClaimGump_Callback( Mobile from, bool okay, object state ) + { + if ( okay && m_Owner != null && m_Owner.Owner == null && m_Owner.DecayLevel != DecayLevel.DemolitionPending ) + { + bool canClaim = false; + + if ( m_Owner.CoOwners == null || m_Owner.CoOwners.Count == 0 ) + canClaim = m_Owner.IsFriend( from ); + else + canClaim = m_Owner.IsCoOwner( from ); + + if ( canClaim && !BaseHouse.HasAccountHouse( from ) ) + { + m_Owner.Owner = from; + m_Owner.LastTraded = DateTime.Now; + } + } + + ShowSign( from ); + } + + public override void OnDoubleClick( Mobile m ) + { + if ( m_Owner == null ) + return; + + if ( m.AccessLevel < AccessLevel.GameMaster && m_Owner.Owner == null && m_Owner.DecayLevel != DecayLevel.DemolitionPending ) + { + bool canClaim = false; + + if ( m_Owner.CoOwners == null || m_Owner.CoOwners.Count == 0 ) + canClaim = m_Owner.IsFriend( m ); + else + canClaim = m_Owner.IsCoOwner( m ); + + if ( canClaim && !BaseHouse.HasAccountHouse( m ) ) + { + /* You do not currently own any house on any shard with this account, + * and this house currently does not have an owner. If you wish, you + * may choose to claim this house and become its rightful owner. If + * you do this, it will become your Primary house and automatically + * refresh. If you claim this house, you will be unable to place + * another house or have another house transferred to you for the + * next 7 days. Do you wish to claim this house? + */ + m.SendGump( new WarningGump( 501036, 32512, 1049719, 32512, 420, 280, new WarningGumpCallback( ClaimGump_Callback ), null ) ); + } + } + + ShowSign( m ); + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + + if ( BaseHouse.NewVendorSystem && from.Alive && Owner != null && Owner.IsAosRules ) + { + if ( Owner.AreThereAvailableVendorsFor( from ) ) + list.Add( new VendorsEntry( this ) ); + + if ( Owner.VendorInventories.Count > 0 ) + list.Add( new ReclaimVendorInventoryEntry( this ) ); + } + } + + private class VendorsEntry : ContextMenuEntry + { + private HouseSign m_Sign; + + public VendorsEntry( HouseSign sign ) : base( 6211 ) + { + m_Sign = sign; + } + + public override void OnClick() + { + Mobile from = this.Owner.From; + + if ( !from.CheckAlive() || m_Sign.Deleted || m_Sign.Owner == null || !m_Sign.Owner.AreThereAvailableVendorsFor( from ) ) + return; + + if ( from.Map != m_Sign.Map || !from.InRange( m_Sign, 5 ) ) + { + from.SendLocalizedMessage( 1062429 ); // You must be within five paces of the house sign to use this option. + } + else + { + from.SendGump( new HouseGumpAOS( HouseGumpPageAOS.Vendors, from, m_Sign.Owner ) ); + } + } + } + + private class ReclaimVendorInventoryEntry : ContextMenuEntry + { + private HouseSign m_Sign; + + public ReclaimVendorInventoryEntry( HouseSign sign ) : base( 6213 ) + { + m_Sign = sign; + } + + public override void OnClick() + { + Mobile from = this.Owner.From; + + if ( m_Sign.Deleted || m_Sign.Owner == null || m_Sign.Owner.VendorInventories.Count == 0 || !from.CheckAlive() ) + return; + + if ( from.Map != m_Sign.Map || !from.InRange( m_Sign, 5 ) ) + { + from.SendLocalizedMessage( 1062429 ); // You must be within five paces of the house sign to use this option. + } + else + { + from.CloseGump( typeof( VendorInventoryGump ) ); + from.SendGump( new VendorInventoryGump( m_Sign.Owner, from ) ); + } + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Owner ); + writer.Write( m_OrgOwner ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Owner = reader.ReadItem() as BaseHouse; + m_OrgOwner = reader.ReadMobile(); + + break; + } + } + + if ( this.Name == "a house sign" ) + this.Name = null; + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/HouseTeleporter.cs b/Scripts/Multis/HouseTeleporter.cs new file mode 100644 index 000000000..4a6538c69 --- /dev/null +++ b/Scripts/Multis/HouseTeleporter.cs @@ -0,0 +1,187 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Multis; +using Server.Targeting; +using System.Collections.Generic; +using Server.ContextMenus; + +namespace Server.Items +{ + public class HouseTeleporter : Item, ISecurable + { + private Item m_Target; + private SecureLevel m_Level; + + [CommandProperty( AccessLevel.GameMaster )] + public Item Target + { + get{ return m_Target; } + set{ m_Target = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public SecureLevel Level + { + get{ return m_Level; } + set{ m_Level = value; } + } + + [Constructable] + public HouseTeleporter( int itemID ) : this( itemID, null ) + { + } + + public HouseTeleporter( int itemID, Item target ) : base( itemID ) + { + Movable = false; + + m_Level = SecureLevel.Anyone; + + m_Target = target; + } + + public bool CheckAccess( Mobile m ) + { + BaseHouse house = BaseHouse.FindHouseAt( this ); + + if ( house != null && (house.Public ? house.IsBanned( m ) : !house.HasAccess( m )) ) + return false; + + return ( house != null && house.HasSecureAccess( m, m_Level ) ); + } + + public override bool OnMoveOver( Mobile m ) + { + if ( m_Target != null && !m_Target.Deleted ) + { + if ( CheckAccess( m ) ) + { + if ( !m.Hidden || m.AccessLevel == AccessLevel.Player ) + new EffectTimer( Location, Map, 2023, 0x1F0, TimeSpan.FromSeconds( 0.4 ) ).Start(); + + new DelayTimer( this, m ).Start(); + } + else + { + m.SendLocalizedMessage( 1061637 ); // You are not allowed to access this. + } + } + + return true; + } + + public override void GetContextMenuEntries( Mobile from, List list ) + { + base.GetContextMenuEntries( from, list ); + SetSecureLevelEntry.AddTo( from, this, list ); + } + + public HouseTeleporter( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( (int) m_Level ); + + writer.Write( (Item) m_Target ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Level = (SecureLevel)reader.ReadInt(); + goto case 0; + } + case 0: + { + m_Target = reader.ReadItem(); + + if ( version < 0 ) + m_Level = SecureLevel.Anyone; + + break; + } + } + } + + private class EffectTimer : Timer + { + private Point3D m_Location; + private Map m_Map; + private int m_EffectID; + private int m_SoundID; + + public EffectTimer( Point3D p, Map map, int effectID, int soundID, TimeSpan delay ) : base( delay ) + { + m_Location = p; + m_Map = map; + m_EffectID = effectID; + m_SoundID = soundID; + } + + protected override void OnTick() + { + Effects.SendLocationParticles( EffectItem.Create( m_Location, m_Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, m_EffectID, 0 ); + + if ( m_SoundID != -1 ) + Effects.PlaySound( m_Location, m_Map, m_SoundID ); + } + } + + private class DelayTimer : Timer + { + private HouseTeleporter m_Teleporter; + private Mobile m_Mobile; + + public DelayTimer( HouseTeleporter tp, Mobile m ) : base( TimeSpan.FromSeconds( 1.0 ) ) + { + m_Teleporter = tp; + m_Mobile = m; + } + + protected override void OnTick() + { + Item target = m_Teleporter.m_Target; + + if ( target != null && !target.Deleted ) + { + Mobile m = m_Mobile; + + if ( m.Location == m_Teleporter.Location && m.Map == m_Teleporter.Map ) + { + Point3D p = target.GetWorldTop(); + Map map = target.Map; + + Server.Mobiles.BaseCreature.TeleportPets( m, p, map ); + + m.MoveToWorld( p, map ); + + if ( !m.Hidden || m.AccessLevel == AccessLevel.Player ) + { + Effects.PlaySound( target.Location, target.Map, 0x1FE ); + + Effects.SendLocationParticles( EffectItem.Create( m_Teleporter.Location, m_Teleporter.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023, 0 ); + Effects.SendLocationParticles( EffectItem.Create( target.Location, target.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023, 0 ); + + new EffectTimer( target.Location, target.Map, 2023, -1, TimeSpan.FromSeconds( 0.4 ) ).Start(); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/Houses.cs b/Scripts/Multis/Houses.cs new file mode 100644 index 000000000..9968e8f14 --- /dev/null +++ b/Scripts/Multis/Houses.cs @@ -0,0 +1,577 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Multis.Deeds; + +namespace Server.Multis +{ + public class SmallOldHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D(-3,-3,7,7 ), new Rectangle2D( -1, 4, 3, 1 ) }; + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 2, 4, 0 ); } } + + public override int DefaultPrice{ get{ return 43800; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.TwoStoryFoundations[0]; } } + + public SmallOldHouse( Mobile owner, int id ) : base( id, owner, 425, 3 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoor( 0, 3, 7, keyValue ); + + SetSign( 2, 4, 5 ); + } + + public SmallOldHouse( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() + { + switch ( ItemID ^ 0x4000 ) + { + case 0x64: return new StonePlasterHouseDeed(); + case 0x66: return new FieldStoneHouseDeed(); + case 0x68: return new SmallBrickHouseDeed(); + case 0x6A: return new WoodHouseDeed(); + case 0x6C: return new WoodPlasterHouseDeed(); + case 0x6E: + default: return new ThatchedRoofCottageDeed(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class GuildHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -7, -7, 14, 14 ), new Rectangle2D( -2, 7, 4, 1 ) }; + + public override int DefaultPrice{ get{ return 144500; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.ThreeStoryFoundations[20]; } } + public override int ConvertOffsetX{ get{ return -1; } } + public override int ConvertOffsetY{ get{ return -1; } } + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 4, 8, 0 ); } } + + public GuildHouse( Mobile owner ) : base( 0x74, owner, 1100, 8 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( -1, 6, 7, keyValue ); + + SetSign( 4, 8, 16 ); + + AddSouthDoor( -3, -1, 7 ); + AddSouthDoor( 3, -1, 7 ); + } + + public GuildHouse( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new BrickHouseDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class TwoStoryHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -7, 0, 14, 7 ), new Rectangle2D( -7, -7, 9, 7 ), new Rectangle2D( -4, 7, 4, 1 ) }; + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 2, 8, 0 ); } } + + public override int DefaultPrice{ get{ return 192400; } } + + public TwoStoryHouse( Mobile owner, int id ) : base( id, owner, 1370, 10 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( -3, 6, 7, keyValue ); + + SetSign( 2, 8, 16 ); + + AddSouthDoor( -3, 0, 7 ); + AddSouthDoor( id == 0x76 ? -2 : -3, 0, 27 ); + } + + public TwoStoryHouse( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() + { + switch( ItemID ^ 0x4000 ) + { + case 0x76: return new TwoStoryWoodPlasterHouseDeed(); + case 0x78: + default: return new TwoStoryStonePlasterHouseDeed(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class Tower : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -7, -7, 16, 14 ), new Rectangle2D( -1, 7, 4, 2 ), new Rectangle2D( -11, 0, 4, 7 ), new Rectangle2D( 9, 0, 4, 7 ) }; + + public override int DefaultPrice{ get{ return 433200; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.ThreeStoryFoundations[37]; } } + public override int ConvertOffsetY{ get{ return -1; } } + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 5, 8, 0 ); } } + + public Tower( Mobile owner ) : base( 0x7A, owner, 2119, 15 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( false, 0, 6, 6, keyValue ); + + SetSign( 5, 8, 16 ); + + AddSouthDoor( false, 3, -2, 6 ); + AddEastDoor( false, 1, 4, 26 ); + AddEastDoor( false, 1, 4, 46 ); + } + + public Tower( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new TowerDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class Keep : BaseHouse//warning: ODD shape! + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -11, -11, 7, 8 ), new Rectangle2D( -11, 5, 7, 8 ), new Rectangle2D( 6, -11, 7, 8 ), new Rectangle2D( 6, 5, 7, 8 ), new Rectangle2D( -9, -3, 5, 8 ), new Rectangle2D( 6, -3, 5, 8 ), new Rectangle2D( -4, -9, 10, 20 ), new Rectangle2D( -1, 11, 4, 1 ) }; + + public override int DefaultPrice{ get{ return 665200; } } + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 5, 13, 0 ); } } + + public Keep( Mobile owner ) : base( 0x7C, owner, 2625, 18 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( false, 0, 10, 6, keyValue ); + + SetSign( 5, 12, 16 ); + } + + public Keep( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new KeepDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class Castle : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -15, -15, 31, 31 ), new Rectangle2D( -1, 16, 4, 1 ) }; + + public override int DefaultPrice{ get{ return 1022800; } } + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 5, 17, 0 ); } } + + public Castle( Mobile owner ) : base( 0x7E, owner, 4076, 28 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( false, 0, 15, 6, keyValue ); + + SetSign( 5, 17, 16 ); + + AddSouthDoors( false, 0, 11, 6, true ); + AddSouthDoors( false, 0, 5, 6, false ); + AddSouthDoors( false, -1, -11, 6, false ); + } + + public Castle( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new CastleDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class LargePatioHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -7, -7, 15, 14 ), new Rectangle2D( -5, 7, 4, 1 ) }; + + public override int DefaultPrice{ get{ return 152800; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.ThreeStoryFoundations[29]; } } + public override int ConvertOffsetY{ get{ return -1; } } + + public override Rectangle2D[] Area { get { return AreaArray; } } + public override Point3D BaseBanLocation { get { return new Point3D( 1, 8, 0 ); } } + + public LargePatioHouse( Mobile owner ) : base( 0x8C, owner, 1100, 8 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( -4, 6, 7, keyValue ); + + SetSign( 1, 8, 16 ); + + AddEastDoor( 1, 4, 7 ); + AddEastDoor( 1, -4, 7 ); + AddSouthDoor( 4, -1, 7 ); + } + + public LargePatioHouse( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new LargePatioDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class LargeMarbleHouse : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -7, -7, 15, 14 ), new Rectangle2D( -6, 7, 6, 1 ) }; + + public override int DefaultPrice{ get{ return 192000; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.ThreeStoryFoundations[29]; } } + public override int ConvertOffsetY{ get{ return -1; } } + + public override Rectangle2D[] Area { get { return AreaArray; } } + public override Point3D BaseBanLocation { get { return new Point3D( 1, 8, 0 ); } } + + public LargeMarbleHouse( Mobile owner ) : base( 0x96, owner, 1370, 10 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( false, -4, 3, 4, keyValue ); + + SetSign( 1, 8, 11 ); + } + + public LargeMarbleHouse( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new LargeMarbleDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class SmallTower : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -3, -3, 8, 7 ), new Rectangle2D( 2, 4, 3, 1 ) }; + + public override int DefaultPrice{ get{ return 88500; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.TwoStoryFoundations[6]; } } + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 1, 4, 0 ); } } + + public SmallTower( Mobile owner ) : base( 0x98, owner, 580, 4 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoor( false, 3, 3, 6, keyValue ); + + SetSign( 1, 4, 5 ); + } + + public SmallTower( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new SmallTowerDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class LogCabin : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -3, -6, 8, 13 ) }; + + public override int DefaultPrice{ get{ return 97800; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.TwoStoryFoundations[12]; } } + + public override Rectangle2D[] Area { get { return AreaArray; } } + public override Point3D BaseBanLocation { get { return new Point3D( 5, 8, 0 ); } } + + public LogCabin( Mobile owner ) : base( 0x9A, owner, 1100, 8 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoor( 1, 4, 8, keyValue ); + + SetSign( 5, 8, 20 ); + + AddSouthDoor( 1, 0, 29 ); + } + + public LogCabin( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new LogCabinDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class SandStonePatio : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -5, -4, 12, 8 ), new Rectangle2D( -2, 4, 3, 1 ) }; + + public override int DefaultPrice{ get{ return 90900; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.TwoStoryFoundations[35]; } } + public override int ConvertOffsetY{ get{ return -1; } } + + public override Rectangle2D[] Area { get { return AreaArray; } } + public override Point3D BaseBanLocation { get { return new Point3D( 4, 6, 0 ); } } + + public SandStonePatio( Mobile owner ) : base( 0x9C, owner, 850, 6 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoor( -1, 3, 6, keyValue ); + + SetSign( 4, 6, 24 ); + } + + public SandStonePatio( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new SandstonePatioDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class TwoStoryVilla : BaseHouse + { + public static Rectangle2D[] AreaArray = new Rectangle2D[]{ new Rectangle2D( -5, -5, 11, 11 ), new Rectangle2D( 2, 6, 4, 1 ) }; + + public override int DefaultPrice{ get{ return 136500; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.TwoStoryFoundations[31]; } } + + public override Rectangle2D[] Area{ get{ return AreaArray; } } + public override Point3D BaseBanLocation{ get{ return new Point3D( 3, 8, 0 ); } } + + public TwoStoryVilla( Mobile owner ) : base( 0x9E, owner, 1100, 8 ) + { + uint keyValue = CreateKeys( owner ); + + AddSouthDoors( 3, 1, 5, keyValue ); + + SetSign( 3, 8, 24 ); + + AddEastDoor( 1, 0, 25 ); + AddSouthDoor( -3, -1, 25 ); + } + + public TwoStoryVilla( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() { return new VillaDeed(); } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } + + public class SmallShop : BaseHouse + { + public override Rectangle2D[] Area { get { return ( ItemID == 0x40A2 ? AreaArray1 : AreaArray2 ); } } + public override Point3D BaseBanLocation { get { return new Point3D( 3, 4, 0 ); } } + + public override int DefaultPrice{ get{ return 63000; } } + + public override HousePlacementEntry ConvertEntry{ get{ return HousePlacementEntry.TwoStoryFoundations[0]; } } + + public static Rectangle2D[] AreaArray1 = new Rectangle2D[]{ new Rectangle2D(-3,-3,7,7), new Rectangle2D( -1, 4, 4, 1 ) }; + public static Rectangle2D[] AreaArray2 = new Rectangle2D[]{ new Rectangle2D(-3,-3,7,7), new Rectangle2D( -2, 4, 3, 1 ) }; + + public SmallShop( Mobile owner, int id ) : base( id, owner, 425, 3 ) + { + uint keyValue = CreateKeys( owner ); + + BaseDoor door = MakeDoor( false, DoorFacing.EastCW ); + + door.Locked = true; + door.KeyValue = keyValue; + + if ( door is BaseHouseDoor ) + ((BaseHouseDoor)door).Facing = DoorFacing.EastCCW; + + AddDoor( door, -2, 0, id == 0xA2 ? 24 : 27 ); + + //AddSouthDoor( false, -2, 0, 27 - (id == 0xA2 ? 3 : 0), keyValue ); + + SetSign( 3, 4, 7 - (id == 0xA2 ? 2 : 0) ); + } + + public SmallShop( Serial serial ) : base( serial ) + { + } + + public override HouseDeed GetDeed() + { + switch ( ItemID ^ 0x4000 ) + { + case 0xA0: return new StoneWorkshopDeed(); + case 0xA2: + default: return new MarbleWorkshopDeed(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + writer.Write( (int)0 );//version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + int version = reader.ReadInt(); + } + } +} diff --git a/Scripts/Multis/MovingCrate.cs b/Scripts/Multis/MovingCrate.cs new file mode 100644 index 000000000..f8f195b62 --- /dev/null +++ b/Scripts/Multis/MovingCrate.cs @@ -0,0 +1,330 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Network; + +namespace Server.Multis +{ + public class MovingCrate : Container + { + public static readonly int MaxItemsPerSubcontainer = 20; + public static readonly int Rows = 3; + public static readonly int Columns = 5; + public static readonly int HorizontalSpacing = 25; + public static readonly int VerticalSpacing = 25; + + public override int LabelNumber{ get{ return 1061690; } } // Packing Crate + + private BaseHouse m_House; + + private Timer m_InternalizeTimer; + + public BaseHouse House + { + get{ return m_House; } + set{ m_House = value; } + } + + public override int DefaultMaxItems{ get{ return 0; } } + public override int DefaultMaxWeight{ get{ return 0; } } + + public MovingCrate( BaseHouse house ) : base( 0xE3D ) + { + Hue = 0x8A5; + Movable = false; + + m_House = house; + } + + public MovingCrate( Serial serial ) : base( serial ) + { + } + + /* + public override void AddNameProperties( ObjectPropertyList list ) + { + base.AddNameProperties( list ); + + if ( House != null && House.InternalizedVendors.Count > 0 ) + list.Add( 1061833, House.InternalizedVendors.Count.ToString() ); // This packing crate contains ~1_COUNT~ vendors/barkeepers. + } + */ + + public override void DropItem( Item dropped ) + { + // 1. Try to stack the item + foreach ( Item item in this.Items ) + { + if ( item is PackingBox ) + { + List subItems = item.Items; + + for ( int i = 0; i < subItems.Count; i++ ) + { + Item subItem = subItems[i]; + + if ( !(subItem is Container) && subItem.StackWith( null, dropped, false ) ) + return; + } + } + } + + // 2. Try to drop the item into an existing container + foreach ( Item item in this.Items ) + { + if ( item is PackingBox ) + { + Container box = (Container) item; + List subItems = box.Items; + + if ( subItems.Count < MaxItemsPerSubcontainer ) + { + box.DropItem( dropped ); + return; + } + } + } + + // 3. Drop the item into a new container + Container subContainer = new PackingBox(); + subContainer.DropItem( dropped ); + + Point3D location = GetFreeLocation(); + if ( location != Point3D.Zero ) + { + this.AddItem( subContainer ); + subContainer.Location = location; + } + else + { + base.DropItem( subContainer ); + } + } + + private Point3D GetFreeLocation() + { + bool[,] positions = new bool[Rows, Columns]; + + foreach ( Item item in this.Items ) + { + if ( item is PackingBox ) + { + int i = (item.Y - this.Bounds.Y) / VerticalSpacing; + if ( i < 0 ) + i = 0; + else if ( i >= Rows ) + i = Rows - 1; + + int j = (item.X - this.Bounds.X) / HorizontalSpacing; + if ( j < 0 ) + j = 0; + else if ( j >= Columns ) + j = Columns - 1; + + positions[i, j] = true; + } + } + + for ( int i = 0; i < Rows; i++ ) + { + for ( int j = 0; j < Columns; j++ ) + { + if ( !positions[i, j] ) + { + int x = this.Bounds.X + j * HorizontalSpacing; + int y = this.Bounds.Y + i * VerticalSpacing; + + return new Point3D( x, y, 0 ); + } + } + } + + return Point3D.Zero; + } + + public override bool IsDecoContainer + { + get{ return false; } + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + if ( m.AccessLevel < AccessLevel.GameMaster ) + { + m.SendLocalizedMessage( 1061145 ); // You cannot place items into a house moving crate. + return false; + } + + return base.CheckHold( m, item, message, checkItems, plusItems, plusWeight ); + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + return base.CheckLift( from, item, ref reject ) && House != null && !House.Deleted && House.IsOwner( from ); + } + + public override bool CheckItemUse( Mobile from, Item item ) + { + return base.CheckItemUse( from, item ) && House != null && !House.Deleted && House.IsOwner( from ); + } + + public override void OnItemRemoved( Item item ) + { + base.OnItemRemoved( item ); + + if ( this.TotalItems == 0 ) + Delete(); + } + + public void RestartTimer() + { + if ( m_InternalizeTimer == null ) + { + m_InternalizeTimer = new InternalizeTimer( this ); + m_InternalizeTimer.Start(); + } + else + { + m_InternalizeTimer.Stop(); + m_InternalizeTimer.Start(); + } + } + + public void Hide() + { + if ( m_InternalizeTimer != null ) + { + m_InternalizeTimer.Stop(); + m_InternalizeTimer = null; + } + + ArrayList toRemove = new ArrayList(); + foreach ( Item item in this.Items ) + { + if ( item is PackingBox && item.Items.Count == 0 ) + toRemove.Add( item ); + } + + foreach ( Item item in toRemove ) + { + item.Delete(); + } + + if ( this.TotalItems == 0 ) + Delete(); + else + Internalize(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( House != null && House.MovingCrate == this ) + House.MovingCrate = null; + + if ( m_InternalizeTimer != null ) + m_InternalizeTimer.Stop(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); + + writer.Write( (Item) m_House ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_House = reader.ReadItem() as BaseHouse; + + if( m_House != null ) + { + m_House.MovingCrate = this; + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( Hide ) ); + } + else + { + this.Delete(); + } + + if ( version == 0 ) + MaxItems = -1; // reset to default + } + + public class InternalizeTimer : Timer + { + private MovingCrate m_Crate; + + public InternalizeTimer( MovingCrate crate ) : base( TimeSpan.FromMinutes( 5.0 ) ) + { + m_Crate = crate; + + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Crate.Hide(); + } + } + } + + public class PackingBox : BaseContainer + { + public override int LabelNumber{ get{ return 1061690; } } // Packing Crate + + public override int DefaultMaxItems{ get{ return 0; } } + public override int DefaultMaxWeight{ get{ return 0; } } + + public PackingBox() : base( 0x9A8 ) + { + Movable = false; + } + + public PackingBox( Serial serial ) : base( serial ) + { + } + + public override void OnItemRemoved( Item item ) + { + base.OnItemRemoved( item ); + + if ( item.GetBounce() == null && this.TotalItems == 0 ) + Delete(); + } + + public override void OnItemBounceCleared( Item item ) + { + base.OnItemBounceCleared( item ); + + if ( this.TotalItems == 0 ) + Delete(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 1 ); // version + } + + public override void Deserialize(GenericReader reader) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + if ( version == 0 ) + MaxItems = -1; // reset to default + } + } +} \ No newline at end of file diff --git a/Scripts/Multis/PreviewHouse.cs b/Scripts/Multis/PreviewHouse.cs new file mode 100644 index 000000000..36adcdf3f --- /dev/null +++ b/Scripts/Multis/PreviewHouse.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections; +using Server; +using Server.Items; + +namespace Server.Multis +{ + public class PreviewHouse : BaseMulti + { + private ArrayList m_Components; + private Timer m_Timer; + + public PreviewHouse( int multiID ) : base( multiID | 0x4000 ) + { + m_Components = new ArrayList(); + + MultiComponentList mcl = this.Components; + + for ( int i = 1; i < mcl.List.Length; ++i ) + { + MultiTileEntry entry = mcl.List[i]; + + if ( entry.m_Flags == 0 ) + { + Item item = new Static( entry.m_ItemID & 0x3FFF ); + + item.MoveToWorld( new Point3D( X + entry.m_OffsetX, Y + entry.m_OffsetY, Z + entry.m_OffsetZ ), Map ); + + m_Components.Add( item ); + } + } + + m_Timer = new DecayTimer( this ); + m_Timer.Start(); + } + + public override void OnLocationChange( Point3D oldLocation ) + { + base.OnLocationChange( oldLocation ); + + if ( m_Components == null ) + return; + + int xOffset = X - oldLocation.X; + int yOffset = Y - oldLocation.Y; + int zOffset = Z - oldLocation.Z; + + for ( int i = 0; i < m_Components.Count; ++i ) + { + Item item = (Item)m_Components[i]; + + item.MoveToWorld( new Point3D( item.X + xOffset, item.Y + yOffset, item.Z + zOffset ), this.Map ); + } + } + + public override void OnMapChange() + { + base.OnMapChange(); + + if ( m_Components == null ) + return; + + for ( int i = 0; i < m_Components.Count; ++i ) + { + Item item = (Item)m_Components[i]; + + item.Map = this.Map; + } + } + + public override void OnDelete() + { + base.OnDelete(); + + if ( m_Components == null ) + return; + + for ( int i = 0; i < m_Components.Count; ++i ) + { + Item item = (Item)m_Components[i]; + + item.Delete(); + } + } + + public override void OnAfterDelete() + { + if ( m_Timer != null ) + m_Timer.Stop(); + + m_Timer = null; + + base.OnAfterDelete(); + } + + public PreviewHouse( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.WriteItemList( m_Components ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Components = reader.ReadItemList(); + + break; + } + } + + Delete(); + } + + private class DecayTimer : Timer + { + private Item m_Item; + + public DecayTimer( Item item ) : base( TimeSpan.FromSeconds( 20.0 ) ) + { + m_Item = item; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/BaseRegion.cs b/Scripts/Regions/BaseRegion.cs new file mode 100644 index 000000000..42a808112 --- /dev/null +++ b/Scripts/Regions/BaseRegion.cs @@ -0,0 +1,561 @@ +using System; +using System.Collections; +using System.Xml; +using Server; +using System.Collections.Generic; + +namespace Server.Regions +{ + public enum SpawnZLevel + { + Lowest, + Highest, + Random + } + + public class BaseRegion : Region + { + public static void Configure() + { + Region.DefaultRegionType = typeof( BaseRegion ); + } + + private string m_RuneName; + private bool m_NoLogoutDelay; + + private SpawnEntry[] m_Spawns; + private SpawnZLevel m_SpawnZLevel; + private bool m_ExcludeFromParentSpawns; + + public string RuneName{ get{ return m_RuneName; } set{ m_RuneName = value; } } + + public bool NoLogoutDelay{ get{ return m_NoLogoutDelay; } set{ m_NoLogoutDelay = value; } } + + public SpawnEntry[] Spawns + { + get{ return m_Spawns; } + set + { + if ( m_Spawns != null ) + { + for ( int i = 0; i < m_Spawns.Length; i++ ) + m_Spawns[i].Delete(); + } + + m_Spawns = value; + } + } + + public SpawnZLevel SpawnZLevel{ get{ return m_SpawnZLevel; } set{ m_SpawnZLevel = value; } } + + public bool ExcludeFromParentSpawns{ get{ return m_ExcludeFromParentSpawns; } set{ m_ExcludeFromParentSpawns = value; } } + + public override void OnUnregister() + { + base.OnUnregister(); + + this.Spawns = null; + } + + public static string GetRuneNameFor( Region region ) + { + while ( region != null ) + { + BaseRegion br = region as BaseRegion; + + if ( br != null && br.m_RuneName != null ) + return br.m_RuneName; + + region = region.Parent; + } + + return null; + } + + public override TimeSpan GetLogoutDelay( Mobile m ) + { + if ( m_NoLogoutDelay ) + { + if ( m.Aggressors.Count == 0 && m.Aggressed.Count == 0 && !m.Criminal ) + return TimeSpan.Zero; + } + + return base.GetLogoutDelay( m ); + } + + public static bool CanSpawn( Region region, params Type[] types ) + { + while ( region != null ) + { + if ( !region.AllowSpawn() ) + return false; + + BaseRegion br = region as BaseRegion; + + if ( br != null ) + { + if ( br.Spawns != null ) + { + for ( int i = 0; i < br.Spawns.Length; i++ ) + { + SpawnEntry entry = br.Spawns[i]; + + if ( entry.Definition.CanSpawn( types ) ) + return true; + } + } + + if ( br.ExcludeFromParentSpawns ) + return false; + } + + region = region.Parent; + } + + return false; + } + + public override bool AcceptsSpawnsFrom( Region region ) + { + if ( region == this || !m_ExcludeFromParentSpawns ) + return base.AcceptsSpawnsFrom( region ); + + return false; + } + + private Rectangle3D[] m_Rectangles; + private int[] m_RectangleWeights; + private int m_TotalWeight; + + private static ArrayList m_Buffer1 = new ArrayList(); + private static ArrayList m_Buffer2 = new ArrayList(); + + private void InitRectangles() + { + if ( m_Rectangles != null ) + return; + + // Test if area rectangles are overlapping, and in that case break them into smaller non overlapping rectangles + for ( int i = 0; i < this.Area.Length; i++ ) + { + m_Buffer2.Add( this.Area[i] ); + + for ( int j = 0; j < m_Buffer1.Count && m_Buffer2.Count > 0; j++ ) + { + Rectangle3D comp = (Rectangle3D) m_Buffer1[j]; + + for ( int k = m_Buffer2.Count - 1; k >= 0; k-- ) + { + Rectangle3D rect = (Rectangle3D) m_Buffer2[k]; + + int l1 = rect.Start.X, r1 = rect.End.X, t1 = rect.Start.Y, b1 = rect.End.Y; + int l2 = comp.Start.X, r2 = comp.End.X, t2 = comp.Start.Y, b2 = comp.End.Y; + + if ( l1 < r2 && r1 > l2 && t1 < b2 && b1 > t2 ) + { + m_Buffer2.RemoveAt( k ); + + int sz = rect.Start.Z; + int ez = rect.End.X; + + if ( l1 < l2 ) + { + m_Buffer2.Add( new Rectangle3D( new Point3D( l1, t1, sz ), new Point3D( l2, b1, ez ) ) ); + } + + if ( r1 > r2 ) + { + m_Buffer2.Add( new Rectangle3D( new Point3D( r2, t1, sz ), new Point3D( r1, b1, ez ) ) ); + } + + if ( t1 < t2 ) + { + m_Buffer2.Add( new Rectangle3D( new Point3D( Math.Max( l1, l2 ), t1, sz ), new Point3D( Math.Min( r1, r2 ), t2, ez ) ) ); + } + + if ( b1 > b2 ) + { + m_Buffer2.Add( new Rectangle3D( new Point3D( Math.Max( l1, l2 ), b2, sz ), new Point3D( Math.Min( r1, r2 ), b1, ez ) ) ); + } + } + } + } + + m_Buffer1.AddRange( m_Buffer2 ); + m_Buffer2.Clear(); + } + + m_Rectangles = (Rectangle3D[]) m_Buffer1.ToArray( typeof( Rectangle3D ) ); + m_Buffer1.Clear(); + + m_RectangleWeights = new int[m_Rectangles.Length]; + for ( int i = 0; i < m_Rectangles.Length; i++ ) + { + Rectangle3D rect = m_Rectangles[i]; + int weight = rect.Width * rect.Height; + + m_RectangleWeights[i] = weight; + m_TotalWeight += weight; + } + } + + public Point3D RandomSpawnLocation( int spawnHeight, bool land, bool water, Point3D home, int range ) + { + Map map = this.Map; + + if ( map == Map.Internal ) + return Point3D.Zero; + + InitRectangles(); + + if ( m_TotalWeight <= 0 ) + return Point3D.Zero; + + for ( int i = 0; i < 10; i++ ) // Try 10 times + { + int x, y, minZ, maxZ; + + if ( home == Point3D.Zero ) + { + int rand = Utility.Random( m_TotalWeight ); + + x = int.MinValue; y = int.MinValue; + minZ = int.MaxValue; maxZ = int.MinValue; + for ( int j = 0; j < m_RectangleWeights.Length; j++ ) + { + int curWeight = m_RectangleWeights[j]; + + if ( rand < curWeight ) + { + Rectangle3D rect = m_Rectangles[j]; + + x = rect.Start.X + rand % rect.Width; + y = rect.Start.Y + rand / rect.Width; + + minZ = rect.Start.Z; + maxZ = rect.End.Z; + + break; + } + + rand -= curWeight; + } + } + else + { + x = Utility.RandomMinMax( home.X - range, home.X + range ); + y = Utility.RandomMinMax( home.Y - range, home.Y + range ); + + minZ = int.MaxValue; maxZ = int.MinValue; + for ( int j = 0; j < this.Area.Length; j++ ) + { + Rectangle3D rect = this.Area[j]; + + if ( x >= rect.Start.X && x < rect.End.X && y >= rect.Start.Y && y < rect.End.Y ) + { + minZ = rect.Start.Z; + maxZ = rect.End.Z; + break; + } + } + + if ( minZ == int.MaxValue ) + continue; + } + + if ( x < 0 || y < 0 || x >= map.Width || y >= map.Height ) + continue; + + + Tile lt = map.Tiles.GetLandTile( x, y ); + + int ltLowZ = 0, ltAvgZ = 0, ltTopZ = 0; + map.GetAverageZ( x, y, ref ltLowZ, ref ltAvgZ, ref ltTopZ ); + + TileFlag ltFlags = TileData.LandTable[lt.ID & 0x3FFF].Flags; + bool ltImpassable = ( (ltFlags & TileFlag.Impassable) != 0 ); + + if ( !lt.Ignored && ltAvgZ >= minZ && ltAvgZ < maxZ ) + { + if ( (ltFlags & TileFlag.Wet) != 0 ) + { + if ( water ) + m_Buffer1.Add( ltAvgZ ); + } + else if ( land && !ltImpassable ) + { + m_Buffer1.Add( ltAvgZ ); + } + } + + + Tile[] staticTiles = map.Tiles.GetStaticTiles( x, y, true ); + + for ( int j = 0; j < staticTiles.Length; j++ ) + { + Tile tile = staticTiles[j]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + int tileZ = tile.Z + id.CalcHeight; + + if ( tileZ >= minZ && tileZ < maxZ ) + { + if ( (id.Flags & TileFlag.Wet) != 0 ) + { + if ( water ) + m_Buffer1.Add( tileZ ); + } + else if ( land && id.Surface && !id.Impassable ) + { + m_Buffer1.Add( tileZ ); + } + } + } + + + Sector sector = map.GetSector( x, y ); + + for ( int j = 0; j < sector.Items.Count; j++ ) + { + Item item = sector.Items[j]; + + if ( item.ItemID < 0x4000 && item.AtWorldPoint( x, y ) ) + { + m_Buffer2.Add( item ); + + if ( !item.Movable ) + { + ItemData id = item.ItemData; + int itemZ = item.Z + id.CalcHeight; + + if ( itemZ >= minZ && itemZ < maxZ ) + { + if ( (id.Flags & TileFlag.Wet) != 0 ) + { + if ( water ) + m_Buffer1.Add( itemZ ); + } + else if ( land && id.Surface && !id.Impassable ) + { + m_Buffer1.Add( itemZ ); + } + } + } + } + } + + + if ( m_Buffer1.Count == 0 ) + { + m_Buffer1.Clear(); + m_Buffer2.Clear(); + continue; + } + + int z; + switch ( m_SpawnZLevel ) + { + case SpawnZLevel.Lowest: + { + z = int.MaxValue; + + for ( int j = 0; j < m_Buffer1.Count; j++ ) + { + int l = (int) m_Buffer1[j]; + + if ( l < z ) + z = l; + } + + break; + } + case SpawnZLevel.Highest: + { + z = int.MinValue; + + for ( int j = 0; j < m_Buffer1.Count; j++ ) + { + int l = (int) m_Buffer1[j]; + + if ( l > z ) + z = l; + } + + break; + } + default: // SpawnZLevel.Random + { + int index = Utility.Random( m_Buffer1.Count ); + z = (int) m_Buffer1[index]; + + break; + } + } + + m_Buffer1.Clear(); + + + if ( !Region.Find( new Point3D( x, y, z ), map ).AcceptsSpawnsFrom( this ) ) + { + m_Buffer2.Clear(); + continue; + } + + + int top = z + spawnHeight; + + bool ok = true; + for ( int j = 0; j < m_Buffer2.Count; j++ ) + { + Item item = (Item) m_Buffer2[j]; + ItemData id = item.ItemData; + + if ( ( id.Surface || id.Impassable ) && item.Z + id.CalcHeight > z && item.Z < top ) + { + ok = false; + break; + } + } + + m_Buffer2.Clear(); + + if ( !ok ) + continue; + + + if ( ltImpassable && ltAvgZ > z && ltLowZ < top ) + continue; + + + for ( int j = 0; j < staticTiles.Length; j++ ) + { + Tile tile = staticTiles[j]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + if ( ( id.Surface || id.Impassable ) && tile.Z + id.CalcHeight > z && tile.Z < top ) + { + ok = false; + break; + } + } + + if ( !ok ) + continue; + + + for ( int j = 0; j < sector.Mobiles.Count; j++ ) + { + Mobile m = (Mobile) sector.Mobiles[j]; + + if ( m.X == x && m.Y == y && ( m.AccessLevel == AccessLevel.Player || !m.Hidden ) ) + { + if ( m.Z + 16 > z && m.Z < top ) + { + ok = false; + break; + } + } + } + + + if ( ok ) + return new Point3D( x, y, z ); + } + + return Point3D.Zero; + } + + public override string ToString() + { + if ( this.Name != null ) + return this.Name; + else if ( this.RuneName != null ) + return this.RuneName; + else + return this.GetType().Name; + } + + public BaseRegion( string name, Map map, int priority, params Rectangle2D[] area ) : base( name, map, priority, area ) + { + } + + public BaseRegion( string name, Map map, int priority, params Rectangle3D[] area ) : base( name, map, priority, area ) + { + } + + public BaseRegion( string name, Map map, Region parent, params Rectangle2D[] area ) : base( name, map, parent, area ) + { + } + + public BaseRegion( string name, Map map, Region parent, params Rectangle3D[] area ) : base( name, map, parent, area ) + { + } + + public BaseRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + ReadString( xml["rune"], "name", ref m_RuneName, false ); + + bool logoutDelayActive = true; + ReadBoolean( xml["logoutDelay"], "active", ref logoutDelayActive, false ); + m_NoLogoutDelay = !logoutDelayActive; + + + XmlElement spawning = xml["spawning"]; + if ( spawning != null ) + { + ReadBoolean( spawning, "excludeFromParent", ref m_ExcludeFromParentSpawns, false ); + + object zLevel = SpawnZLevel.Lowest; + ReadEnum( spawning, "zLevel", typeof( SpawnZLevel ), ref zLevel, false ); + m_SpawnZLevel = (SpawnZLevel) zLevel; + + + List list = new List(); + + foreach ( XmlNode node in spawning.ChildNodes ) + { + XmlElement el = node as XmlElement; + + if ( el != null ) + { + SpawnDefinition def = SpawnDefinition.GetSpawnDefinition( el ); + if ( def == null ) + continue; + + int id = 0; + if ( !ReadInt32( el, "id", ref id, true ) ) + continue; + + int amount = 0; + if ( !ReadInt32( el, "amount", ref amount, true ) ) + continue; + + TimeSpan minSpawnTime = SpawnEntry.DefaultMinSpawnTime; + ReadTimeSpan( el, "minSpawnTime", ref minSpawnTime, false ); + + TimeSpan maxSpawnTime = SpawnEntry.DefaultMaxSpawnTime; + ReadTimeSpan( el, "maxSpawnTime", ref maxSpawnTime, false ); + + Point3D home = Point3D.Zero; + int range = 0; + + XmlElement homeEl = el["home"]; + if ( ReadPoint3D( homeEl, map, ref home, false ) ) + ReadInt32( homeEl, "range", ref range, false ); + + object oDir = SpawnEntry.InvalidDirection; + ReadEnum( el["direction"], "value" , typeof( Direction ), ref oDir, false ); + Direction dir = (Direction) oDir; + + SpawnEntry entry = new SpawnEntry( id, this, home, range, dir, def, amount, minSpawnTime, maxSpawnTime ); + list.Add( entry ); + } + } + + if ( list.Count > 0 ) + { + m_Spawns = list.ToArray(); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/DungeonRegion.cs b/Scripts/Regions/DungeonRegion.cs new file mode 100644 index 000000000..82b3c11b4 --- /dev/null +++ b/Scripts/Regions/DungeonRegion.cs @@ -0,0 +1,52 @@ +using System; +using System.Xml; +using Server; +using Server.Mobiles; +using Server.Gumps; + +namespace Server.Regions +{ + public class DungeonRegion : BaseRegion + { + private Point3D m_EntranceLocation; + private Map m_EntranceMap; + + public Point3D EntranceLocation{ get{ return m_EntranceLocation; } set{ m_EntranceLocation = value; } } + public Map EntranceMap{ get{ return m_EntranceMap; } set{ m_EntranceMap = value; } } + + public DungeonRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + XmlElement entrEl = xml["entrance"]; + + Map entrMap = map; + ReadMap( entrEl, "map", ref entrMap, false ); + + if ( ReadPoint3D( entrEl, entrMap, ref m_EntranceLocation, false ) ) + m_EntranceMap = entrMap; + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return false; + } + + public override void OnEnter( Mobile m ) + { + if ( m is PlayerMobile && ((PlayerMobile)m).Young ) + m.SendGump( new YoungDungeonWarning() ); + } + + public override void AlterLightLevel( Mobile m, ref int global, ref int personal ) + { + global = LightCycle.DungeonLevel; + } + + public override bool CanUseStuckMenu( Mobile m ) + { + if ( this.Map == Map.Felucca ) + return false; + + return base.CanUseStuckMenu( m ); + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/GreenAcres.cs b/Scripts/Regions/GreenAcres.cs new file mode 100644 index 000000000..fc732b8ed --- /dev/null +++ b/Scripts/Regions/GreenAcres.cs @@ -0,0 +1,40 @@ +using System; +using System.Xml; +using Server; +using Server.Mobiles; +using Server.Spells; +using Server.Spells.Seventh; +using Server.Spells.Fourth; +using Server.Spells.Sixth; +using Server.Spells.Chivalry; + +namespace Server.Regions +{ + public class GreenAcres : BaseRegion + { + public GreenAcres( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + if ( from.AccessLevel == AccessLevel.Player ) + return false; + else + return base.AllowHousing( from, p ); + } + + public override bool OnBeginSpellCast( Mobile m, ISpell s ) + { + if ( ( s is GateTravelSpell || s is RecallSpell || s is MarkSpell || s is SacredJourneySpell ) && m.AccessLevel == AccessLevel.Player ) + { + m.SendMessage( "You cannot cast that spell here." ); + return false; + } + else + { + return base.OnBeginSpellCast( m, s ); + } + } + } +} diff --git a/Scripts/Regions/GuardedRegion.cs b/Scripts/Regions/GuardedRegion.cs new file mode 100644 index 000000000..7d2241937 --- /dev/null +++ b/Scripts/Regions/GuardedRegion.cs @@ -0,0 +1,371 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using Server; +using Server.Commands; +using Server.Mobiles; +using Server.Spells; + +namespace Server.Regions +{ + public class GuardedRegion : BaseRegion + { + private static object[] m_GuardParams = new object[1]; + private Type m_GuardType; + private bool m_Disabled; + + public bool Disabled{ get{ return m_Disabled; } set{ m_Disabled = value; } } + + public virtual bool IsDisabled() + { + return m_Disabled; + } + + public static void Initialize() + { + CommandSystem.Register( "CheckGuarded", AccessLevel.GameMaster, new CommandEventHandler( CheckGuarded_OnCommand ) ); + CommandSystem.Register( "SetGuarded", AccessLevel.Administrator, new CommandEventHandler( SetGuarded_OnCommand ) ); + CommandSystem.Register( "ToggleGuarded", AccessLevel.Administrator, new CommandEventHandler( ToggleGuarded_OnCommand ) ); + } + + [Usage( "CheckGuarded" )] + [Description( "Returns a value indicating if the current region is guarded or not." )] + private static void CheckGuarded_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + GuardedRegion reg = (GuardedRegion) from.Region.GetRegion( typeof( GuardedRegion ) ); + + if ( reg == null ) + from.SendMessage( "You are not in a guardable region." ); + else if ( reg.Disabled ) + from.SendMessage( "The guards in this region have been disabled." ); + else + from.SendMessage( "This region is actively guarded." ); + } + + [Usage( "SetGuarded " )] + [Description( "Enables or disables guards for the current region." )] + private static void SetGuarded_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + + if ( e.Length == 1 ) + { + GuardedRegion reg = (GuardedRegion) from.Region.GetRegion( typeof( GuardedRegion ) ); + + if ( reg == null ) + { + from.SendMessage( "You are not in a guardable region." ); + } + else + { + reg.Disabled = !e.GetBoolean( 0 ); + + if ( reg.Disabled ) + from.SendMessage( "The guards in this region have been disabled." ); + else + from.SendMessage( "The guards in this region have been enabled." ); + } + } + else + { + from.SendMessage( "Format: SetGuarded " ); + } + } + + [Usage( "ToggleGuarded" )] + [Description( "Toggles the state of guards for the current region." )] + private static void ToggleGuarded_OnCommand( CommandEventArgs e ) + { + Mobile from = e.Mobile; + GuardedRegion reg = (GuardedRegion) from.Region.GetRegion( typeof( GuardedRegion ) ); + + if ( reg == null ) + { + from.SendMessage( "You are not in a guardable region." ); + } + else + { + reg.Disabled = !reg.Disabled; + + if ( reg.Disabled ) + from.SendMessage( "The guards in this region have been disabled." ); + else + from.SendMessage( "The guards in this region have been enabled." ); + } + } + + public static GuardedRegion Disable( GuardedRegion reg ) + { + reg.Disabled = true; + return reg; + } + + public virtual bool AllowReds{ get{ return Core.AOS; } } + + public virtual bool CheckVendorAccess( BaseVendor vendor, Mobile from ) + { + if ( from.AccessLevel >= AccessLevel.GameMaster || IsDisabled() ) + return true; + + return ( from.Kills < 5 ); + } + + public virtual Type DefaultGuardType + { + get + { + if ( this.Map == Map.Ilshenar || this.Map == Map.Malas ) + return typeof( ArcherGuard ); + else + return typeof( WarriorGuard ); + } + } + + public GuardedRegion( string name, Map map, int priority, params Rectangle3D[] area ) : base( name, map, priority, area ) + { + m_GuardType = DefaultGuardType; + } + + public GuardedRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + XmlElement el = xml["guards"]; + + if ( ReadType( el, "type", ref m_GuardType, false ) ) + { + if ( !typeof( Mobile ).IsAssignableFrom( m_GuardType ) ) + { + Console.WriteLine( "Invalid guard type for region '{0}'", this ); + m_GuardType = DefaultGuardType; + } + } + else + { + m_GuardType = DefaultGuardType; + } + + bool disabled = false; + if ( ReadBoolean( el, "disabled", ref disabled, false ) ) + this.Disabled = disabled; + } + + public override bool OnBeginSpellCast( Mobile m, ISpell s ) + { + if ( !IsDisabled() && !s.OnCastInTown( this ) ) + { + m.SendLocalizedMessage( 500946 ); // You cannot cast this in town! + return false; + } + + return base.OnBeginSpellCast( m, s ); + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return false; + } + + public override void MakeGuard( Mobile focus ) + { + BaseGuard useGuard = null; + + foreach ( Mobile m in focus.GetMobilesInRange( 8 ) ) + { + if ( m is BaseGuard ) + { + BaseGuard g = (BaseGuard)m; + + if ( g.Focus == null ) // idling + { + useGuard = g; + break; + } + } + } + + if ( useGuard == null ) + { + m_GuardParams[0] = focus; + + try { Activator.CreateInstance( m_GuardType, m_GuardParams ); } catch {} + } + else + useGuard.Focus = focus; + } + + public override void OnEnter( Mobile m ) + { + if ( IsDisabled() ) + return; + + if ( !AllowReds && m.Kills >= 5 ) + CheckGuardCandidate( m ); + } + + public override void OnExit( Mobile m ) + { + if ( IsDisabled() ) + return; + } + + public override void OnSpeech( SpeechEventArgs args ) + { + base.OnSpeech( args ); + + if ( IsDisabled() ) + return; + + if ( args.Mobile.Alive && args.HasKeyword( 0x0007 ) ) // *guards* + CallGuards( args.Mobile.Location ); + } + + public override void OnAggressed( Mobile aggressor, Mobile aggressed, bool criminal ) + { + base.OnAggressed( aggressor, aggressed, criminal ); + + if ( !IsDisabled() && aggressor != aggressed && criminal ) + CheckGuardCandidate( aggressor ); + } + + public override void OnGotBeneficialAction( Mobile helper, Mobile helped ) + { + base.OnGotBeneficialAction( helper, helped ); + + if ( IsDisabled() ) + return; + + int noto = Notoriety.Compute( helper, helped ); + + if ( helper != helped && (noto == Notoriety.Criminal || noto == Notoriety.Murderer) ) + CheckGuardCandidate( helper ); + } + + public override void OnCriminalAction( Mobile m, bool message ) + { + base.OnCriminalAction( m, message ); + + if ( !IsDisabled() ) + CheckGuardCandidate( m ); + } + + private Dictionary m_GuardCandidates = new Dictionary(); + + public void CheckGuardCandidate( Mobile m ) + { + if ( IsDisabled() ) + return; + + if ( IsGuardCandidate( m ) ) + { + GuardTimer timer = null; + m_GuardCandidates.TryGetValue( m, out timer ); + + if ( timer == null ) + { + timer = new GuardTimer( m, m_GuardCandidates ); + timer.Start(); + + m_GuardCandidates[m] = timer; + m.SendLocalizedMessage( 502275 ); // Guards can now be called on you! + + Map map = m.Map; + + if ( map != null ) + { + Mobile fakeCall = null; + double prio = 0.0; + + foreach ( Mobile v in m.GetMobilesInRange( 8 ) ) + { + if( !v.Player && v != m && !IsGuardCandidate( v ) && ((v is BaseCreature)? ((BaseCreature)v).IsHumanInTown() : (v.Body.IsHuman && v.Region.IsPartOf( this ))) ) + { + double dist = m.GetDistanceToSqrt( v ); + + if ( fakeCall == null || dist < prio ) + { + fakeCall = v; + prio = dist; + } + } + } + + if ( fakeCall != null ) + { + fakeCall.Say( Utility.RandomList( 1007037, 501603, 1013037, 1013038, 1013039, 1013041, 1013042, 1013043, 1013052 ) ); + MakeGuard( m ); + timer.Stop(); + m_GuardCandidates.Remove( m ); + m.SendLocalizedMessage( 502276 ); // Guards can no longer be called on you. + } + } + } + else + { + timer.Stop(); + timer.Start(); + } + } + } + + public void CallGuards( Point3D p ) + { + if ( IsDisabled() ) + return; + + IPooledEnumerable eable = Map.GetMobilesInRange( p, 14 ); + + foreach ( Mobile m in eable ) + { + if ( IsGuardCandidate( m ) && ( ( !AllowReds && m.Kills >= 5 && m.Region.IsPartOf( this ) ) || m_GuardCandidates.ContainsKey( m ) ) ) + { + GuardTimer timer = null; + m_GuardCandidates.TryGetValue( m, out timer ); + + if ( timer != null ) + { + timer.Stop(); + m_GuardCandidates.Remove( m ); + } + + MakeGuard( m ); + m.SendLocalizedMessage( 502276 ); // Guards can no longer be called on you. + break; + } + } + + eable.Free(); + } + + public bool IsGuardCandidate( Mobile m ) + { + if ( m is BaseGuard || !m.Alive || m.AccessLevel > AccessLevel.Player || m.Blessed || IsDisabled() ) + return false; + + return (!AllowReds && m.Kills >= 5) || m.Criminal; + } + + private class GuardTimer : Timer + { + private Mobile m_Mobile; + private Dictionary m_Table; + + public GuardTimer( Mobile m, Dictionary table ) : base( TimeSpan.FromSeconds( 15.0 ) ) + { + Priority = TimerPriority.TwoFiftyMS; + + m_Mobile = m; + m_Table = table; + } + + protected override void OnTick() + { + if ( m_Table.ContainsKey( m_Mobile ) ) + { + m_Table.Remove( m_Mobile ); + m_Mobile.SendLocalizedMessage( 502276 ); // Guards can no longer be called on you. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/HouseRegion.cs b/Scripts/Regions/HouseRegion.cs new file mode 100644 index 000000000..452b3eff3 --- /dev/null +++ b/Scripts/Regions/HouseRegion.cs @@ -0,0 +1,393 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Items; +using Server.Multis; +using Server.Spells; +using Server.Spells.Sixth; +using Server.Guilds; +using Server.Gumps; + +namespace Server.Regions +{ + public class HouseRegion : BaseRegion + { + public static readonly int HousePriority = Region.DefaultPriority + 1; + + private BaseHouse m_House; + + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( OnLogin ); + } + + public static void OnLogin( LoginEventArgs e ) + { + BaseHouse house = BaseHouse.FindHouseAt( e.Mobile ); + + if ( house != null && !house.Public && !house.IsFriend( e.Mobile ) ) + e.Mobile.Location = house.BanLocation; + } + + public HouseRegion( BaseHouse house ) : base( null, house.Map, HousePriority, GetArea( house ) ) + { + m_House = house; + + Point3D ban = house.RelativeBanLocation; + + this.GoLocation = new Point3D( house.X + ban.X, house.Y + ban.Y, house.Z + ban.Z ); + } + + private static Rectangle3D[] GetArea( BaseHouse house ) + { + int x = house.X; + int y = house.Y; + int z = house.Z; + + Rectangle2D[] houseArea = house.Area; + Rectangle3D[] area = new Rectangle3D[houseArea.Length]; + + for ( int i = 0; i < area.Length; i++ ) + { + Rectangle2D rect = houseArea[i]; + area[i] = Region.ConvertTo3D( new Rectangle2D( x + rect.Start.X, y + rect.Start.Y, rect.Width, rect.Height ) ); + } + + return area; + } + + public override bool SendInaccessibleMessage( Item item, Mobile from ) + { + if ( item is Container ) + item.SendLocalizedMessageTo( from, 501647 ); // That is secure. + else + item.SendLocalizedMessageTo( from, 1061637 ); // You are not allowed to access this. + + return true; + } + + public override bool CheckAccessibility( Item item, Mobile from ) + { + return m_House.CheckAccessibility( item, from ); + } + + private bool m_Recursion; + + // Use OnLocationChanged instead of OnEnter because it can be that we enter a house region even though we're not actually inside the house + public override void OnLocationChanged( Mobile m, Point3D oldLocation ) + { + if ( m_Recursion ) + return; + + base.OnLocationChanged( m, oldLocation ); + + m_Recursion = true; + + if ( m is BaseCreature && ((BaseCreature)m).NoHouseRestrictions ) + { + } + else if ( m is BaseCreature && ((BaseCreature)m).IsHouseSummonable && (BaseCreature.Summoning || m_House.IsInside( oldLocation, 16 )) ) + { + } + else if ( (m_House.Public || !m_House.IsAosRules) && m_House.IsBanned( m ) && m_House.IsInside( m ) ) + { + m.Location = m_House.BanLocation; + + if( !Core.SE ) + m.SendLocalizedMessage( 501284 ); // You may not enter. + } + else if ( m_House.IsAosRules && !m_House.Public && !m_House.HasAccess( m ) && m_House.IsInside( m ) ) + { + m.Location = m_House.BanLocation; + + if( !Core.SE ) + m.SendLocalizedMessage( 501284 ); // You may not enter. + } + else if ( m_House.IsCombatRestricted( m ) && m_House.IsInside( m ) && !m_House.IsInside( oldLocation, 16 ) ) + { + m.Location = m_House.BanLocation; + m.SendLocalizedMessage( 1061637 ); // You are not allowed to access this. + } + else if ( m_House is HouseFoundation ) + { + HouseFoundation foundation = (HouseFoundation)m_House; + + if ( foundation.Customizer != null && foundation.Customizer != m && m_House.IsInside( m ) ) + m.Location = m_House.BanLocation; + } + + if ( m_House.InternalizedVendors.Count > 0 && m_House.IsInside( m ) && !m_House.IsInside( oldLocation, 16 ) && m_House.IsOwner( m ) && m.Alive && !m.HasGump( typeof( NoticeGump ) ) ) + { + /* This house has been customized recently, and vendors that work out of this + * house have been temporarily relocated. You must now put your vendors back to work. + * To do this, walk to a location inside the house where you wish to station + * your vendor, then activate the context-sensitive menu on your avatar and + * select "Get Vendor". + */ + m.SendGump( new NoticeGump( 1060635, 30720, 1061826, 32512, 320, 180, null, null ) ); + } + + m_Recursion = false; + } + + public override bool OnMoveInto( Mobile from, Direction d, Point3D newLocation, Point3D oldLocation ) + { + if ( !base.OnMoveInto( from, d, newLocation, oldLocation ) ) + return false; + + if ( from is BaseCreature && ((BaseCreature)from).NoHouseRestrictions ) + { + } + else if ( from is BaseCreature && ((BaseCreature)from).IsHouseSummonable && (BaseCreature.Summoning || m_House.IsInside( oldLocation, 16 )) ) + { + } + else if ( (m_House.Public || !m_House.IsAosRules) && m_House.IsBanned( from ) && m_House.IsInside( newLocation, 16 ) ) + { + from.Location = m_House.BanLocation; + + if( !Core.SE ) + from.SendLocalizedMessage( 501284 ); // You may not enter. + + return false; + } + else if ( m_House.IsAosRules && !m_House.Public && !m_House.HasAccess( from ) && m_House.IsInside( newLocation, 16 ) ) + { + if( !Core.SE ) + from.SendLocalizedMessage( 501284 ); // You may not enter. + + return false; + } + else if ( m_House.IsCombatRestricted( from ) && !m_House.IsInside( oldLocation, 16 ) && m_House.IsInside( newLocation, 16 ) ) + { + from.SendLocalizedMessage( 1061637 ); // You are not allowed to access this. + return false; + } + else if ( m_House is HouseFoundation ) + { + HouseFoundation foundation = (HouseFoundation)m_House; + + if ( foundation.Customizer != null && foundation.Customizer != from && m_House.IsInside( newLocation, 16 ) ) + return false; + } + + if ( m_House.InternalizedVendors.Count > 0 && m_House.IsInside( from ) && !m_House.IsInside( oldLocation, 16 ) && m_House.IsOwner( from ) && from.Alive && !from.HasGump( typeof( NoticeGump ) ) ) + { + /* This house has been customized recently, and vendors that work out of this + * house have been temporarily relocated. You must now put your vendors back to work. + * To do this, walk to a location inside the house where you wish to station + * your vendor, then activate the context-sensitive menu on your avatar and + * select "Get Vendor". + */ + from.SendGump( new NoticeGump( 1060635, 30720, 1061826, 32512, 320, 180, null, null ) ); + } + + return true; + } + + public override bool OnDecay( Item item ) + { + if ( (m_House.IsLockedDown( item ) || m_House.IsSecure( item )) && m_House.IsInside( item ) ) + return false; + else + return base.OnDecay(item ); + } + + public static TimeSpan CombatHeatDelay = TimeSpan.FromSeconds( 30.0 ); + + public override TimeSpan GetLogoutDelay( Mobile m ) + { + if ( m_House.IsFriend( m ) && m_House.IsInside( m ) ) + { + for ( int i = 0; i < m.Aggressed.Count; ++i ) + { + AggressorInfo info = m.Aggressed[i]; + + if ( info.Defender.Player && (DateTime.Now - info.LastCombatTime) < CombatHeatDelay ) + return base.GetLogoutDelay( m ); + } + + return TimeSpan.Zero; + } + + return base.GetLogoutDelay( m ); + } + + public override void OnSpeech( SpeechEventArgs e ) + { + base.OnSpeech( e ); + + Mobile from = e.Mobile; + + if ( !from.Alive || !m_House.IsInside( from ) || !m_House.IsActive ) + return; + + bool isOwner = m_House.IsOwner( from ); + bool isCoOwner = isOwner || m_House.IsCoOwner( from ); + bool isFriend = isCoOwner || m_House.IsFriend( from ); + + if ( !isFriend ) + return; + + if ( e.HasKeyword( 0x33 ) ) // remove thyself + { + if ( isFriend ) + { + from.SendLocalizedMessage( 501326 ); // Target the individual to eject from this house. + from.Target = new HouseKickTarget( m_House ); + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + else if ( e.HasKeyword( 0x34 ) ) // I ban thee + { + if ( !isFriend ) + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + else if ( !m_House.Public && m_House.IsAosRules ) + { + from.SendLocalizedMessage( 1062521 ); // You cannot ban someone from a private house. Revoke their access instead. + } + else + { + from.SendLocalizedMessage( 501325 ); // Target the individual to ban from this house. + from.Target = new HouseBanTarget( true, m_House ); + } + } + else if ( e.HasKeyword( 0x23 ) ) // I wish to lock this down + { + if ( isCoOwner ) + { + from.SendLocalizedMessage( 502097 ); // Lock what down? + from.Target = new LockdownTarget( false, m_House ); + } + else if ( isFriend ) + { + from.SendLocalizedMessage( 1010587 ); // You are not a co-owner of this house. + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + else if ( e.HasKeyword( 0x24 ) ) // I wish to release this + { + if ( isCoOwner ) + { + from.SendLocalizedMessage( 502100 ); // Choose the item you wish to release + from.Target = new LockdownTarget( true, m_House ); + } + else if ( isFriend ) + { + from.SendLocalizedMessage( 1010587 ); // You are not a co-owner of this house. + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + else if ( e.HasKeyword( 0x25 ) ) // I wish to secure this + { + if ( isOwner ) + { + from.SendLocalizedMessage( 502103 ); // Choose the item you wish to secure + from.Target = new SecureTarget( false, m_House ); + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + else if ( e.HasKeyword( 0x26 ) ) // I wish to unsecure this + { + if ( isOwner ) + { + from.SendLocalizedMessage( 502106 ); // Choose the item you wish to unsecure + from.Target = new SecureTarget( true, m_House ); + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + else if ( e.HasKeyword( 0x27 ) ) // I wish to place a strong box + { + if ( isOwner ) + { + from.SendLocalizedMessage( 502109 ); // Owners do not get a strongbox of their own. + } + else if ( isCoOwner ) + { + m_House.AddStrongBox( from ); + } + else if ( isFriend ) + { + from.SendLocalizedMessage( 1010587 ); // You are not a co-owner of this house. + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + else if ( e.HasKeyword( 0x28 ) ) + { + if ( isCoOwner ) + { + m_House.AddTrashBarrel( from ); + } + else if ( isFriend ) + { + from.SendLocalizedMessage( 1010587 ); // You are not a co-owner of this house. + } + else + { + from.SendLocalizedMessage( 502094 ); // You must be in your house to do this. + } + } + } + + public override bool OnDoubleClick( Mobile from, object o ) + { + if ( o is Container ) + { + Container c = (Container)o; + + SecureAccessResult res = m_House.CheckSecureAccess( from, c ); + + switch ( res ) + { + case SecureAccessResult.Insecure: break; + case SecureAccessResult.Accessible: return true; + case SecureAccessResult.Inaccessible: c.SendLocalizedMessageTo( from, 1010563 ); return false; + } + } + + return base.OnDoubleClick( from, o ); + } + + public override bool OnSingleClick( Mobile from, object o ) + { + if ( o is Item ) + { + Item item = (Item)o; + + if ( m_House.IsLockedDown( item ) ) + item.LabelTo( from, 501643 ); // [locked down] + else if ( m_House.IsSecure( item ) ) + item.LabelTo( from, 501644 ); // [locked down & secure] + } + + return base.OnSingleClick( from, o ); + } + + public BaseHouse House + { + get + { + return m_House; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/Jail.cs b/Scripts/Regions/Jail.cs new file mode 100644 index 000000000..291081588 --- /dev/null +++ b/Scripts/Regions/Jail.cs @@ -0,0 +1,61 @@ +using System; +using System.Xml; +using Server; +using Server.Spells; + +namespace Server.Regions +{ + public class Jail : BaseRegion + { + public Jail( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + } + + public override bool AllowBeneficial( Mobile from, Mobile target ) + { + if ( from.AccessLevel == AccessLevel.Player ) + from.SendMessage( "You may not do that in jail." ); + + return ( from.AccessLevel > AccessLevel.Player ); + } + + public override bool AllowHarmful( Mobile from, Mobile target ) + { + if ( from.AccessLevel == AccessLevel.Player ) + from.SendMessage( "You may not do that in jail." ); + + return ( from.AccessLevel > AccessLevel.Player ); + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return false; + } + + public override void AlterLightLevel( Mobile m, ref int global, ref int personal ) + { + global = LightCycle.JailLevel; + } + + public override bool OnBeginSpellCast( Mobile from, ISpell s ) + { + if ( from.AccessLevel == AccessLevel.Player ) + from.SendLocalizedMessage( 502629 ); // You cannot cast spells here. + + return ( from.AccessLevel > AccessLevel.Player ); + } + + public override bool OnSkillUse( Mobile from, int Skill ) + { + if ( from.AccessLevel == AccessLevel.Player ) + from.SendMessage( "You may not use skills in jail." ); + + return ( from.AccessLevel > AccessLevel.Player ); + } + + public override bool OnCombatantChange( Mobile from, Mobile Old, Mobile New ) + { + return ( from.AccessLevel > AccessLevel.Player ); + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/NoHousingRegion.cs b/Scripts/Regions/NoHousingRegion.cs new file mode 100644 index 000000000..5589149e9 --- /dev/null +++ b/Scripts/Regions/NoHousingRegion.cs @@ -0,0 +1,26 @@ +using System; +using System.Xml; +using Server; + +namespace Server.Regions +{ + public class NoHousingRegion : BaseRegion + { + /* - False: this uses 'stupid OSI' house placement checking: part of the house may be placed here provided that the center is not in the region + * - True: this uses 'smart RunUO' house placement checking: no part of the house may be in the region + */ + private bool m_SmartChecking; + + public bool SmartChecking{ get{ return m_SmartChecking; } } + + public NoHousingRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + ReadBoolean( xml["smartNoHousing"], "active", ref m_SmartChecking, false ); + } + + public override bool AllowHousing( Mobile from, Point3D p ) + { + return m_SmartChecking; + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/Spawning/SpawnDefinition.cs b/Scripts/Regions/Spawning/SpawnDefinition.cs new file mode 100644 index 000000000..6bec053d2 --- /dev/null +++ b/Scripts/Regions/Spawning/SpawnDefinition.cs @@ -0,0 +1,405 @@ +using System; +using System.Collections; +using System.IO; +using System.Xml; +using Server; +using Server.Mobiles; +using Server.Items; + +namespace Server.Regions +{ + public abstract class SpawnDefinition + { + protected SpawnDefinition() + { + } + + public abstract object Spawn( SpawnEntry entry ); + + public abstract bool CanSpawn( params Type[] types ); + + public static SpawnDefinition GetSpawnDefinition( XmlElement xml ) + { + switch ( xml.Name ) + { + case "object": + { + Type type = null; + if ( !Region.ReadType( xml, "type", ref type ) ) + return null; + + if ( typeof( Mobile ).IsAssignableFrom( type ) ) + { + return SpawnMobile.Get( type ); + } + else if ( typeof( Item ).IsAssignableFrom( type ) ) + { + return SpawnItem.Get( type ); + } + else + { + Console.WriteLine( "Invalid type '{0}' in a SpawnDefinition", type.FullName ); + return null; + } + } + case "group": + { + string group = null; + if ( !Region.ReadString( xml, "name", ref group ) ) + return null; + + SpawnDefinition def = (SpawnDefinition) SpawnGroup.Table[group]; + + if ( def == null ) + { + Console.WriteLine( "Could not find group '{0}' in a SpawnDefinition", group ); + return null; + } + else + { + return def; + } + } + case "treasureChest": + { + int itemID = 0xE43; + Region.ReadInt32( xml, "itemID", ref itemID, false ); + + object oLevel = BaseTreasureChest.TreasureLevel.Level2; + Region.ReadEnum( xml, "level", typeof( BaseTreasureChest.TreasureLevel ), ref oLevel, false ); + + return new SpawnTreasureChest( itemID, (BaseTreasureChest.TreasureLevel) oLevel ); + } + default: + { + return null; + } + } + } + } + + public abstract class SpawnType : SpawnDefinition + { + private Type m_Type; + private bool m_Init; + + public Type Type{ get{ return m_Type; } } + + public abstract int Height{ get; } + public abstract bool Land{ get; } + public abstract bool Water{ get; } + + protected SpawnType( Type type ) + { + m_Type = type; + m_Init = false; + } + + protected void EnsureInit() + { + if ( m_Init ) + return; + + Init(); + m_Init = true; + } + + protected virtual void Init() + { + } + + public override object Spawn( SpawnEntry entry ) + { + BaseRegion region = entry.Region; + Map map = region.Map; + + Point3D loc = entry.RandomSpawnLocation( this.Height, this.Land, this.Water ); + if ( loc == Point3D.Zero ) + return null; + + return Construct( entry, loc, map ); + } + + protected abstract object Construct( SpawnEntry entry, Point3D loc, Map map ); + + public override bool CanSpawn( params Type[] types ) + { + for ( int i = 0; i < types.Length; i++ ) + { + if ( types[i] == m_Type ) + return true; + } + + return false; + } + } + + public class SpawnMobile : SpawnType + { + private static Hashtable m_Table = new Hashtable(); + + public static SpawnMobile Get( Type type ) + { + SpawnMobile sm = (SpawnMobile) m_Table[type]; + + if ( sm == null ) + { + sm = new SpawnMobile( type ); + m_Table[type] = sm; + } + + return sm; + } + + protected bool m_Land; + protected bool m_Water; + + public override int Height{ get{ return 16; } } + public override bool Land{ get{ EnsureInit(); return m_Land; } } + public override bool Water{ get{ EnsureInit(); return m_Water; } } + + protected SpawnMobile( Type type ) : base( type ) + { + } + + protected override void Init() + { + Mobile mob = (Mobile) Activator.CreateInstance( Type ); + + m_Land = !mob.CantWalk; + m_Water = mob.CanSwim; + + mob.Delete(); + } + + protected override object Construct( SpawnEntry entry, Point3D loc, Map map ) + { + Mobile mobile = CreateMobile(); + + BaseCreature creature = mobile as BaseCreature; + if ( creature != null ) + { + creature.Home = entry.Home; + creature.RangeHome = entry.Range; + } + + if ( entry.Direction != SpawnEntry.InvalidDirection ) + mobile.Direction = entry.Direction; + + mobile.OnBeforeSpawn( loc, map ); + mobile.MoveToWorld( loc, map ); + mobile.OnAfterSpawn(); + + return mobile; + } + + protected virtual Mobile CreateMobile() + { + return (Mobile) Activator.CreateInstance( Type ); + } + } + + public class SpawnItem : SpawnType + { + private static Hashtable m_Table = new Hashtable(); + + public static SpawnItem Get( Type type ) + { + SpawnItem si = (SpawnItem) m_Table[type]; + + if ( si == null ) + { + si = new SpawnItem( type ); + m_Table[type] = si; + } + + return si; + } + + protected int m_Height; + + public override int Height{ get{ EnsureInit(); return m_Height; } } + public override bool Land{ get{ return true; } } + public override bool Water{ get{ return false; } } + + protected SpawnItem( Type type ) : base( type ) + { + } + + protected override void Init() + { + Item item = (Item) Activator.CreateInstance( Type ); + + m_Height = item.ItemData.Height; + + item.Delete(); + } + + protected override object Construct( SpawnEntry entry, Point3D loc, Map map ) + { + Item item = CreateItem(); + + item.OnBeforeSpawn( loc, map ); + item.MoveToWorld( loc, map ); + item.OnAfterSpawn(); + + return item; + } + + protected virtual Item CreateItem() + { + return (Item) Activator.CreateInstance( Type ); + } + } + + public class SpawnTreasureChest : SpawnItem + { + private int m_ItemID; + private BaseTreasureChest.TreasureLevel m_Level; + + public int ItemID{ get{ return m_ItemID; } } + public BaseTreasureChest.TreasureLevel Level{ get{ return m_Level; } } + + public SpawnTreasureChest( int itemID, BaseTreasureChest.TreasureLevel level ) : base( typeof( BaseTreasureChest ) ) + { + m_ItemID = itemID; + m_Level = level; + } + + protected override void Init() + { + m_Height = TileData.ItemTable[m_ItemID & 0x3FFF].Height; + } + + protected override Item CreateItem() + { + return new BaseTreasureChest( m_ItemID, m_Level ); + } + } + + public class SpawnGroupElement + { + private SpawnDefinition m_SpawnDefinition; + private int m_Weight; + + public SpawnDefinition SpawnDefinition{ get{ return m_SpawnDefinition; } } + public int Weight{ get{ return m_Weight; } } + + public SpawnGroupElement( SpawnDefinition spawnDefinition, int weight ) + { + m_SpawnDefinition = spawnDefinition; + m_Weight = weight; + } + } + + public class SpawnGroup : SpawnDefinition + { + private static Hashtable m_Table = new Hashtable(); + + public static Hashtable Table{ get{ return m_Table; } } + + public static void Register( SpawnGroup group ) + { + if ( m_Table.Contains( group.Name ) ) + Console.WriteLine( "Warning: Double SpawnGroup name '{0}'", group.Name ); + else + m_Table[group.Name] = group; + } + + static SpawnGroup() + { + string path = Path.Combine( Core.BaseDirectory, "Data/SpawnDefinitions.xml" ); + if ( !File.Exists( path ) ) + return; + + try + { + XmlDocument doc = new XmlDocument(); + doc.Load( path ); + + XmlElement root = doc["spawnDefinitions"]; + if ( root == null ) + return; + + foreach ( XmlElement xmlDef in root.SelectNodes( "spawnGroup" ) ) + { + string name = null; + if ( !Region.ReadString( xmlDef, "name", ref name ) ) + continue; + + ArrayList list = new ArrayList(); + foreach ( XmlNode node in xmlDef.ChildNodes ) + { + XmlElement el = node as XmlElement; + + if ( el != null ) + { + SpawnDefinition def = GetSpawnDefinition( el ); + if ( def == null ) + continue; + + int weight = 1; + Region.ReadInt32( el, "weight", ref weight, false ); + + SpawnGroupElement groupElement = new SpawnGroupElement( def, weight ); + list.Add( groupElement ); + } + } + + SpawnGroupElement[] elements = (SpawnGroupElement[]) list.ToArray( typeof( SpawnGroupElement ) ); + SpawnGroup group = new SpawnGroup( name, elements ); + Register( group ); + } + } + catch ( Exception ex ) + { + Console.WriteLine( "Could not load SpawnDefinitions.xml: " + ex.Message ); + } + } + + private string m_Name; + private SpawnGroupElement[] m_Elements; + private int m_TotalWeight; + + public string Name{ get{ return m_Name; } } + public SpawnGroupElement[] Elements{ get{ return m_Elements; } } + + public SpawnGroup( string name, SpawnGroupElement[] elements ) + { + m_Name = name; + m_Elements = elements; + + m_TotalWeight = 0; + for ( int i = 0; i < elements.Length; i++ ) + m_TotalWeight += elements[i].Weight; + } + + public override object Spawn( SpawnEntry entry ) + { + int index = Utility.Random( m_TotalWeight ); + + for ( int i = 0; i < m_Elements.Length; i++ ) + { + SpawnGroupElement element = m_Elements[i]; + + if ( index < element.Weight ) + return element.SpawnDefinition.Spawn( entry ); + + index -= element.Weight; + } + + return null; + } + + public override bool CanSpawn( params Type[] types ) + { + for ( int i = 0; i < m_Elements.Length; i++ ) + { + if ( m_Elements[i].SpawnDefinition.CanSpawn( types ) ) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/Spawning/SpawnEntry.cs b/Scripts/Regions/Spawning/SpawnEntry.cs new file mode 100644 index 000000000..2976e6997 --- /dev/null +++ b/Scripts/Regions/Spawning/SpawnEntry.cs @@ -0,0 +1,487 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Commands; + +namespace Server.Regions +{ + public class SpawnEntry : ISpawner + { + public static readonly TimeSpan DefaultMinSpawnTime = TimeSpan.FromMinutes( 2.0 ); + public static readonly TimeSpan DefaultMaxSpawnTime = TimeSpan.FromMinutes( 5.0 ); + + private static Hashtable m_Table = new Hashtable(); + + public static Hashtable Table{ get{ return m_Table; } } + + + // When a creature's AI is deactivated (PlayerRangeSensitive optimization) does it return home? + public bool ReturnOnDeactivate{ get{ return true; } } + + // Are creatures unlinked on taming (true) or should they also go out of the region (false)? + public bool UnlinkOnTaming{ get{ return false; } } + + // Are unlinked and untamed creatures removed after 20 hours? + public bool RemoveIfUntamed{ get{ return true; } } + + + public static readonly Direction InvalidDirection = Direction.Running; + + private int m_ID; + private BaseRegion m_Region; + private Point3D m_Home; + private int m_Range; + private Direction m_Direction; + private SpawnDefinition m_Definition; + private ArrayList m_SpawnedObjects; + private int m_Max; + private TimeSpan m_MinSpawnTime; + private TimeSpan m_MaxSpawnTime; + private bool m_Running; + + private DateTime m_NextSpawn; + private Timer m_SpawnTimer; + + public int ID{ get{ return m_ID; } } + public BaseRegion Region{ get{ return m_Region; } } + public Point3D Home{ get{ return m_Home; } } + public int Range{ get{ return m_Range; } } + public Direction Direction{ get{ return m_Direction; } } + public SpawnDefinition Definition{ get{ return m_Definition; } } + public ArrayList SpawnedObjects{ get{ return m_SpawnedObjects; } } + public int Max{ get{ return m_Max; } } + public TimeSpan MinSpawnTime{ get{ return m_MinSpawnTime; } } + public TimeSpan MaxSpawnTime{ get{ return m_MaxSpawnTime; } } + public bool Running{ get{ return m_Running; } } + + public bool Complete{ get{ return m_SpawnedObjects.Count >= m_Max; } } + public bool Spawning{ get{ return m_Running && !this.Complete; } } + + public SpawnEntry( int id, BaseRegion region, Point3D home, int range, Direction direction, SpawnDefinition definition, int max, TimeSpan minSpawnTime, TimeSpan maxSpawnTime ) + { + m_ID = id; + m_Region = region; + m_Home = home; + m_Range = range; + m_Direction = direction; + m_Definition = definition; + m_SpawnedObjects = new ArrayList(); + m_Max = max; + m_MinSpawnTime = minSpawnTime; + m_MaxSpawnTime = maxSpawnTime; + m_Running = false; + + if ( m_Table.Contains( id ) ) + Console.WriteLine( "Warning: double SpawnEntry ID '{0}'", id ); + else + m_Table[id] = this; + } + + public Point3D RandomSpawnLocation( int spawnHeight, bool land, bool water ) + { + return m_Region.RandomSpawnLocation( spawnHeight, land, water, m_Home, m_Range ); + } + + public void Start() + { + if ( m_Running ) + return; + + m_Running = true; + CheckTimer(); + } + + public void Stop() + { + if ( !m_Running ) + return; + + m_Running = false; + CheckTimer(); + } + + private void Spawn() + { + object spawn = m_Definition.Spawn( this ); + + if ( spawn != null ) + Add( spawn ); + } + + private void Add( object spawn ) + { + m_SpawnedObjects.Add( spawn ); + + if ( spawn is Mobile ) + { + ((Mobile)spawn).Spawner = this; + + if ( spawn is BaseCreature ) + ((BaseCreature)spawn).RemoveIfUntamed = this.RemoveIfUntamed; + } + else + { + ((Item)spawn).Spawner = this; + } + } + + void ISpawner.Remove( object spawn ) + { + m_SpawnedObjects.Remove( spawn ); + + CheckTimer(); + } + + private TimeSpan RandomTime() + { + int min = (int) m_MinSpawnTime.TotalSeconds; + int max = (int) m_MaxSpawnTime.TotalSeconds; + + int rand = Utility.RandomMinMax( min, max ); + return TimeSpan.FromSeconds( rand ); + } + + private void CheckTimer() + { + if ( this.Spawning ) + { + if ( m_SpawnTimer == null ) + { + TimeSpan time = RandomTime(); + m_SpawnTimer = Timer.DelayCall( time, new TimerCallback( TimerCallback ) ); + m_NextSpawn = DateTime.Now + time; + } + } + else if ( m_SpawnTimer != null ) + { + m_SpawnTimer.Stop(); + m_SpawnTimer = null; + } + } + + private void TimerCallback() + { + int amount = Math.Max( (m_Max - m_SpawnedObjects.Count) / 3, 1 ); + + for ( int i = 0; i < amount; i++ ) + Spawn(); + + m_SpawnTimer = null; + CheckTimer(); + } + + public void DeleteSpawnedObjects() + { + InternalDeleteSpawnedObjects(); + + m_Running = false; + CheckTimer(); + } + + private void InternalDeleteSpawnedObjects() + { + foreach ( object obj in m_SpawnedObjects ) + { + if ( obj is Mobile ) + { + Mobile mob = (Mobile) obj; + + mob.Spawner = null; + if ( !(mob is BaseCreature) || !((BaseCreature)mob).Controlled ) + mob.Delete(); + } + else + { + Item item = (Item) obj; + + item.Spawner = null; + item.Delete(); + } + } + + m_SpawnedObjects.Clear(); + } + + public void Respawn() + { + InternalDeleteSpawnedObjects(); + + for ( int i = 0; !this.Complete && i < m_Max * 2; i++ ) + Spawn(); + + m_Running = true; + CheckTimer(); + } + + public void Delete() + { + m_Max = 0; + InternalDeleteSpawnedObjects(); + + if ( m_SpawnTimer != null ) + { + m_SpawnTimer.Stop(); + m_SpawnTimer = null; + } + + if ( m_Table[m_ID] == this ) + m_Table.Remove( m_ID ); + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int) m_SpawnedObjects.Count ); + + for ( int i = 0; i < m_SpawnedObjects.Count; i++ ) + { + object spawn = m_SpawnedObjects[i]; + + int serial; + if ( spawn is Mobile ) + serial = ((Mobile)spawn).Serial; + else + serial = ((Item)spawn).Serial; + + writer.Write( (int) serial ); + } + + writer.Write( (bool) m_Running ); + + if ( m_SpawnTimer != null ) + { + writer.Write( true ); + writer.WriteDeltaTime( (DateTime) m_NextSpawn ); + } + else + { + writer.Write( false ); + } + } + + public void Deserialize( GenericReader reader, int version ) + { + int count = reader.ReadInt(); + + for ( int i = 0; i < count; i++ ) + { + int serial = reader.ReadInt(); + IEntity entity = World.FindEntity( serial ); + + if ( entity != null ) + Add( entity ); + } + + m_Running = reader.ReadBool(); + + if ( reader.ReadBool() ) + { + m_NextSpawn = reader.ReadDeltaTime(); + + if ( this.Spawning ) + { + if ( m_SpawnTimer != null ) + m_SpawnTimer.Stop(); + + TimeSpan delay = m_NextSpawn - DateTime.Now; + m_SpawnTimer = Timer.DelayCall( delay > TimeSpan.Zero ? delay : TimeSpan.Zero, new TimerCallback( TimerCallback ) ); + } + } + + CheckTimer(); + } + + private static ArrayList m_RemoveList; + + public static void Remove( GenericReader reader, int version ) + { + int count = reader.ReadInt(); + + for ( int i = 0; i < count; i++ ) + { + int serial = reader.ReadInt(); + IEntity entity = World.FindEntity( serial ); + + if ( entity != null ) + { + if ( m_RemoveList == null ) + m_RemoveList = new ArrayList(); + + m_RemoveList.Add( entity ); + } + } + + reader.ReadBool(); // m_Running + + if ( reader.ReadBool() ) + reader.ReadDeltaTime(); // m_NextSpawn + } + + public static void Initialize() + { + if ( m_RemoveList != null ) + { + foreach ( object obj in m_RemoveList ) + { + if ( obj is Mobile ) + ((Mobile)obj).Delete(); + else + ((Item)obj).Delete(); + } + + m_RemoveList = null; + } + + SpawnPersistence.EnsureExistence(); + + CommandSystem.Register( "RespawnAllRegions", AccessLevel.Administrator, new CommandEventHandler( RespawnAllRegions_OnCommand ) ); + CommandSystem.Register( "RespawnRegion", AccessLevel.GameMaster, new CommandEventHandler( RespawnRegion_OnCommand ) ); + CommandSystem.Register( "DelAllRegionSpawns", AccessLevel.Administrator, new CommandEventHandler( DelAllRegionSpawns_OnCommand ) ); + CommandSystem.Register( "DelRegionSpawns", AccessLevel.GameMaster, new CommandEventHandler( DelRegionSpawns_OnCommand ) ); + CommandSystem.Register( "StartAllRegionSpawns", AccessLevel.Administrator, new CommandEventHandler( StartAllRegionSpawns_OnCommand ) ); + CommandSystem.Register( "StartRegionSpawns", AccessLevel.GameMaster, new CommandEventHandler( StartRegionSpawns_OnCommand ) ); + CommandSystem.Register( "StopAllRegionSpawns", AccessLevel.Administrator, new CommandEventHandler( StopAllRegionSpawns_OnCommand ) ); + CommandSystem.Register( "StopRegionSpawns", AccessLevel.GameMaster, new CommandEventHandler( StopRegionSpawns_OnCommand ) ); + } + + private static BaseRegion GetCommandData( CommandEventArgs args ) + { + Mobile from = args.Mobile; + + Region reg; + if ( args.Length == 0 ) + { + reg = from.Region; + } + else + { + string name = args.GetString( 0 ); + //reg = (Region) from.Map.Regions[name]; + + if ( !from.Map.Regions.TryGetValue( name, out reg ) ) + { + from.SendMessage( "Could not find region '{0}'.", name ); + return null; + } + } + + BaseRegion br = reg as BaseRegion; + + if ( br == null || br.Spawns == null ) + { + from.SendMessage( "There are no spawners in region '{0}'.", reg ); + return null; + } + + return br; + } + + [Usage( "RespawnAllRegions" )] + [Description( "Respawns all regions and sets the spawners as running." )] + private static void RespawnAllRegions_OnCommand( CommandEventArgs args ) + { + foreach ( SpawnEntry entry in m_Table.Values ) + { + entry.Respawn(); + } + + args.Mobile.SendMessage( "All regions have respawned." ); + } + + [Usage( "RespawnRegion []" )] + [Description( "Respawns the region in which you are (or that you provided) and sets the spawners as running." )] + private static void RespawnRegion_OnCommand( CommandEventArgs args ) + { + BaseRegion region = GetCommandData( args ); + + if ( region == null ) + return; + + for ( int i = 0; i < region.Spawns.Length; i++ ) + region.Spawns[i].Respawn(); + + args.Mobile.SendMessage( "Region '{0}' has respawned.", region ); + } + + [Usage( "DelAllRegionSpawns" )] + [Description( "Deletes all spawned objects of every regions and sets the spawners as not running." )] + private static void DelAllRegionSpawns_OnCommand( CommandEventArgs args ) + { + foreach ( SpawnEntry entry in m_Table.Values ) + { + entry.DeleteSpawnedObjects(); + } + + args.Mobile.SendMessage( "All region spawned objects have been deleted." ); + } + + [Usage( "DelRegionSpawns []" )] + [Description( "Deletes all spawned objects of the region in which you are (or that you provided) and sets the spawners as not running." )] + private static void DelRegionSpawns_OnCommand( CommandEventArgs args ) + { + BaseRegion region = GetCommandData( args ); + + if ( region == null ) + return; + + for ( int i = 0; i < region.Spawns.Length; i++ ) + region.Spawns[i].DeleteSpawnedObjects(); + + args.Mobile.SendMessage( "Spawned objects of region '{0}' have been deleted.", region ); + } + + [Usage( "StartAllRegionSpawns" )] + [Description( "Sets the region spawners of all regions as running." )] + private static void StartAllRegionSpawns_OnCommand( CommandEventArgs args ) + { + foreach ( SpawnEntry entry in m_Table.Values ) + { + entry.Start(); + } + + args.Mobile.SendMessage( "All region spawners have started." ); + } + + [Usage( "StartRegionSpawns []" )] + [Description( "Sets the region spawners of the region in which you are (or that you provided) as running." )] + private static void StartRegionSpawns_OnCommand( CommandEventArgs args ) + { + BaseRegion region = GetCommandData( args ); + + if ( region == null ) + return; + + for ( int i = 0; i < region.Spawns.Length; i++ ) + region.Spawns[i].Start(); + + args.Mobile.SendMessage( "Spawners of region '{0}' have started.", region ); + } + + [Usage( "StopAllRegionSpawns" )] + [Description( "Sets the region spawners of all regions as not running." )] + private static void StopAllRegionSpawns_OnCommand( CommandEventArgs args ) + { + foreach ( SpawnEntry entry in m_Table.Values ) + { + entry.Stop(); + } + + args.Mobile.SendMessage( "All region spawners have stopped." ); + } + + [Usage( "StopRegionSpawns []" )] + [Description( "Sets the region spawners of the region in which you are (or that you provided) as not running." )] + private static void StopRegionSpawns_OnCommand( CommandEventArgs args ) + { + BaseRegion region = GetCommandData( args ); + + if ( region == null ) + return; + + for ( int i = 0; i < region.Spawns.Length; i++ ) + region.Spawns[i].Stop(); + + args.Mobile.SendMessage( "Spawners of region '{0}' have stopped.", region ); + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/Spawning/SpawnPersistence.cs b/Scripts/Regions/Spawning/SpawnPersistence.cs new file mode 100644 index 000000000..9946950a2 --- /dev/null +++ b/Scripts/Regions/Spawning/SpawnPersistence.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections; +using Server; + +namespace Server.Regions +{ + public class SpawnPersistence : Item + { + private static SpawnPersistence m_Instance; + + public SpawnPersistence Instance{ get{ return m_Instance; } } + + public static void EnsureExistence() + { + if ( m_Instance == null ) + m_Instance = new SpawnPersistence(); + } + + public override string DefaultName + { + get { return "Region spawn persistence - Internal"; } + } + + private SpawnPersistence() : base( 1 ) + { + Movable = false; + } + + public SpawnPersistence( Serial serial ) : base( serial ) + { + m_Instance = this; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.Write( (int) SpawnEntry.Table.Values.Count ); + foreach ( SpawnEntry entry in SpawnEntry.Table.Values ) + { + writer.Write( (int) entry.ID ); + + entry.Serialize( writer ); + } + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + int count = reader.ReadInt(); + for ( int i = 0; i < count; i++ ) + { + int id = reader.ReadInt(); + + SpawnEntry entry = (SpawnEntry) SpawnEntry.Table[id]; + + if ( entry != null ) + entry.Deserialize( reader, version ); + else + SpawnEntry.Remove( reader, version ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Regions/TownRegion.cs b/Scripts/Regions/TownRegion.cs new file mode 100644 index 000000000..1f9afc441 --- /dev/null +++ b/Scripts/Regions/TownRegion.cs @@ -0,0 +1,13 @@ +using System; +using System.Xml; +using Server; + +namespace Server.Regions +{ + public class TownRegion : GuardedRegion + { + public TownRegion( XmlElement xml, Map map, Region parent ) : base( xml, map, parent ) + { + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Anatomy.cs b/Scripts/Skills/Anatomy.cs new file mode 100644 index 000000000..99cd0c196 --- /dev/null +++ b/Scripts/Skills/Anatomy.cs @@ -0,0 +1,87 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.SkillHandlers +{ + public class Anatomy + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Anatomy].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.Target = new Anatomy.InternalTarget(); + + m.SendLocalizedMessage( 500321 ); // Whom shall I examine? + + return TimeSpan.FromSeconds( 1.0 ); + } + + private class InternalTarget : Target + { + public InternalTarget() : base ( 8, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( from == targeted ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500324 ); // You know yourself quite well enough already. + } + else if ( targeted is TownCrier ) + { + ((TownCrier)targeted).PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500322, from.NetState ); // This person looks fine to me, though he may have some news... + } + else if ( targeted is BaseVendor && ((BaseVendor)targeted).IsInvulnerable ) + { + ((BaseVendor)targeted).PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500326, from.NetState ); // That can not be inspected. + } + else if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + + int marginOfError = Math.Max( 0, 25 - (int)(from.Skills[SkillName.Anatomy].Value / 4) ); + + int str = targ.Str + Utility.RandomMinMax( -marginOfError, +marginOfError ); + int dex = targ.Dex + Utility.RandomMinMax( -marginOfError, +marginOfError ); + int stm = ((targ.Stam * 100) / Math.Max( targ.StamMax, 1 )) + Utility.RandomMinMax( -marginOfError, +marginOfError ); + + int strMod = str / 10; + int dexMod = dex / 10; + int stmMod = stm / 10; + + if ( strMod < 0 ) strMod = 0; + else if ( strMod > 10 ) strMod = 10; + + if ( dexMod < 0 ) dexMod = 0; + else if ( dexMod > 10 ) dexMod = 10; + + if ( stmMod > 10 ) stmMod = 10; + else if ( stmMod < 0 ) stmMod = 0; + + if ( from.CheckTargetSkill( SkillName.Anatomy, targ, 0, 100 ) ) + { + targ.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1038045 + (strMod * 11) + dexMod, from.NetState ); // That looks [strong] and [dexterous]. + + if ( from.Skills[SkillName.Anatomy].Base >= 65.0 ) + targ.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1038303 + stmMod, from.NetState ); // That being is at [10,20,...] percent endurance. + } + else + { + targ.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042666, from.NetState ); // You can not quite get a sense of their physical characteristics. + } + } + else if ( targeted is Item ) + { + ((Item)targeted).SendLocalizedMessageTo( from, 500323, "" ); // Only living things have anatomies! + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/AnimalLore.cs b/Scripts/Skills/AnimalLore.cs new file mode 100644 index 000000000..fd2e09f2a --- /dev/null +++ b/Scripts/Skills/AnimalLore.cs @@ -0,0 +1,356 @@ +using System; +using Server; +using Server.Gumps; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.SkillHandlers +{ + public class AnimalLore + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.AnimalLore].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse(Mobile m) + { + m.Target = new InternalTarget(); + + m.SendLocalizedMessage( 500328 ); // What animal should I look at? + + return TimeSpan.FromSeconds( 1.0 ); + } + + private class InternalTarget : Target + { + public InternalTarget() : base( 8, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( !from.Alive ) + { + from.SendLocalizedMessage( 500331 ); // The spirits of the dead are not the province of animal lore. + } + else if ( targeted is BaseCreature ) + { + BaseCreature c = (BaseCreature)targeted; + + if ( !c.IsDeadPet ) + { + if ( c.Body.IsAnimal || c.Body.IsMonster || c.Body.IsSea ) + { + if ( (!c.Controlled || !c.Tamable) && from.Skills[SkillName.AnimalLore].Base < 100.0 ) + { + from.SendLocalizedMessage( 1049674 ); // At your skill level, you can only lore tamed creatures. + } + else if ( !c.Tamable && from.Skills[SkillName.AnimalLore].Base < 110.0 ) + { + from.SendLocalizedMessage( 1049675 ); // At your skill level, you can only lore tamed or tameable creatures. + } + else if ( !from.CheckTargetSkill( SkillName.AnimalLore, c, 0.0, 120.0 ) ) + { + from.SendLocalizedMessage( 500334 ); // You can't think of anything you know offhand. + } + else + { + from.CloseGump( typeof( AnimalLoreGump ) ); + from.SendGump( new AnimalLoreGump( c ) ); + } + } + else + { + from.SendLocalizedMessage( 500329 ); // That's not an animal! + } + } + else + { + from.SendLocalizedMessage( 500331 ); // The spirits of the dead are not the province of animal lore. + } + } + else + { + from.SendLocalizedMessage( 500329 ); // That's not an animal! + } + } + } + } + + public class AnimalLoreGump : Gump + { + private static string FormatSkill( BaseCreature c, SkillName name ) + { + Skill skill = c.Skills[name]; + + if ( skill.Base < 10.0 ) + return "
---
"; + + return String.Format( "
{0:F1}
", skill.Base ); + } + + private static string FormatAttributes( int cur, int max ) + { + if ( max == 0 ) + return "
---
"; + + return String.Format( "
{0}/{1}
", cur, max ); + } + + private static string FormatStat( int val ) + { + if ( val == 0 ) + return "
---
"; + + return String.Format( "
{0}
", val ); + } + + private static string FormatDouble( double val ) + { + if ( val == 0 ) + return "
---
"; + + return String.Format( "
{0:F1}
", val ); + } + + private static string FormatElement( int val ) + { + if ( val <= 0 ) + return "
---
"; + + return String.Format( "
{0}%
", val ); + } + + private const int LabelColor = 0x24E5; + + public AnimalLoreGump( BaseCreature c ) : base( 250, 50 ) + { + AddPage( 0 ); + + AddImage( 100, 100, 2080 ); + AddImage( 118, 137, 2081 ); + AddImage( 118, 207, 2081 ); + AddImage( 118, 277, 2081 ); + AddImage( 118, 347, 2083 ); + + AddHtml( 147, 108, 210, 18, String.Format( "
{0}
", c.Name ), false, false ); + + AddButton( 240, 77, 2093, 2093, 2, GumpButtonType.Reply, 0 ); + + AddImage( 140, 138, 2091 ); + AddImage( 140, 335, 2091 ); + + int pages = ( Core.AOS ? 5 : 3 ); + int page = 0; + + + #region Attributes + AddPage( ++page ); + + AddImage( 128, 152, 2086 ); + AddHtmlLocalized( 147, 150, 160, 18, 1049593, 200, false, false ); // Attributes + + AddHtmlLocalized( 153, 168, 160, 18, 1049578, LabelColor, false, false ); // Hits + AddHtml( 280, 168, 75, 18, FormatAttributes( c.Hits, c.HitsMax ), false, false ); + + AddHtmlLocalized( 153, 186, 160, 18, 1049579, LabelColor, false, false ); // Stamina + AddHtml( 280, 186, 75, 18, FormatAttributes( c.Stam, c.StamMax ), false, false ); + + AddHtmlLocalized( 153, 204, 160, 18, 1049580, LabelColor, false, false ); // Mana + AddHtml( 280, 204, 75, 18, FormatAttributes( c.Mana, c.ManaMax ), false, false ); + + AddHtmlLocalized( 153, 222, 160, 18, 1028335, LabelColor, false, false ); // Strength + AddHtml( 320, 222, 35, 18, FormatStat( c.Str ), false, false ); + + AddHtmlLocalized( 153, 240, 160, 18, 3000113, LabelColor, false, false ); // Dexterity + AddHtml( 320, 240, 35, 18, FormatStat( c.Dex ), false, false ); + + AddHtmlLocalized( 153, 258, 160, 18, 3000112, LabelColor, false, false ); // Intelligence + AddHtml( 320, 258, 35, 18, FormatStat( c.Int ), false, false ); + + if ( Core.AOS ) + { + int y = 276; + + if ( Core.SE ) + { + double bd = Items.BaseInstrument.GetBaseDifficulty( c ); + if ( c.Uncalmable ) + bd = 0; + + AddHtmlLocalized( 153, 276, 160, 18, 1070793, LabelColor, false, false ); // Barding Difficulty + AddHtml( 320, y, 35, 18, FormatDouble( bd ), false, false ); + + y += 18; + } + + AddImage( 128, y + 2, 2086 ); + AddHtmlLocalized( 147, y, 160, 18, 1049594, 200, false, false ); // Loyalty Rating + y += 18; + + AddHtmlLocalized( 153, y, 160, 18, (!c.Controlled || c.Loyalty == 0) ? 1061643 : 1049595 + (c.Loyalty / 10), LabelColor, false, false ); + } + else + { + AddImage( 128, 278, 2086 ); + AddHtmlLocalized( 147, 276, 160, 18, 3001016, 200, false, false ); // Miscellaneous + + AddHtmlLocalized( 153, 294, 160, 18, 1049581, LabelColor, false, false ); // Armor Rating + AddHtml( 320, 294, 35, 18, FormatStat( c.VirtualArmor ), false, false ); + } + + AddButton( 340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1 ); + AddButton( 317, 358, 5603, 5607, 0, GumpButtonType.Page, pages ); + #endregion + + #region Resistances + if ( Core.AOS ) + { + AddPage( ++page ); + + AddImage( 128, 152, 2086 ); + AddHtmlLocalized( 147, 150, 160, 18, 1061645, 200, false, false ); // Resistances + + AddHtmlLocalized( 153, 168, 160, 18, 1061646, LabelColor, false, false ); // Physical + AddHtml( 320, 168, 35, 18, FormatElement( c.PhysicalResistance ), false, false ); + + AddHtmlLocalized( 153, 186, 160, 18, 1061647, LabelColor, false, false ); // Fire + AddHtml( 320, 186, 35, 18, FormatElement( c.FireResistance ), false, false ); + + AddHtmlLocalized( 153, 204, 160, 18, 1061648, LabelColor, false, false ); // Cold + AddHtml( 320, 204, 35, 18, FormatElement( c.ColdResistance ), false, false ); + + AddHtmlLocalized( 153, 222, 160, 18, 1061649, LabelColor, false, false ); // Poison + AddHtml( 320, 222, 35, 18, FormatElement( c.PoisonResistance ), false, false ); + + AddHtmlLocalized( 153, 240, 160, 18, 1061650, LabelColor, false, false ); // Energy + AddHtml( 320, 240, 35, 18, FormatElement( c.EnergyResistance ), false, false ); + + AddButton( 340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1 ); + AddButton( 317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1 ); + } + #endregion + + #region Damage + if ( Core.AOS ) + { + AddPage( ++page ); + + AddImage( 128, 152, 2086 ); + AddHtmlLocalized( 147, 150, 160, 18, 1017319, 200, false, false ); // Damage + + AddHtmlLocalized( 153, 168, 160, 18, 1061646, LabelColor, false, false ); // Physical + AddHtml( 320, 168, 35, 18, FormatElement( c.PhysicalDamage ), false, false ); + + AddHtmlLocalized( 153, 186, 160, 18, 1061647, LabelColor, false, false ); // Fire + AddHtml( 320, 186, 35, 18, FormatElement( c.FireDamage ), false, false ); + + AddHtmlLocalized( 153, 204, 160, 18, 1061648, LabelColor, false, false ); // Cold + AddHtml( 320, 204, 35, 18, FormatElement( c.ColdDamage ), false, false ); + + AddHtmlLocalized( 153, 222, 160, 18, 1061649, LabelColor, false, false ); // Poison + AddHtml( 320, 222, 35, 18, FormatElement( c.PoisonDamage ), false, false ); + + AddHtmlLocalized( 153, 240, 160, 18, 1061650, LabelColor, false, false ); // Energy + AddHtml( 320, 240, 35, 18, FormatElement( c.EnergyDamage ), false, false ); + + AddButton( 340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1 ); + AddButton( 317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1 ); + } + #endregion + + #region Skills + AddPage( ++page ); + + AddImage( 128, 152, 2086 ); + AddHtmlLocalized( 147, 150, 160, 18, 3001030, 200, false, false ); // Combat Ratings + + AddHtmlLocalized( 153, 168, 160, 18, 1044103, LabelColor, false, false ); // Wrestling + AddHtml( 320, 168, 35, 18, FormatSkill( c, SkillName.Wrestling ), false, false ); + + AddHtmlLocalized( 153, 186, 160, 18, 1044087, LabelColor, false, false ); // Tactics + AddHtml( 320, 186, 35, 18, FormatSkill( c, SkillName.Tactics ), false, false ); + + AddHtmlLocalized( 153, 204, 160, 18, 1044086, LabelColor, false, false ); // Magic Resistance + AddHtml( 320, 204, 35, 18, FormatSkill( c, SkillName.MagicResist ), false, false ); + + AddHtmlLocalized( 153, 222, 160, 18, 1044061, LabelColor, false, false ); // Anatomy + AddHtml( 320, 222, 35, 18, FormatSkill( c, SkillName.Anatomy ), false, false ); + + AddHtmlLocalized( 153, 240, 160, 18, 1044090, LabelColor, false, false ); // Poisoning + AddHtml( 320, 240, 35, 18, FormatSkill( c, SkillName.Poisoning ), false, false ); + + AddImage( 128, 260, 2086 ); + AddHtmlLocalized( 147, 258, 160, 18, 3001032, 200, false, false ); // Lore & Knowledge + + AddHtmlLocalized( 153, 276, 160, 18, 1044085, LabelColor, false, false ); // Magery + AddHtml( 320, 276, 35, 18, FormatSkill( c, SkillName.Magery ), false, false ); + + AddHtmlLocalized( 153, 294, 160, 18, 1044076, LabelColor, false, false ); // Evaluating Intelligence + AddHtml( 320, 294, 35, 18,FormatSkill( c, SkillName.EvalInt ), false, false ); + + AddHtmlLocalized( 153, 312, 160, 18, 1044106, LabelColor, false, false ); // Meditation + AddHtml( 320, 312, 35, 18, FormatSkill( c, SkillName.Meditation ), false, false ); + + AddButton( 340, 358, 5601, 5605, 0, GumpButtonType.Page, page + 1 ); + AddButton( 317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1 ); + #endregion + + #region Misc + AddPage( ++page ); + + AddImage( 128, 152, 2086 ); + AddHtmlLocalized( 147, 150, 160, 18, 1049563, 200, false, false ); // Preferred Foods + + int foodPref = 3000340; + + if ( (c.FavoriteFood & FoodType.FruitsAndVegies) != 0 ) + foodPref = 1049565; // Fruits and Vegetables + else if ( (c.FavoriteFood & FoodType.GrainsAndHay) != 0 ) + foodPref = 1049566; // Grains and Hay + else if ( (c.FavoriteFood & FoodType.Fish) != 0 ) + foodPref = 1049568; // Fish + else if ( (c.FavoriteFood & FoodType.Meat) != 0 ) + foodPref = 1049564; // Meat + + AddHtmlLocalized( 153, 168, 160, 18, foodPref, LabelColor, false, false ); + + AddImage( 128, 188, 2086 ); + AddHtmlLocalized( 147, 186, 160, 18, 1049569, 200, false, false ); // Pack Instincts + + int packInstinct = 3000340; + + if ( (c.PackInstinct & PackInstinct.Canine) != 0 ) + packInstinct = 1049570; // Canine + else if ( (c.PackInstinct & PackInstinct.Ostard) != 0 ) + packInstinct = 1049571; // Ostard + else if ( (c.PackInstinct & PackInstinct.Feline) != 0 ) + packInstinct = 1049572; // Feline + else if ( (c.PackInstinct & PackInstinct.Arachnid) != 0 ) + packInstinct = 1049573; // Arachnid + else if ( (c.PackInstinct & PackInstinct.Daemon) != 0 ) + packInstinct = 1049574; // Daemon + else if ( (c.PackInstinct & PackInstinct.Bear) != 0 ) + packInstinct = 1049575; // Bear + else if ( (c.PackInstinct & PackInstinct.Equine) != 0 ) + packInstinct = 1049576; // Equine + else if ( (c.PackInstinct & PackInstinct.Bull) != 0 ) + packInstinct = 1049577; // Bull + + AddHtmlLocalized( 153, 204, 160, 18, packInstinct, LabelColor, false, false ); + + if ( !Core.AOS ) + { + AddImage( 128, 224, 2086 ); + AddHtmlLocalized( 147, 222, 160, 18, 1049594, 200, false, false ); // Loyalty Rating + + AddHtmlLocalized( 153, 240, 160, 18, (!c.Controlled || c.Loyalty == 0) ? 1061643 : 1049595 + (c.Loyalty / 10), LabelColor, false, false ); + } + + AddButton( 340, 358, 5601, 5605, 0, GumpButtonType.Page, 1 ); + AddButton( 317, 358, 5603, 5607, 0, GumpButtonType.Page, page - 1 ); + #endregion + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/AnimalTaming.cs b/Scripts/Skills/AnimalTaming.cs new file mode 100644 index 000000000..678ea6a6b --- /dev/null +++ b/Scripts/Skills/AnimalTaming.cs @@ -0,0 +1,363 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; +using Server.Factions; + +namespace Server.SkillHandlers +{ + public class AnimalTaming + { + private static Hashtable m_BeingTamed = new Hashtable(); + + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.AnimalTaming].Callback = new SkillUseCallback( OnUse ); + } + + private static bool m_DisableMessage; + + public static bool DisableMessage + { + get{ return m_DisableMessage; } + set{ m_DisableMessage = value; } + } + + public static TimeSpan OnUse( Mobile m ) + { + m.RevealingAction(); + + m.Target = new InternalTarget(); + m.RevealingAction(); + + if ( !m_DisableMessage ) + m.SendLocalizedMessage( 502789 ); // Tame which animal? + + return TimeSpan.FromHours( 6.0 ); + } + + public static bool CheckMastery( Mobile tamer, BaseCreature creature ) + { + BaseCreature familiar = (BaseCreature)Spells.Necromancy.SummonFamiliarSpell.Table[tamer]; + + if ( familiar != null && !familiar.Deleted && familiar is DarkWolfFamiliar ) + { + if ( creature is DireWolf || creature is GreyWolf || creature is TimberWolf || creature is WhiteWolf ) + return true; + } + + return false; + } + + public static bool MustBeSubdued( BaseCreature bc ) + { + return bc.SubdueBeforeTame && (bc.Hits > (bc.HitsMax / 10)); + } + + public static void ScaleStats( BaseCreature bc, double scalar ) + { + if ( bc.RawStr > 0 ) + bc.RawStr = (int)Math.Max( 1, bc.RawStr * scalar ); + + if ( bc.RawDex > 0 ) + bc.RawDex = (int)Math.Max( 1, bc.RawDex * scalar ); + + if ( bc.RawInt > 0 ) + bc.RawInt = (int)Math.Max( 1, bc.RawInt * scalar ); + + if ( bc.HitsMaxSeed > 0 ) + { + bc.HitsMaxSeed = (int)Math.Max( 1, bc.HitsMaxSeed * scalar ); + bc.Hits = bc.Hits; + } + + if ( bc.StamMaxSeed > 0 ) + { + bc.StamMaxSeed = (int)Math.Max( 1, bc.StamMaxSeed * scalar ); + bc.Stam = bc.Stam; + } + } + + public static void ScaleSkills( BaseCreature bc, double scalar ) + { + for ( int i = 0; i < bc.Skills.Length; ++i ) + { + bc.Skills[i].Base *= scalar; + if ( bc.Skills[i].Base > 100.0 ) + bc.Skills[i].Cap = bc.Skills[i].Base; + } + } + + private class InternalTarget : Target + { + private bool m_SetSkillTime = true; + + public InternalTarget() : base ( 2, false, TargetFlags.None ) + { + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( m_SetSkillTime ) + from.NextSkillTime = DateTime.Now; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + from.RevealingAction(); + + if ( targeted is Mobile ) + { + if ( targeted is BaseCreature ) + { + BaseCreature creature = (BaseCreature)targeted; + + if ( !creature.Tamable ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1049655, from.NetState ); // That creature cannot be tamed. + } + else if ( creature.Controlled ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502804, from.NetState ); // That animal looks tame already. + } + else if ( from.Female && !creature.AllowFemaleTamer ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1049653, from.NetState ); // That creature can only be tamed by males. + } + else if ( !from.Female && !creature.AllowMaleTamer ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1049652, from.NetState ); // That creature can only be tamed by females. + } + else if ( from.Followers + creature.ControlSlots > from.FollowersMax ) + { + from.SendLocalizedMessage( 1049611 ); // You have too many followers to tame that creature. + } + else if ( creature.Owners.Count >= BaseCreature.MaxOwners && !creature.Owners.Contains( from ) ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1005615, from.NetState ); // This animal has had too many owners and is too upset for you to tame. + } + else if ( MustBeSubdued( creature ) ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1054025, from.NetState ); // You must subdue this creature before you can tame it! + } + else if ( CheckMastery( from, creature ) || from.Skills[SkillName.AnimalTaming].Value >= creature.MinTameSkill ) + { + FactionWarHorse warHorse = creature as FactionWarHorse; + + if ( warHorse != null ) + { + Faction faction = Faction.Find( from ); + + if ( faction == null || faction != warHorse.Faction ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1042590, from.NetState ); // You cannot tame this creature. + return; + } + } + + if ( m_BeingTamed.Contains( targeted ) ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502802, from.NetState ); // Someone else is already taming this. + } + else if ( creature.CanAngerOnTame && 0.95 >= Utility.RandomDouble() ) + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502805, from.NetState ); // You seem to anger the beast! + creature.PlaySound( creature.GetAngerSound() ); + creature.Direction = creature.GetDirectionTo( from ); + creature.Combatant = from; + } + else + { + m_BeingTamed[targeted] = from; + + from.LocalOverheadMessage( MessageType.Emote, 0x59, 1010597 ); // You start to tame the creature. + from.NonlocalOverheadMessage( MessageType.Emote, 0x59, 1010598 ); // *begins taming a creature.* + + new InternalTimer( from, creature, Utility.Random( 3, 2 ) ).Start(); + + m_SetSkillTime = false; + } + } + else + { + creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502806, from.NetState ); // You have no chance of taming this creature. + } + } + else + { + ((Mobile)targeted).PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502469, from.NetState ); // That being cannot be tamed. + } + } + else + { + from.SendLocalizedMessage( 502801 ); // You can't tame that! + } + } + public static void AdjustSkillCaps( BaseCreature bc ) + { + for ( int i = 0; i < bc.Skills.Length; ++i ) + { + if ( bc.Skills[i].Base > bc.Skills[i].Cap ) + bc.Skills[i].Cap = bc.Skills[i].Base; + } + } + + private class InternalTimer : Timer + { + private Mobile m_Tamer; + private BaseCreature m_Creature; + private int m_MaxCount; + private int m_Count; + private bool m_Paralyzed; + private DateTime m_StartTime; + + public InternalTimer( Mobile tamer, BaseCreature creature, int count ) : base( TimeSpan.FromSeconds( 3.0 ), TimeSpan.FromSeconds( 3.0 ), count ) + { + m_Tamer = tamer; + m_Creature = creature; + m_MaxCount = count; + m_Paralyzed = creature.Paralyzed; + m_StartTime = DateTime.Now; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + m_Count++; + + DamageEntry de = m_Creature.FindMostRecentDamageEntry( false ); + bool alreadyOwned = m_Creature.Owners.Contains( m_Tamer ); + + if ( !m_Tamer.InRange( m_Creature, 6 ) ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502795, m_Tamer.NetState ); // You are too far away to continue taming. + Stop(); + } + else if ( !m_Tamer.CheckAlive() ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502796, m_Tamer.NetState ); // You are dead, and cannot continue taming. + Stop(); + } + else if ( !m_Tamer.CanSee( m_Creature ) || !m_Tamer.InLOS( m_Creature ) ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1049654, m_Tamer.NetState ); // You do not have a clear path to the animal you are taming, and must cease your attempt. + Stop(); + } + else if ( !m_Creature.Tamable ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1049655, m_Tamer.NetState ); // That creature cannot be tamed. + Stop(); + } + else if ( m_Creature.Controlled ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502804, m_Tamer.NetState ); // That animal looks tame already. + Stop(); + } + else if ( m_Creature.Owners.Count >= BaseCreature.MaxOwners && !m_Creature.Owners.Contains( m_Tamer ) ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1005615, m_Tamer.NetState ); // This animal has had too many owners and is too upset for you to tame. + Stop(); + } + else if ( MustBeSubdued( m_Creature ) ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1054025, m_Tamer.NetState ); // You must subdue this creature before you can tame it! + Stop(); + } + else if ( de != null && de.LastDamage > m_StartTime ) + { + m_BeingTamed.Remove( m_Creature ); + m_Tamer.NextSkillTime = DateTime.Now; + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502794, m_Tamer.NetState ); // The animal is too angry to continue taming. + Stop(); + } + else if ( m_Count < m_MaxCount ) + { + m_Tamer.RevealingAction(); + + switch ( Utility.Random( 3 ) ) + { + case 0: m_Tamer.PublicOverheadMessage( MessageType.Regular, 0x3B2, Utility.Random( 502790, 4 ) ); break; + case 1: m_Tamer.PublicOverheadMessage( MessageType.Regular, 0x3B2, Utility.Random( 1005608, 6 ) ); break; + case 2: m_Tamer.PublicOverheadMessage( MessageType.Regular, 0x3B2, Utility.Random( 1010593, 4 ) ); break; + } + + if ( !alreadyOwned ) // Passively check animal lore for gain + m_Tamer.CheckTargetSkill( SkillName.AnimalLore, m_Creature, 0.0, 120.0 ); + + if ( m_Creature.Paralyzed ) + m_Paralyzed = true; + } + else + { + m_Tamer.RevealingAction(); + m_Tamer.NextSkillTime = DateTime.Now; + m_BeingTamed.Remove( m_Creature ); + + if ( m_Creature.Paralyzed ) + m_Paralyzed = true; + + if ( !alreadyOwned ) // Passively check animal lore for gain + m_Tamer.CheckTargetSkill( SkillName.AnimalLore, m_Creature, 0.0, 120.0 ); + + double minSkill = m_Creature.MinTameSkill + (m_Creature.Owners.Count * 6.0); + + if ( minSkill > -24.9 && CheckMastery( m_Tamer, m_Creature ) ) + minSkill = -24.9; // 50% at 0.0? + + minSkill += 24.9; + + if ( alreadyOwned || m_Tamer.CheckTargetSkill( SkillName.AnimalTaming, m_Creature, minSkill - 25.0, minSkill + 25.0 ) ) + { + if ( m_Creature.Owners.Count == 0 ) // First tame + { + if ( m_Paralyzed ) + ScaleSkills( m_Creature, 0.86 ); // 86% of original skills if they were paralyzed during the taming + else + ScaleSkills( m_Creature, 0.90 ); // 90% of original skills + + if ( m_Creature.StatLossAfterTame ) + ScaleStats( m_Creature, 0.50 ); + + if( Core.SE ) + AdjustSkillCaps( m_Creature ); + } + + if ( alreadyOwned ) + { + m_Tamer.SendLocalizedMessage( 502797 ); // That wasn't even challenging. + } + else + { + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502799, m_Tamer.NetState ); // It seems to accept you as master. + m_Creature.Owners.Add( m_Tamer ); + } + + m_Creature.SetControlMaster( m_Tamer ); + m_Creature.IsBonded = false; + } + else + { + m_Creature.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 502798, m_Tamer.NetState ); // You fail to tame the creature. + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/ArmsLore.cs b/Scripts/Skills/ArmsLore.cs new file mode 100644 index 000000000..820781f64 --- /dev/null +++ b/Scripts/Skills/ArmsLore.cs @@ -0,0 +1,167 @@ +using System; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.SkillHandlers +{ + public class ArmsLore + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.ArmsLore].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse(Mobile m) + { + m.Target = new InternalTarget(); + + m.SendLocalizedMessage( 500349 ); // What item do you wish to get information about? + + return TimeSpan.FromSeconds( 1.0 ); + } + + [PlayerVendorTarget] + private class InternalTarget : Target + { + public InternalTarget() : base( 2, false, TargetFlags.None ) + { + AllowNonlocal = true; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is BaseWeapon ) + { + if ( from.CheckTargetSkill( SkillName.ArmsLore, targeted, 0, 100 ) ) + { + BaseWeapon weap = (BaseWeapon)targeted; + + if ( weap.MaxHitPoints != 0 ) + { + int hp = (int)((weap.HitPoints / (double)weap.MaxHitPoints) * 10); + + if ( hp < 0 ) + hp = 0; + else if ( hp > 9 ) + hp = 9; + + from.SendLocalizedMessage( 1038285 + hp ); + } + + int damage = (weap.MaxDamage + weap.MinDamage) / 2; + int hand = (weap.Layer == Layer.OneHanded ? 0 : 1); + + if ( damage < 3 ) + damage = 0; + else + damage = (int)Math.Ceiling( Math.Min( damage, 30 ) / 5.0 ); + /* + else if ( damage < 6 ) + damage = 1; + else if ( damage < 11 ) + damage = 2; + else if ( damage < 16 ) + damage = 3; + else if ( damage < 21 ) + damage = 4; + else if ( damage < 26 ) + damage = 5; + else + damage = 6; + * */ + + WeaponType type = weap.Type; + + if ( type == WeaponType.Ranged ) + from.SendLocalizedMessage( 1038224 + (damage * 9) ); + else if ( type == WeaponType.Piercing ) + from.SendLocalizedMessage( 1038218 + hand + (damage * 9) ); + else if ( type == WeaponType.Slashing ) + from.SendLocalizedMessage( 1038220 + hand + (damage * 9) ); + else if ( type == WeaponType.Bashing ) + from.SendLocalizedMessage( 1038222 + hand + (damage * 9) ); + else + from.SendLocalizedMessage( 1038216 + hand + (damage * 9) ); + + if ( weap.Poison != null && weap.PoisonCharges > 0 ) + from.SendLocalizedMessage( 1038284 ); // It appears to have poison smeared on it. + } + else + { + from.SendLocalizedMessage( 500353 ); // You are not certain... + } + } + else if(targeted is BaseArmor) + { + if( from.CheckTargetSkill(SkillName.ArmsLore, targeted, 0, 100) ) + { + BaseArmor arm = (BaseArmor)targeted; + + if ( arm.MaxHitPoints != 0 ) + { + int hp = (int)((arm.HitPoints / (double)arm.MaxHitPoints) * 10); + + if ( hp < 0 ) + hp = 0; + else if ( hp > 9 ) + hp = 9; + + from.SendLocalizedMessage( 1038285 + hp ); + } + + + from.SendLocalizedMessage( 1038295 + (int)Math.Ceiling( Math.Min( arm.ArmorRating, 35 ) / 5.0 ) ); + /* + if ( arm.ArmorRating < 1 ) + from.SendLocalizedMessage( 1038295 ); // This armor offers no defense against attackers. + else if ( arm.ArmorRating < 6 ) + from.SendLocalizedMessage( 1038296 ); // This armor provides almost no protection. + else if ( arm.ArmorRating < 11 ) + from.SendLocalizedMessage( 1038297 ); // This armor provides very little protection. + else if ( arm.ArmorRating < 16 ) + from.SendLocalizedMessage( 1038298 ); // This armor offers some protection against blows. + else if ( arm.ArmorRating < 21 ) + from.SendLocalizedMessage( 1038299 ); // This armor serves as sturdy protection. + else if ( arm.ArmorRating < 26 ) + from.SendLocalizedMessage( 1038300 ); // This armor is a superior defense against attack. + else if ( arm.ArmorRating < 31 ) + from.SendLocalizedMessage( 1038301 ); // This armor offers excellent protection. + else + from.SendLocalizedMessage( 1038302 ); // This armor is superbly crafted to provide maximum protection. + * */ + } + else + { + from.SendLocalizedMessage( 500353 ); // You are not certain... + } + } + else if ( targeted is SwampDragon && ((SwampDragon)targeted).HasBarding ) + { + SwampDragon pet = (SwampDragon)targeted; + + if ( from.CheckTargetSkill( SkillName.ArmsLore, targeted, 0, 100 ) ) + { + int perc = (4 * pet.BardingHP) / pet.BardingMaxHP; + + if ( perc < 0 ) + perc = 0; + else if ( perc > 4 ) + perc = 4; + + pet.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1053021 - perc, from.NetState ); + } + else + { + from.SendLocalizedMessage( 500353 ); // You are not certain... + } + } + else + { + from.SendLocalizedMessage( 500352 ); // This is neither weapon nor armor. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Begging.cs b/Scripts/Skills/Begging.cs new file mode 100644 index 000000000..9422eb63c --- /dev/null +++ b/Scripts/Skills/Begging.cs @@ -0,0 +1,175 @@ +using System; +using Server; +using Server.Misc; +using Server.Targeting; +using Server.Items; +using Server.Network; + +namespace Server.SkillHandlers +{ + public class Begging + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Begging].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.RevealingAction(); + + m.Target = new InternalTarget(); + m.RevealingAction(); + + m.SendLocalizedMessage( 500397 ); // To whom do you wish to grovel? + + return TimeSpan.FromHours( 6.0 ); + } + + private class InternalTarget : Target + { + private bool m_SetSkillTime = true; + + public InternalTarget() : base ( 12, false, TargetFlags.None ) + { + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( m_SetSkillTime ) + from.NextSkillTime = DateTime.Now; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + from.RevealingAction(); + + int number = -1; + + if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + + if ( targ.Player ) // We can't beg from players + { + number = 500398; // Perhaps just asking would work better. + } + else if ( !targ.Body.IsHuman ) // Make sure the NPC is human + { + number = 500399; // There is little chance of getting money from that! + } + else if ( !from.InRange( targ, 2 ) ) + { + if ( !targ.Female ) + number = 500401; // You are too far away to beg from him. + else + number = 500402; // You are too far away to beg from her. + } + else if ( from.Mounted ) // If we're on a mount, who would give us money? + { + number = 500404; // They seem unwilling to give you any money. + } + else + { + // Face eachother + from.Direction = from.GetDirectionTo( targ ); + targ.Direction = targ.GetDirectionTo( from ); + + from.Animate( 32, 5, 1, true, false, 0 ); // Bow + + new InternalTimer( from, targ ).Start(); + + m_SetSkillTime = false; + } + } + else // Not a Mobile + { + number = 500399; // There is little chance of getting money from that! + } + + if ( number != -1 ) + from.SendLocalizedMessage( number ); + } + + private class InternalTimer : Timer + { + private Mobile m_From, m_Target; + + public InternalTimer( Mobile from, Mobile target ) : base( TimeSpan.FromSeconds( 2.0 ) ) + { + m_From = from; + m_Target = target; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + Container theirPack = m_Target.Backpack; + + double badKarmaChance = 0.5 - ((double)m_From.Karma / 8570); + + if ( theirPack == null ) + { + m_From.SendLocalizedMessage( 500404 ); // They seem unwilling to give you any money. + } + else if ( m_From.Karma < 0 && badKarmaChance > Utility.RandomDouble() ) + { + m_Target.PublicOverheadMessage( MessageType.Regular, m_Target.SpeechHue, 500406 ); // Thou dost not look trustworthy... no gold for thee today! + } + else if ( m_From.CheckTargetSkill( SkillName.Begging, m_Target, 0.0, 100.0 ) ) + { + int toConsume = theirPack.GetAmount( typeof( Gold ) ) / 10; + int max = 10 + (m_From.Fame / 2500); + + if ( max > 14 ) + max = 14; + else if ( max < 10 ) + max = 10; + + if ( toConsume > max ) + toConsume = max; + + if ( toConsume > 0 ) + { + int consumed = theirPack.ConsumeUpTo( typeof( Gold ), toConsume ); + + if ( consumed > 0 ) + { + m_Target.PublicOverheadMessage( MessageType.Regular, m_Target.SpeechHue, 500405 ); // I feel sorry for thee... + + Gold gold = new Gold( consumed ); + + m_From.AddToBackpack( gold ); + m_From.PlaySound( gold.GetDropSound() ); + + if ( m_From.Karma > -3000 ) + { + int toLose = m_From.Karma + 3000; + + if ( toLose > 40 ) + toLose = 40; + + Titles.AwardKarma( m_From, -toLose, true ); + } + } + else + { + m_Target.PublicOverheadMessage( MessageType.Regular, m_Target.SpeechHue, 500407 ); // I have not enough money to give thee any! + } + } + else + { + m_Target.PublicOverheadMessage( MessageType.Regular, m_Target.SpeechHue, 500407 ); // I have not enough money to give thee any! + } + } + else + { + m_Target.SendLocalizedMessage( 500404 ); // They seem unwilling to give you any money. + } + + m_From.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/DetectHidden.cs b/Scripts/Skills/DetectHidden.cs new file mode 100644 index 000000000..b551f3b0c --- /dev/null +++ b/Scripts/Skills/DetectHidden.cs @@ -0,0 +1,116 @@ +using System; +using Server.Multis; +using Server.Targeting; +using Server.Items; +using Server.Regions; +using Server.Factions; + +namespace Server.SkillHandlers +{ + public class DetectHidden + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.DetectHidden].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile src ) + { + src.SendLocalizedMessage( 500819 );//Where will you search? + src.Target = new InternalTarget(); + + return TimeSpan.FromSeconds( 1.0 ); + } + + private class InternalTarget : Target + { + public InternalTarget() : base( 12, true, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile src, object targ ) + { + bool foundAnyone = false; + + Point3D p; + if ( targ is Mobile ) + p = ((Mobile)targ).Location; + else if ( targ is Item ) + p = ((Item)targ).Location; + else if ( targ is IPoint3D ) + p = new Point3D( (IPoint3D)targ ); + else + p = src.Location; + + double srcSkill = src.Skills[SkillName.DetectHidden].Value; + int range = (int)(srcSkill / 10.0); + + if ( !src.CheckSkill( SkillName.DetectHidden, 0.0, 100.0 ) ) + range /= 2; + + BaseHouse house = BaseHouse.FindHouseAt( p, src.Map, 16 ); + + bool inHouse = ( house != null && house.IsFriend( src ) ); + + if ( inHouse ) + range = 22; + + if ( range > 0 ) + { + IPooledEnumerable inRange = src.Map.GetMobilesInRange( p, range ); + + foreach ( Mobile trg in inRange ) + { + if ( trg.Hidden && src != trg ) + { + double ss = srcSkill + Utility.Random( 21 ) - 10; + double ts = trg.Skills[SkillName.Hiding].Value + Utility.Random( 21 ) - 10; + + if ( src.AccessLevel >= trg.AccessLevel && ( ss >= ts || ( inHouse && house.IsInside( trg ) ) ) ) + { + if ( trg is Mobiles.ShadowKnight && (trg.X != p.X || trg.Y != p.Y) ) + continue; + + trg.RevealingAction(); + trg.SendLocalizedMessage( 500814 ); // You have been revealed! + foundAnyone = true; + } + } + } + + inRange.Free(); + + if ( Faction.Find( src ) != null ) + { + IPooledEnumerable itemsInRange = src.Map.GetItemsInRange( p, range ); + + foreach ( Item item in itemsInRange ) + { + if ( item is BaseFactionTrap ) + { + BaseFactionTrap trap = (BaseFactionTrap) item; + + if ( src.CheckTargetSkill( SkillName.DetectHidden, trap, 80.0, 100.0 ) ) + { + src.SendLocalizedMessage( 1042712, true, " " + (trap.Faction == null ? "" : trap.Faction.Definition.FriendlyName) ); // You reveal a trap placed by a faction: + + trap.Visible = true; + trap.BeginConceal(); + + foundAnyone = true; + } + } + } + + itemsInRange.Free(); + } + } + + if ( !foundAnyone ) + { + src.SendLocalizedMessage( 500817 ); // You can see nothing hidden there. + } + } + } + } +} diff --git a/Scripts/Skills/Discordance.cs b/Scripts/Skills/Discordance.cs new file mode 100644 index 000000000..55059a3a1 --- /dev/null +++ b/Scripts/Skills/Discordance.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections; +using Server.Items; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.SkillHandlers +{ + public class Discordance + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Discordance].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.RevealingAction(); + + BaseInstrument.PickInstrument( m, new InstrumentPickedCallback( OnPickedInstrument ) ); + + return TimeSpan.FromSeconds( 1.0 ); // Cannot use another skill for 1 second + } + + public static void OnPickedInstrument( Mobile from, BaseInstrument instrument ) + { + from.RevealingAction(); + from.SendLocalizedMessage( 1049541 ); // Choose the target for your song of discordance. + from.Target = new DiscordanceTarget( from, instrument ); + } + + private class DiscordanceInfo + { + public Mobile m_From; + public Mobile m_Creature; + public DateTime m_EndTime; + public Timer m_Timer; + public int m_Effect; + public ArrayList m_Mods; + + public DiscordanceInfo( Mobile from, Mobile creature, TimeSpan duration, int effect, ArrayList mods ) + { + m_From = from; + m_Creature = creature; + m_EndTime = DateTime.Now + duration; + m_Effect = effect; + m_Mods = mods; + + Apply(); + } + + public void Apply() + { + for ( int i = 0; i < m_Mods.Count; ++i ) + { + object mod = m_Mods[i]; + + if ( mod is ResistanceMod ) + m_Creature.AddResistanceMod( (ResistanceMod) mod ); + else if ( mod is StatMod ) + m_Creature.AddStatMod( (StatMod) mod ); + else if ( mod is SkillMod ) + m_Creature.AddSkillMod( (SkillMod) mod ); + } + } + + public void Clear() + { + for ( int i = 0; i < m_Mods.Count; ++i ) + { + object mod = m_Mods[i]; + + if ( mod is ResistanceMod ) + m_Creature.RemoveResistanceMod( (ResistanceMod) mod ); + else if ( mod is StatMod ) + m_Creature.RemoveStatMod( ((StatMod) mod).Name ); + else if ( mod is SkillMod ) + m_Creature.RemoveSkillMod( (SkillMod) mod ); + } + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool GetEffect( Mobile targ, ref int effect ) + { + DiscordanceInfo info = m_Table[targ] as DiscordanceInfo; + + if ( info == null ) + return false; + + effect = info.m_Effect; + return true; + } + + private static void ProcessDiscordance( object state ) + { + DiscordanceInfo info = (DiscordanceInfo)state; + Mobile from = info.m_From; + Mobile targ = info.m_Creature; + + if ( DateTime.Now >= info.m_EndTime || targ.Deleted || from.Map != targ.Map || targ.GetDistanceToSqrt( from ) > 16 ) + { + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + info.Clear(); + m_Table.Remove( targ ); + } + else + { + targ.FixedEffect( 0x376A, 1, 32 ); + } + } + + public class DiscordanceTarget : Target + { + private BaseInstrument m_Instrument; + + public DiscordanceTarget( Mobile from, BaseInstrument inst ) : base( BaseInstrument.GetBardRange( from, SkillName.Discordance ), false, TargetFlags.Harmful ) + { + m_Instrument = inst; + } + + protected override void OnTarget( Mobile from, object target ) + { + from.RevealingAction(); + + if ( !m_Instrument.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062488 ); // The instrument you are trying to play is no longer in your backpack! + } + else if ( target is Mobile ) + { + Mobile targ = (Mobile)target; + + if ( targ == from || (targ is BaseCreature && ( ((BaseCreature)targ).BardImmune || !from.CanBeHarmful( targ, false ) )) ) + { + from.SendLocalizedMessage( 1049535 ); // A song of discord would have no effect on that. + } + else if ( !targ.Player ) + { + TimeSpan len = TimeSpan.FromSeconds( from.Skills[SkillName.Discordance].Value * 2 ); + double diff = m_Instrument.GetDifficultyFor( targ ) - 10.0; + double music = from.Skills[SkillName.Musicianship].Value; + + if ( music > 100.0 ) + diff -= (music - 100.0) * 0.5; + + if ( !BaseInstrument.CheckMusicianship( from ) ) + { + from.SendLocalizedMessage( 500612 ); // You play poorly, and there is no effect. + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else if ( from.CheckTargetSkill( SkillName.Discordance, target, diff-25.0, diff+25.0 ) ) + { + if ( !m_Table.Contains( targ ) ) + { + from.SendLocalizedMessage( 1049539 ); // You play the song surpressing your targets strength + m_Instrument.PlayInstrumentWell( from ); + m_Instrument.ConsumeUse( from ); + + ArrayList mods = new ArrayList(); + int effect; + double scalar; + + if ( Core.AOS ) + { + double discord = from.Skills[SkillName.Discordance].Value; + + if ( discord > 100.0 ) + effect = -20 + (int)((discord - 100.0) / -2.5); + else + effect = (int)(discord / -5.0); + + if ( Core.SE && BaseInstrument.GetBaseDifficulty( targ ) >= 160.0 ) + effect /= 2; + + scalar = effect * 0.01; + + mods.Add( new ResistanceMod( ResistanceType.Physical, effect ) ); + mods.Add( new ResistanceMod( ResistanceType.Fire, effect ) ); + mods.Add( new ResistanceMod( ResistanceType.Cold, effect ) ); + mods.Add( new ResistanceMod( ResistanceType.Poison, effect ) ); + mods.Add( new ResistanceMod( ResistanceType.Energy, effect ) ); + + for ( int i = 0; i < targ.Skills.Length; ++i ) + { + if ( targ.Skills[i].Value > 0 ) + mods.Add( new DefaultSkillMod( (SkillName)i, true, targ.Skills[i].Value * scalar ) ); + } + } + else + { + effect = (int)( from.Skills[SkillName.Discordance].Value / -5.0 ); + scalar = effect * 0.01; + + mods.Add( new StatMod( StatType.Str, "DiscordanceStr", (int)(targ.RawStr * scalar), TimeSpan.Zero ) ); + mods.Add( new StatMod( StatType.Int, "DiscordanceInt", (int)(targ.RawInt * scalar), TimeSpan.Zero ) ); + mods.Add( new StatMod( StatType.Dex, "DiscordanceDex", (int)(targ.RawDex * scalar), TimeSpan.Zero ) ); + + for ( int i = 0; i < targ.Skills.Length; ++i ) + { + if ( targ.Skills[i].Value > 0 ) + mods.Add( new DefaultSkillMod( (SkillName)i, true, targ.Skills[i].Value * scalar ) ); + } + } + + DiscordanceInfo info = new DiscordanceInfo( from, targ, len, Math.Abs( effect ), mods ); + info.m_Timer = Timer.DelayCall( TimeSpan.Zero, TimeSpan.FromSeconds( 1.25 ), new TimerStateCallback( ProcessDiscordance ), info ); + + m_Table[targ] = info; + } + else + { + from.SendLocalizedMessage( 1049537 );// Your target is already in discord. + } + } + else + { + from.SendLocalizedMessage( 1049540 );// You fail to disrupt your target + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + } + else + { + m_Instrument.PlayInstrumentBadly( from ); + } + } + else + { + from.SendLocalizedMessage( 1049535 ); // A song of discord would have no effect on that. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/EvalInt.cs b/Scripts/Skills/EvalInt.cs new file mode 100644 index 000000000..2a4d25e1f --- /dev/null +++ b/Scripts/Skills/EvalInt.cs @@ -0,0 +1,89 @@ +using System; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.SkillHandlers +{ + public class EvalInt + { + public static void Initialize() + { + SkillInfo.Table[16].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.Target = new EvalInt.InternalTarget(); + + m.SendLocalizedMessage( 500906 ); // What do you wish to evaluate? + + return TimeSpan.FromSeconds( 1.0 ); + } + + private class InternalTarget : Target + { + public InternalTarget() : base ( 8, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( from == targeted ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500910 ); // Hmm, that person looks really silly. + } + else if ( targeted is TownCrier ) + { + ((TownCrier)targeted).PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500907, from.NetState ); // He looks smart enough to remember the news. Ask him about it. + } + else if ( targeted is BaseVendor && ((BaseVendor)targeted).IsInvulnerable ) + { + ((BaseVendor)targeted).PrivateOverheadMessage( MessageType.Regular, 0x3B2, 500909, from.NetState ); // That person could probably calculate the cost of what you buy from them. + } + else if ( targeted is Mobile ) + { + Mobile targ = (Mobile)targeted; + + int marginOfError = Math.Max( 0, 20 - (int)(from.Skills[SkillName.EvalInt].Value / 5) ); + + int intel = targ.Int + Utility.RandomMinMax( -marginOfError, +marginOfError ); + int mana = ((targ.Mana * 100) / Math.Max( targ.ManaMax, 1 )) + Utility.RandomMinMax( -marginOfError, +marginOfError ); + + int intMod = intel / 10; + int mnMod = mana / 10; + + if ( intMod > 10 ) intMod = 10; + else if ( intMod < 0 ) intMod = 0; + + if ( mnMod > 10 ) mnMod = 10; + else if ( mnMod < 0 ) mnMod = 0; + + int body; + + if ( targ.Body.IsHuman ) + body = targ.Female ? 11 : 0; + else + body = 22; + + if ( from.CheckTargetSkill( SkillName.EvalInt, targ, 0.0, 120.0 ) ) + { + targ.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1038169 + intMod + body, from.NetState ); // He/She/It looks [slighly less intelligent than a rock.] [Of Average intellect] [etc...] + + if ( from.Skills[SkillName.EvalInt].Base >= 76.0 ) + targ.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1038202 + mnMod, from.NetState ); // That being is at [10,20,...] percent mental strength. + } + else + { + targ.PrivateOverheadMessage( MessageType.Regular, 0x3B2, 1038166 + (body / 11), from.NetState ); // You cannot judge his/her/its mental abilities. + } + } + else if ( targeted is Item ) + { + ((Item)targeted).SendLocalizedMessageTo( from, 500908, "" ); // It looks smarter than a rock, but dumber than a piece of wood. + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/ForensicEval.cs b/Scripts/Skills/ForensicEval.cs new file mode 100644 index 000000000..36231b421 --- /dev/null +++ b/Scripts/Skills/ForensicEval.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.Text; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Items; + +namespace Server.SkillHandlers +{ + public class ForensicEvaluation + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Forensics].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.Target = new ForensicTarget(); + m.RevealingAction(); + + m.SendLocalizedMessage( 500906 ); // What would you like to evaluate? + + return TimeSpan.FromSeconds( 1.0 ); + } + + public class ForensicTarget : Target + { + public ForensicTarget() : base( 10, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object target ) + { + if ( target is Mobile ) + { + if ( from.CheckTargetSkill( SkillName.Forensics, target, 40.0, 100.0 ) ) + { + if ( target is PlayerMobile && ((PlayerMobile)target).NpcGuild == NpcGuild.ThievesGuild ) + from.SendLocalizedMessage( 501004 );//That individual is a thief! + else + from.SendLocalizedMessage( 501003 );//You notice nothing unusual. + } + else + { + from.SendLocalizedMessage( 501001 );//You cannot determain anything useful. + } + } + else if ( target is Corpse ) + { + if ( from.CheckTargetSkill( SkillName.Forensics, target, 0.0, 100.0 ) ) + { + Corpse c = (Corpse)target; + + if ( ((Body)c.Amount).IsHuman ) + c.LabelTo( from, 1042751, ( c.Killer == null ? "no one" : c.Killer.Name ) );//This person was killed by ~1_KILLER_NAME~ + + if ( c.Looters.Count > 0 ) + { + StringBuilder sb = new StringBuilder(); + for (int i=0;i0 ) + sb.Append( ", " ); + sb.Append( ((Mobile)c.Looters[i]).Name ); + } + + c.LabelTo( from, 1042752, sb.ToString() );//This body has been distrubed by ~1_PLAYER_NAMES~ + } + else + { + c.LabelTo( from, 501002 );//The corpse has not be desecrated. + } + } + else + { + from.SendLocalizedMessage( 501001 );//You cannot determain anything useful. + } + } + else if ( target is ILockpickable ) + { + ILockpickable p = (ILockpickable)target; + if ( p.Picker != null ) + from.SendLocalizedMessage( 1042749, p.Picker.Name );//This lock was opened by ~1_PICKER_NAME~ + else + from.SendLocalizedMessage( 501003 );//You notice nothing unusual. + } + } + } + } +} diff --git a/Scripts/Skills/Hiding.cs b/Scripts/Skills/Hiding.cs new file mode 100644 index 000000000..c8d32197a --- /dev/null +++ b/Scripts/Skills/Hiding.cs @@ -0,0 +1,108 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; +using Server.Multis; + +namespace Server.SkillHandlers +{ + public class Hiding + { + private static bool m_CombatOverride; + + public static bool CombatOverride + { + get{ return m_CombatOverride; } + set{ m_CombatOverride = value; } + } + + public static void Initialize() + { + SkillInfo.Table[21].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + if ( m.Target != null || m.Spell != null ) + { + m.SendLocalizedMessage( 501238 ); // You are busy doing something else and cannot hide. + return TimeSpan.FromSeconds( 1.0 ); + } + + double bonus = 0.0; + + BaseHouse house = BaseHouse.FindHouseAt( m ); + + if ( house != null && house.IsFriend( m ) ) + { + bonus = 100.0; + } + else if ( !Core.AOS ) + { + if ( house == null ) + house = BaseHouse.FindHouseAt( new Point3D( m.X - 1, m.Y, 127 ), m.Map, 16 ); + + if ( house == null ) + house = BaseHouse.FindHouseAt( new Point3D( m.X + 1, m.Y, 127 ), m.Map, 16 ); + + if ( house == null ) + house = BaseHouse.FindHouseAt( new Point3D( m.X, m.Y - 1, 127 ), m.Map, 16 ); + + if ( house == null ) + house = BaseHouse.FindHouseAt( new Point3D( m.X, m.Y + 1, 127 ), m.Map, 16 ); + + if ( house != null ) + bonus = 50.0; + } + + //int range = 18 - (int)(m.Skills[SkillName.Hiding].Value / 10); + int range = Math.Min( (int)((100 - m.Skills[SkillName.Hiding].Value)/2) + 8, 18 ); //Cap of 18 not OSI-exact, intentional difference + + bool badCombat = ( !m_CombatOverride && m.Combatant != null && m.InRange( m.Combatant.Location, range ) && m.Combatant.InLOS( m ) ); + bool ok = ( !badCombat /*&& m.CheckSkill( SkillName.Hiding, 0.0 - bonus, 100.0 - bonus )*/ ); + + if ( ok ) + { + if ( !m_CombatOverride ) + { + foreach ( Mobile check in m.GetMobilesInRange( range ) ) + { + if ( check.InLOS( m ) && check.Combatant == m ) + { + badCombat = true; + ok = false; + break; + } + } + } + + ok = ( !badCombat && m.CheckSkill( SkillName.Hiding, 0.0 - bonus, 100.0 - bonus ) ); + } + + if ( badCombat ) + { + m.RevealingAction(); + + m.LocalOverheadMessage( MessageType.Regular, 0x22, 501237 ); // You can't seem to hide right now. + + return TimeSpan.FromSeconds( 1.0 ); + } + else + { + if ( ok ) + { + m.Hidden = true; + m.LocalOverheadMessage( MessageType.Regular, 0x1F4, 501240 ); // You have hidden yourself well. + } + else + { + m.RevealingAction(); + + m.LocalOverheadMessage( MessageType.Regular, 0x22, 501241 ); // You can't seem to hide here. + } + + return TimeSpan.FromSeconds( 10.0 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Inscribe.cs b/Scripts/Skills/Inscribe.cs new file mode 100644 index 000000000..40f5bb9c6 --- /dev/null +++ b/Scripts/Skills/Inscribe.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Items; + +namespace Server.SkillHandlers +{ + public class Inscribe + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Inscribe].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + Target target = new InternalTargetSrc(); + m.Target = target; + m.SendLocalizedMessage( 1046295 ); // Target the book you wish to copy. + target.BeginTimeout( m, TimeSpan.FromMinutes( 1.0 ) ); + + return TimeSpan.FromSeconds( 1.0 ); + } + + private static Hashtable m_UseTable = new Hashtable(); + + private static void SetUser( BaseBook book, Mobile mob ) + { + m_UseTable[book] = mob; + } + + private static void CancelUser( BaseBook book ) + { + m_UseTable.Remove( book ); + } + + public static Mobile GetUser( BaseBook book ) + { + return (Mobile)m_UseTable[book]; + } + + public static bool IsEmpty( BaseBook book ) + { + foreach ( BookPageInfo page in book.Pages ) + { + foreach ( string line in page.Lines ) + { + if ( line.Trim() != "" ) + return false; + } + } + return true; + } + + public static void Copy( BaseBook bookSrc, BaseBook bookDst ) + { + bookDst.Title = bookSrc.Title; + bookDst.Author = bookSrc.Author; + + BookPageInfo[] pagesSrc = bookSrc.Pages; + BookPageInfo[] pagesDst = bookDst.Pages; + for ( int i = 0; i < pagesSrc.Length && i < pagesDst.Length; i++ ) + { + BookPageInfo pageSrc = pagesSrc[i]; + BookPageInfo pageDst = pagesDst[i]; + + int length = pageSrc.Lines.Length; + pageDst.Lines = new string[length]; + + for ( int j = 0; j < length; j++ ) + pageDst.Lines[j] = pageSrc.Lines[j]; + } + } + + private class InternalTargetSrc : Target + { + public InternalTargetSrc() : base ( 3, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + BaseBook book = targeted as BaseBook; + if ( book == null ) + from.SendLocalizedMessage( 1046296 ); // That is not a book + else if ( Inscribe.IsEmpty( book ) ) + from.SendLocalizedMessage( 501611 ); // Can't copy an empty book. + else if ( Inscribe.GetUser( book ) != null ) + from.SendLocalizedMessage( 501621 ); // Someone else is inscribing that item. + else + { + Target target = new InternalTargetDst( book ); + from.Target = target; + from.SendLocalizedMessage( 501612 ); // Select a book to copy this to. + target.BeginTimeout( from, TimeSpan.FromMinutes( 1.0 ) ); + Inscribe.SetUser( book, from ); + } + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + if ( cancelType == TargetCancelType.Timeout ) + from.SendLocalizedMessage( 501619 ); // You have waited too long to make your inscribe selection, your inscription attempt has timed out. + } + } + + private class InternalTargetDst : Target + { + private BaseBook m_BookSrc; + + public InternalTargetDst( BaseBook bookSrc ) : base ( 3, false, TargetFlags.None ) + { + m_BookSrc = bookSrc; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_BookSrc.Deleted ) + return; + + BaseBook bookDst = targeted as BaseBook; + + if ( bookDst == null ) + from.SendLocalizedMessage( 1046296 ); // That is not a book + else if ( Inscribe.IsEmpty( m_BookSrc ) ) + from.SendLocalizedMessage( 501611 ); // Can't copy an empty book. + else if ( bookDst == m_BookSrc ) + from.SendLocalizedMessage( 501616 ); // Cannot copy a book onto itself. + else if ( !bookDst.Writable ) + from.SendLocalizedMessage( 501614 ); // Cannot write into that book. + else if ( Inscribe.GetUser( bookDst ) != null ) + from.SendLocalizedMessage( 501621 ); // Someone else is inscribing that item. + else + { + if ( from.CheckTargetSkill( SkillName.Inscribe, bookDst, 0, 50 ) ) + { + Inscribe.Copy( m_BookSrc, bookDst ); + + from.SendLocalizedMessage( 501618 ); // You make a copy of the book. + from.PlaySound( 0x249 ); + } + else + { + from.SendLocalizedMessage( 501617 ); // You fail to make a copy of the book. + } + } + } + + protected override void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + if ( cancelType == TargetCancelType.Timeout ) + from.SendLocalizedMessage( 501619 ); // You have waited too long to make your inscribe selection, your inscription attempt has timed out. + } + + protected override void OnTargetFinish( Mobile from ) + { + Inscribe.CancelUser( m_BookSrc ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/ItemIdentification.cs b/Scripts/Skills/ItemIdentification.cs new file mode 100644 index 000000000..7a086a979 --- /dev/null +++ b/Scripts/Skills/ItemIdentification.cs @@ -0,0 +1,61 @@ +using System; +using Server; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Items +{ + public class ItemIdentification + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.ItemID].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile from ) + { + from.SendLocalizedMessage( 500343 ); // What do you wish to appraise and identify? + from.Target = new InternalTarget(); + + return TimeSpan.FromSeconds( 1.0 ); + } + + [PlayerVendorTarget] + private class InternalTarget : Target + { + public InternalTarget() : base ( 8, false, TargetFlags.None ) + { + AllowNonlocal = true; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Item ) + { + if ( from.CheckTargetSkill( SkillName.ItemID, o, 0, 100 ) ) + { + if ( o is BaseWeapon ) + ((BaseWeapon)o).Identified = true; + else if ( o is BaseArmor ) + ((BaseArmor)o).Identified = true; + + if ( !Core.AOS ) + ((Item)o).OnSingleClick( from ); + } + else + { + from.SendLocalizedMessage( 500353 ); // You are not certain... + } + } + else if ( o is Mobile ) + { + ((Mobile)o).OnSingleClick( from ); + } + else + { + from.SendLocalizedMessage( 500353 ); // You are not certain... + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Meditation.cs b/Scripts/Skills/Meditation.cs new file mode 100644 index 000000000..95ee6d3b1 --- /dev/null +++ b/Scripts/Skills/Meditation.cs @@ -0,0 +1,101 @@ +using System; +using Server.Items; + +namespace Server.SkillHandlers +{ + class Meditation + { + public static void Initialize() + { + SkillInfo.Table[46].Callback = new SkillUseCallback( OnUse ); + } + + public static bool CheckOkayHolding( Item item ) + { + if ( item == null ) + return true; + + if ( item is Spellbook || item is Runebook ) + return true; + + if ( Core.AOS && item is BaseWeapon && ((BaseWeapon)item).Attributes.SpellChanneling != 0 ) + return true; + + if ( Core.AOS && item is BaseArmor && ((BaseArmor)item).Attributes.SpellChanneling != 0 ) + return true; + + return false; + } + + public static TimeSpan OnUse( Mobile m ) + { + m.RevealingAction(); + + if ( m.Target != null ) + { + m.SendLocalizedMessage( 501845 ); // You are busy doing something else and cannot focus. + + return TimeSpan.FromSeconds( 5.0 ); + } + else if ( !Core.AOS && m.Hits < (m.HitsMax / 10) ) // Less than 10% health + { + m.SendLocalizedMessage( 501849 ); // The mind is strong but the body is weak. + + return TimeSpan.FromSeconds( 5.0 ); + } + else if ( m.Mana >= m.ManaMax ) + { + m.SendLocalizedMessage( 501846 ); // You are at peace. + + return TimeSpan.FromSeconds( Core.AOS ? 10.0 : 5.0 ); + } + else if ( Core.AOS && Server.Misc.RegenRates.GetArmorOffset( m ) > 0 ) + { + m.SendLocalizedMessage( 500135 ); // Regenative forces cannot penetrate your armor! + + return TimeSpan.FromSeconds( 10.0 ); + } + else + { + Item oneHanded = m.FindItemOnLayer( Layer.OneHanded ); + Item twoHanded = m.FindItemOnLayer( Layer.TwoHanded ); + + if ( Core.AOS ) + { + if ( !CheckOkayHolding( oneHanded ) ) + m.AddToBackpack( oneHanded ); + + if ( !CheckOkayHolding( twoHanded ) ) + m.AddToBackpack( twoHanded ); + } + else if ( !CheckOkayHolding( oneHanded ) || !CheckOkayHolding( twoHanded ) ) + { + m.SendLocalizedMessage( 502626 ); // Your hands must be free to cast spells or meditate. + + return TimeSpan.FromSeconds( 2.5 ); + } + + double skillVal = m.Skills[SkillName.Meditation].Value; + double chance = (50.0 + (( skillVal - ( m.ManaMax - m.Mana ) ) * 2)) / 100; + + if ( chance > Utility.RandomDouble() ) + { + m.CheckSkill( SkillName.Meditation, 0.0, 100.0 ); + + m.SendLocalizedMessage( 501851 ); // You enter a meditative trance. + m.Meditating = true; + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.ActiveMeditation, 1075657 ) ); + + if ( m.Player || m.Body.IsHuman ) + m.PlaySound( 0xF9 ); + } + else + { + m.SendLocalizedMessage( 501850 ); // You cannot focus your concentration. + } + + return TimeSpan.FromSeconds( 10.0 ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Peacemaking.cs b/Scripts/Skills/Peacemaking.cs new file mode 100644 index 000000000..f1967583b --- /dev/null +++ b/Scripts/Skills/Peacemaking.cs @@ -0,0 +1,199 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; +using Server.Items; + +namespace Server.SkillHandlers +{ + public class Peacemaking + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Peacemaking].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.RevealingAction(); + + BaseInstrument.PickInstrument( m, new InstrumentPickedCallback( OnPickedInstrument ) ); + + return TimeSpan.FromSeconds( 1.0 ); // Cannot use another skill for 1 second + } + + public static void OnPickedInstrument( Mobile from, BaseInstrument instrument ) + { + from.RevealingAction(); + from.SendLocalizedMessage( 1049525 ); // Whom do you wish to calm? + from.Target = new InternalTarget( from, instrument ); + from.NextSkillTime = DateTime.Now + TimeSpan.FromHours( 6.0 ); + } + + private class InternalTarget : Target + { + private BaseInstrument m_Instrument; + private bool m_SetSkillTime = true; + + public InternalTarget( Mobile from, BaseInstrument instrument ) : base( BaseInstrument.GetBardRange( from, SkillName.Peacemaking ), false, TargetFlags.None ) + { + m_Instrument = instrument; + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( m_SetSkillTime ) + from.NextSkillTime = DateTime.Now; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + from.RevealingAction(); + + if ( !(targeted is Mobile) ) + { + from.SendLocalizedMessage( 1049528 ); // You cannot calm that! + } + else if ( !m_Instrument.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062488 ); // The instrument you are trying to play is no longer in your backpack! + } + else + { + m_SetSkillTime = false; + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + + if ( targeted == from ) + { + // Standard mode : reset combatants for everyone in the area + + if ( !BaseInstrument.CheckMusicianship( from ) ) + { + from.SendLocalizedMessage( 500612 ); // You play poorly, and there is no effect. + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else if ( !from.CheckSkill( SkillName.Peacemaking, 0.0, 120.0 ) ) + { + from.SendLocalizedMessage( 500613 ); // You attempt to calm everyone, but fail. + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else + { + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 5.0 ); + m_Instrument.PlayInstrumentWell( from ); + m_Instrument.ConsumeUse( from ); + + Map map = from.Map; + + if ( map != null ) + { + int range = BaseInstrument.GetBardRange( from, SkillName.Peacemaking ); + + bool calmed = false; + + foreach ( Mobile m in from.GetMobilesInRange( range ) ) + { + if ( (m is BaseCreature && ((BaseCreature)m).Uncalmable) || m == from || !from.CanBeHarmful( m, false ) ) + continue; + + calmed = true; + + m.SendLocalizedMessage( 500616 ); // You hear lovely music, and forget to continue battling! + m.Combatant = null; + m.Warmode = false; + + if ( m is BaseCreature && !((BaseCreature)m).BardPacified ) + ((BaseCreature)m).Pacify( from, DateTime.Now + TimeSpan.FromSeconds( 1.0 ) ); + } + + if ( !calmed ) + from.SendLocalizedMessage( 1049648 ); // You play hypnotic music, but there is nothing in range for you to calm. + else + from.SendLocalizedMessage( 500615 ); // You play your hypnotic music, stopping the battle. + } + } + } + else + { + // Target mode : pacify a single target for a longer duration + + Mobile targ = (Mobile)targeted; + + if ( !from.CanBeHarmful( targ, false ) ) + { + from.SendLocalizedMessage( 1049528 ); + m_SetSkillTime = true; + } + else if ( targ is BaseCreature && ((BaseCreature)targ).Uncalmable ) + { + from.SendLocalizedMessage( 1049526 ); // You have no chance of calming that creature. + m_SetSkillTime = true; + } + else if ( targ is BaseCreature && ((BaseCreature)targ).BardPacified ) + { + from.SendLocalizedMessage( 1049527 ); // That creature is already being calmed. + m_SetSkillTime = true; + } + else if ( !BaseInstrument.CheckMusicianship( from ) ) + { + from.SendLocalizedMessage( 500612 ); // You play poorly, and there is no effect. + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 5.0 ); + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else + { + double diff = m_Instrument.GetDifficultyFor( targ ) - 10.0; + double music = from.Skills[SkillName.Musicianship].Value; + + if ( music > 100.0 ) + diff -= (music - 100.0) * 0.5; + + if ( !from.CheckTargetSkill( SkillName.Peacemaking, targ, diff - 25.0, diff + 25.0 ) ) + { + from.SendLocalizedMessage( 1049531 ); // You attempt to calm your target, but fail. + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else + { + m_Instrument.PlayInstrumentWell( from ); + m_Instrument.ConsumeUse( from ); + + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 5.0 ); + if ( targ is BaseCreature ) + { + BaseCreature bc = (BaseCreature)targ; + + from.SendLocalizedMessage( 1049532 ); // You play hypnotic music, calming your target. + + targ.Combatant = null; + targ.Warmode = false; + + double seconds = 100 - (diff / 1.5); + + if ( seconds > 120 ) + seconds = 120; + else if ( seconds < 10 ) + seconds = 10; + + bc.Pacify( from, DateTime.Now + TimeSpan.FromSeconds( seconds ) ); + } + else + { + from.SendLocalizedMessage( 1049532 ); // You play hypnotic music, calming your target. + + targ.SendLocalizedMessage( 500616 ); // You hear lovely music, and forget to continue battling! + targ.Combatant = null; + targ.Warmode = false; + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Poisoning.cs b/Scripts/Skills/Poisoning.cs new file mode 100644 index 000000000..57d695998 --- /dev/null +++ b/Scripts/Skills/Poisoning.cs @@ -0,0 +1,171 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; + +namespace Server.SkillHandlers +{ + public class Poisoning + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Poisoning].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.Target = new InternalTargetPoison(); + + m.SendLocalizedMessage( 502137 ); // Select the poison you wish to use + + return TimeSpan.FromSeconds( 10.0 ); // 10 second delay before beign able to re-use a skill + } + + private class InternalTargetPoison : Target + { + public InternalTargetPoison() : base ( 2, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is BasePoisonPotion ) + { + from.SendLocalizedMessage( 502142 ); // To what do you wish to apply the poison? + from.Target = new InternalTarget( (BasePoisonPotion)targeted ); + } + else // Not a Poison Potion + { + from.SendLocalizedMessage( 502139 ); // That is not a poison potion. + } + } + + private class InternalTarget : Target + { + private BasePoisonPotion m_Potion; + + public InternalTarget( BasePoisonPotion potion ) : base ( 2, false, TargetFlags.None ) + { + m_Potion = potion; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Potion.Deleted ) + return; + + bool startTimer = false; + + if ( targeted is Food || targeted is FukiyaDarts || targeted is Shuriken ) + { + startTimer = true; + } + else if ( targeted is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)targeted; + + if ( Core.AOS ) + { + startTimer = ( weapon.PrimaryAbility == WeaponAbility.InfectiousStrike || weapon.SecondaryAbility == WeaponAbility.InfectiousStrike ); + } + else if ( weapon.Layer == Layer.OneHanded ) + { + // Only Bladed or Piercing weapon can be poisoned + startTimer = ( weapon.Type == WeaponType.Slashing || weapon.Type == WeaponType.Piercing ); + } + } + + if ( startTimer ) + { + new InternalTimer( from, (Item)targeted, m_Potion ).Start(); + + from.PlaySound( 0x4F ); + + m_Potion.Consume(); + from.AddToBackpack( new Bottle() ); + } + else // Target can't be poisoned + { + if ( Core.AOS ) + from.SendLocalizedMessage( 1060204 ); // You cannot poison that! You can only poison infectious weapons, food or drink. + else + from.SendLocalizedMessage( 502145 ); // You cannot poison that! You can only poison bladed or piercing weapons, food or drink. + } + } + + private class InternalTimer : Timer + { + private Mobile m_From; + private Item m_Target; + private Poison m_Poison; + private double m_MinSkill, m_MaxSkill; + + public InternalTimer( Mobile from, Item target, BasePoisonPotion potion ) : base( TimeSpan.FromSeconds( 2.0 ) ) + { + m_From = from; + m_Target = target; + m_Poison = potion.Poison; + m_MinSkill = potion.MinPoisoningSkill; + m_MaxSkill = potion.MaxPoisoningSkill; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + if ( m_From.CheckTargetSkill( SkillName.Poisoning, m_Target, m_MinSkill, m_MaxSkill ) ) + { + if ( m_Target is Food ) + { + ((Food)m_Target).Poison = m_Poison; + } + else if ( m_Target is BaseWeapon ) + { + ((BaseWeapon)m_Target).Poison = m_Poison; + ((BaseWeapon)m_Target).PoisonCharges = 18 - (m_Poison.Level * 2); + } + else if ( m_Target is FukiyaDarts ) + { + ((FukiyaDarts)m_Target).Poison = m_Poison; + ((FukiyaDarts)m_Target).PoisonCharges = Math.Min( 18 - (m_Poison.Level * 2), ((FukiyaDarts)m_Target).UsesRemaining ); + } + else if ( m_Target is Shuriken ) + { + ((Shuriken)m_Target).Poison = m_Poison; + ((Shuriken)m_Target).PoisonCharges = Math.Min( 18 - (m_Poison.Level * 2), ((Shuriken)m_Target).UsesRemaining ); + } + + m_From.SendLocalizedMessage( 1010517 ); // You apply the poison + + Misc.Titles.AwardKarma( m_From, -20, true ); + } + else // Failed + { + // 5% of chance of getting poisoned if failed + if ( m_From.Skills[SkillName.Poisoning].Base < 80.0 && Utility.Random( 20 ) == 0 ) + { + m_From.SendLocalizedMessage( 502148 ); // You make a grave mistake while applying the poison. + m_From.ApplyPoison( m_From, m_Poison ); + } + else + { + if ( m_Target is BaseWeapon ) + { + BaseWeapon weapon = (BaseWeapon)m_Target; + + if ( weapon.Type == WeaponType.Slashing ) + m_From.SendLocalizedMessage( 1010516 ); // You fail to apply a sufficient dose of poison on the blade + else + m_From.SendLocalizedMessage( 1010518 ); // You fail to apply a sufficient dose of poison + } + else + { + m_From.SendLocalizedMessage( 1010518 ); // You fail to apply a sufficient dose of poison + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Provocation.cs b/Scripts/Skills/Provocation.cs new file mode 100644 index 000000000..a4abe4269 --- /dev/null +++ b/Scripts/Skills/Provocation.cs @@ -0,0 +1,160 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; +using Server.Items; + +namespace Server.SkillHandlers +{ + public class Provocation + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Provocation].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.RevealingAction(); + + BaseInstrument.PickInstrument( m, new InstrumentPickedCallback( OnPickedInstrument ) ); + + return TimeSpan.FromSeconds( 1.0 ); // Cannot use another skill for 1 second + } + + public static void OnPickedInstrument( Mobile from, BaseInstrument instrument ) + { + from.RevealingAction(); + from.SendLocalizedMessage( 501587 ); // Whom do you wish to incite? + from.Target = new InternalFirstTarget( from, instrument ); + } + + private class InternalFirstTarget : Target + { + private BaseInstrument m_Instrument; + + public InternalFirstTarget( Mobile from, BaseInstrument instrument ) : base( BaseInstrument.GetBardRange( from, SkillName.Provocation ), false, TargetFlags.None ) + { + m_Instrument = instrument; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + from.RevealingAction(); + + if ( targeted is BaseCreature && from.CanBeHarmful( (Mobile)targeted, true ) ) + { + BaseCreature creature = (BaseCreature)targeted; + + if ( !m_Instrument.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062488 ); // The instrument you are trying to play is no longer in your backpack! + } + else if ( creature.Controlled ) + { + from.SendLocalizedMessage( 501590 ); // They are too loyal to their master to be provoked. + } + else if ( creature.IsParagon && BaseInstrument.GetBaseDifficulty( creature ) >= 160.0 ) + { + from.SendLocalizedMessage( 1049446 ); // You have no chance of provoking those creatures. + } + else + { + from.RevealingAction(); + m_Instrument.PlayInstrumentWell( from ); + from.SendLocalizedMessage( 1008085 ); // You play your music and your target becomes angered. Whom do you wish them to attack? + from.Target = new InternalSecondTarget( from, m_Instrument, creature ); + } + } + else + { + from.SendLocalizedMessage( 501589 ); // You can't incite that! + } + } + } + + private class InternalSecondTarget : Target + { + private BaseCreature m_Creature; + private BaseInstrument m_Instrument; + + public InternalSecondTarget( Mobile from, BaseInstrument instrument, BaseCreature creature ) : base( BaseInstrument.GetBardRange( from, SkillName.Provocation ), false, TargetFlags.None ) + { + m_Instrument = instrument; + m_Creature = creature; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + from.RevealingAction(); + + if ( targeted is BaseCreature ) + { + BaseCreature creature = (BaseCreature)targeted; + + if ( !m_Instrument.IsChildOf( from.Backpack ) ) + { + from.SendLocalizedMessage( 1062488 ); // The instrument you are trying to play is no longer in your backpack! + } + else if ( m_Creature.Unprovokable || creature.Unprovokable ) + { + from.SendLocalizedMessage( 1049446 ); // You have no chance of provoking those creatures. + } + else if ( m_Creature.Map != creature.Map || !m_Creature.InRange( creature, BaseInstrument.GetBardRange( from, SkillName.Provocation ) ) ) + { + from.SendLocalizedMessage( 1049450 ); // The creatures you are trying to provoke are too far away from each other for your music to have an effect. + } + else if ( m_Creature != creature ) + { + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + + double diff = ((m_Instrument.GetDifficultyFor( m_Creature ) + m_Instrument.GetDifficultyFor( creature )) * 0.5) - 5.0; + double music = from.Skills[SkillName.Musicianship].Value; + + if ( music > 100.0 ) + diff -= (music - 100.0) * 0.5; + + if ( from.CanBeHarmful( m_Creature, true ) && from.CanBeHarmful( creature, true ) ) + { + if ( !BaseInstrument.CheckMusicianship( from ) ) + { + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 5.0 ); + from.SendLocalizedMessage( 500612 ); // You play poorly, and there is no effect. + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else + { + //from.DoHarmful( m_Creature ); + //from.DoHarmful( creature ); + + if ( !from.CheckTargetSkill( SkillName.Provocation, creature, diff-25.0, diff+25.0 ) ) + { + from.NextSkillTime = DateTime.Now + TimeSpan.FromSeconds( 5.0 ); + from.SendLocalizedMessage( 501599 ); // Your music fails to incite enough anger. + m_Instrument.PlayInstrumentBadly( from ); + m_Instrument.ConsumeUse( from ); + } + else + { + from.SendLocalizedMessage( 501602 ); // Your music succeeds, as you start a fight. + m_Instrument.PlayInstrumentWell( from ); + m_Instrument.ConsumeUse( from ); + m_Creature.Provoke( from, creature, true ); + } + } + } + } + else + { + from.SendLocalizedMessage( 501593 ); // You can't tell someone to attack themselves! + } + } + else + { + from.SendLocalizedMessage( 501589 ); // You can't incite that! + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/RemoveTrap.cs b/Scripts/Skills/RemoveTrap.cs new file mode 100644 index 000000000..c03346d95 --- /dev/null +++ b/Scripts/Skills/RemoveTrap.cs @@ -0,0 +1,127 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; +using Server.Factions; + +namespace Server.SkillHandlers +{ + public class RemoveTrap + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.RemoveTrap].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + if ( m.Skills[SkillName.Lockpicking].Value < 50 ) + { + m.SendLocalizedMessage( 502366 ); // You do not know enough about locks. Become better at picking locks. + } + else if ( m.Skills[SkillName.DetectHidden].Value < 50 ) + { + m.SendLocalizedMessage( 502367 ); // You are not perceptive enough. Become better at detect hidden. + } + else + { + m.Target = new InternalTarget(); + + m.SendLocalizedMessage( 502368 ); // Wich trap will you attempt to disarm? + } + + return TimeSpan.FromSeconds( 10.0 ); // 10 second delay before beign able to re-use a skill + } + + private class InternalTarget : Target + { + public InternalTarget() : base ( 2, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + from.SendLocalizedMessage( 502816 ); // You feel that such an action would be inappropriate + } + else if ( targeted is TrapableContainer ) + { + TrapableContainer targ = (TrapableContainer)targeted; + + from.Direction = from.GetDirectionTo( targ ); + + if ( targ.TrapType == TrapType.None ) + { + from.SendLocalizedMessage( 502373 ); // That doesn't appear to be trapped + return; + } + + from.PlaySound( 0x241 ); + + if ( from.CheckTargetSkill( SkillName.RemoveTrap, targ, targ.TrapPower, targ.TrapPower + 30 ) ) + { + targ.TrapPower = 0; + targ.TrapLevel = 0; + targ.TrapType = TrapType.None; + from.SendLocalizedMessage( 502377 ); // You successfully render the trap harmless + } + else + { + from.SendLocalizedMessage( 502372 ); // You fail to disarm the trap... but you don't set it off + } + } + else if ( targeted is BaseFactionTrap ) + { + BaseFactionTrap trap = (BaseFactionTrap) targeted; + Faction faction = Faction.Find( from ); + + FactionTrapRemovalKit kit = ( from.Backpack == null ? null : from.Backpack.FindItemByType( typeof( FactionTrapRemovalKit ) ) as FactionTrapRemovalKit ); + + bool isOwner = ( trap.Placer == from || ( trap.Faction != null && trap.Faction.IsCommander( from ) ) ); + + if ( faction == null ) + { + from.SendLocalizedMessage( 1010538 ); // You may not disarm faction traps unless you are in an opposing faction + } + else if ( faction == trap.Faction && trap.Faction != null && !isOwner ) + { + from.SendLocalizedMessage( 1010537 ); // You may not disarm traps set by your own faction! + } + else if ( !isOwner && kit == null ) + { + from.SendLocalizedMessage( 1042530 ); // You must have a trap removal kit at the base level of your pack to disarm a faction trap. + } + else + { + if ( from.CheckTargetSkill( SkillName.RemoveTrap, trap, 80.0, 100.0 ) && from.CheckTargetSkill( SkillName.Tinkering, trap, 80.0, 100.0 ) ) + { + from.PrivateOverheadMessage( MessageType.Regular, trap.MessageHue, trap.DisarmMessage, from.NetState ); + + if ( !isOwner ) + { + int silver = faction.AwardSilver( from, trap.SilverFromDisarm ); + + if ( silver > 0 ) + from.SendLocalizedMessage( 1008113, true, silver.ToString( "N0" ) ); // You have been granted faction silver for removing the enemy trap : + } + + trap.Delete(); + } + else + { + from.SendLocalizedMessage( 502372 ); // You fail to disarm the trap... but you don't set it off + } + + if ( !isOwner && kit != null ) + kit.ConsumeCharge( from ); + } + } + else + { + from.SendLocalizedMessage( 502373 ); // That does'nt appear to be trapped + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Snooping.cs b/Scripts/Skills/Snooping.cs new file mode 100644 index 000000000..5bc456d13 --- /dev/null +++ b/Scripts/Skills/Snooping.cs @@ -0,0 +1,103 @@ +using System; +using Server; +using Server.Misc; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Regions; + +namespace Server.SkillHandlers +{ + public class Snooping + { + public static void Configure() + { + Container.SnoopHandler = new ContainerSnoopHandler( Container_Snoop ); + } + + public static bool CheckSnoopAllowed( Mobile from, Mobile to ) + { + Map map = from.Map; + + if ( to.Player ) + return from.CanBeHarmful( to, false, true ); // normal restrictions + + if ( map != null && (map.Rules & MapRules.HarmfulRestrictions) == 0 ) + return true; // felucca you can snoop anybody + + GuardedRegion reg = (GuardedRegion) to.Region.GetRegion( typeof( GuardedRegion ) ); + + if ( reg == null || reg.IsDisabled() ) + return true; // not in town? we can snoop any npc + + BaseCreature cret = to as BaseCreature; + + if ( to.Body.IsHuman && (cret == null || (!cret.AlwaysAttackable && !cret.AlwaysMurderer)) ) + return false; // in town we cannot snoop blue human npcs + + return true; + } + + public static void Container_Snoop( Container cont, Mobile from ) + { + if ( from.AccessLevel > AccessLevel.Player || from.InRange( cont.GetWorldLocation(), 1 ) ) + { + Mobile root = cont.RootParent as Mobile; + + if ( root != null && !root.Alive ) + return; + + if ( root != null && root.AccessLevel > AccessLevel.Player && from.AccessLevel == AccessLevel.Player ) + { + from.SendLocalizedMessage( 500209 ); // You can not peek into the container. + return; + } + + if ( root != null && from.AccessLevel == AccessLevel.Player && !CheckSnoopAllowed( from, root ) ) + { + from.SendLocalizedMessage( 1001018 ); // You cannot perform negative acts on your target. + return; + } + + if ( root != null && from.AccessLevel == AccessLevel.Player && from.Skills[SkillName.Snooping].Value < Utility.Random( 100 ) ) + { + Map map = from.Map; + + if ( map != null ) + { + string message = String.Format( "You notice {0} attempting to peek into {1}'s belongings.", from.Name, root.Name ); + + IPooledEnumerable eable = map.GetClientsInRange( from.Location, 8 ); + + foreach ( NetState ns in eable ) + { + if ( ns != from.NetState ) + ns.Mobile.SendMessage( message ); + } + + eable.Free(); + } + } + + if ( from.AccessLevel == AccessLevel.Player ) + Titles.AwardKarma( from, -4, true ); + + if ( from.AccessLevel > AccessLevel.Player || from.CheckTargetSkill( SkillName.Snooping, cont, 0.0, 100.0 ) ) + { + if ( cont is TrapableContainer && ((TrapableContainer)cont).ExecuteTrap( from ) ) + return; + + cont.DisplayTo( from ); + } + else + { + from.SendLocalizedMessage( 500210 ); // You failed to peek into the container. + } + } + else + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/SpiritSpeak.cs b/Scripts/Skills/SpiritSpeak.cs new file mode 100644 index 000000000..ac83bf71a --- /dev/null +++ b/Scripts/Skills/SpiritSpeak.cs @@ -0,0 +1,203 @@ +using System; +using Server; +using Server.Items; +using Server.Spells; +using Server.Network; + +namespace Server.SkillHandlers +{ + class SpiritSpeak + { + public static void Initialize() + { + SkillInfo.Table[32].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + if ( Core.AOS ) + { + Spell spell = new SpiritSpeakSpell( m ); + + spell.Cast(); + + if ( spell.IsCasting ) + return TimeSpan.FromSeconds( 5.0 ); + + return TimeSpan.Zero; + } + + m.RevealingAction(); + + if ( m.CheckSkill( SkillName.SpiritSpeak, 0, 100 ) ) + { + if ( !m.CanHearGhosts ) + { + Timer t = new SpiritSpeakTimer( m ); + double secs = m.Skills[SkillName.SpiritSpeak].Base / 50; + secs *= 90; + if ( secs < 15 ) + secs = 15; + + t.Delay = TimeSpan.FromSeconds( secs );//15seconds to 3 minutes + t.Start(); + m.CanHearGhosts = true; + } + + m.PlaySound( 0x24A ); + m.SendLocalizedMessage( 502444 );//You contact the neitherworld. + } + else + { + m.SendLocalizedMessage( 502443 );//You fail to contact the neitherworld. + m.CanHearGhosts = false; + } + + return TimeSpan.FromSeconds( 1.0 ); + } + + private class SpiritSpeakTimer : Timer + { + private Mobile m_Owner; + public SpiritSpeakTimer( Mobile m ) : base( TimeSpan.FromMinutes( 2.0 ) ) + { + m_Owner = m; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + m_Owner.CanHearGhosts = false; + m_Owner.SendLocalizedMessage( 502445 );//You feel your contact with the neitherworld fading. + } + } + + private class SpiritSpeakSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( "Spirit Speak", "", SpellCircle.Second, 269 ); + + public override bool BlockedByHorrificBeast{ get{ return false; } } + + public SpiritSpeakSpell( Mobile caster ) : base( caster, null, m_Info ) + { + } + + public override bool ClearHandsOnCast{ get{ return false; } } + + public override TimeSpan GetCastDelay() + { + return TimeSpan.FromSeconds( 1.0 ); + } + + public override int GetMana() + { + return 0; + } + + public override void OnCasterHurt() + { + if ( IsCasting ) + Disturb( DisturbType.Hurt, false, true ); + } + + public override bool ConsumeReagents() + { + return true; + } + + public override bool CheckFizzle() + { + return true; + } + + public override bool CheckNextSpellTime{ get{ return false; } } + + public override void OnDisturb( DisturbType type, bool message ) + { + Caster.NextSkillTime = DateTime.Now; + + base.OnDisturb( type, message ); + } + + public override bool CheckDisturb( DisturbType type, bool checkFirst, bool resistable ) + { + if ( type == DisturbType.EquipRequest || type == DisturbType.UseRequest ) + return false; + + return true; + } + + public override void SayMantra() + { + // Anh Mi Sah Ko + Caster.PublicOverheadMessage( MessageType.Regular, 0x3B2, 1062074, "", false ); + Caster.PlaySound( 0x24A ); + } + + public override void OnCast() + { + Corpse toChannel = null; + + foreach ( Item item in Caster.GetItemsInRange( 3 ) ) + { + if ( item is Corpse && !((Corpse)item).Channeled ) + { + toChannel = (Corpse)item; + break; + } + } + + int max, min, mana, number; + + if ( toChannel != null ) + { + min = 1 + (int)(Caster.Skills[SkillName.SpiritSpeak].Value * 0.25); + max = min + 4; + mana = 0; + number = 1061287; // You channel energy from a nearby corpse to heal your wounds. + } + else + { + min = 1 + (int)(Caster.Skills[SkillName.SpiritSpeak].Value * 0.25); + max = min + 4; + mana = 10; + number = 1061286; // You channel your own spiritual energy to heal your wounds. + } + + if ( Caster.Mana < mana ) + { + Caster.SendLocalizedMessage( 1061285 ); // You lack the mana required to use this skill. + } + else + { + Caster.CheckSkill( SkillName.SpiritSpeak, 0.0, 120.0 ); + + if ( Utility.RandomDouble() > (Caster.Skills[SkillName.SpiritSpeak].Value / 100.0) ) + { + Caster.SendLocalizedMessage( 502443 ); // You fail your attempt at contacting the netherworld. + } + else + { + if ( toChannel != null ) + { + toChannel.Channeled = true; + toChannel.Hue = 0x835; + } + + Caster.Mana -= mana; + Caster.SendLocalizedMessage( number ); + + if ( min > max ) + min = max; + + Caster.Hits += Utility.RandomMinMax( min, max ); + + Caster.FixedParticles( 0x375A, 1, 15, 9501, 2100, 4, EffectLayer.Waist ); + } + } + + FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Stealing.cs b/Scripts/Skills/Stealing.cs new file mode 100644 index 000000000..2e4e48008 --- /dev/null +++ b/Scripts/Skills/Stealing.cs @@ -0,0 +1,478 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Targeting; +using Server.Items; +using Server.Network; +using Server.Factions; +using Server.Spells.Seventh; +using Server.Spells.Fifth; +using Server.Spells.Necromancy; + +namespace Server.SkillHandlers +{ + public class Stealing + { + public static void Initialize() + { + SkillInfo.Table[33].Callback = new SkillUseCallback( OnUse ); + } + + public static readonly bool ClassicMode = false; + public static readonly bool SuspendOnMurder = false; + + public static bool IsInGuild( Mobile m ) + { + return ( m is PlayerMobile && ((PlayerMobile)m).NpcGuild == NpcGuild.ThievesGuild ); + } + + public static bool IsInnocentTo( Mobile from, Mobile to ) + { + return ( Notoriety.Compute( from, (Mobile)to ) == Notoriety.Innocent ); + } + + private class StealingTarget : Target + { + private Mobile m_Thief; + + public StealingTarget( Mobile thief ) : base ( 1, false, TargetFlags.None ) + { + m_Thief = thief; + + AllowNonlocal = true; + } + + private Item TryStealItem( Item toSteal, ref bool caught ) + { + Item stolen = null; + + object root = toSteal.RootParent; + + StealableArtifactsSpawner.StealableInstance si = null; + if ( toSteal.Parent == null || !toSteal.Movable ) + si = StealableArtifactsSpawner.GetStealableInstance( toSteal ); + + if ( !IsEmptyHanded( m_Thief ) ) + { + m_Thief.SendLocalizedMessage( 1005584 ); // Both hands must be free to steal. + } + else if ( root is Mobile && ((Mobile)root).Player && IsInnocentTo( m_Thief, (Mobile)root ) && !IsInGuild( m_Thief ) ) + { + m_Thief.SendLocalizedMessage( 1005596 ); // You must be in the thieves guild to steal from other players. + } + else if ( SuspendOnMurder && root is Mobile && ((Mobile)root).Player && IsInGuild( m_Thief ) && m_Thief.Kills > 0 ) + { + m_Thief.SendLocalizedMessage( 502706 ); // You are currently suspended from the thieves guild. + } + else if ( root is BaseVendor && ((BaseVendor)root).IsInvulnerable ) + { + m_Thief.SendLocalizedMessage( 1005598 ); // You can't steal from shopkeepers. + } + else if ( root is PlayerVendor ) + { + m_Thief.SendLocalizedMessage( 502709 ); // You can't steal from vendors. + } + else if ( !m_Thief.CanSee( toSteal ) ) + { + m_Thief.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( m_Thief.Backpack == null || !m_Thief.Backpack.CheckHold( m_Thief, toSteal, false, true ) ) + { + m_Thief.SendLocalizedMessage( 1048147 ); // Your backpack can't hold anything else. + } + #region Sigils + else if ( toSteal is Sigil ) + { + PlayerState pl = PlayerState.Find( m_Thief ); + Faction faction = ( pl == null ? null : pl.Faction ); + + Sigil sig = (Sigil) toSteal; + + if ( !m_Thief.InRange( toSteal.GetWorldLocation(), 1 ) ) + { + m_Thief.SendLocalizedMessage( 502703 ); // You must be standing next to an item to steal it. + } + else if ( root != null ) // not on the ground + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + else if ( faction != null ) + { + if ( !m_Thief.CanBeginAction( typeof( IncognitoSpell ) ) ) + { + m_Thief.SendLocalizedMessage( 1010581 ); // You cannot steal the sigil when you are incognito + } + else if ( DisguiseGump.IsDisguised( m_Thief ) ) + { + m_Thief.SendLocalizedMessage( 1010583 ); // You cannot steal the sigil while disguised + } + else if ( !m_Thief.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + m_Thief.SendLocalizedMessage( 1010582 ); // You cannot steal the sigil while polymorphed + } + else if ( TransformationSpell.UnderTransformation( m_Thief ) ) + { + m_Thief.SendLocalizedMessage( 1061622 ); // You cannot steal the sigil while in that form. + } + else if ( Spells.Ninjitsu.AnimalForm.UnderTransformation( m_Thief ) ) + { + m_Thief.SendLocalizedMessage( 1063222 ); // You cannot steal the sigil while mimicking an animal. + } + else if ( pl.IsLeaving ) + { + m_Thief.SendLocalizedMessage( 1005589 ); // You are currently quitting a faction and cannot steal the town sigil + } + else if ( sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction ) + { + m_Thief.SendLocalizedMessage( 1005590 ); // You cannot steal your own sigil + } + else if ( sig.IsPurifying ) + { + m_Thief.SendLocalizedMessage( 1005592 ); // You cannot steal this sigil until it has been purified + } + else if ( m_Thief.CheckTargetSkill( SkillName.Stealing, toSteal, 80.0, 80.0 ) ) + { + if ( Sigil.ExistsOn( m_Thief ) ) + { + m_Thief.SendLocalizedMessage( 1010258 ); // The sigil has gone back to its home location because you already have a sigil. + } + else if ( m_Thief.Backpack == null || !m_Thief.Backpack.CheckHold( m_Thief, sig, false, true ) ) + { + m_Thief.SendLocalizedMessage( 1010259 ); // The sigil has gone home because your backpack is full + } + else + { + if ( sig.IsBeingCorrupted ) + sig.GraceStart = DateTime.Now; // begin grace period + + m_Thief.SendLocalizedMessage( 1010586 ); // YOU STOLE THE SIGIL!!! (woah, calm down now) + + if ( sig.LastMonolith != null ) + sig.LastMonolith.Sigil = null; + + sig.LastStolen = DateTime.Now; + + return sig; + } + } + else + { + m_Thief.SendLocalizedMessage( 1005594 ); // You do not have enough skill to steal the sigil + } + } + else + { + m_Thief.SendLocalizedMessage( 1005588 ); // You must join a faction to do that + } + } + #endregion + else if ( si == null && ( toSteal.Parent == null || !toSteal.Movable ) ) + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + else if ( toSteal.LootType == LootType.Newbied || toSteal.CheckBlessed( root ) ) + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + else if ( Core.AOS && si == null && toSteal is Container ) + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + else if ( !m_Thief.InRange( toSteal.GetWorldLocation(), 1 ) ) + { + m_Thief.SendLocalizedMessage( 502703 ); // You must be standing next to an item to steal it. + } + else if ( si != null && m_Thief.Skills[SkillName.Stealing].Value < 100.0 ) + { + m_Thief.SendLocalizedMessage( 1060025, "", 0x66D ); // You're not skilled enough to attempt the theft of this item. + } + else if ( toSteal.Parent is Mobile ) + { + m_Thief.SendLocalizedMessage( 1005585 ); // You cannot steal items which are equiped. + } + else if ( root == m_Thief ) + { + m_Thief.SendLocalizedMessage( 502704 ); // You catch yourself red-handed. + } + else if ( root is Mobile && ((Mobile)root).AccessLevel > AccessLevel.Player ) + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + else if ( root is Mobile && !m_Thief.CanBeHarmful( (Mobile)root ) ) + { + } + else if ( root is Corpse ) + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + else + { + double w = toSteal.Weight + toSteal.TotalWeight; + + if ( w > 10 ) + { + m_Thief.SendMessage( "That is too heavy to steal." ); + } + else + { + if ( toSteal.Stackable && toSteal.Amount > 1 ) + { + int maxAmount = (int)((m_Thief.Skills[SkillName.Stealing].Value / 10.0) / toSteal.Weight); + + if ( maxAmount < 1 ) + maxAmount = 1; + else if ( maxAmount > toSteal.Amount ) + maxAmount = toSteal.Amount; + + int amount = Utility.RandomMinMax( 1, maxAmount ); + + if ( amount >= toSteal.Amount ) + { + int pileWeight = (int)Math.Ceiling( toSteal.Weight * toSteal.Amount ); + pileWeight *= 10; + + if ( m_Thief.CheckTargetSkill( SkillName.Stealing, toSteal, pileWeight - 22.5, pileWeight + 27.5 ) ) + stolen = toSteal; + } + else + { + int pileWeight = (int)Math.Ceiling( toSteal.Weight * amount ); + pileWeight *= 10; + + if ( m_Thief.CheckTargetSkill( SkillName.Stealing, toSteal, pileWeight - 22.5, pileWeight + 27.5 ) ) + { + stolen = Mobile.LiftItemDupe( toSteal, toSteal.Amount - amount ); + + if ( stolen == null ) + stolen = toSteal; + } + } + } + else + { + int iw = (int)Math.Ceiling( w ); + iw *= 10; + + if ( m_Thief.CheckTargetSkill( SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5 ) ) + stolen = toSteal; + } + + if ( stolen != null ) + { + m_Thief.SendLocalizedMessage( 502724 ); // You succesfully steal the item. + + if ( si != null ) + { + toSteal.Movable = true; + si.Item = null; + } + } + else + { + m_Thief.SendLocalizedMessage( 502723 ); // You fail to steal the item. + } + + caught = ( m_Thief.Skills[SkillName.Stealing].Value < Utility.Random( 150 ) ); + } + } + + return stolen; + } + + protected override void OnTarget( Mobile from, object target ) + { + from.RevealingAction(); + + Item stolen = null; + object root = null; + bool caught = false; + + if ( target is Item ) + { + root = ((Item)target).RootParent; + stolen = TryStealItem( (Item)target, ref caught ); + } + else if ( target is Mobile ) + { + Container pack = ((Mobile)target).Backpack; + + if ( pack != null && pack.Items.Count > 0 ) + { + int randomIndex = Utility.Random( pack.Items.Count ); + + root = target; + stolen = TryStealItem( pack.Items[randomIndex], ref caught ); + } + } + else + { + m_Thief.SendLocalizedMessage( 502710 ); // You can't steal that! + } + + if ( stolen != null ) + { + from.AddToBackpack( stolen ); + + StolenItem.Add( stolen, m_Thief, root as Mobile ); + } + + if ( caught ) + { + if ( root == null ) + { + m_Thief.CriminalAction( false ); + } + else if ( root is Corpse && ((Corpse)root).IsCriminalAction( m_Thief ) ) + { + m_Thief.CriminalAction( false ); + } + else if ( root is Mobile ) + { + Mobile mobRoot = (Mobile)root; + + if ( !IsInGuild( mobRoot ) && IsInnocentTo( m_Thief, mobRoot ) ) + m_Thief.CriminalAction( false ); + + string message = String.Format( "You notice {0} trying to steal from {1}.", m_Thief.Name, mobRoot.Name ); + + foreach ( NetState ns in m_Thief.GetClientsInRange( 8 ) ) + { + if ( ns != m_Thief.NetState ) + ns.Mobile.SendMessage( message ); + } + } + } + else if ( root is Corpse && ((Corpse)root).IsCriminalAction( m_Thief ) ) + { + m_Thief.CriminalAction( false ); + } + + if ( root is Mobile && ((Mobile)root).Player && m_Thief is PlayerMobile && IsInnocentTo( m_Thief, (Mobile)root ) && !IsInGuild( (Mobile)root ) ) + { + PlayerMobile pm = (PlayerMobile)m_Thief; + + pm.PermaFlags.Add( (Mobile)root ); + pm.Delta( MobileDelta.Noto ); + } + } + } + + public static bool IsEmptyHanded( Mobile from ) + { + if ( from.FindItemOnLayer( Layer.OneHanded ) != null ) + return false; + + if ( from.FindItemOnLayer( Layer.TwoHanded ) != null ) + return false; + + return true; + } + + public static TimeSpan OnUse( Mobile m ) + { + if ( !IsEmptyHanded( m ) ) + { + m.SendLocalizedMessage( 1005584 ); // Both hands must be free to steal. + } + else + { + m.Target = new Stealing.StealingTarget( m ); + m.RevealingAction(); + + m.SendLocalizedMessage( 502698 ); // Which item do you want to steal? + } + + return TimeSpan.FromSeconds( 10.0 ); + } + } + + public class StolenItem + { + public static readonly TimeSpan StealTime = TimeSpan.FromMinutes( 2.0 ); + + private Item m_Stolen; + private Mobile m_Thief; + private Mobile m_Victim; + private DateTime m_Expires; + + public Item Stolen{ get{ return m_Stolen; } } + public Mobile Thief{ get{ return m_Thief; } } + public Mobile Victim{ get{ return m_Victim; } } + public DateTime Expires{ get{ return m_Expires; } } + + public bool IsExpired{ get{ return ( DateTime.Now >= m_Expires ); } } + + public StolenItem( Item stolen, Mobile thief, Mobile victim ) + { + m_Stolen = stolen; + m_Thief = thief; + m_Victim = victim; + + m_Expires = DateTime.Now + StealTime; + } + + private static Queue m_Queue = new Queue(); + + public static void Add( Item item, Mobile thief, Mobile victim ) + { + Clean(); + + m_Queue.Enqueue( new StolenItem( item, thief, victim ) ); + } + + public static bool IsStolen( Item item ) + { + Mobile victim = null; + + return IsStolen( item, ref victim ); + } + + public static bool IsStolen( Item item, ref Mobile victim ) + { + Clean(); + + foreach ( StolenItem si in m_Queue ) + { + if ( si.m_Stolen == item && !si.IsExpired ) + { + victim = si.m_Victim; + return true; + } + } + + return false; + } + + public static void ReturnOnDeath( Mobile killed, Container corpse ) + { + Clean(); + + foreach ( StolenItem si in m_Queue ) + { + if ( si.m_Stolen.RootParent == corpse && si.m_Victim != null && !si.IsExpired ) + { + if ( si.m_Victim.AddToBackpack( si.m_Stolen ) ) + si.m_Victim.SendLocalizedMessage( 1010464 ); // the item that was stolen is returned to you. + else + si.m_Victim.SendLocalizedMessage( 1010463 ); // the item that was stolen from you falls to the ground. + + si.m_Expires = DateTime.Now; // such a hack + } + } + } + + public static void Clean() + { + while ( m_Queue.Count > 0 ) + { + StolenItem si = (StolenItem) m_Queue.Peek(); + + if ( si.IsExpired ) + m_Queue.Dequeue(); + else + break; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Stealth.cs b/Scripts/Skills/Stealth.cs new file mode 100644 index 000000000..b890582f8 --- /dev/null +++ b/Scripts/Skills/Stealth.cs @@ -0,0 +1,91 @@ +using System; +using Server.Items; + +namespace Server.SkillHandlers +{ + public class Stealth + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Stealth].Callback = new SkillUseCallback( OnUse ); + } + + public static int[,] ArmorTable{ get { return m_ArmorTable; } } + private static int[,] m_ArmorTable = new int[,] + { + // Gorget Gloves Helmet Arms Legs Chest Shield + /* Cloth */ { 0, 0, 0, 0, 0, 0, 0 }, + /* Leather */ { 0, 0, 0, 0, 0, 0, 0 }, + /* Studded */ { 2, 2, 0, 4, 6, 10, 0 }, + /* Bone */ { 0, 5, 10, 10, 15, 25, 0 }, + /* Spined */ { 0, 0, 0, 0, 0, 0, 0 }, + /* Horned */ { 0, 0, 0, 0, 0, 0, 0 }, + /* Barbed */ { 0, 0, 0, 0, 0, 0, 0 }, + /* Ring */ { 0, 5, 0, 10, 15, 25, 0 }, + /* Chain */ { 0, 0, 10, 0, 15, 25, 0 }, + /* Plate */ { 5, 5, 10, 10, 15, 25, 0 }, + /* Dragon */ { 0, 5, 10, 10, 15, 25, 0 } + }; + + public static int GetArmorRating( Mobile m ) + { + if( !Core.AOS ) + return (int)m.ArmorRating; + + int ar = 0; + + for( int i = 0; i < m.Items.Count; i++ ) + { + BaseArmor armor = m.Items[i] as BaseArmor; + + if( armor != null && armor.ArmorAttributes.MageArmor == 0 ) + ar += m_ArmorTable[(int)armor.MaterialType, (int)armor.BodyPosition]; + } + + return ar; + } + + public static TimeSpan OnUse( Mobile m ) + { + if ( !m.Hidden ) + { + m.SendLocalizedMessage( 502725 ); // You must hide first + } + else if ( m.Skills[SkillName.Hiding].Base < ((Core.SE) ? 50.0 : 80.0) ) + { + m.SendLocalizedMessage( 502726 ); // You are not hidden well enough. Become better at hiding. + m.RevealingAction(); + } + else + { + int armorRating = GetArmorRating( m ); + + if ( armorRating >= (Core.AOS ? 42 : 26) ) //I have a hunch '42' was chosen cause someone's a fan of DNA + { + m.SendLocalizedMessage( 502727 ); // You could not hope to move quietly wearing this much armor. + m.RevealingAction(); + } + else if ( m.CheckSkill( SkillName.Stealth, -20.0 + (armorRating * 2), (Core.AOS ? 60.0 : 80.0) + (armorRating * 2) ) ) + { + int steps = (int)(m.Skills[SkillName.Stealth].Value / (Core.AOS ? 5.0 : 10.0)); + + if ( steps < 1 ) + steps = 1; + + m.AllowedStealthSteps = steps; + + m.SendLocalizedMessage( 502730 ); // You begin to move quietly. + + return TimeSpan.FromSeconds( 10.0 ); + } + else + { + m.SendLocalizedMessage( 502731 ); // You fail in your attempt to move unnoticed. + m.RevealingAction(); + } + } + + return TimeSpan.FromSeconds( 10.0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/TasteID.cs b/Scripts/Skills/TasteID.cs new file mode 100644 index 000000000..8735d1f64 --- /dev/null +++ b/Scripts/Skills/TasteID.cs @@ -0,0 +1,95 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; +using Server.Mobiles; + +namespace Server.SkillHandlers +{ + public class TasteID + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.TasteID].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.Target = new InternalTarget(); + + m.SendLocalizedMessage( 502807 ); // What would you like to taste? + + return TimeSpan.FromSeconds( 1.0 ); + } + + [PlayerVendorTarget] + private class InternalTarget : Target + { + public InternalTarget() : base ( 2, false, TargetFlags.None ) + { + AllowNonlocal = true; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile ) + { + from.SendLocalizedMessage( 502816 ); // You feel that such an action would be inappropriate. + } + else if ( targeted is Food ) + { + Food food = (Food) targeted; + + if ( from.CheckTargetSkill( SkillName.TasteID, food, 0, 100 ) ) + { + if ( food.Poison != null ) + { + food.SendLocalizedMessageTo( from, 1038284 ); // It appears to have poison smeared on it. + } + else + { + // No poison on the food + food.SendLocalizedMessageTo( from, 1010600 ); // You detect nothing unusual about this substance. + } + } + else + { + // Skill check failed + food.SendLocalizedMessageTo( from, 502823 ); // You cannot discern anything about this substance. + } + } + else if ( targeted is BasePotion ) + { + BasePotion potion = (BasePotion) targeted; + + potion.SendLocalizedMessageTo( from, 502813 ); // You already know what kind of potion that is. + potion.SendLocalizedMessageTo( from, potion.LabelNumber ); + } + else if ( targeted is PotionKeg ) + { + PotionKeg keg = (PotionKeg) targeted; + + if ( keg.Held <= 0 ) + { + keg.SendLocalizedMessageTo( from, 502228 ); // There is nothing in the keg to taste! + } + else + { + keg.SendLocalizedMessageTo( from, 502229 ); // You are already familiar with this keg's contents. + keg.SendLocalizedMessageTo( from, keg.LabelNumber ); + } + } + else + { + // The target is not food or potion or potion keg. + from.SendLocalizedMessage( 502820 ); // That's not something you can taste. + } + } + + protected override void OnTargetOutOfRange( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 502815 ); // You are too far away to taste that. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Skills/Tracking.cs b/Scripts/Skills/Tracking.cs new file mode 100644 index 000000000..9ce58c3dc --- /dev/null +++ b/Scripts/Skills/Tracking.cs @@ -0,0 +1,397 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Spells.Necromancy; + +namespace Server.SkillHandlers +{ + public class Tracking + { + public static void Initialize() + { + SkillInfo.Table[(int)SkillName.Tracking].Callback = new SkillUseCallback( OnUse ); + } + + public static TimeSpan OnUse( Mobile m ) + { + m.SendLocalizedMessage( 1011350 ); // What do you wish to track? + + m.CloseGump( typeof( TrackWhatGump ) ); + m.CloseGump( typeof( TrackWhoGump ) ); + m.SendGump( new TrackWhatGump( m ) ); + + return TimeSpan.FromSeconds( 10.0 ); // 10 second delay before beign able to re-use a skill + } + + public class TrackingInfo + { + public Mobile m_Tracker; + public Mobile m_Target; + public Point2D m_Location; + public Map m_Map; + + public TrackingInfo( Mobile tracker, Mobile target ) + { + m_Tracker = tracker; + m_Target = target; + m_Location = new Point2D( target.X, target.Y ); + m_Map = target.Map; + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static void AddInfo( Mobile tracker, Mobile target ) + { + TrackingInfo info = new TrackingInfo( tracker, target ); + m_Table[tracker] = info; + } + + public static double GetStalkingBonus( Mobile tracker, Mobile target ) + { + // Note: This is not reset as of publish 35. + + TrackingInfo info = m_Table[tracker] as TrackingInfo; + + if ( info == null || info.m_Target != target || info.m_Map != target.Map ) + return 0.0; + + int xDelta = info.m_Location.X - target.X; + int yDelta = info.m_Location.Y - target.Y; + + return Math.Sqrt( (xDelta * xDelta) + (yDelta * yDelta) ); + } + + public static void ClearTrackingInfo( Mobile tracker ) + { + m_Table.Remove( tracker ); + } + } + + public class TrackWhatGump : Gump + { + private Mobile m_From; + private bool m_Success; + + public TrackWhatGump( Mobile from ) : base( 20, 30 ) + { + m_From = from; + m_Success = from.CheckSkill( SkillName.Tracking, 0.0, 21.1 ); + + AddPage( 0 ); + + AddBackground( 0, 0, 440, 135, 5054 ); + + AddBackground( 10, 10, 420, 75, 2620 ); + AddBackground( 10, 85, 420, 25, 3000 ); + + AddItem( 20, 20, 9682 ); + AddButton( 20, 110, 4005, 4007, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 20, 90, 100, 20, 1018087, false, false ); // Animals + + AddItem( 120, 20, 9607 ); + AddButton( 120, 110, 4005, 4007, 2, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 120, 90, 100, 20, 1018088, false, false ); // Monsters + + AddItem( 220, 20, 8454 ); + AddButton( 220, 110, 4005, 4007, 3, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 220, 90, 100, 20, 1018089, false, false ); // Human NPCs + + AddItem( 320, 20, 8455 ); + AddButton( 320, 110, 4005, 4007, 4, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 320, 90, 100, 20, 1018090, false, false ); // Players + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + if ( info.ButtonID >= 1 && info.ButtonID <= 4 ) + TrackWhoGump.DisplayTo( m_Success, m_From, info.ButtonID - 1 ); + } + } + + public delegate bool TrackTypeDelegate( Mobile m ); + + public class TrackWhoGump : Gump + { + private Mobile m_From; + private int m_Range; + + private static TrackTypeDelegate[] m_Delegates = new TrackTypeDelegate[] + { + new TrackTypeDelegate( IsAnimal ), + new TrackTypeDelegate( IsMonster ), + new TrackTypeDelegate( IsHumanNPC ), + new TrackTypeDelegate( IsPlayer ) + }; + + private class InternalSorter : IComparer + { + private Mobile m_From; + + public InternalSorter( Mobile from ) + { + m_From = from; + } + + public int Compare( object x, object y ) + { + if ( x == null && y == null ) + return 0; + else if ( x == null ) + return -1; + else if ( y == null ) + return 1; + + Mobile a = x as Mobile; + Mobile b = y as Mobile; + + if ( a == null || b == null ) + throw new ArgumentException(); + + return m_From.GetDistanceToSqrt( a ).CompareTo( m_From.GetDistanceToSqrt( b ) ); + } + } + + public static void DisplayTo( bool success, Mobile from, int type ) + { + if ( !success ) + { + from.SendLocalizedMessage( 1018092 ); // You see no evidence of those in the area. + return; + } + + Map map = from.Map; + + if ( map == null ) + return; + + TrackTypeDelegate check = m_Delegates[type]; + + from.CheckSkill( SkillName.Tracking, 21.1, 100.0 ); // Passive gain + + int range = 10 + (int)(from.Skills[SkillName.Tracking].Value / 10); + + ArrayList list = new ArrayList(); + + foreach ( Mobile m in from.GetMobilesInRange( range ) ) + { + // Ghosts can no longer be tracked + if ( m != from && (!Core.AOS || m.Alive) && (!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) && check( m ) && CheckDifficulty( from, m ) ) + list.Add( m ); + } + + if ( list.Count > 0 ) + { + list.Sort( new InternalSorter( from ) ); + + from.SendGump( new TrackWhoGump( from, list, range ) ); + from.SendLocalizedMessage( 1018093 ); // Select the one you would like to track. + } + else + { + if ( type == 0 ) + from.SendLocalizedMessage( 502991 ); // You see no evidence of animals in the area. + else if ( type == 1 ) + from.SendLocalizedMessage( 502993 ); // You see no evidence of creatures in the area. + else + from.SendLocalizedMessage( 502995 ); // You see no evidence of people in the area. + } + } + + // Tracking players uses tracking and detect hidden vs. hiding and stealth + private static bool CheckDifficulty( Mobile from, Mobile m ) + { + if ( !Core.AOS || !m.Player ) + return true; + + int tracking = from.Skills[SkillName.Tracking].Fixed; + int detectHidden = from.Skills[SkillName.DetectHidden].Fixed; + + int hiding = m.Skills[SkillName.Hiding].Fixed; + int stealth = m.Skills[SkillName.Stealth].Fixed; + int divisor = hiding + stealth; + + // Necromancy forms affect tracking difficulty + if ( TransformationSpell.UnderTransformation( m, typeof( HorrificBeastSpell ) ) ) + divisor -= 200; + else if ( TransformationSpell.UnderTransformation( m, typeof( VampiricEmbraceSpell ) ) && divisor < 500 ) + divisor = 500; + else if ( TransformationSpell.UnderTransformation( m, typeof( WraithFormSpell ) ) && divisor <= 2000 ) + divisor += 200; + + int chance; + if ( divisor > 0 ) + { + if ( Core.SE ) + chance = 50 * (tracking * 2 + detectHidden) / divisor; + else + chance = 50 * (tracking + detectHidden + 10 * Utility.RandomMinMax( 1, 20 )) / divisor; + } + else + chance = 100; + + return chance > Utility.Random( 100 ); + } + + private static bool IsAnimal( Mobile m ) + { + return ( !m.Player && m.Body.IsAnimal ); + } + + private static bool IsMonster( Mobile m ) + { + return ( !m.Player && m.Body.IsMonster ); + } + + private static bool IsHumanNPC( Mobile m ) + { + return ( !m.Player && m.Body.IsHuman ); + } + + private static bool IsPlayer( Mobile m ) + { + return m.Player; + } + + private ArrayList m_List; + + private TrackWhoGump( Mobile from, ArrayList list, int range ) : base( 20, 30 ) + { + m_From = from; + m_List = list; + m_Range = range; + + AddPage( 0 ); + + AddBackground( 0, 0, 440, 155, 5054 ); + + AddBackground( 10, 10, 420, 75, 2620 ); + AddBackground( 10, 85, 420, 45, 3000 ); + + if ( list.Count > 4 ) + { + AddBackground( 0, 155, 440, 155, 5054 ); + + AddBackground( 10, 165, 420, 75, 2620 ); + AddBackground( 10, 240, 420, 45, 3000 ); + + if ( list.Count > 8 ) + { + AddBackground( 0, 310, 440, 155, 5054 ); + + AddBackground( 10, 320, 420, 75, 2620 ); + AddBackground( 10, 395, 420, 45, 3000 ); + } + } + + for ( int i = 0; i < list.Count && i < 12; ++i ) + { + Mobile m = (Mobile)list[i]; + + AddItem( 20 + ((i % 4) * 100), 20 + ((i / 4) * 155), ShrinkTable.Lookup( m ) ); + AddButton( 20 + ((i % 4) * 100), 130 + ((i / 4) * 155), 4005, 4007, i + 1, GumpButtonType.Reply, 0 ); + + if ( m.Name != null ) + AddHtml( 20 + ((i % 4) * 100), 90 + ((i / 4) * 155), 90, 40, m.Name, false, false ); + } + } + + public override void OnResponse( NetState state, RelayInfo info ) + { + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_List.Count && index < 12 ) + { + Mobile m = (Mobile)m_List[index]; + + m_From.QuestArrow = new TrackArrow( m_From, m, m_Range * 2 ); + + if ( Core.SE ) + Tracking.AddInfo( m_From, m ); + } + } + } + + public class TrackArrow : QuestArrow + { + private Mobile m_From; + private Timer m_Timer; + + public TrackArrow( Mobile from, Mobile target, int range ) : base( from ) + { + m_From = from; + m_Timer = new TrackTimer( from, target, range, this ); + m_Timer.Start(); + } + + public override void OnClick( bool rightClick ) + { + if ( rightClick ) + { + Tracking.ClearTrackingInfo( m_From ); + + m_From = null; + + Stop(); + } + } + + public override void OnStop() + { + m_Timer.Stop(); + + if ( m_From != null ) + { + Tracking.ClearTrackingInfo( m_From ); + + m_From.SendLocalizedMessage( 503177 ); // You have lost your quarry. + } + } + } + + public class TrackTimer : Timer + { + private Mobile m_From, m_Target; + private int m_Range; + private int m_LastX, m_LastY; + private QuestArrow m_Arrow; + + public TrackTimer( Mobile from, Mobile target, int range, QuestArrow arrow ) : base( TimeSpan.FromSeconds( 0.25 ), TimeSpan.FromSeconds( 2.5 ) ) + { + m_From = from; + m_Target = target; + m_Range = range; + + m_Arrow = arrow; + } + + protected override void OnTick() + { + if ( !m_Arrow.Running ) + { + Stop(); + return; + } + else if ( m_From.NetState == null || m_From.Deleted || m_Target.Deleted || m_From.Map != m_Target.Map || !m_From.InRange( m_Target, m_Range ) ) + { + m_From.Send( new CancelArrow() ); + m_From.SendLocalizedMessage( 503177 ); // You have lost your quarry. + + Tracking.ClearTrackingInfo( m_From ); + + Stop(); + return; + } + + if ( m_LastX != m_Target.X || m_LastY != m_Target.Y ) + { + m_LastX = m_Target.X; + m_LastY = m_Target.Y; + + m_Arrow.Update( m_LastX, m_LastY ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/DisturbType.cs b/Scripts/Spells/Base/DisturbType.cs new file mode 100644 index 000000000..2bfaed06c --- /dev/null +++ b/Scripts/Spells/Base/DisturbType.cs @@ -0,0 +1,15 @@ +using System; +using Server; + +namespace Server.Spells +{ + public enum DisturbType + { + Unspecified, + EquipRequest, + UseRequest, + Hurt, + Kill, + NewCast + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/SpecialMove.cs b/Scripts/Spells/Base/SpecialMove.cs new file mode 100644 index 000000000..9410dc735 --- /dev/null +++ b/Scripts/Spells/Base/SpecialMove.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; +using Server.Spells.Ninjitsu; +using Server.Spells.Bushido; + +namespace Server.Spells +{ + public abstract class SpecialMove + { + public virtual int BaseMana{ get{ return 0; } } + + public virtual SkillName MoveSkill{ get{ return SkillName.Bushido; } } + public virtual double RequiredSkill{ get{ return 0.0; } } + + public virtual TextDefinition AbilityMessage{ get{ return 0; } } + + public virtual bool BlockedByAnimalForm{ get{ return true; } } + public virtual bool DelayedContext{ get{ return false; } } + + public virtual double GetAccuracyScalar( Mobile attacker ) + { + return 1.0; + } + + public virtual double GetDamageScalar( Mobile attacker, Mobile defender ) + { + return 1.0; + } + + // Called before swinging, to make sure the accuracy scalar is to be computed. + public virtual bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + return true; + } + + // Called when a hit connects, but before damage is calculated. + public virtual bool OnBeforeDamage( Mobile attacker, Mobile defender ) + { + return true; + } + + // Called as soon as the ability is used. + public virtual void OnUse( Mobile from ) + { + } + + // Called when a hit connects, at the end of the weapon.OnHit() method. + public virtual void OnHit( Mobile attacker, Mobile defender, int damage ) + { + } + + // Called when a hit misses. + public virtual void OnMiss( Mobile attacker, Mobile defender ) + { + } + + // Called when the move is cleared. + public virtual void OnClearMove( Mobile from ) + { + } + + public virtual bool IgnoreArmor( Mobile attacker ) + { + return false; + } + + public virtual double GetPropertyBonus( Mobile attacker ) + { + return 1.0; + } + + public virtual bool CheckSkills( Mobile m ) + { + if ( m.Skills[MoveSkill].Value < RequiredSkill ) + { + string args = String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), MoveSkill.ToString() ); + m.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + return false; + } + + return true; + } + + public virtual int ScaleMana( Mobile m, int mana ) + { + double scalar = 1.0; + + if ( !Server.Spells.Necromancy.MindRotSpell.GetMindRotScalar( m, ref scalar ) ) + scalar = 1.0; + + // Lower Mana Cost = 40% + int lmc = Math.Min( AosAttributes.GetValue( m, AosAttribute.LowerManaCost ), 40 ); + + scalar -= (double)lmc / 100; + + int total = (int)(mana * scalar); + + if ( m.Skills[MoveSkill].Value < 50.0 && GetContext( m ) != null ) + total *= 2; + + return total; + } + + public virtual bool CheckMana( Mobile from, bool consume ) + { + int mana = ScaleMana( from, BaseMana ); + + if ( from.Mana < mana ) + { + from.SendLocalizedMessage( 1060181, mana.ToString() ); // You need ~1_MANA_REQUIREMENT~ mana to perform that attack + return false; + } + + if ( consume ) + { + if ( !DelayedContext ) + SetContext( from ); + + from.Mana -= mana; + } + + return true; + } + + public virtual void SetContext( Mobile from ) + { + if ( GetContext( from ) == null ) + { + if ( DelayedContext || from.Skills[MoveSkill].Value < 50.0 ) + { + Timer timer = new SpecialMoveTimer( from ); + timer.Start(); + + AddContext( from, new SpecialMoveContext( timer, this.GetType() ) ); + } + } + } + + public virtual bool Validate( Mobile from ) + { + if ( !from.Player ) + return true; + + if ( Bushido.HonorableExecution.IsUnderPenalty( from ) ) + { + from.SendLocalizedMessage( 1063024 ); // You cannot perform this special move right now. + return false; + } + + if ( Ninjitsu.AnimalForm.UnderTransformation( from ) ) + { + from.SendLocalizedMessage( 1063024 ); // You cannot perform this special move right now. + return false; + } + + return CheckSkills( from ) && CheckMana( from, false ); + } + + public virtual void CheckGain( Mobile m ) + { + m.CheckSkill( MoveSkill, RequiredSkill, RequiredSkill + 37.5 ); + } + + private static Hashtable m_Table = new Hashtable(); + + public static Hashtable Table{ get{ return m_Table; } } + + public static void ClearAllMoves( Mobile m ) + { + foreach ( DictionaryEntry de in SpellRegistry.SpecialMoves ) + { + SpecialMove move = (SpecialMove)de.Value; + + int moveID = SpellRegistry.GetRegistryNumber( move ); + + if ( moveID != -1 ) + m.Send( new ToggleSpecialAbility( moveID + 1, false ) ); + } + } + + public virtual bool ValidatesDuringHit{ get { return true; } } + + public static SpecialMove GetCurrentMove( Mobile m ) + { + if ( m == null ) + return null; + + if ( !Core.SE ) + { + ClearCurrentMove( m ); + return null; + } + + SpecialMove move = (SpecialMove)m_Table[m]; + + if ( move != null && move.ValidatesDuringHit && !move.Validate( m ) ) + { + ClearCurrentMove( m ); + return null; + } + + return move; + } + + public static bool SetCurrentMove( Mobile m, SpecialMove move ) + { + if ( !Core.SE ) + { + ClearCurrentMove( m ); + return false; + } + + if ( move != null && !move.Validate( m ) ) + { + ClearCurrentMove( m ); + return false; + } + + bool sameMove = ( move == GetCurrentMove( m ) ); + + ClearCurrentMove( m ); + + if ( sameMove ) + return true; + + if ( move != null ) + { + WeaponAbility.ClearCurrentAbility( m ); + + m_Table[m] = move; + + move.OnUse( m ); + + int moveID = SpellRegistry.GetRegistryNumber( move ); + + if ( moveID > 0 ) + m.Send( new ToggleSpecialAbility( moveID + 1, true ) ); + + TextDefinition.SendMessageTo( m, move.AbilityMessage ); + } + + return true; + } + + public static void ClearCurrentMove( Mobile m ) + { + SpecialMove move = (SpecialMove)m_Table[m]; + + if ( move != null ) + { + move.OnClearMove( m ); + + int moveID = SpellRegistry.GetRegistryNumber( move ); + + if ( moveID > 0 ) + m.Send( new ToggleSpecialAbility( moveID + 1, false ) ); + } + + m_Table.Remove( m ); + } + + public SpecialMove() + { + } + + + private static Hashtable m_PlayersTable = new Hashtable(); + + private static void AddContext( Mobile m, SpecialMoveContext context ) + { + m_PlayersTable[m] = context; + } + + private static void RemoveContext( Mobile m ) + { + SpecialMoveContext context = GetContext( m ); + + if ( context != null ) + { + m_PlayersTable.Remove( m ); + + context.Timer.Stop(); + } + } + + private static SpecialMoveContext GetContext( Mobile m ) + { + return ( m_PlayersTable[m] as SpecialMoveContext ); + } + + public static bool GetContext( Mobile m, Type type ) + { + SpecialMoveContext context = m_PlayersTable[m] as SpecialMoveContext; + + if ( context == null ) + return false; + + return ( context.Type == type ); + } + + private class SpecialMoveTimer : Timer + { + private Mobile m_Mobile; + + public SpecialMoveTimer( Mobile from ) : base ( TimeSpan.FromSeconds( 3.0 ) ) + { + m_Mobile = from; + + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + RemoveContext( m_Mobile ); + } + } + + public class SpecialMoveContext + { + private Timer m_Timer; + private Type m_Type; + + public Timer Timer{ get{ return m_Timer; } } + public Type Type{ get{ return m_Type; } } + + public SpecialMoveContext( Timer timer, Type type ) + { + m_Timer = timer; + m_Type = type; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/Spell.cs b/Scripts/Spells/Base/Spell.cs new file mode 100644 index 000000000..ef750f81e --- /dev/null +++ b/Scripts/Spells/Base/Spell.cs @@ -0,0 +1,942 @@ +using System; +using Server.Items; +using Server.Network; +using Server.Targeting; +using Server.Mobiles; +using Server.Spells.Second; +using Server.Spells.Necromancy; +using Server.Spells.Ninjitsu; +using System.Collections.Generic; + +namespace Server.Spells +{ + public abstract class Spell : ISpell + { + private Mobile m_Caster; + private Item m_Scroll; + private SpellInfo m_Info; + private SpellState m_State; + private DateTime m_StartCastTime; + + public SpellState State{ get{ return m_State; } set{ m_State = value; } } + public Mobile Caster{ get{ return m_Caster; } } + public SpellInfo Info{ get{ return m_Info; } } + public string Name{ get{ return m_Info.Name; } } + public string Mantra{ get{ return m_Info.Mantra; } } + public SpellCircle Circle{ get{ return m_Info.Circle; } } + public Type[] Reagents{ get{ return m_Info.Reagents; } } + public Item Scroll{ get{ return m_Scroll; } } + public DateTime StartCastTime { get { return m_StartCastTime; } } + + private static TimeSpan NextSpellDelay = TimeSpan.FromSeconds( 0.75 ); + private static TimeSpan AnimateDelay = TimeSpan.FromSeconds( 1.5 ); + + public virtual SkillName CastSkill{ get{ return SkillName.Magery; } } + public virtual SkillName DamageSkill{ get{ return SkillName.EvalInt; } } + + public virtual bool RevealOnCast{ get{ return true; } } + public virtual bool ClearHandsOnCast{ get{ return true; } } + public virtual bool ShowHandMovement{ get{ return true; } } + + public virtual bool DelayedDamage{ get{ return false; } } + + public virtual bool DelayedDamageStacking { get { return true; } } + //In reality, it's ANY delayed Damage spell Post-AoS that can't stack, but, only + //Expo & Magic Arrow have enough delay and a short enough cast time to bring up + //the possibility of stacking 'em. Note that a MA & an Explosion will stack, but + //of course, two MA's won't. + + private static Dictionary> m_ContextTable = new Dictionary>(); + + public void StartDelayedDamageContext( Mobile m, Timer t ) + { + if( DelayedDamageStacking ) + return; //Sanity + + Dictionary contexts; + + if( !m_ContextTable.TryGetValue( GetType(), out contexts ) ) + { + contexts = new Dictionary(); + m_ContextTable.Add( GetType(), contexts ); + } + + if( contexts.ContainsKey( m ) ) + { + contexts[m].Stop(); + contexts.Remove( m ); + } + + contexts.Add( m, t ); + } + + public void RemoveDelayedDamageContext( Mobile m ) + { + Dictionary contexts; + + if( !m_ContextTable.TryGetValue( GetType(), out contexts ) ) + return; + + contexts.Remove( m ); + } + + public Spell( Mobile caster, Item scroll, SpellInfo info ) + { + m_Caster = caster; + m_Scroll = scroll; + m_Info = info; + } + + public virtual int GetNewAosDamage( int bonus, int dice, int sides, Mobile singleTarget ) + { + if( singleTarget != null ) + { + return GetNewAosDamage( bonus, dice, sides, (Caster.Player && singleTarget.Player), GetDamageScalar( singleTarget ) ); + } + else + { + return GetNewAosDamage( bonus, dice, sides, false ); + } + } + + public virtual int GetNewAosDamage( int bonus, int dice, int sides, bool playerVsPlayer ) + { + return GetNewAosDamage( bonus, dice, sides, playerVsPlayer, 1.0 ); + } + + public virtual int GetNewAosDamage( int bonus, int dice, int sides, bool playerVsPlayer, double scalar ) + { + int damage = Utility.Dice( dice, sides, bonus ) * 100; + int damageBonus = 0; + + int inscribeSkill = GetInscribeFixed( m_Caster ); + int inscribeBonus = (inscribeSkill + (1000 * (inscribeSkill / 1000))) / 200; + damageBonus += inscribeBonus; + + int intBonus = Caster.Int / 10; + damageBonus += intBonus; + + int sdiBonus = AosAttributes.GetValue( m_Caster, AosAttribute.SpellDamage ); + // PvP spell damage increase cap of 15% from an item’s magic property + if ( playerVsPlayer && sdiBonus > 15 ) + sdiBonus = 15; + damageBonus += sdiBonus; + + damage = AOS.Scale( damage, 100 + damageBonus ); + + int evalSkill = GetDamageFixed( m_Caster ); + int evalScale = 30 + ((9 * evalSkill) / 100); + + damage = AOS.Scale( damage, evalScale ); + + damage = AOS.Scale( damage, (int)(scalar*100) ); + + return damage / 100; + } + + /* + public virtual double GetAosDamage( int min, int random, double div ) + { + double scale = 1.0; + + scale += GetInscribeSkill( m_Caster ) * 0.001; + + if ( Caster.Player ) + { + scale += Caster.Int * 0.001; + scale += AosAttributes.GetValue( m_Caster, AosAttribute.SpellDamage ) * 0.01; + } + + int baseDamage = min + (int)(GetDamageSkill( m_Caster ) / div); + + double damage = Utility.RandomMinMax( baseDamage, baseDamage + random ); + + return damage * scale; + } + */ + + public virtual bool IsCasting{ get{ return m_State == SpellState.Casting; } } + + public virtual void OnCasterHurt() + { + //Confirm: Monsters and pets cannot be disturbed. + if ( !Caster.Player ) + return; + + if ( IsCasting ) + { + object o = ProtectionSpell.Registry[m_Caster]; + bool disturb = true; + + if ( o != null && o is double ) + { + if ( ((double)o) > Utility.RandomDouble()*100.0 ) + disturb = false; + } + + if ( disturb ) + Disturb( DisturbType.Hurt, false, true ); + } + } + + public virtual void OnCasterKilled() + { + Disturb( DisturbType.Kill ); + } + + public virtual void OnConnectionChanged() + { + FinishSequence(); + } + + public virtual bool OnCasterMoving( Direction d ) + { + if ( IsCasting && BlocksMovement ) + { + m_Caster.SendLocalizedMessage( 500111 ); // You are frozen and can not move. + return false; + } + + return true; + } + + public virtual bool OnCasterEquiping( Item item ) + { + if ( IsCasting ) + Disturb( DisturbType.EquipRequest ); + + return true; + } + + public virtual bool OnCasterUsingObject( object o ) + { + if ( m_State == SpellState.Sequencing ) + Disturb( DisturbType.UseRequest ); + + return true; + } + + public virtual bool OnCastInTown( Region r ) + { + return m_Info.AllowTown; + } + + public virtual bool ConsumeReagents() + { + if ( m_Scroll != null || !m_Caster.Player ) + return true; + + if ( AosAttributes.GetValue( m_Caster, AosAttribute.LowerRegCost ) > Utility.Random( 100 ) ) + return true; + + Container pack = m_Caster.Backpack; + + if ( pack == null ) + return false; + + if ( pack.ConsumeTotal( m_Info.Reagents, m_Info.Amounts ) == -1 ) + return true; + + if ( GetType().BaseType == typeof( Spell ) ) + { + if ( ArcaneGem.ConsumeCharges( m_Caster, ( Core.SE ? 1 : 1 + (int)Circle ) ) ) + return true; + } + + return false; + } + + public virtual bool CheckResisted( Mobile target ) + { + double n = GetResistPercent( target ); + + n /= 100.0; + + if ( n <= 0.0 ) + return false; + + if ( n >= 1.0 ) + return true; + + int maxSkill = (1 + (int)Circle) * 10; + maxSkill += (1 + ((int)Circle / 6)) * 25; + + if ( target.Skills[SkillName.MagicResist].Value < maxSkill ) + target.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); + + return ( n >= Utility.RandomDouble() ); + } + + public virtual double GetInscribeSkill( Mobile m ) + { + // There is no chance to gain + // m.CheckSkill( SkillName.Inscribe, 0.0, 120.0 ); + + return m.Skills[SkillName.Inscribe].Value; + } + + public virtual int GetInscribeFixed( Mobile m ) + { + // There is no chance to gain + // m.CheckSkill( SkillName.Inscribe, 0.0, 120.0 ); + + return m.Skills[SkillName.Inscribe].Fixed; + } + + public virtual int GetDamageFixed( Mobile m ) + { + m.CheckSkill( DamageSkill, 0.0, 120.0 ); + + return m.Skills[DamageSkill].Fixed; + } + + public virtual double GetDamageSkill( Mobile m ) + { + m.CheckSkill( DamageSkill, 0.0, 120.0 ); + + return m.Skills[DamageSkill].Value; + } + + public virtual int GetResistFixed( Mobile m ) + { + int maxSkill = (1 + (int)Circle) * 10; + maxSkill += (1 + ((int)Circle / 6)) * 25; + + if ( m.Skills[SkillName.MagicResist].Value < maxSkill ) + m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); + + return m.Skills[SkillName.MagicResist].Fixed; + } + + public virtual double GetResistSkill( Mobile m ) + { + int maxSkill = (1 + (int)Circle) * 10; + maxSkill += (1 + ((int)Circle / 6)) * 25; + + if ( m.Skills[SkillName.MagicResist].Value < maxSkill ) + m.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); + + return m.Skills[SkillName.MagicResist].Value; + } + + public virtual double GetResistPercentForCircle( Mobile target, SpellCircle circle ) + { + double firstPercent = target.Skills[SkillName.MagicResist].Value / 5.0; + double secondPercent = target.Skills[SkillName.MagicResist].Value - (((m_Caster.Skills[CastSkill].Value - 20.0) / 5.0) + (1 + (int)circle) * 5.0); + + return ( firstPercent > secondPercent ? firstPercent : secondPercent ) / 2.0; // Seems should be about half of what stratics says. + } + + public virtual double GetResistPercent( Mobile target ) + { + return GetResistPercentForCircle( target, m_Info.Circle ); + } + + public virtual double GetDamageScalar( Mobile target ) + { + double scalar = 1.0; + + if( !Core.AOS ) //EvalInt stuff for AoS is handled elsewhere + { + double casterEI = m_Caster.Skills[DamageSkill].Value; + double targetRS = target.Skills[SkillName.MagicResist].Value; + + /* + if( Core.AOS ) + targetRS = 0; + */ + + m_Caster.CheckSkill( DamageSkill, 0.0, 120.0 ); + + if( casterEI > targetRS ) + scalar = (1.0 + ((casterEI - targetRS) / 500.0)); + else + scalar = (1.0 + ((casterEI - targetRS) / 200.0)); + + // magery damage bonus, -25% at 0 skill, +0% at 100 skill, +5% at 120 skill + scalar += (m_Caster.Skills[CastSkill].Value - 100.0) / 400.0; + + if( !target.Player && !target.Body.IsHuman /*&& !Core.AOS*/ ) + scalar *= 2.0; // Double magery damage to monsters/animals if not AOS + } + + if ( target is BaseCreature ) + ((BaseCreature)target).AlterDamageScalarFrom( m_Caster, ref scalar ); + + if ( m_Caster is BaseCreature ) + ((BaseCreature)m_Caster).AlterDamageScalarTo( target, ref scalar ); + + if( Core.SE ) + scalar *= GetSlayerDamageScalar( target ); + + target.Region.SpellDamageScalar( m_Caster, target, ref scalar ); + + return scalar; + } + + public virtual double GetSlayerDamageScalar( Mobile defender ) + { + Spellbook atkBook = Spellbook.FindEquippedSpellbook( m_Caster ); + + double scalar = 1.0; + if( atkBook != null ) + { + SlayerEntry atkSlayer = SlayerGroup.GetEntryByName( atkBook.Slayer ); + SlayerEntry atkSlayer2 = SlayerGroup.GetEntryByName( atkBook.Slayer2 ); + + if( atkSlayer != null && atkSlayer.Slays( defender ) || atkSlayer2 != null && atkSlayer2.Slays( defender ) ) + { + defender.FixedEffect( 0x37B9, 10, 5 ); //TODO: Confirm this displays on OSIs + scalar = 2.0; + } + + + TransformContext context = TransformationSpell.GetContext( defender ); + + if( (atkBook.Slayer == SlayerName.Silver || atkBook.Slayer2 == SlayerName.Silver) && context != null && context.Type != typeof( HorrificBeastSpell ) ) + scalar +=.25; // Every necromancer transformation other than horrific beast take an additional 25% damage + + if( scalar != 1.0 ) + return scalar; + } + + 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( m_Caster ) || defSlayer2 != null && defSlayer2.Group.OppositionSuperSlays( m_Caster ) ) + scalar = 2.0; + } + + return scalar; + } + + public virtual void DoFizzle() + { + m_Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502632 ); // The spell fizzles. + + if ( m_Caster.Player ) + { + if ( Core.AOS ) + m_Caster.FixedParticles( 0x3735, 1, 30, 9503, EffectLayer.Waist ); + else + m_Caster.FixedEffect( 0x3735, 6, 30 ); + + m_Caster.PlaySound( 0x5C ); + } + } + + private CastTimer m_CastTimer; + private AnimTimer m_AnimTimer; + + public void Disturb( DisturbType type ) + { + Disturb( type, true, false ); + } + + public virtual bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable ) + { + if ( resistable && m_Scroll is BaseWand ) + return false; + + return true; + } + + public void Disturb( DisturbType type, bool firstCircle, bool resistable ) + { + if ( !CheckDisturb( type, firstCircle, resistable ) ) + return; + + if ( m_State == SpellState.Casting ) + { + if ( !firstCircle && Circle == SpellCircle.First && !Core.AOS ) + return; + + m_State = SpellState.None; + m_Caster.Spell = null; + + OnDisturb( type, true ); + + if ( m_CastTimer != null ) + m_CastTimer.Stop(); + + if ( m_AnimTimer != null ) + m_AnimTimer.Stop(); + + if ( Core.AOS && m_Caster.Player && type == DisturbType.Hurt ) + DoHurtFizzle(); + + m_Caster.NextSpellTime = DateTime.Now + GetDisturbRecovery(); + } + else if ( m_State == SpellState.Sequencing ) + { + if ( !firstCircle && Circle == SpellCircle.First && !Core.AOS ) + return; + + m_State = SpellState.None; + m_Caster.Spell = null; + + OnDisturb( type, false ); + + Targeting.Target.Cancel( m_Caster ); + + if ( Core.AOS && m_Caster.Player && type == DisturbType.Hurt ) + DoHurtFizzle(); + } + } + + public virtual void DoHurtFizzle() + { + m_Caster.FixedEffect( 0x3735, 6, 30 ); + m_Caster.PlaySound( 0x5C ); + } + + public virtual void OnDisturb( DisturbType type, bool message ) + { + if ( message ) + m_Caster.SendLocalizedMessage( 500641 ); // Your concentration is disturbed, thus ruining thy spell. + } + + public virtual bool CheckCast() + { + return true; + } + + public virtual void SayMantra() + { + if ( m_Scroll is BaseWand ) + return; + + if ( m_Info.Mantra != null && m_Info.Mantra.Length > 0 && m_Caster.Player ) + m_Caster.PublicOverheadMessage( MessageType.Spell, m_Caster.SpeechHue, true, m_Info.Mantra, false ); + } + + public virtual bool BlockedByHorrificBeast{ get{ return true; } } + public virtual bool BlockedByAnimalForm{ get{ return true; } } + public virtual bool BlocksMovement{ get{ return true; } } + + public virtual bool CheckNextSpellTime{ get{ return !(m_Scroll is BaseWand); } } + + public bool Cast() + { + m_StartCastTime = DateTime.Now; + + if ( Core.AOS && m_Caster.Spell is Spell && ((Spell)m_Caster.Spell).State == SpellState.Sequencing ) + ((Spell)m_Caster.Spell).Disturb( DisturbType.NewCast ); + + if ( !m_Caster.CheckAlive() ) + { + return false; + } + else if ( m_Caster.Spell != null && m_Caster.Spell.IsCasting ) + { + m_Caster.SendLocalizedMessage( 502642 ); // You are already casting a spell. + } + else if ( BlockedByHorrificBeast && TransformationSpell.UnderTransformation( m_Caster, typeof( HorrificBeastSpell ) ) || ( BlockedByAnimalForm && AnimalForm.UnderTransformation( m_Caster ) )) + { + m_Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form. + } + else if ( !(m_Scroll is BaseWand) && (m_Caster.Paralyzed || m_Caster.Frozen) ) + { + m_Caster.SendLocalizedMessage( 502643 ); // You can not cast a spell while frozen. + } + else if ( CheckNextSpellTime && DateTime.Now < m_Caster.NextSpellTime ) + { + m_Caster.SendLocalizedMessage( 502644 ); // You have not yet recovered from casting a spell. + } + else if ( m_Caster.Mana >= ScaleMana( GetMana() ) ) + { + if ( m_Caster.Spell == null && m_Caster.CheckSpellCast( this ) && CheckCast() && m_Caster.Region.OnBeginSpellCast( m_Caster, this ) ) + { + m_State = SpellState.Casting; + m_Caster.Spell = this; + + if ( RevealOnCast ) + m_Caster.RevealingAction(); + + SayMantra(); + + TimeSpan castDelay = this.GetCastDelay(); + + if ( ShowHandMovement && m_Caster.Body.IsHuman ) + { + int count = (int)Math.Ceiling( castDelay.TotalSeconds / AnimateDelay.TotalSeconds ); + + if ( count != 0 ) + { + m_AnimTimer = new AnimTimer( this, count ); + m_AnimTimer.Start(); + } + + if ( m_Info.LeftHandEffect > 0 ) + Caster.FixedParticles( 0, 10, 5, m_Info.LeftHandEffect, EffectLayer.LeftHand ); + + if ( m_Info.RightHandEffect > 0 ) + Caster.FixedParticles( 0, 10, 5, m_Info.RightHandEffect, EffectLayer.RightHand ); + } + + if ( ClearHandsOnCast ) + m_Caster.ClearHands(); + + m_CastTimer = new CastTimer( this, castDelay ); + m_CastTimer.Start(); + + OnBeginCast(); + + return true; + } + else + { + return false; + } + } + else + { + m_Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 502625 ); // Insufficient mana + } + + return false; + } + + public abstract void OnCast(); + + public virtual void OnBeginCast() + { + } + + private const double ChanceOffset = 20.0, ChanceLength = 100.0 / 7.0; + + public virtual void GetCastSkills( out double min, out double max ) + { + int circle = (int)m_Info.Circle; + + if ( m_Scroll != null ) + circle -= 2; + + double avg = ChanceLength * circle; + + min = avg - ChanceOffset; + max = avg + ChanceOffset; + } + + public virtual bool CheckFizzle() + { + if ( m_Scroll is BaseWand ) + return true; + + double minSkill, maxSkill; + + GetCastSkills( out minSkill, out maxSkill ); + + return Caster.CheckSkill( CastSkill, minSkill, maxSkill ); + } + + private static int[] m_ManaTable = new int[]{ 4, 6, 9, 11, 14, 20, 40, 50 }; + + public virtual int GetMana() + { + if ( m_Scroll is BaseWand ) + return 0; + + return m_ManaTable[(int)Circle]; + } + + public virtual int ScaleMana( int mana ) + { + double scalar = 1.0; + + if ( !Necromancy.MindRotSpell.GetMindRotScalar( Caster, ref scalar ) ) + scalar = 1.0; + + // Lower Mana Cost = 40% + int lmc = AosAttributes.GetValue( m_Caster, AosAttribute.LowerManaCost ); + if ( lmc > 40 ) + lmc = 40; + + scalar -= (double)lmc / 100; + + return (int)(mana * scalar); + } + + public virtual TimeSpan GetDisturbRecovery() + { + if ( Core.AOS ) + return TimeSpan.Zero; + + double delay = 1.0 - Math.Sqrt( (DateTime.Now - m_StartCastTime).TotalSeconds / GetCastDelay().TotalSeconds ); + + if ( delay < 0.2 ) + delay = 0.2; + + return TimeSpan.FromSeconds( delay ); + } + + public virtual int CastRecoveryBase{ get{ return 6; } } + public virtual int CastRecoveryCircleScalar{ get{ return 0; } } + public virtual int CastRecoveryFastScalar{ get{ return 1; } } + public virtual int CastRecoveryPerSecond{ get{ return 4; } } + public virtual int CastRecoveryMinimum{ get{ return 0; } } + + public virtual TimeSpan GetCastRecovery() + { + if ( !Core.AOS ) + return NextSpellDelay; + + int fcr = AosAttributes.GetValue( m_Caster, AosAttribute.CastRecovery ); + + int circleDelay = CastRecoveryCircleScalar * (1 + (int)Circle); // Note: Circle is 0-based so we must offset + int fcrDelay = -(CastRecoveryFastScalar * fcr); + + int delay = CastRecoveryBase + circleDelay + fcrDelay; + + if ( delay < CastRecoveryMinimum ) + delay = CastRecoveryMinimum; + + return TimeSpan.FromSeconds( (double)delay / CastRecoveryPerSecond ); + } + + public virtual int CastDelayBase{ get{ return 3; } } + public virtual int CastDelayCircleScalar{ get{ return 1; } } + public virtual int CastDelayFastScalar{ get{ return 1; } } + public virtual int CastDelayPerSecond{ get{ return 4; } } + public virtual int CastDelayMinimum{ get{ return 1; } } + + public virtual TimeSpan GetCastDelay() + { + if ( m_Scroll is BaseWand ) + return TimeSpan.Zero; + + if ( !Core.AOS ) + return TimeSpan.FromSeconds( 0.5 + (0.25 * (int)Circle) ); + + // Faster casting cap of 2 (if not using the protection spell) + // Faster casting cap of 0 (if using the protection spell) + // Paladin spells are subject to a faster casting cap of 4 + // Paladins with magery of 70.0 or above are subject to a faster casting cap of 2 + int fcMax = 2; + + if ( CastSkill == SkillName.Chivalry && m_Caster.Skills[SkillName.Magery].Value < 70.0 ) + fcMax = 4; + + int fc = AosAttributes.GetValue( m_Caster, AosAttribute.CastSpeed ); + + if ( fc > fcMax ) + fc = fcMax; + + if ( ProtectionSpell.Registry.Contains( m_Caster ) ) + fc -= 2; + + // Circle is 0-based but we must not offset, first circle spells are cast at base delay. + int circleDelay = CastDelayCircleScalar * (int)Circle; + + int fcDelay = -(CastDelayFastScalar * fc); + + int delay = CastDelayBase + circleDelay + fcDelay; + + if ( delay < CastDelayMinimum ) + delay = CastDelayMinimum; + + return TimeSpan.FromSeconds( (double)delay / CastDelayPerSecond ); + } + + public virtual void FinishSequence() + { + m_State = SpellState.None; + + if ( m_Caster.Spell == this ) + m_Caster.Spell = null; + } + + public virtual int ComputeKarmaAward() + { + return 0; + } + + public virtual bool CheckSequence() + { + int mana = ScaleMana( GetMana() ); + + if ( m_Caster.Deleted || !m_Caster.Alive || m_Caster.Spell != this || m_State != SpellState.Sequencing ) + { + DoFizzle(); + } + else if ( m_Scroll != null && !(m_Scroll is Runebook) && (m_Scroll.Amount <= 0 || m_Scroll.Deleted || m_Scroll.RootParent != m_Caster || (m_Scroll is BaseWand && (((BaseWand)m_Scroll).Charges <= 0 || m_Scroll.Parent != m_Caster))) ) + { + DoFizzle(); + } + else if ( !ConsumeReagents() ) + { + m_Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 502630 ); // More reagents are needed for this spell. + } + else if ( m_Caster.Mana < mana ) + { + m_Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 502625 ); // Insufficient mana for this spell. + } + else if ( Core.AOS && (m_Caster.Frozen || m_Caster.Paralyzed) ) + { + m_Caster.SendLocalizedMessage( 502646 ); // You cannot cast a spell while frozen. + DoFizzle(); + } + else if ( CheckFizzle() ) + { + m_Caster.Mana -= mana; + + if ( m_Scroll is SpellScroll ) + m_Scroll.Consume(); + else if ( m_Scroll is BaseWand ) + ((BaseWand)m_Scroll).ConsumeCharge( m_Caster ); + + if ( m_Scroll is BaseWand ) + { + bool m = m_Scroll.Movable; + + m_Scroll.Movable = false; + + if ( ClearHandsOnCast ) + m_Caster.ClearHands(); + + m_Scroll.Movable = m; + } + else + { + if ( ClearHandsOnCast ) + m_Caster.ClearHands(); + } + + int karma = ComputeKarmaAward(); + + if ( karma != 0 ) + Misc.Titles.AwardKarma( Caster, karma, true ); + + if ( TransformationSpell.UnderTransformation( m_Caster, typeof( VampiricEmbraceSpell ) ) ) + { + bool garlic = false; + + for ( int i = 0; !garlic && i < m_Info.Reagents.Length; ++i ) + garlic = ( m_Info.Reagents[i] == Reagent.Garlic ); + + if ( garlic ) + { + m_Caster.SendLocalizedMessage( 1061651 ); // The garlic burns you! + AOS.Damage( m_Caster, Utility.RandomMinMax( 17, 23 ), 100, 0, 0, 0, 0 ); + } + } + + return true; + } + else + { + DoFizzle(); + } + + return false; + } + + public bool CheckBSequence( Mobile target ) + { + return CheckBSequence( target, false ); + } + + public bool CheckBSequence( Mobile target, bool allowDead ) + { + if ( !target.Alive && !allowDead ) + { + m_Caster.SendLocalizedMessage( 501857 ); // This spell won't work on that! + return false; + } + else if ( Caster.CanBeBeneficial( target, true, allowDead ) && CheckSequence() ) + { + Caster.DoBeneficial( target ); + return true; + } + else + { + return false; + } + } + + public bool CheckHSequence( Mobile target ) + { + if ( !target.Alive ) + { + m_Caster.SendLocalizedMessage( 501857 ); // This spell won't work on that! + return false; + } + else if ( Caster.CanBeHarmful( target ) && CheckSequence() ) + { + Caster.DoHarmful( target ); + return true; + } + else + { + return false; + } + } + + private class AnimTimer : Timer + { + private Spell m_Spell; + + public AnimTimer( Spell spell, int count ) : base( TimeSpan.Zero, AnimateDelay, count ) + { + m_Spell = spell; + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Spell.State != SpellState.Casting || m_Spell.m_Caster.Spell != m_Spell ) + { + Stop(); + return; + } + + if ( !m_Spell.Caster.Mounted && m_Spell.Caster.Body.IsHuman && m_Spell.m_Info.Action >= 0 ) + m_Spell.Caster.Animate( m_Spell.m_Info.Action, 7, 1, true, false, 0 ); + + if ( !Running ) + m_Spell.m_AnimTimer = null; + } + } + + private class CastTimer : Timer + { + private Spell m_Spell; + + public CastTimer( Spell spell, TimeSpan castDelay ) : base( castDelay ) + { + m_Spell = spell; + + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + if ( m_Spell.m_State == SpellState.Casting && m_Spell.m_Caster.Spell == m_Spell ) + { + m_Spell.m_State = SpellState.Sequencing; + m_Spell.m_CastTimer = null; + m_Spell.m_Caster.OnSpellCast( m_Spell ); + m_Spell.m_Caster.Region.OnSpellCast( m_Spell.m_Caster, m_Spell ); + m_Spell.m_Caster.NextSpellTime = DateTime.Now + m_Spell.GetCastRecovery();// Spell.NextSpellDelay; + + Target originalTarget = m_Spell.m_Caster.Target; + + m_Spell.OnCast(); + + if ( m_Spell.m_Caster.Player && m_Spell.m_Caster.Target != originalTarget && m_Spell.Caster.Target != null ) + m_Spell.m_Caster.Target.BeginTimeout( m_Spell.m_Caster, TimeSpan.FromSeconds( 30.0 ) ); + + m_Spell.m_CastTimer = null; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/SpellCircle.cs b/Scripts/Spells/Base/SpellCircle.cs new file mode 100644 index 000000000..a9479b8d8 --- /dev/null +++ b/Scripts/Spells/Base/SpellCircle.cs @@ -0,0 +1,16 @@ +using System; + +namespace Server.Spells +{ + public enum SpellCircle + { + First, + Second, + Third, + Fourth, + Fifth, + Sixth, + Seventh, + Eighth + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/SpellHelper.cs b/Scripts/Spells/Base/SpellHelper.cs new file mode 100644 index 000000000..53c703536 --- /dev/null +++ b/Scripts/Spells/Base/SpellHelper.cs @@ -0,0 +1,946 @@ +using System; +using Server; +using Server.Items; +using Server.Guilds; +using Server.Multis; +using Server.Regions; +using Server.Mobiles; +using Server.Targeting; +using Server.Engines.PartySystem; +using Server.Misc; + +namespace Server +{ + public class DefensiveSpell + { + public static void Nullify( Mobile from ) + { + if ( !from.CanBeginAction( typeof( DefensiveSpell ) ) ) + new InternalTimer( from ).Start(); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m ) + : base( TimeSpan.FromMinutes( 1.0 ) ) + { + m_Mobile = m; + + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Mobile.EndAction( typeof( DefensiveSpell ) ); + } + } + } +} + +namespace Server.Spells +{ + public enum TravelCheckType + { + RecallFrom, + RecallTo, + GateFrom, + GateTo, + Mark, + TeleportFrom, + TeleportTo + } + + public class SpellHelper + { + private static TimeSpan AosDamageDelay = TimeSpan.FromSeconds( 1.0 ); + private static TimeSpan OldDamageDelay = TimeSpan.FromSeconds( 0.5 ); + + public static TimeSpan GetDamageDelayForSpell( Spell sp ) + { + if ( !sp.DelayedDamage ) + return TimeSpan.Zero; + + return ( Core.AOS ? AosDamageDelay : OldDamageDelay ); + } + + public static bool CheckMulti( Point3D p, Map map ) + { + return CheckMulti( p, map, true ); + } + + public static bool CheckMulti( Point3D p, Map map, bool houses ) + { + if ( map == null || map == Map.Internal ) + return false; + + Sector sector = map.GetSector( p.X, p.Y ); + + for ( int i = 0; i < sector.Multis.Count; ++i ) + { + BaseMulti multi = (BaseMulti) sector.Multis[i]; + + if ( multi is BaseHouse ) + { + if ( houses && ( (BaseHouse) multi ).IsInside( p, 16 ) ) + return true; + } + else if ( multi.Contains( p ) ) + { + return true; + } + } + + return false; + } + + public static void Turn( Mobile from, object to ) + { + IPoint3D target = to as IPoint3D; + + if ( target == null ) + return; + + if ( target is Item ) + { + Item item = (Item) target; + + if ( item.RootParent != from ) + from.Direction = from.GetDirectionTo( item.GetWorldLocation() ); + } + else if ( from != target ) + { + from.Direction = from.GetDirectionTo( target ); + } + } + + private static TimeSpan CombatHeatDelay = TimeSpan.FromSeconds( 30.0 ); + private static bool RestrictTravelCombat = true; + + public static bool CheckCombat( Mobile m ) + { + if ( !RestrictTravelCombat ) + return false; + + for ( int i = 0; i < m.Aggressed.Count; ++i ) + { + AggressorInfo info = m.Aggressed[i]; + + if ( info.Defender.Player && ( DateTime.Now - info.LastCombatTime ) < CombatHeatDelay ) + return true; + } + + if ( Core.Expansion == Expansion.AOS ) + { + for ( int i = 0; i < m.Aggressors.Count; ++i ) + { + AggressorInfo info = m.Aggressors[i]; + + if ( info.Attacker.Player && ( DateTime.Now - info.LastCombatTime ) < CombatHeatDelay ) + return true; + } + } + + return false; + } + + public static bool AdjustField( ref Point3D p, Map map, int height, bool mobsBlock ) + { + if ( map == null ) + return false; + + for ( int offset = 0; offset < 10; ++offset ) + { + Point3D loc = new Point3D( p.X, p.Y, p.Z - offset ); + + if ( map.CanFit( loc, height, true, mobsBlock ) ) + { + p = loc; + return true; + } + } + + return false; + } + + public static void GetSurfaceTop( ref IPoint3D p ) + { + if ( p is Item ) + { + p = ( (Item) p ).GetSurfaceTop(); + } + else if ( p is StaticTarget ) + { + StaticTarget t = (StaticTarget) p; + int z = t.Z; + + if ( ( t.Flags & TileFlag.Surface ) == 0 ) + z -= TileData.ItemTable[t.ItemID & 0x3FFF].CalcHeight; + + p = new Point3D( t.X, t.Y, z ); + } + } + + public static bool AddStatOffset( Mobile m, StatType type, int offset, TimeSpan duration ) + { + if ( offset > 0 ) + return AddStatBonus( m, m, type, offset, duration ); + else if ( offset < 0 ) + return AddStatCurse( m, m, type, -offset, duration ); + + return true; + } + + public static bool AddStatBonus( Mobile caster, Mobile target, StatType type ) + { + return AddStatBonus( caster, target, type, GetOffset( caster, target, type, false ), GetDuration( caster, target ) ); + } + + public static bool AddStatBonus( Mobile caster, Mobile target, StatType type, int bonus, TimeSpan duration ) + { + int offset = bonus; + string name = String.Format( "[Magic] {0} Offset", type ); + + StatMod mod = target.GetStatMod( name ); + + if ( mod != null && mod.Offset < 0 ) + { + target.AddStatMod( new StatMod( type, name, mod.Offset + offset, duration ) ); + return true; + } + else if ( mod == null || mod.Offset < offset ) + { + target.AddStatMod( new StatMod( type, name, offset, duration ) ); + return true; + } + + return false; + } + + public static bool AddStatCurse( Mobile caster, Mobile target, StatType type ) + { + return AddStatCurse( caster, target, type, GetOffset( caster, target, type, true ), GetDuration( caster, target ) ); + } + + public static bool AddStatCurse( Mobile caster, Mobile target, StatType type, int curse, TimeSpan duration ) + { + int offset = -curse; + string name = String.Format( "[Magic] {0} Offset", type ); + + StatMod mod = target.GetStatMod( name ); + + if ( mod != null && mod.Offset > 0 ) + { + target.AddStatMod( new StatMod( type, name, mod.Offset + offset, duration ) ); + return true; + } + else if ( mod == null || mod.Offset > offset ) + { + target.AddStatMod( new StatMod( type, name, offset, duration ) ); + return true; + } + + return false; + } + + public static TimeSpan GetDuration( Mobile caster, Mobile target ) + { + if ( Core.AOS ) + return TimeSpan.FromSeconds( ( ( 6 * caster.Skills.EvalInt.Fixed ) / 50 ) + 1 ); + + return TimeSpan.FromSeconds( caster.Skills[SkillName.Magery].Value * 1.2 ); + } + + private static bool m_DisableSkillCheck; + + public static bool DisableSkillCheck + { + get { return m_DisableSkillCheck; } + set { m_DisableSkillCheck = value; } + } + + public static double GetOffsetScalar( Mobile caster, Mobile target, bool curse ) + { + double percent; + + if( curse ) + percent = 8 + (caster.Skills.EvalInt.Fixed / 100) - (target.Skills.MagicResist.Fixed / 100); + else + percent = 1 + (caster.Skills.EvalInt.Fixed / 100); + + percent *= 0.01; + + if( percent < 0 ) + percent = 0; + + return percent; + } + + public static int GetOffset( Mobile caster, Mobile target, StatType type, bool curse ) + { + if ( Core.AOS ) + { + if ( !m_DisableSkillCheck ) + { + caster.CheckSkill( SkillName.EvalInt, 0.0, 120.0 ); + + if ( curse ) + target.CheckSkill( SkillName.MagicResist, 0.0, 120.0 ); + } + + double percent = GetOffsetScalar( caster, target, curse ); + + switch ( type ) + { + case StatType.Str: + return (int) ( target.RawStr * percent ); + case StatType.Dex: + return (int) ( target.RawDex * percent ); + case StatType.Int: + return (int) ( target.RawInt * percent ); + } + } + + return 1 + (int) ( caster.Skills[SkillName.Magery].Value * 0.1 ); + } + + public static Guild GetGuildFor( Mobile m ) + { + Guild g = m.Guild as Guild; + + if ( g == null && m is BaseCreature ) + { + BaseCreature c = (BaseCreature) m; + m = c.ControlMaster; + + if ( m != null ) + g = m.Guild as Guild; + + if ( g == null ) + { + m = c.SummonMaster; + + if ( m != null ) + g = m.Guild as Guild; + } + } + + return g; + } + + public static bool ValidIndirectTarget( Mobile from, Mobile to ) + { + if ( from == to ) + return true; + + if ( to.Hidden && to.AccessLevel > from.AccessLevel ) + return false; + + Guild fromGuild = GetGuildFor( from ); + Guild toGuild = GetGuildFor( to ); + + if ( fromGuild != null && toGuild != null && ( fromGuild == toGuild || fromGuild.IsAlly( toGuild ) ) ) + return false; + + Party p = Party.Get( from ); + + if ( p != null && p.Contains( to ) ) + return false; + + if ( to is BaseCreature ) + { + BaseCreature c = (BaseCreature) to; + + if ( c.Controlled || c.Summoned ) + { + if ( c.ControlMaster == from || c.SummonMaster == from ) + return false; + + if ( p != null && ( p.Contains( c.ControlMaster ) || p.Contains( c.SummonMaster ) ) ) + return false; + } + } + + if ( from is BaseCreature ) + { + BaseCreature c = (BaseCreature) from; + + if ( c.Controlled || c.Summoned ) + { + if ( c.ControlMaster == to || c.SummonMaster == to ) + return false; + + p = Party.Get( to ); + + if ( p != null && ( p.Contains( c.ControlMaster ) || p.Contains( c.SummonMaster ) ) ) + return false; + } + } + + if ( to is BaseCreature && !( (BaseCreature) to ).Controlled && ( (BaseCreature) to ).InitialInnocent ) + return true; + + int noto = Notoriety.Compute( from, to ); + + return ( noto != Notoriety.Innocent || from.Kills >= 5 ); + } + + private static int[] m_Offsets = new int[] + { + -1, -1, + -1, 0, + -1, 1, + 0, -1, + 0, 1, + 1, -1, + 1, 0, + 1, 1 + }; + + public static void Summon( BaseCreature creature, Mobile caster, int sound, TimeSpan duration, bool scaleDuration, bool scaleStats ) + { + Map map = caster.Map; + + if ( map == null ) + return; + + double scale = 1.0 + ( ( caster.Skills[SkillName.Magery].Value - 100.0 ) / 200.0 ); + + if ( scaleDuration ) + duration = TimeSpan.FromSeconds( duration.TotalSeconds * scale ); + + if ( scaleStats ) + { + creature.RawStr = (int) ( creature.RawStr * scale ); + creature.Hits = creature.HitsMax; + + creature.RawDex = (int) ( creature.RawDex * scale ); + creature.Stam = creature.StamMax; + + creature.RawInt = (int) ( creature.RawInt * scale ); + creature.Mana = creature.ManaMax; + } + + int offset = Utility.Random( 8 ) * 2; + + for ( int i = 0; i < m_Offsets.Length; i += 2 ) + { + int x = caster.X + m_Offsets[( offset + i ) % m_Offsets.Length]; + int y = caster.Y + m_Offsets[( offset + i + 1 ) % m_Offsets.Length]; + + if ( map.CanSpawnMobile( x, y, caster.Z ) ) + { + BaseCreature.Summon( creature, caster, new Point3D( x, y, caster.Z ), sound, duration ); + return; + } + else + { + int z = map.GetAverageZ( x, y ); + + if ( map.CanSpawnMobile( x, y, z ) ) + { + BaseCreature.Summon( creature, caster, new Point3D( x, y, z ), sound, duration ); + return; + } + } + } + + creature.Delete(); + caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + + private delegate bool TravelValidator( Map map, Point3D loc ); + + private static TravelValidator[] m_Validators = new TravelValidator[] + { + new TravelValidator( IsFeluccaT2A ), + new TravelValidator( IsIlshenar ), + new TravelValidator( IsTrammelWind ), + new TravelValidator( IsFeluccaWind ), + new TravelValidator( IsFeluccaDungeon ), + new TravelValidator( IsTrammelSolenHive ), + new TravelValidator( IsFeluccaSolenHive ), + new TravelValidator( IsCrystalCave ), + new TravelValidator( IsDoomGauntlet ), + new TravelValidator( IsDoomFerry ), + new TravelValidator( IsFactionStronghold ), + new TravelValidator( IsChampionSpawn ), + new TravelValidator( IsTokunoDungeon ) + }; + + private static bool[,] m_Rules = new bool[,] + { + /*T2A(Fel) Ilshenar Wind(Tram), Wind(Fel), Dungeons(Fel), Solen(Tram), Solen(Fel), CrystalCave(Malas), Gauntlet(Malas), Gauntlet(Ferry), Stronghold, ChampionSpawn, Dungeons(Tokuno[Malas]) */ +/* Recall From */ { false, true, true, false, false, true, false, false, false, false, true, true, true }, +/* Recall To */ { false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Gate From */ { false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Gate To */ { false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Mark In */ { false, false, false, false, false, false, false, false, false, false, false, false, false }, +/* Tele From */ { true, true, true, true, true, true, true, false, true, true, false, true, true }, +/* Tele To */ { true, true, true, true, true, true, true, false, true, false, false, true, true }, + }; + + public static bool CheckTravel( Mobile caster, TravelCheckType type ) + { + if ( CheckTravel( caster, caster.Map, caster.Location, type ) ) + return true; + + SendInvalidMessage( caster, type ); + return false; + } + + public static void SendInvalidMessage( Mobile caster, TravelCheckType type ) + { + if ( type == TravelCheckType.RecallTo || type == TravelCheckType.GateTo ) + caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + else if ( type == TravelCheckType.TeleportTo ) + caster.SendLocalizedMessage( 501035 ); // You cannot teleport from here to the destination. + else + caster.SendLocalizedMessage( 501802 ); // Thy spell doth not appear to work... + } + + public static bool CheckTravel( Map map, Point3D loc, TravelCheckType type ) + { + return CheckTravel( null, map, loc, type ); + } + + private static Mobile m_TravelCaster; + private static TravelCheckType m_TravelType; + + public static bool CheckTravel( Mobile caster, Map map, Point3D loc, TravelCheckType type ) + { + if ( IsInvalid( map, loc ) ) // null, internal, out of bounds + { + if ( caster != null ) + SendInvalidMessage( caster, type ); + + return false; + } + + m_TravelCaster = caster; + m_TravelType = type; + + int v = (int) type; + bool isValid = true; + + for ( int i = 0; isValid && i < m_Validators.Length; ++i ) + isValid = ( m_Rules[v, i] || !m_Validators[i]( map, loc ) ); + + if ( !isValid && caster != null ) + SendInvalidMessage( caster, type ); + + return isValid; + } + + public static bool IsWindLoc( Point3D loc ) + { + int x = loc.X, y = loc.Y; + + return ( x >= 5120 && y >= 0 && x < 5376 && y < 256 ); + } + + public static bool IsFeluccaWind( Map map, Point3D loc ) + { + return ( map == Map.Felucca && IsWindLoc( loc ) ); + } + + public static bool IsTrammelWind( Map map, Point3D loc ) + { + return ( map == Map.Trammel && IsWindLoc( loc ) ); + } + + public static bool IsIlshenar( Map map, Point3D loc ) + { + return ( map == Map.Ilshenar ); + } + + public static bool IsSolenHiveLoc( Point3D loc ) + { + int x = loc.X, y = loc.Y; + + return ( x >= 5640 && y >= 1776 && x < 5935 && y < 2039 ); + } + + public static bool IsTrammelSolenHive( Map map, Point3D loc ) + { + return ( map == Map.Trammel && IsSolenHiveLoc( loc ) ); + } + + public static bool IsFeluccaSolenHive( Map map, Point3D loc ) + { + return ( map == Map.Felucca && IsSolenHiveLoc( loc ) ); + } + + public static bool IsFeluccaT2A( Map map, Point3D loc ) + { + int x = loc.X, y = loc.Y; + + return ( map == Map.Felucca && x >= 5120 && y >= 2304 && x < 6144 && y < 4096 ); + } + + public static bool IsAnyT2A( Map map, Point3D loc ) + { + int x = loc.X, y = loc.Y; + + return ( ( map == Map.Trammel || map == Map.Felucca ) && x >= 5120 && y >= 2304 && x < 6144 && y < 4096 ); + } + + public static bool IsFeluccaDungeon( Map map, Point3D loc ) + { + Region region = Region.Find( loc, map ); + return ( region.IsPartOf( typeof( DungeonRegion ) ) && region.Map == Map.Felucca ); + } + + public static bool IsCrystalCave( Map map, Point3D loc ) + { + if ( map != Map.Malas ) + return false; + + int x = loc.X, y = loc.Y, z = loc.Z; + + bool r1 = ( x >= 1182 && y >= 437 && x < 1211 && y < 470 ); + bool r2 = ( x >= 1156 && y >= 470 && x < 1211 && y < 503 ); + bool r3 = ( x >= 1176 && y >= 503 && x < 1208 && y < 509 ); + bool r4 = ( x >= 1188 && y >= 509 && x < 1201 && y < 513 ); + + return ( z < -80 && ( r1 || r2 || r3 || r4 ) ); + } + + public static bool IsFactionStronghold( Map map, Point3D loc ) + { + /*// Teleporting is allowed, but only for faction members + if ( !Core.AOS && m_TravelCaster != null && (m_TravelType == TravelCheckType.TeleportTo || m_TravelType == TravelCheckType.TeleportFrom) ) + { + if ( Factions.Faction.Find( m_TravelCaster, true, true ) != null ) + return false; + }*/ + + return ( Region.Find( loc, map ).IsPartOf( typeof( Factions.StrongholdRegion ) ) ); + } + + public static bool IsChampionSpawn( Map map, Point3D loc ) + { + return ( Region.Find( loc, map ).IsPartOf( typeof( Engines.CannedEvil.ChampionSpawnRegion ) ) ); + } + + public static bool IsDoomFerry( Map map, Point3D loc ) + { + if ( map != Map.Malas ) + return false; + + int x = loc.X, y = loc.Y; + + if ( x >= 426 && y >= 314 && x <= 430 && y <= 331 ) + return true; + + if ( x >= 406 && y >= 247 && x <= 410 && y <= 264 ) + return true; + + return false; + } + + public static bool IsTokunoDungeon( Map map, Point3D loc ) + { + //The tokuno dungeons are really inside malas + if ( map != Map.Malas ) + return false; + + int x = loc.X, y = loc.Y, z = loc.Z; + + bool r1 = ( x >= 0 && y >= 0 && x <= 128 && y <= 128 ); + bool r2 = ( x >= 45 && y >= 320 && x < 195 && y < 710 ); + + return ( r1 || r2 ); + } + + public static bool IsDoomGauntlet( Map map, Point3D loc ) + { + if ( map != Map.Malas ) + return false; + + int x = loc.X - 256, y = loc.Y - 304; + + return ( x >= 0 && y >= 0 && x < 256 && y < 256 ); + } + + public static bool IsInvalid( Map map, Point3D loc ) + { + if ( map == null || map == Map.Internal ) + return true; + + int x = loc.X, y = loc.Y; + + return ( x < 0 || y < 0 || x >= map.Width || y >= map.Height ); + } + + //towns + public static bool IsTown( IPoint3D loc, Mobile caster ) + { + if ( loc is Item ) + loc = ( (Item) loc ).GetWorldLocation(); + + return IsTown( new Point3D( loc ), caster ); + } + + public static bool IsTown( Point3D loc, Mobile caster ) + { + Map map = caster.Map; + + if ( map == null ) + return false; + + GuardedRegion reg = (GuardedRegion) Region.Find( loc, map ).GetRegion( typeof( GuardedRegion ) ); + + return ( reg != null && !reg.IsDisabled() ); + } + + public static bool CheckTown( IPoint3D loc, Mobile caster ) + { + if ( loc is Item ) + loc = ( (Item) loc ).GetWorldLocation(); + + return CheckTown( new Point3D( loc ), caster ); + } + + public static bool CheckTown( Point3D loc, Mobile caster ) + { + if ( IsTown( loc, caster ) ) + { + caster.SendLocalizedMessage( 500946 ); // You cannot cast this in town! + return false; + } + + return true; + } + + //magic reflection + public static void CheckReflect( int circle, Mobile caster, ref Mobile target ) + { + CheckReflect( circle, ref caster, ref target ); + } + + public static void CheckReflect( int circle, ref Mobile caster, ref Mobile target ) + { + if ( target.MagicDamageAbsorb > 0 ) + { + ++circle; + + target.MagicDamageAbsorb -= circle; + + // This order isn't very intuitive, but you have to nullify reflect before target gets switched + + bool reflect = ( target.MagicDamageAbsorb >= 0 ); + + if ( target is BaseCreature ) + ( (BaseCreature) target ).CheckReflect( caster, ref reflect ); + + if ( target.MagicDamageAbsorb <= 0 ) + { + target.MagicDamageAbsorb = 0; + DefensiveSpell.Nullify( target ); + } + + if ( reflect ) + { + target.FixedEffect( 0x37B9, 10, 5 ); + + Mobile temp = caster; + caster = target; + target = temp; + } + } + else if ( target is BaseCreature ) + { + bool reflect = false; + + ( (BaseCreature) target ).CheckReflect( caster, ref reflect ); + + if ( reflect ) + { + target.FixedEffect( 0x37B9, 10, 5 ); + + Mobile temp = caster; + caster = target; + target = temp; + } + } + } + + public static void Damage( Spell spell, Mobile target, double damage ) + { + TimeSpan ts = GetDamageDelayForSpell( spell ); + + Damage( spell, ts, target, spell.Caster, damage ); + } + + public static void Damage( TimeSpan delay, Mobile target, double damage ) + { + Damage( delay, target, null, damage ); + } + + public static void Damage( TimeSpan delay, Mobile target, Mobile from, double damage ) + { + Damage( null, delay, target, from, damage ); + } + + public static void Damage( Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage ) + { + int iDamage = (int) damage; + + if ( delay == TimeSpan.Zero ) + { + if ( from is BaseCreature ) + ( (BaseCreature) from ).AlterSpellDamageTo( target, ref iDamage ); + + if ( target is BaseCreature ) + ( (BaseCreature) target ).AlterSpellDamageFrom( from, ref iDamage ); + + target.Damage( iDamage, from ); + } + else + { + new SpellDamageTimer( spell, target, from, iDamage, delay ).Start(); + } + + if ( target is BaseCreature && from != null && delay == TimeSpan.Zero ) + ( (BaseCreature) target ).OnDamagedBySpell( from ); + } + + public static void Damage( Spell spell, Mobile target, double damage, int phys, int fire, int cold, int pois, int nrgy ) + { + TimeSpan ts = GetDamageDelayForSpell( spell ); + + Damage( spell, ts, target, spell.Caster, damage, phys, fire, cold, pois, nrgy, DFAlgorithm.Standard ); + } + + public static void Damage( Spell spell, Mobile target, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa ) + { + TimeSpan ts = GetDamageDelayForSpell( spell ); + + Damage( spell, ts, target, spell.Caster, damage, phys, fire, cold, pois, nrgy, dfa ); + } + + public static void Damage( TimeSpan delay, Mobile target, double damage, int phys, int fire, int cold, int pois, int nrgy ) + { + Damage( delay, target, null, damage, phys, fire, cold, pois, nrgy ); + } + + public static void Damage( TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy ) + { + Damage( delay, target, from, damage, phys, fire, cold, pois, nrgy, DFAlgorithm.Standard ); + } + + public static void Damage( TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa ) + { + Damage( null, delay, target, from, damage, phys, fire, cold, pois, nrgy, dfa ); + } + + public static void Damage( Spell spell, TimeSpan delay, Mobile target, Mobile from, double damage, int phys, int fire, int cold, int pois, int nrgy, DFAlgorithm dfa ) + { + int iDamage = (int) damage; + + if ( delay == TimeSpan.Zero ) + { + if ( from is BaseCreature ) + ( (BaseCreature) from ).AlterSpellDamageTo( target, ref iDamage ); + + if ( target is BaseCreature ) + ( (BaseCreature) target ).AlterSpellDamageFrom( from, ref iDamage ); + + WeightOverloading.DFA = dfa; + AOS.Damage( target, from, iDamage, phys, fire, cold, pois, nrgy ); + WeightOverloading.DFA = DFAlgorithm.Standard; + } + else + { + new SpellDamageTimerAOS( spell, target, from, iDamage, phys, fire, cold, pois, nrgy, delay, dfa ).Start(); + } + + if ( target is BaseCreature && from != null && delay == TimeSpan.Zero ) + ( (BaseCreature) target ).OnDamagedBySpell( from ); + } + + private class SpellDamageTimer : Timer + { + private Mobile m_Target, m_From; + private int m_Damage; + private Spell m_Spell; + + public SpellDamageTimer( Spell s, Mobile target, Mobile from, int damage, TimeSpan delay ) + : base( delay ) + { + m_Target = target; + m_From = from; + m_Damage = damage; + m_Spell = s; + + if ( m_Spell != null && m_Spell.DelayedDamage && !m_Spell.DelayedDamageStacking ) + m_Spell.StartDelayedDamageContext( from, this ); + + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + if ( m_From is BaseCreature ) + ( (BaseCreature) m_From ).AlterSpellDamageTo( m_Target, ref m_Damage ); + + if ( m_Target is BaseCreature ) + ( (BaseCreature) m_Target ).AlterSpellDamageFrom( m_From, ref m_Damage ); + + m_Target.Damage( m_Damage ); + if ( m_Spell != null ) + m_Spell.RemoveDelayedDamageContext( m_From ); + } + } + + private class SpellDamageTimerAOS : Timer + { + private Mobile m_Target, m_From; + private int m_Damage; + private int m_Phys, m_Fire, m_Cold, m_Pois, m_Nrgy; + private DFAlgorithm m_DFA; + private Spell m_Spell; + + public SpellDamageTimerAOS( Spell s, Mobile target, Mobile from, int damage, int phys, int fire, int cold, int pois, int nrgy, TimeSpan delay, DFAlgorithm dfa ) + : base( delay ) + { + m_Target = target; + m_From = from; + m_Damage = damage; + m_Phys = phys; + m_Fire = fire; + m_Cold = cold; + m_Pois = pois; + m_Nrgy = nrgy; + m_DFA = dfa; + m_Spell = s; + if ( m_Spell != null && m_Spell.DelayedDamage && !m_Spell.DelayedDamageStacking ) + m_Spell.StartDelayedDamageContext( from, this ); + + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + if ( m_From is BaseCreature && m_Target != null ) + ( (BaseCreature) m_From ).AlterSpellDamageTo( m_Target, ref m_Damage ); + + if ( m_Target is BaseCreature && m_From != null ) + ( (BaseCreature) m_Target ).AlterSpellDamageFrom( m_From, ref m_Damage ); + + WeightOverloading.DFA = m_DFA; + AOS.Damage( m_Target, m_From, m_Damage, m_Phys, m_Fire, m_Cold, m_Pois, m_Nrgy ); + WeightOverloading.DFA = DFAlgorithm.Standard; + + if ( m_Target is BaseCreature && m_From != null ) + ( (BaseCreature) m_Target ).OnDamagedBySpell( m_From ); + + if ( m_Spell != null ) + m_Spell.RemoveDelayedDamageContext( m_From ); + + } + } + } +} diff --git a/Scripts/Spells/Base/SpellInfo.cs b/Scripts/Spells/Base/SpellInfo.cs new file mode 100644 index 000000000..7e4d6bf28 --- /dev/null +++ b/Scripts/Spells/Base/SpellInfo.cs @@ -0,0 +1,69 @@ +using System; +using Server; + +namespace Server.Spells +{ + public class SpellInfo + { + private string m_Name; + private string m_Mantra; + private SpellCircle m_Circle; + private Type[] m_Reagents; + private int[] m_Amounts; + private int m_Action; + private bool m_AllowTown; + private int m_LeftHandEffect, m_RightHandEffect; + + public SpellInfo( string name, string mantra, SpellCircle circle, params Type[] regs ) : this( name, mantra, circle, 16, 0, 0, true, regs ) + { + } + + public SpellInfo( string name, string mantra, SpellCircle circle, bool allowTown, params Type[] regs ) : this( name, mantra, circle, 16, 0, 0, allowTown, regs ) + { + } + + public SpellInfo( string name, string mantra, SpellCircle circle, int action, params Type[] regs ) : this( name, mantra, circle, action, 0, 0, true, regs ) + { + } + + public SpellInfo( string name, string mantra, SpellCircle circle, int action, bool allowTown, params Type[] regs ) : this( name, mantra, circle, action, 0, 0, allowTown, regs ) + { + } + + public SpellInfo( string name, string mantra, SpellCircle circle, int action, int handEffect, params Type[] regs ) : this( name, mantra, circle, action, handEffect, handEffect, true, regs ) + { + } + + public SpellInfo( string name, string mantra, SpellCircle circle, int action, int handEffect, bool allowTown, params Type[] regs ) : this( name, mantra, circle, action, handEffect, handEffect, allowTown, regs ) + { + } + + public SpellInfo( string name, string mantra, SpellCircle circle, int action, int leftHandEffect, int rightHandEffect, bool allowTown, params Type[] regs ) + { + m_Name = name; + m_Mantra = mantra; + m_Circle = circle; + m_Action = action; + m_Reagents = regs; + m_AllowTown = allowTown; + + m_LeftHandEffect = leftHandEffect; + m_RightHandEffect = rightHandEffect; + + m_Amounts = new int[regs.Length]; + + for ( int i = 0; i < regs.Length; ++i ) + m_Amounts[i] = 1; + } + + public int Action{ get{ return m_Action; } set{ m_Action = value; } } + public bool AllowTown{ get{ return m_AllowTown; } set{ m_AllowTown = value; } } + public int[] Amounts{ get{ return m_Amounts; } set{ m_Amounts = value; } } + public SpellCircle Circle{ get{ return m_Circle; } set{ m_Circle = value; } } + public string Mantra{ get{ return m_Mantra; } set{ m_Mantra = value; } } + public string Name{ get{ return m_Name; } set{ m_Name = value; } } + public Type[] Reagents{ get{ return m_Reagents; } set{ m_Reagents = value; } } + public int LeftHandEffect{ get{ return m_LeftHandEffect; } set{ m_LeftHandEffect = value; } } + public int RightHandEffect{ get{ return m_RightHandEffect; } set{ m_RightHandEffect = value; } } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/SpellRegistry.cs b/Scripts/Spells/Base/SpellRegistry.cs new file mode 100644 index 000000000..7e87bd860 --- /dev/null +++ b/Scripts/Spells/Base/SpellRegistry.cs @@ -0,0 +1,182 @@ +using System; +using System.IO; +using Server.Items; +using System.Collections; +using Server.Spells.Necromancy; +using Server.Spells.Chivalry; +using Server.Spells.Bushido; +using Server.Spells.Ninjitsu; + +namespace Server.Spells +{ + public class SpellRegistry + { + private static Type[] m_Types = new Type[600]; + private static int m_Count; + + public static Type[] Types + { + get + { + m_Count = -1; + return m_Types; + } + } + + //What IS this used for anyways. + public static int Count + { + get + { + if ( m_Count == -1 ) + { + m_Count = 0; + + for ( int i = 0; i < m_Types.Length; ++i ) + { + if ( m_Types[i] != null ) + ++m_Count; + } + } + + return m_Count; + } + } + + public static int GetRegistryNumber( ISpell s ) + { + return GetRegistryNumber( s.GetType() ); + } + + public static int GetRegistryNumber( SpecialMove s ) + { + return GetRegistryNumber( s.GetType() ); + } + + private static Hashtable m_IDsFromTypes = new Hashtable( m_Types.Length ); + + public static int GetRegistryNumber( Type type ) + { + if( m_IDsFromTypes.Contains( type ) ) + return (int)m_IDsFromTypes[type]; + + return -1; + } + + private static Hashtable m_SpecialMoves = new Hashtable(); + + public static Hashtable SpecialMoves { get { return m_SpecialMoves; } } + + public static void Register( int spellID, Type type ) + { + if ( spellID < 0 || spellID >= m_Types.Length ) + return; + + if ( m_Types[spellID] == null ) + ++m_Count; + + m_Types[spellID] = type; + + if( m_IDsFromTypes[type] == null ) + m_IDsFromTypes.Add( type, spellID ); + + if( type.IsSubclassOf( typeof( SpecialMove ) ) ) + { + SpecialMove spm = null; + + try + { + spm = Activator.CreateInstance( type ) as SpecialMove; + } + catch + { + } + + if( spm != null ) + m_SpecialMoves.Add( spellID, spm ); + } + } + + public static SpecialMove GetSpecialMove( int spellID ) + { + if ( spellID < 0 || spellID >= m_Types.Length ) + return null; + + Type t = m_Types[spellID]; + + if ( t == null || !t.IsSubclassOf( typeof( SpecialMove ) ) ) //Ensure correct registration + return null; + + if( m_SpecialMoves.ContainsKey( spellID ) ) + return m_SpecialMoves[spellID] as SpecialMove; + + return null; + } + + private static object[] m_Params = new object[2]; + + public static Spell NewSpell( int spellID, Mobile caster, Item scroll ) + { + if ( spellID < 0 || spellID >= m_Types.Length ) + return null; + + Type t = m_Types[spellID]; + + if( t != null && !t.IsSubclassOf( typeof( SpecialMove ) ) ) + { + m_Params[0] = caster; + m_Params[1] = scroll; + + try + { + return (Spell)Activator.CreateInstance( t, m_Params ); + } + catch + { + } + } + + return null; + } + + private static string[] m_CircleNames = new string[] + { + "First", + "Second", + "Third", + "Fourth", + "Fifth", + "Sixth", + "Seventh", + "Eighth", + "Necromancy", + "Chivalry", + "Bushido", + "Ninjitsu" + }; + + public static Spell NewSpell( string name, Mobile caster, Item scroll ) + { + for ( int i = 0; i < m_CircleNames.Length; ++i ) + { + Type t = ScriptCompiler.FindTypeByFullName( String.Format( "Server.Spells.{0}.{1}", m_CircleNames[i], name ) ); + + if ( t != null && !t.IsSubclassOf( typeof( SpecialMove ) ) ) + { + m_Params[0] = caster; + m_Params[1] = scroll; + + try + { + return (Spell)Activator.CreateInstance( t, m_Params ); + } + catch + { + } + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Base/SpellState.cs b/Scripts/Spells/Base/SpellState.cs new file mode 100644 index 000000000..237a99aa1 --- /dev/null +++ b/Scripts/Spells/Base/SpellState.cs @@ -0,0 +1,11 @@ +using System; + +namespace Server.Spells +{ + public enum SpellState + { + None = 0, + Casting = 1, // We are in the process of casting (that is, waiting GetCastTime() and doing animations). Spell casting may be interupted in this state. + Sequencing = 2 // Casting completed, but the full spell sequence isn't. Usually waiting for a target response. Some actions are restricted in this state (using skills for example). + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/Confidence.cs b/Scripts/Spells/Bushido/Confidence.cs new file mode 100644 index 000000000..8d5cbcb30 --- /dev/null +++ b/Scripts/Spells/Bushido/Confidence.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; + +namespace Server.Spells.Bushido +{ + public class Confidence : SamuraiSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Confidence", null, + SpellCircle.First, // 0 + 0.25 = 0.25s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 25.0; } } + public override int RequiredMana{ get{ return 10; } } + + public Confidence( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnBeginCast() + { + base.OnBeginCast(); + + Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 ); + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + Caster.SendLocalizedMessage( 1063115 ); // You exude confidence. + + Caster.FixedParticles( 0x375A, 1, 17, 0x7DA, 0x960, 0x3, EffectLayer.Waist ); + Caster.PlaySound( 0x51A ); + + OnCastSuccessful( Caster ); + + BeginConfidence( Caster ); + BeginRegenerating( Caster ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsConfident( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void BeginConfidence( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( m ); + + m_Table[m] = t; + + t.Start(); + } + + public static void EndConfidence( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table.Remove( m ); + + OnEffectEnd( m, typeof( Confidence ) ); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m ) : base( TimeSpan.FromSeconds( 15.0 ) ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + EndConfidence( m_Mobile ); + m_Mobile.SendLocalizedMessage( 1063116 ); // Your confidence wanes. + } + } + + private static Hashtable m_RegenTable = new Hashtable(); + + public static bool IsRegenerating( Mobile m ) + { + return m_RegenTable.Contains( m ); + } + + public static void BeginRegenerating( Mobile m ) + { + Timer t = (Timer)m_RegenTable[m]; + + if ( t != null ) + t.Stop(); + + t = new RegenTimer( m ); + + m_RegenTable[m] = t; + + t.Start(); + } + + public static void StopRegenerating( Mobile m ) + { + Timer t = (Timer)m_RegenTable[m]; + + if ( t != null ) + t.Stop(); + + m_RegenTable.Remove( m ); + } + + private class RegenTimer : Timer + { + private Mobile m_Mobile; + private int m_Ticks; + private int m_Hits; + + public RegenTimer( Mobile m ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = m; + m_Hits = 15 + (m.Skills.Bushido.Fixed * m.Skills.Bushido.Fixed / 57600); + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + ++m_Ticks; + + if ( m_Ticks >= 5 ) + { + m_Mobile.Hits += (m_Hits - (m_Hits * 4 / 5)); + StopRegenerating( m_Mobile ); + } + + m_Mobile.Hits += (m_Hits / 5); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/CounterAttack.cs b/Scripts/Spells/Bushido/CounterAttack.cs new file mode 100644 index 000000000..e0cdcf453 --- /dev/null +++ b/Scripts/Spells/Bushido/CounterAttack.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Bushido +{ + public class CounterAttack : SamuraiSpell + { + private static SpellInfo m_Info = new SpellInfo( + "CounterAttack", null, + SpellCircle.First, // 0 + 0.25 = 0.25s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 40.0; } } + public override int RequiredMana{ get{ return 5; } } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseShield != null ) + return true; + + if ( Caster.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon != null ) + return true; + + if ( Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon != null ) + return true; + + Caster.SendLocalizedMessage( 1062944 ); // You must have a weapon or a shield equipped to use this ability! + return false; + } + + public CounterAttack( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnBeginCast() + { + base.OnBeginCast(); + + Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 ); + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + Caster.SendLocalizedMessage( 1063118 ); // You prepare to respond immediately to the next blocked blow. + + OnCastSuccessful( Caster ); + + StartCountering( Caster ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsCountering( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void StartCountering( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( m ); + + m_Table[m] = t; + + t.Start(); + } + + public static void StopCountering( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table.Remove( m ); + + OnEffectEnd( m, typeof( CounterAttack ) ); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m ) : base( TimeSpan.FromSeconds( 30.0 ) ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + StopCountering( m_Mobile ); + m_Mobile.SendLocalizedMessage( 1063119 ); // You return to your normal stance. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/Evasion.cs b/Scripts/Spells/Bushido/Evasion.cs new file mode 100644 index 000000000..af597fd9a --- /dev/null +++ b/Scripts/Spells/Bushido/Evasion.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Bushido +{ + public class Evasion : SamuraiSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Evasion", null, + SpellCircle.First, // 0 + 0.25 = 0.25s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 60.0; } } + public override int RequiredMana{ get{ return 10; } } + + public override bool CheckCast() + { + if ( Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseShield != null ) + return base.CheckCast(); + + if ( Caster.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon != null ) + return base.CheckCast(); + + if ( Caster.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon != null ) + return base.CheckCast(); + + Caster.SendLocalizedMessage( 1062944 ); // You must have a weapon or a shield equipped to use this ability! + return false; + } + + public Evasion( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnBeginCast() + { + base.OnBeginCast(); + + Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 ); + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + Caster.SendLocalizedMessage( 1063120 ); // You feel that you might be able to deflect any attack! + + Caster.FixedParticles( 0x376A, 1, 20, 0x7F5, 0x960, 0x3, EffectLayer.Waist ); + Caster.PlaySound( 0x51B ); + + OnCastSuccessful( Caster ); + + BeginEvasion( Caster ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool IsEvading( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static void BeginEvasion( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + t = new InternalTimer( m ); + + m_Table[m] = t; + + t.Start(); + } + + public static void EndEvasion( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + t.Stop(); + + m_Table.Remove( m ); + + OnEffectEnd( m, typeof( Evasion ) ); + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m ) : base( TimeSpan.FromSeconds( 8.0 ) ) + { + m_Mobile = m; + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + EndEvasion( m_Mobile ); + m_Mobile.SendLocalizedMessage( 1063121 ); // You no longer feel that you could deflect any attack. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/HonorableExecution.cs b/Scripts/Spells/Bushido/HonorableExecution.cs new file mode 100644 index 000000000..fd75974ac --- /dev/null +++ b/Scripts/Spells/Bushido/HonorableExecution.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Bushido +{ + public class HonorableExecution : SamuraiMove + { + public HonorableExecution() + { + } + + public override int BaseMana{ get{ return 0; } } + public override double RequiredSkill{ get{ return 25.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063122 ); } } // You better kill your enemy with your next hit or you'll be rather sorry... + + public override double GetAccuracyScalar( Mobile attacker ) + { + double bushido = attacker.Skills[SkillName.Bushido].Value; + + // TODO: 4 -> Perfection / 5 + return 1.0 + ( bushido / 10.0 + 4 ) / 100.0; + } + + public override double GetDamageScalar( Mobile attacker, Mobile defender ) + { + double bushido = attacker.Skills[SkillName.Bushido].Value; + + // TODO: 20 -> Perfection + return 1.0 + (bushido * 20) / 10000; + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentMove( attacker ); + + HonorableExecutionInfo info = m_Table[attacker] as HonorableExecutionInfo; + + if ( info != null ) + { + info.Clear(); + + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + } + + if ( !defender.Alive ) + { + attacker.FixedParticles( 0x373A, 1, 17, 0x7E2, EffectLayer.Waist ); + + double bushido = attacker.Skills[SkillName.Bushido].Value; + + attacker.Hits += 20 + (int)((bushido * bushido) / 480.0); + + int swingBonus = Math.Max( 1, (int)((bushido * bushido) / 720.0) ); + + info = new HonorableExecutionInfo( attacker, swingBonus ); + info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 20.0 ), new TimerStateCallback( EndEffect ), info ); + + m_Table[attacker] = info; + } + else + { + ArrayList mods = new ArrayList(); + + mods.Add( new ResistanceMod( ResistanceType.Physical, -40 ) ); + mods.Add( new ResistanceMod( ResistanceType.Fire, -40 ) ); + mods.Add( new ResistanceMod( ResistanceType.Cold, -40 ) ); + mods.Add( new ResistanceMod( ResistanceType.Poison, -40 ) ); + mods.Add( new ResistanceMod( ResistanceType.Energy, -40 ) ); + + double resSpells = attacker.Skills[SkillName.MagicResist].Value; + + if ( resSpells > 0.0 ) + mods.Add( new DefaultSkillMod( SkillName.MagicResist, true, -resSpells ) ); + + info = new HonorableExecutionInfo( attacker, mods ); + info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 7.0 ), new TimerStateCallback( EndEffect ), info ); + + m_Table[attacker] = info; + } + + CheckGain( attacker ); + } + + + private static Hashtable m_Table = new Hashtable(); + + public static int GetSwingBonus( Mobile target ) + { + HonorableExecutionInfo info = m_Table[target] as HonorableExecutionInfo; + + if ( info == null ) + return 0; + + return info.m_SwingBonus; + } + + public static bool IsUnderPenalty( Mobile target ) + { + HonorableExecutionInfo info = m_Table[target] as HonorableExecutionInfo; + + if ( info == null ) + return false; + + return info.m_Penalty; + } + + public static void RemovePenalty( Mobile target ) + { + HonorableExecutionInfo info = m_Table[target] as HonorableExecutionInfo; + + if ( info == null || !info.m_Penalty ) + return; + + info.Clear(); + + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + m_Table.Remove( target ); + } + + private class HonorableExecutionInfo + { + public Mobile m_Mobile; + public int m_SwingBonus; + public ArrayList m_Mods; + public bool m_Penalty; + public Timer m_Timer; + + public HonorableExecutionInfo( Mobile from, int swingBonus ) : this( from, swingBonus, null, false ) + { + } + + public HonorableExecutionInfo( Mobile from, ArrayList mods ) : this( from, 0, mods, true ) + { + } + + public HonorableExecutionInfo( Mobile from, int swingBonus, ArrayList mods, bool penalty ) + { + m_Mobile = from; + m_SwingBonus = swingBonus; + m_Mods = mods; + m_Penalty = penalty; + + Apply(); + } + + public void Apply() + { + if ( m_Mods == null ) + return; + + for ( int i = 0; i < m_Mods.Count; ++i ) + { + object mod = m_Mods[i]; + + if ( mod is ResistanceMod ) + m_Mobile.AddResistanceMod( (ResistanceMod) mod ); + else if ( mod is SkillMod ) + m_Mobile.AddSkillMod( (SkillMod) mod ); + } + } + + public void Clear() + { + if ( m_Mods == null ) + return; + + for ( int i = 0; i < m_Mods.Count; ++i ) + { + object mod = m_Mods[i]; + + if ( mod is ResistanceMod ) + m_Mobile.RemoveResistanceMod( (ResistanceMod) mod ); + else if ( mod is SkillMod ) + m_Mobile.RemoveSkillMod( (SkillMod) mod ); + } + } + } + + public void EndEffect( object state ) + { + HonorableExecutionInfo info = (HonorableExecutionInfo)state; + + RemovePenalty( info.m_Mobile ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/LightningStrike.cs b/Scripts/Spells/Bushido/LightningStrike.cs new file mode 100644 index 000000000..3033f3968 --- /dev/null +++ b/Scripts/Spells/Bushido/LightningStrike.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Bushido +{ + public class LightningStrike : SamuraiMove + { + public LightningStrike() + { + } + + public override int BaseMana{ get{ return 5; } } + public override double RequiredSkill{ get{ return 50.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063167 ); } } // You prepare to strike quickly. + + public override bool DelayedContext{ get{ return true; } } + + public override double GetAccuracyScalar( Mobile attacker ) + { + if ( GetContext( attacker, typeof( Bushido.LightningStrike ) ) ) + return 1.1; + + return 1.5; + } + + public override bool IgnoreArmor( Mobile attacker ) + { + double bushido = attacker.Skills[SkillName.Bushido].Value; + + double criticalChance = (bushido * bushido) / 72000.0; + + return ( criticalChance >= Utility.RandomDouble() ); + } + + public override bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + return Validate( attacker ) && CheckMana( attacker, true ); + } + + public override bool ValidatesDuringHit { get { return false; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + //Validation in OnBeforeSwing + + ClearCurrentMove( attacker ); + + attacker.SendLocalizedMessage( 1063168 ); // You attack with lightning precision! + defender.SendLocalizedMessage( 1063169 ); // Your opponent's quick strike causes extra damage! + + CheckGain( attacker ); + + SetContext( attacker ); + } + + public override void OnMiss( Mobile attacker, Mobile defender ) + { + ClearCurrentMove( attacker ); + + SetContext( attacker ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/MomentumStrike.cs b/Scripts/Spells/Bushido/MomentumStrike.cs new file mode 100644 index 000000000..e9be7fe45 --- /dev/null +++ b/Scripts/Spells/Bushido/MomentumStrike.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Bushido +{ + public class MomentumStrike : SamuraiMove + { + public MomentumStrike() + { + } + + public override int BaseMana{ get{ return 10; } } + public override double RequiredSkill{ get{ return 70.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1070757 ); } } // You prepare to strike two enemies with one blow. + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, false ) ) + return; + + ClearCurrentMove( attacker ); + + BaseWeapon weapon = attacker.Weapon as BaseWeapon; + + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in attacker.GetMobilesInRange( weapon.MaxRange ) ) + { + if ( m == defender ) + continue; + + if ( m.Combatant != attacker ) + continue; + + targets.Add( m ); + } + + if ( targets.Count > 0 ) + { + if ( !CheckMana( attacker, true ) ) + return; + + Mobile target = (Mobile)targets[Utility.Random( targets.Count )]; + + double damageBonus = attacker.Skills[SkillName.Bushido].Value / 100.0; + + if ( !defender.Alive ) + damageBonus *= 1.5; + + attacker.SendLocalizedMessage( 1063171 ); // You transfer the momentum of your weapon into another enemy! + target.SendLocalizedMessage( 1063172 ); // You were hit by the momentum of a Samurai's weapon! + + target.FixedParticles( 0x37B9, 1, 4, 0x251D, 0, 0, EffectLayer.Waist ); + + weapon.OnSwing( attacker, target, damageBonus ); + + CheckGain( attacker ); + } + else + { + attacker.SendLocalizedMessage( 1063123 ); // There are no valid targets to attack! + } + } + + public override void CheckGain( Mobile m ) + { + m.CheckSkill( MoveSkill, RequiredSkill, 120.0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/SamuraiMove.cs b/Scripts/Spells/Bushido/SamuraiMove.cs new file mode 100644 index 000000000..b165d17a5 --- /dev/null +++ b/Scripts/Spells/Bushido/SamuraiMove.cs @@ -0,0 +1,10 @@ +using System; +using Server; + +namespace Server.Spells +{ + public class SamuraiMove : SpecialMove + { + public override SkillName MoveSkill{ get{ return SkillName.Bushido; } } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Bushido/SamuraiSpell.cs b/Scripts/Spells/Bushido/SamuraiSpell.cs new file mode 100644 index 000000000..830f40811 --- /dev/null +++ b/Scripts/Spells/Bushido/SamuraiSpell.cs @@ -0,0 +1,126 @@ +using System; +using Server; +using Server.Spells; +using Server.Network; +using Server.Items; +using Server.Mobiles; + +namespace Server.Spells.Bushido +{ + public abstract class SamuraiSpell : Spell + { + public abstract double RequiredSkill{ get; } + public abstract int RequiredMana{ get; } + + public override SkillName CastSkill{ get{ return SkillName.Bushido; } } + + public override bool ClearHandsOnCast{ get{ return false; } } + public override bool BlocksMovement{ get{ return false; } } + public override bool ShowHandMovement{ get{ return false; } } + + public override int CastDelayBase{ get{ return 1; } } + public override int CastDelayFastScalar{ get{ return 0; } } + + public override int CastRecoveryBase{ get{ return 7; } } + + public SamuraiSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info ) + { + } + + public static bool CheckExpansion( Mobile from ) + { + if ( !( from is PlayerMobile ) ) + return true; + + if ( from.NetState == null ) + return false; + + return ( (from.NetState.Flags & 0x10) != 0 ); + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( !CheckExpansion( Caster ) ) + { + Caster.SendLocalizedMessage( 1063456 ); // You must upgrade to Samurai Empire in order to use that ability. + return false; + } + + if ( Caster.Skills[CastSkill].Value < RequiredSkill ) + { + string args = String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), CastSkill.ToString() ); + Caster.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + return false; + } + else if ( Caster.Mana < ScaleMana( RequiredMana ) ) + { + Caster.SendLocalizedMessage( 1060174, RequiredMana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + return false; + } + + return true; + } + + public override bool CheckFizzle() + { + int mana = ScaleMana( RequiredMana ); + + if ( Caster.Skills[CastSkill].Value < RequiredSkill ) + { + Caster.SendLocalizedMessage( 1070768, RequiredSkill.ToString( "F1" ) ); // You need ~1_SKILL_REQUIREMENT~ Bushido skill to perform that attack! + return false; + } + else if ( Caster.Mana < mana ) + { + Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + return false; + } + + if ( !base.CheckFizzle() ) + return false; + + Caster.Mana -= mana; + + return true; + } + + public override void GetCastSkills( out double min, out double max ) + { + min = RequiredSkill; + max = RequiredSkill + 37.5; + } + + public override int GetMana() + { + return 0; + } + + public virtual void OnCastSuccessful( Mobile caster ) + { + if ( Evasion.IsEvading( caster ) ) + Evasion.EndEvasion( caster ); + + if ( Confidence.IsConfident( caster ) ) + Confidence.EndConfidence( caster ); + + if ( CounterAttack.IsCountering( caster ) ) + CounterAttack.StopCountering( caster ); + + int spellID = SpellRegistry.GetRegistryNumber( this ); + + if ( spellID > 0 ) + caster.Send( new ToggleSpecialAbility( spellID + 1, true ) ); + } + + public static void OnEffectEnd( Mobile caster, Type type ) + { + int spellID = SpellRegistry.GetRegistryNumber( type ); + + if ( spellID > 0 ) + caster.Send( new ToggleSpecialAbility( spellID + 1, false ) ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/CleanseByFire.cs b/Scripts/Spells/Chivalry/CleanseByFire.cs new file mode 100644 index 000000000..4ea0bff8d --- /dev/null +++ b/Scripts/Spells/Chivalry/CleanseByFire.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class CleanseByFireSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Cleanse By Fire", "Expor Flamus", + SpellCircle.Fourth, // 0 + 1.0 = 1s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 5.0; } } + public override int RequiredMana{ get{ return 10; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060718; } } // Expor Flamus + + public CleanseByFireSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !m.Poisoned ) + { + Caster.SendLocalizedMessage( 1060176 ); // That creature is not poisoned! + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Cures the target of poisons, but causes the caster to be burned by fire damage for 13-55 hit points. + * The amount of fire damage is lessened if the caster has high Karma. + */ + + Poison p = m.Poison; + + if ( p != null ) + { + // Cleanse by fire is now difficulty based + int chanceToCure = 10000 + (int)(Caster.Skills[SkillName.Chivalry].Value * 75) - ((p.Level + 1) * 2000); + chanceToCure /= 100; + + if ( chanceToCure > Utility.Random( 100 ) ) + { + if ( m.CurePoison( Caster ) ) + { + if ( Caster != m ) + Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons! + + m.SendLocalizedMessage( 1010059 ); // You have been cured of all poisons. + } + } + else + { + m.SendLocalizedMessage( 1010060 ); // You have failed to cure your target! + } + } + + m.PlaySound( 0x1E0 ); + m.FixedParticles( 0x373A, 1, 15, 5012, 3, 2, EffectLayer.Waist ); + + IEntity from = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z - 5 ), m.Map ); + IEntity to = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z + 45 ), m.Map ); + Effects.SendMovingParticles( from, to, 0x374B, 1, 0, false, false, 63, 2, 9501, 1, 0, EffectLayer.Head, 0x100 ); + + Caster.PlaySound( 0x208 ); + Caster.FixedParticles( 0x3709, 1, 30, 9934, 0, 7, EffectLayer.Waist ); + + int damage = 50 - ComputePowerValue( 4 ); + + // TODO: Should caps be applied? + if ( damage < 13 ) + damage = 13; + else if ( damage > 55 ) + damage = 55; + + AOS.Damage( Caster, Caster, damage, 0, 100, 0, 0, 0, true ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private CleanseByFireSpell m_Owner; + + public InternalTarget( CleanseByFireSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/CloseWounds.cs b/Scripts/Spells/Chivalry/CloseWounds.cs new file mode 100644 index 000000000..0d0cb811b --- /dev/null +++ b/Scripts/Spells/Chivalry/CloseWounds.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class CloseWoundsSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Close Wounds", "Obsu Vulni", + SpellCircle.Sixth, // 0 + 1.5 = 1.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 0.0; } } + public override int RequiredMana{ get{ return 10; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060719; } } // Obsu Vulni + + public CloseWoundsSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.InRange( m, 2 ) ) + { + Caster.SendLocalizedMessage( 1060178 ); // You are too far away to perform that action! + } + else if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead ) + { + Caster.SendLocalizedMessage( 1061654 ); // You cannot heal that which is not alive. + } + else if ( m.IsDeadBondedPet ) + { + Caster.SendLocalizedMessage( 1060177 ); // You cannot heal a creature that is already dead! + } + else if ( m.Hits >= m.HitsMax ) + { + Caster.SendLocalizedMessage( 500955 ); // That being is not damaged! + } + else if ( m.Poisoned || Server.Items.MortalStrike.IsWounded( m ) ) + { + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, (Caster == m) ? 1005000 : 1010398 ); + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Heals the target for 7 to 39 points of damage. + * The caster's Karma affects the amount of damage healed. + */ + + int toHeal = ComputePowerValue( 6 ) + Utility.RandomMinMax( 0, 2 ); + + // TODO: Should caps be applied? + if ( toHeal < 7 ) + toHeal = 7; + else if ( toHeal > 39 ) + toHeal = 39; + + if ( (m.Hits + toHeal) > m.HitsMax ) + toHeal = m.HitsMax - m.Hits; + + m.Hits += toHeal; + + m.SendLocalizedMessage( 1060203, toHeal.ToString() ); // You have had ~1_HEALED_AMOUNT~ hit points of damage healed. + + m.PlaySound( 0x202 ); + m.FixedParticles( 0x376A, 1, 62, 9923, 3, 3, EffectLayer.Waist ); + m.FixedParticles( 0x3779, 1, 46, 9502, 5, 3, EffectLayer.Waist ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private CloseWoundsSpell m_Owner; + + public InternalTarget( CloseWoundsSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/ConsecrateWeapon.cs b/Scripts/Spells/Chivalry/ConsecrateWeapon.cs new file mode 100644 index 000000000..f26bef5b0 --- /dev/null +++ b/Scripts/Spells/Chivalry/ConsecrateWeapon.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class ConsecrateWeaponSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Consecrate Weapon", "Consecrus Arma", + SpellCircle.Second, // 0 + 0.5 = 0.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 15.0; } } + public override int RequiredMana{ get{ return 10; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060720; } } // Consecrus Arma + public override bool BlocksMovement{ get{ return false; } } + + public ConsecrateWeaponSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + BaseWeapon weapon = Caster.Weapon as BaseWeapon; + + if ( weapon == null || weapon is Fists ) + { + Caster.SendLocalizedMessage( 501078 ); // You must be holding a weapon. + } + else if ( CheckSequence() ) + { + /* Temporarily enchants the weapon the caster is currently wielding. + * The type of damage the weapon inflicts when hitting a target will + * be converted to the target's worst Resistance type. + * Duration of the effect is affected by the caster's Karma and lasts for 3 to 11 seconds. + */ + + int itemID, soundID; + + switch ( weapon.Skill ) + { + case SkillName.Macing: itemID = 0xFB4; soundID = 0x232; break; + case SkillName.Archery: itemID = 0x13B1; soundID = 0x145; break; + default: itemID = 0xF5F; soundID = 0x56; break; + } + + Caster.PlaySound( 0x20C ); + Caster.PlaySound( soundID ); + Caster.FixedParticles( 0x3779, 1, 30, 9964, 3, 3, EffectLayer.Waist ); + + IEntity from = new Entity( Serial.Zero, new Point3D( Caster.X, Caster.Y, Caster.Z ), Caster.Map ); + IEntity to = new Entity( Serial.Zero, new Point3D( Caster.X, Caster.Y, Caster.Z + 50 ), Caster.Map ); + Effects.SendMovingParticles( from, to, itemID, 1, 0, false, false, 33, 3, 9501, 1, 0, EffectLayer.Head, 0x100 ); + + double seconds = ComputePowerValue( 20 ); + + // TODO: Should caps be applied? + if ( seconds < 3.0 ) + seconds = 3.0; + else if ( seconds > 11.0 ) + seconds = 11.0; + + TimeSpan duration = TimeSpan.FromSeconds( seconds ); + + Timer t = (Timer)m_Table[weapon]; + + if ( t != null ) + t.Stop(); + + weapon.Consecrated = true; + + m_Table[weapon] = t = new ExpireTimer( weapon, duration ); + + t.Start(); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private BaseWeapon m_Weapon; + + public ExpireTimer( BaseWeapon weapon, TimeSpan delay ) : base( delay ) + { + m_Weapon = weapon; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + m_Weapon.Consecrated = false; + Effects.PlaySound( m_Weapon.GetWorldLocation(), m_Weapon.Map, 0x1F8 ); + m_Table.Remove( this ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/DispelEvil.cs b/Scripts/Spells/Chivalry/DispelEvil.cs new file mode 100644 index 000000000..499b42390 --- /dev/null +++ b/Scripts/Spells/Chivalry/DispelEvil.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Mobiles; +using Server.Spells.Necromancy; + +namespace Server.Spells.Chivalry +{ + public class DispelEvilSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Dispel Evil", "Dispiro Malas", + SpellCircle.First, // 0 + 0.25 = 0.25s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 35.0; } } + public override int RequiredMana{ get{ return 10; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060721; } } // Dispiro Malas + public override bool BlocksMovement{ get{ return false; } } + + public DispelEvilSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool DelayedDamage{ get{ return false; } } + + public override void SendCastEffect() + { + Caster.FixedEffect( 0x37C4, 10, 7, 4, 3 ); // At player + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in Caster.GetMobilesInRange( 8 ) ) + { + if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) ) + targets.Add( m ); + } + + Caster.PlaySound( 0x299 ); + Caster.FixedParticles( 0x37C4, 1, 25, 9922, 14, 3, EffectLayer.Head ); + + int dispelSkill = ComputePowerValue( 2 ); + + double chiv = Caster.Skills.Chivalry.Value; + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + BaseCreature bc = m as BaseCreature; + + if ( bc != null ) + { + bool dispellable = bc.Summoned && !bc.IsAnimatedDead; + + if ( dispellable ) + { + double dispelChance = (50.0 + ((100 * (chiv - bc.DispelDifficulty)) / (bc.DispelFocus*2))) / 100; + dispelChance *= dispelSkill / 100.0; + + if ( dispelChance > Utility.RandomDouble() ) + { + Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x3728, 8, 20, 5042 ); + Effects.PlaySound( m, m.Map, 0x201 ); + + m.Delete(); + continue; + } + } + + bool evil = !bc.Controlled && bc.Karma < 0; + + if ( evil ) + { + // TODO: Is this right? + double fleeChance = (100 - Math.Sqrt( m.Fame / 2 )) * chiv * dispelSkill; + fleeChance /= 1000000; + + if ( fleeChance > Utility.RandomDouble() ) + { + // guide says 2 seconds, it's longer + bc.BeginFlee( TimeSpan.FromSeconds( 30.0 ) ); + } + } + } + + if ( TransformationSpell.GetContext( m ) != null ) + { + // transformed .. + + double drainChance = 0.5 * (Caster.Skills.Chivalry.Value / Math.Max( m.Skills.Necromancy.Value, 1 )); + + if ( drainChance > Utility.RandomDouble() ) + { + int drain = (5 * dispelSkill) / 100; + + m.Stam -= drain; + m.Mana -= drain; + } + } + } + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/DivineFury.cs b/Scripts/Spells/Chivalry/DivineFury.cs new file mode 100644 index 000000000..ca9728d94 --- /dev/null +++ b/Scripts/Spells/Chivalry/DivineFury.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class DivineFurySpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Divine Fury", "Divinum Furis", + SpellCircle.Fourth, // 0 + 1.0 = 1s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 25.0; } } + public override int RequiredMana{ get{ return 15; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060722; } } // Divinum Furis + public override bool BlocksMovement{ get{ return false; } } + + public DivineFurySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + Caster.PlaySound( 0x20F ); + Caster.PlaySound( Caster.Body.IsFemale ? 0x338 : 0x44A ); + Caster.FixedParticles( 0x376A, 1, 31, 9961, 1160, 0, EffectLayer.Waist ); + Caster.FixedParticles( 0x37C4, 1, 31, 9502, 43, 2, EffectLayer.Waist ); + + Caster.Stam = Caster.StamMax; + + Timer t = (Timer)m_Table[Caster]; + + if ( t != null ) + t.Stop(); + + int delay = ComputePowerValue( 10 ); + + // TODO: Should caps be applied? + if ( delay < 7 ) + delay = 7; + else if ( delay > 24 ) + delay = 24; + + m_Table[Caster] = t = Timer.DelayCall( TimeSpan.FromSeconds( delay ), new TimerStateCallback( Expire_Callback ), Caster ); + Caster.Delta( MobileDelta.WeaponDamage ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool UnderEffect( Mobile m ) + { + return m_Table.Contains( m ); + } + + private static void Expire_Callback( object state ) + { + Mobile m = (Mobile)state; + + m_Table.Remove( m ); + + m.Delta( MobileDelta.WeaponDamage ); + m.PlaySound( 0xF8 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/EnemyOfOne.cs b/Scripts/Spells/Chivalry/EnemyOfOne.cs new file mode 100644 index 000000000..dc1456652 --- /dev/null +++ b/Scripts/Spells/Chivalry/EnemyOfOne.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class EnemyOfOneSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Enemy of One", "Forul Solum", + SpellCircle.Second, // 0 + 0.5 = 0.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 45.0; } } + public override int RequiredMana{ get{ return 20; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060723; } } // Forul Solum + public override bool BlocksMovement{ get{ return false; } } + + public EnemyOfOneSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + Caster.PlaySound( 0x0F5 ); + Caster.PlaySound( 0x1ED ); + Caster.FixedParticles( 0x375A, 1, 30, 9966, 33, 2, EffectLayer.Head ); + Caster.FixedParticles( 0x37B9, 1, 30, 9502, 43, 3, EffectLayer.Head ); + + Timer t = (Timer)m_Table[Caster]; + + if ( t != null ) + t.Stop(); + + double delay = (double)ComputePowerValue( 1 ) / 60; + + // TODO: Should caps be applied? + if ( delay < 1.5 ) + delay = 1.5; + else if ( delay > 3.5 ) + delay = 3.5; + + m_Table[Caster] = Timer.DelayCall( TimeSpan.FromMinutes( delay ), new TimerStateCallback( Expire_Callback ), Caster ); + + if ( Caster is PlayerMobile ) + { + ((PlayerMobile)Caster).EnemyOfOneType = null; + ((PlayerMobile)Caster).WaitingForEnemy = true; + } + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + private static void Expire_Callback( object state ) + { + Mobile m = (Mobile)state; + + m_Table.Remove( m ); + + m.PlaySound( 0x1F8 ); + + if ( m is PlayerMobile ) + { + ((PlayerMobile)m).EnemyOfOneType = null; + ((PlayerMobile)m).WaitingForEnemy = false; + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/HolyLight.cs b/Scripts/Spells/Chivalry/HolyLight.cs new file mode 100644 index 000000000..1797532db --- /dev/null +++ b/Scripts/Spells/Chivalry/HolyLight.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class HolyLightSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Holy Light", "Augus Luminos", + SpellCircle.Seventh, // 0 + 1.75 = 1.75s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 55.0; } } + public override int RequiredMana{ get{ return 10; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060724; } } // Augus Luminos + public override bool BlocksMovement{ get{ return false; } } + + public HolyLightSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool DelayedDamage{ get{ return false; } } + + public override void OnCast() + { + if ( CheckSequence() ) + { + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in Caster.GetMobilesInRange( 3 ) ) + { + if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) ) + targets.Add( m ); + } + + Caster.PlaySound( 0x212 ); + Caster.PlaySound( 0x206 ); + + Effects.SendLocationParticles( EffectItem.Create( Caster.Location, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 1, 29, 0x47D, 2, 9962, 0 ); + Effects.SendLocationParticles( EffectItem.Create( new Point3D( Caster.X, Caster.Y, Caster.Z - 7 ), Caster.Map, EffectItem.DefaultDuration ), 0x37C4, 1, 29, 0x47D, 2, 9502, 0 ); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + int damage = ComputePowerValue( 10 ) + Utility.RandomMinMax( 0, 2 ); + + // TODO: Should caps be applied? + if ( damage < 8 ) + damage = 8; + else if ( damage > 24 ) + damage = 24; + + Caster.DoHarmful( m ); + SpellHelper.Damage( this, m, damage, 0, 0, 0, 0, 100 ); + } + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/NobleSacrifice.cs b/Scripts/Spells/Chivalry/NobleSacrifice.cs new file mode 100644 index 000000000..dd852e8b6 --- /dev/null +++ b/Scripts/Spells/Chivalry/NobleSacrifice.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Gumps; +using Server.Spells.Necromancy; +using Server.Regions; + +namespace Server.Spells.Chivalry +{ + public class NobleSacrificeSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Noble Sacrifice", "Dium Prostra", + SpellCircle.Sixth, // 0 + 1.5 = 1.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 65.0; } } + public override int RequiredMana{ get{ return 20; } } + public override int RequiredTithing{ get{ return 30; } } + public override int MantraNumber{ get{ return 1060725; } } // Dium Prostra + public override bool BlocksMovement{ get{ return false; } } + + public NobleSacrificeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in Caster.GetMobilesInRange( 3 ) ) // TODO: Validate range + { + if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead ) + continue; + + if ( Caster != m && Caster.CanBeBeneficial( m, false, true ) && !(m is Golem) ) + targets.Add( m ); + } + + Caster.PlaySound( 0x244 ); + Caster.FixedParticles( 0x3709, 1, 30, 9965, 5, 7, EffectLayer.Waist ); + Caster.FixedParticles( 0x376A, 1, 30, 9502, 5, 3, EffectLayer.Waist ); + + /* Attempts to Resurrect, Cure and Heal all targets in a radius around the caster. + * If any target is successfully assisted, the Paladin's current + * Hit Points, Mana and Stamina are set to 1. + * Amount of damage healed is affected by the Caster's Karma, from 8 to 24 hit points. + */ + + bool sacrifice = false; + + // TODO: Is there really a resurrection chance? + double resChance = 0.1 + (0.9 * ((double)Caster.Karma / 10000)); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + if ( !m.Alive ) + { + if ( m.Region != null && m.Region.IsPartOf( "Khaldun" ) ) + { + Caster.SendLocalizedMessage( 1010395 ); // The veil of death in this area is too strong and resists thy efforts to restore life. + } + else if( Core.ML && m.Region != null && m.Region.IsPartOf( typeof( HouseRegion ) ) ) + { + } + else if( resChance > Utility.RandomDouble() ) + { + m.FixedParticles( 0x375A, 1, 15, 5005, 5, 3, EffectLayer.Head ); + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, Caster ) ); + sacrifice = true; + } + } + else + { + bool sendEffect = false; + + if ( m.Poisoned && m.CurePoison( Caster ) ) + { + Caster.DoBeneficial( m ); + + if ( Caster != m ) + Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons! + + m.SendLocalizedMessage( 1010059 ); // You have been cured of all poisons. + sendEffect = true; + sacrifice = true; + } + + if ( m.Hits < m.HitsMax ) + { + int toHeal = ComputePowerValue( 10 ) + Utility.RandomMinMax( 0, 2 ); + + // TODO: Should caps be applied? + if ( toHeal < 8 ) + toHeal = 8; + else if ( toHeal > 24 ) + toHeal = 24; + + Caster.DoBeneficial( m ); + m.Heal( toHeal ); + sendEffect = true; + } + + StatMod mod; + + mod = m.GetStatMod( "[Magic] Str Offset" ); + if ( mod != null && mod.Offset < 0 ) + { + m.RemoveStatMod( "[Magic] Str Offset" ); + sendEffect = true; + } + + mod = m.GetStatMod( "[Magic] Dex Offset" ); + if ( mod != null && mod.Offset < 0 ) + { + m.RemoveStatMod( "[Magic] Dex Offset" ); + sendEffect = true; + } + + mod = m.GetStatMod( "[Magic] Int Offset" ); + if ( mod != null && mod.Offset < 0 ) + { + m.RemoveStatMod( "[Magic] Int Offset" ); + sendEffect = true; + } + + if ( m.Paralyzed ) + { + m.Paralyzed = false; + sendEffect = true; + } + + if ( EvilOmenSpell.CheckEffect( m ) ) + sendEffect = true; + + if ( StrangleSpell.RemoveCurse( m ) ) + sendEffect = true; + + if ( CorpseSkinSpell.RemoveCurse( m ) ) + sendEffect = true; + + // TODO: Should this remove blood oath? Pain spike? + + if ( sendEffect ) + { + m.FixedParticles( 0x375A, 1, 15, 5005, 5, 3, EffectLayer.Head ); + sacrifice = true; + } + } + } + + if ( sacrifice ) + { + Caster.PlaySound( 0x423 ); + Caster.Hits = 1; + Caster.Stam = 1; + Caster.Mana = 1; + } + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/PaladinSpell.cs b/Scripts/Spells/Chivalry/PaladinSpell.cs new file mode 100644 index 000000000..127bdb02f --- /dev/null +++ b/Scripts/Spells/Chivalry/PaladinSpell.cs @@ -0,0 +1,148 @@ +using System; +using Server; +using Server.Spells; +using Server.Network; + +namespace Server.Spells.Chivalry +{ + public abstract class PaladinSpell : Spell + { + public abstract double RequiredSkill{ get; } + public abstract int RequiredMana{ get; } + public abstract int RequiredTithing{ get; } + public abstract int MantraNumber{ get; } + + public override SkillName CastSkill{ get{ return SkillName.Chivalry; } } + + public override bool ClearHandsOnCast{ get{ return false; } } + + public override int CastDelayBase{ get{ return 1; } } + + public override int CastRecoveryBase{ get{ return 7; } } + + public PaladinSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( Caster.Skills[SkillName.Chivalry].Value < RequiredSkill ) + { + Caster.SendLocalizedMessage( 1060172, RequiredSkill.ToString( "F1" ) ); // You must have at least ~1_SKILL_REQUIREMENT~ Chivalry to use this ability, + return false; + } + else if ( Caster.TithingPoints < RequiredTithing ) + { + Caster.SendLocalizedMessage( 1060173, RequiredTithing.ToString() ); // You must have at least ~1_TITHE_REQUIREMENT~ Tithing Points to use this ability, + return false; + } + else if ( Caster.Mana < ScaleMana( RequiredMana ) ) + { + Caster.SendLocalizedMessage( 1060174, RequiredMana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + return false; + } + + return true; + } + + public override bool CheckFizzle() + { + int requiredTithing = this.RequiredTithing; + + if ( AosAttributes.GetValue( Caster, AosAttribute.LowerRegCost ) > Utility.Random( 100 ) ) + requiredTithing = 0; + + int mana = ScaleMana( RequiredMana ); + + if ( Caster.Skills[SkillName.Chivalry].Value < RequiredSkill ) + { + Caster.SendLocalizedMessage( 1060172, RequiredSkill.ToString( "F1" ) ); // You must have at least ~1_SKILL_REQUIREMENT~ Chivalry to use this ability, + return false; + } + else if ( Caster.TithingPoints < requiredTithing ) + { + Caster.SendLocalizedMessage( 1060173, RequiredTithing.ToString() ); // You must have at least ~1_TITHE_REQUIREMENT~ Tithing Points to use this ability, + return false; + } + else if ( Caster.Mana < mana ) + { + Caster.SendLocalizedMessage( 1060174, RequiredMana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + return false; + } + + Caster.TithingPoints -= requiredTithing; + + if ( !base.CheckFizzle() ) + return false; + + Caster.Mana -= mana; + + return true; + } + + public override void SayMantra() + { + Caster.PublicOverheadMessage( MessageType.Regular, 0x3B2, MantraNumber, "", false ); + } + + public override void DoFizzle() + { + Caster.PlaySound( 0x1D6 ); + Caster.NextSpellTime = DateTime.Now; + } + + public override void DoHurtFizzle() + { + Caster.PlaySound( 0x1D6 ); + } + + public override void OnDisturb( DisturbType type, bool message ) + { + base.OnDisturb( type, message ); + + if ( message ) + Caster.PlaySound( 0x1D6 ); + } + + public override void OnBeginCast() + { + base.OnBeginCast(); + + SendCastEffect(); + } + + public virtual void SendCastEffect() + { + Caster.FixedEffect( 0x37C4, 10, 42, 4, 3 ); + } + + public override void GetCastSkills( out double min, out double max ) + { + min = RequiredSkill; + max = RequiredSkill + 50.0; + } + + public override int GetMana() + { + return 0; + } + + public int ComputePowerValue( int div ) + { + return ComputePowerValue( Caster, div ); + } + + public static int ComputePowerValue( Mobile from, int div ) + { + if ( from == null ) + return 0; + + int v = (int) Math.Sqrt( from.Karma + 20000 + (from.Skills.Chivalry.Fixed * 10) ); + + return v / div; + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/RemoveCurse.cs b/Scripts/Spells/Chivalry/RemoveCurse.cs new file mode 100644 index 000000000..86b1d0310 --- /dev/null +++ b/Scripts/Spells/Chivalry/RemoveCurse.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Spells.Necromancy; +using Server.Spells.Fourth; + +namespace Server.Spells.Chivalry +{ + public class RemoveCurseSpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Remove Curse", "Extermo Vomica", + SpellCircle.Sixth, // 0 + 1.5 = 1.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 5.0; } } + public override int RequiredMana{ get{ return 20; } } + public override int RequiredTithing{ get{ return 10; } } + public override int MantraNumber{ get{ return 1060726; } } // Extermo Vomica + + public RemoveCurseSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Attempts to remove all Curse effects from Target. + * Curses include Mage spells such as Clumsy, Weaken, Feeblemind and Paralyze + * as well as all Necromancer curses. + * Chance of removing curse is affected by Caster's Karma. + */ + + int chance = 0; + + if ( Caster.Karma < -5000 ) + chance = 0; + else if ( Caster.Karma < 0 ) + chance = (int) Math.Sqrt( 20000 + Caster.Karma ) - 122; + else if ( Caster.Karma < 5625 ) + chance = (int) Math.Sqrt( Caster.Karma ) + 25; + else + chance = 100; + + if ( chance > Utility.Random( 100 ) ) + { + m.PlaySound( 0xF6 ); + m.PlaySound( 0x1F7 ); + m.FixedParticles( 0x3709, 1, 30, 9963, 13, 3, EffectLayer.Head ); + + IEntity from = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z - 10 ), Caster.Map ); + IEntity to = new Entity( Serial.Zero, new Point3D( m.X, m.Y, m.Z + 50 ), Caster.Map ); + Effects.SendMovingParticles( from, to, 0x2255, 1, 0, false, false, 13, 3, 9501, 1, 0, EffectLayer.Head, 0x100 ); + + StatMod mod; + + mod = m.GetStatMod( "[Magic] Str Offset" ); + if ( mod != null && mod.Offset < 0 ) + m.RemoveStatMod( "[Magic] Str Offset" ); + + mod = m.GetStatMod( "[Magic] Dex Offset" ); + if ( mod != null && mod.Offset < 0 ) + m.RemoveStatMod( "[Magic] Dex Offset" ); + + mod = m.GetStatMod( "[Magic] Int Offset" ); + if ( mod != null && mod.Offset < 0 ) + m.RemoveStatMod( "[Magic] Int Offset" ); + + m.Paralyzed = false; + + EvilOmenSpell.CheckEffect( m ); + StrangleSpell.RemoveCurse( m ); + CorpseSkinSpell.RemoveCurse( m ); + CurseSpell.RemoveEffect( m ); + + BuffInfo.RemoveBuff( m, BuffIcon.Clumsy ); + BuffInfo.RemoveBuff( m, BuffIcon.FeebleMind ); + BuffInfo.RemoveBuff( m, BuffIcon.Weaken ); + BuffInfo.RemoveBuff( m, BuffIcon.MassCurse ); + + // TODO: Should this remove blood oath? Pain spike? + } + else + { + m.PlaySound( 0x1DF ); + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private RemoveCurseSpell m_Owner; + + public InternalTarget( RemoveCurseSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Chivalry/SacredJourney.cs b/Scripts/Spells/Chivalry/SacredJourney.cs new file mode 100644 index 000000000..0784cf90f --- /dev/null +++ b/Scripts/Spells/Chivalry/SacredJourney.cs @@ -0,0 +1,189 @@ +using System; +using Server; +using Server.Items; +using Server.Multis; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Chivalry +{ + public class SacredJourneySpell : PaladinSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Sacred Journey", "Sanctum Viatas", + SpellCircle.Sixth, // 0 + 1.5 = 1.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 15.0; } } + public override int RequiredMana{ get{ return 10; } } + public override int RequiredTithing{ get{ return 15; } } + public override int MantraNumber{ get{ return 1060727; } } // Sanctum Viatas + public override bool BlocksMovement{ get{ return false; } } + + private RunebookEntry m_Entry; + private Runebook m_Book; + + public SacredJourneySpell( Mobile caster, Item scroll ) : this( caster, scroll, null, null ) + { + } + + public SacredJourneySpell( Mobile caster, Item scroll, RunebookEntry entry, Runebook book ) : base( caster, scroll, m_Info ) + { + m_Entry = entry; + m_Book = book; + } + + public override void OnCast() + { + if ( m_Entry == null ) + Caster.Target = new InternalTarget( this ); + else + Effect( m_Entry.Location, m_Entry.Map, true ); + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return false; + } + else if ( Caster.Criminal ) + { + Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + return false; + } + else if ( SpellHelper.CheckCombat( Caster ) ) + { + Caster.SendLocalizedMessage( 1061282 ); // You cannot use the Sacred Journey ability to flee from combat. + return false; + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + return false; + } + + return SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom ); + } + + public void Effect( Point3D loc, Map map, bool checkMulti ) + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( map == null || (!Core.AOS && Caster.Map != map) ) + { + Caster.SendLocalizedMessage( 1005569 ); // You can not recall to another facet. + } + else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom ) ) + { + } + else if ( !SpellHelper.CheckTravel( Caster, map, loc, TravelCheckType.RecallTo ) ) + { + } + else if ( map == Map.Felucca && Caster is PlayerMobile && ((PlayerMobile)Caster).Young ) + { + Caster.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young. + } + else if ( Caster.Kills >= 5 && map != Map.Felucca ) + { + Caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + } + else if ( Caster.Criminal ) + { + Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + } + else if ( SpellHelper.CheckCombat( Caster ) ) + { + Caster.SendLocalizedMessage( 1061282 ); // You cannot use the Sacred Journey ability to flee from combat. + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + } + else if ( !map.CanSpawnMobile( loc.X, loc.Y, loc.Z ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( (checkMulti && SpellHelper.CheckMulti( loc, map )) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( m_Book != null && m_Book.CurCharges <= 0 ) + { + Caster.SendLocalizedMessage( 502412 ); // There are no charges left on that item. + } + else if ( CheckSequence() ) + { + BaseCreature.TeleportPets( Caster, loc, map, true ); + + if ( m_Book != null ) + --m_Book.CurCharges; + + Effects.SendLocationParticles( EffectItem.Create( Caster.Location, Caster.Map, EffectItem.DefaultDuration ), 0, 0, 0, 5033 ); + + Caster.MoveToWorld( loc, map ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private SacredJourneySpell m_Owner; + + public InternalTarget( SacredJourneySpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is RecallRune ) + { + RecallRune rune = (RecallRune)o; + + if ( rune.Marked ) + m_Owner.Effect( rune.Target, rune.TargetMap, true ); + else + from.SendLocalizedMessage( 501805 ); // That rune is not yet marked. + } + else if ( o is Runebook ) + { + RunebookEntry e = ((Runebook)o).Default; + + if ( e != null ) + m_Owner.Effect( e.Location, e.Map, true ); + else + from.SendLocalizedMessage( 502354 ); // Target is not marked. + } + else if ( o is Key && ((Key)o).KeyValue != 0 && ((Key)o).Link is BaseBoat ) + { + BaseBoat boat = ((Key)o).Link as BaseBoat; + + if ( !boat.Deleted && boat.CheckKey( ((Key)o).KeyValue ) ) + m_Owner.Effect( boat.GetMarkedLocation(), boat.Map, false ); + else + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object. + } + else + { + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object. + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/AirElemental.cs b/Scripts/Spells/Eighth/AirElemental.cs new file mode 100644 index 000000000..0bb79eed3 --- /dev/null +++ b/Scripts/Spells/Eighth/AirElemental.cs @@ -0,0 +1,54 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class AirElementalSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Air Elemental", "Kal Vas Xen Hur", + SpellCircle.Eighth, + 269, + 9010, + false, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public AirElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + 2) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); + + if ( Core.AOS ) + SpellHelper.Summon( new SummonedAirElemental(), Caster, 0x217, duration, false, false ); + else + SpellHelper.Summon( new AirElemental(), Caster, 0x217, duration, false, false ); + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/EarthElemental.cs b/Scripts/Spells/Eighth/EarthElemental.cs new file mode 100644 index 000000000..4eb2ff30f --- /dev/null +++ b/Scripts/Spells/Eighth/EarthElemental.cs @@ -0,0 +1,54 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class EarthElementalSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Earth Elemental", "Kal Vas Xen Ylem", + SpellCircle.Eighth, + 269, + 9020, + false, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public EarthElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + 2) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); + + if ( Core.AOS ) + SpellHelper.Summon( new SummonedEarthElemental(), Caster, 0x217, duration, false, false ); + else + SpellHelper.Summon( new EarthElemental(), Caster, 0x217, duration, false, false ); + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/Earthquake.cs b/Scripts/Spells/Eighth/Earthquake.cs new file mode 100644 index 000000000..f4f64ee01 --- /dev/null +++ b/Scripts/Spells/Eighth/Earthquake.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class EarthquakeSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Earthquake", "In Vas Por", + SpellCircle.Eighth, + 233, + 9012, + false, + Reagent.Bloodmoss, + Reagent.Ginseng, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + public EarthquakeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool DelayedDamage{ get{ return !Core.AOS; } } + + public override void OnCast() + { + if ( SpellHelper.CheckTown( Caster, Caster ) && CheckSequence() ) + { + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + if ( map != null ) + { + foreach ( Mobile m in Caster.GetMobilesInRange( 1 + (int)(Caster.Skills[SkillName.Magery].Value / 15.0) ) ) + { + if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) && (!Core.AOS || Caster.InLOS( m )) ) + targets.Add( m ); + } + } + + Caster.PlaySound( 0x2F3 ); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + int damage; + + if ( Core.AOS ) + { + damage = m.Hits / 2; + + if ( !m.Player ) + damage = Math.Max( Math.Min( damage, 100 ), 15 ); + damage += Utility.RandomMinMax( 0, 15 ); + + } + else + { + damage = (m.Hits * 6) / 10; + + if ( !m.Player && damage < 10 ) + damage = 10; + else if ( damage > 75 ) + damage = 75; + } + + Caster.DoHarmful( m ); + SpellHelper.Damage( TimeSpan.Zero, m, Caster, damage, 100, 0, 0, 0, 0 ); + } + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/EnergyVortex.cs b/Scripts/Spells/Eighth/EnergyVortex.cs new file mode 100644 index 000000000..119c72275 --- /dev/null +++ b/Scripts/Spells/Eighth/EnergyVortex.cs @@ -0,0 +1,100 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class EnergyVortexSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Energy Vortex", "Vas Corp Por", + SpellCircle.Eighth, + 260, + 9032, + false, + Reagent.Bloodmoss, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.Nightshade + ); + + public EnergyVortexSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + (Core.SE ? 2 : 1)) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + Map map = Caster.Map; + + SpellHelper.GetSurfaceTop( ref p ); + + if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + TimeSpan duration; + + if ( Core.AOS ) + duration = TimeSpan.FromSeconds( 90.0 ); + else + duration = TimeSpan.FromSeconds( Utility.Random( 80, 40 ) ); + + BaseCreature.Summon( new EnergyVortex(), false, Caster, new Point3D( p ), 0x212, duration ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private EnergyVortexSpell m_Owner; + + public InternalTarget( EnergyVortexSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetOutOfLOS( Mobile from, object o ) + { + from.SendLocalizedMessage( 501943 ); // Target cannot be seen. Try again. + from.Target = new InternalTarget( m_Owner ); + from.Target.BeginTimeout( from, TimeoutTime - DateTime.Now ); + m_Owner = null; + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( m_Owner != null ) + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/FireElemental.cs b/Scripts/Spells/Eighth/FireElemental.cs new file mode 100644 index 000000000..7f4136f7e --- /dev/null +++ b/Scripts/Spells/Eighth/FireElemental.cs @@ -0,0 +1,55 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class FireElementalSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Fire Elemental", "Kal Vas Xen Flam", + SpellCircle.Eighth, + 269, + 9050, + false, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public FireElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + 4) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); + + if ( Core.AOS ) + SpellHelper.Summon( new SummonedFireElemental(), Caster, 0x217, duration, false, false ); + else + SpellHelper.Summon( new FireElemental(), Caster, 0x217, duration, false, false ); + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/Resurrection.cs b/Scripts/Spells/Eighth/Resurrection.cs new file mode 100644 index 000000000..1b938fdd5 --- /dev/null +++ b/Scripts/Spells/Eighth/Resurrection.cs @@ -0,0 +1,101 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Gumps; + +namespace Server.Spells.Eighth +{ + public class ResurrectionSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Resurrection", "An Corp", + SpellCircle.Eighth, + 245, + 9062, + Reagent.Bloodmoss, + Reagent.Garlic, + Reagent.Ginseng + ); + + public ResurrectionSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( m == Caster ) + { + Caster.SendLocalizedMessage( 501039 ); // Thou can not resurrect thyself. + } + else if ( !Caster.Alive ) + { + Caster.SendLocalizedMessage( 501040 ); // The resurrecter must be alive. + } + else if ( m.Alive ) + { + Caster.SendLocalizedMessage( 501041 ); // Target is not dead. + } + else if ( !Caster.InRange( m, 1 ) ) + { + Caster.SendLocalizedMessage( 501042 ); // Target is not close enough. + } + else if ( !m.Player ) + { + Caster.SendLocalizedMessage( 501043 ); // Target is not a being. + } + else if ( m.Map == null || !m.Map.CanFit( m.Location, 16, false, false ) ) + { + Caster.SendLocalizedMessage( 501042 ); // Target can not be resurrected at that location. + m.SendLocalizedMessage( 502391 ); // Thou can not be resurrected there! + } + else if ( m.Region != null && m.Region.IsPartOf( "Khaldun" ) ) + { + Caster.SendLocalizedMessage( 1010395 ); // The veil of death in this area is too strong and resists thy efforts to restore life. + } + else if ( CheckBSequence( m, true ) ) + { + SpellHelper.Turn( Caster, m ); + + m.PlaySound( 0x214 ); + m.FixedEffect( 0x376A, 10, 16 ); + + m.CloseGump( typeof( ResurrectGump ) ); + m.SendGump( new ResurrectGump( m, Caster ) ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private ResurrectionSpell m_Owner; + + public InternalTarget( ResurrectionSpell owner ) : base( 1, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/SummonDaemon.cs b/Scripts/Spells/Eighth/SummonDaemon.cs new file mode 100644 index 000000000..3646909e6 --- /dev/null +++ b/Scripts/Spells/Eighth/SummonDaemon.cs @@ -0,0 +1,55 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class SummonDaemonSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Summon Daemon", "Kal Vas Xen Corp", + SpellCircle.Eighth, + 269, + 9050, + false, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public SummonDaemonSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + (Core.SE ? 4 : 5)) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); + + if ( Core.AOS ) + SpellHelper.Summon( new SummonedDaemon(), Caster, 0x216, duration, false, false ); + else + SpellHelper.Summon( new Daemon(), Caster, 0x216, duration, false, false ); + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Eighth/WaterElemental.cs b/Scripts/Spells/Eighth/WaterElemental.cs new file mode 100644 index 000000000..786bb8656 --- /dev/null +++ b/Scripts/Spells/Eighth/WaterElemental.cs @@ -0,0 +1,54 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Eighth +{ + public class WaterElementalSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Water Elemental", "Kal Vas Xen An Flam", + SpellCircle.Eighth, + 269, + 9070, + false, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public WaterElementalSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + 3) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + TimeSpan duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); + + if ( Core.AOS ) + SpellHelper.Summon( new SummonedWaterElemental(), Caster, 0x217, duration, false, false ); + else + SpellHelper.Summon( new WaterElemental(), Caster, 0x217, duration, false, false ); + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fifth/BladeSpirits.cs b/Scripts/Spells/Fifth/BladeSpirits.cs new file mode 100644 index 000000000..c72c7b56b --- /dev/null +++ b/Scripts/Spells/Fifth/BladeSpirits.cs @@ -0,0 +1,107 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Fifth +{ + public class BladeSpiritsSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Blade Spirits", "In Jux Hur Ylem", + SpellCircle.Fifth, + 266, + 9040, + false, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.Nightshade + ); + + public BladeSpiritsSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override TimeSpan GetCastDelay() + { + if ( Core.AOS ) + return TimeSpan.FromTicks( base.GetCastDelay().Ticks * ((Core.SE) ? 3 : 5) ); + + return base.GetCastDelay() + TimeSpan.FromSeconds( 6.0 ); + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if( (Caster.Followers + (Core.SE ? 2 : 1)) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + Map map = Caster.Map; + + SpellHelper.GetSurfaceTop( ref p ); + + if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + TimeSpan duration; + + if ( Core.AOS ) + duration = TimeSpan.FromSeconds( 120 ); + else + duration = TimeSpan.FromSeconds( Utility.Random( 80, 40 ) ); + + BaseCreature.Summon( new BladeSpirits(), false, Caster, new Point3D( p ), 0x212, duration ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private BladeSpiritsSpell m_Owner; + + public InternalTarget( BladeSpiritsSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetOutOfLOS( Mobile from, object o ) + { + from.SendLocalizedMessage( 501943 ); // Target cannot be seen. Try again. + from.Target = new InternalTarget( m_Owner ); + from.Target.BeginTimeout( from, TimeoutTime - DateTime.Now ); + m_Owner = null; + } + + protected override void OnTargetFinish( Mobile from ) + { + if ( m_Owner != null ) + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fifth/DispelField.cs b/Scripts/Spells/Fifth/DispelField.cs new file mode 100644 index 000000000..ffc162ed4 --- /dev/null +++ b/Scripts/Spells/Fifth/DispelField.cs @@ -0,0 +1,87 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Items; +using Server.Misc; + +namespace Server.Spells.Fifth +{ + public class DispelFieldSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Dispel Field", "An Grav", + SpellCircle.Fifth, + 206, + 9002, + Reagent.BlackPearl, + Reagent.SpidersSilk, + Reagent.SulfurousAsh, + Reagent.Garlic + ); + + public DispelFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Item item ) + { + Type t = item.GetType(); + + if ( !Caster.CanSee( item ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( !t.IsDefined( typeof( DispellableFieldAttribute ), false ) ) + { + Caster.SendLocalizedMessage( 1005049 ); // That cannot be dispelled. + } + else if ( item is Moongate && !((Moongate)item).Dispellable ) + { + Caster.SendLocalizedMessage( 1005047 ); // That magic is too chaotic + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, item ); + + Effects.SendLocationParticles( EffectItem.Create( item.Location, item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 20, 5042 ); + Effects.PlaySound( item.GetWorldLocation(), item.Map, 0x201 ); + + item.Delete(); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private DispelFieldSpell m_Owner; + + public InternalTarget( DispelFieldSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Item ) + { + m_Owner.Target( (Item)o ); + } + else + { + m_Owner.Caster.SendLocalizedMessage( 1005049 ); // That cannot be dispelled. + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fifth/Incognito.cs b/Scripts/Spells/Fifth/Incognito.cs new file mode 100644 index 000000000..b00ee4be2 --- /dev/null +++ b/Scripts/Spells/Fifth/Incognito.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections; +using Server; +using Server.Mobiles; +using Server.Misc; +using Server.Items; +using Server.Gumps; +using Server.Spells; +using Server.Spells.Seventh; + +namespace Server.Spells.Fifth +{ + public class IncognitoSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Incognito", "Kal In Ex", + SpellCircle.Fifth, + 206, + 9002, + Reagent.Bloodmoss, + Reagent.Garlic, + Reagent.Nightshade + ); + + public IncognitoSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1010445 ); // You cannot incognito if you have a sigil + return false; + } + else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + return false; + } + else if ( Caster.BodyMod == 183 || Caster.BodyMod == 184 ) + { + Caster.SendLocalizedMessage( 1042402 ); // You cannot use incognito while wearing body paint + return false; + } + + return true; + } + + public override void OnCast() + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1010445 ); // You cannot incognito if you have a sigil + } + else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + else if ( Caster.BodyMod == 183 || Caster.BodyMod == 184 ) + { + Caster.SendLocalizedMessage( 1042402 ); // You cannot use incognito while wearing body paint + } + else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) || Caster.IsBodyMod ) + { + DoFizzle(); + } + else if ( CheckSequence() ) + { + if ( Caster.BeginAction( typeof( IncognitoSpell ) ) ) + { + DisguiseGump.StopTimer( Caster ); + + Caster.BodyMod = Utility.RandomList( 400, 401 ); + Caster.HueMod = Utility.RandomSkinHue(); + Caster.NameMod = Caster.Body.IsFemale ? NameList.RandomName( "female" ) : NameList.RandomName( "male" ); + + PlayerMobile pm = Caster as PlayerMobile; + + if ( pm != null ) + { + if ( pm.Body.IsFemale ) + pm.SetHairMods( Utility.RandomList( m_HairIDs ), 0 ); + else + pm.SetHairMods( Utility.RandomList( m_HairIDs ), Utility.RandomList( m_BeardIDs ) ); + + pm.HairHue = Utility.RandomHairHue(); + pm.FacialHairHue = Utility.RandomHairHue(); + } + + Caster.FixedParticles( 0x373A, 10, 15, 5036, EffectLayer.Head ); + Caster.PlaySound( 0x3BD ); + + BaseArmor.ValidateMobile( Caster ); + BaseClothing.ValidateMobile( Caster ); + + StopTimer( Caster ); + + + int timeVal = ((6 * Caster.Skills.Magery.Fixed) / 50) + 1; + + if( timeVal > 144 ) + timeVal = 144; + + TimeSpan length = TimeSpan.FromSeconds( timeVal ); + + + Timer t = new InternalTimer( Caster, length ); + + m_Timers[Caster] = t; + + t.Start(); + + BuffInfo.AddBuff( Caster, new BuffInfo( BuffIcon.Incognito, 1075819, length, Caster ) ); + + } + else + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + } + + FinishSequence(); + } + + private static Hashtable m_Timers = new Hashtable(); + + public static bool StopTimer( Mobile m ) + { + Timer t = (Timer)m_Timers[m]; + + if ( t != null ) + { + t.Stop(); + m_Timers.Remove( m ); + BuffInfo.RemoveBuff( m, BuffIcon.Incognito ); + } + + return ( t != null ); + } + + private static int[] m_HairIDs = new int[] + { + 0x2044, 0x2045, 0x2046, + 0x203C, 0x203B, 0x203D, + 0x2047, 0x2048, 0x2049, + 0x204A, 0x0000 + }; + + private static int[] m_BeardIDs = new int[] + { + 0x203E, 0x203F, 0x2040, + 0x2041, 0x204B, 0x204C, + 0x204D, 0x0000 + }; + + private class InternalTimer : Timer + { + private Mobile m_Owner; + + public InternalTimer( Mobile owner, TimeSpan length ) : base( length ) + { + m_Owner = owner; + + /* + int val = ((6 * owner.Skills.Magery.Fixed) / 50) + 1; + + if ( val > 144 ) + val = 144; + + Delay = TimeSpan.FromSeconds( val ); + * */ + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + if ( !m_Owner.CanBeginAction( typeof( IncognitoSpell ) ) ) + { + if ( m_Owner is PlayerMobile ) + ((PlayerMobile)m_Owner).SetHairMods( -1, -1 ); + + m_Owner.BodyMod = 0; + m_Owner.HueMod = -1; + m_Owner.NameMod = null; + m_Owner.EndAction( typeof( IncognitoSpell ) ); + + BaseArmor.ValidateMobile( m_Owner ); + BaseClothing.ValidateMobile( m_Owner ); + } + } + } + } +} diff --git a/Scripts/Spells/Fifth/MagicReflect.cs b/Scripts/Spells/Fifth/MagicReflect.cs new file mode 100644 index 000000000..de1369d7b --- /dev/null +++ b/Scripts/Spells/Fifth/MagicReflect.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Fifth +{ + public class MagicReflectSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Magic Reflection", "In Jux Sanct", + SpellCircle.Fifth, + 242, + 9012, + Reagent.Garlic, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public MagicReflectSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( Core.AOS ) + return true; + + if ( Caster.MagicDamageAbsorb > 0 ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + return false; + } + else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + return false; + } + + return true; + } + + private static Hashtable m_Table = new Hashtable(); + + public override void OnCast() + { + if ( Core.AOS ) + { + /* The magic reflection spell decreases the caster's physical resistance, while increasing the caster's elemental resistances. + * Physical decrease = 25 - (Inscription/20). + * Elemental resistance = +10 (-20 physical, +10 elemental at GM Inscription) + * The magic reflection spell has an indefinite duration, becoming active when cast, and deactivated when re-cast. + * Reactive Armor, Protection, and Magic Reflection will stay on—even after logging out, even after dying—until you “turn them off” by casting them again. + */ + + if ( CheckSequence() ) + { + Mobile targ = Caster; + + ResistanceMod[] mods = (ResistanceMod[])m_Table[targ]; + + if ( mods == null ) + { + targ.PlaySound( 0x1E9 ); + targ.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist ); + + int physiMod = -25 + (int)(targ.Skills[SkillName.Inscribe].Value / 20); + int otherMod = 10; + + mods = new ResistanceMod[5] + { + new ResistanceMod( ResistanceType.Physical, physiMod ), + new ResistanceMod( ResistanceType.Fire, otherMod ), + new ResistanceMod( ResistanceType.Cold, otherMod ), + new ResistanceMod( ResistanceType.Poison, otherMod ), + new ResistanceMod( ResistanceType.Energy, otherMod ) + }; + + m_Table[targ] = mods; + + for ( int i = 0; i < mods.Length; ++i ) + targ.AddResistanceMod( mods[i] ); + + string buffFormat = String.Format( "{0}\t+{1}\t+{1}\t+{1}\t+{1}", physiMod, otherMod ); + + BuffInfo.AddBuff( targ, new BuffInfo( BuffIcon.MagicReflection, 1075817, buffFormat, true ) ); + } + else + { + targ.PlaySound( 0x1ED ); + targ.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist ); + + m_Table.Remove( targ ); + + for ( int i = 0; i < mods.Length; ++i ) + targ.RemoveResistanceMod( mods[i] ); + + BuffInfo.RemoveBuff( targ, BuffIcon.MagicReflection ); + } + } + + FinishSequence(); + } + else + { + if ( Caster.MagicDamageAbsorb > 0 ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + } + else if ( CheckSequence() ) + { + if ( Caster.BeginAction( typeof( DefensiveSpell ) ) ) + { + int value = (int)(Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Inscribe].Value); + value = (int)(8 + (value/200)*7.0);//absorb from 8 to 15 "circles" + + Caster.MagicDamageAbsorb = value; + + Caster.FixedParticles( 0x375A, 10, 15, 5037, EffectLayer.Waist ); + Caster.PlaySound( 0x1E9 ); + } + else + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + } + } + + FinishSequence(); + } + } + } +} diff --git a/Scripts/Spells/Fifth/MindBlast.cs b/Scripts/Spells/Fifth/MindBlast.cs new file mode 100644 index 000000000..52068cc8b --- /dev/null +++ b/Scripts/Spells/Fifth/MindBlast.cs @@ -0,0 +1,154 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Fifth +{ + public class MindBlastSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Mind Blast", "Por Corp Wis", + SpellCircle.Fifth, + 218, + Core.AOS ? 9002 : 9032, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.Nightshade, + Reagent.SulfurousAsh + ); + + public MindBlastSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + if ( Core.AOS ) + m_Info.LeftHandEffect = m_Info.RightHandEffect = 9002; + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + private void AosDelay_Callback( object state ) + { + object[] states = (object[])state; + Mobile caster = (Mobile)states[0]; + Mobile target = (Mobile)states[1]; + Mobile defender = (Mobile)states[2]; + int damage = (int)states[3]; + + if ( caster.HarmfulCheck( defender ) ) + { + SpellHelper.Damage( this, target, Utility.RandomMinMax( damage, damage + 4 ), 0, 0, 100, 0, 0 ); + + target.FixedParticles( 0x374A, 10, 15, 5038, 1181, 2, EffectLayer.Head ); + target.PlaySound( 0x213 ); + } + } + + public override bool DelayedDamage{ get{ return !Core.AOS; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( Core.AOS ) + { + if ( Caster.CanBeHarmful( m ) && CheckSequence() ) + { + Mobile from = Caster, target = m; + + SpellHelper.Turn( from, target ); + + SpellHelper.CheckReflect( (int)this.Circle, ref from, ref target ); + + int damage = (int)((Caster.Skills[SkillName.Magery].Value + Caster.Int) / 5); + + if ( damage > 60 ) + damage = 60; + + Timer.DelayCall( TimeSpan.FromSeconds( 1.0 ), + new TimerStateCallback( AosDelay_Callback ), + new object[]{ Caster, target, m, damage } ); + } + } + else if ( CheckHSequence( m ) ) + { + Mobile from = Caster, target = m; + + SpellHelper.Turn( from, target ); + + SpellHelper.CheckReflect( (int)this.Circle, ref from, ref target ); + + // Algorithm: (highestStat - lowestStat) / 2 [- 50% if resisted] + + int highestStat = target.Str, lowestStat = target.Str; + + if ( target.Dex > highestStat ) + highestStat = target.Dex; + + if ( target.Dex < lowestStat ) + lowestStat = target.Dex; + + if ( target.Int > highestStat ) + highestStat = target.Int; + + if ( target.Int < lowestStat ) + lowestStat = target.Int; + + if ( highestStat > 150 ) + highestStat = 150; + + if ( lowestStat > 150 ) + lowestStat = 150; + + int damage = (highestStat - lowestStat) / 4;//less damage + + if ( damage > 45 ) + damage = 45; + + if ( CheckResisted( target ) ) + { + damage /= 2; + target.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + from.FixedParticles( 0x374A, 10, 15, 2038, EffectLayer.Head ); + + target.FixedParticles( 0x374A, 10, 15, 5038, EffectLayer.Head ); + target.PlaySound( 0x213 ); + + SpellHelper.Damage( this, target, damage, 0, 0, 100, 0, 0 ); + } + + FinishSequence(); + } + + public override double GetSlayerDamageScalar( Mobile target ) + { + return 1.0; //This spell isn't affected by slayer spellbooks + } + + private class InternalTarget : Target + { + private MindBlastSpell m_Owner; + + public InternalTarget( MindBlastSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fifth/Paralyze.cs b/Scripts/Spells/Fifth/Paralyze.cs new file mode 100644 index 000000000..882d496bd --- /dev/null +++ b/Scripts/Spells/Fifth/Paralyze.cs @@ -0,0 +1,101 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Fifth +{ + public class ParalyzeSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Paralyze", "An Ex Por", + SpellCircle.Fifth, + 218, + 9012, + Reagent.Garlic, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public ParalyzeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( Core.AOS && (m.Frozen || m.Paralyzed || (m.Spell != null && m.Spell.IsCasting)) ) + { + Caster.SendLocalizedMessage( 1061923 ); // The target is already frozen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + double duration; + + if ( Core.AOS ) + { + int secs = (GetDamageFixed( Caster ) / 100) - (GetResistFixed( m ) / 100); + + if( !Core.SE ) + secs += 2; + + if ( !m.Player ) + secs *= 3; + + if ( secs < 0 ) + secs = 0; + + duration = secs; + } + else + { + // Algorithm: ((20% of magery) + 7) seconds [- 50% if resisted] + + duration = 7.0 + (Caster.Skills[SkillName.Magery].Value * 0.2); + + if ( CheckResisted( m ) ) + duration *= 0.75; + } + + m.Paralyze( TimeSpan.FromSeconds( duration ) ); + + m.PlaySound( 0x204 ); + m.FixedEffect( 0x376A, 6, 1 ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private ParalyzeSpell m_Owner; + + public InternalTarget( ParalyzeSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fifth/PoisonField.cs b/Scripts/Spells/Fifth/PoisonField.cs new file mode 100644 index 000000000..e8100d8f8 --- /dev/null +++ b/Scripts/Spells/Fifth/PoisonField.cs @@ -0,0 +1,294 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +using Server.Misc; +using Server.Items; + +namespace Server.Spells.Fifth +{ + public class PoisonFieldSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Poison Field", "In Nox Grav", + SpellCircle.Fifth, + 230, + 9052, + false, + Reagent.BlackPearl, + Reagent.Nightshade, + Reagent.SpidersSilk + ); + + public PoisonFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + int dx = Caster.Location.X - p.X; + int dy = Caster.Location.Y - p.Y; + int rx = (dx - dy) * 44; + int ry = (dx + dy) * 44; + + bool eastToWest; + + if ( rx >= 0 && ry >= 0 ) + { + eastToWest = false; + } + else if ( rx >= 0 ) + { + eastToWest = true; + } + else if ( ry >= 0 ) + { + eastToWest = true; + } + else + { + eastToWest = false; + } + + Effects.PlaySound( p, Caster.Map, 0x20B ); + + int itemID = eastToWest ? 0x3915 : 0x3922; + + TimeSpan duration = TimeSpan.FromSeconds( 3 + (Caster.Skills.Magery.Fixed / 25) ); + + for ( int i = -2; i <= 2; ++i ) + { + Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z ); + + new InternalItem( itemID, loc, Caster, Caster.Map, duration, i ); + } + } + + FinishSequence(); + } + + [DispellableField] + private class InternalItem : Item + { + private Timer m_Timer; + private DateTime m_End; + private Mobile m_Caster; + + public override bool BlocksFit{ get{ return true; } } + + public InternalItem( int itemID, Point3D loc, Mobile caster, Map map, TimeSpan duration, int val ) : base( itemID ) + { + bool canFit = SpellHelper.AdjustField( ref loc, map, 12, false ); + + Visible = false; + Movable = false; + Light = LightType.Circle300; + + MoveToWorld( loc, map ); + + m_Caster = caster; + + m_End = DateTime.Now + duration; + + m_Timer = new InternalTimer( this, TimeSpan.FromSeconds( Math.Abs( val ) * 0.2 ), caster.InLOS( this ), canFit ); + m_Timer.Start(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Timer != null ) + m_Timer.Stop(); + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_Caster ); + writer.WriteDeltaTime( m_End ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Caster = reader.ReadMobile(); + + goto case 0; + } + case 0: + { + m_End = reader.ReadDeltaTime(); + + m_Timer = new InternalTimer( this, TimeSpan.Zero, true, true ); + m_Timer.Start(); + + break; + } + } + } + + public void ApplyPoisonTo( Mobile m ) + { + if ( m_Caster == null ) + return; + + Poison p; + + if ( Core.AOS ) + { + int total = (m_Caster.Skills.Magery.Fixed + m_Caster.Skills.Poisoning.Fixed) / 2; + + if ( total >= 1000 ) + p = Poison.Deadly; + else if ( total > 850 ) + p = Poison.Greater; + else if ( total > 650 ) + p = Poison.Regular; + else + p = Poison.Lesser; + } + else + { + p = Poison.Regular; + } + + m.ApplyPoison( m_Caster, p ); + } + + public override bool OnMoveOver( Mobile m ) + { + if ( Visible && m_Caster != null && (!Core.AOS || m != m_Caster) && SpellHelper.ValidIndirectTarget( m_Caster, m ) && m_Caster.CanBeHarmful( m, false ) ) + { + m_Caster.DoHarmful( m ); + + ApplyPoisonTo( m ); + m.PlaySound( 0x474 ); + } + + return true; + } + + private class InternalTimer : Timer + { + private InternalItem m_Item; + private bool m_InLOS, m_CanFit; + + private static Queue m_Queue = new Queue(); + + public InternalTimer( InternalItem item, TimeSpan delay, bool inLOS, bool canFit ) : base( delay, TimeSpan.FromSeconds( 1.5 ) ) + { + m_Item = item; + m_InLOS = inLOS; + m_CanFit = canFit; + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Item.Deleted ) + return; + + if ( !m_Item.Visible ) + { + if ( m_InLOS && m_CanFit ) + m_Item.Visible = true; + else + m_Item.Delete(); + + if ( !m_Item.Deleted ) + { + m_Item.ProcessDelta(); + Effects.SendLocationParticles( EffectItem.Create( m_Item.Location, m_Item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5040 ); + } + } + else if ( DateTime.Now > m_Item.m_End ) + { + m_Item.Delete(); + Stop(); + } + else + { + Map map = m_Item.Map; + Mobile caster = m_Item.m_Caster; + + if ( map != null && caster != null ) + { + bool eastToWest = ( m_Item.ItemID == 0x3915 ); + IPooledEnumerable eable = map.GetMobilesInBounds( new Rectangle2D( m_Item.X - (eastToWest ? 0 : 1), m_Item.Y - (eastToWest ? 1 : 0), (eastToWest ? 1 : 2), (eastToWest ? 2 : 1) ) ); + + foreach ( Mobile m in eable ) + { + if ( (m.Z + 16) > m_Item.Z && (m_Item.Z + 12) > m.Z && (!Core.AOS || m != caster) && SpellHelper.ValidIndirectTarget( caster, m ) && caster.CanBeHarmful( m, false ) ) + m_Queue.Enqueue( m ); + } + + eable.Free(); + + while ( m_Queue.Count > 0 ) + { + Mobile m = (Mobile)m_Queue.Dequeue(); + + caster.DoHarmful( m ); + + m_Item.ApplyPoisonTo( m ); + m.PlaySound( 0x474 ); + } + } + } + } + } + } + + private class InternalTarget : Target + { + private PoisonFieldSpell m_Owner; + + public InternalTarget( PoisonFieldSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fifth/SummonCreature.cs b/Scripts/Spells/Fifth/SummonCreature.cs new file mode 100644 index 000000000..d4acf8ea0 --- /dev/null +++ b/Scripts/Spells/Fifth/SummonCreature.cs @@ -0,0 +1,90 @@ +using System; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; + +namespace Server.Spells.Fifth +{ + public class SummonCreatureSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Summon Creature", "Kal Xen", + SpellCircle.Fifth, + 266, + 9040, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public SummonCreatureSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + // TODO: Get real list + private static Type[] m_Types = new Type[] + { + typeof( PolarBear ), + typeof( GrizzlyBear ), + typeof( BlackBear ), + typeof( BrownBear ), + typeof( Horse ), + typeof( Walrus ), + typeof( GreatHart ), + typeof( Hind ), + typeof( Dog ), + typeof( Boar ), + typeof( Chicken ), + typeof( Rabbit ) + }; + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + 2) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + try + { + BaseCreature creature = (BaseCreature)Activator.CreateInstance( m_Types[Utility.Random( m_Types.Length )] ); + + creature.ControlSlots = 2; + + TimeSpan duration; + + if ( Core.AOS ) + duration = TimeSpan.FromSeconds( (2 * Caster.Skills.Magery.Fixed) / 5 ); + else + duration = TimeSpan.FromSeconds( 4.0 * Caster.Skills[SkillName.Magery].Value ); + + SpellHelper.Summon( creature, Caster, 0x215, duration, false, false ); + } + catch + { + } + } + + FinishSequence(); + } + + public override TimeSpan GetCastDelay() + { + if ( Core.AOS ) + return TimeSpan.FromTicks( base.GetCastDelay().Ticks * 5 ); + + return base.GetCastDelay() + TimeSpan.FromSeconds( 6.0 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/Clumsy.cs b/Scripts/Spells/First/Clumsy.cs new file mode 100644 index 000000000..66d31ef99 --- /dev/null +++ b/Scripts/Spells/First/Clumsy.cs @@ -0,0 +1,81 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.First +{ + public class ClumsySpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Clumsy", "Uus Jux", + SpellCircle.First, + 212, + 9031, + Reagent.Bloodmoss, + Reagent.Nightshade + ); + + public ClumsySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + SpellHelper.AddStatCurse( Caster, m, StatType.Dex ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + m.FixedParticles( 0x3779, 10, 15, 5002, EffectLayer.Head ); + m.PlaySound( 0x1DF ); + + int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, true )*100); + TimeSpan length = SpellHelper.GetDuration( Caster, m ); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Clumsy, 1075831, length, m, percentage.ToString() ) ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private ClumsySpell m_Owner; + + public InternalTarget( ClumsySpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/CreateFood.cs b/Scripts/Spells/First/CreateFood.cs new file mode 100644 index 000000000..e184ee251 --- /dev/null +++ b/Scripts/Spells/First/CreateFood.cs @@ -0,0 +1,89 @@ +using System; +using Server.Items; + +namespace Server.Spells.First +{ + public class CreateFoodSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Create Food", "In Mani Ylem", + SpellCircle.First, + 224, + 9011, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.MandrakeRoot + ); + + public CreateFoodSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + private static FoodInfo[] m_Food = new FoodInfo[] + { + new FoodInfo( typeof( Grapes ), "a grape bunch" ), + new FoodInfo( typeof( Ham ), "a ham" ), + new FoodInfo( typeof( CheeseWedge ), "a wedge of cheese" ), + new FoodInfo( typeof( Muffins ), "muffins" ), + new FoodInfo( typeof( FishSteak ), "a fish steak" ), + new FoodInfo( typeof( Ribs ), "cut of ribs" ), + new FoodInfo( typeof( CookedBird ), "a cooked bird" ), + new FoodInfo( typeof( Sausage ), "sausage" ), + new FoodInfo( typeof( Apple ), "an apple" ), + new FoodInfo( typeof( Peach ), "a peach" ) + }; + + public override void OnCast() + { + if ( CheckSequence() ) + { + FoodInfo foodInfo = m_Food[Utility.Random( m_Food.Length )]; + Item food = foodInfo.Create(); + + if ( food != null ) + { + Caster.AddToBackpack( food ); + + // You magically create food in your backpack: + Caster.SendLocalizedMessage( 1042695, true, " " + foodInfo.Name ); + + Caster.FixedParticles( 0, 10, 5, 2003, EffectLayer.RightHand ); + Caster.PlaySound( 0x1E2 ); + } + } + + FinishSequence(); + } + } + + public class FoodInfo + { + private Type m_Type; + private string m_Name; + + public Type Type{ get{ return m_Type; } set{ m_Type = value; } } + public string Name{ get{ return m_Name; } set{ m_Name = value; } } + + public FoodInfo( Type type, string name ) + { + m_Type = type; + m_Name = name; + } + + public Item Create() + { + Item item; + + try + { + item = (Item)Activator.CreateInstance( m_Type ); + } + catch + { + item = null; + } + + return item; + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/Feeblemind.cs b/Scripts/Spells/First/Feeblemind.cs new file mode 100644 index 000000000..c8b7fac5c --- /dev/null +++ b/Scripts/Spells/First/Feeblemind.cs @@ -0,0 +1,81 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.First +{ + public class FeeblemindSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Feeblemind", "Rel Wis", + SpellCircle.First, + 212, + 9031, + Reagent.Ginseng, + Reagent.Nightshade + ); + + public FeeblemindSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + SpellHelper.AddStatCurse( Caster, m, StatType.Int ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + m.FixedParticles( 0x3779, 10, 15, 5004, EffectLayer.Head ); + m.PlaySound( 0x1E4 ); + + int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, true )*100); + TimeSpan length = SpellHelper.GetDuration( Caster, m ); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.FeebleMind, 1075833, length, m, percentage.ToString() ) ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private FeeblemindSpell m_Owner; + + public InternalTarget( FeeblemindSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/Heal.cs b/Scripts/Spells/First/Heal.cs new file mode 100644 index 000000000..a630bcb1b --- /dev/null +++ b/Scripts/Spells/First/Heal.cs @@ -0,0 +1,104 @@ +using System; +using Server; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; + +namespace Server.Spells.First +{ + public class HealSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Heal", "In Mani", + SpellCircle.First, + 224, + 9061, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.SpidersSilk + ); + + public HealSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( m.IsDeadBondedPet ) + { + Caster.SendLocalizedMessage( 1060177 ); // You cannot heal a creature that is already dead! + } + else if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead ) + { + Caster.SendLocalizedMessage( 1061654 ); // You cannot heal that which is not alive. + } + else if ( m is Golem ) + { + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500951 ); // You cannot heal that. + } + else if ( m.Poisoned || Server.Items.MortalStrike.IsWounded( m ) ) + { + Caster.LocalOverheadMessage( MessageType.Regular, 0x22, (Caster == m) ? 1005000 : 1010398 ); + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + int toHeal; + + if ( Core.AOS ) + { + toHeal = Caster.Skills.Magery.Fixed / 120; + toHeal += Utility.RandomMinMax( 1, 4 ); + + if( Core.SE && Caster != m ) + toHeal = (int)(toHeal * 1.5); + } + else + { + toHeal = (int)(Caster.Skills[SkillName.Magery].Value * 0.1); + toHeal += Utility.Random( 1, 5 ); + } + + m.Heal( toHeal ); + + m.FixedParticles( 0x376A, 9, 32, 5005, EffectLayer.Waist ); + m.PlaySound( 0x1F2 ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private HealSpell m_Owner; + + public InternalTarget( HealSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/MagicArrow.cs b/Scripts/Spells/First/MagicArrow.cs new file mode 100644 index 000000000..0e811db1d --- /dev/null +++ b/Scripts/Spells/First/MagicArrow.cs @@ -0,0 +1,96 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.First +{ + public class MagicArrowSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Magic Arrow", "In Por Ylem", + SpellCircle.First, + 212, + 9041, + Reagent.SulfurousAsh + ); + + public MagicArrowSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool DelayedDamageStacking { get { return !Core.AOS; } } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return true; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + Mobile source = Caster; + + SpellHelper.Turn( source, m ); + + SpellHelper.CheckReflect( (int)this.Circle, ref source, ref m ); + + double damage; + + if ( Core.AOS ) + { + damage = GetNewAosDamage( 10, 1, 4, m ); + } + else + { + damage = Utility.Random( 4, 4 ); + + if ( CheckResisted( m ) ) + { + damage *= 0.75; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + damage *= GetDamageScalar( m ); + } + + source.MovingParticles( m, 0x36E4, 5, 0, false, true, 3006, 4006, 0 ); + source.PlaySound( 0x1E5 ); + + SpellHelper.Damage( this, m, damage, 0, 100, 0, 0, 0 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MagicArrowSpell m_Owner; + + public InternalTarget( MagicArrowSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/NightSight.cs b/Scripts/Spells/First/NightSight.cs new file mode 100644 index 000000000..9def601be --- /dev/null +++ b/Scripts/Spells/First/NightSight.cs @@ -0,0 +1,75 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server; + +namespace Server.Spells.First +{ + public class NightSightSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Night Sight", "In Lor", + SpellCircle.First, + 236, + 9031, + Reagent.SulfurousAsh, + Reagent.SpidersSilk + ); + + public NightSightSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new NightSightTarget( this ); + } + + private class NightSightTarget : Target + { + private Spell m_Spell; + + public NightSightTarget( Spell spell ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Spell = spell; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( targeted is Mobile && m_Spell.CheckBSequence( (Mobile) targeted ) ) + { + Mobile targ = (Mobile)targeted; + + SpellHelper.Turn( m_Spell.Caster, targ ); + + if ( targ.BeginAction( typeof( LightCycle ) ) ) + { + new LightCycle.NightSightTimer( targ ).Start(); + int level = (int)( LightCycle.DungeonLevel * ( (Core.AOS ? targ.Skills[SkillName.Magery].Value : from.Skills[SkillName.Magery].Value )/ 100 ) ); + + if ( level < 0 ) + level = 0; + + targ.LightLevel = level; + + targ.FixedParticles( 0x376A, 9, 32, 5007, EffectLayer.Waist ); + targ.PlaySound( 0x1E3 ); + + BuffInfo.AddBuff( targ, new BuffInfo( BuffIcon.NightSight, 1075643 ) ); //Night Sight/You ignore lighting effects + } + else + { + from.SendMessage( "{0} already have nightsight.", from == targ ? "You" : "They" ); + } + } + + m_Spell.FinishSequence(); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Spell.FinishSequence(); + } + } + } +} diff --git a/Scripts/Spells/First/ReactiveArmor.cs b/Scripts/Spells/First/ReactiveArmor.cs new file mode 100644 index 000000000..0412cf5e9 --- /dev/null +++ b/Scripts/Spells/First/ReactiveArmor.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.First +{ + public class ReactiveArmorSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Reactive Armor", "Flam Sanct", + SpellCircle.First, + 236, + 9011, + Reagent.Garlic, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public ReactiveArmorSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( Core.AOS ) + return true; + + if ( Caster.MeleeDamageAbsorb > 0 ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + return false; + } + else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + return false; + } + + return true; + } + + private static Hashtable m_Table = new Hashtable(); + + public override void OnCast() + { + if ( Core.AOS ) + { + /* The reactive armor spell increases the caster's physical resistance, while lowering the caster's elemental resistances. + * 15 + (Inscription/20) Physcial bonus + * -5 Elemental + * The reactive armor spell has an indefinite duration, becoming active when cast, and deactivated when re-cast. + * Reactive Armor, Protection, and Magic Reflection will stay on—even after logging out, even after dying—until you “turn them off” by casting them again. + * (+20 physical -5 elemental at 100 Inscription) + */ + + if ( CheckSequence() ) + { + Mobile targ = Caster; + + ResistanceMod[] mods = (ResistanceMod[])m_Table[targ]; + + if ( mods == null ) + { + targ.PlaySound( 0x1E9 ); + targ.FixedParticles( 0x376A, 9, 32, 5008, EffectLayer.Waist ); + + mods = new ResistanceMod[5] + { + new ResistanceMod( ResistanceType.Physical, 15 + (int)(targ.Skills[SkillName.Inscribe].Value / 20) ), + new ResistanceMod( ResistanceType.Fire, -5 ), + new ResistanceMod( ResistanceType.Cold, -5 ), + new ResistanceMod( ResistanceType.Poison, -5 ), + new ResistanceMod( ResistanceType.Energy, -5 ) + }; + + m_Table[targ] = mods; + + for ( int i = 0; i < mods.Length; ++i ) + targ.AddResistanceMod( mods[i] ); + } + else + { + targ.PlaySound( 0x1ED ); + targ.FixedParticles( 0x376A, 9, 32, 5008, EffectLayer.Waist ); + + m_Table.Remove( targ ); + + for ( int i = 0; i < mods.Length; ++i ) + targ.RemoveResistanceMod( mods[i] ); + } + } + + FinishSequence(); + } + else + { + if ( Caster.MeleeDamageAbsorb > 0 ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + } + else if ( CheckSequence() ) + { + if ( Caster.BeginAction( typeof( DefensiveSpell ) ) ) + { + int value = (int)(Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Meditation].Value + Caster.Skills[SkillName.Inscribe].Value); + value /= 3; + + if ( value < 0 ) + value = 1; + else if ( value > 75 ) + value = 75; + + Caster.MeleeDamageAbsorb = value; + + Caster.FixedParticles( 0x376A, 9, 32, 5008, EffectLayer.Waist ); + Caster.PlaySound( 0x1F2 ); + } + else + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + } + } + + FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/First/Weaken.cs b/Scripts/Spells/First/Weaken.cs new file mode 100644 index 000000000..0481bf225 --- /dev/null +++ b/Scripts/Spells/First/Weaken.cs @@ -0,0 +1,81 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.First +{ + public class WeakenSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Weaken", "Des Mani", + SpellCircle.First, + 212, + 9031, + Reagent.Garlic, + Reagent.Nightshade + ); + + public WeakenSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + SpellHelper.AddStatCurse( Caster, m, StatType.Str ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + m.FixedParticles( 0x3779, 10, 15, 5009, EffectLayer.Waist ); + m.PlaySound( 0x1E6 ); + + int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, true )*100); + TimeSpan length = SpellHelper.GetDuration( Caster, m ); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Weaken, 1075837, length, m, percentage.ToString() ) ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private WeakenSpell m_Owner; + + public InternalTarget( WeakenSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/ArchCure.cs b/Scripts/Spells/Fourth/ArchCure.cs new file mode 100644 index 000000000..4271848a3 --- /dev/null +++ b/Scripts/Spells/Fourth/ArchCure.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Fourth +{ + public class ArchCureSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Arch Cure", "Vas An Nox", + SpellCircle.Fourth, + 215, + 9061, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.MandrakeRoot + ); + + public ArchCureSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + // Archcure is now 1/4th of a second faster + public override int CastDelayBase{ get{ return base.CastDelayBase - 1; } } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 2 ); + + foreach ( Mobile m in eable ) + { + // Archcure doesn't cure aggressors or victims + if ( Caster.CanBeBeneficial( m, false ) && (!Core.AOS || !IsAggressor( m ) && !IsAggressed( m )) ) + targets.Add( m ); + } + + eable.Free(); + } + + Effects.PlaySound( p, Caster.Map, 0x299 ); + + if ( targets.Count > 0 ) + { + int cured = 0; + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + Caster.DoBeneficial( m ); + + Poison poison = m.Poison; + + if ( poison != null ) + { + int chanceToCure = 10000 + (int)(Caster.Skills[SkillName.Magery].Value * 75) - ((poison.Level + 1) * 1750); + chanceToCure /= 100; + chanceToCure -= 1; + + if ( chanceToCure > Utility.Random( 100 ) && m.CurePoison( Caster ) ) + ++cured; + } + + m.FixedParticles( 0x373A, 10, 15, 5012, EffectLayer.Waist ); + m.PlaySound( 0x1E0 ); + } + + if ( cured > 0 ) + Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons! + } + } + + FinishSequence(); + } + + private bool IsAggressor( Mobile m ) + { + foreach ( AggressorInfo info in Caster.Aggressors ) + { + if ( m == info.Attacker && !info.Expired ) + return true; + } + + return false; + } + + private bool IsAggressed( Mobile m ) + { + foreach ( AggressorInfo info in Caster.Aggressed ) + { + if ( m == info.Defender && !info.Expired ) + return true; + } + + return false; + } + + private class InternalTarget : Target + { + private ArchCureSpell m_Owner; + + public InternalTarget( ArchCureSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/ArchProtection.cs b/Scripts/Spells/Fourth/ArchProtection.cs new file mode 100644 index 000000000..5f6a798b4 --- /dev/null +++ b/Scripts/Spells/Fourth/ArchProtection.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Engines.PartySystem; + +namespace Server.Spells.Fourth +{ + public class ArchProtectionSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Arch Protection", "Vas Uus Sanct", + SpellCircle.Fourth, + Core.AOS ? 239 : 215, + 9011, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + public ArchProtectionSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), Core.AOS ? 2 : 3 ); + + foreach ( Mobile m in eable ) + { + if ( Caster.CanBeBeneficial( m, false ) ) + targets.Add( m ); + } + + eable.Free(); + } + + if ( Core.AOS ) + { + Party party = Party.Get( Caster ); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + if ( m == Caster || (party != null && party.Contains( m )) ) + { + Caster.DoBeneficial( m ); + Spells.Second.ProtectionSpell.Toggle( Caster, m ); + } + } + } + else + { + Effects.PlaySound( p, Caster.Map, 0x299 ); + + int val = (int)(Caster.Skills[SkillName.Magery].Value/10.0 + 1); + + if ( targets.Count > 0 ) + { + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + if ( m.BeginAction( typeof( ArchProtectionSpell ) ) ) + { + Caster.DoBeneficial( m ); + m.VirtualArmorMod += val; + new InternalTimer( m, Caster, val ).Start(); + + m.FixedParticles( 0x375A, 9, 20, 5027, EffectLayer.Waist ); + m.PlaySound( 0x1F7 ); + } + } + } + } + } + + FinishSequence(); + } + + private class InternalTimer : Timer + { + private Mobile m_Owner; + private int m_Val; + + public InternalTimer( Mobile target, Mobile caster, int val ) : base( TimeSpan.FromSeconds( 0 ) ) + { + double time = caster.Skills[SkillName.Magery].Value * 1.2; + if ( time > 144 ) + time = 144; + Delay = TimeSpan.FromSeconds( time ); + Priority = TimerPriority.OneSecond; + + m_Owner = target; + m_Val = val; + } + + protected override void OnTick() + { + m_Owner.EndAction( typeof( ArchProtectionSpell ) ); + m_Owner.VirtualArmorMod -= m_Val; + if ( m_Owner.VirtualArmorMod < 0 ) + m_Owner.VirtualArmorMod = 0; + } + } + + private class InternalTarget : Target + { + private ArchProtectionSpell m_Owner; + + public InternalTarget( ArchProtectionSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} diff --git a/Scripts/Spells/Fourth/Curse.cs b/Scripts/Spells/Fourth/Curse.cs new file mode 100644 index 000000000..7366a8c54 --- /dev/null +++ b/Scripts/Spells/Fourth/Curse.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Fourth +{ + public class CurseSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Curse", "Des Sanct", + SpellCircle.Fourth, + 227, + 9031, + Reagent.Nightshade, + Reagent.Garlic, + Reagent.SulfurousAsh + ); + + public CurseSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + private static Hashtable m_UnderEffect = new Hashtable(); + + public static void RemoveEffect( object state ) + { + Mobile m = (Mobile)state; + + m_UnderEffect.Remove( m ); + + m.UpdateResistances(); + } + + public static bool UnderEffect( Mobile m ) + { + return m_UnderEffect.Contains( m ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + SpellHelper.AddStatCurse( Caster, m, StatType.Str ); SpellHelper.DisableSkillCheck = true; + SpellHelper.AddStatCurse( Caster, m, StatType.Dex ); + SpellHelper.AddStatCurse( Caster, m, StatType.Int ); SpellHelper.DisableSkillCheck = false; + + Timer t = (Timer)m_UnderEffect[m]; + + if ( Caster.Player && m.Player /*&& Caster != m */ && t == null ) //On OSI you CAN curse yourself and get this effect. + { + TimeSpan duration = SpellHelper.GetDuration( Caster, m ); + m_UnderEffect[m] = t = Timer.DelayCall( duration, new TimerStateCallback( RemoveEffect ), m ); + m.UpdateResistances(); + } + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + m.FixedParticles( 0x374A, 10, 15, 5028, EffectLayer.Waist ); + m.PlaySound( 0x1EA ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private CurseSpell m_Owner; + + public InternalTarget( CurseSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/FireField.cs b/Scripts/Spells/Fourth/FireField.cs new file mode 100644 index 000000000..89ad623ea --- /dev/null +++ b/Scripts/Spells/Fourth/FireField.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; +using Server.Misc; +using Server.Items; + +namespace Server.Spells.Fourth +{ + public class FireFieldSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Fire Field", "In Flam Grav", + SpellCircle.Fourth, + 215, + 9041, + false, + Reagent.BlackPearl, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public FireFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + int dx = Caster.Location.X - p.X; + int dy = Caster.Location.Y - p.Y; + int rx = (dx - dy) * 44; + int ry = (dx + dy) * 44; + + bool eastToWest; + + if ( rx >= 0 && ry >= 0 ) + { + eastToWest = false; + } + else if ( rx >= 0 ) + { + eastToWest = true; + } + else if ( ry >= 0 ) + { + eastToWest = true; + } + else + { + eastToWest = false; + } + + Effects.PlaySound( p, Caster.Map, 0x20C ); + + int itemID = eastToWest ? 0x398C : 0x3996; + + TimeSpan duration; + + if ( Core.AOS ) + duration = TimeSpan.FromSeconds( (15 + (Caster.Skills.Magery.Fixed / 5)) / 4 ); + else + duration = TimeSpan.FromSeconds( 4.0 + (Caster.Skills[SkillName.Magery].Value * 0.5) ); + + for ( int i = -2; i <= 2; ++i ) + { + Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z ); + + new InternalItem( itemID, loc, Caster, Caster.Map, duration, i ); + } + } + + FinishSequence(); + } + + [DispellableField] + private class InternalItem : Item + { + private Timer m_Timer; + private DateTime m_End; + private Mobile m_Caster; + + public override bool BlocksFit{ get{ return true; } } + + public InternalItem( int itemID, Point3D loc, Mobile caster, Map map, TimeSpan duration, int val ) : base( itemID ) + { + bool canFit = SpellHelper.AdjustField( ref loc, map, 12, false ); + + Visible = false; + Movable = false; + Light = LightType.Circle300; + + MoveToWorld( loc, map ); + + m_Caster = caster; + + m_End = DateTime.Now + duration; + + m_Timer = new InternalTimer( this, TimeSpan.FromSeconds( Math.Abs( val ) * 0.2 ), caster.InLOS( this ), canFit ); + m_Timer.Start(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Timer != null ) + m_Timer.Stop(); + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.Write( m_Caster ); + writer.WriteDeltaTime( m_End ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_Caster = reader.ReadMobile(); + + goto case 0; + } + case 0: + { + m_End = reader.ReadDeltaTime(); + + m_Timer = new InternalTimer( this, TimeSpan.Zero, true, true ); + m_Timer.Start(); + + break; + } + } + } + + public override bool OnMoveOver( Mobile m ) + { + if ( Visible && m_Caster != null && (!Core.AOS || m != m_Caster) && SpellHelper.ValidIndirectTarget( m_Caster, m ) && m_Caster.CanBeHarmful( m, false ) ) + { + m_Caster.DoHarmful( m ); + + int damage = 2; + + if ( !Core.AOS && m.CheckSkill( SkillName.MagicResist, 0.0, 30.0 ) ) + { + damage = 1; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + AOS.Damage( m, m_Caster, damage, 0, 100, 0, 0, 0 ); + m.PlaySound( 0x208 ); + } + + return true; + } + + private class InternalTimer : Timer + { + private InternalItem m_Item; + private bool m_InLOS, m_CanFit; + + private static Queue m_Queue = new Queue(); + + public InternalTimer( InternalItem item, TimeSpan delay, bool inLOS, bool canFit ) : base( delay, TimeSpan.FromSeconds( 1.0 ) ) + { + m_Item = item; + m_InLOS = inLOS; + m_CanFit = canFit; + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Item.Deleted ) + return; + + if ( !m_Item.Visible ) + { + if ( m_InLOS && m_CanFit ) + m_Item.Visible = true; + else + m_Item.Delete(); + + if ( !m_Item.Deleted ) + { + m_Item.ProcessDelta(); + Effects.SendLocationParticles( EffectItem.Create( m_Item.Location, m_Item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5029 ); + } + } + else if ( DateTime.Now > m_Item.m_End ) + { + m_Item.Delete(); + Stop(); + } + else + { + Map map = m_Item.Map; + Mobile caster = m_Item.m_Caster; + + if ( map != null && caster != null ) + { + foreach ( Mobile m in m_Item.GetMobilesInRange( 0 ) ) + { + if ( (m.Z + 16) > m_Item.Z && (m_Item.Z + 12) > m.Z && (!Core.AOS || m != caster) && SpellHelper.ValidIndirectTarget( caster, m ) && caster.CanBeHarmful( m, false ) ) + m_Queue.Enqueue( m ); + } + + while ( m_Queue.Count > 0 ) + { + Mobile m = (Mobile)m_Queue.Dequeue(); + + caster.DoHarmful( m ); + + int damage = 2; + + if ( !Core.AOS && m.CheckSkill( SkillName.MagicResist, 0.0, 30.0 ) ) + { + damage = 1; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + AOS.Damage( m, caster, damage, 0, 100, 0, 0, 0 ); + m.PlaySound( 0x208 ); + } + } + } + } + } + } + + private class InternalTarget : Target + { + private FireFieldSpell m_Owner; + + public InternalTarget( FireFieldSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/GreaterHeal.cs b/Scripts/Spells/Fourth/GreaterHeal.cs new file mode 100644 index 000000000..4b5dc939a --- /dev/null +++ b/Scripts/Spells/Fourth/GreaterHeal.cs @@ -0,0 +1,94 @@ +using System; +using Server; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; + +namespace Server.Spells.Fourth +{ + public class GreaterHealSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Greater Heal", "In Vas Mani", + SpellCircle.Fourth, + 204, + 9061, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public GreaterHealSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( m is BaseCreature && ((BaseCreature)m).IsAnimatedDead ) + { + Caster.SendLocalizedMessage( 1061654 ); // You cannot heal that which is not alive. + } + else if ( m.IsDeadBondedPet ) + { + Caster.SendLocalizedMessage( 1060177 ); // You cannot heal a creature that is already dead! + } + else if ( m is Golem ) + { + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 500951 ); // You cannot heal that. + } + else if ( m.Poisoned || Server.Items.MortalStrike.IsWounded( m ) ) + { + Caster.LocalOverheadMessage( MessageType.Regular, 0x22, (Caster == m) ? 1005000 : 1010398 ); + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + // Algorithm: (40% of magery) + (1-10) + + int toHeal = (int)(Caster.Skills[SkillName.Magery].Value * 0.4); + toHeal += Utility.Random( 1, 10 ); + + m.Heal( toHeal ); + + m.FixedParticles( 0x376A, 9, 32, 5030, EffectLayer.Waist ); + m.PlaySound( 0x202 ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private GreaterHealSpell m_Owner; + + public InternalTarget( GreaterHealSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/Lightning.cs b/Scripts/Spells/Fourth/Lightning.cs new file mode 100644 index 000000000..c4899551b --- /dev/null +++ b/Scripts/Spells/Fourth/Lightning.cs @@ -0,0 +1,90 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Fourth +{ + public class LightningSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Lightning", "Por Ort Grav", + SpellCircle.Fourth, + 239, + 9021, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + public LightningSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return false; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + double damage; + + if ( Core.AOS ) + { + damage = GetNewAosDamage( 23, 1, 4, m ); + } + else + { + damage = Utility.Random( 12, 9 ); + + if ( CheckResisted( m ) ) + { + damage *= 0.75; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + damage *= GetDamageScalar( m ); + } + + m.BoltEffect( 0 ); + + SpellHelper.Damage( this, m, damage, 0, 0, 0, 0, 100 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private LightningSpell m_Owner; + + public InternalTarget( LightningSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/ManaDrain.cs b/Scripts/Spells/Fourth/ManaDrain.cs new file mode 100644 index 000000000..0c538ab02 --- /dev/null +++ b/Scripts/Spells/Fourth/ManaDrain.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Fourth +{ + public class ManaDrainSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Mana Drain", "Ort Rel", + SpellCircle.Fourth, + 215, + 9031, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public ManaDrainSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + private Hashtable m_Table = new Hashtable(); + + private void AosDelay_Callback( object state ) + { + object[] states = (object[])state; + + Mobile m = (Mobile)states[0]; + int mana = (int)states[1]; + + if ( m.Alive && !m.IsDeadBondedPet ) + { + m.Mana += mana; + + m.FixedEffect( 0x3779, 10, 25 ); + m.PlaySound( 0x28E ); + } + + m_Table.Remove( m ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + if ( Core.AOS ) + { + int toDrain = 40 + (int)(GetDamageSkill( Caster ) - GetResistSkill( m )); + + if ( toDrain < 0 ) + toDrain = 0; + else if ( toDrain > m.Mana ) + toDrain = m.Mana; + + if ( m_Table.Contains( m ) ) + toDrain = 0; + + m.FixedParticles( 0x3789, 10, 25, 5032, EffectLayer.Head ); + m.PlaySound( 0x1F8 ); + + if ( toDrain > 0 ) + { + m.Mana -= toDrain; + + m_Table[m] = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( AosDelay_Callback ), new object[]{ m, toDrain } ); + } + } + else + { + if ( CheckResisted( m ) ) + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + else if ( m.Mana >= 100 ) + m.Mana -= Utility.Random( 1, 100 ); + else + m.Mana -= Utility.Random( 1, m.Mana ); + + m.FixedParticles( 0x374A, 10, 15, 5032, EffectLayer.Head ); + m.PlaySound( 0x1F8 ); + } + } + + FinishSequence(); + } + + public override double GetResistPercent( Mobile target ) + { + return 99.0; + } + + private class InternalTarget : Target + { + private ManaDrainSpell m_Owner; + + public InternalTarget( ManaDrainSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Fourth/Recall.cs b/Scripts/Spells/Fourth/Recall.cs new file mode 100644 index 000000000..3ea79bd34 --- /dev/null +++ b/Scripts/Spells/Fourth/Recall.cs @@ -0,0 +1,196 @@ +using System; +using Server.Items; +using Server.Multis; +using Server.Mobiles; +using Server.Network; +using Server.Targeting; +using Server.Regions; +using Server.Spells.Necromancy; + +namespace Server.Spells.Fourth +{ + public class RecallSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Recall", "Kal Ort Por", + SpellCircle.Fourth, + 239, + 9031, + Reagent.BlackPearl, + Reagent.Bloodmoss, + Reagent.MandrakeRoot + ); + + private RunebookEntry m_Entry; + private Runebook m_Book; + + public RecallSpell( Mobile caster, Item scroll ) : this( caster, scroll, null, null ) + { + } + + public RecallSpell( Mobile caster, Item scroll, RunebookEntry entry, Runebook book ) : base( caster, scroll, m_Info ) + { + m_Entry = entry; + m_Book = book; + } + + public override void GetCastSkills( out double min, out double max ) + { + if ( TransformationSpell.UnderTransformation( Caster, typeof( WraithFormSpell ) ) ) + min = max = 0; + else if( Core.SE && m_Book != null ) //recall using Runebook charge + min = max = 0; + else + base.GetCastSkills( out min, out max ); + } + + public override void OnCast() + { + if ( m_Entry == null ) + Caster.Target = new InternalTarget( this ); + else + Effect( m_Entry.Location, m_Entry.Map, true ); + } + + public override bool CheckCast() + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return false; + } + else if ( Caster.Criminal ) + { + Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + return false; + } + else if ( SpellHelper.CheckCombat( Caster ) ) + { + Caster.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + return false; + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + return false; + } + + return SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom ); + } + + public void Effect( Point3D loc, Map map, bool checkMulti ) + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( map == null || (!Core.AOS && Caster.Map != map) ) + { + Caster.SendLocalizedMessage( 1005569 ); // You can not recall to another facet. + } + else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.RecallFrom ) ) + { + } + else if ( !SpellHelper.CheckTravel( Caster, map, loc, TravelCheckType.RecallTo ) ) + { + } + else if ( map == Map.Felucca && Caster is PlayerMobile && ((PlayerMobile)Caster).Young ) + { + Caster.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young. + } + else if ( Caster.Kills >= 5 && map != Map.Felucca ) + { + Caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + } + else if ( Caster.Criminal ) + { + Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + } + else if ( SpellHelper.CheckCombat( Caster ) ) + { + Caster.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + } + else if ( !map.CanSpawnMobile( loc.X, loc.Y, loc.Z ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( (checkMulti && SpellHelper.CheckMulti( loc, map )) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( m_Book != null && m_Book.CurCharges <= 0 ) + { + Caster.SendLocalizedMessage( 502412 ); // There are no charges left on that item. + } + else if ( CheckSequence() ) + { + BaseCreature.TeleportPets( Caster, loc, map, true ); + + if ( m_Book != null ) + --m_Book.CurCharges; + + Caster.PlaySound( 0x1FC ); + Caster.MoveToWorld( loc, map ); + Caster.PlaySound( 0x1FC ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private RecallSpell m_Owner; + + public InternalTarget( RecallSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + + owner.Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501029 ); // Select Marked item. + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is RecallRune ) + { + RecallRune rune = (RecallRune)o; + + if ( rune.Marked ) + m_Owner.Effect( rune.Target, rune.TargetMap, true ); + else + from.SendLocalizedMessage( 501805 ); // That rune is not yet marked. + } + else if ( o is Runebook ) + { + RunebookEntry e = ((Runebook)o).Default; + + if ( e != null ) + m_Owner.Effect( e.Location, e.Map, true ); + else + from.SendLocalizedMessage( 502354 ); // Target is not marked. + } + else if ( o is Key && ((Key)o).KeyValue != 0 && ((Key)o).Link is BaseBoat ) + { + BaseBoat boat = ((Key)o).Link as BaseBoat; + + if ( !boat.Deleted && boat.CheckKey( ((Key)o).KeyValue ) ) + m_Owner.Effect( boat.GetMarkedLocation(), boat.Map, false ); + else + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object. + } + else + { + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 502357, from.Name, "" ) ); // I can not recall from that object. + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Initializer.cs b/Scripts/Spells/Initializer.cs new file mode 100644 index 000000000..333f83adb --- /dev/null +++ b/Scripts/Spells/Initializer.cs @@ -0,0 +1,153 @@ +using System; +using Server; + +namespace Server.Spells +{ + public class Initializer + { + public static void Initialize() + { + // First circle + Register( 00, typeof( First.ClumsySpell ) ); + Register( 01, typeof( First.CreateFoodSpell ) ); + Register( 02, typeof( First.FeeblemindSpell ) ); + Register( 03, typeof( First.HealSpell ) ); + Register( 04, typeof( First.MagicArrowSpell ) ); + Register( 05, typeof( First.NightSightSpell ) ); + Register( 06, typeof( First.ReactiveArmorSpell ) ); + Register( 07, typeof( First.WeakenSpell ) ); + + // Second circle + Register( 08, typeof( Second.AgilitySpell ) ); + Register( 09, typeof( Second.CunningSpell ) ); + Register( 10, typeof( Second.CureSpell ) ); + Register( 11, typeof( Second.HarmSpell ) ); + Register( 12, typeof( Second.MagicTrapSpell ) ); + Register( 13, typeof( Second.RemoveTrapSpell ) ); + Register( 14, typeof( Second.ProtectionSpell ) ); + Register( 15, typeof( Second.StrengthSpell ) ); + + // Third circle + Register( 16, typeof( Third.BlessSpell ) ); + Register( 17, typeof( Third.FireballSpell ) ); + Register( 18, typeof( Third.MagicLockSpell ) ); + Register( 19, typeof( Third.PoisonSpell ) ); + Register( 20, typeof( Third.TelekinesisSpell ) ); + Register( 21, typeof( Third.TeleportSpell ) ); + Register( 22, typeof( Third.UnlockSpell ) ); + Register( 23, typeof( Third.WallOfStoneSpell ) ); + + // Fourth circle + Register( 24, typeof( Fourth.ArchCureSpell ) ); + Register( 25, typeof( Fourth.ArchProtectionSpell ) ); + Register( 26, typeof( Fourth.CurseSpell ) ); + Register( 27, typeof( Fourth.FireFieldSpell ) ); + Register( 28, typeof( Fourth.GreaterHealSpell ) ); + Register( 29, typeof( Fourth.LightningSpell ) ); + Register( 30, typeof( Fourth.ManaDrainSpell ) ); + Register( 31, typeof( Fourth.RecallSpell ) ); + + // Fifth circle + Register( 32, typeof( Fifth.BladeSpiritsSpell ) ); + Register( 33, typeof( Fifth.DispelFieldSpell ) ); + Register( 34, typeof( Fifth.IncognitoSpell ) ); + Register( 35, typeof( Fifth.MagicReflectSpell ) ); + Register( 36, typeof( Fifth.MindBlastSpell ) ); + Register( 37, typeof( Fifth.ParalyzeSpell ) ); + Register( 38, typeof( Fifth.PoisonFieldSpell ) ); + Register( 39, typeof( Fifth.SummonCreatureSpell ) ); + + // Sixth circle + Register( 40, typeof( Sixth.DispelSpell ) ); + Register( 41, typeof( Sixth.EnergyBoltSpell ) ); + Register( 42, typeof( Sixth.ExplosionSpell ) ); + Register( 43, typeof( Sixth.InvisibilitySpell ) ); + Register( 44, typeof( Sixth.MarkSpell ) ); + Register( 45, typeof( Sixth.MassCurseSpell ) ); + Register( 46, typeof( Sixth.ParalyzeFieldSpell ) ); + Register( 47, typeof( Sixth.RevealSpell ) ); + + // Seventh circle + Register( 48, typeof( Seventh.ChainLightningSpell ) ); + Register( 49, typeof( Seventh.EnergyFieldSpell ) ); + Register( 50, typeof( Seventh.FlameStrikeSpell ) ); + Register( 51, typeof( Seventh.GateTravelSpell ) ); + Register( 52, typeof( Seventh.ManaVampireSpell ) ); + Register( 53, typeof( Seventh.MassDispelSpell ) ); + Register( 54, typeof( Seventh.MeteorSwarmSpell ) ); + Register( 55, typeof( Seventh.PolymorphSpell ) ); + + // Eighth circle + Register( 56, typeof( Eighth.EarthquakeSpell ) ); + Register( 57, typeof( Eighth.EnergyVortexSpell ) ); + Register( 58, typeof( Eighth.ResurrectionSpell ) ); + Register( 59, typeof( Eighth.AirElementalSpell ) ); + Register( 60, typeof( Eighth.SummonDaemonSpell ) ); + Register( 61, typeof( Eighth.EarthElementalSpell ) ); + Register( 62, typeof( Eighth.FireElementalSpell ) ); + Register( 63, typeof( Eighth.WaterElementalSpell ) ); + + if ( Core.AOS ) + { + // Necromancy spells + Register( 100, typeof( Necromancy.AnimateDeadSpell ) ); + Register( 101, typeof( Necromancy.BloodOathSpell ) ); + Register( 102, typeof( Necromancy.CorpseSkinSpell ) ); + Register( 103, typeof( Necromancy.CurseWeaponSpell ) ); + Register( 104, typeof( Necromancy.EvilOmenSpell ) ); + Register( 105, typeof( Necromancy.HorrificBeastSpell ) ); + Register( 106, typeof( Necromancy.LichFormSpell ) ); + Register( 107, typeof( Necromancy.MindRotSpell ) ); + Register( 108, typeof( Necromancy.PainSpikeSpell ) ); + Register( 109, typeof( Necromancy.PoisonStrikeSpell ) ); + Register( 110, typeof( Necromancy.StrangleSpell ) ); + Register( 111, typeof( Necromancy.SummonFamiliarSpell ) ); + Register( 112, typeof( Necromancy.VampiricEmbraceSpell ) ); + Register( 113, typeof( Necromancy.VengefulSpiritSpell ) ); + Register( 114, typeof( Necromancy.WitherSpell ) ); + Register( 115, typeof( Necromancy.WraithFormSpell ) ); + + if( Core.SE ) + Register( 116, typeof( Necromancy.ExorcismSpell ) ); + + // Paladin abilities + Register( 200, typeof( Chivalry.CleanseByFireSpell ) ); + Register( 201, typeof( Chivalry.CloseWoundsSpell ) ); + Register( 202, typeof( Chivalry.ConsecrateWeaponSpell ) ); + Register( 203, typeof( Chivalry.DispelEvilSpell ) ); + Register( 204, typeof( Chivalry.DivineFurySpell ) ); + Register( 205, typeof( Chivalry.EnemyOfOneSpell ) ); + Register( 206, typeof( Chivalry.HolyLightSpell ) ); + Register( 207, typeof( Chivalry.NobleSacrificeSpell ) ); + Register( 208, typeof( Chivalry.RemoveCurseSpell ) ); + Register( 209, typeof( Chivalry.SacredJourneySpell ) ); + + if ( Core.SE ) + { + // Samurai abilities + Register( 400, typeof( Bushido.HonorableExecution ) ); + Register( 401, typeof( Bushido.Confidence ) ); + Register( 402, typeof( Bushido.Evasion ) ); + Register( 403, typeof( Bushido.CounterAttack ) ); + Register( 404, typeof( Bushido.LightningStrike ) ); + Register( 405, typeof( Bushido.MomentumStrike ) ); + + // Ninja abilities + Register( 500, typeof( Ninjitsu.FocusAttack ) ); + Register( 501, typeof( Ninjitsu.DeathStrike ) ); + Register( 502, typeof( Ninjitsu.AnimalForm ) ); + Register( 503, typeof( Ninjitsu.KiAttack ) ); + Register( 504, typeof( Ninjitsu.SurpriseAttack ) ); + Register( 505, typeof( Ninjitsu.Backstab ) ); + Register( 506, typeof( Ninjitsu.Shadowjump ) ); + Register( 507, typeof( Ninjitsu.MirrorImage ) ); + } + } + } + + public static void Register( int spellID, Type type ) + { + SpellRegistry.Register( spellID, type ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/AnimateDeadSpell.cs b/Scripts/Spells/Necromancy/AnimateDeadSpell.cs new file mode 100644 index 000000000..71f28640a --- /dev/null +++ b/Scripts/Spells/Necromancy/AnimateDeadSpell.cs @@ -0,0 +1,419 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Mobiles; +using Server.Targeting; +using Server.Items; +using Server.Engines.Quests; +using Server.Engines.Quests.Necro; + +namespace Server.Spells.Necromancy +{ + public class AnimateDeadSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Animate Dead", "Uus Corp", + SpellCircle.Fourth, // 0.5 + 1.0 = 1.5s base cast delay + 203, + 9031, + Reagent.GraveDust, + Reagent.DaemonBlood + ); + + public override double RequiredSkill{ get{ return 40.0; } } + public override int RequiredMana{ get{ return 23; } } + + public AnimateDeadSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + Caster.SendLocalizedMessage( 1061083 ); // Animate what corpse? + } + + private class CreatureGroup + { + public Type[] m_Types; + public SummonEntry[] m_Entries; + + public CreatureGroup( Type[] types, SummonEntry[] entries ) + { + m_Types = types; + m_Entries = entries; + } + } + + private class SummonEntry + { + public Type[] m_ToSummon; + public int m_Requirement; + + public SummonEntry( int requirement, params Type[] toSummon ) + { + m_ToSummon = toSummon; + m_Requirement = requirement; + } + } + + private static CreatureGroup FindGroup( Type type ) + { + for ( int i = 0; i < m_Groups.Length; ++i ) + { + CreatureGroup group = m_Groups[i]; + Type[] types = group.m_Types; + + bool contains = ( types.Length == 0 ); + + for ( int j = 0; !contains && j < types.Length; ++j ) + contains = ( types[j] == type ); + + if ( contains ) + return group; + } + + return null; + } + + private static CreatureGroup[] m_Groups = new CreatureGroup[] + { + // Undead group--empty + new CreatureGroup( SlayerGroup.GetEntryByName( SlayerName.Silver ).Types, new SummonEntry[0] ), + // Insects + new CreatureGroup( new Type[] + { + typeof( DreadSpider ), typeof( FrostSpider ), typeof( GiantSpider ), typeof( GiantBlackWidow ), + typeof( BlackSolenInfiltratorQueen ), typeof( BlackSolenInfiltratorWarrior ), + typeof( BlackSolenQueen ), typeof( BlackSolenWarrior ), typeof( BlackSolenWorker ), + typeof( RedSolenInfiltratorQueen ), typeof( RedSolenInfiltratorWarrior ), + typeof( RedSolenQueen ), typeof( RedSolenWarrior ), typeof( RedSolenWorker ), + typeof( TerathanAvenger ), typeof( TerathanDrone ), typeof( TerathanMatriarch ), + typeof( TerathanWarrior ) + // TODO: Giant beetle? Ant lion? Ophidians? + }, + new SummonEntry[] + { + new SummonEntry( 0, typeof( MoundOfMaggots ) ) + } ), + // Mounts + new CreatureGroup( new Type[] + { + typeof( Horse ), typeof( Nightmare ), typeof( FireSteed ), + typeof( Kirin ), typeof( Unicorn ) + }, new SummonEntry[] + { + new SummonEntry( 10000, typeof( HellSteed ) ), + new SummonEntry( 0, typeof( SkeletalMount ) ) + } ), + // Elementals + new CreatureGroup( new Type[] + { + typeof( BloodElemental ), typeof( EarthElemental ), typeof( SummonedEarthElemental ), + typeof( AgapiteElemental ), typeof( BronzeElemental ), typeof( CopperElemental ), + typeof( DullCopperElemental ), typeof( GoldenElemental ), typeof( ShadowIronElemental ), + typeof( ValoriteElemental ), typeof( VeriteElemental ), typeof( PoisonElemental ), + typeof( FireElemental ), typeof( SummonedFireElemental ), typeof( SnowElemental ), + typeof( AirElemental ), typeof( SummonedAirElemental ), typeof( WaterElemental ), + typeof( SummonedAirElemental ) + }, new SummonEntry[] + { + new SummonEntry( 5000, typeof( WailingBanshee ) ), + new SummonEntry( 0, typeof( Wraith ) ) + } ), + // Dragons + new CreatureGroup( new Type[] + { + typeof( AncientWyrm ), typeof( Dragon ), typeof( SerpentineDragon ), + typeof( ShadowWyrm ), typeof( SkeletalDragon ), typeof( WhiteWyrm ), + typeof( Drake ), typeof( Wyvern ), typeof( LesserHiryu ), typeof( Hiryu ) + }, new SummonEntry[] + { + new SummonEntry( 18000, typeof( SkeletalDragon ) ), + new SummonEntry( 10000, typeof( FleshGolem ) ), + new SummonEntry( 5000, typeof( Lich ) ), + new SummonEntry( 3000, typeof( SkeletalKnight ), typeof( BoneKnight ) ), + new SummonEntry( 2000, typeof( Mummy ) ), + new SummonEntry( 1000, typeof( SkeletalMage ), typeof( BoneMagi ) ), + new SummonEntry( 0, typeof( PatchworkSkeleton ) ) + } ), + // Default group + new CreatureGroup( new Type[0], new SummonEntry[] + { + new SummonEntry( 18000, typeof( LichLord ) ), + new SummonEntry( 10000, typeof( FleshGolem ) ), + new SummonEntry( 5000, typeof( Lich ) ), + new SummonEntry( 3000, typeof( SkeletalKnight ), typeof( BoneKnight ) ), + new SummonEntry( 2000, typeof( Mummy ) ), + new SummonEntry( 1000, typeof( SkeletalMage ), typeof( BoneMagi ) ), + new SummonEntry( 0, typeof( PatchworkSkeleton ) ) + } ), + }; + + public void Target( object obj ) + { + MaabusCoffinComponent comp = obj as MaabusCoffinComponent; + + if ( comp != null ) + { + MaabusCoffin addon = comp.Addon as MaabusCoffin; + + if ( addon != null ) + { + PlayerMobile pm = Caster as PlayerMobile; + + if ( pm != null ) + { + QuestSystem qs = pm.Quest; + + if ( qs is DarkTidesQuest ) + { + QuestObjective objective = qs.FindObjective( typeof( AnimateMaabusCorpseObjective ) ); + + if ( objective != null && !objective.Completed ) + { + addon.Awake( Caster ); + objective.Complete(); + } + } + } + + return; + } + } + + Corpse c = obj as Corpse; + + if ( c == null ) + { + Caster.SendLocalizedMessage( 1061084 ); // You cannot animate that. + } + else + { + Type type = null; + + if ( c.Owner != null ) + type = c.Owner.GetType(); + + if ( c.ItemID != 0x2006 || c.Channeled || type == typeof( PlayerMobile ) || type == null || (c.Owner != null && c.Owner.Fame < 100) || ((c.Owner != null) && (c.Owner is BaseCreature) && (((BaseCreature)c.Owner).Summoned || ((BaseCreature)c.Owner).IsBonded)) ) + { + Caster.SendLocalizedMessage( 1061085 ); // There's not enough life force there to animate. + } + else + { + CreatureGroup group = FindGroup( type ); + + if ( group != null ) + { + if ( group.m_Entries.Length == 0 || type == typeof( DemonKnight ) ) + { + Caster.SendLocalizedMessage( 1061086 ); // You cannot animate undead remains. + } + else if ( CheckSequence() ) + { + Point3D p = c.GetWorldLocation(); + Map map = c.Map; + + if ( map != null ) + { + Effects.PlaySound( p, map, 0x1FB ); + Effects.SendLocationParticles( EffectItem.Create( p, map, EffectItem.DefaultDuration ), 0x3789, 1, 40, 0x3F, 3, 9907, 0 ); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( SummonDelay_Callback ), new object[]{ Caster, c, p, map, group } ); + } + } + } + } + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static void Unregister( Mobile master, Mobile summoned ) + { + if ( master == null ) + return; + + ArrayList list = (ArrayList)m_Table[master]; + + if ( list == null ) + return; + + list.Remove( summoned ); + + if ( list.Count == 0 ) + m_Table.Remove( master ); + } + + public static void Register( Mobile master, Mobile summoned ) + { + if ( master == null ) + return; + + ArrayList list = (ArrayList)m_Table[master]; + + if ( list == null ) + m_Table[master] = list = new ArrayList(); + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i >= list.Count ) + continue; + + Mobile mob = (Mobile)list[i]; + + if ( mob.Deleted ) + list.RemoveAt( i-- ); + } + + list.Add( summoned ); + + if ( list.Count > 3 ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ((Mobile)list[0]).Kill ) ); + + Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( Summoned_Damage ), summoned ); + } + + private static void Summoned_Damage( object state ) + { + Mobile mob = (Mobile)state; + + if ( mob.Hits > 0 ) + --mob.Hits; + else + mob.Kill(); + } + + private static void SummonDelay_Callback( object state ) + { + object[] states = (object[])state; + + Mobile caster = (Mobile)states[0]; + Corpse corpse = (Corpse)states[1]; + Point3D loc = (Point3D)states[2]; + Map map = (Map)states[3]; + CreatureGroup group = (CreatureGroup)states[4]; + + if ( corpse.ItemID != 0x2006 ) + return; + + Mobile owner = corpse.Owner; + + if ( owner == null ) + return; + + double necromancy = caster.Skills[SkillName.Necromancy].Value; + double spiritSpeak = caster.Skills[SkillName.SpiritSpeak].Value; + + int casterAbility = 0; + + casterAbility += (int)(necromancy * 30); + casterAbility += (int)(spiritSpeak * 70); + casterAbility /= 10; + casterAbility *= 18; + + if ( casterAbility > owner.Fame ) + casterAbility = owner.Fame; + + if ( casterAbility < 0 ) + casterAbility = 0; + + Type toSummon = null; + SummonEntry[] entries = group.m_Entries; + + for ( int i = 0; toSummon == null && i < entries.Length; ++i ) + { + SummonEntry entry = entries[i]; + + if ( casterAbility < entry.m_Requirement ) + continue; + + Type[] animates = entry.m_ToSummon; + + if ( animates.Length >= 0 ) + toSummon = animates[Utility.Random( animates.Length )]; + } + + if ( toSummon == null ) + return; + + Mobile summoned = null; + + try{ summoned = Activator.CreateInstance( toSummon ) as Mobile; } + catch{} + + if ( summoned == null ) + return; + + if ( summoned is BaseCreature ) + { + BaseCreature bc = (BaseCreature)summoned; + + // to be sure + bc.Tamable = false; + + if ( bc is BaseMount ) + bc.ControlSlots = 1; + else + bc.ControlSlots = 0; + + Effects.PlaySound( loc, map, bc.GetAngerSound() ); + + BaseCreature.Summon( (BaseCreature)summoned, false, caster, loc, 0x28, TimeSpan.FromDays( 1.0 ) ); + } + + if ( summoned is SkeletalDragon ) + Scale( (SkeletalDragon)summoned, 50 ); // lose 50% hp and strength + + summoned.Fame = 0; + summoned.Karma = -1500; + + summoned.MoveToWorld( loc, map ); + + corpse.ProcessDelta(); + corpse.SendRemovePacket(); + corpse.ItemID = Utility.Random( 0xECA, 9 ); // bone graphic + corpse.Hue = 0; + corpse.ProcessDelta(); + + Register( caster, summoned ); + } + + private static void Scale( BaseCreature bc, int scalar ) + { + int toScale; + + toScale = bc.RawStr; + bc.RawStr = AOS.Scale( toScale, scalar ); + + toScale = bc.HitsMaxSeed; + + if ( toScale > 0 ) + bc.HitsMaxSeed = AOS.Scale( toScale, scalar ); + + bc.Hits = bc.Hits; // refresh hits + } + + private class InternalTarget : Target + { + private AnimateDeadSpell m_Owner; + + public InternalTarget( AnimateDeadSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + m_Owner.Target( o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/BloodOathSpell.cs b/Scripts/Spells/Necromancy/BloodOathSpell.cs new file mode 100644 index 000000000..0b47f37bb --- /dev/null +++ b/Scripts/Spells/Necromancy/BloodOathSpell.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class BloodOathSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Blood Oath", "In Jux Mani Xen", + SpellCircle.Fourth, // 0.5 + 1.0 = 1.5s base cast delay + 203, + 9031, + Reagent.DaemonBlood + ); + + public override double RequiredSkill{ get{ return 20.0; } } + public override int RequiredMana{ get{ return 13; } } + + public BloodOathSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( Caster == m || !(m is PlayerMobile || m is BaseCreature) ) // only PlayerMobile and BaseCreature implement blood oath checking + { + Caster.SendLocalizedMessage( 1060508 ); // You can't curse that. + } + else if ( m_OathTable.Contains( Caster ) ) + { + Caster.SendLocalizedMessage( 1061607 ); // You are already bonded in a Blood Oath. + } + else if ( m_OathTable.Contains( m ) ) + { + if ( m.Player ) + Caster.SendLocalizedMessage( 1061608 ); // That player is already bonded in a Blood Oath. + else + Caster.SendLocalizedMessage( 1061609 ); // That creature is already bonded in a Blood Oath. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Temporarily creates a dark pact between the caster and the target. + * Any damage dealt by the target to the caster is increased, but the target receives the same amount of damage. + * The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 80 ) + 8 seconds. + * + * NOTE: The above algorithm must be fixed point, it should be: + * ((ss-rm)/8)+8 + */ + + m_OathTable[Caster] = Caster; + m_OathTable[m] = Caster; + + Caster.PlaySound( 0x175 ); + + Caster.FixedParticles( 0x375A, 1, 17, 9919, 33, 7, EffectLayer.Waist ); + Caster.FixedParticles( 0x3728, 1, 13, 9502, 33, 7, (EffectLayer)255 ); + + m.FixedParticles( 0x375A, 1, 17, 9919, 33, 7, EffectLayer.Waist ); + m.FixedParticles( 0x3728, 1, 13, 9502, 33, 7, (EffectLayer)255 ); + + TimeSpan duration = TimeSpan.FromSeconds( ((GetDamageSkill( Caster ) - GetResistSkill( m )) / 8) + 8 ); + + new ExpireTimer( Caster, m, duration ).Start(); + } + + FinishSequence(); + } + + private static Hashtable m_OathTable = new Hashtable(); + + public static Mobile GetBloodOath( Mobile m ) + { + if ( m == null ) + return null; + + Mobile oath = (Mobile)m_OathTable[m]; + + if ( oath == m ) + oath = null; + + return oath; + } + + private class ExpireTimer : Timer + { + private Mobile m_Caster; + private Mobile m_Target; + private DateTime m_End; + + public ExpireTimer( Mobile caster, Mobile target, TimeSpan delay ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Caster = caster; + m_Target = target; + m_End = DateTime.Now + delay; + + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + if ( m_Caster.Deleted || m_Target.Deleted || !m_Caster.Alive || !m_Target.Alive || DateTime.Now >= m_End ) + { + m_Caster.SendLocalizedMessage( 1061620 ); // Your Blood Oath has been broken. + m_Target.SendLocalizedMessage( 1061620 ); // Your Blood Oath has been broken. + + m_OathTable.Remove( m_Caster ); + m_OathTable.Remove( m_Target ); + + Stop(); + } + } + } + + private class InternalTarget : Target + { + private BloodOathSpell m_Owner; + + public InternalTarget( BloodOathSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + else + from.SendLocalizedMessage( 1060508 ); // You can't curse that. + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/CorpseSkin.cs b/Scripts/Spells/Necromancy/CorpseSkin.cs new file mode 100644 index 000000000..b8d433f44 --- /dev/null +++ b/Scripts/Spells/Necromancy/CorpseSkin.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class CorpseSkinSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Corpse Skin", "In Agle Corp Ylem", + SpellCircle.Fourth, // 0.5 + 1.0 = 1.5s base cast delay + 203, + 9051, + Reagent.BatWing, + Reagent.GraveDust + ); + + public override double RequiredSkill{ get{ return 20.0; } } + public override int RequiredMana{ get{ return 11; } } + + public CorpseSkinSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Transmogrifies the flesh of the target creature or player to resemble rotted corpse flesh, + * making them more vulnerable to Fire and Poison damage, + * but increasing their resistance to Physical and Cold damage. + * + * The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 25 ) + 40 seconds. + * + * NOTE: Algorithm above is fixed point, should be: + * ((ss-mr)/2.5) + 40 + * + * NOTE: Resistance is not checked if targeting yourself + */ + + ExpireTimer timer = (ExpireTimer)m_Table[m]; + + if ( timer != null ) + timer.DoExpire(); + else + m.SendLocalizedMessage( 1061689 ); // Your skin turns dry and corpselike. + + m.FixedParticles( 0x373A, 1, 15, 9913, 67, 7, EffectLayer.Head ); + m.PlaySound( 0x1BB ); + + double ss = GetDamageSkill( Caster ); + double mr = ( Caster == m ? 0.0 : GetResistSkill( m ) ); + + TimeSpan duration = TimeSpan.FromSeconds( ((ss - mr) / 2.5) + 40.0 ); + + ResistanceMod[] mods = new ResistanceMod[4] + { + new ResistanceMod( ResistanceType.Fire, -15 ), + new ResistanceMod( ResistanceType.Poison, -15 ), + new ResistanceMod( ResistanceType.Cold, +10 ), + new ResistanceMod( ResistanceType.Physical, +10 ) + }; + + timer = new ExpireTimer( m, mods, duration ); + timer.Start(); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.CorpseSkin, 1075663, duration, m ) ); + + m_Table[m] = timer; + + for ( int i = 0; i < mods.Length; ++i ) + m.AddResistanceMod( mods[i] ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool RemoveCurse( Mobile m ) + { + ExpireTimer t = (ExpireTimer)m_Table[m]; + + if ( t == null ) + return false; + + m.SendLocalizedMessage( 1061688 ); // Your skin returns to normal. + t.DoExpire(); + return true; + } + + private class ExpireTimer : Timer + { + private Mobile m_Mobile; + private ResistanceMod[] m_Mods; + + public ExpireTimer( Mobile m, ResistanceMod[] mods, TimeSpan delay ) : base( delay ) + { + m_Mobile = m; + m_Mods = mods; + } + + public void DoExpire() + { + for ( int i = 0; i < m_Mods.Length; ++i ) + m_Mobile.RemoveResistanceMod( m_Mods[i] ); + + Stop(); + BuffInfo.RemoveBuff( m_Mobile, BuffIcon.CorpseSkin ); + m_Table.Remove( m_Mobile ); + } + + protected override void OnTick() + { + m_Mobile.SendLocalizedMessage( 1061688 ); // Your skin returns to normal. + DoExpire(); + } + } + + private class InternalTarget : Target + { + private CorpseSkinSpell m_Owner; + + public InternalTarget( CorpseSkinSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/CurseWeapon.cs b/Scripts/Spells/Necromancy/CurseWeapon.cs new file mode 100644 index 000000000..8a30477cd --- /dev/null +++ b/Scripts/Spells/Necromancy/CurseWeapon.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class CurseWeaponSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Curse Weapon", "An Sanct Gra Char", + SpellCircle.First, // 0.5 + 0.25 = 0.75s base cast delay + 203, + 9031, + Reagent.PigIron + ); + + public override double RequiredSkill{ get{ return 0.0; } } + public override int RequiredMana{ get{ return 7; } } + + public CurseWeaponSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + BaseWeapon weapon = Caster.Weapon as BaseWeapon; + + if ( weapon == null || weapon is Fists ) + { + Caster.SendLocalizedMessage( 501078 ); // You must be holding a weapon. + } + else if ( CheckSequence() ) + { + /* Temporarily imbues a weapon with a life draining effect. + * Half the damage that the weapon inflicts is added to the necromancer's health. + * The effects lasts for (Spirit Speak skill level / 34) + 1 seconds. + * + * NOTE: Above algorithm is fixed point, should be : + * (Spirit Speak skill level / 3.4) + 1 + * + * TODO: What happens if you curse a weapon then give it to someone else? Should they get the drain effect? + */ + + Caster.PlaySound( 0x387 ); + Caster.FixedParticles( 0x3779, 1, 15, 9905, 32, 2, EffectLayer.Head ); + Caster.FixedParticles( 0x37B9, 1, 14, 9502, 32, 5, (EffectLayer)255 ); + new SoundEffectTimer( Caster ).Start(); + + TimeSpan duration = TimeSpan.FromSeconds( (Caster.Skills[SkillName.SpiritSpeak].Value / 3.4) + 1.0 ); + + + Timer t = (Timer)m_Table[weapon]; + + if ( t != null ) + t.Stop(); + + weapon.Cursed = true; + + m_Table[weapon] = t = new ExpireTimer( weapon, duration ); + + t.Start(); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + private class ExpireTimer : Timer + { + private BaseWeapon m_Weapon; + + public ExpireTimer( BaseWeapon weapon, TimeSpan delay ) : base( delay ) + { + m_Weapon = weapon; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + m_Weapon.Cursed = false; + Effects.PlaySound( m_Weapon.GetWorldLocation(), m_Weapon.Map, 0xFA ); + m_Table.Remove( this ); + } + } + + private class SoundEffectTimer : Timer + { + private Mobile m_Mobile; + + public SoundEffectTimer( Mobile m ) : base( TimeSpan.FromSeconds( 0.75 ) ) + { + m_Mobile = m; + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + m_Mobile.PlaySound( 0xFA ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/EvilOmen.cs b/Scripts/Spells/Necromancy/EvilOmen.cs new file mode 100644 index 000000000..da71ce40a --- /dev/null +++ b/Scripts/Spells/Necromancy/EvilOmen.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using Server.Mobiles; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class EvilOmenSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Evil Omen", "Pas Tym An Sanct", + SpellCircle.First, // 0.5 + 0.25 = 0.75s base cast delay + 203, + 9031, + Reagent.BatWing, + Reagent.NoxCrystal + ); + + public override double RequiredSkill{ get{ return 20.0; } } + public override int RequiredMana{ get{ return 11; } } + + public EvilOmenSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !(m is BaseCreature || m is PlayerMobile) ) + { + Caster.SendLocalizedMessage( 1060508 ); // You can't curse that. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Curses the target so that the next harmful event that affects them is magnified. + * Damage to the target's hit points is increased 25%, + * the poison level of the attack will be 1 higher + * and the Resist Magic skill of the target will be fixed on 50. + * + * The effect lasts for one harmful event only. + */ + + m.PlaySound( 0xFC ); + m.FixedParticles( 0x3728, 1, 13, 9912, 1150, 7, EffectLayer.Head ); + m.FixedParticles( 0x3779, 1, 15, 9502, 67, 7, EffectLayer.Head ); + + if ( !m_Table.Contains( m ) ) + { + SkillMod mod = new DefaultSkillMod( SkillName.MagicResist, false, 50.0 ); + + if ( m.Skills[SkillName.MagicResist].Base > 50.0 ) + m.AddSkillMod( mod ); + + m_Table[m] = mod; + } + + TimeSpan duration = TimeSpan.FromSeconds( (Caster.Skills[SkillName.SpiritSpeak].Value / 12) + 1.0 ); + + Timer.DelayCall( duration, new TimerStateCallback( EffectExpire_Callback ), m ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + private static void EffectExpire_Callback( object state ) + { + CheckEffect( (Mobile)state ); + } + + public static bool CheckEffect( Mobile m ) + { + SkillMod mod = (SkillMod)m_Table[m]; + + if ( mod == null ) + return false; + + m_Table.Remove( m ); + mod.Remove(); + + return true; + } + + private class InternalTarget : Target + { + private EvilOmenSpell m_Owner; + + public InternalTarget( EvilOmenSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + else + from.SendLocalizedMessage( 1060508 ); // You can't curse that. + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/Exorcism.cs b/Scripts/Spells/Necromancy/Exorcism.cs new file mode 100644 index 000000000..ea64f66a9 --- /dev/null +++ b/Scripts/Spells/Necromancy/Exorcism.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Engines.CannedEvil; +using Server.Guilds; +using Server.Factions; +using Server.Engines.PartySystem; +using Server.Regions; + +namespace Server.Spells.Necromancy +{ + public class ExorcismSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Exorcism", "Ort Corp Grav", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.NoxCrystal, + Reagent.GraveDust + ); + + public override double RequiredSkill { get { return 80.0; } } + public override int RequiredMana { get { return 40; } } + + public ExorcismSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if( Caster.Skills.SpiritSpeak.Value < 100.0 ) + { + Caster.SendLocalizedMessage( 1072112 ); // You must have GM Spirit Speak to use this spell + return false; + } + + return base.CheckCast(); + } + + + public override bool DelayedDamage { get { return false; } } + + private const int Range = 18; + + public override int ComputeKarmaAward() + { + return 0; //no karma lost from this spell! + } + + public override void OnCast() + { + ChampionSpawnRegion r = Caster.Region.GetRegion( typeof( ChampionSpawnRegion ) ) as ChampionSpawnRegion; + + if( r == null || !Caster.InRange( r.ChampionSpawn, Range ) ) + { + Caster.SendLocalizedMessage( 1072111 ); // You are not in a valid exorcism region. + } + else if( CheckSequence() ) + { + /* Creates a withering frost around the Caster, + * which deals Cold Damage to all valid targets in a radius of 5 tiles. + */ + + Map map = Caster.Map; + + if( map != null ) + { + ArrayList targets = new ArrayList(); + + foreach( Mobile m in r.ChampionSpawn.GetMobilesInRange( Range ) ) + { + if( IsValidTarget( m ) ) + targets.Add( m ); + } + + //Effects.PlaySound( Caster.Location, map, 0x1FB ); + //Effects.PlaySound( Caster.Location, map, 0x10B ); + //Effects.SendLocationParticles( EffectItem.Create( Caster.Location, map, EffectItem.DefaultDuration ), 0x37CC, 1, 40, 97, 3, 9917, 0 ); + + for( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + //m.FixedParticles( 0x374A, 1, 15, 9502, 97, 3, (EffectLayer)255 ); + + //Suprisingly, no effects + + m.Location = GetNearestShrine( m ); + } + } + } + + FinishSequence(); + } + + private bool IsValidTarget( Mobile m ) + { + if( !m.Player || m.Alive ) + return false; + /* + if( m.Corpse != null && m.Corpse.Map == m.Map ) + return false; + * */ + + Corpse c = m.Corpse as Corpse; + Map map = m.Map; + + if( c != null && !c.Deleted && map != null && c.Map == map ) + { + if( SpellHelper.IsAnyT2A( map, c.Location ) && SpellHelper.IsAnyT2A( map, m.Location ) ) + return false; //Same Map, both in T2A, ie, same 'sub server'. + + if( m.Region.IsPartOf( typeof( DungeonRegion ) ) == Region.Find( c.Location, map ).IsPartOf( typeof( DungeonRegion ) ) ) + return false; //Same Map, both in Dungeon region OR They're both NOT in a dungeon region. + + //Just an approximation cause RunUO doens't divide up the world the same way OSI does ;p + + } + + Party p = Party.Get( m ); + + if( p != null && p.Contains( Caster ) ) + return false; + + if( m.Guild != null && Caster.Guild != null ) + { + Guild mGuild = m.Guild as Guild; + Guild cGuild = Caster.Guild as Guild; + + if( mGuild.IsAlly( cGuild ) ) + return false; + + if( mGuild == cGuild ) + return false; + } + + Faction f = Faction.Find( m ); + + if( Faction.Facet == m.Map && f != null && f == Faction.Find( Caster ) ) + return false; + + return true; + } + + private static Point3D GetNearestShrine( Mobile m ) + { + Map map = m.Map; + + Point3D[] locList; + + + if( map == Map.Felucca || map == Map.Trammel ) + locList = m_BritanniaLocs; + else if( map == Map.Ilshenar ) + locList = m_IllshLocs; + else if( map == Map.Tokuno ) + locList = m_TokunoLocs; + else if( map == Map.Malas ) + locList = m_MalasLocs; + else + locList = new Point3D[0]; + + Point3D closest = Point3D.Zero; + double minDist = double.MaxValue; + + for( int i = 0; i < locList.Length; i++ ) + { + Point3D p = locList[i]; + + double dist = m.GetDistanceToSqrt( p ); + if( minDist > dist ) + { + closest = p; + minDist = dist; + } + } + + return closest; + } + + private static readonly Point3D[] m_BritanniaLocs = new Point3D[] + { + new Point3D( 1470, 843, 0 ), + new Point3D( 1857, 865, -1 ), + new Point3D( 4220, 563, 36 ), + new Point3D( 1732, 3528, 0 ), + new Point3D( 1300, 644, 8 ), + new Point3D( 3355, 302, 9 ), + new Point3D( 1606, 2490, 5 ), + new Point3D( 2500, 3931, 3 ), + new Point3D( 4264, 3707, 0 ) + }; + private static readonly Point3D[] m_IllshLocs = new Point3D[] + { + new Point3D( 1222, 474, -17 ), + new Point3D( 718, 1360, -60), + new Point3D( 297, 1014, -19 ), + new Point3D( 986, 1006, -36 ), + new Point3D( 1180, 1288, -30 ), + new Point3D( 1538, 1341, -3 ), + new Point3D( 528, 223, -38 ) + }; + private static readonly Point3D[] m_MalasLocs = new Point3D[] + { + new Point3D ( 976, 517, -30 ) + }; + private static readonly Point3D[] m_TokunoLocs = new Point3D[] + { + new Point3D( 710, 1162, 25 ), + new Point3D( 1034, 515, 18 ), + new Point3D( 295, 712, 55 ) + }; + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/HorrificBeast.cs b/Scripts/Spells/Necromancy/HorrificBeast.cs new file mode 100644 index 000000000..3ecf7572a --- /dev/null +++ b/Scripts/Spells/Necromancy/HorrificBeast.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class HorrificBeastSpell : TransformationSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Horrific Beast", "Rel Xen Vas Bal", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.BatWing, + Reagent.DaemonBlood + ); + + public override double RequiredSkill{ get{ return 40.0; } } + public override int RequiredMana{ get{ return 11; } } + + public override int Body{ get{ return 746; } } + + public HorrificBeastSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void PlayEffect( Mobile m ) + { + m.PlaySound( 0x165 ); + m.FixedParticles( 0x3728, 1, 13, 9918, 92, 3, EffectLayer.Head ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/LichForm.cs b/Scripts/Spells/Necromancy/LichForm.cs new file mode 100644 index 000000000..5914f438b --- /dev/null +++ b/Scripts/Spells/Necromancy/LichForm.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class LichFormSpell : TransformationSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Lich Form", "Rel Xen Corp Ort", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.GraveDust, + Reagent.DaemonBlood, + Reagent.NoxCrystal + ); + + public override double RequiredSkill{ get{ return 70.0; } } + public override int RequiredMana{ get{ return 23; } } + + public override int Body{ get{ return 749; } } + + public override int FireResistOffset{ get{ return -10; } } + public override int ColdResistOffset{ get{ return +10; } } + public override int PoisResistOffset{ get{ return +10; } } + + public override double TickRate{ get{ return 2.5; } } + + public LichFormSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void PlayEffect( Mobile m ) + { + m.PlaySound( 0x19C ); + m.FixedParticles( 0x3709, 1, 30, 9904, 1108, 6, EffectLayer.RightFoot ); + } + + public override void OnTick( Mobile m ) + { + --m.Hits; + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/MindRot.cs b/Scripts/Spells/Necromancy/MindRot.cs new file mode 100644 index 000000000..a12429855 --- /dev/null +++ b/Scripts/Spells/Necromancy/MindRot.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class MindRotSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Mind Rot", "Wis An Ben", + SpellCircle.Fourth, // 0.5 + 1.0 = 1.5s base cast delay + 203, + 9031, + Reagent.BatWing, + Reagent.PigIron, + Reagent.DaemonBlood + ); + + public override double RequiredSkill{ get{ return 30.0; } } + public override int RequiredMana{ get{ return 17; } } + + public MindRotSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( HasMindRotScalar( m ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Attempts to place a curse on the Target that increases the mana cost of any spells they cast, + * for a duration based off a comparison between the Caster's Spirit Speak skill and the Target's Resisting Spells skill. + * The effect lasts for ((Spirit Speak skill level - target's Resist Magic skill level) / 50 ) + 20 seconds. + */ + + m.PlaySound( 0x1FB ); + m.PlaySound( 0x258 ); + m.FixedParticles( 0x373A, 1, 17, 9903, 15, 4, EffectLayer.Head ); + + TimeSpan duration = TimeSpan.FromSeconds( (((GetDamageSkill( Caster ) - GetResistSkill( m )) / 5.0) + 20.0) * (m.Player ? 1.0 : 2.0 ) ); + + if ( m.Player ) + SetMindRotScalar( Caster, m, 1.25, duration ); + else + SetMindRotScalar( Caster, m, 2.00, duration ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static void ClearMindRotScalar( Mobile m ) + { + m_Table.Remove( m ); + BuffInfo.RemoveBuff( m, BuffIcon.Mindrot ); + } + + public static bool HasMindRotScalar( Mobile m ) + { + return m_Table.Contains( m ); + } + + public static bool GetMindRotScalar( Mobile m, ref double scalar ) + { + object obj = m_Table[m]; + + if ( obj == null ) + return false; + + scalar = (double)obj; + return true; + } + + public static void SetMindRotScalar( Mobile caster, Mobile target, double scalar, TimeSpan duration ) + { + m_Table[target] = scalar; + BuffInfo.AddBuff( target, new BuffInfo( BuffIcon.Mindrot, 1075665, duration, target ) ); + new ExpireTimer( caster, target, duration ).Start(); + } + + private class ExpireTimer : Timer + { + private Mobile m_Caster; + private Mobile m_Target; + private DateTime m_End; + + public ExpireTimer( Mobile caster, Mobile target, TimeSpan delay ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Caster = caster; + m_Target = target; + m_End = DateTime.Now + delay; + + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + if ( m_Target.Deleted || !m_Target.Alive || DateTime.Now >= m_End ) + { + m_Target.SendLocalizedMessage( 1060872 ); // Your mind feels normal again. + ClearMindRotScalar( m_Target ); + Stop(); + } + } + } + + private class InternalTarget : Target + { + private MindRotSpell m_Owner; + + public InternalTarget( MindRotSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + else + from.SendLocalizedMessage( 1060508 ); // You can't curse that. + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/NecromancerSpell.cs b/Scripts/Spells/Necromancy/NecromancerSpell.cs new file mode 100644 index 000000000..ab1d52f0c --- /dev/null +++ b/Scripts/Spells/Necromancy/NecromancerSpell.cs @@ -0,0 +1,38 @@ +using System; +using Server; + +namespace Server.Spells.Necromancy +{ + public abstract class NecromancerSpell : Spell + { + public abstract double RequiredSkill{ get; } + public abstract int RequiredMana{ get; } + + public override SkillName CastSkill{ get{ return SkillName.Necromancy; } } + public override SkillName DamageSkill{ get{ return SkillName.SpiritSpeak; } } + + public override bool ClearHandsOnCast{ get{ return false; } } + + public override int CastDelayFastScalar{ get{ return 0; } } // Necromancer spells are not effected by fast cast items, though they are by fast cast recovery + + public NecromancerSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info ) + { + } + + public override int ComputeKarmaAward() + { + return -(70 + (10 * (int)Circle)); + } + + public override void GetCastSkills( out double min, out double max ) + { + min = RequiredSkill; + max = RequiredSkill + 40.0; + } + + public override int GetMana() + { + return RequiredMana; + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/PainSpike.cs b/Scripts/Spells/Necromancy/PainSpike.cs new file mode 100644 index 000000000..fc05a8a3f --- /dev/null +++ b/Scripts/Spells/Necromancy/PainSpike.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class PainSpikeSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Pain Spike", "In Sar", + SpellCircle.Second, // 0.5 + 0.5 = 1s base cast delay + 203, + 9031, + Reagent.GraveDust, + Reagent.PigIron + ); + + public override double RequiredSkill{ get{ return 20.0; } } + public override int RequiredMana{ get{ return 5; } } + + public PainSpikeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return false; } } + + public void Target( Mobile m ) + { + if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + /* Temporarily causes intense physical pain to the target, dealing direct damage. + * After 10 seconds the spell wears off, and if the target is still alive, + * some of the Hit Points lost through Pain Spike are restored. + */ + + m.FixedParticles( 0x37C4, 1, 8, 9916, 39, 3, EffectLayer.Head ); + m.FixedParticles( 0x37C4, 1, 8, 9502, 39, 4, EffectLayer.Head ); + m.PlaySound( 0x210 ); + + double damage = ((GetDamageSkill( Caster ) - GetResistSkill( m )) / 10) + (m.Player ? 18 : 30); + + if ( damage < 1 ) + damage = 1; + + TimeSpan buffTime = TimeSpan.FromSeconds( 10.0 ); + + if( m_Table.Contains( m ) ) + { + damage = Utility.RandomMinMax( 3, 7 ); + Timer t = m_Table[m] as Timer; + + if( t != null ) + { + t.Delay += TimeSpan.FromSeconds( 2.0 ); + + buffTime = t.Next - DateTime.Now; + } + } + else + { + new InternalTimer( m, damage ).Start(); + } + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.PainSpike, 1075667, buffTime, m, (int)damage ) ); + + + + Misc.WeightOverloading.DFA = Misc.DFAlgorithm.PainSpike; + m.Damage( (int) damage, Caster ); + Misc.WeightOverloading.DFA = Misc.DFAlgorithm.Standard; + + //SpellHelper.Damage( this, m, damage, 100, 0, 0, 0, 0, Misc.DFAlgorithm.PainSpike ); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + private int m_ToRestore; + + public InternalTimer( Mobile m, double toRestore ) : base( TimeSpan.FromSeconds( 10.0 ) ) + { + Priority = TimerPriority.OneSecond; + + m_Mobile = m; + m_ToRestore = (int)toRestore; + + m_Table[m] = this; + } + + protected override void OnTick() + { + m_Table.Remove( m_Mobile ); + + if ( m_Mobile.Alive && !m_Mobile.IsDeadBondedPet ) + m_Mobile.Hits += m_ToRestore; + + BuffInfo.RemoveBuff( m_Mobile, BuffIcon.PainSpike ); + } + } + + private class InternalTarget : Target + { + private PainSpikeSpell m_Owner; + + public InternalTarget( PainSpikeSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/PoisonStrike.cs b/Scripts/Spells/Necromancy/PoisonStrike.cs new file mode 100644 index 000000000..3666c9188 --- /dev/null +++ b/Scripts/Spells/Necromancy/PoisonStrike.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class PoisonStrikeSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Poison Strike", "In Vas Nox", + SpellCircle.Fourth, // 0.5 + 1.0 = 1.5s base cast delay + 203, + 9031, + Reagent.NoxCrystal + ); + + public override double RequiredSkill{ get{ return 50.0; } } + public override int RequiredMana{ get{ return 17; } } + + public PoisonStrikeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return false; } } + + public void Target( Mobile m ) + { + if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Creates a blast of poisonous energy centered on the target. + * The main target is inflicted with a large amount of Poison damage, and all valid targets in a radius of 2 tiles around the main target are inflicted with a lesser effect. + * One tile from main target receives 50% damage, two tiles from target receives 33% damage. + */ + + CheckResisted( m ); // Check magic resist for skill, but do not use return value + + Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x36B0, 1, 14, 63, 7, 9915, 0 ); + Effects.PlaySound( m.Location, m.Map, 0x229 ); + + double damage = Utility.RandomMinMax( 36, 40 ) * ((300 + (GetDamageSkill( Caster ) * 9)) / 1000); + + Map map = m.Map; + + if ( map != null ) + { + ArrayList targets = new ArrayList(); + + foreach ( Mobile targ in m.GetMobilesInRange( 2 ) ) + { + if ( (Caster == targ || SpellHelper.ValidIndirectTarget( Caster, targ )) && Caster.CanBeHarmful( targ, false ) ) + targets.Add( targ ); + } + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile targ = (Mobile)targets[i]; + + int num; + + if ( targ.InRange( m.Location, 0 ) ) + num = 1; + else if ( targ.InRange( m.Location, 1 ) ) + num = 2; + else + num = 3; + + Caster.DoHarmful( targ ); + SpellHelper.Damage( this, targ, damage / num, 0, 0, 0, 100, 0 ); + } + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private PoisonStrikeSpell m_Owner; + + public InternalTarget( PoisonStrikeSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/Strangle.cs b/Scripts/Spells/Necromancy/Strangle.cs new file mode 100644 index 000000000..411d31f11 --- /dev/null +++ b/Scripts/Spells/Necromancy/Strangle.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class StrangleSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Strangle", "In Bal Nox", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 209, + 9031, + Reagent.DaemonBlood, + Reagent.NoxCrystal + ); + + public override double RequiredSkill{ get{ return 65.0; } } + public override int RequiredMana{ get{ return 29; } } + + public StrangleSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + /* Temporarily chokes off the air suply of the target with poisonous fumes. + * The target is inflicted with poison damage over time. + * The amount of damage dealt each "hit" is based off of the caster's Spirit Speak skill and the Target's current Stamina. + * The less Stamina the target has, the more damage is done by Strangle. + * Duration of the effect is Spirit Speak skill level / 10 rounds, with a minimum number of 4 rounds. + * The first round of damage is dealt after 5 seconds, and every next round after that comes 1 second sooner than the one before, until there is only 1 second between rounds. + * The base damage of the effect lies between (Spirit Speak skill level / 10) - 2 and (Spirit Speak skill level / 10) + 1. + * Base damage is multiplied by the following formula: (3 - (target's current Stamina / target's maximum Stamina) * 2). + * Example: + * For a target at full Stamina the damage multiplier is 1, + * for a target at 50% Stamina the damage multiplier is 2 and + * for a target at 20% Stamina the damage multiplier is 2.6 + */ + + m.PlaySound( 0x22F ); + m.FixedParticles( 0x36CB, 1, 9, 9911, 67, 5, EffectLayer.Head ); + m.FixedParticles( 0x374A, 1, 17, 9502, 1108, 4, (EffectLayer)255 ); + + if ( !m_Table.Contains( m ) ) + { + Timer t = new InternalTimer( m, Caster ); + t.Start(); + + m_Table[m] = t; + } + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool RemoveCurse( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t == null ) + return false; + + t.Stop(); + m.SendLocalizedMessage( 1061687 ); // You can breath normally again. + + m_Table.Remove( m ); + return true; + } + + private class InternalTimer : Timer + { + private Mobile m_Target, m_From; + private double m_MinBaseDamage, m_MaxBaseDamage; + + private DateTime m_NextHit; + private int m_HitDelay; + + private int m_Count, m_MaxCount; + + public InternalTimer( Mobile target, Mobile from ) : base( TimeSpan.FromSeconds( 0.1 ), TimeSpan.FromSeconds( 0.1 ) ) + { + Priority = TimerPriority.FiftyMS; + + m_Target = target; + m_From = from; + + double spiritLevel = from.Skills[SkillName.SpiritSpeak].Value / 10; + + m_MinBaseDamage = spiritLevel - 2; + m_MaxBaseDamage = spiritLevel + 1; + + m_HitDelay = 5; + m_NextHit = DateTime.Now + TimeSpan.FromSeconds( m_HitDelay ); + + m_Count = (int)spiritLevel; + + if ( m_Count < 4 ) + m_Count = 4; + + m_MaxCount = m_Count; + } + + protected override void OnTick() + { + if ( !m_Target.Alive ) + { + m_Table.Remove( m_Target ); + Stop(); + } + + if ( !m_Target.Alive || DateTime.Now < m_NextHit ) + return; + + --m_Count; + + if ( m_HitDelay > 1 ) + { + if ( m_MaxCount < 5 ) + { + --m_HitDelay; + } + else + { + int delay = (int)(Math.Ceiling( (1.0 + (5 * m_Count)) / m_MaxCount ) ); + + if ( delay <= 5 ) + m_HitDelay = delay; + else + m_HitDelay = 5; + } + } + + if ( m_Count == 0 ) + { + m_Target.SendLocalizedMessage( 1061687 ); // You can breath normally again. + m_Table.Remove( m_Target ); + Stop(); + } + else + { + m_NextHit = DateTime.Now + TimeSpan.FromSeconds( m_HitDelay ); + + double damage = m_MinBaseDamage + (Utility.RandomDouble() * (m_MaxBaseDamage - m_MinBaseDamage)); + + damage *= (3 - (((double)m_Target.Stam / m_Target.StamMax) * 2)); + + if ( damage < 1 ) + damage = 1; + + if ( !m_Target.Player ) + damage *= 1.75; + + AOS.Damage( m_Target, m_From, (int)damage, 0, 0, 0, 100, 0 ); + } + } + } + + private class InternalTarget : Target + { + private StrangleSpell m_Owner; + + public InternalTarget( StrangleSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/SummonFamiliar.cs b/Scripts/Spells/Necromancy/SummonFamiliar.cs new file mode 100644 index 000000000..d4e10058c --- /dev/null +++ b/Scripts/Spells/Necromancy/SummonFamiliar.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Gumps; +using Server.Mobiles; + +namespace Server.Spells.Necromancy +{ + public class SummonFamiliarSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Summon Familiar", "Kal Xen Bal", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.BatWing, + Reagent.GraveDust, + Reagent.DaemonBlood + ); + + public override double RequiredSkill{ get{ return 30.0; } } + public override int RequiredMana{ get{ return 17; } } + + public SummonFamiliarSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + private static Hashtable m_Table = new Hashtable(); + + public static Hashtable Table{ get{ return m_Table; } } + + public override bool CheckCast() + { + BaseCreature check = (BaseCreature)m_Table[Caster]; + + if ( check != null && !check.Deleted ) + { + Caster.SendLocalizedMessage( 1061605 ); // You already have a familiar. + return false; + } + + return base.CheckCast(); + } + + public override void OnCast() + { + if ( CheckSequence() ) + { + Caster.CloseGump( typeof( SummonFamiliarGump ) ); + Caster.SendGump( new SummonFamiliarGump( Caster, m_Entries, this ) ); + } + + FinishSequence(); + } + + private static SummonFamiliarEntry[] m_Entries = new SummonFamiliarEntry[] + { + new SummonFamiliarEntry( typeof( HordeMinionFamiliar ), 1060146, 30.0, 30.0 ), // Horde Minion + new SummonFamiliarEntry( typeof( ShadowWispFamiliar ), 1060142, 50.0, 50.0 ), // Shadow Wisp + new SummonFamiliarEntry( typeof( DarkWolfFamiliar ), 1060143, 60.0, 60.0 ), // Dark Wolf + new SummonFamiliarEntry( typeof( DeathAdder ), 1060145, 80.0, 80.0 ), // Death Adder + new SummonFamiliarEntry( typeof( VampireBatFamiliar ), 1060144, 100.0, 100.0 ) // Vampire Bat + }; + + public static SummonFamiliarEntry[] Entries{ get{ return m_Entries; } } + } + + public class SummonFamiliarEntry + { + private Type m_Type; + private object m_Name; + private double m_ReqNecromancy; + private double m_ReqSpiritSpeak; + + public Type Type{ get{ return m_Type; } } + public object Name{ get{ return m_Name; } } + public double ReqNecromancy{ get{ return m_ReqNecromancy; } } + public double ReqSpiritSpeak{ get{ return m_ReqSpiritSpeak; } } + + public SummonFamiliarEntry( Type type, object name, double reqNecromancy, double reqSpiritSpeak ) + { + m_Type = type; + m_Name = name; + m_ReqNecromancy = reqNecromancy; + m_ReqSpiritSpeak = reqSpiritSpeak; + } + } + + public class SummonFamiliarGump : Gump + { + private Mobile m_From; + private SummonFamiliarEntry[] m_Entries; + + private SummonFamiliarSpell m_Spell; + + private const int EnabledColor16 = 0x0F20; + private const int DisabledColor16 = 0x262A; + + private const int EnabledColor32 = 0x18CD00; + private const int DisabledColor32 = 0x4A8B52; + + public SummonFamiliarGump( Mobile from, SummonFamiliarEntry[] entries, SummonFamiliarSpell spell ) : base( 200, 100 ) + { + m_From = from; + m_Entries = entries; + m_Spell = spell; + + AddPage( 0 ); + + AddBackground( 10, 10, 250, 178, 9270 ); + AddAlphaRegion( 20, 20, 230, 158 ); + + AddImage( 220, 20, 10464 ); + AddImage( 220, 72, 10464 ); + AddImage( 220, 124, 10464 ); + + AddItem( 188, 16, 6883 ); + AddItem( 198, 168, 6881 ); + AddItem( 8, 15, 6882 ); + AddItem( 2, 168, 6880 ); + + AddHtmlLocalized( 30, 26, 200, 20, 1060147, EnabledColor16, false, false ); // Chose thy familiar... + + double necro = from.Skills[SkillName.Necromancy].Base; + double spirit = from.Skills[SkillName.SpiritSpeak].Base; + + for ( int i = 0; i < entries.Length; ++i ) + { + object name = entries[i].Name; + + bool enabled = ( necro >= entries[i].ReqNecromancy && spirit >= entries[i].ReqSpiritSpeak ); + + AddButton( 27, 53 + (i * 21), 9702, 9703, i + 1, GumpButtonType.Reply, 0 ); + + if ( name is int ) + AddHtmlLocalized( 50, 51 + (i * 21), 150, 20, (int)name, enabled ? EnabledColor16 : DisabledColor16, false, false ); + else if ( name is string ) + AddHtml( 50, 51 + (i * 21), 150, 20, String.Format( "{1}", enabled ? EnabledColor32 : DisabledColor32, name ), false, false ); + } + } + + private static Hashtable m_Table = new Hashtable(); + + public override void OnResponse( NetState sender, RelayInfo info ) + { + int index = info.ButtonID - 1; + + if ( index >= 0 && index < m_Entries.Length ) + { + SummonFamiliarEntry entry = m_Entries[index]; + + double necro = m_From.Skills[SkillName.Necromancy].Base; + double spirit = m_From.Skills[SkillName.SpiritSpeak].Base; + + BaseCreature check = (BaseCreature)SummonFamiliarSpell.Table[m_From]; + + if ( check != null && !check.Deleted ) + { + m_From.SendLocalizedMessage( 1061605 ); // You already have a familiar. + } + else if ( necro < entry.ReqNecromancy || spirit < entry.ReqSpiritSpeak ) + { + // That familiar requires ~1_NECROMANCY~ Necromancy and ~2_SPIRIT~ Spirit Speak. + m_From.SendLocalizedMessage( 1061606, String.Format( "{0:F1}\t{1:F1}", entry.ReqNecromancy, entry.ReqSpiritSpeak ) ); + + m_From.CloseGump( typeof( SummonFamiliarGump ) ); + m_From.SendGump( new SummonFamiliarGump( m_From, SummonFamiliarSpell.Entries, m_Spell ) ); + } + else if ( entry.Type == null ) + { + m_From.SendMessage( "That familiar has not yet been defined." ); + + m_From.CloseGump( typeof( SummonFamiliarGump ) ); + m_From.SendGump( new SummonFamiliarGump( m_From, SummonFamiliarSpell.Entries, m_Spell ) ); + } + else + { + try + { + BaseCreature bc = (BaseCreature)Activator.CreateInstance( entry.Type ); + + bc.Skills.MagicResist = m_From.Skills.MagicResist; + + if ( BaseCreature.Summon( bc, m_From, m_From.Location, -1, TimeSpan.FromDays( 1.0 ) ) ) + { + m_From.FixedParticles( 0x3728, 1, 10, 9910, EffectLayer.Head ); + bc.PlaySound( bc.GetIdleSound() ); + SummonFamiliarSpell.Table[m_From] = bc; + } + } + catch + { + } + } + } + else + { + m_From.SendLocalizedMessage( 1061825 ); // You decide not to summon a familiar. + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/TransformationSpell.cs b/Scripts/Spells/Necromancy/TransformationSpell.cs new file mode 100644 index 000000000..c1ba6b535 --- /dev/null +++ b/Scripts/Spells/Necromancy/TransformationSpell.cs @@ -0,0 +1,254 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Spells.Fifth; +using Server.Spells.Seventh; + +namespace Server.Spells.Necromancy +{ + public abstract class TransformationSpell : NecromancerSpell + { + public abstract int Body{ get; } + public virtual int Hue{ get{ return 0; } } + + public virtual int PhysResistOffset{ get{ return 0; } } + public virtual int FireResistOffset{ get{ return 0; } } + public virtual int ColdResistOffset{ get{ return 0; } } + public virtual int PoisResistOffset{ get{ return 0; } } + public virtual int NrgyResistOffset{ get{ return 0; } } + + public TransformationSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info ) + { + } + + public override bool BlockedByHorrificBeast{ get{ return false; } } + + public override bool CheckCast() + { + /*if ( Caster.Mounted ) + { + Caster.SendLocalizedMessage( 1042561 ); // Please dismount first. + return false; + } + else */ + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return false; + } + else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed. + return false; + } + else if ( Spells.Ninjitsu.AnimalForm.UnderTransformation( Caster ) ) + { + Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form. + return false; + } + + return base.CheckCast(); + } + + public override void OnCast() + { + Mobile caster = this.Caster; + + /*if ( caster.Mounted ) + { + caster.SendLocalizedMessage( 1042561 ); // Please dismount first. + } + else */ + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( !caster.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed. + } + else if ( Spells.Ninjitsu.AnimalForm.UnderTransformation( Caster ) ) + { + Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form. + } + else if ( !caster.CanBeginAction( typeof( IncognitoSpell ) ) || (caster.IsBodyMod && GetContext( caster ) == null) ) + { + DoFizzle(); + } + else if ( CheckSequence() ) + { + TransformContext context = GetContext( caster ); + Type ourType = this.GetType(); + + bool wasTransformed = ( context != null ); + bool ourTransform = ( wasTransformed && context.Type == ourType ); + + if ( wasTransformed ) + { + RemoveContext( caster, context, ourTransform ); + + if ( ourTransform ) + { + caster.PlaySound( 0xFA ); + caster.FixedParticles( 0x3728, 1, 13, 5042, EffectLayer.Waist ); + } + } + + if ( !ourTransform ) + { + List mods = new List(); + + if ( PhysResistOffset != 0 ) + mods.Add( new ResistanceMod( ResistanceType.Physical, PhysResistOffset ) ); + + if ( FireResistOffset != 0 ) + mods.Add( new ResistanceMod( ResistanceType.Fire, FireResistOffset ) ); + + if ( ColdResistOffset != 0 ) + mods.Add( new ResistanceMod( ResistanceType.Cold, ColdResistOffset ) ); + + if ( PoisResistOffset != 0 ) + mods.Add( new ResistanceMod( ResistanceType.Poison, PoisResistOffset ) ); + + if ( NrgyResistOffset != 0 ) + mods.Add( new ResistanceMod( ResistanceType.Energy, NrgyResistOffset ) ); + + if ( !((Body)this.Body).IsHuman ) + { + Mobiles.IMount mt = Caster.Mount; + + if ( mt != null ) + mt.Rider = null; + } + + caster.BodyMod = this.Body; + caster.HueMod = this.Hue; + + for ( int i = 0; i < mods.Count; ++i ) + caster.AddResistanceMod( mods[i] ); + + PlayEffect( caster ); + + Timer timer = new TransformTimer( caster, this ); + timer.Start(); + + AddContext( caster, new TransformContext( timer, mods, ourType ) ); + } + } + + FinishSequence(); + } + + public virtual double TickRate{ get{ return 1.0; } } + + public virtual void OnTick( Mobile m ) + { + } + + public virtual void PlayEffect( Mobile m ) + { + } + + private static Hashtable m_Table = new Hashtable(); + + public static void AddContext( Mobile m, TransformContext context ) + { + m_Table[m] = context; + + if ( context.Type == typeof( HorrificBeastSpell ) ) + m.Delta( MobileDelta.WeaponDamage ); + } + + public static void RemoveContext( Mobile m, bool resetGraphics ) + { + TransformContext context = GetContext( m ); + + if ( context != null ) + RemoveContext( m, context, resetGraphics ); + } + + public static void RemoveContext( Mobile m, TransformContext context, bool resetGraphics ) + { + m_Table.Remove( m ); + + List mods = context.Mods; + + for ( int i = 0; i < mods.Count; ++i ) + m.RemoveResistanceMod( mods[i] ); + + if ( resetGraphics ) + { + m.HueMod = -1; + m.BodyMod = 0; + } + + context.Timer.Stop(); + + if ( context.Type == typeof( HorrificBeastSpell ) ) + m.Delta( MobileDelta.WeaponDamage ); + } + + public static TransformContext GetContext( Mobile m ) + { + return ( m_Table[m] as TransformContext ); + } + + public static bool UnderTransformation( Mobile m ) + { + return ( GetContext( m ) != null ); + } + + public static bool UnderTransformation( Mobile m, Type type ) + { + TransformContext context = GetContext( m ); + + return ( context != null && context.Type == type ); + } + } + + public class TransformContext + { + private Timer m_Timer; + private List m_Mods; + private Type m_Type; + + public Timer Timer{ get{ return m_Timer; } } + public List Mods{ get{ return m_Mods; } } + public Type Type{ get{ return m_Type; } } + + public TransformContext( Timer timer, List mods, Type type ) + { + m_Timer = timer; + m_Mods = mods; + m_Type = type; + } + } + + public class TransformTimer : Timer + { + private Mobile m_Mobile; + private TransformationSpell m_Spell; + + public TransformTimer( Mobile from, TransformationSpell spell ) : base( TimeSpan.FromSeconds( spell.TickRate ), TimeSpan.FromSeconds( spell.TickRate ) ) + { + m_Mobile = from; + m_Spell = spell; + + Priority = TimerPriority.TwoFiftyMS; + } + + protected override void OnTick() + { + if ( m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Spell.Body || m_Mobile.Hue != m_Spell.Hue ) + { + TransformationSpell.RemoveContext( m_Mobile, true ); + Stop(); + } + else + { + m_Spell.OnTick( m_Mobile ); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/VampiricEmbrace.cs b/Scripts/Spells/Necromancy/VampiricEmbrace.cs new file mode 100644 index 000000000..2554731ce --- /dev/null +++ b/Scripts/Spells/Necromancy/VampiricEmbrace.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class VampiricEmbraceSpell : TransformationSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Vampiric Embrace", "Rel Xen An Sanct", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.BatWing, + Reagent.NoxCrystal, + Reagent.PigIron + ); + + public override double RequiredSkill{ get{ return 99.0; } } + public override int RequiredMana{ get{ return 23; } } + + public override int Body{ get{ return Caster.Female ? 745 : 744; } } + public override int Hue{ get{ return 0x847E; } } + + public override int FireResistOffset{ get{ return -25; } } + + public VampiricEmbraceSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void GetCastSkills( out double min, out double max ) + { + if ( Caster.Skills[CastSkill].Value >= RequiredSkill ) + { + min = 80.0; + max = 120.0; + } + else + { + base.GetCastSkills( out min, out max ); + } + } + + public override void PlayEffect( Mobile m ) + { + Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x373A, 1, 17, 1108, 7, 9914, 0 ); + Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x376A, 1, 22, 67, 7, 9502, 0 ); + Effects.PlaySound( m.Location, m.Map, 0x4B1 ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/VengefulSpirit.cs b/Scripts/Spells/Necromancy/VengefulSpirit.cs new file mode 100644 index 000000000..d6d08c534 --- /dev/null +++ b/Scripts/Spells/Necromancy/VengefulSpirit.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Spells.Necromancy +{ + public class VengefulSpiritSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Vengeful Spirit", "Kal Xen Bal Beh", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.BatWing, + Reagent.GraveDust, + Reagent.PigIron + ); + + public override double RequiredSkill{ get{ return 80.0; } } + public override int RequiredMana{ get{ return 41; } } + + public VengefulSpiritSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( (Caster.Followers + 3) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1049645 ); // You have too many followers to summon that creature. + return false; + } + + return true; + } + + public void Target( Mobile m ) + { + if ( Caster == m ) + { + Caster.SendLocalizedMessage( 1061832 ); // You cannot exact vengeance on yourself. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + /* Summons a Revenant which haunts the target until either the target or the Revenant is dead. + * Revenants have the ability to track down their targets wherever they may travel. + * A Revenant's strength is determined by the Necromancy and Spirit Speak skills of the Caster. + * The effect lasts for ((Spirit Speak skill level * 80) / 120) + 10 seconds. + */ + + TimeSpan duration = TimeSpan.FromSeconds( ((GetDamageSkill( Caster ) * 80) / 120) + 10 ); + + Revenant rev = new Revenant( Caster, m, duration ); + + if ( BaseCreature.Summon( rev, false, Caster, m.Location, 0x81, TimeSpan.FromSeconds( duration.TotalSeconds + 2.0 ) ) ) + rev.FixedParticles( 0x373A, 1, 15, 9909, EffectLayer.Waist ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private VengefulSpiritSpell m_Owner; + + public InternalTarget( VengefulSpiritSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/Wither.cs b/Scripts/Spells/Necromancy/Wither.cs new file mode 100644 index 000000000..980569541 --- /dev/null +++ b/Scripts/Spells/Necromancy/Wither.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class WitherSpell : NecromancerSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Wither", "Kal Vas An Flam", + SpellCircle.Third, // 0.5 + 0.75 = 1.25s base cast delay + 203, + 9031, + Reagent.NoxCrystal, + Reagent.GraveDust, + Reagent.PigIron + ); + + public override double RequiredSkill{ get{ return 60.0; } } + public override int RequiredMana{ get{ return 23; } } + + public WitherSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool DelayedDamage{ get{ return false; } } + + public override void OnCast() + { + if ( CheckSequence() ) + { + /* Creates a withering frost around the Caster, + * which deals Cold Damage to all valid targets in a radius of 5 tiles. + */ + + Map map = Caster.Map; + + if ( map != null ) + { + ArrayList targets = new ArrayList(); + + foreach ( Mobile m in Caster.GetMobilesInRange( 5 ) ) + { + if ( Caster != m && Caster.InLOS( m ) && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) ) + targets.Add( m ); + } + + Effects.PlaySound( Caster.Location, map, 0x1FB ); + Effects.PlaySound( Caster.Location, map, 0x10B ); + Effects.SendLocationParticles( EffectItem.Create( Caster.Location, map, EffectItem.DefaultDuration ), 0x37CC, 1, 40, 97, 3, 9917, 0 ); + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + Caster.DoHarmful( m ); + m.FixedParticles( 0x374A, 1, 15, 9502, 97, 3, (EffectLayer)255 ); + + double damage = Utility.RandomMinMax( 30, 35 ); + + damage *= (300 + (m.Karma / 100) + (GetDamageSkill( Caster ) * 10)); + damage /= 1000; + + // TODO: cap? + //if ( damage > 40 ) + // damage = 40; + + SpellHelper.Damage( this, m, damage, 0, 0, 100, 0, 0 ); + } + } + } + + FinishSequence(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Necromancy/WraithForm.cs b/Scripts/Spells/Necromancy/WraithForm.cs new file mode 100644 index 000000000..71eb4baf6 --- /dev/null +++ b/Scripts/Spells/Necromancy/WraithForm.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Necromancy +{ + public class WraithFormSpell : TransformationSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Wraith Form", "Rel Xen Um", + SpellCircle.Sixth, // 0.5 + 1.5 = 2s base cast delay + 203, + 9031, + Reagent.NoxCrystal, + Reagent.PigIron + ); + + public override double RequiredSkill{ get{ return 20.0; } } + public override int RequiredMana{ get{ return 17; } } + + public override int Body{ get{ return Caster.Female ? 747 : 748; } } + public override int Hue{ get{ return Caster.Female ? 0 : 0x4001; } } + + public override int PhysResistOffset{ get{ return +10; } } + public override int FireResistOffset{ get{ return -25; } } + public override int ColdResistOffset{ get{ return -05; } } + public override int PoisResistOffset{ get{ return -05; } } + public override int NrgyResistOffset{ get{ return -05; } } + + public WraithFormSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void PlayEffect( Mobile m ) + { + m.PlaySound( 0x17F ); + m.FixedParticles( 0x374A, 1, 15, 9902, 1108, 4, EffectLayer.Waist ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/AnimalForm.cs b/Scripts/Spells/Ninjitsu/AnimalForm.cs new file mode 100644 index 000000000..806f9005c --- /dev/null +++ b/Scripts/Spells/Ninjitsu/AnimalForm.cs @@ -0,0 +1,433 @@ +using System; +using System.Collections; +using Server; +using Server.Gumps; +using Server.Network; +using Server.Mobiles; +using Server.Spells.Fifth; +using Server.Spells.Seventh; + +namespace Server.Spells.Ninjitsu +{ + public class AnimalForm : NinjaSpell + { + + public static void Initialize() + { + EventSink.Login += new LoginEventHandler( OnLogin ); + } + + public static void OnLogin( LoginEventArgs e ) + { + AnimalFormContext context = AnimalForm.GetContext( e.Mobile ); + + if( context != null && context.SpeedBoost ) + e.Mobile.Send( SpeedBoost.Enabled ); + } + + private static SpellInfo m_Info = new SpellInfo( + "Animal Form", null, + SpellCircle.Fourth, // 1.0s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 0.0; } } + public override int RequiredMana{ get{ return (Core.ML ? 10 : 0); } } + public override int CastRecoveryBase{ get { return (Core.ML ? 10 : base.CastRecoveryBase); } } + + public override bool BlockedByAnimalForm{ get{ return false; } } + + public AnimalForm( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed. + return false; + } + else if ( Necromancy.TransformationSpell.UnderTransformation( Caster ) ) + { + Caster.SendLocalizedMessage( 1063219 ); // You cannot mimic an animal while in that form. + return false; + } + + return base.CheckCast(); + } + + public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable ) + { + return false; + } + + public override void OnBeginCast() + { + base.OnBeginCast(); + + Caster.FixedEffect( 0x37C4, 10, 14, 4, 3 ); + } + + public override bool CheckFizzle() + { + // Spell is initially always successful, and with no skill gain. + return true; + } + + public override void OnCast() + { + if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + Caster.SendLocalizedMessage( 1061628 ); // You can't do that while polymorphed. + } + else if ( Necromancy.TransformationSpell.UnderTransformation( Caster ) ) + { + Caster.SendLocalizedMessage( 1063219 ); // You cannot mimic an animal while in that form. + } + else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) || (Caster.IsBodyMod && GetContext( Caster ) == null) ) + { + DoFizzle(); + } + else if ( CheckSequence() ) + { + AnimalFormContext context = GetContext( Caster ); + + if ( context != null ) + { + RemoveContext( Caster, context, true ); + } + else if ( Caster is PlayerMobile ) + { + if ( GetLastAnimalForm( Caster ) == -1 || DateTime.Now - Caster.LastMoveTime > Caster.ComputeMovementSpeed( Caster.Direction ) ) + { + Caster.CloseGump( typeof( AnimalFormGump ) ); + Caster.SendGump( new AnimalFormGump( Caster, m_Entries, this ) ); + } + else + { + if ( Morph( Caster, GetLastAnimalForm( Caster ) ) == MorphResult.Fail ) + DoFizzle(); + } + } + else + { + if ( Morph( Caster, GetLastAnimalForm( Caster ) ) == MorphResult.Fail ) + DoFizzle(); + } + } + + FinishSequence(); + } + + private static Hashtable m_LastAnimalForms = new Hashtable(); + + public int GetLastAnimalForm( Mobile m ) + { + if ( m_LastAnimalForms.Contains( m ) ) + return (int)m_LastAnimalForms[m]; + + return -1; + } + + public enum MorphResult + { + Success, + Fail, + NoSkill + } + + public static MorphResult Morph( Mobile m, int entryID ) + { + if ( entryID < 0 || entryID >= m_Entries.Length ) + return MorphResult.Fail; + + AnimalFormEntry entry = m_Entries[entryID]; + + m_LastAnimalForms[m] = entryID; //On OSI, it's the last /attempted/ one not the last succeeded one + + if ( m.Skills.Ninjitsu.Value < entry.ReqSkill ) + { + string args = String.Format( "{0}\t{1}\t ", entry.ReqSkill.ToString( "F1" ), SkillName.Ninjitsu ); + m.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + return MorphResult.NoSkill; + } + + double ninjitsu = m.Skills.Ninjitsu.Value; + + if ( ninjitsu < entry.ReqSkill + 37.5 ) + { + double chance = (ninjitsu - entry.ReqSkill) / 37.5; + + if ( chance < Utility.RandomDouble() ) + return MorphResult.Fail; + } + + m.CheckSkill( SkillName.Ninjitsu, 0.0, 37.5 ); + + BaseMount.Dismount( m ); + + m.BodyMod = entry.BodyMod; + + if ( entry.HueMod > 0 ) + m.HueMod = entry.HueMod; + + if ( entry.SpeedBoost ) + m.Send( SpeedBoost.Instantiate( true ) ); + + SkillMod mod = null; + + if ( entry.StealthBonus ) + { + mod = new DefaultSkillMod( SkillName.Stealth, true, 20.0 ); + mod.ObeyCap = true; + m.AddSkillMod( mod ); + } + + Timer timer = new AnimalFormTimer( m, entry.BodyMod, entry.HueMod ); + timer.Start(); + + AddContext( m, new AnimalFormContext( timer, mod, entry.SpeedBoost, entry.Type ) ); + return MorphResult.Success; + } + + + private static Hashtable m_Table = new Hashtable(); + + public static void AddContext( Mobile m, AnimalFormContext context ) + { + m_Table[m] = context; + + if ( context.Type == typeof( BakeKitsune ) || context.Type == typeof( GreyWolf ) ) + m.Hits += 20; + } + + public static void RemoveContext( Mobile m, bool resetGraphics ) + { + AnimalFormContext context = GetContext( m ); + + if ( context != null ) + RemoveContext( m, context, resetGraphics ); + } + + public static void RemoveContext( Mobile m, AnimalFormContext context, bool resetGraphics ) + { + m_Table.Remove( m ); + + if ( context.SpeedBoost ) + m.Send( SpeedBoost.Instantiate( false ) ); + + SkillMod mod = context.Mod; + + if ( mod != null ) + m.RemoveSkillMod( mod ); + + if ( resetGraphics ) + { + m.HueMod = -1; + m.BodyMod = 0; + } + + context.Timer.Stop(); + } + + public static AnimalFormContext GetContext( Mobile m ) + { + return ( m_Table[m] as AnimalFormContext ); + } + + public static bool UnderTransformation( Mobile m ) + { + return ( GetContext( m ) != null ); + } + + public static bool UnderTransformation( Mobile m, Type type ) + { + AnimalFormContext context = GetContext( m ); + + return ( context != null && context.Type == type ); + } +/* + private delegate void AnimalFormCallback( Mobile from ); + private delegate bool AnimalFormRequirementCallback( Mobile from ); + * */ + + public class AnimalFormEntry + { + private Type m_Type; + private TextDefinition m_Name; + private int m_ItemID; + private int m_Hue; + private int m_Tooltip; + private double m_ReqSkill; + private int m_BodyMod; + private int m_HueMod; + private bool m_StealthBonus; + private bool m_SpeedBoost; + + public Type Type{ get{ return m_Type; } } + public TextDefinition Name{ get{ return m_Name; } } + public int ItemID{ get{ return m_ItemID; } } + public int Hue{ get{ return m_Hue; } } + public int Tooltip{ get{ return m_Tooltip; } } + public double ReqSkill{ get{ return m_ReqSkill; } } + public int BodyMod{ get{ return m_BodyMod; } } + public int HueMod{ get{ return m_HueMod; } } + public bool StealthBonus{ get{ return m_StealthBonus; } } + public bool SpeedBoost{ get{ return m_SpeedBoost; } } + /* + private AnimalFormCallback m_TransformCallback; + private AnimalFormCallback m_UntransformCallback; + private AnimalFormRequirementCallback m_RequirementCallback; + * */ + + public AnimalFormEntry( Type type, TextDefinition name, int itemID, int hue, int tooltip, double reqSkill, int bodyMod, bool stealthBonus, bool speedBoost ) + : this( type, name, itemID, hue, tooltip, reqSkill, bodyMod, 0, stealthBonus, speedBoost ) + { + } + + public AnimalFormEntry( Type type, TextDefinition name, int itemID, int hue, int tooltip, double reqSkill, int bodyMod, int hueMod, bool stealthBonus, bool speedBoost ) + { + m_Type = type; + m_Name = name; + m_ItemID = itemID; + m_Hue = hue; + m_Tooltip = tooltip; + m_ReqSkill = reqSkill; + m_BodyMod = bodyMod; + m_HueMod = hueMod; + m_StealthBonus = stealthBonus; + m_SpeedBoost = speedBoost; + } + } + + private static AnimalFormEntry[] m_Entries = new AnimalFormEntry[] + { + new AnimalFormEntry( typeof( Kirin ), 1029632, 9632, 0, 1070811, 100.0, 0x84, false, true ), + new AnimalFormEntry( typeof( Unicorn ), 1018214, 9678, 0, 1070812, 100.0, 0x7A, false, true ), + new AnimalFormEntry( typeof( BakeKitsune ), 1030083, 10083, 0, 1070810, 82.5, 0xF6, false, true ), + new AnimalFormEntry( typeof( GreyWolf ), 1028482, 9681, 2309, 1070810, 82.5, 0x19, false, true ), + new AnimalFormEntry( typeof( Llama ), 1028438, 8438, 0, 1070809, 70.0, 0xDC, false, true ), + new AnimalFormEntry( typeof( ForestOstard ), 1018273, 8503, 2212, 1070809, 70.0, 0xDA, false, true ), + new AnimalFormEntry( typeof( BullFrog ), 1028496, 8496, 2003, 1070807, 50.0, 0x51, 0x5A3, false, false ), + new AnimalFormEntry( typeof( GiantSerpent ), 1018114, 9663, 2009, 1070808, 50.0, 0x15, false, false ), + new AnimalFormEntry( typeof( Dog ), 1018280, 8476, 2309, 1070806, 40.0, 0xD9, false, false ), + new AnimalFormEntry( typeof( Cat ), 1018264, 8475, 2309, 1070806, 40.0, 0xC9, false, false ), + new AnimalFormEntry( typeof( Rat ), 1018294, 8483, 2309, 1070805, 20.0, 0xEE, true, false ), + new AnimalFormEntry( typeof( Rabbit ), 1028485, 8485, 2309, 1070805, 20.0, 0xCD, true, false ) + }; + + public static AnimalFormEntry[] Entries{ get{ return m_Entries; } } + + public class AnimalFormGump : Gump + { + + //TODO: Convert this for ML to the BaseImageTileButtonsgump + private Mobile m_Caster; + private AnimalForm m_Spell; + + public AnimalFormGump( Mobile caster, AnimalFormEntry[] entries, AnimalForm spell ) : base( 50, 50 ) + { + m_Caster = caster; + m_Spell = spell; + + AddPage( 0 ); + + AddBackground( 0, 0, 408, 298, 0x13BE ); + AddBackground( 4, 28, 400, 240, 0xBB8 ); + + AddHtmlLocalized( 4, 4, 400, 20, 1063394, 0x0, false, false ); //
Animal Form Selection Menu
+ + AddButton( 25, 272, 0xFA5, 0xFA7, 1, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 60, 274, 150, 20, 1011036, 0x0, false, false ); // OKAY + + AddButton( 285, 272, 0xFA5, 0xFA7, 0, GumpButtonType.Reply, 0 ); + AddHtmlLocalized( 320, 274, 150, 20, 1011012, 0x0, false, false ); // CANCEL + + double ninjitsu = caster.Skills.Ninjitsu.Value; + + for ( int i = 0; i < entries.Length; ++i ) + { + bool enabled = ( ninjitsu >= entries[i].ReqSkill ); + + int x = 100 * ( i % 4 ); + int y = 80 * ( i / 4 ); + + TextDefinition.AddHtmlText( this, 10 + x, 30 + y, 100, 18, entries[i].Name, false, false ); + + if ( enabled ) + { + AddRadio( 10 + x, 50 + y, 0xD2, 0xD3, false, 100 + i ); + AddItem( 30 + x, 50 + y, entries[i].ItemID, entries[i].Hue ); + } + else + AddItem( 10 + x, 50 + y, entries[i].ItemID, 0x3E3 ); + + AddTooltip( enabled ? entries[i].Tooltip : 1070708 ); + } + } + + public override void OnResponse( NetState sender, RelayInfo info ) + { + if ( info.ButtonID == 1 && info.Switches.Length > 0 ) + { + int entryID = info.Switches[0] - 100; + + if ( AnimalForm.Morph( m_Caster, entryID ) == MorphResult.Fail ) + { + m_Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 502632 ); // The spell fizzles. + m_Caster.FixedParticles( 0x3735, 1, 30, 9503, EffectLayer.Waist ); + m_Caster.PlaySound( 0x5C ); + } + } + } + } + } + + public class AnimalFormContext + { + private Timer m_Timer; + private SkillMod m_Mod; + private bool m_SpeedBoost; + private Type m_Type; + + public Timer Timer{ get{ return m_Timer; } } + public SkillMod Mod{ get{ return m_Mod; } } + public bool SpeedBoost{ get{ return m_SpeedBoost; } } + public Type Type{ get{ return m_Type; } } + + public AnimalFormContext( Timer timer, SkillMod mod, bool speedBoost, Type type ) + { + m_Timer = timer; + m_Mod = mod; + m_SpeedBoost = speedBoost; + m_Type = type; + } + } + + public class AnimalFormTimer : Timer + { + private Mobile m_Mobile; + private int m_Body; + private int m_Hue; + + public AnimalFormTimer( Mobile from, int body, int hue ) : base( TimeSpan.FromSeconds( 1.0 ), TimeSpan.FromSeconds( 1.0 ) ) + { + m_Mobile = from; + m_Body = body; + m_Hue = hue; + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Mobile.Deleted || !m_Mobile.Alive || m_Mobile.Body != m_Body || (m_Hue != 0 && m_Mobile.Hue != m_Hue) ) + { + AnimalForm.RemoveContext( m_Mobile, true ); + Stop(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/Backstab.cs b/Scripts/Spells/Ninjitsu/Backstab.cs new file mode 100644 index 000000000..5a8e9edac --- /dev/null +++ b/Scripts/Spells/Ninjitsu/Backstab.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; +using Server.SkillHandlers; + +namespace Server.Spells.Ninjitsu +{ + public class Backstab : NinjaMove + { + // TODO: Cannot hide for 5s + + public Backstab() + { + } + + public override int BaseMana{ get{ return 30; } } + public override double RequiredSkill{ get{ return 20.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063089 ); } } // You prepare to Backstab your opponent. + + public override double GetDamageScalar( Mobile attacker, Mobile defender ) + { + double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value; + + return 1.0 + (ninjitsu / 360) + Tracking.GetStalkingBonus( attacker, defender ) / 100; + } + + public override bool Validate( Mobile from ) + { + if( !from.Hidden || from.AllowedStealthSteps <= 0 ) + { + from.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability. + return false; + } + + return base.Validate( from ); + } + + public override bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + return Validate( attacker ) && CheckMana( attacker, true ); + } + + public override bool ValidatesDuringHit { get { return false; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + //Validates before swing + + ClearCurrentMove( attacker ); + + attacker.SendLocalizedMessage( 1063090 ); // You quickly stab your opponent as you come out of hiding! + + defender.FixedParticles( 0x37B9, 1, 5, 0x251D, 0x651, 0, EffectLayer.Waist ); + + attacker.RevealingAction(); + + CheckGain( attacker ); + } + + public override void OnMiss( Mobile attacker, Mobile defender ) + { + ClearCurrentMove( attacker ); + + attacker.SendLocalizedMessage( 1063161 ); // You failed to properly use the element of surprise. + + attacker.RevealingAction(); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/DeathStrike.cs b/Scripts/Spells/Ninjitsu/DeathStrike.cs new file mode 100644 index 000000000..e96f68139 --- /dev/null +++ b/Scripts/Spells/Ninjitsu/DeathStrike.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; +using Server.SkillHandlers; + +namespace Server.Spells.Ninjitsu +{ + public class DeathStrike : NinjaMove + { + public DeathStrike() + { + } + + public override int BaseMana { get { return 30; } } + public override double RequiredSkill { get { return 85.0; } } + + public override TextDefinition AbilityMessage { get { return new TextDefinition( 1063091 ); } } // You prepare to hit your opponent with a Death Strike. + + public override double GetDamageScalar( Mobile attacker, Mobile defender ) + { + return 0.5; + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + ClearCurrentMove( attacker ); + + double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value; + + double chance; + + if( ninjitsu < 100 ) //This formula is an approximation from OSI data. TODO: find correct formula + chance = 30 + (ninjitsu - 85) * 2.2; + else + chance = 63 + (ninjitsu - 100) * 1.1; + + if( (chance / 100) < Utility.RandomDouble() ) + { + attacker.SendLocalizedMessage( 1070779 ); // You missed your opponent with a Death Strike. + return; + } + + + DeathStrikeInfo info; + + int damageBonus = 0; + + if( m_Table.Contains( defender ) ) + { + attacker.SendLocalizedMessage( 1063092 ); // Your opponent lands another Death Strike! + + info = (DeathStrikeInfo)m_Table[defender]; + + if( info.m_Steps > 0 ) + damageBonus = info.m_Attacker.Skills[SkillName.Ninjitsu].Fixed / 150; + + if( info.m_Timer != null ) + info.m_Timer.Stop(); + + m_Table.Remove( defender ); + } + else + { + attacker.SendLocalizedMessage( 1063094 ); // You inflict a Death Strike upon your opponent! + defender.SendLocalizedMessage( 1063093 ); // You have been hit by a Death Strike! Move with caution! + } + + defender.FixedParticles( 0x374A, 1, 17, 0x26BC, EffectLayer.Waist ); + attacker.PlaySound( 0x50D ); + + info = new DeathStrikeInfo( defender, attacker, damageBonus ); + info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 5.0 ), new TimerStateCallback( ProcessDeathStrike ), info ); + + m_Table[defender] = info; + + CheckGain( attacker ); + } + + + private static Hashtable m_Table = new Hashtable(); + + private class DeathStrikeInfo + { + public Mobile m_Target; + public Mobile m_Attacker; + public int m_Steps; + public int m_DamageBonus; + public Timer m_Timer; + + public DeathStrikeInfo( Mobile target, Mobile attacker, int damageBonus ) + { + m_Target = target; + m_Attacker = attacker; + m_DamageBonus = damageBonus; + } + } + + public static void AddStep( Mobile m ) + { + DeathStrikeInfo info = m_Table[m] as DeathStrikeInfo; + + if( info == null ) + return; + + info.m_Steps++; + } + + private static void ProcessDeathStrike( object state ) + { + DeathStrikeInfo info = (DeathStrikeInfo)state; + + double ninjitsu = info.m_Attacker.Skills[SkillName.Ninjitsu].Fixed; + int divisor = (info.m_Steps >= 5) ? 30 : 80; + + double baseDamage = ninjitsu / divisor; + double stalkingBonus = Tracking.GetStalkingBonus( info.m_Attacker, info.m_Target ); + + int maxDamage = (info.m_Steps >= 5) ? 62 : 22; + + int damage = Math.Max( 0, Math.Min( maxDamage, (int)(baseDamage + stalkingBonus) ) ); + + // This bonus is 8 at most. That brings the cap up to 70/30. + damage += info.m_DamageBonus; + + // Damage is direct. + //info.m_Target.Damage( damage, info.m_Attacker ); + + AOS.Damage( info.m_Target, info.m_Attacker, damage, true, 100, 0, 0, 0, 0 ); + m_Table.Remove( info.m_Target ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/FocusAttack.cs b/Scripts/Spells/Ninjitsu/FocusAttack.cs new file mode 100644 index 000000000..c12338709 --- /dev/null +++ b/Scripts/Spells/Ninjitsu/FocusAttack.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Ninjitsu +{ + public class FocusAttack : NinjaMove + { + // TODO: Display property bonus on equipped weapons. + + public FocusAttack() + { + } + + public override int BaseMana{ get{ return 20; } } + public override double RequiredSkill{ get{ return 60.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063095 ); } } // You prepare to focus all of your abilities into your next strike. + + public override bool Validate( Mobile from ) + { + if ( from.FindItemOnLayer( Layer.TwoHanded ) as BaseShield != null ) + { + from.SendLocalizedMessage( 1063096 ); // You cannot use this ability while holding a shield. + return false; + } + + Item handOne = from.FindItemOnLayer( Layer.OneHanded ) as BaseWeapon; + + if ( handOne != null && !(handOne is BaseRanged) ) + return base.Validate( from ); + + Item handTwo = from.FindItemOnLayer( Layer.TwoHanded ) as BaseWeapon; + + if ( handTwo != null && !(handTwo is BaseRanged) ) + return base.Validate( from ); + + from.SendLocalizedMessage( 1063097 ); // You must be wielding a melee weapon without a shield to use this ability. + return false; + } + + public override double GetDamageScalar( Mobile attacker, Mobile defender ) + { + double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value; + + return 1.0 + (ninjitsu * ninjitsu) / 43636; + } + + public override double GetPropertyBonus( Mobile attacker ) + { + double ninjitsu = attacker.Skills[SkillName.Ninjitsu].Value; + + double bonus = (ninjitsu * ninjitsu) / 43636; + + return 1.0 + (bonus * 3 + 0.01); + } + + public override bool OnBeforeDamage( Mobile attacker, Mobile defender ) + { + return Validate( attacker ) && CheckMana( attacker, true ); + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + ClearCurrentMove( attacker ); + + attacker.SendLocalizedMessage( 1063098 ); // You focus all of your abilities and strike with deadly force! + + CheckGain( attacker ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/KiAttack.cs b/Scripts/Spells/Ninjitsu/KiAttack.cs new file mode 100644 index 000000000..1981d9b2f --- /dev/null +++ b/Scripts/Spells/Ninjitsu/KiAttack.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Ninjitsu +{ + public class KiAttack : NinjaMove + { + public KiAttack() + { + } + + public override int BaseMana{ get{ return 25; } } + public override double RequiredSkill{ get{ return 80.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063099 ); } } // Your Ki Attack must be complete within 2 seconds for the damage bonus! + + public override void OnUse( Mobile from ) + { + if ( !Validate( from ) ) + return; + + KiAttackInfo info = new KiAttackInfo( from ); + info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 2.0 ), new TimerStateCallback( EndKiAttack ), info ); + + m_Table[from] = info; + } + + public override bool Validate( Mobile from ) + { + if ( from.Hidden && from.AllowedStealthSteps > 0 ) + { + from.SendLocalizedMessage( 1063127 ); // You cannot use this ability while in stealth mode. + return false; + } + + return base.Validate( from ); + } + + public override double GetDamageScalar( Mobile attacker, Mobile defender ) + { + if ( attacker.Hidden ) + return 1.0; + + return 1.0 + GetBonus( attacker ) / 10; + } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + if ( !Validate( attacker ) || !CheckMana( attacker, true ) ) + return; + + if ( GetBonus( attacker ) == 0.0 ) + { + attacker.SendLocalizedMessage( 1063101 ); // You were too close to your target to cause any additional damage. + } + else + { + attacker.SendLocalizedMessage( 1063100 ); // Your quick flight to your target causes extra damage as you strike! + defender.FixedParticles( 0x37BE, 1, 5, 0x26BD, 0, 0x1, EffectLayer.Waist ); + } + + ClearCurrentMove( attacker ); + } + + public override void OnClearMove( Mobile from ) + { + KiAttackInfo info = m_Table[from] as KiAttackInfo; + + if ( info != null ) + { + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + m_Table.Remove( info.m_Mobile ); + } + } + + private static Hashtable m_Table = new Hashtable(); + + public static double GetBonus( Mobile from ) + { + KiAttackInfo info = m_Table[from] as KiAttackInfo; + + if ( info == null ) + return 0.0; + + int xDelta = info.m_Location.X - from.X; + int yDelta = info.m_Location.Y - from.Y; + + double bonus = Math.Sqrt( (xDelta * xDelta) + (yDelta * yDelta) ); + + if ( bonus > 20.0 ) + bonus = 20.0; + + return bonus; + } + + private class KiAttackInfo + { + public Mobile m_Mobile; + public Point3D m_Location; + public Timer m_Timer; + + public KiAttackInfo( Mobile m ) + { + m_Mobile = m; + m_Location = m.Location; + } + } + + private static void EndKiAttack( object state ) + { + KiAttackInfo info = (KiAttackInfo)state; + + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + ClearCurrentMove( info.m_Mobile ); + info.m_Mobile.SendLocalizedMessage( 1063102 ); // You failed to complete your Ki Attack in time. + + m_Table.Remove( info.m_Mobile ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/MirrorImage.cs b/Scripts/Spells/Ninjitsu/MirrorImage.cs new file mode 100644 index 000000000..fac6c494d --- /dev/null +++ b/Scripts/Spells/Ninjitsu/MirrorImage.cs @@ -0,0 +1,266 @@ +using System; +using System.Collections; +using Server; +using Server.Spells; +using Server.Spells.Necromancy; +using Server.Mobiles; +using Server.Items; +using Server.Spells.Ninjitsu; + +namespace Server.Spells.Ninjitsu +{ + public class MirrorImage : NinjaSpell + { + private static Hashtable m_CloneCount = new Hashtable(); + + public static bool HasClone( Mobile m ) + { + return (m_CloneCount.Contains( m ) && ((int)m_CloneCount[m]) > 0); + } + + public static void AddClone( Mobile m ) + { + if( m == null ) + return; + + if( m_CloneCount.Contains( m ) ) + m_CloneCount[m] = ((int)m_CloneCount[m] +1); + else + m_CloneCount.Add( m, 1 ); + } + + public static void RemoveClone( Mobile m ) + { + if( m == null ) + return; + + if( m_CloneCount.Contains( m ) ) + { + m_CloneCount[m] = ((int)m_CloneCount[m] -1); + + if( ((int)m_CloneCount[m]) <= 0 ) + m_CloneCount.Remove( m ); + } + } + + private static SpellInfo m_Info = new SpellInfo( + "Mirror Image", null, + SpellCircle.Sixth, // 1.5s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 40.0; } } + public override int RequiredMana{ get{ return 10; } } + + public override bool BlockedByAnimalForm{ get{ return false; } } + + public MirrorImage( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( Caster.Mounted ) + { + Caster.SendLocalizedMessage( 1063132 ); // You cannot use this ability while mounted. + return false; + } + else if ( (Caster.Followers + 1) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1063133 ); // You cannot summon a mirror image because you have too many followers. + return false; + } + else if ( Necromancy.TransformationSpell.UnderTransformation( Caster, typeof( HorrificBeastSpell ) ) ) + { + Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form. + return false; + } + + return base.CheckCast(); + } + + public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable ) + { + return false; + } + + public override void OnBeginCast() + { + base.OnBeginCast(); + + Caster.SendLocalizedMessage( 1063134 ); // You begin to summon a mirror image of yourself. + } + + public override void OnCast() + { + if ( Caster.Mounted ) + { + Caster.SendLocalizedMessage( 1063132 ); // You cannot use this ability while mounted. + } + else if ( (Caster.Followers + 1) > Caster.FollowersMax ) + { + Caster.SendLocalizedMessage( 1063133 ); // You cannot summon a mirror image because you have too many followers. + } + else if ( Necromancy.TransformationSpell.UnderTransformation( Caster, typeof( HorrificBeastSpell ) ) ) + { + Caster.SendLocalizedMessage( 1061091 ); // You cannot cast that spell in this form. + } + else if ( CheckSequence() ) + { + Caster.FixedParticles( 0x376A, 1, 14, 0x13B5, EffectLayer.Waist ); + Caster.PlaySound( 0x511 ); + + new Clone( Caster ).MoveToWorld( Caster.Location, Caster.Map ); + } + + FinishSequence(); + } + } +} + +namespace Server.Mobiles +{ + public class Clone : BaseCreature + { + private Mobile m_Caster; + + public Clone( Mobile caster ) : base( AIType.AI_Melee, FightMode.None, 10, 1, 0.2, 0.4 ) + { + m_Caster = caster; + + Body = caster.Body; + + Hue = caster.Hue; + Female = caster.Female; + + Name = caster.Name; + NameHue = caster.NameHue; + + Title = caster.Title; + Kills = caster.Kills; + + HairItemID = caster.HairItemID; + HairHue = caster.HairHue; + + FacialHairItemID = caster.FacialHairItemID; + FacialHairHue = caster.FacialHairHue; + + for ( int i = 0; i < caster.Skills.Length; ++i ) + { + Skills[i].Base = caster.Skills[i].Base; + Skills[i].Cap = caster.Skills[i].Cap; + } + + for( int i = 0; i < caster.Items.Count; i++ ) + { + AddItem( CloneItem( caster.Items[i] ) ); + } + + Warmode = true; + + Summoned = true; + SummonMaster = caster; + + ControlOrder = OrderType.Follow; + ControlTarget = caster; + + TimeSpan duration = TimeSpan.FromSeconds( 30 + caster.Skills.Ninjitsu.Fixed / 40 ); + + new UnsummonTimer( caster, this, duration ).Start(); + SummonEnd = DateTime.Now + duration; + + MirrorImage.AddClone( m_Caster ); + } + + protected override BaseAI ForcedAI { get { return new CloneAI( this ); } } + + public override bool IsHumanInTown() { return false; } + + private Item CloneItem( Item item ) + { + Item newItem = new Item( item.ItemID ); + newItem.Hue = item.Hue; + newItem.Layer = item.Layer; + + return newItem; + } + + public override void OnDamage( int amount, Mobile from, bool willKill ) + { + Delete(); + } + + public override bool DeleteCorpseOnDeath { get { return true; } } + + public override void OnDelete() + { + Effects.SendLocationParticles( EffectItem.Create( Location, Map, EffectItem.DefaultDuration ), 0x3728, 10, 15, 5042 ); + + base.OnDelete(); + } + + public override void OnAfterDelete() + { + MirrorImage.RemoveClone( m_Caster ); + base.OnAfterDelete(); + } + + public override bool IsDispellable { get { return false; } } + public override bool Commandable { get { return false; } } + + public Clone( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.WriteEncodedInt( 0 ); // version + + writer.Write( m_Caster ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadEncodedInt(); + + m_Caster = reader.ReadMobile(); + + MirrorImage.AddClone( m_Caster ); + } + } +} + + +namespace Server.Mobiles +{ + public class CloneAI : BaseAI + { + public CloneAI( Clone m ) : base ( m ) + { + m.CurrentSpeed = m.ActiveSpeed; + } + + public override bool Think() + { + // Clones only follow their owners + Mobile master = m_Mobile.SummonMaster; + + if ( master != null && master.Map == m_Mobile.Map && master.InRange( m_Mobile, m_Mobile.RangePerception ) ) + { + int iCurrDist = (int)m_Mobile.GetDistanceToSqrt( master ); + bool bRun = (iCurrDist > 5); + + WalkMobileRange( master, 2, bRun, 0, 1 ); + } + else + WalkRandom( 2, 2, 1 ); + + return true; + } + } +} diff --git a/Scripts/Spells/Ninjitsu/NinjaMove.cs b/Scripts/Spells/Ninjitsu/NinjaMove.cs new file mode 100644 index 000000000..d4ea196f7 --- /dev/null +++ b/Scripts/Spells/Ninjitsu/NinjaMove.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Mobiles; +using Server.Network; + +namespace Server.Spells +{ + public class NinjaMove : SpecialMove + { + public override SkillName MoveSkill{ get{ return SkillName.Ninjitsu; } } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/NinjaSpell.cs b/Scripts/Spells/Ninjitsu/NinjaSpell.cs new file mode 100644 index 000000000..da39d58fd --- /dev/null +++ b/Scripts/Spells/Ninjitsu/NinjaSpell.cs @@ -0,0 +1,102 @@ +using System; +using Server; +using Server.Spells; +using Server.Network; +using Server.Items; +using Server.Mobiles; + +namespace Server.Spells.Ninjitsu +{ + public abstract class NinjaSpell : Spell + { + public abstract double RequiredSkill{ get; } + public abstract int RequiredMana{ get; } + + public override SkillName CastSkill{ get{ return SkillName.Ninjitsu; } } + + public override bool RevealOnCast{ get{ return false; } } + public override bool ClearHandsOnCast{ get{ return false; } } + public override bool ShowHandMovement{ get{ return false; } } + + public override bool BlocksMovement{ get{ return false; } } + + public override int CastDelayBase{ get{ return 1; } } + + public override int CastRecoveryBase{ get{ return 7; } } + + public NinjaSpell( Mobile caster, Item scroll, SpellInfo info ) : base( caster, scroll, info ) + { + } + + public static bool CheckExpansion( Mobile from ) + { + if ( !( from is PlayerMobile ) ) + return true; + + if ( from.NetState == null ) + return false; + + return ( (from.NetState.Flags & 0x10) != 0 ); + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + if ( !CheckExpansion( Caster ) ) + { + Caster.SendLocalizedMessage( 1063456 ); // You must upgrade to Samurai Empire in order to use that ability. + return false; + } + + if ( Caster.Skills[CastSkill].Value < RequiredSkill ) + { + string args = String.Format( "{0}\t{1}\t ", RequiredSkill.ToString( "F1" ), CastSkill.ToString() ); + Caster.SendLocalizedMessage( 1063013, args ); // You need at least ~1_SKILL_REQUIREMENT~ ~2_SKILL_NAME~ skill to use that ability. + return false; + } + else if ( Caster.Mana < ScaleMana( RequiredMana ) ) + { + Caster.SendLocalizedMessage( 1060174, RequiredMana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + return false; + } + + return true; + } + + public override bool CheckFizzle() + { + int mana = ScaleMana( RequiredMana ); + + if ( Caster.Skills[CastSkill].Value < RequiredSkill ) + { + Caster.SendLocalizedMessage( 1063352, RequiredSkill.ToString( "F1" ) ); // You need ~1_SKILL_REQUIREMENT~ Ninjitsu skill to perform that attack! + return false; + } + else if ( Caster.Mana < mana ) + { + Caster.SendLocalizedMessage( 1060174, mana.ToString() ); // You must have at least ~1_MANA_REQUIREMENT~ Mana to use this ability. + return false; + } + + if ( !base.CheckFizzle() ) + return false; + + Caster.Mana -= mana; + + return true; + } + + public override void GetCastSkills( out double min, out double max ) + { + min = RequiredSkill; + max = RequiredSkill + 37.5; + } + + public override int GetMana() + { + return 0; + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/ShadowJump.cs b/Scripts/Spells/Ninjitsu/ShadowJump.cs new file mode 100644 index 000000000..4110db2fd --- /dev/null +++ b/Scripts/Spells/Ninjitsu/ShadowJump.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; + +namespace Server.Spells.Ninjitsu +{ + public class Shadowjump : NinjaSpell + { + private static SpellInfo m_Info = new SpellInfo( + "Shadowjump", null, + SpellCircle.Fourth, // 1.0s base cast delay + -1, + 9002 + ); + + public override double RequiredSkill{ get{ return 50.0; } } + public override int RequiredMana{ get{ return 15; } } + + public override bool BlockedByAnimalForm{ get{ return false; } } + + public Shadowjump( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( !Caster.Hidden || Caster.AllowedStealthSteps <= 0 ) + { + Caster.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability. + return false; + } + + return base.CheckCast(); + } + + public override bool CheckDisturb( DisturbType type, bool firstCircle, bool resistable ) + { + return false; + } + + public override void OnCast() + { + Caster.SendLocalizedMessage( 1063088 ); // You prepare to perform a Shadowjump. + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + IPoint3D orig = p; + Map map = Caster.Map; + + SpellHelper.GetSurfaceTop( ref p ); + + if ( !Caster.Hidden || Caster.AllowedStealthSteps <= 0 ) + { + Caster.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability. + } + else if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + } + else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.TeleportFrom ) || !SpellHelper.CheckTravel( Caster, map, new Point3D( p ), TravelCheckType.TeleportTo )) + { + } + else if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) ) + { + Caster.SendLocalizedMessage( 502831 ); // Cannot teleport to that spot. + } + else if ( SpellHelper.CheckMulti( new Point3D( p ), map ) ) + { + // TODO: Cannot shadowjump within 5 tiles from any house. + Caster.SendLocalizedMessage( 502831 ); // Cannot teleport to that spot. + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, orig ); + + Mobile m = Caster; + + Point3D from = m.Location; + Point3D to = new Point3D( p ); + + m.Location = to; + m.ProcessDelta(); + + Effects.SendLocationParticles( EffectItem.Create( from, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + + m.PlaySound( 0x512 ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private Shadowjump m_Owner; + + public InternalTarget( Shadowjump owner ) : base( 11, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + + if ( !from.CheckSkill( SkillName.Hiding, 0.0, 100.0 ) ) //TODO: Hiding check or stealth check? + from.RevealingAction(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Ninjitsu/SurpriseAttack.cs b/Scripts/Spells/Ninjitsu/SurpriseAttack.cs new file mode 100644 index 000000000..f816cea2b --- /dev/null +++ b/Scripts/Spells/Ninjitsu/SurpriseAttack.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Mobiles; +using Server.Targeting; +using Server.SkillHandlers; + +namespace Server.Spells.Ninjitsu +{ + public class SurpriseAttack : NinjaMove + { + // TODO: Cannot hide for 5s + + public SurpriseAttack() + { + } + + public override int BaseMana{ get{ return 20; } } + public override double RequiredSkill{ get{ return 30.0; } } + + public override TextDefinition AbilityMessage{ get{ return new TextDefinition( 1063128 ); } } // You prepare to surprise your prey. + + public override bool Validate( Mobile from ) + { + if( !from.Hidden || from.AllowedStealthSteps <= 0 ) + { + from.SendLocalizedMessage( 1063087 ); // You must be in stealth mode to use this ability. + return false; + } + + return base.Validate( from ); + } + + public override bool OnBeforeSwing( Mobile attacker, Mobile defender ) + { + return Validate( attacker ) && CheckMana( attacker, true ); + } + + public override bool ValidatesDuringHit { get { return false; } } + + public override void OnHit( Mobile attacker, Mobile defender, int damage ) + { + //Validates before swing + + ClearCurrentMove( attacker ); + + attacker.SendLocalizedMessage( 1063129 ); // You catch your opponent off guard with your Surprise Attack! + defender.SendLocalizedMessage( 1063130 ); // Your defenses are lowered as your opponent surprises you! + + defender.FixedParticles( 0x37B9, 1, 5, 0x26DA, 0, 3, EffectLayer.Head ); + + attacker.RevealingAction(); + + SurpriseAttackInfo info; + + if ( m_Table.Contains( defender ) ) + { + info = (SurpriseAttackInfo)m_Table[defender]; + + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + m_Table.Remove( defender ); + } + + int ninjitsu = attacker.Skills[SkillName.Ninjitsu].Fixed; + + int malus = ninjitsu / 60 + (int)Tracking.GetStalkingBonus( attacker, defender ); + + info = new SurpriseAttackInfo( defender, malus ); + info.m_Timer = Timer.DelayCall( TimeSpan.FromSeconds( 8.0 ), new TimerStateCallback( EndSurprise ), info ); + + m_Table[defender] = info; + + CheckGain( attacker ); + } + + public override void OnMiss( Mobile attacker, Mobile defender ) + { + ClearCurrentMove( attacker ); + + attacker.SendLocalizedMessage( 1063161 ); // You failed to properly use the element of surprise. + + attacker.RevealingAction(); + } + + + private static Hashtable m_Table = new Hashtable(); + + public static bool GetMalus( Mobile target, ref int malus ) + { + SurpriseAttackInfo info = m_Table[target] as SurpriseAttackInfo; + + if ( info == null ) + return false; + + malus = info.m_Malus; + return true; + } + + private class SurpriseAttackInfo + { + public Mobile m_Target; + public int m_Malus; + public Timer m_Timer; + + public SurpriseAttackInfo( Mobile target, int effect ) + { + m_Target = target; + m_Malus = effect; + } + } + + private static void EndSurprise( object state ) + { + SurpriseAttackInfo info = (SurpriseAttackInfo)state; + + if ( info.m_Timer != null ) + info.m_Timer.Stop(); + + info.m_Target.SendLocalizedMessage( 1063131 ); // Your defenses have returned to normal. + + m_Table.Remove( info.m_Target ); + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Reagent.cs b/Scripts/Spells/Reagent.cs new file mode 100644 index 000000000..360d92e83 --- /dev/null +++ b/Scripts/Spells/Reagent.cs @@ -0,0 +1,108 @@ +using System; +using Server.Items; + +namespace Server.Spells +{ + public class Reagent + { + private static Type[] m_Types = new Type[13] + { + typeof( BlackPearl ), + typeof( Bloodmoss ), + typeof( Garlic ), + typeof( Ginseng ), + typeof( MandrakeRoot ), + typeof( Nightshade ), + typeof( SulfurousAsh ), + typeof( SpidersSilk ), + typeof( BatWing ), + typeof( GraveDust ), + typeof( DaemonBlood ), + typeof( NoxCrystal ), + typeof( PigIron ) + }; + + public Type[] Types + { + get{ return m_Types; } + } + + public static Type BlackPearl + { + get{ return m_Types[0]; } + set{ m_Types[0] = value; } + } + + public static Type Bloodmoss + { + get{ return m_Types[1]; } + set{ m_Types[1] = value; } + } + + public static Type Garlic + { + get{ return m_Types[2]; } + set{ m_Types[2] = value; } + } + + public static Type Ginseng + { + get{ return m_Types[3]; } + set{ m_Types[3] = value; } + } + + public static Type MandrakeRoot + { + get{ return m_Types[4]; } + set{ m_Types[4] = value; } + } + + public static Type Nightshade + { + get{ return m_Types[5]; } + set{ m_Types[5] = value; } + } + + public static Type SulfurousAsh + { + get{ return m_Types[6]; } + set{ m_Types[6] = value; } + } + + public static Type SpidersSilk + { + get{ return m_Types[7]; } + set{ m_Types[7] = value; } + } + + public static Type BatWing + { + get{ return m_Types[8]; } + set{ m_Types[8] = value; } + } + + public static Type GraveDust + { + get{ return m_Types[9]; } + set{ m_Types[9] = value; } + } + + public static Type DaemonBlood + { + get{ return m_Types[10]; } + set{ m_Types[10] = value; } + } + + public static Type NoxCrystal + { + get{ return m_Types[11]; } + set{ m_Types[11] = value; } + } + + public static Type PigIron + { + get{ return m_Types[12]; } + set{ m_Types[12] = value; } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/Agility.cs b/Scripts/Spells/Second/Agility.cs new file mode 100644 index 000000000..65910f94a --- /dev/null +++ b/Scripts/Spells/Second/Agility.cs @@ -0,0 +1,74 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Second +{ + public class AgilitySpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Agility", "Ex Uus", + SpellCircle.Second, + 212, + 9061, + Reagent.Bloodmoss, + Reagent.MandrakeRoot + ); + + public AgilitySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.AddStatBonus( Caster, m, StatType.Dex ); + + m.FixedParticles( 0x375A, 10, 15, 5010, EffectLayer.Waist ); + m.PlaySound( 0x28E ); + + int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, false )*100); + TimeSpan length = SpellHelper.GetDuration( Caster, m ); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Agility, 1075841, length, m, percentage.ToString() ) ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private AgilitySpell m_Owner; + + public InternalTarget( AgilitySpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/Cunning.cs b/Scripts/Spells/Second/Cunning.cs new file mode 100644 index 000000000..5fdb73a1f --- /dev/null +++ b/Scripts/Spells/Second/Cunning.cs @@ -0,0 +1,74 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Second +{ + public class CunningSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Cunning", "Uus Wis", + SpellCircle.Second, + 212, + 9061, + Reagent.MandrakeRoot, + Reagent.Nightshade + ); + + public CunningSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.AddStatBonus( Caster, m, StatType.Int ); + + m.FixedParticles( 0x375A, 10, 15, 5011, EffectLayer.Head ); + m.PlaySound( 0x1EB ); + + int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, false )*100); + TimeSpan length = SpellHelper.GetDuration( Caster, m ); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Cunning, 1075843, length, m, percentage.ToString() ) ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private CunningSpell m_Owner; + + public InternalTarget( CunningSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/Cure.cs b/Scripts/Spells/Second/Cure.cs new file mode 100644 index 000000000..754a97994 --- /dev/null +++ b/Scripts/Spells/Second/Cure.cs @@ -0,0 +1,90 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Second +{ + public class CureSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Cure", "An Nox", + SpellCircle.Second, + 212, + 9061, + Reagent.Garlic, + Reagent.Ginseng + ); + + public CureSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + Poison p = m.Poison; + + if ( p != null ) + { + int chanceToCure = 10000 + (int)(Caster.Skills[SkillName.Magery].Value * 75) - ((p.Level + 1) * (Core.AOS ? (p.Level < 4 ? 3300 : 3100) : 1750)); + chanceToCure /= 100; + + if ( chanceToCure > Utility.Random( 100 ) ) + { + if ( m.CurePoison( Caster ) ) + { + if ( Caster != m ) + Caster.SendLocalizedMessage( 1010058 ); // You have cured the target of all poisons! + + m.SendLocalizedMessage( 1010059 ); // You have been cured of all poisons. + } + } + else + { + m.SendLocalizedMessage( 1010060 ); // You have failed to cure your target! + } + } + + m.FixedParticles( 0x373A, 10, 15, 5012, EffectLayer.Waist ); + m.PlaySound( 0x1E0 ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private CureSpell m_Owner; + + public InternalTarget( CureSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/Harm.cs b/Scripts/Spells/Second/Harm.cs new file mode 100644 index 000000000..1f57dbfb2 --- /dev/null +++ b/Scripts/Spells/Second/Harm.cs @@ -0,0 +1,113 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Second +{ + public class HarmSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Harm", "An Mani", + SpellCircle.Second, + 212, + Core.AOS ? 9001 : 9041, + Reagent.Nightshade, + Reagent.SpidersSilk + ); + + public HarmSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return false; } } + + + public override double GetSlayerDamageScalar( Mobile target ) + { + return 1.0; //This spell isn't affected by slayer spellbooks + } + + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + double damage; + + if ( Core.AOS ) + { + damage = GetNewAosDamage( 17, 1, 5, m ); + } + else + { + damage = Utility.Random( 1, 15 ); + + if ( CheckResisted( m ) ) + { + damage *= 0.75; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + damage *= GetDamageScalar( m ); + } + + if ( !m.InRange( Caster, 2 ) ) + damage *= 0.25; // 1/4 damage at > 2 tile range + else if ( !m.InRange( Caster, 1 ) ) + damage *= 0.50; // 1/2 damage at 2 tile range + + if ( Core.AOS ) + { + m.FixedParticles( 0x374A, 10, 30, 5013, 1153, 2, EffectLayer.Waist ); + m.PlaySound( 0x0FC ); + } + else + { + m.FixedParticles( 0x374A, 10, 15, 5013, EffectLayer.Waist ); + m.PlaySound( 0x1F1 ); + } + + SpellHelper.Damage( this, m, damage, 0, 0, 100, 0, 0 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private HarmSpell m_Owner; + + public InternalTarget( HarmSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/MagicTrap.cs b/Scripts/Spells/Second/MagicTrap.cs new file mode 100644 index 000000000..890d70631 --- /dev/null +++ b/Scripts/Spells/Second/MagicTrap.cs @@ -0,0 +1,88 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Items; + +namespace Server.Spells.Second +{ + public class MagicTrapSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Magic Trap", "In Jux", + SpellCircle.Second, + 212, + 9001, + Reagent.Garlic, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public MagicTrapSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( TrapableContainer item ) + { + if ( !Caster.CanSee( item ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( item.TrapType != TrapType.None && item.TrapType != TrapType.MagicTrap ) + { + base.DoFizzle(); + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, item ); + + item.TrapType = TrapType.MagicTrap; + item.TrapPower = Core.AOS ? Utility.RandomMinMax( 10, 50 ) : 1; + item.TrapLevel = 0; + + Point3D loc = item.GetWorldLocation(); + + Effects.SendLocationParticles( EffectItem.Create( new Point3D( loc.X + 1, loc.Y, loc.Z ), item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 9502 ); + Effects.SendLocationParticles( EffectItem.Create( new Point3D( loc.X, loc.Y - 1, loc.Z ), item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 9502 ); + Effects.SendLocationParticles( EffectItem.Create( new Point3D( loc.X - 1, loc.Y, loc.Z ), item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 9502 ); + Effects.SendLocationParticles( EffectItem.Create( new Point3D( loc.X, loc.Y + 1, loc.Z ), item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 9502 ); + Effects.SendLocationParticles( EffectItem.Create( new Point3D( loc.X, loc.Y, loc.Z ), item.Map, EffectItem.DefaultDuration ), 0, 0, 0, 5014 ); + + Effects.PlaySound( loc, item.Map, 0x1EF ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MagicTrapSpell m_Owner; + + public InternalTarget( MagicTrapSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is TrapableContainer ) + { + m_Owner.Target( (TrapableContainer)o ); + } + else + { + from.SendMessage( "You can't trap that" ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/Protection.cs b/Scripts/Spells/Second/Protection.cs new file mode 100644 index 000000000..e28c6d8bf --- /dev/null +++ b/Scripts/Spells/Second/Protection.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Second +{ + public class ProtectionSpell : Spell + { + private static Hashtable m_Registry = new Hashtable(); + public static Hashtable Registry { get { return m_Registry; } } + + private static SpellInfo m_Info = new SpellInfo( + "Protection", "Uus Sanct", + SpellCircle.Second, + 236, + 9011, + Reagent.Garlic, + Reagent.Ginseng, + Reagent.SulfurousAsh + ); + + public ProtectionSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( Core.AOS ) + return true; + + if ( m_Registry.ContainsKey( Caster ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + return false; + } + else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + return false; + } + + return true; + } + + private static Hashtable m_Table = new Hashtable(); + + public static void Toggle( Mobile caster, Mobile target ) + { + /* Players under the protection spell effect can no longer have their spells "disrupted" when hit. + * Players under the protection spell have decreased physical resistance stat value, + * a decreased "resisting spells" skill value by -35, + * and a slower casting speed modifier (technically, a negative "faster cast speed") of 2 points. + * The protection spell has an indefinite duration, becoming active when cast, and deactivated when re-cast. + * Reactive Armor, Protection, and Magic Reflection will stay on—even after logging out, + * even after dying—until you “turn them off” by casting them again. + */ + + object[] mods = (object[])m_Table[target]; + + if ( mods == null ) + { + target.PlaySound( 0x1E9 ); + target.FixedParticles( 0x375A, 9, 20, 5016, EffectLayer.Waist ); + + mods = new object[2] + { + new ResistanceMod( ResistanceType.Physical, -15 + (int)(caster.Skills[SkillName.Inscribe].Value / 20) ), + new DefaultSkillMod( SkillName.MagicResist, true, -35 + (int)(caster.Skills[SkillName.Inscribe].Value / 20) ) + }; + + m_Table[target] = mods; + Registry[target] = 100.0; + + target.AddResistanceMod( (ResistanceMod)mods[0] ); + target.AddSkillMod( (SkillMod)mods[1] ); + } + else + { + target.PlaySound( 0x1ED ); + target.FixedParticles( 0x375A, 9, 20, 5016, EffectLayer.Waist ); + + m_Table.Remove( target ); + Registry.Remove( target ); + + target.RemoveResistanceMod( (ResistanceMod)mods[0] ); + target.RemoveSkillMod( (SkillMod)mods[1] ); + } + } + + public override void OnCast() + { + if ( Core.AOS ) + { + if ( CheckSequence() ) + Toggle( Caster, Caster ); + + FinishSequence(); + } + else + { + if ( m_Registry.ContainsKey( Caster ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + else if ( !Caster.CanBeginAction( typeof( DefensiveSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + } + else if ( CheckSequence() ) + { + if ( Caster.BeginAction( typeof( DefensiveSpell ) ) ) + { + double value = (int)(Caster.Skills[SkillName.EvalInt].Value + Caster.Skills[SkillName.Meditation].Value + Caster.Skills[SkillName.Inscribe].Value); + value /= 4; + + if ( value < 0 ) + value = 0; + else if ( value > 75 ) + value = 75.0; + + Registry.Add( Caster, value ); + new InternalTimer( Caster ).Start(); + + Caster.FixedParticles( 0x375A, 9, 20, 5016, EffectLayer.Waist ); + Caster.PlaySound( 0x1ED ); + } + else + { + Caster.SendLocalizedMessage( 1005385 ); // The spell will not adhere to you at this time. + } + } + + FinishSequence(); + } + } + + private class InternalTimer : Timer + { + private Mobile m_Caster; + + public InternalTimer( Mobile caster ) : base( TimeSpan.FromSeconds( 0 ) ) + { + double val = caster.Skills[SkillName.Magery].Value * 2.0; + if ( val < 15 ) + val = 15; + else if ( val > 240 ) + val = 240; + + m_Caster = caster; + Delay = TimeSpan.FromSeconds( val ); + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + ProtectionSpell.Registry.Remove( m_Caster ); + DefensiveSpell.Nullify( m_Caster ); + } + } + } +} diff --git a/Scripts/Spells/Second/RemoveTrap.cs b/Scripts/Spells/Second/RemoveTrap.cs new file mode 100644 index 000000000..5796d2b4c --- /dev/null +++ b/Scripts/Spells/Second/RemoveTrap.cs @@ -0,0 +1,83 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Items; + +namespace Server.Spells.Second +{ + public class RemoveTrapSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Remove Trap", "An Jux", + SpellCircle.Second, + 212, + 9001, + Reagent.Bloodmoss, + Reagent.SulfurousAsh + ); + + public RemoveTrapSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + Caster.SendMessage( "What do you wish to untrap?" ); + } + + public void Target( TrapableContainer item ) + { + if ( !Caster.CanSee( item ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( item.TrapType != TrapType.None && item.TrapType != TrapType.MagicTrap ) + { + base.DoFizzle(); + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, item ); + + Point3D loc = item.GetWorldLocation(); + + Effects.SendLocationParticles( EffectItem.Create( loc, item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 32, 5015 ); + Effects.PlaySound( loc, item.Map, 0x1F0 ); + + item.TrapType = TrapType.None; + item.TrapPower = 0; + item.TrapLevel = 0; + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private RemoveTrapSpell m_Owner; + + public InternalTarget( RemoveTrapSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is TrapableContainer ) + { + m_Owner.Target( (TrapableContainer)o ); + } + else + { + from.SendMessage( "You can't disarm that" ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Second/Strength.cs b/Scripts/Spells/Second/Strength.cs new file mode 100644 index 000000000..db7ebb11f --- /dev/null +++ b/Scripts/Spells/Second/Strength.cs @@ -0,0 +1,74 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Second +{ + public class StrengthSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Strength", "Uus Mani", + SpellCircle.Second, + 212, + 9061, + Reagent.MandrakeRoot, + Reagent.Nightshade + ); + + public StrengthSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.AddStatBonus( Caster, m, StatType.Str ); + + m.FixedParticles( 0x375A, 10, 15, 5017, EffectLayer.Waist ); + m.PlaySound( 0x1EE ); + + int percentage = (int)(SpellHelper.GetOffsetScalar( Caster, m, false )*100); + TimeSpan length = SpellHelper.GetDuration( Caster, m ); + + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Strength, 1075845, length, m, percentage.ToString() ) ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private StrengthSpell m_Owner; + + public InternalTarget( StrengthSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/ChainLightning.cs b/Scripts/Spells/Seventh/ChainLightning.cs new file mode 100644 index 000000000..35cb3562c --- /dev/null +++ b/Scripts/Spells/Seventh/ChainLightning.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Seventh +{ + public class ChainLightningSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Chain Lightning", "Vas Ort Grav", + SpellCircle.Seventh, + 209, + 9022, + false, + Reagent.BlackPearl, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + public ChainLightningSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return true; } } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + if ( p is Item ) + p = ((Item)p).GetWorldLocation(); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + bool playerVsPlayer = false; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 2 ); + + foreach ( Mobile m in eable ) + { + if ( Core.AOS && m == Caster ) + continue; + + if ( SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) ) + { + if ( Core.AOS && !Caster.InLOS( m ) ) + continue; + + targets.Add( m ); + + if ( m.Player ) + playerVsPlayer = true; + } + } + + eable.Free(); + } + + double damage; + + if ( Core.AOS ) + damage = GetNewAosDamage( 51, 1, 5, playerVsPlayer ); + else + damage = Utility.Random( 27, 22 ); + + if ( targets.Count > 0 ) + { + if ( Core.AOS && targets.Count > 2 ) + damage = (damage * 2) / targets.Count; + else if ( !Core.AOS ) + damage /= targets.Count; + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + double toDeal = damage; + + if ( !Core.AOS && CheckResisted( m ) ) + { + toDeal *= 0.5; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + Caster.DoHarmful( m ); + SpellHelper.Damage( this, m, toDeal, 0, 0, 0, 0, 100 ); + + m.BoltEffect( 0 ); + } + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private ChainLightningSpell m_Owner; + + public InternalTarget( ChainLightningSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/EnergyField.cs b/Scripts/Spells/Seventh/EnergyField.cs new file mode 100644 index 000000000..8f0cab2a0 --- /dev/null +++ b/Scripts/Spells/Seventh/EnergyField.cs @@ -0,0 +1,190 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Misc; +using Server.Items; + +namespace Server.Spells.Seventh +{ + public class EnergyFieldSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Energy Field", "In Sanct Grav", + SpellCircle.Seventh, + 221, + 9022, + false, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public EnergyFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + int dx = Caster.Location.X - p.X; + int dy = Caster.Location.Y - p.Y; + int rx = (dx - dy) * 44; + int ry = (dx + dy) * 44; + + bool eastToWest; + + if ( rx >= 0 && ry >= 0 ) + { + eastToWest = false; + } + else if ( rx >= 0 ) + { + eastToWest = true; + } + else if ( ry >= 0 ) + { + eastToWest = true; + } + else + { + eastToWest = false; + } + + Effects.PlaySound( p, Caster.Map, 0x20B ); + + TimeSpan duration; + + if ( Core.AOS ) + duration = TimeSpan.FromSeconds( (15 + (Caster.Skills.Magery.Fixed / 5)) / 7 ); + else + duration = TimeSpan.FromSeconds( Caster.Skills[SkillName.Magery].Value * 0.28 + 2.0 ); // (28% of magery) + 2.0 seconds + + int itemID = eastToWest ? 0x3946 : 0x3956; + + for ( int i = -2; i <= 2; ++i ) + { + Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z ); + bool canFit = SpellHelper.AdjustField( ref loc, Caster.Map, 12, false ); + + if ( !canFit ) + continue; + + Item item = new InternalItem( loc, Caster.Map, duration, itemID, Caster ); + item.ProcessDelta(); + + Effects.SendLocationParticles( EffectItem.Create( loc, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5051 ); + } + } + + FinishSequence(); + } + + [DispellableField] + private class InternalItem : Item + { + private Timer m_Timer; + + public override bool BlocksFit{ get{ return true; } } + + public InternalItem( Point3D loc, Map map, TimeSpan duration, int itemID, Mobile caster ) : base( itemID ) + { + Visible = false; + Movable = false; + Light = LightType.Circle300; + + MoveToWorld( loc, map ); + + if ( caster.InLOS( this ) ) + Visible = true; + else + Delete(); + + if ( Deleted ) + return; + + m_Timer = new InternalTimer( this, duration ); + m_Timer.Start(); + } + + public InternalItem( Serial serial ) : base( serial ) + { + m_Timer = new InternalTimer( this, TimeSpan.FromSeconds( 5.0 ) ); + m_Timer.Start(); + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Timer != null ) + m_Timer.Stop(); + } + + private class InternalTimer : Timer + { + private InternalItem m_Item; + + public InternalTimer( InternalItem item, TimeSpan duration ) : base( duration ) + { + Priority = TimerPriority.OneSecond; + m_Item = item; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } + + private class InternalTarget : Target + { + private EnergyFieldSpell m_Owner; + + public InternalTarget( EnergyFieldSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/FlameStrike.cs b/Scripts/Spells/Seventh/FlameStrike.cs new file mode 100644 index 000000000..5a17e6a68 --- /dev/null +++ b/Scripts/Spells/Seventh/FlameStrike.cs @@ -0,0 +1,93 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Seventh +{ + public class FlameStrikeSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Flame Strike", "Kal Vas Flam", + SpellCircle.Seventh, + 245, + 9042, + Reagent.SpidersSilk, + Reagent.SulfurousAsh + ); + + public FlameStrikeSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return true; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + double damage; + + if ( Core.AOS ) + { + damage = GetNewAosDamage( 48, 1, 5, m ); + } + else + { + damage = Utility.Random( 27, 22 ); + + if ( CheckResisted( m ) ) + { + damage *= 0.6; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + damage *= GetDamageScalar( m ); + } + + m.FixedParticles( 0x3709, 10, 30, 5052, EffectLayer.LeftFoot ); + m.PlaySound( 0x208 ); + + SpellHelper.Damage( this, m, damage, 0, 100, 0, 0, 0 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private FlameStrikeSpell m_Owner; + + public InternalTarget( FlameStrikeSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/GateTravel.cs b/Scripts/Spells/Seventh/GateTravel.cs new file mode 100644 index 000000000..67ae8007f --- /dev/null +++ b/Scripts/Spells/Seventh/GateTravel.cs @@ -0,0 +1,225 @@ +using System; +using Server.Network; +using Server.Multis; +using Server.Items; +using Server.Targeting; +using Server.Misc; +using Server.Regions; +using Server.Mobiles; + +namespace Server.Spells.Seventh +{ + public class GateTravelSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Gate Travel", "Vas Rel Por", + SpellCircle.Seventh, + 263, + 9032, + Reagent.BlackPearl, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + private RunebookEntry m_Entry; + + public GateTravelSpell( Mobile caster, Item scroll ) : this( caster, scroll, null ) + { + } + + public GateTravelSpell( Mobile caster, Item scroll, RunebookEntry entry ) : base( caster, scroll, m_Info ) + { + m_Entry = entry; + } + + public override void OnCast() + { + if ( m_Entry == null ) + Caster.Target = new InternalTarget( this ); + else + Effect( m_Entry.Location, m_Entry.Map, true ); + } + + public override bool CheckCast() + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return false; + } + else if ( Caster.Criminal ) + { + Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + return false; + } + else if ( SpellHelper.CheckCombat( Caster ) ) + { + Caster.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + return false; + } + + return SpellHelper.CheckTravel( Caster, TravelCheckType.GateFrom ); + } + + public void Effect( Point3D loc, Map map, bool checkMulti ) + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( map == null || (!Core.AOS && Caster.Map != map) ) + { + Caster.SendLocalizedMessage( 1005570 ); // You can not gate to another facet. + } + else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.GateFrom ) ) + { + } + else if ( !SpellHelper.CheckTravel( Caster, map, loc, TravelCheckType.GateTo ) ) + { + } + else if ( map == Map.Felucca && Caster is PlayerMobile && ((PlayerMobile)Caster).Young ) + { + Caster.SendLocalizedMessage( 1049543 ); // You decide against traveling to Felucca while you are still young. + } + else if ( Caster.Kills >= 5 && map != Map.Felucca ) + { + Caster.SendLocalizedMessage( 1019004 ); // You are not allowed to travel there. + } + else if ( Caster.Criminal ) + { + Caster.SendLocalizedMessage( 1005561, "", 0x22 ); // Thou'rt a criminal and cannot escape so easily. + } + else if ( SpellHelper.CheckCombat( Caster ) ) + { + Caster.SendLocalizedMessage( 1005564, "", 0x22 ); // Wouldst thou flee during the heat of battle?? + } + else if ( !map.CanSpawnMobile( loc.X, loc.Y, loc.Z ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( (checkMulti && SpellHelper.CheckMulti( loc, map )) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( CheckSequence() ) + { + Caster.SendLocalizedMessage( 501024 ); // You open a magical gate to another location + + Effects.PlaySound( Caster.Location, Caster.Map, 0x20E ); + + InternalItem firstGate = new InternalItem( loc, map ); + firstGate.MoveToWorld( Caster.Location, Caster.Map ); + + Effects.PlaySound( loc, map, 0x20E ); + + InternalItem secondGate = new InternalItem( Caster.Location, Caster.Map ); + secondGate.MoveToWorld( loc, map ); + } + + FinishSequence(); + } + + [DispellableField] + private class InternalItem : Moongate + { + public override bool ShowFeluccaWarning{ get{ return Core.AOS; } } + + public InternalItem( Point3D target, Map map ) : base( target, map ) + { + Map = map; + + if ( ShowFeluccaWarning && map == Map.Felucca ) + ItemID = 0xDDA; + + Dispellable = true; + + InternalTimer t = new InternalTimer( this ); + t.Start(); + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + Delete(); + } + + private class InternalTimer : Timer + { + private Item m_Item; + + public InternalTimer( Item item ) : base( TimeSpan.FromSeconds( 30.0 ) ) + { + Priority = TimerPriority.OneSecond; + m_Item = item; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } + + private class InternalTarget : Target + { + private GateTravelSpell m_Owner; + + public InternalTarget( GateTravelSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + + owner.Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501029 ); // Select Marked item. + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is RecallRune ) + { + RecallRune rune = (RecallRune)o; + + if ( rune.Marked ) + m_Owner.Effect( rune.Target, rune.TargetMap, true ); + else + from.SendLocalizedMessage( 501803 ); // That rune is not yet marked. + } + else if ( o is Runebook ) + { + RunebookEntry e = ((Runebook)o).Default; + + if ( e != null ) + m_Owner.Effect( e.Location, e.Map, true ); + else + from.SendLocalizedMessage( 502354 ); // Target is not marked. + } + /*else if ( o is Key && ((Key)o).KeyValue != 0 && ((Key)o).Link is BaseBoat ) + { + BaseBoat boat = ((Key)o).Link as BaseBoat; + + if ( !boat.Deleted && boat.CheckKey( ((Key)o).KeyValue ) ) + m_Owner.Effect( boat.GetMarkedLocation(), boat.Map, false ); + else + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 501030, from.Name, "" ) ); // I can not gate travel from that object. + }*/ + else + { + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 501030, from.Name, "" ) ); // I can not gate travel from that object. + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/ManaVampire.cs b/Scripts/Spells/Seventh/ManaVampire.cs new file mode 100644 index 000000000..fdf002ee2 --- /dev/null +++ b/Scripts/Spells/Seventh/ManaVampire.cs @@ -0,0 +1,119 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Seventh +{ + public class ManaVampireSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Mana Vampire", "Ort Sanct", + SpellCircle.Seventh, + 221, + 9032, + Reagent.BlackPearl, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SpidersSilk + ); + + public ManaVampireSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + int toDrain = 0; + + if ( Core.AOS ) + { + toDrain = (int)(GetDamageSkill( Caster ) - GetResistSkill( m )); + + if ( !m.Player ) + toDrain /= 2; + + if ( toDrain < 0 ) + toDrain = 0; + else if ( toDrain > m.Mana ) + toDrain = m.Mana; + } + else + { + if ( CheckResisted( m ) ) + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + else + toDrain = m.Mana; + } + + if ( toDrain > (Caster.ManaMax - Caster.Mana) ) + toDrain = Caster.ManaMax - Caster.Mana; + + m.Mana -= toDrain; + Caster.Mana += toDrain; + + if ( Core.AOS ) + { + m.FixedParticles( 0x374A, 1, 15, 5054, 23, 7, EffectLayer.Head ); + m.PlaySound( 0x1F9 ); + + Caster.FixedParticles( 0x0000, 10, 5, 2054, EffectLayer.Head ); + } + else + { + m.FixedParticles( 0x374A, 10, 15, 5054, EffectLayer.Head ); + m.PlaySound( 0x1F9 ); + } + } + + FinishSequence(); + } + + public override double GetResistPercent( Mobile target ) + { + return 98.0; + } + + private class InternalTarget : Target + { + private ManaVampireSpell m_Owner; + + public InternalTarget( ManaVampireSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/MassDispel.cs b/Scripts/Spells/Seventh/MassDispel.cs new file mode 100644 index 000000000..d51a59ed3 --- /dev/null +++ b/Scripts/Spells/Seventh/MassDispel.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Network; +using Server.Items; +using Server.Targeting; +using Server.Mobiles; + +namespace Server.Spells.Seventh +{ + public class MassDispelSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Mass Dispel", "Vas An Ort", + SpellCircle.Seventh, + 263, + 9002, + Reagent.Garlic, + Reagent.MandrakeRoot, + Reagent.BlackPearl, + Reagent.SulfurousAsh + ); + + public MassDispelSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 8 ); + + foreach ( Mobile m in eable ) + { + if ( m is BaseCreature && (m as BaseCreature).IsDispellable && Caster.CanBeHarmful( m, false ) ) + targets.Add( m ); + } + + eable.Free(); + } + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + BaseCreature bc = m as BaseCreature; + + if ( bc == null ) + continue; + + double dispelChance = (50.0 + ((100 * (Caster.Skills.Magery.Value - bc.DispelDifficulty)) / (bc.DispelFocus*2))) / 100; + + if ( dispelChance > Utility.RandomDouble() ) + { + Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x3728, 8, 20, 5042 ); + Effects.PlaySound( m, m.Map, 0x201 ); + + m.Delete(); + } + else + { + Caster.DoHarmful( m ); + + m.FixedEffect( 0x3779, 10, 20 ); + } + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MassDispelSpell m_Owner; + + public InternalTarget( MassDispelSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/MeteorSwarm.cs b/Scripts/Spells/Seventh/MeteorSwarm.cs new file mode 100644 index 000000000..f5b728ce0 --- /dev/null +++ b/Scripts/Spells/Seventh/MeteorSwarm.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Spells.Seventh +{ + public class MeteorSwarmSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Meteor Swarm", "Flam Kal Des Ylem", + SpellCircle.Seventh, + 233, + 9042, + false, + Reagent.Bloodmoss, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh, + Reagent.SpidersSilk + ); + + public MeteorSwarmSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return true; } } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + if ( p is Item ) + p = ((Item)p).GetWorldLocation(); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + bool playerVsPlayer = false; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 2 ); + + foreach ( Mobile m in eable ) + { + if ( Caster != m && SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanBeHarmful( m, false ) ) + { + if ( Core.AOS && !Caster.InLOS( m ) ) + continue; + + targets.Add( m ); + + if ( m.Player ) + playerVsPlayer = true; + } + } + + eable.Free(); + } + + double damage; + + if ( Core.AOS ) + damage = GetNewAosDamage( 51, 1, 5, playerVsPlayer ); + else + damage = Utility.Random( 27, 22 ); + + if ( targets.Count > 0 ) + { + Effects.PlaySound( p, Caster.Map, 0x160 ); + + if ( Core.AOS && targets.Count > 2 ) + damage = (damage * 2) / targets.Count; + else if ( !Core.AOS ) + damage /= targets.Count; + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + double toDeal = damage; + + if ( !Core.AOS && CheckResisted( m ) ) + { + toDeal *= 0.5; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + Caster.DoHarmful( m ); + SpellHelper.Damage( this, m, toDeal, 0, 100, 0, 0, 0 ); + + Caster.MovingParticles( m, 0x36D4, 7, 0, false, true, 9501, 1, 0, 0x100 ); + } + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MeteorSwarmSpell m_Owner; + + public InternalTarget( MeteorSwarmSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Seventh/Polymorph.cs b/Scripts/Spells/Seventh/Polymorph.cs new file mode 100644 index 000000000..d7a8559ba --- /dev/null +++ b/Scripts/Spells/Seventh/Polymorph.cs @@ -0,0 +1,201 @@ +using System; +using System.Collections; +using Server; +using Server.Items; +using Server.Gumps; +using Server.Spells; +using Server.Spells.Fifth; + +namespace Server.Spells.Seventh +{ + public class PolymorphSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Polymorph", "Vas Ylem Rel", + SpellCircle.Seventh, + 221, + 9002, + Reagent.Bloodmoss, + Reagent.SpidersSilk, + Reagent.MandrakeRoot + ); + + private int m_NewBody; + + public PolymorphSpell( Mobile caster, Item scroll, int body ) : base( caster, scroll, m_Info ) + { + m_NewBody = body; + } + + public PolymorphSpell( Mobile caster, Item scroll ) : this(caster,scroll,0) + { + } + + public override bool CheckCast() + { + /*if ( Caster.Mounted ) + { + Caster.SendLocalizedMessage( 1042561 ); //Please dismount first. + return false; + } + else */ + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1010521 ); // You cannot polymorph while you have a Town Sigil + return false; + } + else if ( Necromancy.TransformationSpell.UnderTransformation( Caster ) ) + { + Caster.SendLocalizedMessage( 1061633 ); // You cannot polymorph while in that form. + return false; + } + else if ( DisguiseGump.IsDisguised( Caster ) ) + { + Caster.SendLocalizedMessage( 502167 ); // You cannot polymorph while disguised. + return false; + } + else if ( Caster.BodyMod == 183 || Caster.BodyMod == 184 ) + { + Caster.SendLocalizedMessage( 1042512 ); // You cannot polymorph while wearing body paint + return false; + } + else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + return false; + } + else if ( m_NewBody == 0 ) + { + Gump gump; + if ( Core.SE ) + gump = new NewPolymorphGump( Caster, Scroll ); + else + gump = new PolymorphGump( Caster, Scroll ); + + Caster.SendGump( gump ); + return false; + } + + return true; + } + + public override void OnCast() + { + /*if ( Caster.Mounted ) + { + Caster.SendLocalizedMessage( 1042561 ); //Please dismount first. + } + else */ + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1010521 ); // You cannot polymorph while you have a Town Sigil + } + else if ( !Caster.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + else if ( Necromancy.TransformationSpell.UnderTransformation( Caster ) ) + { + Caster.SendLocalizedMessage( 1061633 ); // You cannot polymorph while in that form. + } + else if ( DisguiseGump.IsDisguised( Caster ) ) + { + Caster.SendLocalizedMessage( 502167 ); // You cannot polymorph while disguised. + } + else if ( Caster.BodyMod == 183 || Caster.BodyMod == 184 ) + { + Caster.SendLocalizedMessage( 1042512 ); // You cannot polymorph while wearing body paint + } + else if ( !Caster.CanBeginAction( typeof( IncognitoSpell ) ) || Caster.IsBodyMod ) + { + DoFizzle(); + } + else if ( CheckSequence() ) + { + if ( Caster.BeginAction( typeof( PolymorphSpell ) ) ) + { + if ( m_NewBody != 0 ) + { + if ( !((Body)m_NewBody).IsHuman ) + { + Mobiles.IMount mt = Caster.Mount; + + if ( mt != null ) + mt.Rider = null; + } + + Caster.BodyMod = m_NewBody; + + if ( m_NewBody == 400 || m_NewBody == 401 ) + Caster.HueMod = Utility.RandomSkinHue(); + else + Caster.HueMod = 0; + + BaseArmor.ValidateMobile( Caster ); + BaseClothing.ValidateMobile( Caster ); + + StopTimer( Caster ); + + Timer t = new InternalTimer( Caster ); + + m_Timers[Caster] = t; + + t.Start(); + } + } + else + { + Caster.SendLocalizedMessage( 1005559 ); // This spell is already in effect. + } + } + + FinishSequence(); + } + + private static Hashtable m_Timers = new Hashtable(); + + public static bool StopTimer( Mobile m ) + { + Timer t = (Timer)m_Timers[m]; + + if ( t != null ) + { + t.Stop(); + m_Timers.Remove( m ); + } + + return ( t != null ); + } + + private class InternalTimer : Timer + { + private Mobile m_Owner; + + public InternalTimer( Mobile owner ) : base( TimeSpan.FromSeconds( 0 ) ) + { + m_Owner = owner; + + int val = (int)owner.Skills[SkillName.Magery].Value; + + if ( val > 120 ) + val = 120; + + Delay = TimeSpan.FromSeconds( val ); + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + if ( !m_Owner.CanBeginAction( typeof( PolymorphSpell ) ) ) + { + m_Owner.BodyMod = 0; + m_Owner.HueMod = -1; + m_Owner.EndAction( typeof( PolymorphSpell ) ); + + BaseArmor.ValidateMobile( m_Owner ); + BaseClothing.ValidateMobile( m_Owner ); + } + } + } + } +} diff --git a/Scripts/Spells/Sixth/Dispel.cs b/Scripts/Spells/Sixth/Dispel.cs new file mode 100644 index 000000000..70af011ad --- /dev/null +++ b/Scripts/Spells/Sixth/Dispel.cs @@ -0,0 +1,98 @@ +using System; +using Server.Misc; +using Server.Items; +using Server.Targeting; +using Server.Network; +using Server.Mobiles; + +namespace Server.Spells.Sixth +{ + public class DispelSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Dispel", "An Ort", + SpellCircle.Sixth, + 218, + 9002, + Reagent.Garlic, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + public DispelSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + Type t = m.GetType(); + bool dispellable = false; + + if ( m is BaseCreature ) + dispellable = ( m as BaseCreature ).IsDispellable; + + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( !dispellable ) + { + Caster.SendLocalizedMessage( 1005049 ); // That cannot be dispelled. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + BaseCreature bc = m as BaseCreature; + + double dispelChance = 0; + + if ( bc != null ) + dispelChance = (50.0 + ((100 * (Caster.Skills.Magery.Value - bc.DispelDifficulty)) / (bc.DispelFocus*2))) / 100; + + if ( dispelChance > Utility.RandomDouble() ) + { + Effects.SendLocationParticles( EffectItem.Create( m.Location, m.Map, EffectItem.DefaultDuration ), 0x3728, 8, 20, 5042 ); + Effects.PlaySound( m, m.Map, 0x201 ); + + m.Delete(); + } + else + { + m.FixedEffect( 0x3779, 10, 20 ); + Caster.SendLocalizedMessage( 1010084 ); // The creature resisted the attempt to dispel it! + } + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private DispelSpell m_Owner; + + public InternalTarget( DispelSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/EnergyBolt.cs b/Scripts/Spells/Sixth/EnergyBolt.cs new file mode 100644 index 000000000..b1fb0121c --- /dev/null +++ b/Scripts/Spells/Sixth/EnergyBolt.cs @@ -0,0 +1,96 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Sixth +{ + public class EnergyBoltSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Energy Bolt", "Corp Por", + SpellCircle.Sixth, + 230, + 9022, + Reagent.BlackPearl, + Reagent.Nightshade + ); + + public EnergyBoltSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return true; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + Mobile source = Caster; + + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, ref source, ref m ); + + double damage; + + if ( Core.AOS ) + { + damage = GetNewAosDamage( 40, 1, 5, m ); + } + else + { + damage = Utility.Random( 24, 18 ); + + if ( CheckResisted( m ) ) + { + damage *= 0.75; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + // Scale damage based on evalint and resist + damage *= GetDamageScalar( m ); + } + + // Do the effects + source.MovingParticles( m, 0x379F, 7, 0, false, true, 3043, 4043, 0x211 ); + source.PlaySound( 0x20A ); + + // Deal the damage + SpellHelper.Damage( this, m, damage, 0, 0, 0, 0, 100 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private EnergyBoltSpell m_Owner; + + public InternalTarget( EnergyBoltSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/Explosion.cs b/Scripts/Spells/Sixth/Explosion.cs new file mode 100644 index 000000000..80fcf6509 --- /dev/null +++ b/Scripts/Spells/Sixth/Explosion.cs @@ -0,0 +1,130 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Sixth +{ + public class ExplosionSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Explosion", "Vas Ort Flam", + SpellCircle.Sixth, + 230, + 9041, + Reagent.Bloodmoss, + Reagent.MandrakeRoot + ); + + public ExplosionSpell( Mobile caster, Item scroll ) + : base( caster, scroll, m_Info ) + { + } + + public override bool DelayedDamageStacking { get { return !Core.AOS; } } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage { get { return false; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( Caster.CanBeHarmful( m ) && CheckSequence() ) + { + Mobile attacker = Caster, defender = m; + + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int) this.Circle, Caster, ref m ); + + InternalTimer t = new InternalTimer( this, attacker, defender, m ); + t.Start(); + } + + FinishSequence(); + } + + private class InternalTimer : Timer + { + private Spell m_Spell; + private Mobile m_Target; + private Mobile m_Attacker, m_Defender; + + public InternalTimer( Spell spell, Mobile attacker, Mobile defender, Mobile target ) + : base( TimeSpan.FromSeconds( Core.AOS ? 3.0 : 2.5 ) ) + { + m_Spell = spell; + m_Attacker = attacker; + m_Defender = defender; + m_Target = target; + + if ( m_Spell != null ) + m_Spell.StartDelayedDamageContext( attacker, this ); + + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( m_Attacker.HarmfulCheck( m_Defender ) ) + { + double damage; + + if ( Core.AOS ) + { + damage = m_Spell.GetNewAosDamage( 40, 1, 5, m_Defender ); + } + else + { + damage = Utility.Random( 23, 22 ); + + if ( m_Spell.CheckResisted( m_Target ) ) + { + damage *= 0.75; + + m_Target.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + damage *= m_Spell.GetDamageScalar( m_Target ); + } + + m_Target.FixedParticles( 0x36BD, 20, 10, 5044, EffectLayer.Head ); + m_Target.PlaySound( 0x307 ); + + SpellHelper.Damage( m_Spell, m_Target, damage, 0, 100, 0, 0, 0 ); + + if ( m_Spell != null ) + m_Spell.RemoveDelayedDamageContext( m_Attacker ); + } + } + } + + private class InternalTarget : Target + { + private ExplosionSpell m_Owner; + + public InternalTarget( ExplosionSpell owner ) + : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile) o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/Invisibility.cs b/Scripts/Spells/Sixth/Invisibility.cs new file mode 100644 index 000000000..74e0d155e --- /dev/null +++ b/Scripts/Spells/Sixth/Invisibility.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections; +using Server; +using Server.Targeting; +using Server.Items; + +namespace Server.Spells.Sixth +{ + public class InvisibilitySpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Invisibility", "An Lor Xen", + SpellCircle.Sixth, + 206, + 9002, + Reagent.Bloodmoss, + Reagent.Nightshade + ); + + public InvisibilitySpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( m is Mobiles.BaseVendor || m is Mobiles.PlayerVendor || m is Mobiles.PlayerBarkeeper || m.AccessLevel > Caster.AccessLevel ) + { + Caster.SendLocalizedMessage( 501857 ); // This spell won't work on that! + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + Effects.SendLocationParticles( EffectItem.Create( new Point3D( m.X, m.Y, m.Z + 16 ), Caster.Map, EffectItem.DefaultDuration ), 0x376A, 10, 15, 5045 ); + m.PlaySound( 0x3C4 ); + + m.Hidden = true; + + BuffInfo.RemoveBuff( m, BuffIcon.HidingAndOrStealth ); + BuffInfo.AddBuff( m, new BuffInfo( BuffIcon.Invisibility, 1075825 ) ); //Invisibility/Invisible + + RemoveTimer( m ); + + TimeSpan duration = TimeSpan.FromSeconds( ((6 * Caster.Skills.Magery.Fixed) / 50) + 1 ); + + Timer t = new InternalTimer( m, duration ); + + m_Table[m] = t; + + t.Start(); + } + + FinishSequence(); + } + + private static Hashtable m_Table = new Hashtable(); + + public static bool HasTimer( Mobile m ) + { + return m_Table[m] != null; + } + + public static void RemoveTimer( Mobile m ) + { + Timer t = (Timer)m_Table[m]; + + if ( t != null ) + { + t.Stop(); + m_Table.Remove( m ); + } + } + + private class InternalTimer : Timer + { + private Mobile m_Mobile; + + public InternalTimer( Mobile m, TimeSpan duration ) : base( duration ) + { + Priority = TimerPriority.OneSecond; + m_Mobile = m; + } + + protected override void OnTick() + { + m_Mobile.RevealingAction(); + RemoveTimer( m_Mobile ); + } + } + + public class InternalTarget : Target + { + private InvisibilitySpell m_Owner; + + public InternalTarget( InvisibilitySpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/Mark.cs b/Scripts/Spells/Sixth/Mark.cs new file mode 100644 index 000000000..8322bc924 --- /dev/null +++ b/Scripts/Spells/Sixth/Mark.cs @@ -0,0 +1,93 @@ +using System; +using Server.Items; +using Server.Targeting; +using Server.Network; +using Server.Regions; + +namespace Server.Spells.Sixth +{ + public class MarkSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Mark", "Kal Por Ylem", + SpellCircle.Sixth, + 218, + 9002, + Reagent.BlackPearl, + Reagent.Bloodmoss, + Reagent.MandrakeRoot + ); + + public MarkSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool CheckCast() + { + if ( !base.CheckCast() ) + return false; + + return SpellHelper.CheckTravel( Caster, TravelCheckType.Mark ); + } + + public void Target( RecallRune rune ) + { + if ( !Caster.CanSee( rune ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.Mark ) ) + { + } + else if ( SpellHelper.CheckMulti( Caster.Location, Caster.Map, !Core.AOS ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( !rune.IsChildOf( Caster.Backpack ) ) + { + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1062422 ); // You must have this rune in your backpack in order to mark it. + } + else if ( CheckSequence() ) + { + rune.Mark( Caster ); + + Caster.PlaySound( 0x1FA ); + Effects.SendLocationEffect( Caster, Caster.Map, 14201, 16 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MarkSpell m_Owner; + + public InternalTarget( MarkSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is RecallRune ) + { + m_Owner.Target( (RecallRune) o ); + } + else + { + from.Send( new MessageLocalized( from.Serial, from.Body, MessageType.Regular, 0x3B2, 3, 501797, from.Name, "" ) ); // I cannot mark that object. + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/MassCurse.cs b/Scripts/Spells/Sixth/MassCurse.cs new file mode 100644 index 000000000..bd824d697 --- /dev/null +++ b/Scripts/Spells/Sixth/MassCurse.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Sixth +{ + public class MassCurseSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Mass Curse", "Vas Des Sanct", + SpellCircle.Sixth, + 218, + 9031, + false, + Reagent.Garlic, + Reagent.Nightshade, + Reagent.MandrakeRoot, + Reagent.SulfurousAsh + ); + + public MassCurseSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 2 ); + + foreach ( Mobile m in eable ) + { + if ( Core.AOS && m == Caster ) + continue; + + if ( SpellHelper.ValidIndirectTarget( Caster, m ) && Caster.CanSee( m ) && Caster.CanBeHarmful( m, false ) ) + targets.Add( m ); + } + + eable.Free(); + } + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + Caster.DoHarmful( m ); + + SpellHelper.AddStatCurse( Caster, m, StatType.Str ); SpellHelper.DisableSkillCheck = true; + SpellHelper.AddStatCurse( Caster, m, StatType.Dex ); + SpellHelper.AddStatCurse( Caster, m, StatType.Int ); SpellHelper.DisableSkillCheck = false; + + m.FixedParticles( 0x374A, 10, 15, 5028, EffectLayer.Waist ); + m.PlaySound( 0x1FB ); + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MassCurseSpell m_Owner; + + public InternalTarget( MassCurseSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/ParalyzeField.cs b/Scripts/Spells/Sixth/ParalyzeField.cs new file mode 100644 index 000000000..048d7cfda --- /dev/null +++ b/Scripts/Spells/Sixth/ParalyzeField.cs @@ -0,0 +1,229 @@ +using System; +using Server.Targeting; +using Server.Items; +using Server.Network; +using Server.Misc; + +namespace Server.Spells.Sixth +{ + public class ParalyzeFieldSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Paralyze Field", "In Ex Grav", + SpellCircle.Sixth, + 230, + 9012, + false, + Reagent.BlackPearl, + Reagent.Ginseng, + Reagent.SpidersSilk + ); + + public ParalyzeFieldSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + int dx = Caster.Location.X - p.X; + int dy = Caster.Location.Y - p.Y; + int rx = (dx - dy) * 44; + int ry = (dx + dy) * 44; + + bool eastToWest; + + if ( rx >= 0 && ry >= 0 ) + eastToWest = false; + else if ( rx >= 0 ) + eastToWest = true; + else if ( ry >= 0 ) + eastToWest = true; + else + eastToWest = false; + + Effects.PlaySound( p, Caster.Map, 0x20B ); + + int itemID = eastToWest ? 0x3967 : 0x3979; + + TimeSpan duration = TimeSpan.FromSeconds( 3.0 + (Caster.Skills[SkillName.Magery].Value / 3.0) ); + + for ( int i = -2; i <= 2; ++i ) + { + Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z ); + bool canFit = SpellHelper.AdjustField( ref loc, Caster.Map, 12, false ); + + if ( !canFit ) + continue; + + Item item = new InternalItem( Caster, itemID, loc, Caster.Map, duration ); + item.ProcessDelta(); + + Effects.SendLocationParticles( EffectItem.Create( loc, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5048 ); + } + } + + FinishSequence(); + } + + [DispellableField] + private class InternalItem : Item + { + private Timer m_Timer; + private Mobile m_Caster; + private DateTime m_End; + + public override bool BlocksFit{ get{ return true; } } + + public InternalItem( Mobile caster, int itemID, Point3D loc, Map map, TimeSpan duration ) : base( itemID ) + { + Visible = false; + Movable = false; + Light = LightType.Circle300; + + MoveToWorld( loc, map ); + + if ( caster.InLOS( this ) ) + Visible = true; + else + Delete(); + + if ( Deleted ) + return; + + m_Caster = caster; + + m_Timer = new InternalTimer( this, duration ); + m_Timer.Start(); + + m_End = DateTime.Now + duration; + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Timer != null ) + m_Timer.Stop(); + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( m_Caster ); + writer.WriteDeltaTime( m_End ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Caster = reader.ReadMobile(); + m_End = reader.ReadDeltaTime(); + + m_Timer = new InternalTimer( this, m_End - DateTime.Now ); + m_Timer.Start(); + + break; + } + } + } + + public override bool OnMoveOver( Mobile m ) + { + if ( Visible && m_Caster != null && (!Core.AOS || m != m_Caster) && SpellHelper.ValidIndirectTarget( m_Caster, m ) && m_Caster.CanBeHarmful( m, false ) ) + { + m_Caster.DoHarmful( m ); + + double duration; + + if ( Core.AOS ) + { + duration = 2.0 + ((int)(m_Caster.Skills[SkillName.EvalInt].Value / 10) - (int)(m.Skills[SkillName.MagicResist].Value / 10)); + + if ( !m.Player ) + duration *= 3.0; + + if ( duration < 0.0 ) + duration = 0.0; + } + else + { + duration = 7.0 + (m_Caster.Skills[SkillName.Magery].Value * 0.2); + } + + m.Paralyze( TimeSpan.FromSeconds( duration ) ); + + m.PlaySound( 0x204 ); + m.FixedEffect( 0x376A, 10, 16 ); + } + + return true; + } + + private class InternalTimer : Timer + { + private Item m_Item; + + public InternalTimer( Item item, TimeSpan duration ) : base( duration ) + { + Priority = TimerPriority.OneSecond; + m_Item = item; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } + + private class InternalTarget : Target + { + private ParalyzeFieldSpell m_Owner; + + public InternalTarget( ParalyzeFieldSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Sixth/Reveal.cs b/Scripts/Spells/Sixth/Reveal.cs new file mode 100644 index 000000000..c96d0c444 --- /dev/null +++ b/Scripts/Spells/Sixth/Reveal.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using Server.Misc; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Sixth +{ + public class RevealSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Reveal", "Wis Quas", + SpellCircle.Sixth, + 206, + 9002, + Reagent.Bloodmoss, + Reagent.SulfurousAsh + ); + + public RevealSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + ArrayList targets = new ArrayList(); + + Map map = Caster.Map; + + if ( map != null ) + { + IPooledEnumerable eable = map.GetMobilesInRange( new Point3D( p ), 1 + (int)(Caster.Skills[SkillName.Magery].Value / 20.0) ); + + foreach ( Mobile m in eable ) + { + if ( m is Mobiles.ShadowKnight && (m.X != p.X || m.Y != p.Y) ) + continue; + + if ( m.Hidden && (m.AccessLevel == AccessLevel.Player || Caster.AccessLevel > m.AccessLevel) && Caster.CanBeHarmful( m, false, true ) && CheckDifficulty( Caster, m ) ) + targets.Add( m ); + } + + eable.Free(); + } + + for ( int i = 0; i < targets.Count; ++i ) + { + Mobile m = (Mobile)targets[i]; + + m.RevealingAction(); + + m.FixedParticles( 0x375A, 9, 20, 5049, EffectLayer.Head ); + m.PlaySound( 0x1FD ); + } + } + + FinishSequence(); + } + + // Reveal uses magery and detect hidden vs. hide and stealth + private static bool CheckDifficulty( Mobile from, Mobile m ) + { + // Reveal always reveals vs. invisibility spell + if ( !Core.AOS || InvisibilitySpell.HasTimer( m ) ) + return true; + + int magery = from.Skills[SkillName.Magery].Fixed; + int detectHidden = from.Skills[SkillName.DetectHidden].Fixed; + + int hiding = m.Skills[SkillName.Hiding].Fixed; + int stealth = m.Skills[SkillName.Stealth].Fixed; + int divisor = hiding + stealth; + + int chance; + if ( divisor > 0 ) + chance = 50 * (magery + detectHidden) / divisor; + else + chance = 100; + + return chance > Utility.Random( 100 ); + } + + private class InternalTarget : Target + { + private RevealSpell m_Owner; + + public InternalTarget( RevealSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/Bless.cs b/Scripts/Spells/Third/Bless.cs new file mode 100644 index 000000000..2b410919b --- /dev/null +++ b/Scripts/Spells/Third/Bless.cs @@ -0,0 +1,71 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Third +{ + public class BlessSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Bless", "Rel Sanct", + SpellCircle.Third, + 203, + 9061, + Reagent.Garlic, + Reagent.MandrakeRoot + ); + + public BlessSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckBSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.AddStatBonus( Caster, m, StatType.Str ); SpellHelper.DisableSkillCheck = true; + SpellHelper.AddStatBonus( Caster, m, StatType.Dex ); + SpellHelper.AddStatBonus( Caster, m, StatType.Int ); SpellHelper.DisableSkillCheck = false; + + m.FixedParticles( 0x373A, 10, 15, 5018, EffectLayer.Waist ); + m.PlaySound( 0x1EA ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private BlessSpell m_Owner; + + public InternalTarget( BlessSpell owner ) : base( 12, false, TargetFlags.Beneficial ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/Fireball.cs b/Scripts/Spells/Third/Fireball.cs new file mode 100644 index 000000000..29cd3029e --- /dev/null +++ b/Scripts/Spells/Third/Fireball.cs @@ -0,0 +1,92 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Third +{ + public class FireballSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Fireball", "Vas Flam", + SpellCircle.Third, + 203, + 9041, + Reagent.BlackPearl + ); + + public FireballSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public override bool DelayedDamage{ get{ return true; } } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + Mobile source = Caster; + + SpellHelper.Turn( source, m ); + + SpellHelper.CheckReflect( (int)this.Circle, ref source, ref m ); + + double damage; + + if ( Core.AOS ) + { + damage = GetNewAosDamage( 19, 1, 5, m ); + } + else + { + damage = Utility.Random( 10, 7 ); + + if ( CheckResisted( m ) ) + { + damage *= 0.75; + + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + + damage *= GetDamageScalar( m ); + } + + source.MovingParticles( m, 0x36D4, 7, 0, false, true, 9502, 4019, 0x160 ); + source.PlaySound( Core.AOS ? 0x15E : 0x44B ); + + SpellHelper.Damage( this, m, damage, 0, 100, 0, 0, 0 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private FireballSpell m_Owner; + + public InternalTarget( FireballSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + m_Owner.Target( (Mobile)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/MagicLock.cs b/Scripts/Spells/Third/MagicLock.cs new file mode 100644 index 000000000..89f4d5561 --- /dev/null +++ b/Scripts/Spells/Third/MagicLock.cs @@ -0,0 +1,86 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Items; + +namespace Server.Spells.Third +{ + public class MagicLockSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Magic Lock", "An Por", + SpellCircle.Third, + 215, + 9001, + Reagent.Garlic, + Reagent.Bloodmoss, + Reagent.SulfurousAsh + ); + + public MagicLockSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( LockableContainer targ ) + { + if ( Multis.BaseHouse.CheckLockedDownOrSecured( targ ) ) + { + // You cannot cast this on a locked down item. + Caster.LocalOverheadMessage( MessageType.Regular, 0x22, 501761 ); + } + else if ( targ.Locked || targ.LockLevel == 0 || targ is ParagonChest ) + { + // Target must be an unlocked chest. + Caster.SendLocalizedMessage( 501762 ); + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, targ ); + + Point3D loc = targ.GetWorldLocation(); + + Effects.SendLocationParticles( + EffectItem.Create( loc, targ.Map, EffectItem.DefaultDuration ), + 0x376A, 9, 32, 5020 ); + + Effects.PlaySound( loc, targ.Map, 0x1FA ); + + // The chest is now locked! + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501763 ); + + targ.LockLevel = -255; // signal magic lock + targ.Locked = true; + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private MagicLockSpell m_Owner; + + public InternalTarget( MagicLockSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is LockableContainer ) + m_Owner.Target( (LockableContainer)o ); + else + from.SendLocalizedMessage( 501762 ); // Target must be an unlocked chest. + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/Poison.cs b/Scripts/Spells/Third/Poison.cs new file mode 100644 index 000000000..f43ccf149 --- /dev/null +++ b/Scripts/Spells/Third/Poison.cs @@ -0,0 +1,123 @@ +using System; +using Server.Targeting; +using Server.Network; + +namespace Server.Spells.Third +{ + public class PoisonSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Poison", "In Nox", + SpellCircle.Third, + 203, + 9051, + Reagent.Nightshade + ); + + public PoisonSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( Mobile m ) + { + if ( !Caster.CanSee( m ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( CheckHSequence( m ) ) + { + SpellHelper.Turn( Caster, m ); + + SpellHelper.CheckReflect( (int)this.Circle, Caster, ref m ); + + if ( m.Spell != null ) + m.Spell.OnCasterHurt(); + + m.Paralyzed = false; + + if ( CheckResisted( m ) ) + { + m.SendLocalizedMessage( 501783 ); // You feel yourself resisting magical energy. + } + else + { + int level; + + if ( Core.AOS ) + { + if ( Caster.InRange( m, 2 ) ) + { + int total = (Caster.Skills.Magery.Fixed + Caster.Skills.Poisoning.Fixed) / 2; + + if ( total >= 1000 ) + level = 3; + else if ( total > 850 ) + level = 2; + else if ( total > 650 ) + level = 1; + else + level = 0; + } + else + { + level = 0; + } + } + else + { + double total = Caster.Skills[SkillName.Magery].Value + Caster.Skills[SkillName.Poisoning].Value; + + double dist = Caster.GetDistanceToSqrt( m ); + + if ( dist >= 3.0 ) + total -= (dist - 3.0) * 10.0; + + if ( total >= 200.0 && 1 > Utility.Random( 10 ) ) + level = 3; + else if ( total > (Core.AOS ? 170.1 : 170.0) ) + level = 2; + else if ( total > (Core.AOS ? 130.1 : 130.0) ) + level = 1; + else + level = 0; + } + + m.ApplyPoison( Caster, Poison.GetPoison( level ) ); + } + + m.FixedParticles( 0x374A, 10, 15, 5021, EffectLayer.Waist ); + m.PlaySound( 0x474 ); + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private PoisonSpell m_Owner; + + public InternalTarget( PoisonSpell owner ) : base( 12, false, TargetFlags.Harmful ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is Mobile ) + { + m_Owner.Target( (Mobile)o ); + } + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/Telekinesis.cs b/Scripts/Spells/Third/Telekinesis.cs new file mode 100644 index 000000000..b6c40a7a9 --- /dev/null +++ b/Scripts/Spells/Third/Telekinesis.cs @@ -0,0 +1,109 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Regions; +using Server.Items; + +namespace Server.Spells.Third +{ + public class TelekinesisSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Telekinesis", "Ort Por Ylem", + SpellCircle.Third, + 203, + 9031, + Reagent.Bloodmoss, + Reagent.MandrakeRoot + ); + + public TelekinesisSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( ITelekinesisable obj ) + { + if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, obj ); + + obj.OnTelekinesis( Caster ); + } + + FinishSequence(); + } + + public void Target( Container item ) + { + if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, item ); + + object root = item.RootParent; + + if ( !item.IsAccessibleTo( Caster ) ) + { + item.OnDoubleClickNotAccessible( Caster ); + } + else if ( !item.CheckItemUse( Caster, item ) ) + { + } + else if ( root != null && root is Mobile && root != Caster ) + { + item.OnSnoop( Caster ); + } + else if ( item is Corpse && !((Corpse)item).CheckLoot( Caster ) ) + { + } + else if ( Caster.Region.OnDoubleClick( Caster, item ) ) + { + Effects.SendLocationParticles( EffectItem.Create( item.Location, item.Map, EffectItem.DefaultDuration ), 0x376A, 9, 32, 5022 ); + Effects.PlaySound( item.Location, item.Map, 0x1F5 ); + + item.DisplayTo( Caster ); + item.OnItemUsed( Caster, item ); + } + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private TelekinesisSpell m_Owner; + + public InternalTarget( TelekinesisSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is ITelekinesisable ) + m_Owner.Target( (ITelekinesisable)o ); + else if ( o is Container ) + m_Owner.Target( (Container)o ); + else + from.SendLocalizedMessage( 501857 ); // This spell won't work on that! + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} + +namespace Server +{ + public interface ITelekinesisable : IPoint3D + { + void OnTelekinesis( Mobile from ); + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/Teleport.cs b/Scripts/Spells/Third/Teleport.cs new file mode 100644 index 000000000..5bc593300 --- /dev/null +++ b/Scripts/Spells/Third/Teleport.cs @@ -0,0 +1,125 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Regions; +using Server.Items; + +namespace Server.Spells.Third +{ + public class TeleportSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Teleport", "Rel Por", + SpellCircle.Third, + 215, + 9031, + Reagent.Bloodmoss, + Reagent.MandrakeRoot + ); + + public TeleportSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override bool CheckCast() + { + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + return false; + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + return false; + } + + return SpellHelper.CheckTravel( Caster, TravelCheckType.TeleportFrom ); + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + IPoint3D orig = p; + Map map = Caster.Map; + + SpellHelper.GetSurfaceTop( ref p ); + + if ( Factions.Sigil.ExistsOn( Caster ) ) + { + Caster.SendLocalizedMessage( 1061632 ); // You can't do that while carrying the sigil. + } + else if ( Server.Misc.WeightOverloading.IsOverloaded( Caster ) ) + { + Caster.SendLocalizedMessage( 502359, "", 0x22 ); // Thou art too encumbered to move. + } + else if ( !SpellHelper.CheckTravel( Caster, TravelCheckType.TeleportFrom ) ) + { + } + else if ( !SpellHelper.CheckTravel( Caster, map, new Point3D( p ), TravelCheckType.TeleportTo ) ) + { + } + else if ( map == null || !map.CanSpawnMobile( p.X, p.Y, p.Z ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( SpellHelper.CheckMulti( new Point3D( p ), map ) ) + { + Caster.SendLocalizedMessage( 501942 ); // That location is blocked. + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, orig ); + + Mobile m = Caster; + + Point3D from = m.Location; + Point3D to = new Point3D( p ); + + m.Location = to; + m.ProcessDelta(); + + if ( m.Player ) + { + Effects.SendLocationParticles( EffectItem.Create( from, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 2023 ); + Effects.SendLocationParticles( EffectItem.Create( to, m.Map, EffectItem.DefaultDuration ), 0x3728, 10, 10, 5023 ); + } + else + { + m.FixedParticles( 0x376A, 9, 32, 0x13AF, EffectLayer.Waist ); + } + + m.PlaySound( 0x1FE ); + } + + FinishSequence(); + } + + public class InternalTarget : Target + { + private TeleportSpell m_Owner; + + public InternalTarget( TeleportSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + m_Owner.Target( p ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/Unlock.cs b/Scripts/Spells/Third/Unlock.cs new file mode 100644 index 000000000..8b0c7dc14 --- /dev/null +++ b/Scripts/Spells/Third/Unlock.cs @@ -0,0 +1,103 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Items; + +namespace Server.Spells.Third +{ + public class UnlockSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Unlock Spell", "Ex Por", + SpellCircle.Third, + 215, + 9001, + Reagent.Bloodmoss, + Reagent.SulfurousAsh + ); + + public UnlockSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( LockableContainer targ ) + { + if ( Multis.BaseHouse.CheckSecured( targ ) ) + { + // You cannot cast this on a secure item. + Caster.SendLocalizedMessage( 503098 ); + } + else if ( CheckSequence() ) + { + SpellHelper.Turn( Caster, targ ); + + Point3D loc = targ.GetWorldLocation(); + + Effects.SendLocationParticles( + EffectItem.Create( loc, targ.Map, EffectItem.DefaultDuration ), + 0x376A, 9, 32, 5024 ); + + Effects.PlaySound( loc, targ.Map, 0x1FF ); + + if ( targ.Locked && targ.LockLevel != 0 ) + { + double magery = Caster.Skills[SkillName.Magery].Value; + int level = (int)(magery * 0.8) - 4; + + if ( level >= targ.RequiredSkill && !(targ is TreasureMapChest && ((TreasureMapChest)targ).Level > 2) ) + { + targ.Locked = false; + + if ( targ.LockLevel == -255 ) + targ.LockLevel = targ.RequiredSkill - 10; + + if ( targ.LockLevel == 0 ) + targ.LockLevel = -1; + } + else + { + // My spell does not seem to have an effect on that lock. + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 503099 ); + } + } + else + { + // That did not need to be unlocked. + Caster.LocalOverheadMessage( MessageType.Regular, 0x3B2, 503101 ); + } + } + + FinishSequence(); + } + + private class InternalTarget : Target + { + private UnlockSpell m_Owner; + + public InternalTarget( UnlockSpell owner ) : base( 12, false, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is LockableContainer ) + m_Owner.Target( (LockableContainer)o ); + else + from.SendLocalizedMessage( 501666 ); // You can't unlock that! + + // TODO: Really we can cast on anything, even mobiles, but it will effect and say 'That did not need to be unlocked' + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/Third/WallOfStone.cs b/Scripts/Spells/Third/WallOfStone.cs new file mode 100644 index 000000000..e6fd51359 --- /dev/null +++ b/Scripts/Spells/Third/WallOfStone.cs @@ -0,0 +1,208 @@ +using System; +using Server.Targeting; +using Server.Network; +using Server.Misc; +using Server.Items; + +namespace Server.Spells.Third +{ + public class WallOfStoneSpell : Spell + { + private static SpellInfo m_Info = new SpellInfo( + "Wall of Stone", "In Sanct Ylem", + SpellCircle.Third, + 227, + 9011, + false, + Reagent.Bloodmoss, + Reagent.Garlic + ); + + public WallOfStoneSpell( Mobile caster, Item scroll ) : base( caster, scroll, m_Info ) + { + } + + public override void OnCast() + { + Caster.Target = new InternalTarget( this ); + } + + public void Target( IPoint3D p ) + { + if ( !Caster.CanSee( p ) ) + { + Caster.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + else if ( SpellHelper.CheckTown( p, Caster ) && CheckSequence() ) + { + SpellHelper.Turn( Caster, p ); + + SpellHelper.GetSurfaceTop( ref p ); + + int dx = Caster.Location.X - p.X; + int dy = Caster.Location.Y - p.Y; + int rx = (dx - dy) * 44; + int ry = (dx + dy) * 44; + + bool eastToWest; + + if ( rx >= 0 && ry >= 0 ) + { + eastToWest = false; + } + else if ( rx >= 0 ) + { + eastToWest = true; + } + else if ( ry >= 0 ) + { + eastToWest = true; + } + else + { + eastToWest = false; + } + + Effects.PlaySound( p, Caster.Map, 0x1F6 ); + + for ( int i = -1; i <= 1; ++i ) + { + Point3D loc = new Point3D( eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z ); + bool canFit = SpellHelper.AdjustField( ref loc, Caster.Map, 22, true ); + + //Effects.SendLocationParticles( EffectItem.Create( loc, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5025 ); + + if ( !canFit ) + continue; + + Item item = new InternalItem( loc, Caster.Map, Caster ); + + Effects.SendLocationParticles( item, 0x376A, 9, 10, 5025 ); + + //new InternalItem( loc, Caster.Map, Caster ); + } + } + + FinishSequence(); + } + + [DispellableField] + private class InternalItem : Item + { + private Timer m_Timer; + private DateTime m_End; + + public override bool BlocksFit{ get{ return true; } } + + public InternalItem( Point3D loc, Map map, Mobile caster ) : base( 0x80 ) + { + Visible = false; + Movable = false; + + MoveToWorld( loc, map ); + + if ( caster.InLOS( this ) ) + Visible = true; + else + Delete(); + + if ( Deleted ) + return; + + m_Timer = new InternalTimer( this, TimeSpan.FromSeconds( 10.0 ) ); + m_Timer.Start(); + + m_End = DateTime.Now + TimeSpan.FromSeconds( 10.0 ); + } + + public InternalItem( Serial serial ) : base( serial ) + { + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 1 ); // version + + writer.WriteDeltaTime( m_End ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 1: + { + m_End = reader.ReadDeltaTime(); + + m_Timer = new InternalTimer( this, m_End - DateTime.Now ); + m_Timer.Start(); + + break; + } + case 0: + { + TimeSpan duration = TimeSpan.FromSeconds( 10.0 ); + + m_Timer = new InternalTimer( this, duration ); + m_Timer.Start(); + + m_End = DateTime.Now + duration; + + break; + } + } + } + + public override void OnAfterDelete() + { + base.OnAfterDelete(); + + if ( m_Timer != null ) + m_Timer.Stop(); + } + + private class InternalTimer : Timer + { + private InternalItem m_Item; + + public InternalTimer( InternalItem item, TimeSpan duration ) : base( duration ) + { + Priority = TimerPriority.OneSecond; + m_Item = item; + } + + protected override void OnTick() + { + m_Item.Delete(); + } + } + } + + private class InternalTarget : Target + { + private WallOfStoneSpell m_Owner; + + public InternalTarget( WallOfStoneSpell owner ) : base( 12, true, TargetFlags.None ) + { + m_Owner = owner; + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( o is IPoint3D ) + m_Owner.Target( (IPoint3D)o ); + } + + protected override void OnTargetFinish( Mobile from ) + { + m_Owner.FinishSequence(); + } + } + } +} \ No newline at end of file diff --git a/Scripts/Spells/UnsummonTimer.cs b/Scripts/Spells/UnsummonTimer.cs new file mode 100644 index 000000000..b20ed4d18 --- /dev/null +++ b/Scripts/Spells/UnsummonTimer.cs @@ -0,0 +1,24 @@ +using System; +using Server.Mobiles; + +namespace Server.Spells +{ + class UnsummonTimer : Timer + { + private BaseCreature m_Creature; + private Mobile m_Caster; + + public UnsummonTimer( Mobile caster, BaseCreature creature, TimeSpan delay ) : base( delay ) + { + m_Caster = caster; + m_Creature = creature; + Priority = TimerPriority.OneSecond; + } + + protected override void OnTick() + { + if ( !m_Creature.Deleted ) + m_Creature.Delete(); + } + } +} \ No newline at end of file diff --git a/Scripts/Targets/BladedItemTarget.cs b/Scripts/Targets/BladedItemTarget.cs new file mode 100644 index 000000000..0884eb109 --- /dev/null +++ b/Scripts/Targets/BladedItemTarget.cs @@ -0,0 +1,124 @@ +using System; +using Server; +using Server.Targeting; +using Server.Items; +using Server.Engines.Harvest; +using Server.Mobiles; +using Server.Engines.Quests; +using Server.Engines.Quests.Hag; + +namespace Server.Targets +{ + public class BladedItemTarget : Target + { + private Item m_Item; + + public BladedItemTarget( Item item ) : base( 2, false, TargetFlags.None ) + { + m_Item = item; + } + + protected override void OnTargetOutOfRange( Mobile from, object targeted ) + { + if ( targeted is UnholyBone && from.InRange( ((UnholyBone)targeted), 12 ) ) + ((UnholyBone)targeted).Carve( from, m_Item ); + else + base.OnTargetOutOfRange (from, targeted); + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Item.Deleted ) + return; + + if ( targeted is ICarvable ) + { + ((ICarvable)targeted).Carve( from, m_Item ); + } + else if ( targeted is SwampDragon && ((SwampDragon)targeted).HasBarding ) + { + SwampDragon pet = (SwampDragon)targeted; + + if ( !pet.Controlled || pet.ControlMaster != from ) + from.SendLocalizedMessage( 1053022 ); // You cannot remove barding from a swamp dragon you do not own. + else + pet.HasBarding = false; + } + else + { + if ( targeted is StaticTarget ) + { + int itemID = ((StaticTarget)targeted).ItemID; + + if ( itemID == 0xD15 || itemID == 0xD16 ) // red mushroom + { + PlayerMobile player = from as PlayerMobile; + + if ( player != null ) + { + QuestSystem qs = player.Quest; + + if ( qs is WitchApprenticeQuest ) + { + FindIngredientObjective obj = qs.FindObjective( typeof( FindIngredientObjective ) ) as FindIngredientObjective; + + if ( obj != null && !obj.Completed && obj.Ingredient == Ingredient.RedMushrooms ) + { + player.SendLocalizedMessage( 1055036 ); // You slice a red cap mushroom from its stem. + obj.Complete(); + return; + } + } + } + } + } + + HarvestSystem system = Lumberjacking.System; + HarvestDefinition def = Lumberjacking.System.Definition; + + int tileID; + Map map; + Point3D loc; + + if ( !system.GetHarvestDetails( from, m_Item, targeted, out tileID, out map, out loc ) ) + { + from.SendLocalizedMessage( 500494 ); // You can't use a bladed item on that! + } + else if ( !def.Validate( tileID ) ) + { + from.SendLocalizedMessage( 500494 ); // You can't use a bladed item on that! + } + else + { + HarvestBank bank = def.GetBank( map, loc.X, loc.Y ); + + if ( bank == null ) + return; + + if ( bank.Current < 5 ) + { + from.SendLocalizedMessage( 500493 ); // There's not enough wood here to harvest. + } + else + { + bank.Consume( def, 5 ); + + Item item = new Kindling(); + + if ( from.PlaceInBackpack( item ) ) + { + from.SendLocalizedMessage( 500491 ); // You put some kindling into your backpack. + from.SendLocalizedMessage( 500492 ); // An axe would probably get you more wood. + } + else + { + from.SendLocalizedMessage( 500490 ); // You can't place any kindling into your backpack! + + item.Delete(); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Targets/MoveTarget.cs b/Scripts/Targets/MoveTarget.cs new file mode 100644 index 000000000..08d76ebf0 --- /dev/null +++ b/Scripts/Targets/MoveTarget.cs @@ -0,0 +1,52 @@ +using System; +using Server; +using Server.Targeting; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Targets +{ + public class MoveTarget : Target + { + private object m_Object; + + public MoveTarget( object o ) : base( -1, true, TargetFlags.None ) + { + m_Object = o; + } + + protected override void OnTarget( Mobile from, object o ) + { + IPoint3D p = o as IPoint3D; + + if ( p != null ) + { + if ( !BaseCommand.IsAccessible( from, m_Object ) ) + { + from.SendMessage( "That is not accessible." ); + return; + } + + if ( p is Item ) + p = ((Item)p).GetWorldTop(); + + CommandLogging.WriteLine( from, "{0} {1} moving {2} to {3}", from.AccessLevel, CommandLogging.Format( from ), CommandLogging.Format( m_Object ), new Point3D( p ) ); + + if ( m_Object is Item ) + { + Item item = (Item)m_Object; + + if ( !item.Deleted ) + item.MoveToWorld( new Point3D( p ), from.Map ); + } + else if ( m_Object is Mobile ) + { + Mobile m = (Mobile)m_Object; + + if ( !m.Deleted ) + m.MoveToWorld( new Point3D( p ), from.Map ); + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/Targets/PickMoveTarget.cs b/Scripts/Targets/PickMoveTarget.cs new file mode 100644 index 000000000..0b0441ba3 --- /dev/null +++ b/Scripts/Targets/PickMoveTarget.cs @@ -0,0 +1,27 @@ +using System; +using Server; +using Server.Targeting; +using Server.Commands; +using Server.Commands.Generic; + +namespace Server.Targets +{ + public class PickMoveTarget : Target + { + public PickMoveTarget() : base( -1, false, TargetFlags.None ) + { + } + + protected override void OnTarget( Mobile from, object o ) + { + if ( !BaseCommand.IsAccessible( from, o ) ) + { + from.SendMessage( "That is not accessible." ); + return; + } + + if ( o is Item || o is Mobile ) + from.Target = new MoveTarget( o ); + } + } +} \ No newline at end of file diff --git a/Server/AggressorInfo.cs b/Server/AggressorInfo.cs new file mode 100644 index 000000000..b1b955f02 --- /dev/null +++ b/Server/AggressorInfo.cs @@ -0,0 +1,212 @@ +/*************************************************************************** + * AggressorInfo.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: AggressorInfo.cs 68 2006-01-23 03:15:37Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; + +namespace Server +{ + public class AggressorInfo + { + private Mobile m_Attacker, m_Defender; + private DateTime m_LastCombatTime; + private bool m_CanReportMurder; + private bool m_Reported; + private bool m_CriminalAggression; + + private bool m_Queued; + + private static Queue m_Pool = new Queue(); + + public static AggressorInfo Create( Mobile attacker, Mobile defender, bool criminal ) + { + AggressorInfo info; + + if ( m_Pool.Count > 0 ) + { + info = m_Pool.Dequeue(); + + info.m_Attacker = attacker; + info.m_Defender = defender; + + info.m_CanReportMurder = criminal; + info.m_CriminalAggression = criminal; + + info.m_Queued = false; + + info.Refresh(); + } + else + { + info = new AggressorInfo( attacker, defender, criminal ); + } + + return info; + } + + public void Free() + { + if ( m_Queued ) + return; + + m_Queued = true; + m_Pool.Enqueue( this ); + } + + private AggressorInfo( Mobile attacker, Mobile defender, bool criminal ) + { + m_Attacker = attacker; + m_Defender = defender; + + m_CanReportMurder = criminal; + m_CriminalAggression = criminal; + + Refresh(); + } + + private static TimeSpan m_ExpireDelay = TimeSpan.FromMinutes( 2.0 ); + + public static TimeSpan ExpireDelay + { + get{ return m_ExpireDelay; } + set{ m_ExpireDelay = value; } + } + + public static void DumpAccess() + { + using ( StreamWriter op = new StreamWriter( "warnings.log", true ) ) + { + op.WriteLine( "Warning: Access to queued AggressorInfo:" ); + op.WriteLine( new System.Diagnostics.StackTrace() ); + op.WriteLine(); + op.WriteLine(); + } + } + + public bool Expired + { + get + { + if ( m_Queued ) + DumpAccess(); + + return ( m_Attacker.Deleted || m_Defender.Deleted || DateTime.Now >= (m_LastCombatTime + m_ExpireDelay) ); + } + } + + public bool CriminalAggression + { + get + { + if ( m_Queued ) + DumpAccess(); + + return m_CriminalAggression; + } + set + { + if ( m_Queued ) + DumpAccess(); + + m_CriminalAggression = value; + } + } + + public Mobile Attacker + { + get + { + if ( m_Queued ) + DumpAccess(); + + return m_Attacker; + } + } + + public Mobile Defender + { + get + { + if ( m_Queued ) + DumpAccess(); + + return m_Defender; + } + } + + public DateTime LastCombatTime + { + get + { + if ( m_Queued ) + DumpAccess(); + + return m_LastCombatTime; + } + } + + public bool Reported + { + get + { + if ( m_Queued ) + DumpAccess(); + + return m_Reported; + } + set + { + if ( m_Queued ) + DumpAccess(); + + m_Reported = value; + } + } + + public bool CanReportMurder + { + get + { + if ( m_Queued ) + DumpAccess(); + + return m_CanReportMurder; + } + set + { + if ( m_Queued ) + DumpAccess(); + + m_CanReportMurder = value; + } + } + + public void Refresh() + { + if ( m_Queued ) + DumpAccess(); + + m_LastCombatTime = DateTime.Now; + m_Reported = false; + } + } +} \ No newline at end of file diff --git a/Server/AssemblyInfo.cs b/Server/AssemblyInfo.cs new file mode 100644 index 000000000..7482bd088 --- /dev/null +++ b/Server/AssemblyInfo.cs @@ -0,0 +1,78 @@ +/*************************************************************************** + * AssemblyInfo.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: AssemblyInfo.cs 138 2006-05-15 01:50:59Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +// +[assembly: AssemblyTitle("RunUO Server Core")] //Having just RunUO there is reundant, ie, RunUO.exe with the word 'RunUO' under it +[assembly: AssemblyDescription("UO Server Software")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The RunUO Team")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("2.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Server/Attributes.cs b/Server/Attributes.cs new file mode 100644 index 000000000..d10e61510 --- /dev/null +++ b/Server/Attributes.cs @@ -0,0 +1,197 @@ +/*************************************************************************** + * Attributes.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Attributes.cs 102 2006-02-04 01:52:29Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; + +namespace Server +{ + [AttributeUsage( AttributeTargets.Property )] + public class HueAttribute : Attribute + { + public HueAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Property )] + public class BodyAttribute : Attribute + { + public BodyAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct )] + public class PropertyObjectAttribute : Attribute + { + public PropertyObjectAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct )] + public class NoSortAttribute : Attribute + { + public NoSortAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Method )] + public class CallPriorityAttribute : Attribute + { + private int m_Priority; + + public int Priority + { + get{ return m_Priority; } + set{ m_Priority = value; } + } + + public CallPriorityAttribute( int priority ) + { + m_Priority = priority; + } + } + + public class CallPriorityComparer : IComparer + { + public int Compare( MethodInfo x, MethodInfo y ) + { + if ( x == null && y == null ) + return 0; + + if ( x == null ) + return 1; + + if ( y == null ) + return -1; + + return GetPriority( x ) - GetPriority( y ); + } + + private int GetPriority( MethodInfo mi ) + { + object[] objs = mi.GetCustomAttributes( typeof( CallPriorityAttribute ), true ); + + if ( objs == null ) + return 0; + + if ( objs.Length == 0 ) + return 0; + + CallPriorityAttribute attr = objs[0] as CallPriorityAttribute; + + if ( attr == null ) + return 0; + + return attr.Priority; + } + } + + [AttributeUsage( AttributeTargets.Class )] + public class TypeAliasAttribute : Attribute + { + private string[] m_Aliases; + + public string[] Aliases + { + get + { + return m_Aliases; + } + } + + public TypeAliasAttribute( params string[] aliases ) + { + m_Aliases = aliases; + } + } + + [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct )] + public class ParsableAttribute : Attribute + { + public ParsableAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum )] + public class CustomEnumAttribute : Attribute + { + private string[] m_Names; + + public string[] Names + { + get + { + return m_Names; + } + } + + public CustomEnumAttribute( string[] names ) + { + m_Names = names; + } + } + + [AttributeUsage( AttributeTargets.Constructor )] + public class ConstructableAttribute : Attribute + { + public ConstructableAttribute() + { + } + } + + [AttributeUsage( AttributeTargets.Property )] + public class CommandPropertyAttribute : Attribute + { + private AccessLevel m_ReadLevel, m_WriteLevel; + + public AccessLevel ReadLevel + { + get + { + return m_ReadLevel; + } + } + + public AccessLevel WriteLevel + { + get + { + return m_WriteLevel; + } + } + + public CommandPropertyAttribute( AccessLevel level ) : this( level, level ) + { + } + + public CommandPropertyAttribute( AccessLevel readLevel, AccessLevel writeLevel ) + { + m_ReadLevel = readLevel; + m_WriteLevel = writeLevel; + } + } +} \ No newline at end of file diff --git a/Server/BaseVendor.cs b/Server/BaseVendor.cs new file mode 100644 index 000000000..372982610 --- /dev/null +++ b/Server/BaseVendor.cs @@ -0,0 +1,213 @@ +/*************************************************************************** + * BaseVendor.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: BaseVendor.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using Server.Items; +using Server.Network; +using Server.ContextMenus; +using Server.Mobiles; + +namespace Server.Mobiles +{ + public class BuyItemStateComparer : IComparer + { + public int Compare( object l, object r ) + { + if ( l == null && r == null ) return 0; + if ( l == null ) return -1; + if ( r == null ) return 1; + + return ((BuyItemState)l).MySerial.CompareTo( ((BuyItemState)r).MySerial ); + } + } + + public class BuyItemResponse + { + private Serial m_Serial; + private int m_Amount; + + public BuyItemResponse( Serial serial, int amount ) + { + m_Serial = serial; + m_Amount = amount; + } + + public Serial Serial + { + get + { + return m_Serial; + } + } + + public int Amount + { + get + { + return m_Amount; + } + } + } + + public class SellItemResponse + { + private Item m_Item; + private int m_Amount; + + public SellItemResponse( Item i, int amount ) + { + m_Item = i; + m_Amount = amount; + } + + public Item Item + { + get + { + return m_Item; + } + } + + public int Amount + { + get + { + return m_Amount; + } + } + } + + public class SellItemState + { + private Item m_Item; + private int m_Price; + private string m_Name; + + public SellItemState( Item item, int price, string name ) + { + m_Item = item; + m_Price = price; + m_Name = name; + } + + public Item Item + { + get + { + return m_Item; + } + } + + public int Price + { + get + { + return m_Price; + } + } + + public string Name + { + get + { + return m_Name; + } + } + } + + public class BuyItemState + { + private Serial m_ContSer; + private Serial m_MySer; + private int m_ItemID; + private int m_Amount; + private int m_Hue; + private int m_Price; + private string m_Desc; + + public BuyItemState( string name, Serial cont, Serial serial, int price, int amount, int itemID, int hue ) + { + m_Desc = name; + m_ContSer = cont; + m_MySer = serial; + m_Price = price; + m_Amount = amount; + m_ItemID = itemID; + m_Hue = hue; + } + + public int Price + { + get + { + return m_Price; + } + } + + public Serial MySerial + { + get + { + return m_MySer; + } + } + + public Serial ContainerSerial + { + get + { + return m_ContSer; + } + } + + public int ItemID + { + get + { + return m_ItemID; + } + } + + public int Amount + { + get + { + return m_Amount; + } + } + + public int Hue + { + get + { + return m_Hue; + } + } + + public string Description + { + get + { + return m_Desc; + } + } + } +} diff --git a/Server/Body.cs b/Server/Body.cs new file mode 100644 index 000000000..73a6a24ef --- /dev/null +++ b/Server/Body.cs @@ -0,0 +1,269 @@ +/*************************************************************************** + * Body.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Body.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; + +namespace Server +{ + public enum BodyType : byte + { + Empty, + Monster, + Sea, + Animal, + Human, + Equipment + } + + public struct Body + { + private int m_BodyID; + + private static BodyType[] m_Types; + + static Body() + { + if ( File.Exists( "Data/bodyTable.cfg" ) ) + { + using ( StreamReader ip = new StreamReader( "Data/bodyTable.cfg" ) ) + { + m_Types = new BodyType[1000]; + + string line; + + while ( (line = ip.ReadLine()) != null ) + { + if ( line.Length == 0 || line.StartsWith( "#" ) ) + continue; + + string[] split = line.Split( '\t' ); + + try + { + int bodyID = int.Parse( split[0] ); + BodyType type = (BodyType)Enum.Parse( typeof( BodyType ), split[1], true ); + + if ( bodyID >= 0 && bodyID < m_Types.Length ) + m_Types[bodyID] = type; + } + catch + { + Console.WriteLine( "Warning: Invalid bodyTable entry:" ); + Console.WriteLine( line ); + } + } + } + } + else + { + Console.WriteLine( "Warning: Data/bodyTable.cfg does not exist" ); + + m_Types = new BodyType[0]; + } + } + + public Body( int bodyID ) + { + m_BodyID = bodyID; + } + + public BodyType Type + { + get + { + if ( m_BodyID >= 0 && m_BodyID < m_Types.Length ) + return m_Types[m_BodyID]; + else + return BodyType.Empty; + } + } + + public bool IsHuman + { + get + { + return m_BodyID >= 0 + && m_BodyID < m_Types.Length + && m_Types[m_BodyID] == BodyType.Human + && m_BodyID != 402 + && m_BodyID != 403 + && m_BodyID != 607 + && m_BodyID != 608 + && m_BodyID != 970; + } + } + + public bool IsMale + { + get + { + return m_BodyID == 183 + || m_BodyID == 185 + || m_BodyID == 400 + || m_BodyID == 402 + || m_BodyID == 605 + || m_BodyID == 607 + || m_BodyID == 750; + } + } + + public bool IsFemale + { + get + { + return m_BodyID == 184 + || m_BodyID == 186 + || m_BodyID == 401 + || m_BodyID == 403 + || m_BodyID == 606 + || m_BodyID == 608 + || m_BodyID == 751; + } + } + + public bool IsGhost + { + get + { + return m_BodyID == 402 + || m_BodyID == 403 + || m_BodyID == 607 + || m_BodyID == 608 + || m_BodyID == 970; + } + } + + public bool IsMonster + { + get + { + return m_BodyID >= 0 + && m_BodyID < m_Types.Length + && m_Types[m_BodyID] == BodyType.Monster; + } + } + + public bool IsAnimal + { + get + { + return m_BodyID >= 0 + && m_BodyID < m_Types.Length + && m_Types[m_BodyID] == BodyType.Animal; + } + } + + public bool IsEmpty + { + get + { + return m_BodyID >= 0 + && m_BodyID < m_Types.Length + && m_Types[m_BodyID] == BodyType.Empty; + } + } + + public bool IsSea + { + get + { + return m_BodyID >= 0 + && m_BodyID < m_Types.Length + && m_Types[m_BodyID] == BodyType.Sea; + } + } + + public bool IsEquipment + { + get + { + return m_BodyID >= 0 + && m_BodyID < m_Types.Length + && m_Types[m_BodyID] == BodyType.Equipment; + } + } + + public int BodyID + { + get + { + return m_BodyID; + } + } + + public static implicit operator int( Body a ) + { + return a.m_BodyID; + } + + public static implicit operator Body( int a ) + { + return new Body( a ); + } + + public override string ToString() + { + return string.Format( "0x{0:X}", m_BodyID ); + } + + public override int GetHashCode() + { + return m_BodyID; + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is Body) ) return false; + + return ((Body)o).m_BodyID == m_BodyID; + } + + public static bool operator == ( Body l, Body r ) + { + return l.m_BodyID == r.m_BodyID; + } + + public static bool operator != ( Body l, Body r ) + { + return l.m_BodyID != r.m_BodyID; + } + + public static bool operator > ( Body l, Body r ) + { + return l.m_BodyID > r.m_BodyID; + } + + public static bool operator >= ( Body l, Body r ) + { + return l.m_BodyID >= r.m_BodyID; + } + + public static bool operator < ( Body l, Body r ) + { + return l.m_BodyID < r.m_BodyID; + } + + public static bool operator <= ( Body l, Body r ) + { + return l.m_BodyID <= r.m_BodyID; + } + } +} \ No newline at end of file diff --git a/Server/ClientVersion.cs b/Server/ClientVersion.cs new file mode 100644 index 000000000..7c0601f12 --- /dev/null +++ b/Server/ClientVersion.cs @@ -0,0 +1,347 @@ +/*************************************************************************** + * ClientVersion.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ClientVersion.cs 56 2006-01-22 04:45:18Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Text; +using System.Collections; + +namespace Server +{ + public enum ClientType + { + Regular, + UOTD, + God + } + + public class ClientVersion : IComparable, IComparer + { + private static ClientVersion m_Required; + private static bool m_AllowRegular = true, m_AllowUOTD = true, m_AllowGod = true; + private static TimeSpan m_KickDelay = TimeSpan.FromSeconds( 10.0 ); + + public static ClientVersion Required + { + get + { + return m_Required; + } + set + { + m_Required = value; + } + } + + public static bool AllowRegular + { + get + { + return m_AllowRegular; + } + set + { + m_AllowRegular = value; + } + } + + public static bool AllowUOTD + { + get + { + return m_AllowUOTD; + } + set + { + m_AllowUOTD = value; + } + } + + public static bool AllowGod + { + get + { + return m_AllowGod; + } + set + { + m_AllowGod = value; + } + } + + public static TimeSpan KickDelay + { + get + { + return m_KickDelay; + } + set + { + m_KickDelay = value; + } + } + + private int m_Major, m_Minor, m_Revision, m_Patch; + private ClientType m_Type; + private string m_SourceString; + + public int Major + { + get + { + return m_Major; + } + } + + public int Minor + { + get + { + return m_Minor; + } + } + + public int Revision + { + get + { + return m_Revision; + } + } + + public int Patch + { + get + { + return m_Patch; + } + } + + public ClientType Type + { + get + { + return m_Type; + } + } + + public string SourceString + { + get + { + return m_SourceString; + } + } + + public ClientVersion( int maj, int min, int rev, int pat, ClientType type ) + { + m_Major = maj; + m_Minor = min; + m_Revision = rev; + m_Patch = pat; + m_Type = type; + + m_SourceString = ToString(); + } + + public static bool operator == ( ClientVersion l, ClientVersion r ) + { + return ( Compare( l, r ) == 0 ); + } + + public static bool operator != ( ClientVersion l, ClientVersion r ) + { + return ( Compare( l, r ) != 0 ); + } + + public static bool operator >= ( ClientVersion l, ClientVersion r ) + { + return ( Compare( l, r ) >= 0 ); + } + + public static bool operator > ( ClientVersion l, ClientVersion r ) + { + return ( Compare( l, r ) > 0 ); + } + + public static bool operator <= ( ClientVersion l, ClientVersion r ) + { + return ( Compare( l, r ) <= 0 ); + } + + public static bool operator < ( ClientVersion l, ClientVersion r ) + { + return ( Compare( l, r ) < 0 ); + } + + public override int GetHashCode() + { + return m_Major ^ m_Minor ^ m_Revision ^ m_Patch ^ (int)m_Type; + } + + public override bool Equals( object obj ) + { + if ( obj == null ) + return false; + + ClientVersion v = obj as ClientVersion; + + if ( v == null ) + return false; + + return m_Major == v.m_Major + && m_Minor == v.m_Minor + && m_Revision == v.m_Revision + && m_Patch == v.m_Patch + && m_Type == v.m_Type; + } + + public override string ToString() + { + StringBuilder builder = new StringBuilder( 16 ); + + builder.Append( m_Major ); + builder.Append( '.' ); + builder.Append( m_Minor ); + builder.Append( '.' ); + builder.Append( m_Revision ); + + if ( m_Patch > 0 ) + builder.Append( (char)('a' + (m_Patch - 1)) ); + + if ( m_Type != ClientType.Regular ) + { + builder.Append( ' ' ); + builder.Append( m_Type.ToString() ); + } + + return builder.ToString(); + } + + public ClientVersion( string fmt ) + { + m_SourceString = fmt; + + try + { + fmt = fmt.ToLower(); + + int br1 = fmt.IndexOf( '.' ); + int br2 = fmt.IndexOf( '.', br1 + 1 ); + + int br3 = br2 + 1; + while ( br3 < fmt.Length && Char.IsDigit( fmt, br3 ) ) + br3++; + + //m_Major = int.Parse( fmt.Substring( 0, br1 ) ); + //m_Minor = int.Parse( fmt.Substring( br1 + 1, br2 - br1 - 1 ) ); + //m_Revision = int.Parse( fmt.Substring( br2 + 1, br3 - br2 - 1 ) ); + + m_Major = Utility.ToInt32( fmt.Substring( 0, br1 ) ); + m_Minor = Utility.ToInt32( fmt.Substring( br1 + 1, br2 - br1 - 1 ) ); + m_Revision = Utility.ToInt32( fmt.Substring( br2 + 1, br3 - br2 - 1 ) ); + + if ( br3 < fmt.Length && !Char.IsWhiteSpace( fmt, br3 ) ) + m_Patch = (fmt[br3] - 'a') + 1; + else + m_Patch = 0; + + if ( fmt.IndexOf( "god" ) >= 0 || fmt.IndexOf( "gq" ) >= 0 ) + m_Type = ClientType.God; + else if ( fmt.IndexOf( "third dawn" ) >= 0 || fmt.IndexOf( "uo:td" ) >= 0 || fmt.IndexOf( "uotd" ) >= 0 || fmt.IndexOf( "uo3d" ) >= 0 || fmt.IndexOf( "uo:3d" ) >= 0 ) + m_Type = ClientType.UOTD; + else + m_Type = ClientType.Regular; + } + catch + { + m_Major = 0; + m_Minor = 0; + m_Revision = 0; + m_Patch = 0; + m_Type = ClientType.Regular; + } + } + + public int CompareTo( object obj ) + { + if ( obj == null ) + return 1; + + ClientVersion o = obj as ClientVersion; + + if ( o == null ) + throw new ArgumentException(); + + if ( m_Major > o.m_Major ) + return 1; + else if ( m_Major < o.m_Major ) + return -1; + else if ( m_Minor > o.m_Minor ) + return 1; + else if ( m_Minor < o.m_Minor ) + return -1; + else if ( m_Revision > o.m_Revision ) + return 1; + else if ( m_Revision < o.m_Revision ) + return -1; + else if ( m_Patch > o.m_Patch ) + return 1; + else if ( m_Patch < o.m_Patch ) + return -1; + else + return 0; + } + + public static bool IsNull( object x ) + { + return Object.ReferenceEquals( x, null ); + } + + public int Compare( object x, object y ) + { + if ( IsNull( x ) && IsNull( y ) ) + return 0; + else if ( IsNull( x ) ) + return -1; + else if ( IsNull( y ) ) + return 1; + + ClientVersion a = x as ClientVersion; + ClientVersion b = y as ClientVersion; + + if ( IsNull( a ) || IsNull( b ) ) + throw new ArgumentException(); + + return a.CompareTo( b ); + } + + public static int Compare( ClientVersion a, ClientVersion b ) + { + if ( IsNull( a ) && IsNull( b ) ) + return 0; + else if ( IsNull( a ) ) + return -1; + else if ( IsNull( b ) ) + return 1; + + return a.CompareTo( b ); + } + } +} \ No newline at end of file diff --git a/Server/Commands.cs b/Server/Commands.cs new file mode 100644 index 000000000..62f4e885e --- /dev/null +++ b/Server/Commands.cs @@ -0,0 +1,347 @@ +/*************************************************************************** + * Commands.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Commands.cs 102 2006-02-04 01:52:29Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Server.Guilds; +using Server.Gumps; +using Server.Menus; +using Server.Menus.ItemLists; +using Server.Menus.Questions; +using Server.Network; +using Server.Items; +using Server.Targeting; + +namespace Server.Commands +{ + public delegate void CommandEventHandler( CommandEventArgs e ); + + public class CommandEventArgs : EventArgs + { + private Mobile m_Mobile; + private string m_Command, m_ArgString; + private string[] m_Arguments; + + public Mobile Mobile + { + get + { + return m_Mobile; + } + } + + public string Command + { + get + { + return m_Command; + } + } + + public string ArgString + { + get + { + return m_ArgString; + } + } + + public string[] Arguments + { + get + { + return m_Arguments; + } + } + + public int Length + { + get + { + return m_Arguments.Length; + } + } + + public string GetString( int index ) + { + if ( index < 0 || index >= m_Arguments.Length ) + return ""; + + return m_Arguments[index]; + } + + public int GetInt32( int index ) + { + if ( index < 0 || index >= m_Arguments.Length ) + return 0; + + return Utility.ToInt32( m_Arguments[index] ); + } + + public bool GetBoolean( int index ) + { + if ( index < 0 || index >= m_Arguments.Length ) + return false; + + return Utility.ToBoolean( m_Arguments[index] ); + } + + public double GetDouble( int index ) + { + if ( index < 0 || index >= m_Arguments.Length ) + return 0.0; + + return Utility.ToDouble( m_Arguments[index] ); + } + + public TimeSpan GetTimeSpan( int index ) + { + if ( index < 0 || index >= m_Arguments.Length ) + return TimeSpan.Zero; + + return Utility.ToTimeSpan( m_Arguments[index] ); + } + + public CommandEventArgs( Mobile mobile, string command, string argString, string[] arguments ) + { + m_Mobile = mobile; + m_Command = command; + m_ArgString = argString; + m_Arguments = arguments; + } + } + + public class CommandEntry : IComparable + { + private string m_Command; + private CommandEventHandler m_Handler; + private AccessLevel m_AccessLevel; + + public string Command + { + get + { + return m_Command; + } + } + + public CommandEventHandler Handler + { + get + { + return m_Handler; + } + } + + public AccessLevel AccessLevel + { + get + { + return m_AccessLevel; + } + } + + public CommandEntry( string command, CommandEventHandler handler, AccessLevel accessLevel ) + { + m_Command = command; + m_Handler = handler; + m_AccessLevel = accessLevel; + } + + public int CompareTo( object obj ) + { + if ( obj == this ) + return 0; + else if ( obj == null ) + return 1; + + CommandEntry e = obj as CommandEntry; + + if ( e == null ) + throw new ArgumentException(); + + return m_Command.CompareTo( e.m_Command ); + } + } + + public class CommandSystem + { + private static string m_Prefix = "["; + + public static string Prefix + { + get + { + return m_Prefix; + } + set + { + m_Prefix = value; + } + } + + public static string[] Split( string value ) + { + char[] array = value.ToCharArray(); + List list = new List(); + + int start = 0, end = 0; + + while ( start < array.Length ) + { + char c = array[start]; + + if ( c == '"' ) + { + ++start; + end = start; + + while ( end < array.Length ) + { + if ( array[end] != '"' || array[end - 1] == '\\' ) + ++end; + else + break; + } + + list.Add( value.Substring( start, end - start ) ); + + start = end + 2; + } + else if ( c != ' ' ) + { + end = start; + + while ( end < array.Length ) + { + if ( array[end] != ' ' ) + ++end; + else + break; + } + + list.Add( value.Substring( start, end - start ) ); + + start = end + 1; + } + else + { + ++start; + } + } + + return list.ToArray(); + } + + private static Dictionary m_Entries; + + public static Dictionary Entries + { + get + { + return m_Entries; + } + } + + static CommandSystem() + { + m_Entries = new Dictionary( StringComparer.OrdinalIgnoreCase ); + } + + public static void Register( string command, AccessLevel access, CommandEventHandler handler ) + { + m_Entries[command] = new CommandEntry( command, handler, access ); + } + + private static AccessLevel m_BadCommandIngoreLevel = AccessLevel.Player; + + public static AccessLevel BadCommandIgnoreLevel{ get{ return m_BadCommandIngoreLevel; } set{ m_BadCommandIngoreLevel = value; } } + + public static bool Handle( Mobile from, string text ) + { + return Handle( from, text, MessageType.Regular ); + } + + public static bool Handle( Mobile from, string text, MessageType type ) + { + if ( text.StartsWith( m_Prefix ) || type == MessageType.Command ) + { + if( type != MessageType.Command ) + text = text.Substring( m_Prefix.Length ); + + int indexOf = text.IndexOf( ' ' ); + + string command; + string[] args; + string argString; + + if ( indexOf >= 0 ) + { + argString = text.Substring( indexOf + 1 ); + + command = text.Substring( 0, indexOf ); + args = Split( argString ); + } + else + { + argString = ""; + command = text.ToLower(); + args = new string[0]; + } + + CommandEntry entry = null; + m_Entries.TryGetValue( command, out entry ); + + if ( entry != null ) + { + if ( from.AccessLevel >= entry.AccessLevel ) + { + if ( entry.Handler != null ) + { + CommandEventArgs e = new CommandEventArgs( from, command, argString, args ); + entry.Handler( e ); + EventSink.InvokeCommand( e ); + } + } + else + { + if ( from.AccessLevel <= m_BadCommandIngoreLevel ) + return false; + + from.SendMessage( "You do not have access to that command." ); + } + } + else + { + if ( from.AccessLevel <= m_BadCommandIngoreLevel ) + return false; + + from.SendMessage( "That is not a valid command." ); + } + + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Server/ContextMenus/ContextMenu.cs b/Server/ContextMenus/ContextMenu.cs new file mode 100644 index 000000000..38868418c --- /dev/null +++ b/Server/ContextMenus/ContextMenu.cs @@ -0,0 +1,98 @@ +/*************************************************************************** + * ContextMenu.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ContextMenu.cs 136 2006-05-08 09:03:33Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Server.ContextMenus +{ + /// + /// Represents the state of an active context menu. This includes who opened the menu, the menu's focus object, and a list of entries that the menu is composed of. + /// + /// + public class ContextMenu + { + private Mobile m_From; + private object m_Target; + private ContextMenuEntry[] m_Entries; + + /// + /// Gets the who opened this ContextMenu. + /// + public Mobile From + { + get{ return m_From; } + } + + /// + /// Gets an object of the or for which this ContextMenu is on. + /// + public object Target + { + get{ return m_Target; } + } + + /// + /// Gets the list of entries contained in this ContextMenu. + /// + public ContextMenuEntry[] Entries + { + get{ return m_Entries; } + } + + /// + /// Instantiates a new ContextMenu instance. + /// + /// + /// The who opened this ContextMenu. + /// + /// + /// + /// The or for which this ContextMenu is on. + /// + /// + public ContextMenu( Mobile from, object target ) + { + m_From = from; + m_Target = target; + + List list = new List(); + + if ( target is Mobile ) + { + ((Mobile)target).GetContextMenuEntries( from, list ); + } + else if ( target is Item ) + { + ((Item)target).GetContextMenuEntries( from, list ); + } + + //m_Entries = (ContextMenuEntry[])list.ToArray( typeof( ContextMenuEntry ) ); + + m_Entries = list.ToArray(); + + for ( int i = 0; i < m_Entries.Length; ++i ) + { + m_Entries[i].Owner = this; + } + } + } +} \ No newline at end of file diff --git a/Server/ContextMenus/ContextMenuEntry.cs b/Server/ContextMenus/ContextMenuEntry.cs new file mode 100644 index 000000000..c63a0e15a --- /dev/null +++ b/Server/ContextMenus/ContextMenuEntry.cs @@ -0,0 +1,139 @@ +/*************************************************************************** + * ContextMenuEntry.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ContextMenuEntry.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server; +using Server.Network; + +namespace Server.ContextMenus +{ + /// + /// Represents a single entry of a context menu. + /// + /// + public class ContextMenuEntry + { + private int m_Number; + private int m_Color; + private bool m_Enabled; + private int m_Range; + private CMEFlags m_Flags; + private ContextMenu m_Owner; + + /// + /// Gets or sets additional flags used in client communication. + /// + public CMEFlags Flags + { + get{ return m_Flags; } + set{ m_Flags = value; } + } + + /// + /// Gets or sets the that owns this entry. + /// + public ContextMenu Owner + { + get{ return m_Owner; } + set{ m_Owner = value; } + } + + /// + /// Gets or sets the localization number containing the name of this entry. + /// + public int Number + { + get{ return m_Number; } + set{ m_Number = value; } + } + + /// + /// Gets or sets the maximum range at which this entry may be used, in tiles. A value of -1 signifies no maximum range. + /// + public int Range + { + get{ return m_Range; } + set{ m_Range = value; } + } + + /// + /// Gets or sets the color for this entry. Format is A1-R5-G5-B5. + /// + public int Color + { + get{ return m_Color; } + set{ m_Color = value; } + } + + /// + /// Gets or sets whether this entry is enabled. When false, the entry will appear in a gray hue and will never be invoked. + /// + public bool Enabled + { + get{ return m_Enabled; } + set{ m_Enabled = value; } + } + + /// + /// Gets a value indicating if non local use of this entry is permitted. + /// + public virtual bool NonLocalUse + { + get{ return false; } + } + + /// + /// Instantiates a new ContextMenuEntry with a given localization number (). No maximum range is used. + /// + /// + /// The localization number containing the name of this entry. + /// + /// + public ContextMenuEntry( int number ) : this( number, -1 ) + { + } + + /// + /// Instantiates a new ContextMenuEntry with a given localization number () and maximum range (). + /// + /// + /// The localization number containing the name of this entry. + /// + /// + /// + /// The maximum range at which this entry can be used. + /// + /// + public ContextMenuEntry( int number, int range ) + { + m_Number = number; + m_Range = range; + m_Enabled = true; + m_Color = 0xFFFF; + } + + /// + /// Overridable. Virtual event invoked when the entry is clicked. + /// + public virtual void OnClick() + { + } + } +} \ No newline at end of file diff --git a/Server/ContextMenus/OpenBackpackEntry.cs b/Server/ContextMenus/OpenBackpackEntry.cs new file mode 100644 index 000000000..063dfb78c --- /dev/null +++ b/Server/ContextMenus/OpenBackpackEntry.cs @@ -0,0 +1,40 @@ +/*************************************************************************** + * OpenBackpackEntry.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: OpenBackpackEntry.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Items; + +namespace Server.ContextMenus +{ + public class OpenBackpackEntry : ContextMenuEntry + { + private Mobile m_Mobile; + + public OpenBackpackEntry( Mobile m ) : base( 6145 ) + { + m_Mobile = m; + } + + public override void OnClick() + { + m_Mobile.Use( m_Mobile.Backpack ); + } + } +} \ No newline at end of file diff --git a/Server/ContextMenus/PaperdollEntry.cs b/Server/ContextMenus/PaperdollEntry.cs new file mode 100644 index 000000000..e7cf1ae08 --- /dev/null +++ b/Server/ContextMenus/PaperdollEntry.cs @@ -0,0 +1,40 @@ +/*************************************************************************** + * PaperdollEntry.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: PaperdollEntry.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.ContextMenus +{ + public class PaperdollEntry : ContextMenuEntry + { + private Mobile m_Mobile; + + public PaperdollEntry( Mobile m ) : base( 6123, 18 ) + { + m_Mobile = m; + } + + public override void OnClick() + { + if ( m_Mobile.CanPaperdollBeOpenedBy( Owner.From ) ) + m_Mobile.DisplayPaperdollTo( Owner.From ); + } + } +} \ No newline at end of file diff --git a/Server/Effects.cs b/Server/Effects.cs new file mode 100644 index 000000000..75ec7283b --- /dev/null +++ b/Server/Effects.cs @@ -0,0 +1,408 @@ +/*************************************************************************** + * Effects.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Effects.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using Server.Network; + +namespace Server +{ + public enum EffectLayer + { + Head = 0, + RightHand = 1, + LeftHand = 2, + Waist = 3, + LeftFoot = 4, + RightFoot = 5, + CenterFeet = 7 + } + + public enum ParticleSupportType + { + Full, + Detect, + None + } + + public class Effects + { + private static ParticleSupportType m_ParticleSupportType = ParticleSupportType.Detect; + + public static ParticleSupportType ParticleSupportType + { + get{ return m_ParticleSupportType; } + set{ m_ParticleSupportType = value; } + } + + public static bool SendParticlesTo( NetState state ) + { + return ( m_ParticleSupportType == ParticleSupportType.Full || (m_ParticleSupportType == ParticleSupportType.Detect && state.Version != null && state.Version.Type == ClientType.UOTD) ); + } + + public static void PlaySound( IPoint3D p, Map map, int soundID ) + { + if ( soundID == -1 ) + return; + + if ( map != null ) + { + Packet playSound = null; + + IPooledEnumerable eable = map.GetClientsInRange( new Point3D( p ) ); + + foreach ( NetState state in eable ) + { + state.Mobile.ProcessDelta(); + + if ( playSound == null ) + playSound = Packet.Acquire( new PlaySound( soundID, p ) ); + + state.Send( playSound ); + } + + Packet.Release( playSound ); + + eable.Free(); + } + } + + public static void SendBoltEffect( IEntity e ) + { + SendBoltEffect( e, true, 0 ); + } + + public static void SendBoltEffect( IEntity e, bool sound ) + { + SendBoltEffect( e, sound, 0 ); + } + + public static void SendBoltEffect( IEntity e, bool sound, int hue ) + { + Map map = e.Map; + + if ( map == null ) + return; + + if ( e is Item ) + ((Item)e).ProcessDelta(); + else if ( e is Mobile ) + ((Mobile)e).ProcessDelta(); + + Packet preEffect = null, boltEffect = null, playSound = null; + + IPooledEnumerable eable = map.GetClientsInRange( e.Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( e ) ) + { + if ( SendParticlesTo( state ) ) + { + if ( preEffect == null ) + preEffect = Packet.Acquire( new TargetParticleEffect( e, 0, 10, 5, 0, 0, 5031, 3, 0 ) ); + + state.Send( preEffect ); + } + + if ( boltEffect == null ) + boltEffect = Packet.Acquire( new BoltEffect( e, hue ) ); + + state.Send( boltEffect ); + + if ( sound ) + { + if ( playSound == null ) + playSound = Packet.Acquire( new PlaySound( 0x29, e ) ); + + state.Send( playSound ); + } + } + } + + Packet.Release( preEffect ); + Packet.Release( boltEffect ); + Packet.Release( playSound ); + + eable.Free(); + } + + public static void SendLocationEffect( IPoint3D p, Map map, int itemID, int duration ) + { + SendLocationEffect( p, map, itemID, duration, 10, 0, 0 ); + } + + public static void SendLocationEffect( IPoint3D p, Map map, int itemID, int duration, int speed ) + { + SendLocationEffect( p, map, itemID, duration, speed, 0, 0 ); + } + + public static void SendLocationEffect( IPoint3D p, Map map, int itemID, int duration, int hue, int renderMode ) + { + SendLocationEffect( p, map, itemID, duration, 10, hue, renderMode ); + } + + public static void SendLocationEffect( IPoint3D p, Map map, int itemID, int duration, int speed, int hue, int renderMode ) + { + SendPacket( p, map, new LocationEffect( p, itemID, speed, duration, hue, renderMode ) ); + } + + public static void SendLocationParticles( IEntity e, int itemID, int speed, int duration, int effect ) + { + SendLocationParticles( e, itemID, speed, duration, 0, 0, effect, 0 ); + } + + public static void SendLocationParticles( IEntity e, int itemID, int speed, int duration, int effect, int unknown ) + { + SendLocationParticles( e, itemID, speed, duration, 0, 0, effect, unknown ); + } + + public static void SendLocationParticles( IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown ) + { + Map map = e.Map; + + if ( map != null ) + { + Packet particles = null, regular = null; + + IPooledEnumerable eable = map.GetClientsInRange( e.Location ); + + foreach ( NetState state in eable ) + { + state.Mobile.ProcessDelta(); + + if ( SendParticlesTo( state ) ) + { + if ( particles == null ) + particles = Packet.Acquire( new LocationParticleEffect( e, itemID, speed, duration, hue, renderMode, effect, unknown ) ); + + state.Send( particles ); + } + else if ( itemID != 0 ) + { + if ( regular == null ) + regular = Packet.Acquire( new LocationEffect( e, itemID, speed, duration, hue, renderMode ) ); + + state.Send( regular ); + } + } + + Packet.Release( particles ); + Packet.Release( regular ); + + eable.Free(); + } + //SendPacket( e.Location, e.Map, new LocationParticleEffect( e, itemID, speed, duration, hue, renderMode, effect, unknown ) ); + } + + public static void SendTargetEffect( IEntity target, int itemID, int duration ) + { + SendTargetEffect( target, itemID, duration, 0, 0 ); + } + + public static void SendTargetEffect( IEntity target, int itemID, int speed, int duration ) + { + SendTargetEffect( target, itemID, speed, duration, 0, 0 ); + } + + public static void SendTargetEffect( IEntity target, int itemID, int duration, int hue, int renderMode ) + { + SendTargetEffect( target, itemID, 10, duration, hue, renderMode ); + } + + public static void SendTargetEffect( IEntity target, int itemID, int speed, int duration, int hue, int renderMode ) + { + if ( target is Mobile ) + ((Mobile)target).ProcessDelta(); + + SendPacket( target.Location, target.Map, new TargetEffect( target, itemID, speed, duration, hue, renderMode ) ); + } + + public static void SendTargetParticles( IEntity target, int itemID, int speed, int duration, int effect, EffectLayer layer ) + { + SendTargetParticles( target, itemID, speed, duration, 0, 0, effect, layer, 0 ); + } + + public static void SendTargetParticles( IEntity target, int itemID, int speed, int duration, int effect, EffectLayer layer, int unknown ) + { + SendTargetParticles( target, itemID, speed, duration, 0, 0, effect, layer, unknown ); + } + + public static void SendTargetParticles( IEntity target, int itemID, int speed, int duration, int hue, int renderMode, int effect, EffectLayer layer, int unknown ) + { + if ( target is Mobile ) + ((Mobile)target).ProcessDelta(); + + Map map = target.Map; + + if ( map != null ) + { + Packet particles = null, regular = null; + + IPooledEnumerable eable = map.GetClientsInRange( target.Location ); + + foreach ( NetState state in eable ) + { + state.Mobile.ProcessDelta(); + + if ( SendParticlesTo( state ) ) + { + if ( particles == null ) + particles = Packet.Acquire( new TargetParticleEffect( target, itemID, speed, duration, hue, renderMode, effect, (int)layer, unknown ) ); + + state.Send( particles ); + } + else if ( itemID != 0 ) + { + if ( regular == null ) + regular = Packet.Acquire( new TargetEffect( target, itemID, speed, duration, hue, renderMode ) ); + + state.Send( regular ); + } + } + + Packet.Release( particles ); + Packet.Release( regular ); + + eable.Free(); + } + + //SendPacket( target.Location, target.Map, new TargetParticleEffect( target, itemID, speed, duration, hue, renderMode, effect, (int)layer, unknown ) ); + } + + public static void SendMovingEffect( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes ) + { + SendMovingEffect( from, to, itemID, speed, duration, fixedDirection, explodes, 0, 0 ); + } + + public static void SendMovingEffect( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode ) + { + if ( from is Mobile ) + ((Mobile)from).ProcessDelta(); + + if ( to is Mobile ) + ((Mobile)to).ProcessDelta(); + + SendPacket( from.Location, from.Map, new MovingEffect( from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode ) ); + } + + public static void SendMovingParticles( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int effect, int explodeEffect, int explodeSound ) + { + SendMovingParticles( from, to, itemID, speed, duration, fixedDirection, explodes, 0, 0, effect, explodeEffect, explodeSound, 0 ); + } + + public static void SendMovingParticles( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int effect, int explodeEffect, int explodeSound, int unknown ) + { + SendMovingParticles( from, to, itemID, speed, duration, fixedDirection, explodes, 0, 0, effect, explodeEffect, explodeSound, unknown ); + } + + public static void SendMovingParticles( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, int unknown ) + { + SendMovingParticles( from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, (EffectLayer)255, unknown ); + } + + public static void SendMovingParticles( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, int unknown ) + { + if ( from is Mobile ) + ((Mobile)from).ProcessDelta(); + + if ( to is Mobile ) + ((Mobile)to).ProcessDelta(); + + Map map = from.Map; + + if ( map != null ) + { + Packet particles = null, regular = null; + + IPooledEnumerable eable = map.GetClientsInRange( from.Location ); + + foreach ( NetState state in eable ) + { + state.Mobile.ProcessDelta(); + + if ( SendParticlesTo( state ) ) + { + if ( particles == null ) + particles = Packet.Acquire( new MovingParticleEffect( from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, layer, unknown ) ); + + state.Send( particles ); + } + else if ( itemID > 1 ) + { + if ( regular == null ) + regular = Packet.Acquire( new MovingEffect( from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode ) ); + + state.Send( regular ); + } + } + + Packet.Release( particles ); + Packet.Release( regular ); + + eable.Free(); + } + + //SendPacket( from.Location, from.Map, new MovingParticleEffect( from, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, unknown ) ); + } + + public static void SendPacket( Point3D origin, Map map, Packet p ) + { + if ( map != null ) + { + IPooledEnumerable eable = map.GetClientsInRange( origin ); + + p.Acquire(); + + foreach ( NetState state in eable ) + { + state.Mobile.ProcessDelta(); + + state.Send( p ); + } + + p.Release(); + + eable.Free(); + } + } + + public static void SendPacket( IPoint3D origin, Map map, Packet p ) + { + if ( map != null ) + { + IPooledEnumerable eable = map.GetClientsInRange( new Point3D( origin ) ); + + p.Acquire(); + + foreach ( NetState state in eable ) + { + state.Mobile.ProcessDelta(); + + state.Send( p ); + } + + p.Release(); + + eable.Free(); + } + } + } +} \ No newline at end of file diff --git a/Server/EventLog.cs b/Server/EventLog.cs new file mode 100644 index 000000000..c14be198d --- /dev/null +++ b/Server/EventLog.cs @@ -0,0 +1,67 @@ +/*************************************************************************** + * EventLog.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: EventLog.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Diagnostics; +using DiagELog = System.Diagnostics.EventLog; + +namespace Server +{ + public class EventLog + { + static EventLog() + { + if ( !DiagELog.SourceExists( "RunUO" ) ) + { + DiagELog.CreateEventSource( "RunUO", "Application" ); + } + } + + public static void Error( int eventID, string text ) + { + DiagELog.WriteEntry( "RunUO", text, EventLogEntryType.Error, eventID ); + } + + public static void Error( int eventID, string format, params object[] args ) + { + Error( eventID, String.Format( format, args ) ); + } + + public static void Warning( int eventID, string text ) + { + DiagELog.WriteEntry( "RunUO", text, EventLogEntryType.Warning, eventID ); + } + + public static void Warning( int eventID, string format, params object[] args ) + { + Warning( eventID, String.Format( format, args ) ); + } + + public static void Inform( int eventID, string text ) + { + DiagELog.WriteEntry( "RunUO", text, EventLogEntryType.Information, eventID ); + } + + public static void Inform( int eventID, string format, params object[] args ) + { + Inform( eventID, String.Format( format, args ) ); + } + } +} \ No newline at end of file diff --git a/Server/EventSink.cs b/Server/EventSink.cs new file mode 100644 index 000000000..4da44cf78 --- /dev/null +++ b/Server/EventSink.cs @@ -0,0 +1,1110 @@ +/*************************************************************************** + * EventSink.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: EventSink.cs 102 2006-02-04 01:52:29Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Net; +using System.Net.Sockets; +using System.Collections; +using System.Collections.Generic; +using Server; +using Server.Items; +using Server.Accounting; +using Server.Network; +using Server.Guilds; +using Server.Commands; + +namespace Server +{ + public delegate void CharacterCreatedEventHandler( CharacterCreatedEventArgs e ); + public delegate void OpenDoorMacroEventHandler( OpenDoorMacroEventArgs e ); + public delegate void SpeechEventHandler( SpeechEventArgs e ); + public delegate void LoginEventHandler( LoginEventArgs e ); + public delegate void ServerListEventHandler( ServerListEventArgs e ); + public delegate void MovementEventHandler( MovementEventArgs e ); + public delegate void HungerChangedEventHandler( HungerChangedEventArgs e ); + public delegate void CrashedEventHandler( CrashedEventArgs e ); + public delegate void ShutdownEventHandler( ShutdownEventArgs e ); + public delegate void HelpRequestEventHandler( HelpRequestEventArgs e ); + public delegate void DisarmRequestEventHandler( DisarmRequestEventArgs e ); + public delegate void StunRequestEventHandler( StunRequestEventArgs e ); + public delegate void OpenSpellbookRequestEventHandler( OpenSpellbookRequestEventArgs e ); + public delegate void CastSpellRequestEventHandler( CastSpellRequestEventArgs e ); + public delegate void AnimateRequestEventHandler( AnimateRequestEventArgs e ); + public delegate void LogoutEventHandler( LogoutEventArgs e ); + public delegate void SocketConnectEventHandler( SocketConnectEventArgs e ); + public delegate void ConnectedEventHandler( ConnectedEventArgs e ); + public delegate void DisconnectedEventHandler( DisconnectedEventArgs e ); + public delegate void RenameRequestEventHandler( RenameRequestEventArgs e ); + public delegate void PlayerDeathEventHandler( PlayerDeathEventArgs e ); + public delegate void VirtueGumpRequestEventHandler( VirtueGumpRequestEventArgs e ); + public delegate void VirtueItemRequestEventHandler( VirtueItemRequestEventArgs e ); + public delegate void VirtueMacroRequestEventHandler( VirtueMacroRequestEventArgs e ); + public delegate void ChatRequestEventHandler( ChatRequestEventArgs e ); + public delegate void AccountLoginEventHandler( AccountLoginEventArgs e ); + public delegate void PaperdollRequestEventHandler( PaperdollRequestEventArgs e ); + public delegate void ProfileRequestEventHandler( ProfileRequestEventArgs e ); + public delegate void ChangeProfileRequestEventHandler( ChangeProfileRequestEventArgs e ); + public delegate void AggressiveActionEventHandler( AggressiveActionEventArgs e ); + public delegate void GameLoginEventHandler( GameLoginEventArgs e ); + public delegate void DeleteRequestEventHandler( DeleteRequestEventArgs e ); + public delegate void WorldLoadEventHandler(); + public delegate void WorldSaveEventHandler( WorldSaveEventArgs e ); + public delegate void SetAbilityEventHandler( SetAbilityEventArgs e ); + public delegate void FastWalkEventHandler( FastWalkEventArgs e ); + public delegate void ServerStartedEventHandler(); + public delegate BaseGuild CreateGuildHandler( CreateGuildEventArgs e ); + public delegate void GuildGumpRequestHandler( GuildGumpRequestArgs e ); + public delegate void QuestGumpRequestHandler( QuestGumpRequestArgs e ); + + public class CreateGuildEventArgs : EventArgs + { + private int m_Id; + public int Id { get { return m_Id; } set { m_Id = value; } } + + public CreateGuildEventArgs( int id ) + { + m_Id = id; + } + } + + public class GuildGumpRequestArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public GuildGumpRequestArgs( Mobile mobile ) + { + m_Mobile = mobile; + } + } + + public class QuestGumpRequestArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile { get { return m_Mobile; } } + + public QuestGumpRequestArgs( Mobile mobile ) + { + m_Mobile = mobile; + } + } + + public class SetAbilityEventArgs : EventArgs + { + private Mobile m_Mobile; + private int m_Index; + + public Mobile Mobile{ get{ return m_Mobile; } } + public int Index{ get{ return m_Index; } } + + public SetAbilityEventArgs( Mobile mobile, int index ) + { + m_Mobile = mobile; + m_Index = index; + } + } + + public class DeleteRequestEventArgs : EventArgs + { + private NetState m_State; + private int m_Index; + + public NetState State{ get{ return m_State; } } + public int Index{ get{ return m_Index; } } + + public DeleteRequestEventArgs( NetState state, int index ) + { + m_State = state; + m_Index = index; + } + } + + public class GameLoginEventArgs : EventArgs + { + private NetState m_State; + private string m_Username; + private string m_Password; + private bool m_Accepted; + private CityInfo[] m_CityInfo; + + public NetState State{ get{ return m_State; } } + public string Username{ get{ return m_Username; } } + public string Password{ get{ return m_Password; } } + public bool Accepted{ get{ return m_Accepted; } set{ m_Accepted = value; } } + public CityInfo[] CityInfo{ get{ return m_CityInfo; } set{ m_CityInfo = value; } } + + public GameLoginEventArgs( NetState state, string un, string pw ) + { + m_State = state; + m_Username = un; + m_Password = pw; + } + } + + public class AggressiveActionEventArgs : EventArgs + { + private Mobile m_Aggressed; + private Mobile m_Aggressor; + private bool m_Criminal; + + public Mobile Aggressed{ get{ return m_Aggressed; } } + public Mobile Aggressor{ get{ return m_Aggressor; } } + public bool Criminal{ get{ return m_Criminal; } } + + private static Queue m_Pool = new Queue(); + + public static AggressiveActionEventArgs Create( Mobile aggressed, Mobile aggressor, bool criminal ) + { + AggressiveActionEventArgs args; + + if ( m_Pool.Count > 0 ) + { + args = m_Pool.Dequeue(); + + args.m_Aggressed = aggressed; + args.m_Aggressor = aggressor; + args.m_Criminal = criminal; + } + else + { + args = new AggressiveActionEventArgs( aggressed, aggressor, criminal ); + } + + return args; + } + + private AggressiveActionEventArgs( Mobile aggressed, Mobile aggressor, bool criminal ) + { + m_Aggressed = aggressed; + m_Aggressor = aggressor; + m_Criminal = criminal; + } + + public void Free() + { + m_Pool.Enqueue( this ); + } + } + + public class ProfileRequestEventArgs : EventArgs + { + private Mobile m_Beholder; + private Mobile m_Beheld; + + public Mobile Beholder{ get{ return m_Beholder; } } + public Mobile Beheld{ get{ return m_Beheld; } } + + public ProfileRequestEventArgs( Mobile beholder, Mobile beheld ) + { + m_Beholder = beholder; + m_Beheld = beheld; + } + } + + public class ChangeProfileRequestEventArgs : EventArgs + { + private Mobile m_Beholder; + private Mobile m_Beheld; + private string m_Text; + + public Mobile Beholder{ get{ return m_Beholder; } } + public Mobile Beheld{ get{ return m_Beheld; } } + public string Text{ get{ return m_Text; } } + + public ChangeProfileRequestEventArgs( Mobile beholder, Mobile beheld, string text ) + { + m_Beholder = beholder; + m_Beheld = beheld; + m_Text = text; + } + } + + public class PaperdollRequestEventArgs : EventArgs + { + private Mobile m_Beholder; + private Mobile m_Beheld; + + public Mobile Beholder{ get{ return m_Beholder; } } + public Mobile Beheld{ get{ return m_Beheld; } } + + public PaperdollRequestEventArgs( Mobile beholder, Mobile beheld ) + { + m_Beholder = beholder; + m_Beheld = beheld; + } + } + + public class AccountLoginEventArgs : EventArgs + { + private NetState m_State; + private string m_Username; + private string m_Password; + + private bool m_Accepted; + private ALRReason m_RejectReason; + + public NetState State{ get{ return m_State; } } + public string Username{ get{ return m_Username; } } + public string Password{ get{ return m_Password; } } + public bool Accepted{ get{ return m_Accepted; } set{ m_Accepted = value; } } + public ALRReason RejectReason{ get{ return m_RejectReason; } set{ m_RejectReason = value; } } + + public AccountLoginEventArgs( NetState state, string username, string password ) + { + m_State = state; + m_Username = username; + m_Password = password; + } + } + + public class VirtueItemRequestEventArgs : EventArgs + { + private Mobile m_Beholder; + private Mobile m_Beheld; + private int m_GumpID; + + public Mobile Beholder{ get{ return m_Beholder; } } + public Mobile Beheld{ get{ return m_Beheld; } } + public int GumpID{ get{ return m_GumpID; } } + + public VirtueItemRequestEventArgs( Mobile beholder, Mobile beheld, int gumpID ) + { + m_Beholder = beholder; + m_Beheld = beheld; + m_GumpID = gumpID; + } + } + + public class VirtueGumpRequestEventArgs : EventArgs + { + private Mobile m_Beholder, m_Beheld; + + public Mobile Beholder{ get{ return m_Beholder; } } + public Mobile Beheld{ get{ return m_Beheld; } } + + public VirtueGumpRequestEventArgs( Mobile beholder, Mobile beheld ) + { + m_Beholder = beholder; + m_Beheld = beheld; + } + } + + public class VirtueMacroRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + private int m_VirtueID; + + public Mobile Mobile{ get{ return m_Mobile; } } + public int VirtueID{ get{ return m_VirtueID; } } + + public VirtueMacroRequestEventArgs( Mobile mobile, int virtueID ) + { + m_Mobile = mobile; + m_VirtueID = virtueID; + } + } + + public class ChatRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public ChatRequestEventArgs( Mobile mobile ) + { + m_Mobile = mobile; + } + } + + public class PlayerDeathEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public PlayerDeathEventArgs( Mobile mobile ) + { + m_Mobile = mobile; + } + } + + public class RenameRequestEventArgs : EventArgs + { + private Mobile m_From, m_Target; + private string m_Name; + + public Mobile From{ get{ return m_From; } } + public Mobile Target{ get{ return m_Target; } } + public string Name{ get{ return m_Name; } } + + public RenameRequestEventArgs( Mobile from, Mobile target, string name ) + { + m_From = from; + m_Target = target; + m_Name = name; + } + } + + public class LogoutEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public LogoutEventArgs( Mobile m ) + { + m_Mobile = m; + } + } + + public class SocketConnectEventArgs : EventArgs + { + private Socket m_Socket; + private bool m_AllowConnection; + + public Socket Socket{ get{ return m_Socket; } } + public bool AllowConnection{ get { return m_AllowConnection; } set { m_AllowConnection = value; } } + + public SocketConnectEventArgs( Socket s ) + { + m_Socket = s; + m_AllowConnection = true; + } + } + + public class ConnectedEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public ConnectedEventArgs( Mobile m ) + { + m_Mobile = m; + } + } + + public class DisconnectedEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public DisconnectedEventArgs( Mobile m ) + { + m_Mobile = m; + } + } + + public class AnimateRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + private string m_Action; + + public Mobile Mobile{ get{ return m_Mobile; } } + public string Action{ get{ return m_Action; } } + + public AnimateRequestEventArgs( Mobile m, string action ) + { + m_Mobile = m; + m_Action = action; + } + } + + public class CastSpellRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + private Item m_Spellbook; + private int m_SpellID; + + public Mobile Mobile{ get{ return m_Mobile; } } + public Item Spellbook{ get{ return m_Spellbook; } } + public int SpellID{ get{ return m_SpellID; } } + + public CastSpellRequestEventArgs( Mobile m, int spellID, Item book ) + { + m_Mobile = m; + m_Spellbook = book; + m_SpellID = spellID; + } + } + + public class OpenSpellbookRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + private int m_Type; + + public Mobile Mobile{ get{ return m_Mobile; } } + public int Type{ get{ return m_Type; } } + + public OpenSpellbookRequestEventArgs( Mobile m, int type ) + { + m_Mobile = m; + m_Type = type; + } + } + + public class StunRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public StunRequestEventArgs( Mobile m ) + { + m_Mobile = m; + } + } + + public class DisarmRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public DisarmRequestEventArgs( Mobile m ) + { + m_Mobile = m; + } + } + + public class HelpRequestEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public HelpRequestEventArgs( Mobile m ) + { + m_Mobile = m; + } + } + + public class ShutdownEventArgs : EventArgs + { + public ShutdownEventArgs() + { + } + } + + public class CrashedEventArgs : EventArgs + { + private Exception m_Exception; + private bool m_Close; + + public Exception Exception{ get{ return m_Exception; } } + public bool Close{ get{ return m_Close; } set{ m_Close = value; } } + + public CrashedEventArgs( Exception e ) + { + m_Exception = e; + } + } + + public class HungerChangedEventArgs : EventArgs + { + private Mobile m_Mobile; + private int m_OldValue; + + public Mobile Mobile{ get{ return m_Mobile; } } + public int OldValue{ get{ return m_OldValue; } } + + public HungerChangedEventArgs( Mobile mobile, int oldValue ) + { + m_Mobile = mobile; + m_OldValue = oldValue; + } + } + + public class MovementEventArgs : EventArgs + { + private Mobile m_Mobile; + private Direction m_Direction; + private bool m_Blocked; + + public Mobile Mobile{ get{ return m_Mobile; } } + public Direction Direction{ get{ return m_Direction; } } + public bool Blocked{ get{ return m_Blocked; } set{ m_Blocked = value; } } + + private static Queue m_Pool = new Queue(); + + public static MovementEventArgs Create( Mobile mobile, Direction dir ) + { + MovementEventArgs args; + + if ( m_Pool.Count > 0 ) + { + args = (MovementEventArgs)m_Pool.Dequeue(); + + args.m_Mobile = mobile; + args.m_Direction = dir; + args.m_Blocked = false; + } + else + { + args = new MovementEventArgs( mobile, dir ); + } + + return args; + } + + public MovementEventArgs( Mobile mobile, Direction dir ) + { + m_Mobile = mobile; + m_Direction = dir; + } + + public void Free() + { + m_Pool.Enqueue( this ); + } + } + + public class ServerListEventArgs : EventArgs + { + private NetState m_State; + private IAccount m_Account; + private bool m_Rejected; + private List m_Servers; + + public NetState State{ get{ return m_State; } } + public IAccount Account{ get{ return m_Account; } } + public bool Rejected{ get{ return m_Rejected; } set{ m_Rejected = value; } } + public List Servers{ get{ return m_Servers; } } + + public void AddServer( string name, IPEndPoint address ) + { + AddServer( name, 0, TimeZone.CurrentTimeZone, address ); + } + + public void AddServer( string name, int fullPercent, TimeZone tz, IPEndPoint address ) + { + m_Servers.Add( new ServerInfo( name, fullPercent, tz, address ) ); + } + + public ServerListEventArgs( NetState state, IAccount account ) + { + m_State = state; + m_Account = account; + m_Servers = new List(); + } + } + + public struct SkillNameValue + { + private SkillName m_Name; + private int m_Value; + + public SkillName Name{ get{ return m_Name; } } + public int Value{ get{ return m_Value; } } + + public SkillNameValue( SkillName name, int value ) + { + m_Name = name; + m_Value = value; + } + } + + public class CharacterCreatedEventArgs : EventArgs + { + private NetState m_State; + private IAccount m_Account; + private CityInfo m_City; + private SkillNameValue[] m_Skills; + private int m_ShirtHue, m_PantsHue; + private int m_HairID, m_HairHue; + private int m_BeardID, m_BeardHue; + private string m_Name; + private bool m_Female; + private int m_Hue; + private int m_Str, m_Dex, m_Int; + private int m_Profession; + private Mobile m_Mobile; + + private Race m_Race; + + public NetState State{ get{ return m_State; } } + public IAccount Account{ get{ return m_Account; } } + public Mobile Mobile{ get{ return m_Mobile; } set{ m_Mobile = value; } } + public string Name{ get{ return m_Name; } } + public bool Female{ get{ return m_Female; } } + public int Hue{ get{ return m_Hue; } } + public int Str{ get{ return m_Str; } } + public int Dex{ get{ return m_Dex; } } + public int Int{ get{ return m_Int; } } + public CityInfo City{ get{ return m_City; } } + public SkillNameValue[] Skills{ get{ return m_Skills; } } + public int ShirtHue{ get{ return m_ShirtHue; } } + public int PantsHue{ get{ return m_PantsHue; } } + public int HairID{ get{ return m_HairID; } } + public int HairHue{ get{ return m_HairHue; } } + public int BeardID{ get{ return m_BeardID; } } + public int BeardHue{ get{ return m_BeardHue; } } + public int Profession{ get{ return m_Profession; } set{ m_Profession = value; }} + public Race Race { get { return m_Race; } } + + public CharacterCreatedEventArgs( NetState state, IAccount a, string name, bool female, int hue, int str, int dex, int intel, CityInfo city, SkillNameValue[] skills, int shirtHue, int pantsHue, int hairID, int hairHue, int beardID, int beardHue, int profession, Race race ) + { + m_State = state; + m_Account = a; + m_Name = name; + m_Female = female; + m_Hue = hue; + m_Str = str; + m_Dex = dex; + m_Int = intel; + m_City = city; + m_Skills = skills; + m_ShirtHue = shirtHue; + m_PantsHue = pantsHue; + m_HairID = hairID; + m_HairHue = hairHue; + m_BeardID = beardID; + m_BeardHue = beardHue; + m_Profession = profession; + m_Race = race; + } + } + + public class OpenDoorMacroEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public OpenDoorMacroEventArgs( Mobile mobile ) + { + m_Mobile = mobile; + } + } + + public class SpeechEventArgs : EventArgs + { + private Mobile m_Mobile; + private string m_Speech; + private MessageType m_Type; + private int m_Hue; + private int[] m_Keywords; + private bool m_Handled; + private bool m_Blocked; + + public Mobile Mobile{ get{ return m_Mobile; } } + public string Speech{ get{ return m_Speech; } set{ m_Speech = value; } } + public MessageType Type{ get{ return m_Type; } } + public int Hue{ get{ return m_Hue; } } + public int[] Keywords{ get{ return m_Keywords; } } + public bool Handled{ get{ return m_Handled; } set{ m_Handled = value; } } + public bool Blocked{ get{ return m_Blocked; } set{ m_Blocked = value; } } + + public bool HasKeyword( int keyword ) + { + for ( int i = 0; i < m_Keywords.Length; ++i ) + if ( m_Keywords[i] == keyword ) + return true; + + return false; + } + + public SpeechEventArgs( Mobile mobile, string speech, MessageType type, int hue, int[] keywords ) + { + m_Mobile = mobile; + m_Speech = speech; + m_Type = type; + m_Hue = hue; + m_Keywords = keywords; + } + } + + public class LoginEventArgs : EventArgs + { + private Mobile m_Mobile; + + public Mobile Mobile{ get{ return m_Mobile; } } + + public LoginEventArgs( Mobile mobile ) + { + m_Mobile = mobile; + } + } + + public class WorldSaveEventArgs : EventArgs + { + private bool m_Msg; + + public bool Message{ get{ return m_Msg; } } + + public WorldSaveEventArgs( bool msg ) + { + m_Msg = msg; + } + } + + public class FastWalkEventArgs + { + private NetState m_State; + private bool m_Blocked; + + public FastWalkEventArgs( NetState state ) + { + m_State = state; + m_Blocked = false; + } + + public NetState NetState{ get{ return m_State; } } + public bool Blocked{ get{ return m_Blocked; } set{ m_Blocked = value; } } + } + + public class EventSink + { + public static event CharacterCreatedEventHandler CharacterCreated; + public static event OpenDoorMacroEventHandler OpenDoorMacroUsed; + public static event SpeechEventHandler Speech; + public static event LoginEventHandler Login; + public static event ServerListEventHandler ServerList; + public static event MovementEventHandler Movement; + public static event HungerChangedEventHandler HungerChanged; + public static event CrashedEventHandler Crashed; + public static event ShutdownEventHandler Shutdown; + public static event HelpRequestEventHandler HelpRequest; + public static event DisarmRequestEventHandler DisarmRequest; + public static event StunRequestEventHandler StunRequest; + public static event OpenSpellbookRequestEventHandler OpenSpellbookRequest; + public static event CastSpellRequestEventHandler CastSpellRequest; + public static event AnimateRequestEventHandler AnimateRequest; + public static event LogoutEventHandler Logout; + public static event SocketConnectEventHandler SocketConnect; + public static event ConnectedEventHandler Connected; + public static event DisconnectedEventHandler Disconnected; + public static event RenameRequestEventHandler RenameRequest; + public static event PlayerDeathEventHandler PlayerDeath; + public static event VirtueGumpRequestEventHandler VirtueGumpRequest; + public static event VirtueItemRequestEventHandler VirtueItemRequest; + public static event VirtueMacroRequestEventHandler VirtueMacroRequest; + public static event ChatRequestEventHandler ChatRequest; + public static event AccountLoginEventHandler AccountLogin; + public static event PaperdollRequestEventHandler PaperdollRequest; + public static event ProfileRequestEventHandler ProfileRequest; + public static event ChangeProfileRequestEventHandler ChangeProfileRequest; + public static event AggressiveActionEventHandler AggressiveAction; + public static event CommandEventHandler Command; + public static event GameLoginEventHandler GameLogin; + public static event DeleteRequestEventHandler DeleteRequest; + public static event WorldLoadEventHandler WorldLoad; + public static event WorldSaveEventHandler WorldSave; + public static event SetAbilityEventHandler SetAbility; + public static event FastWalkEventHandler FastWalk; + public static event CreateGuildHandler CreateGuild; + public static event ServerStartedEventHandler ServerStarted; + public static event GuildGumpRequestHandler GuildGumpRequest; + public static event QuestGumpRequestHandler QuestGumpRequest; + + public static void InvokeServerStarted() + { + if ( ServerStarted != null ) + ServerStarted(); + } + + public static BaseGuild InvokeCreateGuild( CreateGuildEventArgs e ) + { + if ( CreateGuild != null ) + return CreateGuild( e ); + else + return null; + } + + public static void InvokeSetAbility( SetAbilityEventArgs e ) + { + if ( SetAbility != null ) + SetAbility( e ); + } + + public static void InvokeGuildGumpRequest( GuildGumpRequestArgs e ) + { + if( GuildGumpRequest != null ) + GuildGumpRequest( e ); + } + + public static void InvokeQuestGumpRequest( QuestGumpRequestArgs e ) + { + if( QuestGumpRequest != null ) + QuestGumpRequest( e ); + } + + public static void InvokeFastWalk( FastWalkEventArgs e ) + { + if ( FastWalk != null ) + FastWalk( e ); + } + + public static void InvokeDeleteRequest( DeleteRequestEventArgs e ) + { + if ( DeleteRequest != null ) + DeleteRequest( e ); + } + + public static void InvokeGameLogin( GameLoginEventArgs e ) + { + if ( GameLogin != null ) + GameLogin( e ); + } + + public static void InvokeCommand( CommandEventArgs e ) + { + if ( Command != null ) + Command( e ); + } + + public static void InvokeAggressiveAction( AggressiveActionEventArgs e ) + { + if ( AggressiveAction != null ) + AggressiveAction( e ); + } + + public static void InvokeProfileRequest( ProfileRequestEventArgs e ) + { + if ( ProfileRequest != null ) + ProfileRequest( e ); + } + + public static void InvokeChangeProfileRequest( ChangeProfileRequestEventArgs e ) + { + if ( ChangeProfileRequest != null ) + ChangeProfileRequest( e ); + } + + public static void InvokePaperdollRequest( PaperdollRequestEventArgs e ) + { + if ( PaperdollRequest != null ) + PaperdollRequest( e ); + } + + public static void InvokeAccountLogin( AccountLoginEventArgs e ) + { + if ( AccountLogin != null ) + AccountLogin( e ); + } + + public static void InvokeChatRequest( ChatRequestEventArgs e ) + { + if ( ChatRequest != null ) + ChatRequest( e ); + } + + public static void InvokeVirtueItemRequest( VirtueItemRequestEventArgs e ) + { + if ( VirtueItemRequest != null ) + VirtueItemRequest( e ); + } + + public static void InvokeVirtueGumpRequest( VirtueGumpRequestEventArgs e ) + { + if ( VirtueGumpRequest != null ) + VirtueGumpRequest( e ); + } + + public static void InvokeVirtueMacroRequest( VirtueMacroRequestEventArgs e ) + { + if ( VirtueMacroRequest != null ) + VirtueMacroRequest( e ); + } + + public static void InvokePlayerDeath( PlayerDeathEventArgs e ) + { + if ( PlayerDeath != null ) + PlayerDeath( e ); + } + + public static void InvokeRenameRequest( RenameRequestEventArgs e ) + { + if ( RenameRequest != null ) + RenameRequest( e ); + } + + public static void InvokeLogout( LogoutEventArgs e ) + { + if ( Logout != null ) + Logout( e ); + } + + public static void InvokeSocketConnect( SocketConnectEventArgs e ) + { + if ( SocketConnect != null ) + SocketConnect( e ); + } + + public static void InvokeConnected( ConnectedEventArgs e ) + { + if ( Connected != null ) + Connected( e ); + } + + public static void InvokeDisconnected( DisconnectedEventArgs e ) + { + if ( Disconnected != null ) + Disconnected( e ); + } + + public static void InvokeAnimateRequest( AnimateRequestEventArgs e ) + { + if ( AnimateRequest != null ) + AnimateRequest( e ); + } + + public static void InvokeCastSpellRequest( CastSpellRequestEventArgs e ) + { + if ( CastSpellRequest != null ) + CastSpellRequest( e ); + } + + public static void InvokeOpenSpellbookRequest( OpenSpellbookRequestEventArgs e ) + { + if ( OpenSpellbookRequest != null ) + OpenSpellbookRequest( e ); + } + + public static void InvokeDisarmRequest( DisarmRequestEventArgs e ) + { + if ( DisarmRequest != null ) + DisarmRequest( e ); + } + + public static void InvokeStunRequest( StunRequestEventArgs e ) + { + if ( StunRequest != null ) + StunRequest( e ); + } + + public static void InvokeHelpRequest( HelpRequestEventArgs e ) + { + if ( HelpRequest != null ) + HelpRequest( e ); + } + + public static void InvokeShutdown( ShutdownEventArgs e ) + { + if ( Shutdown != null ) + Shutdown( e ); + } + + public static void InvokeCrashed( CrashedEventArgs e ) + { + if ( Crashed != null ) + Crashed( e ); + } + + public static void InvokeHungerChanged( HungerChangedEventArgs e ) + { + if ( HungerChanged != null ) + HungerChanged( e ); + } + + public static void InvokeMovement( MovementEventArgs e ) + { + if ( Movement != null ) + Movement( e ); + } + + public static void InvokeServerList( ServerListEventArgs e ) + { + if ( ServerList != null ) + ServerList( e ); + } + + public static void InvokeLogin( LoginEventArgs e ) + { + if ( Login != null ) + Login( e ); + } + + public static void InvokeSpeech( SpeechEventArgs e ) + { + if ( Speech != null ) + Speech( e ); + } + + public static void InvokeCharacterCreated( CharacterCreatedEventArgs e ) + { + if ( CharacterCreated != null ) + CharacterCreated( e ); + } + + public static void InvokeOpenDoorMacroUsed( OpenDoorMacroEventArgs e ) + { + if ( OpenDoorMacroUsed != null ) + OpenDoorMacroUsed( e ); + } + + public static void InvokeWorldLoad() + { + if ( WorldLoad != null ) + WorldLoad(); + } + + public static void InvokeWorldSave( WorldSaveEventArgs e ) + { + if ( WorldSave != null ) + WorldSave( e ); + } + + public static void Reset() + { + CharacterCreated = null; + OpenDoorMacroUsed = null; + Speech = null; + Login = null; + ServerList = null; + Movement = null; + HungerChanged = null; + Crashed = null; + Shutdown = null; + HelpRequest = null; + DisarmRequest = null; + StunRequest = null; + OpenSpellbookRequest = null; + CastSpellRequest = null; + AnimateRequest = null; + Logout = null; + SocketConnect = null; + Connected = null; + Disconnected = null; + RenameRequest = null; + PlayerDeath = null; + VirtueGumpRequest = null; + VirtueItemRequest = null; + VirtueMacroRequest = null; + ChatRequest = null; + AccountLogin = null; + PaperdollRequest = null; + ProfileRequest = null; + ChangeProfileRequest = null; + AggressiveAction = null; + Command = null; + GameLogin = null; + DeleteRequest = null; + WorldLoad = null; + WorldSave = null; + SetAbility = null; + GuildGumpRequest = null; + QuestGumpRequest = null; + } + } +} \ No newline at end of file diff --git a/Server/ExpansionInfo.cs b/Server/ExpansionInfo.cs new file mode 100644 index 000000000..dd0d76516 --- /dev/null +++ b/Server/ExpansionInfo.cs @@ -0,0 +1,104 @@ +/*************************************************************************** + * ExpansionInfo.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ExpansionInfo.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public enum Expansion + { + None, + /* + T2A, + UOR, + LBR, + UOTD, + */ + AOS, + SE, + ML + } + public class ExpansionInfo + { + private string m_Name; + private int m_ID, m_NetStateFlag, m_SupportedFeatures, m_CharListFlags, m_CustomHousingFlag; + + private ClientVersion m_RequiredClient; //Used as an alternative to the flags + + public string Name{ get{ return m_Name; } } + public int ID{ get{ return m_ID; } } + public int NetStateFlag{ get{ return m_NetStateFlag; } } + public int SupportedFeatures{ get{ return m_SupportedFeatures; } } + public int CharacterListFlags { get { return m_CharListFlags; } } + public int CustomHousingFlag { get{ return m_CustomHousingFlag; } } + public ClientVersion RequiredClient { get { return m_RequiredClient; } } + + public ExpansionInfo( int id, string name, int netStateFlag, int supportedFeatures, int charListFlags, int customHousingFlag ) + { + m_Name = name; + m_ID = id; + m_NetStateFlag = netStateFlag; + m_SupportedFeatures = supportedFeatures; + m_CharListFlags = charListFlags; + m_CustomHousingFlag = customHousingFlag; + } + + public ExpansionInfo( int id, string name, ClientVersion requiredClient, int supportedFeatures, int charListFlags, int customHousingFlag ) + { + m_Name = name; + m_ID = id; + m_SupportedFeatures = supportedFeatures; + m_CharListFlags = charListFlags; + m_CustomHousingFlag = customHousingFlag; + m_RequiredClient = requiredClient; + } + + public static ExpansionInfo[] Table { get { return m_Table; } } + private static ExpansionInfo[] m_Table = new ExpansionInfo[] + { + new ExpansionInfo( 0, "None" , 0x00, 0x0003, 0x008, 0x00 ), + new ExpansionInfo( 1, "Age of Shadows" , 0x08, 0x801F, 0x028, 0x20 ), + new ExpansionInfo( 2, "Samurai Empire" , 0x10, 0x805F, 0x0A8, 0x60 ), //0x40 | 0x20 = 0x60 + new ExpansionInfo( 3, "Mondain's Legacy", new ClientVersion( "5.0.0a" ), 0x80DF, 0x1A8, 0xE0 ) //0x80 | 0x60 = 0xE0 + }; + + public static ExpansionInfo GetInfo( Expansion ex ) + { + return GetInfo( (int)ex ); + } + + public static ExpansionInfo GetInfo( int ex ) + { + int v = (int)ex; + + if( v < 0 || v >= m_Table.Length ) + v = 0; + + return m_Table[v]; + } + + public static ExpansionInfo CurrentExpansion { get { return GetInfo( Core.Expansion ); } } + + public override string ToString() + { + return m_Name; + } + } +} diff --git a/Server/Geometry.cs b/Server/Geometry.cs new file mode 100644 index 000000000..4da32882e --- /dev/null +++ b/Server/Geometry.cs @@ -0,0 +1,638 @@ +/*************************************************************************** + * Geometry.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Geometry.cs 36 2006-01-18 10:19:46Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + [Parsable] + public struct Point2D : IPoint2D, IComparable, IComparable + { + internal int m_X; + internal int m_Y; + + public static readonly Point2D Zero = new Point2D( 0, 0 ); + + public Point2D( int x, int y ) + { + m_X = x; + m_Y = y; + } + + public Point2D( IPoint2D p ) : this( p.X, p.Y ) + { + } + + [CommandProperty( AccessLevel.Counselor )] + public int X + { + get + { + return m_X; + } + set + { + m_X = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Y + { + get + { + return m_Y; + } + set + { + m_Y = value; + } + } + + public override string ToString() + { + return String.Format( "({0}, {1})", m_X, m_Y ); + } + + public static Point2D Parse( string value ) + { + int start = value.IndexOf( '(' ); + int end = value.IndexOf( ',', start + 1 ); + + string param1 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ')', start + 1 ); + + string param2 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + return new Point2D( Convert.ToInt32( param1 ), Convert.ToInt32( param2 ) ); + } + + public int CompareTo( Point2D other ) + { + int v = ( m_X.CompareTo( other.m_X ) ); + + if ( v == 0 ) + v = ( m_Y.CompareTo( other.m_Y ) ); + + return v; + } + + public int CompareTo( object other ) + { + if ( other is Point2D ) + return this.CompareTo( (Point2D) other ); + else if ( other == null ) + return -1; + + throw new ArgumentException(); + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is IPoint2D) ) return false; + + IPoint2D p = (IPoint2D)o; + + return m_X == p.X && m_Y == p.Y; + } + + public override int GetHashCode() + { + return m_X ^ m_Y; + } + + public static bool operator == ( Point2D l, Point2D r ) + { + return l.m_X == r.m_X && l.m_Y == r.m_Y; + } + + public static bool operator != ( Point2D l, Point2D r ) + { + return l.m_X != r.m_X || l.m_Y != r.m_Y; + } + + public static bool operator == ( Point2D l, IPoint2D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X == r.X && l.m_Y == r.Y; + } + + public static bool operator != ( Point2D l, IPoint2D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X !=r.X || l.m_Y != r.Y; + } + + public static bool operator > ( Point2D l, Point2D r ) + { + return l.m_X > r.m_X && l.m_Y > r.m_Y; + } + + public static bool operator > ( Point2D l, Point3D r ) + { + return l.m_X > r.m_X && l.m_Y > r.m_Y; + } + + public static bool operator > ( Point2D l, IPoint2D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X > r.X && l.m_Y > r.Y; + } + + public static bool operator < ( Point2D l, Point2D r ) + { + return l.m_X < r.m_X && l.m_Y < r.m_Y; + } + + public static bool operator < ( Point2D l, Point3D r ) + { + return l.m_X < r.m_X && l.m_Y < r.m_Y; + } + + public static bool operator < ( Point2D l, IPoint2D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X < r.X && l.m_Y < r.Y; + } + + public static bool operator >= ( Point2D l, Point2D r ) + { + return l.m_X >= r.m_X && l.m_Y >= r.m_Y; + } + + public static bool operator >= ( Point2D l, Point3D r ) + { + return l.m_X >= r.m_X && l.m_Y >= r.m_Y; + } + + public static bool operator >= ( Point2D l, IPoint2D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X >= r.X && l.m_Y >= r.Y; + } + + public static bool operator <= ( Point2D l, Point2D r ) + { + return l.m_X <= r.m_X && l.m_Y <= r.m_Y; + } + + public static bool operator <= ( Point2D l, Point3D r ) + { + return l.m_X <= r.m_X && l.m_Y <= r.m_Y; + } + + public static bool operator <= ( Point2D l, IPoint2D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X <= r.X && l.m_Y <= r.Y; + } + } + + [Parsable] + public struct Point3D : IPoint3D, IComparable, IComparable + { + internal int m_X; + internal int m_Y; + internal int m_Z; + + public static readonly Point3D Zero = new Point3D( 0, 0, 0 ); + + public Point3D( int x, int y, int z ) + { + m_X = x; + m_Y = y; + m_Z = z; + } + + public Point3D( IPoint3D p ) + : this( p.X, p.Y, p.Z ) + { + } + + public Point3D( IPoint2D p, int z ) + : this( p.X, p.Y, z ) + { + } + + [CommandProperty( AccessLevel.Counselor )] + public int X + { + get + { + return m_X; + } + set + { + m_X = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Y + { + get + { + return m_Y; + } + set + { + m_Y = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Z + { + get + { + return m_Z; + } + set + { + m_Z = value; + } + } + + public override string ToString() + { + return String.Format( "({0}, {1}, {2})", m_X, m_Y, m_Z ); + } + + public override bool Equals( object o ) + { + if ( o == null || !( o is IPoint3D ) ) + return false; + + IPoint3D p = (IPoint3D) o; + + return m_X == p.X && m_Y == p.Y && m_Z == p.Z; + } + + public override int GetHashCode() + { + return m_X ^ m_Y ^ m_Z; + } + + public static Point3D Parse( string value ) + { + int start = value.IndexOf( '(' ); + int end = value.IndexOf( ',', start + 1 ); + + string param1 = value.Substring( start + 1, end - ( start + 1 ) ).Trim(); + + start = end; + end = value.IndexOf( ',', start + 1 ); + + string param2 = value.Substring( start + 1, end - ( start + 1 ) ).Trim(); + + start = end; + end = value.IndexOf( ')', start + 1 ); + + string param3 = value.Substring( start + 1, end - ( start + 1 ) ).Trim(); + + return new Point3D( Convert.ToInt32( param1 ), Convert.ToInt32( param2 ), Convert.ToInt32( param3 ) ); + } + + public static bool operator ==( Point3D l, Point3D r ) + { + return l.m_X == r.m_X && l.m_Y == r.m_Y && l.m_Z == r.m_Z; + } + + public static bool operator !=( Point3D l, Point3D r ) + { + return l.m_X != r.m_X || l.m_Y != r.m_Y || l.m_Z != r.m_Z; + } + + public static bool operator ==( Point3D l, IPoint3D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X == r.X && l.m_Y == r.Y && l.m_Z == r.Z; + } + + public static bool operator !=( Point3D l, IPoint3D r ) + { + if ( Object.ReferenceEquals( r, null ) ) + return false; + + return l.m_X != r.X || l.m_Y != r.Y || l.m_Z != r.Z; + } + + public int CompareTo( Point3D other ) + { + int v = ( m_X.CompareTo( other.m_X ) ); + + if ( v == 0 ) + { + v = ( m_Y.CompareTo( other.m_Y ) ); + + if ( v == 0 ) + v = ( m_Z.CompareTo( other.m_Z ) ); + } + + return v; + } + + public int CompareTo( object other ) + { + if ( other is Point3D ) + return this.CompareTo( (Point3D) other ); + else if ( other == null ) + return -1; + + throw new ArgumentException(); + } + } + + [NoSort] + [Parsable] + [PropertyObject] + public struct Rectangle2D + { + private Point2D m_Start; + private Point2D m_End; + + public Rectangle2D( IPoint2D start, IPoint2D end ) + { + m_Start = new Point2D( start ); + m_End = new Point2D( end ); + } + + public Rectangle2D( int x, int y, int width, int height ) + { + m_Start = new Point2D( x, y ); + m_End = new Point2D( x + width, y + height ); + } + + public void Set( int x, int y, int width, int height ) + { + m_Start = new Point2D( x, y ); + m_End = new Point2D( x + width, y + height ); + } + + public static Rectangle2D Parse( string value ) + { + int start = value.IndexOf( '(' ); + int end = value.IndexOf( ',', start + 1 ); + + string param1 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ',', start + 1 ); + + string param2 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ',', start + 1 ); + + string param3 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + start = end; + end = value.IndexOf( ')', start + 1 ); + + string param4 = value.Substring( start + 1, end - (start + 1) ).Trim(); + + return new Rectangle2D( Convert.ToInt32( param1 ), Convert.ToInt32( param2 ), Convert.ToInt32( param3 ), Convert.ToInt32( param4 ) ); + } + + [CommandProperty( AccessLevel.Counselor )] + public Point2D Start + { + get + { + return m_Start; + } + set + { + m_Start = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public Point2D End + { + get + { + return m_End; + } + set + { + m_End = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int X + { + get + { + return m_Start.m_X; + } + set + { + m_Start.m_X = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Y + { + get + { + return m_Start.m_Y; + } + set + { + m_Start.m_Y = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Width + { + get + { + return m_End.m_X - m_Start.m_X; + } + set + { + m_End.m_X = m_Start.m_X + value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Height + { + get + { + return m_End.m_Y - m_Start.m_Y; + } + set + { + m_End.m_Y = m_Start.m_Y + value; + } + } + + public void MakeHold( Rectangle2D r ) + { + if ( r.m_Start.m_X < m_Start.m_X ) + m_Start.m_X = r.m_Start.m_X; + + if ( r.m_Start.m_Y < m_Start.m_Y ) + m_Start.m_Y = r.m_Start.m_Y; + + if ( r.m_End.m_X > m_End.m_X ) + m_End.m_X = r.m_End.m_X; + + if ( r.m_End.m_Y > m_End.m_Y ) + m_End.m_Y = r.m_End.m_Y; + } + + public bool Contains( Point3D p ) + { + return ( m_Start.m_X <= p.m_X && m_Start.m_Y <= p.m_Y && m_End.m_X > p.m_X && m_End.m_Y > p.m_Y ); + //return ( m_Start <= p && m_End > p ); + } + + public bool Contains( Point2D p ) + { + return ( m_Start.m_X <= p.m_X && m_Start.m_Y <= p.m_Y && m_End.m_X > p.m_X && m_End.m_Y > p.m_Y ); + //return ( m_Start <= p && m_End > p ); + } + + public bool Contains( IPoint2D p ) + { + return ( m_Start <= p && m_End > p ); + } + + public override string ToString() + { + return String.Format( "({0}, {1})+({2}, {3})", X, Y, Width, Height ); + } + } + + [NoSort] + [PropertyObject] + public struct Rectangle3D + { + private Point3D m_Start; + private Point3D m_End; + + public Rectangle3D( Point3D start, Point3D end ) + { + m_Start = start; + m_End = end; + } + + public Rectangle3D( int x, int y, int z, int width, int height, int depth ) + { + m_Start = new Point3D( x, y, z ); + m_End = new Point3D( x + width, y + height, z + depth ); + } + + [CommandProperty( AccessLevel.Counselor )] + public Point3D Start + { + get + { + return m_Start; + } + set + { + m_Start = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public Point3D End + { + get + { + return m_End; + } + set + { + m_End = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Width + { + get + { + return m_End.X - m_Start.X; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Height + { + get + { + return m_End.Y - m_Start.Y; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Depth + { + get + { + return m_End.Z - m_Start.Z; + } + } + + public bool Contains( Point3D p ) + { + return ( p.m_X >= m_Start.m_X ) + && ( p.m_X < m_End.m_X ) + && ( p.m_Y >= m_Start.m_Y ) + && ( p.m_Y < m_End.m_Y ) + && ( p.m_Z >= m_Start.m_Z ) + && ( p.m_Z < m_End.m_Z ); + } + + public bool Contains( IPoint3D p ) + { + return ( p.X >= m_Start.m_X ) + && ( p.X < m_End.m_X ) + && ( p.Y >= m_Start.m_Y ) + && ( p.Y < m_End.m_Y ) + && ( p.Z >= m_Start.m_Z ) + && ( p.Z < m_End.m_Z ); + } + } +} \ No newline at end of file diff --git a/Server/Guild.cs b/Server/Guild.cs new file mode 100644 index 000000000..4d7124671 --- /dev/null +++ b/Server/Guild.cs @@ -0,0 +1,139 @@ +/*************************************************************************** + * Guild.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Guild.cs 156 2006-06-11 09:36:25Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; + +namespace Server.Guilds +{ + public enum GuildType + { + Regular, + Chaos, + Order + } + + public abstract class BaseGuild : ISerializable + { + private int m_Id; + + public BaseGuild( int Id )//serialization ctor + { + m_Id = Id; + m_GuildList.Add( m_Id, this ); + if ( m_Id+1 > m_NextID ) + m_NextID = m_Id + 1; + } + + public BaseGuild() + { + m_Id = m_NextID++; + m_GuildList.Add( m_Id, this ); + } + + public int Id { get { return m_Id; } } + + int ISerializable.TypeReference { + get { return 0; } + } + + int ISerializable.SerialIdentity { + get { return m_Id; } + } + + public abstract void Deserialize( GenericReader reader ); + public abstract void Serialize( GenericWriter writer ); + + public abstract string Abbreviation { get; set; } + public abstract string Name { get; set; } + public abstract GuildType Type { get; set; } + public abstract bool Disbanded{ get; } + public abstract void OnDelete( Mobile mob ); + + private static Dictionary m_GuildList = new Dictionary(); + private static int m_NextID = 1; + + public static Dictionary List + { + get + { + return m_GuildList; + } + } + + public static BaseGuild Find( int id ) + { + BaseGuild g; + + m_GuildList.TryGetValue( id, out g ); + + return g; + } + + public static BaseGuild FindByName( string name ) + { + foreach ( BaseGuild g in m_GuildList.Values ) + { + if ( g.Name == name ) + return g; + } + + return null; + } + + public static BaseGuild FindByAbbrev( string abbr ) + { + foreach ( BaseGuild g in m_GuildList.Values ) + { + if ( g.Abbreviation == abbr ) + return g; + } + + return null; + } + + public static List Search( string find ) + { + string[] words = find.ToLower().Split( ' ' ); + List results = new List(); + + foreach ( BaseGuild g in m_GuildList.Values ) + { + bool match = true; + string name = g.Name.ToLower(); + for (int i=0;i m_Entries; + private List m_Strings; + + internal int m_TextEntries, m_Switches; + + private static int m_NextSerial = 1; + + private int m_Serial; + private int m_TypeID; + private int m_X, m_Y; + + private bool m_Dragable = true; + private bool m_Closable = true; + private bool m_Resizable = true; + private bool m_Disposable = true; + + public static int GetTypeID( Type type ) + { + return type.FullName.GetHashCode(); + } + + public Gump( int x, int y ) + { + do + { + m_Serial = m_NextSerial++; + } while ( m_Serial == 0 ); // standard client apparently doesn't send a gump response packet if serial == 0 + + m_X = x; + m_Y = y; + + m_TypeID = GetTypeID( this.GetType() ); + + m_Entries = new List(); + m_Strings = new List(); + } + + public void Invalidate() + { + //if ( m_Strings.Count > 0 ) + // m_Strings.Clear(); + } + + public int TypeID + { + get + { + return m_TypeID; + } + } + + public List Entries + { + get{ return m_Entries; } + } + + public int Serial + { + get + { + return m_Serial; + } + set + { + if ( m_Serial != value ) + { + m_Serial = value; + Invalidate(); + } + } + } + + public int X + { + get + { + return m_X; + } + set + { + if ( m_X != value ) + { + m_X = value; + Invalidate(); + } + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + if ( m_Y != value ) + { + m_Y = value; + Invalidate(); + } + } + } + + public bool Disposable + { + get + { + return m_Disposable; + } + set + { + if ( m_Disposable != value ) + { + m_Disposable = value; + Invalidate(); + } + } + } + + public bool Resizable + { + get + { + return m_Resizable; + } + set + { + if ( m_Resizable != value ) + { + m_Resizable = value; + Invalidate(); + } + } + } + + public bool Dragable + { + get + { + return m_Dragable; + } + set + { + if ( m_Dragable != value ) + { + m_Dragable = value; + Invalidate(); + } + } + } + + public bool Closable + { + get + { + return m_Closable; + } + set + { + if ( m_Closable != value ) + { + m_Closable = value; + Invalidate(); + } + } + } + + public void AddPage( int page ) + { + Add( new GumpPage( page ) ); + } + + public void AddAlphaRegion( int x, int y, int width, int height ) + { + Add( new GumpAlphaRegion( x, y, width, height ) ); + } + + public void AddBackground( int x, int y, int width, int height, int gumpID ) + { + Add( new GumpBackground( x, y, width, height, gumpID ) ); + } + + public void AddButton( int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param ) + { + Add( new GumpButton( x, y, normalID, pressedID, buttonID, type, param ) ); + } + + public void AddCheck( int x, int y, int inactiveID, int activeID, bool initialState, int switchID ) + { + Add( new GumpCheck( x, y, inactiveID, activeID, initialState, switchID ) ); + } + + public void AddGroup( int group ) + { + Add( new GumpGroup( group ) ); + } + + public void AddTooltip( int number ) + { + Add( new GumpTooltip( number ) ); + } + + public void AddHtml( int x, int y, int width, int height, string text, bool background, bool scrollbar ) + { + Add( new GumpHtml( x, y, width, height, text, background, scrollbar ) ); + } + + public void AddHtmlLocalized( int x, int y, int width, int height, int number, bool background, bool scrollbar ) + { + Add( new GumpHtmlLocalized( x, y, width, height, number, background, scrollbar ) ); + } + + public void AddHtmlLocalized( int x, int y, int width, int height, int number, int color, bool background, bool scrollbar ) + { + Add( new GumpHtmlLocalized( x, y, width, height, number, color, background, scrollbar ) ); + } + + public void AddHtmlLocalized( int x, int y, int width, int height, int number, string args, int color, bool background, bool scrollbar ) + { + Add( new GumpHtmlLocalized( x, y, width, height, number, args, color, background, scrollbar ) ); + } + + public void AddImage( int x, int y, int gumpID ) + { + Add( new GumpImage( x, y, gumpID ) ); + } + + public void AddImage( int x, int y, int gumpID, int hue ) + { + Add( new GumpImage( x, y, gumpID, hue ) ); + } + + public void AddImageTiled( int x, int y, int width, int height, int gumpID ) + { + Add( new GumpImageTiled( x, y, width, height, gumpID ) ); + } + + public void AddImageTiledButton( int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param, int itemID, int hue, int width, int height ) + { + Add( new GumpImageTileButton( x, y, normalID, pressedID, buttonID, type, param, itemID, hue, width, height ) ); + } + public void AddImageTiledButton( int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param, int itemID, int hue, int width, int height, int localizedTooltip ) + { + Add( new GumpImageTileButton( x, y, normalID, pressedID, buttonID, type, param, itemID, hue, width, height, localizedTooltip ) ); + } + + public void AddItem( int x, int y, int itemID ) + { + Add( new GumpItem( x, y, itemID ) ); + } + + public void AddItem( int x, int y, int itemID, int hue ) + { + Add( new GumpItem( x, y, itemID, hue ) ); + } + + public void AddLabel( int x, int y, int hue, string text ) + { + Add( new GumpLabel( x, y, hue, text ) ); + } + + public void AddLabelCropped( int x, int y, int width, int height, int hue, string text ) + { + Add( new GumpLabelCropped( x, y, width, height, hue, text ) ); + } + + public void AddRadio( int x, int y, int inactiveID, int activeID, bool initialState, int switchID ) + { + Add( new GumpRadio( x, y, inactiveID, activeID, initialState, switchID ) ); + } + + public void AddTextEntry( int x, int y, int width, int height, int hue, int entryID, string initialText ) + { + Add( new GumpTextEntry( x, y, width, height, hue, entryID, initialText ) ); + } + + public void Add( GumpEntry g ) + { + if ( g.Parent != this ) + { + g.Parent = this; + } + else if ( !m_Entries.Contains( g ) ) + { + Invalidate(); + m_Entries.Add( g ); + } + } + + public void Remove( GumpEntry g ) + { + Invalidate(); + m_Entries.Remove( g ); + g.Parent = null; + } + + public int Intern( string value ) + { + int indexOf = m_Strings.IndexOf( value ); + + if ( indexOf >= 0 ) + { + return indexOf; + } + else + { + Invalidate(); + m_Strings.Add( value ); + return m_Strings.Count - 1; + } + } + + public void SendTo( NetState state ) + { + state.AddGump( this ); + state.Send( Compile( state ) ); + } + + public static byte[] StringToBuffer( string str ) + { + return Encoding.ASCII.GetBytes( str ); + } + + private static byte[] m_BeginLayout = StringToBuffer( "{ " ); + private static byte[] m_EndLayout = StringToBuffer( " }" ); + + private static byte[] m_NoMove = StringToBuffer( "{ nomove }" ); + private static byte[] m_NoClose = StringToBuffer( "{ noclose }" ); + private static byte[] m_NoDispose = StringToBuffer( "{ nodispose }" ); + private static byte[] m_NoResize = StringToBuffer( "{ noresize }" ); + + public static readonly ClientVersion UnpackVersion = new ClientVersion( "5.0.0a" ); + + private Packet Compile() + { + return Compile( null ); + } + + private Packet Compile( NetState ns ) + { + IGumpWriter disp; + + if ( ns != null && ns.Version != null && ns.Version >= UnpackVersion ) + disp = new DisplayGumpPacked( this ); + else + disp = new DisplayGumpFast( this ); + + if ( !m_Dragable ) + disp.AppendLayout( m_NoMove ); + + if ( !m_Closable ) + disp.AppendLayout( m_NoClose ); + + if ( !m_Disposable ) + disp.AppendLayout( m_NoDispose ); + + if ( !m_Resizable ) + disp.AppendLayout( m_NoResize ); + + int count = m_Entries.Count; + GumpEntry e; + + for ( int i = 0; i < count; ++i ) + { + e = m_Entries[i]; + + disp.AppendLayout( m_BeginLayout ); + e.AppendTo( disp ); + disp.AppendLayout( m_EndLayout ); + } + + disp.WriteStrings( m_Strings ); + + disp.Flush(); + + m_TextEntries = disp.TextEntries; + m_Switches = disp.Switches; + + return disp as Packet; + } + + public virtual void OnResponse( NetState sender, RelayInfo info ) + { + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpAlphaRegion.cs b/Server/Gumps/GumpAlphaRegion.cs new file mode 100644 index 000000000..955be819c --- /dev/null +++ b/Server/Gumps/GumpAlphaRegion.cs @@ -0,0 +1,103 @@ +/*************************************************************************** + * GumpAlphaRegion.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpAlphaRegion.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpAlphaRegion : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public GumpAlphaRegion( int x, int y, int width, int height ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + } + + public override string Compile() + { + return String.Format( "{{ checkertrans {0} {1} {2} {3} }}", m_X, m_Y, m_Width, m_Height ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "checkertrans" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpBackground.cs b/Server/Gumps/GumpBackground.cs new file mode 100644 index 000000000..d68363d84 --- /dev/null +++ b/Server/Gumps/GumpBackground.cs @@ -0,0 +1,118 @@ +/*************************************************************************** + * GumpBackground.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpBackground.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpBackground : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + private int m_GumpID; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public int GumpID + { + get + { + return m_GumpID; + } + set + { + Delta( ref m_GumpID, value ); + } + } + + public GumpBackground( int x, int y, int width, int height, int gumpID ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_GumpID = gumpID; + } + + public override string Compile() + { + return String.Format( "{{ resizepic {0} {1} {2} {3} {4} }}", m_X, m_Y, m_GumpID, m_Width, m_Height ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "resizepic" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_GumpID ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpButton.cs b/Server/Gumps/GumpButton.cs new file mode 100644 index 000000000..e7100f682 --- /dev/null +++ b/Server/Gumps/GumpButton.cs @@ -0,0 +1,164 @@ +/*************************************************************************** + * GumpButton.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpButton.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public enum GumpButtonType + { + Page = 0, + Reply = 1 + } + + public class GumpButton : GumpEntry + { + private int m_X, m_Y; + private int m_ID1, m_ID2; + private int m_ButtonID; + private GumpButtonType m_Type; + private int m_Param; + + public GumpButton( int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param ) + { + m_X = x; + m_Y = y; + m_ID1 = normalID; + m_ID2 = pressedID; + m_ButtonID = buttonID; + m_Type = type; + m_Param = param; + } + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int NormalID + { + get + { + return m_ID1; + } + set + { + Delta( ref m_ID1, value ); + } + } + + public int PressedID + { + get + { + return m_ID2; + } + set + { + Delta( ref m_ID2, value ); + } + } + + public int ButtonID + { + get + { + return m_ButtonID; + } + set + { + Delta( ref m_ButtonID, value ); + } + } + + public GumpButtonType Type + { + get + { + return m_Type; + } + set + { + if ( m_Type != value ) + { + m_Type = value; + + Gump parent = Parent; + + if ( parent != null ) + { + parent.Invalidate(); + } + } + } + } + + public int Param + { + get + { + return m_Param; + } + set + { + Delta( ref m_Param, value ); + } + } + + public override string Compile() + { + return String.Format( "{{ button {0} {1} {2} {3} {4} {5} {6} }}", m_X, m_Y, m_ID1, m_ID2, (int)m_Type, m_Param, m_ButtonID ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "button" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_ID1 ); + disp.AppendLayout( m_ID2 ); + disp.AppendLayout( (int)m_Type ); + disp.AppendLayout( m_Param ); + disp.AppendLayout( m_ButtonID ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpCheck.cs b/Server/Gumps/GumpCheck.cs new file mode 100644 index 000000000..41b7ab83f --- /dev/null +++ b/Server/Gumps/GumpCheck.cs @@ -0,0 +1,135 @@ +/*************************************************************************** + * GumpCheck.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpCheck.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpCheck : GumpEntry + { + private int m_X, m_Y; + private int m_ID1, m_ID2; + private bool m_InitialState; + private int m_SwitchID; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int InactiveID + { + get + { + return m_ID1; + } + set + { + Delta( ref m_ID1, value ); + } + } + + public int ActiveID + { + get + { + return m_ID2; + } + set + { + Delta( ref m_ID2, value ); + } + } + + public bool InitialState + { + get + { + return m_InitialState; + } + set + { + Delta( ref m_InitialState, value ); + } + } + + public int SwitchID + { + get + { + return m_SwitchID; + } + set + { + Delta( ref m_SwitchID, value ); + } + } + + public GumpCheck( int x, int y, int inactiveID, int activeID, bool initialState, int switchID ) + { + m_X = x; + m_Y = y; + m_ID1 = inactiveID; + m_ID2 = activeID; + m_InitialState = initialState; + m_SwitchID = switchID; + } + + public override string Compile() + { + return String.Format( "{{ checkbox {0} {1} {2} {3} {4} {5} }}", m_X, m_Y, m_ID1, m_ID2, m_InitialState ? 1 : 0, m_SwitchID ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "checkbox" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_ID1 ); + disp.AppendLayout( m_ID2 ); + disp.AppendLayout( m_InitialState ); + disp.AppendLayout( m_SwitchID ); + + disp.Switches++; + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpEntry.cs b/Server/Gumps/GumpEntry.cs new file mode 100644 index 000000000..2185fddf1 --- /dev/null +++ b/Server/Gumps/GumpEntry.cs @@ -0,0 +1,98 @@ +/*************************************************************************** + * GumpEntry.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpEntry.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public abstract class GumpEntry + { + private Gump m_Parent; + + public GumpEntry() + { + } + + protected void Delta( ref int var, int val ) + { + if ( var != val ) + { + var = val; + + if ( m_Parent != null ) + { + m_Parent.Invalidate(); + } + } + } + + protected void Delta( ref bool var, bool val ) + { + if ( var != val ) + { + var = val; + + if ( m_Parent != null ) + { + m_Parent.Invalidate(); + } + } + } + + protected void Delta( ref string var, string val ) + { + if ( var != val ) + { + var = val; + + if ( m_Parent != null ) + { + m_Parent.Invalidate(); + } + } + } + + public Gump Parent + { + get + { + return m_Parent; + } + set + { + if ( m_Parent != value ) + { + if ( m_Parent != null ) + { + m_Parent.Remove( this ); + } + + m_Parent = value; + + m_Parent.Add( this ); + } + } + } + + public abstract string Compile(); + public abstract void AppendTo( IGumpWriter disp ); + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpGroup.cs b/Server/Gumps/GumpGroup.cs new file mode 100644 index 000000000..2b2b7b1c4 --- /dev/null +++ b/Server/Gumps/GumpGroup.cs @@ -0,0 +1,60 @@ +/*************************************************************************** + * GumpGroup.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpGroup.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpGroup : GumpEntry + { + private int m_Group; + + public GumpGroup( int group ) + { + m_Group = group; + } + + public int Group + { + get + { + return m_Group; + } + set + { + Delta( ref m_Group, value ); + } + } + + public override string Compile() + { + return String.Format( "{{ group {0} }}", m_Group ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "group" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_Group ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpHtml.cs b/Server/Gumps/GumpHtml.cs new file mode 100644 index 000000000..42efdf15d --- /dev/null +++ b/Server/Gumps/GumpHtml.cs @@ -0,0 +1,147 @@ +/*************************************************************************** + * GumpHtml.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpHtml.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpHtml : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + private string m_Text; + private bool m_Background, m_Scrollbar; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public string Text + { + get + { + return m_Text; + } + set + { + Delta( ref m_Text, value ); + } + } + + public bool Background + { + get + { + return m_Background; + } + set + { + Delta( ref m_Background, value ); + } + } + + public bool Scrollbar + { + get + { + return m_Scrollbar; + } + set + { + Delta( ref m_Scrollbar, value ); + } + } + + public GumpHtml( int x, int y, int width, int height, string text, bool background, bool scrollbar ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_Text = text; + m_Background = background; + m_Scrollbar = scrollbar; + } + + public override string Compile() + { + return String.Format( "{{ htmlgump {0} {1} {2} {3} {4} {5} {6} }}", m_X, m_Y, m_Width, m_Height, Parent.Intern( m_Text ), m_Background ? 1 : 0, m_Scrollbar ? 1 : 0 ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "htmlgump" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( Parent.Intern( m_Text ) ); + disp.AppendLayout( m_Background ); + disp.AppendLayout( m_Scrollbar ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpHtmlLocalized.cs b/Server/Gumps/GumpHtmlLocalized.cs new file mode 100644 index 000000000..3028db8b9 --- /dev/null +++ b/Server/Gumps/GumpHtmlLocalized.cs @@ -0,0 +1,287 @@ +/*************************************************************************** + * GumpHtmlLocalized.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpHtmlLocalized.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public enum GumpHtmlLocalizedType + { + Plain, + Color, + Args + } + + public class GumpHtmlLocalized : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + private int m_Number; + private string m_Args; + private int m_Color; + private bool m_Background, m_Scrollbar; + + private GumpHtmlLocalizedType m_Type; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public int Number + { + get + { + return m_Number; + } + set + { + Delta( ref m_Number, value ); + } + } + + public string Args + { + get + { + return m_Args; + } + set + { + Delta( ref m_Args, value ); + } + } + + public int Color + { + get + { + return m_Color; + } + set + { + Delta( ref m_Color, value ); + } + } + + public bool Background + { + get + { + return m_Background; + } + set + { + Delta( ref m_Background, value ); + } + } + + public bool Scrollbar + { + get + { + return m_Scrollbar; + } + set + { + Delta( ref m_Scrollbar, value ); + } + } + + public GumpHtmlLocalizedType Type + { + get + { + return m_Type; + } + set + { + if ( m_Type != value ) + { + m_Type = value; + + if ( Parent != null ) + Parent.Invalidate(); + } + } + } + + public GumpHtmlLocalized( int x, int y, int width, int height, int number, bool background, bool scrollbar ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_Number = number; + m_Background = background; + m_Scrollbar = scrollbar; + + m_Type = GumpHtmlLocalizedType.Plain; + } + + public GumpHtmlLocalized( int x, int y, int width, int height, int number, int color, bool background, bool scrollbar ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_Number = number; + m_Color = color; + m_Background = background; + m_Scrollbar = scrollbar; + + m_Type = GumpHtmlLocalizedType.Color; + } + + public GumpHtmlLocalized( int x, int y, int width, int height, int number, string args, int color, bool background, bool scrollbar ) + { + // Are multiple arguments unsupported? And what about non ASCII arguments? + + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_Number = number; + m_Args = args; + m_Color = color; + m_Background = background; + m_Scrollbar = scrollbar; + + m_Type = GumpHtmlLocalizedType.Args; + } + + public override string Compile() + { + switch ( m_Type ) + { + case GumpHtmlLocalizedType.Plain: + return String.Format( "{{ xmfhtmlgump {0} {1} {2} {3} {4} {5} {6} }}", m_X, m_Y, m_Width, m_Height, m_Number, m_Background ? 1 : 0, m_Scrollbar ? 1 : 0 ); + + case GumpHtmlLocalizedType.Color: + return String.Format( "{{ xmfhtmlgumpcolor {0} {1} {2} {3} {4} {5} {6} {7} }}", m_X, m_Y, m_Width, m_Height, m_Number, m_Background ? 1 : 0, m_Scrollbar ? 1 : 0, m_Color ); + + default: // GumpHtmlLocalizedType.Args + return String.Format( "{{ xmfhtmltok {0} {1} {2} {3} {4} {5} {6} {7} @{8}@ }}", m_X, m_Y, m_Width, m_Height, m_Background ? 1 : 0, m_Scrollbar ? 1 : 0, m_Color, m_Number, m_Args ); + } + } + + private static byte[] m_LayoutNamePlain = Gump.StringToBuffer( "xmfhtmlgump" ); + private static byte[] m_LayoutNameColor = Gump.StringToBuffer( "xmfhtmlgumpcolor" ); + private static byte[] m_LayoutNameArgs = Gump.StringToBuffer( "xmfhtmltok" ); + + public override void AppendTo( IGumpWriter disp ) + { + switch ( m_Type ) + { + case GumpHtmlLocalizedType.Plain: + { + disp.AppendLayout( m_LayoutNamePlain ); + + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( m_Number ); + disp.AppendLayout( m_Background ); + disp.AppendLayout( m_Scrollbar ); + + break; + } + + case GumpHtmlLocalizedType.Color: + { + disp.AppendLayout( m_LayoutNameColor ); + + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( m_Number ); + disp.AppendLayout( m_Background ); + disp.AppendLayout( m_Scrollbar ); + disp.AppendLayout( m_Color ); + + break; + } + + case GumpHtmlLocalizedType.Args: + { + disp.AppendLayout( m_LayoutNameArgs ); + + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( m_Background ); + disp.AppendLayout( m_Scrollbar ); + disp.AppendLayout( m_Color ); + disp.AppendLayout( m_Number ); + disp.AppendLayout( m_Args ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpImage.cs b/Server/Gumps/GumpImage.cs new file mode 100644 index 000000000..96172e53f --- /dev/null +++ b/Server/Gumps/GumpImage.cs @@ -0,0 +1,117 @@ +/*************************************************************************** + * GumpImage.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpImage.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpImage : GumpEntry + { + private int m_X, m_Y; + private int m_GumpID; + private int m_Hue; + + public GumpImage( int x, int y, int gumpID ) : this( x, y, gumpID, 0 ) + { + } + + public GumpImage( int x, int y, int gumpID, int hue ) + { + m_X = x; + m_Y = y; + m_GumpID = gumpID; + m_Hue = hue; + } + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int GumpID + { + get + { + return m_GumpID; + } + set + { + Delta( ref m_GumpID, value ); + } + } + + public int Hue + { + get + { + return m_Hue; + } + set + { + Delta( ref m_Hue, value ); + } + } + + public override string Compile() + { + if ( m_Hue == 0 ) + return String.Format( "{{ gumppic {0} {1} {2} }}", m_X, m_Y, m_GumpID ); + else + return String.Format( "{{ gumppic {0} {1} {2} hue={3} }}", m_X, m_Y, m_GumpID, m_Hue ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "gumppic" ); + private static byte[] m_HueEquals = Gump.StringToBuffer( " hue=" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_GumpID ); + + if ( m_Hue != 0 ) + { + disp.AppendLayout( m_HueEquals ); + disp.AppendLayoutNS( m_Hue ); + } + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpImageTileButton.cs b/Server/Gumps/GumpImageTileButton.cs new file mode 100644 index 000000000..0153c7737 --- /dev/null +++ b/Server/Gumps/GumpImageTileButton.cs @@ -0,0 +1,251 @@ +/*************************************************************************** + * GumpImageTileButton.cs + * ------------------- + * begin : April 26, 2005 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpImageTileButton.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpImageTileButton : GumpEntry + { + //Note, on OSI, The tooltip supports ONLY clilocs as far as I can figure out, and the tooltip ONLY works after the buttonTileArt (as far as I can tell from testing) + private int m_X, m_Y; + private int m_ID1, m_ID2; + private int m_ButtonID; + private GumpButtonType m_Type; + private int m_Param; + + private int m_ItemID; + private int m_Hue; + private int m_Width; + private int m_Height; + + private int m_LocalizedTooltip; + + public GumpImageTileButton( int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param, int itemID, int hue, int width, int height ) : this(x, y, normalID, pressedID, buttonID, type, param, itemID, hue, width, height, -1 ) + { + } + public GumpImageTileButton( int x, int y, int normalID, int pressedID, int buttonID, GumpButtonType type, int param, int itemID, int hue, int width, int height, int localizedTooltip ) + { + m_X = x; + m_Y = y; + m_ID1 = normalID; + m_ID2 = pressedID; + m_ButtonID = buttonID; + m_Type = type; + m_Param = param; + + m_ItemID = itemID; + m_Hue = hue; + m_Width = width; + m_Height = height; + + m_LocalizedTooltip = localizedTooltip; + } + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int NormalID + { + get + { + return m_ID1; + } + set + { + Delta( ref m_ID1, value ); + } + } + + public int PressedID + { + get + { + return m_ID2; + } + set + { + Delta( ref m_ID2, value ); + } + } + + public int ButtonID + { + get + { + return m_ButtonID; + } + set + { + Delta( ref m_ButtonID, value ); + } + } + + public GumpButtonType Type + { + get + { + return m_Type; + } + set + { + if( m_Type != value ) + { + m_Type = value; + + Gump parent = Parent; + + if( parent != null ) + { + parent.Invalidate(); + } + } + } + } + + public int Param + { + get + { + return m_Param; + } + set + { + Delta( ref m_Param, value ); + } + } + + public int ItemID + { + get + { + return m_ItemID; + } + set + { + Delta( ref m_ItemID, value ); + } + } + + public int Hue + { + get + { + return m_Hue; + } + set + { + Delta( ref m_Hue, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public int LocalizedTooltip + { + get + { + return m_LocalizedTooltip; + } + set + { + m_LocalizedTooltip = value; + } + } + + public override string Compile() + { + if( m_LocalizedTooltip > 0 ) + return String.Format( "{{ buttontileart {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} }}{{ tooltip {11} }}", m_X, m_Y, m_ID1, m_ID2, (int)m_Type, m_Param, m_ButtonID, m_ItemID, m_Hue, m_Width, m_Height, m_LocalizedTooltip ); + else + return String.Format( "{{ buttontileart {0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} }}", m_X, m_Y, m_ID1, m_ID2, (int)m_Type, m_Param, m_ButtonID, m_ItemID, m_Hue, m_Width, m_Height ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "buttontileart" ); + private static byte[] m_LayoutTooltip = Gump.StringToBuffer( " }{ tooltip" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_ID1 ); + disp.AppendLayout( m_ID2 ); + disp.AppendLayout( (int)m_Type ); + disp.AppendLayout( m_Param ); + disp.AppendLayout( m_ButtonID ); + + disp.AppendLayout( m_ItemID ); + disp.AppendLayout( m_Hue ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + + if( m_LocalizedTooltip > 0 ) + { + disp.AppendLayout( m_LayoutTooltip ); + disp.AppendLayout( m_LocalizedTooltip ); + } + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpImageTiled.cs b/Server/Gumps/GumpImageTiled.cs new file mode 100644 index 000000000..638332b6e --- /dev/null +++ b/Server/Gumps/GumpImageTiled.cs @@ -0,0 +1,118 @@ +/*************************************************************************** + * GumpImageTiled.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpImageTiled.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpImageTiled : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + private int m_GumpID; + + public GumpImageTiled( int x, int y, int width, int height, int gumpID ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_GumpID = gumpID; + } + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public int GumpID + { + get + { + return m_GumpID; + } + set + { + Delta( ref m_GumpID, value ); + } + } + + public override string Compile() + { + return String.Format( "{{ gumppictiled {0} {1} {2} {3} {4} }}", m_X, m_Y, m_Width, m_Height, m_GumpID ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "gumppictiled" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( m_GumpID ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpItem.cs b/Server/Gumps/GumpItem.cs new file mode 100644 index 000000000..76a8ab2c4 --- /dev/null +++ b/Server/Gumps/GumpItem.cs @@ -0,0 +1,114 @@ +/*************************************************************************** + * GumpItem.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpItem.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpItem : GumpEntry + { + private int m_X, m_Y; + private int m_ItemID; + private int m_Hue; + + public GumpItem( int x, int y, int itemID ) : this( x, y, itemID, 0 ) + { + } + + public GumpItem( int x, int y, int itemID, int hue ) + { + m_X = x; + m_Y = y; + m_ItemID = itemID; + m_Hue = hue; + } + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int ItemID + { + get + { + return m_ItemID; + } + set + { + Delta( ref m_ItemID, value ); + } + } + + public int Hue + { + get + { + return m_Hue; + } + set + { + Delta( ref m_Hue, value ); + } + } + + public override string Compile() + { + if ( m_Hue == 0 ) + return String.Format( "{{ tilepic {0} {1} {2} }}", m_X, m_Y, m_ItemID ); + else + return String.Format( "{{ tilepichue {0} {1} {2} {3} }}", m_X, m_Y, m_ItemID, m_Hue ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "tilepic" ); + private static byte[] m_LayoutNameHue = Gump.StringToBuffer( "tilepichue" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_Hue == 0 ? m_LayoutName : m_LayoutNameHue ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_ItemID ); + + if ( m_Hue != 0 ) + disp.AppendLayout( m_Hue ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpLabel.cs b/Server/Gumps/GumpLabel.cs new file mode 100644 index 000000000..93bf6f525 --- /dev/null +++ b/Server/Gumps/GumpLabel.cs @@ -0,0 +1,104 @@ +/*************************************************************************** + * GumpLabel.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpLabel.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpLabel : GumpEntry + { + private int m_X, m_Y; + private int m_Hue; + private string m_Text; + + public GumpLabel( int x, int y, int hue, string text ) + { + m_X = x; + m_Y = y; + m_Hue = hue; + m_Text = text; + } + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Hue + { + get + { + return m_Hue; + } + set + { + Delta( ref m_Hue, value ); + } + } + + public string Text + { + get + { + return m_Text; + } + set + { + Delta( ref m_Text, value ); + } + } + + public override string Compile() + { + return String.Format( "{{ text {0} {1} {2} {3} }}", m_X, m_Y, m_Hue, Parent.Intern( m_Text ) ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "text" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Hue ); + disp.AppendLayout( Parent.Intern( m_Text ) ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpLabelCropped.cs b/Server/Gumps/GumpLabelCropped.cs new file mode 100644 index 000000000..3f7ef26df --- /dev/null +++ b/Server/Gumps/GumpLabelCropped.cs @@ -0,0 +1,133 @@ +/*************************************************************************** + * GumpLabelCropped.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpLabelCropped.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpLabelCropped : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + private int m_Hue; + private string m_Text; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public int Hue + { + get + { + return m_Hue; + } + set + { + Delta( ref m_Hue, value ); + } + } + + public string Text + { + get + { + return m_Text; + } + set + { + Delta( ref m_Text, value ); + } + } + + public GumpLabelCropped( int x, int y, int width, int height, int hue, string text ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_Hue = hue; + m_Text = text; + } + + public override string Compile() + { + return String.Format( "{{ croppedtext {0} {1} {2} {3} {4} {5} }}", m_X, m_Y, m_Width, m_Height, m_Hue, Parent.Intern( m_Text ) ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "croppedtext" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( m_Hue ); + disp.AppendLayout( Parent.Intern( m_Text ) ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpPage.cs b/Server/Gumps/GumpPage.cs new file mode 100644 index 000000000..1744ea6b5 --- /dev/null +++ b/Server/Gumps/GumpPage.cs @@ -0,0 +1,60 @@ +/*************************************************************************** + * GumpPage.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpPage.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpPage : GumpEntry + { + private int m_Page; + + public GumpPage( int page ) + { + m_Page = page; + } + + public int Page + { + get + { + return m_Page; + } + set + { + Delta( ref m_Page, value ); + } + } + + public override string Compile() + { + return String.Format( "{{ page {0} }}", m_Page ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "page" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_Page ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpRadio.cs b/Server/Gumps/GumpRadio.cs new file mode 100644 index 000000000..add6df744 --- /dev/null +++ b/Server/Gumps/GumpRadio.cs @@ -0,0 +1,135 @@ +/*************************************************************************** + * GumpRadio.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpRadio.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpRadio : GumpEntry + { + private int m_X, m_Y; + private int m_ID1, m_ID2; + private bool m_InitialState; + private int m_SwitchID; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int InactiveID + { + get + { + return m_ID1; + } + set + { + Delta( ref m_ID1, value ); + } + } + + public int ActiveID + { + get + { + return m_ID2; + } + set + { + Delta( ref m_ID2, value ); + } + } + + public bool InitialState + { + get + { + return m_InitialState; + } + set + { + Delta( ref m_InitialState, value ); + } + } + + public int SwitchID + { + get + { + return m_SwitchID; + } + set + { + Delta( ref m_SwitchID, value ); + } + } + + public GumpRadio( int x, int y, int inactiveID, int activeID, bool initialState, int switchID ) + { + m_X = x; + m_Y = y; + m_ID1 = inactiveID; + m_ID2 = activeID; + m_InitialState = initialState; + m_SwitchID = switchID; + } + + public override string Compile() + { + return String.Format( "{{ radio {0} {1} {2} {3} {4} {5} }}", m_X, m_Y, m_ID1, m_ID2, m_InitialState ? 1 : 0, m_SwitchID ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "radio" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_ID1 ); + disp.AppendLayout( m_ID2 ); + disp.AppendLayout( m_InitialState ); + disp.AppendLayout( m_SwitchID ); + + disp.Switches++; + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpTextEntry.cs b/Server/Gumps/GumpTextEntry.cs new file mode 100644 index 000000000..6b23c47e0 --- /dev/null +++ b/Server/Gumps/GumpTextEntry.cs @@ -0,0 +1,150 @@ +/*************************************************************************** + * GumpTextEntry.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpTextEntry.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpTextEntry : GumpEntry + { + private int m_X, m_Y; + private int m_Width, m_Height; + private int m_Hue; + private int m_EntryID; + private string m_InitialText; + + public int X + { + get + { + return m_X; + } + set + { + Delta( ref m_X, value ); + } + } + + public int Y + { + get + { + return m_Y; + } + set + { + Delta( ref m_Y, value ); + } + } + + public int Width + { + get + { + return m_Width; + } + set + { + Delta( ref m_Width, value ); + } + } + + public int Height + { + get + { + return m_Height; + } + set + { + Delta( ref m_Height, value ); + } + } + + public int Hue + { + get + { + return m_Hue; + } + set + { + Delta( ref m_Hue, value ); + } + } + + public int EntryID + { + get + { + return m_EntryID; + } + set + { + Delta( ref m_EntryID, value ); + } + } + + public string InitialText + { + get + { + return m_InitialText; + } + set + { + Delta( ref m_InitialText, value ); + } + } + + public GumpTextEntry( int x, int y, int width, int height, int hue, int entryID, string initialText ) + { + m_X = x; + m_Y = y; + m_Width = width; + m_Height = height; + m_Hue = hue; + m_EntryID = entryID; + m_InitialText = initialText; + } + + public override string Compile() + { + return String.Format( "{{ textentry {0} {1} {2} {3} {4} {5} {6} }}", m_X, m_Y, m_Width, m_Height, m_Hue, m_EntryID, Parent.Intern( m_InitialText ) ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "textentry" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_X ); + disp.AppendLayout( m_Y ); + disp.AppendLayout( m_Width ); + disp.AppendLayout( m_Height ); + disp.AppendLayout( m_Hue ); + disp.AppendLayout( m_EntryID ); + disp.AppendLayout( Parent.Intern( m_InitialText ) ); + + disp.TextEntries++; + } + } +} \ No newline at end of file diff --git a/Server/Gumps/GumpTooltip.cs b/Server/Gumps/GumpTooltip.cs new file mode 100644 index 000000000..7257d9948 --- /dev/null +++ b/Server/Gumps/GumpTooltip.cs @@ -0,0 +1,60 @@ +/*************************************************************************** + * GumpTooltip.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: GumpTooltip.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Gumps +{ + public class GumpTooltip : GumpEntry + { + private int m_Number; + + public GumpTooltip( int number ) + { + m_Number = number; + } + + public int Number + { + get + { + return m_Number; + } + set + { + Delta( ref m_Number, value ); + } + } + + public override string Compile() + { + return String.Format( "{{ tooltip {0} }}", m_Number ); + } + + private static byte[] m_LayoutName = Gump.StringToBuffer( "tooltip" ); + + public override void AppendTo( IGumpWriter disp ) + { + disp.AppendLayout( m_LayoutName ); + disp.AppendLayout( m_Number ); + } + } +} \ No newline at end of file diff --git a/Server/Gumps/RelayInfo.cs b/Server/Gumps/RelayInfo.cs new file mode 100644 index 000000000..c1e3d6341 --- /dev/null +++ b/Server/Gumps/RelayInfo.cs @@ -0,0 +1,116 @@ +/*************************************************************************** + * RelayInfo.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: RelayInfo.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Gumps +{ + public class TextRelay + { + private int m_EntryID; + private string m_Text; + + public TextRelay( int entryID, string text ) + { + m_EntryID = entryID; + m_Text = text; + } + + public int EntryID + { + get + { + return m_EntryID; + } + } + + public string Text + { + get + { + return m_Text; + } + } + } + + public class RelayInfo + { + private int m_ButtonID; + private int[] m_Switches; + private TextRelay[] m_TextEntries; + + public RelayInfo( int buttonID, int[] switches, TextRelay[] textEntries ) + { + m_ButtonID = buttonID; + m_Switches = switches; + m_TextEntries = textEntries; + } + + public int ButtonID + { + get + { + return m_ButtonID; + } + } + + public int[] Switches + { + get + { + return m_Switches; + } + } + + public TextRelay[] TextEntries + { + get + { + return m_TextEntries; + } + } + + public bool IsSwitched( int switchID ) + { + for ( int i = 0; i < m_Switches.Length; ++i ) + { + if ( m_Switches[i] == switchID ) + { + return true; + } + } + + return false; + } + + public TextRelay GetTextEntry( int entryID ) + { + for ( int i = 0; i < m_TextEntries.Length; ++i ) + { + if ( m_TextEntries[i].EntryID == entryID ) + { + return m_TextEntries[i]; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/Server/HuePicker.cs b/Server/HuePicker.cs new file mode 100644 index 000000000..080af8102 --- /dev/null +++ b/Server/HuePicker.cs @@ -0,0 +1,69 @@ +/*************************************************************************** + * HuePicker.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: HuePicker.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.HuePickers +{ + public class HuePicker + { + private static int m_NextSerial = 1; + + private int m_Serial; + private int m_ItemID; + + public int Serial + { + get + { + return m_Serial; + } + } + + public int ItemID + { + get + { + return m_ItemID; + } + } + + public HuePicker( int itemID ) + { + do + { + m_Serial = m_NextSerial++; + } while ( m_Serial == 0 ); + + m_ItemID = itemID; + } + + public virtual void OnResponse( int hue ) + { + } + + public void SendTo( NetState state ) + { + state.Send( new DisplayHuePicker( this ) ); + state.AddHuePicker( this ); + } + } +} \ No newline at end of file diff --git a/Server/IAccount.cs b/Server/IAccount.cs new file mode 100644 index 000000000..87ed32baa --- /dev/null +++ b/Server/IAccount.cs @@ -0,0 +1,39 @@ +/*************************************************************************** + * IAccount.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: IAccount.cs 102 2006-02-04 01:52:29Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Accounting +{ + public interface IAccount + { + string Username { get; set; } + AccessLevel AccessLevel { get; set; } + + int Length { get; } + int Limit { get; } + int Count { get; } + Mobile this[int index] { get; set; } + + void Delete(); + void SetPassword( string password ); + bool CheckPassword( string password ); + } +} diff --git a/Server/IEntity.cs b/Server/IEntity.cs new file mode 100644 index 000000000..b1739e363 --- /dev/null +++ b/Server/IEntity.cs @@ -0,0 +1,114 @@ +/*************************************************************************** + * IEntity.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: IEntity.cs 36 2006-01-18 10:19:46Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public interface IEntity : IPoint3D, IComparable, IComparable + { + Serial Serial{ get; } + Point3D Location{ get; } + Map Map{ get; } + } + + public class Entity : IEntity, IComparable, IComparable, IComparable + { + public int CompareTo( IEntity other ) + { + if ( other == null ) + return -1; + + return m_Serial.CompareTo( other.Serial ); + } + + public int CompareTo( Entity other ) + { + return this.CompareTo( (IEntity) other ); + } + + public int CompareTo( object other ) + { + if ( other == null || other is IEntity ) + return this.CompareTo( (IEntity) other ); + + throw new ArgumentException(); + } + + private Serial m_Serial; + private Point3D m_Location; + private Map m_Map; + + public Entity( Serial serial, Point3D loc, Map map ) + { + m_Serial = serial; + m_Location = loc; + m_Map = map; + } + + public Serial Serial + { + get + { + return m_Serial; + } + } + + public Point3D Location + { + get + { + return m_Location; + } + } + + public int X + { + get + { + return m_Location.X; + } + } + + public int Y + { + get + { + return m_Location.Y; + } + } + + public int Z + { + get + { + return m_Location.Z; + } + } + + public Map Map + { + get + { + return m_Map; + } + } + } +} \ No newline at end of file diff --git a/Server/Insensitive.cs b/Server/Insensitive.cs new file mode 100644 index 000000000..39065032b --- /dev/null +++ b/Server/Insensitive.cs @@ -0,0 +1,81 @@ +/*************************************************************************** + * Insensitive.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Insensitive.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; + +namespace Server +{ + public class Insensitive + { + private static IComparer m_Comparer = CaseInsensitiveComparer.Default; + + public static IComparer Comparer + { + get{ return m_Comparer; } + } + + private Insensitive() + { + } + + public static int Compare( string a, string b ) + { + return m_Comparer.Compare( a, b ); + } + + public static bool Equals( string a, string b ) + { + if ( a == null && b == null ) + return true; + else if ( a == null || b == null || a.Length != b.Length ) + return false; + + return ( m_Comparer.Compare( a, b ) == 0 ); + } + + public static bool StartsWith( string a, string b ) + { + if ( a == null || b == null || a.Length < b.Length ) + return false; + + return ( m_Comparer.Compare( a.Substring( 0, b.Length ), b ) == 0 ); + } + + public static bool EndsWith( string a, string b ) + { + if ( a == null || b == null || a.Length < b.Length ) + return false; + + return ( m_Comparer.Compare( a.Substring( a.Length - b.Length ), b ) == 0 ); + } + + public static bool Contains( string a, string b ) + { + if ( a == null || b == null || a.Length < b.Length ) + return false; + + a = a.ToLower(); + b = b.ToLower(); + + return ( a.IndexOf( b ) >= 0 ); + } + } +} \ No newline at end of file diff --git a/Server/Interfaces.cs b/Server/Interfaces.cs new file mode 100644 index 000000000..a87e12f74 --- /dev/null +++ b/Server/Interfaces.cs @@ -0,0 +1,97 @@ +/*************************************************************************** + * Interfaces.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Interfaces.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; + +namespace Server.Mobiles +{ + public interface IMount + { + Mobile Rider{ get; set; } + void OnRiderDamaged( int amount, Mobile from, bool willKill ); + } + + public interface IMountItem + { + IMount Mount{ get; } + } +} + +namespace Server +{ + public interface IVendor + { + bool OnBuyItems( Mobile from, ArrayList list ); + bool OnSellItems( Mobile from, ArrayList list ); + + DateTime LastRestock{ get; set; } + TimeSpan RestockDelay{ get; } + void Restock(); + } + + public interface IPoint2D + { + int X{ get; } + int Y{ get; } + } + + public interface IPoint3D : IPoint2D + { + int Z{ get; } + } + + public interface ICarvable + { + void Carve( Mobile from, Item item ); + } + + public interface IWeapon + { + int MaxRange{ get; } + void OnBeforeSwing( Mobile attacker, Mobile defender ); + TimeSpan OnSwing( Mobile attacker, Mobile defender ); + void GetStatusDamage( Mobile from, out int min, out int max ); + } + + public interface IHued + { + int HuedItemID{ get; } + } + + public interface ISpell + { + bool IsCasting{ get; } + void OnCasterHurt(); + void OnCasterKilled(); + void OnConnectionChanged(); + bool OnCasterMoving( Direction d ); + bool OnCasterEquiping( Item item ); + bool OnCasterUsingObject( object o ); + bool OnCastInTown( Region r ); + } + + public interface IParty + { + void OnStamChanged( Mobile m ); + void OnManaChanged( Mobile m ); + void OnStatsQuery( Mobile beholder, Mobile beheld ); + } +} \ No newline at end of file diff --git a/Server/Item.cs b/Server/Item.cs new file mode 100644 index 000000000..e58ed0dd6 --- /dev/null +++ b/Server/Item.cs @@ -0,0 +1,4474 @@ +/*************************************************************************** + * Item.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Item.cs 163 2006-06-15 00:59:08Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using Server.Network; +using Server.Items; +using Server.ContextMenus; + +namespace Server +{ + /// + /// Enumeration of item layer values. + /// + public enum Layer : byte + { + /// + /// Invalid layer. + /// + Invalid = 0x00, + /// + /// First valid layer. Equivalent to Layer.OneHanded. + /// + FirstValid = 0x01, + /// + /// One handed weapon. + /// + OneHanded = 0x01, + /// + /// Two handed weapon or shield. + /// + TwoHanded = 0x02, + /// + /// Shoes. + /// + Shoes = 0x03, + /// + /// Pants. + /// + Pants = 0x04, + /// + /// Shirts. + /// + Shirt = 0x05, + /// + /// Helmets, hats, and masks. + /// + Helm = 0x06, + /// + /// Gloves. + /// + Gloves = 0x07, + /// + /// Rings. + /// + Ring = 0x08, + /// + /// Talismans. + /// + Talisman = 0x09, + /// + /// Gorgets and necklaces. + /// + Neck = 0x0A, + /// + /// Hair. + /// + Hair = 0x0B, + /// + /// Half aprons. + /// + Waist = 0x0C, + /// + /// Torso, inner layer. + /// + InnerTorso = 0x0D, + /// + /// Bracelets. + /// + Bracelet = 0x0E, + /// + /// Unused. + /// + Unused_xF = 0x0F, + /// + /// Beards and mustaches. + /// + FacialHair = 0x10, + /// + /// Torso, outer layer. + /// + MiddleTorso = 0x11, + /// + /// Earings. + /// + Earrings = 0x12, + /// + /// Arms and sleeves. + /// + Arms = 0x13, + /// + /// Cloaks. + /// + Cloak = 0x14, + /// + /// Backpacks. + /// + Backpack = 0x15, + /// + /// Torso, outer layer. + /// + OuterTorso = 0x16, + /// + /// Leggings, outer layer. + /// + OuterLegs = 0x17, + /// + /// Leggings, inner layer. + /// + InnerLegs = 0x18, + /// + /// Last valid non-internal layer. Equivalent to Layer.InnerLegs. + /// + LastUserValid= 0x18, + /// + /// Mount item layer. + /// + Mount = 0x19, + /// + /// Vendor 'buy pack' layer. + /// + ShopBuy = 0x1A, + /// + /// Vendor 'resale pack' layer. + /// + ShopResale = 0x1B, + /// + /// Vendor 'sell pack' layer. + /// + ShopSell = 0x1C, + /// + /// Bank box layer. + /// + Bank = 0x1D, + /// + /// Last valid layer. Equivalent to Layer.Bank. + /// + LastValid = 0x1D + } + + /// + /// Internal flags used to signal how the item should be updated and resent to nearby clients. + /// + [Flags] + public enum ItemDelta + { + /// + /// Nothing. + /// + None = 0x00000000, + /// + /// Resend the item. + /// + Update = 0x00000001, + /// + /// Resend the item only if it is equiped. + /// + EquipOnly = 0x00000002, + /// + /// Resend the item's properties. + /// + Properties = 0x00000004 + } + + /// + /// Enumeration containing possible ways to handle item ownership on death. + /// + public enum DeathMoveResult + { + /// + /// The item should be placed onto the corpse. + /// + MoveToCorpse, + /// + /// The item should remain equiped. + /// + RemainEquiped, + /// + /// The item should be placed into the owners backpack. + /// + MoveToBackpack + } + + /// + /// Enumeration containing all possible light types. These are only applicable to light source items, like lanterns, candles, braziers, etc. + /// + public enum LightType + { + /// + /// Window shape, arched, ray shining east. + /// + ArchedWindowEast, + /// + /// Medium circular shape. + /// + Circle225, + /// + /// Small circular shape. + /// + Circle150, + /// + /// Door shape, shining south. + /// + DoorSouth, + /// + /// Door shape, shining east. + /// + DoorEast, + /// + /// Large semicircular shape (180 degrees), north wall. + /// + NorthBig, + /// + /// Large pie shape (90 degrees), north-east corner. + /// + NorthEastBig, + /// + /// Large semicircular shape (180 degrees), east wall. + /// + EastBig, + /// + /// Large semicircular shape (180 degrees), west wall. + /// + WestBig, + /// + /// Large pie shape (90 degrees), south-west corner. + /// + SouthWestBig, + /// + /// Large semicircular shape (180 degrees), south wall. + /// + SouthBig, + /// + /// Medium semicircular shape (180 degrees), north wall. + /// + NorthSmall, + /// + /// Medium pie shape (90 degrees), north-east corner. + /// + NorthEastSmall, + /// + /// Medium semicircular shape (180 degrees), east wall. + /// + EastSmall, + /// + /// Medium semicircular shape (180 degrees), west wall. + /// + WestSmall, + /// + /// Medium semicircular shape (180 degrees), south wall. + /// + SouthSmall, + /// + /// Shaped like a wall decoration, north wall. + /// + DecorationNorth, + /// + /// Shaped like a wall decoration, north-east corner. + /// + DecorationNorthEast, + /// + /// Small semicircular shape (180 degrees), east wall. + /// + EastTiny, + /// + /// Shaped like a wall decoration, west wall. + /// + DecorationWest, + /// + /// Shaped like a wall decoration, south-west corner. + /// + DecorationSouthWest, + /// + /// Small semicircular shape (180 degrees), south wall. + /// + SouthTiny, + /// + /// Window shape, rectangular, no ray, shining south. + /// + RectWindowSouthNoRay, + /// + /// Window shape, rectangular, no ray, shining east. + /// + RectWindowEastNoRay, + /// + /// Window shape, rectangular, ray shining south. + /// + RectWindowSouth, + /// + /// Window shape, rectangular, ray shining east. + /// + RectWindowEast, + /// + /// Window shape, arched, no ray, shining south. + /// + ArchedWindowSouthNoRay, + /// + /// Window shape, arched, no ray, shining east. + /// + ArchedWindowEastNoRay, + /// + /// Window shape, arched, ray shining south. + /// + ArchedWindowSouth, + /// + /// Large circular shape. + /// + Circle300, + /// + /// Large pie shape (90 degrees), north-west corner. + /// + NorthWestBig, + /// + /// Negative light. Medium pie shape (90 degrees), south-east corner. + /// + DarkSouthEast, + /// + /// Negative light. Medium semicircular shape (180 degrees), south wall. + /// + DarkSouth, + /// + /// Negative light. Medium pie shape (90 degrees), north-west corner. + /// + DarkNorthWest, + /// + /// Negative light. Medium pie shape (90 degrees), south-east corner. Equivalent to LightType.SouthEast. + /// + DarkSouthEast2, + /// + /// Negative light. Medium circular shape (180 degrees), east wall. + /// + DarkEast, + /// + /// Negative light. Large circular shape. + /// + DarkCircle300, + /// + /// Opened door shape, shining south. + /// + DoorOpenSouth, + /// + /// Opened door shape, shining east. + /// + DoorOpenEast, + /// + /// Window shape, square, ray shining east. + /// + SquareWindowEast, + /// + /// Window shape, square, no ray, shining east. + /// + SquareWindowEastNoRay, + /// + /// Window shape, square, ray shining south. + /// + SquareWindowSouth, + /// + /// Window shape, square, no ray, shining south. + /// + SquareWindowSouthNoRay, + /// + /// Empty. + /// + Empty, + /// + /// Window shape, skinny, no ray, shining south. + /// + SkinnyWindowSouthNoRay, + /// + /// Window shape, skinny, ray shining east. + /// + SkinnyWindowEast, + /// + /// Window shape, skinny, no ray, shining east. + /// + SkinnyWindowEastNoRay, + /// + /// Shaped like a hole, shining south. + /// + HoleSouth, + /// + /// Shaped like a hole, shining south. + /// + HoleEast, + /// + /// Large circular shape with a moongate graphic embeded. + /// + Moongate, + /// + /// Unknown usage. Many rows of slightly angled lines. + /// + Strips, + /// + /// Shaped like a small hole, shining south. + /// + SmallHoleSouth, + /// + /// Shaped like a small hole, shining east. + /// + SmallHoleEast, + /// + /// Large semicircular shape (180 degrees), north wall. Identical graphic as LightType.NorthBig, but slightly different positioning. + /// + NorthBig2, + /// + /// Large semicircular shape (180 degrees), west wall. Identical graphic as LightType.WestBig, but slightly different positioning. + /// + WestBig2, + /// + /// Large pie shape (90 degrees), north-west corner. Equivalent to LightType.NorthWestBig. + /// + NorthWestBig2 + } + + /// + /// Enumeration of an item's loot and steal state. + /// + public enum LootType : byte + { + /// + /// Stealable. Lootable. + /// + Regular = 0, + /// + /// Unstealable. Unlootable, unless owned by a murderer. + /// + Newbied = 1, + /// + /// Unstealable. Unlootable, always. + /// + Blessed = 2, + /// + /// Stealable. Lootable, always. + /// + Cursed = 3 + } + + public class BounceInfo + { + public Map m_Map; + public Point3D m_Location, m_WorldLoc; + public object m_Parent; + + public BounceInfo( Item item ) + { + m_Map = item.Map; + m_Location = item.Location; + m_WorldLoc = item.GetWorldLocation(); + m_Parent = item.Parent; + } + + private BounceInfo( Map map, Point3D loc, Point3D worldLoc, object parent ) + { + m_Map = map; + m_Location = loc; + m_WorldLoc = worldLoc; + m_Parent = parent; + } + + public static BounceInfo Deserialize( GenericReader reader ) + { + if ( reader.ReadBool() ) + { + Map map = reader.ReadMap(); + Point3D loc = reader.ReadPoint3D(); + Point3D worldLoc = reader.ReadPoint3D(); + + object parent; + + Serial serial = reader.ReadInt(); + + if ( serial.IsItem ) + parent = World.FindItem( serial ); + else if ( serial.IsMobile ) + parent = World.FindMobile( serial ); + else + parent = null; + + return new BounceInfo( map, loc, worldLoc, parent ); + } + else + { + return null; + } + } + + public static void Serialize( BounceInfo info, GenericWriter writer ) + { + if ( info == null ) + { + writer.Write( false ); + } + else + { + writer.Write( true ); + + writer.Write( info.m_Map ); + writer.Write( info.m_Location ); + writer.Write( info.m_WorldLoc ); + + if ( info.m_Parent is Mobile ) + writer.Write( (Mobile) info.m_Parent ); + else if ( info.m_Parent is Item ) + writer.Write( (Item) info.m_Parent ); + else + writer.Write( (Serial) 0 ); + } + } + } + + public enum TotalType + { + Gold, + Items, + Weight, + } + + public enum ExpandFlag + { + None = 0x00, + + Name = 0x01, + Items = 0x02, + Bounce = 0x04, + Holder = 0x08, + Blessed = 0x10, + TempFlag = 0x20, + SaveFlag = 0x40, + Weight = 0x80 + } + + public class Item : IPoint3D, IEntity, IHued, IComparable, IComparable, IComparable, ISerializable + { + //public static readonly EmptyArrayList EmptyItems = new EmptyArrayList(); + public static readonly List EmptyItems = new List( 0 ); + + public int CompareTo( IEntity other ) + { + if ( other == null ) + return -1; + + return m_Serial.CompareTo( other.Serial ); + } + + public int CompareTo( Item other ) + { + return this.CompareTo( (IEntity) other ); + } + + public int CompareTo( object other ) + { + if ( other == null || other is IEntity ) + return this.CompareTo( (IEntity) other ); + + throw new ArgumentException(); + } + + #region Standard fields + private Serial m_Serial; + private Point3D m_Location; + private int m_ItemID; + private int m_Hue; + private int m_Amount; + private Layer m_Layer; + private object m_Parent; // Mobile, Item, or null=World + private Map m_Map; + private LootType m_LootType; + private DateTime m_LastMovedTime; + private Direction m_Direction; + #endregion + + private ItemDelta m_DeltaFlags; + private ImplFlag m_Flags; + + #region Packet caches + private Packet m_WorldPacket; + private Packet m_RemovePacket; + + private Packet m_OPLPacket; + private ObjectPropertyList m_PropertyList; + #endregion + + public int TempFlags + { + get + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + return info.m_TempFlags; + + return 0; + } + set + { + CompactInfo info = AcquireCompactInfo(); + + info.m_TempFlags = value; + + if ( info.m_TempFlags == 0 ) + VerifyCompactInfo(); + } + } + + public int SavedFlags + { + get + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + return info.m_SavedFlags; + + return 0; + } + set + { + CompactInfo info = AcquireCompactInfo(); + + info.m_SavedFlags = value; + + if ( info.m_SavedFlags == 0 ) + VerifyCompactInfo(); + } + } + + /// + /// The who is currently holding this item. + /// + public Mobile HeldBy + { + get + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + return info.m_HeldBy; + + return null; + } + set + { + CompactInfo info = AcquireCompactInfo(); + + info.m_HeldBy = value; + + if ( info.m_HeldBy == null ) + VerifyCompactInfo(); + } + } + + [Flags] + private enum ImplFlag : byte + { + None = 0x00, + Visible = 0x01, + Movable = 0x02, + Deleted = 0x04, + Stackable = 0x08, + InQueue = 0x10, + Insured = 0x20, + PayedInsurance = 0x40 + } + + private class CompactInfo + { + public string m_Name; + + public List m_Items; + public BounceInfo m_Bounce; + + public Mobile m_HeldBy; + public Mobile m_BlessedFor; + + public int m_TempFlags; + public int m_SavedFlags; + + public double m_Weight = -1; + } + + private CompactInfo m_CompactInfo; + + public ExpandFlag GetExpandFlags() + { + CompactInfo info = LookupCompactInfo(); + + ExpandFlag flags = 0; + + if ( info != null ) + { + if ( info.m_BlessedFor != null ) + flags |= ExpandFlag.Blessed; + + if ( info.m_Bounce != null ) + flags |= ExpandFlag.Bounce; + + if ( info.m_HeldBy != null ) + flags |= ExpandFlag.Holder; + + if ( info.m_Items != null ) + flags |= ExpandFlag.Items; + + if ( info.m_Name != null ) + flags |= ExpandFlag.Name; + + if ( info.m_SavedFlags != 0 ) + flags |= ExpandFlag.SaveFlag; + + if ( info.m_TempFlags != 0 ) + flags |= ExpandFlag.TempFlag; + + if ( info.m_Weight != -1 ) + flags |= ExpandFlag.Weight; + } + + return flags; + } + + private CompactInfo LookupCompactInfo() + { + return m_CompactInfo; + } + + private CompactInfo AcquireCompactInfo() + { + if ( m_CompactInfo == null ) + m_CompactInfo = new CompactInfo(); + + return m_CompactInfo; + } + + private void ReleaseCompactInfo() + { + m_CompactInfo = null; + } + + private void VerifyCompactInfo() + { + CompactInfo info = m_CompactInfo; + + if ( info == null ) + return; + + bool isValid = ( info.m_Name != null ) + || ( info.m_Items != null ) + || ( info.m_Bounce != null ) + || ( info.m_HeldBy != null ) + || ( info.m_BlessedFor != null ) + || ( info.m_TempFlags != 0 ) + || ( info.m_SavedFlags != 0 ) + || ( info.m_Weight != -1 ); + + if ( !isValid ) + ReleaseCompactInfo(); + } + + public List LookupItems() + { + if ( this is Container ) + return ( this as Container ).m_Items; + + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + return info.m_Items; + + return null; + } + + public List AcquireItems() + { + if ( this is Container ) + { + Container cont = this as Container; + + if ( cont.m_Items == null ) + cont.m_Items = new List( 4 ); + + return cont.m_Items; + } + + CompactInfo info = AcquireCompactInfo(); + + info.m_Items = new List( 4 ); + + return info.m_Items; + } + + private void SetFlag( ImplFlag flag, bool value ) + { + if ( value ) + m_Flags |= flag; + else + m_Flags &= ~flag; + } + + private bool GetFlag( ImplFlag flag ) + { + return ( (m_Flags & flag) != 0 ); + } + + public BounceInfo GetBounce() + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + return info.m_Bounce; + + return null; + } + + public void RecordBounce() + { + CompactInfo info = AcquireCompactInfo(); + + info.m_Bounce = new BounceInfo( this ); + } + + public void ClearBounce() + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + { + BounceInfo bounce = info.m_Bounce; + + if ( bounce != null ) + { + info.m_Bounce = null; + + if ( bounce.m_Parent is Item ) + { + Item parent = (Item) bounce.m_Parent; + + if ( !parent.Deleted ) + parent.OnItemBounceCleared( this ); + } + else if ( bounce.m_Parent is Mobile ) + { + Mobile parent = (Mobile) bounce.m_Parent; + + if ( !parent.Deleted ) + parent.OnItemBounceCleared( this ); + } + + VerifyCompactInfo(); + } + } + } + + /// + /// Overridable. Virtual event invoked when a client, , invokes a 'help request' for the Item. Seemingly no longer functional in newer clients. + /// + public virtual void OnHelpRequest( Mobile from ) + { + } + + /// + /// Overridable. Method checked to see if the item can be traded. + /// + /// True if the trade is allowed, false if not. + public virtual bool AllowSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when a trade has completed, either successfully or not. + /// + public virtual void OnSecureTrade( Mobile from, Mobile to, Mobile newOwner, bool accepted ) + { + } + + /// + /// Overridable. Method checked to see if the elemental resistances of this Item conflict with another Item on the . + /// + /// + /// + /// + /// True + /// There is a confliction. The elemental resistance bonuses of this Item should not be applied to the + /// + /// + /// False + /// There is no confliction. The bonuses should be applied. + /// + /// + /// + public virtual bool CheckPropertyConfliction( Mobile m ) + { + return false; + } + + /// + /// Overridable. Sends the object property list to . + /// + public virtual void SendPropertiesTo( Mobile from ) + { + from.Send( PropertyList ); + } + + /// + /// Overridable. Adds the name of this item to the given . This method should be overriden if the item requires a complex naming format. + /// + public virtual void AddNameProperty( ObjectPropertyList list ) + { + string name = this.Name; + + if ( name == null ) + { + if ( m_Amount <= 1 ) + list.Add( LabelNumber ); + else + list.Add( 1050039, "{0}\t#{1}", m_Amount, LabelNumber ); // ~1_NUMBER~ ~2_ITEMNAME~ + } + else + { + if ( m_Amount <= 1 ) + list.Add( name ); + else + list.Add( 1050039, "{0}\t{1}", m_Amount, Name ); // ~1_NUMBER~ ~2_ITEMNAME~ + } + } + + /// + /// Overridable. Adds the loot type of this item to the given . By default, this will be either 'blessed', 'cursed', or 'insured'. + /// + public virtual void AddLootTypeProperty( ObjectPropertyList list ) + { + if ( m_LootType == LootType.Blessed ) + list.Add( 1038021 ); // blessed + else if ( m_LootType == LootType.Cursed ) + list.Add( 1049643 ); // cursed + else if ( Insured ) + list.Add( 1061682 ); // insured + } + + /// + /// Overridable. Adds any elemental resistances of this item to the given . + /// + public virtual void AddResistanceProperties( ObjectPropertyList list ) + { + int v = PhysicalResistance; + + if ( v != 0 ) + list.Add( 1060448, v.ToString() ); // physical resist ~1_val~% + + v = FireResistance; + + if ( v != 0 ) + list.Add( 1060447, v.ToString() ); // fire resist ~1_val~% + + v = ColdResistance; + + if ( v != 0 ) + list.Add( 1060445, v.ToString() ); // cold resist ~1_val~% + + v = PoisonResistance; + + if ( v != 0 ) + list.Add( 1060449, v.ToString() ); // poison resist ~1_val~% + + v = EnergyResistance; + + if ( v != 0 ) + list.Add( 1060446, v.ToString() ); // energy resist ~1_val~% + } + + /// + /// Overridable. Adds header properties. By default, this invokes , (if applicable), and (if ). + /// + public virtual void AddNameProperties( ObjectPropertyList list ) + { + AddNameProperty( list ); + + if ( IsSecure ) + AddSecureProperty( list ); + else if ( IsLockedDown ) + AddLockedDownProperty( list ); + + Mobile blessedFor = this.BlessedFor; + + if ( blessedFor != null && !blessedFor.Deleted ) + AddBlessedForProperty( list, blessedFor ); + + if ( DisplayLootType ) + AddLootTypeProperty( list ); + + AppendChildNameProperties( list ); + } + + /// + /// Overridable. Adds the "Locked Down & Secure" property to the given . + /// + public virtual void AddSecureProperty( ObjectPropertyList list ) + { + list.Add( 501644 ); // locked down & secure + } + + /// + /// Overridable. Adds the "Locked Down" property to the given . + /// + public virtual void AddLockedDownProperty( ObjectPropertyList list ) + { + list.Add( 501643 ); // locked down + } + + /// + /// Overridable. Adds the "Blessed for ~1_NAME~" property to the given . + /// + public virtual void AddBlessedForProperty( ObjectPropertyList list, Mobile m ) + { + list.Add( 1062203, "{0}", m.Name ); // Blessed for ~1_NAME~ + } + + /// + /// Overridable. Fills an with everything applicable. By default, this invokes , then Item.GetChildProperties or Mobile.GetChildProperties. This method should be overriden to add any custom properties. + /// + public virtual void GetProperties( ObjectPropertyList list ) + { + AddNameProperties( list ); + } + + /// + /// Overridable. Event invoked when a child () is building it's . Recursively calls Item.GetChildProperties or Mobile.GetChildProperties. + /// + public virtual void GetChildProperties( ObjectPropertyList list, Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).GetChildProperties( list, item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).GetChildProperties( list, item ); + } + + /// + /// Overridable. Event invoked when a child () is building it's Name . Recursively calls Item.GetChildNameProperties or Mobile.GetChildNameProperties. + /// + public virtual void GetChildNameProperties( ObjectPropertyList list, Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).GetChildNameProperties( list, item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).GetChildNameProperties( list, item ); + } + + public void Bounce( Mobile from ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).RemoveItem( this ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).RemoveItem( this ); + + m_Parent = null; + + BounceInfo bounce = this.GetBounce(); + + if ( bounce != null ) + { + object parent = bounce.m_Parent; + + if ( parent is Item && !((Item)parent).Deleted ) + { + Item p = (Item)parent; + object root = p.RootParent; + if ( p.IsAccessibleTo( from ) && ( !(root is Mobile) || ((Mobile)root).CheckNonlocalDrop( from, this, p ) ) ) + { + Location = bounce.m_Location; + p.AddItem( this ); + } + else + { + MoveToWorld( from.Location, from.Map ); + } + } + else if ( parent is Mobile && !((Mobile)parent).Deleted ) + { + if ( !((Mobile)parent).EquipItem( this ) ) + MoveToWorld( bounce.m_WorldLoc, bounce.m_Map ); + } + else + { + MoveToWorld( bounce.m_WorldLoc, bounce.m_Map ); + } + + ClearBounce(); + } + else + { + MoveToWorld( from.Location, from.Map ); + } + } + + /// + /// Overridable. Method checked to see if this item may be equiped while casting a spell. By default, this returns false. It is overriden on spellbook and spell channeling weapons or shields. + /// + /// True if it may, false if not. + /// + /// + /// public override bool AllowEquipedCast( Mobile from ) + /// { + /// if ( from.Int >= 100 ) + /// return true; + /// + /// return base.AllowEquipedCast( from ); + /// } + /// + /// When placed in an Item script, the item may be cast when equiped if the has 100 or more intelligence. Otherwise, it will drop to their backpack. + /// + public virtual bool AllowEquipedCast( Mobile from ) + { + return false; + } + + public virtual bool CheckConflictingLayer( Mobile m, Item item, Layer layer ) + { + return ( m_Layer == layer ); + } + + public virtual bool CanEquip( Mobile m ) + { + return ( m_Layer != Layer.Invalid && m.FindItemOnLayer( m_Layer ) == null ); + } + + public virtual void GetChildContextMenuEntries( Mobile from, List list, Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).GetChildContextMenuEntries( from, list, item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).GetChildContextMenuEntries( from, list, item ); + } + + public virtual void GetContextMenuEntries( Mobile from, List list ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).GetChildContextMenuEntries( from, list, this ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).GetChildContextMenuEntries( from, list, this ); + } + + public virtual bool VerifyMove( Mobile from ) + { + return Movable; + } + + public virtual DeathMoveResult OnParentDeath( Mobile parent ) + { + if ( !Movable ) + return DeathMoveResult.RemainEquiped; + else if ( parent.KeepsItemsOnDeath ) + return DeathMoveResult.MoveToBackpack; + else if ( CheckBlessed( parent ) ) + return DeathMoveResult.MoveToBackpack; + else if ( CheckNewbied() && parent.Kills < 5 ) + return DeathMoveResult.MoveToBackpack; + else + return DeathMoveResult.MoveToCorpse; + } + + public virtual DeathMoveResult OnInventoryDeath( Mobile parent ) + { + if ( !Movable ) + return DeathMoveResult.MoveToBackpack; + else if ( parent.KeepsItemsOnDeath ) + return DeathMoveResult.MoveToBackpack; + else if ( CheckBlessed( parent ) ) + return DeathMoveResult.MoveToBackpack; + else if ( CheckNewbied() && parent.Kills < 5 ) + return DeathMoveResult.MoveToBackpack; + else + return DeathMoveResult.MoveToCorpse; + } + + /// + /// Moves the Item to . The Item does not change maps. + /// + public virtual void MoveToWorld( Point3D location ) + { + MoveToWorld( location, m_Map ); + } + + public void LabelTo( Mobile to, int number ) + { + to.Send( new MessageLocalized( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", "" ) ); + } + + public void LabelTo( Mobile to, int number, string args ) + { + to.Send( new MessageLocalized( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", args ) ); + } + + public void LabelTo( Mobile to, string text ) + { + to.Send( new UnicodeMessage( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, "ENU", "", text ) ); + } + + public void LabelTo( Mobile to, string format, params object[] args ) + { + LabelTo( to, String.Format( format, args ) ); + } + + public void LabelToAffix( Mobile to, int number, AffixType type, string affix ) + { + to.Send( new MessageLocalizedAffix( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", type, affix, "" ) ); + } + + public void LabelToAffix( Mobile to, int number, AffixType type, string affix, string args ) + { + to.Send( new MessageLocalizedAffix( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, number, "", type, affix, args ) ); + } + + public virtual void LabelLootTypeTo( Mobile to ) + { + if ( m_LootType == LootType.Blessed ) + LabelTo( to, 1041362 ); // (blessed) + else if ( m_LootType == LootType.Cursed ) + LabelTo( to, "(cursed)" ); + } + + public bool AtWorldPoint( int x, int y ) + { + return ( m_Parent == null && m_Location.m_X == x && m_Location.m_Y == y ); + } + + public bool AtPoint( int x, int y ) + { + return ( m_Location.m_X == x && m_Location.m_Y == y ); + } + + /// + /// Moves the Item to a given and . + /// + public void MoveToWorld( Point3D location, Map map ) + { + if ( Deleted ) + return; + + Point3D oldLocation = GetWorldLocation(); + Point3D oldRealLocation = m_Location; + + SetLastMoved(); + + if ( Parent is Mobile ) + ((Mobile)Parent).RemoveItem( this ); + else if ( Parent is Item ) + ((Item)Parent).RemoveItem( this ); + + if ( m_Map != map ) + { + Map old = m_Map; + + if ( m_Map != null ) + { + m_Map.OnLeave( this ); + + if ( oldLocation.m_X != 0 ) + { + Packet remPacket = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( oldLocation, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.InRange( oldLocation, GetUpdateRange( m ) ) ) + { + if ( remPacket == null ) + remPacket = this.RemovePacket; + + state.Send( remPacket ); + } + } + + eable.Free(); + } + } + + m_Location = location; + this.OnLocationChange( oldRealLocation ); + + Packet.Release( ref m_WorldPacket ); + + List items = LookupItems(); + + if ( items != null ) + { + for ( int i = 0; i < items.Count; ++i ) + items[i].Map = map; + } + + m_Map = map; + + if ( m_Map != null ) + m_Map.OnEnter( this ); + + OnMapChange(); + + if ( m_Map != null ) + { + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( m_Location, GetUpdateRange( m ) ) ) + SendInfoTo( state ); + } + + eable.Free(); + } + + RemDelta( ItemDelta.Update ); + + if ( old == null || old == Map.Internal ) + InvalidateProperties(); + } + else if ( m_Map != null ) + { + IPooledEnumerable eable; + + if ( oldLocation.m_X != 0 ) + { + Packet removeThis = null; + + eable = m_Map.GetClientsInRange( oldLocation, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( !m.InRange( location, GetUpdateRange( m ) ) ) + { + if ( removeThis == null ) + removeThis = this.RemovePacket; + + state.Send( removeThis ); + } + } + + eable.Free(); + } + + Point3D oldInternalLocation = m_Location; + + m_Location = location; + this.OnLocationChange( oldRealLocation ); + + Packet.Release( ref m_WorldPacket ); + + eable = m_Map.GetClientsInRange( m_Location, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( m_Location, GetUpdateRange( m ) ) ) + SendInfoTo( state ); + } + + eable.Free(); + + m_Map.OnMove( oldInternalLocation, this ); + + RemDelta( ItemDelta.Update ); + } + else + { + Map = map; + Location = location; + } + } + + Point3D IEntity.Location + { + get + { + return m_Location; + } + } + + /// + /// Has the item been deleted? + /// + public bool Deleted{ get{ return GetFlag( ImplFlag.Deleted ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public LootType LootType + { + get + { + return m_LootType; + } + set + { + if ( m_LootType != value ) + { + m_LootType = value; + + if ( DisplayLootType ) + InvalidateProperties(); + } + } + } + + private static TimeSpan m_DDT = TimeSpan.FromHours( 1.0 ); + + public static TimeSpan DefaultDecayTime{ get{ return m_DDT; } set{ m_DDT = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual TimeSpan DecayTime + { + get + { + return m_DDT; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual bool Decays + { + get + { + return (Movable && Visible); + } + } + + public virtual bool OnDecay() + { + return ( Decays && Parent == null && Map != Map.Internal && Region.Find( Location, Map ).OnDecay( this ) ); + } + + public void SetLastMoved() + { + m_LastMovedTime = DateTime.Now; + } + + public DateTime LastMoved + { + get + { + return m_LastMovedTime; + } + set + { + m_LastMovedTime = value; + } + } + + public virtual bool StackWith( Mobile from, Item dropped ) + { + return StackWith( from, dropped, true ); + } + + public virtual bool StackWith( Mobile from, Item dropped, bool playSound ) + { + if ( Stackable && dropped.Stackable && dropped.GetType() == GetType() && dropped.ItemID == ItemID && dropped.Hue == Hue && (dropped.Amount + Amount) <= 60000 ) + { + if ( m_LootType != dropped.m_LootType ) + m_LootType = LootType.Regular; + + Amount += dropped.Amount; + dropped.Delete(); + + if ( playSound && from != null ) + { + int soundID = GetDropSound(); + + if ( soundID == -1 ) + soundID = 0x42; + + from.SendSound( soundID, GetWorldLocation() ); + } + + return true; + } + + return false; + } + + public virtual bool OnDragDrop( Mobile from, Item dropped ) + { + if ( Parent is Container ) + return ((Container)Parent).OnStackAttempt( from, this, dropped ); + + return StackWith( from, dropped ); + } + + public Rectangle2D GetGraphicBounds() + { + int itemID = m_ItemID; + bool doubled = m_Amount > 1; + + if ( itemID >= 0xEEA && itemID <= 0xEF2 ) // Are we coins? + { + int coinBase = (itemID - 0xEEA) / 3; + coinBase *= 3; + coinBase += 0xEEA; + + doubled = false; + + if ( m_Amount <= 1 ) + { + // A single coin + itemID = coinBase; + } + else if ( m_Amount <= 5 ) + { + // A stack of coins + itemID = coinBase + 1; + } + else // m_Amount > 5 + { + // A pile of coins + itemID = coinBase + 2; + } + } + + Rectangle2D bounds = ItemBounds.Table[itemID & 0x3FFF]; + + if ( doubled ) + { + bounds.Set( bounds.X, bounds.Y, bounds.Width + 5, bounds.Height + 5 ); + } + + return bounds; + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Stackable + { + get{ return GetFlag( ImplFlag.Stackable ); } + set{ SetFlag( ImplFlag.Stackable, value ); } + } + + public Packet RemovePacket + { + get + { + if ( m_RemovePacket == null ) + { + m_RemovePacket = new RemoveItem( this ); + m_RemovePacket.SetStatic(); + } + + return m_RemovePacket; + } + } + + public Packet OPLPacket + { + get + { + if ( m_OPLPacket == null ) + { + m_OPLPacket = new OPLInfo( PropertyList ); + m_OPLPacket.SetStatic(); + } + + return m_OPLPacket; + } + } + + public ObjectPropertyList PropertyList + { + get + { + if ( m_PropertyList == null ) + { + m_PropertyList = new ObjectPropertyList( this ); + + GetProperties( m_PropertyList ); + AppendChildProperties( m_PropertyList ); + + m_PropertyList.Terminate(); + m_PropertyList.SetStatic(); + } + + return m_PropertyList; + } + } + + public virtual void AppendChildProperties( ObjectPropertyList list ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).GetChildProperties( list, this ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).GetChildProperties( list, this ); + } + + public virtual void AppendChildNameProperties( ObjectPropertyList list ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).GetChildNameProperties( list, this ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).GetChildNameProperties( list, this ); + } + + public void ClearProperties() + { + Packet.Release( ref m_PropertyList ); + Packet.Release( ref m_OPLPacket ); + } + + public void InvalidateProperties() + { + if ( !Core.AOS ) + return; + + if ( m_Map != null && m_Map != Map.Internal && !World.Loading ) + { + ObjectPropertyList oldList = m_PropertyList; + m_PropertyList = null; + ObjectPropertyList newList = PropertyList; + + if ( oldList == null || oldList.Hash != newList.Hash ) + { + Packet.Release( ref m_OPLPacket ); + Delta( ItemDelta.Properties ); + } + } + else + { + ClearProperties(); + } + } + + public Packet WorldPacket + { + get + { + // This needs to be invalidated when any of the following changes: + // - ItemID + // - Amount + // - Location + // - Hue + // - Packet Flags + // - Direction + + if ( m_WorldPacket == null ) + { + m_WorldPacket = new WorldItem( this ); + m_WorldPacket.SetStatic(); + } + + return m_WorldPacket; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Visible + { + get{ return GetFlag( ImplFlag.Visible ); } + set + { + if ( GetFlag( ImplFlag.Visible ) != value ) + { + SetFlag( ImplFlag.Visible, value ); + Packet.Release( ref m_WorldPacket ); + + if ( m_Map != null ) + { + Packet removeThis = null; + Point3D worldLoc = GetWorldLocation(); + + IPooledEnumerable eable = m_Map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( !m.CanSee( this ) && m.InRange( worldLoc, GetUpdateRange( m ) ) ) + { + if ( removeThis == null ) + removeThis = this.RemovePacket; + + state.Send( removeThis ); + } + } + + eable.Free(); + } + + Delta( ItemDelta.Update ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Movable + { + get{ return GetFlag( ImplFlag.Movable ); } + set + { + if ( GetFlag( ImplFlag.Movable ) != value ) + { + SetFlag( ImplFlag.Movable, value ); + Packet.Release( ref m_WorldPacket ); + Delta( ItemDelta.Update ); + } + } + } + + public virtual bool ForceShowProperties{ get{ return false; } } + + public virtual int GetPacketFlags() + { + int flags = 0; + + if ( !Visible ) + flags |= 0x80; + + if ( Movable || ForceShowProperties ) + flags |= 0x20; + + return flags; + } + + public virtual bool OnMoveOff( Mobile m ) + { + return true; + } + + public virtual bool OnMoveOver( Mobile m ) + { + return true; + } + + public virtual bool HandlesOnMovement{ get{ return false; } } + + public virtual void OnMovement( Mobile m, Point3D oldLocation ) + { + } + + public void Internalize() + { + MoveToWorld( Point3D.Zero, Map.Internal ); + } + + public virtual void OnMapChange() + { + } + + public virtual void OnRemoved( object parent ) + { + } + + public virtual void OnAdded( object parent ) + { + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public Map Map + { + get + { + return m_Map; + } + set + { + if ( m_Map != value ) + { + Map old = m_Map; + + if ( m_Map != null && m_Parent == null ) + { + m_Map.OnLeave( this ); + SendRemovePacket(); + } + + List items = LookupItems(); + + if ( items != null ) + { + for ( int i = 0; i < items.Count; ++i ) + items[i].Map = value; + } + + m_Map = value; + + if ( m_Map != null && m_Parent == null ) + m_Map.OnEnter( this ); + + Delta( ItemDelta.Update ); + + this.OnMapChange(); + + if ( old == null || old == Map.Internal ) + InvalidateProperties(); + } + } + } + + [Flags] + private enum SaveFlag + { + None = 0x00000000, + Direction = 0x00000001, + Bounce = 0x00000002, + LootType = 0x00000004, + LocationFull = 0x00000008, + ItemID = 0x00000010, + Hue = 0x00000020, + Amount = 0x00000040, + Layer = 0x00000080, + Name = 0x00000100, + Parent = 0x00000200, + Items = 0x00000400, + WeightNot1or0 = 0x00000800, + Map = 0x00001000, + Visible = 0x00002000, + Movable = 0x00004000, + Stackable = 0x00008000, + WeightIs0 = 0x00010000, + LocationSByteZ = 0x00020000, + LocationShortXY = 0x00040000, + LocationByteXY = 0x00080000, + ImplFlags = 0x00100000, + InsuredFor = 0x00200000, + BlessedFor = 0x00400000, + HeldBy = 0x00800000, + IntWeight = 0x01000000, + SavedFlags = 0x02000000, + NullWeight = 0x04000000 + } + + 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 ); + } + + int ISerializable.TypeReference { + get { return m_TypeRef; } + } + + int ISerializable.SerialIdentity { + get { return m_Serial; } + } + + public virtual void Serialize( GenericWriter writer ) + { + writer.Write( 9 ); // version + + SaveFlag flags = SaveFlag.None; + + int x = m_Location.m_X, y = m_Location.m_Y, z = m_Location.m_Z; + + if ( x != 0 || y != 0 || z != 0 ) + { + if ( x >= short.MinValue && x <= short.MaxValue && y >= short.MinValue && y <= short.MaxValue && z >= sbyte.MinValue && z <= sbyte.MaxValue ) + { + if ( x != 0 || y != 0 ) + { + if ( x >= byte.MinValue && x <= byte.MaxValue && y >= byte.MinValue && y <= byte.MaxValue ) + flags |= SaveFlag.LocationByteXY; + else + flags |= SaveFlag.LocationShortXY; + } + + if ( z != 0 ) + flags |= SaveFlag.LocationSByteZ; + } + else + { + flags |= SaveFlag.LocationFull; + } + } + + CompactInfo info = LookupCompactInfo(); + List items = LookupItems(); + + if ( m_Direction != Direction.North ) + flags |= SaveFlag.Direction; + if ( info != null && info.m_Bounce != null ) + flags |= SaveFlag.Bounce; + if ( m_LootType != LootType.Regular ) + flags |= SaveFlag.LootType; + if ( m_ItemID != 0 ) + flags |= SaveFlag.ItemID; + if ( m_Hue != 0 ) + flags |= SaveFlag.Hue; + if ( m_Amount != 1 ) + flags |= SaveFlag.Amount; + if ( m_Layer != Layer.Invalid ) + flags |= SaveFlag.Layer; + if ( info != null && info.m_Name != null ) + flags |= SaveFlag.Name; + if ( m_Parent != null ) + flags |= SaveFlag.Parent; + if ( items != null && items.Count > 0 ) + flags |= SaveFlag.Items; + if ( m_Map != Map.Internal ) + flags |= SaveFlag.Map; + //if ( m_InsuredFor != null && !m_InsuredFor.Deleted ) + //flags |= SaveFlag.InsuredFor; + if ( info != null && info.m_BlessedFor != null && !info.m_BlessedFor.Deleted ) + flags |= SaveFlag.BlessedFor; + if ( info != null && info.m_HeldBy != null && !info.m_HeldBy.Deleted ) + flags |= SaveFlag.HeldBy; + if ( info != null && info.m_SavedFlags != 0 ) + flags |= SaveFlag.SavedFlags; + + if ( info == null || info.m_Weight == -1 ) + { + flags |= SaveFlag.NullWeight; + } + else + { + if ( info.m_Weight == 0.0 ) + { + flags |= SaveFlag.WeightIs0; + } + else if ( info.m_Weight != 1.0 ) + { + if ( info.m_Weight == (int) info.m_Weight ) + flags |= SaveFlag.IntWeight; + else + flags |= SaveFlag.WeightNot1or0; + } + } + + ImplFlag implFlags = ( m_Flags & ( ImplFlag.Visible | ImplFlag.Movable | ImplFlag.Stackable | ImplFlag.Insured | ImplFlag.PayedInsurance ) ); + + if ( implFlags != ( ImplFlag.Visible | ImplFlag.Movable ) ) + flags |= SaveFlag.ImplFlags; + + writer.Write( (int) flags ); + + /* begin last moved time optimization */ + long ticks = m_LastMovedTime.Ticks; + long now = DateTime.Now.Ticks; + + TimeSpan d; + + try { d = new TimeSpan( ticks - now ); } + catch { if ( ticks < now ) d = TimeSpan.MaxValue; else d = TimeSpan.MaxValue; } + + double minutes = -d.TotalMinutes; + + if ( minutes < int.MinValue ) + minutes = int.MinValue; + else if ( minutes > int.MaxValue ) + minutes = int.MaxValue; + + writer.WriteEncodedInt( (int) minutes ); + /* end */ + + if ( GetSaveFlag( flags, SaveFlag.Direction ) ) + writer.Write( (byte) m_Direction ); + + if ( GetSaveFlag( flags, SaveFlag.Bounce ) ) + BounceInfo.Serialize( info.m_Bounce, writer ); + + if ( GetSaveFlag( flags, SaveFlag.LootType ) ) + writer.Write( (byte) m_LootType ); + + if ( GetSaveFlag( flags, SaveFlag.LocationFull ) ) + { + writer.WriteEncodedInt( x ); + writer.WriteEncodedInt( y ); + writer.WriteEncodedInt( z ); + } + else + { + if ( GetSaveFlag( flags, SaveFlag.LocationByteXY ) ) + { + writer.Write( (byte) x ); + writer.Write( (byte) y ); + } + else if ( GetSaveFlag( flags, SaveFlag.LocationShortXY ) ) + { + writer.Write( (short) x ); + writer.Write( (short) y ); + } + + if ( GetSaveFlag( flags, SaveFlag.LocationSByteZ ) ) + writer.Write( (sbyte) z ); + } + + if ( GetSaveFlag( flags, SaveFlag.ItemID ) ) + writer.WriteEncodedInt( (int) m_ItemID ); + + if ( GetSaveFlag( flags, SaveFlag.Hue ) ) + writer.WriteEncodedInt( (int) m_Hue ); + + if ( GetSaveFlag( flags, SaveFlag.Amount ) ) + writer.WriteEncodedInt( (int) m_Amount ); + + if ( GetSaveFlag( flags, SaveFlag.Layer ) ) + writer.Write( (byte) m_Layer ); + + if ( GetSaveFlag( flags, SaveFlag.Name ) ) + writer.Write( (string) info.m_Name ); + + if ( GetSaveFlag( flags, SaveFlag.Parent ) ) + { + if ( m_Parent is Mobile && !( (Mobile) m_Parent ).Deleted ) + writer.Write( ( (Mobile) m_Parent ).Serial ); + else if ( m_Parent is Item && !( (Item) m_Parent ).Deleted ) + writer.Write( ( (Item) m_Parent ).Serial ); + else + writer.Write( (int) Serial.MinusOne ); + } + + if ( GetSaveFlag( flags, SaveFlag.Items ) ) + writer.Write( items, false ); + + if ( GetSaveFlag( flags, SaveFlag.IntWeight ) ) + writer.WriteEncodedInt( (int) info.m_Weight ); + else if ( GetSaveFlag( flags, SaveFlag.WeightNot1or0 ) ) + writer.Write( (double) info.m_Weight ); + + if ( GetSaveFlag( flags, SaveFlag.Map ) ) + writer.Write( (Map) m_Map ); + + if ( GetSaveFlag( flags, SaveFlag.ImplFlags ) ) + writer.WriteEncodedInt( (int) implFlags ); + + if ( GetSaveFlag( flags, SaveFlag.InsuredFor ) ) + writer.Write( (Mobile) null ); + + if ( GetSaveFlag( flags, SaveFlag.BlessedFor ) ) + writer.Write( info.m_BlessedFor ); + + if ( GetSaveFlag( flags, SaveFlag.HeldBy ) ) + writer.Write( info.m_HeldBy ); + + if ( GetSaveFlag( flags, SaveFlag.SavedFlags ) ) + writer.WriteEncodedInt( info.m_SavedFlags ); + } + + public IPooledEnumerable GetObjectsInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + if ( m_Parent == null ) + return map.GetObjectsInRange( m_Location, range ); + + return map.GetObjectsInRange( GetWorldLocation(), range ); + } + + public IPooledEnumerable GetItemsInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + if ( m_Parent == null ) + return map.GetItemsInRange( m_Location, range ); + + return map.GetItemsInRange( GetWorldLocation(), range ); + } + + public IPooledEnumerable GetMobilesInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + if ( m_Parent == null ) + return map.GetMobilesInRange( m_Location, range ); + + return map.GetMobilesInRange( GetWorldLocation(), range ); + } + + public IPooledEnumerable GetClientsInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + if ( m_Parent == null ) + return map.GetClientsInRange( m_Location, range ); + + return map.GetClientsInRange( GetWorldLocation(), range ); + } + + private static int m_LockedDownFlag; + private static int m_SecureFlag; + + public static int LockedDownFlag + { + get{ return m_LockedDownFlag; } + set{ m_LockedDownFlag = value; } + } + + public static int SecureFlag + { + get{ return m_SecureFlag; } + set{ m_SecureFlag = value; } + } + + public bool IsLockedDown + { + get{ return GetTempFlag( m_LockedDownFlag ); } + set{ SetTempFlag( m_LockedDownFlag, value ); InvalidateProperties(); } + } + + public bool IsSecure + { + get{ return GetTempFlag( m_SecureFlag ); } + set{ SetTempFlag( m_SecureFlag, value ); InvalidateProperties(); } + } + + public bool GetTempFlag( int flag ) + { + CompactInfo info = LookupCompactInfo(); + + if ( info == null ) + return false; + + return ( (info.m_TempFlags & flag) != 0 ); + } + + public void SetTempFlag( int flag, bool value ) + { + CompactInfo info = AcquireCompactInfo(); + + if ( value ) + info.m_TempFlags |= flag; + else + info.m_TempFlags &= ~flag; + + if ( info.m_TempFlags == 0 ) + VerifyCompactInfo(); + } + + public bool GetSavedFlag( int flag ) + { + CompactInfo info = LookupCompactInfo(); + + if ( info == null ) + return false; + + return ( ( info.m_SavedFlags & flag ) != 0 ); + } + + public void SetSavedFlag( int flag, bool value ) + { + CompactInfo info = AcquireCompactInfo(); + + if ( value ) + info.m_SavedFlags |= flag; + else + info.m_SavedFlags &= ~flag; + + if ( info.m_SavedFlags == 0 ) + VerifyCompactInfo(); + } + + public virtual void Deserialize( GenericReader reader ) + { + int version = reader.ReadInt(); + + SetLastMoved(); + + switch ( version ) + { + case 9: + case 8: + case 7: + case 6: + { + SaveFlag flags = (SaveFlag)reader.ReadInt(); + + if ( version < 7 ) + { + LastMoved = reader.ReadDeltaTime(); + } + else + { + int minutes = reader.ReadEncodedInt(); + + try{ LastMoved = DateTime.Now - TimeSpan.FromMinutes( minutes ); } + catch{ LastMoved = DateTime.Now; } + } + + if ( GetSaveFlag( flags, SaveFlag.Direction ) ) + m_Direction = (Direction)reader.ReadByte(); + + if ( GetSaveFlag( flags, SaveFlag.Bounce ) ) + AcquireCompactInfo().m_Bounce = BounceInfo.Deserialize( reader ); + + if ( GetSaveFlag( flags, SaveFlag.LootType ) ) + m_LootType = (LootType)reader.ReadByte(); + + int x = 0, y = 0, z = 0; + + if ( GetSaveFlag( flags, SaveFlag.LocationFull ) ) + { + x = reader.ReadEncodedInt(); + y = reader.ReadEncodedInt(); + z = reader.ReadEncodedInt(); + } + else + { + if ( GetSaveFlag( flags, SaveFlag.LocationByteXY ) ) + { + x = reader.ReadByte(); + y = reader.ReadByte(); + } + else if ( GetSaveFlag( flags, SaveFlag.LocationShortXY ) ) + { + x = reader.ReadShort(); + y = reader.ReadShort(); + } + + if ( GetSaveFlag( flags, SaveFlag.LocationSByteZ ) ) + z = reader.ReadSByte(); + } + + m_Location = new Point3D( x, y, z ); + + if ( GetSaveFlag( flags, SaveFlag.ItemID ) ) + m_ItemID = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Hue ) ) + m_Hue = reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.Amount ) ) + m_Amount = reader.ReadEncodedInt(); + else + m_Amount = 1; + + if ( GetSaveFlag( flags, SaveFlag.Layer ) ) + m_Layer = (Layer)reader.ReadByte(); + + if ( GetSaveFlag( flags, SaveFlag.Name ) ) + { + string name = reader.ReadString(); + + if ( name != this.DefaultName ) + AcquireCompactInfo().m_Name = name; + } + + if ( GetSaveFlag( flags, SaveFlag.Parent ) ) + { + Serial parent = reader.ReadInt(); + + if ( parent.IsMobile ) + m_Parent = World.FindMobile( parent ); + else if ( parent.IsItem ) + m_Parent = World.FindItem( parent ); + else + m_Parent = null; + + if ( m_Parent == null && (parent.IsMobile || parent.IsItem) ) + Delete(); + } + + if ( GetSaveFlag( flags, SaveFlag.Items ) ) + { + List items = reader.ReadStrongItemList(); + + if ( this is Container ) + ( this as Container ).m_Items = items; + else + AcquireCompactInfo().m_Items = items; + } + + if ( version < 8 || !GetSaveFlag( flags, SaveFlag.NullWeight ) ) + { + double weight; + + if ( GetSaveFlag( flags, SaveFlag.IntWeight ) ) + weight = reader.ReadEncodedInt(); + else if ( GetSaveFlag( flags, SaveFlag.WeightNot1or0 ) ) + weight = reader.ReadDouble(); + else if ( GetSaveFlag( flags, SaveFlag.WeightIs0 ) ) + weight = 0.0; + else + weight = 1.0; + + if ( weight != DefaultWeight ) + AcquireCompactInfo().m_Weight = weight; + } + + if ( GetSaveFlag( flags, SaveFlag.Map ) ) + m_Map = reader.ReadMap(); + else + m_Map = Map.Internal; + + if ( GetSaveFlag( flags, SaveFlag.Visible ) ) + SetFlag( ImplFlag.Visible, reader.ReadBool() ); + else + SetFlag( ImplFlag.Visible, true ); + + if ( GetSaveFlag( flags, SaveFlag.Movable ) ) + SetFlag( ImplFlag.Movable, reader.ReadBool() ); + else + SetFlag( ImplFlag.Movable, true ); + + if ( GetSaveFlag( flags, SaveFlag.Stackable ) ) + SetFlag( ImplFlag.Stackable, reader.ReadBool() ); + + if ( GetSaveFlag( flags, SaveFlag.ImplFlags ) ) + m_Flags = (ImplFlag)reader.ReadEncodedInt(); + + if ( GetSaveFlag( flags, SaveFlag.InsuredFor ) ) + /*m_InsuredFor = */reader.ReadMobile(); + + if ( GetSaveFlag( flags, SaveFlag.BlessedFor ) ) + AcquireCompactInfo().m_BlessedFor = reader.ReadMobile(); + + if ( GetSaveFlag( flags, SaveFlag.HeldBy ) ) + AcquireCompactInfo().m_HeldBy = reader.ReadMobile(); + + if ( GetSaveFlag( flags, SaveFlag.SavedFlags ) ) + AcquireCompactInfo().m_SavedFlags = reader.ReadEncodedInt(); + + if ( m_Map != null && m_Parent == null ) + m_Map.OnEnter( this ); + + break; + } + case 5: + { + SaveFlag flags = (SaveFlag)reader.ReadInt(); + + LastMoved = reader.ReadDeltaTime(); + + if ( GetSaveFlag( flags, SaveFlag.Direction ) ) + m_Direction = (Direction)reader.ReadByte(); + + if ( GetSaveFlag( flags, SaveFlag.Bounce ) ) + AcquireCompactInfo().m_Bounce = BounceInfo.Deserialize( reader ); + + if ( GetSaveFlag( flags, SaveFlag.LootType ) ) + m_LootType = (LootType)reader.ReadByte(); + + if ( GetSaveFlag( flags, SaveFlag.LocationFull ) ) + m_Location = reader.ReadPoint3D(); + + if ( GetSaveFlag( flags, SaveFlag.ItemID ) ) + m_ItemID = reader.ReadInt(); + + if ( GetSaveFlag( flags, SaveFlag.Hue ) ) + m_Hue = reader.ReadInt(); + + if ( GetSaveFlag( flags, SaveFlag.Amount ) ) + m_Amount = reader.ReadInt(); + else + m_Amount = 1; + + if ( GetSaveFlag( flags, SaveFlag.Layer ) ) + m_Layer = (Layer)reader.ReadByte(); + + if ( GetSaveFlag( flags, SaveFlag.Name ) ) + { + string name = reader.ReadString(); + + if ( name != this.DefaultName ) + AcquireCompactInfo().m_Name = name; + } + + if ( GetSaveFlag( flags, SaveFlag.Parent ) ) + { + Serial parent = reader.ReadInt(); + + if ( parent.IsMobile ) + m_Parent = World.FindMobile( parent ); + else if ( parent.IsItem ) + m_Parent = World.FindItem( parent ); + else + m_Parent = null; + + if ( m_Parent == null && (parent.IsMobile || parent.IsItem) ) + Delete(); + } + + if ( GetSaveFlag( flags, SaveFlag.Items ) ) + { + List items = reader.ReadStrongItemList(); + + if ( this is Container ) + ( this as Container ).m_Items = items; + else + AcquireCompactInfo().m_Items = items; + } + + double weight; + + if ( GetSaveFlag( flags, SaveFlag.IntWeight ) ) + weight = reader.ReadEncodedInt(); + else if ( GetSaveFlag( flags, SaveFlag.WeightNot1or0 ) ) + weight = reader.ReadDouble(); + else if ( GetSaveFlag( flags, SaveFlag.WeightIs0 ) ) + weight = 0.0; + else + weight = 1.0; + + if ( weight != DefaultWeight ) + AcquireCompactInfo().m_Weight = weight; + + if ( GetSaveFlag( flags, SaveFlag.Map ) ) + m_Map = reader.ReadMap(); + else + m_Map = Map.Internal; + + if ( GetSaveFlag( flags, SaveFlag.Visible ) ) + SetFlag( ImplFlag.Visible, reader.ReadBool() ); + else + SetFlag( ImplFlag.Visible, true ); + + if ( GetSaveFlag( flags, SaveFlag.Movable ) ) + SetFlag( ImplFlag.Movable, reader.ReadBool() ); + else + SetFlag( ImplFlag.Movable, true ); + + if ( GetSaveFlag( flags, SaveFlag.Stackable ) ) + SetFlag( ImplFlag.Stackable, reader.ReadBool() ); + + if ( m_Map != null && m_Parent == null ) + m_Map.OnEnter( this ); + + break; + } + case 4: // Just removed variables + case 3: + { + m_Direction = (Direction)reader.ReadInt(); + + goto case 2; + } + case 2: + { + AcquireCompactInfo().m_Bounce = BounceInfo.Deserialize( reader ); + LastMoved = reader.ReadDeltaTime(); + + goto case 1; + } + case 1: + { + m_LootType = (LootType) reader.ReadByte();//m_Newbied = reader.ReadBool(); + + goto case 0; + } + case 0: + { + m_Location = reader.ReadPoint3D(); + m_ItemID = reader.ReadInt(); + m_Hue = reader.ReadInt(); + m_Amount = reader.ReadInt(); + m_Layer = (Layer) reader.ReadByte(); + + string name = reader.ReadString(); + + if ( name != this.DefaultName ) + AcquireCompactInfo().m_Name = name; + + Serial parent = reader.ReadInt(); + + if ( parent.IsMobile ) + m_Parent = World.FindMobile( parent ); + else if ( parent.IsItem ) + m_Parent = World.FindItem( parent ); + else + m_Parent = null; + + if ( m_Parent == null && (parent.IsMobile || parent.IsItem) ) + Delete(); + + int count = reader.ReadInt(); + + if ( count > 0 ) + { + List items = new List( count ); + + for ( int i = 0; i < count; ++i ) + { + Item item = reader.ReadItem(); + + if ( item != null ) + items.Add( item ); + } + + if ( this is Container ) + ( this as Container ).m_Items = items; + else + AcquireCompactInfo().m_Items = items; + } + + double weight = reader.ReadDouble(); + + if ( weight != DefaultWeight ) + AcquireCompactInfo().m_Weight = weight; + + if ( version <= 3 ) + { + reader.ReadInt(); + reader.ReadInt(); + reader.ReadInt(); + } + + m_Map = reader.ReadMap(); + SetFlag( ImplFlag.Visible, reader.ReadBool() ); + SetFlag( ImplFlag.Movable, reader.ReadBool() ); + + if ( version <= 3 ) + /*m_Deleted =*/ reader.ReadBool(); + + Stackable = reader.ReadBool(); + + if ( m_Map != null && m_Parent == null ) + m_Map.OnEnter( this ); + + break; + } + } + + if ( this.HeldBy != null ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( FixHolding_Sandbox ) ); + + //if ( version < 9 ) + VerifyCompactInfo(); + } + + private void FixHolding_Sandbox() + { + Mobile heldBy = this.HeldBy; + + if ( heldBy != null ) + { + if ( this.GetBounce() != null ) + { + Bounce( heldBy ); + } + else + { + heldBy.Holding = null; + heldBy.AddToBackpack( this ); + ClearBounce(); + } + } + } + + public virtual int GetMaxUpdateRange() + { + return 18; + } + + public virtual int GetUpdateRange( Mobile m ) + { + return 18; + } + + public virtual void SendInfoTo( NetState state ) + { + state.Send( WorldPacket ); + + if ( ObjectPropertyList.Enabled ) + state.Send( OPLPacket ); + } + + public virtual bool IsVirtualItem{ get{ return false; } } + + public virtual int GetTotal( TotalType type ) + { + return 0; + } + + public virtual void UpdateTotal( Item sender, TotalType type, int delta ) + { + if ( !IsVirtualItem ) + { + if ( m_Parent is Item ) + ( m_Parent as Item ).UpdateTotal( sender, type, delta ); + else if ( m_Parent is Mobile ) + ( m_Parent as Mobile ).UpdateTotal( sender, type, delta ); + } + } + + public virtual void UpdateTotals() + { + } + + public virtual int LabelNumber + { + get + { + return 1020000 + (m_ItemID & 0x3FFF); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TotalGold + { + get { return GetTotal( TotalType.Gold ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TotalItems + { + get { return GetTotal( TotalType.Items ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TotalWeight + { + get { return GetTotal( TotalType.Weight ); } + } + + public virtual double DefaultWeight + { + get + { + if ( m_ItemID < 0 || m_ItemID >= 0x4000 ) + return 0; + + int weight = TileData.ItemTable[m_ItemID].Weight; + + if ( weight == 255 || weight == 0 ) + weight = 1; + + return weight; + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public double Weight + { + get + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null && info.m_Weight != -1 ) + return info.m_Weight; + + return this.DefaultWeight; + } + set + { + if ( this.Weight != value ) + { + CompactInfo info = AcquireCompactInfo(); + + int oldPileWeight = this.PileWeight; + + info.m_Weight = value; + + if ( info.m_Weight == -1 ) + VerifyCompactInfo(); + + int newPileWeight = this.PileWeight; + + UpdateTotal( this, TotalType.Weight, newPileWeight - oldPileWeight ); + + InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int PileWeight + { + get + { + return (int)Math.Ceiling( this.Weight * this.Amount ); + } + } + + public virtual int HuedItemID + { + get + { + return ( m_ItemID & 0x3FFF ); + } + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public virtual int Hue + { + get + { + return m_Hue; + } + set + { + if ( m_Hue != value ) + { + m_Hue = value; + Packet.Release( ref m_WorldPacket ); + + Delta( ItemDelta.Update ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual Layer Layer + { + get + { + return m_Layer; + } + set + { + if ( m_Layer != value ) + { + m_Layer = value; + + Delta( ItemDelta.EquipOnly ); + } + } + } + + public List Items + { + get + { + List items = LookupItems(); + + if( items == null ) + items = EmptyItems; + + return items; + } + } + + public object RootParent + { + get + { + object p = m_Parent; + + while ( p is Item ) + { + Item item = (Item)p; + + if ( item.m_Parent == null ) + { + break; + } + else + { + p = item.m_Parent; + } + } + + return p; + } + } + + public virtual void AddItem( Item item ) + { + if ( item == null || item.Deleted || item.m_Parent == this ) + { + return; + } + else if ( item == this ) + { + Console.WriteLine( "Warning: Adding item to itself: [0x{0:X} {1}].AddItem( [0x{2:X} {3}] )", this.Serial.Value, this.GetType().Name, item.Serial.Value, item.GetType().Name ); + Console.WriteLine( new System.Diagnostics.StackTrace() ); + return; + } + else if ( IsChildOf( item ) ) + { + Console.WriteLine( "Warning: Adding parent item to child: [0x{0:X} {1}].AddItem( [0x{2:X} {3}] )", this.Serial.Value, this.GetType().Name, item.Serial.Value, item.GetType().Name ); + Console.WriteLine( new System.Diagnostics.StackTrace() ); + return; + } + else if ( item.m_Parent is Mobile ) + { + ((Mobile)item.m_Parent).RemoveItem( item ); + } + else if ( item.m_Parent is Item ) + { + ((Item)item.m_Parent).RemoveItem( item ); + } + else + { + item.SendRemovePacket(); + } + + item.Parent = this; + item.Map = m_Map; + + List items = AcquireItems(); + + items.Add( item ); + + if ( !item.IsVirtualItem ) + { + UpdateTotal( item, TotalType.Gold, item.TotalGold ); + UpdateTotal( item, TotalType.Items, item.TotalItems + 1 ); + UpdateTotal( item, TotalType.Weight, item.TotalWeight + item.PileWeight ); + } + + item.Delta( ItemDelta.Update ); + + item.OnAdded( this ); + OnItemAdded( item ); + } + + private static List m_DeltaQueue = new List(); + + public void Delta( ItemDelta flags ) + { + if ( m_Map == null || m_Map == Map.Internal ) + return; + + m_DeltaFlags |= flags; + + if ( !GetFlag( ImplFlag.InQueue ) ) + { + SetFlag( ImplFlag.InQueue, true ); + + m_DeltaQueue.Add( this ); + } + } + + public void RemDelta( ItemDelta flags ) + { + m_DeltaFlags &= ~flags; + + if ( GetFlag( ImplFlag.InQueue ) && m_DeltaFlags == ItemDelta.None ) + { + SetFlag( ImplFlag.InQueue, false ); + + m_DeltaQueue.Remove( this ); + } + } + + public void ProcessDelta() + { + ItemDelta flags = m_DeltaFlags; + + SetFlag( ImplFlag.InQueue, false ); + m_DeltaFlags = ItemDelta.None; + + Map map = m_Map; + + if ( map != null && !Deleted ) + { + bool sendOPLUpdate = ObjectPropertyList.Enabled && (flags & ItemDelta.Properties) != 0; + + Container contParent = m_Parent as Container; + + if ( contParent != null && !contParent.IsPublicContainer ) + { + if ( (flags & ItemDelta.Update) != 0 ) + { + Packet p = null; + Point3D worldLoc = GetWorldLocation(); + + Mobile rootParent = contParent.RootParent as Mobile; + Mobile tradeRecip = null; + + if ( rootParent != null ) + { + NetState ns = rootParent.NetState; + + if ( ns != null ) + { + if ( rootParent.CanSee( this ) && rootParent.InRange( worldLoc, GetUpdateRange( rootParent ) ) ) + { + if ( p == null ) + p = Packet.Acquire( new ContainerContentUpdate( this ) ); + + ns.Send( p ); + + if ( ObjectPropertyList.Enabled ) + ns.Send( OPLPacket ); + } + } + } + + SecureTradeContainer stc = this.GetSecureTradeCont(); + + if ( stc != null ) + { + SecureTrade st = stc.Trade; + + if ( st != null ) + { + Mobile test = st.From.Mobile; + + if ( test != null && test != rootParent ) + tradeRecip = test; + + test = st.To.Mobile; + + if ( test != null && test != rootParent ) + tradeRecip = test; + + if ( tradeRecip != null ) + { + NetState ns = tradeRecip.NetState; + + if ( ns != null ) + { + if ( tradeRecip.CanSee( this ) && tradeRecip.InRange( worldLoc, GetUpdateRange( tradeRecip ) ) ) + { + if ( p == null ) + p = Packet.Acquire( new ContainerContentUpdate( this ) ); + + ns.Send( p ); + + if ( ObjectPropertyList.Enabled ) + ns.Send( OPLPacket ); + } + } + } + } + } + + List openers = contParent.Openers; + + if ( openers != null ) + { + for ( int i = 0; i < openers.Count; ++i ) + { + Mobile mob = openers[i]; + + int range = GetUpdateRange( mob ); + + if ( mob.Map != map || !mob.InRange( worldLoc, range ) ) + { + openers.RemoveAt( i-- ); + } + else + { + if ( mob == rootParent || mob == tradeRecip ) + continue; + + NetState ns = mob.NetState; + + if ( ns != null ) + { + if ( mob.CanSee( this ) ) + { + if ( p == null ) + p = Packet.Acquire( new ContainerContentUpdate( this ) ); + + ns.Send( p ); + + if ( ObjectPropertyList.Enabled ) + ns.Send( OPLPacket ); + } + } + } + } + + if ( openers.Count == 0 ) + contParent.Openers = null; + } + + Packet.Release( p ); + + return; + } + } + + if ( (flags & ItemDelta.Update) != 0 ) + { + Packet p = null; + Point3D worldLoc = GetWorldLocation(); + + IPooledEnumerable eable = map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( worldLoc, GetUpdateRange( m ) ) ) + { + //if ( sendOPLUpdate ) + // state.Send( RemovePacket ); + + if ( m_Parent == null ) + { + SendInfoTo( state ); + } + else + { + if ( p == null ) + { + if ( m_Parent is Item ) + p = new ContainerContentUpdate( this ); + else if ( m_Parent is Mobile ) + p = new EquipUpdate( this ); + + p.Acquire(); + } + + state.Send( p ); + + if ( ObjectPropertyList.Enabled ) + state.Send( OPLPacket ); + } + } + } + + Packet.Release( p ); + + eable.Free(); + sendOPLUpdate = false; + } + else if ( (flags & ItemDelta.EquipOnly ) != 0 ) + { + if ( m_Parent is Mobile ) + { + Packet p = null; + Point3D worldLoc = GetWorldLocation(); + + IPooledEnumerable eable = map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( worldLoc, GetUpdateRange( m ) ) ) + { + //if ( sendOPLUpdate ) + // state.Send( RemovePacket ); + + if ( p == null ) + p = Packet.Acquire( new EquipUpdate( this ) ); + + state.Send( p ); + + if ( ObjectPropertyList.Enabled ) + state.Send( OPLPacket ); + } + } + + Packet.Release( p ); + + eable.Free(); + sendOPLUpdate = false; + } + } + + if ( sendOPLUpdate ) + { + Point3D worldLoc = GetWorldLocation(); + IPooledEnumerable eable = map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( worldLoc, GetUpdateRange( m ) ) ) + state.Send( OPLPacket ); + } + + eable.Free(); + } + } + } + + public static void ProcessDeltaQueue() + { + int count = m_DeltaQueue.Count; + + for ( int i = 0; i < m_DeltaQueue.Count; ++i ) + { + m_DeltaQueue[i].ProcessDelta(); + + if ( i >= count ) + break; + } + + if ( m_DeltaQueue.Count > 0 ) + m_DeltaQueue.Clear(); + } + + public virtual void OnDelete() + { + if ( m_Spawner != null ) + { + m_Spawner.Remove( this ); + m_Spawner = null; + } + } + + public virtual void OnParentDeleted( object parent ) + { + this.Delete(); + } + + public virtual void FreeCache() + { + Packet.Release( ref m_RemovePacket ); + Packet.Release( ref m_WorldPacket ); + Packet.Release( ref m_OPLPacket ); + Packet.Release( ref m_PropertyList ); + } + + public virtual void Delete() + { + if ( Deleted ) + return; + else if ( !World.OnDelete( this ) ) + return; + + OnDelete(); + + List items = LookupItems(); + + if ( items != null ) + { + for ( int i = items.Count - 1; i >= 0; --i ) + { + if ( i < items.Count ) + ( (Item) items[i] ).OnParentDeleted( this ); + } + } + + SendRemovePacket(); + + SetFlag( ImplFlag.Deleted, true ); + + if ( Parent is Mobile ) + ((Mobile)Parent).RemoveItem( this ); + else if ( Parent is Item ) + ((Item)Parent).RemoveItem( this ); + + ClearBounce(); + + if ( m_Map != null ) + { + if ( m_Parent == null ) + m_Map.OnLeave( this ); + m_Map = null; + } + + World.RemoveItem( this ); + + OnAfterDelete(); + + FreeCache(); + } + + public void PublicOverheadMessage( MessageType type, int hue, bool ascii, string text ) + { + if ( m_Map != null ) + { + Packet p = null; + Point3D worldLoc = GetWorldLocation(); + + IPooledEnumerable eable = m_Map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( worldLoc, GetUpdateRange( m ) ) ) + { + if ( p == null ) + { + if ( ascii ) + p = new AsciiMessage( m_Serial, m_ItemID, type, hue, 3, this.Name, text ); + else + p = new UnicodeMessage( m_Serial, m_ItemID, type, hue, 3, "ENU", this.Name, text ); + + p.Acquire(); + } + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public void PublicOverheadMessage( MessageType type, int hue, int number ) + { + PublicOverheadMessage( type, hue, number, "" ); + } + + public void PublicOverheadMessage( MessageType type, int hue, int number, string args ) + { + if ( m_Map != null ) + { + Packet p = null; + Point3D worldLoc = GetWorldLocation(); + + IPooledEnumerable eable = m_Map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( worldLoc, GetUpdateRange( m ) ) ) + { + if ( p == null ) + p = Packet.Acquire( new MessageLocalized( m_Serial, m_ItemID, type, hue, 3, number, this.Name, args ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public virtual void OnAfterDelete() + { + } + + public virtual void RemoveItem( Item item ) + { + List items = LookupItems(); + + if ( items != null && items.Contains( item ) ) + { + item.SendRemovePacket(); + + items.Remove( item ); + + if ( !item.IsVirtualItem ) + { + UpdateTotal( item, TotalType.Gold, -item.TotalGold ); + UpdateTotal( item, TotalType.Items, -( item.TotalItems + 1 ) ); + UpdateTotal( item, TotalType.Weight, -( item.TotalWeight + item.PileWeight ) ); + } + + item.Parent = null; + + item.OnRemoved( this ); + OnItemRemoved( item ); + } + } + + public virtual void OnAfterDuped( Item newItem ) + { + } + + public virtual bool OnDragLift( Mobile from ) + { + return true; + } + + public virtual bool OnEquip( Mobile from ) + { + return true; + } + + private ISpawner m_Spawner; + + public ISpawner Spawner{ get{ return m_Spawner; } set{ m_Spawner = value; } } + + public virtual void OnBeforeSpawn( Point3D location, Map m ) + { + } + + public virtual void OnAfterSpawn() + { + } + + public virtual int PhysicalResistance{ get{ return 0; } } + public virtual int FireResistance{ get{ return 0; } } + public virtual int ColdResistance{ get{ return 0; } } + public virtual int PoisonResistance{ get{ return 0; } } + public virtual int EnergyResistance{ get{ return 0; } } + + [CommandProperty( AccessLevel.Counselor )] + public Serial Serial + { + get + { + return m_Serial; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public IEntity ParentEntity + { + get + { + IEntity p = Parent as IEntity; + + return p; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public IEntity RootParentEntity + { + get + { + IEntity p = RootParent as IEntity; + + return p; + } + } + + public virtual void OnLocationChange( Point3D oldLocation ) + { + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public virtual Point3D Location + { + get + { + return m_Location; + } + set + { + Point3D oldLocation = m_Location; + + if ( oldLocation != value ) + { + if ( m_Map != null ) + { + if ( m_Parent == null ) + { + IPooledEnumerable eable; + + if ( m_Location.m_X != 0 ) + { + Packet removeThis = null; + + eable = m_Map.GetClientsInRange( oldLocation, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( !m.InRange( value, GetUpdateRange( m ) ) ) + { + if ( removeThis == null ) + removeThis = this.RemovePacket; + + state.Send( removeThis ); + } + } + + eable.Free(); + } + + m_Location = value; + Packet.Release( ref m_WorldPacket ); + + SetLastMoved(); + + eable = m_Map.GetClientsInRange( m_Location, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.CanSee( this ) && m.InRange( m_Location, GetUpdateRange( m ) ) ) + SendInfoTo( state ); + } + + eable.Free(); + + RemDelta( ItemDelta.Update ); + } + else if ( m_Parent is Item ) + { + m_Location = value; + Packet.Release( ref m_WorldPacket ); + + Delta( ItemDelta.Update ); + } + else + { + m_Location = value; + Packet.Release( ref m_WorldPacket ); + } + + if ( m_Parent == null ) + m_Map.OnMove( oldLocation, this ); + } + else + { + m_Location = value; + Packet.Release( ref m_WorldPacket ); + } + + this.OnLocationChange( oldLocation ); + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int X + { + get{ return m_Location.m_X; } + set{ Location = new Point3D( value, m_Location.m_Y, m_Location.m_Z ); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Y + { + get{ return m_Location.m_Y; } + set{ Location = new Point3D( m_Location.m_X, value, m_Location.m_Z ); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Z + { + get{ return m_Location.m_Z; } + set{ Location = new Point3D( m_Location.m_X, m_Location.m_Y, value ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual int ItemID + { + get + { + return m_ItemID; + } + set + { + if ( m_ItemID != value ) + { + int oldPileWeight = this.PileWeight; + + m_ItemID = value; + Packet.Release( ref m_WorldPacket ); + + int newPileWeight = this.PileWeight; + + UpdateTotal( this, TotalType.Weight, newPileWeight - oldPileWeight ); + + InvalidateProperties(); + Delta( ItemDelta.Update ); + } + } + } + + public virtual string DefaultName + { + get { return null; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Name + { + get + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null && info.m_Name != null ) + return info.m_Name; + + return this.DefaultName; + } + set + { + if ( value == null || value != DefaultName ) + { + CompactInfo info = AcquireCompactInfo(); + + info.m_Name = value; + + if ( info.m_Name == null ) + VerifyCompactInfo(); + + InvalidateProperties(); + } + } + } + + public virtual object Parent + { + get + { + return m_Parent; + } + set + { + if ( m_Parent == value ) + return; + + object oldParent = m_Parent; + + m_Parent = value; + + if ( m_Map != null ) + { + if ( oldParent != null && m_Parent == null ) + m_Map.OnEnter( this ); + else if ( m_Parent != null ) + m_Map.OnLeave( this ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public LightType Light + { + get + { + return (LightType)m_Direction; + } + set + { + if ( (LightType)m_Direction != value ) + { + m_Direction = (Direction)value; + Packet.Release( ref m_WorldPacket ); + + Delta( ItemDelta.Update ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Direction Direction + { + get + { + return m_Direction; + } + set + { + if ( m_Direction != value ) + { + m_Direction = value; + Packet.Release( ref m_WorldPacket ); + + Delta( ItemDelta.Update ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Amount + { + get + { + return m_Amount; + } + set + { + int oldValue = m_Amount; + + if ( oldValue != value ) + { + int oldPileWeight = this.PileWeight; + + m_Amount = value; + Packet.Release( ref m_WorldPacket ); + + int newPileWeight = this.PileWeight; + + UpdateTotal( this, TotalType.Weight, newPileWeight - oldPileWeight ); + + OnAmountChange( oldValue ); + + Delta( ItemDelta.Update ); + + if ( oldValue > 1 || value > 1 ) + InvalidateProperties(); + + if ( !Stackable && m_Amount > 1 ) + Console.WriteLine( "Warning: 0x{0:X}: Amount changed for non-stackable item '{2}'. ({1})", m_Serial.Value, m_Amount, GetType().Name ); + } + } + } + + protected virtual void OnAmountChange( int oldValue ) + { + } + + public virtual bool HandlesOnSpeech{ get{ return false; } } + + public virtual void OnSpeech( SpeechEventArgs e ) + { + } + + public virtual bool OnDroppedToMobile( Mobile from, Mobile target ) + { + return true; + } + + public virtual bool DropToMobile( Mobile from, Mobile target, Point3D p ) + { + if ( Deleted || from.Deleted || target.Deleted || from.Map != target.Map || from.Map == null || target.Map == null ) + return false; + else if ( from.AccessLevel < AccessLevel.GameMaster && !from.InRange( target.Location, 2 ) ) + return false; + else if ( !from.CanSee( target ) || !from.InLOS( target ) ) + return false; + else if ( !from.OnDroppedItemToMobile( this, target ) ) + return false; + else if ( !OnDroppedToMobile( from, target ) ) + return false; + else if ( !target.OnDragDrop( from, this ) ) + return false; + else + return true; + } + + public virtual bool OnDroppedInto( Mobile from, Container target, Point3D p ) + { + if ( !from.OnDroppedItemInto( this, target, p ) ) + return false; + + return target.OnDragDropInto( from, this, p ); + } + + public virtual bool OnDroppedOnto( Mobile from, Item target ) + { + if ( Deleted || from.Deleted || target.Deleted || from.Map != target.Map || from.Map == null || target.Map == null ) + return false; + else if ( from.AccessLevel < AccessLevel.GameMaster && !from.InRange( target.GetWorldLocation(), 2 ) ) + return false; + else if ( !from.CanSee( target ) || !from.InLOS( target ) ) + return false; + else if ( !target.IsAccessibleTo( from ) ) + return false; + else if ( !from.OnDroppedItemOnto( this, target ) ) + return false; + else + return target.OnDragDrop( from, this ); + } + + public virtual bool DropToItem( Mobile from, Item target, Point3D p ) + { + if ( Deleted || from.Deleted || target.Deleted || from.Map != target.Map || from.Map == null || target.Map == null ) + return false; + + object root = target.RootParent; + + if ( from.AccessLevel < AccessLevel.GameMaster && !from.InRange( target.GetWorldLocation(), 2 ) ) + return false; + else if ( !from.CanSee( target ) || !from.InLOS( target ) ) + return false; + else if ( !target.IsAccessibleTo( from ) ) + return false; + else if ( root is Mobile && !((Mobile)root).CheckNonlocalDrop( from, this, target ) ) + return false; + else if ( !from.OnDroppedItemToItem( this, target, p ) ) + return false; + else if ( target is Container && p.m_X != -1 && p.m_Y != -1 ) + return OnDroppedInto( from, (Container)target, p ); + else + return OnDroppedOnto( from, target ); + } + + public virtual bool OnDroppedToWorld( Mobile from, Point3D p ) + { + return true; + } + + public virtual int GetLiftSound( Mobile from ) + { + return 0x57; + } + + private static int m_OpenSlots; + + public virtual bool DropToWorld( Mobile from, Point3D p ) + { + if ( Deleted || from.Deleted || from.Map == null ) + return false; + else if ( !from.InRange( p, 2 ) ) + return false; + + Map map = from.Map; + + if ( map == null ) + return false; + + int x = p.m_X, y = p.m_Y; + int z = int.MinValue; + + int maxZ = from.Z + 16; + + Tile landTile = map.Tiles.GetLandTile( x, y ); + TileFlag landFlags = TileData.LandTable[landTile.ID & 0x3FFF].Flags; + + int landZ = 0, landAvg = 0, landTop = 0; + map.GetAverageZ( x, y, ref landZ, ref landAvg, ref landTop ); + + if ( !landTile.Ignored && (landFlags & TileFlag.Impassable) == 0 ) + { + if ( landAvg <= maxZ ) + z = landAvg; + } + + Tile[] tiles = map.Tiles.GetStaticTiles( x, y, true ); + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + if ( !id.Surface ) + continue; + + int top = tile.Z + id.CalcHeight; + + if ( top > maxZ || top < z ) + continue; + + z = top; + } + + List items = new List(); + + IPooledEnumerable eable = map.GetItemsInRange( p, 0 ); + + foreach ( Item item in eable ) + { + if ( item.ItemID >= 0x4000 ) + continue; + + items.Add( item ); + + ItemData id = item.ItemData; + + if ( !id.Surface ) + continue; + + int top = item.Z + id.CalcHeight; + + if ( top > maxZ || top < z ) + continue; + + z = top; + } + + eable.Free(); + + if ( z == int.MinValue ) + return false; + + if ( z > maxZ ) + return false; + + m_OpenSlots = (1<<20)-1; + + int surfaceZ = z; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + int checkZ = tile.Z; + int checkTop = checkZ + id.CalcHeight; + + if ( checkTop == checkZ && !id.Surface ) + ++checkTop; + + int zStart = checkZ - z; + int zEnd = checkTop - z; + + if ( zStart >= 20 || zEnd < 0 ) + continue; + + if ( zStart < 0 ) + zStart = 0; + + if ( zEnd > 19 ) + zEnd = 19; + + int bitCount = zEnd-zStart; + + m_OpenSlots &= ~(((1<= 20 || zEnd < 0 ) + continue; + + if ( zStart < 0 ) + zStart = 0; + + if ( zEnd > 19 ) + zEnd = 19; + + int bitCount = zEnd-zStart; + + m_OpenSlots &= ~(((1< 30 ) + height = 30; + + int match = (1< 20 ) + match >>= 1; + + okay = ((m_OpenSlots>>i)&match) == match; + + if ( okay ) + { + z += i; + break; + } + } + + if ( !okay ) + return false; + + height = ItemData.Height; + + if ( height == 0 ) + ++height; + + if ( landAvg > z && (z + height) > landZ ) + return false; + else if ( (landFlags & TileFlag.Impassable) != 0 && landAvg > surfaceZ && (z + height) > landZ ) + return false; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + int checkZ = tile.Z; + int checkTop = checkZ + id.CalcHeight; + + if ( checkTop > z && (z + height) > checkZ ) + return false; + else if ( (id.Surface || id.Impassable) && checkTop > surfaceZ && (z + height) > checkZ ) + return false; + } + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + ItemData id = item.ItemData; + + //int checkZ = item.Z; + //int checkTop = checkZ + id.CalcHeight; + + if ( (item.Z + id.CalcHeight) > z && (z + height) > item.Z ) + return false; + } + + p = new Point3D( x, y, z ); + + if ( !from.InLOS( new Point3D( x, y, z + 1 ) ) ) + return false; + else if ( !from.OnDroppedItemToWorld( this, p ) ) + return false; + else if ( !OnDroppedToWorld( from, p ) ) + return false; + + int soundID = GetDropSound(); + + MoveToWorld( p, from.Map ); + + from.SendSound( soundID == -1 ? 0x42 : soundID, GetWorldLocation() ); + + return true; + } + + public void SendRemovePacket() + { + if ( !Deleted && m_Map != null ) + { + Packet p = null; + Point3D worldLoc = GetWorldLocation(); + + IPooledEnumerable eable = m_Map.GetClientsInRange( worldLoc, GetMaxUpdateRange() ); + + foreach ( NetState state in eable ) + { + Mobile m = state.Mobile; + + if ( m.InRange( worldLoc, GetUpdateRange( m ) ) ) + { + if ( p == null ) + p = this.RemovePacket; + + state.Send( p ); + } + } + + eable.Free(); + } + } + + public virtual int GetDropSound() + { + return -1; + } + + public Point3D GetWorldLocation() + { + object root = RootParent; + + if ( root == null ) + return m_Location; + else + return ((IEntity)root).Location; + + //return root == null ? m_Location : new Point3D( (IPoint3D) root ); + } + + public virtual bool BlocksFit{ get{ return false; } } + + public Point3D GetSurfaceTop() + { + object root = RootParent; + + if ( root == null ) + return new Point3D( m_Location.m_X, m_Location.m_Y, m_Location.m_Z + (ItemData.Surface ? ItemData.CalcHeight : 0) ); + else + return ((IEntity)root).Location; + } + + public Point3D GetWorldTop() + { + object root = RootParent; + + if ( root == null ) + return new Point3D( m_Location.m_X, m_Location.m_Y, m_Location.m_Z + ItemData.CalcHeight ); + else + return ((IEntity)root).Location; + } + + public void SendLocalizedMessageTo( Mobile to, int number ) + { + if ( Deleted || !to.CanSee( this ) ) + return; + + to.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", "" ) ); + } + + public void SendLocalizedMessageTo( Mobile to, int number, string args ) + { + if ( Deleted || !to.CanSee( this ) ) + return; + + to.Send( new MessageLocalized( Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", args ) ); + } + + public void SendLocalizedMessageTo( Mobile to, int number, AffixType affixType, string affix, string args ) + { + if ( Deleted || !to.CanSee( this ) ) + return; + + to.Send( new MessageLocalizedAffix( Serial, ItemID, MessageType.Regular, 0x3B2, 3, number, "", affixType, affix, args ) ); + } + + public virtual void OnDoubleClick( Mobile from ) + { + } + + public virtual void OnDoubleClickOutOfRange( Mobile from ) + { + } + + public virtual void OnDoubleClickCantSee( Mobile from ) + { + } + + public virtual void OnDoubleClickDead( Mobile from ) + { + from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019048 ); // I am dead and cannot do that. + } + + public virtual void OnDoubleClickNotAccessible( Mobile from ) + { + from.SendLocalizedMessage( 500447 ); // That is not accessible. + } + + public virtual void OnDoubleClickSecureTrade( Mobile from ) + { + from.SendLocalizedMessage( 500447 ); // That is not accessible. + } + + public virtual void OnSnoop( Mobile from ) + { + } + + public bool InSecureTrade + { + get + { + return ( GetSecureTradeCont() != null ); + } + } + + public SecureTradeContainer GetSecureTradeCont() + { + object p = this; + + while ( p is Item ) + { + if ( p is SecureTradeContainer ) + return (SecureTradeContainer)p; + + p = ((Item)p).m_Parent; + } + + return null; + } + + public virtual void OnItemAdded( Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSubItemAdded( item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnSubItemAdded( item ); + } + + public virtual void OnItemRemoved( Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSubItemRemoved( item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnSubItemRemoved( item ); + } + + public virtual void OnSubItemAdded( Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSubItemAdded( item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnSubItemAdded( item ); + } + + public virtual void OnSubItemRemoved( Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSubItemRemoved( item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnSubItemRemoved( item ); + } + + public virtual void OnItemBounceCleared( Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSubItemBounceCleared( item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnSubItemBounceCleared( item ); + } + + public virtual void OnSubItemBounceCleared( Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSubItemBounceCleared( item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnSubItemBounceCleared( item ); + } + + public virtual bool CheckTarget( Mobile from, Server.Targeting.Target targ, object targeted ) + { + if ( m_Parent is Item ) + return ((Item)m_Parent).CheckTarget( from, targ, targeted ); + else if ( m_Parent is Mobile ) + return ((Mobile)m_Parent).CheckTarget( from, targ, targeted ); + + return true; + } + + public virtual bool IsAccessibleTo( Mobile check ) + { + if ( m_Parent is Item ) + return ((Item)m_Parent).IsAccessibleTo( check ); + + Region reg = Region.Find( GetWorldLocation(), m_Map ); + + return reg.CheckAccessibility( this, check ); + + /*SecureTradeContainer cont = GetSecureTradeCont(); + + if ( cont != null && !cont.IsChildOf( check ) ) + return false; + + return true;*/ + } + + public bool IsChildOf( object o ) + { + return IsChildOf( o, false ); + } + + public bool IsChildOf( object o, bool allowNull ) + { + object p = m_Parent; + + if ( (p == null || o == null) && !allowNull ) + return false; + + if ( p == o ) + return true; + + while ( p is Item ) + { + Item item = (Item)p; + + if ( item.m_Parent == null ) + { + break; + } + else + { + p = item.m_Parent; + + if ( p == o ) + return true; + } + } + + return false; + } + + public ItemData ItemData + { + get + { + return TileData.ItemTable[m_ItemID & 0x3FFF]; + } + } + + public virtual void OnItemUsed( Mobile from, Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnItemUsed( from, item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnItemUsed( from, item ); + } + + public virtual bool CheckItemUse( Mobile from, Item item ) + { + if ( m_Parent is Item ) + return ((Item)m_Parent).CheckItemUse( from, item ); + else if ( m_Parent is Mobile ) + return ((Mobile)m_Parent).CheckItemUse( from, item ); + else + return true; + } + + public virtual void OnItemLifted( Mobile from, Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnItemLifted( from, item ); + else if ( m_Parent is Mobile ) + ((Mobile)m_Parent).OnItemLifted( from, item ); + } + + public virtual bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( m_Parent is Item ) + return ((Item)m_Parent).CheckLift( from, item, ref reject ); + else if ( m_Parent is Mobile ) + return ((Mobile)m_Parent).CheckLift( from, item, ref reject ); + else + return true; + } + + public virtual bool CanTarget{ get{ return true; } } + public virtual bool DisplayLootType{ get{ return true; } } + + public virtual void OnSingleClickContained( Mobile from, Item item ) + { + if ( m_Parent is Item ) + ((Item)m_Parent).OnSingleClickContained( from, item ); + } + + public virtual void OnAosSingleClick( Mobile from ) + { + ObjectPropertyList opl = this.PropertyList; + + if ( opl.Header > 0 ) + from.Send( new MessageLocalized( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, opl.Header, this.Name, opl.HeaderArgs ) ); + } + + public virtual void OnSingleClick( Mobile from ) + { + if ( Deleted || !from.CanSee( this ) ) + return; + + if ( DisplayLootType ) + LabelLootTypeTo( from ); + + NetState ns = from.NetState; + + if ( ns != null ) + { + if ( this.Name == null ) + { + if ( m_Amount <= 1 ) + ns.Send( new MessageLocalized( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, LabelNumber, "", "" ) ); + else + ns.Send( new MessageLocalizedAffix( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, LabelNumber, "", AffixType.Append, String.Format( " : {0}", m_Amount ), "" ) ); + } + else + { + ns.Send( new UnicodeMessage( m_Serial, m_ItemID, MessageType.Label, 0x3B2, 3, "ENU", "", this.Name + ( m_Amount > 1 ? " : " + m_Amount : "" ) ) ); + } + } + } + + private static bool m_ScissorCopyLootType; + + public static bool ScissorCopyLootType + { + get{ return m_ScissorCopyLootType; } + set{ m_ScissorCopyLootType = value; } + } + + public virtual void ScissorHelper( Mobile from, Item newItem, int amountPerOldItem ) + { + ScissorHelper( from, newItem, amountPerOldItem, true ); + } + + public virtual void ScissorHelper( Mobile from, Item newItem, int amountPerOldItem, bool carryHue ) + { + int amount = Amount; + + if ( amount > (60000 / amountPerOldItem) ) // let's not go over 60000 + amount = (60000 / amountPerOldItem); + + Amount -= amount; + + int ourHue = Hue; + Map thisMap = this.Map; + object thisParent = this.m_Parent; + Point3D worldLoc = this.GetWorldLocation(); + LootType type = this.LootType; + + if ( Amount == 0 ) + Delete(); + + newItem.Amount = amount * amountPerOldItem; + + if ( carryHue ) + newItem.Hue = ourHue; + + if ( m_ScissorCopyLootType ) + newItem.LootType = type; + + if ( !(thisParent is Container) || !((Container)thisParent).TryDropItem( from, newItem, false ) ) + newItem.MoveToWorld( worldLoc, thisMap ); + } + + public virtual void Consume() + { + Consume( 1 ); + } + + public virtual void Consume( int amount ) + { + this.Amount -= amount; + + if ( this.Amount <= 0 ) + this.Delete(); + } + + public bool Insured + { + get{ return GetFlag( ImplFlag.Insured ); } + set{ SetFlag( ImplFlag.Insured, value ); InvalidateProperties(); } + } + + public bool PayedInsurance + { + get{ return GetFlag( ImplFlag.PayedInsurance ); } + set{ SetFlag( ImplFlag.PayedInsurance, value ); } + } + + public Mobile BlessedFor + { + get + { + CompactInfo info = LookupCompactInfo(); + + if ( info != null ) + return info.m_BlessedFor; + + return null; + } + set + { + CompactInfo info = AcquireCompactInfo(); + + info.m_BlessedFor = value; + + if ( info.m_BlessedFor == null ) + VerifyCompactInfo(); + + InvalidateProperties(); + } + } + + public virtual bool CheckBlessed( object obj ) + { + return CheckBlessed( obj as Mobile ); + } + + public virtual bool CheckBlessed( Mobile m ) + { + if ( m_LootType == LootType.Blessed || (Mobile.InsuranceEnabled && Insured) ) + return true; + + return ( m != null && m == this.BlessedFor ); + } + + public virtual bool CheckNewbied() + { + return ( m_LootType == LootType.Newbied ); + } + + public virtual bool IsStandardLoot() + { + if ( Mobile.InsuranceEnabled && Insured ) + return false; + + if ( this.BlessedFor != null ) + return false; + + return ( m_LootType == LootType.Regular ); + } + + public override string ToString() + { + return String.Format( "0x{0:X} \"{1}\"", m_Serial.Value, GetType().Name ); + } + + internal int m_TypeRef; + + public Item() + { + m_Serial = Serial.NewItem; + + //m_Items = new ArrayList( 1 ); + Visible = true; + Movable = true; + Amount = 1; + m_Map = Map.Internal; + + SetLastMoved(); + + World.AddItem( this ); + + Type ourType = this.GetType(); + m_TypeRef = World.m_ItemTypes.IndexOf( ourType ); + + if ( m_TypeRef == -1 ) + { + World.m_ItemTypes.Add( ourType ); + m_TypeRef = World.m_ItemTypes.Count - 1; + } + } + + [Constructable] + public Item( int itemID ) : this() + { + m_ItemID = itemID; + } + + public Item( Serial serial ) + { + m_Serial = serial; + + Type ourType = this.GetType(); + m_TypeRef = World.m_ItemTypes.IndexOf( ourType ); + + if ( m_TypeRef == -1 ) + { + World.m_ItemTypes.Add( ourType ); + m_TypeRef = World.m_ItemTypes.Count - 1; + } + } + + public virtual void OnSectorActivate() + { + } + + public virtual void OnSectorDeactivate() + { + } + } + /* public class EmptyArrayList : ArrayList + { + public override bool IsReadOnly{ get{ return true; } } + public override bool IsFixedSize{ get{ return true; } } + + private void OnPopulate() + { + Console.WriteLine( "Warning: Attempted to populate a static empty ArrayList" ); + Console.WriteLine( new System.Diagnostics.StackTrace() ); + } + + public override int Add( object value ) + { + OnPopulate(); + return -1; + } + + public override void AddRange( ICollection c ) + { + OnPopulate(); + } + + public override void InsertRange( int index, ICollection c ) + { + OnPopulate(); + } + + public override void Insert( int index, object value ) + { + OnPopulate(); + } + + public override void SetRange( int index, ICollection c ) + { + OnPopulate(); + } + }*/ +} \ No newline at end of file diff --git a/Server/ItemBounds.cs b/Server/ItemBounds.cs new file mode 100644 index 000000000..3ccf1ff7b --- /dev/null +++ b/Server/ItemBounds.cs @@ -0,0 +1,69 @@ +/*************************************************************************** + * ItemBounds.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ItemBounds.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; + +namespace Server +{ + public class ItemBounds + { + private static Rectangle2D[] m_Bounds; + + public static Rectangle2D[] Table + { + get + { + return m_Bounds; + } + } + + static ItemBounds() + { + if ( File.Exists( "Data/Binary/Bounds.bin" ) ) + { + using ( FileStream fs = new FileStream( "Data/Binary/Bounds.bin", FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + BinaryReader bin = new BinaryReader( fs ); + + m_Bounds = new Rectangle2D[0x4000]; + + for ( int i = 0; i < 0x4000; ++i ) + { + int xMin = bin.ReadInt16(); + int yMin = bin.ReadInt16(); + int xMax = bin.ReadInt16(); + int yMax = bin.ReadInt16(); + + m_Bounds[i].Set( xMin, yMin, (xMax - xMin) + 1, (yMax - yMin) + 1 ); + } + + bin.Close(); + } + } + else + { + Console.WriteLine( "Warning: Data/Binary/Bounds.bin does not exist" ); + + m_Bounds = new Rectangle2D[0x4000]; + } + } + } +} \ No newline at end of file diff --git a/Server/Items/BaseMulti.cs b/Server/Items/BaseMulti.cs new file mode 100644 index 000000000..f86e91fe7 --- /dev/null +++ b/Server/Items/BaseMulti.cs @@ -0,0 +1,141 @@ +/*************************************************************************** + * BaseMulti.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: BaseMulti.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Items +{ + public class BaseMulti : Item + { + [Constructable] + public BaseMulti( int itemID ) : base( itemID ) + { + Movable = false; + } + + public BaseMulti( Serial serial ) : base( serial ) + { + } + + public virtual void RefreshComponents() + { + if ( Parent != null ) + return; + + Map map = Map; + + if ( map != null ) + { + map.OnLeave( this ); + map.OnEnter( this ); + } + } + + public override int LabelNumber + { + get + { + MultiComponentList mcl = this.Components; + + if ( mcl.List.Length > 0 ) + return 1020000 + (mcl.List[0].m_ItemID & 0x3FFF); + + return base.LabelNumber; + } + } + + public override int GetMaxUpdateRange() + { + return 22; + } + + public override int GetUpdateRange( Mobile m ) + { + return 22; + } + + public virtual MultiComponentList Components + { + get + { + return MultiData.GetComponents( ItemID ); + } + } + + public virtual bool Contains( Point2D p ) + { + return Contains( p.m_X, p.m_Y ); + } + + public virtual bool Contains( Point3D p ) + { + return Contains( p.m_X, p.m_Y ); + } + + public virtual bool Contains( IPoint3D p ) + { + return Contains( p.X, p.Y ); + } + + public virtual bool Contains( int x, int y ) + { + MultiComponentList mcl = this.Components; + + x -= this.X + mcl.Min.m_X; + y -= this.Y + mcl.Min.m_Y; + + return x >= 0 + && x < mcl.Width + && y >= 0 + && y < mcl.Height + && mcl.Tiles[x][y].Length > 0; + } + + public bool Contains( Mobile m ) + { + if ( m.Map == this.Map ) + return Contains( m.X, m.Y ); + else + return false; + } + + public bool Contains( Item item ) + { + if ( item.Map == this.Map ) + return Contains( item.X, item.Y ); + else + return false; + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Server/Items/Container.cs b/Server/Items/Container.cs new file mode 100644 index 000000000..427ba94d4 --- /dev/null +++ b/Server/Items/Container.cs @@ -0,0 +1,1692 @@ +/*************************************************************************** + * Container.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Container.cs 142 2006-06-08 17:27:25Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Server.Network; + +namespace Server.Items +{ + public delegate void OnItemConsumed( Item item, int amount ); + public delegate int CheckItemGroup( Item a, Item b ); + + public delegate void ContainerSnoopHandler( Container cont, Mobile from ); + + public class Container : Item + { + private static ContainerSnoopHandler m_SnoopHandler; + + public static ContainerSnoopHandler SnoopHandler + { + get{ return m_SnoopHandler; } + set{ m_SnoopHandler = value; } + } + + private ContainerData m_ContainerData; + + private int m_DropSound; + private int m_GumpID; + private int m_MaxItems; + + private int m_TotalItems; + private int m_TotalWeight; + private int m_TotalGold; + + private bool m_LiftOverride; + + internal List m_Items; + + public ContainerData ContainerData + { + get + { + if ( m_ContainerData == null ) + UpdateContainerData(); + + return m_ContainerData; + } + set{ m_ContainerData = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public override int ItemID + { + get{ return base.ItemID; } + set + { + int oldID = this.ItemID; + + base.ItemID = value; + + if ( this.ItemID != oldID ) + UpdateContainerData(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int GumpID + { + get{ return ( m_GumpID == -1 ? DefaultGumpID : m_GumpID ); } + set{ m_GumpID = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int DropSound + { + get{ return ( m_DropSound == -1 ? DefaultDropSound : m_DropSound ); } + set{ m_DropSound = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MaxItems + { + get{ return ( m_MaxItems == -1 ? DefaultMaxItems : m_MaxItems ); } + set{ m_MaxItems = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual int MaxWeight + { + get + { + int maxWeight; + + if ( Parent is Container ) + { + maxWeight = ((Container)Parent).MaxWeight; + + if ( maxWeight > 0 ) + maxWeight = Math.Max( maxWeight, DefaultMaxWeight ); + } + else + { + maxWeight = DefaultMaxWeight; + } + + return maxWeight; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool LiftOverride + { + get{ return m_LiftOverride; } + set{ m_LiftOverride = value; } + } + + public virtual void UpdateContainerData() + { + this.ContainerData = ContainerData.GetData( this.ItemID ); + } + + public virtual Rectangle2D Bounds{ get{ return ContainerData.Bounds; } } + public virtual int DefaultGumpID{ get{ return ContainerData.GumpID; } } + public virtual int DefaultDropSound{ get{ return ContainerData.DropSound; } } + + public virtual int DefaultMaxItems{ get{ return m_GlobalMaxItems; } } + public virtual int DefaultMaxWeight{ get{ return m_GlobalMaxWeight; } } + + public virtual bool IsDecoContainer + { + get{ return !Movable && !IsLockedDown && !IsSecure && Parent == null && !m_LiftOverride; } + } + + public virtual int GetDroppedSound( Item item ) + { + int dropSound = item.GetDropSound(); + + return dropSound != -1 ? dropSound : DropSound; + } + + public override void OnSnoop( Mobile from ) + { + if ( m_SnoopHandler != null ) + m_SnoopHandler( this, from ); + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + if ( from.AccessLevel < AccessLevel.GameMaster && IsDecoContainer ) + { + reject = LRReason.CannotLift; + return false; + } + + return base.CheckLift( from, item, ref reject ); + } + + public bool CheckHold( Mobile m, Item item, bool message ) + { + return CheckHold( m, item, message, true, 0, 0 ); + } + + public bool CheckHold( Mobile m, Item item, bool message, bool checkItems ) + { + return CheckHold( m, item, message, checkItems, 0, 0 ); + } + + public virtual bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + if ( m.AccessLevel < AccessLevel.GameMaster ) + { + if ( IsDecoContainer ) + { + if ( message ) + SendCantStoreMessage( m, item ); + + return false; + } + + int maxItems = this.MaxItems; + + if ( checkItems && maxItems != 0 && (this.TotalItems + plusItems + item.TotalItems + (item.IsVirtualItem ? 0 : 1)) > maxItems ) + { + if ( message ) + SendFullItemsMessage( m, item ); + + return false; + } + else + { + int maxWeight = this.MaxWeight; + + if ( maxWeight != 0 && (this.TotalWeight + plusWeight + item.TotalWeight + item.PileWeight) > maxWeight ) + { + if ( message ) + SendFullWeightMessage( m, item ); + + return false; + } + } + } + + object parent = this.Parent; + + while ( parent != null ) + { + if ( parent is Container ) + return ((Container)parent).CheckHold( m, item, message, checkItems, plusItems, plusWeight ); + else if ( parent is Item ) + parent = ((Item)parent).Parent; + else + break; + } + + return true; + } + + public virtual void SendFullItemsMessage( Mobile to, Item item ) + { + to.SendMessage( "That container cannot hold more items." ); + } + + public virtual void SendFullWeightMessage( Mobile to, Item item ) + { + to.SendMessage( "That container cannot hold more weight." ); + } + + public virtual void SendCantStoreMessage( Mobile to, Item item ) + { + to.SendLocalizedMessage( 500176 ); // That is not your container, you can't store things here. + } + + public virtual bool OnDragDropInto( Mobile from, Item item, Point3D p ) + { + if ( !CheckHold( from, item, true, true ) ) + return false; + + item.Location = new Point3D( p.m_X, p.m_Y, 0 ); + AddItem( item ); + + from.SendSound( GetDroppedSound( item ), GetWorldLocation() ); + + return true; + } + + private class GroupComparer : IComparer + { + private CheckItemGroup m_Grouper; + + public GroupComparer( CheckItemGroup grouper ) + { + m_Grouper = grouper; + } + + public int Compare( object x, object y ) + { + Item a = (Item)x; + Item b = (Item)y; + + return m_Grouper( a, b ); + } + } + + public bool ConsumeTotalGrouped( Type type, int amount, bool recurse, OnItemConsumed callback, CheckItemGroup grouper ) + { + if ( grouper == null ) + throw new ArgumentNullException(); + + Item[] typedItems = FindItemsByType( type, recurse ); + + List> groups = new List>(); + int idx = 0; + + while ( idx < typedItems.Length ) + { + Item a = typedItems[idx++]; + List group = new List(); + + group.Add( a ); + + while ( idx < typedItems.Length ) + { + Item b = typedItems[idx]; + int v = grouper( a, b ); + + if ( v == 0 ) + group.Add( b ); + else + break; + + ++idx; + } + + groups.Add( group ); + } + + Item[][] items = new Item[groups.Count][]; + int[] totals = new int[groups.Count]; + + bool hasEnough = false; + + for ( int i = 0; i < groups.Count; ++i ) + { + items[i] = groups[i].ToArray(); + //items[i] = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) )); + + for ( int j = 0; j < items[i].Length; ++j ) + totals[i] += items[i][j].Amount; + + if ( totals[i] >= amount ) + hasEnough = true; + } + + if ( !hasEnough ) + return false; + + for ( int i = 0; i < items.Length; ++i ) + { + if ( totals[i] >= amount ) + { + int need = amount; + + for ( int j = 0; j < items[i].Length; ++j ) + { + Item item = items[i][j]; + + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + if ( callback != null ) + callback( item, theirAmount ); + + item.Delete(); + need -= theirAmount; + } + else + { + if ( callback != null ) + callback( item, need ); + + item.Consume( need ); + break; + } + } + + break; + } + } + + return true; + } + + public int ConsumeTotalGrouped( Type[] types, int[] amounts, bool recurse, OnItemConsumed callback, CheckItemGroup grouper ) + { + if ( types.Length != amounts.Length ) + throw new ArgumentException(); + else if ( grouper == null ) + throw new ArgumentNullException(); + + Item[][][] items = new Item[types.Length][][]; + int[][] totals = new int[types.Length][]; + + for ( int i = 0; i < types.Length; ++i ) + { + Item[] typedItems = FindItemsByType( types[i], recurse ); + + List> groups = new List>(); + int idx = 0; + + while ( idx < typedItems.Length ) + { + Item a = typedItems[idx++]; + List group = new List(); + + group.Add( a ); + + while ( idx < typedItems.Length ) + { + Item b = typedItems[idx]; + int v = grouper( a, b ); + + if ( v == 0 ) + group.Add( b ); + else + break; + + ++idx; + } + + groups.Add( group ); + } + + items[i] = new Item[groups.Count][]; + totals[i] = new int[groups.Count]; + + bool hasEnough = false; + + for ( int j = 0; j < groups.Count; ++j ) + { + items[i][j] = groups[j].ToArray(); + //items[i][j] = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) )); + + for ( int k = 0; k < items[i][j].Length; ++k ) + totals[i][j] += items[i][j][k].Amount; + + if ( totals[i][j] >= amounts[i] ) + hasEnough = true; + } + + if ( !hasEnough ) + return i; + } + + for ( int i = 0; i < items.Length; ++i ) + { + for ( int j = 0; j < items[i].Length; ++j ) + { + if ( totals[i][j] >= amounts[i] ) + { + int need = amounts[i]; + + for ( int k = 0; k < items[i][j].Length; ++k ) + { + Item item = items[i][j][k]; + + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + if ( callback != null ) + callback( item, theirAmount ); + + item.Delete(); + need -= theirAmount; + } + else + { + if ( callback != null ) + callback( item, need ); + + item.Consume( need ); + break; + } + } + + break; + } + } + } + + return -1; + } + + public int GetBestGroupAmount( Type type, bool recurse, CheckItemGroup grouper ) + { + if ( grouper == null ) + throw new ArgumentNullException(); + + int best = 0; + + Item[] typedItems = FindItemsByType( type, recurse ); + + List> groups = new List>(); + int idx = 0; + + while ( idx < typedItems.Length ) + { + Item a = typedItems[idx++]; + List group = new List(); + + group.Add( a ); + + while ( idx < typedItems.Length ) + { + Item b = typedItems[idx]; + int v = grouper( a, b ); + + if ( v == 0 ) + group.Add( b ); + else + break; + + ++idx; + } + + groups.Add( group ); + } + + for ( int i = 0; i < groups.Count; ++i ) + { + Item[] items = groups[i].ToArray(); + + //Item[] items = (Item[])(((ArrayList)groups[i]).ToArray( typeof( Item ) )); + int total = 0; + + for ( int j = 0; j < items.Length; ++j ) + total += items[j].Amount; + + if ( total >= best ) + best = total; + } + + return best; + } + + public int GetBestGroupAmount( Type[] types, bool recurse, CheckItemGroup grouper ) + { + if ( grouper == null ) + throw new ArgumentNullException(); + + int best = 0; + + Item[] typedItems = FindItemsByType( types, recurse ); + + List> groups = new List>(); + int idx = 0; + + while ( idx < typedItems.Length ) + { + Item a = typedItems[idx++]; + List group = new List(); + + group.Add( a ); + + while ( idx < typedItems.Length ) + { + Item b = typedItems[idx]; + int v = grouper( a, b ); + + if ( v == 0 ) + group.Add( b ); + else + break; + + ++idx; + } + + groups.Add( group ); + } + + for ( int j = 0; j < groups.Count; ++j ) + { + Item[] items = groups[j].ToArray(); + //Item[] items = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) )); + int total = 0; + + for ( int k = 0; k < items.Length; ++k ) + total += items[k].Amount; + + if ( total >= best ) + best = total; + } + + return best; + } + + public int GetBestGroupAmount( Type[][] types, bool recurse, CheckItemGroup grouper ) + { + if ( grouper == null ) + throw new ArgumentNullException(); + + int best = 0; + + for ( int i = 0; i < types.Length; ++i ) + { + Item[] typedItems = FindItemsByType( types[i], recurse ); + + List> groups = new List>(); + int idx = 0; + + while ( idx < typedItems.Length ) + { + Item a = typedItems[idx++]; + List group = new List(); + + group.Add( a ); + + while ( idx < typedItems.Length ) + { + Item b = typedItems[idx]; + int v = grouper( a, b ); + + if ( v == 0 ) + group.Add( b ); + else + break; + + ++idx; + } + + groups.Add( group ); + } + + for ( int j = 0; j < groups.Count; ++j ) + { + Item[] items = groups[j].ToArray(); + //Item[] items = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) )); + int total = 0; + + for ( int k = 0; k < items.Length; ++k ) + total += items[k].Amount; + + if ( total >= best ) + best = total; + } + } + + return best; + } + + public int ConsumeTotalGrouped( Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback, CheckItemGroup grouper ) + { + if ( types.Length != amounts.Length ) + throw new ArgumentException(); + else if ( grouper == null ) + throw new ArgumentNullException(); + + Item[][][] items = new Item[types.Length][][]; + int[][] totals = new int[types.Length][]; + + for ( int i = 0; i < types.Length; ++i ) + { + Item[] typedItems = FindItemsByType( types[i], recurse ); + + ArrayList groups = new ArrayList(); + int idx = 0; + + while ( idx < typedItems.Length ) + { + Item a = typedItems[idx++]; + ArrayList group = new ArrayList(); + + group.Add( a ); + + while ( idx < typedItems.Length ) + { + Item b = typedItems[idx]; + int v = grouper( a, b ); + + if ( v == 0 ) + group.Add( b ); + else + break; + + ++idx; + } + + groups.Add( group ); + } + + items[i] = new Item[groups.Count][]; + totals[i] = new int[groups.Count]; + + bool hasEnough = false; + + for ( int j = 0; j < groups.Count; ++j ) + { + items[i][j] = (Item[])(((ArrayList)groups[j]).ToArray( typeof( Item ) )); + + for ( int k = 0; k < items[i][j].Length; ++k ) + totals[i][j] += items[i][j][k].Amount; + + if ( totals[i][j] >= amounts[i] ) + hasEnough = true; + } + + if ( !hasEnough ) + return i; + } + + for ( int i = 0; i < items.Length; ++i ) + { + for ( int j = 0; j < items[i].Length; ++j ) + { + if ( totals[i][j] >= amounts[i] ) + { + int need = amounts[i]; + + for ( int k = 0; k < items[i][j].Length; ++k ) + { + Item item = items[i][j][k]; + + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + if ( callback != null ) + callback( item, theirAmount ); + + item.Delete(); + need -= theirAmount; + } + else + { + if ( callback != null ) + callback( item, need ); + + item.Consume( need ); + break; + } + } + + break; + } + } + } + + return -1; + } + + public int ConsumeTotal( Type[][] types, int[] amounts ) + { + return ConsumeTotal( types, amounts, true, null ); + } + + public int ConsumeTotal( Type[][] types, int[] amounts, bool recurse ) + { + return ConsumeTotal( types, amounts, recurse, null ); + } + + public int ConsumeTotal( Type[][] types, int[] amounts, bool recurse, OnItemConsumed callback ) + { + if ( types.Length != amounts.Length ) + throw new ArgumentException(); + + Item[][] items = new Item[types.Length][]; + int[] totals = new int[types.Length]; + + for ( int i = 0; i < types.Length; ++i ) + { + items[i] = FindItemsByType( types[i], recurse ); + + for ( int j = 0; j < items[i].Length; ++j ) + totals[i] += items[i][j].Amount; + + if ( totals[i] < amounts[i] ) + return i; + } + + for ( int i = 0; i < types.Length; ++i ) + { + int need = amounts[i]; + + for ( int j = 0; j < items[i].Length; ++j ) + { + Item item = items[i][j]; + + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + if ( callback != null ) + callback( item, theirAmount ); + + item.Delete(); + need -= theirAmount; + } + else + { + if ( callback != null ) + callback( item, need ); + + item.Consume( need ); + break; + } + } + } + + return -1; + } + + + + public int ConsumeTotal( Type[] types, int[] amounts ) + { + return ConsumeTotal( types, amounts, true, null ); + } + + public int ConsumeTotal( Type[] types, int[] amounts, bool recurse ) + { + return ConsumeTotal( types, amounts, recurse, null ); + } + + public int ConsumeTotal( Type[] types, int[] amounts, bool recurse, OnItemConsumed callback ) + { + if ( types.Length != amounts.Length ) + throw new ArgumentException(); + + Item[][] items = new Item[types.Length][]; + int[] totals = new int[types.Length]; + + for ( int i = 0; i < types.Length; ++i ) + { + items[i] = FindItemsByType( types[i], recurse ); + + for ( int j = 0; j < items[i].Length; ++j ) + totals[i] += items[i][j].Amount; + + if ( totals[i] < amounts[i] ) + return i; + } + + for ( int i = 0; i < types.Length; ++i ) + { + int need = amounts[i]; + + for ( int j = 0; j < items[i].Length; ++j ) + { + Item item = items[i][j]; + + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + if ( callback != null ) + callback( item, theirAmount ); + + item.Delete(); + need -= theirAmount; + } + else + { + if ( callback != null ) + callback( item, need ); + + item.Consume( need ); + break; + } + } + } + + return -1; + } + + public int GetAmount( Type type ) + { + return GetAmount( type, true ); + } + + public int GetAmount( Type type, bool recurse ) + { + Item[] items = FindItemsByType( type, recurse ); + + int amount = 0; + + for ( int i = 0; i < items.Length; ++i ) + amount += items[i].Amount; + + return amount; + } + + public int GetAmount( Type[] types ) + { + return GetAmount( types, true ); + } + + public int GetAmount( Type[] types, bool recurse ) + { + Item[] items = FindItemsByType( types, recurse ); + + int amount = 0; + + for ( int i = 0; i < items.Length; ++i ) + amount += items[i].Amount; + + return amount; + } + + public bool ConsumeTotal( Type type, int amount ) + { + return ConsumeTotal( type, amount, true, null ); + } + + public bool ConsumeTotal( Type type, int amount, bool recurse ) + { + return ConsumeTotal( type, amount, recurse, null ); + } + + public bool ConsumeTotal( Type type, int amount, bool recurse, OnItemConsumed callback ) + { + Item[] items = FindItemsByType( type, recurse ); + + // First pass, compute total + int total = 0; + + for ( int i = 0; i < items.Length; ++i ) + total += items[i].Amount; + + if ( total >= amount ) + { + // We've enough, so consume it + + int need = amount; + + for ( int i = 0; i < items.Length; ++i ) + { + Item item = items[i]; + + int theirAmount = item.Amount; + + if ( theirAmount < need ) + { + if ( callback != null ) + callback( item, theirAmount ); + + item.Delete(); + need -= theirAmount; + } + else + { + if ( callback != null ) + callback( item, need ); + + item.Consume( need ); + + return true; + } + } + } + + return false; + } + + public int ConsumeUpTo( Type type, int amount ) + { + return ConsumeUpTo( type, amount, true ); + } + + public int ConsumeUpTo( Type type, int amount, bool recurse ) + { + int consumed = 0; + + Queue toDelete = new Queue(); + + RecurseConsumeUpTo( this, type, amount, recurse, ref consumed, toDelete ); + + while ( toDelete.Count > 0 ) + toDelete.Dequeue().Delete(); + + return consumed; + } + + private static void RecurseConsumeUpTo( Item current, Type type, int amount, bool recurse, ref int consumed, Queue toDelete ) + { + if ( current != null && current.Items.Count > 0 ) + { + List list = current.Items; + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = list[i]; + + if ( type.IsAssignableFrom( item.GetType() ) ) + { + int need = amount - consumed; + int theirAmount = item.Amount; + + if ( theirAmount <= need ) + { + toDelete.Enqueue( item ); + consumed += theirAmount; + } + else + { + item.Amount -= need; + consumed += need; + + return; + } + } + else if ( recurse && item is Container ) + { + RecurseConsumeUpTo( item, type, amount, recurse, ref consumed, toDelete ); + } + } + } + } + + private static List m_FindItemsList = new List(); + + public Item[] FindItemsByType( Type type ) + { + return FindItemsByType( type, true ); + } + + public Item[] FindItemsByType( Type type, bool recurse ) + { + if ( m_FindItemsList.Count > 0 ) + m_FindItemsList.Clear(); + + RecurseFindItemsByType( this, type, recurse, m_FindItemsList ); + + return m_FindItemsList.ToArray(); + } + + private static void RecurseFindItemsByType( Item current, Type type, bool recurse, List list ) + { + if ( current != null && current.Items.Count > 0 ) + { + List items = current.Items; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + if ( type.IsAssignableFrom( item.GetType() ) )// item.GetType().IsAssignableFrom( type ) ) + list.Add( item ); + + if ( recurse && item is Container ) + RecurseFindItemsByType( item, type, recurse, list ); + } + } + } + + public Item[] FindItemsByType( Type[] types ) + { + return FindItemsByType( types, true ); + } + + public Item[] FindItemsByType( Type[] types, bool recurse ) + { + if ( m_FindItemsList.Count > 0 ) + m_FindItemsList.Clear(); + + RecurseFindItemsByType( this, types, recurse, m_FindItemsList ); + + return m_FindItemsList.ToArray(); + } + + private static void RecurseFindItemsByType( Item current, Type[] types, bool recurse, List list ) + { + if ( current != null && current.Items.Count > 0 ) + { + List items = current.Items; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + if ( InTypeList( item, types ) ) + list.Add( item ); + + if ( recurse && item is Container ) + RecurseFindItemsByType( item, types, recurse, list ); + } + } + } + + public Item FindItemByType( Type type ) + { + return FindItemByType( type, true ); + } + + public Item FindItemByType( Type type, bool recurse ) + { + return RecurseFindItemByType( this, type, recurse ); + } + + private static Item RecurseFindItemByType( Item current, Type type, bool recurse ) + { + if ( current != null && current.Items.Count > 0 ) + { + List list = current.Items; + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = list[i]; + + if ( type.IsAssignableFrom( item.GetType() ) ) + { + return item; + } + else if ( recurse && item is Container ) + { + Item check = RecurseFindItemByType( item, type, recurse ); + + if ( check != null ) + return check; + } + } + } + + return null; + } + + public Item FindItemByType( Type[] types ) + { + return FindItemByType( types, true ); + } + + public Item FindItemByType( Type[] types, bool recurse ) + { + return RecurseFindItemByType( this, types, recurse ); + } + + private static Item RecurseFindItemByType( Item current, Type[] types, bool recurse ) + { + if ( current != null && current.Items.Count > 0 ) + { + List list = current.Items; + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = list[i]; + + if ( InTypeList( item, types ) ) + { + return item; + } + else if ( recurse && item is Container ) + { + Item check = RecurseFindItemByType( item, types, recurse ); + + if ( check != null ) + return check; + } + } + } + + return null; + } + + private static bool InTypeList( Item item, Type[] types ) + { + Type t = item.GetType(); + + for ( int i = 0; i < types.Length; ++i ) + if ( types[i].IsAssignableFrom( t ) ) + return true; + + return false; + } + + 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 ); + } + + [Flags] + private enum SaveFlag : byte + { + None = 0x00000000, + MaxItems = 0x00000001, + GumpID = 0x00000002, + DropSound = 0x00000004, + LiftOverride = 0x00000008 + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 2 ); // version + + SaveFlag flags = SaveFlag.None; + + SetSaveFlag( ref flags, SaveFlag.MaxItems, m_MaxItems != -1 ); + SetSaveFlag( ref flags, SaveFlag.GumpID, m_GumpID != -1 ); + SetSaveFlag( ref flags, SaveFlag.DropSound, m_DropSound != -1 ); + SetSaveFlag( ref flags, SaveFlag.LiftOverride, m_LiftOverride ); + + writer.Write( (byte) flags ); + + if ( GetSaveFlag( flags, SaveFlag.MaxItems ) ) + writer.WriteEncodedInt( (int) m_MaxItems ); + + if ( GetSaveFlag( flags, SaveFlag.GumpID ) ) + writer.WriteEncodedInt( (int) m_GumpID ); + + if ( GetSaveFlag( flags, SaveFlag.DropSound ) ) + writer.WriteEncodedInt( (int) m_DropSound ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 2: + { + SaveFlag flags = (SaveFlag)reader.ReadByte(); + + if ( GetSaveFlag( flags, SaveFlag.MaxItems ) ) + m_MaxItems = reader.ReadEncodedInt(); + else + m_MaxItems = -1; + + if ( GetSaveFlag( flags, SaveFlag.GumpID ) ) + m_GumpID = reader.ReadEncodedInt(); + else + m_GumpID = -1; + + if ( GetSaveFlag( flags, SaveFlag.DropSound ) ) + m_DropSound = reader.ReadEncodedInt(); + else + m_DropSound = -1; + + m_LiftOverride = GetSaveFlag( flags, SaveFlag.LiftOverride ); + + break; + } + case 1: + { + m_MaxItems = reader.ReadInt(); + goto case 0; + } + case 0: + { + if ( version < 1 ) + m_MaxItems = m_GlobalMaxItems; + + m_GumpID = reader.ReadInt(); + m_DropSound = reader.ReadInt(); + + if ( m_GumpID == DefaultGumpID ) + m_GumpID = -1; + + if ( m_DropSound == DefaultDropSound ) + m_DropSound = -1; + + if ( m_MaxItems == DefaultMaxItems ) + m_MaxItems = -1; + + //m_Bounds = new Rectangle2D( reader.ReadPoint2D(), reader.ReadPoint2D() ); + reader.ReadPoint2D(); + reader.ReadPoint2D(); + + break; + } + } + + UpdateContainerData(); + } + + private static int m_GlobalMaxItems = 125; + private static int m_GlobalMaxWeight = 400; + + public static int GlobalMaxItems{ get{ return m_GlobalMaxItems; } set{ m_GlobalMaxItems = value; } } + public static int GlobalMaxWeight{ get{ return m_GlobalMaxWeight; } set{ m_GlobalMaxWeight = value; } } + + public Container( int itemID ) : base( itemID ) + { + m_GumpID = -1; + m_DropSound = -1; + m_MaxItems = -1; + + UpdateContainerData(); + } + + public override int GetTotal( TotalType type ) + { + switch ( type ) + { + case TotalType.Gold: + return m_TotalGold; + + case TotalType.Items: + return m_TotalItems; + + case TotalType.Weight: + return m_TotalWeight; + } + + return base.GetTotal( type ); + } + + public override void UpdateTotal( Item sender, TotalType type, int delta ) + { + if ( sender != this && delta != 0 && !sender.IsVirtualItem ) + { + switch ( type ) + { + case TotalType.Gold: + m_TotalGold += delta; + break; + + case TotalType.Items: + m_TotalItems += delta; + InvalidateProperties(); + break; + + case TotalType.Weight: + m_TotalWeight += delta; + InvalidateProperties(); + break; + } + } + + base.UpdateTotal( sender, type, delta ); + } + + public override void UpdateTotals() + { + m_TotalGold = 0; + m_TotalItems = 0; + m_TotalWeight = 0; + + List items = m_Items; + + if ( items == null ) + return; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + item.UpdateTotals(); + + if ( item.IsVirtualItem ) + continue; + + m_TotalGold += item.TotalGold; + m_TotalItems += item.TotalItems + 1; + m_TotalWeight += item.TotalWeight + item.PileWeight; + } + } + + public Container( Serial serial ) : base( serial ) + { + } + + public virtual bool OnStackAttempt( Mobile from, Item stack, Item dropped ) + { + if ( !CheckHold( from, dropped, true, false ) ) + return false; + + return stack.StackWith( from, dropped ); + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( TryDropItem( from, dropped, true ) ) + { + from.SendSound( GetDroppedSound( dropped ), GetWorldLocation() ); + + return true; + } + else + { + return false; + } + } + + public virtual bool TryDropItem( Mobile from, Item dropped, bool sendFullMessage ) + { + if ( !CheckHold( from, dropped, sendFullMessage, true ) ) + return false; + + List list = this.Items; + + for ( int i = 0; i < list.Count; ++i ) + { + Item item = list[i]; + + if ( !(item is Container) && item.StackWith( from, dropped, false ) ) + return true; + } + + DropItem( dropped ); + + return true; + } + + public virtual void Destroy() + { + Point3D loc = GetWorldLocation(); + Map map = Map; + + for ( int i = Items.Count - 1; i >= 0; --i ) + { + if ( i < Items.Count ) + { + Items[i].SetLastMoved(); + Items[i].MoveToWorld( loc, map ); + } + } + + Delete(); + } + + public virtual void DropItem( Item dropped ) + { + if ( dropped == null ) + return; + + AddItem( dropped ); + + Rectangle2D bounds = dropped.GetGraphicBounds(); + Rectangle2D ourBounds = this.Bounds; + + int x, y; + + if ( bounds.Width >= ourBounds.Width ) + x = (ourBounds.Width - bounds.Width) / 2; + else + x = Utility.Random( ourBounds.Width - bounds.Width ); + + if ( bounds.Height >= ourBounds.Height ) + y = (ourBounds.Height - bounds.Height) / 2; + else + y = Utility.Random( ourBounds.Height - bounds.Height ); + + x += ourBounds.X; + x -= bounds.X; + + y += ourBounds.Y; + y -= bounds.Y; + + dropped.Location = new Point3D( x, y, 0 ); + } + + public override void OnDoubleClickSecureTrade( Mobile from ) + { + if ( from.InRange( GetWorldLocation(), 2 ) ) + { + DisplayTo( from ); + + SecureTradeContainer cont = GetSecureTradeCont(); + + if ( cont != null ) + { + SecureTrade trade = cont.Trade; + + if ( trade != null && trade.From.Mobile == from ) + DisplayTo( trade.To.Mobile ); + else if ( trade != null && trade.To.Mobile == from ) + DisplayTo( trade.From.Mobile ); + } + } + else + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + } + + public virtual bool DisplaysContent{ get{ return true; } } + + public virtual bool CheckContentDisplay( Mobile from ) + { + if ( !DisplaysContent ) + return false; + + object root = this.RootParent; + + if ( root == null || root is Item || root == from || from.AccessLevel > AccessLevel.Player ) + return true; + + return false; + } + + public override void OnSingleClick( Mobile from ) + { + base.OnSingleClick( from ); + + if ( CheckContentDisplay( from ) ) + LabelTo( from, "({0} items, {1} stones)", TotalItems, TotalWeight ); + } + + private List m_Openers; + + public List Openers + { + get{ return m_Openers; } + set{ m_Openers = value; } + } + + public virtual bool IsPublicContainer{ get{ return false; } } + + public override void OnDelete() + { + base.OnDelete(); + + m_Openers = null; + } + + public virtual void DisplayTo( Mobile to ) + { + if ( !IsPublicContainer ) + { + bool contains = false; + + if ( m_Openers != null ) + { + Point3D worldLoc = GetWorldLocation(); + Map map = this.Map; + + for ( int i = 0; i < m_Openers.Count; ++i ) + { + Mobile mob = m_Openers[i]; + + if ( mob == to ) + { + contains = true; + } + else + { + int range = GetUpdateRange( mob ); + + if ( mob.Map != map || !mob.InRange( worldLoc, range ) ) + m_Openers.RemoveAt( i-- ); + } + } + } + + if ( !contains ) + { + if ( m_Openers == null ) + m_Openers = new List( 4 ); + + m_Openers.Add( to ); + } + else if ( m_Openers != null && m_Openers.Count == 0 ) + { + m_Openers = null; + } + } + + to.Send( new ContainerDisplay( this ) ); + to.Send( new ContainerContent( to, this ) ); + + if ( ObjectPropertyList.Enabled ) + { + List items = this.Items; + + for ( int i = 0; i < items.Count; ++i ) + to.Send( items[i].OPLPacket ); + } + } + + public override void GetProperties( ObjectPropertyList list ) + { + base.GetProperties( list ); + + if ( DisplaysContent )//CheckContentDisplay( from ) ) + list.Add( 1050044, "{0}\t{1}", TotalItems, TotalWeight ); + } + + public override void OnDoubleClick( Mobile from ) + { + if ( from.AccessLevel > AccessLevel.Player || from.InRange( this.GetWorldLocation(), 2 ) ) + DisplayTo( from ); + else + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + } + + public class ContainerData + { + static ContainerData() + { + m_Table = new Dictionary(); + + string path = Path.Combine( Core.BaseDirectory, "Data/containers.cfg" ); + + if ( !File.Exists( path ) ) + { + m_Default = new ContainerData( 0x3C, new Rectangle2D( 44, 65, 142, 94 ), 0x48 ); + return; + } + + using ( StreamReader reader = new StreamReader( path ) ) + { + string line; + + while ( (line = reader.ReadLine()) != null ) + { + line = line.Trim(); + + if ( line.Length == 0 || line.StartsWith( "#" ) ) + continue; + + try + { + string[] split = line.Split( '\t' ); + + if ( split.Length >= 3 ) + { + int gumpID = Utility.ToInt32( split[0] ); + + string[] aRect = split[1].Split( ' ' ); + if ( aRect.Length < 4 ) + continue; + + int x = Utility.ToInt32( aRect[0] ); + int y = Utility.ToInt32( aRect[1] ); + int width = Utility.ToInt32( aRect[2] ); + int height = Utility.ToInt32( aRect[3] ); + + Rectangle2D bounds = new Rectangle2D( x, y, width, height ); + + int dropSound = Utility.ToInt32( split[2] ); + + ContainerData data = new ContainerData( gumpID, bounds, dropSound ); + + if ( m_Default == null ) + m_Default = data; + + if ( split.Length >= 4 ) + { + string[] aIDs = split[3].Split( ',' ); + + for ( int i = 0; i < aIDs.Length; i++ ) + { + int id = Utility.ToInt32( aIDs[i] ); + + if ( m_Table.ContainsKey( id ) ) + { + Console.WriteLine( @"Warning: double ItemID entry in Data\containers.cfg" ); + } + else + { + m_Table[id] = data; + } + } + } + } + } + catch + { + } + } + } + + if ( m_Default == null ) + m_Default = new ContainerData( 0x3C, new Rectangle2D( 44, 65, 142, 94 ), 0x48 ); + } + + private static ContainerData m_Default; + private static Dictionary m_Table; + + public static ContainerData Default + { + get{ return m_Default; } + set{ m_Default = value; } + } + + public static ContainerData GetData( int itemID ) + { + ContainerData data = null; + m_Table.TryGetValue( itemID, out data ); + + if ( data != null ) + return data; + else + return m_Default; + } + + private int m_GumpID; + private Rectangle2D m_Bounds; + private int m_DropSound; + + public int GumpID{ get{ return m_GumpID; } } + public Rectangle2D Bounds{ get{ return m_Bounds; } } + public int DropSound{ get{ return m_DropSound; } } + + public ContainerData( int gumpID, Rectangle2D bounds, int dropSound ) + { + m_GumpID = gumpID; + m_Bounds = bounds; + m_DropSound = dropSound; + } + } +} \ No newline at end of file diff --git a/Server/Items/Containers.cs b/Server/Items/Containers.cs new file mode 100644 index 000000000..82ac56b16 --- /dev/null +++ b/Server/Items/Containers.cs @@ -0,0 +1,166 @@ +/*************************************************************************** + * Containers.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Containers.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Items +{ + public class BankBox : Container + { + private Mobile m_Owner; + private bool m_Open; + + public override int DefaultMaxWeight + { + get + { + return 0; + } + } + + public override bool IsVirtualItem + { + get { return true; } + } + + public BankBox( Serial serial ) : base( serial ) + { + } + + public Mobile Owner + { + get + { + return m_Owner; + } + } + + public bool Opened + { + get + { + return m_Open; + } + } + + public void Open() + { + m_Open = true; + + if ( m_Owner != null ) + { + m_Owner.PrivateOverheadMessage( MessageType.Regular, 0x3B2, true, String.Format( "Bank container has {0} items, {1} stones", TotalItems, TotalWeight ), m_Owner.NetState ); + m_Owner.Send( new EquipUpdate( this ) ); + DisplayTo( m_Owner ); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + + writer.Write( (Mobile) m_Owner ); + writer.Write( (bool) m_Open ); + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Owner = reader.ReadMobile(); + m_Open = reader.ReadBool(); + + if ( m_Owner == null ) + Delete(); + + break; + } + } + + if ( this.ItemID == 0xE41 ) + this.ItemID = 0xE7C; + } + + private static bool m_SendRemovePacket; + + public static bool SendDeleteOnClose{ get{ return m_SendRemovePacket; } set{ m_SendRemovePacket = value; } } + + public void Close() + { + m_Open = false; + + if ( m_Owner != null && m_SendRemovePacket ) + m_Owner.Send( this.RemovePacket ); + } + + public override void OnSingleClick( Mobile from ) + { + } + + public override void OnDoubleClick( Mobile from ) + { + } + + public override DeathMoveResult OnParentDeath( Mobile parent ) + { + return DeathMoveResult.RemainEquiped; + } + + public BankBox( Mobile owner ) : base( 0xE7C ) + { + Layer = Layer.Bank; + Movable = false; + m_Owner = owner; + } + + public override bool IsAccessibleTo(Mobile check) + { + if ( ( check == m_Owner && m_Open ) || check.AccessLevel >= AccessLevel.GameMaster ) + return base.IsAccessibleTo (check); + else + return false; + } + + public override bool OnDragDrop( Mobile from, Item dropped ) + { + if ( ( from == m_Owner && m_Open ) || from.AccessLevel >= AccessLevel.GameMaster ) + return base.OnDragDrop( from, dropped ); + else + return false; + } + + public override bool OnDragDropInto(Mobile from, Item item, Point3D p) + { + if ( ( from == m_Owner && m_Open ) || from.AccessLevel >= AccessLevel.GameMaster ) + return base.OnDragDropInto (from, item, p); + else + return false; + } + } +} \ No newline at end of file diff --git a/Server/Items/SecureTradeContainer.cs b/Server/Items/SecureTradeContainer.cs new file mode 100644 index 000000000..4f8f40f78 --- /dev/null +++ b/Server/Items/SecureTradeContainer.cs @@ -0,0 +1,119 @@ +/*************************************************************************** + * SecureTradeContainer.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: SecureTradeContainer.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Items +{ + public class SecureTradeContainer : Container + { + private SecureTrade m_Trade; + + public SecureTrade Trade + { + get + { + return m_Trade; + } + } + + public SecureTradeContainer( SecureTrade trade ) : base( 0x1E5E ) + { + m_Trade = trade; + + Movable = false; + } + + public SecureTradeContainer( Serial serial ) : base( serial ) + { + } + + public override bool CheckHold( Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight ) + { + Mobile to; + + if ( this.Trade.From.Container != this ) + to = this.Trade.From.Mobile; + else + to = this.Trade.To.Mobile; + + return m.CheckTrade( to, item, this, message, checkItems, plusItems, plusWeight ); + } + + public override bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + reject = LRReason.CannotLift; + return false; + } + + public override bool IsAccessibleTo( Mobile check ) + { + if ( !IsChildOf( check ) ) + return false; + + return base.IsAccessibleTo( check ); + } + + public override void OnItemAdded( Item item ) + { + ClearChecks(); + } + + public override void OnItemRemoved( Item item ) + { + ClearChecks(); + } + + public override void OnSubItemAdded( Item item ) + { + ClearChecks(); + } + + public override void OnSubItemRemoved( Item item ) + { + ClearChecks(); + } + + public void ClearChecks() + { + if ( m_Trade != null ) + { + m_Trade.From.Accepted = false; + m_Trade.To.Accepted = false; + m_Trade.Update(); + } + } + + public override void Serialize( GenericWriter writer ) + { + base.Serialize( writer ); + + writer.Write( (int) 0 ); // version + } + + public override void Deserialize( GenericReader reader ) + { + base.Deserialize( reader ); + + int version = reader.ReadInt(); + } + } +} \ No newline at end of file diff --git a/Server/Items/VirtualHair.cs b/Server/Items/VirtualHair.cs new file mode 100644 index 000000000..76fc1f17b --- /dev/null +++ b/Server/Items/VirtualHair.cs @@ -0,0 +1,177 @@ +/*************************************************************************** + * VirtualHair.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: VirtualHair.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server; +using Server.Network; + +namespace Server +{ + public abstract class BaseHairInfo + { + private int m_ItemID; + private int m_Hue; + + [CommandProperty( AccessLevel.GameMaster )] + public int ItemID { get { return m_ItemID; } set { m_ItemID = value; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int Hue { get { return m_Hue; } set { m_Hue = value; } } + + public BaseHairInfo( int itemid ) + : this( itemid, 0 ) + { + } + + public BaseHairInfo( int itemid, int hue ) + { + m_ItemID = itemid; + m_Hue = hue; + } + + public BaseHairInfo( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch( version ) + { + case 0: + { + m_ItemID = reader.ReadInt(); + m_Hue = reader.ReadInt(); + break; + } + } + } + + public virtual void Serialize( GenericWriter writer ) + { + writer.Write( (int)0 ); //version + writer.Write( (int)m_ItemID ); + writer.Write( (int)m_Hue ); + } + } + + public class HairInfo : BaseHairInfo + { + public HairInfo( int itemid ) + : base( itemid, 0 ) + { + } + + public HairInfo( int itemid, int hue ) + : base( itemid, hue ) + { + } + + public HairInfo( GenericReader reader ) + : base( reader ) + { + } + + public static int FakeSerial( Mobile parent ) + { + return (0x7FFFFFFF - 0x400 - (parent.Serial * 4)); + } + } + + public class FacialHairInfo : BaseHairInfo + { + public FacialHairInfo( int itemid ) + : base( itemid, 0 ) + { + } + + public FacialHairInfo( int itemid, int hue ) + : base( itemid, hue ) + { + } + + public FacialHairInfo( GenericReader reader ) + : base( reader ) + { + } + + public static int FakeSerial( Mobile parent ) + { + return (0x7FFFFFFF - 0x400 - 1 - (parent.Serial * 4)); + } + } + + public sealed class HairEquipUpdate : Packet + { + public HairEquipUpdate( Mobile parent ) + : base( 0x2E, 15 ) + { + int hue = parent.HairHue; + + if( parent.SolidHueOverride >= 0 ) + hue = parent.SolidHueOverride; + + int hairSerial = HairInfo.FakeSerial( parent ); + + m_Stream.Write( (int)hairSerial ); + m_Stream.Write( (short)parent.HairItemID ); + m_Stream.Write( (byte)0 ); + m_Stream.Write( (byte)Layer.Hair ); + m_Stream.Write( (int)parent.Serial ); + m_Stream.Write( (short)hue ); + } + } + + public sealed class FacialHairEquipUpdate : Packet + { + public FacialHairEquipUpdate( Mobile parent ) + : base( 0x2E, 15 ) + { + int hue = parent.FacialHairHue; + + if( parent.SolidHueOverride >= 0 ) + hue = parent.SolidHueOverride; + + int hairSerial = FacialHairInfo.FakeSerial( parent ); + + m_Stream.Write( (int)hairSerial ); + m_Stream.Write( (short)parent.FacialHairItemID ); + m_Stream.Write( (byte)0 ); + m_Stream.Write( (byte)Layer.FacialHair ); + m_Stream.Write( (int)parent.Serial ); + m_Stream.Write( (short)hue ); + } + } + + public sealed class RemoveHair : Packet + { + public RemoveHair( Mobile parent ) + : base( 0x1D, 5 ) + { + m_Stream.Write( (int)HairInfo.FakeSerial( parent ) ); + } + } + + public sealed class RemoveFacialHair : Packet + { + public RemoveFacialHair( Mobile parent ) + : base( 0x1D, 5 ) + { + m_Stream.Write( (int)FacialHairInfo.FakeSerial( parent ) ); + } + } +} \ No newline at end of file diff --git a/Server/KeywordList.cs b/Server/KeywordList.cs new file mode 100644 index 000000000..a3bc7a0e7 --- /dev/null +++ b/Server/KeywordList.cs @@ -0,0 +1,85 @@ +/*************************************************************************** + * KeywordList.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: KeywordList.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public class KeywordList + { + private int[] m_Keywords; + private int m_Count; + + public KeywordList() + { + m_Keywords = new int[8]; + m_Count = 0; + } + + public int Count + { + get + { + return m_Count; + } + } + + public bool Contains( int keyword ) + { + bool contains = false; + + for ( int i = 0; !contains && i < m_Count; ++i ) + contains = ( keyword == m_Keywords[i] ); + + return contains; + } + + public void Add( int keyword ) + { + if ( (m_Count + 1) > m_Keywords.Length ) + { + int[] old = m_Keywords; + m_Keywords = new int[old.Length * 2]; + + for ( int i = 0; i < old.Length; ++i ) + m_Keywords[i] = old[i]; + } + + m_Keywords[m_Count++] = keyword; + } + + private static int[] m_EmptyInts = new int[0]; + + public int[] ToArray() + { + if ( m_Count == 0 ) + return m_EmptyInts; + + int[] keywords = new int[m_Count]; + + for ( int i = 0; i < m_Count; ++i ) + keywords[i] = m_Keywords[i]; + + m_Count = 0; + + return keywords; + } + } +} \ No newline at end of file diff --git a/Server/Main.cs b/Server/Main.cs new file mode 100644 index 000000000..cad2db0f1 --- /dev/null +++ b/Server/Main.cs @@ -0,0 +1,603 @@ +/*************************************************************************** + * Main.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Main.cs 153 2006-06-11 05:22:23Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Threading; +using Server; +using Server.Accounting; +using Server.Gumps; +using Server.Network; + +namespace Server { + public delegate void Slice(); + + public class Core { + private static bool m_Crashed; + private static Thread timerThread; + private static string m_BaseDirectory; + private static string m_ExePath; + private static List m_DataDirectories = new List(); + private static Assembly m_Assembly; + private static Process m_Process; + private static Thread m_Thread; + private static bool m_Service; + private static bool m_Debug; + private static MultiTextWriter m_MultiConOut; + + private static bool m_Profiling; + private static DateTime m_ProfileStart; + private static TimeSpan m_ProfileTime; + + private static MessagePump m_MessagePump; + + public static MessagePump MessagePump { + get { return m_MessagePump; } + set { m_MessagePump = value; } + } + + public static Slice Slice; + + public static bool Profiling { + get { return m_Profiling; } + set { + if ( m_Profiling == value ) + return; + + m_Profiling = value; + + if ( m_ProfileStart > DateTime.MinValue ) + m_ProfileTime += DateTime.Now - m_ProfileStart; + + m_ProfileStart = ( m_Profiling ? DateTime.Now : DateTime.MinValue ); + } + } + + public static TimeSpan ProfileTime { + get { + if ( m_ProfileStart > DateTime.MinValue ) + return m_ProfileTime + ( DateTime.Now - m_ProfileStart ); + + return m_ProfileTime; + } + } + + public static bool Service { get { return m_Service; } } + public static bool Debug { get { return m_Debug; } } + public static List DataDirectories { get { return m_DataDirectories; } } + public static Assembly Assembly { get { return m_Assembly; } set { m_Assembly = value; } } + public static Process Process { get { return m_Process; } } + public static Thread Thread { get { return m_Thread; } } + public static MultiTextWriter MultiConsoleOut { get { return m_MultiConOut; } } + + public static readonly bool Is64Bit = ( IntPtr.Size == 8 ); + + private static bool m_MultiProcessor; + private static int m_ProcessorCount; + + public static bool MultiProcessor { get { return m_MultiProcessor; } set { m_MultiProcessor = value; } } + public static int ProcessorCount { get { return m_ProcessorCount; } set { m_ProcessorCount = value; } } + + public static string FindDataFile( string path ) { + if ( m_DataDirectories.Count == 0 ) + throw new InvalidOperationException( "Attempted to FindDataFile before DataDirectories list has been filled." ); + + string fullPath = null; + + for ( int i = 0; i < m_DataDirectories.Count; ++i ) { + fullPath = Path.Combine( m_DataDirectories[i], path ); + + if ( File.Exists( fullPath ) ) + break; + + fullPath = null; + } + + return fullPath; + } + + public static string FindDataFile( string format, params object[] args ) { + return FindDataFile( String.Format( format, args ) ); + } + + #region Expansions + + private static Expansion m_Expansion; + public static Expansion Expansion { + get { return m_Expansion; } + set { m_Expansion = value; } + } + + public static bool AOS { + get { + return m_Expansion >= Expansion.AOS; + } + } + + public static bool SE { + get { + return m_Expansion >= Expansion.SE; + } + } + + public static bool ML { + get { + return m_Expansion >= Expansion.ML; + } + } + + #endregion + + public static string ExePath { + get { + if ( m_ExePath == null ) { + m_ExePath = Assembly.Location; + //m_ExePath = Process.GetCurrentProcess().MainModule.FileName; + } + + return m_ExePath; + } + } + + public static string BaseDirectory { + get { + if ( m_BaseDirectory == null ) { + try { + m_BaseDirectory = ExePath; + + if ( m_BaseDirectory.Length > 0 ) + m_BaseDirectory = Path.GetDirectoryName( m_BaseDirectory ); + } catch { + m_BaseDirectory = ""; + } + } + + return m_BaseDirectory; + } + } + + private static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e ) { + Console.WriteLine( e.IsTerminating ? "Error:" : "Warning:" ); + Console.WriteLine( e.ExceptionObject ); + + if ( e.IsTerminating ) { + m_Crashed = true; + + bool close = false; + + try { + CrashedEventArgs args = new CrashedEventArgs( e.ExceptionObject as Exception ); + + EventSink.InvokeCrashed( args ); + + close = args.Close; + } catch { + } + + if ( !close && !m_Service ) { + try { + for ( int i = 0; i < m_MessagePump.Listeners.Length; i++ ) { + m_MessagePump.Listeners[i].Dispose(); + } + } catch { + } + + Console.WriteLine( "This exception is fatal, press return to exit" ); + Console.ReadLine(); + } + + m_Closing = true; + } + } + +#if !MONO + private enum ConsoleEventType { + CTRL_C_EVENT, + CTRL_BREAK_EVENT, + CTRL_CLOSE_EVENT, + CTRL_LOGOFF_EVENT = 5, + CTRL_SHUTDOWN_EVENT + } + + private delegate bool ConsoleEventHandler( ConsoleEventType type ); + private static ConsoleEventHandler m_ConsoleEventHandler; + + [System.Runtime.InteropServices.DllImport( "Kernel32.dll" )] + private static extern bool SetConsoleCtrlHandler( ConsoleEventHandler callback, bool add ); + + private static bool OnConsoleEvent( ConsoleEventType type ) { + if ( World.Saving ) + return true; + + return false; + } +#endif + + private static void CurrentDomain_ProcessExit( object sender, EventArgs e ) { + HandleClosed(); + } + + private static bool m_Closing; + + public static bool Closing { get { return m_Closing; } set { m_Closing = value; } } + + private static int m_CycleIndex; + private static float[] m_CyclesPerSecond = new float[100]; + + public static float CyclesPerSecond { + get { return m_CyclesPerSecond[Math.Max( 0, ( m_CycleIndex - 1 ) % m_CyclesPerSecond.Length )]; } + } + + public static float AverageCPS { + get { + float t = 0.0f; + int c = 0; + + for ( int i = 0; i < m_CycleIndex && i < m_CyclesPerSecond.Length; ++i ) { + t += m_CyclesPerSecond[i]; + ++c; + } + + return ( t / Math.Max( c, 1 ) ); + } + } + + private static void HandleClosed() { + if ( m_Closing ) + return; + + m_Closing = true; + + Console.Write( "Exiting..." ); + + if ( !m_Crashed ) + EventSink.InvokeShutdown( new ShutdownEventArgs() ); + + if ( timerThread.ThreadState != System.Threading.ThreadState.Unstarted ) + timerThread.Join(); + + Console.WriteLine( "done" ); + } + + public static void Main( string[] args ) { + AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( CurrentDomain_UnhandledException ); + AppDomain.CurrentDomain.ProcessExit += new EventHandler( CurrentDomain_ProcessExit ); + + for ( int i = 0; i < args.Length; ++i ) { + if ( Insensitive.Equals( args[i], "-debug" ) ) + m_Debug = true; + else if ( Insensitive.Equals( args[i], "-service" ) ) + m_Service = true; + else if ( Insensitive.Equals( args[i], "-profile" ) ) + Profiling = true; + } + + try { + if ( m_Service ) { + if ( !Directory.Exists( "Logs" ) ) + Directory.CreateDirectory( "Logs" ); + + Console.SetOut( m_MultiConOut = new MultiTextWriter( Console.Out, new FileLogger( "Logs/Console.log" ) ) ); + } else { + Console.SetOut( m_MultiConOut = new MultiTextWriter( Console.Out ) ); + } + } catch { + } + +#if !MONO + m_ConsoleEventHandler = new ConsoleEventHandler( OnConsoleEvent ); + SetConsoleCtrlHandler( m_ConsoleEventHandler, true ); +#endif + + m_Thread = Thread.CurrentThread; + m_Process = Process.GetCurrentProcess(); + m_Assembly = Assembly.GetEntryAssembly(); + + if ( m_Thread != null ) + m_Thread.Name = "Core Thread"; + + if ( BaseDirectory.Length > 0 ) + Directory.SetCurrentDirectory( BaseDirectory ); + + Timer.TimerThread ttObj = new Timer.TimerThread(); + timerThread = new Thread( new ThreadStart( ttObj.TimerMain ) ); + timerThread.Name = "Timer Thread"; + + Version ver = m_Assembly.GetName().Version; + + // Added to help future code support on forums, as a 'check' people can ask for to it see if they recompiled core or not + Console.WriteLine( "RunUO - [www.runuo.com] Version {0}.{1}, Build {2}.{3}", ver.Major, ver.Minor, ver.Build, ver.Revision ); + Console.WriteLine( "Core: Running on .NET Framework Version {0}.{1}.{2}", Environment.Version.Major, Environment.Version.Minor, Environment.Version.Build ); + + string s = Arguments; + + if ( s.Length > 0 ) + Console.WriteLine( "Core: Running with arguments: {0}", s ); + + int processorCount = Utility.ToInt32( Environment.GetEnvironmentVariable( "NUMBER_OF_PROCESSORS" ) ); + + if ( processorCount > 1 ) + m_MultiProcessor = true; + + if ( processorCount > 1 || Is64Bit ) + Console.WriteLine( "Core: Optimizing for {0} {2}processor{1}", processorCount, processorCount == 1 ? "" : "s", Is64Bit ? "64-bit " : "" ); + + m_ProcessorCount = processorCount; + + while ( !ScriptCompiler.Compile( m_Debug ) ) { + Console.WriteLine( "Scripts: One or more scripts failed to compile or no script files were found." ); + Console.WriteLine( " - Press return to exit, or R to try again." ); + + if ( Console.ReadKey( true ).Key != ConsoleKey.R ) + return; + } + + MessagePump ms = m_MessagePump = new MessagePump( new Listener( Listener.Port ) ); + + timerThread.Start(); + + for ( int i = 0; i < Map.AllMaps.Count; ++i ) + Map.AllMaps[i].Tiles.Force(); + + NetState.Initialize(); + + EventSink.InvokeServerStarted(); + + try { + DateTime now, last = DateTime.Now; + + const int sampleInterval = 100; + const float ticksPerSecond = ( float ) ( TimeSpan.TicksPerSecond * sampleInterval ); + + int sample = 0; + + while ( !m_Closing ) { + Thread.Sleep( m_MultiProcessor ? 0 : 1 ); + + Mobile.ProcessDeltaQueue(); + Item.ProcessDeltaQueue(); + + Timer.Slice(); + m_MessagePump.Slice(); + + NetState.FlushAll(); + NetState.ProcessDisposedQueue(); + + if ( Slice != null ) + Slice(); + + if ( ( ++sample % sampleInterval ) == 0 ) { + now = DateTime.Now; + m_CyclesPerSecond[m_CycleIndex++ % m_CyclesPerSecond.Length] = + ticksPerSecond / ( now.Ticks - last.Ticks ); + last = now; + } + } + } catch ( Exception e ) { + CurrentDomain_UnhandledException( null, new UnhandledExceptionEventArgs( e, true ) ); + } + + if ( timerThread.IsAlive ) + timerThread.Abort(); + } + + public static string Arguments { + get { + StringBuilder sb = new StringBuilder(); + + if ( Core.Debug ) + Utility.Seperate( sb, "-debug", " " ); + + if ( Core.Service ) + Utility.Seperate( sb, "-service", " " ); + + if ( Core.Profiling ) + Utility.Seperate( sb, "-profile", " " ); + + return sb.ToString(); + } + } + + private static int m_GlobalMaxUpdateRange = 24; + + public static int GlobalMaxUpdateRange { + get { return m_GlobalMaxUpdateRange; } + set { m_GlobalMaxUpdateRange = value; } + } + + private static int m_ItemCount, m_MobileCount; + + public static int ScriptItems { get { return m_ItemCount; } } + public static int ScriptMobiles { get { return m_MobileCount; } } + + public static void VerifySerialization() { + m_ItemCount = 0; + m_MobileCount = 0; + + VerifySerialization( Assembly.GetCallingAssembly() ); + + for ( int a = 0; a < ScriptCompiler.Assemblies.Length; ++a ) + VerifySerialization( ScriptCompiler.Assemblies[a] ); + } + + private static void VerifySerialization( Assembly a ) { + if ( a == null ) + return; + + Type[] ctorTypes = new Type[] { typeof( Serial ) }; + + foreach ( Type t in a.GetTypes() ) { + bool isItem = t.IsSubclassOf( typeof( Item ) ); + + if ( isItem || t.IsSubclassOf( typeof( Mobile ) ) ) { + if ( isItem ) + ++m_ItemCount; + else + ++m_MobileCount; + + bool warned = false; + + try { + + /* + if( isItem && t.IsPublic && !t.IsAbstract ) + { + ConstructorInfo cInfo = t.GetConstructor( Type.EmptyTypes ); + if( cInfo == null ) + { + if( !warned ) + Console.WriteLine( "Warning: {0}", t ); + + warned = true; + Console.WriteLine( " - No zero paramater constructor" ); + } + } + */ + + if ( t.GetConstructor( ctorTypes ) == null ) { + if ( !warned ) + Console.WriteLine( "Warning: {0}", t ); + + warned = true; + Console.WriteLine( " - No serialization constructor" ); + } + + if ( t.GetMethod( "Serialize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly ) == null ) { + if ( !warned ) + Console.WriteLine( "Warning: {0}", t ); + + warned = true; + Console.WriteLine( " - No Serialize() method" ); + } + + if ( t.GetMethod( "Deserialize", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly ) == null ) { + if ( !warned ) + Console.WriteLine( "Warning: {0}", t ); + + warned = true; + Console.WriteLine( " - No Deserialize() method" ); + } + + if ( warned ) + Console.WriteLine(); + } catch { + } + } + } + } + } + + public class FileLogger : TextWriter, IDisposable { + private string m_FileName; + private bool m_NewLine; + public const string DateFormat = "[MMMM dd hh:mm:ss.f tt]: "; + + public string FileName { get { return m_FileName; } } + + public FileLogger( string file ) + : this( file, false ) { + } + + public FileLogger( string file, bool append ) { + m_FileName = file; + using ( StreamWriter writer = new StreamWriter( new FileStream( m_FileName, append ? FileMode.Append : FileMode.Create, FileAccess.Write, FileShare.Read ) ) ) { + writer.WriteLine( ">>>Logging started on {0}.", DateTime.Now.ToString( "f" ) ); //f = Tuesday, April 10, 2001 3:51 PM + } + m_NewLine = true; + } + + public override void Write( char ch ) { + using ( StreamWriter writer = new StreamWriter( new FileStream( m_FileName, FileMode.Append, FileAccess.Write, FileShare.Read ) ) ) { + if ( m_NewLine ) { + writer.Write( DateTime.Now.ToString( DateFormat ) ); + m_NewLine = false; + } + writer.Write( ch ); + } + } + + public override void Write( string str ) { + using ( StreamWriter writer = new StreamWriter( new FileStream( m_FileName, FileMode.Append, FileAccess.Write, FileShare.Read ) ) ) { + if ( m_NewLine ) { + writer.Write( DateTime.Now.ToString( DateFormat ) ); + m_NewLine = false; + } + writer.Write( str ); + } + } + + public override void WriteLine( string line ) { + using ( StreamWriter writer = new StreamWriter( new FileStream( m_FileName, FileMode.Append, FileAccess.Write, FileShare.Read ) ) ) { + if ( m_NewLine ) + writer.Write( DateTime.Now.ToString( DateFormat ) ); + writer.WriteLine( line ); + m_NewLine = true; + } + } + + public override System.Text.Encoding Encoding { + get { return System.Text.Encoding.Default; } + } + } + + public class MultiTextWriter : TextWriter { + private List m_Streams; + + public MultiTextWriter( params TextWriter[] streams ) { + m_Streams = new List( streams ); + + if ( m_Streams.Count < 0 ) + throw new ArgumentException( "You must specify at least one stream." ); + } + + public void Add( TextWriter tw ) { + m_Streams.Add( tw ); + } + + public void Remove( TextWriter tw ) { + m_Streams.Remove( tw ); + } + + public override void Write( char ch ) { + for ( int i = 0; i < m_Streams.Count; i++ ) + m_Streams[i].Write( ch ); + } + + public override void WriteLine( string line ) { + for ( int i = 0; i < m_Streams.Count; i++ ) + m_Streams[i].WriteLine( line ); + } + + public override void WriteLine( string line, params object[] args ) { + WriteLine( String.Format( line, args ) ); + } + + public override Encoding Encoding { + get { return Encoding.Default; } + } + } +} \ No newline at end of file diff --git a/Server/Map.cs b/Server/Map.cs new file mode 100644 index 000000000..2a7ca94bb --- /dev/null +++ b/Server/Map.cs @@ -0,0 +1,2074 @@ +/*************************************************************************** + * Map.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Map.cs 145 2006-06-09 02:40:53Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Server.Items; +using Server.Network; +using Server.Targeting; + +namespace Server +{ + [Flags] + public enum MapRules + { + None = 0x0000, + Internal = 0x0001, // Internal map (used for dragging, commodity deeds, etc) + FreeMovement = 0x0002, // Anyone can move over anyone else without taking stamina loss + BeneficialRestrictions = 0x0004, // Disallow performing beneficial actions on criminals/murderers + HarmfulRestrictions = 0x0008, // Disallow performing harmful actions on innocents + TrammelRules = FreeMovement | BeneficialRestrictions | HarmfulRestrictions, + FeluccaRules = None + } + + public interface IPooledEnumerable : IEnumerable + { + void Free(); + } + + public interface IPooledEnumerator : IEnumerator + { + IPooledEnumerable Enumerable{ get; set; } + void Free(); + } + + [Parsable] + //[CustomEnum( new string[]{ "Felucca", "Trammel", "Ilshenar", "Malas", "Internal" } )] + public sealed class Map : IComparable, IComparable + { + public const int SectorSize = 16; + public const int SectorShift = 4; + public static int SectorActiveRange = 2; + + private static Map[] m_Maps = new Map[0x100]; + + public static Map[] Maps { get { return m_Maps; } } + + public static Map Felucca { get { return m_Maps[0]; } } + public static Map Trammel { get { return m_Maps[1]; } } + public static Map Ilshenar { get { return m_Maps[2]; } } + public static Map Malas { get { return m_Maps[3]; } } + public static Map Tokuno { get { return m_Maps[4]; } } + public static Map Internal { get { return m_Maps[0x7F]; } } + + private static List m_AllMaps = new List(); + + public static List AllMaps { get { return m_AllMaps; } } + + private int m_MapID, m_MapIndex, m_FileIndex; + + private int m_Width, m_Height; + private int m_SectorsWidth, m_SectorsHeight; + private int m_Season; + private Dictionary m_Regions; + private Region m_DefaultRegion; + + public int Season { get { return m_Season; } set { m_Season = value; } } + + private string m_Name; + private MapRules m_Rules; + private Sector[][] m_Sectors; + private Sector m_InvalidSector; + + private TileMatrix m_Tiles; + + private static string[] m_MapNames; + private static Map[] m_MapValues; + + public static string[] GetMapNames() + { + CheckNamesAndValues(); + return m_MapNames; + } + + public static Map[] GetMapValues() + { + CheckNamesAndValues(); + return m_MapValues; + } + + public static Map Parse( string value ) + { + CheckNamesAndValues(); + + for ( int i = 0; i < m_MapNames.Length; ++i ) + { + if ( Insensitive.Equals( m_MapNames[i], value ) ) + return m_MapValues[i]; + } + + int index; + + if( int.TryParse( value, out index ) ) + { + if( index >= 0 && index < m_Maps.Length && m_Maps[index] != null ) + return m_Maps[index]; + } + + throw new Exception( "Invalid map name" ); + } + + private static void CheckNamesAndValues() + { + if ( m_MapNames != null && m_MapNames.Length == m_AllMaps.Count ) + return; + + m_MapNames = new string[m_AllMaps.Count]; + m_MapValues = new Map[m_AllMaps.Count]; + + for ( int i = 0; i < m_AllMaps.Count; ++i ) + { + Map map = m_AllMaps[i]; + + m_MapNames[i] = map.Name; + m_MapValues[i] = map; + } + } + + public override string ToString() + { + return m_Name; + } + + public int GetAverageZ( int x, int y ) + { + int z = 0, avg = 0, top = 0; + + GetAverageZ( x, y, ref z, ref avg, ref top ); + + return avg; + } + + public void GetAverageZ( int x, int y, ref int z, ref int avg, ref int top ) + { + int zTop = Tiles.GetLandTile( x, y ).Z; + int zLeft = Tiles.GetLandTile( x, y + 1 ).Z; + int zRight = Tiles.GetLandTile( x + 1, y ).Z; + int zBottom = Tiles.GetLandTile( x + 1, y + 1 ).Z; + + z = zTop; + if ( zLeft < z ) + z = zLeft; + if ( zRight < z ) + z = zRight; + if ( zBottom < z ) + z = zBottom; + + top = zTop; + if ( zLeft > top ) + top = zLeft; + if ( zRight > top ) + top = zRight; + if ( zBottom > top ) + top = zBottom; + + if ( Math.Abs( zTop - zBottom ) > Math.Abs( zLeft - zRight ) ) + avg = FloorAverage( zLeft, zRight ); + else + avg = FloorAverage( zTop, zBottom ); + } + + private static int FloorAverage( int a, int b ) + { + int v = a + b; + + if ( v < 0 ) + --v; + + return ( v / 2 ); + } + + #region Get*InRange/Bounds + public IPooledEnumerable GetObjectsInRange( Point3D p ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( ObjectEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ) ) ); + } + + public IPooledEnumerable GetObjectsInRange( Point3D p, int range ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( ObjectEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ) ) ); + } + + public IPooledEnumerable GetObjectsInBounds( Rectangle2D bounds ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( ObjectEnumerator.Instantiate( this, bounds ) ); + } + + public IPooledEnumerable GetClientsInRange( Point3D p ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ), SectorEnumeratorType.Clients ) ); + } + + public IPooledEnumerable GetClientsInRange( Point3D p, int range ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ), SectorEnumeratorType.Clients ) ); + } + + public IPooledEnumerable GetClientsInBounds( Rectangle2D bounds ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, bounds, SectorEnumeratorType.Clients ) ); + } + + public IPooledEnumerable GetItemsInRange( Point3D p ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ), SectorEnumeratorType.Items ) ); + } + + public IPooledEnumerable GetItemsInRange( Point3D p, int range ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ), SectorEnumeratorType.Items ) ); + } + + public IPooledEnumerable GetItemsInBounds( Rectangle2D bounds ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, bounds, SectorEnumeratorType.Items ) ); + } + + public IPooledEnumerable GetMobilesInRange( Point3D p ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - 18, p.m_Y - 18, 37, 37 ), SectorEnumeratorType.Mobiles ) ); + } + + public IPooledEnumerable GetMobilesInRange( Point3D p, int range ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, new Rectangle2D( p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1 ), SectorEnumeratorType.Mobiles ) ); + } + + public IPooledEnumerable GetMobilesInBounds( Rectangle2D bounds ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( TypedEnumerator.Instantiate( this, bounds, SectorEnumeratorType.Mobiles ) ); + } + #endregion + + public IPooledEnumerable GetMultiTilesAt( int x, int y ) + { + if ( this == Map.Internal ) + return NullEnumerable.Instance; + + Sector sector = GetSector( x, y ); + + if ( sector.Multis.Count == 0 ) + return NullEnumerable.Instance; + + return PooledEnumerable.Instantiate( MultiTileEnumerator.Instantiate( sector, new Point2D( x, y ) ) ); + } + + #region CanFit + public bool CanFit( Point3D p, int height, bool checkBlocksFit ) + { + return CanFit( p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, true, true ); + } + + public bool CanFit( Point3D p, int height, bool checkBlocksFit, bool checkMobiles ) + { + return CanFit( p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, true ); + } + + public bool CanFit( Point2D p, int z, int height, bool checkBlocksFit ) + { + return CanFit( p.m_X, p.m_Y, z, height, checkBlocksFit, true, true ); + } + + public bool CanFit( Point3D p, int height ) + { + return CanFit( p.m_X, p.m_Y, p.m_Z, height, false, true, true ); + } + + public bool CanFit( Point2D p, int z, int height ) + { + return CanFit( p.m_X, p.m_Y, z, height, false, true, true ); + } + + public bool CanFit( int x, int y, int z, int height ) + { + return CanFit( x, y, z, height, false, true, true ); + } + + public bool CanFit( int x, int y, int z, int height, bool checksBlocksFit ) + { + return CanFit( x, y, z, height, checksBlocksFit, true, true ); + } + + public bool CanFit( int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles ) + { + return CanFit( x, y, z, height, checkBlocksFit, checkMobiles, true ); + } + + public bool CanFit( int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles, bool requireSurface ) + { + if ( this == Map.Internal ) + return false; + + if ( x < 0 || y < 0 || x >= m_Width || y >= m_Height ) + return false; + + bool hasSurface = false; + + Tile lt = Tiles.GetLandTile( x, y ); + int lowZ = 0, avgZ = 0, topZ = 0; + + GetAverageZ( x, y, ref lowZ, ref avgZ, ref topZ ); + TileFlag landFlags = TileData.LandTable[lt.ID & 0x3FFF].Flags; + + if ( ( landFlags & TileFlag.Impassable ) != 0 && avgZ > z && ( z + height ) > lowZ ) + return false; + else if ( ( landFlags & TileFlag.Impassable ) == 0 && z == avgZ && !lt.Ignored ) + hasSurface = true; + + Tile[] staticTiles = Tiles.GetStaticTiles( x, y, true ); + + bool surface, impassable; + + for ( int i = 0; i < staticTiles.Length; ++i ) + { + ItemData id = TileData.ItemTable[staticTiles[i].ID & 0x3FFF]; + surface = id.Surface; + impassable = id.Impassable; + + if ( ( surface || impassable ) && ( staticTiles[i].Z + id.CalcHeight ) > z && ( z + height ) > staticTiles[i].Z ) + return false; + else if ( surface && !impassable && z == ( staticTiles[i].Z + id.CalcHeight ) ) + hasSurface = true; + } + + Sector sector = GetSector( x, y ); + List items = sector.Items; + List mobs = sector.Mobiles; + + for ( int i = 0; i < items.Count; ++i ) + { + Item item = items[i]; + + if ( item.ItemID < 0x4000 && item.AtWorldPoint( x, y ) ) + { + ItemData id = item.ItemData; + surface = id.Surface; + impassable = id.Impassable; + + if ( ( surface || impassable || ( checkBlocksFit && item.BlocksFit ) ) && ( item.Z + id.CalcHeight ) > z && ( z + height ) > item.Z ) + return false; + else if ( surface && !impassable && !item.Movable && z == ( item.Z + id.CalcHeight ) ) + hasSurface = true; + } + } + + if ( checkMobiles ) + { + for ( int i = 0; i < mobs.Count; ++i ) + { + Mobile m = mobs[i]; + + if ( m.Location.m_X == x && m.Location.m_Y == y && ( m.AccessLevel == AccessLevel.Player || !m.Hidden ) ) + if ( ( m.Z + 16 ) > z && ( z + height ) > m.Z ) + return false; + } + } + + return !requireSurface || hasSurface; + } + + #endregion + + #region CanSpawnMobile + public bool CanSpawnMobile( Point3D p ) + { + return CanSpawnMobile( p.m_X, p.m_Y, p.m_Z ); + } + + public bool CanSpawnMobile( Point2D p, int z ) + { + return CanSpawnMobile( p.m_X, p.m_Y, z ); + } + + public bool CanSpawnMobile( int x, int y, int z ) + { + if ( !Region.Find( new Point3D( x, y, z ), this ).AllowSpawn() ) + return false; + + return CanFit( x, y, z, 16 ); + } + #endregion + + private class ZComparer : IComparer + { + public static readonly ZComparer Default = new ZComparer(); + + public int Compare( Item x, Item y ) + { + return x.Z.CompareTo( y.Z ); + } + } + + public void FixColumn( int x, int y ) + { + Tile landTile = Tiles.GetLandTile( x, y ); + + int landZ = 0, landAvg = 0, landTop = 0; + GetAverageZ( x, y, ref landZ, ref landAvg, ref landTop ); + + Tile[] tiles = Tiles.GetStaticTiles( x, y, true ); + + List items = new List(); + + IPooledEnumerable eable = GetItemsInRange( new Point3D( x, y, 0 ), 0 ); + + foreach ( Item item in eable ) + { + if ( item.ItemID < 0x4000 ) + { + items.Add( item ); + + if ( items.Count > 100 ) + break; + } + } + + eable.Free(); + + if ( items.Count > 100 ) + return; + + items.Sort( ZComparer.Default ); + + for ( int i = 0; i < items.Count; ++i ) + { + Item toFix = items[i]; + + if ( !toFix.Movable ) + continue; + + int z = int.MinValue; + int currentZ = toFix.Z; + + if ( !landTile.Ignored && landAvg <= currentZ ) + z = landAvg; + + for ( int j = 0; j < tiles.Length; ++j ) + { + Tile tile = tiles[j]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + int checkZ = tile.Z; + int checkTop = checkZ + id.CalcHeight; + + if ( checkTop == checkZ && !id.Surface ) + ++checkTop; + + if ( checkTop > z && checkTop <= currentZ ) + z = checkTop; + } + + for ( int j = 0; j < items.Count; ++j ) + { + if ( j == i ) + continue; + + Item item = items[j]; + ItemData id = item.ItemData; + + int checkZ = item.Z; + int checkTop = checkZ + id.CalcHeight; + + if ( checkTop == checkZ && !id.Surface ) + ++checkTop; + + if ( checkTop > z && checkTop <= currentZ ) + z = checkTop; + } + + if ( z != int.MinValue ) + toFix.Location = new Point3D( toFix.X, toFix.Y, z ); + } + } + + public ArrayList GetTilesAt( Point2D p, bool items, bool land, bool statics ) + { + ArrayList list = new ArrayList(); + + if ( this == Map.Internal ) + return list; + + if ( land ) + { + list.Add( Tiles.GetLandTile( p.m_X, p.m_Y ) ); + } + + if ( statics ) + { + list.AddRange( Tiles.GetStaticTiles( p.m_X, p.m_Y, true ) ); + } + + if ( items ) + { + Sector sector = GetSector( p ); + + foreach ( Item item in sector.Items ) + { + if ( item.AtWorldPoint( p.m_X, p.m_Y ) ) + { + list.Add( new Tile( (short) ( ( item.ItemID & 0x3FFF ) + 0x4000 ), (sbyte) item.Z ) ); + } + } + } + + return list; + } + + /// + /// Gets the highest surface that is lower than . + /// + /// The reference point. + /// A surface or . + public object GetTopSurface( Point3D p ) + { + if ( this == Map.Internal ) + return null; + + object surface = null; + int surfaceZ = int.MinValue; + + + Tile lt = Tiles.GetLandTile( p.X, p.Y ); + + if ( !lt.Ignored ) + { + int avgZ = GetAverageZ( p.X, p.Y ); + + if ( avgZ <= p.Z ) + { + surface = lt; + surfaceZ = avgZ; + + if ( surfaceZ == p.Z ) + return surface; + } + } + + + Tile[] staticTiles = Tiles.GetStaticTiles( p.X, p.Y, true ); + + for ( int i = 0; i < staticTiles.Length; i++ ) + { + Tile tile = staticTiles[i]; + ItemData id = TileData.ItemTable[tile.ID & 0x3FFF]; + + if ( id.Surface || ( id.Flags & TileFlag.Wet ) != 0 ) + { + int tileZ = tile.Z + id.CalcHeight; + + if ( tileZ > surfaceZ && tileZ <= p.Z ) + { + surface = tile; + surfaceZ = tileZ; + + if ( surfaceZ == p.Z ) + return surface; + } + } + } + + + Sector sector = GetSector( p.X, p.Y ); + + for ( int i = 0; i < sector.Items.Count; i++ ) + { + Item item = sector.Items[i]; + + if ( item.ItemID < 0x4000 && item.AtWorldPoint( p.X, p.Y ) && !item.Movable ) + { + ItemData id = item.ItemData; + + if ( id.Surface || ( id.Flags & TileFlag.Wet ) != 0 ) + { + int itemZ = item.Z + id.CalcHeight; + + if ( itemZ > surfaceZ && itemZ <= p.Z ) + { + surface = item; + surfaceZ = itemZ; + + if ( surfaceZ == p.Z ) + return surface; + } + } + } + } + + + return surface; + } + + public void Bound( int x, int y, out int newX, out int newY ) + { + if ( x < 0 ) + newX = 0; + else if ( x >= m_Width ) + newX = m_Width - 1; + else + newX = x; + + if ( y < 0 ) + newY = 0; + else if ( y >= m_Height ) + newY = m_Height - 1; + else + newY = y; + } + + public Point2D Bound( Point2D p ) + { + int x = p.m_X, y = p.m_Y; + + if ( x < 0 ) + x = 0; + else if ( x >= m_Width ) + x = m_Width - 1; + + if ( y < 0 ) + y = 0; + else if ( y >= m_Height ) + y = m_Height - 1; + + return new Point2D( x, y ); + } + + public Map( int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules ) + { + m_MapID = mapID; + m_MapIndex = mapIndex; + m_FileIndex = fileIndex; + m_Width = width; + m_Height = height; + m_Season = season; + m_Name = name; + m_Rules = rules; + m_Regions = new Dictionary( StringComparer.OrdinalIgnoreCase ); + m_InvalidSector = new Sector( 0, 0, this ); + m_SectorsWidth = width >> SectorShift; + m_SectorsHeight = height >> SectorShift; + m_Sectors = new Sector[m_SectorsWidth][]; + } + + #region GetSector + public Sector GetSector( Point3D p ) + { + return InternalGetSector( p.m_X >> SectorShift, p.m_Y >> SectorShift ); + } + + public Sector GetSector( Point2D p ) + { + return InternalGetSector( p.m_X >> SectorShift, p.m_Y >> SectorShift ); + } + + public Sector GetSector( IPoint2D p ) + { + return InternalGetSector( p.X >> SectorShift, p.Y >> SectorShift ); + } + + public Sector GetSector( int x, int y ) + { + return InternalGetSector( x >> SectorShift, y >> SectorShift ); + } + + public Sector GetRealSector( int x, int y ) + { + return InternalGetSector( x, y ); + } + + private Sector InternalGetSector( int x, int y ) + { + if ( x >= 0 && x < m_SectorsWidth && y >= 0 && y < m_SectorsHeight ) + { + Sector[] xSectors = m_Sectors[x]; + + if ( xSectors == null ) + m_Sectors[x] = xSectors = new Sector[m_SectorsHeight]; + + Sector sec = xSectors[y]; + + if ( sec == null ) + xSectors[y] = sec = new Sector( x, y, this ); + + return sec; + } + else + { + return m_InvalidSector; + } + } + #endregion + + public void ActivateSectors( int cx, int cy ) + { + for ( int x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x ) + { + for ( int y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y ) + { + Sector sect = GetRealSector( x, y ); + if ( sect != m_InvalidSector ) + sect.Activate(); + } + } + } + + public void DeactivateSectors( int cx, int cy ) + { + for ( int x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x ) + { + for ( int y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y ) + { + Sector sect = GetRealSector( x, y ); + if ( sect != m_InvalidSector && !PlayersInRange( sect, SectorActiveRange ) ) + sect.Deactivate(); + } + } + } + + private bool PlayersInRange( Sector sect, int range ) + { + for ( int x = sect.X - range; x <= sect.X + range; ++x ) + { + for ( int y = sect.Y - range; y <= sect.Y + range; ++y ) + { + Sector check = GetRealSector( x, y ); + if ( check != m_InvalidSector && check.Players.Count > 0 ) + return true; + } + } + + return false; + } + + public void OnClientChange( NetState oldState, NetState newState, Mobile m ) + { + if ( this == Map.Internal ) + return; + + GetSector( m ).OnClientChange( oldState, newState ); + } + + public void OnEnter( Mobile m ) + { + if ( this == Map.Internal ) + return; + + Sector sector = GetSector( m ); + + sector.OnEnter( m ); + } + + public void OnEnter( Item item ) + { + if ( this == Map.Internal ) + return; + + GetSector( item ).OnEnter( item ); + + if ( item is BaseMulti ) + { + BaseMulti m = (BaseMulti)item; + MultiComponentList mcl = m.Components; + + Sector start = GetMultiMinSector( item.Location, mcl ); + Sector end = GetMultiMaxSector( item.Location, mcl ); + + AddMulti( m, start, end ); + } + } + + public void OnLeave( Mobile m ) + { + if ( this == Map.Internal ) + return; + + Sector sector = GetSector( m ); + + sector.OnLeave( m ); + } + + public void OnLeave( Item item ) + { + if ( this == Map.Internal ) + return; + + GetSector( item ).OnLeave( item ); + + if ( item is BaseMulti ) + { + BaseMulti m = (BaseMulti)item; + MultiComponentList mcl = m.Components; + + Sector start = GetMultiMinSector( item.Location, mcl ); + Sector end = GetMultiMaxSector( item.Location, mcl ); + + RemoveMulti( m, start, end ); + } + } + + public void RemoveMulti( BaseMulti m, Sector start, Sector end ) + { + if ( this == Map.Internal ) + return; + + for ( int x = start.X; x <= end.X; ++x ) + for ( int y = start.Y; y <= end.Y; ++y ) + InternalGetSector( x, y ).OnMultiLeave( m ); + } + + public void AddMulti( BaseMulti m, Sector start, Sector end ) + { + if ( this == Map.Internal ) + return; + + for ( int x = start.X; x <= end.X; ++x ) + for ( int y = start.Y; y <= end.Y; ++y ) + InternalGetSector( x, y ).OnMultiEnter( m ); + } + + public Sector GetMultiMinSector( Point3D loc, MultiComponentList mcl ) + { + return GetSector( Bound( new Point2D( loc.m_X + mcl.Min.m_X, loc.m_Y + mcl.Min.m_Y ) ) ); + } + + public Sector GetMultiMaxSector( Point3D loc, MultiComponentList mcl ) + { + return GetSector( Bound( new Point2D( loc.m_X + mcl.Max.m_X, loc.m_Y + mcl.Max.m_Y ) ) ); + } + + public void OnMove( Point3D oldLocation, Mobile m ) + { + if ( this == Map.Internal ) + return; + + Sector oldSector = GetSector( oldLocation ); + Sector newSector = GetSector( m.Location ); + + if ( oldSector != newSector ) + { + oldSector.OnLeave( m ); + newSector.OnEnter( m ); + } + } + + public void OnMove( Point3D oldLocation, Item item ) + { + if ( this == Map.Internal ) + return; + + Sector oldSector = GetSector( oldLocation ); + Sector newSector = GetSector( item.Location ); + + if ( oldSector != newSector ) + { + oldSector.OnLeave( item ); + newSector.OnEnter( item ); + } + + if ( item is BaseMulti ) + { + BaseMulti m = (BaseMulti)item; + MultiComponentList mcl = m.Components; + + Sector start = GetMultiMinSector( item.Location, mcl ); + Sector end = GetMultiMaxSector( item.Location, mcl ); + + Sector oldStart = GetMultiMinSector( oldLocation, mcl ); + Sector oldEnd = GetMultiMaxSector( oldLocation, mcl ); + + if ( oldStart != start || oldEnd != end ) + { + RemoveMulti( m, oldStart, oldEnd ); + AddMulti( m, start, end ); + } + } + } + + public TileMatrix Tiles + { + get + { + if ( m_Tiles == null ) + m_Tiles = new TileMatrix( this, m_FileIndex, m_MapID, m_Width, m_Height ); + + return m_Tiles; + } + } + + public int MapID + { + get + { + return m_MapID; + } + } + + public int MapIndex + { + get + { + return m_MapIndex; + } + } + + public int Width + { + get + { + return m_Width; + } + } + + public int Height + { + get + { + return m_Height; + } + } + + public Dictionary Regions + { + get + { + return m_Regions; + } + } + + public void RegisterRegion( Region reg ) + { + string regName = reg.Name; + + if ( regName != null ) + { + if ( m_Regions.ContainsKey( regName ) ) + Console.WriteLine( "Warning: Duplicate region name '{0}' for map '{1}'", regName, this.Name ); + else + m_Regions[regName] = reg; + } + } + + public void UnregisterRegion( Region reg ) + { + string regName = reg.Name; + + if ( regName != null ) + m_Regions.Remove( regName ); + } + + public Region DefaultRegion + { + get + { + if ( m_DefaultRegion == null ) + m_DefaultRegion = new Region( null, this, 0, new Rectangle3D[0] ); + + return m_DefaultRegion; + } + set + { + m_DefaultRegion = value; + } + } + + public MapRules Rules + { + get + { + return m_Rules; + } + set + { + m_Rules = value; + } + } + + public Sector InvalidSector + { + get + { + return m_InvalidSector; + } + } + + public string Name + { + get + { + return m_Name; + } + set + { + m_Name = value; + } + } + + private enum SectorEnumeratorType + { + Mobiles, + Items, + Clients + } + + public class NullEnumerable : IPooledEnumerable + { + private InternalEnumerator m_Enumerator; + + public static readonly NullEnumerable Instance = new NullEnumerable(); + + private NullEnumerable() + { + m_Enumerator = new InternalEnumerator(); + } + + public IEnumerator GetEnumerator() + { + return m_Enumerator; + } + + public void Free() + { + } + + private class InternalEnumerator : IEnumerator + { + public void Reset() + { + } + + public object Current + { + get + { + return null; + } + } + + public bool MoveNext() + { + return false; + } + } + } + + private class PooledEnumerable : IPooledEnumerable, IDisposable + { + private IPooledEnumerator m_Enumerator; + + private static Queue m_InstancePool = new Queue(); + private static int m_Depth = 0; + + public static PooledEnumerable Instantiate( IPooledEnumerator etor ) + { + ++m_Depth; + + if ( m_Depth >= 5 ) + Console.WriteLine( "Warning: Make sure to call .Free() on pooled enumerables." ); + + PooledEnumerable e; + + if ( m_InstancePool.Count > 0 ) + { + e = m_InstancePool.Dequeue(); + e.m_Enumerator = etor; + } + else + { + e = new PooledEnumerable( etor ); + } + + etor.Enumerable = e; + + return e; + } + + private PooledEnumerable( IPooledEnumerator etor ) + { + m_Enumerator = etor; + } + + public IEnumerator GetEnumerator() + { + if ( m_Enumerator == null ) + throw new ObjectDisposedException( "PooledEnumerable", "GetEnumerator() called after Free()" ); + + return m_Enumerator; + } + + public void Free() + { + if ( m_Enumerator != null ) + { + m_InstancePool.Enqueue( this ); + + m_Enumerator.Free(); + m_Enumerator = null; + + --m_Depth; + } + } + + public void Dispose() + { + Free(); + } + } + + private class TypedEnumerator : IPooledEnumerator, IDisposable + { + private IPooledEnumerable m_Enumerable; + + public IPooledEnumerable Enumerable + { + get { return m_Enumerable; } + set { m_Enumerable = value; } + } + + private Map m_Map; + private Rectangle2D m_Bounds; + private SectorEnumerator m_Enumerator; + private SectorEnumeratorType m_Type; + private object m_Current; + + private static Queue m_InstancePool = new Queue(); + + public static TypedEnumerator Instantiate( Map map, Rectangle2D bounds, SectorEnumeratorType type ) + { + TypedEnumerator e; + + if ( m_InstancePool.Count > 0 ) + { + e = m_InstancePool.Dequeue(); + + e.m_Map = map; + e.m_Bounds = bounds; + e.m_Type = type; + + e.Reset(); + } + else + { + e = new TypedEnumerator( map, bounds, type ); + } + + return e; + } + + public void Free() + { + if ( m_Map == null ) + return; + + m_InstancePool.Enqueue( this ); + + m_Map = null; + + if ( m_Enumerator != null ) + { + m_Enumerator.Free(); + m_Enumerator = null; + } + + if ( m_Enumerable != null ) + m_Enumerable.Free(); + } + + public TypedEnumerator( Map map, Rectangle2D bounds, SectorEnumeratorType type ) + { + m_Map = map; + m_Bounds = bounds; + m_Type = type; + + Reset(); + } + + public object Current + { + get + { + return m_Current; + } + } + + public bool MoveNext() + { + while ( true ) + { + if ( m_Enumerator.MoveNext() ) + { + object o = m_Enumerator.Current; + + if ( o is Mobile ) + { + Mobile m = (Mobile) o; + + if ( !m.Deleted && m_Bounds.Contains( m.Location ) ) + { + m_Current = o; + return true; + } + } + else if ( o is Item ) + { + Item item = (Item) o; + + if ( !item.Deleted && item.Parent == null && m_Bounds.Contains( item.Location ) ) + { + m_Current = o; + return true; + } + } + else if ( o is NetState ) + { + Mobile m = ( (NetState) o ).Mobile; + + if ( m != null && !m.Deleted && m_Bounds.Contains( m.Location ) ) + { + m_Current = o; + return true; + } + } + } + else + { + m_Current = null; + + m_Enumerator.Free(); + m_Enumerator = null; + + return false; + } + } + } + + public void Reset() + { + m_Current = null; + + if ( m_Enumerator != null ) + m_Enumerator.Free(); + + m_Enumerator = SectorEnumerator.Instantiate( m_Map, m_Bounds, m_Type );//new SectorEnumerator( m_Map, m_Origin, m_Type, m_Range ); + } + + public void Dispose() + { + Free(); + } + } + + private class MultiTileEnumerator : IPooledEnumerator, IDisposable + { + private IPooledEnumerable m_Enumerable; + + public IPooledEnumerable Enumerable + { + get { return m_Enumerable; } + set { m_Enumerable = value; } + } + + private List m_List; + private Point2D m_Location; + private object m_Current; + private int m_Index; + + private static Queue m_InstancePool = new Queue(); + + public static MultiTileEnumerator Instantiate( Sector sector, Point2D loc ) + { + MultiTileEnumerator e; + + if ( m_InstancePool.Count > 0 ) + { + e = m_InstancePool.Dequeue(); + + e.m_List = sector.Multis; + e.m_Location = loc; + + e.Reset(); + } + else + { + e = new MultiTileEnumerator( sector, loc ); + } + + return e; + } + + private MultiTileEnumerator( Sector sector, Point2D loc ) + { + m_List = sector.Multis; + m_Location = loc; + + Reset(); + } + + public object Current + { + get + { + return m_Current; + } + } + + public bool MoveNext() + { + while ( ++m_Index < m_List.Count ) + { + BaseMulti m = m_List[m_Index]; + + if ( m != null && !m.Deleted ) + { + MultiComponentList list = m.Components; + + int xOffset = m_Location.m_X - ( m.Location.m_X + list.Min.m_X ); + int yOffset = m_Location.m_Y - ( m.Location.m_Y + list.Min.m_Y ); + + if ( xOffset >= 0 && xOffset < list.Width && yOffset >= 0 && yOffset < list.Height ) + { + Tile[] tiles = list.Tiles[xOffset][yOffset]; + + if ( tiles.Length > 0 ) + { + // TODO: How to avoid this copy? + Tile[] copy = new Tile[tiles.Length]; + + for ( int i = 0; i < copy.Length; ++i ) + { + copy[i] = tiles[i]; + copy[i].Z += m.Z; + } + + m_Current = copy; + return true; + } + } + } + } + + return false; + } + + public void Free() + { + if ( m_List == null ) + return; + + m_InstancePool.Enqueue( this ); + + m_List = null; + + if ( m_Enumerable != null ) + m_Enumerable.Free(); + } + + public void Reset() + { + m_Current = null; + m_Index = -1; + } + + public void Dispose() + { + Free(); + } + } + + private class ObjectEnumerator : IPooledEnumerator, IDisposable + { + private IPooledEnumerable m_Enumerable; + + public IPooledEnumerable Enumerable + { + get { return m_Enumerable; } + set { m_Enumerable = value; } + } + + private Map m_Map; + private Rectangle2D m_Bounds; + private SectorEnumerator m_Enumerator; + private int m_Stage; // 0 = items, 1 = mobiles + private object m_Current; + + private static Queue m_InstancePool = new Queue(); + + public static ObjectEnumerator Instantiate( Map map, Rectangle2D bounds ) + { + ObjectEnumerator e; + + if ( m_InstancePool.Count > 0 ) + { + e = m_InstancePool.Dequeue(); + + e.m_Map = map; + e.m_Bounds = bounds; + + e.Reset(); + } + else + { + e = new ObjectEnumerator( map, bounds ); + } + + return e; + } + + public void Free() + { + if ( m_Map == null ) + return; + + m_InstancePool.Enqueue( this ); + + m_Map = null; + + if ( m_Enumerator != null ) + { + m_Enumerator.Free(); + m_Enumerator = null; + } + + if ( m_Enumerable != null ) + m_Enumerable.Free(); + } + + private ObjectEnumerator( Map map, Rectangle2D bounds ) + { + m_Map = map; + m_Bounds = bounds; + + Reset(); + } + + public object Current + { + get + { + return m_Current; + } + } + + public bool MoveNext() + { + while ( true ) + { + if ( m_Enumerator.MoveNext() ) + { + object o = m_Enumerator.Current; + + if ( o is Mobile ) + { + Mobile m = (Mobile) o; + + if ( m_Bounds.Contains( m.Location ) ) + { + m_Current = o; + return true; + } + } + else if ( o is Item ) + { + Item item = (Item) o; + + if ( item.Parent == null && m_Bounds.Contains( item.Location ) ) + { + m_Current = o; + return true; + } + } + } + else if ( m_Stage == 0 ) + { + m_Enumerator.Free(); + m_Enumerator = SectorEnumerator.Instantiate( m_Map, m_Bounds, SectorEnumeratorType.Mobiles ); + + m_Current = null; + m_Stage = 1; + } + else + { + m_Enumerator.Free(); + m_Enumerator = null; + + m_Current = null; + m_Stage = -1; + + return false; + } + } + } + + public void Reset() + { + m_Stage = 0; + + m_Current = null; + + if ( m_Enumerator != null ) + m_Enumerator.Free(); + + m_Enumerator = SectorEnumerator.Instantiate( m_Map, m_Bounds, SectorEnumeratorType.Items ); + } + + public void Dispose() + { + Free(); + } + } + + private class SectorEnumerator : IPooledEnumerator, IDisposable + { + private IPooledEnumerable m_Enumerable; + + public IPooledEnumerable Enumerable + { + get { return m_Enumerable; } + set { m_Enumerable = value; } + } + + private Map m_Map; + private Rectangle2D m_Bounds; + + private int m_xSector, m_ySector; + private int m_xSectorStart, m_ySectorStart; + private int m_xSectorEnd, m_ySectorEnd; + private IList m_CurrentList; + private int m_CurrentIndex; + private SectorEnumeratorType m_Type; + + private static Queue m_InstancePool = new Queue(); + + public static SectorEnumerator Instantiate( Map map, Rectangle2D bounds, SectorEnumeratorType type ) + { + SectorEnumerator e; + + if ( m_InstancePool.Count > 0 ) + { + e = m_InstancePool.Dequeue(); + + e.m_Map = map; + e.m_Bounds = bounds; + e.m_Type = type; + + e.Reset(); + } + else + { + e = new SectorEnumerator( map, bounds, type ); + } + + return e; + } + + public void Free() + { + if ( m_Map == null ) + return; + + m_InstancePool.Enqueue( this ); + + m_Map = null; + + if ( m_Enumerable != null ) + m_Enumerable.Free(); + } + + private SectorEnumerator( Map map, Rectangle2D bounds, SectorEnumeratorType type ) + { + m_Map = map; + m_Bounds = bounds; + m_Type = type; + + Reset(); + } + + private IList GetListForSector( Sector sector ) + { + switch ( m_Type ) + { + case SectorEnumeratorType.Clients: + return sector.Clients; + case SectorEnumeratorType.Mobiles: + return sector.Mobiles; + case SectorEnumeratorType.Items: + return sector.Items; + default: + throw new Exception( "Invalid SectorEnumeratorType" ); + } + } + + public object Current + { + get + { + return m_CurrentList[m_CurrentIndex]; + /*try + { + return m_CurrentList[m_CurrentIndex]; + } + catch + { + Console.WriteLine( "Warning: Object removed during enumeration. May not be recoverable" ); + + m_CurrentIndex = -1; + m_CurrentList = GetListForSector( m_Map.InternalGetSector( m_xSector, m_ySector ) ); + + if ( MoveNext() ) + { + return Current; + } + else + { + throw new Exception( "Object disposed during enumeration. Was not recoverable." ); + } + }*/ + } + } + + public bool MoveNext() + { + while ( true ) + { + ++m_CurrentIndex; + + if ( m_CurrentIndex == m_CurrentList.Count ) + { + ++m_ySector; + + if ( m_ySector > m_ySectorEnd ) + { + m_ySector = m_ySectorStart; + ++m_xSector; + + if ( m_xSector > m_xSectorEnd ) + { + m_CurrentIndex = -1; + m_CurrentList = null; + + return false; + } + } + + m_CurrentIndex = -1; + m_CurrentList = GetListForSector( m_Map.InternalGetSector( m_xSector, m_ySector ) );//m_Map.m_Sectors[m_xSector][m_ySector] ); + } + else + { + return true; + } + } + } + + public void Reset() + { + m_Map.Bound( m_Bounds.Start.m_X, m_Bounds.Start.m_Y, out m_xSectorStart, out m_ySectorStart ); + m_Map.Bound( m_Bounds.End.m_X - 1, m_Bounds.End.m_Y - 1, out m_xSectorEnd, out m_ySectorEnd ); + + m_xSector = m_xSectorStart >>= Map.SectorShift; + m_ySector = m_ySectorStart >>= Map.SectorShift; + + m_xSectorEnd >>= Map.SectorShift; + m_ySectorEnd >>= Map.SectorShift; + + m_CurrentIndex = -1; + m_CurrentList = GetListForSector( m_Map.InternalGetSector( m_xSector, m_ySector ) ); + } + + public void Dispose() + { + Free(); + } + } + + public bool LineOfSight( object from, object dest ) + { + if ( from == dest || ( from is Mobile && ( (Mobile) from ).AccessLevel > AccessLevel.Player ) ) + return true; + else if ( dest is Item && from is Mobile && ( (Item) dest ).RootParent == from ) + return true; + + return LineOfSight( GetPoint( from, true ), GetPoint( dest, false ) ); + } + + public Point3D GetPoint( object o, bool eye ) + { + Point3D p; + + if ( o is Mobile ) + { + p = ( (Mobile) o ).Location; + p.Z += 14;//eye ? 15 : 10; + } + else if ( o is Item ) + { + p = ( (Item) o ).GetWorldLocation(); + p.Z += ( ( (Item) o ).ItemData.Height / 2 ) + 1; + } + else if ( o is Point3D ) + { + p = (Point3D) o; + } + else if ( o is LandTarget ) + { + p = ( (LandTarget) o ).Location; + + int low = 0, avg = 0, top = 0; + GetAverageZ( p.X, p.Y, ref low, ref avg, ref top ); + + p.Z = top + 1; + } + else if ( o is StaticTarget ) + { + StaticTarget st = (StaticTarget) o; + ItemData id = TileData.ItemTable[st.ItemID & 0x3FFF]; + + p = new Point3D( st.X, st.Y, st.Z - id.CalcHeight + ( id.Height / 2 ) + 1 ); + } + else if ( o is IPoint3D ) + { + p = new Point3D( (IPoint3D) o ); + } + else + { + Console.WriteLine( "Warning: Invalid object ({0}) in line of sight", o ); + p = Point3D.Zero; + } + + return p; + } + + public bool LineOfSight( Mobile from, Point3D target ) + { + if ( from.AccessLevel > AccessLevel.Player ) + return true; + + Point3D eye = from.Location; + + eye.Z += 14; + + return LineOfSight( eye, target ); + } + + public bool LineOfSight( Mobile from, Mobile to ) + { + if ( from == to || from.AccessLevel > AccessLevel.Player ) + return true; + + Point3D eye = from.Location; + Point3D target = to.Location; + + eye.Z += 14; + target.Z += 14;//10; + + return LineOfSight( eye, target ); + } + + private static int[] m_InvalidLandTiles = new int[] { 0x244 }; + + public static int[] InvalidLandTiles + { + get { return m_InvalidLandTiles; } + set { m_InvalidLandTiles = value; } + } + + private static Point3DList m_PathList = new Point3DList(); + + private static int m_MaxLOSDistance = 25; + + public static int MaxLOSDistance + { + get { return m_MaxLOSDistance; } + set { m_MaxLOSDistance = value; } + } + + public bool LineOfSight( Point3D org, Point3D dest ) + { + if ( this == Map.Internal ) + return false; + + if ( !Utility.InRange( org, dest, m_MaxLOSDistance ) ) + return false; + + Point3D start = org; + Point3D end = dest; + + if ( org.X > dest.X || ( org.X == dest.X && org.Y > dest.Y ) || ( org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z ) ) + { + Point3D swap = org; + org = dest; + dest = swap; + } + + double rise, run, zslp; + double sq3d; + double x, y, z; + int xd, yd, zd; + int ix, iy, iz; + int height; + bool found; + Point3D p; + Point3DList path = m_PathList; + TileFlag flags; + + if ( org == dest ) + return true; + + if ( path.Count > 0 ) + path.Clear(); + + xd = dest.m_X - org.m_X; + yd = dest.m_Y - org.m_Y; + zd = dest.m_Z - org.m_Z; + zslp = Math.Sqrt( xd * xd + yd * yd ); + if ( zd != 0 ) + sq3d = Math.Sqrt( zslp * zslp + zd * zd ); + else + sq3d = zslp; + + rise = ( (float) yd ) / sq3d; + run = ( (float) xd ) / sq3d; + zslp = ( (float) zd ) / sq3d; + + y = org.m_Y; + z = org.m_Z; + x = org.m_X; + while ( Utility.NumberBetween( x, dest.m_X, org.m_X, 0.5 ) && Utility.NumberBetween( y, dest.m_Y, org.m_Y, 0.5 ) && Utility.NumberBetween( z, dest.m_Z, org.m_Z, 0.5 ) ) + { + ix = (int) Math.Round( x ); + iy = (int) Math.Round( y ); + iz = (int) Math.Round( z ); + if ( path.Count > 0 ) + { + p = path.Last; + + if ( p.m_X != ix || p.m_Y != iy || p.m_Z != iz ) + path.Add( ix, iy, iz ); + } + else + { + path.Add( ix, iy, iz ); + } + x += run; + y += rise; + z += zslp; + } + + if ( path.Count == 0 ) + return true;//<--should never happen, but to be safe. + + p = path.Last; + + if ( p != dest ) + path.Add( dest ); + + Point3D pTop = org, pBottom = dest; + Utility.FixPoints( ref pTop, ref pBottom ); + + int pathCount = path.Count; + + for ( int i = 0; i < pathCount; ++i ) + { + Point3D point = path[i]; + + Tile landTile = Tiles.GetLandTile( point.X, point.Y ); + int landZ = 0, landAvg = 0, landTop = 0; + GetAverageZ( point.m_X, point.m_Y, ref landZ, ref landAvg, ref landTop ); + + if ( landZ <= point.m_Z && landTop >= point.m_Z && ( point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > end.m_Z || landTop < end.m_Z ) && !landTile.Ignored ) + return false; + + /* --Do land tiles need to be checked? There is never land between two people, always statics.-- + Tile landTile = Tiles.GetLandTile( point.X, point.Y ); + if ( landTile.Z-1 >= point.Z && landTile.Z+1 <= point.Z && (TileData.LandTable[landTile.ID & 0x3FFF].Flags & TileFlag.Impassable) != 0 ) + return false; + */ + + Tile[] statics = Tiles.GetStaticTiles( point.m_X, point.m_Y, true ); + + bool contains = false; + int ltID = landTile.ID; + + for ( int j = 0; !contains && j < m_InvalidLandTiles.Length; ++j ) + contains = ( ltID == m_InvalidLandTiles[j] ); + + if ( contains && statics.Length == 0 ) + { + IPooledEnumerable eable = GetItemsInRange( point, 0 ); + + foreach ( Item item in eable ) + { + if ( item.Visible ) + contains = false; + + if ( !contains ) + break; + } + + eable.Free(); + + if ( contains ) + return false; + } + + for ( int j = 0; j < statics.Length; ++j ) + { + Tile t = statics[j]; + + ItemData id = TileData.ItemTable[t.ID & 0x3FFF]; + + flags = id.Flags; + height = id.CalcHeight; + + if ( t.Z <= point.Z && t.Z + height >= point.Z && ( flags & ( TileFlag.Window | TileFlag.NoShoot ) ) != 0 ) + { + if ( point.m_X == end.m_X && point.m_Y == end.m_Y && t.Z <= end.m_Z && t.Z + height >= end.m_Z ) + continue; + + return false; + } + + /*if ( t.Z <= point.Z && t.Z+height >= point.Z && (flags&TileFlag.Window)==0 && (flags&TileFlag.NoShoot)!=0 + && ( (flags&TileFlag.Wall)!=0 || (flags&TileFlag.Roof)!=0 || (((flags&TileFlag.Surface)!=0 && zd != 0)) ) )*/ + /*{ + //Console.WriteLine( "LoS: Blocked by Static \"{0}\" Z:{1} T:{3} P:{2} F:x{4:X}", TileData.ItemTable[t.ID&0x3FFF].Name, t.Z, point, t.Z+height, flags ); + //Console.WriteLine( "if ( {0} && {1} && {2} && ( {3} || {4} || {5} || ({6} && {7} && {8}) ) )", t.Z <= point.Z, t.Z+height >= point.Z, (flags&TileFlag.Window)==0, (flags&TileFlag.Impassable)!=0, (flags&TileFlag.Wall)!=0, (flags&TileFlag.Roof)!=0, (flags&TileFlag.Surface)!=0, t.Z != dest.Z, zd != 0 ) ; + return false; + }*/ + } + } + + Rectangle2D rect = new Rectangle2D( pTop.m_X, pTop.m_Y, ( pBottom.m_X - pTop.m_X ) + 1, ( pBottom.m_Y - pTop.m_Y ) + 1 ); + + IPooledEnumerable area = GetItemsInBounds( rect ); + + foreach ( Item i in area ) + { + if ( !i.Visible ) + continue; + + if ( i.ItemID >= 0x4000 ) + continue; + + ItemData id = i.ItemData; + flags = id.Flags; + + if ( ( flags & ( TileFlag.Window | TileFlag.NoShoot ) ) == 0 ) + continue; + + height = id.CalcHeight; + + found = false; + + int count = path.Count; + + for ( int j = 0; j < count; ++j ) + { + Point3D point = path[j]; + Point3D loc = i.Location; + + //if ( t.Z <= point.Z && t.Z+height >= point.Z && ( height != 0 || ( t.Z == dest.Z && zd != 0 ) ) ) + if ( loc.m_X == point.m_X && loc.m_Y == point.m_Y && + loc.m_Z <= point.m_Z && loc.m_Z + height >= point.m_Z ) + { + if ( loc.m_X == end.m_X && loc.m_Y == end.m_Y && loc.m_Z <= end.m_Z && loc.m_Z + height >= end.m_Z ) + continue; + + found = true; + break; + } + } + + if ( !found ) + continue; + + area.Free(); + return false; + + /*if ( (flags & (TileFlag.Impassable | TileFlag.Surface | TileFlag.Roof)) != 0 ) + + //flags = TileData.ItemTable[i.ItemID&0x3FFF].Flags; + //if ( (flags&TileFlag.Window)==0 && (flags&TileFlag.NoShoot)!=0 && ( (flags&TileFlag.Wall)!=0 || (flags&TileFlag.Roof)!=0 || (((flags&TileFlag.Surface)!=0 && zd != 0)) ) ) + { + //height = TileData.ItemTable[i.ItemID&0x3FFF].Height; + //Console.WriteLine( "LoS: Blocked by ITEM \"{0}\" P:{1} T:{2} F:x{3:X}", TileData.ItemTable[i.ItemID&0x3FFF].Name, i.Location, i.Location.Z+height, flags ); + area.Free(); + return false; + }*/ + } + + area.Free(); + + return true; + } + + public int CompareTo( Map other ) + { + if ( other == null ) + return -1; + + return m_MapID.CompareTo( other.m_MapID ); + } + + public int CompareTo( object other ) + { + if ( other == null || other is Map ) + return this.CompareTo( other ); + + throw new ArgumentException(); + } + } +} \ No newline at end of file diff --git a/Server/Menus/IMenu.cs b/Server/Menus/IMenu.cs new file mode 100644 index 000000000..e1620a7d2 --- /dev/null +++ b/Server/Menus/IMenu.cs @@ -0,0 +1,34 @@ +/*************************************************************************** + * IMenu.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: IMenu.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Menus +{ + public interface IMenu + { + int Serial{ get; } + int EntryLength{ get; } + void SendTo( NetState state ); + void OnCancel( NetState state ); + void OnResponse( NetState state, int index ); + } +} \ No newline at end of file diff --git a/Server/Menus/ItemListMenu.cs b/Server/Menus/ItemListMenu.cs new file mode 100644 index 000000000..60f29cd72 --- /dev/null +++ b/Server/Menus/ItemListMenu.cs @@ -0,0 +1,140 @@ +/*************************************************************************** + * ItemListMenu.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ItemListMenu.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Menus.ItemLists +{ + public class ItemListEntry + { + private string m_Name; + private int m_ItemID; + private int m_Hue; + + public string Name + { + get + { + return m_Name; + } + } + + public int ItemID + { + get + { + return m_ItemID; + } + } + + public int Hue + { + get + { + return m_Hue; + } + } + + public ItemListEntry( string name, int itemID ) : this( name, itemID, 0 ) + { + } + + public ItemListEntry( string name, int itemID, int hue ) + { + m_Name = name; + m_ItemID = itemID; + m_Hue = hue; + } + } + + public class ItemListMenu : IMenu + { + private string m_Question; + private ItemListEntry[] m_Entries; + + private int m_Serial; + private static int m_NextSerial; + + int IMenu.Serial + { + get + { + return m_Serial; + } + } + + int IMenu.EntryLength + { + get + { + return m_Entries.Length; + } + } + + public string Question + { + get + { + return m_Question; + } + } + + public ItemListEntry[] Entries + { + get + { + return m_Entries; + } + set + { + m_Entries = value; + } + } + + public ItemListMenu( string question, ItemListEntry[] entries ) + { + m_Question = question; + m_Entries = entries; + + do + { + m_Serial = m_NextSerial++; + m_Serial &= 0x7FFFFFFF; + } while ( m_Serial == 0 ); + + m_Serial = (int)((uint)m_Serial | 0x80000000); + } + + public virtual void OnCancel( NetState state ) + { + } + + public virtual void OnResponse( NetState state, int index ) + { + } + + public void SendTo( NetState state ) + { + state.AddMenu( this ); + state.Send( new DisplayItemListMenu( this ) ); + } + } +} \ No newline at end of file diff --git a/Server/Menus/QuestionMenu.cs b/Server/Menus/QuestionMenu.cs new file mode 100644 index 000000000..b2232ba51 --- /dev/null +++ b/Server/Menus/QuestionMenu.cs @@ -0,0 +1,96 @@ +/*************************************************************************** + * QuestionMenu.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: QuestionMenu.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Menus.Questions +{ + public class QuestionMenu : IMenu + { + private string m_Question; + private string[] m_Answers; + + private int m_Serial; + private static int m_NextSerial; + + int IMenu.Serial + { + get + { + return m_Serial; + } + } + + int IMenu.EntryLength + { + get + { + return m_Answers.Length; + } + } + + public string Question + { + get + { + return m_Question; + } + set + { + m_Question = value; + } + } + + public string[] Answers + { + get + { + return m_Answers; + } + } + + public QuestionMenu( string question, string[] answers ) + { + m_Question = question; + m_Answers = answers; + + do + { + m_Serial = ++m_NextSerial; + m_Serial &= 0x7FFFFFFF; + } while ( m_Serial == 0 ); + } + + public virtual void OnCancel( NetState state ) + { + } + + public virtual void OnResponse( NetState state, int index ) + { + } + + public void SendTo( NetState state ) + { + state.AddMenu( this ); + state.Send( new DisplayQuestionMenu( this ) ); + } + } +} \ No newline at end of file diff --git a/Server/Mobile.cs b/Server/Mobile.cs new file mode 100644 index 000000000..cfb8c6785 --- /dev/null +++ b/Server/Mobile.cs @@ -0,0 +1,10958 @@ +/*************************************************************************** + * Mobile.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Mobile.cs 150 2006-06-11 02:38:48Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Server; +using Server.Accounting; +using Server.Commands; +using Server.ContextMenus; +using Server.Guilds; +using Server.Gumps; +using Server.HuePickers; +using Server.Items; +using Server.Menus; +using Server.Mobiles; +using Server.Network; +using Server.Prompts; +using Server.Targeting; + +namespace Server +{ + #region Callbacks + public delegate void TargetCallback( Mobile from, object targeted ); + public delegate void TargetStateCallback( Mobile from, object targeted, object state ); + + public delegate void PromptCallback( Mobile from, string text ); + public delegate void PromptStateCallback( Mobile from, string text, object state ); + #endregion + + #region [...]Mods + public class TimedSkillMod : SkillMod + { + private DateTime m_Expire; + + public TimedSkillMod( SkillName skill, bool relative, double value, TimeSpan delay ) + : this( skill, relative, value, DateTime.Now + delay ) + { + } + + public TimedSkillMod( SkillName skill, bool relative, double value, DateTime expire ) + : base( skill, relative, value ) + { + m_Expire = expire; + } + + public override bool CheckCondition() + { + return ( DateTime.Now < m_Expire ); + } + } + + public class EquipedSkillMod : SkillMod + { + private Item m_Item; + private Mobile m_Mobile; + + public EquipedSkillMod( SkillName skill, bool relative, double value, Item item, Mobile mobile ) + : base( skill, relative, value ) + { + m_Item = item; + m_Mobile = mobile; + } + + public override bool CheckCondition() + { + return ( !m_Item.Deleted && !m_Mobile.Deleted && m_Item.Parent == m_Mobile ); + } + } + + public class DefaultSkillMod : SkillMod + { + public DefaultSkillMod( SkillName skill, bool relative, double value ) + : base( skill, relative, value ) + { + } + + public override bool CheckCondition() + { + return true; + } + } + + public abstract class SkillMod + { + private Mobile m_Owner; + private SkillName m_Skill; + private bool m_Relative; + private double m_Value; + private bool m_ObeyCap; + + public SkillMod( SkillName skill, bool relative, double value ) + { + m_Skill = skill; + m_Relative = relative; + m_Value = value; + } + + public bool ObeyCap + { + get { return m_ObeyCap; } + set + { + m_ObeyCap = value; + + if ( m_Owner != null ) + { + Skill sk = m_Owner.Skills[m_Skill]; + + if ( sk != null ) + sk.Update(); + } + } + } + + public Mobile Owner + { + get + { + return m_Owner; + } + set + { + if ( m_Owner != value ) + { + if ( m_Owner != null ) + m_Owner.RemoveSkillMod( this ); + + m_Owner = value; + + if ( m_Owner != value ) + m_Owner.AddSkillMod( this ); + } + } + } + + public void Remove() + { + Owner = null; + } + + public SkillName Skill + { + get + { + return m_Skill; + } + set + { + if ( m_Skill != value ) + { + Skill oldUpdate = ( m_Owner == null ? m_Owner.Skills[m_Skill] : null ); + + m_Skill = value; + + if ( m_Owner != null ) + { + Skill sk = m_Owner.Skills[m_Skill]; + + if ( sk != null ) + sk.Update(); + } + + if ( oldUpdate != null ) + oldUpdate.Update(); + } + } + } + + public bool Relative + { + get + { + return m_Relative; + } + set + { + if ( m_Relative != value ) + { + m_Relative = value; + + if ( m_Owner != null ) + { + Skill sk = m_Owner.Skills[m_Skill]; + + if ( sk != null ) + sk.Update(); + } + } + } + } + + public bool Absolute + { + get + { + return !m_Relative; + } + set + { + if ( m_Relative == value ) + { + m_Relative = !value; + + if ( m_Owner != null ) + { + Skill sk = m_Owner.Skills[m_Skill]; + + if ( sk != null ) + sk.Update(); + } + } + } + } + + public double Value + { + get + { + return m_Value; + } + set + { + if ( m_Value != value ) + { + m_Value = value; + + if ( m_Owner != null ) + { + Skill sk = m_Owner.Skills[m_Skill]; + + if ( sk != null ) + sk.Update(); + } + } + } + } + + public abstract bool CheckCondition(); + } + + public class ResistanceMod + { + private Mobile m_Owner; + private ResistanceType m_Type; + private int m_Offset; + + public Mobile Owner + { + get { return m_Owner; } + set { m_Owner = value; } + } + + public ResistanceType Type + { + get { return m_Type; } + set + { + if ( m_Type != value ) + { + m_Type = value; + + if ( m_Owner != null ) + m_Owner.UpdateResistances(); + } + } + } + + public int Offset + { + get { return m_Offset; } + set + { + if ( m_Offset != value ) + { + m_Offset = value; + + if ( m_Owner != null ) + m_Owner.UpdateResistances(); + } + } + } + + public ResistanceMod( ResistanceType type, int offset ) + { + m_Type = type; + m_Offset = offset; + } + } + + public class StatMod + { + private StatType m_Type; + private string m_Name; + private int m_Offset; + private TimeSpan m_Duration; + private DateTime m_Added; + + public StatType Type { get { return m_Type; } } + public string Name { get { return m_Name; } } + public int Offset { get { return m_Offset; } } + + public bool HasElapsed() + { + if ( m_Duration == TimeSpan.Zero ) + return false; + + return ( DateTime.Now - m_Added ) >= m_Duration; + } + + public StatMod( StatType type, string name, int offset, TimeSpan duration ) + { + m_Type = type; + m_Name = name; + m_Offset = offset; + m_Duration = duration; + m_Added = DateTime.Now; + } + } + + #endregion + + public class DamageEntry + { + private Mobile m_Damager; + private int m_DamageGiven; + private DateTime m_LastDamage; + private List m_Responsible; + + public Mobile Damager { get { return m_Damager; } } + public int DamageGiven { get { return m_DamageGiven; } set { m_DamageGiven = value; } } + public DateTime LastDamage { get { return m_LastDamage; } set { m_LastDamage = value; } } + public bool HasExpired { get { return (DateTime.Now > (m_LastDamage + m_ExpireDelay)); } } + public List Responsible { get { return m_Responsible; } set { m_Responsible = value; } } + + private static TimeSpan m_ExpireDelay = TimeSpan.FromMinutes( 2.0 ); + + public static TimeSpan ExpireDelay + { + get { return m_ExpireDelay; } + set { m_ExpireDelay = value; } + } + + public DamageEntry( Mobile damager ) + { + m_Damager = damager; + } + } + + #region Enums + [Flags] + public enum StatType + { + Str=1, + Dex=2, + Int=4, + All=7 + } + + public enum StatLockType : byte + { + Up, + Down, + Locked + } + + [CustomEnum( new string[] { "North", "Right", "East", "Down", "South", "Left", "West", "Up" } )] + public enum Direction : byte + { + North = 0x0, + Right = 0x1, + East = 0x2, + Down = 0x3, + South = 0x4, + Left = 0x5, + West = 0x6, + Up = 0x7, + + Mask = 0x7, + Running = 0x80, + ValueMask = 0x87 + } + + [Flags] + public enum MobileDelta + { + None = 0x00000000, + Name = 0x00000001, + Flags = 0x00000002, + Hits = 0x00000004, + Mana = 0x00000008, + Stam = 0x00000010, + Stat = 0x00000020, + Noto = 0x00000040, + Gold = 0x00000080, + Weight = 0x00000100, + Direction = 0x00000200, + Hue = 0x00000400, + Body = 0x00000800, + Armor = 0x00001000, + StatCap = 0x00002000, + GhostUpdate = 0x00004000, + Followers = 0x00008000, + Properties = 0x00010000, + TithingPoints = 0x00020000, + Resistances = 0x00040000, + WeaponDamage = 0x00080000, + Hair = 0x00100000, + FacialHair = 0x00200000, + Race = 0x00400000, + + Attributes = 0x0000001C + } + + public enum AccessLevel + { + Player, + Counselor, + GameMaster, + Seer, + Administrator, + Developer, + Owner + } + + public enum VisibleDamageType + { + None, + Related, + Everyone + } + + public enum ResistanceType + { + Physical, + Fire, + Cold, + Poison, + Energy + } + + public enum ApplyPoisonResult + { + Poisoned, + Immune, + HigherPoisonActive, + Cured + } + #endregion + + public class MobileNotConnectedException : Exception + { + public MobileNotConnectedException( Mobile source, string message ) : base( message ) + { + this.Source = source.ToString(); + } + } + + #region Delegates + + public delegate bool SkillCheckTargetHandler( Mobile from, SkillName skill, object target, double minSkill, double maxSkill ); + public delegate bool SkillCheckLocationHandler( Mobile from, SkillName skill, double minSkill, double maxSkill ); + + public delegate bool SkillCheckDirectTargetHandler( Mobile from, SkillName skill, object target, double chance ); + public delegate bool SkillCheckDirectLocationHandler( Mobile from, SkillName skill, double chance ); + + public delegate TimeSpan RegenRateHandler( Mobile from ); + + public delegate bool AllowBeneficialHandler( Mobile from, Mobile target ); + public delegate bool AllowHarmfulHandler( Mobile from, Mobile target ); + + public delegate Container CreateCorpseHandler( Mobile from, HairInfo hair, FacialHairInfo facialhair, List initialContent, List equipedItems ); + + #endregion + + /// + /// Base class representing players, npcs, and creatures. + /// + public class Mobile : IEntity, IPoint3D, IHued, IComparable, IComparable, ISerializable + { + public int CompareTo( IEntity other ) + { + if ( other == null ) + return -1; + + return m_Serial.CompareTo( other.Serial ); + } + + public int CompareTo( Mobile other ) + { + return this.CompareTo( (IEntity) other ); + } + + public int CompareTo( object other ) + { + if ( other == null || other is IEntity ) + return this.CompareTo( (IEntity) other ); + + throw new ArgumentException(); + } + + private static bool m_DragEffects = true; + + public static bool DragEffects + { + get { return m_DragEffects; } + set { m_DragEffects = value; } + } + + #region Handlers + + private static AllowBeneficialHandler m_AllowBeneficialHandler; + private static AllowHarmfulHandler m_AllowHarmfulHandler; + + public static AllowBeneficialHandler AllowBeneficialHandler + { + get { return m_AllowBeneficialHandler; } + set { m_AllowBeneficialHandler = value; } + } + + public static AllowHarmfulHandler AllowHarmfulHandler + { + get { return m_AllowHarmfulHandler; } + set { m_AllowHarmfulHandler = value; } + } + + private static SkillCheckTargetHandler m_SkillCheckTargetHandler; + private static SkillCheckLocationHandler m_SkillCheckLocationHandler; + private static SkillCheckDirectTargetHandler m_SkillCheckDirectTargetHandler; + private static SkillCheckDirectLocationHandler m_SkillCheckDirectLocationHandler; + + public static SkillCheckTargetHandler SkillCheckTargetHandler + { + get { return m_SkillCheckTargetHandler; } + set { m_SkillCheckTargetHandler = value; } + } + + public static SkillCheckLocationHandler SkillCheckLocationHandler + { + get { return m_SkillCheckLocationHandler; } + set { m_SkillCheckLocationHandler = value; } + } + + public static SkillCheckDirectTargetHandler SkillCheckDirectTargetHandler + { + get { return m_SkillCheckDirectTargetHandler; } + set { m_SkillCheckDirectTargetHandler = value; } + } + + public static SkillCheckDirectLocationHandler SkillCheckDirectLocationHandler + { + get { return m_SkillCheckDirectLocationHandler; } + set { m_SkillCheckDirectLocationHandler = value; } + } + + #endregion + + #region Regeneration + + private static RegenRateHandler m_HitsRegenRate, m_StamRegenRate, m_ManaRegenRate; + private static TimeSpan m_DefaultHitsRate, m_DefaultStamRate, m_DefaultManaRate; + + public static RegenRateHandler HitsRegenRateHandler + { + get { return m_HitsRegenRate; } + set { m_HitsRegenRate = value; } + } + + public static TimeSpan DefaultHitsRate + { + get { return m_DefaultHitsRate; } + set { m_DefaultHitsRate = value; } + } + + public static RegenRateHandler StamRegenRateHandler + { + get { return m_StamRegenRate; } + set { m_StamRegenRate = value; } + } + + public static TimeSpan DefaultStamRate + { + get { return m_DefaultStamRate; } + set { m_DefaultStamRate = value; } + } + + public static RegenRateHandler ManaRegenRateHandler + { + get { return m_ManaRegenRate; } + set { m_ManaRegenRate = value; } + } + + public static TimeSpan DefaultManaRate + { + get { return m_DefaultManaRate; } + set { m_DefaultManaRate = value; } + } + + public static TimeSpan GetHitsRegenRate( Mobile m ) + { + if ( m_HitsRegenRate == null ) + return m_DefaultHitsRate; + else + return m_HitsRegenRate( m ); + } + + public static TimeSpan GetStamRegenRate( Mobile m ) + { + if ( m_StamRegenRate == null ) + return m_DefaultStamRate; + else + return m_StamRegenRate( m ); + } + + public static TimeSpan GetManaRegenRate( Mobile m ) + { + if ( m_ManaRegenRate == null ) + return m_DefaultManaRate; + else + return m_ManaRegenRate( m ); + } + + #endregion + + private class MovementRecord + { + public DateTime m_End; + + private static Queue m_InstancePool = new Queue(); + + public static MovementRecord NewInstance( DateTime end ) + { + MovementRecord r; + + if ( m_InstancePool.Count > 0 ) + { + r = m_InstancePool.Dequeue(); + + r.m_End = end; + } + else + { + r = new MovementRecord( end ); + } + + return r; + } + + private MovementRecord( DateTime end ) + { + m_End = end; + } + + public bool Expired() + { + bool v = ( DateTime.Now >= m_End ); + + if ( v ) + m_InstancePool.Enqueue( this ); + + return v; + } + } + + #region Var declarations + private Serial m_Serial; + private Map m_Map; + private Point3D m_Location; + private Direction m_Direction; + private Body m_Body; + private int m_Hue; + private Poison m_Poison; + private Timer m_PoisonTimer; + private BaseGuild m_Guild; + private string m_GuildTitle; + private bool m_Criminal; + private string m_Name; + private int m_Kills, m_ShortTermMurders; + private int m_SpeechHue, m_EmoteHue, m_WhisperHue, m_YellHue; + private string m_Language; + private NetState m_NetState; + private bool m_Female, m_Warmode, m_Hidden, m_Blessed; + private int m_StatCap; + private int m_Str, m_Dex, m_Int; + private int m_Hits, m_Stam, m_Mana; + private int m_Fame, m_Karma; + private AccessLevel m_AccessLevel; + private Skills m_Skills; + private List m_Items; + private bool m_Player; + private string m_Title; + private string m_Profile; + private bool m_ProfileLocked; + private int m_LightLevel; + private int m_TotalGold, m_TotalItems, m_TotalWeight; + private List m_StatMods; + private ISpell m_Spell; + private Target m_Target; + private Prompt m_Prompt; + private ContextMenu m_ContextMenu; + private List m_Aggressors, m_Aggressed; + private Mobile m_Combatant; + private List m_Stabled; + private bool m_AutoPageNotify; + private bool m_Meditating; + private bool m_CanHearGhosts; + private bool m_CanSwim, m_CantWalk; + private int m_TithingPoints; + private bool m_DisplayGuildTitle; + private Mobile m_GuildFealty; + private DateTime m_NextSpellTime; + private DateTime[] m_StuckMenuUses; + private Timer m_ExpireCombatant; + private Timer m_ExpireCriminal; + private Timer m_ExpireAggrTimer; + private Timer m_LogoutTimer; + private Timer m_CombatTimer; + private Timer m_ManaTimer, m_HitsTimer, m_StamTimer; + private DateTime m_NextSkillTime; + private DateTime m_NextActionTime; + private DateTime m_NextActionMessage; + private bool m_Paralyzed; + private ParalyzedTimer m_ParaTimer; + private bool m_Frozen; + private FrozenTimer m_FrozenTimer; + private int m_AllowedStealthSteps; + private int m_Hunger; + private int m_NameHue = -1; + private Region m_Region; + private bool m_DisarmReady, m_StunReady; + private int m_BaseSoundID; + private int m_VirtualArmor; + private bool m_Squelched; + private int m_MeleeDamageAbsorb; + private int m_MagicDamageAbsorb; + private int m_Followers, m_FollowersMax; + private ArrayList m_Actions; + private Queue m_MoveRecords; + private int m_WarmodeChanges = 0; + private DateTime m_NextWarmodeChange; + private WarmodeTimer m_WarmodeTimer; + private int m_Thirst, m_BAC; + private int m_VirtualArmorMod; + private VirtueInfo m_Virtues; + private object m_Party; + private List m_SkillMods; + private Body m_BodyMod; + private DateTime m_LastStrGain; + private DateTime m_LastIntGain; + private DateTime m_LastDexGain; + private Race m_Race; + + #endregion + + private static TimeSpan WarmodeSpamCatch = TimeSpan.FromSeconds( 0.5 ); + private static TimeSpan WarmodeSpamDelay = TimeSpan.FromSeconds( 2.0 ); + private const int WarmodeCatchCount = 4; // Allow four warmode changes in 0.5 seconds, any more will be delay for two seconds + + [CommandProperty( AccessLevel.GameMaster )] + public Race Race + { + get + { + if( m_Race == null ) + m_Race = Race.DefaultRace; + + return m_Race; + } + set + { + Race oldRace = this.Race; + + m_Race = value; + + if( m_Race == null ) + m_Race = Race.DefaultRace; + + this.Body = m_Race.Body( this ); + this.UpdateResistances(); + + Delta( MobileDelta.Race ); + + OnRaceChange( oldRace ); + } + } + + protected virtual void OnRaceChange( Race oldRace ) + { + } + + public virtual double RacialSkillBonus { get { return 0; } } + + private List m_ResistMods; + + private int[] m_Resistances; + + public int[] Resistances { get { return m_Resistances; } } + + public virtual int BasePhysicalResistance { get { return 0; } } + public virtual int BaseFireResistance { get { return 0; } } + public virtual int BaseColdResistance { get { return 0; } } + public virtual int BasePoisonResistance { get { return 0; } } + public virtual int BaseEnergyResistance { get { return 0; } } + + public virtual void ComputeLightLevels( out int global, out int personal ) + { + ComputeBaseLightLevels( out global, out personal ); + + if ( m_Region != null ) + m_Region.AlterLightLevel( this, ref global, ref personal ); + } + + public virtual void ComputeBaseLightLevels( out int global, out int personal ) + { + global = 0; + personal = m_LightLevel; + } + + public virtual void CheckLightLevels( bool forceResend ) + { + } + + [CommandProperty( AccessLevel.Counselor )] + public virtual int PhysicalResistance + { + get { return GetResistance( ResistanceType.Physical ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public virtual int FireResistance + { + get { return GetResistance( ResistanceType.Fire ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public virtual int ColdResistance + { + get { return GetResistance( ResistanceType.Cold ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public virtual int PoisonResistance + { + get { return GetResistance( ResistanceType.Poison ); } + } + + [CommandProperty( AccessLevel.Counselor )] + public virtual int EnergyResistance + { + get { return GetResistance( ResistanceType.Energy ); } + } + + public virtual void UpdateResistances() + { + if ( m_Resistances == null ) + m_Resistances = new int[5] { int.MinValue, int.MinValue, int.MinValue, int.MinValue, int.MinValue }; + + bool delta = false; + + for ( int i = 0; i < m_Resistances.Length; ++i ) + { + if ( m_Resistances[i] != int.MinValue ) + { + m_Resistances[i] = int.MinValue; + delta = true; + } + } + + if ( delta ) + Delta( MobileDelta.Resistances ); + } + + public virtual int GetResistance( ResistanceType type ) + { + if ( m_Resistances == null ) + m_Resistances = new int[5] { int.MinValue, int.MinValue, int.MinValue, int.MinValue, int.MinValue }; + + int v = (int) type; + + if ( v < 0 || v >= m_Resistances.Length ) + return 0; + + int res = m_Resistances[v]; + + if ( res == int.MinValue ) + { + ComputeResistances(); + res = m_Resistances[v]; + } + + return res; + } + + public List ResistanceMods + { + get { return m_ResistMods; } + set { m_ResistMods = value; } + } + + public virtual void AddResistanceMod( ResistanceMod toAdd ) + { + if ( m_ResistMods == null ) + m_ResistMods = new List( 2 ); + + m_ResistMods.Add( toAdd ); + UpdateResistances(); + } + + public virtual void RemoveResistanceMod( ResistanceMod toRemove ) + { + if ( m_ResistMods != null ) + { + m_ResistMods.Remove( toRemove ); + + if ( m_ResistMods.Count == 0 ) + m_ResistMods = null; + } + + UpdateResistances(); + } + + private static int m_MaxPlayerResistance = 70; + + public static int MaxPlayerResistance { get { return m_MaxPlayerResistance; } set { m_MaxPlayerResistance = value; } } + + public virtual void ComputeResistances() + { + if ( m_Resistances == null ) + m_Resistances = new int[5] { int.MinValue, int.MinValue, int.MinValue, int.MinValue, int.MinValue }; + + for ( int i = 0; i < m_Resistances.Length; ++i ) + m_Resistances[i] = 0; + + m_Resistances[0] += this.BasePhysicalResistance; + m_Resistances[1] += this.BaseFireResistance; + m_Resistances[2] += this.BaseColdResistance; + m_Resistances[3] += this.BasePoisonResistance; + m_Resistances[4] += this.BaseEnergyResistance; + + for ( int i = 0; m_ResistMods != null && i < m_ResistMods.Count; ++i ) + { + ResistanceMod mod = m_ResistMods[i]; + int v = (int) mod.Type; + + if ( v >= 0 && v < m_Resistances.Length ) + m_Resistances[v] += mod.Offset; + } + + for ( int i = 0; i < m_Items.Count; ++i ) + { + Item item = m_Items[i]; + + if ( item.CheckPropertyConfliction( this ) ) + continue; + + m_Resistances[0] += item.PhysicalResistance; + m_Resistances[1] += item.FireResistance; + m_Resistances[2] += item.ColdResistance; + m_Resistances[3] += item.PoisonResistance; + m_Resistances[4] += item.EnergyResistance; + } + + for ( int i = 0; i < m_Resistances.Length; ++i ) + { + int min = GetMinResistance( (ResistanceType) i ); + int max = GetMaxResistance( (ResistanceType) i ); + + if ( max < min ) + max = min; + + if ( m_Resistances[i] > max ) + m_Resistances[i] = max; + else if ( m_Resistances[i] < min ) + m_Resistances[i] = min; + } + } + + public virtual int GetMinResistance( ResistanceType type ) + { + return int.MinValue; + } + + public virtual int GetMaxResistance( ResistanceType type ) + { + if ( m_Player ) + return m_MaxPlayerResistance; + + return int.MaxValue; + } + + public virtual void SendPropertiesTo( Mobile from ) + { + from.Send( PropertyList ); + } + + public virtual void OnAosSingleClick( Mobile from ) + { + ObjectPropertyList opl = this.PropertyList; + + if ( opl.Header > 0 ) + { + int hue; + + if ( m_NameHue != -1 ) + hue = m_NameHue; + else if ( m_AccessLevel > AccessLevel.Player ) + hue = 11; + else + hue = Notoriety.GetHue( Notoriety.Compute( from, this ) ); + + from.Send( new MessageLocalized( m_Serial, Body, MessageType.Label, hue, 3, opl.Header, Name, opl.HeaderArgs ) ); + } + } + + public virtual string ApplyNameSuffix( string suffix ) + { + return suffix; + } + + public virtual void AddNameProperties( ObjectPropertyList list ) + { + string name = Name; + + if ( name == null ) + name = String.Empty; + + string prefix = ""; + + if ( ShowFameTitle && ( m_Player || m_Body.IsHuman ) && m_Fame >= 10000 ) + prefix = m_Female ? "Lady" : "Lord"; + + string suffix = ""; + + if ( PropertyTitle && Title != null && Title.Length > 0 ) + suffix = Title; + + BaseGuild guild = m_Guild; + + if ( guild != null && ( m_Player || m_DisplayGuildTitle ) ) + { + if ( suffix.Length > 0 ) + suffix = String.Format( "{0} [{1}]", suffix, Utility.FixHtml( guild.Abbreviation ) ); + else + suffix = String.Format( "[{0}]", Utility.FixHtml( guild.Abbreviation ) ); + } + + suffix = ApplyNameSuffix( suffix ); + + list.Add( 1050045, "{0} \t{1}\t {2}", prefix, name, suffix ); // ~1_PREFIX~~2_NAME~~3_SUFFIX~ + + if ( guild != null && ( m_DisplayGuildTitle || ( m_Player && guild.Type != GuildType.Regular ) ) ) + { + string type; + + if ( guild.Type >= 0 && (int) guild.Type < m_GuildTypes.Length ) + type = m_GuildTypes[(int) guild.Type]; + else + type = ""; + + string title = GuildTitle; + + if ( title == null ) + title = ""; + else + title = title.Trim(); + + if ( title.Length > 0 ) + list.Add( "{0}, {1} Guild{2}", Utility.FixHtml( title ), Utility.FixHtml( guild.Name ), type ); + else + list.Add( Utility.FixHtml( guild.Name ) ); + } + } + + public virtual void GetProperties( ObjectPropertyList list ) + { + AddNameProperties( list ); + } + + public virtual void GetChildProperties( ObjectPropertyList list, Item item ) + { + } + + public virtual void GetChildNameProperties( ObjectPropertyList list, Item item ) + { + } + + private void UpdateAggrExpire() + { + if ( m_Deleted || ( m_Aggressors.Count == 0 && m_Aggressed.Count == 0 ) ) + { + StopAggrExpire(); + } + else if ( m_ExpireAggrTimer == null ) + { + m_ExpireAggrTimer = new ExpireAggressorsTimer( this ); + m_ExpireAggrTimer.Start(); + } + } + + private void StopAggrExpire() + { + if ( m_ExpireAggrTimer != null ) + m_ExpireAggrTimer.Stop(); + + m_ExpireAggrTimer = null; + } + + private void CheckAggrExpire() + { + for ( int i = m_Aggressors.Count - 1; i >= 0; --i ) + { + if ( i >= m_Aggressors.Count ) + continue; + + AggressorInfo info = m_Aggressors[i]; + + if ( info.Expired ) + { + Mobile attacker = info.Attacker; + attacker.RemoveAggressed( this ); + + m_Aggressors.RemoveAt( i ); + info.Free(); + + if ( m_NetState != null && this.CanSee( attacker ) && Utility.InUpdateRange( m_Location, attacker.m_Location ) ) + m_NetState.Send( new MobileIncoming( this, attacker ) ); + } + } + + for ( int i = m_Aggressed.Count - 1; i >= 0; --i ) + { + if ( i >= m_Aggressed.Count ) + continue; + + AggressorInfo info = m_Aggressed[i]; + + if ( info.Expired ) + { + Mobile defender = info.Defender; + defender.RemoveAggressor( this ); + + m_Aggressed.RemoveAt( i ); + info.Free(); + + if ( m_NetState != null && this.CanSee( defender ) && Utility.InUpdateRange( m_Location, defender.m_Location ) ) + m_NetState.Send( new MobileIncoming( this, defender ) ); + } + } + + UpdateAggrExpire(); + } + + public List Stabled { get { return m_Stabled; } set { m_Stabled = value; } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public VirtueInfo Virtues { get { return m_Virtues; } set { } } + + public object Party { get { return m_Party; } set { m_Party = value; } } + public List SkillMods { get { return m_SkillMods; } } + + [CommandProperty( AccessLevel.GameMaster )] + public int VirtualArmorMod + { + get + { + return m_VirtualArmorMod; + } + set + { + if ( m_VirtualArmorMod != value ) + { + m_VirtualArmorMod = value; + + Delta( MobileDelta.Armor ); + } + } + } + + /// + /// Overridable. Virtual event invoked when changes in some way. + /// + public virtual void OnSkillInvalidated( Skill skill ) + { + } + + public virtual void UpdateSkillMods() + { + ValidateSkillMods(); + + for ( int i = 0; i < m_SkillMods.Count; ++i ) + { + SkillMod mod = m_SkillMods[i]; + + Skill sk = m_Skills[mod.Skill]; + + if ( sk != null ) + sk.Update(); + } + } + + public virtual void ValidateSkillMods() + { + for ( int i = 0; i < m_SkillMods.Count; ) + { + SkillMod mod = m_SkillMods[i]; + + if ( mod.CheckCondition() ) + ++i; + else + InternalRemoveSkillMod( mod ); + } + } + + public virtual void AddSkillMod( SkillMod mod ) + { + if ( mod == null ) + return; + + ValidateSkillMods(); + + if ( !m_SkillMods.Contains( mod ) ) + { + m_SkillMods.Add( mod ); + mod.Owner = this; + + Skill sk = m_Skills[mod.Skill]; + + if ( sk != null ) + sk.Update(); + } + } + + public virtual void RemoveSkillMod( SkillMod mod ) + { + if ( mod == null ) + return; + + ValidateSkillMods(); + + InternalRemoveSkillMod( mod ); + } + + private void InternalRemoveSkillMod( SkillMod mod ) + { + if ( m_SkillMods.Contains( mod ) ) + { + m_SkillMods.Remove( mod ); + mod.Owner = null; + + Skill sk = m_Skills[mod.Skill]; + + if ( sk != null ) + sk.Update(); + } + } + + private class WarmodeTimer : Timer + { + private Mobile m_Mobile; + private bool m_Value; + + public bool Value + { + get + { + return m_Value; + } + set + { + m_Value = value; + } + } + + public WarmodeTimer( Mobile m, bool value ) + : base( WarmodeSpamDelay ) + { + m_Mobile = m; + m_Value = value; + } + + protected override void OnTick() + { + m_Mobile.Warmode = m_Value; + m_Mobile.m_WarmodeChanges = 0; + + m_Mobile.m_WarmodeTimer = null; + } + } + + /// + /// Overridable. Virtual event invoked when a client, , invokes a 'help request' for the Mobile. Seemingly no longer functional in newer clients. + /// + public virtual void OnHelpRequest( Mobile from ) + { + } + + public void DelayChangeWarmode( bool value ) + { + if ( m_WarmodeTimer != null ) + { + m_WarmodeTimer.Value = value; + return; + } + + if ( m_Warmode == value ) + return; + + DateTime now = DateTime.Now, next = m_NextWarmodeChange; + + if ( now > next || m_WarmodeChanges == 0 ) + { + m_WarmodeChanges = 1; + m_NextWarmodeChange = now + WarmodeSpamCatch; + } + else if ( m_WarmodeChanges == WarmodeCatchCount ) + { + m_WarmodeTimer = new WarmodeTimer( this, value ); + m_WarmodeTimer.Start(); + + return; + } + else + { + ++m_WarmodeChanges; + } + + Warmode = value; + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MeleeDamageAbsorb + { + get + { + return m_MeleeDamageAbsorb; + } + set + { + m_MeleeDamageAbsorb = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int MagicDamageAbsorb + { + get + { + return m_MagicDamageAbsorb; + } + set + { + m_MagicDamageAbsorb = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int SkillsTotal + { + get + { + return m_Skills == null ? 0 : m_Skills.Total; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int SkillsCap + { + get + { + return m_Skills == null ? 0 : m_Skills.Cap; + } + set + { + if ( m_Skills != null ) + m_Skills.Cap = value; + } + } + + public bool InLOS( Mobile target ) + { + if ( m_Deleted || m_Map == null ) + return false; + else if ( target == this || m_AccessLevel > AccessLevel.Player ) + return true; + + return m_Map.LineOfSight( this, target ); + } + + public bool InLOS( object target ) + { + if ( m_Deleted || m_Map == null ) + return false; + else if ( target == this || m_AccessLevel > AccessLevel.Player ) + return true; + else if ( target is Item && ( (Item) target ).RootParent == this ) + return true; + + return m_Map.LineOfSight( this, target ); + } + + public bool InLOS( Point3D target ) + { + if ( m_Deleted || m_Map == null ) + return false; + else if ( m_AccessLevel > AccessLevel.Player ) + return true; + + return m_Map.LineOfSight( this, target ); + } + + [CommandProperty( AccessLevel.GameMaster )] + public int BaseSoundID + { + get + { + return m_BaseSoundID; + } + set + { + m_BaseSoundID = value; + } + } + + public DateTime NextCombatTime + { + get + { + return m_NextCombatTime; + } + set + { + m_NextCombatTime = value; + } + } + + public bool BeginAction( object toLock ) + { + if ( m_Actions == null ) + { + m_Actions = new ArrayList( 2 ); + + m_Actions.Add( toLock ); + + return true; + } + else if ( !m_Actions.Contains( toLock ) ) + { + m_Actions.Add( toLock ); + + return true; + } + + return false; + } + + public bool CanBeginAction( object toLock ) + { + return ( m_Actions == null || !m_Actions.Contains( toLock ) ); + } + + public void EndAction( object toLock ) + { + if ( m_Actions != null ) + { + m_Actions.Remove( toLock ); + + if ( m_Actions.Count == 0 ) + m_Actions = null; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int NameHue + { + get + { + return m_NameHue; + } + set + { + m_NameHue = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Hunger + { + get + { + return m_Hunger; + } + set + { + int oldValue = m_Hunger; + + if ( oldValue != value ) + { + m_Hunger = value; + + EventSink.InvokeHungerChanged( new HungerChangedEventArgs( this, oldValue ) ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Thirst + { + get + { + return m_Thirst; + } + set + { + m_Thirst = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int BAC + { + get + { + return m_BAC; + } + set + { + m_BAC = value; + } + } + + private DateTime m_LastMoveTime; + + /// + /// Gets or sets the number of steps this player may take when hidden before being revealed. + /// + [CommandProperty( AccessLevel.GameMaster )] + public int AllowedStealthSteps + { + get + { + return m_AllowedStealthSteps; + } + set + { + m_AllowedStealthSteps = value; + } + } + + /* Logout: + * + * When a client logs into mobile x + * - if ( x is Internalized ) move x to logout location and map + * + * When a client attached to a mobile disconnects + * - LogoutTimer is started + * - Delay is taken from Region.GetLogoutDelay to allow insta-logout regions. + * - OnTick : Location and map are stored, and mobile is internalized + * + * Some things to consider: + * - An internalized person getting killed (say, by poison). Where does the body go? + * - Regions now have a GetLogoutDelay( Mobile m ); virtual function (see above) + */ + private Point3D m_LogoutLocation; + private Map m_LogoutMap; + + public virtual TimeSpan GetLogoutDelay() + { + return Region.GetLogoutDelay( this ); + } + + private StatLockType m_StrLock, m_DexLock, m_IntLock; + + private Item m_Holding; + + public Item Holding + { + get + { + return m_Holding; + } + set + { + if ( m_Holding != value ) + { + if ( m_Holding != null ) + { + UpdateTotal( m_Holding, TotalType.Weight, -( m_Holding.TotalWeight + m_Holding.PileWeight ) ); + + if ( m_Holding.HeldBy == this ) + m_Holding.HeldBy = null; + } + + if ( value != null && m_Holding != null ) + DropHolding(); + + m_Holding = value; + + if ( m_Holding != null ) + { + UpdateTotal( m_Holding, TotalType.Weight, m_Holding.TotalWeight + m_Holding.PileWeight ); + + if ( m_Holding.HeldBy == null ) + m_Holding.HeldBy = this; + } + } + } + } + + public DateTime LastMoveTime + { + get + { + return m_LastMoveTime; + } + set + { + m_LastMoveTime = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual bool Paralyzed + { + get + { + return m_Paralyzed; + } + set + { + if ( m_Paralyzed != value ) + { + m_Paralyzed = value; + + this.SendLocalizedMessage( m_Paralyzed ? 502381 : 502382 ); + + if ( m_ParaTimer != null ) + { + m_ParaTimer.Stop(); + m_ParaTimer = null; + } + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool DisarmReady + { + get + { + return m_DisarmReady; + } + set + { + m_DisarmReady = value; + //SendLocalizedMessage( value ? 1019013 : 1019014 ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool StunReady + { + get + { + return m_StunReady; + } + set + { + m_StunReady = value; + //SendLocalizedMessage( value ? 1019011 : 1019012 ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Frozen + { + get + { + return m_Frozen; + } + set + { + if ( m_Frozen != value ) + { + m_Frozen = value; + + if ( m_FrozenTimer != null ) + { + m_FrozenTimer.Stop(); + m_FrozenTimer = null; + } + } + } + } + + public void Paralyze( TimeSpan duration ) + { + if ( !m_Paralyzed ) + { + Paralyzed = true; + + m_ParaTimer = new ParalyzedTimer( this, duration ); + m_ParaTimer.Start(); + } + } + + public void Freeze( TimeSpan duration ) + { + if ( !m_Frozen ) + { + m_Frozen = true; + + m_FrozenTimer = new FrozenTimer( this, duration ); + m_FrozenTimer.Start(); + } + } + + /// + /// Gets or sets the lock state for the property. + /// + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public StatLockType StrLock + { + get + { + return m_StrLock; + } + set + { + if ( m_StrLock != value ) + { + m_StrLock = value; + + if ( m_NetState != null ) + m_NetState.Send( new StatLockInfo( this ) ); + } + } + } + + /// + /// Gets or sets the lock state for the property. + /// + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public StatLockType DexLock + { + get + { + return m_DexLock; + } + set + { + if ( m_DexLock != value ) + { + m_DexLock = value; + + if ( m_NetState != null ) + m_NetState.Send( new StatLockInfo( this ) ); + } + } + } + + /// + /// Gets or sets the lock state for the property. + /// + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public StatLockType IntLock + { + get + { + return m_IntLock; + } + set + { + if ( m_IntLock != value ) + { + m_IntLock = value; + + if ( m_NetState != null ) + m_NetState.Send( new StatLockInfo( this ) ); + } + } + } + + public override string ToString() + { + return String.Format( "0x{0:X} \"{1}\"", m_Serial.Value, Name ); + } + + public DateTime NextActionTime + { + get + { + return m_NextActionTime; + } + set + { + m_NextActionTime = value; + } + } + + public DateTime NextActionMessage + { + get + { + return m_NextActionMessage; + } + set + { + m_NextActionMessage = value; + } + } + + private static TimeSpan m_ActionMessageDelay = TimeSpan.FromSeconds( 0.125 ); + + public static TimeSpan ActionMessageDelay + { + get { return m_ActionMessageDelay; } + set { m_ActionMessageDelay = value; } + } + + public virtual void SendSkillMessage() + { + if ( DateTime.Now < m_NextActionMessage ) + return; + + m_NextActionMessage = DateTime.Now + m_ActionMessageDelay; + + SendLocalizedMessage( 500118 ); // You must wait a few moments to use another skill. + } + + public virtual void SendActionMessage() + { + if ( DateTime.Now < m_NextActionMessage ) + return; + + m_NextActionMessage = DateTime.Now + m_ActionMessageDelay; + + SendLocalizedMessage( 500119 ); // You must wait to perform another action. + } + + public virtual void ClearHands() + { + ClearHand( FindItemOnLayer( Layer.OneHanded ) ); + ClearHand( FindItemOnLayer( Layer.TwoHanded ) ); + } + + public virtual void ClearHand( Item item ) + { + if ( item != null && item.Movable && !item.AllowEquipedCast( this ) ) + { + Container pack = this.Backpack; + + if ( pack == null ) + AddToBackpack( item ); + else + pack.DropItem( item ); + } + } + + + private static bool m_GlobalRegenThroughPoison = true; + + public static bool GlobalRegenThroughPoison + { + get { return m_GlobalRegenThroughPoison; } + set { m_GlobalRegenThroughPoison = value; } + } + + public virtual bool RegenThroughPoison { get { return m_GlobalRegenThroughPoison; } } + + public virtual bool CanRegenHits { get { return this.Alive && ( RegenThroughPoison || !this.Poisoned ); } } + public virtual bool CanRegenStam { get { return this.Alive; } } + public virtual bool CanRegenMana { get { return this.Alive; } } + + #region Timers + + private class ManaTimer : Timer + { + private Mobile m_Owner; + + public ManaTimer( Mobile m ) + : base( Mobile.GetManaRegenRate( m ), Mobile.GetManaRegenRate( m ) ) + { + this.Priority = TimerPriority.FiftyMS; + m_Owner = m; + } + + protected override void OnTick() + { + if ( m_Owner.CanRegenMana )// m_Owner.Alive ) + m_Owner.Mana++; + + Delay = Interval = Mobile.GetManaRegenRate( m_Owner ); + } + } + + private class HitsTimer : Timer + { + private Mobile m_Owner; + + public HitsTimer( Mobile m ) + : base( Mobile.GetHitsRegenRate( m ), Mobile.GetHitsRegenRate( m ) ) + { + this.Priority = TimerPriority.FiftyMS; + m_Owner = m; + } + + protected override void OnTick() + { + if ( m_Owner.CanRegenHits )// m_Owner.Alive && !m_Owner.Poisoned ) + m_Owner.Hits++; + + Delay = Interval = Mobile.GetHitsRegenRate( m_Owner ); + } + } + + private class StamTimer : Timer + { + private Mobile m_Owner; + + public StamTimer( Mobile m ) + : base( Mobile.GetStamRegenRate( m ), Mobile.GetStamRegenRate( m ) ) + { + this.Priority = TimerPriority.FiftyMS; + m_Owner = m; + } + + protected override void OnTick() + { + if ( m_Owner.CanRegenStam )// m_Owner.Alive ) + m_Owner.Stam++; + + Delay = Interval = Mobile.GetStamRegenRate( m_Owner ); + } + } + + private class LogoutTimer : Timer + { + private Mobile m_Mobile; + + public LogoutTimer( Mobile m ) + : base( TimeSpan.FromDays( 1.0 ) ) + { + Priority = TimerPriority.OneSecond; + m_Mobile = m; + } + + protected override void OnTick() + { + if ( m_Mobile.m_Map != Map.Internal ) + { + EventSink.InvokeLogout( new LogoutEventArgs( m_Mobile ) ); + + m_Mobile.m_LogoutLocation = m_Mobile.m_Location; + m_Mobile.m_LogoutMap = m_Mobile.m_Map; + + m_Mobile.Internalize(); + } + } + } + + private class ParalyzedTimer : Timer + { + private Mobile m_Mobile; + + public ParalyzedTimer( Mobile m, TimeSpan duration ) + : base( duration ) + { + this.Priority = TimerPriority.TwentyFiveMS; + m_Mobile = m; + } + + protected override void OnTick() + { + m_Mobile.Paralyzed = false; + } + } + + private class FrozenTimer : Timer + { + private Mobile m_Mobile; + + public FrozenTimer( Mobile m, TimeSpan duration ) + : base( duration ) + { + this.Priority = TimerPriority.TwentyFiveMS; + m_Mobile = m; + } + + protected override void OnTick() + { + m_Mobile.Frozen = false; + } + } + + private class CombatTimer : Timer + { + private Mobile m_Mobile; + + public CombatTimer( Mobile m ) + : base( TimeSpan.FromSeconds( 0.0 ), TimeSpan.FromSeconds( 0.01 ), 0 ) + { + m_Mobile = m; + + if ( !m_Mobile.m_Player && m_Mobile.m_Dex <= 100 ) + Priority = TimerPriority.FiftyMS; + } + + protected override void OnTick() + { + if ( DateTime.Now > m_Mobile.m_NextCombatTime ) + { + Mobile combatant = m_Mobile.Combatant; + + // If no combatant, wrong map, one of us is a ghost, or cannot see, or deleted, then stop combat + if ( combatant == null || combatant.m_Deleted || m_Mobile.m_Deleted || combatant.m_Map != m_Mobile.m_Map || !combatant.Alive || !m_Mobile.Alive || !m_Mobile.CanSee( combatant ) || combatant.IsDeadBondedPet || m_Mobile.IsDeadBondedPet ) + { + m_Mobile.Combatant = null; + return; + } + + IWeapon weapon = m_Mobile.Weapon; + + if ( !m_Mobile.InRange( combatant, weapon.MaxRange ) ) + return; + + if ( m_Mobile.InLOS( combatant ) ) + { + weapon.OnBeforeSwing( m_Mobile, combatant ); //OnBeforeSwing for checking in regards to being hidden and whatnot + m_Mobile.RevealingAction(); + m_Mobile.m_NextCombatTime = DateTime.Now + weapon.OnSwing( m_Mobile, combatant ); + } + } + } + } + + private class ExpireCombatantTimer : Timer + { + private Mobile m_Mobile; + + public ExpireCombatantTimer( Mobile m ) + : base( TimeSpan.FromMinutes( 1.0 ) ) + { + this.Priority = TimerPriority.FiveSeconds; + m_Mobile = m; + } + + protected override void OnTick() + { + m_Mobile.Combatant = null; + } + } + + private static TimeSpan m_ExpireCriminalDelay = TimeSpan.FromMinutes( 2.0 ); + + public static TimeSpan ExpireCriminalDelay + { + get { return m_ExpireCriminalDelay; } + set { m_ExpireCriminalDelay = value; } + } + + private class ExpireCriminalTimer : Timer + { + private Mobile m_Mobile; + + public ExpireCriminalTimer( Mobile m ) + : base( m_ExpireCriminalDelay ) + { + this.Priority = TimerPriority.FiveSeconds; + m_Mobile = m; + } + + protected override void OnTick() + { + m_Mobile.Criminal = false; + } + } + + private class ExpireAggressorsTimer : Timer + { + private Mobile m_Mobile; + + public ExpireAggressorsTimer( Mobile m ) + : base( TimeSpan.FromSeconds( 5.0 ), TimeSpan.FromSeconds( 5.0 ) ) + { + m_Mobile = m; + Priority = TimerPriority.FiveSeconds; + } + + protected override void OnTick() + { + if ( m_Mobile.Deleted || ( m_Mobile.Aggressors.Count == 0 && m_Mobile.Aggressed.Count == 0 ) ) + m_Mobile.StopAggrExpire(); + else + m_Mobile.CheckAggrExpire(); + } + } + + #endregion + + private DateTime m_NextCombatTime; + + public DateTime NextSkillTime + { + get + { + return m_NextSkillTime; + } + set + { + m_NextSkillTime = value; + } + } + + public List Aggressors + { + get + { + return m_Aggressors; + } + } + + public List Aggressed + { + get + { + return m_Aggressed; + } + } + + private int m_ChangingCombatant; + + public bool ChangingCombatant + { + get { return ( m_ChangingCombatant > 0 ); } + } + + public virtual void Attack( Mobile m ) + { + if ( CheckAttack( m ) ) + Combatant = m; + } + + public virtual bool CheckAttack( Mobile m ) + { + return ( Utility.InUpdateRange( this, m ) && CanSee( m ) && InLOS( m ) ); + } + + /// + /// Overridable. Gets or sets which Mobile that this Mobile is currently engaged in combat with. + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual Mobile Combatant + { + get + { + return m_Combatant; + } + set + { + if ( m_Deleted ) + return; + + if ( m_Combatant != value && value != this ) + { + Mobile old = m_Combatant; + + ++m_ChangingCombatant; + m_Combatant = value; + + if ( ( m_Combatant != null && !CanBeHarmful( m_Combatant, false ) ) || !Region.OnCombatantChange( this, old, m_Combatant ) ) + { + m_Combatant = old; + --m_ChangingCombatant; + return; + } + + if ( m_NetState != null ) + m_NetState.Send( new ChangeCombatant( m_Combatant ) ); + + if ( m_Combatant == null ) + { + if ( m_ExpireCombatant != null ) + m_ExpireCombatant.Stop(); + + if ( m_CombatTimer != null ) + m_CombatTimer.Stop(); + + m_ExpireCombatant = null; + m_CombatTimer = null; + } + else + { + if ( m_ExpireCombatant == null ) + m_ExpireCombatant = new ExpireCombatantTimer( this ); + + m_ExpireCombatant.Start(); + + if ( m_CombatTimer == null ) + m_CombatTimer = new CombatTimer( this ); + + m_CombatTimer.Start(); + } + + if ( m_Combatant != null && CanBeHarmful( m_Combatant, false ) ) + { + DoHarmful( m_Combatant ); + + if ( m_Combatant != null ) + m_Combatant.PlaySound( m_Combatant.GetAngerSound() ); + } + + OnCombatantChange(); + --m_ChangingCombatant; + } + } + } + + /// + /// Overridable. Virtual event invoked after the property has changed. + /// + /// + public virtual void OnCombatantChange() + { + } + + public double GetDistanceToSqrt( Point3D p ) + { + int xDelta = m_Location.m_X - p.m_X; + int yDelta = m_Location.m_Y - p.m_Y; + + return Math.Sqrt( ( xDelta * xDelta ) + ( yDelta * yDelta ) ); + } + + public double GetDistanceToSqrt( Mobile m ) + { + int xDelta = m_Location.m_X - m.m_Location.m_X; + int yDelta = m_Location.m_Y - m.m_Location.m_Y; + + return Math.Sqrt( ( xDelta * xDelta ) + ( yDelta * yDelta ) ); + } + + public double GetDistanceToSqrt( IPoint2D p ) + { + int xDelta = m_Location.m_X - p.X; + int yDelta = m_Location.m_Y - p.Y; + + return Math.Sqrt( ( xDelta * xDelta ) + ( yDelta * yDelta ) ); + } + + public virtual void AggressiveAction( Mobile aggressor ) + { + AggressiveAction( aggressor, false ); + } + + public virtual void AggressiveAction( Mobile aggressor, bool criminal ) + { + if ( aggressor == this ) + return; + + AggressiveActionEventArgs args = AggressiveActionEventArgs.Create( this, aggressor, criminal ); + + EventSink.InvokeAggressiveAction( args ); + + args.Free(); + + if ( Combatant == aggressor ) + { + if ( m_ExpireCombatant == null ) + m_ExpireCombatant = new ExpireCombatantTimer( this ); + else + m_ExpireCombatant.Stop(); + + m_ExpireCombatant.Start(); + } + + bool addAggressor = true; + + List list = m_Aggressors; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Attacker == aggressor ) + { + info.Refresh(); + info.CriminalAggression = criminal; + info.CanReportMurder = criminal; + + addAggressor = false; + } + } + + list = aggressor.m_Aggressors; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Attacker == this ) + { + info.Refresh(); + + addAggressor = false; + } + } + + bool addAggressed = true; + + list = m_Aggressed; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Defender == aggressor ) + { + info.Refresh(); + + addAggressed = false; + } + } + + list = aggressor.m_Aggressed; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Defender == this ) + { + info.Refresh(); + info.CriminalAggression = criminal; + info.CanReportMurder = criminal; + + addAggressed = false; + } + } + + bool setCombatant = false; + + if ( addAggressor ) + { + m_Aggressors.Add( AggressorInfo.Create( aggressor, this, criminal ) ); // new AggressorInfo( aggressor, this, criminal, true ) ); + + if ( this.CanSee( aggressor ) && m_NetState != null ) + m_NetState.Send( new MobileIncoming( this, aggressor ) ); + + if ( Combatant == null ) + setCombatant = true; + + UpdateAggrExpire(); + } + + if ( addAggressed ) + { + aggressor.m_Aggressed.Add( AggressorInfo.Create( aggressor, this, criminal ) ); // new AggressorInfo( aggressor, this, criminal, false ) ); + + if ( this.CanSee( aggressor ) && m_NetState != null ) + m_NetState.Send( new MobileIncoming( this, aggressor ) ); + + if ( Combatant == null ) + setCombatant = true; + + UpdateAggrExpire(); + } + + if ( setCombatant ) + Combatant = aggressor; + + Region.OnAggressed( aggressor, this, criminal ); + } + + public void RemoveAggressed( Mobile aggressed ) + { + if ( m_Deleted ) + return; + + List list = m_Aggressed; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Defender == aggressed ) + { + m_Aggressed.RemoveAt( i ); + info.Free(); + + if ( m_NetState != null && this.CanSee( aggressed ) ) + m_NetState.Send( new MobileIncoming( this, aggressed ) ); + + break; + } + } + + UpdateAggrExpire(); + } + + public void RemoveAggressor( Mobile aggressor ) + { + if ( m_Deleted ) + return; + + List list = m_Aggressors; + + for ( int i = 0; i < list.Count; ++i ) + { + AggressorInfo info = list[i]; + + if ( info.Attacker == aggressor ) + { + m_Aggressors.RemoveAt( i ); + info.Free(); + + if ( m_NetState != null && this.CanSee( aggressor ) ) + m_NetState.Send( new MobileIncoming( this, aggressor ) ); + + break; + } + } + + UpdateAggrExpire(); + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TotalGold + { + get{ return GetTotal( TotalType.Gold ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TotalItems + { + get { return GetTotal( TotalType.Items ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TotalWeight + { + get { return GetTotal( TotalType.Weight ); } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int TithingPoints + { + get + { + return m_TithingPoints; + } + set + { + if ( m_TithingPoints != value ) + { + m_TithingPoints = value; + + Delta( MobileDelta.TithingPoints ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Followers + { + get + { + return m_Followers; + } + set + { + if ( m_Followers != value ) + { + m_Followers = value; + + Delta( MobileDelta.Followers ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int FollowersMax + { + get + { + return m_FollowersMax; + } + set + { + if ( m_FollowersMax != value ) + { + m_FollowersMax = value; + + Delta( MobileDelta.Followers ); + } + } + } + + public virtual int GetTotal( TotalType type ) + { + switch ( type ) + { + case TotalType.Gold: + return m_TotalGold; + + case TotalType.Items: + return m_TotalItems; + + case TotalType.Weight: + return m_TotalWeight; + } + + return 0; + } + + public virtual void UpdateTotal( Item sender, TotalType type, int delta ) + { + if ( delta == 0 || sender.IsVirtualItem ) + return; + + switch ( type ) + { + case TotalType.Gold: + m_TotalGold += delta; + Delta( MobileDelta.Gold ); + break; + + case TotalType.Items: + m_TotalItems += delta; + break; + + case TotalType.Weight: + m_TotalWeight += delta; + Delta( MobileDelta.Weight ); + OnWeightChange( m_TotalWeight - delta ); + break; + } + } + + public virtual void UpdateTotals() + { + if ( m_Items == null ) + return; + + int oldWeight = m_TotalWeight; + + m_TotalGold = 0; + m_TotalItems = 0; + m_TotalWeight = 0; + + for ( int i = 0; i < m_Items.Count; ++i ) + { + Item item = m_Items[i]; + + item.UpdateTotals(); + + if ( item.IsVirtualItem ) + continue; + + m_TotalGold += item.TotalGold; + m_TotalItems += item.TotalItems + 1; + m_TotalWeight += item.TotalWeight + item.PileWeight; + } + + if ( m_Holding != null ) + m_TotalWeight += m_Holding.TotalWeight + m_Holding.PileWeight; + + if ( m_TotalWeight != oldWeight ) + OnWeightChange( oldWeight ); + } + + public void ClearQuestArrow() + { + m_QuestArrow = null; + } + + public void ClearTarget() + { + m_Target = null; + } + + private bool m_TargetLocked; + + public bool TargetLocked + { + get + { + return m_TargetLocked; + } + set + { + m_TargetLocked = value; + } + } + + private class SimpleTarget : Target + { + private TargetCallback m_Callback; + + public SimpleTarget( int range, TargetFlags flags, bool allowGround, TargetCallback callback ) + : base( range, allowGround, flags ) + { + m_Callback = callback; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Callback != null ) + m_Callback( from, targeted ); + } + } + + public Target BeginTarget( int range, bool allowGround, TargetFlags flags, TargetCallback callback ) + { + Target t = new SimpleTarget( range, flags, allowGround, callback ); + + this.Target = t; + + return t; + } + + private class SimpleStateTarget : Target + { + private TargetStateCallback m_Callback; + private object m_State; + + public SimpleStateTarget( int range, TargetFlags flags, bool allowGround, TargetStateCallback callback, object state ) + : base( range, allowGround, flags ) + { + m_Callback = callback; + m_State = state; + } + + protected override void OnTarget( Mobile from, object targeted ) + { + if ( m_Callback != null ) + m_Callback( from, targeted, m_State ); + } + } + + public Target BeginTarget( int range, bool allowGround, TargetFlags flags, TargetStateCallback callback, object state ) + { + Target t = new SimpleStateTarget( range, flags, allowGround, callback, state ); + + this.Target = t; + + return t; + } + + public Target Target + { + get + { + return m_Target; + } + set + { + Target oldTarget = m_Target; + Target newTarget = value; + + if ( oldTarget == newTarget ) + return; + + m_Target = null; + + if ( oldTarget != null && newTarget != null ) + oldTarget.Cancel( this, TargetCancelType.Overriden ); + + m_Target = newTarget; + + if ( newTarget != null && m_NetState != null && !m_TargetLocked ) + m_NetState.Send( newTarget.GetPacket() ); + + OnTargetChange(); + + + + /*if ( m_Target != value ) + { + if ( m_Target != null && value != null ) + m_Target.Cancel( this, TargetCancelType.Overriden ); + + m_Target = value; + + if ( m_Target != null && m_NetState != null && !m_TargetLocked ) + m_NetState.Send( m_Target.GetPacket() ); + //m_NetState.Send( new TargetReq( m_Target ) ); + + OnTargetChange(); + }*/ + } + } + + /// + /// Overridable. Virtual event invoked after the Target property has changed. + /// + protected virtual void OnTargetChange() + { + } + + public ContextMenu ContextMenu + { + get + { + return m_ContextMenu; + } + set + { + m_ContextMenu = value; + + if ( m_ContextMenu != null ) + Send( new DisplayContextMenu( m_ContextMenu ) ); + } + } + + public virtual bool CheckContextMenuDisplay( IEntity target ) + { + return true; + } + + private class SimplePrompt : Prompt + { + private PromptCallback m_Callback; + private PromptCallback m_CancelCallback; + private bool m_CallbackHandlesCancel; + + public SimplePrompt( PromptCallback callback, PromptCallback cancelCallback ) + { + m_Callback = callback; + m_CancelCallback = cancelCallback; + } + + public SimplePrompt( PromptCallback callback, bool callbackHandlesCancel ) + { + m_Callback = callback; + m_CallbackHandlesCancel = callbackHandlesCancel; + } + + public SimplePrompt( PromptCallback callback ) + : this( callback, false ) + { + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Callback != null ) + m_Callback( from, text ); + } + + public override void OnCancel( Mobile from ) + { + if ( m_CallbackHandlesCancel && m_Callback != null ) + m_Callback( from, "" ); + else if ( m_CancelCallback != null ) + m_CancelCallback( from, "" ); + } + } + public Prompt BeginPrompt( PromptCallback callback, PromptCallback cancelCallback ) + { + Prompt p = new SimplePrompt( callback, cancelCallback ); + + this.Prompt = p; + return p; + } + public Prompt BeginPrompt( PromptCallback callback, bool callbackHandlesCancel ) + { + Prompt p = new SimplePrompt( callback, callbackHandlesCancel ); + + this.Prompt = p; + return p; + } + public Prompt BeginPrompt( PromptCallback callback ) + { + return BeginPrompt( callback, false ); + } + + private class SimpleStatePrompt : Prompt + { + private PromptStateCallback m_Callback; + private PromptStateCallback m_CancelCallback; + + private bool m_CallbackHandlesCancel; + + private object m_State; + + public SimpleStatePrompt( PromptStateCallback callback, PromptStateCallback cancelCallback, object state ) + { + m_Callback = callback; + m_CancelCallback = cancelCallback; + m_State = state; + } + public SimpleStatePrompt( PromptStateCallback callback, bool callbackHandlesCancel, object state ) + { + m_Callback = callback; + m_State = state; + m_CallbackHandlesCancel = callbackHandlesCancel; + } + public SimpleStatePrompt( PromptStateCallback callback, object state ) + : this( callback, false, state ) + { + } + + public override void OnResponse( Mobile from, string text ) + { + if ( m_Callback != null ) + m_Callback( from, text, m_State ); + } + + public override void OnCancel( Mobile from ) + { + if ( m_CallbackHandlesCancel && m_Callback != null ) + m_Callback( from, "", m_State ); + else if ( m_CancelCallback != null ) + m_CancelCallback( from, "", m_State ); + } + } + public Prompt BeginPrompt( PromptStateCallback callback, PromptStateCallback cancelCallback, object state ) + { + Prompt p = new SimpleStatePrompt( callback, cancelCallback, state ); + + this.Prompt = p; + return p; + } + public Prompt BeginPrompt( PromptStateCallback callback, bool callbackHandlesCancel, object state ) + { + Prompt p = new SimpleStatePrompt( callback, callbackHandlesCancel, state ); + + this.Prompt = p; + return p; + } + public Prompt BeginPrompt( PromptStateCallback callback, object state ) + { + return BeginPrompt( callback, false, state ); + } + + public Prompt Prompt + { + get + { + return m_Prompt; + } + set + { + Prompt oldPrompt = m_Prompt; + Prompt newPrompt = value; + + if ( oldPrompt == newPrompt ) + return; + + m_Prompt = null; + + if ( oldPrompt != null && newPrompt != null ) + oldPrompt.OnCancel( this ); + + m_Prompt = newPrompt; + + if ( newPrompt != null ) + Send( new UnicodePrompt( newPrompt ) ); + } + } + + private bool InternalOnMove( Direction d ) + { + if ( !OnMove( d ) ) + return false; + + MovementEventArgs e = MovementEventArgs.Create( this, d ); + + EventSink.InvokeMovement( e ); + + bool ret = !e.Blocked; + + e.Free(); + + return ret; + } + + /// + /// Overridable. Event invoked before the Mobile moves. + /// + /// True if the move is allowed, false if not. + protected virtual bool OnMove( Direction d ) + { + if ( m_Hidden && m_AccessLevel == AccessLevel.Player ) + { + if ( m_AllowedStealthSteps-- <= 0 || ( d & Direction.Running ) != 0 || this.Mounted ) + RevealingAction(); + } + + return true; + } + + private static Packet[] m_MovingPacketCache = new Packet[8]; + + private bool m_Pushing; + + public bool Pushing + { + get + { + return m_Pushing; + } + set + { + m_Pushing = value; + } + } + + private static TimeSpan m_WalkFoot = TimeSpan.FromSeconds( 0.4 ); + private static TimeSpan m_RunFoot = TimeSpan.FromSeconds( 0.2 ); + private static TimeSpan m_WalkMount = TimeSpan.FromSeconds( 0.2 ); + private static TimeSpan m_RunMount = TimeSpan.FromSeconds( 0.1 ); + + public static TimeSpan WalkFoot { get { return m_WalkFoot; } } + public static TimeSpan RunFoot { get { return m_RunFoot; } } + public static TimeSpan WalkMount { get { return m_WalkMount; } } + public static TimeSpan RunMount { get { return m_RunMount; } } + + private DateTime m_EndQueue; + + private static ArrayList m_MoveList = new ArrayList(); + + private static AccessLevel m_FwdAccessOverride = AccessLevel.Counselor; + private static bool m_FwdEnabled = true; + private static bool m_FwdUOTDOverride = false; + private static int m_FwdMaxSteps = 4; + + public static AccessLevel FwdAccessOverride { get { return m_FwdAccessOverride; } set { m_FwdAccessOverride = value; } } + public static bool FwdEnabled { get { return m_FwdEnabled; } set { m_FwdEnabled = value; } } + public static bool FwdUOTDOverride { get { return m_FwdUOTDOverride; } set { m_FwdUOTDOverride = value; } } + public static int FwdMaxSteps { get { return m_FwdMaxSteps; } set { m_FwdMaxSteps = value; } } + + public virtual void ClearFastwalkStack() + { + if ( m_MoveRecords != null && m_MoveRecords.Count > 0 ) + m_MoveRecords.Clear(); + + m_EndQueue = DateTime.Now; + } + + public virtual bool CheckMovement( Direction d, out int newZ ) + { + return Movement.Movement.CheckMovement( this, d, out newZ ); + } + + public virtual bool Move( Direction d ) + { + if ( m_Deleted ) + return false; + + BankBox box = FindBankNoCreate(); + + if ( box != null && box.Opened ) + box.Close(); + + Point3D newLocation = m_Location; + Point3D oldLocation = newLocation; + + if ( ( m_Direction & Direction.Mask ) == ( d & Direction.Mask ) ) + { + // We are actually moving (not just a direction change) + + if ( m_Spell != null && !m_Spell.OnCasterMoving( d ) ) + return false; + + if ( m_Paralyzed || m_Frozen ) + { + SendLocalizedMessage( 500111 ); // You are frozen and can not move. + + return false; + } + + int newZ; + + if ( CheckMovement( d, out newZ ) ) + { + int x = oldLocation.m_X, y = oldLocation.m_Y; + int oldX = x, oldY = y; + int oldZ = oldLocation.m_Z; + + switch ( d & Direction.Mask ) + { + case Direction.North: + --y; + break; + case Direction.Right: + ++x; + --y; + break; + case Direction.East: + ++x; + break; + case Direction.Down: + ++x; + ++y; + break; + case Direction.South: + ++y; + break; + case Direction.Left: + --x; + ++y; + break; + case Direction.West: + --x; + break; + case Direction.Up: + --x; + --y; + break; + } + + newLocation.m_X = x; + newLocation.m_Y = y; + newLocation.m_Z = newZ; + + m_Pushing = false; + + Map map = m_Map; + + if ( map != null ) + { + Sector oldSector = map.GetSector( oldX, oldY ); + Sector newSector = map.GetSector( x, y ); + + if ( oldSector != newSector ) + { + for ( int i = 0; i < oldSector.Mobiles.Count; ++i ) + { + Mobile m = oldSector.Mobiles[i]; + + if ( m != this && m.X == oldX && m.Y == oldY && ( m.Z + 15 ) > oldZ && ( oldZ + 15 ) > m.Z && !m.OnMoveOff( this ) ) + return false; + } + + for ( int i = 0; i < oldSector.Items.Count; ++i ) + { + Item item = oldSector.Items[i]; + + if ( item.AtWorldPoint( oldX, oldY ) && ( item.Z == oldZ || ( ( item.Z + item.ItemData.Height ) > oldZ && ( oldZ + 15 ) > item.Z ) ) && !item.OnMoveOff( this ) ) + return false; + } + + for ( int i = 0; i < newSector.Mobiles.Count; ++i ) + { + Mobile m = newSector.Mobiles[i]; + + if ( m.X == x && m.Y == y && ( m.Z + 15 ) > newZ && ( newZ + 15 ) > m.Z && !m.OnMoveOver( this ) ) + return false; + } + + for ( int i = 0; i < newSector.Items.Count; ++i ) + { + Item item = newSector.Items[i]; + + if ( item.AtWorldPoint( x, y ) && ( item.Z == newZ || ( ( item.Z + item.ItemData.Height ) > newZ && ( newZ + 15 ) > item.Z ) ) && !item.OnMoveOver( this ) ) + return false; + } + } + else + { + for ( int i = 0; i < oldSector.Mobiles.Count; ++i ) + { + Mobile m = oldSector.Mobiles[i]; + + if ( m != this && m.X == oldX && m.Y == oldY && ( m.Z + 15 ) > oldZ && ( oldZ + 15 ) > m.Z && !m.OnMoveOff( this ) ) + return false; + else if ( m.X == x && m.Y == y && ( m.Z + 15 ) > newZ && ( newZ + 15 ) > m.Z && !m.OnMoveOver( this ) ) + return false; + } + + for ( int i = 0; i < oldSector.Items.Count; ++i ) + { + Item item = oldSector.Items[i]; + + if ( item.AtWorldPoint( oldX, oldY ) && ( item.Z == oldZ || ( ( item.Z + item.ItemData.Height ) > oldZ && ( oldZ + 15 ) > item.Z ) ) && !item.OnMoveOff( this ) ) + return false; + else if ( item.AtWorldPoint( x, y ) && ( item.Z == newZ || ( ( item.Z + item.ItemData.Height ) > newZ && ( newZ + 15 ) > item.Z ) ) && !item.OnMoveOver( this ) ) + return false; + } + } + + if ( !Region.CanMove( this, d, newLocation, oldLocation, m_Map ) ) + return false; + } + else + { + return false; + } + + if ( !InternalOnMove( d ) ) + return false; + + if ( m_FwdEnabled && m_NetState != null && m_AccessLevel < m_FwdAccessOverride && ( !m_FwdUOTDOverride || ( m_NetState.Version != null && m_NetState.Version.Type != ClientType.UOTD ) ) ) + { + if ( m_MoveRecords == null ) + m_MoveRecords = new Queue( 6 ); + + while ( m_MoveRecords.Count > 0 ) + { + MovementRecord r = m_MoveRecords.Peek(); + + if ( r.Expired() ) + m_MoveRecords.Dequeue(); + else + break; + } + + if ( m_MoveRecords.Count >= m_FwdMaxSteps ) + { + FastWalkEventArgs fw = new FastWalkEventArgs( m_NetState ); + EventSink.InvokeFastWalk( fw ); + + if ( fw.Blocked ) + return false; + } + + TimeSpan delay = ComputeMovementSpeed( d ); + + /*if ( Mounted ) + delay = ( d & Direction.Running ) != 0 ? m_RunMount : m_WalkMount; + else + delay = ( d & Direction.Running ) != 0 ? m_RunFoot : m_WalkFoot;*/ + + DateTime end; + + if ( m_MoveRecords.Count > 0 ) + end = m_EndQueue + delay; + else + end = DateTime.Now + delay; + + m_MoveRecords.Enqueue( MovementRecord.NewInstance( end ) ); + + m_EndQueue = end; + } + + m_LastMoveTime = DateTime.Now; + } + else + { + return false; + } + + DisruptiveAction(); + } + + if ( m_NetState != null ) + m_NetState.Send( MovementAck.Instantiate( m_NetState.Sequence, this ) );//new MovementAck( m_NetState.Sequence, this ) ); + + SetLocation( newLocation, false ); + SetDirection( d ); + + if ( m_Map != null ) + { + IPooledEnumerable eable = m_Map.GetObjectsInRange( m_Location, Core.GlobalMaxUpdateRange ); + + foreach ( object o in eable ) + { + if ( o == this ) + continue; + + if ( o is Mobile ) + { + m_MoveList.Add( o ); + } + else if ( o is Item ) + { + Item item = (Item) o; + + if ( item.HandlesOnMovement ) + m_MoveList.Add( item ); + } + } + + eable.Free(); + + Packet[] cache = m_MovingPacketCache; + + for ( int i = 0; i < cache.Length; ++i ) + Packet.Release( ref cache[i] ); + + for ( int i = 0; i < m_MoveList.Count; ++i ) + { + object o = m_MoveList[i]; + + if ( o is Mobile ) + { + Mobile m = (Mobile) m_MoveList[i]; + NetState ns = m.NetState; + + if ( ns != null && Utility.InUpdateRange( m_Location, m.m_Location ) && m.CanSee( this ) ) + { + int noto = Notoriety.Compute( m, this ); + Packet p = cache[noto]; + + if ( p == null ) + cache[noto] = p = Packet.Acquire( new MobileMoving( this, noto ) ); + + ns.Send( p ); + } + + m.OnMovement( this, oldLocation ); + } + else if ( o is Item ) + { + ( (Item) o ).OnMovement( this, oldLocation ); + } + } + + for ( int i = 0; i < cache.Length; ++i ) + Packet.Release( ref cache[i] ); + + if ( m_MoveList.Count > 0 ) + m_MoveList.Clear(); + } + + OnAfterMove( oldLocation ); + return true; + } + + public virtual void OnAfterMove( Point3D oldLocation ) + { + } + + public TimeSpan ComputeMovementSpeed() + { + return ComputeMovementSpeed( this.Direction, false ); + } + public TimeSpan ComputeMovementSpeed( Direction dir ) + { + return ComputeMovementSpeed( dir, true ); + } + public virtual TimeSpan ComputeMovementSpeed( Direction dir, bool checkTurning ) + { + TimeSpan delay; + + if( Mounted ) + delay = (dir & Direction.Running) != 0 ? m_RunMount : m_WalkMount; + else + delay = (dir & Direction.Running) != 0 ? m_RunFoot : m_WalkFoot; + + return delay; + } + + /// + /// Overridable. Virtual event invoked when a Mobile moves off this Mobile. + /// + /// True if the move is allowed, false if not. + public virtual bool OnMoveOff( Mobile m ) + { + return true; + } + + public virtual bool IsDeadBondedPet { get { return false; } } + + /// + /// Overridable. Event invoked when a Mobile moves over this Mobile. + /// + /// True if the move is allowed, false if not. + public virtual bool OnMoveOver( Mobile m ) + { + if ( m_Map == null || m_Deleted ) + return true; + + return m.CheckShove( this ); + } + + public virtual bool CheckShove( Mobile shoved ) + { + if ( ( m_Map.Rules & MapRules.FreeMovement ) == 0 ) + { + if ( !shoved.Alive || !Alive || shoved.IsDeadBondedPet || IsDeadBondedPet ) + return true; + else if ( shoved.m_Hidden && shoved.m_AccessLevel > AccessLevel.Player ) + return true; + + if ( !m_Pushing ) + { + m_Pushing = true; + + int number; + + if ( this.AccessLevel > AccessLevel.Player ) + { + number = shoved.m_Hidden ? 1019041 : 1019040; + } + else + { + if ( Stam == StamMax ) + { + number = shoved.m_Hidden ? 1019043 : 1019042; + Stam -= 10; + + RevealingAction(); + } + else + { + return false; + } + } + + SendLocalizedMessage( number ); + } + } + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile sees another Mobile, , move. + /// + public virtual void OnMovement( Mobile m, Point3D oldLocation ) + { + } + + public ISpell Spell + { + get + { + return m_Spell; + } + set + { + if ( m_Spell != null && value != null ) + Console.WriteLine( "Warning: Spell has been overwritten" ); + + m_Spell = value; + } + } + + [CommandProperty( AccessLevel.Administrator )] + public bool AutoPageNotify + { + get + { + return m_AutoPageNotify; + } + set + { + m_AutoPageNotify = value; + } + } + + public virtual void CriminalAction( bool message ) + { + if ( m_Deleted ) + return; + + Criminal = true; + + this.Region.OnCriminalAction( this, message ); + } + + public virtual bool CanUseStuckMenu() + { + if ( m_StuckMenuUses == null ) + { + return true; + } + else + { + for ( int i = 0; i < m_StuckMenuUses.Length; ++i ) + { + if ( ( DateTime.Now - m_StuckMenuUses[i] ) > TimeSpan.FromDays( 1.0 ) ) + { + return true; + } + } + + return false; + } + } + + public virtual bool IsSnoop( Mobile from ) + { + return ( from != this ); + } + + /// + /// Overridable. Any call to will silently fail if this method returns false. + /// + /// + public virtual bool CheckResurrect() + { + return true; + } + + /// + /// Overridable. Event invoked before the Mobile is resurrected. + /// + /// + public virtual void OnBeforeResurrect() + { + } + + /// + /// Overridable. Event invoked after the Mobile is resurrected. + /// + /// + public virtual void OnAfterResurrect() + { + } + + public virtual void Resurrect() + { + if ( !Alive ) + { + if ( !Region.OnResurrect( this ) ) + return; + + if ( !CheckResurrect() ) + return; + + OnBeforeResurrect(); + + BankBox box = FindBankNoCreate(); + + if ( box != null && box.Opened ) + box.Close(); + + Poison = null; + + Warmode = false; + + Hits = 10; + Stam = StamMax; + Mana = 0; + + BodyMod = 0; + //Body = m_Female ? 0x191 : 0x190; + Body = this.Race.AliveBody( this ); + + ProcessDeltaQueue(); + + for ( int i = m_Items.Count - 1; i >= 0; --i ) + { + if ( i >= m_Items.Count ) + continue; + + Item item = m_Items[i]; + + if ( item.ItemID == 0x204E ) + item.Delete(); + } + + this.SendIncomingPacket(); + this.SendIncomingPacket(); + + OnAfterResurrect(); + + //Send( new DeathStatus( false ) ); + } + } + + private IAccount m_Account; + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Owner )] + public IAccount Account + { + get + { + return m_Account; + } + set + { + m_Account = value; + } + } + + private bool m_Deleted; + + public bool Deleted + { + get + { + return m_Deleted; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int VirtualArmor + { + get + { + return m_VirtualArmor; + } + set + { + if ( m_VirtualArmor != value ) + { + m_VirtualArmor = value; + + Delta( MobileDelta.Armor ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual double ArmorRating + { + get + { + return 0.0; + } + } + + public void DropHolding() + { + Item holding = m_Holding; + + if ( holding != null ) + { + if ( !holding.Deleted && holding.Map == Map.Internal ) + AddToBackpack( holding ); + + Holding = null; + holding.ClearBounce(); + } + } + + public virtual void Delete() + { + if ( m_Deleted ) + return; + else if ( !World.OnDelete( this ) ) + return; + + if ( m_NetState != null ) + m_NetState.CancelAllTrades(); + + if ( m_NetState != null ) + m_NetState.Dispose(); + + DropHolding(); + + Region.OnRegionChange( this, m_Region, null ); + + m_Region = null; + //Is the above line REALLY needed? The old Region system did NOT have said line + //and worked fine, because of this a LOT of extra checks have to be done everywhere... + //I guess this should be there for Garbage collection purposes, but, still, is it /really/ needed? + + OnDelete(); + + for ( int i = m_Items.Count - 1; i >= 0; --i ) + if ( i < m_Items.Count ) + m_Items[i].OnParentDeleted( this ); + + for ( int i = 0; i < m_Stabled.Count; i++ ) + m_Stabled[i].Delete(); + + SendRemovePacket(); + + if ( m_Guild != null ) + m_Guild.OnDelete( this ); + + m_Deleted = true; + + if ( m_Map != null ) + { + m_Map.OnLeave( this ); + m_Map = null; + } + + m_Hair = null; + m_FacialHair = null; + m_MountItem = null; + + World.RemoveMobile( this ); + + OnAfterDelete(); + + FreeCache(); + } + + /// + /// Overridable. Virtual event invoked before the Mobile is deleted. + /// + public virtual void OnDelete() + { + if ( m_Spawner != null ) + { + m_Spawner.Remove( this ); + m_Spawner = null; + } + } + + /// + /// Overridable. Returns true if the player is alive, false if otherwise. By default, this is computed by: !Deleted && (!Player || !Body.IsGhost) + /// + [CommandProperty( AccessLevel.Counselor )] + public virtual bool Alive + { + get + { + return !m_Deleted && ( !m_Player || !m_Body.IsGhost ); + } + } + + public virtual bool CheckSpellCast( ISpell spell ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile casts a . + /// + /// + public virtual void OnSpellCast( ISpell spell ) + { + } + + /// + /// Overridable. Virtual event invoked after changes. + /// + public virtual void OnWeightChange( int oldValue ) + { + } + + /// + /// Overridable. Virtual event invoked when the or property of changes. + /// + public virtual void OnSkillChange( SkillName skill, double oldBase ) + { + } + + /// + /// Overridable. Invoked after the mobile is deleted. When overriden, be sure to call the base method. + /// + public virtual void OnAfterDelete() + { + StopAggrExpire(); + + CheckAggrExpire(); + + if ( m_PoisonTimer != null ) + m_PoisonTimer.Stop(); + + if ( m_HitsTimer != null ) + m_HitsTimer.Stop(); + + if ( m_StamTimer != null ) + m_StamTimer.Stop(); + + if ( m_ManaTimer != null ) + m_ManaTimer.Stop(); + + if ( m_CombatTimer != null ) + m_CombatTimer.Stop(); + + if ( m_ExpireCombatant != null ) + m_ExpireCombatant.Stop(); + + if ( m_LogoutTimer != null ) + m_LogoutTimer.Stop(); + + if ( m_ExpireCriminal != null ) + m_ExpireCriminal.Stop(); + + if ( m_WarmodeTimer != null ) + m_WarmodeTimer.Stop(); + + if ( m_ParaTimer != null ) + m_ParaTimer.Stop(); + + if ( m_FrozenTimer != null ) + m_FrozenTimer.Stop(); + + if ( m_AutoManifestTimer != null ) + m_AutoManifestTimer.Stop(); + } + + public virtual bool AllowSkillUse( SkillName name ) + { + return true; + } + + public virtual bool UseSkill( SkillName name ) + { + return Skills.UseSkill( this, name ); + } + + public virtual bool UseSkill( int skillID ) + { + return Skills.UseSkill( this, skillID ); + } + + private static CreateCorpseHandler m_CreateCorpse; + + public static CreateCorpseHandler CreateCorpseHandler + { + get { return m_CreateCorpse; } + set { m_CreateCorpse = value; } + } + + public virtual DeathMoveResult GetParentMoveResultFor( Item item ) + { + return item.OnParentDeath( this ); + } + + public virtual DeathMoveResult GetInventoryMoveResultFor( Item item ) + { + return item.OnInventoryDeath( this ); + } + + public virtual bool RetainPackLocsOnDeath { get { return Core.AOS; } } + + public virtual void Kill() + { + if ( !CanBeDamaged() ) + return; + else if ( !Alive || IsDeadBondedPet ) + return; + else if ( m_Deleted ) + return; + else if ( !Region.OnDeath( this ) ) + return; + else if ( !OnBeforeDeath() ) + return; + + BankBox box = FindBankNoCreate(); + + if ( box != null && box.Opened ) + box.Close(); + + if ( m_NetState != null ) + m_NetState.CancelAllTrades(); + + if ( m_Spell != null ) + m_Spell.OnCasterKilled(); + //m_Spell.Disturb( DisturbType.Kill ); + + if ( m_Target != null ) + m_Target.Cancel( this, TargetCancelType.Canceled ); + + DisruptiveAction(); + + Warmode = false; + + DropHolding(); + + Hits = 0; + Stam = 0; + Mana = 0; + + Poison = null; + Combatant = null; + + if ( Paralyzed ) + { + Paralyzed = false; + + if ( m_ParaTimer != null ) + m_ParaTimer.Stop(); + } + + if ( Frozen ) + { + Frozen = false; + + if ( m_FrozenTimer != null ) + m_FrozenTimer.Stop(); + } + + List content = new List(); + List equip = new List(); + List moveToPack = new List(); + + List itemsCopy = new List( m_Items ); + + Container pack = this.Backpack; + + for ( int i = 0; i < itemsCopy.Count; ++i ) + { + Item item = itemsCopy[i]; + + if ( item == pack ) + continue; + + DeathMoveResult res = GetParentMoveResultFor( item ); + + switch ( res ) + { + case DeathMoveResult.MoveToCorpse: + { + content.Add( item ); + equip.Add( item ); + break; + } + case DeathMoveResult.MoveToBackpack: + { + moveToPack.Add( item ); + break; + } + } + } + + if ( pack != null ) + { + List packCopy = new List( pack.Items ); + + for ( int i = 0; i < packCopy.Count; ++i ) + { + Item item = packCopy[i]; + + DeathMoveResult res = GetInventoryMoveResultFor( item ); + + if ( res == DeathMoveResult.MoveToCorpse ) + content.Add( item ); + else + moveToPack.Add( item ); + } + + for ( int i = 0; i < moveToPack.Count; ++i ) + { + Item item = moveToPack[i]; + + if ( RetainPackLocsOnDeath && item.Parent == pack ) + continue; + + pack.DropItem( item ); + } + } + + HairInfo hair = null; + if( m_Hair != null ) + hair = new HairInfo( m_Hair.ItemID, m_Hair.Hue ); + + FacialHairInfo facialhair = null; + if( m_FacialHair != null ) + facialhair = new FacialHairInfo( m_FacialHair.ItemID, m_FacialHair.Hue ); + + Container c = (m_CreateCorpse == null ? null : m_CreateCorpse( this, hair, facialhair, content, equip )); + + + /*m_Corpse = c; + + for ( int i = 0; c != null && i < content.Count; ++i ) + c.DropItem( (Item)content[i] ); + + if ( c != null ) + c.MoveToWorld( this.Location, this.Map );*/ + + if ( m_Map != null ) + { + Packet animPacket = null;//new DeathAnimation( this, c ); + Packet remPacket = null;//this.RemovePacket; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state != m_NetState ) + { + if ( animPacket == null ) + animPacket = Packet.Acquire( new DeathAnimation( this, c ) ); + + state.Send( animPacket ); + + if ( !state.Mobile.CanSee( this ) ) + { + if ( remPacket == null ) + remPacket = this.RemovePacket; + + state.Send( remPacket ); + } + } + } + + Packet.Release( animPacket ); + + eable.Free(); + } + + OnDeath( c ); + } + + private Container m_Corpse; + + [CommandProperty( AccessLevel.GameMaster )] + public Container Corpse + { + get + { + return m_Corpse; + } + set + { + m_Corpse = value; + } + } + + /// + /// Overridable. Event invoked before the Mobile is killed. + /// + /// + /// + /// True to continue with death, false to override it. + public virtual bool OnBeforeDeath() + { + return true; + } + + /// + /// Overridable. Event invoked after the Mobile is killed. Primarily, this method is responsible for deleting an NPC or turning a PC into a ghost. + /// + /// + /// + public virtual void OnDeath( Container c ) + { + int sound = this.GetDeathSound(); + + if ( sound >= 0 ) + Effects.PlaySound( this, this.Map, sound ); + + if ( !m_Player ) + { + Delete(); + } + else + { + Send( DeathStatus.Instantiate( true ) ); + + Warmode = false; + + BodyMod = 0; + //Body = this.Female ? 0x193 : 0x192; + Body = this.Race.GhostBody( this ); + + Item deathShroud = new Item( 0x204E ); + + deathShroud.Movable = false; + deathShroud.Layer = Layer.OuterTorso; + + AddItem( deathShroud ); + + m_Items.Remove( deathShroud ); + m_Items.Insert( 0, deathShroud ); + + Poison = null; + Combatant = null; + + Hits = 0; + Stam = 0; + Mana = 0; + + EventSink.InvokePlayerDeath( new PlayerDeathEventArgs( this ) ); + + ProcessDeltaQueue(); + + Send( DeathStatus.Instantiate( false ) ); + + CheckStatTimers(); + } + } + + public virtual int GetAngerSound() + { + if ( m_BaseSoundID != 0 ) + return m_BaseSoundID; + + return -1; + } + + public virtual int GetIdleSound() + { + if ( m_BaseSoundID != 0 ) + return m_BaseSoundID + 1; + + return -1; + } + + public virtual int GetAttackSound() + { + if ( m_BaseSoundID != 0 ) + return m_BaseSoundID + 2; + + return -1; + } + + public virtual int GetHurtSound() + { + if ( m_BaseSoundID != 0 ) + return m_BaseSoundID + 3; + + return -1; + } + + public virtual int GetDeathSound() + { + if ( m_BaseSoundID != 0 ) + { + return m_BaseSoundID + 4; + } + else if ( m_Body.IsHuman ) + { + return Utility.Random( m_Female ? 0x314 : 0x423, m_Female ? 4 : 5 ); + } + else + { + return -1; + } + } + + private static char[] m_GhostChars = new char[2] { 'o', 'O' }; + + public static char[] GhostChars { get { return m_GhostChars; } set { m_GhostChars = value; } } + + private static bool m_NoSpeechLOS; + + public static bool NoSpeechLOS { get { return m_NoSpeechLOS; } set { m_NoSpeechLOS = value; } } + + private static TimeSpan m_AutoManifestTimeout = TimeSpan.FromSeconds( 5.0 ); + + public static TimeSpan AutoManifestTimeout { get { return m_AutoManifestTimeout; } set { m_AutoManifestTimeout = value; } } + + private Timer m_AutoManifestTimer; + + private class AutoManifestTimer : Timer + { + private Mobile m_Mobile; + + public AutoManifestTimer( Mobile m, TimeSpan delay ) + : base( delay ) + { + m_Mobile = m; + } + + protected override void OnTick() + { + if ( !m_Mobile.Alive ) + m_Mobile.Warmode = false; + } + } + + public virtual bool CheckTarget( Mobile from, Target targ, object targeted ) + { + return true; + } + + private static bool m_InsuranceEnabled; + + public static bool InsuranceEnabled + { + get { return m_InsuranceEnabled; } + set { m_InsuranceEnabled = value; } + } + + public virtual void Use( Item item ) + { + if ( item == null || item.Deleted || this.Deleted ) + return; + + DisruptiveAction(); + + if ( m_Spell != null && !m_Spell.OnCasterUsingObject( item ) ) + return; + + object root = item.RootParent; + bool okay = false; + + if ( !Utility.InUpdateRange( this, item.GetWorldLocation() ) ) + item.OnDoubleClickOutOfRange( this ); + else if ( !CanSee( item ) ) + item.OnDoubleClickCantSee( this ); + else if ( !item.IsAccessibleTo( this ) ) + { + Region reg = Region.Find( item.GetWorldLocation(), item.Map ); + + if ( reg == null || !reg.SendInaccessibleMessage( item, this ) ) + item.OnDoubleClickNotAccessible( this ); + } + else if ( !CheckAlive( false ) ) + item.OnDoubleClickDead( this ); + else if ( item.InSecureTrade ) + item.OnDoubleClickSecureTrade( this ); + else if ( !AllowItemUse( item ) ) + okay = false; + else if ( !item.CheckItemUse( this, item ) ) + okay = false; + else if ( root != null && root is Mobile && ( (Mobile) root ).IsSnoop( this ) ) + item.OnSnoop( this ); + else if ( this.Region.OnDoubleClick( this, item ) ) + okay = true; + + if ( okay ) + { + if ( !item.Deleted ) + item.OnItemUsed( this, item ); + + if ( !item.Deleted ) + item.OnDoubleClick( this ); + } + } + + public virtual void Use( Mobile m ) + { + if ( m == null || m.Deleted || this.Deleted ) + return; + + DisruptiveAction(); + + if ( m_Spell != null && !m_Spell.OnCasterUsingObject( m ) ) + return; + + if ( !Utility.InUpdateRange( this, m ) ) + m.OnDoubleClickOutOfRange( this ); + else if ( !CanSee( m ) ) + m.OnDoubleClickCantSee( this ); + else if ( !CheckAlive( false ) ) + m.OnDoubleClickDead( this ); + else if ( this.Region.OnDoubleClick( this, m ) && !m.Deleted ) + m.OnDoubleClick( this ); + } + + public virtual void Lift( Item item, int amount, out bool rejected, out LRReason reject ) + { + rejected = true; + reject = LRReason.Inspecific; + + if ( item == null ) + return; + + Mobile from = this; + NetState state = m_NetState; + + if ( from.AccessLevel >= AccessLevel.GameMaster || DateTime.Now >= from.NextActionTime ) + { + if ( from.CheckAlive() ) + { + from.DisruptiveAction(); + + if ( from.Holding != null ) + { + reject = LRReason.AreHolding; + } + else if ( from.AccessLevel < AccessLevel.GameMaster && !from.InRange( item.GetWorldLocation(), 2 ) ) + { + reject = LRReason.OutOfRange; + } + else if ( !from.CanSee( item ) || !from.InLOS( item ) ) + { + reject = LRReason.OutOfSight; + } + else if ( !item.VerifyMove( from ) ) + { + reject = LRReason.CannotLift; + } + else if ( !item.IsAccessibleTo( from ) ) + { + reject = LRReason.CannotLift; + } + else if ( !item.CheckLift( from, item, ref reject ) ) + { + } + else + { + object root = item.RootParent; + + if ( root != null && root is Mobile && !( (Mobile) root ).CheckNonlocalLift( from, item ) ) + { + reject = LRReason.TryToSteal; + } + else if ( !from.OnDragLift( item ) || !item.OnDragLift( from ) ) + { + reject = LRReason.Inspecific; + } + else if ( !from.CheckAlive() ) + { + reject = LRReason.Inspecific; + } + else + { + item.SetLastMoved(); + + if ( item.Spawner != null ) + { + item.Spawner.Remove( item ); + item.Spawner = null; + } + + if ( amount == 0 ) + amount = 1; + + if ( amount > item.Amount ) + amount = item.Amount; + + int oldAmount = item.Amount; + //item.Amount = amount; //Set in LiftItemDupe + + if ( amount < oldAmount ) + LiftItemDupe( item, amount ); + //item.Dupe( oldAmount - amount ); + + Map map = from.Map; + + if ( Mobile.DragEffects && map != null && ( root == null || root is Item ) ) + { + IPooledEnumerable eable = map.GetClientsInRange( from.Location ); + Packet p = null; + + foreach ( NetState ns in eable ) + { + if ( ns.Mobile != from && ns.Mobile.CanSee( from ) ) + { + if ( p == null ) + { + IEntity src; + + if ( root == null ) + src = new Entity( Serial.Zero, item.Location, map ); + else + src = new Entity( ( (Item) root ).Serial, ( (Item) root ).Location, map ); + + p = Packet.Acquire( new DragEffect( src, from, item.ItemID, item.Hue, amount ) ); + } + + ns.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + + Point3D fixLoc = item.Location; + Map fixMap = item.Map; + bool shouldFix = ( item.Parent == null ); + + item.RecordBounce(); + item.OnItemLifted( from, item ); + item.Internalize(); + + from.Holding = item; + + int liftSound = item.GetLiftSound( from ); + + if ( liftSound != -1 ) + from.Send( new PlaySound( liftSound, from ) ); + + from.NextActionTime = DateTime.Now + TimeSpan.FromSeconds( 0.5 ); + + if ( fixMap != null && shouldFix ) + fixMap.FixColumn( fixLoc.m_X, fixLoc.m_Y ); + + reject = LRReason.Inspecific; + rejected = false; + } + } + } + else + { + reject = LRReason.Inspecific; + } + } + else + { + SendActionMessage(); + reject = LRReason.Inspecific; + } + + if ( rejected && state != null ) + { + state.Send( new LiftRej( reject ) ); + + if ( item.Parent is Item ) + state.Send( new ContainerContentUpdate( item ) ); + else if ( item.Parent is Mobile ) + state.Send( new EquipUpdate( item ) ); + else + item.SendInfoTo( state ); + + if ( ObjectPropertyList.Enabled && item.Parent != null ) + state.Send( item.OPLPacket ); + } + } + + public static Item LiftItemDupe( Item oldItem, int amount ) + { + Item item; + try + { + item = (Item)Activator.CreateInstance( oldItem.GetType() ); + } + catch + { + Console.WriteLine( "Warning: 0x{0:X}: Item must have a zero paramater constructor to be seperated from a stack. '{1}'.", oldItem.Serial.Value, oldItem.GetType().Name ); + return null; + } + item.Visible = oldItem.Visible; + item.Movable = oldItem.Movable; + item.LootType = oldItem.LootType; + item.Direction = oldItem.Direction; + item.Hue = oldItem.Hue; + item.ItemID = oldItem.ItemID; + item.Location = oldItem.Location; + item.Layer = oldItem.Layer; + item.Name = oldItem.Name; + item.Weight = oldItem.Weight; + //item.Amount = oldItem.amount; + item.Amount = oldItem.Amount - amount; + item.Map = oldItem.Map; + + oldItem.Amount = amount; + oldItem.OnAfterDuped( item ); + + if ( oldItem.Parent is Mobile ) + { + ( (Mobile)oldItem.Parent ).AddItem( item ); + } + else if ( oldItem.Parent is Item ) + { + ( (Item)oldItem.Parent ).AddItem( item ); + } + + item.Delta( ItemDelta.Update ); + + return item; + } + + public virtual void SendDropEffect( Item item ) + { + if ( Mobile.DragEffects ) + { + Map map = m_Map; + object root = item.RootParent; + + if ( map != null && ( root == null || root is Item ) ) + { + IPooledEnumerable eable = map.GetClientsInRange( m_Location ); + Packet p = null; + + foreach ( NetState ns in eable ) + { + if ( ns.Mobile != this && ns.Mobile.CanSee( this ) ) + { + if ( p == null ) + { + IEntity trg; + + if ( root == null ) + trg = new Entity( Serial.Zero, item.Location, map ); + else + trg = new Entity( ( (Item) root ).Serial, ( (Item) root ).Location, map ); + + p = Packet.Acquire( new DragEffect( this, trg, item.ItemID, item.Hue, item.Amount ) ); + } + + ns.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + } + + public virtual bool Drop( Item to, Point3D loc ) + { + Mobile from = this; + Item item = from.Holding; + + if ( item == null ) + return false; + + from.Holding = null; + bool bounced = true; + + item.SetLastMoved(); + + if ( to == null || !item.DropToItem( from, to, loc ) ) + item.Bounce( from ); + else + bounced = false; + + item.ClearBounce(); + + if ( !bounced ) + SendDropEffect( item ); + + return !bounced; + } + + public virtual bool Drop( Point3D loc ) + { + Mobile from = this; + Item item = from.Holding; + + if ( item == null ) + return false; + + from.Holding = null; + bool bounced = true; + + item.SetLastMoved(); + + if ( !item.DropToWorld( from, loc ) ) + item.Bounce( from ); + else + bounced = false; + + item.ClearBounce(); + + if ( !bounced ) + SendDropEffect( item ); + + return !bounced; + } + + public virtual bool Drop( Mobile to, Point3D loc ) + { + Mobile from = this; + Item item = from.Holding; + + if ( item == null ) + return false; + + from.Holding = null; + bool bounced = true; + + item.SetLastMoved(); + + if ( to == null || !item.DropToMobile( from, to, loc ) ) + item.Bounce( from ); + else + bounced = false; + + item.ClearBounce(); + + if ( !bounced ) + SendDropEffect( item ); + + return !bounced; + } + + private static object m_GhostMutateContext = new object(); + + public virtual bool MutateSpeech( List hears, ref string text, ref object context ) + { + if ( Alive ) + return false; + + StringBuilder sb = new StringBuilder( text.Length, text.Length ); + + for ( int i = 0; i < text.Length; ++i ) + { + if ( text[i] != ' ' ) + sb.Append( m_GhostChars[Utility.Random( m_GhostChars.Length )] ); + else + sb.Append( ' ' ); + } + + text = sb.ToString(); + context = m_GhostMutateContext; + return true; + } + + public virtual void Manifest( TimeSpan delay ) + { + Warmode = true; + + if ( m_AutoManifestTimer == null ) + m_AutoManifestTimer = new AutoManifestTimer( this, delay ); + else + m_AutoManifestTimer.Stop(); + + m_AutoManifestTimer.Start(); + } + + public virtual bool CheckSpeechManifest() + { + if ( Alive ) + return false; + + TimeSpan delay = m_AutoManifestTimeout; + + if ( delay > TimeSpan.Zero && ( !Warmode || m_AutoManifestTimer != null ) ) + { + Manifest( delay ); + return true; + } + + return false; + } + + public virtual bool CheckHearsMutatedSpeech( Mobile m, object context ) + { + if ( context == m_GhostMutateContext ) + return ( m.Alive && !m.CanHearGhosts ); + + return true; + } + + private void AddSpeechItemsFrom( ArrayList list, Container cont ) + { + for ( int i = 0; i < cont.Items.Count; ++i ) + { + Item item = cont.Items[i]; + + if ( item.HandlesOnSpeech ) + list.Add( item ); + + if ( item is Container ) + AddSpeechItemsFrom( list, (Container) item ); + } + } + + private class LocationComparer : IComparer + { + private static LocationComparer m_Instance; + + public static LocationComparer GetInstance( IPoint3D relativeTo ) + { + if ( m_Instance == null ) + m_Instance = new LocationComparer( relativeTo ); + else + m_Instance.m_RelativeTo = relativeTo; + + return m_Instance; + } + + private IPoint3D m_RelativeTo; + + public IPoint3D RelativeTo + { + get { return m_RelativeTo; } + set { m_RelativeTo = value; } + } + + public LocationComparer( IPoint3D relativeTo ) + { + m_RelativeTo = relativeTo; + } + + private int GetDistance( IPoint3D p ) + { + int x = m_RelativeTo.X - p.X; + int y = m_RelativeTo.Y - p.Y; + int z = m_RelativeTo.Z - p.Z; + + x *= 11; + y *= 11; + + return ( x * x ) + ( y * y ) + ( z * z ); + } + + public int Compare( object x, object y ) + { + IPoint3D a = x as IPoint3D; + IPoint3D b = y as IPoint3D; + + return GetDistance( a ) - GetDistance( b ); + } + } + + public IPooledEnumerable GetItemsInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + return map.GetItemsInRange( m_Location, range ); + } + + public IPooledEnumerable GetObjectsInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + return map.GetObjectsInRange( m_Location, range ); + } + + public IPooledEnumerable GetMobilesInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + return map.GetMobilesInRange( m_Location, range ); + } + + public IPooledEnumerable GetClientsInRange( int range ) + { + Map map = m_Map; + + if ( map == null ) + return Server.Map.NullEnumerable.Instance; + + return map.GetClientsInRange( m_Location, range ); + } + + private static List m_Hears; + private static ArrayList m_OnSpeech; + + public virtual void DoSpeech( string text, int[] keywords, MessageType type, int hue ) + { + if ( m_Deleted || CommandSystem.Handle( this, text, type ) ) + return; + + int range = 15; + + switch ( type ) + { + case MessageType.Regular: + m_SpeechHue = hue; + break; + case MessageType.Emote: + m_EmoteHue = hue; + break; + case MessageType.Whisper: + m_WhisperHue = hue; + range = 1; + break; + case MessageType.Yell: + m_YellHue = hue; + range = 18; + break; + default: + type = MessageType.Regular; + break; + } + + SpeechEventArgs regArgs = new SpeechEventArgs( this, text, type, hue, keywords ); + + EventSink.InvokeSpeech( regArgs ); + this.Region.OnSpeech( regArgs ); + OnSaid( regArgs ); + + if ( regArgs.Blocked ) + return; + + text = regArgs.Speech; + + if ( text == null || text.Length == 0 ) + return; + + if ( m_Hears == null ) + m_Hears = new List(); + else if ( m_Hears.Count > 0 ) + m_Hears.Clear(); + + if ( m_OnSpeech == null ) + m_OnSpeech = new ArrayList(); + else if ( m_OnSpeech.Count > 0 ) + m_OnSpeech.Clear(); + + List hears = m_Hears; + ArrayList onSpeech = m_OnSpeech; + + if ( m_Map != null ) + { + IPooledEnumerable eable = m_Map.GetObjectsInRange( m_Location, range ); + + foreach ( object o in eable ) + { + if ( o is Mobile ) + { + Mobile heard = (Mobile) o; + + if ( heard.CanSee( this ) && ( m_NoSpeechLOS || !heard.Player || heard.InLOS( this ) ) ) + { + if ( heard.m_NetState != null ) + hears.Add( heard ); + + if ( heard.HandlesOnSpeech( this ) ) + onSpeech.Add( heard ); + + for ( int i = 0; i < heard.Items.Count; ++i ) + { + Item item = heard.Items[i]; + + if ( item.HandlesOnSpeech ) + onSpeech.Add( item ); + + if ( item is Container ) + AddSpeechItemsFrom( onSpeech, (Container) item ); + } + } + } + else if ( o is Item ) + { + if ( ( (Item) o ).HandlesOnSpeech ) + onSpeech.Add( o ); + + if ( o is Container ) + AddSpeechItemsFrom( onSpeech, (Container) o ); + } + } + + //eable.Free(); + + object mutateContext = null; + string mutatedText = text; + SpeechEventArgs mutatedArgs = null; + + if ( MutateSpeech( hears, ref mutatedText, ref mutateContext ) ) + mutatedArgs = new SpeechEventArgs( this, mutatedText, type, hue, new int[0] ); + + CheckSpeechManifest(); + + ProcessDelta(); + + Packet regp = null; + Packet mutp = null; + + for ( int i = 0; i < hears.Count; ++i ) + { + Mobile heard = hears[i]; + + if ( mutatedArgs == null || !CheckHearsMutatedSpeech( heard, mutateContext ) ) + { + heard.OnSpeech( regArgs ); + + NetState ns = heard.NetState; + + if ( ns != null ) + { + if ( regp == null ) + regp = Packet.Acquire( new UnicodeMessage( m_Serial, Body, type, hue, 3, m_Language, Name, text ) ); + + ns.Send( regp ); + } + } + else + { + heard.OnSpeech( mutatedArgs ); + + NetState ns = heard.NetState; + + if ( ns != null ) + { + if ( mutp == null ) + mutp = Packet.Acquire( new UnicodeMessage( m_Serial, Body, type, hue, 3, m_Language, Name, mutatedText ) ); + + ns.Send( mutp ); + } + } + } + + Packet.Release( regp ); + Packet.Release( mutp ); + + if ( onSpeech.Count > 1 ) + onSpeech.Sort( LocationComparer.GetInstance( this ) ); + + for ( int i = 0; i < onSpeech.Count; ++i ) + { + object obj = onSpeech[i]; + + if ( obj is Mobile ) + { + Mobile heard = (Mobile) obj; + + if ( mutatedArgs == null || !CheckHearsMutatedSpeech( heard, mutateContext ) ) + heard.OnSpeech( regArgs ); + else + heard.OnSpeech( mutatedArgs ); + } + else + { + Item item = (Item) obj; + + item.OnSpeech( regArgs ); + } + } + } + } + + private static VisibleDamageType m_VisibleDamageType; + + public static VisibleDamageType VisibleDamageType + { + get { return m_VisibleDamageType; } + set { m_VisibleDamageType = value; } + } + + private List m_DamageEntries; + + public List DamageEntries + { + get { return m_DamageEntries; } + } + + public static Mobile GetDamagerFrom( DamageEntry de ) + { + return ( de == null ? null : de.Damager ); + } + + public Mobile FindMostRecentDamager( bool allowSelf ) + { + return GetDamagerFrom( FindMostRecentDamageEntry( allowSelf ) ); + } + + public DamageEntry FindMostRecentDamageEntry( bool allowSelf ) + { + for ( int i = m_DamageEntries.Count - 1; i >= 0; --i ) + { + if ( i >= m_DamageEntries.Count ) + continue; + + DamageEntry de = m_DamageEntries[i]; + + if ( de.HasExpired ) + m_DamageEntries.RemoveAt( i ); + else if ( allowSelf || de.Damager != this ) + return de; + } + + return null; + } + + public Mobile FindLeastRecentDamager( bool allowSelf ) + { + return GetDamagerFrom( FindLeastRecentDamageEntry( allowSelf ) ); + } + + public DamageEntry FindLeastRecentDamageEntry( bool allowSelf ) + { + for ( int i = 0; i < m_DamageEntries.Count; ++i ) + { + if ( i < 0 ) + continue; + + DamageEntry de = m_DamageEntries[i]; + + if ( de.HasExpired ) + { + m_DamageEntries.RemoveAt( i ); + --i; + } + else if ( allowSelf || de.Damager != this ) + { + return de; + } + } + + return null; + } + + public Mobile FindMostTotalDamger( bool allowSelf ) + { + return GetDamagerFrom( FindMostTotalDamageEntry( allowSelf ) ); + } + + public DamageEntry FindMostTotalDamageEntry( bool allowSelf ) + { + DamageEntry mostTotal = null; + + for ( int i = m_DamageEntries.Count - 1; i >= 0; --i ) + { + if ( i >= m_DamageEntries.Count ) + continue; + + DamageEntry de = m_DamageEntries[i]; + + if ( de.HasExpired ) + m_DamageEntries.RemoveAt( i ); + else if ( ( allowSelf || de.Damager != this ) && ( mostTotal == null || de.DamageGiven > mostTotal.DamageGiven ) ) + mostTotal = de; + } + + return mostTotal; + } + + public Mobile FindLeastTotalDamger( bool allowSelf ) + { + return GetDamagerFrom( FindLeastTotalDamageEntry( allowSelf ) ); + } + + public DamageEntry FindLeastTotalDamageEntry( bool allowSelf ) + { + DamageEntry mostTotal = null; + + for ( int i = m_DamageEntries.Count - 1; i >= 0; --i ) + { + if ( i >= m_DamageEntries.Count ) + continue; + + DamageEntry de = m_DamageEntries[i]; + + if ( de.HasExpired ) + m_DamageEntries.RemoveAt( i ); + else if ( ( allowSelf || de.Damager != this ) && ( mostTotal == null || de.DamageGiven < mostTotal.DamageGiven ) ) + mostTotal = de; + } + + return mostTotal; + } + + public DamageEntry FindDamageEntryFor( Mobile m ) + { + for ( int i = m_DamageEntries.Count - 1; i >= 0; --i ) + { + if ( i >= m_DamageEntries.Count ) + continue; + + DamageEntry de = m_DamageEntries[i]; + + if ( de.HasExpired ) + m_DamageEntries.RemoveAt( i ); + else if ( de.Damager == m ) + return de; + } + + return null; + } + + public virtual Mobile GetDamageMaster( Mobile damagee ) + { + return null; + } + + public virtual DamageEntry RegisterDamage( int amount, Mobile from ) + { + DamageEntry de = FindDamageEntryFor( from ); + + if ( de == null ) + de = new DamageEntry( from ); + + de.DamageGiven += amount; + de.LastDamage = DateTime.Now; + + m_DamageEntries.Remove( de ); + m_DamageEntries.Add( de ); + + Mobile master = from.GetDamageMaster( this ); + + if ( master != null ) + { + List list = de.Responsible; + + if ( list == null ) + de.Responsible = list = new List(); + + DamageEntry resp = null; + + for ( int i = 0; i < list.Count; ++i ) + { + DamageEntry check = list[i]; + + if ( check.Damager == master ) + { + resp = check; + break; + } + } + + if ( resp == null ) + list.Add( resp = new DamageEntry( master ) ); + + resp.DamageGiven += amount; + resp.LastDamage = DateTime.Now; + } + + return de; + } + + private Mobile m_LastKiller; + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile LastKiller + { + get { return m_LastKiller; } + set { m_LastKiller = value; } + } + + /// + /// Overridable. Virtual event invoked when the Mobile is damaged. It is called before hit points are lowered or the Mobile is killed. + /// + /// + /// + /// + public virtual void OnDamage( int amount, Mobile from, bool willKill ) + { + } + + public virtual void Damage( int amount ) + { + Damage( amount, null ); + } + + public virtual bool CanBeDamaged() + { + return !m_Blessed; + } + + public virtual void Damage( int amount, Mobile from ) + { + Damage( amount, from, true ); + } + + public virtual void Damage( int amount, Mobile from, bool informMount ) + { + if ( !CanBeDamaged() || m_Deleted ) + return; + + if ( !this.Region.OnDamage( this, ref amount ) ) + return; + + if ( amount > 0 ) + { + int oldHits = Hits; + int newHits = oldHits - amount; + + if ( m_Spell != null ) + m_Spell.OnCasterHurt(); + + //if ( m_Spell != null && m_Spell.State == SpellState.Casting ) + // m_Spell.Disturb( DisturbType.Hurt, false, true ); + + if ( from != null ) + RegisterDamage( amount, from ); + + DisruptiveAction(); + + Paralyzed = false; + + switch ( m_VisibleDamageType ) + { + case VisibleDamageType.Related: + { + NetState ourState = m_NetState, theirState = ( from == null ? null : from.m_NetState ); + + if ( ourState == null ) + { + Mobile master = GetDamageMaster( from ); + + if ( master != null ) + ourState = master.m_NetState; + } + + if ( theirState == null && from != null ) + { + Mobile master = from.GetDamageMaster( this ); + + if ( master != null ) + theirState = master.m_NetState; + } + + if ( amount > 0 && ( ourState != null || theirState != null ) ) + { + Packet p = null;// = new DamagePacket( this, amount ); + + if ( ourState != null ) + { + bool newPacket = ( ourState.Version != null && ourState.Version >= DamagePacket.Version ); + + if ( newPacket ) + p = Packet.Acquire( new DamagePacket( this, amount ) ); + else + p = Packet.Acquire( new DamagePacketOld( this, amount ) ); + + ourState.Send( p ); + } + + if ( theirState != null && theirState != ourState ) + { + bool newPacket = ( theirState.Version != null && theirState.Version >= DamagePacket.Version ); + + if ( newPacket && ( p == null || !( p is DamagePacket ) ) ) + { + Packet.Release( p ); + p = Packet.Acquire( new DamagePacket( this, amount ) ); + } + else if ( !newPacket && ( p == null || !( p is DamagePacketOld ) ) ) + { + Packet.Release( p ); + p = Packet.Acquire( new DamagePacketOld( this, amount ) ); + } + + theirState.Send( p ); + } + + Packet.Release( p ); + } + + break; + } + case VisibleDamageType.Everyone: + { + SendDamageToAll( amount ); + break; + } + } + + OnDamage( amount, from, newHits < 0 ); + + IMount m = this.Mount; + if( m != null && informMount ) + m.OnRiderDamaged( amount, from, newHits < 0 ); + + if ( newHits < 0 ) + { + m_LastKiller = from; + + Hits = 0; + + if ( oldHits >= 0 ) + Kill(); + } + else + { + Hits = newHits; + } + } + } + + public virtual void SendDamageToAll( int amount ) + { + if ( amount < 0 ) + return; + + Map map = m_Map; + + if ( map == null ) + return; + + IPooledEnumerable eable = map.GetClientsInRange( m_Location ); + + Packet pNew = null; + Packet pOld = null; + + foreach ( NetState ns in eable ) + { + if ( ns.Mobile.CanSee( this ) ) + { + bool newPacket = ( ns.Version != null && ns.Version >= DamagePacket.Version ); + Packet p; + + if ( newPacket ) + { + if ( pNew == null ) + pNew = Packet.Acquire( new DamagePacket( this, amount ) ); + + p = pNew; + } + else + { + if ( pOld == null ) + pOld = Packet.Acquire( new DamagePacketOld( this, amount ) ); + + p = pOld; + } + + ns.Send( p ); + } + } + + Packet.Release( pNew ); + Packet.Release( pOld ); + + eable.Free(); + } + + public void Heal( int amount ) + { + Heal( amount, true ); + } + + public void Heal( int amount, bool message ) + { + if ( !Alive || IsDeadBondedPet ) + return; + + if ( !Region.OnHeal( this, ref amount ) ) + return; + + if ( ( Hits + amount ) > HitsMax ) + { + amount = HitsMax - Hits; + } + + Hits += amount; + + if ( message && amount > 0 && m_NetState != null ) + m_NetState.Send( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Label, 0x3B2, 3, 1008158, "", AffixType.Append | AffixType.System, amount.ToString(), "" ) ); + } + + public void UsedStuckMenu() + { + if ( m_StuckMenuUses == null ) + { + m_StuckMenuUses = new DateTime[2]; + } + + for ( int i = 0; i < m_StuckMenuUses.Length; ++i ) + { + if ( ( DateTime.Now - m_StuckMenuUses[i] ) > TimeSpan.FromDays( 1.0 ) ) + { + m_StuckMenuUses[i] = DateTime.Now; + return; + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Squelched + { + get + { + return m_Squelched; + } + set + { + m_Squelched = value; + } + } + + public virtual void Deserialize( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch ( version ) + { + case 31: + { + m_LastStrGain = reader.ReadDeltaTime(); + m_LastIntGain = reader.ReadDeltaTime(); + m_LastDexGain = reader.ReadDeltaTime(); + + goto case 30; + } + case 30: + { + byte hairflag = reader.ReadByte(); + + if( (hairflag & 0x01) != 0 ) + m_Hair = new HairInfo( reader ); + if( (hairflag & 0x02) != 0 ) + m_FacialHair = new FacialHairInfo( reader ); + + goto case 29; + } + case 29: + { + m_Race = reader.ReadRace(); + goto case 28; + } + case 28: + { + if( version <= 30 ) + LastStatGain = reader.ReadDeltaTime(); + + goto case 27; + } + case 27: + { + m_TithingPoints = reader.ReadInt(); + + goto case 26; + } + case 26: + case 25: + case 24: + { + m_Corpse = reader.ReadItem() as Container; + + goto case 23; + } + case 23: + { + m_CreationTime = reader.ReadDateTime(); + + goto case 22; + } + case 22: // Just removed followers + case 21: + { + m_Stabled = reader.ReadStrongMobileList(); + + goto case 20; + } + case 20: + { + m_CantWalk = reader.ReadBool(); + + goto case 19; + } + case 19: // Just removed variables + case 18: + { + m_Virtues = new VirtueInfo( reader ); + + goto case 17; + } + case 17: + { + m_Thirst = reader.ReadInt(); + m_BAC = reader.ReadInt(); + + goto case 16; + } + case 16: + { + m_ShortTermMurders = reader.ReadInt(); + + if ( version <= 24 ) + { + reader.ReadDateTime(); + reader.ReadDateTime(); + } + + goto case 15; + } + case 15: + { + if ( version < 22 ) + reader.ReadInt(); // followers + + m_FollowersMax = reader.ReadInt(); + + goto case 14; + } + case 14: + { + m_MagicDamageAbsorb = reader.ReadInt(); + + goto case 13; + } + case 13: + { + m_GuildFealty = reader.ReadMobile(); + + goto case 12; + } + case 12: + { + m_Guild = reader.ReadGuild(); + + goto case 11; + } + case 11: + { + m_DisplayGuildTitle = reader.ReadBool(); + + goto case 10; + } + case 10: + { + m_CanSwim = reader.ReadBool(); + + goto case 9; + } + case 9: + { + m_Squelched = reader.ReadBool(); + + goto case 8; + } + case 8: + { + m_Holding = reader.ReadItem(); + + goto case 7; + } + case 7: + { + m_VirtualArmor = reader.ReadInt(); + + goto case 6; + } + case 6: + { + m_BaseSoundID = reader.ReadInt(); + + goto case 5; + } + case 5: + { + m_DisarmReady = reader.ReadBool(); + m_StunReady = reader.ReadBool(); + + goto case 4; + } + case 4: + { + if ( version <= 25 ) + { + Poison.Deserialize( reader ); + } + + goto case 3; + } + case 3: + { + m_StatCap = reader.ReadInt(); + + goto case 2; + } + case 2: + { + m_NameHue = reader.ReadInt(); + + goto case 1; + } + case 1: + { + m_Hunger = reader.ReadInt(); + + goto case 0; + } + case 0: + { + if ( version < 21 ) + m_Stabled = new List(); + + if ( version < 18 ) + m_Virtues = new VirtueInfo(); + + if ( version < 11 ) + m_DisplayGuildTitle = true; + + if ( version < 3 ) + m_StatCap = 225; + + if ( version < 15 ) + { + m_Followers = 0; + m_FollowersMax = 5; + } + + m_Location = reader.ReadPoint3D(); + m_Body = new Body( reader.ReadInt() ); + m_Name = reader.ReadString(); + m_GuildTitle = reader.ReadString(); + m_Criminal = reader.ReadBool(); + m_Kills = reader.ReadInt(); + m_SpeechHue = reader.ReadInt(); + m_EmoteHue = reader.ReadInt(); + m_WhisperHue = reader.ReadInt(); + m_YellHue = reader.ReadInt(); + m_Language = reader.ReadString(); + m_Female = reader.ReadBool(); + m_Warmode = reader.ReadBool(); + m_Hidden = reader.ReadBool(); + m_Direction = (Direction) reader.ReadByte(); + m_Hue = reader.ReadInt(); + m_Str = reader.ReadInt(); + m_Dex = reader.ReadInt(); + m_Int = reader.ReadInt(); + m_Hits = reader.ReadInt(); + m_Stam = reader.ReadInt(); + m_Mana = reader.ReadInt(); + m_Map = reader.ReadMap(); + m_Blessed = reader.ReadBool(); + m_Fame = reader.ReadInt(); + m_Karma = reader.ReadInt(); + m_AccessLevel = (AccessLevel) reader.ReadByte(); + + m_Skills = new Skills( this, reader ); + + m_Items = reader.ReadStrongItemList(); + + m_Player = reader.ReadBool(); + m_Title = reader.ReadString(); + m_Profile = reader.ReadString(); + m_ProfileLocked = reader.ReadBool(); + + if ( version <= 18 ) + { + reader.ReadInt(); + reader.ReadInt(); + reader.ReadInt(); + } + + m_AutoPageNotify = reader.ReadBool(); + + m_LogoutLocation = reader.ReadPoint3D(); + m_LogoutMap = reader.ReadMap(); + + m_StrLock = (StatLockType) reader.ReadByte(); + m_DexLock = (StatLockType) reader.ReadByte(); + m_IntLock = (StatLockType) reader.ReadByte(); + + m_StatMods = new List(); + m_SkillMods = new List(); + + if ( reader.ReadBool() ) + { + m_StuckMenuUses = new DateTime[reader.ReadInt()]; + + for ( int i = 0; i < m_StuckMenuUses.Length; ++i ) + { + m_StuckMenuUses[i] = reader.ReadDateTime(); + } + } + else + { + m_StuckMenuUses = null; + } + + if ( m_Player && m_Map != Map.Internal ) + { + m_LogoutLocation = m_Location; + m_LogoutMap = m_Map; + + m_Map = Map.Internal; + } + + if ( m_Map != null ) + m_Map.OnEnter( this ); + + if ( m_Criminal ) + { + if ( m_ExpireCriminal == null ) + m_ExpireCriminal = new ExpireCriminalTimer( this ); + + m_ExpireCriminal.Start(); + } + + if ( ShouldCheckStatTimers ) + CheckStatTimers(); + + if ( !m_Player && m_Dex <= 100 && m_CombatTimer != null ) + m_CombatTimer.Priority = TimerPriority.FiftyMS; + else if ( m_CombatTimer != null ) + m_CombatTimer.Priority = TimerPriority.EveryTick; + + UpdateRegion(); + + UpdateResistances(); + + break; + } + } + + if ( !m_Player ) + Utility.Intern( ref m_Name ); + + Utility.Intern( ref m_Title ); + Utility.Intern( ref m_Language ); + + /* //Moved into cleanup in scripts. + if( version < 30 ) + Timer.DelayCall( TimeSpan.Zero, new TimerCallback( ConvertHair ) ); + * */ + + } + + public void ConvertHair() + { + Item hair; + + if( (hair = FindItemOnLayer( Layer.Hair )) != null ) + { + HairItemID = hair.ItemID; + HairHue = hair.Hue; + hair.Delete(); + } + + if( (hair = FindItemOnLayer( Layer.FacialHair )) != null ) + { + FacialHairItemID = hair.ItemID; + FacialHairHue = hair.Hue; + hair.Delete(); + } + } + + public virtual bool ShouldCheckStatTimers { get { return true; } } + + public virtual void CheckStatTimers() + { + if ( m_Deleted ) + return; + + if ( Hits < HitsMax ) + { + if ( CanRegenHits ) + { + if ( m_HitsTimer == null ) + m_HitsTimer = new HitsTimer( this ); + + m_HitsTimer.Start(); + } + else if ( m_HitsTimer != null ) + { + m_HitsTimer.Stop(); + } + } + else + { + Hits = HitsMax; + } + + if ( Stam < StamMax ) + { + if ( CanRegenStam ) + { + if ( m_StamTimer == null ) + m_StamTimer = new StamTimer( this ); + + m_StamTimer.Start(); + } + else if ( m_StamTimer != null ) + { + m_StamTimer.Stop(); + } + } + else + { + Stam = StamMax; + } + + if ( Mana < ManaMax ) + { + if ( CanRegenMana ) + { + if ( m_ManaTimer == null ) + m_ManaTimer = new ManaTimer( this ); + + m_ManaTimer.Start(); + } + else if ( m_ManaTimer != null ) + { + m_ManaTimer.Stop(); + } + } + else + { + Mana = ManaMax; + } + } + + private DateTime m_CreationTime; + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime CreationTime + { + get + { + return m_CreationTime; + } + } + + int ISerializable.TypeReference { + get { return m_TypeRef; } + } + + int ISerializable.SerialIdentity { + get { return m_Serial; } + } + + public virtual void Serialize( GenericWriter writer ) + { + writer.Write( (int) 31 ); // version + + writer.WriteDeltaTime( m_LastStrGain ); + writer.WriteDeltaTime( m_LastIntGain ); + writer.WriteDeltaTime( m_LastDexGain ); + + byte hairflag = 0x00; + + if( m_Hair != null ) + hairflag |= 0x01; + if( m_FacialHair != null ) + hairflag |= 0x02; + + writer.Write( (byte)hairflag ); + + if( (hairflag & 0x01) != 0 ) + m_Hair.Serialize( writer ); + if( (hairflag & 0x02) != 0 ) + m_FacialHair.Serialize( writer ); + + writer.Write( this.Race ); + + writer.Write( (int) m_TithingPoints ); + + writer.Write( m_Corpse ); + + writer.Write( m_CreationTime ); + + writer.Write( m_Stabled, true ); + + writer.Write( m_CantWalk ); + + VirtueInfo.Serialize( writer, m_Virtues ); + + writer.Write( m_Thirst ); + writer.Write( m_BAC ); + + writer.Write( m_ShortTermMurders ); + //writer.Write( m_ShortTermElapse ); + //writer.Write( m_LongTermElapse ); + + //writer.Write( m_Followers ); + writer.Write( m_FollowersMax ); + + writer.Write( m_MagicDamageAbsorb ); + + writer.Write( m_GuildFealty ); + + writer.Write( m_Guild ); + + writer.Write( m_DisplayGuildTitle ); + + writer.Write( m_CanSwim ); + + writer.Write( m_Squelched ); + + writer.Write( m_Holding ); + + writer.Write( m_VirtualArmor ); + + writer.Write( m_BaseSoundID ); + + writer.Write( m_DisarmReady ); + writer.Write( m_StunReady ); + + //Poison.Serialize( m_Poison, writer ); + + writer.Write( m_StatCap ); + + writer.Write( m_NameHue ); + + writer.Write( m_Hunger ); + + writer.Write( m_Location ); + writer.Write( (int) m_Body ); + writer.Write( m_Name ); + writer.Write( m_GuildTitle ); + writer.Write( m_Criminal ); + writer.Write( m_Kills ); + writer.Write( m_SpeechHue ); + writer.Write( m_EmoteHue ); + writer.Write( m_WhisperHue ); + writer.Write( m_YellHue ); + writer.Write( m_Language ); + writer.Write( m_Female ); + writer.Write( m_Warmode ); + writer.Write( m_Hidden ); + writer.Write( (byte) m_Direction ); + writer.Write( m_Hue ); + writer.Write( m_Str ); + writer.Write( m_Dex ); + writer.Write( m_Int ); + writer.Write( m_Hits ); + writer.Write( m_Stam ); + writer.Write( m_Mana ); + + writer.Write( m_Map ); + + writer.Write( m_Blessed ); + writer.Write( m_Fame ); + writer.Write( m_Karma ); + writer.Write( (byte) m_AccessLevel ); + m_Skills.Serialize( writer ); + + writer.Write( m_Items ); + + writer.Write( m_Player ); + writer.Write( m_Title ); + writer.Write( m_Profile ); + writer.Write( m_ProfileLocked ); + writer.Write( m_AutoPageNotify ); + + writer.Write( m_LogoutLocation ); + writer.Write( m_LogoutMap ); + + writer.Write( (byte) m_StrLock ); + writer.Write( (byte) m_DexLock ); + writer.Write( (byte) m_IntLock ); + + if ( m_StuckMenuUses != null ) + { + writer.Write( true ); + + writer.Write( m_StuckMenuUses.Length ); + + for ( int i = 0; i < m_StuckMenuUses.Length; ++i ) + { + writer.Write( m_StuckMenuUses[i] ); + } + } + else + { + writer.Write( false ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int LightLevel + { + get + { + return m_LightLevel; + } + set + { + if ( m_LightLevel != value ) + { + m_LightLevel = value; + + CheckLightLevels( false ); + + /*if ( m_NetState != null ) + m_NetState.Send( new PersonalLightLevel( this ) );*/ + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public string Profile + { + get + { + return m_Profile; + } + set + { + m_Profile = value; + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public bool ProfileLocked + { + get + { + return m_ProfileLocked; + } + set + { + m_ProfileLocked = value; + } + } + + [CommandProperty( AccessLevel.GameMaster, AccessLevel.Administrator )] + public bool Player + { + get + { + return m_Player; + } + set + { + m_Player = value; + InvalidateProperties(); + + if ( !m_Player && m_Dex <= 100 && m_CombatTimer != null ) + m_CombatTimer.Priority = TimerPriority.FiftyMS; + else if ( m_CombatTimer != null ) + m_CombatTimer.Priority = TimerPriority.EveryTick; + + CheckStatTimers(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Title + { + get + { + return m_Title; + } + set + { + m_Title = value; + InvalidateProperties(); + } + } + + private static string[] m_AccessLevelNames = new string[] + { + "a player", + "a counselor", + "a game master", + "a seer", + "an administrator", + "a developer", + "an owner" + }; + + public static string GetAccessLevelName( AccessLevel level ) + { + return m_AccessLevelNames[(int) level]; + } + + public virtual bool CanPaperdollBeOpenedBy( Mobile from ) + { + return ( Body.IsHuman || Body.IsGhost || IsBodyMod ); + } + + public virtual void GetChildContextMenuEntries( Mobile from, List list, Item item ) + { + } + + public virtual void GetContextMenuEntries( Mobile from, List list ) + { + if ( m_Deleted ) + return; + + if ( CanPaperdollBeOpenedBy( from ) ) + list.Add( new PaperdollEntry( this ) ); + + if ( from == this && Backpack != null && CanSee( Backpack ) && CheckAlive( false ) ) + list.Add( new OpenBackpackEntry( this ) ); + } + + public void Internalize() + { + Map = Map.Internal; + } + + public List Items + { + get + { + return m_Items; + } + } + + /// + /// Overridable. Virtual event invoked when is added from the Mobile, such as when it is equiped. + /// + /// + /// + public virtual void OnItemAdded( Item item ) + { + } + + /// + /// Overridable. Virtual event invoked when is removed from the Mobile. + /// + /// + /// + public virtual void OnItemRemoved( Item item ) + { + } + + /// + /// Overridable. Virtual event invoked when is becomes a child of the Mobile; it's worn or contained at some level of the Mobile's backpack or bank box + /// + /// + /// + public virtual void OnSubItemAdded( Item item ) + { + } + + /// + /// Overridable. Virtual event invoked when is removed from the Mobile, its backpack, or its bank box. + /// + /// + /// + public virtual void OnSubItemRemoved( Item item ) + { + } + + public virtual void OnItemBounceCleared( Item item ) + { + } + + public virtual void OnSubItemBounceCleared( Item item ) + { + } + + public virtual int MaxWeight { get { return int.MaxValue; } } + + public void AddItem( Item item ) + { + if ( item == null || item.Deleted ) + return; + + if ( item.Parent == this ) + return; + else if ( item.Parent is Mobile ) + ( (Mobile) item.Parent ).RemoveItem( item ); + else if ( item.Parent is Item ) + ( (Item) item.Parent ).RemoveItem( item ); + else + item.SendRemovePacket(); + + item.Parent = this; + item.Map = m_Map; + + m_Items.Add( item ); + + if ( !item.IsVirtualItem ) + { + UpdateTotal( item, TotalType.Gold, item.TotalGold ); + UpdateTotal( item, TotalType.Items, item.TotalItems + 1 ); + UpdateTotal( item, TotalType.Weight, item.TotalWeight + item.PileWeight ); + } + + item.Delta( ItemDelta.Update ); + + item.OnAdded( this ); + OnItemAdded( item ); + + if ( item.PhysicalResistance != 0 || item.FireResistance != 0 || item.ColdResistance != 0 || + item.PoisonResistance != 0 || item.EnergyResistance != 0 ) + UpdateResistances(); + } + + private static IWeapon m_DefaultWeapon; + + public static IWeapon DefaultWeapon + { + get + { + return m_DefaultWeapon; + } + set + { + m_DefaultWeapon = value; + } + } + + public void RemoveItem( Item item ) + { + if ( item == null || m_Items == null ) + return; + + if ( m_Items.Contains( item ) ) + { + item.SendRemovePacket(); + + //int oldCount = m_Items.Count; + + m_Items.Remove( item ); + + if ( !item.IsVirtualItem ) + { + UpdateTotal( item, TotalType.Gold, -item.TotalGold ); + UpdateTotal( item, TotalType.Items, -( item.TotalItems + 1 ) ); + UpdateTotal( item, TotalType.Weight, -( item.TotalWeight + item.PileWeight ) ); + } + + item.Parent = null; + + item.OnRemoved( this ); + OnItemRemoved( item ); + + if ( item.PhysicalResistance != 0 || item.FireResistance != 0 || item.ColdResistance != 0 || + item.PoisonResistance != 0 || item.EnergyResistance != 0 ) + UpdateResistances(); + } + } + + public virtual void Animate( int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay ) + { + Map map = m_Map; + + if ( map != null ) + { + ProcessDelta(); + + Packet p = null; + + IPooledEnumerable eable = map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( this ) ) + { + state.Mobile.ProcessDelta(); + + if ( p == null ) + p = Packet.Acquire( new MobileAnimation( this, action, frameCount, repeatCount, forward, repeat, delay ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public void SendSound( int soundID ) + { + if ( soundID != -1 && m_NetState != null ) + Send( new PlaySound( soundID, this ) ); + } + + public void SendSound( int soundID, IPoint3D p ) + { + if ( soundID != -1 && m_NetState != null ) + Send( new PlaySound( soundID, p ) ); + } + + public void PlaySound( int soundID ) + { + if ( soundID == -1 ) + return; + + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( this ) ) + { + if ( p == null ) + p = Packet.Acquire( new PlaySound( soundID, this ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + [CommandProperty( AccessLevel.Counselor )] + public Skills Skills + { + get + { + return m_Skills; + } + set + { + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.Administrator )] + public AccessLevel AccessLevel + { + get + { + return m_AccessLevel; + } + set + { + AccessLevel oldValue = m_AccessLevel; + + if ( oldValue != value ) + { + m_AccessLevel = value; + Delta( MobileDelta.Noto ); + InvalidateProperties(); + + SendMessage( "Your access level has been changed. You are now {0}.", GetAccessLevelName( value ) ); + + ClearScreen(); + SendEverything(); + + OnAccessLevelChanged( oldValue ); + } + } + } + + public virtual void OnAccessLevelChanged( AccessLevel oldLevel ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Fame + { + get + { + return m_Fame; + } + set + { + int oldValue = m_Fame; + + if ( oldValue != value ) + { + m_Fame = value; + + if ( ShowFameTitle && ( m_Player || m_Body.IsHuman ) && ( oldValue >= 10000 ) != ( value >= 10000 ) ) + InvalidateProperties(); + + OnFameChange( oldValue ); + } + } + } + + public virtual void OnFameChange( int oldValue ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public int Karma + { + get + { + return m_Karma; + } + set + { + int old = m_Karma; + + if ( old != value ) + { + m_Karma = value; + OnKarmaChange( old ); + } + } + } + + public virtual void OnKarmaChange( int oldValue ) + { + } + + // Mobile did something which should unhide him + public virtual void RevealingAction() + { + if ( m_Hidden && m_AccessLevel == AccessLevel.Player ) + Hidden = false; + + DisruptiveAction(); // Anything that unhides you will also distrupt meditation + } + + #region Say/SayTo/Emote/Whisper/Yell + public void SayTo( Mobile to, bool ascii, string text ) + { + PrivateOverheadMessage( MessageType.Regular, m_SpeechHue, ascii, text, to.NetState ); + } + + public void SayTo( Mobile to, string text ) + { + SayTo( to, false, text ); + } + + public void SayTo( Mobile to, string format, params object[] args ) + { + SayTo( to, false, String.Format( format, args ) ); + } + + public void SayTo( Mobile to, bool ascii, string format, params object[] args ) + { + SayTo( to, ascii, String.Format( format, args ) ); + } + + public void SayTo( Mobile to, int number ) + { + to.Send( new MessageLocalized( m_Serial, Body, MessageType.Regular, m_SpeechHue, 3, number, Name, "" ) ); + } + + public void SayTo( Mobile to, int number, string args ) + { + to.Send( new MessageLocalized( m_Serial, Body, MessageType.Regular, m_SpeechHue, 3, number, Name, args ) ); + } + + public void Say( bool ascii, string text ) + { + PublicOverheadMessage( MessageType.Regular, m_SpeechHue, ascii, text ); + } + + public void Say( string text ) + { + PublicOverheadMessage( MessageType.Regular, m_SpeechHue, false, text ); + } + + public void Say( string format, params object[] args ) + { + Say( String.Format( format, args ) ); + } + + public void Say( int number, AffixType type, string affix, string args ) + { + PublicOverheadMessage( MessageType.Regular, m_SpeechHue, number, type, affix, args ); + } + + public void Say( int number ) + { + Say( number, "" ); + } + + public void Say( int number, string args ) + { + PublicOverheadMessage( MessageType.Regular, m_SpeechHue, number, args ); + } + + public void Emote( string text ) + { + PublicOverheadMessage( MessageType.Emote, m_EmoteHue, false, text ); + } + + public void Emote( string format, params object[] args ) + { + Emote( String.Format( format, args ) ); + } + + public void Emote( int number ) + { + Emote( number, "" ); + } + + public void Emote( int number, string args ) + { + PublicOverheadMessage( MessageType.Emote, m_EmoteHue, number, args ); + } + + public void Whisper( string text ) + { + PublicOverheadMessage( MessageType.Whisper, m_WhisperHue, false, text ); + } + + public void Whisper( string format, params object[] args ) + { + Whisper( String.Format( format, args ) ); + } + + public void Whisper( int number ) + { + Whisper( number, "" ); + } + + public void Whisper( int number, string args ) + { + PublicOverheadMessage( MessageType.Whisper, m_WhisperHue, number, args ); + } + + public void Yell( string text ) + { + PublicOverheadMessage( MessageType.Yell, m_YellHue, false, text ); + } + + public void Yell( string format, params object[] args ) + { + Yell( String.Format( format, args ) ); + } + + public void Yell( int number ) + { + Yell( number, "" ); + } + + public void Yell( int number, string args ) + { + PublicOverheadMessage( MessageType.Yell, m_YellHue, number, args ); + } + #endregion + + [CommandProperty( AccessLevel.GameMaster )] + public bool Blessed + { + get + { + return m_Blessed; + } + set + { + if ( m_Blessed != value ) + { + m_Blessed = value; + Delta( MobileDelta.Flags ); + } + } + } + + public void SendRemovePacket() + { + SendRemovePacket( true ); + } + + public void SendRemovePacket( bool everyone ) + { + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state != m_NetState && ( everyone || !state.Mobile.CanSee( this ) ) ) + { + if ( p == null ) + p = this.RemovePacket; + + state.Send( p ); + } + } + + eable.Free(); + } + } + + public void ClearScreen() + { + NetState ns = m_NetState; + + if ( m_Map != null && ns != null ) + { + IPooledEnumerable eable = m_Map.GetObjectsInRange( m_Location, Core.GlobalMaxUpdateRange ); + + foreach ( object o in eable ) + { + if ( o is Mobile ) + { + Mobile m = (Mobile) o; + + if ( m != this && Utility.InUpdateRange( m_Location, m.m_Location ) ) + ns.Send( m.RemovePacket ); + } + else if ( o is Item ) + { + Item item = (Item) o; + + if ( InRange( item.Location, item.GetUpdateRange( this ) ) ) + ns.Send( item.RemovePacket ); + } + } + + eable.Free(); + } + } + + public bool Send( Packet p ) + { + return Send( p, false ); + } + + public bool Send( Packet p, bool throwOnOffline ) + { + if ( m_NetState != null ) + { + m_NetState.Send( p ); + return true; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Packet could not be sent." ); + } + else + { + return false; + } + } + + public bool SendHuePicker( HuePicker p ) + { + return SendHuePicker( p, false ); + } + + public bool SendHuePicker( HuePicker p, bool throwOnOffline ) + { + if ( m_NetState != null ) + { + p.SendTo( m_NetState ); + return true; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Hue picker could not be sent." ); + } + else + { + return false; + } + } + + public Gump FindGump( Type type ) + { + NetState ns = m_NetState; + + if ( ns != null ) + { + List gumps = ns.Gumps; + + for ( int i = 0; i < gumps.Count; ++i ) + { + if ( gumps[i].GetType() == type ) + return gumps[i]; + } + } + + return null; + } + + private static readonly ClientVersion m_SendsReponseOnForcedGumpClose = new ClientVersion( "4.0.9b" ); + + public bool CloseGump( Type type ) + { + return CloseGump( type, 0, false ); + } + + public bool CloseGump( Type type, int buttonID ) + { + return CloseGump( type, buttonID, false ); + } + + public bool CloseGump( Type type, int buttonID, bool throwOnOffline ) + { + if ( m_NetState != null ) + { + if ( HasGump( type ) ) + m_NetState.Send( new CloseGump( Gump.GetTypeID( type ), buttonID ) ); + + Gump gump = FindGump( type ); + + if ( gump != null ) + { + m_NetState.Send( new CloseGump( Gump.GetTypeID( type ), buttonID ) ); + + if ( m_NetState.Version > m_SendsReponseOnForcedGumpClose ) + { + m_NetState.Gumps.Remove( gump ); + gump.OnResponse( m_NetState, new RelayInfo( buttonID, new int[0], new TextRelay[0] ) ); + } + } + + return true; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Gump close packet could not be sent." ); + } + else + { + return false; + } + } + + public bool CloseAllGumps() + { + return CloseAllGumps( false ); + } + + public bool CloseAllGumps( bool throwOnOffline ) + { + NetState ns = m_NetState; + + if ( ns != null ) + { + List gumps = ns.Gumps; + + for ( int i = 0; i < gumps.Count; ++i ) + ns.Send( new CloseGump( gumps[i].TypeID, 0 ) ); + + for ( int i = gumps.Count - 1; i >= 0; --i ) + { + Gump gump = gumps[i]; + + ns.Send( new CloseGump( gump.TypeID, 0 ) ); + + if ( ns.Version > m_SendsReponseOnForcedGumpClose ) + { + ns.Gumps.Remove( gump ); + gump.OnResponse( ns, new RelayInfo( 0, new int[0], new TextRelay[0] ) ); + } + } + + return true; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Gump close packets could not be sent." ); + } + else + { + return false; + } + } + + public bool HasGump( Type type ) + { + return HasGump( type, false ); + } + + public bool HasGump( Type type, bool throwOnOffline ) + { + NetState ns = m_NetState; + + if ( ns != null ) + { + bool contains = false; + List gumps = ns.Gumps; + + for ( int i = 0; !contains && i < gumps.Count; ++i ) + contains = ( gumps[i].GetType() == type ); + + return contains; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Mobile is not connected." ); + } + else + { + return false; + } + } + + public bool SendGump( Gump g ) + { + return SendGump( g, false ); + } + + public bool SendGump( Gump g, bool throwOnOffline ) + { + if ( m_NetState != null ) + { + g.SendTo( m_NetState ); + return true; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Gump could not be sent." ); + } + else + { + return false; + } + } + + public bool SendMenu( IMenu m ) + { + return SendMenu( m, false ); + } + + public bool SendMenu( IMenu m, bool throwOnOffline ) + { + if ( m_NetState != null ) + { + m.SendTo( m_NetState ); + return true; + } + else if ( throwOnOffline ) + { + throw new MobileNotConnectedException( this, "Menu could not be sent." ); + } + else + { + return false; + } + } + + /// + /// Overridable. Event invoked before the Mobile says something. + /// + /// + public virtual void OnSaid( SpeechEventArgs e ) + { + if ( m_Squelched ) + { + if( Core.ML ) + this.SendLocalizedMessage( 500168 ); // You can not say anything, you have been muted. + else + this.SendMessage( "You can not say anything, you have been squelched." ); //Cliloc ITSELF changed during ML. + + e.Blocked = true; + } + + if ( !e.Blocked ) + RevealingAction(); + } + + public virtual bool HandlesOnSpeech( Mobile from ) + { + return false; + } + + /// + /// Overridable. Virtual event invoked when the Mobile hears speech. This event will only be invoked if returns true. + /// + /// + public virtual void OnSpeech( SpeechEventArgs e ) + { + } + + public void SendEverything() + { + NetState ns = m_NetState; + + if ( m_Map != null && ns != null ) + { + IPooledEnumerable eable = m_Map.GetObjectsInRange( m_Location, Core.GlobalMaxUpdateRange ); + + foreach ( object o in eable ) + { + if ( o is Item ) + { + Item item = (Item) o; + + if ( CanSee( item ) && InRange( item.Location, item.GetUpdateRange( this ) ) ) + item.SendInfoTo( ns ); + } + else if ( o is Mobile ) + { + Mobile m = (Mobile) o; + + if ( CanSee( m ) && Utility.InUpdateRange( m_Location, m.m_Location ) ) + { + ns.Send( new MobileIncoming( this, m ) ); + + if ( m.IsDeadBondedPet ) + ns.Send( new BondedStatus( 0, m.m_Serial, 1 ) ); + + if ( ObjectPropertyList.Enabled ) + { + ns.Send( m.OPLPacket ); + + //foreach ( Item item in m.m_Items ) + // ns.Send( item.OPLPacket ); + } + } + } + } + + eable.Free(); + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public Map Map + { + get + { + return m_Map; + } + set + { + if ( m_Deleted ) + return; + + if ( m_Map != value ) + { + if ( m_NetState != null ) + m_NetState.ValidateAllTrades(); + + Map oldMap = m_Map; + + if ( m_Map != null ) + { + m_Map.OnLeave( this ); + + ClearScreen(); + SendRemovePacket(); + } + + for ( int i = 0; i < m_Items.Count; ++i ) + m_Items[i].Map = value; + + m_Map = value; + + UpdateRegion(); + + if ( m_Map != null ) + m_Map.OnEnter( this ); + + NetState ns = m_NetState; + + if ( m_Map != null ) + { + if ( ns != null && m_Map != null ) + { + ns.Sequence = 0; + ns.Send( new MapChange( this ) ); + ns.Send( new MapPatches() ); + ns.Send( SeasonChange.Instantiate( GetSeason(), true ) ); + ns.Send( new MobileUpdate( this ) ); + ClearFastwalkStack(); + } + } + + if ( ns != null ) + { + if ( m_Map != null ) + Send( new ServerChange( this, m_Map ) ); + + ns.Sequence = 0; + ClearFastwalkStack(); + + Send( new MobileIncoming( this, this ) ); + Send( new MobileUpdate( this ) ); + CheckLightLevels( true ); + Send( new MobileUpdate( this ) ); + } + + SendEverything(); + SendIncomingPacket(); + + if ( ns != null ) + { + ns.Sequence = 0; + ClearFastwalkStack(); + + Send( new MobileIncoming( this, this ) ); + Send( SupportedFeatures.Instantiate( ns.Account ) ); + Send( new MobileUpdate( this ) ); + Send( new MobileAttributes( this ) ); + } + + OnMapChange( oldMap ); + } + } + } + + public void UpdateRegion() + { + if ( m_Deleted ) + return; + + Region newRegion = Region.Find( m_Location, m_Map ); + + if ( newRegion != m_Region ) + { + Region.OnRegionChange( this, m_Region, newRegion ); + + m_Region = newRegion; + OnRegionChange( m_Region, newRegion ); + } + } + + /// + /// Overridable. Virtual event invoked when changes. + /// + protected virtual void OnMapChange( Map oldMap ) + { + } + + public virtual bool CanBeBeneficial( Mobile target ) + { + return CanBeBeneficial( target, true, false ); + } + + public virtual bool CanBeBeneficial( Mobile target, bool message ) + { + return CanBeBeneficial( target, message, false ); + } + + public virtual bool CanBeBeneficial( Mobile target, bool message, bool allowDead ) + { + if ( target == null ) + return false; + + if ( m_Deleted || target.m_Deleted || !Alive || IsDeadBondedPet || ( !allowDead && ( !target.Alive || IsDeadBondedPet ) ) ) + { + if ( message ) + SendLocalizedMessage( 1001017 ); // You can not perform beneficial acts on your target. + + return false; + } + + if ( target == this ) + return true; + + if ( /*m_Player &&*/ !Region.AllowBeneficial( this, target ) ) + { + // TODO: Pets + //if ( !(target.m_Player || target.Body.IsHuman || target.Body.IsAnimal) ) + //{ + if ( message ) + SendLocalizedMessage( 1001017 ); // You can not perform beneficial acts on your target. + + return false; + //} + } + + return true; + } + + public virtual bool IsBeneficialCriminal( Mobile target ) + { + if ( this == target ) + return false; + + int n = Notoriety.Compute( this, target ); + + return ( n == Notoriety.Criminal || n == Notoriety.Murderer ); + } + + /// + /// Overridable. Event invoked when the Mobile does a beneficial action. + /// + public virtual void OnBeneficialAction( Mobile target, bool isCriminal ) + { + if ( isCriminal ) + CriminalAction( false ); + } + + public virtual void DoBeneficial( Mobile target ) + { + if ( target == null ) + return; + + OnBeneficialAction( target, IsBeneficialCriminal( target ) ); + + Region.OnBeneficialAction( this, target ); + target.Region.OnGotBeneficialAction( this, target ); + } + + public virtual bool BeneficialCheck( Mobile target ) + { + if ( CanBeBeneficial( target, true ) ) + { + DoBeneficial( target ); + return true; + } + + return false; + } + + public virtual bool CanBeHarmful( Mobile target ) + { + return CanBeHarmful( target, true ); + } + + public virtual bool CanBeHarmful( Mobile target, bool message ) + { + return CanBeHarmful( target, message, false ); + } + + public virtual bool CanBeHarmful( Mobile target, bool message, bool ignoreOurBlessedness ) + { + if ( target == null ) + return false; + + if ( m_Deleted || ( !ignoreOurBlessedness && m_Blessed ) || target.m_Deleted || target.m_Blessed || !Alive || IsDeadBondedPet || !target.Alive || target.IsDeadBondedPet ) + { + if ( message ) + SendLocalizedMessage( 1001018 ); // You can not perform negative acts on your target. + + return false; + } + + if ( target == this ) + return true; + + // TODO: Pets + if ( /*m_Player &&*/ !Region.AllowHarmful( this, target ) )//(target.m_Player || target.Body.IsHuman) && !Region.AllowHarmful( this, target ) ) + { + if ( message ) + SendLocalizedMessage( 1001018 ); // You can not perform negative acts on your target. + + return false; + } + + return true; + } + + public virtual int Luck + { + get { return 0; } + } + + public virtual bool IsHarmfulCriminal( Mobile target ) + { + if ( this == target ) + return false; + + return ( Notoriety.Compute( this, target ) == Notoriety.Innocent ); + } + + /// + /// Overridable. Event invoked when the Mobile does a harmful action. + /// + public virtual void OnHarmfulAction( Mobile target, bool isCriminal ) + { + if ( isCriminal ) + CriminalAction( false ); + } + + public virtual void DoHarmful( Mobile target ) + { + DoHarmful( target, false ); + } + + public virtual void DoHarmful( Mobile target, bool indirect ) + { + if ( target == null || m_Deleted ) + return; + + bool isCriminal = IsHarmfulCriminal( target ); + + OnHarmfulAction( target, isCriminal ); + target.AggressiveAction( this, isCriminal ); + + this.Region.OnDidHarmful( this, target ); + target.Region.OnGotHarmful( this, target ); + + if ( !indirect ) + Combatant = target; + + if ( m_ExpireCombatant == null ) + m_ExpireCombatant = new ExpireCombatantTimer( this ); + else + m_ExpireCombatant.Stop(); + + m_ExpireCombatant.Start(); + } + + public virtual bool HarmfulCheck( Mobile target ) + { + if ( CanBeHarmful( target ) ) + { + DoHarmful( target ); + return true; + } + + return false; + } + + #region Stats + + /// + /// Gets a list of all StatMod's currently active for the Mobile. + /// + public List StatMods { get { return m_StatMods; } } + + public bool RemoveStatMod( string name ) + { + for ( int i = 0; i < m_StatMods.Count; ++i ) + { + StatMod check = m_StatMods[i]; + + if ( check.Name == name ) + { + m_StatMods.RemoveAt( i ); + CheckStatTimers(); + Delta( MobileDelta.Stat | GetStatDelta( check.Type ) ); + return true; + } + } + + return false; + } + + public StatMod GetStatMod( string name ) + { + for ( int i = 0; i < m_StatMods.Count; ++i ) + { + StatMod check = m_StatMods[i]; + + if ( check.Name == name ) + return check; + } + + return null; + } + + public void AddStatMod( StatMod mod ) + { + for ( int i = 0; i < m_StatMods.Count; ++i ) + { + StatMod check = m_StatMods[i]; + + if ( check.Name == mod.Name ) + { + Delta( MobileDelta.Stat | GetStatDelta( check.Type ) ); + m_StatMods.RemoveAt( i ); + break; + } + } + + m_StatMods.Add( mod ); + Delta( MobileDelta.Stat | GetStatDelta( mod.Type ) ); + CheckStatTimers(); + } + + private MobileDelta GetStatDelta( StatType type ) + { + MobileDelta delta = 0; + + if ( ( type & StatType.Str ) != 0 ) + delta |= MobileDelta.Hits; + + if ( ( type & StatType.Dex ) != 0 ) + delta |= MobileDelta.Stam; + + if ( ( type & StatType.Int ) != 0 ) + delta |= MobileDelta.Mana; + + return delta; + } + + /// + /// Computes the total modified offset for the specified stat type. Expired instances are removed. + /// + public int GetStatOffset( StatType type ) + { + int offset = 0; + + for ( int i = 0; i < m_StatMods.Count; ++i ) + { + StatMod mod = m_StatMods[i]; + + if ( mod.HasElapsed() ) + { + m_StatMods.RemoveAt( i ); + Delta( MobileDelta.Stat | GetStatDelta( mod.Type ) ); + CheckStatTimers(); + + --i; + } + else if ( ( mod.Type & type ) != 0 ) + { + offset += mod.Offset; + } + } + + return offset; + } + + /// + /// Overridable. Virtual event invoked when the changes. + /// + /// + /// + public virtual void OnRawStrChange( int oldValue ) + { + } + + /// + /// Overridable. Virtual event invoked when changes. + /// + /// + /// + public virtual void OnRawDexChange( int oldValue ) + { + } + + /// + /// Overridable. Virtual event invoked when the changes. + /// + /// + /// + public virtual void OnRawIntChange( int oldValue ) + { + } + + /// + /// Overridable. Virtual event invoked when the , , or changes. + /// + /// + /// + /// + public virtual void OnRawStatChange( StatType stat, int oldValue ) + { + } + + /// + /// Gets or sets the base, unmodified, strength of the Mobile. Ranges from 1 to 65000, inclusive. + /// + /// + /// + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public int RawStr + { + get + { + return m_Str; + } + set + { + if ( value < 1 ) + value = 1; + else if ( value > 65000 ) + value = 65000; + + if ( m_Str != value ) + { + int oldValue = m_Str; + + m_Str = value; + Delta( MobileDelta.Stat | MobileDelta.Hits ); + + if ( Hits < HitsMax ) + { + if ( m_HitsTimer == null ) + m_HitsTimer = new HitsTimer( this ); + + m_HitsTimer.Start(); + } + else if ( Hits > HitsMax ) + { + Hits = HitsMax; + } + + OnRawStrChange( oldValue ); + OnRawStatChange( StatType.Str, oldValue ); + } + } + } + + /// + /// Gets or sets the effective strength of the Mobile. This is the sum of the plus any additional modifiers. Any attempts to set this value when under the influence of a will result in no change. It ranges from 1 to 65000, inclusive. + /// + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual int Str + { + get + { + int value = m_Str + GetStatOffset( StatType.Str ); + + if ( value < 1 ) + value = 1; + else if ( value > 65000 ) + value = 65000; + + return value; + } + set + { + if ( m_StatMods.Count == 0 ) + RawStr = value; + } + } + + /// + /// Gets or sets the base, unmodified, dexterity of the Mobile. Ranges from 1 to 65000, inclusive. + /// + /// + /// + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public int RawDex + { + get + { + return m_Dex; + } + set + { + if ( value < 1 ) + value = 1; + else if ( value > 65000 ) + value = 65000; + + if ( m_Dex != value ) + { + int oldValue = m_Dex; + + m_Dex = value; + Delta( MobileDelta.Stat | MobileDelta.Stam ); + + if ( Stam < StamMax ) + { + if ( m_StamTimer == null ) + m_StamTimer = new StamTimer( this ); + + m_StamTimer.Start(); + } + else if ( Stam > StamMax ) + { + Stam = StamMax; + } + + OnRawDexChange( oldValue ); + OnRawStatChange( StatType.Dex, oldValue ); + } + } + } + + /// + /// Gets or sets the effective dexterity of the Mobile. This is the sum of the plus any additional modifiers. Any attempts to set this value when under the influence of a will result in no change. It ranges from 1 to 65000, inclusive. + /// + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual int Dex + { + get + { + int value = m_Dex + GetStatOffset( StatType.Dex ); + + if ( value < 1 ) + value = 1; + else if ( value > 65000 ) + value = 65000; + + return value; + } + set + { + if ( m_StatMods.Count == 0 ) + RawDex = value; + } + } + + /// + /// Gets or sets the base, unmodified, intelligence of the Mobile. Ranges from 1 to 65000, inclusive. + /// + /// + /// + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public int RawInt + { + get + { + return m_Int; + } + set + { + if ( value < 1 ) + value = 1; + else if ( value > 65000 ) + value = 65000; + + if ( m_Int != value ) + { + int oldValue = m_Int; + + m_Int = value; + Delta( MobileDelta.Stat | MobileDelta.Mana ); + + if ( Mana < ManaMax ) + { + if ( m_ManaTimer == null ) + m_ManaTimer = new ManaTimer( this ); + + m_ManaTimer.Start(); + } + else if ( Mana > ManaMax ) + { + Mana = ManaMax; + } + + OnRawIntChange( oldValue ); + OnRawStatChange( StatType.Int, oldValue ); + } + } + } + + /// + /// Gets or sets the effective intelligence of the Mobile. This is the sum of the plus any additional modifiers. Any attempts to set this value when under the influence of a will result in no change. It ranges from 1 to 65000, inclusive. + /// + /// + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual int Int + { + get + { + int value = m_Int + GetStatOffset( StatType.Int ); + + if ( value < 1 ) + value = 1; + else if ( value > 65000 ) + value = 65000; + + return value; + } + set + { + if ( m_StatMods.Count == 0 ) + RawInt = value; + } + } + + public virtual void OnHitsChange( int oldValue ) + { + } + + public virtual void OnStamChange( int oldValue ) + { + } + + public virtual void OnManaChange( int oldValue ) + { + } + + /// + /// Gets or sets the current hit point of the Mobile. This value ranges from 0 to , inclusive. When set to the value of , the CanReportMurder flag of all aggressors is reset to false, and the list of damage entries is cleared. + /// + [CommandProperty( AccessLevel.GameMaster )] + public int Hits + { + get + { + return m_Hits; + } + set + { + if ( m_Deleted ) + return; + + if ( value < 0 ) + { + value = 0; + } + else if ( value >= HitsMax ) + { + value = HitsMax; + + if ( m_HitsTimer != null ) + m_HitsTimer.Stop(); + + for ( int i = 0; i < m_Aggressors.Count; i++ ) //reset reports on full HP + m_Aggressors[i].CanReportMurder = false; + + if ( m_DamageEntries.Count > 0 ) + m_DamageEntries.Clear(); // reset damage entries on full HP + } + + if ( value < HitsMax ) + { + if ( CanRegenHits ) + { + if ( m_HitsTimer == null ) + m_HitsTimer = new HitsTimer( this ); + + m_HitsTimer.Start(); + } + else if ( m_HitsTimer != null ) + { + m_HitsTimer.Stop(); + } + } + + if ( m_Hits != value ) + { + int oldValue = m_Hits; + m_Hits = value; + Delta( MobileDelta.Hits ); + OnHitsChange( oldValue ); + } + } + } + + /// + /// Overridable. Gets the maximum hit point of the Mobile. By default, this returns: 50 + ( / 2) + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual int HitsMax + { + get + { + return 50 + ( Str / 2 ); + } + } + + /// + /// Gets or sets the current stamina of the Mobile. This value ranges from 0 to , inclusive. + /// + [CommandProperty( AccessLevel.GameMaster )] + public int Stam + { + get + { + return m_Stam; + } + set + { + if ( m_Deleted ) + return; + + if ( value < 0 ) + { + value = 0; + } + else if ( value >= StamMax ) + { + value = StamMax; + + if ( m_StamTimer != null ) + m_StamTimer.Stop(); + } + + if ( value < StamMax ) + { + if ( CanRegenStam ) + { + if ( m_StamTimer == null ) + m_StamTimer = new StamTimer( this ); + + m_StamTimer.Start(); + } + else if ( m_StamTimer != null ) + { + m_StamTimer.Stop(); + } + } + + if ( m_Stam != value ) + { + int oldValue = m_Stam; + m_Stam = value; + Delta( MobileDelta.Stam ); + OnStamChange( oldValue ); + } + } + } + + /// + /// Overridable. Gets the maximum stamina of the Mobile. By default, this returns: + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual int StamMax + { + get + { + return Dex; + } + } + + /// + /// Gets or sets the current stamina of the Mobile. This value ranges from 0 to , inclusive. + /// + [CommandProperty( AccessLevel.GameMaster )] + public int Mana + { + get + { + return m_Mana; + } + set + { + if ( m_Deleted ) + return; + + if ( value < 0 ) + { + value = 0; + } + else if ( value >= ManaMax ) + { + value = ManaMax; + + if ( m_ManaTimer != null ) + m_ManaTimer.Stop(); + + if ( Meditating ) + { + Meditating = false; + SendLocalizedMessage( 501846 ); // You are at peace. + } + } + + if ( value < ManaMax ) + { + if ( CanRegenMana ) + { + if ( m_ManaTimer == null ) + m_ManaTimer = new ManaTimer( this ); + + m_ManaTimer.Start(); + } + else if ( m_ManaTimer != null ) + { + m_ManaTimer.Stop(); + } + } + + if ( m_Mana != value ) + { + int oldValue = m_Mana; + m_Mana = value; + Delta( MobileDelta.Mana ); + OnManaChange( oldValue ); + } + } + } + + /// + /// Overridable. Gets the maximum mana of the Mobile. By default, this returns: + /// + [CommandProperty( AccessLevel.GameMaster )] + public virtual int ManaMax + { + get + { + return Int; + } + } + + #endregion + + public virtual int HuedItemID + { + get + { + return ( m_Female ? 0x2107 : 0x2106 ); + } + } + + private int m_HueMod = -1; + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public int HueMod + { + get + { + return m_HueMod; + } + set + { + if ( m_HueMod != value ) + { + m_HueMod = value; + + Delta( MobileDelta.Hue ); + } + } + } + + [Hue, CommandProperty( AccessLevel.GameMaster )] + public virtual int Hue + { + get + { + if ( m_HueMod != -1 ) + return m_HueMod; + + return m_Hue; + } + set + { + int oldHue = m_Hue; + + if ( oldHue != value ) + { + m_Hue = value; + + Delta( MobileDelta.Hue ); + } + } + } + + + public void SetDirection( Direction dir ) + { + m_Direction = dir; + } + + [CommandProperty( AccessLevel.GameMaster )] + public Direction Direction + { + get + { + return m_Direction; + } + set + { + if ( m_Direction != value ) + { + m_Direction = value; + + Delta( MobileDelta.Direction ); + //ProcessDelta(); + } + } + } + + public virtual int GetSeason() + { + if ( m_Map != null ) + return m_Map.Season; + + return 1; + } + + public virtual int GetPacketFlags() + { + int flags = 0x0; + + if ( m_Female ) + flags |= 0x02; + + if ( m_Poison != null ) + flags |= 0x04; + + if ( m_Blessed || m_YellowHealthbar ) + flags |= 0x08; + + if ( m_Warmode ) + flags |= 0x40; + + if ( m_Hidden ) + flags |= 0x80; + + return flags; + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Female + { + get + { + return m_Female; + } + set + { + if ( m_Female != value ) + { + m_Female = value; + Delta( MobileDelta.Flags ); + OnGenderChanged( !m_Female ); + } + } + } + + public virtual void OnGenderChanged( bool oldFemale ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Warmode + { + get + { + return m_Warmode; + } + set + { + if ( m_Deleted ) + return; + + if ( m_Warmode != value ) + { + if ( m_AutoManifestTimer != null ) + { + m_AutoManifestTimer.Stop(); + m_AutoManifestTimer = null; + } + + m_Warmode = value; + Delta( MobileDelta.Flags ); + + if ( m_NetState != null ) + Send( SetWarMode.Instantiate( value ) ); + + if ( !m_Warmode ) + Combatant = null; + + if ( !Alive ) + { + if ( value ) + Delta( MobileDelta.GhostUpdate ); + else + SendRemovePacket( false ); + } + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public virtual bool Hidden + { + get + { + return m_Hidden; + } + set + { + if ( m_Hidden != value ) + { + m_AllowedStealthSteps = 0; + + m_Hidden = value; + //Delta( MobileDelta.Flags ); + + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( !state.Mobile.CanSee( this ) ) + { + if ( p == null ) + p = this.RemovePacket; + + state.Send( p ); + } + else + { + state.Send( new MobileIncoming( state.Mobile, this ) ); + + if ( IsDeadBondedPet ) + state.Send( new BondedStatus( 0, m_Serial, 1 ) ); + + if ( ObjectPropertyList.Enabled ) + { + state.Send( OPLPacket ); + + //foreach ( Item item in m_Items ) + // state.Send( item.OPLPacket ); + } + } + } + + eable.Free(); + } + } + } + } + + public virtual void OnConnected() + { + } + + public virtual void OnDisconnected() + { + } + + public virtual void OnNetStateChanged() + { + } + + public NetState NetState + { + get + { + if ( m_NetState != null && m_NetState.Socket == null ) + NetState = null; + + return m_NetState; + } + set + { + if ( m_NetState != value ) + { + if ( m_Map != null ) + m_Map.OnClientChange( m_NetState, value, this ); + + if ( m_Target != null ) + m_Target.Cancel( this, TargetCancelType.Disconnected ); + + if ( m_QuestArrow != null ) + QuestArrow = null; + + if ( m_Spell != null ) + m_Spell.OnConnectionChanged(); + + //if ( m_Spell != null ) + // m_Spell.FinishSequence(); + + if ( m_NetState != null ) + m_NetState.CancelAllTrades(); + + BankBox box = FindBankNoCreate(); + + if ( box != null && box.Opened ) + box.Close(); + + // REMOVED: + //m_Actions.Clear(); + + m_NetState = value; + + if ( m_NetState == null ) + { + OnDisconnected(); + EventSink.InvokeDisconnected( new DisconnectedEventArgs( this ) ); + + // Disconnected, start the logout timer + + if ( m_LogoutTimer == null ) + m_LogoutTimer = new LogoutTimer( this ); + else + m_LogoutTimer.Stop(); + + m_LogoutTimer.Delay = GetLogoutDelay(); + m_LogoutTimer.Start(); + } + else + { + OnConnected(); + EventSink.InvokeConnected( new ConnectedEventArgs( this ) ); + + // Connected, stop the logout timer and if needed, move to the world + + if ( m_LogoutTimer != null ) + m_LogoutTimer.Stop(); + + m_LogoutTimer = null; + + if ( m_Map == Map.Internal && m_LogoutMap != null ) + { + Map = m_LogoutMap; + Location = m_LogoutLocation; + } + } + + for ( int i = m_Items.Count - 1; i >= 0; --i ) + { + if ( i >= m_Items.Count ) + continue; + + Item item = m_Items[i]; + + if ( item is SecureTradeContainer ) + { + for ( int j = item.Items.Count - 1; j >= 0; --j ) + { + if ( j < item.Items.Count ) + { + item.Items[j].OnSecureTrade( this, this, this, false ); + AddToBackpack( item.Items[j] ); + } + } + + item.Delete(); + } + } + + DropHolding(); + OnNetStateChanged(); + } + } + } + + public virtual bool CanSee( object o ) + { + if ( o is Item ) + { + return CanSee( (Item) o ); + } + else if ( o is Mobile ) + { + return CanSee( (Mobile) o ); + } + else + { + return true; + } + } + + public virtual bool CanSee( Item item ) + { + if ( m_Map == Map.Internal ) + return false; + else if ( item.Map == Map.Internal ) + return false; + + if ( item.Parent != null ) + { + if ( item.Parent is Item ) + { + if ( !CanSee( (Item) item.Parent ) ) + return false; + } + else if ( item.Parent is Mobile ) + { + if ( !CanSee( (Mobile) item.Parent ) ) + return false; + } + } + + if ( item is BankBox ) + { + BankBox box = item as BankBox; + + if ( box != null && m_AccessLevel <= AccessLevel.Counselor && ( box.Owner != this || !box.Opened ) ) + return false; + } + else if ( item is SecureTradeContainer ) + { + SecureTrade trade = ( (SecureTradeContainer) item ).Trade; + + if ( trade != null && trade.From.Mobile != this && trade.To.Mobile != this ) + return false; + } + + return !item.Deleted && item.Map == m_Map && ( item.Visible || m_AccessLevel > AccessLevel.Counselor ); + } + + public virtual bool CanSee( Mobile m ) + { + if ( m_Deleted || m.m_Deleted || m_Map == Map.Internal || m.m_Map == Map.Internal ) + return false; + + return this == m || ( + m.m_Map == m_Map && + ( !m.Hidden || ( m_AccessLevel != AccessLevel.Player && ( m_AccessLevel >= m.AccessLevel || m_AccessLevel >= AccessLevel.Administrator )) ) && //Admins/devs can still see/find eachother though admin gump, they should be able to see 'em too. + ( ( m.Alive || ( Core.SE && Skills.SpiritSpeak.Value >= 100.0 ) ) || !Alive || m_AccessLevel > AccessLevel.Player || m.Warmode ) ); + + } + + public virtual bool CanBeRenamedBy( Mobile from ) + { + return ( from.AccessLevel >= AccessLevel.GameMaster && from.m_AccessLevel > m_AccessLevel ); + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Language + { + get + { + return m_Language; + } + set + { + if ( m_Language != value ) + m_Language = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int SpeechHue + { + get + { + return m_SpeechHue; + } + set + { + m_SpeechHue = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int EmoteHue + { + get + { + return m_EmoteHue; + } + set + { + m_EmoteHue = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int WhisperHue + { + get + { + return m_WhisperHue; + } + set + { + m_WhisperHue = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int YellHue + { + get + { + return m_YellHue; + } + set + { + m_YellHue = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string GuildTitle + { + get + { + return m_GuildTitle; + } + set + { + string old = m_GuildTitle; + + if ( old != value ) + { + m_GuildTitle = value; + + if ( m_Guild != null && !m_Guild.Disbanded && m_GuildTitle != null ) + this.SendLocalizedMessage( 1018026, true, m_GuildTitle ); // Your guild title has changed : + + InvalidateProperties(); + + OnGuildTitleChange( old ); + } + } + } + + public virtual void OnGuildTitleChange( string oldTitle ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool DisplayGuildTitle + { + get + { + return m_DisplayGuildTitle; + } + set + { + m_DisplayGuildTitle = value; + InvalidateProperties(); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Mobile GuildFealty + { + get + { + return m_GuildFealty; + } + set + { + m_GuildFealty = value; + } + } + + private string m_NameMod; + + [CommandProperty( AccessLevel.GameMaster )] + public string NameMod + { + get + { + return m_NameMod; + } + set + { + if ( m_NameMod != value ) + { + m_NameMod = value; + Delta( MobileDelta.Name ); + InvalidateProperties(); + } + } + } + + private bool m_YellowHealthbar; + + [CommandProperty( AccessLevel.GameMaster )] + public bool YellowHealthbar + { + get + { + return m_YellowHealthbar; + } + set + { + m_YellowHealthbar = value; + Delta( MobileDelta.Flags ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string RawName + { + get { return m_Name; } + set { Name = value; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public string Name + { + get + { + if ( m_NameMod != null ) + return m_NameMod; + + return m_Name; + } + set + { + if ( m_Name != value ) // I'm leaving out the && m_NameMod == null + { + m_Name = value; + Delta( MobileDelta.Name ); + InvalidateProperties(); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastStrGain + { + get + { + return m_LastStrGain; + } + set + { + m_LastStrGain = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastIntGain + { + get + { + return m_LastIntGain; + } + set + { + m_LastIntGain = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public DateTime LastDexGain + { + get + { + return m_LastDexGain; + } + set + { + m_LastDexGain = value; + } + } + + public DateTime LastStatGain + { + get + { + DateTime d = m_LastStrGain; + + if( m_LastIntGain > d ) + d = m_LastIntGain; + + if( m_LastDexGain > d ) + d = m_LastDexGain; + + return d; + } + set + { + m_LastStrGain = value; + m_LastIntGain = value; + m_LastDexGain = value; + } + } + + public BaseGuild Guild + { + get + { + return m_Guild; + } + set + { + BaseGuild old = m_Guild; + + if ( old != value ) + { + if ( value == null ) + GuildTitle = null; + + m_Guild = value; + + Delta( MobileDelta.Noto ); + InvalidateProperties(); + + OnGuildChange( old ); + } + } + } + + public virtual void OnGuildChange( BaseGuild oldGuild ) + { + } + + public Timer PoisonTimer + { + get { return m_PoisonTimer; } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Poison Poison + { + get + { + return m_Poison; + } + set + { + /*if ( m_Poison != value && (m_Poison == null || value == null || m_Poison.Level < value.Level) ) + {*/ + m_Poison = value; + Delta( MobileDelta.Flags ); + + if ( m_PoisonTimer != null ) + { + m_PoisonTimer.Stop(); + m_PoisonTimer = null; + } + + if ( m_Poison != null ) + { + m_PoisonTimer = m_Poison.ConstructTimer( this ); + + if ( m_PoisonTimer != null ) + m_PoisonTimer.Start(); + } + + CheckStatTimers(); + /*}*/ + } + } + + /// + /// Overridable. Event invoked when a call to failed because returned false: the Mobile was resistant to the poison. By default, this broadcasts an overhead message: * The poison seems to have no effect. * + /// + /// + /// + /// + public virtual void OnPoisonImmunity( Mobile from, Poison poison ) + { + this.PublicOverheadMessage( MessageType.Emote, 0x3B2, 1005534 ); // * The poison seems to have no effect. * + } + + /// + /// Overridable. Virtual event invoked when a call to failed because returned false: the Mobile was already poisoned by an equal or greater strength poison. + /// + /// + /// + /// + public virtual void OnHigherPoison( Mobile from, Poison poison ) + { + } + + /// + /// Overridable. Event invoked when a call to succeeded. By default, this broadcasts an overhead message varying by the level of the poison. Example: * Zippy begins to spasm uncontrollably. * + /// + /// + /// + public virtual void OnPoisoned( Mobile from, Poison poison, Poison oldPoison ) + { + if ( poison != null ) + { + this.LocalOverheadMessage( MessageType.Regular, 0x21, 1042857 + ( poison.Level * 2 ) ); + this.NonlocalOverheadMessage( MessageType.Regular, 0x21, 1042858 + ( poison.Level * 2 ), Name ); + } + } + + /// + /// Overridable. Called from , this method checks if the Mobile is immune to some . If true, will be invoked and is returned. + /// + /// + /// + /// + public virtual bool CheckPoisonImmunity( Mobile from, Poison poison ) + { + return false; + } + + /// + /// Overridable. Called from , this method checks if the Mobile is already poisoned by some of equal or greater strength. If true, will be invoked and is returned. + /// + /// + /// + /// + public virtual bool CheckHigherPoison( Mobile from, Poison poison ) + { + return ( m_Poison != null && m_Poison.Level >= poison.Level ); + } + + /// + /// Overridable. Attempts to apply poison to the Mobile. Checks are made such that no higher poison is active and that the Mobile is not immune to the poison. Provided those assertions are true, the is applied and is invoked. + /// + /// + /// + /// One of four possible values: + /// + /// + /// Cured + /// The parameter was null and so was invoked. + /// + /// + /// HigherPoisonActive + /// The call to returned false. + /// + /// + /// Immune + /// The call to returned false. + /// + /// + /// Poisoned + /// The was successfully applied. + /// + /// + /// + public virtual ApplyPoisonResult ApplyPoison( Mobile from, Poison poison ) + { + if ( poison == null ) + { + CurePoison( from ); + return ApplyPoisonResult.Cured; + } + + if ( CheckHigherPoison( from, poison ) ) + { + OnHigherPoison( from, poison ); + return ApplyPoisonResult.HigherPoisonActive; + } + + if ( CheckPoisonImmunity( from, poison ) ) + { + OnPoisonImmunity( from, poison ); + return ApplyPoisonResult.Immune; + } + + Poison oldPoison = m_Poison; + this.Poison = poison; + + OnPoisoned( from, poison, oldPoison ); + + return ApplyPoisonResult.Poisoned; + } + + /// + /// Overridable. Called from , this method checks to see that the Mobile can be cured of + /// + /// + /// + public virtual bool CheckCure( Mobile from ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when a call to succeeded. + /// + /// + /// + /// + public virtual void OnCured( Mobile from, Poison oldPoison ) + { + } + + /// + /// Overridable. Virtual event invoked when a call to failed. + /// + /// + /// + /// + public virtual void OnFailedCure( Mobile from ) + { + } + + /// + /// Overridable. Attempts to cure any poison that is currently active. + /// + /// True if poison was cured, false if otherwise. + public virtual bool CurePoison( Mobile from ) + { + if ( CheckCure( from ) ) + { + Poison oldPoison = m_Poison; + this.Poison = null; + + OnCured( from, oldPoison ); + + return true; + } + + OnFailedCure( from ); + + return false; + } + + private ISpawner m_Spawner; + + public ISpawner Spawner { get { return m_Spawner; } set { m_Spawner = value; } } + + private Region m_WalkRegion; + + public Region WalkRegion { get { return m_WalkRegion; } set { m_WalkRegion = value; } } + + public virtual void OnBeforeSpawn( Point3D location, Map m ) + { + } + + public virtual void OnAfterSpawn() + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Poisoned + { + get + { + return ( m_Poison != null ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool IsBodyMod + { + get + { + return ( m_BodyMod.BodyID != 0 ); + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public Body BodyMod + { + get + { + return m_BodyMod; + } + set + { + if ( m_BodyMod != value ) + { + m_BodyMod = value; + + Delta( MobileDelta.Body ); + InvalidateProperties(); + + CheckStatTimers(); + } + } + } + + private static int[] m_InvalidBodies = new int[] + { + 32, + 95, + 156, + 197, + 198, + }; + + [Body, CommandProperty( AccessLevel.GameMaster )] + public Body Body + { + get + { + if ( IsBodyMod ) + return m_BodyMod; + + return m_Body; + } + set + { + if ( m_Body != value && !IsBodyMod ) + { + m_Body = SafeBody( value ); + + Delta( MobileDelta.Body ); + InvalidateProperties(); + + CheckStatTimers(); + } + } + } + + public virtual int SafeBody( int body ) + { + int delta = -1; + + for ( int i = 0; delta < 0 && i < m_InvalidBodies.Length; ++i ) + delta = ( m_InvalidBodies[i] - body ); + + if ( delta != 0 ) + return body; + + return 0; + } + + [Body, CommandProperty( AccessLevel.GameMaster )] + public int BodyValue + { + get + { + return Body.BodyID; + } + set + { + Body = value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public Serial Serial + { + get + { + return m_Serial; + } + } + + Point3D IEntity.Location + { + get + { + return m_Location; + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public Point3D Location + { + get + { + return m_Location; + } + set + { + SetLocation( value, true ); + } + } + + public Point3D LogoutLocation + { + get + { + return m_LogoutLocation; + } + set + { + m_LogoutLocation = value; + } + } + + public Map LogoutMap + { + get + { + return m_LogoutMap; + } + set + { + m_LogoutMap = value; + } + } + + public Region Region + { + get + { + if ( m_Region == null ) + if ( this.Map == null ) + return Map.Internal.DefaultRegion; + else + return this.Map.DefaultRegion; + else + return m_Region; + } + } + + public void FreeCache() + { + Packet.Release( ref m_RemovePacket ); + Packet.Release( ref m_PropertyList ); + Packet.Release( ref m_OPLPacket ); + } + + private Packet m_RemovePacket; + + public Packet RemovePacket + { + get + { + if ( m_RemovePacket == null ) + { + m_RemovePacket = new RemoveMobile( this ); + m_RemovePacket.SetStatic(); + } + + return m_RemovePacket; + } + } + + private Packet m_OPLPacket; + + public Packet OPLPacket + { + get + { + if ( m_OPLPacket == null ) + { + m_OPLPacket = new OPLInfo( PropertyList ); + m_OPLPacket.SetStatic(); + } + + return m_OPLPacket; + } + } + + private ObjectPropertyList m_PropertyList; + + public ObjectPropertyList PropertyList + { + get + { + if ( m_PropertyList == null ) + { + m_PropertyList = new ObjectPropertyList( this ); + + GetProperties( m_PropertyList ); + + m_PropertyList.Terminate(); + m_PropertyList.SetStatic(); + } + + return m_PropertyList; + } + } + + public void ClearProperties() + { + Packet.Release( ref m_PropertyList ); + Packet.Release( ref m_OPLPacket ); + } + + public void InvalidateProperties() + { + if ( !Core.AOS ) + return; + + if ( m_Map != null && m_Map != Map.Internal && !World.Loading ) + { + ObjectPropertyList oldList = m_PropertyList; + Packet.Release( ref m_PropertyList ); + ObjectPropertyList newList = PropertyList; + + if ( oldList == null || oldList.Hash != newList.Hash ) + { + Packet.Release( ref m_OPLPacket ); + Delta( MobileDelta.Properties ); + } + } + else + { + ClearProperties(); + } + } + + private int m_SolidHueOverride = -1; + + [CommandProperty( AccessLevel.GameMaster )] + public int SolidHueOverride + { + get { return m_SolidHueOverride; } + set { if ( m_SolidHueOverride == value ) return; m_SolidHueOverride = value; Delta( MobileDelta.Hue | MobileDelta.Body ); } + } + + public virtual void MoveToWorld( Point3D newLocation, Map map ) + { + if ( m_Deleted ) + return; + + if ( m_Map == map ) + { + SetLocation( newLocation, true ); + return; + } + + BankBox box = FindBankNoCreate(); + + if ( box != null && box.Opened ) + box.Close(); + + Point3D oldLocation = m_Location; + Map oldMap = m_Map; + + Region oldRegion = m_Region; + + if ( oldMap != null ) + { + oldMap.OnLeave( this ); + + ClearScreen(); + SendRemovePacket(); + } + + for ( int i = 0; i < m_Items.Count; ++i ) + m_Items[i].Map = map; + + m_Map = map; + + m_Location = newLocation; + + NetState ns = m_NetState; + + if ( m_Map != null ) + { + m_Map.OnEnter( this ); + + UpdateRegion(); + + if ( ns != null && m_Map != null ) + { + ns.Sequence = 0; + ns.Send( new MapChange( this ) ); + ns.Send( new MapPatches() ); + ns.Send( SeasonChange.Instantiate( GetSeason(), true ) ); + ns.Send( new MobileUpdate( this ) ); + ClearFastwalkStack(); + } + } + else + { + UpdateRegion(); + } + + if ( ns != null ) + { + if ( m_Map != null ) + Send( new ServerChange( this, m_Map ) ); + + ns.Sequence = 0; + ClearFastwalkStack(); + + Send( new MobileIncoming( this, this ) ); + Send( new MobileUpdate( this ) ); + CheckLightLevels( true ); + Send( new MobileUpdate( this ) ); + } + + SendEverything(); + SendIncomingPacket(); + + if ( ns != null ) + { + m_NetState.Sequence = 0; + ClearFastwalkStack(); + + Send( new MobileIncoming( this, this ) ); + Send( SupportedFeatures.Instantiate( ns.Account ) ); + Send( new MobileUpdate( this ) ); + Send( new MobileAttributes( this ) ); + } + + OnMapChange( oldMap ); + OnLocationChange( oldLocation ); + + m_Region.OnLocationChanged( this, oldLocation ); + } + + public virtual void SetLocation( Point3D newLocation, bool isTeleport ) + { + if ( m_Deleted ) + return; + + Point3D oldLocation = m_Location; + + if ( oldLocation != newLocation ) + { + m_Location = newLocation; + UpdateRegion(); + + BankBox box = FindBankNoCreate(); + + if ( box != null && box.Opened ) + box.Close(); + + if ( m_NetState != null ) + m_NetState.ValidateAllTrades(); + + if ( m_Map != null ) + m_Map.OnMove( oldLocation, this ); + + if ( isTeleport && m_NetState != null ) + { + m_NetState.Sequence = 0; + m_NetState.Send( new MobileUpdate( this ) ); + ClearFastwalkStack(); + } + + Map map = m_Map; + + if ( map != null ) + { + // First, send a remove message to everyone who can no longer see us. (inOldRange && !inNewRange) + Packet removeThis = null; + + IPooledEnumerable eable = map.GetClientsInRange( oldLocation ); + + foreach ( NetState ns in eable ) + { + if ( ns != m_NetState && !Utility.InUpdateRange( newLocation, ns.Mobile.Location ) ) + { + if ( removeThis == null ) + removeThis = this.RemovePacket; + + ns.Send( removeThis ); + } + } + + eable.Free(); + + NetState ourState = m_NetState; + + // Check to see if we are attached to a client + if ( ourState != null ) + { + eable = map.GetObjectsInRange( newLocation, Core.GlobalMaxUpdateRange ); + + // We are attached to a client, so it's a bit more complex. We need to send new items and people to ourself, and ourself to other clients + foreach ( object o in eable ) + { + if ( o is Item ) + { + Item item = (Item) o; + + int range = item.GetUpdateRange( this ); + Point3D loc = item.Location; + + if ( !Utility.InRange( oldLocation, loc, range ) && Utility.InRange( newLocation, loc, range ) && CanSee( item ) ) + item.SendInfoTo( ourState ); + } + else if ( o != this && o is Mobile ) + { + Mobile m = (Mobile) o; + + if ( !Utility.InUpdateRange( newLocation, m.m_Location ) ) + continue; + + bool inOldRange = Utility.InUpdateRange( oldLocation, m.m_Location ); + + if ( ( isTeleport || !inOldRange ) && m.m_NetState != null && m.CanSee( this ) ) + { + m.m_NetState.Send( new MobileIncoming( m, this ) ); + + if ( IsDeadBondedPet ) + m.m_NetState.Send( new BondedStatus( 0, m_Serial, 1 ) ); + + if ( ObjectPropertyList.Enabled ) + { + m.m_NetState.Send( OPLPacket ); + + //foreach ( Item item in m_Items ) + // m.m_NetState.Send( item.OPLPacket ); + } + } + + if ( !inOldRange && CanSee( m ) ) + { + ourState.Send( new MobileIncoming( this, m ) ); + + if ( m.IsDeadBondedPet ) + ourState.Send( new BondedStatus( 0, m.m_Serial, 1 ) ); + + if ( ObjectPropertyList.Enabled ) + { + ourState.Send( m.OPLPacket ); + + //foreach ( Item item in m.m_Items ) + // ourState.Send( item.OPLPacket ); + } + } + } + } + + eable.Free(); + } + else + { + eable = map.GetClientsInRange( newLocation ); + + // We're not attached to a client, so simply send an Incoming + foreach ( NetState ns in eable ) + { + if ( ( isTeleport || !Utility.InUpdateRange( oldLocation, ns.Mobile.Location ) ) && ns.Mobile.CanSee( this ) ) + { + ns.Send( new MobileIncoming( ns.Mobile, this ) ); + + if ( IsDeadBondedPet ) + ns.Send( new BondedStatus( 0, m_Serial, 1 ) ); + + if ( ObjectPropertyList.Enabled ) + { + ns.Send( OPLPacket ); + + //foreach ( Item item in m_Items ) + // ns.Send( item.OPLPacket ); + } + } + } + + eable.Free(); + } + } + + OnLocationChange( oldLocation ); + + this.Region.OnLocationChanged( this, oldLocation ); + } + } + + /// + /// Overridable. Virtual event invoked when changes. + /// + protected virtual void OnLocationChange( Point3D oldLocation ) + { + } + + + private HairInfo m_Hair; + private FacialHairInfo m_FacialHair; + + [CommandProperty( AccessLevel.GameMaster )] + public int HairItemID + { + get + { + if( m_Hair == null ) + return 0; + + return m_Hair.ItemID; + } + set + { + if( m_Hair == null && value > 0 ) + m_Hair = new HairInfo( value ); + else if( value <= 0 ) + m_Hair = null; + else + m_Hair.ItemID = value; + + Delta( MobileDelta.Hair ); + } + } + +// [CommandProperty( AccessLevel.GameMaster )] +// public int HairSerial { get { return HairInfo.FakeSerial( this ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int FacialHairItemID + { + get + { + if( m_FacialHair == null ) + return 0; + + return m_FacialHair.ItemID; + } + set + { + if( m_FacialHair == null && value > 0 ) + m_FacialHair = new FacialHairInfo( value ); + else if( value <= 0 ) + m_FacialHair = null; + else + m_FacialHair.ItemID = value; + + Delta( MobileDelta.FacialHair ); + } + } + +// [CommandProperty( AccessLevel.GameMaster )] +// public int FacialHairSerial { get { return FacialHairInfo.FakeSerial( this ); } } + + [CommandProperty( AccessLevel.GameMaster )] + public int HairHue { + get + { + if( m_Hair == null ) + return 0; + return m_Hair.Hue; + } + set + { + if( m_Hair != null ) + { + m_Hair.Hue = value; + Delta( MobileDelta.Hair ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int FacialHairHue { + get + { + if( m_FacialHair == null ) + return 0; + + return m_FacialHair.Hue; + } + set + { + if( m_FacialHair != null ) + { + m_FacialHair.Hue = value; + Delta( MobileDelta.FacialHair ); + } + } + } + + public bool HasFreeHand() + { + return FindItemOnLayer( Layer.TwoHanded ) == null; + } + + private IWeapon m_Weapon; + + [CommandProperty( AccessLevel.GameMaster )] + public virtual IWeapon Weapon + { + get + { + Item item = m_Weapon as Item; + + if ( item != null && !item.Deleted && item.Parent == this && CanSee( item ) ) + return m_Weapon; + + m_Weapon = null; + + item = FindItemOnLayer( Layer.OneHanded ); + + if ( item == null ) + item = FindItemOnLayer( Layer.TwoHanded ); + + if ( item is IWeapon ) + return ( m_Weapon = (IWeapon) item ); + else + return GetDefaultWeapon(); + } + } + + public virtual IWeapon GetDefaultWeapon() + { + return m_DefaultWeapon; + } + + private BankBox m_BankBox; + + [CommandProperty( AccessLevel.GameMaster )] + public BankBox BankBox + { + get + { + if ( m_BankBox != null && !m_BankBox.Deleted && m_BankBox.Parent == this ) + return m_BankBox; + + m_BankBox = FindItemOnLayer( Layer.Bank ) as BankBox; + + if ( m_BankBox == null ) + AddItem( m_BankBox = new BankBox( this ) ); + + return m_BankBox; + } + } + + public BankBox FindBankNoCreate() + { + if ( m_BankBox != null && !m_BankBox.Deleted && m_BankBox.Parent == this ) + return m_BankBox; + + m_BankBox = FindItemOnLayer( Layer.Bank ) as BankBox; + + return m_BankBox; + } + + private Container m_Backpack; + + [CommandProperty( AccessLevel.GameMaster )] + public Container Backpack + { + get + { + if ( m_Backpack != null && !m_Backpack.Deleted && m_Backpack.Parent == this ) + return m_Backpack; + + return ( m_Backpack = ( FindItemOnLayer( Layer.Backpack ) as Container ) ); + } + } + + public virtual bool KeepsItemsOnDeath { get { return m_AccessLevel > AccessLevel.Player; } } + + public Item FindItemOnLayer( Layer layer ) + { + List eq = m_Items; + int count = eq.Count; + + for ( int i = 0; i < count; ++i ) + { + Item item = eq[i]; + + if ( !item.Deleted && item.Layer == layer ) + { + return item; + } + } + + return null; + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int X + { + get { return m_Location.m_X; } + set { Location = new Point3D( value, m_Location.m_Y, m_Location.m_Z ); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Y + { + get { return m_Location.m_Y; } + set { Location = new Point3D( m_Location.m_X, value, m_Location.m_Z ); } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Z + { + get { return m_Location.m_Z; } + set { Location = new Point3D( m_Location.m_X, m_Location.m_Y, value ); } + } + + #region Effects & Particles + + public void MovingEffect( IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode ) + { + Effects.SendMovingEffect( this, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode ); + } + + public void MovingEffect( IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes ) + { + Effects.SendMovingEffect( this, to, itemID, speed, duration, fixedDirection, explodes, 0, 0 ); + } + + public void MovingParticles( IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, int unknown ) + { + Effects.SendMovingParticles( this, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, layer, unknown ); + } + + public void MovingParticles( IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, int unknown ) + { + Effects.SendMovingParticles( this, to, itemID, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, (EffectLayer) 255, unknown ); + } + + public void MovingParticles( IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int effect, int explodeEffect, int explodeSound, int unknown ) + { + Effects.SendMovingParticles( this, to, itemID, speed, duration, fixedDirection, explodes, effect, explodeEffect, explodeSound, unknown ); + } + + public void MovingParticles( IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int effect, int explodeEffect, int explodeSound ) + { + Effects.SendMovingParticles( this, to, itemID, speed, duration, fixedDirection, explodes, 0, 0, effect, explodeEffect, explodeSound, 0 ); + } + + public void FixedEffect( int itemID, int speed, int duration, int hue, int renderMode ) + { + Effects.SendTargetEffect( this, itemID, speed, duration, hue, renderMode ); + } + + public void FixedEffect( int itemID, int speed, int duration ) + { + Effects.SendTargetEffect( this, itemID, speed, duration, 0, 0 ); + } + + public void FixedParticles( int itemID, int speed, int duration, int effect, int hue, int renderMode, EffectLayer layer, int unknown ) + { + Effects.SendTargetParticles( this, itemID, speed, duration, hue, renderMode, effect, layer, unknown ); + } + + public void FixedParticles( int itemID, int speed, int duration, int effect, int hue, int renderMode, EffectLayer layer ) + { + Effects.SendTargetParticles( this, itemID, speed, duration, hue, renderMode, effect, layer, 0 ); + } + + public void FixedParticles( int itemID, int speed, int duration, int effect, EffectLayer layer, int unknown ) + { + Effects.SendTargetParticles( this, itemID, speed, duration, 0, 0, effect, layer, unknown ); + } + + public void FixedParticles( int itemID, int speed, int duration, int effect, EffectLayer layer ) + { + Effects.SendTargetParticles( this, itemID, speed, duration, 0, 0, effect, layer, 0 ); + } + + public void BoltEffect( int hue ) + { + Effects.SendBoltEffect( this, true, hue ); + } + + #endregion + + public void SendIncomingPacket() + { + if ( m_Map != null ) + { + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( this ) ) + { + state.Send( new MobileIncoming( state.Mobile, this ) ); + + if ( IsDeadBondedPet ) + state.Send( new BondedStatus( 0, m_Serial, 1 ) ); + + if ( ObjectPropertyList.Enabled ) + { + state.Send( OPLPacket ); + + //foreach ( Item item in m_Items ) + // state.Send( item.OPLPacket ); + } + } + } + + eable.Free(); + } + } + + public bool PlaceInBackpack( Item item ) + { + if ( item.Deleted ) + return false; + + Container pack = this.Backpack; + + return pack != null && pack.TryDropItem( this, item, false ); + } + + public bool AddToBackpack( Item item ) + { + if ( item.Deleted ) + return false; + + if ( !PlaceInBackpack( item ) ) + { + Point3D loc = m_Location; + Map map = m_Map; + + if ( ( map == null || map == Map.Internal ) && m_LogoutMap != null ) + { + loc = m_LogoutLocation; + map = m_LogoutMap; + } + + item.MoveToWorld( loc, map ); + return false; + } + + return true; + } + + public virtual bool CheckLift( Mobile from, Item item, ref LRReason reject ) + { + return true; + } + + public virtual bool CheckNonlocalLift( Mobile from, Item item ) + { + if ( from == this || ( from.AccessLevel > this.AccessLevel && from.AccessLevel >= AccessLevel.GameMaster ) ) + return true; + + return false; + } + + public bool HasTrade + { + get + { + if ( m_NetState != null ) + return m_NetState.Trades.Count > 0; + + return false; + } + } + + public virtual bool CheckTrade( Mobile to, Item item, SecureTradeContainer cont, bool message, bool checkItems, int plusItems, int plusWeight ) + { + return true; + } + + /// + /// Overridable. Event invoked when a Mobile () drops an onto the Mobile. + /// + public virtual bool OnDragDrop( Mobile from, Item dropped ) + { + if ( from == this ) + { + Container pack = this.Backpack; + + if ( pack != null ) + return dropped.DropToItem( from, pack, new Point3D( -1, -1, 0 ) ); + + return false; + } + else if ( from.Player && this.Player && from.Alive && this.Alive && from.InRange( Location, 2 ) ) + { + NetState ourState = m_NetState; + NetState theirState = from.m_NetState; + + if ( ourState != null && theirState != null ) + { + SecureTradeContainer cont = theirState.FindTradeContainer( this ); + + if ( !from.CheckTrade( this, dropped, cont, true, true, 0, 0 ) ) + return false; + + if ( cont == null ) + cont = theirState.AddTrade( ourState ); + + cont.DropItem( dropped ); + + return true; + } + + return false; + } + else + { + return false; + } + } + + public virtual bool CheckEquip( Item item ) + { + for ( int i = 0; i < m_Items.Count; ++i ) + if ( m_Items[i].CheckConflictingLayer( this, item, item.Layer ) || item.CheckConflictingLayer( this, m_Items[i], m_Items[i].Layer ) ) + return false; + + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to wear . + /// + /// True if the request is accepted, false if otherwise. + public virtual bool OnEquip( Item item ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to lift . + /// + /// True if the lift is allowed, false if otherwise. + /// + /// The following example demonstrates usage. It will disallow any attempts to pick up a pick axe if the Mobile does not have enough strength. + /// + /// public override bool OnDragLift( Item item ) + /// { + /// if ( item is Pickaxe && this.Str < 60 ) + /// { + /// SendMessage( "That is too heavy for you to lift." ); + /// return false; + /// } + /// + /// return base.OnDragLift( item ); + /// } + /// + public virtual bool OnDragLift( Item item ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to drop into a . + /// + /// True if the drop is allowed, false if otherwise. + public virtual bool OnDroppedItemInto( Item item, Container container, Point3D loc ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to drop directly onto another , . This is the case of stacking items. + /// + /// True if the drop is allowed, false if otherwise. + public virtual bool OnDroppedItemOnto( Item item, Item target ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to drop into another , . The target item is most likely a . + /// + /// True if the drop is allowed, false if otherwise. + public virtual bool OnDroppedItemToItem( Item item, Item target, Point3D loc ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to give to a Mobile (). + /// + /// True if the drop is allowed, false if otherwise. + public virtual bool OnDroppedItemToMobile( Item item, Mobile target ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when the Mobile attempts to drop to the world at a . + /// + /// True if the drop is allowed, false if otherwise. + public virtual bool OnDroppedItemToWorld( Item item, Point3D location ) + { + return true; + } + + /// + /// Overridable. Virtual event when successfully uses while it's on this Mobile. + /// + /// + public virtual void OnItemUsed( Mobile from, Item item ) + { + } + + public virtual bool CheckNonlocalDrop( Mobile from, Item item, Item target ) + { + if ( from == this || ( from.AccessLevel > this.AccessLevel && from.AccessLevel >= AccessLevel.GameMaster ) ) + return true; + + return false; + } + + public virtual bool CheckItemUse( Mobile from, Item item ) + { + return true; + } + + /// + /// Overridable. Virtual event invoked when successfully lifts from this Mobile. + /// + /// + public virtual void OnItemLifted( Mobile from, Item item ) + { + } + + public virtual bool AllowItemUse( Item item ) + { + return true; + } + + public virtual bool AllowEquipFrom( Mobile mob ) + { + return ( mob == this || ( mob.AccessLevel >= AccessLevel.GameMaster && mob.AccessLevel > this.AccessLevel ) ); + } + + public virtual bool EquipItem( Item item ) + { + if ( item == null || item.Deleted || !item.CanEquip( this ) ) + return false; + + if ( CheckEquip( item ) && OnEquip( item ) && item.OnEquip( this ) ) + { + if ( m_Spell != null && !m_Spell.OnCasterEquiping( item ) ) + return false; + + //if ( m_Spell != null && m_Spell.State == SpellState.Casting ) + // m_Spell.Disturb( DisturbType.EquipRequest ); + + AddItem( item ); + return true; + } + + return false; + } + + internal int m_TypeRef; + + public Mobile( Serial serial ) + { + m_Region = Map.Internal.DefaultRegion; + m_Serial = serial; + m_Aggressors = new List( 1 ); + m_Aggressed = new List( 1 ); + m_NextSkillTime = DateTime.MinValue; + m_DamageEntries = new List( 1 ); + + Type ourType = this.GetType(); + m_TypeRef = World.m_MobileTypes.IndexOf( ourType ); + + if ( m_TypeRef == -1 ) + { + World.m_MobileTypes.Add( ourType ); + m_TypeRef = World.m_MobileTypes.Count - 1; + } + } + + public Mobile() + { + m_Region = Map.Internal.DefaultRegion; + m_Serial = Server.Serial.NewMobile; + + DefaultMobileInit(); + + World.AddMobile( this ); + + Type ourType = this.GetType(); + m_TypeRef = World.m_MobileTypes.IndexOf( ourType ); + + if ( m_TypeRef == -1 ) + { + World.m_MobileTypes.Add( ourType ); + m_TypeRef = World.m_MobileTypes.Count - 1; + } + } + + public void DefaultMobileInit() + { + m_StatCap = 225; + m_FollowersMax = 5; + m_Skills = new Skills( this ); + m_Items = new List( 1 ); + m_StatMods = new List( 1 ); + m_SkillMods = new List( 1 ); + Map = Map.Internal; + m_AutoPageNotify = true; + m_Aggressors = new List( 1 ); + m_Aggressed = new List( 1 ); + m_Virtues = new VirtueInfo(); + m_Stabled = new List( 1 ); + m_DamageEntries = new List( 1 ); + + m_NextSkillTime = DateTime.MinValue; + m_CreationTime = DateTime.Now; + } + + private static Queue m_DeltaQueue = new Queue(); + + private bool m_InDeltaQueue; + private MobileDelta m_DeltaFlags; + + public virtual void Delta( MobileDelta flag ) + { + if ( m_Map == null || m_Map == Map.Internal || m_Deleted ) + return; + + m_DeltaFlags |= flag; + + if ( !m_InDeltaQueue ) + { + m_InDeltaQueue = true; + + m_DeltaQueue.Enqueue( this ); + } + } + + public Direction GetDirectionTo( int x, int y ) + { + int dx = m_Location.m_X - x; + int dy = m_Location.m_Y - y; + + int rx = ( dx - dy ) * 44; + int ry = ( dx + dy ) * 44; + + int ax = Math.Abs( rx ); + int ay = Math.Abs( ry ); + + Direction ret; + + if ( ( ( ay >> 1 ) - ax ) >= 0 ) + ret = ( ry > 0 ) ? Direction.Up : Direction.Down; + else if ( ( ( ax >> 1 ) - ay ) >= 0 ) + ret = ( rx > 0 ) ? Direction.Left : Direction.Right; + else if ( rx >= 0 && ry >= 0 ) + ret = Direction.West; + else if ( rx >= 0 && ry < 0 ) + ret = Direction.South; + else if ( rx < 0 && ry < 0 ) + ret = Direction.East; + else + ret = Direction.North; + + return ret; + } + + public Direction GetDirectionTo( Point2D p ) + { + return GetDirectionTo( p.m_X, p.m_Y ); + } + + public Direction GetDirectionTo( Point3D p ) + { + return GetDirectionTo( p.m_X, p.m_Y ); + } + + public Direction GetDirectionTo( IPoint2D p ) + { + if ( p == null ) + return Direction.North; + + return GetDirectionTo( p.X, p.Y ); + } + + public virtual void ProcessDelta() + { + Mobile m = this; + MobileDelta delta; + + delta = m.m_DeltaFlags; + + if ( delta == MobileDelta.None ) + return; + + MobileDelta attrs = delta & MobileDelta.Attributes; + + m.m_DeltaFlags = MobileDelta.None; + m.m_InDeltaQueue = false; + + bool sendHits = false, sendStam = false, sendMana = false, sendAll = false, sendAny = false; + bool sendIncoming = false, sendNonlocalIncoming = false; + bool sendUpdate = false, sendRemove = false; + bool sendPublicStats = false, sendPrivateStats = false; + bool sendMoving = false, sendNonlocalMoving = false; + bool sendOPLUpdate = Core.AOS && ( delta & MobileDelta.Properties ) != 0; + + bool sendHair = false, sendFacialHair = false, removeHair = false, removeFacialHair = false; + + if ( attrs != MobileDelta.None ) + { + sendAny = true; + + if ( attrs == MobileDelta.Attributes ) + { + sendAll = true; + } + else + { + sendHits = ( ( attrs & MobileDelta.Hits ) != 0 ); + sendStam = ( ( attrs & MobileDelta.Stam ) != 0 ); + sendMana = ( ( attrs & MobileDelta.Mana ) != 0 ); + } + } + + if ( ( delta & MobileDelta.GhostUpdate ) != 0 ) + { + sendNonlocalIncoming = true; + } + + if ( ( delta & MobileDelta.Hue ) != 0 ) + { + sendNonlocalIncoming = true; + sendUpdate = true; + sendRemove = true; + } + + if ( ( delta & MobileDelta.Direction ) != 0 ) + { + sendNonlocalMoving = true; + sendUpdate = true; + } + + if ( ( delta & MobileDelta.Body ) != 0 ) + { + sendUpdate = true; + sendIncoming = true; + } + + /*if ( (delta & MobileDelta.Hue) != 0 ) + { + sendNonlocalIncoming = true; + sendUpdate = true; + } + else if ( (delta & (MobileDelta.Direction | MobileDelta.Body)) != 0 ) + { + sendNonlocalMoving = true; + sendUpdate = true; + } + else*/ + if ( ( delta & ( MobileDelta.Flags | MobileDelta.Noto ) ) != 0 ) + { + sendMoving = true; + } + + if ( ( delta & MobileDelta.Name ) != 0 ) + { + sendAll = false; + sendHits = false; + sendAny = sendStam || sendMana; + sendPublicStats = true; + } + + if ( ( delta & ( MobileDelta.WeaponDamage | MobileDelta.Resistances | MobileDelta.Stat | MobileDelta.Weight | MobileDelta.Gold | MobileDelta.Armor | MobileDelta.StatCap | MobileDelta.Followers | MobileDelta.TithingPoints | MobileDelta.Race ) ) != 0 ) + { + sendPrivateStats = true; + } + + if( (delta & MobileDelta.Hair) != 0 ) + { + if ( m.HairItemID <= 0 ) + removeHair = true; + + sendHair = true; + } + + if( (delta & MobileDelta.FacialHair) != 0 ) + { + if ( m.FacialHairItemID <= 0 ) + removeFacialHair = true; + + sendFacialHair = true; + } + + Packet[] cache = m_MovingPacketCache; + + if ( sendMoving || sendNonlocalMoving ) + { + for ( int i = 0; i < cache.Length; ++i ) + Packet.Release( ref cache[i] ); + } + + NetState ourState = m.m_NetState; + + if ( ourState != null ) + { + if ( sendUpdate ) + { + ourState.Sequence = 0; + ourState.Send( new MobileUpdate( m ) ); + ClearFastwalkStack(); + } + + if ( sendIncoming ) + ourState.Send( new MobileIncoming( m, m ) ); + + if ( sendMoving ) + { + int noto = Notoriety.Compute( m, m ); + ourState.Send( cache[noto] = Packet.Acquire( new MobileMoving( m, noto ) ) ); + } + + if ( sendPublicStats || sendPrivateStats ) + { + ourState.Send( new MobileStatusExtended( m ) ); + } + else if ( sendAll ) + { + ourState.Send( new MobileAttributes( m ) ); + } + else if ( sendAny ) + { + if ( sendHits ) + ourState.Send( new MobileHits( m ) ); + + if ( sendStam ) + ourState.Send( new MobileStam( m ) ); + + if ( sendMana ) + ourState.Send( new MobileMana( m ) ); + } + + if ( sendStam || sendMana ) + { + IParty ip = m_Party as IParty; + + if ( ip != null && sendStam ) + ip.OnStamChanged( this ); + + if ( ip != null && sendMana ) + ip.OnManaChanged( this ); + } + + if( sendHair ) + { + if( removeHair ) + ourState.Send( new RemoveHair( m ) ); + else + ourState.Send( new HairEquipUpdate( m ) ); + } + + if( sendFacialHair ) + { + if( removeFacialHair ) + ourState.Send( new RemoveFacialHair( m ) ); + else + ourState.Send( new FacialHairEquipUpdate( m ) ); + } + + if ( sendOPLUpdate ) + ourState.Send( OPLPacket ); + } + + sendMoving = sendMoving || sendNonlocalMoving; + sendIncoming = sendIncoming || sendNonlocalIncoming; + sendHits = sendHits || sendAll; + + if( m.m_Map != null && (sendRemove || sendIncoming || sendPublicStats || sendHits || sendMoving || sendOPLUpdate || sendHair || sendFacialHair ) ) + { + Mobile beholder; + + IPooledEnumerable eable = m.Map.GetClientsInRange( m.m_Location ); + + Packet hitsPacket = null; + Packet statPacketTrue = null, statPacketFalse = null; + Packet deadPacket = null; + Packet hairPacket = null, facialhairPacket = null; + + foreach ( NetState state in eable ) + { + beholder = state.Mobile; + + if ( beholder != m && beholder.CanSee( m ) ) + { + if ( sendRemove ) + state.Send( m.RemovePacket ); + + if ( sendIncoming ) + { + state.Send( new MobileIncoming( beholder, m ) ); + + if ( m.IsDeadBondedPet ) + { + if ( deadPacket == null ) + deadPacket = Packet.Acquire( new BondedStatus( 0, m.m_Serial, 1 ) ); + + state.Send( deadPacket ); + } + } + + if ( sendMoving ) + { + int noto = Notoriety.Compute( beholder, m ); + + Packet p = cache[noto]; + + if ( p == null ) + cache[noto] = p = Packet.Acquire( new MobileMoving( m, noto ) ); + + state.Send( p ); + } + + if ( sendPublicStats ) + { + if ( m.CanBeRenamedBy( beholder ) ) + { + if ( statPacketTrue == null ) + statPacketTrue = Packet.Acquire( new MobileStatusCompact( true, m ) ); + + state.Send( statPacketTrue ); + } + else + { + if ( statPacketFalse == null ) + statPacketFalse = Packet.Acquire( new MobileStatusCompact( false, m ) ); + + state.Send( statPacketFalse ); + } + } + else if ( sendHits ) + { + if ( hitsPacket == null ) + hitsPacket = Packet.Acquire( new MobileHitsN( m ) ); + + state.Send( hitsPacket ); + } + + if( sendHair ) + { + if( hairPacket == null ) + { + if( removeHair ) + hairPacket = Packet.Acquire( new RemoveHair( m ) ); + else + hairPacket = Packet.Acquire( new HairEquipUpdate( m ) ); + } + + state.Send( hairPacket ); + } + + if( sendFacialHair ) + { + if( facialhairPacket == null ) + { + if( removeFacialHair ) + facialhairPacket = Packet.Acquire( new RemoveFacialHair( m ) ); + else + facialhairPacket = Packet.Acquire( new FacialHairEquipUpdate( m ) ); + } + + state.Send( facialhairPacket ); + } + + if ( sendOPLUpdate ) + state.Send( OPLPacket ); + } + } + + Packet.Release( hitsPacket ); + Packet.Release( statPacketTrue ); + Packet.Release( statPacketFalse ); + Packet.Release( deadPacket ); + Packet.Release( hairPacket ); + Packet.Release( facialhairPacket ); + + eable.Free(); + } + + if ( sendMoving || sendNonlocalMoving ) + { + for ( int i = 0; i < cache.Length; ++i ) + Packet.Release( ref cache[i] ); + } + } + + public static void ProcessDeltaQueue() + { + int count = m_DeltaQueue.Count; + int index = 0; + + while ( m_DeltaQueue.Count > 0 && index++ < count ) + (m_DeltaQueue.Dequeue()).ProcessDelta(); + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Kills + { + get + { + return m_Kills; + } + set + { + int oldValue = m_Kills; + + if ( m_Kills != value ) + { + m_Kills = value; + + if ( m_Kills < 0 ) + m_Kills = 0; + + if ( ( oldValue >= 5 ) != ( m_Kills >= 5 ) ) + { + Delta( MobileDelta.Noto ); + InvalidateProperties(); + } + + OnKillsChange( oldValue ); + } + } + } + + public virtual void OnKillsChange( int oldValue ) + { + } + + [CommandProperty( AccessLevel.GameMaster )] + public int ShortTermMurders + { + get + { + return m_ShortTermMurders; + } + set + { + if ( m_ShortTermMurders != value ) + { + m_ShortTermMurders = value; + + if ( m_ShortTermMurders < 0 ) + m_ShortTermMurders = 0; + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public bool Criminal + { + get + { + return m_Criminal; + } + set + { + if ( m_Criminal != value ) + { + m_Criminal = value; + Delta( MobileDelta.Noto ); + InvalidateProperties(); + } + + if ( m_Criminal ) + { + if ( m_ExpireCriminal == null ) + m_ExpireCriminal = new ExpireCriminalTimer( this ); + else + m_ExpireCriminal.Stop(); + + m_ExpireCriminal.Start(); + } + else if ( m_ExpireCriminal != null ) + { + m_ExpireCriminal.Stop(); + m_ExpireCriminal = null; + } + } + } + + public bool CheckAlive() + { + return CheckAlive( true ); + } + + public bool CheckAlive( bool message ) + { + if ( !Alive ) + { + if ( message ) + this.LocalOverheadMessage( MessageType.Regular, 0x3B2, 1019048 ); // I am dead and cannot do that. + + return false; + } + else + { + return true; + } + } + + #region Overhead messages + + public void PublicOverheadMessage( MessageType type, int hue, bool ascii, string text ) + { + PublicOverheadMessage( type, hue, ascii, text, true ); + } + + public void PublicOverheadMessage( MessageType type, int hue, bool ascii, string text, bool noLineOfSight ) + { + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( this ) && ( noLineOfSight || state.Mobile.InLOS( this ) ) ) + { + if ( p == null ) + { + if ( ascii ) + p = new AsciiMessage( m_Serial, Body, type, hue, 3, Name, text ); + else + p = new UnicodeMessage( m_Serial, Body, type, hue, 3, m_Language, Name, text ); + + p.Acquire(); + } + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public void PublicOverheadMessage( MessageType type, int hue, int number ) + { + PublicOverheadMessage( type, hue, number, "", true ); + } + + public void PublicOverheadMessage( MessageType type, int hue, int number, string args ) + { + PublicOverheadMessage( type, hue, number, args, true ); + } + + public void PublicOverheadMessage( MessageType type, int hue, int number, string args, bool noLineOfSight ) + { + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( this ) && ( noLineOfSight || state.Mobile.InLOS( this ) ) ) + { + if ( p == null ) + p = Packet.Acquire( new MessageLocalized( m_Serial, Body, type, hue, 3, number, Name, args ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public void PublicOverheadMessage( MessageType type, int hue, int number, AffixType affixType, string affix, string args ) + { + PublicOverheadMessage( type, hue, number, affixType, affix, args, true ); + } + + public void PublicOverheadMessage( MessageType type, int hue, int number, AffixType affixType, string affix, string args, bool noLineOfSight ) + { + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state.Mobile.CanSee( this ) && ( noLineOfSight || state.Mobile.InLOS( this ) ) ) + { + if ( p == null ) + p = Packet.Acquire( new MessageLocalizedAffix( m_Serial, Body, type, hue, 3, number, Name, affixType, affix, args ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public void PrivateOverheadMessage( MessageType type, int hue, bool ascii, string text, NetState state ) + { + if ( state == null ) + return; + + if ( ascii ) + state.Send( new AsciiMessage( m_Serial, Body, type, hue, 3, Name, text ) ); + else + state.Send( new UnicodeMessage( m_Serial, Body, type, hue, 3, m_Language, Name, text ) ); + } + + public void PrivateOverheadMessage( MessageType type, int hue, int number, NetState state ) + { + PrivateOverheadMessage( type, hue, number, "", state ); + } + + public void PrivateOverheadMessage( MessageType type, int hue, int number, string args, NetState state ) + { + if ( state == null ) + return; + + state.Send( new MessageLocalized( m_Serial, Body, type, hue, 3, number, Name, args ) ); + } + + public void LocalOverheadMessage( MessageType type, int hue, bool ascii, string text ) + { + NetState ns = m_NetState; + + if ( ns != null ) + { + if ( ascii ) + ns.Send( new AsciiMessage( m_Serial, Body, type, hue, 3, Name, text ) ); + else + ns.Send( new UnicodeMessage( m_Serial, Body, type, hue, 3, m_Language, Name, text ) ); + } + } + + public void LocalOverheadMessage( MessageType type, int hue, int number ) + { + LocalOverheadMessage( type, hue, number, "" ); + } + + public void LocalOverheadMessage( MessageType type, int hue, int number, string args ) + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( new MessageLocalized( m_Serial, Body, type, hue, 3, number, Name, args ) ); + } + + public void NonlocalOverheadMessage( MessageType type, int hue, int number ) + { + NonlocalOverheadMessage( type, hue, number, "" ); + } + + public void NonlocalOverheadMessage( MessageType type, int hue, int number, string args ) + { + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state != m_NetState && state.Mobile.CanSee( this ) ) + { + if ( p == null ) + p = Packet.Acquire( new MessageLocalized( m_Serial, Body, type, hue, 3, number, Name, args ) ); + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + public void NonlocalOverheadMessage( MessageType type, int hue, bool ascii, string text ) + { + if ( m_Map != null ) + { + Packet p = null; + + IPooledEnumerable eable = m_Map.GetClientsInRange( m_Location ); + + foreach ( NetState state in eable ) + { + if ( state != m_NetState && state.Mobile.CanSee( this ) ) + { + if ( p == null ) + { + if ( ascii ) + p = new AsciiMessage( m_Serial, Body, type, hue, 3, Name, text ); + else + p = new UnicodeMessage( m_Serial, Body, type, hue, 3, Language, Name, text ); + + p.Acquire(); + } + + state.Send( p ); + } + } + + Packet.Release( p ); + + eable.Free(); + } + } + + #endregion + + #region SendLocalizedMessage + + public void SendLocalizedMessage( int number ) + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( MessageLocalized.InstantiateGeneric( number ) ); + } + + public void SendLocalizedMessage( int number, string args ) + { + SendLocalizedMessage( number, args, 0x3B2 ); + } + + public void SendLocalizedMessage( int number, string args, int hue ) + { + if ( hue == 0x3B2 && ( args == null || args.Length == 0 ) ) + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( MessageLocalized.InstantiateGeneric( number ) ); + } + else + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( new MessageLocalized( Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", args ) ); + } + } + + public void SendLocalizedMessage( int number, bool append, string affix ) + { + SendLocalizedMessage( number, append, affix, "", 0x3B2 ); + } + + public void SendLocalizedMessage( int number, bool append, string affix, string args ) + { + SendLocalizedMessage( number, append, affix, args ); + } + + public void SendLocalizedMessage( int number, bool append, string affix, string args, int hue ) + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( new MessageLocalizedAffix( Serial.MinusOne, -1, MessageType.Regular, hue, 3, number, "System", ( append ? AffixType.Append : AffixType.Prepend ) | AffixType.System, affix, args ) ); + } + + #endregion + + public void LaunchBrowser( string url ) + { + if ( m_NetState != null ) + m_NetState.LaunchBrowser( url ); + } + + #region Send[ASCII]Message + + public void SendMessage( string text ) + { + SendMessage( 0x3B2, text ); + } + + public void SendMessage( string format, params object[] args ) + { + SendMessage( 0x3B2, String.Format( format, args ) ); + } + + public void SendMessage( int hue, string text ) + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( new UnicodeMessage( Serial.MinusOne, -1, MessageType.Regular, hue, 3, "ENU", "System", text ) ); + } + + public void SendMessage( int hue, string format, params object[] args ) + { + SendMessage( hue, String.Format( format, args ) ); + } + + public void SendAsciiMessage( string text ) + { + SendAsciiMessage( 0x3B2, text ); + } + + public void SendAsciiMessage( string format, params object[] args ) + { + SendAsciiMessage( 0x3B2, String.Format( format, args ) ); + } + + public void SendAsciiMessage( int hue, string text ) + { + NetState ns = m_NetState; + + if ( ns != null ) + ns.Send( new AsciiMessage( Serial.MinusOne, -1, MessageType.Regular, hue, 3, "System", text ) ); + } + + public void SendAsciiMessage( int hue, string format, params object[] args ) + { + SendAsciiMessage( hue, String.Format( format, args ) ); + } + + #endregion + + public bool InRange( Point2D p, int range ) + { + return ( p.m_X >= ( m_Location.m_X - range ) ) + && ( p.m_X <= ( m_Location.m_X + range ) ) + && ( p.m_Y >= ( m_Location.m_Y - range ) ) + && ( p.m_Y <= ( m_Location.m_Y + range ) ); + } + + public bool InRange( Point3D p, int range ) + { + return ( p.m_X >= ( m_Location.m_X - range ) ) + && ( p.m_X <= ( m_Location.m_X + range ) ) + && ( p.m_Y >= ( m_Location.m_Y - range ) ) + && ( p.m_Y <= ( m_Location.m_Y + range ) ); + } + + public bool InRange( IPoint2D p, int range ) + { + return ( p.X >= ( m_Location.m_X - range ) ) + && ( p.X <= ( m_Location.m_X + range ) ) + && ( p.Y >= ( m_Location.m_Y - range ) ) + && ( p.Y <= ( m_Location.m_Y + range ) ); + } + + public void InitStats( int str, int dex, int intel ) + { + m_Str = str; + m_Dex = dex; + m_Int = intel; + + Hits = HitsMax; + Stam = StamMax; + Mana = ManaMax; + + Delta( MobileDelta.Stat | MobileDelta.Hits | MobileDelta.Stam | MobileDelta.Mana ); + } + + public virtual void DisplayPaperdollTo( Mobile to ) + { + EventSink.InvokePaperdollRequest( new PaperdollRequestEventArgs( to, this ) ); + } + + private static bool m_DisableDismountInWarmode; + + public static bool DisableDismountInWarmode { get { return m_DisableDismountInWarmode; } set { m_DisableDismountInWarmode = value; } } + + /// + /// Overridable. Event invoked when the Mobile is double clicked. By default, this method can either dismount or open the paperdoll. + /// + /// + /// + public virtual void OnDoubleClick( Mobile from ) + { + if ( this == from && ( !m_DisableDismountInWarmode || !m_Warmode ) ) + { + IMount mount = Mount; + + if ( mount != null ) + { + mount.Rider = null; + return; + } + } + + if ( CanPaperdollBeOpenedBy( from ) ) + DisplayPaperdollTo( from ); + } + + /// + /// Overridable. Virtual event invoked when the Mobile is double clicked by someone who is over 18 tiles away. + /// + /// + public virtual void OnDoubleClickOutOfRange( Mobile from ) + { + } + + /// + /// Overridable. Virtual event invoked when the Mobile is double clicked by someone who can no longer see the Mobile. This may happen, for example, using 'Last Object' after the Mobile has hidden. + /// + /// + public virtual void OnDoubleClickCantSee( Mobile from ) + { + } + + /// + /// Overridable. Event invoked when the Mobile is double clicked by someone who is not alive. Similar to , this method will show the paperdoll. It does not, however, provide any dismount functionality. + /// + /// + public virtual void OnDoubleClickDead( Mobile from ) + { + if ( CanPaperdollBeOpenedBy( from ) ) + DisplayPaperdollTo( from ); + } + + /// + /// Overridable. Event invoked when the Mobile requests to open his own paperdoll via the 'Open Paperdoll' macro. + /// + public virtual void OnPaperdollRequest() + { + if ( CanPaperdollBeOpenedBy( this ) ) + DisplayPaperdollTo( this ); + } + + private static int m_BodyWeight = 14; + + public static int BodyWeight { get { return m_BodyWeight; } set { m_BodyWeight = value; } } + + /// + /// Overridable. Event invoked when wants to see this Mobile's stats. + /// + /// + public virtual void OnStatsQuery( Mobile from ) + { + if ( from.Map == this.Map && Utility.InUpdateRange( this, from ) && from.CanSee( this ) ) + from.Send( new MobileStatus( from, this ) ); + + if ( from == this ) + Send( new StatLockInfo( this ) ); + + IParty ip = m_Party as IParty; + + if ( ip != null ) + ip.OnStatsQuery( from, this ); + } + + /// + /// Overridable. Event invoked when wants to see this Mobile's skills. + /// + public virtual void OnSkillsQuery( Mobile from ) + { + if ( from == this ) + Send( new SkillUpdate( m_Skills ) ); + } + + /// + /// Overridable. Virtual event invoked when changes. + /// + public virtual void OnRegionChange( Region Old, Region New ) + { + } + + private Item m_MountItem; + + [CommandProperty( AccessLevel.GameMaster )] + public IMount Mount + { + get + { + IMountItem mountItem = null; + + if ( m_MountItem != null && !m_MountItem.Deleted && m_MountItem.Parent == this ) + mountItem = (IMountItem) m_MountItem; + + if ( mountItem == null ) + m_MountItem = ( mountItem = ( FindItemOnLayer( Layer.Mount ) as IMountItem ) ) as Item; + + return mountItem == null ? null : mountItem.Mount; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Mounted + { + get + { + return ( Mount != null ); + } + } + + private QuestArrow m_QuestArrow; + + public QuestArrow QuestArrow + { + get + { + return m_QuestArrow; + } + set + { + if ( m_QuestArrow != value ) + { + if ( m_QuestArrow != null ) + m_QuestArrow.Stop(); + + m_QuestArrow = value; + } + } + } + + private static string[] m_GuildTypes = new string[] + { + "", + " (Chaos)", + " (Order)" + }; + + public virtual bool CanTarget { get { return true; } } + public virtual bool ClickTitle { get { return true; } } + + public virtual bool PropertyTitle { get { return m_OldPropertyTitles ? ClickTitle : true; } } + + private static bool m_DisableHiddenSelfClick = true; + private static bool m_AsciiClickMessage = true; + private static bool m_GuildClickMessage = true; + private static bool m_OldPropertyTitles; + + public static bool DisableHiddenSelfClick { get { return m_DisableHiddenSelfClick; } set { m_DisableHiddenSelfClick = value; } } + public static bool AsciiClickMessage { get { return m_AsciiClickMessage; } set { m_AsciiClickMessage = value; } } + public static bool GuildClickMessage { get { return m_GuildClickMessage; } set { m_GuildClickMessage = value; } } + public static bool OldPropertyTitles { get { return m_OldPropertyTitles; } set { m_OldPropertyTitles = value; } } + + public virtual bool ShowFameTitle { get { return true; } }//(m_Player || m_Body.IsHuman) && m_Fame >= 10000; } + + /// + /// Overridable. Event invoked when the Mobile is single clicked. + /// + public virtual void OnSingleClick( Mobile from ) + { + if ( m_Deleted ) + return; + else if ( AccessLevel == AccessLevel.Player && DisableHiddenSelfClick && Hidden && from == this ) + return; + + if ( m_GuildClickMessage ) + { + BaseGuild guild = m_Guild; + + if ( guild != null && ( m_DisplayGuildTitle || ( m_Player && guild.Type != GuildType.Regular ) ) ) + { + string title = GuildTitle; + string type; + + if ( title == null ) + title = ""; + else + title = title.Trim(); + + if ( guild.Type >= 0 && (int) guild.Type < m_GuildTypes.Length ) + type = m_GuildTypes[(int) guild.Type]; + else + type = ""; + + string text = String.Format( title.Length <= 0 ? "[{1}]{2}" : "[{0}, {1}]{2}", title, guild.Abbreviation, type ); + + PrivateOverheadMessage( MessageType.Regular, SpeechHue, true, text, from.NetState ); + } + } + + int hue; + + if ( m_NameHue != -1 ) + hue = m_NameHue; + else if ( AccessLevel > AccessLevel.Player ) + hue = 11; + else + hue = Notoriety.GetHue( Notoriety.Compute( from, this ) ); + + string name = Name; + + if ( name == null ) + name = String.Empty; + + string prefix = ""; + + if ( ShowFameTitle && ( m_Player || m_Body.IsHuman ) && m_Fame >= 10000 ) + prefix = ( m_Female ? "Lady" : "Lord" ); + + string suffix = ""; + + if ( ClickTitle && Title != null && Title.Length > 0 ) + suffix = Title; + + suffix = ApplyNameSuffix( suffix ); + + string val; + + if ( prefix.Length > 0 && suffix.Length > 0 ) + val = String.Concat( prefix, " ", name, " ", suffix ); + else if ( prefix.Length > 0 ) + val = String.Concat( prefix, " ", name ); + else if ( suffix.Length > 0 ) + val = String.Concat( name, " ", suffix ); + else + val = name; + + PrivateOverheadMessage( MessageType.Label, hue, m_AsciiClickMessage, val, from.NetState ); + } + + public bool CheckSkill( SkillName skill, double minSkill, double maxSkill ) + { + if ( m_SkillCheckLocationHandler == null ) + return false; + else + return m_SkillCheckLocationHandler( this, skill, minSkill, maxSkill ); + } + + public bool CheckSkill( SkillName skill, double chance ) + { + if ( m_SkillCheckDirectLocationHandler == null ) + return false; + else + return m_SkillCheckDirectLocationHandler( this, skill, chance ); + } + + public bool CheckTargetSkill( SkillName skill, object target, double minSkill, double maxSkill ) + { + if ( m_SkillCheckTargetHandler == null ) + return false; + else + return m_SkillCheckTargetHandler( this, skill, target, minSkill, maxSkill ); + } + + public bool CheckTargetSkill( SkillName skill, object target, double chance ) + { + if ( m_SkillCheckDirectTargetHandler == null ) + return false; + else + return m_SkillCheckDirectTargetHandler( this, skill, target, chance ); + } + + public virtual void DisruptiveAction() + { + if ( Meditating ) + { + Meditating = false; + SendLocalizedMessage( 500134 ); // You stop meditating. + } + } + + #region Armor + public Item ShieldArmor + { + get + { + return FindItemOnLayer( Layer.TwoHanded ) as Item; + } + } + + public Item NeckArmor + { + get + { + return FindItemOnLayer( Layer.Neck ) as Item; + } + } + + public Item HandArmor + { + get + { + return FindItemOnLayer( Layer.Gloves ) as Item; + } + } + + public Item HeadArmor + { + get + { + return FindItemOnLayer( Layer.Helm ) as Item; + } + } + + public Item ArmsArmor + { + get + { + return FindItemOnLayer( Layer.Arms ) as Item; + } + } + + public Item LegsArmor + { + get + { + Item ar = FindItemOnLayer( Layer.InnerLegs ) as Item; + + if ( ar == null ) + ar = FindItemOnLayer( Layer.Pants ) as Item; + + return ar; + } + } + + public Item ChestArmor + { + get + { + Item ar = FindItemOnLayer( Layer.InnerTorso ) as Item; + + if ( ar == null ) + ar = FindItemOnLayer( Layer.Shirt ) as Item; + + return ar; + } + } + + public Item Talisman + { + get + { + return FindItemOnLayer( Layer.Talisman ) as Item; + } + } + #endregion + + /// + /// Gets or sets the maximum attainable value for , , and . + /// + [CommandProperty( AccessLevel.GameMaster )] + public int StatCap + { + get + { + return m_StatCap; + } + set + { + if ( m_StatCap != value ) + { + m_StatCap = value; + + Delta( MobileDelta.StatCap ); + } + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool Meditating + { + get + { + return m_Meditating; + } + set + { + m_Meditating = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool CanSwim + { + get + { + return m_CanSwim; + } + set + { + m_CanSwim = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool CantWalk + { + get + { + return m_CantWalk; + } + set + { + m_CantWalk = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public bool CanHearGhosts + { + get + { + return m_CanHearGhosts || AccessLevel >= AccessLevel.Counselor; + } + set + { + m_CanHearGhosts = value; + } + } + + [CommandProperty( AccessLevel.GameMaster )] + public int RawStatTotal + { + get + { + return RawStr + RawDex + RawInt; + } + } + + public DateTime NextSpellTime + { + get + { + return m_NextSpellTime; + } + set + { + m_NextSpellTime = value; + } + } + + /// + /// Overridable. Virtual event invoked when the sector this Mobile is in gets activated. + /// + public virtual void OnSectorActivate() + { + } + + /// + /// Overridable. Virtual event invoked when the sector this Mobile is in gets deactivated. + /// + public virtual void OnSectorDeactivate() + { + } + } +} \ No newline at end of file diff --git a/Server/Movement.cs b/Server/Movement.cs new file mode 100644 index 000000000..54d6b302d --- /dev/null +++ b/Server/Movement.cs @@ -0,0 +1,75 @@ +/*************************************************************************** + * Movement.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Movement.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; + +namespace Server.Movement +{ + public class Movement + { + private static IMovementImpl m_Impl; + + public static IMovementImpl Impl + { + get{ return m_Impl; } + set{ m_Impl = value; } + } + + public static bool CheckMovement( Mobile m, Direction d, out int newZ ) + { + if ( m_Impl != null ) + return m_Impl.CheckMovement( m, d, out newZ ); + + newZ = m.Z; + return false; + } + + public static bool CheckMovement( Mobile m, Map map, Point3D loc, Direction d, out int newZ ) + { + if ( m_Impl != null ) + return m_Impl.CheckMovement( m, map, loc, d, out newZ ); + + newZ = m.Z; + return false; + } + + public static void Offset( Direction d, ref int x, ref int y ) + { + switch ( d & Direction.Mask ) + { + case Direction.North: --y; break; + case Direction.South: ++y; break; + case Direction.West: --x; break; + case Direction.East: ++x; break; + case Direction.Right: ++x; --y; break; + case Direction.Left: --x; ++y; break; + case Direction.Down: ++x; ++y; break; + case Direction.Up: --x; --y; break; + } + } + } + + public interface IMovementImpl + { + bool CheckMovement( Mobile m, Direction d, out int newZ ); + bool CheckMovement( Mobile m, Map map, Point3D loc, Direction d, out int newZ ); + } +} \ No newline at end of file diff --git a/Server/MultiData.cs b/Server/MultiData.cs new file mode 100644 index 000000000..1bd7c5cfc --- /dev/null +++ b/Server/MultiData.cs @@ -0,0 +1,584 @@ +/*************************************************************************** + * MultiData.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: MultiData.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.IO; + +namespace Server +{ + public class MultiData + { + private static MultiComponentList[] m_Components; + + private static FileStream m_Index, m_Stream; + private static BinaryReader m_IndexReader, m_StreamReader; + + public static MultiComponentList GetComponents( int multiID ) + { + MultiComponentList mcl; + + multiID &= 0x3FFF; + + if ( multiID >= 0 && multiID < m_Components.Length ) + { + mcl = m_Components[multiID]; + + if ( mcl == null ) + m_Components[multiID] = mcl = Load( multiID ); + } + else + { + mcl = MultiComponentList.Empty; + } + + return mcl; + } + + public static MultiComponentList Load( int multiID ) + { + try + { + m_IndexReader.BaseStream.Seek( multiID * 12, SeekOrigin.Begin ); + + int lookup = m_IndexReader.ReadInt32(); + int length = m_IndexReader.ReadInt32(); + + if ( lookup < 0 || length <= 0 ) + return MultiComponentList.Empty; + + m_StreamReader.BaseStream.Seek( lookup, SeekOrigin.Begin ); + + return new MultiComponentList( m_StreamReader, length / 12 ); + } + catch + { + return MultiComponentList.Empty; + } + } + + static MultiData() + { + string idxPath = Core.FindDataFile( "multi.idx" ); + string mulPath = Core.FindDataFile( "multi.mul" ); + + if ( File.Exists( idxPath ) && File.Exists( mulPath ) ) + { + m_Index = new FileStream( idxPath, FileMode.Open, FileAccess.Read, FileShare.Read ); + m_IndexReader = new BinaryReader( m_Index ); + + m_Stream = new FileStream( mulPath, FileMode.Open, FileAccess.Read, FileShare.Read ); + m_StreamReader = new BinaryReader( m_Stream ); + + m_Components = new MultiComponentList[(int)(m_Index.Length / 12)]; + + string vdPath = Core.FindDataFile( "verdata.mul" ); + + if ( File.Exists( vdPath ) ) + { + using ( FileStream fs = new FileStream( vdPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + BinaryReader bin = new BinaryReader( fs ); + + int count = bin.ReadInt32(); + + for ( int i = 0; i < count; ++i ) + { + int file = bin.ReadInt32(); + int index = bin.ReadInt32(); + int lookup = bin.ReadInt32(); + int length = bin.ReadInt32(); + int extra = bin.ReadInt32(); + + if ( file == 14 && index >= 0 && index < m_Components.Length && lookup >= 0 && length > 0 ) + { + bin.BaseStream.Seek( lookup, SeekOrigin.Begin ); + + m_Components[index] = new MultiComponentList( bin, length / 12 ); + + bin.BaseStream.Seek( 24 + (i * 20), SeekOrigin.Begin ); + } + } + + bin.Close(); + } + } + } + else + { + Console.WriteLine( "Warning: Multi data files not found" ); + + m_Components = new MultiComponentList[0]; + } + } + } + + public struct MultiTileEntry + { + public short m_ItemID; + public short m_OffsetX, m_OffsetY, m_OffsetZ; + public int m_Flags; + + public MultiTileEntry( short itemID, short xOffset, short yOffset, short zOffset, int flags ) + { + m_ItemID = itemID; + m_OffsetX = xOffset; + m_OffsetY = yOffset; + m_OffsetZ = zOffset; + m_Flags = flags; + } + } + + public sealed class MultiComponentList + { + private Point2D m_Min, m_Max, m_Center; + private int m_Width, m_Height; + private Tile[][][] m_Tiles; + private MultiTileEntry[] m_List; + + public static readonly MultiComponentList Empty = new MultiComponentList(); + + public Point2D Min{ get{ return m_Min; } } + public Point2D Max{ get{ return m_Max; } } + + public Point2D Center{ get{ return m_Center; } } + + public int Width{ get{ return m_Width; } } + public int Height{ get{ return m_Height; } } + + public Tile[][][] Tiles{ get{ return m_Tiles; } } + public MultiTileEntry[] List{ get{ return m_List; } } + + public void Add( int itemID, int x, int y, int z ) + { + itemID &= 0x3FFF; + itemID |= 0x4000; + + int vx = x + m_Center.m_X; + int vy = y + m_Center.m_Y; + + if ( vx >= 0 && vx < m_Width && vy >= 0 && vy < m_Height ) + { + Tile[] oldTiles = m_Tiles[vx][vy]; + + for ( int i = oldTiles.Length - 1; i >= 0; --i ) + { + ItemData data = TileData.ItemTable[itemID & 0x3FFF]; + + if ( oldTiles[i].Z == z && (oldTiles[i].Height > 0 == data.Height > 0 ) ) + { + bool newIsRoof = ( data.Flags & TileFlag.Roof) != 0; + bool oldIsRoof = (TileData.ItemTable[oldTiles[i].ID & 0x3FFF].Flags & TileFlag.Roof ) != 0; + + if ( newIsRoof == oldIsRoof ) + Remove( oldTiles[i].ID, x, y, z ); + } + } + + oldTiles = m_Tiles[vx][vy]; + + Tile[] newTiles = new Tile[oldTiles.Length + 1]; + + for ( int i = 0; i < oldTiles.Length; ++i ) + newTiles[i] = oldTiles[i]; + + newTiles[oldTiles.Length] = new Tile( (short)itemID, (sbyte)z ); + + m_Tiles[vx][vy] = newTiles; + + MultiTileEntry[] oldList = m_List; + MultiTileEntry[] newList = new MultiTileEntry[oldList.Length + 1]; + + for ( int i = 0; i < oldList.Length; ++i ) + newList[i] = oldList[i]; + + newList[oldList.Length] = new MultiTileEntry( (short)itemID, (short)x, (short)y, (short)z, 1 ); + + m_List = newList; + + if ( x < m_Min.m_X ) + m_Min.m_X = x; + + if ( y < m_Min.m_Y ) + m_Min.m_Y = y; + + if ( x > m_Max.m_X ) + m_Max.m_X = x; + + if ( y > m_Max.m_Y ) + m_Max.m_Y = y; + } + } + + public void RemoveXYZH( int x, int y, int z, int minHeight ) + { + int vx = x + m_Center.m_X; + int vy = y + m_Center.m_Y; + + if ( vx >= 0 && vx < m_Width && vy >= 0 && vy < m_Height ) + { + Tile[] oldTiles = m_Tiles[vx][vy]; + + for ( int i = 0; i < oldTiles.Length; ++i ) + { + Tile tile = oldTiles[i]; + + if ( tile.Z == z && tile.Height >= minHeight ) + { + Tile[] newTiles = new Tile[oldTiles.Length - 1]; + + for ( int j = 0; j < i; ++j ) + newTiles[j] = oldTiles[j]; + + for ( int j = i + 1; j < oldTiles.Length; ++j ) + newTiles[j - 1] = oldTiles[j]; + + m_Tiles[vx][vy] = newTiles; + + break; + } + } + + MultiTileEntry[] oldList = m_List; + + for ( int i = 0; i < oldList.Length; ++i ) + { + MultiTileEntry tile = oldList[i]; + + if ( tile.m_OffsetX == (short)x && tile.m_OffsetY == (short)y && tile.m_OffsetZ == (short)z && TileData.ItemTable[tile.m_ItemID & 0x3FFF].Height >= minHeight ) + { + MultiTileEntry[] newList = new MultiTileEntry[oldList.Length - 1]; + + for ( int j = 0; j < i; ++j ) + newList[j] = oldList[j]; + + for ( int j = i + 1; j < oldList.Length; ++j ) + newList[j - 1] = oldList[j]; + + m_List = newList; + + break; + } + } + } + } + + public void Remove( int itemID, int x, int y, int z ) + { + int vx = x + m_Center.m_X; + int vy = y + m_Center.m_Y; + + if ( vx >= 0 && vx < m_Width && vy >= 0 && vy < m_Height ) + { + Tile[] oldTiles = m_Tiles[vx][vy]; + + for ( int i = 0; i < oldTiles.Length; ++i ) + { + Tile tile = oldTiles[i]; + + if ( (tile.ID & 0x3FFF) == (itemID & 0x3FFF) && tile.Z == z ) + { + Tile[] newTiles = new Tile[oldTiles.Length - 1]; + + for ( int j = 0; j < i; ++j ) + newTiles[j] = oldTiles[j]; + + for ( int j = i + 1; j < oldTiles.Length; ++j ) + newTiles[j - 1] = oldTiles[j]; + + m_Tiles[vx][vy] = newTiles; + + break; + } + } + + MultiTileEntry[] oldList = m_List; + + for ( int i = 0; i < oldList.Length; ++i ) + { + MultiTileEntry tile = oldList[i]; + + if ( (tile.m_ItemID & 0x3FFF) == (short)(itemID & 0x3FFF) && tile.m_OffsetX == (short)x && tile.m_OffsetY == (short)y && tile.m_OffsetZ == (short)z ) + { + MultiTileEntry[] newList = new MultiTileEntry[oldList.Length - 1]; + + for ( int j = 0; j < i; ++j ) + newList[j] = oldList[j]; + + for ( int j = i + 1; j < oldList.Length; ++j ) + newList[j - 1] = oldList[j]; + + m_List = newList; + + break; + } + } + } + } + + public void Resize( int newWidth, int newHeight ) + { + int oldWidth = m_Width, oldHeight = m_Height; + Tile[][][] oldTiles = m_Tiles; + + int totalLength = 0; + + Tile[][][] newTiles = new Tile[newWidth][][]; + + for ( int x = 0; x < newWidth; ++x ) + { + newTiles[x] = new Tile[newHeight][]; + + for ( int y = 0; y < newHeight; ++y ) + { + if ( x < oldWidth && y < oldHeight ) + newTiles[x][y] = oldTiles[x][y]; + else + newTiles[x][y] = new Tile[0]; + + totalLength += newTiles[x][y].Length; + } + } + + m_Tiles = newTiles; + m_List = new MultiTileEntry[totalLength]; + m_Width = newWidth; + m_Height = newHeight; + + m_Min = Point2D.Zero; + m_Max = Point2D.Zero; + + int index = 0; + + for ( int x = 0; x < newWidth; ++x ) + { + for ( int y = 0; y < newHeight; ++y ) + { + Tile[] tiles = newTiles[x][y]; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + int vx = x - m_Center.X; + int vy = y - m_Center.Y; + + if ( vx < m_Min.m_X ) + m_Min.m_X = vx; + + if ( vy < m_Min.m_Y ) + m_Min.m_Y = vy; + + if ( vx > m_Max.m_X ) + m_Max.m_X = vx; + + if ( vy > m_Max.m_Y ) + m_Max.m_Y = vy; + + m_List[index++] = new MultiTileEntry( (short)tile.ID, (short)vx, (short)vy, (short)tile.Z, 1 ); + } + } + } + } + + public MultiComponentList( MultiComponentList toCopy ) + { + m_Min = toCopy.m_Min; + m_Max = toCopy.m_Max; + + m_Center = toCopy.m_Center; + + m_Width = toCopy.m_Width; + m_Height = toCopy.m_Height; + + m_Tiles = new Tile[m_Width][][]; + + for ( int x = 0; x < m_Width; ++x ) + { + m_Tiles[x] = new Tile[m_Height][]; + + for ( int y = 0; y < m_Height; ++y ) + { + m_Tiles[x][y] = new Tile[toCopy.m_Tiles[x][y].Length]; + + for ( int i = 0; i < m_Tiles[x][y].Length; ++i ) + m_Tiles[x][y][i] = toCopy.m_Tiles[x][y][i]; + } + } + + m_List = new MultiTileEntry[toCopy.m_List.Length]; + + for ( int i = 0; i < m_List.Length; ++i ) + m_List[i] = toCopy.m_List[i]; + } + + public void Serialize( GenericWriter writer ) + { + writer.Write( (int) 0 ); // version; + + writer.Write( m_Min ); + writer.Write( m_Max ); + writer.Write( m_Center ); + + writer.Write( (int) m_Width ); + writer.Write( (int) m_Height ); + + writer.Write( (int) m_List.Length ); + + for ( int i = 0; i < m_List.Length; ++i ) + { + MultiTileEntry ent = m_List[i]; + + writer.Write( (short) ent.m_ItemID ); + writer.Write( (short) ent.m_OffsetX ); + writer.Write( (short) ent.m_OffsetY ); + writer.Write( (short) ent.m_OffsetZ ); + writer.Write( (int) ent.m_Flags ); + } + } + + public MultiComponentList( GenericReader reader ) + { + int version = reader.ReadInt(); + + switch ( version ) + { + case 0: + { + m_Min = reader.ReadPoint2D(); + m_Max = reader.ReadPoint2D(); + m_Center = reader.ReadPoint2D(); + m_Width = reader.ReadInt(); + m_Height = reader.ReadInt(); + + int length = reader.ReadInt(); + + MultiTileEntry[] allTiles = m_List = new MultiTileEntry[length]; + + for ( int i = 0; i < length; ++i ) + { + allTiles[i].m_ItemID = reader.ReadShort(); + allTiles[i].m_OffsetX = reader.ReadShort(); + allTiles[i].m_OffsetY = reader.ReadShort(); + allTiles[i].m_OffsetZ = reader.ReadShort(); + allTiles[i].m_Flags = reader.ReadInt(); + } + + TileList[][] tiles = new TileList[m_Width][]; + m_Tiles = new Tile[m_Width][][]; + + for ( int x = 0; x < m_Width; ++x ) + { + tiles[x] = new TileList[m_Height]; + m_Tiles[x] = new Tile[m_Height][]; + + for ( int y = 0; y < m_Height; ++y ) + tiles[x][y] = new TileList(); + } + + for ( int i = 0; i < allTiles.Length; ++i ) + { + if ( i == 0 || allTiles[i].m_Flags != 0 ) + { + int xOffset = allTiles[i].m_OffsetX + m_Center.m_X; + int yOffset = allTiles[i].m_OffsetY + m_Center.m_Y; + + tiles[xOffset][yOffset].Add( (short)((allTiles[i].m_ItemID & 0x3FFF) | 0x4000), (sbyte)allTiles[i].m_OffsetZ ); + } + } + + for ( int x = 0; x < m_Width; ++x ) + for ( int y = 0; y < m_Height; ++y ) + m_Tiles[x][y] = tiles[x][y].ToArray(); + + break; + } + } + } + + public MultiComponentList( BinaryReader reader, int count ) + { + MultiTileEntry[] allTiles = m_List = new MultiTileEntry[count]; + + for ( int i = 0; i < count; ++i ) + { + allTiles[i].m_ItemID = reader.ReadInt16(); + allTiles[i].m_OffsetX = reader.ReadInt16(); + allTiles[i].m_OffsetY = reader.ReadInt16(); + allTiles[i].m_OffsetZ = reader.ReadInt16(); + allTiles[i].m_Flags = reader.ReadInt32(); + + MultiTileEntry e = allTiles[i]; + + if ( i == 0 || e.m_Flags != 0 ) + { + if ( e.m_OffsetX < m_Min.m_X ) + m_Min.m_X = e.m_OffsetX; + + if ( e.m_OffsetY < m_Min.m_Y ) + m_Min.m_Y = e.m_OffsetY; + + if ( e.m_OffsetX > m_Max.m_X ) + m_Max.m_X = e.m_OffsetX; + + if ( e.m_OffsetY > m_Max.m_Y ) + m_Max.m_Y = e.m_OffsetY; + } + } + + m_Center = new Point2D( -m_Min.m_X, -m_Min.m_Y ); + m_Width = (m_Max.m_X - m_Min.m_X) + 1; + m_Height = (m_Max.m_Y - m_Min.m_Y) + 1; + + TileList[][] tiles = new TileList[m_Width][]; + m_Tiles = new Tile[m_Width][][]; + + for ( int x = 0; x < m_Width; ++x ) + { + tiles[x] = new TileList[m_Height]; + m_Tiles[x] = new Tile[m_Height][]; + + for ( int y = 0; y < m_Height; ++y ) + tiles[x][y] = new TileList(); + } + + for ( int i = 0; i < allTiles.Length; ++i ) + { + if ( i == 0 || allTiles[i].m_Flags != 0 ) + { + int xOffset = allTiles[i].m_OffsetX + m_Center.m_X; + int yOffset = allTiles[i].m_OffsetY + m_Center.m_Y; + + tiles[xOffset][yOffset].Add( (short)((allTiles[i].m_ItemID & 0x3FFF) | 0x4000), (sbyte)allTiles[i].m_OffsetZ ); + } + } + + for ( int x = 0; x < m_Width; ++x ) + for ( int y = 0; y < m_Height; ++y ) + m_Tiles[x][y] = tiles[x][y].ToArray(); + } + + private MultiComponentList() + { + m_Tiles = new Tile[0][][]; + m_List = new MultiTileEntry[0]; + } + } +} \ No newline at end of file diff --git a/Server/Network/BufferPool.cs b/Server/Network/BufferPool.cs new file mode 100644 index 000000000..9bbd928a6 --- /dev/null +++ b/Server/Network/BufferPool.cs @@ -0,0 +1,102 @@ +/*************************************************************************** + * BufferPool.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: BufferPool.cs 43 2006-01-20 05:39:57Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Server.Network +{ + public class BufferPool + { + private static List m_Pools = new List(); + + public static List Pools{ get{ return m_Pools; } set{ m_Pools = value; } } + + private string m_Name; + + private int m_InitialCapacity; + private int m_BufferSize; + + private int m_Misses; + + private Queue m_FreeBuffers; + + public void GetInfo( out string name, out int freeCount, out int initialCapacity, out int currentCapacity, out int bufferSize, out int misses ) + { + lock ( this ) + { + name = m_Name; + freeCount = m_FreeBuffers.Count; + initialCapacity = m_InitialCapacity; + currentCapacity = m_InitialCapacity * (1 + m_Misses); + bufferSize = m_BufferSize; + misses = m_Misses; + } + } + + public BufferPool( string name, int initialCapacity, int bufferSize ) + { + m_Name = name; + + m_InitialCapacity = initialCapacity; + m_BufferSize = bufferSize; + + m_FreeBuffers = new Queue( initialCapacity ); + + for ( int i = 0; i < initialCapacity; ++i ) + m_FreeBuffers.Enqueue( new byte[bufferSize] ); + + lock ( m_Pools ) + m_Pools.Add( this ); + } + + public byte[] AcquireBuffer() + { + lock ( this ) + { + if ( m_FreeBuffers.Count > 0 ) + return m_FreeBuffers.Dequeue(); + + ++m_Misses; + + for ( int i = 0; i < m_InitialCapacity; ++i ) + m_FreeBuffers.Enqueue( new byte[m_BufferSize] ); + + return m_FreeBuffers.Dequeue(); + } + } + + public void ReleaseBuffer( byte[] buffer ) + { + if ( buffer == null ) + return; + + lock ( this ) + m_FreeBuffers.Enqueue( buffer ); + } + + public void Free() + { + lock ( m_Pools ) + m_Pools.Remove( this ); + } + } +} \ No newline at end of file diff --git a/Server/Network/ByteQueue.cs b/Server/Network/ByteQueue.cs new file mode 100644 index 000000000..9963dcbfb --- /dev/null +++ b/Server/Network/ByteQueue.cs @@ -0,0 +1,153 @@ +/*************************************************************************** + * ByteQueue.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ByteQueue.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; + +namespace Server.Network +{ + public class ByteQueue + { + private int m_Head; + private int m_Tail; + private int m_Size; + + private byte[] m_Buffer; + + public int Length{ get{ return m_Size; } } + + public ByteQueue() + { + m_Buffer = new byte[2048]; + } + + public void Clear() + { + m_Head = 0; + m_Tail = 0; + m_Size = 0; + } + + private void SetCapacity( int capacity ) + { + byte[] newBuffer = new byte[capacity]; + + if ( m_Size > 0 ) + { + if ( m_Head < m_Tail ) + { + Buffer.BlockCopy( m_Buffer, m_Head, newBuffer, 0, m_Size ); + } + else + { + Buffer.BlockCopy( m_Buffer, m_Head, newBuffer, 0, m_Buffer.Length - m_Head ); + Buffer.BlockCopy( m_Buffer, 0, newBuffer, m_Buffer.Length - m_Head, m_Tail ); + } + } + + m_Head = 0; + m_Tail = m_Size; + m_Buffer = newBuffer; + } + + public byte GetPacketID() + { + if ( m_Size >= 1 ) + return m_Buffer[m_Head]; + + return 0xFF; + } + + public int GetPacketLength() + { + if ( m_Size >= 3 ) + return (m_Buffer[(m_Head + 1) % m_Buffer.Length] << 8) | m_Buffer[(m_Head + 2) % m_Buffer.Length]; + + return 0; + } + + public int Dequeue( byte[] buffer, int offset, int size ) + { + if ( size > m_Size ) + size = m_Size; + + if ( size == 0 ) + return 0; + + if ( m_Head < m_Tail ) + { + Buffer.BlockCopy( m_Buffer, m_Head, buffer, offset, size ); + } + else + { + int rightLength = ( m_Buffer.Length - m_Head ); + + if ( rightLength >= size ) + { + Buffer.BlockCopy( m_Buffer, m_Head, buffer, offset, size ); + } + else + { + Buffer.BlockCopy( m_Buffer, m_Head, buffer, offset, rightLength ); + Buffer.BlockCopy( m_Buffer, 0, buffer, offset + rightLength, size - rightLength ); + } + } + + m_Head = ( m_Head + size ) % m_Buffer.Length; + m_Size -= size; + + if ( m_Size == 0 ) + { + m_Head = 0; + m_Tail = 0; + } + + return size; + } + + public void Enqueue( byte[] buffer, int offset, int size ) + { + if ( (m_Size + size) > m_Buffer.Length ) + SetCapacity( (m_Size + size + 2047) & ~2047 ); + + if ( m_Head < m_Tail ) + { + int rightLength = ( m_Buffer.Length - m_Tail ); + + if ( rightLength >= size ) + { + Buffer.BlockCopy( buffer, offset, m_Buffer, m_Tail, size ); + } + else + { + Buffer.BlockCopy( buffer, offset, m_Buffer, m_Tail, rightLength ); + Buffer.BlockCopy( buffer, offset + rightLength, m_Buffer, 0, size - rightLength ); + } + } + else + { + Buffer.BlockCopy( buffer, offset, m_Buffer, m_Tail, size ); + } + + m_Tail = ( m_Tail + size ) % m_Buffer.Length; + m_Size += size; + } + } +} \ No newline at end of file diff --git a/Server/Network/Compression.cs b/Server/Network/Compression.cs new file mode 100644 index 000000000..a6a16805e --- /dev/null +++ b/Server/Network/Compression.cs @@ -0,0 +1,281 @@ +/*************************************************************************** + * Compression.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Compression.cs 131 2006-04-21 03:29:53Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Server.Network +{ + /// + /// Handles outgoing packet compression for the network. + /// + public class Compression + { + private static int[] m_Table = new int[514] + { + 0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032, + 0x8, 0x0E0, 0x8, 0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057, + 0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC, 0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D, + 0x9, 0x191, 0x9, 0x1CE, 0x7, 0x03F, 0x9, 0x090, 0x8, 0x059, 0x8, 0x07B, 0x8, 0x091, 0x8, 0x0C6, + 0x6, 0x02D, 0x9, 0x186, 0x8, 0x06F, 0x9, 0x093, 0xA, 0x1CC, 0x8, 0x05A, 0xA, 0x1AE, 0xA, 0x1C0, + 0x9, 0x148, 0x9, 0x14A, 0x9, 0x082, 0xA, 0x19F, 0x9, 0x171, 0x9, 0x120, 0x9, 0x0E7, 0xA, 0x1F3, + 0x9, 0x14B, 0x9, 0x100, 0x9, 0x190, 0x6, 0x013, 0x9, 0x161, 0x9, 0x125, 0x9, 0x133, 0x9, 0x195, + 0x9, 0x173, 0x9, 0x1CA, 0x9, 0x086, 0x9, 0x1E9, 0x9, 0x0DB, 0x9, 0x1EC, 0x9, 0x08B, 0x9, 0x085, + 0x5, 0x00A, 0x8, 0x096, 0x8, 0x09C, 0x9, 0x1C3, 0x9, 0x19C, 0x9, 0x08F, 0x9, 0x18F, 0x9, 0x091, + 0x9, 0x087, 0x9, 0x0C6, 0x9, 0x177, 0x9, 0x089, 0x9, 0x0D6, 0x9, 0x08C, 0x9, 0x1EE, 0x9, 0x1EB, + 0x9, 0x084, 0x9, 0x164, 0x9, 0x175, 0x9, 0x1CD, 0x8, 0x05E, 0x9, 0x088, 0x9, 0x12B, 0x9, 0x172, + 0x9, 0x10A, 0x9, 0x08D, 0x9, 0x13A, 0x9, 0x11C, 0xA, 0x1E1, 0xA, 0x1E0, 0x9, 0x187, 0xA, 0x1DC, + 0xA, 0x1DF, 0x7, 0x074, 0x9, 0x19F, 0x8, 0x08D, 0x8, 0x0E4, 0x7, 0x079, 0x9, 0x0EA, 0x9, 0x0E1, + 0x8, 0x040, 0x7, 0x041, 0x9, 0x10B, 0x9, 0x0B0, 0x8, 0x06A, 0x8, 0x0C1, 0x7, 0x071, 0x7, 0x078, + 0x8, 0x0B1, 0x9, 0x14C, 0x7, 0x043, 0x8, 0x076, 0x7, 0x066, 0x7, 0x04D, 0x9, 0x08A, 0x6, 0x02F, + 0x8, 0x0C9, 0x9, 0x0CE, 0x9, 0x149, 0x9, 0x160, 0xA, 0x1BA, 0xA, 0x19E, 0xA, 0x39F, 0x9, 0x0E5, + 0x9, 0x194, 0x9, 0x184, 0x9, 0x126, 0x7, 0x030, 0x8, 0x06C, 0x9, 0x121, 0x9, 0x1E8, 0xA, 0x1C1, + 0xA, 0x11D, 0xA, 0x163, 0xA, 0x385, 0xA, 0x3DB, 0xA, 0x17D, 0xA, 0x106, 0xA, 0x397, 0xA, 0x24E, + 0x7, 0x02E, 0x8, 0x098, 0xA, 0x33C, 0xA, 0x32E, 0xA, 0x1E9, 0x9, 0x0BF, 0xA, 0x3DF, 0xA, 0x1DD, + 0xA, 0x32D, 0xA, 0x2ED, 0xA, 0x30B, 0xA, 0x107, 0xA, 0x2E8, 0xA, 0x3DE, 0xA, 0x125, 0xA, 0x1E8, + 0x9, 0x0E9, 0xA, 0x1CD, 0xA, 0x1B5, 0x9, 0x165, 0xA, 0x232, 0xA, 0x2E1, 0xB, 0x3AE, 0xB, 0x3C6, + 0xB, 0x3E2, 0xA, 0x205, 0xA, 0x29A, 0xA, 0x248, 0xA, 0x2CD, 0xA, 0x23B, 0xB, 0x3C5, 0xA, 0x251, + 0xA, 0x2E9, 0xA, 0x252, 0x9, 0x1EA, 0xB, 0x3A0, 0xB, 0x391, 0xA, 0x23C, 0xB, 0x392, 0xB, 0x3D5, + 0xA, 0x233, 0xA, 0x2CC, 0xB, 0x390, 0xA, 0x1BB, 0xB, 0x3A1, 0xB, 0x3C4, 0xA, 0x211, 0xA, 0x203, + 0x9, 0x12A, 0xA, 0x231, 0xB, 0x3E0, 0xA, 0x29B, 0xB, 0x3D7, 0xA, 0x202, 0xB, 0x3AD, 0xA, 0x213, + 0xA, 0x253, 0xA, 0x32C, 0xA, 0x23D, 0xA, 0x23F, 0xA, 0x32F, 0xA, 0x11C, 0xA, 0x384, 0xA, 0x31C, + 0xA, 0x17C, 0xA, 0x30A, 0xA, 0x2E0, 0xA, 0x276, 0xA, 0x250, 0xB, 0x3E3, 0xA, 0x396, 0xA, 0x18F, + 0xA, 0x204, 0xA, 0x206, 0xA, 0x230, 0xA, 0x265, 0xA, 0x212, 0xA, 0x23E, 0xB, 0x3AC, 0xB, 0x393, + 0xB, 0x3E1, 0xA, 0x1DE, 0xB, 0x3D6, 0xA, 0x31D, 0xB, 0x3E5, 0xB, 0x3E4, 0xA, 0x207, 0xB, 0x3C7, + 0xA, 0x277, 0xB, 0x3D4, 0x8, 0x0C0, 0xA, 0x162, 0xA, 0x3DA, 0xA, 0x124, 0xA, 0x1B4, 0xA, 0x264, + 0xA, 0x33D, 0xA, 0x1D1, 0xA, 0x1AF, 0xA, 0x39E, 0xA, 0x24F, 0xB, 0x373, 0xA, 0x249, 0xB, 0x372, + 0x9, 0x167, 0xA, 0x210, 0xA, 0x23A, 0xA, 0x1B8, 0xB, 0x3AF, 0xA, 0x18E, 0xA, 0x2EC, 0x7, 0x062, + 0x4, 0x00D + }; + + private static byte[] m_OutputBuffer = new byte[0x40000]; + private static object m_SyncRoot = new object(); + + public unsafe static void Compress( byte[] input, int length, out byte[] output, out int outputLength ) + { + if ( length >= m_OutputBuffer.Length ) + { + output = null; + outputLength = length; + return; + } + + lock ( m_SyncRoot ) + { + int holdCount = 0; + int holdValue = 0; + + int packCount = 0; + int packValue = 0; + + int byteValue = 0; + + int inputLength = length; + int inputIndex = 0; + + int outputCount = 0; + + fixed ( int *pTable = m_Table ) + { + fixed ( byte *pOutputBuffer = m_OutputBuffer ) + { + while ( inputIndex < inputLength ) + { + byteValue = input[inputIndex++] << 1; + + packCount = pTable[byteValue]; + packValue = pTable[byteValue | 1]; + + holdValue <<= packCount; + holdValue |= packValue; + holdCount += packCount; + + while ( holdCount >= 8 ) + { + holdCount -= 8; + + pOutputBuffer[outputCount++] = (byte)(holdValue >> holdCount); + } + } + + packCount = pTable[0x200]; + packValue = pTable[0x201]; + + holdValue <<= packCount; + holdValue |= packValue; + holdCount += packCount; + + while ( holdCount >= 8 ) + { + holdCount -= 8; + + pOutputBuffer[outputCount++] = (byte)(holdValue >> holdCount); + } + + if ( holdCount > 0 ) + pOutputBuffer[outputCount++] = (byte)(holdValue << (8 - holdCount)); + } + } + + output = m_OutputBuffer; + outputLength = outputCount; + } + } + + public static readonly ICompressor Compressor; + + static Compression() + { + if ( Core.Is64Bit ) + Compressor = new Compressor64(); + else + Compressor = new Compressor32(); + } + + public static ZLibError Pack( byte[] dest, ref int destLength, byte[] source, int sourceLength ) + { + return Compressor.Compress( dest, ref destLength, source, sourceLength ); + } + + public static ZLibError Pack( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality ) + { + return Compressor.Compress( dest, ref destLength, source, sourceLength, quality ); + } + + public static ZLibError Unpack( byte[] dest, ref int destLength, byte[] source, int sourceLength ) + { + return Compressor.Decompress( dest, ref destLength, source, sourceLength ); + } + } + + public interface ICompressor + { + string Version { get; } + + ZLibError Compress( byte[] dest, ref int destLength, byte[] source, int sourceLength ); + ZLibError Compress( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality ); + + ZLibError Decompress( byte[] dest, ref int destLength, byte[] source, int sourceLength ); + } + + public sealed class Compressor32 : ICompressor + { + [DllImport( "zlib32" )] + private static extern string zlibVersion(); + + [DllImport( "zlib32" )] + private static extern ZLibError compress( byte[] dest, ref int destLength, byte[] source, int sourceLength ); + + [DllImport( "zlib32" )] + private static extern ZLibError compress2( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality ); + + [DllImport( "zlib32" )] + private static extern ZLibError uncompress( byte[] dest, ref int destLen, byte[] source, int sourceLen ); + + public Compressor32() + { + } + + public string Version + { + get { return zlibVersion(); } + } + + public ZLibError Compress( byte[] dest, ref int destLength, byte[] source, int sourceLength ) + { + return compress( dest, ref destLength, source, sourceLength ); + } + + public ZLibError Compress( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality ) + { + return compress2( dest, ref destLength, source, sourceLength, quality ); + } + + public ZLibError Decompress( byte[] dest, ref int destLength, byte[] source, int sourceLength ) + { + return uncompress( dest, ref destLength, source, sourceLength ); + } + } + + public sealed class Compressor64 : ICompressor + { + [DllImport( "zlib64" )] + private static extern string zlibVersion(); + + [DllImport( "zlib64" )] + private static extern ZLibError compress( byte[] dest, ref int destLength, byte[] source, int sourceLength ); + + [DllImport( "zlib64" )] + private static extern ZLibError compress2( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality ); + + [DllImport( "zlib64" )] + private static extern ZLibError uncompress( byte[] dest, ref int destLen, byte[] source, int sourceLen ); + + public Compressor64() + { + } + + public string Version + { + get { return zlibVersion(); } + } + + public ZLibError Compress( byte[] dest, ref int destLength, byte[] source, int sourceLength ) + { + return compress( dest, ref destLength, source, sourceLength ); + } + + public ZLibError Compress( byte[] dest, ref int destLength, byte[] source, int sourceLength, ZLibQuality quality ) + { + return compress2( dest, ref destLength, source, sourceLength, quality ); + } + + public ZLibError Decompress( byte[] dest, ref int destLength, byte[] source, int sourceLength ) + { + return uncompress( dest, ref destLength, source, sourceLength ); + } + } + + public enum ZLibError : int + { + VersionError = -6, + BufferError = -5, + MemoryError = -4, + DataError = -3, + StreamError = -2, + FileError = -1, + + Okay = 0, + + StreamEnd = 1, + NeedDictionary = 2 + } + + public enum ZLibQuality : int + { + Default = -1, + + None = 0, + + Speed = 1, + Size = 9 + } +} \ No newline at end of file diff --git a/Server/Network/EncodedPacketHandler.cs b/Server/Network/EncodedPacketHandler.cs new file mode 100644 index 000000000..213880d93 --- /dev/null +++ b/Server/Network/EncodedPacketHandler.cs @@ -0,0 +1,64 @@ +/*************************************************************************** + * EncodedPacketHandler.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: EncodedPacketHandler.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Network +{ + public delegate void OnEncodedPacketReceive( NetState state, IEntity ent, EncodedReader pvSrc ); + + public class EncodedPacketHandler + { + private int m_PacketID; + private bool m_Ingame; + private OnEncodedPacketReceive m_OnReceive; + + public EncodedPacketHandler( int packetID, bool ingame, OnEncodedPacketReceive onReceive ) + { + m_PacketID = packetID; + m_Ingame = ingame; + m_OnReceive = onReceive; + } + + public int PacketID + { + get + { + return m_PacketID; + } + } + + public OnEncodedPacketReceive OnReceive + { + get + { + return m_OnReceive; + } + } + + public bool Ingame + { + get + { + return m_Ingame; + } + } + } +} \ No newline at end of file diff --git a/Server/Network/EncodedReader.cs b/Server/Network/EncodedReader.cs new file mode 100644 index 000000000..9ca9a4167 --- /dev/null +++ b/Server/Network/EncodedReader.cs @@ -0,0 +1,85 @@ +/*************************************************************************** + * EncodedReader.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: EncodedReader.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Text; +using System.IO; + +namespace Server.Network +{ + public class EncodedReader + { + private PacketReader m_Reader; + + public EncodedReader( PacketReader reader ) + { + m_Reader = reader; + } + + public byte[] Buffer + { + get + { + return m_Reader.Buffer; + } + } + + public void Trace( NetState state ) + { + m_Reader.Trace( state ); + } + + public int ReadInt32() + { + if ( m_Reader.ReadByte() != 0 ) + return 0; + + return m_Reader.ReadInt32(); + } + + public Point3D ReadPoint3D() + { + if ( m_Reader.ReadByte() != 3 ) + return Point3D.Zero; + + return new Point3D( m_Reader.ReadInt16(), m_Reader.ReadInt16(), m_Reader.ReadByte() ); + } + + public string ReadUnicodeStringSafe() + { + if ( m_Reader.ReadByte() != 2 ) + return ""; + + int length = m_Reader.ReadUInt16(); + + return m_Reader.ReadUnicodeStringSafe( length ); + } + + public string ReadUnicodeString() + { + if ( m_Reader.ReadByte() != 2 ) + return ""; + + int length = m_Reader.ReadUInt16(); + + return m_Reader.ReadUnicodeString( length ); + } + } +} \ No newline at end of file diff --git a/Server/Network/Listener.cs b/Server/Network/Listener.cs new file mode 100644 index 000000000..e3709a912 --- /dev/null +++ b/Server/Network/Listener.cs @@ -0,0 +1,193 @@ +/*************************************************************************** + * Listener.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Listener.cs 102 2006-02-04 01:52:29Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using Server; + +namespace Server.Network +{ + public class Listener : IDisposable + { + private Socket m_Listener; + private bool m_Disposed; + private int m_ThisPort; + + private Queue m_Accepted; + private object m_AcceptedSyncRoot; + + private AsyncCallback m_OnAccept; + + private static Socket[] m_EmptySockets = new Socket[0]; + + public int UsedPort + { + get{ return m_ThisPort; } + } + + private static int m_Port = 2593; + + public static int Port + { + get + { + return m_Port; + } + set + { + m_Port = value; + } + } + + public Listener( int port ) + { + m_ThisPort = port; + m_Disposed = false; + m_Accepted = new Queue(); + m_AcceptedSyncRoot = ((ICollection)m_Accepted).SyncRoot; + m_OnAccept = new AsyncCallback( OnAccept ); + + m_Listener = Bind( IPAddress.Any, port ); + + try + { + IPHostEntry iphe = Dns.GetHostEntry( Dns.GetHostName() ); + + Console.WriteLine( "Address: {0}:{1}", IPAddress.Loopback, port ); + + IPAddress[] ip = iphe.AddressList; + + for ( int i = 0; i < ip.Length; ++i ) + Console.WriteLine( "Address: {0}:{1}", ip[i], port ); + } + catch + { + } + } + + private Socket Bind( IPAddress ip, int port ) + { + IPEndPoint ipep = new IPEndPoint( ip, port ); + + Socket s = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); + + try + { + s.LingerState.Enabled = false; + s.ExclusiveAddressUse = false; + + s.Bind( ipep ); + s.Listen( 8 ); + + IAsyncResult res = s.BeginAccept( m_OnAccept, s ); + + return s; + } + catch ( Exception e ) + { + Console.WriteLine( "Listener bind exception:" ); + Console.WriteLine( e ); + + try { s.Shutdown( SocketShutdown.Both ); } + catch{} + + try { s.Close(); } + catch{} + + return null; + } + } + + private void OnAccept( IAsyncResult asyncResult ) + { + Socket listener = asyncResult.AsyncState as Socket; + + try + { + Socket socket = listener.EndAccept( asyncResult ); + + if ( socket != null ) + { + SocketConnectEventArgs e = new SocketConnectEventArgs( socket ); + EventSink.InvokeSocketConnect( e ); + + if ( e.AllowConnection ) + { + lock ( m_AcceptedSyncRoot ) + m_Accepted.Enqueue( socket ); + } + else + { + try { socket.Shutdown( SocketShutdown.Both ); } + catch { } + + try { socket.Close(); } + catch { } + } + } + } + catch + { + } + finally + { + IAsyncResult res = listener.BeginAccept( m_OnAccept, listener ); + } + } + + public Socket[] Slice() + { + Socket[] array; + + lock ( m_AcceptedSyncRoot ) + { + if ( m_Accepted.Count == 0 ) + return m_EmptySockets; + + array = m_Accepted.ToArray(); + m_Accepted.Clear(); + } + + return array; + } + + public void Dispose() + { + if ( !m_Disposed ) + { + m_Disposed = true; + + if ( m_Listener != null ) + { + try { m_Listener.Shutdown( SocketShutdown.Both ); } + catch {} + + try { m_Listener.Close(); } + catch {} + + m_Listener = null; + } + } + } + } +} \ No newline at end of file diff --git a/Server/Network/MessagePump.cs b/Server/Network/MessagePump.cs new file mode 100644 index 000000000..1f76e0de6 --- /dev/null +++ b/Server/Network/MessagePump.cs @@ -0,0 +1,256 @@ +/*************************************************************************** + * MessagePump.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: MessagePump.cs 43 2006-01-20 05:39:57Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Network; + +namespace Server.Network +{ + public class MessagePump + { + private Listener[] m_Listeners; + private Queue m_Queue; + private Queue m_WorkingQueue; + private Queue m_Throttled; + private byte[] m_Peek; + + public MessagePump( Listener l ) + { + m_Listeners = new Listener[]{ l }; + m_Queue = new Queue(); + m_WorkingQueue = new Queue(); + m_Throttled = new Queue(); + m_Peek = new byte[4]; + } + + public Listener[] Listeners + { + get{ return m_Listeners; } + set{ m_Listeners = value; } + } + + public void AddListener( Listener l ) + { + Listener[] old = m_Listeners; + + m_Listeners = new Listener[old.Length + 1]; + + for ( int i = 0; i < old.Length; ++i ) + m_Listeners[i] = old[i]; + + m_Listeners[old.Length] = l; + } + + private void CheckListener() + { + for ( int j = 0; j < m_Listeners.Length; ++j ) + { + Socket[] accepted = m_Listeners[j].Slice(); + + for ( int i = 0; i < accepted.Length; ++i ) + { + NetState ns = new NetState( accepted[i], this ); + ns.Start(); + + if ( ns.Running ) + Console.WriteLine( "Client: {0}: Connected. [{1} Online]", ns, NetState.Instances.Count ); + } + } + } + + public void OnReceive( NetState ns ) + { + lock ( this ) + m_Queue.Enqueue( ns ); + } + + public void Slice() + { + CheckListener(); + + lock ( this ) + { + Queue temp = m_WorkingQueue; + m_WorkingQueue = m_Queue; + m_Queue = temp; + } + + while ( m_WorkingQueue.Count > 0 ) + { + NetState ns = m_WorkingQueue.Dequeue(); + + if ( ns.Running ) + HandleReceive( ns ); + } + + lock ( this ) + { + while ( m_Throttled.Count > 0 ) + m_Queue.Enqueue( m_Throttled.Dequeue() ); + } + } + + private const int BufferSize = 4096; + private BufferPool m_Buffers = new BufferPool( "Processor", 4, BufferSize ); + + public bool HandleReceive( NetState ns ) + { + ByteQueue buffer = ns.Buffer; + + if ( buffer == null || buffer.Length <= 0 ) + return true; + + lock ( buffer ) + { + int length = buffer.Length; + + if ( !ns.Seeded ) + { + if ( buffer.Length >= 4 ) + { + buffer.Dequeue( m_Peek, 0, 4 ); + + int seed = (m_Peek[0] << 24) | (m_Peek[1] << 16) | (m_Peek[2] << 8) | m_Peek[3]; + + if ( seed == 0 ) + { + Console.WriteLine( "Login: {0}: Invalid client detected, disconnecting", ns ); + ns.Dispose(); + return false; + } + + ns.m_Seed = seed; + ns.Seeded = true; + + length = buffer.Length; + } + else + { + return true; + } + } + + while ( length > 0 && ns.Running ) + { + int packetID = buffer.GetPacketID(); + + if ( !ns.SentFirstPacket && packetID != 0xF1 && packetID != 0xCF && packetID != 0x80 && packetID != 0x91 && packetID != 0xA4 ) + { + Console.WriteLine( "Client: {0}: Encrypted client detected, disconnecting", ns ); + ns.Dispose(); + break; + } + + PacketHandler handler = PacketHandlers.GetHandler( packetID ); + + if ( handler == null ) + { + byte[] data = new byte[length]; + length = buffer.Dequeue( data, 0, length ); + + new PacketReader( data, length, false ).Trace( ns ); + + break; + } + + int packetLength = handler.Length; + + if ( packetLength <= 0 ) + { + if ( length >= 3 ) + { + packetLength = buffer.GetPacketLength(); + + if ( packetLength < 3 ) + { + ns.Dispose(); + break; + } + } + else + { + break; + } + } + + if ( length >= packetLength ) + { + if ( handler.Ingame && ns.Mobile == null ) + { + Console.WriteLine( "Client: {0}: Sent ingame packet (0x{1:X2}) before having been attached to a mobile", ns, packetID ); + ns.Dispose(); + break; + } + else if ( handler.Ingame && ns.Mobile.Deleted ) + { + ns.Dispose(); + break; + } + else + { + ThrottlePacketCallback throttler = handler.ThrottleCallback; + + if ( throttler != null && !throttler( ns ) ) + { + m_Throttled.Enqueue( ns ); + return false; + } + + PacketProfile profile = PacketProfile.GetIncomingProfile( packetID ); + DateTime start = ( profile == null ? DateTime.MinValue : DateTime.Now ); + + byte[] packetBuffer; + + if ( BufferSize >= packetLength ) + packetBuffer = m_Buffers.AcquireBuffer(); + else + packetBuffer = new byte[packetLength]; + + packetLength = buffer.Dequeue( packetBuffer, 0, packetLength ); + + PacketReader r = new PacketReader( packetBuffer, packetLength, handler.Length != 0 ); + + handler.OnReceive( ns, r ); + length = buffer.Length; + + if ( BufferSize >= packetLength ) + m_Buffers.ReleaseBuffer( packetBuffer ); + + if ( profile != null ) + profile.Record( packetLength, DateTime.Now - start ); + } + } + else + { + break; + } + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/Server/Network/NetState.cs b/Server/Network/NetState.cs new file mode 100644 index 000000000..a31919e95 --- /dev/null +++ b/Server/Network/NetState.cs @@ -0,0 +1,907 @@ +/*************************************************************************** + * NetState.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: NetState.cs 156 2006-06-11 09:36:25Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using Server; +using Server.Accounting; +using Server.Network; +using Server.Items; +using Server.Gumps; +using Server.Menus; +using Server.HuePickers; + +namespace Server.Network +{ + public interface IPacketEncoder + { + void EncodeOutgoingPacket( NetState to, ref byte[] buffer, ref int length ); + void DecodeIncomingPacket( NetState from, ref byte[] buffer, ref int length ); + } + + public delegate void NetStateCreatedCallback( NetState ns ); + + public class NetState + { + private Socket m_Socket; + private IPAddress m_Address; + private ByteQueue m_Buffer; + private byte[] m_RecvBuffer; + private SendQueue m_SendQueue; + private bool m_Seeded; + private bool m_Running; + private AsyncCallback m_OnReceive, m_OnSend; + private MessagePump m_MessagePump; + private ServerInfo[] m_ServerInfo; + private IAccount m_Account; + private Mobile m_Mobile; + private CityInfo[] m_CityInfo; + private List m_Gumps; + private List m_HuePickers; + private List m_Menus; + private List m_Trades; + private int m_Sequence; + private bool m_CompressionEnabled; + private string m_ToString; + private ClientVersion m_Version; + private bool m_SentFirstPacket; + private bool m_BlockAllPackets; + + private DateTime m_ConnectedOn; + + public DateTime ConnectedOn + { + get { return m_ConnectedOn; } + } + + public TimeSpan ConnectedFor + { + get { return ( DateTime.Now - m_ConnectedOn ); } + } + + internal int m_Seed; + internal int m_AuthID; + + public IPAddress Address + { + get{ return m_Address; } + } + + private int m_Flags; + + private static bool m_Paused; + + [Flags] + private enum AsyncState + { + Pending = 0x01, + Paused = 0x02 + } + + private AsyncState m_AsyncState; + private object m_AsyncLock = new object(); + + public static void Pause() + { + m_Paused = true; + + for ( int i = 0; i < m_Instances.Count; ++i ) + { + NetState ns = m_Instances[i]; + + lock ( ns.m_AsyncLock ) + ns.m_AsyncState |= AsyncState.Paused; + } + } + + private void InternalBeginReceive() + { + m_AsyncState |= AsyncState.Pending; + + IAsyncResult res = m_Socket.BeginReceive( m_RecvBuffer, 0, m_RecvBuffer.Length, SocketFlags.None, m_OnReceive, null ); + } + + public static void Resume() + { + m_Paused = false; + + for ( int i = 0; i < m_Instances.Count; ++i ) + { + NetState ns = m_Instances[i]; + + if ( ns.m_Socket == null ) + continue; + + lock ( ns.m_AsyncLock ) + { + ns.m_AsyncState &= ~AsyncState.Paused; + + try + { + if ( (ns.m_AsyncState & AsyncState.Pending) == 0 ) + ns.InternalBeginReceive(); + } + catch + { + ns.Dispose( false ); + } + } + } + } + + private IPacketEncoder m_Encoder = null; + + public IPacketEncoder PacketEncoder + { + get{ return m_Encoder; } + set{ m_Encoder = value; } + } + + private static NetStateCreatedCallback m_CreatedCallback; + + public static NetStateCreatedCallback CreatedCallback + { + get{ return m_CreatedCallback; } + set{ m_CreatedCallback = value; } + } + + public bool SentFirstPacket{ get{ return m_SentFirstPacket; } set{ m_SentFirstPacket = value; } } + + public bool BlockAllPackets + { + get + { + return m_BlockAllPackets; + } + set + { + m_BlockAllPackets = value; + } + } + + public int Flags + { + get + { + return m_Flags; + } + set + { + m_Flags = value; + } + } + + public ClientVersion Version + { + get + { + return m_Version; + } + set + { + m_Version = value; + } + } + + public List Trades + { + get + { + return m_Trades; + } + } + + public void ValidateAllTrades() + { + for ( int i = m_Trades.Count - 1; i >= 0; --i ) + { + if ( i >= m_Trades.Count ) + continue; + + SecureTrade trade = m_Trades[i]; + + if ( trade.From.Mobile.Deleted || trade.To.Mobile.Deleted || !trade.From.Mobile.Alive || !trade.To.Mobile.Alive || !trade.From.Mobile.InRange( trade.To.Mobile, 2 ) || trade.From.Mobile.Map != trade.To.Mobile.Map ) + trade.Cancel(); + } + } + + public void CancelAllTrades() + { + for ( int i = m_Trades.Count - 1; i >= 0; --i ) + if ( i < m_Trades.Count ) + m_Trades[i].Cancel(); + } + + public void RemoveTrade( SecureTrade trade ) + { + m_Trades.Remove( trade ); + } + + public SecureTrade FindTrade( Mobile m ) + { + for ( int i = 0; i < m_Trades.Count; ++i ) + { + SecureTrade trade = m_Trades[i]; + + if ( trade.From.Mobile == m || trade.To.Mobile == m ) + return trade; + } + + return null; + } + + public SecureTradeContainer FindTradeContainer( Mobile m ) + { + for ( int i = 0; i < m_Trades.Count; ++i ) + { + SecureTrade trade = m_Trades[i]; + SecureTradeInfo from = trade.From; + SecureTradeInfo to = trade.To; + + if ( from.Mobile == m_Mobile && to.Mobile == m ) + return from.Container; + else if ( from.Mobile == m && to.Mobile == m_Mobile ) + return to.Container; + } + + return null; + } + + public SecureTradeContainer AddTrade( NetState state ) + { + SecureTrade newTrade = new SecureTrade( m_Mobile, state.m_Mobile ); + + m_Trades.Add( newTrade ); + state.m_Trades.Add( newTrade ); + + return newTrade.From.Container; + } + + public bool CompressionEnabled + { + get + { + return m_CompressionEnabled; + } + set + { + m_CompressionEnabled = value; + } + } + + public int Sequence + { + get + { + return m_Sequence; + } + set + { + m_Sequence = value; + } + } + + public List Gumps{ get{ return m_Gumps; } } + public List HuePickers{ get{ return m_HuePickers; } } + public List Menus{ get{ return m_Menus; } } + + private static int m_GumpCap = 512, m_HuePickerCap = 512, m_MenuCap = 512; + + public static int GumpCap{ get{ return m_GumpCap; } set{ m_GumpCap = value; } } + public static int HuePickerCap{ get{ return m_HuePickerCap; } set{ m_HuePickerCap = value; } } + public static int MenuCap{ get{ return m_MenuCap; } set{ m_MenuCap = value; } } + + public void AddMenu( IMenu menu ) + { + if ( m_Menus == null ) + return; + + if ( m_Menus.Count >= m_MenuCap ) + { + Console.WriteLine( "Client: {0}: Exceeded menu cap, disconnecting...", this ); + Dispose(); + } + else + { + m_Menus.Add( menu ); + } + } + + public void RemoveMenu( int index ) + { + if ( m_Menus == null ) + return; + + m_Menus.RemoveAt( index ); + } + + public void AddHuePicker( HuePicker huePicker ) + { + if ( m_HuePickers == null ) + return; + + if ( m_HuePickers.Count >= m_HuePickerCap ) + { + Console.WriteLine( "Client: {0}: Exceeded hue picker cap, disconnecting...", this ); + Dispose(); + } + else + { + m_HuePickers.Add( huePicker ); + } + } + + public void RemoveHuePicker( int index ) + { + if ( m_HuePickers == null ) + return; + + m_HuePickers.RemoveAt( index ); + } + + public void AddGump( Gump g ) + { + if ( m_Gumps == null ) + return; + + if ( m_Gumps.Count >= m_GumpCap ) + { + Console.WriteLine( "Client: {0}: Exceeded gump cap, disconnecting...", this ); + Dispose(); + } + else + { + m_Gumps.Add( g ); + } + } + + public void RemoveGump( int index ) + { + if ( m_Gumps == null ) + return; + else if( index >= m_Gumps.Count ) + { + Console.WriteLine( "Error: Attempting to remove Gump with index not in bounds of array." ); + return; + } + + m_Gumps.RemoveAt( index ); + } + + public CityInfo[] CityInfo + { + get + { + return m_CityInfo; + } + set + { + m_CityInfo = value; + } + } + + public Mobile Mobile + { + get + { + return m_Mobile; + } + set + { + m_Mobile = value; + } + } + + public ServerInfo[] ServerInfo + { + get + { + return m_ServerInfo; + } + set + { + m_ServerInfo = value; + } + } + + public IAccount Account + { + get + { + return m_Account; + } + set + { + m_Account = value; + } + } + + public override string ToString() + { + return m_ToString; + } + + private static List m_Instances = new List(); + + public static List Instances + { + get + { + return m_Instances; + } + } + + private static BufferPool m_ReceiveBufferPool = new BufferPool( "Receive", 2048, 2048 ); + + public NetState( Socket socket, MessagePump messagePump ) + { + m_Socket = socket; + m_Buffer = new ByteQueue(); + m_Seeded = false; + m_Running = false; + m_RecvBuffer = m_ReceiveBufferPool.AcquireBuffer(); + m_MessagePump = messagePump; + m_Gumps = new List(); + m_HuePickers = new List(); + m_Menus = new List(); + m_Trades = new List(); + + m_SendQueue = new SendQueue(); + + m_NextCheckActivity = DateTime.Now + TimeSpan.FromMinutes( 0.5 ); + + m_Instances.Add( this ); + + try{ m_Address = ((IPEndPoint)m_Socket.RemoteEndPoint).Address; m_ToString = m_Address.ToString(); } + catch{ m_Address = IPAddress.None; m_ToString = "(error)"; } + + m_ConnectedOn = DateTime.Now; + + if ( m_CreatedCallback != null ) + m_CreatedCallback( this ); + } + + public void Send( Packet p ) + { + if ( m_Socket == null || m_BlockAllPackets ) + { + p.OnSend(); + return; + } + + PacketProfile prof = PacketProfile.GetOutgoingProfile( (byte)p.PacketID ); + DateTime start = ( prof == null ? DateTime.MinValue : DateTime.Now ); + + int length; + byte[] buffer = p.Compile( m_CompressionEnabled, out length ); + + if ( buffer != null ) + { + if ( buffer.Length <= 0 || length <= 0 ) + { + p.OnSend(); + return; + } + + if ( m_Encoder != null ) + m_Encoder.EncodeOutgoingPacket( this, ref buffer, ref length ); + + SendEnqueueResult enqueueResult; + + lock ( m_SendQueue ) + enqueueResult = ( m_SendQueue.Enqueue( buffer, length ) ); + + if ( enqueueResult == SendEnqueueResult.Begin ) + { + int sendLength = 0; + byte[] sendBuffer = m_SendQueue.Peek( ref sendLength ); + + try + { + IAsyncResult res = m_Socket.BeginSend( sendBuffer, 0, sendLength, SocketFlags.None, m_OnSend, null ); + //Console.WriteLine( "Send: {0}: Begin send of {1} bytes", this, sendLength ); + } + catch // ( Exception ex ) + { + //Console.WriteLine(ex); + Dispose( false ); + } + } + else if ( enqueueResult == SendEnqueueResult.Overflow ) + { + Console.WriteLine( "Client: {0}: Too much data pending, disconnecting...", this ); + Dispose( false ); + } + + p.OnSend(); + + if ( prof != null ) + prof.Record( length, DateTime.Now - start ); + } + else + { + Dispose(); + } + } + + public static void FlushAll() + { + for ( int i = 0; i < m_Instances.Count; ++i ) + { + NetState ns = m_Instances[i]; + + ns.Flush(); + } + } + + public bool Flush() + { + if ( m_Socket == null || !m_SendQueue.IsFlushReady ) + return false; + + int length = 0; + byte[] buffer; + + lock ( m_SendQueue ) + buffer = m_SendQueue.CheckFlushReady( ref length ); + + if ( buffer != null ) + { + try + { + IAsyncResult res = m_Socket.BeginSend( buffer, 0, length, SocketFlags.None, m_OnSend, null ); + return true; + //Console.WriteLine( "Flush: {0}: Begin send of {1} bytes", this, length ); + } + catch // ( Exception ex ) + { + //Console.WriteLine(ex); + Dispose( false ); + } + } + + return false; + } + + private static int m_CoalesceSleep = -1; + + public static int CoalesceSleep + { + get{ return m_CoalesceSleep; } + set{ m_CoalesceSleep = value; } + } + + private void OnSend( IAsyncResult asyncResult ) + { + if ( m_Socket == null ) + return; + + try + { + int bytes = m_Socket.EndSend( asyncResult ); + + if ( bytes <= 0 ) + { + Dispose( false ); + return; + } + + //Console.WriteLine( "OnSend: {0}: Complete send of {1} bytes", this, bytes ); + + m_NextCheckActivity = DateTime.Now + TimeSpan.FromMinutes( 1.2 ); + + if ( m_CoalesceSleep >= 0 ) + System.Threading.Thread.Sleep( m_CoalesceSleep ); + + int length = 0; + byte[] queued; + + lock ( m_SendQueue ) + queued = m_SendQueue.Dequeue( ref length ); + + if ( queued != null ) + { + IAsyncResult res = m_Socket.BeginSend( queued, 0, length, SocketFlags.None, m_OnSend, null ); + //Console.WriteLine( "OnSend: {0}: Begin send of {1} bytes", this, length ); + } + } + catch // ( Exception ex ) + { + //Console.WriteLine(ex); + Dispose( false ); + } + } + + public void Start() + { + m_OnReceive = new AsyncCallback( OnReceive ); + m_OnSend = new AsyncCallback( OnSend ); + + m_Running = true; + + if ( m_Socket == null || m_Paused ) + return; + + try + { + lock ( m_AsyncLock ) + { + if ( (m_AsyncState & (AsyncState.Pending | AsyncState.Paused)) == 0 ) + InternalBeginReceive(); + } + } + catch // ( Exception ex ) + { + //Console.WriteLine(ex); + Dispose( false ); + } + } + + public void LaunchBrowser( string url ) + { + Send( new MessageLocalized( Serial.MinusOne, -1, MessageType.Label, 0x35, 3, 501231, "", "" ) ); + Send( new LaunchBrowser( url ) ); + } + + private DateTime m_NextCheckActivity; + + public bool CheckAlive() + { + if ( m_Socket == null ) + return false; + + if ( DateTime.Now < m_NextCheckActivity ) + return true; + + Console.WriteLine( "Client: {0}: Disconnecting due to inactivity...", this ); + + Dispose(); + return false; + } + + private void OnReceive( IAsyncResult asyncResult ) + { + if ( m_Socket == null ) + return; + + try + { + int byteCount = m_Socket.EndReceive( asyncResult ); + + if ( byteCount > 0 ) + { + m_NextCheckActivity = DateTime.Now + TimeSpan.FromMinutes( 1.2 ); + + byte[] buffer = m_RecvBuffer; + + if ( m_Encoder != null ) + m_Encoder.DecodeIncomingPacket( this, ref buffer, ref byteCount ); + + lock ( m_Buffer ) + m_Buffer.Enqueue( buffer, 0, byteCount ); + + m_MessagePump.OnReceive( this ); + + lock ( m_AsyncLock ) + { + m_AsyncState &= ~AsyncState.Pending; + + if ( (m_AsyncState & AsyncState.Paused) == 0 ) + InternalBeginReceive(); + } + } + else + { + Dispose( false ); + } + } + catch // ( Exception ex ) + { + //Console.WriteLine(ex); + Dispose( false ); + } + } + + public void Dispose() + { + Dispose( true ); + } + + private bool m_Disposing; + + public void Dispose( bool flush ) + { + if ( m_Socket == null || m_Disposing ) + return; + + m_Disposing = true; + + if ( flush ) + flush = Flush(); + + try { m_Socket.Shutdown( SocketShutdown.Both ); } + catch {} + + try { m_Socket.Close(); } + catch {} + + if ( m_RecvBuffer != null ) + m_ReceiveBufferPool.ReleaseBuffer( m_RecvBuffer ); + + m_Socket = null; + + m_Buffer = null; + m_RecvBuffer = null; + m_OnReceive = null; + m_OnSend = null; + m_Running = false; + + m_Disposed.Enqueue( this ); + + if ( /*!flush &&*/ !m_SendQueue.IsEmpty ) + { + lock ( m_SendQueue ) + m_SendQueue.Clear(); + } + } + + public static void Initialize() + { + Timer.DelayCall( TimeSpan.FromMinutes( 1.0 ), TimeSpan.FromMinutes( 1.5 ), new TimerCallback( CheckAllAlive ) ); + } + + public static void CheckAllAlive() + { + try + { + for ( int i = 0; i < m_Instances.Count; ++i ) + m_Instances[i].CheckAlive(); + } + catch // ( Exception ex ) + { + //Console.WriteLine(ex); + } + } + + private static Queue m_Disposed = Queue.Synchronized( new Queue() ); + + public static void ProcessDisposedQueue() + { + int breakout = 0; + + while ( breakout < 200 && m_Disposed.Count > 0 ) + { + ++breakout; + + NetState ns = (NetState)m_Disposed.Dequeue(); + + Mobile m = ns.m_Mobile; + IAccount a = ns.m_Account; + + if ( m != null ) + { + m.NetState = null; + ns.m_Mobile = null; + } + + ns.m_Gumps.Clear(); + ns.m_Menus.Clear(); + ns.m_HuePickers.Clear(); + ns.m_Account = null; + ns.m_ServerInfo = null; + ns.m_CityInfo = null; + + m_Instances.Remove( ns ); + + if ( a != null ) + Console.WriteLine( "Client: {0}: Disconnected. [{1} Online] [{2}]", ns, m_Instances.Count, a ); + else + Console.WriteLine( "Client: {0}: Disconnected. [{1} Online]", ns, m_Instances.Count ); + } + } + + public bool Running + { + get + { + return m_Running; + } + } + + public bool Seeded + { + get + { + return m_Seeded; + } + set + { + m_Seeded = value; + } + } + + public Socket Socket + { + get + { + return m_Socket; + } + } + + public ByteQueue Buffer + { + get + { + return m_Buffer; + } + } + + public ExpansionInfo ExpansionInfo + { + get + { + for( int i = ExpansionInfo.Table.Length -1; i >= 0; i-- ) + { + ExpansionInfo info = ExpansionInfo.Table[i]; + + if ( (info.RequiredClient != null && this.Version >= info.RequiredClient) || ((this.Flags & info.NetStateFlag) != 0) ) + return info; + } + + return ExpansionInfo.GetInfo( Expansion.None ); + } + } + + public Expansion Expansion + { + get{ return (Expansion)this.ExpansionInfo.ID; } + } + + public bool SupportsExpansion( ExpansionInfo info, bool checkCoreExpansion ) + { + if( info == null || ( checkCoreExpansion && (int)Core.Expansion < info.ID ) ) + return false; + + if ( info.RequiredClient != null ) + return (this.Version >= info.RequiredClient); + + return ( (this.Flags & info.NetStateFlag) != 0); + } + + public bool SupportsExpansion( Expansion ex, bool checkCoreExpansion ) + { + return SupportsExpansion( ExpansionInfo.GetInfo( ex ), checkCoreExpansion ); + } + + public bool SupportsExpansion( Expansion ex ) + { + return SupportsExpansion( ex, true ); + } + + public bool SupportsExpansion( ExpansionInfo info ) + { + return SupportsExpansion( info, true ); + } + } +} \ No newline at end of file diff --git a/Server/Network/PacketHandler.cs b/Server/Network/PacketHandler.cs new file mode 100644 index 000000000..8412fe730 --- /dev/null +++ b/Server/Network/PacketHandler.cs @@ -0,0 +1,82 @@ +/*************************************************************************** + * PacketHandler.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: PacketHandler.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Network +{ + public delegate void OnPacketReceive( NetState state, PacketReader pvSrc ); + public delegate bool ThrottlePacketCallback( NetState state ); + + public class PacketHandler + { + private int m_PacketID; + private int m_Length; + private bool m_Ingame; + private OnPacketReceive m_OnReceive; + private ThrottlePacketCallback m_ThrottleCallback; + + public PacketHandler( int packetID, int length, bool ingame, OnPacketReceive onReceive ) + { + m_PacketID = packetID; + m_Length = length; + m_Ingame = ingame; + m_OnReceive = onReceive; + } + + public int PacketID + { + get + { + return m_PacketID; + } + } + + public int Length + { + get + { + return m_Length; + } + } + + public OnPacketReceive OnReceive + { + get + { + return m_OnReceive; + } + } + + public ThrottlePacketCallback ThrottleCallback + { + get{ return m_ThrottleCallback; } + set{ m_ThrottleCallback = value; } + } + + public bool Ingame + { + get + { + return m_Ingame; + } + } + } +} \ No newline at end of file diff --git a/Server/Network/PacketHandlers.cs b/Server/Network/PacketHandlers.cs new file mode 100644 index 000000000..41cbd24ab --- /dev/null +++ b/Server/Network/PacketHandlers.cs @@ -0,0 +1,2261 @@ +/*************************************************************************** + * PacketHandlers.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: PacketHandlers.cs 128 2006-03-08 20:41:33Z zippy $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Text; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using Server.Accounting; +using Server.Gumps; +using Server.Targeting; +using Server.Items; +using Server.Menus; +using Server.Mobiles; +using Server.Movement; +using Server.Prompts; +using Server.HuePickers; +using Server.ContextMenus; +using CV = Server.ClientVersion; + +namespace Server.Network +{ + public enum MessageType + { + Regular = 0x00, + System = 0x01, + Emote = 0x02, + Label = 0x06, + Focus = 0x07, + Whisper = 0x08, + Yell = 0x09, + Spell = 0x0A, + + Guild = 0x0D, + Alliance = 0x0E, + Command = 0x0F, + + Encoded = 0xC0 + } + + public class PacketHandlers + { + private static PacketHandler[] m_Handlers; + + private static PacketHandler[] m_ExtendedHandlersLow; + private static Dictionary m_ExtendedHandlersHigh; + + private static EncodedPacketHandler[] m_EncodedHandlersLow; + private static Dictionary m_EncodedHandlersHigh; + + public static PacketHandler[] Handlers + { + get{ return m_Handlers; } + } + + static PacketHandlers() + { + m_Handlers = new PacketHandler[0x100]; + + m_ExtendedHandlersLow = new PacketHandler[0x100]; + m_ExtendedHandlersHigh = new Dictionary(); + + m_EncodedHandlersLow = new EncodedPacketHandler[0x100]; + m_EncodedHandlersHigh = new Dictionary(); + + Register( 0x00, 104, false, new OnPacketReceive( CreateCharacter ) ); + Register( 0x01, 5, false, new OnPacketReceive( Disconnect ) ); + Register( 0x02, 7, true, new OnPacketReceive( MovementReq ) ); + Register( 0x03, 0, true, new OnPacketReceive( AsciiSpeech ) ); + Register( 0x04, 2, true, new OnPacketReceive( GodModeRequest ) ); + Register( 0x05, 5, true, new OnPacketReceive( AttackReq ) ); + Register( 0x06, 5, true, new OnPacketReceive( UseReq ) ); + Register( 0x07, 7, true, new OnPacketReceive( LiftReq ) ); + Register( 0x08, 14, true, new OnPacketReceive( DropReq ) ); + Register( 0x09, 5, true, new OnPacketReceive( LookReq ) ); + Register( 0x0A, 11, true, new OnPacketReceive( Edit ) ); + Register( 0x12, 0, true, new OnPacketReceive( TextCommand ) ); + Register( 0x13, 10, true, new OnPacketReceive( EquipReq ) ); + Register( 0x14, 6, true, new OnPacketReceive( ChangeZ ) ); + Register( 0x22, 3, true, new OnPacketReceive( Resynchronize ) ); + Register( 0x2C, 2, true, new OnPacketReceive( DeathStatusResponse ) ); + Register( 0x34, 10, true, new OnPacketReceive( MobileQuery ) ); + Register( 0x3A, 0, true, new OnPacketReceive( ChangeSkillLock ) ); + Register( 0x3B, 0, true, new OnPacketReceive( VendorBuyReply ) ); + Register( 0x47, 11, true, new OnPacketReceive( NewTerrain ) ); + Register( 0x48, 73, true, new OnPacketReceive( NewAnimData ) ); + Register( 0x58, 106, true, new OnPacketReceive( NewRegion ) ); + Register( 0x5D, 73, false, new OnPacketReceive( PlayCharacter ) ); + Register( 0x61, 9, true, new OnPacketReceive( DeleteStatic ) ); + Register( 0x6C, 19, true, new OnPacketReceive( TargetResponse ) ); + Register( 0x6F, 0, true, new OnPacketReceive( SecureTrade ) ); + Register( 0x72, 5, true, new OnPacketReceive( SetWarMode ) ); + Register( 0x73, 2, false, new OnPacketReceive( PingReq ) ); + Register( 0x75, 35, true, new OnPacketReceive( RenameRequest ) ); + Register( 0x79, 9, true, new OnPacketReceive( ResourceQuery ) ); + Register( 0x7E, 2, true, new OnPacketReceive( GodviewQuery ) ); + Register( 0x7D, 13, true, new OnPacketReceive( MenuResponse ) ); + Register( 0x80, 62, false, new OnPacketReceive( AccountLogin ) ); + Register( 0x83, 39, false, new OnPacketReceive( DeleteCharacter ) ); + Register( 0x91, 65, false, new OnPacketReceive( GameLogin ) ); + Register( 0x95, 9, true, new OnPacketReceive( HuePickerResponse ) ); + Register( 0x96, 0, true, new OnPacketReceive( GameCentralMoniter ) ); + Register( 0x98, 0, true, new OnPacketReceive( MobileNameRequest ) ); + Register( 0x9A, 0, true, new OnPacketReceive( AsciiPromptResponse ) ); + Register( 0x9B, 258, true, new OnPacketReceive( HelpRequest ) ); + Register( 0x9D, 51, true, new OnPacketReceive( GMSingle ) ); + Register( 0x9F, 0, true, new OnPacketReceive( VendorSellReply ) ); + Register( 0xA0, 3, false, new OnPacketReceive( PlayServer ) ); + Register( 0xA4, 149, false, new OnPacketReceive( SystemInfo ) ); + Register( 0xA7, 4, true, new OnPacketReceive( RequestScrollWindow ) ); + Register( 0xAD, 0, true, new OnPacketReceive( UnicodeSpeech ) ); + Register( 0xB1, 0, true, new OnPacketReceive( DisplayGumpResponse ) ); + Register( 0xB5, 64, true, new OnPacketReceive( ChatRequest ) ); + Register( 0xB6, 9, true, new OnPacketReceive( ObjectHelpRequest ) ); + Register( 0xB8, 0, true, new OnPacketReceive( ProfileReq ) ); + Register( 0xBB, 9, false, new OnPacketReceive( AccountID ) ); + Register( 0xBD, 0, true, new OnPacketReceive( ClientVersion ) ); + Register( 0xBE, 0, true, new OnPacketReceive( AssistVersion ) ); + Register( 0xBF, 0, true, new OnPacketReceive( ExtendedCommand ) ); + Register( 0xC2, 0, true, new OnPacketReceive( UnicodePromptResponse ) ); + Register( 0xC8, 2, true, new OnPacketReceive( SetUpdateRange ) ); + Register( 0xC9, 6, true, new OnPacketReceive( TripTime ) ); + Register( 0xCA, 6, true, new OnPacketReceive( UTripTime ) ); + Register( 0xCF, 0, false, new OnPacketReceive( AccountLogin ) ); + Register( 0xD0, 0, true, new OnPacketReceive( ConfigurationFile ) ); + Register( 0xD1, 2, true, new OnPacketReceive( LogoutReq ) ); + Register( 0xD6, 0, true, new OnPacketReceive( BatchQueryProperties ) ); + Register( 0xD7, 0, true, new OnPacketReceive( EncodedCommand ) ); + + RegisterExtended( 0x05, false, new OnPacketReceive( ScreenSize ) ); + RegisterExtended( 0x06, true, new OnPacketReceive( PartyMessage ) ); + RegisterExtended( 0x07, true, new OnPacketReceive( QuestArrow ) ); + RegisterExtended( 0x09, true, new OnPacketReceive( DisarmRequest ) ); + RegisterExtended( 0x0A, true, new OnPacketReceive( StunRequest ) ); + RegisterExtended( 0x0B, false, new OnPacketReceive( Language ) ); + RegisterExtended( 0x0C, true, new OnPacketReceive( CloseStatus ) ); + RegisterExtended( 0x0E, true, new OnPacketReceive( Animate ) ); + RegisterExtended( 0x0F, false, new OnPacketReceive( Empty ) ); // What's this? + RegisterExtended( 0x10, true, new OnPacketReceive( QueryProperties ) ); + RegisterExtended( 0x13, true, new OnPacketReceive( ContextMenuRequest ) ); + RegisterExtended( 0x15, true, new OnPacketReceive( ContextMenuResponse ) ); + RegisterExtended( 0x1A, true, new OnPacketReceive( StatLockChange ) ); + RegisterExtended( 0x1C, true, new OnPacketReceive( CastSpell ) ); + RegisterExtended( 0x24, false, new OnPacketReceive( UnhandledBF ) ); + + RegisterEncoded( 0x19, true, new OnEncodedPacketReceive( SetAbility ) ); + RegisterEncoded( 0x28, true, new OnEncodedPacketReceive( GuildGumpRequest ) ); + + RegisterEncoded( 0x32, true, new OnEncodedPacketReceive( QuestGumpRequest ) ); + } + + public static void Register( int packetID, int length, bool ingame, OnPacketReceive onReceive ) + { + m_Handlers[packetID] = new PacketHandler( packetID, length, ingame, onReceive ); + } + + public static PacketHandler GetHandler( int packetID ) + { + return m_Handlers[packetID]; + } + + public static void RegisterExtended( int packetID, bool ingame, OnPacketReceive onReceive ) + { + if ( packetID >= 0 && packetID < 0x100 ) + m_ExtendedHandlersLow[packetID] = new PacketHandler( packetID, 0, ingame, onReceive ); + else + m_ExtendedHandlersHigh[packetID] = new PacketHandler( packetID, 0, ingame, onReceive ); + } + + public static PacketHandler GetExtendedHandler( int packetID ) + { + if ( packetID >= 0 && packetID < 0x100 ) + return m_ExtendedHandlersLow[packetID]; + else + { + PacketHandler handler; + m_ExtendedHandlersHigh.TryGetValue( packetID, out handler ); + return handler; + } + } + + public static void RemoveExtendedHandler( int packetID ) + { + if ( packetID >= 0 && packetID < 0x100 ) + m_ExtendedHandlersLow[packetID] = null; + else + m_ExtendedHandlersHigh.Remove( packetID ); + } + + public static void RegisterEncoded( int packetID, bool ingame, OnEncodedPacketReceive onReceive ) + { + if ( packetID >= 0 && packetID < 0x100 ) + m_EncodedHandlersLow[packetID] = new EncodedPacketHandler( packetID, ingame, onReceive ); + else + m_EncodedHandlersHigh[packetID] = new EncodedPacketHandler( packetID, ingame, onReceive ); + } + + public static EncodedPacketHandler GetEncodedHandler( int packetID ) + { + if ( packetID >= 0 && packetID < 0x100 ) + return m_EncodedHandlersLow[packetID]; + else + { + EncodedPacketHandler handler; + m_EncodedHandlersHigh.TryGetValue( packetID, out handler ); + return handler; + } + } + + public static void RemoveEncodedHandler( int packetID ) + { + if ( packetID >= 0 && packetID < 0x100 ) + m_EncodedHandlersLow[packetID] = null; + else + m_EncodedHandlersHigh.Remove( packetID ); + } + + private static void UnhandledBF( NetState state, PacketReader pvSrc ) + { + } + + public static void Empty( NetState state, PacketReader pvSrc ) + { + } + + public static void SetAbility( NetState state, IEntity e, EncodedReader reader ) + { + EventSink.InvokeSetAbility( new SetAbilityEventArgs( state.Mobile, reader.ReadInt32() ) ); + } + + public static void GuildGumpRequest( NetState state, IEntity e, EncodedReader reader ) + { + EventSink.InvokeGuildGumpRequest( new GuildGumpRequestArgs( state.Mobile ) ); + } + + public static void QuestGumpRequest( NetState state, IEntity e, EncodedReader reader ) + { + EventSink.InvokeQuestGumpRequest( new QuestGumpRequestArgs( state.Mobile ) ); + } + + public static void EncodedCommand( NetState state, PacketReader pvSrc ) + { + IEntity e = World.FindEntity( pvSrc.ReadInt32() ); + int packetID = pvSrc.ReadUInt16(); + + EncodedPacketHandler ph = GetEncodedHandler( packetID ); + + if ( ph != null ) + { + if ( ph.Ingame && state.Mobile == null ) + { + Console.WriteLine( "Client: {0}: Sent ingame packet (0xD7x{1:X2}) before having been attached to a mobile", state, packetID ); + state.Dispose(); + } + else if ( ph.Ingame && state.Mobile.Deleted ) + { + state.Dispose(); + } + else + { + ph.OnReceive( state, e, new EncodedReader( pvSrc ) ); + } + } + else + { + pvSrc.Trace( state ); + } + } + + public static void RenameRequest( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + Mobile targ = World.FindMobile( pvSrc.ReadInt32() ); + + if ( targ != null ) + EventSink.InvokeRenameRequest( new RenameRequestEventArgs( from, targ, pvSrc.ReadStringSafe() ) ); + } + + public static void ChatRequest( NetState state, PacketReader pvSrc ) + { + EventSink.InvokeChatRequest( new ChatRequestEventArgs( state.Mobile ) ); + } + + public static void SecureTrade( NetState state, PacketReader pvSrc ) + { + switch ( pvSrc.ReadByte() ) + { + case 1: // Cancel + { + Serial serial = pvSrc.ReadInt32(); + + SecureTradeContainer cont = World.FindItem( serial ) as SecureTradeContainer; + + if ( cont != null && cont.Trade != null && (cont.Trade.From.Mobile == state.Mobile || cont.Trade.To.Mobile == state.Mobile) ) + cont.Trade.Cancel(); + + break; + } + case 2: // Check + { + Serial serial = pvSrc.ReadInt32(); + + SecureTradeContainer cont = World.FindItem( serial ) as SecureTradeContainer; + + if ( cont != null ) + { + SecureTrade trade = cont.Trade; + + bool value = ( pvSrc.ReadInt32() != 0 ); + + if ( trade != null && trade.From.Mobile == state.Mobile ) + { + trade.From.Accepted = value; + trade.Update(); + } + else if ( trade != null && trade.To.Mobile == state.Mobile ) + { + trade.To.Accepted = value; + trade.Update(); + } + } + + break; + } + } + } + + public static void VendorBuyReply( NetState state, PacketReader pvSrc ) + { + pvSrc.Seek( 1, SeekOrigin.Begin ); + + int msgSize = pvSrc.ReadUInt16(); + Mobile vendor = World.FindMobile( pvSrc.ReadInt32() ); + byte flag = pvSrc.ReadByte(); + + if ( vendor == null ) + { + return; + } + else if ( vendor.Deleted || !Utility.RangeCheck( vendor.Location, state.Mobile.Location, 10 ) ) + { + state.Send( new EndVendorBuy( vendor ) ); + return; + } + + if ( flag == 0x02 ) + { + msgSize -= 1+2+4+1; + + if ( (msgSize / 7) > 100 ) + return; + + ArrayList buyList = new ArrayList( msgSize/7 ); + for ( ;msgSize>0;msgSize-=7) + { + byte layer = pvSrc.ReadByte(); + Serial serial = pvSrc.ReadInt32(); + int amount = pvSrc.ReadInt16(); + + buyList.Add( new BuyItemResponse( serial, amount ) ); + } + + if ( buyList.Count > 0 ) + { + IVendor v = vendor as IVendor; + + if ( v != null && v.OnBuyItems( state.Mobile, buyList ) ) + state.Send( new EndVendorBuy( vendor ) ); + } + } + else + { + state.Send( new EndVendorBuy( vendor ) ); + } + } + + public static void VendorSellReply( NetState state, PacketReader pvSrc ) + { + Serial serial = pvSrc.ReadInt32(); + Mobile vendor = World.FindMobile( serial ); + + if ( vendor == null ) + { + return; + } + else if ( vendor.Deleted || !Utility.RangeCheck( vendor.Location, state.Mobile.Location, 10 ) ) + { + state.Send( new EndVendorSell( vendor ) ); + return; + } + + int count = pvSrc.ReadUInt16(); + if ( count < 100 && pvSrc.Size == (1+2+4+2+(count*6)) ) + { + ArrayList sellList = new ArrayList( count ); + + for (int i=0;i 0 ) + sellList.Add( new SellItemResponse( item, Amount ) ); + } + + if ( sellList.Count > 0 ) + { + IVendor v = vendor as IVendor; + + if ( v != null && v.OnSellItems( state.Mobile, sellList ) ) + state.Send( new EndVendorSell( vendor ) ); + } + } + } + + public static void DeleteCharacter( NetState state, PacketReader pvSrc ) + { + pvSrc.Seek( 30, SeekOrigin.Current ); + int index = pvSrc.ReadInt32(); + + EventSink.InvokeDeleteRequest( new DeleteRequestEventArgs( state, index ) ); + } + + public static void ResourceQuery( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + } + } + + public static void GameCentralMoniter( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + int type = pvSrc.ReadByte(); + int num1 = pvSrc.ReadInt32(); + + Console.WriteLine( "God Client: {0}: Game central moniter", state ); + Console.WriteLine( " - Type: {0}", type ); + Console.WriteLine( " - Number: {0}", num1 ); + + pvSrc.Trace( state ); + } + } + + public static void GodviewQuery( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + Console.WriteLine( "God Client: {0}: Godview query 0x{1:X}", state, pvSrc.ReadByte() ); + } + } + + public static void GMSingle( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + pvSrc.Trace( state ); + } + + public static void DeathStatusResponse( NetState state, PacketReader pvSrc ) + { + // Ignored + } + + public static void ObjectHelpRequest( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + Serial serial = pvSrc.ReadInt32(); + int unk = pvSrc.ReadByte(); + string lang = pvSrc.ReadString( 3 ); + + if ( serial.IsItem ) + { + Item item = World.FindItem( serial ); + + if ( item != null && from.Map == item.Map && Utility.InUpdateRange( item.GetWorldLocation(), from.Location ) && from.CanSee( item ) ) + item.OnHelpRequest( from ); + } + else if ( serial.IsMobile ) + { + Mobile m = World.FindMobile( serial ); + + if ( m != null && from.Map == m.Map && Utility.InUpdateRange( m.Location, from.Location ) && from.CanSee( m ) ) + m.OnHelpRequest( m ); + } + } + + public static void MobileNameRequest( NetState state, PacketReader pvSrc ) + { + Mobile m = World.FindMobile( pvSrc.ReadInt32() ); + + if ( m != null && Utility.InUpdateRange( state.Mobile, m ) && state.Mobile.CanSee( m ) ) + state.Send( new MobileName( m ) ); + } + + public static void RequestScrollWindow( NetState state, PacketReader pvSrc ) + { + int lastTip = pvSrc.ReadInt16(); + int type = pvSrc.ReadByte(); + } + + public static void AttackReq( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + Mobile m = World.FindMobile( pvSrc.ReadInt32() ); + + if ( m != null ) + from.Attack( m ); + } + + public static void HuePickerResponse( NetState state, PacketReader pvSrc ) + { + int serial = pvSrc.ReadInt32(); + int value = pvSrc.ReadInt16(); + int hue = pvSrc.ReadInt16() & 0x3FFF; + + hue = Utility.ClipDyedHue( hue ); + + List pickers = state.HuePickers; + + for ( int i = 0; i < pickers.Count; ++i ) + { + HuePicker p = pickers[i]; + + if ( p.Serial == serial ) + { + state.RemoveHuePicker( i ); + + p.OnResponse( hue ); + + break; + } + } + } + + public static void TripTime( NetState state, PacketReader pvSrc ) + { + int unk1 = pvSrc.ReadByte(); + int unk2 = pvSrc.ReadInt32(); + + state.Send( new TripTimeResponse( unk1 ) ); + } + + public static void UTripTime( NetState state, PacketReader pvSrc ) + { + int unk1 = pvSrc.ReadByte(); + int unk2 = pvSrc.ReadInt32(); + + state.Send( new UTripTimeResponse( unk1 ) ); + } + + public static void ChangeZ( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + int z = pvSrc.ReadSByte(); + + Console.WriteLine( "God Client: {0}: Change Z ({1}, {2}, {3})", state, x, y, z ); + } + } + + public static void SystemInfo( NetState state, PacketReader pvSrc ) + { + int v1 = pvSrc.ReadByte(); + int v2 = pvSrc.ReadUInt16(); + int v3 = pvSrc.ReadByte(); + string s1 = pvSrc.ReadString( 32 ); + string s2 = pvSrc.ReadString( 32 ); + string s3 = pvSrc.ReadString( 32 ); + string s4 = pvSrc.ReadString( 32 ); + int v4 = pvSrc.ReadUInt16(); + int v5 = pvSrc.ReadUInt16(); + int v6 = pvSrc.ReadInt32(); + int v7 = pvSrc.ReadInt32(); + int v8 = pvSrc.ReadInt32(); + } + + public static void Edit( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + int type = pvSrc.ReadByte(); // 10 = static, 7 = npc, 4 = dynamic + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + int id = pvSrc.ReadInt16(); + int z = pvSrc.ReadSByte(); + int hue = pvSrc.ReadUInt16(); + + Console.WriteLine( "God Client: {0}: Edit {6} ({1}, {2}, {3}) 0x{4:X} (0x{5:X})", state, x, y, z, id, hue, type ); + } + } + + public static void DeleteStatic( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + int z = pvSrc.ReadInt16(); + int id = pvSrc.ReadUInt16(); + + Console.WriteLine( "God Client: {0}: Delete Static ({1}, {2}, {3}) 0x{4:X}", state, x, y, z, id ); + } + } + + public static void NewAnimData( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + Console.WriteLine( "God Client: {0}: New tile animation", state ); + + pvSrc.Trace( state ); + } + } + + public static void NewTerrain( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + int id = pvSrc.ReadUInt16(); + int width = pvSrc.ReadInt16(); + int height = pvSrc.ReadInt16(); + + Console.WriteLine( "God Client: {0}: New Terrain ({1}, {2})+({3}, {4}) 0x{5:X4}", state, x, y, width, height, id ); + } + } + + public static void NewRegion( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + string name = pvSrc.ReadString( 40 ); + int unk = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + int width = pvSrc.ReadInt16(); + int height = pvSrc.ReadInt16(); + int zStart = pvSrc.ReadInt16(); + int zEnd = pvSrc.ReadInt16(); + string desc = pvSrc.ReadString( 40 ); + int soundFX = pvSrc.ReadInt16(); + int music = pvSrc.ReadInt16(); + int nightFX = pvSrc.ReadInt16(); + int dungeon = pvSrc.ReadByte(); + int light = pvSrc.ReadInt16(); + + Console.WriteLine( "God Client: {0}: New Region '{1}' ('{2}')", state, name, desc ); + } + } + + public static void AccountID( NetState state, PacketReader pvSrc ) + { + } + + public static bool VerifyGC( NetState state ) + { + if ( state.Mobile == null || state.Mobile.AccessLevel <= AccessLevel.Counselor ) + { + if ( state.Running ) + Console.WriteLine( "Warning: {0}: Player using godclient, disconnecting", state ); + + state.Dispose(); + return false; + } + else + { + return true; + } + } + + public static void TextCommand( NetState state, PacketReader pvSrc ) + { + int type = pvSrc.ReadByte(); + string command = pvSrc.ReadString(); + + Mobile m = state.Mobile; + + switch ( type ) + { + case 0x00: // Go + { + if ( VerifyGC( state ) ) + { + try + { + string[] split = command.Split( ' ' ); + + int x = Utility.ToInt32( split[0] ); + int y = Utility.ToInt32( split[1] ); + + int z; + + if ( split.Length >= 3 ) + z = Utility.ToInt32( split[2] ); + else if ( m.Map != null ) + z = m.Map.GetAverageZ( x, y ); + else + z = 0; + + m.Location = new Point3D( x, y, z ); + } + catch + { + } + } + + break; + } + case 0xC7: // Animate + { + EventSink.InvokeAnimateRequest( new AnimateRequestEventArgs( m, command ) ); + + break; + } + case 0x24: // Use skill + { + int skillIndex; + + try{ skillIndex = Convert.ToInt32( command.Split( ' ' )[0] ); } + catch{ break; } + + Skills.UseSkill( m, skillIndex ); + + break; + } + case 0x43: // Open spellbook + { + int booktype; + + try{ booktype = Convert.ToInt32( command ); } + catch{ booktype = 1; } + + EventSink.InvokeOpenSpellbookRequest( new OpenSpellbookRequestEventArgs( m, booktype ) ); + + break; + } + case 0x27: // Cast spell from book + { + string[] split = command.Split( ' ' ); + + if ( split.Length > 0 ) + { + int spellID = Utility.ToInt32( split[0] ) - 1; + int serial = split.Length > 1 ? Utility.ToInt32( split[1] ) : -1; + + EventSink.InvokeCastSpellRequest( new CastSpellRequestEventArgs( m, spellID, World.FindItem( serial ) ) ); + } + + break; + } + case 0x58: // Open door + { + EventSink.InvokeOpenDoorMacroUsed( new OpenDoorMacroEventArgs( m ) ); + + break; + } + case 0x56: // Cast spell from macro + { + int spellID = Utility.ToInt32( command ) - 1; + + EventSink.InvokeCastSpellRequest( new CastSpellRequestEventArgs( m, spellID, null ) ); + + break; + } + case 0xF4: // Invoke virtues from macro + { + int virtueID = Utility.ToInt32( command ) - 1; + + EventSink.InvokeVirtueMacroRequest( new VirtueMacroRequestEventArgs( m, virtueID ) ); + + break; + } + default: + { + Console.WriteLine( "Client: {0}: Unknown text-command type 0x{1:X2}: {2}", state, type, command ); + break; + } + } + } + + public static void GodModeRequest( NetState state, PacketReader pvSrc ) + { + if ( VerifyGC( state ) ) + { + state.Send( new GodModeReply( pvSrc.ReadBoolean() ) ); + } + } + + public static void AsciiPromptResponse( NetState state, PacketReader pvSrc ) + { + int serial = pvSrc.ReadInt32(); + int prompt = pvSrc.ReadInt32(); + int type = pvSrc.ReadInt32(); + string text = pvSrc.ReadStringSafe(); + + if ( text.Length > 128 ) + return; + + Mobile from = state.Mobile; + Prompt p = from.Prompt; + + if ( p != null && p.Serial == serial && p.Serial == prompt ) + { + from.Prompt = null; + + if ( type == 0 ) + p.OnCancel( from ); + else + p.OnResponse( from, text ); + } + } + + public static void UnicodePromptResponse( NetState state, PacketReader pvSrc ) + { + int serial = pvSrc.ReadInt32(); + int prompt = pvSrc.ReadInt32(); + int type = pvSrc.ReadInt32(); + string lang = pvSrc.ReadString( 4 ); + string text = pvSrc.ReadUnicodeStringLESafe(); + + if ( text.Length > 128 ) + return; + + Mobile from = state.Mobile; + Prompt p = from.Prompt; + + if ( p != null && p.Serial == serial && p.Serial == prompt ) + { + from.Prompt = null; + + if ( type == 0 ) + p.OnCancel( from ); + else + p.OnResponse( from, text ); + } + } + + public static void MenuResponse( NetState state, PacketReader pvSrc ) + { + int serial = pvSrc.ReadInt32(); + int menuID = pvSrc.ReadInt16(); // unused in our implementation + int index = pvSrc.ReadInt16(); + int itemID = pvSrc.ReadInt16(); + int hue = pvSrc.ReadInt16(); + + List menus = state.Menus; + + for ( int i = 0; i < menus.Count; ++i ) + { + IMenu menu = menus[i]; + + if ( menu.Serial == serial ) + { + if ( index > 0 && index <= menu.EntryLength ) + menu.OnResponse( state, index - 1 ); + else + menu.OnCancel( state ); + + state.RemoveMenu( i ); + + return; + } + } + } + + public static void ProfileReq( NetState state, PacketReader pvSrc ) + { + int type = pvSrc.ReadByte(); + Serial serial = pvSrc.ReadInt32(); + + Mobile beholder = state.Mobile; + Mobile beheld = World.FindMobile( serial ); + + if ( beheld == null ) + return; + + switch ( type ) + { + case 0x00: // display request + { + EventSink.InvokeProfileRequest( new ProfileRequestEventArgs( beholder, beheld ) ); + + break; + } + case 0x01: // edit request + { + pvSrc.ReadInt16(); // Skip + int length = pvSrc.ReadUInt16(); + + if ( length > 511 ) + return; + + string text = pvSrc.ReadUnicodeString( length ); + + EventSink.InvokeChangeProfileRequest( new ChangeProfileRequestEventArgs( beholder, beheld, text ) ); + + break; + } + } + } + + public static void Disconnect( NetState state, PacketReader pvSrc ) + { + int minusOne = pvSrc.ReadInt32(); + } + + public static void LiftReq( NetState state, PacketReader pvSrc ) + { + Serial serial = pvSrc.ReadInt32(); + int amount = pvSrc.ReadUInt16(); + Item item = World.FindItem( serial ); + + bool rejected; + LRReason reject; + + state.Mobile.Lift( item, amount, out rejected, out reject ); + } + + public static void EquipReq( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + Item item = from.Holding; + + if ( item == null ) + return; + + from.Holding = null; + + pvSrc.Seek( 5, SeekOrigin.Current ); + Mobile to = World.FindMobile( pvSrc.ReadInt32() ); + + if ( to == null ) + to = from; + + if ( !to.AllowEquipFrom( from ) || !to.EquipItem( item ) ) + item.Bounce( from ); + + item.ClearBounce(); + } + + public static void DropReq( NetState state, PacketReader pvSrc ) + { + pvSrc.ReadInt32(); // serial, ignored + int x = pvSrc.ReadInt16(); + int y = pvSrc.ReadInt16(); + int z = pvSrc.ReadSByte(); + Serial dest = pvSrc.ReadInt32(); + + Point3D loc = new Point3D( x, y, z ); + + Mobile from = state.Mobile; + + if ( dest.IsMobile ) + from.Drop( World.FindMobile( dest ), loc ); + else if ( dest.IsItem ) + from.Drop( World.FindItem( dest ), loc ); + else + from.Drop( loc ); + } + + public static void ConfigurationFile( NetState state, PacketReader pvSrc ) + { + } + + public static void LogoutReq( NetState state, PacketReader pvSrc ) + { + state.Send( new LogoutAck() ); + } + + public static void ChangeSkillLock( NetState state, PacketReader pvSrc ) + { + Skill s = state.Mobile.Skills[pvSrc.ReadInt16()]; + + if ( s != null ) + s.SetLockNoRelay( (SkillLock)pvSrc.ReadByte() ); + } + + public static void HelpRequest( NetState state, PacketReader pvSrc ) + { + EventSink.InvokeHelpRequest( new HelpRequestEventArgs( state.Mobile ) ); + } + + public static void TargetResponse( NetState state, PacketReader pvSrc ) + { + int type = pvSrc.ReadByte(); + int targetID = pvSrc.ReadInt32(); + int flags = pvSrc.ReadByte(); + Serial serial = pvSrc.ReadInt32(); + int x = pvSrc.ReadInt16(), y = pvSrc.ReadInt16(), z = pvSrc.ReadInt16(); + int graphic = pvSrc.ReadInt16(); + + if ( targetID == unchecked( (int) 0xDEADBEEF ) ) + return; + + Mobile from = state.Mobile; + + Target t = from.Target; + + if ( t != null ) + { + if ( x == -1 && y == -1 && !serial.IsValid ) + { + // User pressed escape + t.Cancel( from, TargetCancelType.Canceled ); + } + else + { + object toTarget; + + if ( type == 1 ) + { + if ( graphic == 0 ) + { + toTarget = new LandTarget( new Point3D( x, y, z ), from.Map ); + } + else + { + Map map = from.Map; + + if ( map == null || map == Map.Internal ) + { + t.Cancel( from, TargetCancelType.Canceled ); + return; + } + else + { + Tile[] tiles = map.Tiles.GetStaticTiles( x, y, !t.DisallowMultis ); + + bool valid = false; + + for ( int i = 0; !valid && i < tiles.Length; ++i ) + { + if ( tiles[i].Z == z && (tiles[i].ID & 0x3FFF) == (graphic & 0x3FFF) ) + valid = true; + } + + if ( !valid ) + { + t.Cancel( from, TargetCancelType.Canceled ); + return; + } + else + { + toTarget = new StaticTarget( new Point3D( x, y, z ), graphic ); + } + } + } + } + else if ( serial.IsMobile ) + { + toTarget = World.FindMobile( serial ); + } + else if ( serial.IsItem ) + { + toTarget = World.FindItem( serial ); + } + else + { + t.Cancel( from, TargetCancelType.Canceled ); + return; + } + + t.Invoke( from, toTarget ); + } + } + } + + public static void DisplayGumpResponse( NetState state, PacketReader pvSrc ) + { + int serial = pvSrc.ReadInt32(); + int typeID = pvSrc.ReadInt32(); + int buttonID = pvSrc.ReadInt32(); + + List gumps = state.Gumps; + + for ( int i = 0; i < gumps.Count; ++i ) + { + Gump gump = gumps[i]; + + if ( gump.Serial == serial && gump.TypeID == typeID ) + { + int switchCount = pvSrc.ReadInt32(); + + if ( switchCount < 0 || switchCount > gump.m_Switches ) + { + Console.WriteLine( "Client: {0}: Invalid gump response, disconnecting...", state ); + state.Dispose(); + return; + } + + int[] switches = new int[switchCount]; + + for ( int j = 0; j < switches.Length; ++j ) + switches[j] = pvSrc.ReadInt32(); + + int textCount = pvSrc.ReadInt32(); + + if ( textCount < 0 || textCount > gump.m_TextEntries ) + { + Console.WriteLine( "Client: {0}: Invalid gump response, disconnecting...", state ); + state.Dispose(); + return; + } + + TextRelay[] textEntries = new TextRelay[textCount]; + + for ( int j = 0; j < textEntries.Length; ++j ) + { + int entryID = pvSrc.ReadUInt16(); + int textLength = pvSrc.ReadUInt16(); + + if ( textLength > 239 ) + return; + + string text = pvSrc.ReadUnicodeStringSafe( textLength ); + textEntries[j] = new TextRelay( entryID, text ); + } + + state.RemoveGump( i ); + gump.OnResponse( state, new RelayInfo( buttonID, switches, textEntries ) ); + + return; + } + } + + if ( typeID == 461 ) // Virtue gump + { + int switchCount = pvSrc.ReadInt32(); + + if ( buttonID == 1 && switchCount > 0 ) + { + Mobile beheld = World.FindMobile( pvSrc.ReadInt32() ); + + if ( beheld != null ) + EventSink.InvokeVirtueGumpRequest( new VirtueGumpRequestEventArgs( state.Mobile, beheld ) ); + } + else + { + Mobile beheld = World.FindMobile( serial ); + + if ( beheld != null ) + EventSink.InvokeVirtueItemRequest( new VirtueItemRequestEventArgs( state.Mobile, beheld, buttonID ) ); + } + } + } + + public static void SetWarMode( NetState state, PacketReader pvSrc ) + { + state.Mobile.DelayChangeWarmode( pvSrc.ReadBoolean() ); + } + + public static void Resynchronize( NetState state, PacketReader pvSrc ) + { + Mobile m = state.Mobile; + + state.Send( new MobileUpdate( m ) ); + state.Send( new MobileIncoming( m, m ) ); + + m.SendEverything(); + + state.Sequence = 0; + + m.ClearFastwalkStack(); + } + + private static int[] m_EmptyInts = new int[0]; + + public static void AsciiSpeech( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + MessageType type = (MessageType)pvSrc.ReadByte(); + int hue = pvSrc.ReadInt16(); + pvSrc.ReadInt16(); // font + string text = pvSrc.ReadStringSafe().Trim(); + + if ( text.Length <= 0 || text.Length > 128 ) + return; + + if ( !Enum.IsDefined( typeof( MessageType ), type ) ) + type = MessageType.Regular; + + from.DoSpeech( text, m_EmptyInts, type, Utility.ClipDyedHue( hue ) ); + } + + private static KeywordList m_KeywordList = new KeywordList(); + + public static void UnicodeSpeech( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + MessageType type = (MessageType)pvSrc.ReadByte(); + int hue = pvSrc.ReadInt16(); + pvSrc.ReadInt16(); // font + string lang = pvSrc.ReadString( 4 ); + string text; + + bool isEncoded = (type & MessageType.Encoded) != 0; + int[] keywords; + + if ( isEncoded ) + { + int value = pvSrc.ReadInt16(); + int count = (value & 0xFFF0) >> 4; + int hold = value & 0xF; + + if ( count < 0 || count > 50 ) + return; + + KeywordList keyList = m_KeywordList; + + for ( int i = 0; i < count; ++i ) + { + int speechID; + + if ( (i & 1) == 0 ) + { + hold <<= 8; + hold |= pvSrc.ReadByte(); + speechID = hold; + hold = 0; + } + else + { + value = pvSrc.ReadInt16(); + speechID = (value & 0xFFF0) >> 4; + hold = value & 0xF; + } + + if ( !keyList.Contains( speechID ) ) + keyList.Add( speechID ); + } + + text = pvSrc.ReadUTF8StringSafe(); + + keywords = keyList.ToArray(); + } + else + { + text = pvSrc.ReadUnicodeStringSafe(); + + keywords = m_EmptyInts; + } + + text = text.Trim(); + + if ( text.Length <= 0 || text.Length > 128 ) + return; + + type &= ~MessageType.Encoded; + + if ( !Enum.IsDefined( typeof( MessageType ), type ) ) + type = MessageType.Regular; + + from.Language = lang; + from.DoSpeech( text, keywords, type, Utility.ClipDyedHue( hue ) ); + } + + public static void UseReq( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + if ( from.AccessLevel >= AccessLevel.Counselor || DateTime.Now >= from.NextActionTime ) + { + int value = pvSrc.ReadInt32(); + + if ( (value & ~0x7FFFFFFF) != 0 ) + { + from.OnPaperdollRequest(); + } + else + { + Serial s = value; + + if ( s.IsMobile ) + { + Mobile m = World.FindMobile( s ); + + if ( m != null && !m.Deleted ) + from.Use( m ); + } + else if ( s.IsItem ) + { + Item item = World.FindItem( s ); + + if ( item != null && !item.Deleted ) + from.Use( item ); + } + } + + from.NextActionTime = DateTime.Now + TimeSpan.FromSeconds( 0.5 ); + } + else + { + from.SendActionMessage(); + } + } + + private static bool m_SingleClickProps; + + public static bool SingleClickProps + { + get{ return m_SingleClickProps; } + set{ m_SingleClickProps = value; } + } + + public static void LookReq( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + Serial s = pvSrc.ReadInt32(); + + if ( s.IsMobile ) + { + Mobile m = World.FindMobile( s ); + + if ( m != null && from.CanSee( m ) && Utility.InUpdateRange( from, m ) ) + { + if ( m_SingleClickProps ) + { + m.OnAosSingleClick( from ); + } + else + { + if ( from.Region.OnSingleClick( from, m ) ) + m.OnSingleClick( from ); + } + } + } + else if ( s.IsItem ) + { + Item item = World.FindItem( s ); + + if ( item != null && !item.Deleted && from.CanSee( item ) && Utility.InUpdateRange( from.Location, item.GetWorldLocation() ) ) + { + if ( m_SingleClickProps ) + { + item.OnAosSingleClick( from ); + } + else if ( from.Region.OnSingleClick( from, item ) ) + { + if ( item.Parent is Item ) + ((Item)item.Parent).OnSingleClickContained( from, item ); + + item.OnSingleClick( from ); + } + } + } + } + + public static void PingReq( NetState state, PacketReader pvSrc ) + { + state.Send( PingAck.Instantiate( pvSrc.ReadByte() ) ); + } + + public static void SetUpdateRange( NetState state, PacketReader pvSrc ) + { + state.Send( ChangeUpdateRange.Instantiate( 18 ) ); + } + + private const int BadFood = unchecked( (int)0xBAADF00D ); + private const int BadUOTD = unchecked( (int)0xFFCEFFCE ); + + public static void MovementReq( NetState state, PacketReader pvSrc ) + { + Direction dir = (Direction)pvSrc.ReadByte(); + int seq = pvSrc.ReadByte(); + int key = pvSrc.ReadInt32(); + + Mobile m = state.Mobile; + + if ( (state.Sequence == 0 && seq != 0) || !m.Move( dir ) ) + { + state.Send( new MovementRej( seq, m ) ); + state.Sequence = 0; + + m.ClearFastwalkStack(); + } + else + { + ++seq; + + if ( seq == 256 ) + seq = 1; + + state.Sequence = seq; + } + } + + public static int[] m_ValidAnimations = new int[] + { + 6, 21, 32, 33, + 100, 101, 102, + 103, 104, 105, + 106, 107, 108, + 109, 110, 111, + 112, 113, 114, + 115, 116, 117, + 118, 119, 120, + 121, 123, 124, + 125, 126, 127, + 128 + }; + + public static int[] ValidAnimations{ get{ return m_ValidAnimations; } set{ m_ValidAnimations = value; } } + + public static void Animate( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + int action = pvSrc.ReadInt32(); + + bool ok = false; + + for ( int i = 0; !ok && i < m_ValidAnimations.Length; ++i ) + ok = ( action == m_ValidAnimations[i] ); + + if ( from != null && ok && from.Alive && from.Body.IsHuman && !from.Mounted ) + from.Animate( action, 7, 1, true, false, 0 ); + } + + public static void QuestArrow( NetState state, PacketReader pvSrc ) + { + bool rightClick = pvSrc.ReadBoolean(); + Mobile from = state.Mobile; + + if ( from != null && from.QuestArrow != null ) + from.QuestArrow.OnClick( rightClick ); + } + + public static void ExtendedCommand( NetState state, PacketReader pvSrc ) + { + int packetID = pvSrc.ReadUInt16(); + + PacketHandler ph = GetExtendedHandler( packetID ); + + if ( ph != null ) + { + if ( ph.Ingame && state.Mobile == null ) + { + Console.WriteLine( "Client: {0}: Sent ingame packet (0xBFx{1:X2}) before having been attached to a mobile", state, packetID ); + state.Dispose(); + } + else if ( ph.Ingame && state.Mobile.Deleted ) + { + state.Dispose(); + } + else + { + ph.OnReceive( state, pvSrc ); + } + } + else + { + pvSrc.Trace( state ); + } + } + + public static void CastSpell( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + if ( from == null ) + return; + + Item spellbook = null; + + if ( pvSrc.ReadInt16() == 1 ) + spellbook = World.FindItem( pvSrc.ReadInt32() ); + + int spellID = pvSrc.ReadInt16() - 1; + + EventSink.InvokeCastSpellRequest( new CastSpellRequestEventArgs( from, spellID, spellbook ) ); + } + + public static void BatchQueryProperties( NetState state, PacketReader pvSrc ) + { + if ( !ObjectPropertyList.Enabled ) + return; + + Mobile from = state.Mobile; + + int length = pvSrc.Size-3; + + if ( length < 0 || (length%4) != 0 ) + return; + + int count = length/4; + + for ( int i = 0; i < count; ++i ) + { + Serial s = pvSrc.ReadInt32(); + + if ( s.IsMobile ) + { + Mobile m = World.FindMobile( s ); + + if ( m != null && from.CanSee( m ) && Utility.InUpdateRange( from, m ) ) + m.SendPropertiesTo( from ); + } + else if ( s.IsItem ) + { + Item item = World.FindItem( s ); + + if ( item != null && !item.Deleted && from.CanSee( item ) && Utility.InUpdateRange( from.Location, item.GetWorldLocation() ) ) + item.SendPropertiesTo( from ); + } + } + } + + public static void QueryProperties( NetState state, PacketReader pvSrc ) + { + if ( !ObjectPropertyList.Enabled ) + return; + + Mobile from = state.Mobile; + + Serial s = pvSrc.ReadInt32(); + + if ( s.IsMobile ) + { + Mobile m = World.FindMobile( s ); + + if ( m != null && from.CanSee( m ) && Utility.InUpdateRange( from, m ) ) + m.SendPropertiesTo( from ); + } + else if ( s.IsItem ) + { + Item item = World.FindItem( s ); + + if ( item != null && !item.Deleted && from.CanSee( item ) && Utility.InUpdateRange( from.Location, item.GetWorldLocation() ) ) + item.SendPropertiesTo( from ); + } + } + + public static void PartyMessage( NetState state, PacketReader pvSrc ) + { + if ( state.Mobile == null ) + return; + + switch ( pvSrc.ReadByte() ) + { + case 0x01: PartyMessage_AddMember( state, pvSrc ); break; + case 0x02: PartyMessage_RemoveMember( state, pvSrc ); break; + case 0x03: PartyMessage_PrivateMessage( state, pvSrc ); break; + case 0x04: PartyMessage_PublicMessage( state, pvSrc ); break; + case 0x06: PartyMessage_SetCanLoot( state, pvSrc ); break; + case 0x08: PartyMessage_Accept( state, pvSrc ); break; + case 0x09: PartyMessage_Decline( state, pvSrc ); break; + default: pvSrc.Trace( state ); break; + } + } + + public static void PartyMessage_AddMember( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnAdd( state.Mobile ); + } + + public static void PartyMessage_RemoveMember( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnRemove( state.Mobile, World.FindMobile( pvSrc.ReadInt32() ) ); + } + + public static void PartyMessage_PrivateMessage( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnPrivateMessage( state.Mobile, World.FindMobile( pvSrc.ReadInt32() ), pvSrc.ReadUnicodeStringSafe() ); + } + + public static void PartyMessage_PublicMessage( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnPublicMessage( state.Mobile, pvSrc.ReadUnicodeStringSafe() ); + } + + public static void PartyMessage_SetCanLoot( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnSetCanLoot( state.Mobile, pvSrc.ReadBoolean() ); + } + + public static void PartyMessage_Accept( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnAccept( state.Mobile, World.FindMobile( pvSrc.ReadInt32() ) ); + } + + public static void PartyMessage_Decline( NetState state, PacketReader pvSrc ) + { + if ( PartyCommands.Handler != null ) + PartyCommands.Handler.OnDecline( state.Mobile, World.FindMobile( pvSrc.ReadInt32() ) ); + } + + public static void StunRequest( NetState state, PacketReader pvSrc ) + { + EventSink.InvokeStunRequest( new StunRequestEventArgs( state.Mobile ) ); + } + + public static void DisarmRequest( NetState state, PacketReader pvSrc ) + { + EventSink.InvokeDisarmRequest( new DisarmRequestEventArgs( state.Mobile ) ); + } + + public static void StatLockChange( NetState state, PacketReader pvSrc ) + { + int stat = pvSrc.ReadByte(); + int lockValue = pvSrc.ReadByte(); + + if ( lockValue > 2 ) lockValue = 0; + + Mobile m = state.Mobile; + + if ( m != null ) + { + switch ( stat ) + { + case 0: m.StrLock = (StatLockType)lockValue; break; + case 1: m.DexLock = (StatLockType)lockValue; break; + case 2: m.IntLock = (StatLockType)lockValue; break; + } + } + } + + public static void ScreenSize( NetState state, PacketReader pvSrc ) + { + int width = pvSrc.ReadInt32(); + int unk = pvSrc.ReadInt32(); + } + + public static void ContextMenuResponse( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + if ( from != null ) + { + ContextMenu menu = from.ContextMenu; + + from.ContextMenu = null; + + if ( menu != null && from != null && from == menu.From ) + { + IEntity entity = World.FindEntity( pvSrc.ReadInt32() ); + + if ( entity != null && entity == menu.Target && from.CanSee( entity ) ) + { + Point3D p; + + if ( entity is Mobile ) + p = entity.Location; + else if ( entity is Item ) + p = ((Item)entity).GetWorldLocation(); + else + return; + + int index = pvSrc.ReadUInt16(); + + if ( index >= 0 && index < menu.Entries.Length ) + { + ContextMenuEntry e = menu.Entries[index]; + + int range = e.Range; + + if ( range == -1 ) + range = 18; + + if ( e.Enabled && from.InRange( p, range ) ) + e.OnClick(); + } + } + } + } + } + + public static void ContextMenuRequest( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + IEntity target = World.FindEntity( pvSrc.ReadInt32() ); + + if ( from != null && target != null && from.Map == target.Map && from.CanSee( target ) ) + { + if ( target is Mobile && !Utility.InUpdateRange( from.Location, target.Location ) ) + return; + else if ( target is Item && !Utility.InUpdateRange( from.Location, ((Item)target).GetWorldLocation() ) ) + return; + + if ( !from.CheckContextMenuDisplay( target ) ) + return; + + ContextMenu c = new ContextMenu( from, target ); + + if ( c.Entries.Length > 0 ) + { + if ( target is Item ) + { + object root = ((Item)target).RootParent; + + if ( root is Mobile && root != from && ((Mobile)root).AccessLevel >= from.AccessLevel ) + { + for ( int i = 0; i < c.Entries.Length; ++i ) + { + if ( !c.Entries[i].NonLocalUse ) + c.Entries[i].Enabled = false; + } + } + } + + from.ContextMenu = c; + } + } + } + + public static void CloseStatus( NetState state, PacketReader pvSrc ) + { + Serial serial = pvSrc.ReadInt32(); + } + + public static void Language( NetState state, PacketReader pvSrc ) + { + string lang = pvSrc.ReadString( 4 ); + + if ( state.Mobile != null ) + state.Mobile.Language = lang; + } + + public static void AssistVersion( NetState state, PacketReader pvSrc ) + { + int unk = pvSrc.ReadInt32(); + string av = pvSrc.ReadString(); + } + + public static void ClientVersion( NetState state, PacketReader pvSrc ) + { + CV version = state.Version = new CV( pvSrc.ReadString() ); + + string kickMessage = null; + + if ( CV.Required != null && version < CV.Required ) + { + kickMessage = String.Format( "This server requires your client version be at least {0}.", CV.Required ); + } + else if ( !CV.AllowGod || !CV.AllowRegular || !CV.AllowUOTD ) + { + if ( !CV.AllowGod && version.Type == ClientType.God ) + kickMessage = "This server does not allow god clients to connect."; + else if ( !CV.AllowRegular && version.Type == ClientType.Regular ) + kickMessage = "This server does not allow regular clients to connect."; + else if ( !CV.AllowUOTD && version.Type == ClientType.UOTD ) + kickMessage = "This server does not allow UO:TD clients to connect."; + + if ( !CV.AllowGod && !CV.AllowRegular && !CV.AllowUOTD ) + { + kickMessage = "This server does not allow any clients to connect."; + } + else if ( CV.AllowGod && !CV.AllowRegular && !CV.AllowUOTD && version.Type != ClientType.God ) + { + kickMessage = "This server requires you to use the god client."; + } + else if ( kickMessage != null ) + { + if ( CV.AllowRegular && CV.AllowUOTD ) + kickMessage += " You can use regular or UO:TD clients."; + else if ( CV.AllowRegular ) + kickMessage += " You can use regular clients."; + else if ( CV.AllowUOTD ) + kickMessage += " You can use UO:TD clients."; + } + } + + if ( kickMessage != null ) + { + state.Mobile.SendMessage( 0x22, kickMessage ); + state.Mobile.SendMessage( 0x22, "You will be disconnected in {0} seconds.", CV.KickDelay.TotalSeconds ); + + new KickTimer( state, CV.KickDelay ).Start(); + } + } + + private class KickTimer : Timer + { + private NetState m_State; + + public KickTimer( NetState state, TimeSpan delay ) : base( delay ) + { + m_State = state; + } + + protected override void OnTick() + { + if ( m_State.Socket != null ) + { + Console.WriteLine( "Client: {0}: Disconnecting, bad version", m_State ); + m_State.Dispose(); + } + } + } + + public static void MobileQuery( NetState state, PacketReader pvSrc ) + { + Mobile from = state.Mobile; + + pvSrc.ReadInt32(); // 0xEDEDEDED + int type = pvSrc.ReadByte(); + Mobile m = World.FindMobile( pvSrc.ReadInt32() ); + + if ( m != null ) + { + switch ( type ) + { + case 0x00: // Unknown, sent by godclient + { + if ( VerifyGC( state ) ) + Console.WriteLine( "God Client: {0}: Query 0x{1:X2} on {2} '{3}'", state, type, m.Serial, m.Name ); + + break; + } + case 0x04: // Stats + { + m.OnStatsQuery( from ); + break; + } + case 0x05: + { + m.OnSkillsQuery( from ); + break; + } + default: + { + pvSrc.Trace( state ); + break; + } + } + } + } + + public static void PlayCharacter( NetState state, PacketReader pvSrc ) + { + pvSrc.ReadInt32(); // 0xEDEDEDED + + string name = pvSrc.ReadString( 30 ); + + pvSrc.Seek( 2, SeekOrigin.Current ); + int flags = pvSrc.ReadInt32(); + pvSrc.Seek( 24, SeekOrigin.Current ); + + int charSlot = pvSrc.ReadInt32(); + int clientIP = pvSrc.ReadInt32(); + + IAccount a = state.Account; + + if ( a == null || charSlot < 0 || charSlot >= a.Length ) + { + state.Dispose(); + } + else + { + Mobile m = a[charSlot]; + + // Check if anyone is using this account + for ( int i = 0; i < a.Length; ++i ) + { + Mobile check = a[i]; + + if ( check != null && check.Map != Map.Internal && check != m ) + { + Console.WriteLine( "Login: {0}: Account in use", state ); + state.Send( new PopupMessage( PMMessage.CharInWorld ) ); + return; + } + } + + if ( m == null ) + { + state.Dispose(); + } + else + { + if ( m.NetState != null ) + m.NetState.Dispose(); + + NetState.ProcessDisposedQueue(); + + state.BlockAllPackets = true; + + state.Flags = flags; + + state.Mobile = m; + m.NetState = state; + + state.BlockAllPackets = false; + DoLogin( state, m ); + } + } + } + + public static void DoLogin( NetState state, Mobile m ) + { + state.Send( new LoginConfirm( m ) ); + + if ( m.Map != null ) + state.Send( new MapChange( m ) ); + + state.Send( new MapPatches() ); + + state.Send( SeasonChange.Instantiate( m.GetSeason(), true ) ); + + state.Send( SupportedFeatures.Instantiate( state.Account ) ); + + state.Sequence = 0; + state.Send( new MobileUpdate( m ) ); + state.Send( new MobileUpdate( m ) ); + + m.CheckLightLevels( true ); + + state.Send( new MobileUpdate( m ) ); + + state.Send( new MobileIncoming( m, m ) ); + //state.Send( new MobileAttributes( m ) ); + state.Send( new MobileStatus( m, m ) ); + state.Send( Server.Network.SetWarMode.Instantiate( m.Warmode ) ); + + m.SendEverything(); + + state.Send( SupportedFeatures.Instantiate( state.Account ) ); + state.Send( new MobileUpdate( m ) ); + //state.Send( new MobileAttributes( m ) ); + state.Send( new MobileStatus( m, m ) ); + state.Send( Server.Network.SetWarMode.Instantiate( m.Warmode ) ); + state.Send( new MobileIncoming( m, m ) ); + + state.Send( LoginComplete.Instance ); + state.Send( new CurrentTime() ); + state.Send( SeasonChange.Instantiate( m.GetSeason(), true ) ); + state.Send( new MapChange( m ) ); + + EventSink.InvokeLogin( new LoginEventArgs( m ) ); + + m.ClearFastwalkStack(); + } + + public static void CreateCharacter( NetState state, PacketReader pvSrc ) + { + int unk1 = pvSrc.ReadInt32(); + int unk2 = pvSrc.ReadInt32(); + int unk3 = pvSrc.ReadByte(); + string name = pvSrc.ReadString( 30 ); + + pvSrc.Seek( 2, SeekOrigin.Current ); + int flags = pvSrc.ReadInt32(); + pvSrc.Seek( 8, SeekOrigin.Current ); + int prof = pvSrc.ReadByte(); + pvSrc.Seek( 15, SeekOrigin.Current ); + + //bool female = pvSrc.ReadBoolean(); + + int genderRace = pvSrc.ReadByte(); + + int str = pvSrc.ReadByte(); + int dex = pvSrc.ReadByte(); + int intl= pvSrc.ReadByte(); + int is1 = pvSrc.ReadByte(); + int vs1 = pvSrc.ReadByte(); + int is2 = pvSrc.ReadByte(); + int vs2 = pvSrc.ReadByte(); + int is3 = pvSrc.ReadByte(); + int vs3 = pvSrc.ReadByte(); + int hue = pvSrc.ReadUInt16(); + int hairVal = pvSrc.ReadInt16(); + int hairHue = pvSrc.ReadInt16(); + int hairValf= pvSrc.ReadInt16(); + int hairHuef= pvSrc.ReadInt16(); + pvSrc.ReadByte(); + int cityIndex = pvSrc.ReadByte(); + int charSlot = pvSrc.ReadInt32(); + int clientIP = pvSrc.ReadInt32(); + int shirtHue = pvSrc.ReadInt16(); + int pantsHue = pvSrc.ReadInt16(); + + bool female = ((genderRace % 2) != 0); + + Race race = Race.Races[(byte)(genderRace / 2)]; + + if( race == null ) + race = Race.DefaultRace; + + CityInfo[] info = state.CityInfo; + IAccount a = state.Account; + + if ( info == null || a == null || cityIndex < 0 || cityIndex >= info.Length ) + { + state.Dispose(); + } + else + { + // Check if anyone is using this account + for ( int i = 0; i < a.Length; ++i ) + { + Mobile check = a[i]; + + if ( check != null && check.Map != Map.Internal ) + { + Console.WriteLine( "Login: {0}: Account in use", state ); + state.Send( new PopupMessage( PMMessage.CharInWorld ) ); + return; + } + } + + state.Flags = flags; + + CharacterCreatedEventArgs args = new CharacterCreatedEventArgs( + state, a, + name, female, hue, + str, dex, intl, + info[cityIndex], + new SkillNameValue[3] + { + new SkillNameValue( (SkillName)is1, vs1 ), + new SkillNameValue( (SkillName)is2, vs2 ), + new SkillNameValue( (SkillName)is3, vs3 ), + }, + shirtHue, pantsHue, + hairVal, hairHue, + hairValf, hairHuef, + prof, + race + ); + + state.BlockAllPackets = true; + + EventSink.InvokeCharacterCreated( args ); + + Mobile m = args.Mobile; + + if ( m != null ) + { + state.Mobile = m; + m.NetState = state; + + state.BlockAllPackets = false; + DoLogin( state, m ); + } + else + { + state.BlockAllPackets = false; + state.Dispose(); + } + } + } + + private const int AuthIDWindowSize = 128; + private static int[] m_AuthIDWindow = new int[AuthIDWindowSize]; + private static DateTime[] m_AuthIDWindowAge = new DateTime[AuthIDWindowSize]; + + private static bool m_ClientVerification = true; + + public static bool ClientVerification + { + get{ return m_ClientVerification; } + set{ m_ClientVerification = value; } + } + + private static int GenerateAuthID() + { + int authID = Utility.Random( 1, int.MaxValue - 1 ); + + if ( Utility.RandomBool() ) + authID |= 1<<31; + + bool wasSet = false; + DateTime oldest = DateTime.MaxValue; + int oldestIndex = 0; + + for ( int i = 0; i < m_AuthIDWindow.Length; ++i ) + { + if ( m_AuthIDWindow[i] == 0 ) + { + m_AuthIDWindow[i] = authID; + m_AuthIDWindowAge[i] = DateTime.Now; + wasSet = true; + break; + } + else if ( m_AuthIDWindowAge[i] < oldest ) + { + oldest = m_AuthIDWindowAge[i]; + oldestIndex = i; + } + } + + if ( !wasSet ) + { + m_AuthIDWindow[oldestIndex] = authID; + m_AuthIDWindowAge[oldestIndex] = DateTime.Now; + } + + return authID; + } + + private static bool IsValidAuthID( int authID ) + { + for ( int i = 0; i < m_AuthIDWindow.Length; ++i ) + { + if ( m_AuthIDWindow[i] == authID ) + { + m_AuthIDWindow[i] = 0; + return true; + } + } + + return !m_ClientVerification; + } + + public static void GameLogin( NetState state, PacketReader pvSrc ) + { + if ( state.SentFirstPacket ) + { + state.Dispose(); + return; + } + + state.SentFirstPacket = true; + + int authID = pvSrc.ReadInt32(); + + if ( !IsValidAuthID( authID ) ) + { + Console.WriteLine( "Login: {0}: Invalid client detected, disconnecting", state ); + state.Dispose(); + return; + } + else if ( state.m_AuthID != 0 && authID != state.m_AuthID ) + { + Console.WriteLine( "Login: {0}: Invalid client detected, disconnecting", state ); + state.Dispose(); + return; + } + else if ( state.m_AuthID == 0 && authID != state.m_Seed ) + { + Console.WriteLine( "Login: {0}: Invalid client detected, disconnecting", state ); + state.Dispose(); + return; + } + + string username = pvSrc.ReadString( 30 ); + string password = pvSrc.ReadString( 30 ); + + GameLoginEventArgs e = new GameLoginEventArgs( state, username, password ); + + EventSink.InvokeGameLogin( e ); + + if ( e.Accepted ) + { + state.CityInfo = e.CityInfo; + state.CompressionEnabled = true; + + if ( Core.AOS ) + state.Send( SupportedFeatures.Instantiate( state.Account ) ); + + state.Send( new CharacterList( state.Account, state.CityInfo ) ); + } + else + { + state.Dispose(); + } + } + + public static void PlayServer( NetState state, PacketReader pvSrc ) + { + int index = pvSrc.ReadInt16(); + ServerInfo[] info = state.ServerInfo; + IAccount a = state.Account; + + if ( info == null || a == null || index < 0 || index >= info.Length ) + { + state.Dispose(); + } + else + { + ServerInfo si = info[index]; + + state.m_AuthID = PlayServerAck.m_AuthID = GenerateAuthID(); + + state.SentFirstPacket = false; + state.Send( new PlayServerAck( si ) ); + } + } + + public static void AccountLogin( NetState state, PacketReader pvSrc ) + { + if ( state.SentFirstPacket ) + { + state.Dispose(); + return; + } + + state.SentFirstPacket = true; + + string username = pvSrc.ReadString( 30 ); + string password = pvSrc.ReadString( 30 ); + + AccountLoginEventArgs e = new AccountLoginEventArgs( state, username, password ); + + EventSink.InvokeAccountLogin( e ); + + if ( e.Accepted ) + AccountLogin_ReplyAck( state ); + else + AccountLogin_ReplyRej( state, e.RejectReason ); + } + + public static void AccountLogin_ReplyAck( NetState state ) + { + ServerListEventArgs e = new ServerListEventArgs( state, state.Account ); + + EventSink.InvokeServerList( e ); + + if ( e.Rejected ) + { + state.Account = null; + state.Send( new AccountLoginRej( ALRReason.BadComm ) ); + state.Dispose(); + } + else + { + ServerInfo[] info = e.Servers.ToArray(); + + state.ServerInfo = info; + + state.Send( new AccountLoginAck( info ) ); + } + } + + public static void AccountLogin_ReplyRej( NetState state, ALRReason reason ) + { + state.Send( new AccountLoginRej( reason ) ); + state.Dispose(); + } + } +} \ No newline at end of file diff --git a/Server/Network/PacketProfile.cs b/Server/Network/PacketProfile.cs new file mode 100644 index 000000000..e94661257 --- /dev/null +++ b/Server/Network/PacketProfile.cs @@ -0,0 +1,159 @@ +/*************************************************************************** + * PacketProfile.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: PacketProfile.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Network +{ + public class PacketProfile + { + private int m_Constructed; + private int m_Count; + private int m_TotalByteLength; + private TimeSpan m_TotalProcTime; + private TimeSpan m_PeakProcTime; + private bool m_Outgoing; + + [CommandProperty( AccessLevel.Administrator )] + public bool Outgoing + { + get{ return m_Outgoing; } + } + + [CommandProperty( AccessLevel.Administrator )] + public int Constructed + { + get{ return m_Constructed; } + } + + [CommandProperty( AccessLevel.Administrator )] + public int TotalByteLength + { + get{ return m_TotalByteLength; } + } + + [CommandProperty( AccessLevel.Administrator )] + public TimeSpan TotalProcTime + { + get{ return m_TotalProcTime; } + } + + [CommandProperty( AccessLevel.Administrator )] + public TimeSpan PeakProcTime + { + get{ return m_PeakProcTime; } + } + + [CommandProperty( AccessLevel.Administrator )] + public int Count + { + get{ return m_Count; } + } + + [CommandProperty( AccessLevel.Administrator )] + public double AverageByteLength + { + get + { + if ( m_Count == 0 ) + return 0; + + return Math.Round( (double) m_TotalByteLength / m_Count, 2 ); + } + } + + [CommandProperty( AccessLevel.Administrator )] + public TimeSpan AverageProcTime + { + get + { + if ( m_Count == 0 ) + return TimeSpan.Zero; + + return TimeSpan.FromTicks( m_TotalProcTime.Ticks / m_Count ); + } + } + + public void Record( int byteLength, TimeSpan processTime ) + { + ++m_Count; + m_TotalByteLength += byteLength; + m_TotalProcTime += processTime; + + if ( processTime > m_PeakProcTime ) + m_PeakProcTime = processTime; + } + + public void RegConstruct() + { + ++m_Constructed; + } + + public PacketProfile( bool outgoing ) + { + m_Outgoing = outgoing; + } + + private static PacketProfile[] m_OutgoingProfiles; + private static PacketProfile[] m_IncomingProfiles; + + public static PacketProfile GetOutgoingProfile( int packetID ) + { + if ( !Core.Profiling ) + return null; + + PacketProfile prof = m_OutgoingProfiles[packetID]; + + if ( prof == null ) + m_OutgoingProfiles[packetID] = prof = new PacketProfile( true ); + + return prof; + } + + public static PacketProfile GetIncomingProfile( int packetID ) + { + if ( !Core.Profiling ) + return null; + + PacketProfile prof = m_IncomingProfiles[packetID]; + + if ( prof == null ) + m_IncomingProfiles[packetID] = prof = new PacketProfile( false ); + + return prof; + } + + public static PacketProfile[] OutgoingProfiles + { + get{ return m_OutgoingProfiles; } + } + + public static PacketProfile[] IncomingProfiles + { + get{ return m_IncomingProfiles; } + } + + static PacketProfile() + { + m_OutgoingProfiles = new PacketProfile[0x100]; + m_IncomingProfiles = new PacketProfile[0x100]; + } + } +} \ No newline at end of file diff --git a/Server/Network/PacketReader.cs b/Server/Network/PacketReader.cs new file mode 100644 index 000000000..d5d596060 --- /dev/null +++ b/Server/Network/PacketReader.cs @@ -0,0 +1,457 @@ +/*************************************************************************** + * PacketReader.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: PacketReader.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Text; +using System.IO; + +namespace Server.Network +{ + public class PacketReader + { + private byte[] m_Data; + private int m_Size; + private int m_Index; + + public PacketReader( byte[] data, int size, bool fixedSize ) + { + m_Data = data; + m_Size = size; + m_Index = fixedSize ? 1 : 3; + } + + public byte[] Buffer + { + get + { + return m_Data; + } + } + + public int Size + { + get + { + return m_Size; + } + } + + public void Trace( NetState state ) + { + try + { + using ( StreamWriter sw = new StreamWriter( "Packets.log", true ) ) + { + byte[] buffer = m_Data; + + if ( buffer.Length > 0 ) + sw.WriteLine( "Client: {0}: Unhandled packet 0x{1:X2}", state, buffer[0] ); + + using ( MemoryStream ms = new MemoryStream( buffer ) ) + Utility.FormatBuffer( sw, ms, buffer.Length ); + + sw.WriteLine(); + sw.WriteLine(); + } + } + catch + { + } + } + + public int Seek( int offset, SeekOrigin origin ) + { + switch ( origin ) + { + case SeekOrigin.Begin: m_Index = offset; break; + case SeekOrigin.Current: m_Index += offset; break; + case SeekOrigin.End: m_Index = m_Size - offset; break; + } + + return m_Index; + } + + public int ReadInt32() + { + if ( (m_Index + 4) > m_Size ) + return 0; + + return (m_Data[m_Index++] << 24) + | (m_Data[m_Index++] << 16) + | (m_Data[m_Index++] << 8) + | m_Data[m_Index++]; + } + + public short ReadInt16() + { + if ( (m_Index + 2) > m_Size ) + return 0; + + return (short)((m_Data[m_Index++] << 8) | m_Data[m_Index++]); + } + + public byte ReadByte() + { + if ( (m_Index + 1) > m_Size ) + return 0; + + return m_Data[m_Index++]; + } + + public uint ReadUInt32() + { + if ( (m_Index + 4) > m_Size ) + return 0; + + return (uint)((m_Data[m_Index++] << 24) | (m_Data[m_Index++] << 16) | (m_Data[m_Index++] << 8) | m_Data[m_Index++]); + } + + public ushort ReadUInt16() + { + if ( (m_Index + 2) > m_Size ) + return 0; + + return (ushort)((m_Data[m_Index++] << 8) | m_Data[m_Index++]); + } + + public sbyte ReadSByte() + { + if ( (m_Index + 1) > m_Size ) + return 0; + + return (sbyte)m_Data[m_Index++]; + } + + public bool ReadBoolean() + { + if ( (m_Index + 1) > m_Size ) + return false; + + return ( m_Data[m_Index++] != 0 ); + } + + public string ReadUnicodeStringLE() + { + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < m_Size && (c = (m_Data[m_Index++] | (m_Data[m_Index++] << 8))) != 0 ) + sb.Append( (char)c ); + + return sb.ToString(); + } + + public string ReadUnicodeStringLESafe( int fixedLength ) + { + int bound = m_Index + (fixedLength << 1); + int end = bound; + + if ( bound > m_Size ) + bound = m_Size; + + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < bound && (c = (m_Data[m_Index++] | (m_Data[m_Index++] << 8))) != 0 ) + { + if ( IsSafeChar( c ) ) + sb.Append( (char)c ); + } + + m_Index = end; + + return sb.ToString(); + } + + public string ReadUnicodeStringLESafe() + { + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < m_Size && (c = (m_Data[m_Index++] | (m_Data[m_Index++] << 8))) != 0 ) + { + if ( IsSafeChar( c ) ) + sb.Append( (char)c ); + } + + return sb.ToString(); + } + + public string ReadUnicodeStringSafe() + { + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < m_Size && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0 ) + { + if ( IsSafeChar( c ) ) + sb.Append( (char)c ); + } + + return sb.ToString(); + } + + public string ReadUnicodeString() + { + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < m_Size && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0 ) + sb.Append( (char)c ); + + return sb.ToString(); + } + + public bool IsSafeChar( int c ) + { + return ( c >= 0x20 && c < 0xFFFE ); + } + + public string ReadUTF8StringSafe( int fixedLength ) + { + if ( m_Index >= m_Size ) + { + m_Index += fixedLength; + return String.Empty; + } + + int bound = m_Index + fixedLength; + //int end = bound; + + if ( bound > m_Size ) + bound = m_Size; + + int count = 0; + int index = m_Index; + int start = m_Index; + + while ( index < bound && m_Data[index++] != 0 ) + ++count; + + index = 0; + + byte[] buffer = new byte[count]; + int value = 0; + + while ( m_Index < bound && (value = m_Data[m_Index++]) != 0 ) + buffer[index++] = (byte)value; + + string s = Utility.UTF8.GetString( buffer ); + + bool isSafe = true; + + for ( int i = 0; isSafe && i < s.Length; ++i ) + isSafe = IsSafeChar( (int) s[i] ); + + m_Index = start + fixedLength; + + if ( isSafe ) + return s; + + StringBuilder sb = new StringBuilder( s.Length ); + + for ( int i = 0; i < s.Length; ++i ) + if ( IsSafeChar( (int) s[i] ) ) + sb.Append( s[i] ); + + return sb.ToString(); + } + + public string ReadUTF8StringSafe() + { + if ( m_Index >= m_Size ) + return String.Empty; + + int count = 0; + int index = m_Index; + + while ( index < m_Size && m_Data[index++] != 0 ) + ++count; + + index = 0; + + byte[] buffer = new byte[count]; + int value = 0; + + while ( m_Index < m_Size && (value = m_Data[m_Index++]) != 0 ) + buffer[index++] = (byte)value; + + string s = Utility.UTF8.GetString( buffer ); + + bool isSafe = true; + + for ( int i = 0; isSafe && i < s.Length; ++i ) + isSafe = IsSafeChar( (int) s[i] ); + + if ( isSafe ) + return s; + + StringBuilder sb = new StringBuilder( s.Length ); + + for ( int i = 0; i < s.Length; ++i ) + { + if ( IsSafeChar( (int) s[i] ) ) + sb.Append( s[i] ); + } + + return sb.ToString(); + } + + public string ReadUTF8String() + { + if ( m_Index >= m_Size ) + return String.Empty; + + int count = 0; + int index = m_Index; + + while ( index < m_Size && m_Data[index++] != 0 ) + ++count; + + index = 0; + + byte[] buffer = new byte[count]; + int value = 0; + + while ( m_Index < m_Size && (value = m_Data[m_Index++]) != 0 ) + buffer[index++] = (byte)value; + + return Utility.UTF8.GetString( buffer ); + } + + public string ReadString() + { + StringBuilder sb = new StringBuilder(); + + int c; + + while ( m_Index < m_Size && (c = m_Data[m_Index++]) != 0 ) + sb.Append( (char)c ); + + return sb.ToString(); + } + + public string ReadStringSafe() + { + StringBuilder sb = new StringBuilder(); + + int c; + + while ( m_Index < m_Size && (c = m_Data[m_Index++]) != 0 ) + { + if ( IsSafeChar( c ) ) + sb.Append( (char)c ); + } + + return sb.ToString(); + } + + public string ReadUnicodeStringSafe( int fixedLength ) + { + int bound = m_Index + (fixedLength << 1); + int end = bound; + + if ( bound > m_Size ) + bound = m_Size; + + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < bound && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0 ) + { + if ( IsSafeChar( c ) ) + sb.Append( (char)c ); + } + + m_Index = end; + + return sb.ToString(); + } + + public string ReadUnicodeString( int fixedLength ) + { + int bound = m_Index + (fixedLength << 1); + int end = bound; + + if ( bound > m_Size ) + bound = m_Size; + + StringBuilder sb = new StringBuilder(); + + int c; + + while ( (m_Index + 1) < bound && (c = ((m_Data[m_Index++] << 8) | m_Data[m_Index++])) != 0 ) + sb.Append( (char)c ); + + m_Index = end; + + return sb.ToString(); + } + + public string ReadStringSafe( int fixedLength ) + { + int bound = m_Index + fixedLength; + int end = bound; + + if ( bound > m_Size ) + bound = m_Size; + + StringBuilder sb = new StringBuilder(); + + int c; + + while ( m_Index < bound && (c = m_Data[m_Index++]) != 0 ) + { + if ( IsSafeChar( c ) ) + sb.Append( (char)c ); + } + + m_Index = end; + + return sb.ToString(); + } + + public string ReadString( int fixedLength ) + { + int bound = m_Index + fixedLength; + int end = bound; + + if ( bound > m_Size ) + bound = m_Size; + + StringBuilder sb = new StringBuilder(); + + int c; + + while ( m_Index < bound && (c = m_Data[m_Index++]) != 0 ) + sb.Append( (char)c ); + + m_Index = end; + + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/Server/Network/PacketWriter.cs b/Server/Network/PacketWriter.cs new file mode 100644 index 000000000..10dcc1f38 --- /dev/null +++ b/Server/Network/PacketWriter.cs @@ -0,0 +1,409 @@ +/*************************************************************************** + * PacketWriter.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: PacketWriter.cs 43 2006-01-20 05:39:57Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Text; +using System.Collections; +using System.Collections.Generic; + +namespace Server.Network +{ + /// + /// Provides functionality for writing primitive binary data. + /// + public class PacketWriter + { + private static Stack m_Pool = new Stack(); + + public static PacketWriter CreateInstance() + { + return CreateInstance( 32 ); + } + + public static PacketWriter CreateInstance( int capacity ) + { + PacketWriter pw = null; + + lock ( m_Pool ) + { + if ( m_Pool.Count > 0 ) + { + pw = m_Pool.Pop(); + + if ( pw != null ) + { + pw.m_Capacity = capacity; + pw.m_Stream.SetLength( 0 ); + } + } + } + + if ( pw == null ) + pw = new PacketWriter( capacity ); + + return pw; + } + + public static void ReleaseInstance( PacketWriter pw ) + { + lock ( m_Pool ) + { + if ( !m_Pool.Contains( pw ) ) + { + m_Pool.Push( pw ); + } + else + { + try + { + using ( StreamWriter op = new StreamWriter( "neterr.log" ) ) + { + op.WriteLine( "{0}\tInstance pool contains writer", DateTime.Now ); + } + } + catch + { + Console.WriteLine( "net error" ); + } + } + } + } + + /// + /// Internal stream which holds the entire packet. + /// + private MemoryStream m_Stream; + + private int m_Capacity; + + /// + /// Internal format buffer. + /// + private static byte[] m_Buffer = new byte[4]; + + /// + /// Instantiates a new PacketWriter instance with the default capacity of 4 bytes. + /// + public PacketWriter() : this( 32 ) + { + } + + /// + /// Instantiates a new PacketWriter instance with a given capacity. + /// + /// Initial capacity for the internal stream. + public PacketWriter( int capacity ) + { + m_Stream = new MemoryStream( capacity ); + m_Capacity = capacity; + } + + /// + /// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1. + /// + public void Write( bool value ) + { + m_Stream.WriteByte( (byte)(value ? 1 : 0) ); + } + + /// + /// Writes a 1-byte unsigned integer value to the underlying stream. + /// + public void Write( byte value ) + { + m_Stream.WriteByte( value ); + } + + /// + /// Writes a 1-byte signed integer value to the underlying stream. + /// + public void Write( sbyte value ) + { + m_Stream.WriteByte( (byte) value ); + } + + /// + /// Writes a 2-byte signed integer value to the underlying stream. + /// + public void Write( short value ) + { + m_Buffer[0] = (byte)(value >> 8); + m_Buffer[1] = (byte) value; + + m_Stream.Write( m_Buffer, 0, 2 ); + } + + /// + /// Writes a 2-byte unsigned integer value to the underlying stream. + /// + public void Write( ushort value ) + { + m_Buffer[0] = (byte)(value >> 8); + m_Buffer[1] = (byte) value; + + m_Stream.Write( m_Buffer, 0, 2 ); + } + + /// + /// Writes a 4-byte signed integer value to the underlying stream. + /// + public void Write( int value ) + { + m_Buffer[0] = (byte)(value >> 24); + m_Buffer[1] = (byte)(value >> 16); + m_Buffer[2] = (byte)(value >> 8); + m_Buffer[3] = (byte) value; + + m_Stream.Write( m_Buffer, 0, 4 ); + } + + /// + /// Writes a 4-byte unsigned integer value to the underlying stream. + /// + public void Write( uint value ) + { + m_Buffer[0] = (byte)(value >> 24); + m_Buffer[1] = (byte)(value >> 16); + m_Buffer[2] = (byte)(value >> 8); + m_Buffer[3] = (byte) value; + + m_Stream.Write( m_Buffer, 0, 4 ); + } + + /// + /// Writes a sequence of bytes to the underlying stream + /// + public void Write( byte[] buffer, int offset, int size ) + { + m_Stream.Write( buffer, offset, size ); + } + + /// + /// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters. + /// + public void WriteAsciiFixed( string value, int size ) + { + if ( value == null ) + { + Console.WriteLine( "Network: Attempted to WriteAsciiFixed() with null value" ); + value = String.Empty; + } + + byte[] buffer = Encoding.ASCII.GetBytes( value ); + + if ( buffer.Length >= size ) + { + m_Stream.Write( buffer, 0, size ); + } + else + { + m_Stream.Write( buffer, 0, buffer.Length ); + Fill( size - buffer.Length ); + } + } + + /// + /// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character. + /// + public void WriteAsciiNull( string value ) + { + if ( value == null ) + { + Console.WriteLine( "Network: Attempted to WriteAsciiNull() with null value" ); + value = String.Empty; + } + + byte[] buffer = Encoding.ASCII.GetBytes( value ); + + m_Stream.Write( buffer, 0, buffer.Length ); + m_Stream.WriteByte( 0 ); + } + + /// + /// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null character. + /// + public void WriteLittleUniNull( string value ) + { + if ( value == null ) + { + Console.WriteLine( "Network: Attempted to WriteLittleUniNull() with null value" ); + value = String.Empty; + } + + byte[] buffer = Encoding.Unicode.GetBytes( value ); + + m_Stream.Write( buffer, 0, buffer.Length ); + + m_Buffer[0] = 0; + m_Buffer[1] = 0; + m_Stream.Write( m_Buffer, 0, 2 ); + } + + /// + /// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters. + /// + public void WriteLittleUniFixed( string value, int size ) + { + if ( value == null ) + { + Console.WriteLine( "Network: Attempted to WriteLittleUniFixed() with null value" ); + value = String.Empty; + } + + size *= 2; + + byte[] buffer = Encoding.Unicode.GetBytes( value ); + + if ( buffer.Length >= size ) + { + m_Stream.Write( buffer, 0, size ); + } + else + { + m_Stream.Write( buffer, 0, buffer.Length ); + Fill( size - buffer.Length ); + } + } + + /// + /// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null character. + /// + public void WriteBigUniNull( string value ) + { + if ( value == null ) + { + Console.WriteLine( "Network: Attempted to WriteBigUniNull() with null value" ); + value = String.Empty; + } + + byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value ); + + m_Stream.Write( buffer, 0, buffer.Length ); + + m_Buffer[0] = 0; + m_Buffer[1] = 0; + m_Stream.Write( m_Buffer, 0, 2 ); + } + + /// + /// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content is either truncated or padded with null characters. + /// + public void WriteBigUniFixed( string value, int size ) + { + if ( value == null ) + { + Console.WriteLine( "Network: Attempted to WriteBigUniFixed() with null value" ); + value = String.Empty; + } + + size *= 2; + + byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value ); + + if ( buffer.Length >= size ) + { + m_Stream.Write( buffer, 0, size ); + } + else + { + m_Stream.Write( buffer, 0, buffer.Length ); + Fill( size - buffer.Length ); + } + } + + /// + /// Fills the stream from the current position up to (capacity) with 0x00's + /// + public void Fill() + { + Fill( (int) (m_Capacity - m_Stream.Length) ); + } + + /// + /// Writes a number of 0x00 byte values to the underlying stream. + /// + public void Fill( int length ) + { + if ( m_Stream.Position == m_Stream.Length ) + { + m_Stream.SetLength( m_Stream.Length + length ); + m_Stream.Seek( 0, SeekOrigin.End ); + } + else + { + m_Stream.Write( new byte[length], 0, length ); + } + } + + /// + /// Gets the total stream length. + /// + public long Length + { + get + { + return m_Stream.Length; + } + } + + /// + /// Gets or sets the current stream position. + /// + public long Position + { + get + { + return m_Stream.Position; + } + set + { + m_Stream.Position = value; + } + } + + /// + /// The internal stream used by this PacketWriter instance. + /// + public MemoryStream UnderlyingStream + { + get + { + return m_Stream; + } + } + + /// + /// Offsets the current position from an origin. + /// + public long Seek( long offset, SeekOrigin origin ) + { + return m_Stream.Seek( offset, origin ); + } + + /// + /// Gets the entire stream content as a byte array. + /// + public byte[] ToArray() + { + return m_Stream.ToArray(); + } + } +} \ No newline at end of file diff --git a/Server/Network/Packets.cs b/Server/Network/Packets.cs new file mode 100644 index 000000000..475012133 --- /dev/null +++ b/Server/Network/Packets.cs @@ -0,0 +1,3678 @@ +/*************************************************************************** + * Packets.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Packets.cs 142 2006-06-08 17:27:25Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using Server.Accounting; +using Server.Targeting; +using Server.Items; +using Server.Mobiles; +using Server.Gumps; +using Server.Menus; +using Server.Menus.ItemLists; +using Server.Menus.Questions; +using Server.Prompts; +using Server.HuePickers; +using Server.ContextMenus; + +namespace Server.Network +{ + public enum PMMessage : byte + { + CharNoExist = 1, + CharExists = 2, + CharInWorld = 5, + LoginSyncError = 6, + IdleWarning = 7 + } + + public enum LRReason : byte + { + CannotLift = 0, + OutOfRange = 1, + OutOfSight = 2, + TryToSteal = 3, + AreHolding = 4, + Inspecific = 5 + } + + /*public enum CMEFlags + { + None = 0x00, + Locked = 0x01, + Arrow = 0x02, + x0004 = 0x04, + Color = 0x20, + x0040 = 0x40, + x0080 = 0x80 + }*/ + + public sealed class DamagePacketOld : Packet + { + public DamagePacketOld( Mobile m, int amount ) : base( 0xBF ) + { + EnsureCapacity( 11 ); + + m_Stream.Write( (short) 0x22 ); + m_Stream.Write( (byte) 1 ); + m_Stream.Write( (int) m.Serial ); + + if ( amount > 255 ) + amount = 255; + else if ( amount < 0 ) + amount = 0; + + m_Stream.Write( (byte)amount ); + } + } + + public sealed class DamagePacket : Packet + { + public static readonly ClientVersion Version = new ClientVersion( "4.0.7a" ); + + public DamagePacket( Mobile m, int amount ) : base( 0x0B, 7 ) + { + m_Stream.Write( (int) m.Serial ); + + if ( amount > 0xFFFF ) + amount = 0xFFFF; + else if ( amount < 0 ) + amount = 0; + + m_Stream.Write( (ushort) amount ); + } + + /*public DamagePacket( Mobile m, int amount ) : base( 0xBF ) + { + EnsureCapacity( 11 ); + + m_Stream.Write( (short) 0x22 ); + m_Stream.Write( (byte) 1 ); + m_Stream.Write( (int) m.Serial ); + + if ( amount > 255 ) + amount = 255; + else if ( amount < 0 ) + amount = 0; + + m_Stream.Write( (byte)amount ); + }*/ + } + + public sealed class CancelArrow : Packet + { + public CancelArrow() : base( 0xBA, 6 ) + { + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (short) -1 ); + m_Stream.Write( (short) -1 ); + } + } + + public sealed class SetArrow : Packet + { + public SetArrow( int x, int y ) : base( 0xBA, 6 ) + { + m_Stream.Write( (byte) 1 ); + m_Stream.Write( (short) x ); + m_Stream.Write( (short) y ); + } + } + + public sealed class DisplaySecureTrade : Packet + { + public DisplaySecureTrade( Mobile them, Container first, Container second, string name ) : base( 0x6F ) + { + if ( name == null ) + name = ""; + + EnsureCapacity( 18 + name.Length ); + + m_Stream.Write( (byte) 0 ); // Display + m_Stream.Write( (int) them.Serial ); + m_Stream.Write( (int) first.Serial ); + m_Stream.Write( (int) second.Serial ); + m_Stream.Write( (bool) true ); + + m_Stream.WriteAsciiFixed( name, 30 ); + } + } + + public sealed class CloseSecureTrade : Packet + { + public CloseSecureTrade( Container cont ) : base( 0x6F ) + { + EnsureCapacity( 8 ); + + m_Stream.Write( (byte) 1 ); // Close + m_Stream.Write( (int) cont.Serial ); + } + } + + public sealed class UpdateSecureTrade : Packet + { + public UpdateSecureTrade( Container cont, bool first, bool second ) : base( 0x6F ) + { + EnsureCapacity( 8 ); + + m_Stream.Write( (byte) 2 ); // Update + m_Stream.Write( (int) cont.Serial ); + m_Stream.Write( (int) (first ? 1 : 0) ); + m_Stream.Write( (int) (second ? 1 : 0) ); + } + } + + public sealed class SecureTradeEquip : Packet + { + public SecureTradeEquip( Item item, Mobile m ) : base( 0x25, 20 ) + { + m_Stream.Write( (int) item.Serial ); + m_Stream.Write( (short) item.ItemID ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (short) item.Amount ); + m_Stream.Write( (short) item.X ); + m_Stream.Write( (short) item.Y ); + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) item.Hue ); + } + } + + public sealed class MapPatches : Packet + { + public MapPatches() : base( 0xBF ) + { + EnsureCapacity( 9 + (3 * 8) ); + + m_Stream.Write( (short) 0x0018 ); + + m_Stream.Write( (int) 4 ); + + m_Stream.Write( (int) Map.Felucca.Tiles.Patch.StaticBlocks ); + m_Stream.Write( (int) Map.Felucca.Tiles.Patch.LandBlocks ); + + m_Stream.Write( (int) Map.Trammel.Tiles.Patch.StaticBlocks ); + m_Stream.Write( (int) Map.Trammel.Tiles.Patch.LandBlocks ); + + m_Stream.Write( (int) Map.Ilshenar.Tiles.Patch.StaticBlocks ); + m_Stream.Write( (int) Map.Ilshenar.Tiles.Patch.LandBlocks ); + + m_Stream.Write( (int) Map.Malas.Tiles.Patch.StaticBlocks ); + m_Stream.Write( (int) Map.Malas.Tiles.Patch.LandBlocks ); + } + } + + public sealed class ObjectHelpResponse : Packet + { + public ObjectHelpResponse( IEntity e, string text ) : base( 0xB7 ) + { + this.EnsureCapacity( 9 + (text.Length * 2) ); + + m_Stream.Write( (int) e.Serial ); + m_Stream.WriteBigUniNull( text ); + } + } + + public sealed class VendorBuyContent : Packet + { + public VendorBuyContent( ArrayList list ) : base( 0x3c ) + { + this.EnsureCapacity( list.Count*19 + 5 ); + + m_Stream.Write( (short)list.Count ); + + //The client sorts these by their X/Y value. + //OSI sends these in wierd order. X/Y highest to lowest and serial loest to highest + //These are already sorted by serial (done by the vendor class) but we have to send them by x/y + //(the x74 packet is sent in 'correct' order.) + for ( int i = list.Count - 1; i >= 0; --i ) + { + BuyItemState bis = (BuyItemState)list[i]; + + m_Stream.Write( (int)bis.MySerial ); + m_Stream.Write( (ushort)(bis.ItemID & 0x3FFF) ); + m_Stream.Write( (byte)0 );//itemid offset + m_Stream.Write( (ushort)bis.Amount ); + m_Stream.Write( (short)(i+1) );//x + m_Stream.Write( (short)1 );//y + m_Stream.Write( (int)bis.ContainerSerial ); + m_Stream.Write( (ushort)bis.Hue ); + } + } + } + + public sealed class DisplayBuyList : Packet + { + public DisplayBuyList( Mobile vendor ) : base( 0x24, 7 ) + { + m_Stream.Write( (int)vendor.Serial ); + m_Stream.Write( (short)0x30 );//buy window id? + } + } + + public sealed class VendorBuyList : Packet + { + public VendorBuyList( Mobile vendor, ArrayList list ) : base( 0x74 ) + { + this.EnsureCapacity( 256 ); + + Container BuyPack = vendor.FindItemOnLayer( Layer.ShopBuy ) as Container; + m_Stream.Write( (int)(BuyPack == null ? Serial.MinusOne : BuyPack.Serial) ); + + m_Stream.Write( (byte)list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + { + BuyItemState bis = (BuyItemState)list[i]; + + m_Stream.Write( (int) bis.Price ); + + string desc = bis.Description; + + if ( desc == null ) + desc = ""; + + m_Stream.Write( (byte)(desc.Length + 1) ); + m_Stream.WriteAsciiNull( desc ); + } + } + } + + public sealed class VendorSellList : Packet + { + public VendorSellList( Mobile shopkeeper, Hashtable table ) : base( 0x9E ) + { + this.EnsureCapacity( 256 ); + + m_Stream.Write( (int) shopkeeper.Serial ); + + m_Stream.Write( (ushort) table.Count ); + + foreach ( SellItemState state in table.Values ) + { + m_Stream.Write( (int) state.Item.Serial ); + m_Stream.Write( (ushort) (state.Item.ItemID & 0x3FFF) ); + m_Stream.Write( (ushort) state.Item.Hue ); + m_Stream.Write( (ushort) state.Item.Amount ); + m_Stream.Write( (ushort) state.Price ); + + string name = state.Item.Name; + + if ( name == null || (name = name.Trim()).Length <= 0 ) + name = state.Name; + + if ( name == null ) + name = ""; + + m_Stream.Write( (ushort) (name.Length) ); + m_Stream.WriteAsciiFixed( name, (ushort) (name.Length) ); + } + } + } + + public sealed class EndVendorSell : Packet + { + public EndVendorSell( Mobile Vendor ) : base( 0x3B, 8 ) + { + m_Stream.Write( (ushort)8 );//length + m_Stream.Write( (int)Vendor.Serial ); + m_Stream.Write( (byte)0 ); + } + } + + public sealed class EndVendorBuy : Packet + { + public EndVendorBuy( Mobile Vendor ) : base( 0x3B, 8 ) + { + m_Stream.Write( (ushort)8 );//length + m_Stream.Write( (int)Vendor.Serial ); + m_Stream.Write( (byte)0 ); + } + } + + public sealed class DeathAnimation : Packet + { + public DeathAnimation( Mobile killed, Item corpse ) : base( 0xAF, 13 ) + { + m_Stream.Write( (int) killed.Serial ); + m_Stream.Write( (int) (corpse == null ? Serial.Zero : corpse.Serial) ); + m_Stream.Write( (int) 0 ) ; + } + } + + public sealed class StatLockInfo : Packet + { + public StatLockInfo( Mobile m ) : base( 0xBF ) + { + this.EnsureCapacity( 12 ); + + m_Stream.Write( (short) 0x19 ); + m_Stream.Write( (byte) 2 ); + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (byte) 0 ); + + int lockBits = 0; + + lockBits |= (int)m.StrLock << 4; + lockBits |= (int)m.DexLock << 2; + lockBits |= (int)m.IntLock; + + m_Stream.Write( (byte) lockBits ); + } + } + + public class EquipInfoAttribute + { + private int m_Number; + private int m_Charges; + + public int Number + { + get + { + return m_Number; + } + } + + public int Charges + { + get + { + return m_Charges; + } + } + + public EquipInfoAttribute( int number ) : this( number, -1 ) + { + } + + public EquipInfoAttribute( int number, int charges ) + { + m_Number = number; + m_Charges = charges; + } + } + + public class EquipmentInfo + { + private int m_Number; + private Mobile m_Crafter; + private bool m_Unidentified; + private EquipInfoAttribute[] m_Attributes; + + public int Number + { + get + { + return m_Number; + } + } + + public Mobile Crafter + { + get + { + return m_Crafter; + } + } + + public bool Unidentified + { + get + { + return m_Unidentified; + } + } + + public EquipInfoAttribute[] Attributes + { + get + { + return m_Attributes; + } + } + + public EquipmentInfo( int number, Mobile crafter, bool unidentified, EquipInfoAttribute[] attributes ) + { + m_Number = number; + m_Crafter = crafter; + m_Unidentified = unidentified; + m_Attributes = attributes; + } + } + + public sealed class DisplayEquipmentInfo : Packet + { + public DisplayEquipmentInfo( Item item, EquipmentInfo info ) : base( 0xBF ) + { + EquipInfoAttribute[] attrs = info.Attributes; + + this.EnsureCapacity( 17 + (info.Crafter == null ? 0 : 6 + info.Crafter.Name == null ? 0 : info.Crafter.Name.Length) + (info.Unidentified ? 4 : 0) + (attrs.Length * 6) ); + + m_Stream.Write( (short) 0x10 ); + m_Stream.Write( (int) item.Serial ); + + m_Stream.Write( (int) info.Number ); + + if ( info.Crafter != null ) + { + string name = info.Crafter.Name; + + if ( name == null ) name = ""; + + int length = (ushort)name.Length; + + m_Stream.Write( (int) -3 ); + m_Stream.Write( (ushort) length ); + m_Stream.WriteAsciiFixed( name, length ); + } + + if ( info.Unidentified ) + { + m_Stream.Write( (int) -4 ); + } + + for ( int i = 0; i < attrs.Length; ++i ) + { + m_Stream.Write( (int) attrs[i].Number ); + m_Stream.Write( (short) attrs[i].Charges ); + } + + m_Stream.Write( (int) -1 ); + } + } + + public sealed class ChangeUpdateRange : Packet + { + private static ChangeUpdateRange[] m_Cache = new ChangeUpdateRange[0x100]; + + public static ChangeUpdateRange Instantiate( int range ) + { + byte idx = (byte)range; + ChangeUpdateRange p = m_Cache[idx]; + + if ( p == null ) + { + m_Cache[idx] = p = new ChangeUpdateRange( range ); + p.SetStatic(); + } + + return p; + } + + public ChangeUpdateRange( int range ) : base( 0xC8, 2 ) + { + m_Stream.Write( (byte) range ); + } + } + + public sealed class ChangeCombatant : Packet + { + public ChangeCombatant( Mobile combatant ) : base( 0xAA, 5 ) + { + m_Stream.Write( combatant != null ? combatant.Serial : Serial.Zero ); + } + } + + public sealed class DisplayHuePicker : Packet + { + public DisplayHuePicker( HuePicker huePicker ) : base( 0x95, 9 ) + { + m_Stream.Write( (int) huePicker.Serial ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) huePicker.ItemID ); + } + } + + public sealed class TripTimeResponse : Packet + { + public TripTimeResponse( int unk ) : base( 0xC9, 6 ) + { + m_Stream.Write( (byte) unk ); + m_Stream.Write( (int) Environment.TickCount ); + } + } + + public sealed class UTripTimeResponse : Packet + { + public UTripTimeResponse( int unk ) : base( 0xCA, 6 ) + { + m_Stream.Write( (byte) unk ); + m_Stream.Write( (int) Environment.TickCount ); + } + } + + public sealed class UnicodePrompt : Packet + { + public UnicodePrompt( Prompt prompt ) : base( 0xC2 ) + { + this.EnsureCapacity( 21 ); + + m_Stream.Write( (int) prompt.Serial ); + m_Stream.Write( (int) prompt.Serial ); + m_Stream.Write( (int) 0 ); + m_Stream.Write( (int) 0 ); + m_Stream.Write( (short) 0 ); + } + } + + public sealed class ChangeCharacter : Packet + { + public ChangeCharacter( IAccount a ) : base( 0x81 ) + { + this.EnsureCapacity( 305 ); + + int count = 0; + + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] != null ) + ++count; + } + + m_Stream.Write( (byte) count ); + m_Stream.Write( (byte) 0 ); + + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] != null ) + { + string name = a[i].Name; + + if ( name == null ) + name = "-null-"; + else if ( (name = name.Trim()).Length == 0 ) + name = "-empty-"; + + m_Stream.WriteAsciiFixed( name, 30 ); + m_Stream.Fill( 30 ); // password + } + else + { + m_Stream.Fill( 60 ); + } + } + } + } + + public sealed class DeathStatus : Packet + { + public static readonly Packet Dead = Packet.SetStatic( new DeathStatus( true ) ); + public static readonly Packet Alive = Packet.SetStatic( new DeathStatus( false ) ); + + public static Packet Instantiate( bool dead ) + { + return ( dead ? Dead : Alive ); + } + + public DeathStatus( bool dead ) : base( 0x2C, 2 ) + { + m_Stream.Write( (byte) (dead ? 0 : 2) ); + } + } + + public sealed class SpeedBoost : Packet + { + public static readonly Packet Enabled = Packet.SetStatic( new SpeedBoost( true ) ); + public static readonly Packet Disabled = Packet.SetStatic( new SpeedBoost( false ) ); + + public static Packet Instantiate( bool enable ) + { + return (enable ? Enabled : Disabled); + } + + public SpeedBoost( bool enable ) + : base( 0xBF ) + { + EnsureCapacity( 3 ); + + m_Stream.Write( (short)0x26 ); + m_Stream.Write( (bool)enable ); + } + } + + public sealed class InvalidMapEnable : Packet + { + public InvalidMapEnable() : base( 0xC6, 1 ) + { + } + } + + public sealed class BondedStatus : Packet + { + public BondedStatus( int val1, Serial serial, int val2 ) : base( 0xBF ) + { + this.EnsureCapacity( 11 ); + + m_Stream.Write( (short) 0x19 ); + m_Stream.Write( (byte) val1 ); + m_Stream.Write( (int) serial ); + m_Stream.Write( (byte) val2 ); + } + } + + public sealed class ToggleSpecialAbility : Packet + { + public ToggleSpecialAbility( int abilityID, bool active ) + : base( 0xBF ) + { + EnsureCapacity( 7 ); + + m_Stream.Write( (short)0x25 ); + + m_Stream.Write( (short)abilityID ); + m_Stream.Write( (bool)active ); + } + } + + public sealed class DisplayItemListMenu : Packet + { + public DisplayItemListMenu( ItemListMenu menu ) : base( 0x7C ) + { + this.EnsureCapacity( 256 ); + + m_Stream.Write( (int) ((IMenu)menu).Serial ); + m_Stream.Write( (short) 0 ); + + string question = menu.Question; + + if ( question == null ) question = ""; + + int questionLength = (byte)question.Length; + + m_Stream.Write( (byte) questionLength ); + m_Stream.WriteAsciiFixed( question, questionLength ); + + ItemListEntry[] entries = menu.Entries; + + int entriesLength = (byte)entries.Length; + + m_Stream.Write( (byte) entriesLength ); + + for ( int i = 0; i < entriesLength; ++i ) + { + ItemListEntry e = entries[i]; + + m_Stream.Write( (short) (e.ItemID & 0x3FFF) ); + m_Stream.Write( (short) e.Hue ); + + string name = e.Name; + + if ( name == null ) name = ""; + + int nameLength = (byte)name.Length; + + m_Stream.Write( (byte) nameLength ); + m_Stream.WriteAsciiFixed( name, nameLength ); + } + } + } + + public sealed class DisplayQuestionMenu : Packet + { + public DisplayQuestionMenu( QuestionMenu menu ) : base( 0x7C ) + { + this.EnsureCapacity( 256 ); + + m_Stream.Write( (int) ((IMenu)menu).Serial ); + m_Stream.Write( (short) 0 ); + + string question = menu.Question; + + if ( question == null ) question = ""; + + int questionLength = (byte)question.Length; + + m_Stream.Write( (byte) questionLength ); + m_Stream.WriteAsciiFixed( question, questionLength ); + + string[] answers = menu.Answers; + + int answersLength = (byte)answers.Length; + + m_Stream.Write( (byte) answersLength ); + + for ( int i = 0; i < answersLength; ++i ) + { + m_Stream.Write( (int) 0 ); + + string answer = answers[i]; + + if ( answer == null ) answer = ""; + + int answerLength = (byte)answer.Length; + + m_Stream.Write( (byte) answerLength ); + m_Stream.WriteAsciiFixed( answer, answerLength ); + } + } + } + + public sealed class GlobalLightLevel : Packet + { + private static GlobalLightLevel[] m_Cache = new GlobalLightLevel[0x100]; + + public static GlobalLightLevel Instantiate( int level ) + { + byte lvl = (byte)level; + GlobalLightLevel p = m_Cache[lvl]; + + if ( p == null ) + { + m_Cache[lvl] = p = new GlobalLightLevel( level ); + p.SetStatic(); + } + + return p; + } + + public GlobalLightLevel( int level ) : base( 0x4F, 2 ) + { + m_Stream.Write( (sbyte) level ); + } + } + + public sealed class PersonalLightLevel : Packet + { + public PersonalLightLevel( Mobile m ) : this( m, m.LightLevel ) + { + } + + public PersonalLightLevel( Mobile m, int level ) : base( 0x4E, 6 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (sbyte) level ); + } + } + + public sealed class PersonalLightLevelZero : Packet + { + public PersonalLightLevelZero( Mobile m ) : base( 0x4E, 6 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (sbyte) 0 ); + } + } + + public enum CMEFlags + { + None = 0x00, + Disabled = 0x01, + Colored = 0x20 + } + + public sealed class DisplayContextMenu : Packet + { + public DisplayContextMenu( ContextMenu menu ) : base( 0xBF ) + { + ContextMenuEntry[] entries = menu.Entries; + + int length = (byte) entries.Length; + + this.EnsureCapacity( 12 + (length * 8) ); + + m_Stream.Write( (short) 0x14 ); + m_Stream.Write( (short) 0x01 ); + + IEntity target = menu.Target as IEntity; + + m_Stream.Write( (int) ( target == null ? Serial.MinusOne : target.Serial ) ); + + m_Stream.Write( (byte) length ); + + Point3D p; + + if ( target is Mobile ) + p = target.Location; + else if ( target is Item ) + p = ((Item)target).GetWorldLocation(); + else + p = Point3D.Zero; + + for ( int i = 0; i < length; ++i ) + { + ContextMenuEntry e = entries[i]; + + m_Stream.Write( (short) i ); + m_Stream.Write( (ushort) e.Number ); + + int range = e.Range; + + if ( range == -1 ) + range = 18; + + CMEFlags flags = (e.Enabled && menu.From.InRange( p, range )) ? CMEFlags.None : CMEFlags.Disabled; + + int color = e.Color & 0xFFFF; + + if ( color != 0xFFFF ) + flags |= CMEFlags.Colored; + + flags |= e.Flags; + + m_Stream.Write( (short) flags ); + + if ( (flags & CMEFlags.Colored) != 0 ) + m_Stream.Write( (short) color ); + } + } + } + + public sealed class DisplayProfile : Packet + { + public DisplayProfile( bool realSerial, Mobile m, string header, string body, string footer ) : base( 0xB8 ) + { + if ( header == null ) + header = ""; + + if ( body == null ) + body = ""; + + if ( footer == null ) + footer = ""; + + EnsureCapacity( 12 + header.Length + (footer.Length * 2) + (body.Length * 2) ); + + m_Stream.Write( (int) (realSerial ? m.Serial : Serial.Zero) ); + m_Stream.WriteAsciiNull( header ); + m_Stream.WriteBigUniNull( footer ); + m_Stream.WriteBigUniNull( body ); + } + } + + public sealed class CloseGump : Packet + { + public CloseGump( int typeID, int buttonID ) : base( 0xBF ) + { + this.EnsureCapacity( 13 ); + + m_Stream.Write( (short) 0x04 ); + m_Stream.Write( (int) typeID ); + m_Stream.Write( (int) buttonID ); + } + } + + public sealed class EquipUpdate : Packet + { + public EquipUpdate( Item item ) : base( 0x2E, 15 ) + { + Serial parentSerial; + + if ( item.Parent is Mobile ) + { + parentSerial = ((Mobile)item.Parent).Serial; + } + else + { + Console.WriteLine( "Warning: EquipUpdate on item with !(parent is Mobile)" ); + parentSerial = Serial.Zero; + } + + int hue = item.Hue; + + if ( item.Parent is Mobile ) + { + Mobile mob = (Mobile)item.Parent; + + if ( mob.SolidHueOverride >= 0 ) + hue = mob.SolidHueOverride; + } + + m_Stream.Write( (int) item.Serial ); + m_Stream.Write( (short) item.ItemID ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) item.Layer ); + m_Stream.Write( (int) parentSerial ); + m_Stream.Write( (short) hue ); + } + } + + public sealed class WorldItem : Packet + { + public WorldItem( Item item ) : base( 0x1A ) + { + this.EnsureCapacity( 20 ); + + // 14 base length + // +2 - Amount + // +2 - Hue + // +1 - Flags + + uint serial = (uint)item.Serial.Value; + int itemID = item.ItemID; + int amount = item.Amount; + Point3D loc = item.Location; + int x = loc.m_X; + int y = loc.m_Y; + int hue = item.Hue; + int flags = item.GetPacketFlags(); + int direction = (int)item.Direction; + + if ( amount != 0 ) + { + serial |= 0x80000000; + } + else + { + serial &= 0x7FFFFFFF; + } + + m_Stream.Write( (uint) serial ); + m_Stream.Write( (short) (itemID & 0x7FFF) ); + + if ( amount != 0 ) + { + m_Stream.Write( (short) amount ); + } + + x &= 0x7FFF; + + if ( direction != 0 ) + { + x |= 0x8000; + } + + m_Stream.Write( (short) x ); + + y &= 0x3FFF; + + if ( hue != 0 ) + { + y |= 0x8000; + } + + if ( flags != 0 ) + { + y |= 0x4000; + } + + m_Stream.Write( (short) y ); + + if ( direction != 0 ) + m_Stream.Write( (byte) direction ); + + m_Stream.Write( (sbyte) loc.m_Z ); + + if ( hue != 0 ) + m_Stream.Write( (ushort) hue ); + + if ( flags != 0 ) + m_Stream.Write( (byte) flags ); + } + } + + public sealed class LiftRej : Packet + { + public LiftRej( LRReason reason ) : base( 0x27, 2 ) + { + m_Stream.Write( (byte) reason ); + } + } + + public sealed class LogoutAck : Packet + { + public LogoutAck() : base( 0xD1, 2 ) + { + m_Stream.Write( (byte) 0x01 ); + } + } + + public sealed class Weather : Packet + { + public Weather( int v1, int v2, int v3 ) : base( 0x65, 4 ) + { + m_Stream.Write( (byte) v1 ); + m_Stream.Write( (byte) v2 ); + m_Stream.Write( (byte) v3 ); + } + } + + public sealed class UnkD3 : Packet + { + public UnkD3( Mobile beholder, Mobile beheld ) : base( 0xD3 ) + { + this.EnsureCapacity( 256 ); + + //int + //short + //short + //short + //byte + //byte + //short + //byte + //byte + //short + //short + //short + //while ( int != 0 ) + //{ + //short + //byte + //short + //} + + m_Stream.Write( (int) beheld.Serial ); + m_Stream.Write( (short) beheld.Body ); + m_Stream.Write( (short) beheld.X ); + m_Stream.Write( (short) beheld.Y ); + m_Stream.Write( (sbyte) beheld.Z ); + m_Stream.Write( (byte) beheld.Direction ); + m_Stream.Write( (ushort) beheld.Hue ); + m_Stream.Write( (byte) beheld.GetPacketFlags() ); + m_Stream.Write( (byte) Notoriety.Compute( beholder, beheld ) ); + + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) 0 ); + + m_Stream.Write( (int) 0 ); + } + } + + public sealed class GQRequest : Packet + { + public GQRequest() : base( 0xC3 ) + { + this.EnsureCapacity( 256 ); + + m_Stream.Write( (int) 1 ); + m_Stream.Write( (int) 2 ); // ID + m_Stream.Write( (int) 3 ); // Customer ? (this) + m_Stream.Write( (int) 4 ); // Customer this (?) + m_Stream.Write( (int) 0 ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) 6 ); + m_Stream.Write( (byte) 'r' ); + m_Stream.Write( (byte) 'e' ); + m_Stream.Write( (byte) 'g' ); + m_Stream.Write( (byte) 'i' ); + m_Stream.Write( (byte) 'o' ); + m_Stream.Write( (byte) 'n' ); + m_Stream.Write( (int) 7 ); // Call time in seconds + m_Stream.Write( (short) 2 ); // Map (0=fel,1=tram,2=ilsh) + m_Stream.Write( (int) 8 ); // X + m_Stream.Write( (int) 9 ); // Y + m_Stream.Write( (int) 10 ); // Z + m_Stream.Write( (int) 11 ); // Volume + m_Stream.Write( (int) 12 ); // Rank + m_Stream.Write( (int) -1 ); + m_Stream.Write( (int) 1 ); // type + } + } + + /// + /// Causes the client to walk in a given direction. It does not send a movement request. + /// + public sealed class PlayerMove : Packet + { + public PlayerMove( Direction d ) : base( 0x97, 2 ) + { + m_Stream.Write( (byte) d ); + + // @4C63B0 + } + } + + /// + /// Displays a message "There are currently [count] available calls in the global queue.". + /// + public sealed class GQCount : Packet + { + public GQCount( int unk, int count ) : base( 0xCB, 7 ) + { + m_Stream.Write( (short) unk ); + m_Stream.Write( (int) count ); + } + } + + /// + /// Asks the client for it's version + /// + public sealed class ClientVersionReq : Packet + { + public ClientVersionReq() : base( 0xBD ) + { + this.EnsureCapacity( 3 ); + } + } + + /// + /// Asks the client for it's "assist version". (Perhaps for UOAssist?) + /// + public sealed class AssistVersionReq : Packet + { + public AssistVersionReq( int unk ) : base( 0xBE ) + { + this.EnsureCapacity( 7 ); + + m_Stream.Write( (int) unk ); + } + } + + public enum EffectType + { + Moving = 0x00, + Lightning = 0x01, + FixedXYZ = 0x02, + FixedFrom = 0x03 + } + + public class ParticleEffect : Packet + { + public ParticleEffect( EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint, int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, Serial serial, int layer, int unknown ) : base( 0xC7, 49 ) + { + m_Stream.Write( (byte) type ); + m_Stream.Write( (int) from ); + m_Stream.Write( (int) to ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (short) fromPoint.m_X ); + m_Stream.Write( (short) fromPoint.m_Y ); + m_Stream.Write( (sbyte) fromPoint.m_Z ); + m_Stream.Write( (short) toPoint.m_X ); + m_Stream.Write( (short) toPoint.m_Y ); + m_Stream.Write( (sbyte) toPoint.m_Z ); + m_Stream.Write( (byte) speed ); + m_Stream.Write( (byte) duration ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (bool) fixedDirection ); + m_Stream.Write( (bool) explode ); + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) renderMode ); + m_Stream.Write( (short) effect ); + m_Stream.Write( (short) explodeEffect ); + m_Stream.Write( (short) explodeSound ); + m_Stream.Write( (int) serial ); + m_Stream.Write( (byte) layer ); + m_Stream.Write( (short) unknown ); + } + + public ParticleEffect( EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, Serial serial, int layer, int unknown ) : base( 0xC7, 49 ) + { + m_Stream.Write( (byte) type ); + m_Stream.Write( (int) from ); + m_Stream.Write( (int) to ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (short) fromPoint.X ); + m_Stream.Write( (short) fromPoint.Y ); + m_Stream.Write( (sbyte) fromPoint.Z ); + m_Stream.Write( (short) toPoint.X ); + m_Stream.Write( (short) toPoint.Y ); + m_Stream.Write( (sbyte) toPoint.Z ); + m_Stream.Write( (byte) speed ); + m_Stream.Write( (byte) duration ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (bool) fixedDirection ); + m_Stream.Write( (bool) explode ); + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) renderMode ); + m_Stream.Write( (short) effect ); + m_Stream.Write( (short) explodeEffect ); + m_Stream.Write( (short) explodeSound ); + m_Stream.Write( (int) serial ); + m_Stream.Write( (byte) layer ); + m_Stream.Write( (short) unknown ); + } + } + + public class HuedEffect : Packet + { + public HuedEffect( EffectType type, Serial from, Serial to, int itemID, Point3D fromPoint, Point3D toPoint, int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode ) : base( 0xC0, 36 ) + { + m_Stream.Write( (byte) type ); + m_Stream.Write( (int) from ); + m_Stream.Write( (int) to ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (short) fromPoint.m_X ); + m_Stream.Write( (short) fromPoint.m_Y ); + m_Stream.Write( (sbyte) fromPoint.m_Z ); + m_Stream.Write( (short) toPoint.m_X ); + m_Stream.Write( (short) toPoint.m_Y ); + m_Stream.Write( (sbyte) toPoint.m_Z ); + m_Stream.Write( (byte) speed ); + m_Stream.Write( (byte) duration ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (bool) fixedDirection ); + m_Stream.Write( (bool) explode ); + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) renderMode ); + } + + public HuedEffect( EffectType type, Serial from, Serial to, int itemID, IPoint3D fromPoint, IPoint3D toPoint, int speed, int duration, bool fixedDirection, bool explode, int hue, int renderMode ) : base( 0xC0, 36 ) + { + m_Stream.Write( (byte) type ); + m_Stream.Write( (int) from ); + m_Stream.Write( (int) to ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (short) fromPoint.X ); + m_Stream.Write( (short) fromPoint.Y ); + m_Stream.Write( (sbyte) fromPoint.Z ); + m_Stream.Write( (short) toPoint.X ); + m_Stream.Write( (short) toPoint.Y ); + m_Stream.Write( (sbyte) toPoint.Z ); + m_Stream.Write( (byte) speed ); + m_Stream.Write( (byte) duration ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (bool) fixedDirection ); + m_Stream.Write( (bool) explode ); + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) renderMode ); + } + } + + public sealed class TargetParticleEffect : ParticleEffect + { + public TargetParticleEffect( IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int layer, int unknown ) : base( EffectType.FixedFrom, e.Serial, Serial.Zero, itemID, e.Location, e.Location, speed, duration, true, false, hue, renderMode, effect, 1, 0, e.Serial, layer, unknown ) + { + } + } + + public sealed class TargetEffect : HuedEffect + { + public TargetEffect( IEntity e, int itemID, int speed, int duration, int hue, int renderMode ) : base( EffectType.FixedFrom, e.Serial, Serial.Zero, itemID, e.Location, e.Location, speed, duration, true, false, hue, renderMode ) + { + } + } + + public sealed class LocationParticleEffect : ParticleEffect + { + public LocationParticleEffect( IEntity e, int itemID, int speed, int duration, int hue, int renderMode, int effect, int unknown ) : base( EffectType.FixedXYZ, e.Serial, Serial.Zero, itemID, e.Location, e.Location, speed, duration, true, false, hue, renderMode, effect, 1, 0, e.Serial, 255, unknown ) + { + } + } + + public sealed class LocationEffect : HuedEffect + { + public LocationEffect( IPoint3D p, int itemID, int speed, int duration, int hue, int renderMode ) : base( EffectType.FixedXYZ, Serial.Zero, Serial.Zero, itemID, p, p, speed, duration, true, false, hue, renderMode ) + { + } + } + + public sealed class MovingParticleEffect : ParticleEffect + { + public MovingParticleEffect( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode, int effect, int explodeEffect, int explodeSound, EffectLayer layer, int unknown ) : base( EffectType.Moving, from.Serial, to.Serial, itemID, from.Location, to.Location, speed, duration, fixedDirection, explodes, hue, renderMode, effect, explodeEffect, explodeSound, Serial.Zero, (int)layer, unknown ) + { + } + } + + public sealed class MovingEffect : HuedEffect + { + public MovingEffect( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool explodes, int hue, int renderMode ) : base( EffectType.Moving, from.Serial, to.Serial, itemID, from.Location, to.Location, speed, duration, fixedDirection, explodes, hue, renderMode ) + { + } + } + + public enum DeleteResultType + { + PasswordInvalid, + CharNotExist, + CharBeingPlayed, + CharTooYoung, + CharQueued, + BadRequest + } + + public sealed class DeleteResult : Packet + { + public DeleteResult( DeleteResultType res ) : base( 0x85, 2 ) + { + m_Stream.Write( (byte) res ); + } + } + + /*public sealed class MovingEffect : Packet + { + public MovingEffect( IEntity from, IEntity to, int itemID, int speed, int duration, bool fixedDirection, bool turn, int hue, int renderMode ) : base( 0xC0, 36 ) + { + m_Stream.Write( (byte) 0x00 ); + m_Stream.Write( (int) from.Serial ); + m_Stream.Write( (int) to.Serial ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (short) from.Location.m_X ); + m_Stream.Write( (short) from.Location.m_Y ); + m_Stream.Write( (sbyte) from.Location.m_Z ); + m_Stream.Write( (short) to.Location.m_X ); + m_Stream.Write( (short) to.Location.m_Y ); + m_Stream.Write( (sbyte) to.Location.m_Z ); + m_Stream.Write( (byte) speed ); + m_Stream.Write( (byte) duration ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (bool) fixedDirection ); + m_Stream.Write( (bool) turn ); + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) renderMode ); + } + }*/ + + /*public sealed class LocationEffect : Packet + { + public LocationEffect( IPoint3D p, int itemID, int duration, int hue, int renderMode ) : base( 0xC0, 36 ) + { + m_Stream.Write( (byte) 0x02 ); + m_Stream.Write( (int) Serial.Zero ); + m_Stream.Write( (int) Serial.Zero ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (short) p.X ); + m_Stream.Write( (short) p.Y ); + m_Stream.Write( (sbyte) p.Z ); + m_Stream.Write( (short) p.X ); + m_Stream.Write( (short) p.Y ); + m_Stream.Write( (sbyte) p.Z ); + m_Stream.Write( (byte) 10 ); + m_Stream.Write( (byte) duration ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 1 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) renderMode ); + } + }*/ + + public sealed class BoltEffect : Packet + { + public BoltEffect( IEntity target, int hue ) : base( 0xC0, 36 ) + { + m_Stream.Write( (byte) 0x01 ); // type + m_Stream.Write( (int) target.Serial ); + m_Stream.Write( (int) Serial.Zero ); + m_Stream.Write( (short) 0 ); // itemID + m_Stream.Write( (short) target.X ); + m_Stream.Write( (short) target.Y ); + m_Stream.Write( (sbyte) target.Z ); + m_Stream.Write( (short) target.X ); + m_Stream.Write( (short) target.Y ); + m_Stream.Write( (sbyte) target.Z ); + m_Stream.Write( (byte) 0 ); // speed + m_Stream.Write( (byte) 0 ); // duration + m_Stream.Write( (short) 0 ); // unk + m_Stream.Write( false ); // fixed direction + m_Stream.Write( false ); // explode + m_Stream.Write( (int) hue ); + m_Stream.Write( (int) 0 ); // render mode + } + } + + public sealed class DisplaySpellbook : Packet + { + public DisplaySpellbook( Item book ) : base( 0x24, 7 ) + { + m_Stream.Write( (int) book.Serial ); + m_Stream.Write( (short) -1 ); + } + } + + public sealed class NewSpellbookContent : Packet + { + public NewSpellbookContent( Item item, int graphic, int offset, ulong content ) : base( 0xBF ) + { + EnsureCapacity( 23 ); + + m_Stream.Write( (short) 0x1B ); + m_Stream.Write( (short) 0x01 ); + + m_Stream.Write( (int) item.Serial ); + m_Stream.Write( (short) graphic ); + m_Stream.Write( (short) offset ); + + for ( int i = 0; i < 8; ++i ) + m_Stream.Write( (byte)(content >> (i * 8)) ); + } + } + + public sealed class SpellbookContent : Packet + { + public SpellbookContent( int count, int offset, ulong content, Item item ) : base( 0x3C ) + { + this.EnsureCapacity( 5 + (count * 19) ); + + int written = 0; + + m_Stream.Write( (ushort) 0 ); + + ulong mask = 1; + + for ( int i = 0; i < 64; ++i, mask <<= 1 ) + { + if ( (content & mask) != 0 ) + { + m_Stream.Write( (int) (0x7FFFFFFF - i) ); + m_Stream.Write( (ushort) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (ushort) (i + offset) ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (int) item.Serial ); + m_Stream.Write( (short) 0 ); + + ++written; + } + } + + m_Stream.Seek( 3, SeekOrigin.Begin ); + m_Stream.Write( (ushort) written ); + } + } + + public sealed class ContainerDisplay : Packet + { + public ContainerDisplay( Container c ) : base( 0x24, 7 ) + { + m_Stream.Write( (int) c.Serial ); + m_Stream.Write( (short) c.GumpID ); + } + } + + public sealed class ContainerContentUpdate : Packet + { + public ContainerContentUpdate( Item item ) : base( 0x25, 20 ) + { + Serial parentSerial; + + if ( item.Parent is Item ) + { + parentSerial = ((Item)item.Parent).Serial; + } + else + { + Console.WriteLine( "Warning: ContainerContentUpdate on item with !(parent is Item)" ); + parentSerial = Serial.Zero; + } + + ushort cid = (ushort) item.ItemID; + + if ( cid > 0x3FFF ) + cid = 0x9D7; + + m_Stream.Write( (int) item.Serial ); + m_Stream.Write( (short) cid ); + m_Stream.Write( (byte) 0 ); // signed, itemID offset + m_Stream.Write( (ushort) item.Amount ); + m_Stream.Write( (short) item.X ); + m_Stream.Write( (short) item.Y ); + m_Stream.Write( (int) parentSerial ); + m_Stream.Write( (ushort) item.Hue ); + } + } + + public sealed class ContainerContent : Packet + { + public ContainerContent( Mobile beholder, Item beheld ) : base( 0x3C ) + { + List items = beheld.Items; + int count = items.Count; + + this.EnsureCapacity( 5 + (count * 19) ); + + long pos = m_Stream.Position; + + int written = 0; + + m_Stream.Write( (ushort) 0 ); + + for ( int i = 0; i < count; ++i ) + { + Item child = items[i]; + + if ( !child.Deleted && beholder.CanSee( child ) ) + { + Point3D loc = child.Location; + + ushort cid = (ushort) child.ItemID; + + if ( cid > 0x3FFF ) + cid = 0x9D7; + + m_Stream.Write( (int) child.Serial ); + m_Stream.Write( (ushort) cid ); + m_Stream.Write( (byte) 0 ); // signed, itemID offset + m_Stream.Write( (ushort) child.Amount ); + m_Stream.Write( (short) loc.m_X ); + m_Stream.Write( (short) loc.m_Y ); + m_Stream.Write( (int) beheld.Serial ); + m_Stream.Write( (ushort) child.Hue ); + + ++written; + } + } + + m_Stream.Seek( pos, SeekOrigin.Begin ); + m_Stream.Write( (ushort) written ); + } + } + + public sealed class SetWarMode : Packet + { + public static readonly Packet InWarMode = Packet.SetStatic( new SetWarMode( true ) ); + public static readonly Packet InPeaceMode = Packet.SetStatic( new SetWarMode( false ) ); + + public static Packet Instantiate( bool mode ) + { + return ( mode ? InWarMode : InPeaceMode ); + } + + public SetWarMode( bool mode ) : base( 0x72, 5 ) + { + m_Stream.Write( mode ); + m_Stream.Write( (byte) 0x00 ); + m_Stream.Write( (byte) 0x32 ); + m_Stream.Write( (byte) 0x00 ); + //m_Stream.Fill(); + } + } + + public sealed class Swing : Packet + { + public Swing( int flag, Mobile attacker, Mobile defender ) : base( 0x2F, 10 ) + { + m_Stream.Write( (byte) flag ); + m_Stream.Write( (int) attacker.Serial ); + m_Stream.Write( (int) defender.Serial ); + } + } + + public sealed class NullFastwalkStack : Packet + { + public NullFastwalkStack() : base( 0xBF ) + { + EnsureCapacity(256); + m_Stream.Write( (short) 0x1 ); + m_Stream.Write( (int) 0x0 ); + m_Stream.Write( (int) 0x0 ); + m_Stream.Write( (int) 0x0 ); + m_Stream.Write( (int) 0x0 ); + m_Stream.Write( (int) 0x0 ); + m_Stream.Write( (int) 0x0 ); + } + } + + public sealed class RemoveItem : Packet + { + public RemoveItem( Item item ) : base( 0x1D, 5 ) + { + m_Stream.Write( (int) item.Serial ); + } + } + + public sealed class RemoveMobile : Packet + { + public RemoveMobile( Mobile m ) : base( 0x1D, 5 ) + { + m_Stream.Write( (int) m.Serial ); + } + } + + public sealed class ServerChange : Packet + { + public ServerChange( Mobile m, Map map ) : base( 0x76, 16 ) + { + m_Stream.Write( (short) m.X ); + m_Stream.Write( (short) m.Y ); + m_Stream.Write( (short) m.Z ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) map.Width ); + m_Stream.Write( (short) map.Height ); + } + } + + public sealed class SkillUpdate : Packet + { + public SkillUpdate( Skills skills ) : base( 0x3A ) + { + this.EnsureCapacity( 6 + (skills.Length * 9) ); + + m_Stream.Write( (byte) 0x02 ); // type: absolute, capped + + for ( int i = 0; i < skills.Length; ++i ) + { + Skill s = skills[i]; + + double v = s.NonRacialValue; + int uv = (int)(v * 10); + + if ( uv < 0 ) + uv = 0; + else if ( uv >= 0x10000 ) + uv = 0xFFFF; + + m_Stream.Write( (ushort) (s.Info.SkillID + 1) ); + m_Stream.Write( (ushort) uv ); + m_Stream.Write( (ushort) s.BaseFixedPoint ); + m_Stream.Write( (byte) s.Lock ); + m_Stream.Write( (ushort) s.CapFixedPoint ); + } + + m_Stream.Write( (short) 0 ); // terminate + } + } + + public sealed class Sequence : Packet + { + public Sequence( int num ) : base( 0x7B, 2 ) + { + m_Stream.Write( (byte)num ); + } + } + + public sealed class SkillChange : Packet + { + public SkillChange( Skill skill ) : base( 0x3A ) + { + this.EnsureCapacity( 13 ); + + double v = skill.NonRacialValue; + int uv = (int)(v * 10); + + if ( uv < 0 ) + uv = 0; + else if ( uv >= 0x10000 ) + uv = 0xFFFF; + + m_Stream.Write( (byte) 0xDF ); // type: delta, capped + m_Stream.Write( (ushort) skill.Info.SkillID ); + m_Stream.Write( (ushort) uv ); + m_Stream.Write( (ushort) skill.BaseFixedPoint ); + m_Stream.Write( (byte) skill.Lock ); + m_Stream.Write( (ushort) skill.CapFixedPoint ); + + /*m_Stream.Write( (short) skill.Info.SkillID ); + m_Stream.Write( (short) (skill.Value * 10.0) ); + m_Stream.Write( (short) (skill.Base * 10.0) ); + m_Stream.Write( (byte) skill.Lock ); + m_Stream.Write( (short) skill.CapFixedPoint );*/ + } + } + + public sealed class LaunchBrowser : Packet + { + public LaunchBrowser( string url ) : base( 0xA5 ) + { + if ( url == null ) url = ""; + + this.EnsureCapacity( 4 + url.Length ); + + m_Stream.WriteAsciiNull( url ); + } + } + + public sealed class MessageLocalized : Packet + { + private static MessageLocalized[] m_Cache_IntLoc = new MessageLocalized[15000]; + private static MessageLocalized[] m_Cache_CliLoc = new MessageLocalized[100000]; + private static MessageLocalized[] m_Cache_CliLocCmp = new MessageLocalized[5000]; + + public static MessageLocalized InstantiateGeneric( int number ) + { + MessageLocalized[] cache = null; + int index = 0; + + if ( number >= 3000000 ) + { + cache = m_Cache_IntLoc; + index = number - 3000000; + } + else if ( number >= 1000000 ) + { + cache = m_Cache_CliLoc; + index = number - 1000000; + } + else if ( number >= 500000 ) + { + cache = m_Cache_CliLocCmp; + index = number - 500000; + } + + MessageLocalized p; + + if ( cache != null && index >= 0 && index < cache.Length ) + { + p = cache[index]; + + if ( p == null ) + { + cache[index] = p = new MessageLocalized( Serial.MinusOne, -1, MessageType.Regular, 0x3B2, 3, number, "System", "" ); + p.SetStatic(); + } + } + else + { + p = new MessageLocalized( Serial.MinusOne, -1, MessageType.Regular, 0x3B2, 3, number, "System", "" ); + } + + return p; + } + + public MessageLocalized( Serial serial, int graphic, MessageType type, int hue, int font, int number, string name, string args ) : base( 0xC1 ) + { + if ( name == null ) name = ""; + if ( args == null ) args = ""; + + if ( hue == 0 ) + hue = 0x3B2; + + this.EnsureCapacity( 50 + (args.Length * 2) ); + + m_Stream.Write( (int) serial ); + m_Stream.Write( (short) graphic ); + m_Stream.Write( (byte) type ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (short) font ); + m_Stream.Write( (int) number ); + m_Stream.WriteAsciiFixed( name, 30 ); + m_Stream.WriteLittleUniNull( args ); + } + } + + public sealed class MobileMoving : Packet + { + public MobileMoving( Mobile m, int noto/*Mobile beholder, Mobile beheld*/ ) : base( 0x77, 17 ) + { + Point3D loc = m.Location; + + int hue = m.Hue; + + if ( m.SolidHueOverride >= 0 ) + hue = m.SolidHueOverride; + + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) m.Body ); + m_Stream.Write( (short) loc.m_X ); + m_Stream.Write( (short) loc.m_Y ); + m_Stream.Write( (sbyte) loc.m_Z ); + m_Stream.Write( (byte) m.Direction ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (byte) m.GetPacketFlags() ); + m_Stream.Write( (byte) noto );//Notoriety.Compute( beholder, beheld ) ); + } + } + + public sealed class MultiTargetReq : Packet + { + public MultiTargetReq( MultiTarget t ) : base( 0x99, 26 ) + { + m_Stream.Write( (bool) t.AllowGround ); + m_Stream.Write( (int) t.TargetID ); + m_Stream.Write( (byte) t.Flags ); + + m_Stream.Fill(); + + m_Stream.Seek( 18, SeekOrigin.Begin ); + m_Stream.Write( (short) t.MultiID ); + m_Stream.Write( (short) t.Offset.X ); + m_Stream.Write( (short) t.Offset.Y ); + m_Stream.Write( (short) t.Offset.Z ); + } + } + + public sealed class CancelTarget : Packet + { + public static readonly Packet Instance = Packet.SetStatic( new CancelTarget() ); + + public CancelTarget() : base( 0x6C, 19 ) + { + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (int) 0 ); + m_Stream.Write( (byte) 3 ); + m_Stream.Fill(); + } + } + + public sealed class TargetReq : Packet + { + public TargetReq( Target t ) : base( 0x6C, 19 ) + { + m_Stream.Write( (bool) t.AllowGround ); + m_Stream.Write( (int) t.TargetID ); + m_Stream.Write( (byte) t.Flags ); + m_Stream.Fill(); + } + } + + public sealed class DragEffect : Packet + { + public DragEffect( IEntity src, IEntity trg, int itemID, int hue, int amount ) : base( 0x23, 26 ) + { + m_Stream.Write( (short) itemID ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (short) amount ); + m_Stream.Write( (int) src.Serial ); + m_Stream.Write( (short) src.X ); + m_Stream.Write( (short) src.Y ); + m_Stream.Write( (sbyte) src.Z ); + m_Stream.Write( (int) trg.Serial ); + m_Stream.Write( (short) trg.X ); + m_Stream.Write( (short) trg.Y ); + m_Stream.Write( (sbyte) trg.Z ); + } + } + + public interface IGumpWriter + { + int TextEntries { get; set; } + int Switches { get; set; } + + void AppendLayout( bool val ); + void AppendLayout( int val ); + void AppendLayoutNS( int val ); + void AppendLayout( string text ); + void AppendLayout( byte[] buffer ); + void WriteStrings( List strings ); + void Flush(); + } + + public sealed class DisplayGumpPacked : Packet, IGumpWriter + { + private int m_TextEntries, m_Switches; + + public int TextEntries { get { return m_TextEntries; } set { m_TextEntries = value; } } + public int Switches { get { return m_Switches; } set { m_Switches = value; } } + + private Gump m_Gump; + + private PacketWriter m_Layout; + private PacketWriter m_Strings; + + private int m_StringCount; + + public DisplayGumpPacked( Gump gump ) + : base( 0xDD ) + { + m_Gump = gump; + + m_Layout = PacketWriter.CreateInstance( 8192 ); + m_Strings = PacketWriter.CreateInstance( 8192 ); + } + + private static byte[] m_True = Gump.StringToBuffer( " 1" ); + private static byte[] m_False = Gump.StringToBuffer( " 0" ); + + private static byte[] m_BeginTextSeparator = Gump.StringToBuffer( " @" ); + private static byte[] m_EndTextSeparator = Gump.StringToBuffer( "@" ); + + private static byte[] m_Buffer = new byte[48]; + + static DisplayGumpPacked() + { + m_Buffer[0] = (byte)' '; + } + + public void AppendLayout( bool val ) + { + AppendLayout( val ? m_True : m_False ); + } + + public void AppendLayout( int val ) + { + string toString = val.ToString(); + int bytes = System.Text.Encoding.ASCII.GetBytes( toString, 0, toString.Length, m_Buffer, 1 ) + 1; + + m_Layout.Write( m_Buffer, 0, bytes ); + } + + public void AppendLayoutNS( int val ) + { + string toString = val.ToString(); + int bytes = System.Text.Encoding.ASCII.GetBytes( toString, 0, toString.Length, m_Buffer, 1 ); + + m_Layout.Write( m_Buffer, 1, bytes ); + } + + public void AppendLayout( string text ) + { + AppendLayout( m_BeginTextSeparator ); + + m_Layout.WriteAsciiFixed( text, text.Length ); + + AppendLayout( m_EndTextSeparator ); + } + + public void AppendLayout( byte[] buffer ) + { + m_Layout.Write( buffer, 0, buffer.Length ); + } + + public void WriteStrings( List strings ) + { + m_StringCount = strings.Count; + + for ( int i = 0; i < strings.Count; ++i ) + { + string v = strings[i]; + + if ( v == null ) + v = String.Empty; + + m_Strings.Write( (ushort) v.Length ); + m_Strings.WriteBigUniFixed( v, v.Length ); + } + } + + public void Flush() + { + EnsureCapacity( 28 + (int) m_Layout.Length + (int) m_Strings.Length ); + + m_Stream.Write( (int) m_Gump.Serial ); + m_Stream.Write( (int) m_Gump.TypeID ); + m_Stream.Write( (int) m_Gump.X ); + m_Stream.Write( (int) m_Gump.Y ); + + // Note: layout seems to be null terminated on OSI, but it works either ways + WritePacked( m_Layout ); + + m_Stream.Write( (int) m_StringCount ); + + WritePacked( m_Strings ); + + PacketWriter.ReleaseInstance( m_Layout ); + PacketWriter.ReleaseInstance( m_Strings ); + } + + private static byte[] m_PackBuffer; + + private void WritePacked( PacketWriter src ) + { + byte[] buffer = src.UnderlyingStream.GetBuffer(); + int length = (int) src.Length; + + if ( length == 0 ) + { + m_Stream.Write( (int) 0 ); + return; + } + + int wantLength = 1 + ( ( buffer.Length * 1024 ) / 1000 ); + + wantLength += 4095; + wantLength &= ~4095; + + if ( m_PackBuffer == null || m_PackBuffer.Length < wantLength ) + m_PackBuffer = new byte[wantLength]; + + int packLength = m_PackBuffer.Length; + + Compression.Pack( m_PackBuffer, ref packLength, buffer, length, ZLibQuality.Default ); + + m_Stream.Write( (int) ( 4 + packLength ) ); + m_Stream.Write( (int) length ); + m_Stream.Write( m_PackBuffer, 0, packLength ); + } + } + + public sealed class DisplayGumpFast : Packet, IGumpWriter + { + private int m_TextEntries, m_Switches; + + private int m_LayoutLength; + + public int TextEntries{ get{ return m_TextEntries; } set{ m_TextEntries = value; } } + public int Switches{ get{ return m_Switches; } set{ m_Switches = value; } } + + public DisplayGumpFast( Gump g ) : base( 0xB0 ) + { + EnsureCapacity( 4096 ); + + m_Stream.Write( (int) g.Serial ); + m_Stream.Write( (int) g.TypeID ); + m_Stream.Write( (int) g.X ); + m_Stream.Write( (int) g.Y ); + m_Stream.Write( (ushort) 0xFFFF ); + } + + private static byte[] m_True = Gump.StringToBuffer( " 1" ); + private static byte[] m_False = Gump.StringToBuffer( " 0" ); + + private static byte[] m_BeginTextSeparator = Gump.StringToBuffer( " @" ); + private static byte[] m_EndTextSeparator = Gump.StringToBuffer( "@" ); + + private static byte[] m_Buffer = new byte[48]; + + static DisplayGumpFast() + { + m_Buffer[0] = (byte)' '; + } + + public void AppendLayout( bool val ) + { + AppendLayout( val ? m_True : m_False ); + } + + public void AppendLayout( int val ) + { + string toString = val.ToString(); + int bytes = System.Text.Encoding.ASCII.GetBytes( toString, 0, toString.Length, m_Buffer, 1 ) + 1; + + m_Stream.Write( m_Buffer, 0, bytes ); + m_LayoutLength += bytes; + } + + public void AppendLayoutNS( int val ) + { + string toString = val.ToString(); + int bytes = System.Text.Encoding.ASCII.GetBytes( toString, 0, toString.Length, m_Buffer, 1 ); + + m_Stream.Write( m_Buffer, 1, bytes ); + m_LayoutLength += bytes; + } + + public void AppendLayout( string text ) + { + AppendLayout( m_BeginTextSeparator ); + + int length = text.Length; + m_Stream.WriteAsciiFixed( text, length ); + m_LayoutLength += length; + + AppendLayout( m_EndTextSeparator ); + } + + public void AppendLayout( byte[] buffer ) + { + int length = buffer.Length; + m_Stream.Write( buffer, 0, length ); + m_LayoutLength += length; + } + + public void WriteStrings( List text ) + { + m_Stream.Seek( 19, SeekOrigin.Begin ); + m_Stream.Write( (ushort) m_LayoutLength ); + m_Stream.Seek( 0, SeekOrigin.End ); + + m_Stream.Write( (ushort) text.Count ); + + for ( int i = 0; i < text.Count; ++i ) + { + string v = text[i]; + + if ( v == null ) + v = String.Empty; + + int length = (ushort) v.Length; + + m_Stream.Write( (ushort) length ); + m_Stream.WriteBigUniFixed( v, length ); + } + } + + public void Flush() + { + } + } + + public sealed class DisplayGump : Packet + { + public DisplayGump( Gump g, string layout, string[] text ) : base( 0xB0 ) + { + if ( layout == null ) layout = ""; + + this.EnsureCapacity( 256 ); + + m_Stream.Write( (int) g.Serial ); + m_Stream.Write( (int) g.TypeID ); + m_Stream.Write( (int) g.X ); + m_Stream.Write( (int) g.Y ); + m_Stream.Write( (ushort) (layout.Length + 1) ); + m_Stream.WriteAsciiNull( layout ); + + m_Stream.Write( (ushort) text.Length ); + + for ( int i = 0; i < text.Length; ++i ) + { + string v = text[i]; + + if ( v == null ) v = ""; + + int length = (ushort) v.Length; + + m_Stream.Write( (ushort) length ); + m_Stream.WriteBigUniFixed( v, length ); + } + } + } + + public sealed class DisplayPaperdoll : Packet + { + public DisplayPaperdoll( Mobile m, string text, bool canLift ) : base( 0x88, 66 ) + { + byte flags = 0x00; + + if ( m.Warmode ) + flags |= 0x01; + + if ( canLift ) + flags |= 0x02; + + m_Stream.Write( (int) m.Serial ); + m_Stream.WriteAsciiFixed( text, 60 ); + m_Stream.Write( (byte) flags ); + } + } + + public sealed class PopupMessage : Packet + { + public PopupMessage( PMMessage msg ) : base( 0x53, 2 ) + { + m_Stream.Write( (byte)msg ); + } + } + + public sealed class PlaySound : Packet + { + public PlaySound( int soundID, IPoint3D target ) : base( 0x54, 12 ) + { + m_Stream.Write( (byte) 1 ); // flags + m_Stream.Write( (short) soundID ); + m_Stream.Write( (short) 0 ); // volume + m_Stream.Write( (short) target.X ); + m_Stream.Write( (short) target.Y ); + m_Stream.Write( (short) target.Z ); + } + } + + public sealed class PlayMusic : Packet + { + public static readonly Packet InvalidInstance = Packet.SetStatic( new PlayMusic( MusicName.Invalid ) ); + + private static Packet[] m_Instances = new Packet[60]; + + public static Packet GetInstance( MusicName name ) + { + if ( name == MusicName.Invalid ) + return InvalidInstance; + + int v = (int)name; + Packet p; + + if ( v >= 0 && v < m_Instances.Length ) + { + p = m_Instances[v]; + + if ( p == null ) + m_Instances[v] = p = Packet.SetStatic( new PlayMusic( name ) ); + } + else + { + p = new PlayMusic( name ); + } + + return p; + } + + public PlayMusic( MusicName name ) : base( 0x6D, 3 ) + { + m_Stream.Write( (short)name ); + } + } + + public sealed class ScrollMessage : Packet + { + public ScrollMessage( int type, int tip, string text ) : base( 0xA6 ) + { + if ( text == null ) text = ""; + + this.EnsureCapacity( 10 + text.Length ); + + m_Stream.Write( (byte) type ); + m_Stream.Write( (int) tip ); + m_Stream.Write( (ushort) text.Length ); + m_Stream.WriteAsciiFixed( text, text.Length ); + } + } + + public sealed class CurrentTime : Packet + { + public CurrentTime() : base( 0x5B, 4 ) + { + DateTime now = DateTime.Now; + + m_Stream.Write( (byte) now.Hour ); + m_Stream.Write( (byte) now.Minute ); + m_Stream.Write( (byte) now.Second ); + } + } + + public sealed class MapChange : Packet + { + public MapChange( Mobile m ) : base( 0xBF ) + { + this.EnsureCapacity( 6 ); + + m_Stream.Write( (short) 0x08 ); + m_Stream.Write( (byte) (m.Map == null ? 0 : m.Map.MapID) ); + } + } + + public sealed class SeasonChange : Packet + { + private static SeasonChange[][] m_Cache = new SeasonChange[5][] + { + new SeasonChange[2], + new SeasonChange[2], + new SeasonChange[2], + new SeasonChange[2], + new SeasonChange[2] + }; + + public static SeasonChange Instantiate( int season ) + { + return Instantiate( season, true ); + } + + public static SeasonChange Instantiate( int season, bool playSound ) + { + if ( season >= 0 && season < m_Cache.Length ) + { + int idx = playSound ? 1 : 0; + + SeasonChange p = m_Cache[season][idx]; + + if ( p == null ) + { + m_Cache[season][idx] = p = new SeasonChange( season, playSound ); + p.SetStatic(); + } + + return p; + } + else + { + return new SeasonChange( season, playSound ); + } + } + + public SeasonChange( int season ) : this( season, true ) + { + } + + public SeasonChange( int season, bool playSound ) : base( 0xBC, 3 ) + { + m_Stream.Write( (byte) season ); + m_Stream.Write( (bool) playSound ); + } + } + + public sealed class SupportedFeatures : Packet + { + private static int m_AdditionalFlags; + + public static int Value{ get{ return m_AdditionalFlags; } set{ m_AdditionalFlags = value; } } + + [Obsolete( "Specify account instead" )] + public static SupportedFeatures Instantiate() + { + return Instantiate( null ); + } + + public static SupportedFeatures Instantiate( IAccount account ) + { + return new SupportedFeatures( account ); + } + + public SupportedFeatures( IAccount acct ) : base( 0xB9, 3 ) + { + int flags = ExpansionInfo.CurrentExpansion.SupportedFeatures; + + flags |= m_AdditionalFlags; + /* + int flags = 0x3 | m_AdditionalFlags; + + if ( Core.SE ) + flags |= 0x0040; + + if ( Core.AOS ) + flags |= 0x801C; + * */ + + if ( acct != null && acct.Limit >= 6 ) + { + flags |= 0x8020; + flags &= ~0x004; + } + + m_Stream.Write( (ushort) flags ); + //m_Stream.Write( (ushort) m_Value ); // 0x01 = T2A, 0x02 = LBR + } + } + + public class AttributeNormalizer + { + private static int m_Maximum = 25; + private static bool m_Enabled = true; + + public static int Maximum + { + get{ return m_Maximum; } + set{ m_Maximum = value; } + } + + public static bool Enabled + { + get{ return m_Enabled; } + set{ m_Enabled = value; } + } + + public static void Write( PacketWriter stream, int cur, int max ) + { + if ( m_Enabled && max != 0 ) + { + stream.Write( (short) m_Maximum ); + stream.Write( (short) ((cur * m_Maximum) / max) ); + } + else + { + stream.Write( (short) max ); + stream.Write( (short) cur ); + } + } + + public static void WriteReverse( PacketWriter stream, int cur, int max ) + { + if ( m_Enabled && max != 0 ) + { + stream.Write( (short) ((cur * m_Maximum) / max) ); + stream.Write( (short) m_Maximum ); + } + else + { + stream.Write( (short) cur ); + stream.Write( (short) max ); + } + } + } + + public sealed class MobileHits : Packet + { + public MobileHits( Mobile m ) : base( 0xA1, 9 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) m.HitsMax ); + m_Stream.Write( (short) m.Hits ); + } + } + + public sealed class MobileHitsN : Packet + { + public MobileHitsN( Mobile m ) : base( 0xA1, 9 ) + { + m_Stream.Write( (int) m.Serial ); + AttributeNormalizer.Write( m_Stream, m.Hits, m.HitsMax ); + } + } + + public sealed class MobileMana : Packet + { + public MobileMana( Mobile m ) : base( 0xA2, 9 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) m.ManaMax ); + m_Stream.Write( (short) m.Mana ); + } + } + + public sealed class MobileManaN : Packet + { + public MobileManaN( Mobile m ) : base( 0xA2, 9 ) + { + m_Stream.Write( (int) m.Serial ); + AttributeNormalizer.Write( m_Stream, m.Mana, m.ManaMax ); + } + } + + public sealed class MobileStam : Packet + { + public MobileStam( Mobile m ) : base( 0xA3, 9 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) m.StamMax ); + m_Stream.Write( (short) m.Stam ); + } + } + + public sealed class MobileStamN : Packet + { + public MobileStamN( Mobile m ) : base( 0xA3, 9 ) + { + m_Stream.Write( (int) m.Serial ); + AttributeNormalizer.Write( m_Stream, m.Stam, m.StamMax ); + } + } + + public sealed class MobileAttributes : Packet + { + public MobileAttributes( Mobile m ) : base( 0x2D, 17 ) + { + m_Stream.Write( m.Serial ); + + m_Stream.Write( (short) m.HitsMax ); + m_Stream.Write( (short) m.Hits ); + + m_Stream.Write( (short) m.ManaMax ); + m_Stream.Write( (short) m.Mana ); + + m_Stream.Write( (short) m.StamMax ); + m_Stream.Write( (short) m.Stam ); + } + } + + public sealed class MobileAttributesN : Packet + { + public MobileAttributesN( Mobile m ) : base( 0x2D, 17 ) + { + m_Stream.Write( m.Serial ); + + AttributeNormalizer.Write( m_Stream, m.Hits, m.HitsMax ); + AttributeNormalizer.Write( m_Stream, m.Mana, m.ManaMax ); + AttributeNormalizer.Write( m_Stream, m.Stam, m.StamMax ); + } + } + + public sealed class PathfindMessage : Packet + { + public PathfindMessage( IPoint3D p ) : base( 0x38, 7 ) + { + m_Stream.Write( (short) p.X ); + m_Stream.Write( (short) p.Y ); + m_Stream.Write( (short) p.Z ); + } + } + + // unsure of proper format, client crashes + public sealed class MobileName : Packet + { + public MobileName( Mobile m ) : base( 0x98 ) + { + string name = m.Name; + + if ( name == null ) name = ""; + + this.EnsureCapacity( 37 ); + + m_Stream.Write( (int) m.Serial ); + m_Stream.WriteAsciiFixed( name, 30 ); + } + } + + public sealed class MobileAnimation : Packet + { + public MobileAnimation( Mobile m, int action, int frameCount, int repeatCount, bool forward, bool repeat, int delay ) : base( 0x6E, 14 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) action ); + m_Stream.Write( (short) frameCount ); + m_Stream.Write( (short) repeatCount ); + m_Stream.Write( (bool) !forward ); // protocol has really "reverse" but I find this more intuitive + m_Stream.Write( (bool) repeat ); + m_Stream.Write( (byte) delay ); + } + } + + public sealed class MobileStatusCompact : Packet + { + public MobileStatusCompact( bool canBeRenamed, Mobile m ) : base( 0x11 ) + { + string name = m.Name; + if ( name == null ) name = ""; + + this.EnsureCapacity( 43 ); + + m_Stream.Write( (int) m.Serial ); + m_Stream.WriteAsciiFixed( name, 30 ); + + AttributeNormalizer.WriteReverse( m_Stream, m.Hits, m.HitsMax ); + + m_Stream.Write( canBeRenamed ); + + m_Stream.Write( (byte) 0 ); // type + } + } + + public sealed class MobileStatusExtended : Packet + { + public MobileStatusExtended( Mobile m ) : base( 0x11 ) + { + string name = m.Name; + if ( name == null ) name = ""; + + bool sendMaxWeight = (Core.ML && m.NetState != null && m.NetState.SupportsExpansion( Expansion.ML )); + + + this.EnsureCapacity( sendMaxWeight ? 91 :88 ); + + m_Stream.Write( (int) m.Serial ); + m_Stream.WriteAsciiFixed( name, 30 ); + + m_Stream.Write( (short) m.Hits ); + m_Stream.Write( (short) m.HitsMax ); + + m_Stream.Write( m.CanBeRenamedBy( m ) ); + + m_Stream.Write( (byte)(sendMaxWeight ? 0x05 : Core.AOS ? 0x04 : 0x03) ); // type + + m_Stream.Write( m.Female ); + + m_Stream.Write( (short) m.Str ); + m_Stream.Write( (short) m.Dex ); + m_Stream.Write( (short) m.Int ); + + m_Stream.Write( (short) m.Stam ); + m_Stream.Write( (short) m.StamMax ); + + m_Stream.Write( (short) m.Mana ); + m_Stream.Write( (short) m.ManaMax ); + + m_Stream.Write( (int) m.TotalGold ); + m_Stream.Write( (short) (Core.AOS ? m.PhysicalResistance : (int)(m.ArmorRating + 0.5)) ); + m_Stream.Write( (short) (Mobile.BodyWeight + m.TotalWeight) ); + + if( sendMaxWeight ) + { + m_Stream.Write( (short)m.MaxWeight ); + m_Stream.Write( (byte)(m.Race.RaceID + 1)); // Would be 0x00 if it's a non-ML enabled account but... + } + + m_Stream.Write( (short) m.StatCap ); + + m_Stream.Write( (byte) m.Followers ); + m_Stream.Write( (byte) m.FollowersMax ); + + if ( Core.AOS ) + { + m_Stream.Write( (short) m.FireResistance ); // Fire + m_Stream.Write( (short) m.ColdResistance ); // Cold + m_Stream.Write( (short) m.PoisonResistance ); // Poison + m_Stream.Write( (short) m.EnergyResistance ); // Energy + m_Stream.Write( (short) m.Luck ); // Luck + + IWeapon weapon = m.Weapon; + + int min = 0, max = 0; + + if ( weapon != null ) + weapon.GetStatusDamage( m, out min, out max ); + + m_Stream.Write( (short) min ); // Damage min + m_Stream.Write( (short) max ); // Damage max + + m_Stream.Write( (int) m.TithingPoints ); + } + } + } + + public sealed class MobileStatus : Packet + { + public MobileStatus( Mobile beholder, Mobile beheld ) : base( 0x11 ) + { + string name = beheld.Name; + if ( name == null ) name = ""; + + bool sendMaxWeight = (Core.ML && beheld.NetState != null && beheld.NetState.SupportsExpansion( Expansion.ML )); + + this.EnsureCapacity( 43 + (beholder == beheld ? (sendMaxWeight ? 48 : 45) : 0) ); + + m_Stream.Write( beheld.Serial ); + + m_Stream.WriteAsciiFixed( name, 30 ); + + if ( beholder == beheld ) + WriteAttr( beheld.Hits, beheld.HitsMax ); + else + WriteAttrNorm( beheld.Hits, beheld.HitsMax ); + + m_Stream.Write( beheld.CanBeRenamedBy( beholder ) ); + + if ( beholder == beheld ) + { + m_Stream.Write( (byte)(sendMaxWeight ? 0x05 : Core.AOS ? 0x04 : 0x03) ); // type + + m_Stream.Write( beheld.Female ); + + m_Stream.Write( (short) beheld.Str ); + m_Stream.Write( (short) beheld.Dex ); + m_Stream.Write( (short) beheld.Int ); + + WriteAttr( beheld.Stam, beheld.StamMax ); + WriteAttr( beheld.Mana, beheld.ManaMax ); + + m_Stream.Write( (int) beheld.TotalGold ); + m_Stream.Write( (short) (Core.AOS ? beheld.PhysicalResistance : (int)(beheld.ArmorRating + 0.5)) ); + m_Stream.Write( (short) (Mobile.BodyWeight + beheld.TotalWeight) ); + + if( sendMaxWeight ) + { + m_Stream.Write( (short)beheld.MaxWeight ); + m_Stream.Write( (byte)(beheld.Race.RaceID + 1) ); // Would be 0x00 if it's a non-ML enabled account but... + } + + m_Stream.Write( (short) beheld.StatCap ); + + m_Stream.Write( (byte) beheld.Followers ); + m_Stream.Write( (byte) beheld.FollowersMax ); + + if ( Core.AOS ) + { + m_Stream.Write( (short) beheld.FireResistance ); // Fire + m_Stream.Write( (short) beheld.ColdResistance ); // Cold + m_Stream.Write( (short) beheld.PoisonResistance ); // Poison + m_Stream.Write( (short) beheld.EnergyResistance ); // Energy + m_Stream.Write( (short) beheld.Luck ); // Luck + + IWeapon weapon = beheld.Weapon; + + int min = 0, max = 0; + + if ( weapon != null ) + weapon.GetStatusDamage( beheld, out min, out max ); + + m_Stream.Write( (short) min ); // Damage min + m_Stream.Write( (short) max ); // Damage max + + m_Stream.Write( (int) beheld.TithingPoints ); + } + } + else + { + m_Stream.Write( (byte) 0x00 ); + } + } + + private void WriteAttr( int current, int maximum ) + { + m_Stream.Write( (short) current ); + m_Stream.Write( (short) maximum ); + } + + private void WriteAttrNorm( int current, int maximum ) + { + AttributeNormalizer.WriteReverse( m_Stream, current, maximum ); + } + } + + public sealed class MobileUpdate : Packet + { + public MobileUpdate( Mobile m ) : base( 0x20, 19 ) + { + int hue = m.Hue; + + if ( m.SolidHueOverride >= 0 ) + hue = m.SolidHueOverride; + + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (short) m.Body ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (byte) m.GetPacketFlags() ); + m_Stream.Write( (short) m.X ); + m_Stream.Write( (short) m.Y ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (byte) m.Direction ); + m_Stream.Write( (sbyte) m.Z ); + } + } + + public sealed class MobileIncoming : Packet + { + private static int[] m_DupedLayers = new int[256]; + private static int m_Version; + + public Mobile m_Beheld; + + public MobileIncoming( Mobile beholder, Mobile beheld ) : base( 0x78 ) + { + m_Beheld = beheld; + ++m_Version; + + List eq = beheld.Items; + int count = eq.Count; + + if( beheld.HairItemID > 0 ) + count++; + if( beheld.FacialHairItemID > 0 ) + count++; + + this.EnsureCapacity( 23 + (count * 9) ); + + int hue = beheld.Hue; + + if ( beheld.SolidHueOverride >= 0 ) + hue = beheld.SolidHueOverride; + + m_Stream.Write( (int) beheld.Serial ); + m_Stream.Write( (short) beheld.Body ); + m_Stream.Write( (short) beheld.X ); + m_Stream.Write( (short) beheld.Y ); + m_Stream.Write( (sbyte) beheld.Z ); + m_Stream.Write( (byte) beheld.Direction ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (byte) beheld.GetPacketFlags() ); + m_Stream.Write( (byte) Notoriety.Compute( beholder, beheld ) ); + + for ( int i = 0; i < eq.Count; ++i ) + { + Item item = eq[i]; + + byte layer = (byte) item.Layer; + + if ( !item.Deleted && beholder.CanSee( item ) && m_DupedLayers[layer] != m_Version ) + { + m_DupedLayers[layer] = m_Version; + + hue = item.Hue; + + if ( beheld.SolidHueOverride >= 0 ) + hue = beheld.SolidHueOverride; + + int itemID = item.ItemID & 0x3FFF; + bool writeHue = ( hue != 0 ); + + if ( writeHue ) + itemID |= 0x8000; + + m_Stream.Write( (int) item.Serial ); + m_Stream.Write( (short) itemID ); + m_Stream.Write( (byte) layer ); + + if ( writeHue ) + m_Stream.Write( (short) hue ); + } + } + + if( beheld.HairItemID > 0 ) + { + if( m_DupedLayers[(int)Layer.Hair] != m_Version ) + { + m_DupedLayers[(int)Layer.Hair] = m_Version; + hue = beheld.HairHue; + + if( beheld.SolidHueOverride >= 0 ) + hue = beheld.SolidHueOverride; + + int itemID = beheld.HairItemID & 0x3FFF; + + bool writeHue = (hue != 0); + + if( writeHue ) + itemID |= 0x8000; + + m_Stream.Write( (int)HairInfo.FakeSerial( beheld ) ); + m_Stream.Write( (short)itemID ); + m_Stream.Write( (byte)Layer.Hair ); + + if( writeHue ) + m_Stream.Write( (short)hue ); + } + } + + if( beheld.FacialHairItemID > 0 ) + { + if( m_DupedLayers[(int)Layer.FacialHair] != m_Version ) + { + m_DupedLayers[(int)Layer.FacialHair] = m_Version; + hue = beheld.FacialHairHue; + + if( beheld.SolidHueOverride >= 0 ) + hue = beheld.SolidHueOverride; + + int itemID = beheld.FacialHairItemID & 0x3FFF; + + bool writeHue = (hue != 0); + + if( writeHue ) + itemID |= 0x8000; + + m_Stream.Write( (int)FacialHairInfo.FakeSerial( beheld ) ); + m_Stream.Write( (short)itemID ); + m_Stream.Write( (byte)Layer.FacialHair ); + + if( writeHue ) + m_Stream.Write( (short)hue ); + } + } + + m_Stream.Write( (int) 0 ); // terminate + } + } + + public sealed class AsciiMessage : Packet + { + public AsciiMessage( Serial serial, int graphic, MessageType type, int hue, int font, string name, string text ) : base( 0x1C ) + { + if ( name == null ) + name = ""; + + if ( text == null ) + text = ""; + + if ( hue == 0 ) + hue = 0x3B2; + + this.EnsureCapacity( 45 + text.Length ); + + m_Stream.Write( (int) serial ); + m_Stream.Write( (short) graphic ); + m_Stream.Write( (byte) type ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (short) font ); + m_Stream.WriteAsciiFixed( name, 30 ); + m_Stream.WriteAsciiNull( text ); + } + } + + public sealed class UnicodeMessage : Packet + { + public UnicodeMessage( Serial serial, int graphic, MessageType type, int hue, int font, string lang, string name, string text ) : base( 0xAE ) + { + if ( lang == null || lang == "" ) lang = "ENU"; + if ( name == null ) name = ""; + if ( text == null ) text = ""; + + if ( hue == 0 ) + hue = 0x3B2; + + this.EnsureCapacity( 50 + (text.Length * 2) ); + + m_Stream.Write( (int) serial ); + m_Stream.Write( (short) graphic ); + m_Stream.Write( (byte) type ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (short) font ); + m_Stream.WriteAsciiFixed( lang, 4 ); + m_Stream.WriteAsciiFixed( name, 30 ); + m_Stream.WriteBigUniNull( text ); + } + } + + public sealed class PingAck : Packet + { + private static PingAck[] m_Cache = new PingAck[0x100]; + + public static PingAck Instantiate( byte ping ) + { + PingAck p = m_Cache[ping]; + + if ( p == null ) + { + m_Cache[ping] = p = new PingAck( ping ); + p.SetStatic(); + } + + return p; + } + + public PingAck( byte ping ) : base( 0x73, 2 ) + { + m_Stream.Write( ping ); + } + } + + public sealed class MovementRej : Packet + { + public MovementRej( int seq, Mobile m ) : base( 0x21, 8 ) + { + m_Stream.Write( (byte) seq ); + m_Stream.Write( (short) m.X ); + m_Stream.Write( (short) m.Y ); + m_Stream.Write( (byte) m.Direction ); + m_Stream.Write( (sbyte) m.Z ); + } + } + + public sealed class MovementAck : Packet + { + private static MovementAck[][] m_Cache = new MovementAck[8][] + { + new MovementAck[256], + new MovementAck[256], + new MovementAck[256], + new MovementAck[256], + new MovementAck[256], + new MovementAck[256], + new MovementAck[256], + new MovementAck[256] + }; + + public static MovementAck Instantiate( int seq, Mobile m ) + { + int noto = Notoriety.Compute( m, m ); + + MovementAck p = m_Cache[noto][seq]; + + if ( p == null ) + { + m_Cache[noto][seq] = p = new MovementAck( seq, noto ); + p.SetStatic(); + } + + return p; + } + + private MovementAck( int seq, int noto ) : base( 0x22, 3 ) + { + m_Stream.Write( (byte) seq ); + m_Stream.Write( (byte) noto ); + } + } + + public sealed class LoginConfirm : Packet + { + public LoginConfirm( Mobile m ) : base( 0x1B, 37 ) + { + m_Stream.Write( (int) m.Serial ); + m_Stream.Write( (int) 0 ); + m_Stream.Write( (short) m.Body ); + m_Stream.Write( (short) m.X ); + m_Stream.Write( (short) m.Y ); + m_Stream.Write( (short) m.Z ); + m_Stream.Write( (byte) m.Direction ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (int) -1 ); + + Map map = m.Map; + + if ( map == null || map == Map.Internal ) + map = m.LogoutMap; + + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) 0 ); + m_Stream.Write( (short) (map==null?6144:map.Width) ); + m_Stream.Write( (short) (map==null?4096:map.Height) ); + + m_Stream.Fill(); + } + } + + public sealed class LoginComplete : Packet + { + public static readonly Packet Instance = Packet.SetStatic( new LoginComplete() ); + + public LoginComplete() : base( 0x55, 1 ) + { + } + } + + public sealed class CityInfo + { + private string m_City; + private string m_Building; + private Point3D m_Location; + private Map m_Map; + + public CityInfo( string city, string building, int x, int y, int z, Map m ) + { + m_City = city; + m_Building = building; + m_Location = new Point3D( x, y, z ); + m_Map = m; + } + + public CityInfo( string city, string building, int x, int y, int z ) : this( city, building, x, y, z, Map.Trammel ) + { + } + + public string City + { + get + { + return m_City; + } + set + { + m_City = value; + } + } + + public string Building + { + get + { + return m_Building; + } + set + { + m_Building = value; + } + } + + public int X + { + get + { + return m_Location.X; + } + set + { + m_Location.X = value; + } + } + + public int Y + { + get + { + return m_Location.Y; + } + set + { + m_Location.Y = value; + } + } + + public int Z + { + get + { + return m_Location.Z; + } + set + { + m_Location.Z = value; + } + } + + public Point3D Location + { + get + { + return m_Location; + } + set + { + m_Location = value; + } + } + + public Map Map + { + get{ return m_Map; } + set{ m_Map = value; } + } + } + + public sealed class CharacterListUpdate : Packet + { + public CharacterListUpdate( IAccount a ) : base( 0x86 ) + { + this.EnsureCapacity( 4 + (a.Length * 60) ); + + int highSlot = -1; + + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] != null ) + highSlot = i; + } + + int count = Math.Max( Math.Max( highSlot + 1, a.Limit ), 5 ); + + m_Stream.Write( (byte) count ); + + for ( int i = 0; i < count; ++i ) + { + Mobile m = a[i]; + + if ( m != null ) + { + m_Stream.WriteAsciiFixed( m.Name, 30 ); + m_Stream.Fill( 30 ); // password + } + else + { + m_Stream.Fill( 60 ); + } + } + } + } + + public sealed class CharacterList : Packet + { + public CharacterList( IAccount a, CityInfo[] info ) : base( 0xA9 ) + { + this.EnsureCapacity( 9 + (a.Length * 60) + (info.Length * 63) ); + + int highSlot = -1; + + for ( int i = 0; i < a.Length; ++i ) + { + if ( a[i] != null ) + highSlot = i; + } + + int count = Math.Max( Math.Max( highSlot + 1, a.Limit ), 5 ); + + m_Stream.Write( (byte) count ); + + for ( int i = 0; i < count; ++i ) + { + if ( a[i] != null ) + { + m_Stream.WriteAsciiFixed( a[i].Name, 30 ); + m_Stream.Fill( 30 ); // password + } + else + { + m_Stream.Fill( 60 ); + } + } + + m_Stream.Write( (byte) info.Length ); + + for ( int i = 0; i < info.Length; ++i ) + { + CityInfo ci = info[i]; + + m_Stream.Write( (byte) i ); + m_Stream.WriteAsciiFixed( ci.City, 31 ); + m_Stream.WriteAsciiFixed( ci.Building, 31 ); + } +/* + int flags = 0x08; //Context Menus + + if ( Core.SE ) + flags |= 0xA0; //SE & AOS + else if ( Core.AOS ) + flags |= 0x20; //AOS + * */ + + int flags = ExpansionInfo.CurrentExpansion.CharacterListFlags; + + if ( count >= 6 ) + flags |= 0x40; // 6th character slot + else if ( a.Limit == 1 ) + flags |= 0x14; // Limit characters & one character + + m_Stream.Write( (int)(flags | m_AdditionalFlags) ); // flags + } + + private static int m_AdditionalFlags; + + public static int AdditionalFlags + { + get{ return m_AdditionalFlags; } + set{ m_AdditionalFlags = value; } + } + } + + public class ClearWeaponAbility : Packet + { + public static readonly Packet Instance = Packet.SetStatic( new ClearWeaponAbility() ); + + public ClearWeaponAbility() : base( 0xBF ) + { + EnsureCapacity( 5 ); + + m_Stream.Write( (short) 0x21 ); + } + } + + public enum ALRReason : byte + { + Invalid = 0x00, + InUse = 0x01, + Blocked = 0x02, + BadPass = 0x03, + Idle = 0xFE, + BadComm = 0xFF + } + + public sealed class AccountLoginRej : Packet + { + public AccountLoginRej( ALRReason reason ) : base( 0x82, 2 ) + { + m_Stream.Write( (byte)reason ); + } + } + + public enum AffixType : byte + { + Append = 0x00, + Prepend = 0x01, + System = 0x02 + } + + public sealed class MessageLocalizedAffix : Packet + { + public MessageLocalizedAffix( Serial serial, int graphic, MessageType messageType, int hue, int font, int number, string name, AffixType affixType, string affix, string args ) : base( 0xCC ) + { + if ( name == null ) name = ""; + if ( affix == null ) affix = ""; + if ( args == null ) args = ""; + + if ( hue == 0 ) + hue = 0x3B2; + + this.EnsureCapacity( 52 + affix.Length + (args.Length * 2) ); + + m_Stream.Write( (int) serial ); + m_Stream.Write( (short) graphic ); + m_Stream.Write( (byte) messageType ); + m_Stream.Write( (short) hue ); + m_Stream.Write( (short) font ); + m_Stream.Write( (int) number ); + m_Stream.Write( (byte) affixType ); + m_Stream.WriteAsciiFixed( name, 30 ); + m_Stream.WriteAsciiNull( affix ); + m_Stream.WriteBigUniNull( args ); + } + } + + public sealed class ServerInfo + { + private string m_Name; + private int m_FullPercent; + private int m_TimeZone; + private IPEndPoint m_Address; + + public string Name + { + get + { + return m_Name; + } + set + { + m_Name = value; + } + } + + public int FullPercent + { + get + { + return m_FullPercent; + } + set + { + m_FullPercent = value; + } + } + + public int TimeZone + { + get + { + return m_TimeZone; + } + set + { + m_TimeZone = value; + } + } + + public IPEndPoint Address + { + get + { + return m_Address; + } + set + { + m_Address = value; + } + } + + public ServerInfo( string name, int fullPercent, TimeZone tz, IPEndPoint address ) + { + m_Name = name; + m_FullPercent = fullPercent; + m_TimeZone = tz.GetUtcOffset( DateTime.Now ).Hours; + m_Address = address; + } + } + + public sealed class FollowMessage : Packet + { + public FollowMessage( Serial serial1, Serial serial2 ) : base( 0x15, 9 ) + { + m_Stream.Write( (int) serial1 ); + m_Stream.Write( (int) serial2 ); + } + } + + public sealed class AccountLoginAck : Packet + { + public AccountLoginAck( ServerInfo[] info ) : base( 0xA8 ) + { + this.EnsureCapacity( 6 + (info.Length * 40) ); + + m_Stream.Write( (byte) 0x5D ); // Unknown + + m_Stream.Write( (ushort) info.Length ); + + for ( int i = 0; i < info.Length; ++i ) + { + ServerInfo si = info[i]; + + m_Stream.Write( (ushort) i ); + m_Stream.WriteAsciiFixed( si.Name, 32 ); + m_Stream.Write( (byte) si.FullPercent ); + m_Stream.Write( (sbyte) si.TimeZone ); + m_Stream.Write( (int) Utility.GetAddressValue( si.Address.Address ) ); + } + } + } + + public sealed class DisplaySignGump : Packet + { + public DisplaySignGump( Serial serial, int gumpID, string unknown, string caption ) : base( 0x8B ) + { + if ( unknown == null ) unknown = ""; + if ( caption == null ) caption = ""; + + this.EnsureCapacity( 16 + unknown.Length + caption.Length ); + + m_Stream.Write( (int) serial ); + m_Stream.Write( (short) gumpID ); + m_Stream.Write( (short) (unknown.Length) ); + m_Stream.WriteAsciiFixed( unknown, unknown.Length ); + m_Stream.Write( (short) (caption.Length + 1) ); + m_Stream.WriteAsciiFixed( caption, caption.Length + 1 ); + } + } + + public sealed class GodModeReply : Packet + { + public GodModeReply( bool reply ) : base( 0x2B, 2 ) + { + m_Stream.Write( reply ); + } + } + + public sealed class PlayServerAck : Packet + { + internal static int m_AuthID = -1; + + public PlayServerAck( ServerInfo si ) : base( 0x8C, 11 ) + { + int addr = Utility.GetAddressValue( si.Address.Address ); + + m_Stream.Write( (byte) addr ); + m_Stream.Write( (byte)(addr >> 8) ); + m_Stream.Write( (byte)(addr >> 16) ); + m_Stream.Write( (byte)(addr >> 24) ); + + m_Stream.Write( (short) si.Address.Port ); + m_Stream.Write( (int) m_AuthID ); + } + } + + public abstract class Packet + { + [Flags] + private enum State + { + Inactive = 0x00, + Static = 0x01, + Acquired = 0x02, + Accessed = 0x04, + Buffered = 0x08, + Warned = 0x10 + } + + protected PacketWriter m_Stream; + private int m_PacketID; + private int m_Length; + private State m_State; + + public int PacketID + { + get{ return m_PacketID; } + } + + public Packet( int packetID ) + { + m_PacketID = packetID; + + PacketProfile prof = PacketProfile.GetOutgoingProfile( (byte)packetID ); + + if ( prof != null ) + prof.RegConstruct(); + } + + public void EnsureCapacity( int length ) + { + m_Stream = PacketWriter.CreateInstance( length );// new PacketWriter( length ); + m_Stream.Write( (byte) m_PacketID ); + m_Stream.Write( (short) 0 ); + } + + public Packet( int packetID, int length ) + { + m_PacketID = packetID; + m_Length = length; + + m_Stream = PacketWriter.CreateInstance( length );// new PacketWriter( length ); + m_Stream.Write( (byte) packetID ); + + PacketProfile prof = PacketProfile.GetOutgoingProfile( (byte)packetID ); + + if ( prof != null ) + prof.RegConstruct(); + } + + public PacketWriter UnderlyingStream + { + get + { + return m_Stream; + } + } + + private const int BufferSize = 4096; + private static BufferPool m_Buffers = new BufferPool( "Compressed", 16, BufferSize ); + + public static Packet SetStatic( Packet p ) + { + p.SetStatic(); + return p; + } + + public static Packet Acquire( Packet p ) + { + p.Acquire(); + return p; + } + + public static void Release( ref ObjectPropertyList p ) + { + if ( p != null ) + p.Release(); + + p = null; + } + + public static void Release( ref RemoveItem p ) + { + if ( p != null ) + p.Release(); + + p = null; + } + + public static void Release( ref RemoveMobile p ) + { + if ( p != null ) + p.Release(); + + p = null; + } + + public static void Release( ref OPLInfo p ) + { + if ( p != null ) + p.Release(); + + p = null; + } + + public static void Release( ref Packet p ) + { + if ( p != null ) + p.Release(); + + p = null; + } + + public static void Release( Packet p ) + { + if ( p != null ) + p.Release(); + } + + public void SetStatic() + { + m_State |= State.Static | State.Acquired; + } + + public void Acquire() + { + m_State |= State.Acquired; + } + + public void OnSend() + { + if ( (m_State & (State.Acquired | State.Static)) == 0 ) + Free(); + } + + private void Free() + { + if ( m_CompiledBuffer == null ) + return; + + if ( (m_State & State.Buffered) != 0 ) + m_Buffers.ReleaseBuffer( m_CompiledBuffer ); + + m_State &= ~(State.Static | State.Acquired | State.Buffered); + + m_CompiledBuffer = null; + } + + public void Release() + { + if ( (m_State & State.Acquired) != 0 ) + Free(); + } + + private byte[] m_CompiledBuffer; + private int m_CompiledLength; + + public byte[] Compile( bool compress, out int length ) + { + if ( m_CompiledBuffer == null ) + { + if ( (m_State & State.Accessed) == 0 ) + { + m_State |= State.Accessed; + } + else + { + if ( (m_State & State.Warned) == 0 ) + { + m_State |= State.Warned; + + try + { + using ( StreamWriter op = new StreamWriter( "net_opt.log", true ) ) + { + op.WriteLine( "Redundant compile for packet {0}, use Acquire() and Release()", this.GetType() ); + op.WriteLine( new System.Diagnostics.StackTrace() ); + } + } + catch + { + } + } + + m_CompiledBuffer = new byte[0]; + m_CompiledLength = 0; + + length = m_CompiledLength; + return m_CompiledBuffer; + } + + InternalCompile( compress ); + } + + length = m_CompiledLength; + return m_CompiledBuffer; + } + + private void InternalCompile( bool compress ) + { + if ( m_Length == 0 ) + { + long streamLen = m_Stream.Length; + + m_Stream.Seek( 1, SeekOrigin.Begin ); + m_Stream.Write( (ushort) streamLen ); + } + else if ( m_Stream.Length != m_Length ) + { + int diff = (int)m_Stream.Length - m_Length; + + Console.WriteLine( "Packet: 0x{0:X2}: Bad packet length! ({1}{2} bytes)", m_PacketID, diff >= 0 ? "+" : "", diff ); + } + + MemoryStream ms = m_Stream.UnderlyingStream; + + m_CompiledBuffer = ms.GetBuffer(); + int length = (int)ms.Length; + + if ( compress ) + { + Compression.Compress( m_CompiledBuffer, length, out m_CompiledBuffer, out length ); + + if ( m_CompiledBuffer == null ) + Console.WriteLine( "Warning: Compression buffer overflowed on packet 0x{0:X2} ('{1}') (length={2})", m_PacketID, GetType().Name, length ); + } + + if ( m_CompiledBuffer != null ) + { + m_CompiledLength = length; + + byte[] old = m_CompiledBuffer; + + if ( length > BufferSize || (m_State & State.Static) != 0 ) + { + m_CompiledBuffer = new byte[length]; + } + else + { + m_CompiledBuffer = m_Buffers.AcquireBuffer(); + m_State |= State.Buffered; + } + + Buffer.BlockCopy( old, 0, m_CompiledBuffer, 0, length ); + } + + PacketWriter.ReleaseInstance( m_Stream ); + m_Stream = null; + } + } +} diff --git a/Server/Network/SendQueue.cs b/Server/Network/SendQueue.cs new file mode 100644 index 000000000..2bd892fb3 --- /dev/null +++ b/Server/Network/SendQueue.cs @@ -0,0 +1,223 @@ +/*************************************************************************** + * SendQueue.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: SendQueue.cs 43 2006-01-20 05:39:57Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; + +namespace Server.Network +{ + public enum SendEnqueueResult + { + Begin, + Delay, + Overflow + } + + public class SendQueue + { + private class Entry + { + public byte[] m_Buffer; + public int m_Length; + + private Entry( byte[] buffer, int length ) + { + m_Buffer = buffer; + m_Length = length; + } + + private static Stack m_Pool = new Stack(); + + public static Entry Pool( byte[] buffer, int length ) + { + lock ( m_Pool ) + { + if ( m_Pool.Count == 0 ) + return new Entry( buffer, length ); + + Entry e = m_Pool.Pop(); + + e.m_Buffer = buffer; + e.m_Length = length; + + return e; + } + } + + public static void Release( Entry e ) + { + lock ( m_Pool ) + { + m_Pool.Push( e ); + ReleaseBuffer( e.m_Buffer ); + } + } + } + + private static int m_CoalesceBufferSize = 512; + private static BufferPool m_UnusedBuffers = new BufferPool( "Coalesced", 2048, m_CoalesceBufferSize ); + + public static int CoalesceBufferSize + { + get{ return m_CoalesceBufferSize; } + set + { + if ( m_CoalesceBufferSize == value ) + return; + + if ( m_UnusedBuffers != null ) + m_UnusedBuffers.Free(); + + m_CoalesceBufferSize = value; + m_UnusedBuffers = new BufferPool( "Coalesced", 2048, m_CoalesceBufferSize ); + } + } + + public static byte[] GetUnusedBuffer() + { + return m_UnusedBuffers.AcquireBuffer(); + } + + public static void ReleaseBuffer( byte[] buffer ) + { + if ( buffer == null ) + Console.WriteLine( "Warning: Attempting to release null packet buffer" ); + else if ( buffer.Length == m_CoalesceBufferSize ) + m_UnusedBuffers.ReleaseBuffer( buffer ); + } + + private Queue m_Queue; + + private Entry m_Buffered; + + public bool IsFlushReady{ get{ return ( m_Queue.Count == 0 && m_Buffered != null ); } } + public bool IsEmpty{ get{ return ( m_Queue.Count == 0 && m_Buffered == null ); } } + + public void Clear() + { + if ( m_Buffered != null ) + { + Entry.Release( m_Buffered ); + m_Buffered = null; + } + + while ( m_Queue.Count > 0 ) + Entry.Release( m_Queue.Dequeue() ); + } + + public byte[] CheckFlushReady( ref int length ) + { + Entry buffered = m_Buffered; + + if ( m_Queue.Count == 0 && buffered != null ) + { + m_Buffered = null; + + m_Queue.Enqueue( buffered ); + length = buffered.m_Length; + return buffered.m_Buffer; + } + + return null; + } + + public SendQueue() + { + m_Queue = new Queue(); + } + + public byte[] Peek( ref int length ) + { + if ( m_Queue.Count > 0 ) + { + Entry entry = m_Queue.Peek(); + + length = entry.m_Length; + return entry.m_Buffer; + } + + return null; + } + + public byte[] Dequeue( ref int length ) + { + Entry.Release( m_Queue.Dequeue() ); + + if ( m_Queue.Count > 0 ) + { + Entry entry = m_Queue.Peek(); + + length = entry.m_Length; + return entry.m_Buffer; + } + + return null; + } + + private const int PendingCap = 96*1024; + + public SendEnqueueResult Enqueue( byte[] buffer, int length ) + { + if ( buffer == null ) + { + Console.WriteLine( "Warning: Attempting to send null packet buffer" ); + return SendEnqueueResult.Delay; + } + + int existingBytes = ( m_Queue.Count * m_CoalesceBufferSize ) + ( m_Buffered == null ? 0 : m_Buffered.m_Length ); + + if ( (existingBytes + length) > PendingCap ) + return SendEnqueueResult.Overflow; + + int offset = 0; // offset into buffer + int remaining = length; // byte count remaining + + bool startNow = false; // should we start sending the first chunk? + + while ( remaining > 0 ) + { + if ( m_Buffered == null ) // nothing yet buffered + m_Buffered = Entry.Pool( GetUnusedBuffer(), 0 ); + + byte[] page = m_Buffered.m_Buffer; // buffer page + int pageSpace = page.Length - m_Buffered.m_Length; // available bytes in page + int byteCount = ( remaining > pageSpace ? pageSpace : remaining ); // how many we can copy over + + Buffer.BlockCopy( buffer, offset, page, m_Buffered.m_Length, byteCount ); // copy the data + + // apply offsets + m_Buffered.m_Length += byteCount; + offset += byteCount; + remaining -= byteCount; + + if ( m_Buffered.m_Length == page.Length ) // page full + { + startNow = ( startNow || m_Queue.Count == 0 ); + m_Queue.Enqueue( m_Buffered ); + m_Buffered = null; + } + } + + return ( startNow ? SendEnqueueResult.Begin : SendEnqueueResult.Delay ); + } + } +} \ No newline at end of file diff --git a/Server/Notoriety.cs b/Server/Notoriety.cs new file mode 100644 index 000000000..d7502a11b --- /dev/null +++ b/Server/Notoriety.cs @@ -0,0 +1,80 @@ +/*************************************************************************** + * Notoriety.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Notoriety.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using Server; +using Server.Guilds; +using Server.Items; + +namespace Server +{ + public delegate int NotorietyHandler( Mobile source, Mobile target ); + + public class Notoriety + { + public const int Innocent = 1; + public const int Ally = 2; + public const int CanBeAttacked = 3; + public const int Criminal = 4; + public const int Enemy = 5; + public const int Murderer = 6; + public const int Invulnerable = 7; + + private static NotorietyHandler m_Handler; + + public static NotorietyHandler Handler + { + get{ return m_Handler; } + set{ m_Handler = value; } + } + + private static int[] m_Hues = new int[] + { + 0x000, + 0x059, + 0x03F, + 0x3B2, + 0x3B2, + 0x090, + 0x022, + 0x035 + }; + + public static int[] Hues + { + get{ return m_Hues; } + set{ m_Hues = value; } + } + + public static int GetHue( int noto ) + { + if ( noto < 0 || noto >= m_Hues.Length ) + return 0; + + return m_Hues[noto]; + } + + public static int Compute( Mobile source, Mobile target ) + { + return m_Handler == null ? CanBeAttacked : m_Handler( source, target ); + } + } +} \ No newline at end of file diff --git a/Server/ObjectPropertyList.cs b/Server/ObjectPropertyList.cs new file mode 100644 index 000000000..41b1a73b6 --- /dev/null +++ b/Server/ObjectPropertyList.cs @@ -0,0 +1,198 @@ +/*************************************************************************** + * ObjectPropertyList.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ObjectPropertyList.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Text; +using Server; +using Server.Network; + +namespace Server +{ + public class ObjectPropertyList : Packet + { + private IEntity m_Entity; + private int m_Hash; + private int m_Header; + private int m_Strings; + private string m_HeaderArgs; + + public IEntity Entity{ get{ return m_Entity; } } + public int Hash{ get{ return 0x40000000 + m_Hash; } } + + public int Header{ get{ return m_Header; } set{ m_Header = value; } } + public string HeaderArgs{ get{ return m_HeaderArgs; } set{ m_HeaderArgs = value; } } + + private static bool m_Enabled = false; + + public static bool Enabled{ get{ return m_Enabled; } set{ m_Enabled = value; } } + + public ObjectPropertyList( IEntity e ) : base( 0xD6 ) + { + EnsureCapacity( 128 ); + + m_Entity = e; + + m_Stream.Write( (short) 1 ); + m_Stream.Write( (int) e.Serial ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (byte) 0 ); + m_Stream.Write( (int) e.Serial ); + } + + public void Add( int number ) + { + if ( number == 0 ) + return; + + AddHash( number ); + + if ( m_Header == 0 ) + { + m_Header = number; + m_HeaderArgs = ""; + } + + m_Stream.Write( number ); + m_Stream.Write( (short) 0 ); + } + + public void Terminate() + { + m_Stream.Write( (int) 0 ); + + m_Stream.Seek( 11, System.IO.SeekOrigin.Begin ); + m_Stream.Write( (int) m_Hash ); + } + + private static byte[] m_Buffer = new byte[1024]; + private static Encoding m_Encoding = Encoding.Unicode; + + public void AddHash( int val ) + { + m_Hash ^= (val & 0x3FFFFFF); + m_Hash ^= (val >> 26) & 0x3F; + } + + public void Add( int number, string arguments ) + { + if ( number == 0 ) + return; + + if ( arguments == null ) + arguments = ""; + + if ( m_Header == 0 ) + { + m_Header = number; + m_HeaderArgs = arguments; + } + + AddHash( number ); + AddHash( arguments.GetHashCode() ); + + m_Stream.Write( number ); + + int byteCount = m_Encoding.GetByteCount( arguments ); + + if ( byteCount > m_Buffer.Length ) + m_Buffer = new byte[byteCount]; + + byteCount = m_Encoding.GetBytes( arguments, 0, arguments.Length, m_Buffer, 0 ); + + m_Stream.Write( (short) byteCount ); + m_Stream.Write( m_Buffer, 0, byteCount ); + } + + public void Add( int number, string format, object arg0 ) + { + Add( number, String.Format( format, arg0 ) ); + } + + public void Add( int number, string format, object arg0, object arg1 ) + { + Add( number, String.Format( format, arg0, arg1 ) ); + } + + public void Add( int number, string format, object arg0, object arg1, object arg2 ) + { + Add( number, String.Format( format, arg0, arg1, arg2 ) ); + } + + public void Add( int number, string format, params object[] args ) + { + Add( number, String.Format( format, args ) ); + } + + // Each of these are localized to "~1_NOTHING~" which allows the string argument to be used + private static int[] m_StringNumbers = new int[] + { + 1042971, + 1070722 + }; + + private int GetStringNumber() + { + return m_StringNumbers[m_Strings++ % m_StringNumbers.Length]; + } + + public void Add( string text ) + { + Add( GetStringNumber(), text ); + } + + public void Add( string format, string arg0 ) + { + Add( GetStringNumber(), String.Format( format, arg0 ) ); + } + + public void Add( string format, string arg0, string arg1 ) + { + Add( GetStringNumber(), String.Format( format, arg0, arg1 ) ); + } + + public void Add( string format, string arg0, string arg1, string arg2 ) + { + Add( GetStringNumber(), String.Format( format, arg0, arg1, arg2 ) ); + } + + public void Add( string format, params object[] args ) + { + Add( GetStringNumber(), String.Format( format, args ) ); + } + } + + public class OPLInfo : Packet + { + /*public OPLInfo( ObjectPropertyList list ) : base( 0xBF ) + { + EnsureCapacity( 13 ); + + m_Stream.Write( (short) 0x10 ); + m_Stream.Write( (int) list.Entity.Serial ); + m_Stream.Write( (int) list.Hash ); + }*/ + + public OPLInfo( ObjectPropertyList list ) : base( 0xDC, 9 ) + { + m_Stream.Write( (int) list.Entity.Serial ); + m_Stream.Write( (int) list.Hash ); + } + } +} \ No newline at end of file diff --git a/Server/Party.cs b/Server/Party.cs new file mode 100644 index 000000000..a290f6bfc --- /dev/null +++ b/Server/Party.cs @@ -0,0 +1,39 @@ +/*************************************************************************** + * Party.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Party.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public abstract class PartyCommands + { + private static PartyCommands m_Handler; + + public static PartyCommands Handler{ get{ return m_Handler; } set{ m_Handler = value; } } + + public abstract void OnAdd( Mobile from ); + public abstract void OnRemove( Mobile from, Mobile target ); + public abstract void OnPrivateMessage( Mobile from, Mobile target, string text ); + public abstract void OnPublicMessage( Mobile from, string text ); + public abstract void OnSetCanLoot( Mobile from, bool canLoot ); + public abstract void OnAccept( Mobile from, Mobile leader ); + public abstract void OnDecline( Mobile from, Mobile leader ); + } +} \ No newline at end of file diff --git a/Server/Persistence/BinaryMemoryWriter.cs b/Server/Persistence/BinaryMemoryWriter.cs new file mode 100644 index 000000000..e19471844 --- /dev/null +++ b/Server/Persistence/BinaryMemoryWriter.cs @@ -0,0 +1,86 @@ +/*************************************************************************** + * BinaryMemoryWriter.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; + +namespace Server { + public class BinaryMemoryWriter : BinaryFileWriter { + private MemoryStream stream; + + protected override int BufferSize { + get { return 512; } + } + + public BinaryMemoryWriter() + : base( new MemoryStream( 512 ), true ) { + this.stream = this.UnderlyingStream as MemoryStream; + } + + private static byte[] indexBuffer; + + public int CommitTo( SequentialFileWriter dataFile, SequentialFileWriter indexFile, int typeCode, int serial ) { + Flush(); + + byte[] buffer = stream.GetBuffer(); + int length = ( int ) stream.Length; + + long position = dataFile.Position; + + dataFile.Write( buffer, 0, length ); + + if ( indexBuffer == null ) { + indexBuffer = new byte[20]; + } + + indexBuffer[0] = ( byte ) ( typeCode ); + indexBuffer[1] = ( byte ) ( typeCode >> 8 ); + indexBuffer[2] = ( byte ) ( typeCode >> 16 ); + indexBuffer[3] = ( byte ) ( typeCode >> 24 ); + + indexBuffer[4] = ( byte ) ( serial ); + indexBuffer[5] = ( byte ) ( serial >> 8 ); + indexBuffer[6] = ( byte ) ( serial >> 16 ); + indexBuffer[7] = ( byte ) ( serial >> 24 ); + + indexBuffer[8] = ( byte ) ( position ); + indexBuffer[9] = ( byte ) ( position >> 8 ); + indexBuffer[10] = ( byte ) ( position >> 16 ); + indexBuffer[11] = ( byte ) ( position >> 24 ); + indexBuffer[12] = ( byte ) ( position >> 32 ); + indexBuffer[13] = ( byte ) ( position >> 40 ); + indexBuffer[14] = ( byte ) ( position >> 48 ); + indexBuffer[15] = ( byte ) ( position >> 56 ); + + indexBuffer[16] = ( byte ) ( length ); + indexBuffer[17] = ( byte ) ( length >> 8 ); + indexBuffer[18] = ( byte ) ( length >> 16 ); + indexBuffer[19] = ( byte ) ( length >> 24 ); + + indexFile.Write( indexBuffer, 0, indexBuffer.Length ); + + stream.SetLength( 0 ); + + return length; + } + } +} \ No newline at end of file diff --git a/Server/Persistence/DualSaveStrategy.cs b/Server/Persistence/DualSaveStrategy.cs new file mode 100644 index 000000000..82504089e --- /dev/null +++ b/Server/Persistence/DualSaveStrategy.cs @@ -0,0 +1,54 @@ +/*************************************************************************** + * DualSaveStrategy.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Threading; +using System.Diagnostics; + +using Server; +using Server.Guilds; + +namespace Server { + public sealed class DualSaveStrategy : StandardSaveStrategy { + public override string Name { + get { return "Dual"; } + } + + public DualSaveStrategy() { + } + + public override void Save( SaveMetrics metrics ) { + Thread saveThread = new Thread( delegate() { + SaveItems( metrics ); + } ); + + saveThread.Name = "Item Save Subset"; + saveThread.Start(); + + SaveMobiles( metrics ); + SaveGuilds( metrics ); + + saveThread.Join(); + } + } +} \ No newline at end of file diff --git a/Server/Persistence/FileOperations.cs b/Server/Persistence/FileOperations.cs new file mode 100644 index 000000000..8dbd36c6e --- /dev/null +++ b/Server/Persistence/FileOperations.cs @@ -0,0 +1,141 @@ +/*************************************************************************** + * FileOperations.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + + +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; + +#if !MONO +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; +#endif + +namespace Server { + public static class FileOperations { + public const int KB = 1024; + public const int MB = 1024 * KB; + +#if !MONO + private const FileOptions NoBuffering = ( FileOptions ) 0x20000000; + + [DllImport( "Kernel32", CharSet = CharSet.Auto, SetLastError = true )] + private static extern SafeFileHandle CreateFile( string lpFileName, int dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile ); +#endif + + private static int bufferSize = 1 * MB; + private static int concurrency = 1; + + private static bool unbuffered = true; + + public static int BufferSize { + get { + return bufferSize; + } + set { + bufferSize = value; + } + } + + public static int Concurrency { + get { + return concurrency; + } + set { + concurrency = value; + } + } + + public static bool Unbuffered { + get { + return unbuffered; + } + set { + unbuffered = value; + } + } + + public static bool AreSynchronous { + get { + return ( concurrency < 1 ); + } + } + + public static bool AreAsynchronous { + get { + return ( concurrency > 0 ); + } + } + + public static FileStream OpenSequentialStream( string path, FileMode mode, FileAccess access, FileShare share ) { + FileOptions options = FileOptions.SequentialScan; + + if ( concurrency > 0 ) { + options |= FileOptions.Asynchronous; + } + +#if MONO + return new FileStream( path, mode, access, share, bufferSize, options ); +#else + if ( unbuffered ) { + options |= NoBuffering; + } else { + return new FileStream( path, mode, access, share, bufferSize, options ); + } + + SafeFileHandle fileHandle = CreateFile( path, (int) access, share, IntPtr.Zero, mode, (int) options, IntPtr.Zero ); + + if ( fileHandle.IsInvalid ) { + throw new IOException(); + } + + return new UnbufferedFileStream( fileHandle, access, bufferSize, ( concurrency > 0 ) ); +#endif + } + +#if !MONO + private class UnbufferedFileStream : FileStream { + private SafeFileHandle fileHandle; + + public UnbufferedFileStream( SafeFileHandle fileHandle, FileAccess access, int bufferSize, bool isAsync ) + : base( fileHandle, access, bufferSize, isAsync ) { + this.fileHandle = fileHandle; + } + + public override void Write( byte[] array, int offset, int count ) { + base.Write( array, offset, bufferSize ); + } + + public override IAsyncResult BeginWrite( byte[] array, int offset, int numBytes, AsyncCallback userCallback, object stateObject ) { + return base.BeginWrite( array, offset, bufferSize, userCallback, stateObject ); + } + + protected override void Dispose( bool disposing ) { + if ( !fileHandle.IsClosed ) { + fileHandle.Close(); + } + + base.Dispose( disposing ); + } + } +#endif + } +} diff --git a/Server/Persistence/FileQueue.cs b/Server/Persistence/FileQueue.cs new file mode 100644 index 000000000..fb914d9a9 --- /dev/null +++ b/Server/Persistence/FileQueue.cs @@ -0,0 +1,251 @@ +/*************************************************************************** + * FileQueue.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: SendQueue.cs 43 2006-01-20 05:39:57Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +using Server; +using Server.Network; + +namespace Server { + public delegate void FileCommitCallback( FileQueue.Chunk chunk ); + + public sealed class FileQueue : IDisposable { + public sealed class Chunk { + private FileQueue owner; + private int slot; + + private byte[] buffer; + private int offset; + private int size; + + public byte[] Buffer { + get { + return buffer; + } + } + + public int Offset { + get { + return 0; + } + } + + public int Size { + get { + return size; + } + } + + public Chunk( FileQueue owner, int slot, byte[] buffer, int offset, int size ) { + this.owner = owner; + this.slot = slot; + + this.buffer = buffer; + this.offset = offset; + this.size = size; + } + + public void Commit() { + owner.Commit( this, this.slot ); + } + } + + private struct Page { + public byte[] buffer; + public int length; + } + + private static int bufferSize; + private static BufferPool bufferPool; + + static FileQueue() { + bufferSize = FileOperations.BufferSize; + bufferPool = new BufferPool( "File Buffers", 64, bufferSize ); + } + + private object syncRoot; + + private Chunk[] active; + private int activeCount; + + private Queue pending; + private Page buffered; + + private FileCommitCallback callback; + + private ManualResetEvent idle; + + private long position; + + public long Position { + get { + return position; + } + } + + public FileQueue( int concurrentWrites, FileCommitCallback callback ) { + if ( concurrentWrites < 1 ) { + throw new ArgumentOutOfRangeException( "concurrentWrites" ); + } else if ( bufferSize < 1 ) { + throw new ArgumentOutOfRangeException( "bufferSize" ); + } else if ( callback == null ) { + throw new ArgumentNullException( "callback" ); + } + + this.syncRoot = new object(); + + this.active = new Chunk[concurrentWrites]; + this.pending = new Queue(); + + this.callback = callback; + + this.idle = new ManualResetEvent( true ); + } + + private void Append( Page page ) { + lock ( syncRoot ) { + if ( activeCount == 0 ) { + idle.Reset(); + } + + ++activeCount; + + for ( int slot = 0; slot < active.Length; ++slot ) { + if ( active[slot] == null ) { + active[slot] = new Chunk( this, slot, page.buffer, 0, page.length ); + + callback( active[slot] ); + + return; + } + } + + pending.Enqueue( page ); + } + } + + public void Dispose() { + if ( idle != null ) { + idle.Close(); + idle = null; + } + } + + public void Flush() { + if ( buffered.buffer != null ) { + Append( buffered ); + + buffered.buffer = null; + buffered.length = 0; + } + + /*lock ( syncRoot ) { + if ( pending.Count > 0 ) { + idle.Reset(); + } + + for ( int slot = 0; slot < active.Length && pending.Count > 0; ++slot ) { + if ( active[slot] == null ) { + Page page = pending.Dequeue(); + + active[slot] = new Chunk( this, slot, page.buffer, 0, page.length ); + + ++activeCount; + + callback( active[slot] ); + } + } + }*/ + + idle.WaitOne(); + } + + private void Commit( Chunk chunk, int slot ) { + if ( slot < 0 || slot >= active.Length ) { + throw new ArgumentOutOfRangeException( "slot" ); + } + + lock ( syncRoot ) { + if ( active[slot] != chunk ) { + throw new ArgumentException(); + } + + bufferPool.ReleaseBuffer( chunk.Buffer ); + + if ( pending.Count > 0 ) { + Page page = pending.Dequeue(); + + active[slot] = new Chunk( this, slot, page.buffer, 0, page.length ); + + callback( active[slot] ); + } else { + active[slot] = null; + } + + --activeCount; + + if ( activeCount == 0 ) { + idle.Set(); + } + } + } + + public void Enqueue( byte[] buffer, int offset, int size ) { + if ( buffer == null ) { + throw new ArgumentNullException( "buffer" ); + } else if ( offset < 0 ) { + throw new ArgumentOutOfRangeException( "offset" ); + } else if ( size < 0 ) { + throw new ArgumentOutOfRangeException( "size" ); + } else if ( ( buffer.Length - offset ) < size ) { + throw new ArgumentException(); + } + + position += size; + + while ( size > 0 ) { + if ( buffered.buffer == null ) { // nothing yet buffered + buffered.buffer = bufferPool.AcquireBuffer(); + } + + byte[] page = buffered.buffer; // buffer page + int pageSpace = page.Length - buffered.length; // available bytes in page + int byteCount = ( size > pageSpace ? pageSpace : size ); // how many bytes we can copy over + + Buffer.BlockCopy( buffer, offset, page, buffered.length, byteCount ); + + buffered.length += byteCount; + offset += byteCount; + size -= byteCount; + + if ( buffered.length == page.Length ) { // page full + Append( buffered ); + + buffered.buffer = null; + buffered.length = 0; + } + } + } + } +} \ No newline at end of file diff --git a/Server/Persistence/ParallelSaveStrategy.cs b/Server/Persistence/ParallelSaveStrategy.cs new file mode 100644 index 000000000..11cb821d2 --- /dev/null +++ b/Server/Persistence/ParallelSaveStrategy.cs @@ -0,0 +1,342 @@ +/*************************************************************************** + * ParallelSaveStrategy.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Threading; +using System.Diagnostics; + +using Server; +using Server.Guilds; + +namespace Server { + public sealed class ParallelSaveStrategy : SaveStrategy { + public override string Name { + get { return "Parallel"; } + } + + private int processorCount; + + public ParallelSaveStrategy( int processorCount ) { + this.processorCount = processorCount; + } + + private int GetThreadCount() { + return processorCount - 1; + } + + private SaveMetrics metrics; + + private SequentialFileWriter itemData, itemIndex; + private SequentialFileWriter mobileData, mobileIndex; + private SequentialFileWriter guildData, guildIndex; + + private List decaying; + + private Consumer[] consumers; + private int cycle; + + private bool finished; + + public override void Save( SaveMetrics metrics ) { + this.metrics = metrics; + + OpenFiles(); + + consumers = new Consumer[GetThreadCount()]; + + for ( int i = 0; i < consumers.Length; ++i ) { + consumers[i] = new Consumer( this, 256 ); + } + + IEnumerable collection = new Producer(); + + foreach ( ISerializable value in collection ) { + while ( !Enqueue( value ) ) { + if ( !Commit() ) { + Thread.Sleep( 0 ); + } + } + } + + finished = true; + + SaveTypeDatabases(); + + WaitHandle.WaitAll( + Array.ConvertAll( + consumers, + delegate( Consumer input ) { + return input.completionEvent; + } + ) + ); + + Commit(); + + CloseFiles(); + + if ( decaying != null ) { + foreach ( Item item in decaying ) { + if ( item.OnDecay() ) { + item.Delete(); + } + } + } + } + + private void SaveTypeDatabases() { + SaveTypeDatabase( World.ItemTypesPath, World.m_ItemTypes ); + SaveTypeDatabase( World.MobileTypesPath, World.m_MobileTypes ); + } + + private void SaveTypeDatabase( string path, List types ) { + BinaryFileWriter bfw = new BinaryFileWriter( path, false ); + + bfw.Write( types.Count ); + + foreach ( Type type in types ) { + bfw.Write( type.FullName ); + } + + bfw.Flush(); + + bfw.Close(); + } + + private void OpenFiles() { + itemData = new SequentialFileWriter( World.ItemDataPath, metrics ); + itemIndex = new SequentialFileWriter( World.ItemIndexPath, metrics ); + + mobileData = new SequentialFileWriter( World.MobileDataPath, metrics ); + mobileIndex = new SequentialFileWriter( World.MobileIndexPath, metrics ); + + guildData = new SequentialFileWriter( World.GuildDataPath, metrics ); + guildIndex = new SequentialFileWriter( World.GuildIndexPath, metrics ); + + WriteCount( itemIndex, World.Items.Count ); + WriteCount( mobileIndex, World.Mobiles.Count ); + WriteCount( guildIndex, BaseGuild.List.Count ); + } + + private void WriteCount( SequentialFileWriter indexFile, int count ) { + byte[] buffer = new byte[4]; + + buffer[0] = ( byte ) ( count ); + buffer[1] = ( byte ) ( count >> 8 ); + buffer[2] = ( byte ) ( count >> 16 ); + buffer[3] = ( byte ) ( count >> 24 ); + + indexFile.Write( buffer, 0, buffer.Length ); + } + + private void CloseFiles() { + itemData.Close(); + itemIndex.Close(); + + mobileData.Close(); + mobileIndex.Close(); + + guildData.Close(); + guildIndex.Close(); + } + + private void OnSerialized( ConsumableEntry entry ) { + ISerializable value = entry.value; + BinaryMemoryWriter writer = entry.writer; + + Item item = value as Item; + + if ( item != null ) { + Save( item, writer ); + } else { + Mobile mob = value as Mobile; + + if ( mob != null ) { + Save( mob, writer ); + } else { + BaseGuild guild = value as BaseGuild; + + if ( guild != null ) { + Save( guild, writer ); + } + } + } + } + + private void Save( Item item, BinaryMemoryWriter writer ) { + int length = writer.CommitTo( itemData, itemIndex, item.m_TypeRef, item.Serial ); + + if ( metrics != null ) { + metrics.OnItemSaved( length ); + } + + if ( item.Decays && item.Parent == null && item.Map != Map.Internal && DateTime.Now > ( item.LastMoved + item.DecayTime ) ) { + if ( decaying == null ) { + decaying = new List(); + } + + decaying.Add( item ); + } + } + + private void Save( Mobile mob, BinaryMemoryWriter writer ) { + int length = writer.CommitTo( mobileData, mobileIndex, mob.m_TypeRef, mob.Serial ); + + if ( metrics != null ) { + metrics.OnItemSaved( length ); + } + } + + private void Save( BaseGuild guild, BinaryMemoryWriter writer ) { + int length = writer.CommitTo( guildData, guildIndex, 0, guild.Id ); + + if ( metrics != null ) { + metrics.OnGuildSaved( length ); + } + } + + private bool Enqueue( ISerializable value ) { + for ( int i = 0; i < consumers.Length; ++i ) { + Consumer consumer = consumers[cycle++ % consumers.Length]; + + if ( ( consumer.tail - consumer.head ) < consumer.buffer.Length ) { + consumer.buffer[consumer.tail % consumer.buffer.Length].value = value; + consumer.tail++; + + return true; + } + } + + return false; + } + + private bool Commit() { + bool committed = false; + + for ( int i = 0; i < consumers.Length; ++i ) { + Consumer consumer = consumers[i]; + + while ( consumer.head < consumer.done ) { + OnSerialized( consumer.buffer[consumer.head % consumer.buffer.Length] ); + consumer.head++; + + committed = true; + } + } + + return committed; + } + + private sealed class Producer : IEnumerable { + private IEnumerable items; + private IEnumerable mobiles; + private IEnumerable guilds; + + public Producer() { + items = World.Items.Values; + mobiles = World.Mobiles.Values; + guilds = BaseGuild.List.Values; + } + + public IEnumerator GetEnumerator() { + foreach ( Item item in items ) { + yield return item; + } + + foreach ( Mobile mob in mobiles ) { + yield return mob; + } + + foreach ( BaseGuild guild in guilds ) { + yield return guild; + } + } + + IEnumerator IEnumerable.GetEnumerator() { + throw new NotImplementedException(); + } + } + + private struct ConsumableEntry { + public ISerializable value; + public BinaryMemoryWriter writer; + } + + private sealed class Consumer { + private ParallelSaveStrategy owner; + + public ManualResetEvent completionEvent; + + public ConsumableEntry[] buffer; + public int head, done, tail; + + private Thread thread; + + public Consumer( ParallelSaveStrategy owner, int bufferSize ) { + this.owner = owner; + + this.buffer = new ConsumableEntry[bufferSize]; + + for ( int i = 0; i < this.buffer.Length; ++i ) { + this.buffer[i].writer = new BinaryMemoryWriter(); + } + + this.completionEvent = new ManualResetEvent( false ); + + thread = new Thread( Processor ); + + thread.Name = "Parallel Serialization Thread"; + + thread.Start(); + } + + private void Processor() { + try { + while ( !owner.finished ) { + Process(); + Thread.Sleep( 0 ); + } + + Process(); + + completionEvent.Set(); + } catch ( Exception ex ) { + Console.WriteLine( ex ); + } + } + + private void Process() { + ConsumableEntry entry; + + while ( done < tail ) { + entry = buffer[done % buffer.Length]; + + entry.value.Serialize( entry.writer ); + + ++done; + } + } + } + } +} \ No newline at end of file diff --git a/Server/Persistence/SaveMetrics.cs b/Server/Persistence/SaveMetrics.cs new file mode 100644 index 000000000..9f5d8035f --- /dev/null +++ b/Server/Persistence/SaveMetrics.cs @@ -0,0 +1,129 @@ +/*************************************************************************** + * SaveMetrics.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; + +namespace Server { + public sealed class SaveMetrics : IDisposable { + private const string PerformanceCategoryName = "RunUO 2.0"; + private const string PerformanceCategoryDesc = "Performance counters for RunUO 2.0."; + + private PerformanceCounter numberOfWorldSaves; + + private PerformanceCounter itemsPerSecond; + private PerformanceCounter mobilesPerSecond; + + private PerformanceCounter serializedBytesPerSecond; + private PerformanceCounter writtenBytesPerSecond; + + public SaveMetrics() { + if ( !PerformanceCounterCategory.Exists( PerformanceCategoryName ) ) { + CounterCreationDataCollection counters = new CounterCreationDataCollection(); + + counters.Add( new CounterCreationData( + "Save - Count", + "Number of world saves.", + PerformanceCounterType.NumberOfItems32 + ) + ); + + counters.Add( new CounterCreationData( + "Save - Items/sec", + "Number of items saved per second.", + PerformanceCounterType.RateOfCountsPerSecond32 + ) + ); + + counters.Add( new CounterCreationData( + "Save - Mobiles/sec", + "Number of mobiles saved per second.", + PerformanceCounterType.RateOfCountsPerSecond32 + ) + ); + + counters.Add( new CounterCreationData( + "Save - Serialized bytes/sec", + "Amount of world-save bytes serialized per second.", + PerformanceCounterType.RateOfCountsPerSecond32 + ) + ); + + counters.Add( new CounterCreationData( + "Save - Written bytes/sec", + "Amount of world-save bytes written to disk per second.", + PerformanceCounterType.RateOfCountsPerSecond32 + ) + ); + + PerformanceCounterCategory.Create( PerformanceCategoryName, PerformanceCategoryDesc, PerformanceCounterCategoryType.SingleInstance, counters ); + } + + numberOfWorldSaves = new PerformanceCounter( PerformanceCategoryName, "Save - Count", false ); + + itemsPerSecond = new PerformanceCounter( PerformanceCategoryName, "Save - Items/sec", false ); + mobilesPerSecond = new PerformanceCounter( PerformanceCategoryName, "Save - Mobiles/sec", false ); + + serializedBytesPerSecond = new PerformanceCounter( PerformanceCategoryName, "Save - Serialized bytes/sec", false ); + writtenBytesPerSecond = new PerformanceCounter( PerformanceCategoryName, "Save - Written bytes/sec", false ); + + // increment number of world saves + numberOfWorldSaves.Increment(); + } + + public void OnItemSaved( int numberOfBytes ) { + itemsPerSecond.Increment(); + + serializedBytesPerSecond.IncrementBy( numberOfBytes ); + } + + public void OnMobileSaved( int numberOfBytes ) { + mobilesPerSecond.Increment(); + + serializedBytesPerSecond.IncrementBy( numberOfBytes ); + } + + public void OnGuildSaved( int numberOfBytes ) { + serializedBytesPerSecond.IncrementBy( numberOfBytes ); + } + + public void OnFileWritten( int numberOfBytes ) { + writtenBytesPerSecond.IncrementBy( numberOfBytes ); + } + + private bool isDisposed; + + public void Dispose() { + if ( !isDisposed ) { + isDisposed = true; + + numberOfWorldSaves.Dispose(); + + itemsPerSecond.Dispose(); + mobilesPerSecond.Dispose(); + + serializedBytesPerSecond.Dispose(); + writtenBytesPerSecond.Dispose(); + } + } + } +} diff --git a/Server/Persistence/SaveStrategy.cs b/Server/Persistence/SaveStrategy.cs new file mode 100644 index 000000000..c2fa304c1 --- /dev/null +++ b/Server/Persistence/SaveStrategy.cs @@ -0,0 +1,50 @@ +/*************************************************************************** + * SaveStrategy.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Threading; +using System.Diagnostics; + +using Server; +using Server.Guilds; + +namespace Server { + public abstract class SaveStrategy { + public static SaveStrategy Acquire() { + if ( Core.MultiProcessor ) { + int processorCount = Core.ProcessorCount; + + if ( processorCount > 8 ) { + return new ParallelSaveStrategy( processorCount ); + } else { + return new DualSaveStrategy(); + } + } else { + return new StandardSaveStrategy(); + } + } + + public abstract string Name { get; } + public abstract void Save( SaveMetrics metrics ); + } +} \ No newline at end of file diff --git a/Server/Persistence/SequentialFileWriter.cs b/Server/Persistence/SequentialFileWriter.cs new file mode 100644 index 000000000..cf3f14e00 --- /dev/null +++ b/Server/Persistence/SequentialFileWriter.cs @@ -0,0 +1,141 @@ +/*************************************************************************** + * SequentialFileWriter.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Server { + public sealed class SequentialFileWriter : Stream { + private FileStream fileStream; + private FileQueue fileQueue; + + private AsyncCallback writeCallback; + + private SaveMetrics metrics; + + public SequentialFileWriter( string path, SaveMetrics metrics ) { + if ( path == null ) { + throw new ArgumentNullException( "path" ); + } + + this.metrics = metrics; + + this.fileStream = FileOperations.OpenSequentialStream( path, FileMode.Create, FileAccess.Write, FileShare.None ); + + fileQueue = new FileQueue( + Math.Max( 1, FileOperations.Concurrency ), + FileCallback + ); + } + + public override long Position { + get { + return fileQueue.Position; + } + set { + throw new InvalidOperationException(); + } + } + + private void FileCallback( FileQueue.Chunk chunk ) { + if ( FileOperations.AreSynchronous ) { + fileStream.Write( chunk.Buffer, chunk.Offset, chunk.Size ); + + if ( metrics != null ) { + metrics.OnFileWritten( chunk.Size ); + } + + chunk.Commit(); + } else { + if ( writeCallback == null ) { + writeCallback = this.OnWrite; + } + + fileStream.BeginWrite( chunk.Buffer, chunk.Offset, chunk.Size, writeCallback, chunk ); + } + } + + private void OnWrite( IAsyncResult asyncResult ) { + FileQueue.Chunk chunk = asyncResult.AsyncState as FileQueue.Chunk; + + fileStream.EndWrite( asyncResult ); + + if ( metrics != null ) { + metrics.OnFileWritten( chunk.Size ); + } + + chunk.Commit(); + } + + public override void Write( byte[] buffer, int offset, int size ) { + fileQueue.Enqueue( buffer, offset, size ); + } + + public override void Flush() { + fileQueue.Flush(); + fileStream.Flush(); + } + + protected override void Dispose( bool disposing ) { + if ( fileStream != null ) { + Flush(); + + fileQueue.Dispose(); + fileQueue = null; + + fileStream.Close(); + fileStream = null; + } + + base.Dispose( disposing ); + } + + public override bool CanRead { + get { return false; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return true; } + } + + public override long Length { + get { return this.Position; } + } + + public override int Read( byte[] buffer, int offset, int count ) { + throw new InvalidOperationException(); + } + + public override long Seek( long offset, SeekOrigin origin ) { + throw new InvalidOperationException(); + } + + public override void SetLength( long value ) { + fileStream.SetLength( value ); + } + } +} \ No newline at end of file diff --git a/Server/Persistence/StandardSaveStrategy.cs b/Server/Persistence/StandardSaveStrategy.cs new file mode 100644 index 000000000..97dbffb59 --- /dev/null +++ b/Server/Persistence/StandardSaveStrategy.cs @@ -0,0 +1,181 @@ +/*************************************************************************** + * StandardSaveStrategy.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 130 2006-04-11 01:06:32Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Threading; +using System.Diagnostics; + +using Server; +using Server.Guilds; + +namespace Server { + public class StandardSaveStrategy : SaveStrategy { + public override string Name { + get { return "Standard"; } + } + + public StandardSaveStrategy() { + } + + public override void Save( SaveMetrics metrics ) { + SaveMobiles( metrics ); + SaveItems( metrics ); + SaveGuilds( metrics ); + } + + protected void SaveMobiles( SaveMetrics metrics ) { + Dictionary mobiles = World.Mobiles; + + GenericWriter idx; + GenericWriter tdb; + GenericWriter bin; + + if ( World.SaveType == World.SaveOption.Normal ) { + idx = new BinaryFileWriter( World.MobileIndexPath, false ); + tdb = new BinaryFileWriter( World.MobileTypesPath, false ); + bin = new BinaryFileWriter( World.MobileDataPath, true ); + } else { + idx = new AsyncWriter( World.MobileIndexPath, false ); + tdb = new AsyncWriter( World.MobileTypesPath, false ); + bin = new AsyncWriter( World.MobileDataPath, true ); + } + + idx.Write( ( int ) mobiles.Count ); + foreach ( Mobile m in mobiles.Values ) { + long start = bin.Position; + + idx.Write( ( int ) m.m_TypeRef ); + idx.Write( ( int ) m.Serial ); + idx.Write( ( long ) start ); + + m.Serialize( bin ); + + if ( metrics != null ) { + metrics.OnMobileSaved( ( int ) ( bin.Position - start ) ); + } + + idx.Write( ( int ) ( bin.Position - start ) ); + + m.FreeCache(); + } + + tdb.Write( ( int ) World.m_MobileTypes.Count ); + + for ( int i = 0; i < World.m_MobileTypes.Count; ++i ) + tdb.Write( World.m_MobileTypes[i].FullName ); + + idx.Close(); + tdb.Close(); + bin.Close(); + } + + protected void SaveItems( SaveMetrics metrics ) { + Dictionary items = World.Items; + List decaying = new List(); + + GenericWriter idx; + GenericWriter tdb; + GenericWriter bin; + + if ( World.SaveType == World.SaveOption.Normal ) { + idx = new BinaryFileWriter( World.ItemIndexPath, false ); + tdb = new BinaryFileWriter( World.ItemTypesPath, false ); + bin = new BinaryFileWriter( World.ItemDataPath, true ); + } else { + idx = new AsyncWriter( World.ItemIndexPath, false ); + tdb = new AsyncWriter( World.ItemTypesPath, false ); + bin = new AsyncWriter( World.ItemDataPath, true ); + } + + idx.Write( ( int ) items.Count ); + foreach ( Item item in items.Values ) { + if ( item.Decays && item.Parent == null && item.Map != Map.Internal && ( item.LastMoved + item.DecayTime ) <= DateTime.Now ) + decaying.Add( item ); + + long start = bin.Position; + + idx.Write( ( int ) item.m_TypeRef ); + idx.Write( ( int ) item.Serial ); + idx.Write( ( long ) start ); + + item.Serialize( bin ); + + if ( metrics != null ) { + metrics.OnItemSaved( ( int ) ( bin.Position - start ) ); + } + + idx.Write( ( int ) ( bin.Position - start ) ); + + item.FreeCache(); + } + + tdb.Write( ( int ) World.m_ItemTypes.Count ); + for ( int i = 0; i < World.m_ItemTypes.Count; ++i ) + tdb.Write( World.m_ItemTypes[i].FullName ); + + idx.Close(); + tdb.Close(); + bin.Close(); + + for ( int i = 0; i < decaying.Count; ++i ) { + Item item = decaying[i]; + + if ( item.OnDecay() ) + item.Delete(); + } + } + + protected void SaveGuilds( SaveMetrics metrics ) { + GenericWriter idx; + GenericWriter bin; + + if ( World.SaveType == World.SaveOption.Normal ) { + idx = new BinaryFileWriter( World.GuildIndexPath, false ); + bin = new BinaryFileWriter( World.GuildDataPath, true ); + } else { + idx = new AsyncWriter( World.GuildIndexPath, false ); + bin = new AsyncWriter( World.GuildDataPath, true ); + } + + idx.Write( ( int ) BaseGuild.List.Count ); + foreach ( BaseGuild guild in BaseGuild.List.Values ) { + long start = bin.Position; + + idx.Write( ( int ) 0 );//guilds have no typeid + idx.Write( ( int ) guild.Id ); + idx.Write( ( long ) start ); + + guild.Serialize( bin ); + + if ( metrics != null ) { + metrics.OnGuildSaved( ( int ) ( bin.Position - start ) ); + } + + idx.Write( ( int ) ( bin.Position - start ) ); + } + + idx.Close(); + bin.Close(); + } + } +} \ No newline at end of file diff --git a/Server/Point3DList.cs b/Server/Point3DList.cs new file mode 100644 index 000000000..feb49f894 --- /dev/null +++ b/Server/Point3DList.cs @@ -0,0 +1,113 @@ +/*************************************************************************** + * Point3DList.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Point3DList.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public class Point3DList + { + private Point3D[] m_List; + private int m_Count; + + public Point3DList() + { + m_List = new Point3D[8]; + m_Count = 0; + } + + public int Count + { + get + { + return m_Count; + } + } + + public void Clear() + { + m_Count = 0; + } + + public Point3D Last + { + get{ return m_List[m_Count - 1]; } + } + + public Point3D this[int index] + { + get + { + return m_List[index]; + } + } + + public void Add( int x, int y, int z ) + { + if ( (m_Count + 1) > m_List.Length ) + { + Point3D[] old = m_List; + m_List = new Point3D[old.Length * 2]; + + for ( int i = 0; i < old.Length; ++i ) + m_List[i] = old[i]; + } + + m_List[m_Count].m_X = x; + m_List[m_Count].m_Y = y; + m_List[m_Count].m_Z = z; + ++m_Count; + } + + public void Add( Point3D p ) + { + if ( (m_Count + 1) > m_List.Length ) + { + Point3D[] old = m_List; + m_List = new Point3D[old.Length * 2]; + + for ( int i = 0; i < old.Length; ++i ) + m_List[i] = old[i]; + } + + m_List[m_Count].m_X = p.m_X; + m_List[m_Count].m_Y = p.m_Y; + m_List[m_Count].m_Z = p.m_Z; + ++m_Count; + } + + private static Point3D[] m_EmptyList = new Point3D[0]; + + public Point3D[] ToArray() + { + if ( m_Count == 0 ) + return m_EmptyList; + + Point3D[] list = new Point3D[m_Count]; + + for ( int i = 0; i < m_Count; ++i ) + list[i] = m_List[i]; + + m_Count = 0; + + return list; + } + } +} \ No newline at end of file diff --git a/Server/Poison.cs b/Server/Poison.cs new file mode 100644 index 000000000..bc3e7c38b --- /dev/null +++ b/Server/Poison.cs @@ -0,0 +1,146 @@ +/*************************************************************************** + * Poison.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Poison.cs 102 2006-02-04 01:52:29Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; + +namespace Server +{ + [Parsable] + public abstract class Poison + { + /*public abstract TimeSpan Interval{ get; } + public abstract TimeSpan Duration{ get; }*/ + public abstract string Name{ get; } + public abstract int Level{ get; } + public abstract Timer ConstructTimer( Mobile m ); + /*public abstract void OnDamage( Mobile m, ref object state );*/ + + public override string ToString() + { + return this.Name; + } + + + private static ArrayList m_Poisons = new ArrayList(); + + public static void Register( Poison reg ) + { + string regName = reg.Name.ToLower(); + + for(int i=0;i= 0 && index < m_Races.Length && m_Races[index] != null ) + return m_Races[index]; + } + + throw new Exception( "Invalid race name" ); + } + + private static void CheckNamesAndValues() + { + if( m_RaceNames != null && m_RaceNames.Length == m_AllRaces.Count ) + return; + + m_RaceNames = new string[m_AllRaces.Count]; + m_RaceValues = new Race[m_AllRaces.Count]; + + for( int i = 0; i < m_AllRaces.Count; ++i ) + { + Race race = (Race)m_AllRaces[i]; + + m_RaceNames[i] = race.Name; + m_RaceValues[i] = race; + } + } + + public override string ToString() + { + return m_Name; + } + + private int m_MaleBody, m_FemaleBody, m_MaleGhostBody, m_FemaleGhostBody; + + private Expansion m_RequiredExpansion; + + public Expansion RequiredExpasnion { get { return m_RequiredExpansion; } } + + public int MaleBody { get { return m_MaleBody; } } + public int MaleGhostBody { get { return m_MaleGhostBody; } } + + public int FemaleBody { get { return m_FemaleBody; } } + public int FemaleGhostBody { get { return m_FemaleGhostBody; } } + + public Race( int raceID, int raceIndex, string name, string pluralName, int maleBody, int femaleBody, int maleGhostBody, int femaleGhostBody, Expansion requiredExpansion ) + { + m_RaceID = raceID; + m_RaceIndex = raceIndex; + + m_Name = name; + + m_MaleBody = maleBody; + m_FemaleBody = femaleBody; + m_MaleGhostBody = maleGhostBody; + m_FemaleGhostBody = femaleGhostBody; + + m_RequiredExpansion = requiredExpansion; + m_PluralName = pluralName; + } + + public virtual bool ValidateHair( Mobile m, int itemID ) { return ValidateHair( m.Female, itemID ); } + public abstract bool ValidateHair( bool female, int itemID ); + + public virtual int RandomHair( Mobile m ) { return RandomHair( m.Female ); } + public abstract int RandomHair( bool female ); + + public virtual bool ValidateFacialHair( Mobile m, int itemID ) { return ValidateFacialHair( m.Female, itemID ); } + public abstract bool ValidateFacialHair( bool female, int itemID ); + + public virtual int RandomFacialHair( Mobile m ) { return RandomFacialHair( m.Female ); } + public abstract int RandomFacialHair( bool female ); //For the *ahem* bearded ladies + + public abstract int ClipSkinHue( int hue ); + public abstract int RandomSkinHue(); + + public abstract int ClipHairHue( int hue ); + public abstract int RandomHairHue(); + + public virtual int Body( Mobile m ) + { + if( m.Alive ) + return AliveBody( m.Female ); + + return GhostBody( m.Female ); + } + + public virtual int AliveBody( Mobile m ) { return AliveBody( m.Female ); } + public virtual int AliveBody( bool female ) + { + return (female ? m_FemaleBody : m_MaleBody); + } + + public virtual int GhostBody( Mobile m ) { return GhostBody( m.Female ); } + public virtual int GhostBody( bool female ) + { + return (female ? m_FemaleGhostBody : m_MaleGhostBody); + } + + public int RaceID + { + get + { + return m_RaceID; + } + } + + public int RaceIndex + { + get + { + return m_RaceIndex; + } + } + + public string Name + { + get + { + return m_Name; + } + set + { + m_Name = value; + } + } + + public string PluralName + { + get + { + return m_PluralName; + } + set + { + m_PluralName = value; + } + } + } +} \ No newline at end of file diff --git a/Server/Region.cs b/Server/Region.cs new file mode 100644 index 000000000..68470cb7a --- /dev/null +++ b/Server/Region.cs @@ -0,0 +1,1272 @@ +/*************************************************************************** + * Region.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Region.cs 151 2006-06-11 02:57:01Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Xml; +using Server.Network; +using Server.Targeting; + +namespace Server +{ + public enum MusicName + { + Invalid = -1, + OldUlt01 = 0, + Create1, + DragFlit, + OldUlt02, + OldUlt03, + OldUlt04, + OldUlt05, + OldUlt06, + Stones2, + Britain1, + Britain2, + Bucsden, + Jhelom, + LBCastle, + Linelle, + Magincia, + Minoc, + Ocllo, + Samlethe, + Serpents, + Skarabra, + Trinsic, + Vesper, + Wind, + Yew, + Cave01, + Dungeon9, + Forest_a, + InTown01, + Jungle_a, + Mountn_a, + Plains_a, + Sailing, + Swamp_a, + Tavern01, + Tavern02, + Tavern03, + Tavern04, + Combat1, + Combat2, + Combat3, + Approach, + Death, + Victory, + BTCastle, + Nujelm, + Dungeon2, + Cove, + Moonglow, + Zento, + TokunoDungeon, + Taiko + } + + + public interface ISpawner + { + bool UnlinkOnTaming{ get; } + Point3D Home{ get; } + int Range{ get; } + + void Remove( object spawn ); + } + + + public class Region : IComparable + { + private static List m_Regions = new List(); + + public static List Regions{ get{ return m_Regions; } } + + public static Region Find( Point3D p, Map map ) + { + if ( map == null ) + return Map.Internal.DefaultRegion; + + Sector sector = map.GetSector( p ); + List list = sector.RegionRects; + + for ( int i = 0; i < list.Count; ++i ) + { + RegionRect regRect = list[i]; + + if ( regRect.Contains( p ) ) + return regRect.Region; + } + + return map.DefaultRegion; + } + + private static Type m_DefaultRegionType = typeof( Region ); + public static Type DefaultRegionType{ get{ return m_DefaultRegionType; } set{ m_DefaultRegionType = value; } } + + private static TimeSpan m_StaffLogoutDelay = TimeSpan.FromSeconds( 10.0 ); + private static TimeSpan m_DefaultLogoutDelay = TimeSpan.FromMinutes( 5.0 ); + + public static TimeSpan StaffLogoutDelay{ get{ return m_StaffLogoutDelay; } set{ m_StaffLogoutDelay = value; } } + public static TimeSpan DefaultLogoutDelay{ get{ return m_DefaultLogoutDelay; } set{ m_DefaultLogoutDelay = value; } } + + public static readonly int DefaultPriority = 50; + + public static readonly int MinZ = sbyte.MinValue; + public static readonly int MaxZ = sbyte.MaxValue + 1; + + public static Rectangle3D ConvertTo3D( Rectangle2D rect ) + { + return new Rectangle3D( new Point3D( rect.Start, MinZ ), new Point3D( rect.End, MaxZ ) ); + } + + public static Rectangle3D[] ConvertTo3D( Rectangle2D[] rects ) + { + Rectangle3D[] ret = new Rectangle3D[rects.Length]; + + for ( int i = 0; i < ret.Length; i++ ) + { + ret[i] = ConvertTo3D( rects[i] ); + } + + return ret; + } + + + private string m_Name; + private Map m_Map; + private Region m_Parent; + private List m_Children = new List(); + private Rectangle3D[] m_Area; + private Sector[] m_Sectors; + private bool m_Dynamic; + private int m_Priority; + private int m_ChildLevel; + private bool m_Registered; + + private Point3D m_GoLocation; + private MusicName m_Music; + + public string Name{ get{ return m_Name; } } + public Map Map{ get{ return m_Map; } } + public Region Parent{ get{ return m_Parent; } } + public List Children{ get{ return m_Children; } } + public Rectangle3D[] Area{ get{ return m_Area; } } + public Sector[] Sectors{ get{ return m_Sectors; } } + public bool Dynamic{ get{ return m_Dynamic; } } + public int Priority{ get{ return m_Priority; } } + public int ChildLevel{ get{ return m_ChildLevel; } } + public bool Registered{ get{ return m_Registered; } } + + public Point3D GoLocation{ get{ return m_GoLocation; } set { m_GoLocation = value; } } + public MusicName Music{ get{ return m_Music; } set{ m_Music = value; } } + + public bool IsDefault{ get{ return m_Map.DefaultRegion == this; } } + public virtual MusicName DefaultMusic{ get{ return m_Parent != null ? m_Parent.Music : MusicName.Invalid; } } + + public Region( string name, Map map, int priority, params Rectangle2D[] area ) : this( name, map, priority, ConvertTo3D( area ) ) + { + } + + public Region( string name, Map map, int priority, params Rectangle3D[] area ) : this( name, map, null, area ) + { + m_Priority = priority; + } + + public Region( string name, Map map, Region parent, params Rectangle2D[] area ) : this( name, map, parent, ConvertTo3D( area ) ) + { + } + + public Region( string name, Map map, Region parent, params Rectangle3D[] area ) + { + m_Name = name; + m_Map = map; + m_Parent = parent; + m_Area = area; + m_Dynamic = true; + m_Music = this.DefaultMusic; + + if ( m_Parent == null ) + { + m_ChildLevel = 0; + m_Priority = DefaultPriority; + } + else + { + m_ChildLevel = m_Parent.ChildLevel + 1; + m_Priority = m_Parent.Priority; + } + } + + public void Register() + { + if ( m_Registered ) + return; + + OnRegister(); + + m_Registered = true; + + if ( m_Parent != null ) + { + m_Parent.m_Children.Add( this ); + m_Parent.OnChildAdded( this ); + } + + m_Regions.Add( this ); + + m_Map.RegisterRegion( this ); + + List sectors = new List(); + + for ( int i = 0; i < m_Area.Length; i++ ) + { + Rectangle3D rect = m_Area[i]; + + Point2D start = m_Map.Bound( new Point2D( rect.Start ) ); + Point2D end = m_Map.Bound( new Point2D( rect.End ) ); + + Sector startSector = m_Map.GetSector( start ); + Sector endSector = m_Map.GetSector( end ); + + for ( int x = startSector.X; x <= endSector.X; x++ ) + { + for ( int y = startSector.Y; y <= endSector.Y; y++ ) + { + Sector sector = m_Map.GetRealSector( x, y ); + + sector.OnEnter( this, rect ); + + if ( !sectors.Contains( sector ) ) + sectors.Add( sector ); + } + } + } + + m_Sectors = sectors.ToArray(); + } + + public void Unregister() + { + if ( !m_Registered ) + return; + + OnUnregister(); + + m_Registered = false; + + if ( m_Children.Count > 0 ) + Console.WriteLine( "Warning: Unregistering region '{0}' with children", this ); + + if ( m_Parent != null ) + { + m_Parent.m_Children.Remove( this ); + m_Parent.OnChildRemoved( this ); + } + + m_Regions.Remove( this ); + + m_Map.UnregisterRegion( this ); + + if ( m_Sectors != null ) + { + for ( int i = 0; i < m_Sectors.Length; i++ ) + m_Sectors[i].OnLeave( this ); + } + + m_Sectors = null; + } + + public bool Contains( Point3D p ) + { + for ( int i = 0; i < m_Area.Length; i++ ) + { + Rectangle3D rect = m_Area[i]; + + if ( rect.Contains( p ) ) + return true; + } + + return false; + } + + public bool IsChildOf( Region region ) + { + if ( region == null ) + return false; + + Region p = m_Parent; + + while ( p != null ) + { + if ( p == region ) + return true; + + p = p.m_Parent; + } + + return false; + } + + public Region GetRegion( Type regionType ) + { + if ( regionType == null ) + return null; + + Region r = this; + + do + { + if ( regionType.IsAssignableFrom( r.GetType() ) ) + return r; + + r = r.m_Parent; + } + while ( r != null ); + + return null; + } + + public Region GetRegion( string regionName ) + { + if ( regionName == null ) + return null; + + Region r = this; + + do + { + if ( r.m_Name == regionName ) + return r; + + r = r.m_Parent; + } + while ( r != null ); + + return null; + } + + public bool IsPartOf( Region region ) + { + if ( this == region ) + return true; + + return IsChildOf( region ); + } + + public bool IsPartOf( Type regionType ) + { + return ( GetRegion( regionType ) != null ); + } + + public bool IsPartOf( string regionName ) + { + return ( GetRegion( regionName ) != null ); + } + + public virtual bool AcceptsSpawnsFrom( Region region ) + { + if ( !AllowSpawn() ) + return false; + + if ( region == this ) + return true; + + if ( m_Parent != null ) + return m_Parent.AcceptsSpawnsFrom( region ); + + return false; + } + + public List GetPlayers() + { + List list = new List(); + + if ( m_Sectors != null ) + { + for ( int i = 0; i < m_Sectors.Length; i++ ) + { + Sector sector = m_Sectors[i]; + + foreach ( Mobile player in sector.Players ) + { + if ( player.Region.IsPartOf( this ) ) + list.Add( player ); + } + } + } + + return list; + } + + public int GetPlayerCount() + { + int count = 0; + + if ( m_Sectors != null ) + { + for ( int i = 0; i < m_Sectors.Length; i++ ) + { + Sector sector = m_Sectors[i]; + + foreach ( Mobile player in sector.Players ) + { + if ( player.Region.IsPartOf( this ) ) + count++; + } + } + } + + return count; + } + + public List GetMobiles() + { + List list = new List(); + + if ( m_Sectors != null ) + { + for ( int i = 0; i < m_Sectors.Length; i++ ) + { + Sector sector = m_Sectors[i]; + + foreach ( Mobile mobile in sector.Mobiles ) + { + if ( mobile.Region.IsPartOf( this ) ) + list.Add( mobile ); + } + } + } + + return list; + } + + public int GetMobileCount() + { + int count = 0; + + if ( m_Sectors != null ) + { + for ( int i = 0; i < m_Sectors.Length; i++ ) + { + Sector sector = m_Sectors[i]; + + foreach ( Mobile mobile in sector.Mobiles ) + { + if ( mobile.Region.IsPartOf( this ) ) + count++; + } + } + } + + return count; + } + + int IComparable.CompareTo( object obj ) + { + if ( obj == null ) + return 1; + + Region reg = obj as Region; + + if ( reg == null ) + throw new ArgumentException( "obj is not a Region", "obj" ); + + // Dynamic regions go first + if ( this.Dynamic ) + { + if ( !reg.Dynamic ) + return -1; + } + else if ( reg.Dynamic ) + { + return 1; + } + + int thisPriority = this.Priority; + int regPriority = reg.Priority; + + if ( thisPriority != regPriority ) + return ( regPriority - thisPriority ); + + return ( reg.ChildLevel - this.ChildLevel ); + } + + public override string ToString() + { + if ( m_Name != null ) + return m_Name; + else + return GetType().Name; + } + + + public virtual void OnRegister() + { + } + + public virtual void OnUnregister() + { + } + + public virtual void OnChildAdded( Region child ) + { + } + + public virtual void OnChildRemoved( Region child ) + { + } + + public virtual bool OnMoveInto( Mobile m, Direction d, Point3D newLocation, Point3D oldLocation ) + { + return ( m.WalkRegion == null || AcceptsSpawnsFrom( m.WalkRegion ) ); + } + + public virtual void OnEnter( Mobile m ) + { + } + + public virtual void OnExit( Mobile m ) + { + } + + public virtual void MakeGuard( Mobile focus ) + { + if ( m_Parent != null ) + m_Parent.MakeGuard( focus ); + } + + public virtual Type GetResource( Type type ) + { + if ( m_Parent != null ) + return m_Parent.GetResource( type ); + + return type; + } + + public virtual bool CanUseStuckMenu( Mobile m ) + { + if ( m_Parent != null ) + return m_Parent.CanUseStuckMenu( m ); + + return true; + } + + public virtual void OnAggressed( Mobile aggressor, Mobile aggressed, bool criminal ) + { + if ( m_Parent != null ) + m_Parent.OnAggressed( aggressor, aggressed, criminal ); + } + + public virtual void OnDidHarmful( Mobile harmer, Mobile harmed ) + { + if ( m_Parent != null ) + m_Parent.OnDidHarmful( harmer, harmed ); + } + + public virtual void OnGotHarmful( Mobile harmer, Mobile harmed ) + { + if ( m_Parent != null ) + m_Parent.OnGotHarmful( harmer, harmed ); + } + + public virtual void OnLocationChanged( Mobile m, Point3D oldLocation ) + { + if ( m_Parent != null ) + m_Parent.OnLocationChanged( m, oldLocation ); + } + + public virtual bool OnTarget( Mobile m, Target t, object o ) + { + if ( m_Parent != null ) + return m_Parent.OnTarget( m, t, o ); + + return true; + } + + public virtual bool OnCombatantChange( Mobile m, Mobile Old, Mobile New ) + { + if ( m_Parent != null ) + return m_Parent.OnCombatantChange( m, Old, New ); + + return true; + } + + public virtual bool AllowHousing( Mobile from, Point3D p ) + { + if ( m_Parent != null ) + return m_Parent.AllowHousing( from, p ); + + return true; + } + + public virtual bool SendInaccessibleMessage( Item item, Mobile from ) + { + if ( m_Parent != null ) + return m_Parent.SendInaccessibleMessage( item, from ); + + return false; + } + + public virtual bool CheckAccessibility( Item item, Mobile from ) + { + if ( m_Parent != null ) + return m_Parent.CheckAccessibility( item, from ); + + return true; + } + + public virtual bool OnDecay( Item item ) + { + if ( m_Parent != null ) + return m_Parent.OnDecay( item ); + + return true; + } + + public virtual bool AllowHarmful( Mobile from, Mobile target ) + { + if ( m_Parent != null ) + return m_Parent.AllowHarmful( from, target ); + + if ( Mobile.AllowHarmfulHandler != null ) + return Mobile.AllowHarmfulHandler( from, target ); + + return true; + } + + public virtual void OnCriminalAction( Mobile m, bool message ) + { + if ( m_Parent != null ) + m_Parent.OnCriminalAction( m, message ); + else if ( message ) + m.SendLocalizedMessage( 1005040 ); // You've committed a criminal act!! + } + + public virtual bool AllowBeneficial( Mobile from, Mobile target ) + { + if ( m_Parent != null ) + return m_Parent.AllowBeneficial( from, target ); + + if ( Mobile.AllowBeneficialHandler != null ) + return Mobile.AllowBeneficialHandler( from, target ); + + return true; + } + + public virtual void OnBeneficialAction( Mobile helper, Mobile target ) + { + if ( m_Parent != null ) + m_Parent.OnBeneficialAction( helper, target ); + } + + public virtual void OnGotBeneficialAction( Mobile helper, Mobile target ) + { + if ( m_Parent != null ) + m_Parent.OnGotBeneficialAction( helper, target ); + } + + public virtual void SpellDamageScalar( Mobile caster, Mobile target, ref double damage ) + { + if ( m_Parent != null ) + m_Parent.SpellDamageScalar( caster, target, ref damage ); + } + + public virtual void OnSpeech( SpeechEventArgs args ) + { + if ( m_Parent != null ) + m_Parent.OnSpeech( args ); + } + + public virtual bool OnSkillUse( Mobile m, int Skill ) + { + if ( m_Parent != null ) + return m_Parent.OnSkillUse( m, Skill ); + + return true; + } + + public virtual bool OnBeginSpellCast( Mobile m, ISpell s ) + { + if ( m_Parent != null ) + return m_Parent.OnBeginSpellCast( m, s ); + + return true; + } + + public virtual void OnSpellCast( Mobile m, ISpell s ) + { + if ( m_Parent != null ) + m_Parent.OnSpellCast( m, s ); + } + + public virtual bool OnResurrect( Mobile m ) + { + if ( m_Parent != null ) + return m_Parent.OnResurrect( m ); + + return true; + } + + public virtual bool OnDeath( Mobile m ) + { + if ( m_Parent != null ) + return m_Parent.OnDeath( m ); + + return true; + } + + public virtual bool OnDamage( Mobile m, ref int Damage ) + { + if ( m_Parent != null ) + return m_Parent.OnDamage( m, ref Damage ); + + return true; + } + + public virtual bool OnHeal( Mobile m, ref int Heal ) + { + if ( m_Parent != null ) + return m_Parent.OnHeal( m, ref Heal ); + + return true; + } + + public virtual bool OnDoubleClick( Mobile m, object o ) + { + if ( m_Parent != null ) + return m_Parent.OnDoubleClick( m, o ); + + return true; + } + + public virtual bool OnSingleClick( Mobile m, object o ) + { + if ( m_Parent != null ) + return m_Parent.OnSingleClick( m, o ); + + return true; + } + + public virtual bool AllowSpawn() + { + if ( m_Parent != null ) + return m_Parent.AllowSpawn(); + + return true; + } + + public virtual void AlterLightLevel( Mobile m, ref int global, ref int personal ) + { + if ( m_Parent != null ) + m_Parent.AlterLightLevel( m, ref global, ref personal ); + } + + public virtual TimeSpan GetLogoutDelay( Mobile m ) + { + if ( m_Parent != null ) + return m_Parent.GetLogoutDelay( m ); + else if ( m.AccessLevel > AccessLevel.Player ) + return m_StaffLogoutDelay; + else + return m_DefaultLogoutDelay; + } + + + internal static bool CanMove( Mobile m, Direction d, Point3D newLocation, Point3D oldLocation, Map map ) + { + Region oldRegion = m.Region; + Region newRegion = Find( newLocation, map ); + + while ( oldRegion != newRegion ) + { + if ( !newRegion.OnMoveInto( m, d, newLocation, oldLocation ) ) + return false; + + if ( newRegion.m_Parent == null ) + return true; + + newRegion = newRegion.m_Parent; + } + + return true; + } + + internal static void OnRegionChange( Mobile m, Region oldRegion, Region newRegion ) + { + if ( newRegion != null && m.NetState != null ) + { + m.CheckLightLevels( false ); + + if ( oldRegion == null || oldRegion.Music != newRegion.Music ) + { + m.Send( PlayMusic.GetInstance( newRegion.Music ) ); + } + } + + Region oldR = oldRegion; + Region newR = newRegion; + + while ( oldR != newR ) + { + int oldRChild = ( oldR != null ? oldR.ChildLevel : -1 ); + int newRChild = ( newR != null ? newR.ChildLevel : -1 ); + + if ( oldRChild >= newRChild ) + { + oldR.OnExit( m ); + oldR = oldR.Parent; + } + + if ( newRChild >= oldRChild ) + { + newR.OnEnter( m ); + newR = newR.Parent; + } + } + } + + + internal static void Load() + { + if ( !System.IO.File.Exists( "Data/Regions.xml" ) ) + { + Console.WriteLine( "Error: Data/Regions.xml does not exist" ); + return; + } + + Console.Write( "Regions: Loading..." ); + + XmlDocument doc = new XmlDocument(); + doc.Load( System.IO.Path.Combine( Core.BaseDirectory, "Data/Regions.xml" ) ); + + XmlElement root = doc["ServerRegions"]; + + if ( root == null ) + { + Console.WriteLine( "Could not find root element 'ServerRegions' in Regions.xml" ); + } + else + { + foreach ( XmlElement facet in root.SelectNodes( "Facet" ) ) + { + Map map = null; + if ( ReadMap( facet, "name", ref map ) ) + { + if ( map == Map.Internal ) + Console.WriteLine( "Invalid internal map in a facet element" ); + else + LoadRegions( facet, map, null ); + } + } + } + + Console.WriteLine( "done" ); + } + + private static void LoadRegions( XmlElement xml, Map map, Region parent ) + { + foreach ( XmlElement xmlReg in xml.SelectNodes( "region" ) ) + { + Type type = DefaultRegionType; + + ReadType( xmlReg, "type", ref type, false ); + + if ( !typeof( Region ).IsAssignableFrom( type ) ) + { + Console.WriteLine( "Invalid region type '{0}' in regions.xml", type.FullName ); + continue; + } + + Region region = null; + try + { + region = (Region) Activator.CreateInstance( type, new object[] { xmlReg, map, parent } ); + } + catch ( Exception ex ) + { + Console.WriteLine( "Error during the creation of region type '{0}': {1}", type.FullName, ex ); + continue; + } + + region.Register(); + + LoadRegions( xmlReg, map, region ); + } + } + + public Region( XmlElement xml, Map map, Region parent ) + { + m_Map = map; + m_Parent = parent; + m_Dynamic = false; + + if ( m_Parent == null ) + { + m_ChildLevel = 0; + m_Priority = DefaultPriority; + } + else + { + m_ChildLevel = m_Parent.ChildLevel + 1; + m_Priority = m_Parent.Priority; + } + + ReadString( xml, "name", ref m_Name, false ); + + if ( parent == null ) + ReadInt32( xml, "priority", ref m_Priority, false ); + + + int minZ = MinZ; + int maxZ = MaxZ; + + XmlElement zrange = xml["zrange"]; + ReadInt32( zrange, "min", ref minZ, false ); + ReadInt32( zrange, "max", ref maxZ, false ); + + + List area = new List(); + foreach ( XmlElement xmlRect in xml.SelectNodes( "rect" ) ) + { + Rectangle3D rect = new Rectangle3D(); + if ( ReadRectangle3D( xmlRect, minZ, maxZ, ref rect ) ) + area.Add( rect ); + } + + m_Area = area.ToArray(); + + if ( m_Area.Length == 0 ) + Console.WriteLine( "Empty area for region '{0}'", this ); + + + if ( !ReadPoint3D( xml["go"], map, ref m_GoLocation, false ) && m_Area.Length > 0 ) + { + Point3D start = m_Area[0].Start; + Point3D end = m_Area[0].End; + + int x = start.X + (end.X - start.X) / 2; + int y = start.Y + (end.Y - start.Y) / 2; + + m_GoLocation = new Point3D( x, y, m_Map.GetAverageZ( x, y ) ); + } + + + object oMusic = this.DefaultMusic; + + ReadEnum( xml["music"], "name", typeof( MusicName ), ref oMusic, false ); + + m_Music = (MusicName) oMusic; + } + + protected static string GetAttribute( XmlElement xml, string attribute, bool mandatory ) + { + if ( xml == null ) + { + if ( mandatory ) + Console.WriteLine( "Missing element for attribute '{0}'", attribute ); + + return null; + } + else if ( xml.HasAttribute( attribute ) ) + { + return xml.GetAttribute( attribute ); + } + else + { + if ( mandatory ) + Console.WriteLine( "Missing attribute '{0}' in element '{1}'", attribute, xml.Name ); + + return null; + } + } + + public static bool ReadString( XmlElement xml, string attribute, ref string value ) + { + return ReadString( xml, attribute, ref value, true ); + } + + public static bool ReadString( XmlElement xml, string attribute, ref string value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + value = s; + return true; + } + + public static bool ReadInt32( XmlElement xml, string attribute, ref int value ) + { + return ReadInt32( xml, attribute, ref value, true ); + } + + public static bool ReadInt32( XmlElement xml, string attribute, ref int value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + try + { + value = XmlConvert.ToInt32( s ); + } + catch + { + Console.WriteLine( "Could not parse integer attribute '{0}' in element '{1}'", attribute, xml.Name ); + return false; + } + + return true; + } + + public static bool ReadBoolean( XmlElement xml, string attribute, ref bool value ) + { + return ReadBoolean( xml, attribute, ref value, true ); + } + + public static bool ReadBoolean( XmlElement xml, string attribute, ref bool value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + try + { + value = XmlConvert.ToBoolean( s ); + } + catch + { + Console.WriteLine( "Could not parse boolean attribute '{0}' in element '{1}'", attribute, xml.Name ); + return false; + } + + return true; + } + + public static bool ReadDateTime( XmlElement xml, string attribute, ref DateTime value ) + { + return ReadDateTime( xml, attribute, ref value, true ); + } + + public static bool ReadDateTime( XmlElement xml, string attribute, ref DateTime value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + try + { + value = XmlConvert.ToDateTime( s, XmlDateTimeSerializationMode.Local ); + } + catch + { + Console.WriteLine( "Could not parse DateTime attribute '{0}' in element '{1}'", attribute, xml.Name ); + return false; + } + + return true; + } + + public static bool ReadTimeSpan( XmlElement xml, string attribute, ref TimeSpan value ) + { + return ReadTimeSpan( xml, attribute, ref value, true ); + } + + public static bool ReadTimeSpan( XmlElement xml, string attribute, ref TimeSpan value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + try + { + value = XmlConvert.ToTimeSpan( s ); + } + catch + { + Console.WriteLine( "Could not parse TimeSpan attribute '{0}' in element '{1}'", attribute, xml.Name ); + return false; + } + + return true; + } + + public static bool ReadEnum( XmlElement xml, string attribute, Type type, ref object value ) + { + return ReadEnum( xml, attribute, type, ref value, true ); + } + + public static bool ReadEnum( XmlElement xml, string attribute, Type type, ref object value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + try + { + value = Enum.Parse( type, s, true ); + } + catch + { + Console.WriteLine( "Could not parse {0} enum attribute '{1}' in element '{2}'", type, attribute, xml.Name ); + return false; + } + + return true; + } + + public static bool ReadMap( XmlElement xml, string attribute, ref Map value ) + { + return ReadMap( xml, attribute, ref value, true ); + } + + public static bool ReadMap( XmlElement xml, string attribute, ref Map value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + try + { + value = Map.Parse( s ); + } + catch + { + Console.WriteLine( "Could not parse Map attribute '{0}' in element '{1}'", attribute, xml.Name ); + return false; + } + + return true; + } + + public static bool ReadType( XmlElement xml, string attribute, ref Type value ) + { + return ReadType( xml, attribute, ref value, true ); + } + + public static bool ReadType( XmlElement xml, string attribute, ref Type value, bool mandatory ) + { + string s = GetAttribute( xml, attribute, mandatory ); + + if ( s == null ) + return false; + + Type type; + try + { + type = ScriptCompiler.FindTypeByName( s, false ); + } + catch + { + Console.WriteLine( "Could not parse Type attribute '{0}' in element '{1}'", attribute, xml.Name ); + return false; + } + + if ( type == null ) + { + Console.WriteLine( "Could not find Type '{0}'", s ); + return false; + } + + value = type; + return true; + } + + public static bool ReadPoint3D( XmlElement xml, Map map, ref Point3D value ) + { + return ReadPoint3D( xml, map, ref value, true ); + } + + public static bool ReadPoint3D( XmlElement xml, Map map, ref Point3D value, bool mandatory ) + { + int x = 0, y = 0, z = 0; + + bool xyOk = ReadInt32( xml, "x", ref x, mandatory ) & ReadInt32( xml, "y", ref y, mandatory ); + bool zOk = ReadInt32( xml, "z", ref z, mandatory && map == null ); + + if ( xyOk && ( zOk || map != null ) ) + { + if ( !zOk ) + z = map.GetAverageZ( x, y ); + + value = new Point3D( x, y, z ); + return true; + } + + return false; + } + + public static bool ReadRectangle3D( XmlElement xml, int defaultMinZ, int defaultMaxZ, ref Rectangle3D value ) + { + return ReadRectangle3D( xml, defaultMinZ, defaultMaxZ, ref value, true ); + } + + public static bool ReadRectangle3D( XmlElement xml, int defaultMinZ, int defaultMaxZ, ref Rectangle3D value, bool mandatory ) + { + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + + if ( xml.HasAttribute( "x" ) ) + { + if ( ReadInt32( xml, "x", ref x1, mandatory ) + & ReadInt32( xml, "y", ref y1, mandatory ) + & ReadInt32( xml, "width", ref x2, mandatory ) + & ReadInt32( xml, "height", ref y2, mandatory ) ) + { + x2 += x1; + y2 += y1; + } + else + { + return false; + } + } + else + { + if ( !ReadInt32( xml, "x1", ref x1, mandatory ) + | !ReadInt32( xml, "y1", ref y1, mandatory ) + | !ReadInt32( xml, "x2", ref x2, mandatory ) + | !ReadInt32( xml, "y2", ref y2, mandatory ) ) + { + return false; + } + } + + int z1 = defaultMinZ; + int z2 = defaultMaxZ; + + ReadInt32( xml, "zmin", ref z1, false ); + ReadInt32( xml, "zmax", ref z2, false ); + + value = new Rectangle3D( new Point3D( x1, y1, z1 ), new Point3D( x2, y2, z2 ) ); + + return true; + } + } +} \ No newline at end of file diff --git a/Server/ScriptCompiler.cs b/Server/ScriptCompiler.cs new file mode 100644 index 000000000..b3520c4a5 --- /dev/null +++ b/Server/ScriptCompiler.cs @@ -0,0 +1,640 @@ +/*************************************************************************** + * ScriptCompiler.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: ScriptCompiler.cs 163 2006-06-15 00:59:08Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Reflection; +using Microsoft.CSharp; +using Microsoft.VisualBasic; + +namespace Server { + public class ScriptCompiler { + private static Assembly[] m_Assemblies; + + public static Assembly[] Assemblies { + get { + return m_Assemblies; + } + set { + m_Assemblies = value; + } + } + + private static List m_AdditionalReferences = new List(); + + public static string[] GetReferenceAssemblies() { + List list = new List(); + + string path = Path.Combine( Core.BaseDirectory, "Data/Assemblies.cfg" ); + + if ( File.Exists( path ) ) { + using ( StreamReader ip = new StreamReader( path ) ) { + string line; + + while ( ( line = ip.ReadLine() ) != null ) { + if ( line.Length > 0 && !line.StartsWith( "#" ) ) + list.Add( line ); + } + } + } + + list.Add( Core.ExePath ); + + list.AddRange( m_AdditionalReferences ); + + return list.ToArray(); + } + + public static string GetDefines() { + StringBuilder sb = null; + +#if MONO + AppendDefine( ref sb, "MONO" ); +#endif + + //These two defines are legacy, ie, depreciated. + if ( Core.Is64Bit ) + AppendDefine( ref sb, "x64" ); + + AppendDefine( ref sb, "Framework_2_0" ); + + return ( sb == null ? null : sb.ToString() ); + } + + public static void AppendDefine( ref StringBuilder sb, string define ) { + if ( sb == null ) + sb = new StringBuilder(); + else + sb.Append( ';' ); + + sb.Append( define ); + } + + private static byte[] GetHashCode( string compiledFile, string[] scriptFiles, bool debug ) { + using ( MemoryStream ms = new MemoryStream() ) { + using ( BinaryWriter bin = new BinaryWriter( ms ) ) { + FileInfo fileInfo = new FileInfo( compiledFile ); + + bin.Write( fileInfo.LastWriteTimeUtc.Ticks ); + + foreach ( string scriptFile in scriptFiles ) { + fileInfo = new FileInfo( scriptFile ); + + bin.Write( fileInfo.LastWriteTimeUtc.Ticks ); + } + + bin.Write( debug ); + + ms.Position = 0; + + using ( System.Security.Cryptography.SHA1 sha1 = System.Security.Cryptography.SHA1.Create() ) { + return sha1.ComputeHash( ms ); + } + } + } + } + + public static bool CompileCSScripts( out Assembly assembly ) { + return CompileCSScripts( false, out assembly ); + } + + public static bool CompileCSScripts( bool debug, out Assembly assembly ) { + Console.Write( "Scripts: Compiling C# scripts..." ); + string[] files = GetScripts( "*.cs" ); + + if ( files.Length == 0 ) { + Console.WriteLine( "no files found." ); + assembly = null; + return true; + } + + if ( File.Exists( "Scripts/Output/Scripts.CS.dll" ) ) { + if ( File.Exists( "Scripts/Output/Scripts.CS.hash" ) ) { + try { + byte[] hashCode = GetHashCode( "Scripts/Output/Scripts.CS.dll", files, debug ); + + using ( FileStream fs = new FileStream( "Scripts/Output/Scripts.CS.hash", FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + using ( BinaryReader bin = new BinaryReader( fs ) ) { + byte[] bytes = bin.ReadBytes( hashCode.Length ); + + if ( bytes.Length == hashCode.Length ) { + bool valid = true; + + for ( int i = 0; i < bytes.Length; ++i ) { + if ( bytes[i] != hashCode[i] ) { + valid = false; + break; + } + } + + if ( valid ) { + assembly = Assembly.LoadFrom( "Scripts/Output/Scripts.CS.dll" ); + + if ( !m_AdditionalReferences.Contains( assembly.Location ) ) { + m_AdditionalReferences.Add( assembly.Location ); + } + + Console.WriteLine( "done (cached)" ); + + return true; + } + } + } + } + } catch { + } + } + } + + DeleteFiles( "Scripts.CS*.dll" ); + + using ( CSharpCodeProvider provider = new CSharpCodeProvider() ) { + string path = GetUnusedPath( "Scripts.CS" ); + + CompilerParameters parms = new CompilerParameters( GetReferenceAssemblies(), path, debug ); + + string defines = GetDefines(); + + if ( defines != null ) + parms.CompilerOptions = string.Format( "/D:{0}", defines ); + + CompilerResults results = provider.CompileAssemblyFromFile( parms, files ); + m_AdditionalReferences.Add( path ); + + Display( results ); + + if ( results.Errors.Count > 0 ) { + assembly = null; + return false; + } + + if ( Path.GetFileName( path ) == "Scripts.CS.dll" ) { + try { + byte[] hashCode = GetHashCode( path, files, debug ); + + using ( FileStream fs = new FileStream( "Scripts/Output/Scripts.CS.hash", FileMode.Create, FileAccess.Write, FileShare.None ) ) { + using ( BinaryWriter bin = new BinaryWriter( fs ) ) { + bin.Write( hashCode, 0, hashCode.Length ); + } + } + } catch { + } + } + + assembly = results.CompiledAssembly; + return true; + } + } + + public static bool CompileVBScripts( out Assembly assembly ) { + return CompileVBScripts( false, out assembly ); + } + + public static bool CompileVBScripts( bool debug, out Assembly assembly ) { + Console.Write( "Scripts: Compiling VB.NET scripts..." ); + string[] files = GetScripts( "*.vb" ); + + if ( files.Length == 0 ) { + Console.WriteLine( "no files found." ); + assembly = null; + return true; + } + + if ( File.Exists( "Scripts/Output/Scripts.VB.dll" ) ) { + if ( File.Exists( "Scripts/Output/Scripts.VB.hash" ) ) { + byte[] hashCode = GetHashCode( "Scripts/Output/Scripts.VB.dll", files, debug ); + + try { + using ( FileStream fs = new FileStream( "Scripts/Output/Scripts.VB.hash", FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + using ( BinaryReader bin = new BinaryReader( fs ) ) { + byte[] bytes = bin.ReadBytes( hashCode.Length ); + + if ( bytes.Length == hashCode.Length ) { + bool valid = true; + + for ( int i = 0; i < bytes.Length; ++i ) { + if ( bytes[i] != hashCode[i] ) { + valid = false; + break; + } + } + + if ( valid ) { + assembly = Assembly.LoadFrom( "Scripts/Output/Scripts.VB.dll" ); + + if ( !m_AdditionalReferences.Contains( assembly.Location ) ) { + m_AdditionalReferences.Add( assembly.Location ); + } + + Console.WriteLine( "done (cached)" ); + + return true; + } + } + } + } + } catch { + } + } + } + + DeleteFiles( "Scripts.VB*.dll" ); + + using ( VBCodeProvider provider = new VBCodeProvider() ) { + string path = GetUnusedPath( "Scripts.VB" ); + + CompilerParameters parms = new CompilerParameters( GetReferenceAssemblies(), path, debug ); + + string defines = GetDefines(); + + if ( defines != null ) + parms.CompilerOptions = String.Format( "/D:{0}", defines ); + + CompilerResults results = provider.CompileAssemblyFromFile( parms, files ); + m_AdditionalReferences.Add( path ); + + Display( results ); + + if ( results.Errors.Count > 0 ) { + assembly = null; + return false; + } + + if ( Path.GetFileName( path ) == "Scripts.VB.dll" ) { + try { + byte[] hashCode = GetHashCode( path, files, debug ); + + using ( FileStream fs = new FileStream( "Scripts/Output/Scripts.VB.hash", FileMode.Create, FileAccess.Write, FileShare.None ) ) { + using ( BinaryWriter bin = new BinaryWriter( fs ) ) { + bin.Write( hashCode, 0, hashCode.Length ); + } + } + } catch { + } + } + + assembly = results.CompiledAssembly; + return true; + } + } + + public static void Display( CompilerResults results ) { + if ( results.Errors.Count > 0 ) { + Dictionary> errors = new Dictionary>( results.Errors.Count, StringComparer.OrdinalIgnoreCase ); + Dictionary> warnings = new Dictionary>( results.Errors.Count, StringComparer.OrdinalIgnoreCase ); + + foreach ( CompilerError e in results.Errors ) { + Dictionary> table = ( e.IsWarning ? warnings : errors ); + + List list = null; + table.TryGetValue( e.FileName, out list ); + + if ( list == null ) + table[e.FileName] = list = new List(); + + list.Add( e ); + } + + if ( errors.Count > 0 ) + Console.WriteLine( "failed ({0} errors, {1} warnings)", errors.Count, warnings.Count ); + else + Console.WriteLine( "done ({0} errors, {1} warnings)", errors.Count, warnings.Count ); + + string scriptRoot = Path.GetFullPath( Path.Combine( Core.BaseDirectory, "Scripts" + Path.DirectorySeparatorChar ) ); + Uri scriptRootUri = new Uri( scriptRoot ); + + Utility.PushColor( ConsoleColor.Yellow ); + + if ( warnings.Count > 0 ) + Console.WriteLine( "Warnings:" ); + + foreach ( KeyValuePair> kvp in warnings ) { + string fileName = kvp.Key; + List list = kvp.Value; + + string fullPath = Path.GetFullPath( fileName ); + string usedPath = Uri.UnescapeDataString( scriptRootUri.MakeRelativeUri( new Uri( fullPath ) ).OriginalString ); + + Console.WriteLine( " + {0}:", usedPath ); + + Utility.PushColor( ConsoleColor.DarkYellow ); + + foreach ( CompilerError e in list ) + Console.WriteLine( " {0}: Line {1}: {3}", e.ErrorNumber, e.Line, e.Column, e.ErrorText ); + + Utility.PopColor(); + } + + Utility.PopColor(); + + Utility.PushColor( ConsoleColor.Red ); + + if ( errors.Count > 0 ) + Console.WriteLine( "Errors:" ); + + foreach ( KeyValuePair> kvp in errors ) { + string fileName = kvp.Key; + List list = kvp.Value; + + string fullPath = Path.GetFullPath( fileName ); + string usedPath = Uri.UnescapeDataString( scriptRootUri.MakeRelativeUri( new Uri( fullPath ) ).OriginalString ); + + Console.WriteLine( " + {0}:", usedPath ); + + Utility.PushColor( ConsoleColor.DarkRed ); + + foreach ( CompilerError e in list ) + Console.WriteLine( " {0}: Line {1}: {3}", e.ErrorNumber, e.Line, e.Column, e.ErrorText ); + + Utility.PopColor(); + } + + Utility.PopColor(); + } else { + Console.WriteLine( "done (0 errors, 0 warnings)" ); + } + } + + public static string GetUnusedPath( string name ) { + string path = Path.Combine( Core.BaseDirectory, String.Format( "Scripts/Output/{0}.dll", name ) ); + + for ( int i = 2; File.Exists( path ) && i <= 1000; ++i ) + path = Path.Combine( Core.BaseDirectory, String.Format( "Scripts/Output/{0}.{1}.dll", name, i ) ); + + return path; + } + + public static void DeleteFiles( string mask ) { + try { + string[] files = Directory.GetFiles( Path.Combine( Core.BaseDirectory, "Scripts/Output" ), mask ); + + foreach ( string file in files ) { + try { File.Delete( file ); } catch { } + } + } catch { + } + } + + private delegate CompilerResults Compiler( bool debug ); + + private void LoadScriptedAssembly( List assemblies, string fileName, Compiler compiler, bool debug ) { + + + if ( File.Exists( fileName ) ) { + } + } + + public static bool Compile() { + return Compile( false ); + } + + public static bool Compile( bool debug ) { + EnsureDirectory( "Scripts/" ); + EnsureDirectory( "Scripts/Output/" ); + + if ( m_AdditionalReferences.Count > 0 ) + m_AdditionalReferences.Clear(); + + List assemblies = new List(); + + Assembly assembly; + + if ( CompileCSScripts( debug, out assembly ) ) { + if ( assembly != null ) { + assemblies.Add( assembly ); + } + } else { + return false; + } + + if ( CompileVBScripts( debug, out assembly ) ) { + if ( assembly != null ) { + assemblies.Add( assembly ); + } + } else { + return false; + } + + if ( assemblies.Count == 0 ) { + return false; + } + + m_Assemblies = assemblies.ToArray(); + + Console.Write( "Scripts: Verifying..." ); + Core.VerifySerialization(); + Console.WriteLine( "done ({0} items, {1} mobiles)", Core.ScriptItems, Core.ScriptMobiles ); + + List invoke = new List(); + + for ( int a = 0; a < m_Assemblies.Length; ++a ) { + Type[] types = m_Assemblies[a].GetTypes(); + + for ( int i = 0; i < types.Length; ++i ) { + MethodInfo m = types[i].GetMethod( "Configure", BindingFlags.Static | BindingFlags.Public ); + + if ( m != null ) + invoke.Add( m ); + } + } + + invoke.Sort( new CallPriorityComparer() ); + + for ( int i = 0; i < invoke.Count; ++i ) + invoke[i].Invoke( null, null ); + + invoke.Clear(); + + Region.Load(); + World.Load(); + + for ( int a = 0; a < m_Assemblies.Length; ++a ) { + Type[] types = m_Assemblies[a].GetTypes(); + + for ( int i = 0; i < types.Length; ++i ) { + MethodInfo m = types[i].GetMethod( "Initialize", BindingFlags.Static | BindingFlags.Public ); + + if ( m != null ) + invoke.Add( m ); + } + } + + invoke.Sort( new CallPriorityComparer() ); + + for ( int i = 0; i < invoke.Count; ++i ) + invoke[i].Invoke( null, null ); + + return true; + } + + private static Dictionary m_TypeCaches = new Dictionary(); + private static TypeCache m_NullCache; + + public static TypeCache GetTypeCache( Assembly asm ) { + if ( asm == null ) { + if ( m_NullCache == null ) + m_NullCache = new TypeCache( null ); + + return m_NullCache; + } + + TypeCache c = null; + m_TypeCaches.TryGetValue( asm, out c ); + + if ( c == null ) + m_TypeCaches[asm] = c = new TypeCache( asm ); + + return c; + } + + public static Type FindTypeByFullName( string fullName ) { + return FindTypeByFullName( fullName, true ); + } + + public static Type FindTypeByFullName( string fullName, bool ignoreCase ) { + Type type = null; + + for ( int i = 0; type == null && i < m_Assemblies.Length; ++i ) + type = GetTypeCache( m_Assemblies[i] ).GetTypeByFullName( fullName, ignoreCase ); + + if ( type == null ) + type = GetTypeCache( Core.Assembly ).GetTypeByFullName( fullName, ignoreCase ); + + return type; + } + + public static Type FindTypeByName( string name ) { + return FindTypeByName( name, true ); + } + + public static Type FindTypeByName( string name, bool ignoreCase ) { + Type type = null; + + for ( int i = 0; type == null && i < m_Assemblies.Length; ++i ) + type = GetTypeCache( m_Assemblies[i] ).GetTypeByName( name, ignoreCase ); + + if ( type == null ) + type = GetTypeCache( Core.Assembly ).GetTypeByName( name, ignoreCase ); + + return type; + } + + public static void EnsureDirectory( string dir ) { + string path = Path.Combine( Core.BaseDirectory, dir ); + + if ( !Directory.Exists( path ) ) + Directory.CreateDirectory( path ); + } + + public static string[] GetScripts( string filter ) { + List list = new List(); + + GetScripts( list, Path.Combine( Core.BaseDirectory, "Scripts" ), filter ); + + return list.ToArray(); + } + + public static void GetScripts( List list, string path, string filter ) { + foreach ( string dir in Directory.GetDirectories( path ) ) + GetScripts( list, dir, filter ); + + list.AddRange( Directory.GetFiles( path, filter ) ); + } + } + + public class TypeCache { + private Type[] m_Types; + private TypeTable m_Names, m_FullNames; + + public Type[] Types { get { return m_Types; } } + public TypeTable Names { get { return m_Names; } } + public TypeTable FullNames { get { return m_FullNames; } } + + public Type GetTypeByName( string name, bool ignoreCase ) { + return m_Names.Get( name, ignoreCase ); + } + + public Type GetTypeByFullName( string fullName, bool ignoreCase ) { + return m_FullNames.Get( fullName, ignoreCase ); + } + + public TypeCache( Assembly asm ) { + if ( asm == null ) + m_Types = Type.EmptyTypes; + else + m_Types = asm.GetTypes(); + + m_Names = new TypeTable( m_Types.Length ); + m_FullNames = new TypeTable( m_Types.Length ); + + Type typeofTypeAliasAttribute = typeof( TypeAliasAttribute ); + + for ( int i = 0; i < m_Types.Length; ++i ) { + Type type = m_Types[i]; + + m_Names.Add( type.Name, type ); + m_FullNames.Add( type.FullName, type ); + + if ( type.IsDefined( typeofTypeAliasAttribute, false ) ) { + object[] attrs = type.GetCustomAttributes( typeofTypeAliasAttribute, false ); + + if ( attrs != null && attrs.Length > 0 ) { + TypeAliasAttribute attr = attrs[0] as TypeAliasAttribute; + + if ( attr != null ) { + for ( int j = 0; j < attr.Aliases.Length; ++j ) + m_FullNames.Add( attr.Aliases[j], type ); + } + } + } + } + } + } + + public class TypeTable { + private Dictionary m_Sensitive, m_Insensitive; + + public void Add( string key, Type type ) { + m_Sensitive[key] = type; + m_Insensitive[key] = type; + } + + public Type Get( string key, bool ignoreCase ) { + Type t = null; + + if ( ignoreCase ) + m_Insensitive.TryGetValue( key, out t ); + else + m_Sensitive.TryGetValue( key, out t ); + + return t; + } + + public TypeTable( int capacity ) { + m_Sensitive = new Dictionary( capacity ); + m_Insensitive = new Dictionary( capacity, StringComparer.OrdinalIgnoreCase ); + } + } +} \ No newline at end of file diff --git a/Server/Sector.cs b/Server/Sector.cs new file mode 100644 index 000000000..d422e0497 --- /dev/null +++ b/Server/Sector.cs @@ -0,0 +1,365 @@ +/*************************************************************************** + * Sector.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Sector.cs 124 2006-03-07 07:01:19Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Network; + +namespace Server +{ + public class RegionRect : IComparable + { + private Region m_Region; + private Rectangle3D m_Rect; + + public Region Region{ get{ return m_Region; } } + public Rectangle3D Rect{ get{ return m_Rect; } } + + public RegionRect( Region region, Rectangle3D rect ) + { + m_Region = region; + m_Rect = rect; + } + + public bool Contains( Point3D loc ) + { + return m_Rect.Contains( loc ); + } + + int IComparable.CompareTo( object obj ) + { + if ( obj == null ) + return 1; + + RegionRect regRect = obj as RegionRect; + + if ( regRect == null ) + throw new ArgumentException( "obj is not a RegionRect", "obj" ); + + return ((IComparable)m_Region).CompareTo( regRect.m_Region ); + } + } + + + public class Sector + { + private int m_X, m_Y; + private Map m_Owner; + private List m_Mobiles; + private List m_Players; + private List m_Items; + private List m_Clients; + private List m_Multis; + private List m_RegionRects; + private bool m_Active; + + // TODO: Can we avoid this? + private static List m_DefaultMobileList = new List(); + private static List m_DefaultItemList = new List(); + private static List m_DefaultClientList = new List(); + private static List m_DefaultMultiList = new List(); + private static List m_DefaultRectList = new List(); + + public Sector( int x, int y, Map owner ) + { + m_X = x; + m_Y = y; + m_Owner = owner; + m_Active = false; + } + + public void OnClientChange( NetState oldState, NetState newState ) + { + if ( m_Clients != null ) + { + m_Clients.Remove( oldState ); + + if ( newState == null && m_Clients.Count == 0 ) + { + m_Clients = null; + return; + } + } + + if ( newState != null ) + { + if ( m_Clients == null ) + m_Clients = new List(); + + m_Clients.Add( newState ); + } + } + + public void OnEnter( Mobile m ) + { + if ( m_Mobiles == null ) + m_Mobiles = new List(); + + m_Mobiles.Add( m ); + + if ( m.NetState != null ) + { + if ( m_Clients == null ) + m_Clients = new List(); + + m_Clients.Add( m.NetState ); + } + + if ( m.Player ) + { + if ( m_Players == null ) + { + m_Players = new List(); + m_Owner.ActivateSectors( m_X, m_Y ); + } + + m_Players.Add( m ); + } + } + + public void OnEnter( Item item ) + { + if ( m_Items == null ) + m_Items = new List(); + + m_Items.Add( item ); + } + + public void OnEnter( Region r, Rectangle3D rect ) + { + if ( m_RegionRects == null ) + m_RegionRects = new List(); + + RegionRect regRect = new RegionRect( r, rect ); + + m_RegionRects.Add( regRect ); + m_RegionRects.Sort(); + + if ( m_Mobiles != null && m_Mobiles.Count > 0 ) + { + List list = new List( m_Mobiles ); + + for ( int i = 0; i < list.Count; ++i ) + list[i].UpdateRegion(); + } + } + + public void OnMultiEnter( BaseMulti m ) + { + if ( m_Multis == null ) + m_Multis = new List(); + + m_Multis.Add( m ); + } + + public void OnMultiLeave( BaseMulti m ) + { + m_Multis.Remove( m ); + + if ( m_Multis.Count == 0 ) + m_Multis = null; + } + + public void OnLeave( Region r ) + { + for ( int i = m_RegionRects.Count - 1; i >= 0; i-- ) + { + RegionRect regRect = m_RegionRects[i]; + + if ( regRect.Region == r ) + m_RegionRects.RemoveAt( i ); + } + + if ( m_Mobiles != null && m_Mobiles.Count > 0 ) + { + List list = new List( m_Mobiles ); + + for ( int i = 0; i < list.Count; ++i ) + list[i].UpdateRegion(); + } + + if ( m_RegionRects.Count == 0 ) + m_RegionRects = null; + } + + public void OnLeave( Mobile m ) + { + m_Mobiles.Remove( m ); + + if ( m_Clients != null && m.NetState != null ) + { + m_Clients.Remove( m.NetState ); + + if ( m_Clients.Count == 0 ) + m_Clients = null; + } + + if ( m.Player ) + { + m_Players.Remove( m ); + + if ( m_Players.Count == 0 ) + { + m_Owner.DeactivateSectors( m_X, m_Y ); + m_Players = null; + } + } + + if ( m_Mobiles.Count == 0 ) + m_Mobiles = null; + } + + public void OnLeave( Item item ) + { + m_Items.Remove( item ); + + if ( m_Items.Count == 0 ) + m_Items = null; + } + + public void Activate() + { + if ( !Active && m_Owner != Map.Internal ) + { + for ( int i = 0; m_Items != null && i < m_Items.Count; i++ ) + m_Items[i].OnSectorActivate(); + + for ( int i = 0; m_Mobiles != null && i < m_Mobiles.Count; i++ ) + m_Mobiles[i].OnSectorActivate(); + + m_Active = true; + } + } + + public void Deactivate() + { + if ( Active ) + { + for ( int i = 0; m_Items != null && i < m_Items.Count; i++ ) + m_Items[i].OnSectorDeactivate(); + + for ( int i = 0; m_Mobiles != null && i < m_Mobiles.Count; i++ ) + m_Mobiles[i].OnSectorDeactivate(); + + m_Active = false; + } + } + + public List RegionRects + { + get + { + if ( m_RegionRects == null ) + return m_DefaultRectList; + + return m_RegionRects; + } + } + + public List Multis + { + get + { + if ( m_Multis == null ) + return m_DefaultMultiList; + + return m_Multis; + } + } + + public List Mobiles + { + get + { + if ( m_Mobiles == null ) + return m_DefaultMobileList; + + return m_Mobiles; + } + } + + public List Items + { + get + { + if ( m_Items == null ) + return m_DefaultItemList; + + return m_Items; + } + } + + public List Clients + { + get + { + if ( m_Clients == null ) + return m_DefaultClientList; + + return m_Clients; + } + } + + public List Players + { + get + { + if ( m_Players == null ) + return m_DefaultMobileList; + + return m_Players; + } + } + + public bool Active + { + get + { + return ( m_Active && m_Owner != Map.Internal ); + } + } + + public Map Owner + { + get + { + return m_Owner; + } + } + + public int X + { + get + { + return m_X; + } + } + + public int Y + { + get + { + return m_Y; + } + } + } +} \ No newline at end of file diff --git a/Server/SecureTrade.cs b/Server/SecureTrade.cs new file mode 100644 index 000000000..fbb744d25 --- /dev/null +++ b/Server/SecureTrade.cs @@ -0,0 +1,282 @@ +/*************************************************************************** + * SecureTrade.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: SecureTrade.cs 142 2006-06-08 17:27:25Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using Server.Items; +using Server.Network; + +namespace Server +{ + public class SecureTrade + { + private SecureTradeInfo m_From, m_To; + private bool m_Valid; + + public SecureTradeInfo From + { + get + { + return m_From; + } + } + + public SecureTradeInfo To + { + get + { + return m_To; + } + } + + public bool Valid + { + get + { + return m_Valid; + } + } + + public void Cancel() + { + if ( !m_Valid ) + return; + + List list = m_From.Container.Items; + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i < list.Count ) + { + Item item = list[i]; + + item.OnSecureTrade( m_From.Mobile, m_To.Mobile, m_From.Mobile, false ); + + if ( !item.Deleted ) + m_From.Mobile.AddToBackpack( item ); + } + } + + list = m_To.Container.Items; + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i < list.Count ) + { + Item item = list[i]; + + item.OnSecureTrade( m_To.Mobile, m_From.Mobile, m_To.Mobile, false ); + + if ( !item.Deleted ) + m_To.Mobile.AddToBackpack( item ); + } + } + + Close(); + } + + public void Close() + { + if ( !m_Valid ) + return; + + m_From.Mobile.Send( new CloseSecureTrade( m_From.Container ) ); + m_To.Mobile.Send( new CloseSecureTrade( m_To.Container ) ); + + m_Valid = false; + + NetState ns = m_From.Mobile.NetState; + + if ( ns != null ) + ns.RemoveTrade( this ); + + ns = m_To.Mobile.NetState; + + if ( ns != null ) + ns.RemoveTrade( this ); + + m_From.Container.Delete(); + m_To.Container.Delete(); + } + + public void Update() + { + if ( !m_Valid ) + return; + + if ( m_From.Accepted && m_To.Accepted ) + { + List list = m_From.Container.Items; + + bool allowed = true; + + for ( int i = list.Count - 1; allowed && i >= 0; --i ) + { + if ( i < list.Count ) + { + Item item = list[i]; + + if ( !item.AllowSecureTrade( m_From.Mobile, m_To.Mobile, m_To.Mobile, true ) ) + allowed = false; + } + } + + list = m_To.Container.Items; + + for ( int i = list.Count - 1; allowed && i >= 0; --i ) + { + if ( i < list.Count ) + { + Item item = list[i]; + + if ( !item.AllowSecureTrade( m_To.Mobile, m_From.Mobile, m_From.Mobile, true ) ) + allowed = false; + } + } + + if ( !allowed ) + { + m_From.Accepted = false; + m_To.Accepted = false; + + m_From.Mobile.Send( new UpdateSecureTrade( m_From.Container, m_From.Accepted, m_To.Accepted ) ); + m_To.Mobile.Send( new UpdateSecureTrade( m_To.Container, m_To.Accepted, m_From.Accepted ) ); + + return; + } + + list = m_From.Container.Items; + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i < list.Count ) + { + Item item = list[i]; + + item.OnSecureTrade( m_From.Mobile, m_To.Mobile, m_To.Mobile, true ); + + if ( !item.Deleted ) + m_To.Mobile.AddToBackpack( item ); + } + } + + list = m_To.Container.Items; + + for ( int i = list.Count - 1; i >= 0; --i ) + { + if ( i < list.Count ) + { + Item item = list[i]; + + item.OnSecureTrade( m_To.Mobile, m_From.Mobile, m_From.Mobile, true ); + + if ( !item.Deleted ) + m_From.Mobile.AddToBackpack( item ); + } + } + + Close(); + } + else + { + m_From.Mobile.Send( new UpdateSecureTrade( m_From.Container, m_From.Accepted, m_To.Accepted ) ); + m_To.Mobile.Send( new UpdateSecureTrade( m_To.Container, m_To.Accepted, m_From.Accepted ) ); + } + } + + public SecureTrade( Mobile from, Mobile to ) + { + m_Valid = true; + + m_From = new SecureTradeInfo( this, from, new SecureTradeContainer( this ) ); + m_To = new SecureTradeInfo( this, to, new SecureTradeContainer( this ) ); + + from.Send( new MobileStatus( from, to ) ); + from.Send( new UpdateSecureTrade( m_From.Container, false, false ) ); + from.Send( new SecureTradeEquip( m_To.Container, to ) ); + from.Send( new UpdateSecureTrade( m_From.Container, false, false ) ); + from.Send( new SecureTradeEquip( m_From.Container, from ) ); + from.Send( new DisplaySecureTrade( to, m_From.Container, m_To.Container, to.Name ) ); + from.Send( new UpdateSecureTrade( m_From.Container, false, false ) ); + + to.Send( new MobileStatus( to, from ) ); + to.Send( new UpdateSecureTrade( m_To.Container, false, false ) ); + to.Send( new SecureTradeEquip( m_From.Container, from ) ); + to.Send( new UpdateSecureTrade( m_To.Container, false, false ) ); + to.Send( new SecureTradeEquip( m_To.Container, to ) ); + to.Send( new DisplaySecureTrade( from, m_To.Container, m_From.Container, from.Name ) ); + to.Send( new UpdateSecureTrade( m_To.Container, false, false ) ); + } + } + + public class SecureTradeInfo + { + private SecureTrade m_Owner; + private Mobile m_Mobile; + private SecureTradeContainer m_Container; + private bool m_Accepted; + + public SecureTradeInfo( SecureTrade owner, Mobile m, SecureTradeContainer c ) + { + m_Owner = owner; + m_Mobile = m; + m_Container = c; + + m_Mobile.AddItem( m_Container ); + } + + public SecureTrade Owner + { + get + { + return m_Owner; + } + } + + public Mobile Mobile + { + get + { + return m_Mobile; + } + } + + public SecureTradeContainer Container + { + get + { + return m_Container; + } + } + + public bool Accepted + { + get + { + return m_Accepted; + } + set + { + m_Accepted = value; + } + } + } +} \ No newline at end of file diff --git a/Server/Serial.cs b/Server/Serial.cs new file mode 100644 index 000000000..020cbf03b --- /dev/null +++ b/Server/Serial.cs @@ -0,0 +1,172 @@ +/*************************************************************************** + * Serial.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Serial.cs 36 2006-01-18 10:19:46Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public struct Serial : IComparable, IComparable + { + private int m_Serial; + + private static Serial m_LastMobile = Zero; + private static Serial m_LastItem = 0x40000000; + + public static Serial LastMobile { get { return m_LastMobile; } } + public static Serial LastItem { get { return m_LastItem; } } + + public static readonly Serial MinusOne = new Serial( -1 ); + public static readonly Serial Zero = new Serial( 0 ); + + public static Serial NewMobile + { + get + { + while ( World.FindMobile( m_LastMobile = (m_LastMobile + 1) ) != null ); + + return m_LastMobile; + } + } + + public static Serial NewItem + { + get + { + while ( World.FindItem( m_LastItem = (m_LastItem + 1) ) != null ); + + return m_LastItem; + } + } + + private Serial( int serial ) + { + m_Serial = serial; + } + + public int Value + { + get + { + return m_Serial; + } + } + + public bool IsMobile + { + get + { + return ( m_Serial > 0 && m_Serial < 0x40000000 ); + } + } + + public bool IsItem + { + get + { + return ( m_Serial >= 0x40000000 && m_Serial <= 0x7FFFFFFF ); + } + } + + public bool IsValid + { + get + { + return ( m_Serial > 0 ); + } + } + + public override int GetHashCode() + { + return m_Serial; + } + + public int CompareTo( Serial other ) + { + return m_Serial.CompareTo( other.m_Serial ); + } + + public int CompareTo( object other ) + { + if ( other is Serial ) + return this.CompareTo( (Serial) other ); + else if ( other == null ) + return -1; + + throw new ArgumentException(); + } + + public override bool Equals( object o ) + { + if ( o == null || !(o is Serial) ) return false; + + return ((Serial)o).m_Serial == m_Serial; + } + + public static bool operator == ( Serial l, Serial r ) + { + return l.m_Serial == r.m_Serial; + } + + public static bool operator != ( Serial l, Serial r ) + { + return l.m_Serial != r.m_Serial; + } + + public static bool operator > ( Serial l, Serial r ) + { + return l.m_Serial > r.m_Serial; + } + + public static bool operator < ( Serial l, Serial r ) + { + return l.m_Serial < r.m_Serial; + } + + public static bool operator >= ( Serial l, Serial r ) + { + return l.m_Serial >= r.m_Serial; + } + + public static bool operator <= ( Serial l, Serial r ) + { + return l.m_Serial <= r.m_Serial; + } + + /*public static Serial operator ++ ( Serial l ) + { + return new Serial( l + 1 ); + }*/ + + public override string ToString() + { + return String.Format( "0x{0:X8}", m_Serial ); + } + + public static implicit operator int( Serial a ) + { + return a.m_Serial; + } + + public static implicit operator Serial( int a ) + { + return new Serial( a ); + } + } +} \ No newline at end of file diff --git a/Server/Serialization.cs b/Server/Serialization.cs new file mode 100644 index 000000000..b31576db5 --- /dev/null +++ b/Server/Serialization.cs @@ -0,0 +1,1706 @@ +/*************************************************************************** + * Serialization.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Serialization.cs 153 2006-06-11 05:22:23Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Reflection; +using System.Threading; +using System.Net; + +using Server; +using Server.Guilds; + +namespace Server +{ + public abstract class GenericReader + { + public GenericReader() { } + + public abstract string ReadString(); + public abstract DateTime ReadDateTime(); + public abstract TimeSpan ReadTimeSpan(); + public abstract DateTime ReadDeltaTime(); + public abstract decimal ReadDecimal(); + public abstract long ReadLong(); + public abstract ulong ReadULong(); + public abstract int ReadInt(); + public abstract uint ReadUInt(); + public abstract short ReadShort(); + public abstract ushort ReadUShort(); + public abstract double ReadDouble(); + public abstract float ReadFloat(); + public abstract char ReadChar(); + public abstract byte ReadByte(); + public abstract sbyte ReadSByte(); + public abstract bool ReadBool(); + public abstract int ReadEncodedInt(); + public abstract IPAddress ReadIPAddress(); + + public abstract Point3D ReadPoint3D(); + public abstract Point2D ReadPoint2D(); + public abstract Rectangle2D ReadRect2D(); + public abstract Map ReadMap(); + + public abstract Item ReadItem(); + public abstract Mobile ReadMobile(); + public abstract BaseGuild ReadGuild(); + + public abstract T ReadItem() where T : Item; + public abstract T ReadMobile() where T : Mobile; + public abstract T ReadGuild() where T : BaseGuild; + + public abstract ArrayList ReadItemList(); + public abstract ArrayList ReadMobileList(); + public abstract ArrayList ReadGuildList(); + + public abstract List ReadStrongItemList(); + public abstract List ReadStrongItemList() where T : Item; + + public abstract List ReadStrongMobileList(); + public abstract List ReadStrongMobileList() where T : Mobile; + + public abstract List ReadStrongGuildList(); + public abstract List ReadStrongGuildList() where T : BaseGuild; + + public abstract Race ReadRace(); + + public abstract bool End(); + } + + public abstract class GenericWriter + { + public GenericWriter() { } + + public abstract void Close(); + + public abstract long Position { get; } + + public abstract void Write( string value ); + public abstract void Write( DateTime value ); + public abstract void Write( TimeSpan value ); + public abstract void Write( decimal value ); + public abstract void Write( long value ); + public abstract void Write( ulong value ); + public abstract void Write( int value ); + public abstract void Write( uint value ); + public abstract void Write( short value ); + public abstract void Write( ushort value); + public abstract void Write( double value ); + public abstract void Write( float value ); + public abstract void Write( char value ); + public abstract void Write( byte value ); + public abstract void Write( sbyte value ); + public abstract void Write( bool value ); + public abstract void WriteEncodedInt( int value ); + public abstract void Write( IPAddress value ); + + public abstract void WriteDeltaTime( DateTime value ); + + public abstract void Write( Point3D value ); + public abstract void Write( Point2D value ); + public abstract void Write( Rectangle2D value ); + public abstract void Write( Map value ); + + public abstract void Write( Item value ); + public abstract void Write( Mobile value ); + public abstract void Write( BaseGuild value ); + + public abstract void WriteItem( T value ) where T : Item; + public abstract void WriteMobile( T value ) where T : Mobile; + public abstract void WriteGuild( T value ) where T : BaseGuild; + + public abstract void Write( Race value ); + + public abstract void WriteItemList( ArrayList list ); + public abstract void WriteItemList( ArrayList list, bool tidy ); + + public abstract void WriteMobileList( ArrayList list ); + public abstract void WriteMobileList( ArrayList list, bool tidy ); + + public abstract void WriteGuildList( ArrayList list ); + public abstract void WriteGuildList( ArrayList list, bool tidy ); + + public abstract void Write( List list ); + public abstract void Write( List list, bool tidy ); + + public abstract void WriteItemList( List list ) where T : Item; + public abstract void WriteItemList( List list, bool tidy ) where T : Item; + + public abstract void Write( List list ); + public abstract void Write( List list, bool tidy ); + + public abstract void WriteMobileList( List list ) where T : Mobile; + public abstract void WriteMobileList( List list, bool tidy ) where T : Mobile; + + public abstract void Write( List list ); + public abstract void Write( List list, bool tidy ); + + public abstract void WriteGuildList( List list ) where T : BaseGuild; + public abstract void WriteGuildList( List list, bool tidy ) where T : BaseGuild; + + //Stupid compiler won't notice there 'where' to differentiate the generic methods. + } + + public class BinaryFileWriter : GenericWriter + { + private bool PrefixStrings; + private Stream m_File; + + protected virtual int BufferSize { + get { + return 64 * 1024; + } + } + + private byte[] m_Buffer; + + private int m_Index; + + private Encoding m_Encoding; + + public BinaryFileWriter( Stream strm, bool prefixStr ) + { + PrefixStrings = prefixStr; + m_Encoding = Utility.UTF8; + m_Buffer = new byte[BufferSize]; + m_File = strm; + } + + public BinaryFileWriter( string filename, bool prefixStr ) + { + PrefixStrings = prefixStr; + m_Buffer = new byte[BufferSize]; + m_File = new FileStream( filename, FileMode.Create, FileAccess.Write, FileShare.None ); + m_Encoding = Utility.UTF8WithEncoding; + } + + public void Flush() + { + if ( m_Index > 0 ) + { + m_Position += m_Index; + + m_File.Write( m_Buffer, 0, m_Index ); + m_Index = 0; + } + } + + private long m_Position; + + public override long Position + { + get + { + return m_Position + m_Index; + } + } + + public Stream UnderlyingStream + { + get + { + if ( m_Index > 0 ) + Flush(); + + return m_File; + } + } + + public override void Close() + { + if ( m_Index > 0 ) + Flush(); + + m_File.Close(); + } + + public override void WriteEncodedInt( int value ) + { + uint v = (uint) value; + + while ( v >= 0x80 ) + { + if ( (m_Index + 1) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = (byte) (v | 0x80); + v >>= 7; + } + + if ( ( m_Index + 1 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = (byte) v; + } + + private byte[] m_CharacterBuffer; + private int m_MaxBufferChars; + private const int LargeByteBufferSize = 256; + + internal void InternalWriteString( string value ) + { + int length = m_Encoding.GetByteCount( value ); + + WriteEncodedInt( length ); + + if ( m_CharacterBuffer == null ) + { + m_CharacterBuffer = new byte[LargeByteBufferSize]; + m_MaxBufferChars = LargeByteBufferSize / m_Encoding.GetMaxByteCount( 1 ); + } + + if ( length > LargeByteBufferSize ) + { + int current = 0; + int charsLeft = value.Length; + + while ( charsLeft > 0 ) + { + int charCount = ( charsLeft > m_MaxBufferChars ) ? m_MaxBufferChars : charsLeft; + int byteLength = m_Encoding.GetBytes( value, current, charCount, m_CharacterBuffer, 0 ); + + if ( ( m_Index + byteLength ) > m_Buffer.Length ) + Flush(); + + Buffer.BlockCopy( m_CharacterBuffer, 0, m_Buffer, m_Index, byteLength ); + m_Index += byteLength; + + current += charCount; + charsLeft -= charCount; + } + } + else + { + int byteLength = m_Encoding.GetBytes( value, 0, value.Length, m_CharacterBuffer, 0 ); + + if ( ( m_Index + byteLength ) > m_Buffer.Length ) + Flush(); + + Buffer.BlockCopy( m_CharacterBuffer, 0, m_Buffer, m_Index, byteLength ); + m_Index += byteLength; + } + } + + public override void Write( string value ) + { + if ( PrefixStrings ) + { + if ( value == null ) + { + if ( ( m_Index + 1 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = 0; + } + else + { + if ( ( m_Index + 1 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = 1; + + InternalWriteString( value ); + } + } + else + { + InternalWriteString( value ); + } + } + + public override void Write( DateTime value ) + { + Write( value.Ticks ); + } + + public override void WriteDeltaTime( DateTime value ) + { + long ticks = value.Ticks; + long now = DateTime.Now.Ticks; + + TimeSpan d; + + try{ d = new TimeSpan( ticks-now ); } + catch{ if ( ticks < now ) d = TimeSpan.MaxValue; else d = TimeSpan.MaxValue; } + + Write( d ); + } + + public override void Write( IPAddress value ) + { + Write( Utility.GetLongAddressValue( value ) ); + } + + public override void Write( TimeSpan value ) + { + Write( value.Ticks ); + } + + public override void Write( decimal value ) + { + int[] bits = Decimal.GetBits( value ); + + for ( int i = 0; i < bits.Length; ++i ) + Write( bits[i] ); + } + + public override void Write( long value ) + { + if ( ( m_Index + 8 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index ] = (byte) value; + m_Buffer[m_Index + 1] = (byte)(value >> 8); + m_Buffer[m_Index + 2] = (byte)(value >> 16); + m_Buffer[m_Index + 3] = (byte)(value >> 24); + m_Buffer[m_Index + 4] = (byte)(value >> 32); + m_Buffer[m_Index + 5] = (byte)(value >> 40); + m_Buffer[m_Index + 6] = (byte)(value >> 48); + m_Buffer[m_Index + 7] = (byte)(value >> 56); + m_Index += 8; + } + + public override void Write( ulong value ) + { + if ( ( m_Index + 8 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index ] = (byte) value; + m_Buffer[m_Index + 1] = (byte)(value >> 8); + m_Buffer[m_Index + 2] = (byte)(value >> 16); + m_Buffer[m_Index + 3] = (byte)(value >> 24); + m_Buffer[m_Index + 4] = (byte)(value >> 32); + m_Buffer[m_Index + 5] = (byte)(value >> 40); + m_Buffer[m_Index + 6] = (byte)(value >> 48); + m_Buffer[m_Index + 7] = (byte)(value >> 56); + m_Index += 8; + } + + public override void Write( int value ) + { + if ( ( m_Index + 4 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index ] = (byte) value; + m_Buffer[m_Index + 1] = (byte)(value >> 8); + m_Buffer[m_Index + 2] = (byte)(value >> 16); + m_Buffer[m_Index + 3] = (byte)(value >> 24); + m_Index += 4; + } + + public override void Write( uint value ) + { + if ( ( m_Index + 4 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index ] = (byte) value; + m_Buffer[m_Index + 1] = (byte)(value >> 8); + m_Buffer[m_Index + 2] = (byte)(value >> 16); + m_Buffer[m_Index + 3] = (byte)(value >> 24); + m_Index += 4; + } + + public override void Write( short value ) + { + if ( ( m_Index + 2 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index ] = (byte) value; + m_Buffer[m_Index + 1] = (byte)(value >> 8); + m_Index += 2; + } + + public override void Write( ushort value ) + { + if ( ( m_Index + 2 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index ] = (byte) value; + m_Buffer[m_Index + 1] = (byte)(value >> 8); + m_Index += 2; + } + + public unsafe override void Write( double value ) + { + if ( ( m_Index + 8 ) > m_Buffer.Length ) + Flush(); + + fixed ( byte *pBuffer = m_Buffer ) + *((double *)(pBuffer + m_Index)) = value; + + m_Index += 8; + } + + public unsafe override void Write( float value ) + { + if ( ( m_Index + 4 ) > m_Buffer.Length ) + Flush(); + + fixed ( byte *pBuffer = m_Buffer ) + *((float *)(pBuffer + m_Index)) = value; + + m_Index += 4; + } + + private char[] m_SingleCharBuffer = new char[1]; + + public override void Write( char value ) + { + if ( ( m_Index + 8 ) > m_Buffer.Length ) + Flush(); + + m_SingleCharBuffer[0] = value; + + int byteCount = m_Encoding.GetBytes( m_SingleCharBuffer, 0, 1, m_Buffer, m_Index ); + m_Index += byteCount; + } + + public override void Write( byte value ) + { + if ( ( m_Index + 1 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = value; + } + + public override void Write( sbyte value ) + { + if ( ( m_Index + 1 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = (byte) value; + } + + public override void Write( bool value ) + { + if ( ( m_Index + 1 ) > m_Buffer.Length ) + Flush(); + + m_Buffer[m_Index++] = (byte) (value ? 1 : 0); + } + + public override void Write( Point3D value ) + { + Write( value.m_X ); + Write( value.m_Y ); + Write( value.m_Z ); + } + + public override void Write( Point2D value ) + { + Write( value.m_X ); + Write( value.m_Y ); + } + + public override void Write( Rectangle2D value ) + { + Write( value.Start ); + Write( value.End ); + } + + public override void Write( Map value ) + { + if ( value != null ) + Write( (byte)value.MapIndex ); + else + Write( (byte)0xFF ); + } + + public override void Write( Item value ) + { + if ( value == null || value.Deleted ) + Write( Serial.MinusOne ); + else + Write( value.Serial ); + } + + public override void Write( Mobile value ) + { + if ( value == null || value.Deleted ) + Write( Serial.MinusOne ); + else + Write( value.Serial ); + } + + public override void Write( BaseGuild value ) + { + if ( value == null ) + Write( 0 ); + else + Write( value.Id ); + } + + public override void WriteItem( T value ) + { + Write( value ); + } + + public override void WriteMobile( T value ) + { + Write( value ); + } + + public override void WriteGuild( T value ) + { + Write( value ); + } + + public override void WriteMobileList( ArrayList list ) + { + WriteMobileList( list, false ); + } + + public override void WriteMobileList( ArrayList list, bool tidy ) + { + if ( tidy ) + { + for ( int i = 0; i < list.Count; ) + { + if ( ((Mobile)list[i]).Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + Write( (Mobile)list[i] ); + } + + public override void WriteItemList( ArrayList list ) + { + WriteItemList( list, false ); + } + + public override void WriteItemList( ArrayList list, bool tidy ) + { + if ( tidy ) + { + for ( int i = 0; i < list.Count; ) + { + if ( ((Item)list[i]).Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + Write( (Item)list[i] ); + } + + public override void WriteGuildList( ArrayList list ) + { + WriteGuildList( list, false ); + } + + public override void WriteGuildList( ArrayList list, bool tidy ) + { + if ( tidy ) + { + for ( int i = 0; i < list.Count; ) + { + if ( ((BaseGuild)list[i]).Disbanded ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + Write( (BaseGuild)list[i] ); + } + + public override void Write( List list ) + { + Write( list, false ); + } + + public override void Write( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void WriteItemList( List list ) + { + WriteItemList( list, false ); + } + public override void WriteItemList( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void Write( List list ) + { + Write( list, false ); + } + + public override void Write( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void WriteMobileList( List list ) + { + WriteMobileList( list, false ); + } + + public override void WriteMobileList( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void Write( List list ) + { + Write( list, false ); + } + + public override void Write( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Disbanded ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void WriteGuildList( List list ) + { + WriteGuildList( list, false ); + } + + public override void WriteGuildList( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Disbanded ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void Write( Race value ) + { + if ( value != null ) + Write( (byte)value.RaceIndex ); + else + Write( (byte)0xFF ); + } + } + + public class BinaryFileReader : GenericReader + { + private BinaryReader m_File; + + public BinaryFileReader( BinaryReader br ) { m_File = br; } + + public void Close() + { + m_File.Close(); + } + + public long Position + { + get + { + return m_File.BaseStream.Position; + } + } + + public long Seek( long offset, SeekOrigin origin ) + { + return m_File.BaseStream.Seek( offset, origin ); + } + + public override string ReadString() + { + if ( ReadByte() != 0 ) + return m_File.ReadString(); + else + return null; + } + + public override DateTime ReadDeltaTime() + { + long ticks = m_File.ReadInt64(); + long now = DateTime.Now.Ticks; + + if ( ticks > 0 && (ticks+now) < 0 ) + return DateTime.MaxValue; + else if ( ticks < 0 && (ticks+now) < 0 ) + return DateTime.MinValue; + + try{ return new DateTime( now+ticks ); } + catch{ if ( ticks > 0 ) return DateTime.MaxValue; else return DateTime.MinValue; } + } + + public override IPAddress ReadIPAddress() + { + return new IPAddress( m_File.ReadInt64() ); + } + + public override int ReadEncodedInt() + { + int v = 0, shift = 0; + byte b; + + do + { + b = m_File.ReadByte(); + v |= (b & 0x7F) << shift; + shift += 7; + } while ( b >= 0x80 ); + + return v; + } + + public override DateTime ReadDateTime() + { + return new DateTime( m_File.ReadInt64() ); + } + + public override TimeSpan ReadTimeSpan() + { + return new TimeSpan( m_File.ReadInt64() ); + } + + public override decimal ReadDecimal() + { + return m_File.ReadDecimal(); + } + + public override long ReadLong() + { + return m_File.ReadInt64(); + } + + public override ulong ReadULong() + { + return m_File.ReadUInt64(); + } + + public override int ReadInt() + { + return m_File.ReadInt32(); + } + + public override uint ReadUInt() + { + return m_File.ReadUInt32(); + } + + public override short ReadShort() + { + return m_File.ReadInt16(); + } + + public override ushort ReadUShort() + { + return m_File.ReadUInt16(); + } + + public override double ReadDouble() + { + return m_File.ReadDouble(); + } + + public override float ReadFloat() + { + return m_File.ReadSingle(); + } + + public override char ReadChar() + { + return m_File.ReadChar(); + } + + public override byte ReadByte() + { + return m_File.ReadByte(); + } + + public override sbyte ReadSByte() + { + return m_File.ReadSByte(); + } + + public override bool ReadBool() + { + return m_File.ReadBoolean(); + } + + public override Point3D ReadPoint3D() + { + return new Point3D( ReadInt(), ReadInt(), ReadInt() ); + } + + public override Point2D ReadPoint2D() + { + return new Point2D( ReadInt(), ReadInt() ); + } + + public override Rectangle2D ReadRect2D() + { + return new Rectangle2D( ReadPoint2D(), ReadPoint2D() ); + } + + public override Map ReadMap() + { + return Map.Maps[ReadByte()]; + } + + public override Item ReadItem() + { + return World.FindItem( ReadInt() ); + } + + public override Mobile ReadMobile() + { + return World.FindMobile( ReadInt() ); + } + + public override BaseGuild ReadGuild() + { + return BaseGuild.Find( ReadInt() ); + } + + public override T ReadItem() + { + return ReadItem() as T; + } + + public override T ReadMobile() + { + return ReadMobile() as T; + } + + public override T ReadGuild() + { + return ReadGuild() as T; + } + + public override ArrayList ReadItemList() + { + int count = ReadInt(); + + ArrayList list = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + Item item = ReadItem(); + + if ( item != null ) + list.Add( item ); + } + + return list; + } + + public override ArrayList ReadMobileList() + { + int count = ReadInt(); + + ArrayList list = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + Mobile m = ReadMobile(); + + if ( m != null ) + list.Add( m ); + } + + return list; + } + + public override ArrayList ReadGuildList() + { + int count = ReadInt(); + + ArrayList list = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) + { + BaseGuild g = ReadGuild(); + + if ( g != null ) + list.Add( g ); + } + + return list; + } + + public override List ReadStrongItemList() + { + int count = ReadInt(); + + List list = new List( count ); + + for( int i = 0; i < count; ++i ) + { + Item item = ReadItem(); + + if( item != null ) + list.Add( item ); + } + + return list; + } + + public override List ReadStrongItemList() + { + int count = ReadInt(); + + List list = new List( count ); + + for( int i = 0; i < count; ++i ) + { + T item = ReadItem() as T; + + if( item != null ) + list.Add( item ); + } + + return list; + } + + public override List ReadStrongMobileList() + { + int count = ReadInt(); + + List list = new List( count ); + + for( int i = 0; i < count; ++i ) + { + Mobile m = ReadMobile(); + + if( m != null ) + list.Add( m ); + } + + return list; + } + + public override List ReadStrongMobileList() + { + int count = ReadInt(); + + List list = new List( count ); + + for( int i = 0; i < count; ++i ) + { + T m = ReadMobile() as T; + + if( m != null ) + list.Add( m ); + } + + return list; + } + + public override List ReadStrongGuildList() + { + int count = ReadInt(); + + List list = new List( count ); + + for( int i = 0; i < count; ++i ) + { + BaseGuild g = ReadGuild(); + + if( g != null ) + list.Add( g ); + } + + return list; + } + + public override List ReadStrongGuildList() + { + int count = ReadInt(); + + List list = new List( count ); + + for( int i = 0; i < count; ++i ) + { + T g = ReadGuild() as T; + + if( g != null ) + list.Add( g ); + } + + return list; + } + + public override bool End() + { + return m_File.PeekChar() == -1; + } + + public override Race ReadRace() + { + return Race.Races[ReadByte()]; + } + } + + public class AsyncWriter : GenericWriter + { + private static int m_ThreadCount = 0; + public static int ThreadCount { get { return m_ThreadCount; } } + + + private int BufferSize; + + private long m_LastPos, m_CurPos; + private bool m_Closed; + private bool PrefixStrings; + + private MemoryStream m_Mem; + private BinaryWriter m_Bin; + private FileStream m_File; + + private Queue m_WriteQueue; + private Thread m_WorkerThread; + + public AsyncWriter( string filename, bool prefix ) : this(filename, 1048576, prefix)//1 mb buffer + { + } + + public AsyncWriter( string filename, int buffSize, bool prefix ) + { + PrefixStrings = prefix; + m_Closed = false; + m_WriteQueue = Queue.Synchronized( new Queue() ); + BufferSize = buffSize; + + m_File = new FileStream( filename, FileMode.Create, FileAccess.Write, FileShare.None ); + m_Mem = new MemoryStream( BufferSize + 1024 ); + m_Bin = new BinaryWriter( m_Mem, Utility.UTF8WithEncoding ); + } + + private void Enqueue( MemoryStream mem ) + { + m_WriteQueue.Enqueue( mem ); + + if ( m_WorkerThread == null || !m_WorkerThread.IsAlive ) + { + m_WorkerThread = new Thread( new ThreadStart( new WorkerThread( this ).Worker ) ); + m_WorkerThread.Priority = ThreadPriority.BelowNormal; + m_WorkerThread.Start(); + } + } + + private class WorkerThread + { + private AsyncWriter m_Owner; + + public WorkerThread( AsyncWriter owner ) + { + m_Owner = owner; + } + + public void Worker() + { + AsyncWriter.m_ThreadCount++; + while ( m_Owner.m_WriteQueue.Count > 0 ) + { + MemoryStream mem = (MemoryStream)m_Owner.m_WriteQueue.Dequeue(); + + if ( mem != null && mem.Length > 0 ) + mem.WriteTo( m_Owner.m_File ); + } + + if ( m_Owner.m_Closed ) + m_Owner.m_File.Close(); + AsyncWriter.m_ThreadCount--; + } + } + + private void OnWrite() + { + long curlen = m_Mem.Length; + m_CurPos += curlen - m_LastPos; + m_LastPos = curlen; + if ( curlen >= BufferSize ) + { + Enqueue( m_Mem ); + m_Mem = new MemoryStream( BufferSize + 1024 ); + m_Bin = new BinaryWriter( m_Mem, Utility.UTF8WithEncoding ); + m_LastPos = 0; + } + } + + public MemoryStream MemStream + { + get + { + return m_Mem; + } + set + { + if ( m_Mem.Length > 0 ) + Enqueue( m_Mem ); + + m_Mem = value; + m_Bin = new BinaryWriter( m_Mem, Utility.UTF8WithEncoding ); + m_LastPos = 0; + m_CurPos = m_Mem.Length; + m_Mem.Seek( 0, SeekOrigin.End ); + } + } + + public override void Close() + { + Enqueue( m_Mem ); + m_Closed = true; + } + + public override long Position + { + get + { + return m_CurPos; + } + } + + public override void Write( IPAddress value ) + { + m_Bin.Write( Utility.GetLongAddressValue( value ) ); + OnWrite(); + } + + public override void Write( string value ) + { + if ( PrefixStrings ) + { + if ( value == null ) + { + m_Bin.Write( (byte)0 ); + } + else + { + m_Bin.Write( (byte)1 ); + m_Bin.Write( value ); + } + } + else + { + m_Bin.Write( value ); + } + OnWrite(); + } + + public override void WriteDeltaTime( DateTime value ) + { + long ticks = value.Ticks; + long now = DateTime.Now.Ticks; + + TimeSpan d; + + try{ d = new TimeSpan( ticks-now ); } + catch{ if ( ticks < now ) d = TimeSpan.MaxValue; else d = TimeSpan.MaxValue; } + + Write( d ); + } + + public override void Write( DateTime value ) + { + m_Bin.Write( value.Ticks ); + OnWrite(); + } + + public override void Write( TimeSpan value ) + { + m_Bin.Write( value.Ticks ); + OnWrite(); + } + + public override void Write( decimal value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( long value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( ulong value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void WriteEncodedInt( int value ) + { + uint v = (uint) value; + + while ( v >= 0x80 ) + { + m_Bin.Write( (byte) (v | 0x80) ); + v >>= 7; + } + + m_Bin.Write( (byte) v); + OnWrite(); + } + + public override void Write( int value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( uint value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( short value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( ushort value) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( double value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( float value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( char value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( byte value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( sbyte value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( bool value ) + { + m_Bin.Write( value ); + OnWrite(); + } + + public override void Write( Point3D value ) + { + Write( value.m_X ); + Write( value.m_Y ); + Write( value.m_Z ); + } + + public override void Write( Point2D value ) + { + Write( value.m_X ); + Write( value.m_Y ); + } + + public override void Write( Rectangle2D value ) + { + Write( value.Start ); + Write( value.End ); + } + + public override void Write( Map value ) + { + if ( value != null ) + Write( (byte)value.MapIndex ); + else + Write( (byte)0xFF ); + } + + public override void Write( Race value ) + { + if ( value != null ) + Write( (byte)value.RaceIndex ); + else + Write( (byte)0xFF ); + } + + public override void Write( Item value ) + { + if ( value == null || value.Deleted ) + Write( Serial.MinusOne ); + else + Write( value.Serial ); + } + + public override void Write( Mobile value ) + { + if ( value == null || value.Deleted ) + Write( Serial.MinusOne ); + else + Write( value.Serial ); + } + + public override void Write( BaseGuild value ) + { + if ( value == null ) + Write( 0 ); + else + Write( value.Id ); + } + + public override void WriteItem( T value ) + { + Write( value ); + } + + public override void WriteMobile( T value ) + { + Write( value ); + } + + public override void WriteGuild( T value ) + { + Write( value ); + } + + public override void WriteMobileList( ArrayList list ) + { + WriteMobileList( list, false ); + } + + public override void WriteMobileList( ArrayList list, bool tidy ) + { + if ( tidy ) + { + for ( int i = 0; i < list.Count; ) + { + if ( ((Mobile)list[i]).Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + Write( (Mobile)list[i] ); + } + + public override void WriteItemList( ArrayList list ) + { + WriteItemList( list, false ); + } + + public override void WriteItemList( ArrayList list, bool tidy ) + { + if ( tidy ) + { + for ( int i = 0; i < list.Count; ) + { + if ( ((Item)list[i]).Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + Write( (Item)list[i] ); + } + + public override void WriteGuildList( ArrayList list ) + { + WriteGuildList( list, false ); + } + + public override void WriteGuildList( ArrayList list, bool tidy ) + { + if ( tidy ) + { + for ( int i = 0; i < list.Count; ) + { + if ( ((BaseGuild)list[i]).Disbanded ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) + Write( (BaseGuild)list[i] ); + } + + public override void Write( List list ) + { + Write( list, false ); + } + + public override void Write( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void WriteItemList( List list ) + { + WriteItemList( list, false ); + } + public override void WriteItemList( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void Write( List list ) + { + Write( list, false ); + } + + public override void Write( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void WriteMobileList( List list ) + { + WriteMobileList( list, false ); + } + + public override void WriteMobileList( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Deleted ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void Write( List list ) + { + Write( list, false ); + } + + public override void Write( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Disbanded ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + + public override void WriteGuildList( List list ) + { + WriteGuildList( list, false ); + } + + public override void WriteGuildList( List list, bool tidy ) + { + if( tidy ) + { + for( int i = 0; i < list.Count; ) + { + if( list[i].Disbanded ) + list.RemoveAt( i ); + else + ++i; + } + } + + Write( list.Count ); + + for( int i = 0; i < list.Count; ++i ) + Write( list[i] ); + } + } + + public interface ISerializable { + int TypeReference { get; } + int SerialIdentity { get; } + void Serialize( GenericWriter writer ); + } +} diff --git a/Server/Skills.cs b/Server/Skills.cs new file mode 100644 index 000000000..9d29c0423 --- /dev/null +++ b/Server/Skills.cs @@ -0,0 +1,1100 @@ +/*************************************************************************** + * Skills.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Skills.cs 109 2006-02-12 11:45:37Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using Server.Network; +using System.Xml; + +namespace Server +{ + public delegate TimeSpan SkillUseCallback( Mobile user ); + + public enum SkillLock : byte + { + Up = 0, + Down = 1, + Locked = 2 + } + + public enum SkillName + { + Alchemy = 0, + Anatomy = 1, + AnimalLore = 2, + ItemID = 3, + ArmsLore = 4, + Parry = 5, + Begging = 6, + Blacksmith = 7, + Fletching = 8, + Peacemaking = 9, + Camping = 10, + Carpentry = 11, + Cartography = 12, + Cooking = 13, + DetectHidden = 14, + Discordance = 15, + EvalInt = 16, + Healing = 17, + Fishing = 18, + Forensics = 19, + Herding = 20, + Hiding = 21, + Provocation = 22, + Inscribe = 23, + Lockpicking = 24, + Magery = 25, + MagicResist = 26, + Tactics = 27, + Snooping = 28, + Musicianship = 29, + Poisoning = 30, + Archery = 31, + SpiritSpeak = 32, + Stealing = 33, + Tailoring = 34, + AnimalTaming = 35, + TasteID = 36, + Tinkering = 37, + Tracking = 38, + Veterinary = 39, + Swords = 40, + Macing = 41, + Fencing = 42, + Wrestling = 43, + Lumberjacking = 44, + Mining = 45, + Meditation = 46, + Stealth = 47, + RemoveTrap = 48, + Necromancy = 49, + Focus = 50, + Chivalry = 51, + Bushido = 52, + Ninjitsu = 53, + Spellweaving = 54 + } + + [PropertyObject] + public class Skill + { + private Skills m_Owner; + private SkillInfo m_Info; + private ushort m_Base; + private ushort m_Cap; + private SkillLock m_Lock; + + public override string ToString() + { + return String.Format( "[{0}: {1}]", Name, Base ); + } + + public Skill( Skills owner, SkillInfo info, GenericReader reader ) + { + m_Owner = owner; + m_Info = info; + + int version = reader.ReadByte(); + + switch ( version ) + { + case 0: + { + m_Base = reader.ReadUShort(); + m_Cap = reader.ReadUShort(); + m_Lock = (SkillLock)reader.ReadByte(); + + break; + } + case 0xFF: + { + m_Base = 0; + m_Cap = 1000; + m_Lock = SkillLock.Up; + + break; + } + default: + { + if ( (version & 0xC0) == 0x00 ) + { + if ( (version & 0x1) != 0 ) + m_Base = reader.ReadUShort(); + + if ( (version & 0x2) != 0 ) + m_Cap = reader.ReadUShort(); + else + m_Cap = 1000; + + if ( (version & 0x4) != 0 ) + m_Lock = (SkillLock)reader.ReadByte(); + } + + break; + } + } + + if ( m_Lock < SkillLock.Up || m_Lock > SkillLock.Locked ) + { + Console.WriteLine( "Bad skill lock -> {0}.{1}", owner.Owner, m_Lock ); + m_Lock = SkillLock.Up; + } + } + + public Skill( Skills owner, SkillInfo info, int baseValue, int cap, SkillLock skillLock ) + { + m_Owner = owner; + m_Info = info; + m_Base = (ushort)baseValue; + m_Cap = (ushort)cap; + m_Lock = skillLock; + } + + public void SetLockNoRelay( SkillLock skillLock ) + { + if ( skillLock < SkillLock.Up || skillLock > SkillLock.Locked ) + return; + + m_Lock = skillLock; + } + + public void Serialize( GenericWriter writer ) + { + if ( m_Base == 0 && m_Cap == 1000 && m_Lock == SkillLock.Up ) + { + writer.Write( (byte) 0xFF ); // default + } + else + { + int flags = 0x0; + + if ( m_Base != 0 ) + flags |= 0x1; + + if ( m_Cap != 1000 ) + flags |= 0x2; + + if ( m_Lock != SkillLock.Up ) + flags |= 0x4; + + writer.Write( (byte) flags ); // version + + if ( m_Base != 0 ) + writer.Write( (short) m_Base ); + + if ( m_Cap != 1000 ) + writer.Write( (short) m_Cap ); + + if ( m_Lock != SkillLock.Up ) + writer.Write( (byte) m_Lock ); + } + } + + public Skills Owner + { + get + { + return m_Owner; + } + } + + public SkillName SkillName + { + get + { + return (SkillName)m_Info.SkillID; + } + } + + public int SkillID + { + get + { + return m_Info.SkillID; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public string Name + { + get + { + return m_Info.Name; + } + } + + public SkillInfo Info + { + get + { + return m_Info; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public SkillLock Lock + { + get + { + return m_Lock; + } + } + + public int BaseFixedPoint + { + get + { + return m_Base; + } + set + { + if ( value < 0 ) + value = 0; + else if ( value >= 0x10000 ) + value = 0xFFFF; + + ushort sv = (ushort)value; + + int oldBase = m_Base; + + if ( m_Base != sv ) + { + m_Owner.Total = (m_Owner.Total - m_Base) + sv; + + m_Base = sv; + + m_Owner.OnSkillChange( this ); + + Mobile m = m_Owner.Owner; + + if ( m != null ) + m.OnSkillChange( SkillName, (double)oldBase / 10 ); + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public double Base + { + get + { + return ((double)m_Base / 10.0); + } + set + { + BaseFixedPoint = (int)(value * 10.0); + } + } + + public int CapFixedPoint + { + get + { + return m_Cap; + } + set + { + if ( value < 0 ) + value = 0; + else if ( value >= 0x10000 ) + value = 0xFFFF; + + ushort sv = (ushort)value; + + if ( m_Cap != sv ) + { + m_Cap = sv; + + m_Owner.OnSkillChange( this ); + } + } + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public double Cap + { + get + { + return ((double)m_Cap / 10.0); + } + set + { + CapFixedPoint = (int)(value * 10.0); + } + } + + private static bool m_UseStatMods; + + public static bool UseStatMods{ get{ return m_UseStatMods; } set{ m_UseStatMods = value; } } + + public int Fixed + { + get{ return (int)(Value * 10); } + } + + [CommandProperty( AccessLevel.Counselor )] + public double Value + { + get + { + //There has to be this distinction between the racial values and not to account for gaining skills and these skills aren't displayed nor Totaled up. + double value = this.NonRacialValue; + + double raceBonus = m_Owner.Owner.RacialSkillBonus; + + if( raceBonus > value ) + value = raceBonus; + + if ( value > Cap ) //The offchance someone sets their Cap lower than the racial skill bonus + value = Cap; + + return value; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public double NonRacialValue + { + get + { + double baseValue = Base; + double inv = 100.0 - baseValue; + + if( inv < 0.0 ) inv = 0.0; + + inv /= 100.0; + + double statsOffset = ((m_UseStatMods ? m_Owner.Owner.Str : m_Owner.Owner.RawStr) * m_Info.StrScale) + ((m_UseStatMods ? m_Owner.Owner.Dex : m_Owner.Owner.RawDex) * m_Info.DexScale) + ((m_UseStatMods ? m_Owner.Owner.Int : m_Owner.Owner.RawInt) * m_Info.IntScale); + double statTotal = m_Info.StatTotal * inv; + + statsOffset *= inv; + + if( statsOffset > statTotal ) + statsOffset = statTotal; + + double value = baseValue + statsOffset; + + m_Owner.Owner.ValidateSkillMods(); + + List mods = m_Owner.Owner.SkillMods; + + double bonusObey = 0.0, bonusNotObey = 0.0; + + for( int i = 0; i < mods.Count; ++i ) + { + SkillMod mod = mods[i]; + + if( mod.Skill == (SkillName)m_Info.SkillID ) + { + if( mod.Relative ) + { + if( mod.ObeyCap ) + bonusObey += mod.Value; + else + bonusNotObey += mod.Value; + } + else + { + bonusObey = 0.0; + bonusNotObey = 0.0; + value = mod.Value; + } + } + } + + value += bonusNotObey; + + if( value < Cap ) + { + value += bonusObey; + + if( value > Cap ) + value = Cap; + } + + return value; + } + } + + public void Update() + { + m_Owner.OnSkillChange( this ); + } + } + + public class SkillInfo + { + private int m_SkillID; + private string m_Name; + private string m_Title; + private double m_StrScale; + private double m_DexScale; + private double m_IntScale; + private double m_StatTotal; + private SkillUseCallback m_Callback; + private double m_StrGain; + private double m_DexGain; + private double m_IntGain; + private double m_GainFactor; + + public SkillInfo( int skillID, string name, double strScale, double dexScale, double intScale, string title, SkillUseCallback callback, double strGain, double dexGain, double intGain, double gainFactor ) + { + m_Name = name; + m_Title = title; + m_SkillID = skillID; + m_StrScale = strScale / 100.0; + m_DexScale = dexScale / 100.0; + m_IntScale = intScale / 100.0; + m_Callback = callback; + m_StrGain = strGain; + m_DexGain = dexGain; + m_IntGain = intGain; + m_GainFactor = gainFactor; + + m_StatTotal = strScale + dexScale + intScale; + } + + public SkillUseCallback Callback + { + get + { + return m_Callback; + } + set + { + m_Callback = value; + } + } + + public int SkillID + { + get + { + return m_SkillID; + } + } + + public string Name + { + get + { + return m_Name; + } + set + { + m_Name = value; + } + } + + public string Title + { + get + { + return m_Title; + } + set + { + m_Title = value; + } + } + + public double StrScale + { + get + { + return m_StrScale; + } + set + { + m_StrScale = value; + } + } + + public double DexScale + { + get + { + return m_DexScale; + } + set + { + m_DexScale = value; + } + } + + public double IntScale + { + get + { + return m_IntScale; + } + set + { + m_IntScale = value; + } + } + + public double StatTotal + { + get + { + return m_StatTotal; + } + set + { + m_StatTotal = value; + } + } + + public double StrGain + { + get + { + return m_StrGain; + } + set + { + m_StrGain = value; + } + } + + public double DexGain + { + get + { + return m_DexGain; + } + set + { + m_DexGain = value; + } + } + + public double IntGain + { + get + { + return m_IntGain; + } + set + { + m_IntGain = value; + } + } + + public double GainFactor + { + get + { + return m_GainFactor; + } + set + { + m_GainFactor = value; + } + } + + private static SkillInfo[] m_Table = new SkillInfo[55] + { + new SkillInfo( 0, "Alchemy", 0.0, 5.0, 5.0, "Alchemist", null, 0.0, 0.5, 0.5, 1.0 ), + new SkillInfo( 1, "Anatomy", 0.0, 0.0, 0.0, "Healer", null, 0.15, 0.15, 0.7, 1.0 ), + new SkillInfo( 2, "Animal Lore", 0.0, 0.0, 0.0, "Ranger", null, 0.0, 0.0, 1.0, 1.0 ), + new SkillInfo( 3, "Item Identification", 0.0, 0.0, 0.0, "Merchant", null, 0.0, 0.0, 1.0, 1.0 ), + new SkillInfo( 4, "Arms Lore", 0.0, 0.0, 0.0, "Warrior", null, 0.75, 0.15, 0.1, 1.0 ), + new SkillInfo( 5, "Parrying", 7.5, 2.5, 0.0, "Warrior", null, 0.75, 0.25, 0.0, 1.0 ), + new SkillInfo( 6, "Begging", 0.0, 0.0, 0.0, "Beggar", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 7, "Blacksmithy", 10.0, 0.0, 0.0, "Smith", null, 1.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 8, "Bowcraft/Fletching", 6.0, 16.0, 0.0, "Bowyer", null, 0.6, 1.6, 0.0, 1.0 ), + new SkillInfo( 9, "Peacemaking", 0.0, 0.0, 0.0, "Bard", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 10, "Camping", 20.0, 15.0, 15.0, "Ranger", null, 2.0, 1.5, 1.5, 1.0 ), + new SkillInfo( 11, "Carpentry", 20.0, 5.0, 0.0, "Carpenter", null, 2.0, 0.5, 0.0, 1.0 ), + new SkillInfo( 12, "Cartography", 0.0, 7.5, 7.5, "Cartographer", null, 0.0, 0.75, 0.75, 1.0 ), + new SkillInfo( 13, "Cooking", 0.0, 20.0, 30.0, "Chef", null, 0.0, 2.0, 3.0, 1.0 ), + new SkillInfo( 14, "Detecting Hidden", 0.0, 0.0, 0.0, "Scout", null, 0.0, 0.4, 0.6, 1.0 ), + new SkillInfo( 15, "Discordance", 0.0, 2.5, 2.5, "Bard", null, 0.0, 0.25, 0.25, 1.0 ), + new SkillInfo( 16, "Evaluating Intelligence", 0.0, 0.0, 0.0, "Scholar", null, 0.0, 0.0, 1.0, 1.0 ), + new SkillInfo( 17, "Healing", 6.0, 6.0, 8.0, "Healer", null, 0.6, 0.6, 0.8, 1.0 ), + new SkillInfo( 18, "Fishing", 0.0, 0.0, 0.0, "Fisherman", null, 0.5, 0.5, 0.0, 1.0 ), + new SkillInfo( 19, "Forensic Evaluation", 0.0, 0.0, 0.0, "Detective", null, 0.0, 0.2, 0.8, 1.0 ), + new SkillInfo( 20, "Herding", 16.25, 6.25, 2.5, "Shepherd", null, 1.625, 0.625, 0.25, 1.0 ), + new SkillInfo( 21, "Hiding", 0.0, 0.0, 0.0, "Rogue", null, 0.0, 0.8, 0.2, 1.0 ), + new SkillInfo( 22, "Provocation", 0.0, 4.5, 0.5, "Bard", null, 0.0, 0.45, 0.05, 1.0 ), + new SkillInfo( 23, "Inscription", 0.0, 2.0, 8.0, "Scribe", null, 0.0, 0.2, 0.8, 1.0 ), + new SkillInfo( 24, "Lockpicking", 0.0, 25.0, 0.0, "Rogue", null, 0.0, 2.0, 0.0, 1.0 ), + new SkillInfo( 25, "Magery", 0.0, 0.0, 15.0, "Mage", null, 0.0, 0.0, 1.5, 1.0 ), + new SkillInfo( 26, "Resisting Spells", 0.0, 0.0, 0.0, "Mage", null, 0.25, 0.25, 0.5, 1.0 ), + new SkillInfo( 27, "Tactics", 0.0, 0.0, 0.0, "Warrior", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 28, "Snooping", 0.0, 25.0, 0.0, "Pickpocket", null, 0.0, 2.5, 0.0, 1.0 ), + new SkillInfo( 29, "Musicianship", 0.0, 0.0, 0.0, "Bard", null, 0.0, 0.8, 0.2, 1.0 ), + new SkillInfo( 30, "Poisoning", 0.0, 4.0, 16.0, "Assassin", null, 0.0, 0.4, 1.6, 1.0 ), + new SkillInfo( 31, "Archery", 2.5, 7.5, 0.0, "Archer", null, 0.25, 0.75, 0.0, 1.0 ), + new SkillInfo( 32, "Spirit Speak", 0.0, 0.0, 0.0, "Medium", null, 0.0, 0.0, 1.0, 1.0 ), + new SkillInfo( 33, "Stealing", 0.0, 10.0, 0.0, "Rogue", null, 0.0, 1.0, 0.0, 1.0 ), + new SkillInfo( 34, "Tailoring", 3.75, 16.25, 5.0, "Tailor", null, 0.38, 1.63, 0.5, 1.0 ), + new SkillInfo( 35, "Animal Taming", 14.0, 2.0, 4.0, "Tamer", null, 1.4, 0.2, 0.4, 1.0 ), + new SkillInfo( 36, "Taste Identification", 0.0, 0.0, 0.0, "Chef", null, 0.2, 0.0, 0.8, 1.0 ), + new SkillInfo( 37, "Tinkering", 5.0, 2.0, 3.0, "Tinker", null, 0.5, 0.2, 0.3, 1.0 ), + new SkillInfo( 38, "Tracking", 0.0, 12.5, 12.5, "Ranger", null, 0.0, 1.25, 1.25, 1.0 ), + new SkillInfo( 39, "Veterinary", 8.0, 4.0, 8.0, "Veterinarian", null, 0.8, 0.4, 0.8, 1.0 ), + new SkillInfo( 40, "Swordsmanship", 7.5, 2.5, 0.0, "Swordsman", null, 0.75, 0.25, 0.0, 1.0 ), + new SkillInfo( 41, "Mace Fighting", 9.0, 1.0, 0.0, "Armsman", null, 0.9, 0.1, 0.0, 1.0 ), + new SkillInfo( 42, "Fencing", 4.5, 5.5, 0.0, "Fencer", null, 0.45, 0.55, 0.0, 1.0 ), + new SkillInfo( 43, "Wrestling", 9.0, 1.0, 0.0, "Wrestler", null, 0.9, 0.1, 0.0, 1.0 ), + new SkillInfo( 44, "Lumberjacking", 20.0, 0.0, 0.0, "Lumberjack", null, 2.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 45, "Mining", 20.0, 0.0, 0.0, "Miner", null, 2.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 46, "Meditation", 0.0, 0.0, 0.0, "Stoic", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 47, "Stealth", 0.0, 0.0, 0.0, "Rogue", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 48, "Remove Trap", 0.0, 0.0, 0.0, "Rogue", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 49, "Necromancy", 0.0, 0.0, 0.0, "Necromancer", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 50, "Focus", 0.0, 0.0, 0.0, "Stoic", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 51, "Chivalry", 0.0, 0.0, 0.0, "Paladin", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 52, "Bushido", 0.0, 0.0, 0.0, "Samurai", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 53, "Ninjitsu", 0.0, 0.0, 0.0, "Ninja", null, 0.0, 0.0, 0.0, 1.0 ), + new SkillInfo( 54, "Spellweaving", 0.0, 0.0, 0.0, "Arcanist", null, 0.0, 0.0, 0.0, 1.0 ), + }; + + public static SkillInfo[] Table + { + get + { + return m_Table; + } + set + { + m_Table = value; + } + } + } + + [PropertyObject] + public class Skills + { + private Mobile m_Owner; + private Skill[] m_Skills; + private int m_Total, m_Cap; + private Skill m_Highest; + + #region Skill Getters & Setters + [CommandProperty( AccessLevel.Counselor )] + public Skill Alchemy{ get{ return this[SkillName.Alchemy]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Anatomy{ get{ return this[SkillName.Anatomy]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill AnimalLore{ get{ return this[SkillName.AnimalLore]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill ItemID{ get{ return this[SkillName.ItemID]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill ArmsLore{ get{ return this[SkillName.ArmsLore]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Parry{ get{ return this[SkillName.Parry]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Begging{ get{ return this[SkillName.Begging]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Blacksmith{ get{ return this[SkillName.Blacksmith]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Fletching{ get{ return this[SkillName.Fletching]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Peacemaking{ get{ return this[SkillName.Peacemaking]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Camping{ get{ return this[SkillName.Camping]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Carpentry{ get{ return this[SkillName.Carpentry]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Cartography{ get{ return this[SkillName.Cartography]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Cooking{ get{ return this[SkillName.Cooking]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill DetectHidden{ get{ return this[SkillName.DetectHidden]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Discordance{ get{ return this[SkillName.Discordance]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill EvalInt{ get{ return this[SkillName.EvalInt]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Healing{ get{ return this[SkillName.Healing]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Fishing{ get{ return this[SkillName.Fishing]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Forensics{ get{ return this[SkillName.Forensics]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Herding{ get{ return this[SkillName.Herding]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Hiding{ get{ return this[SkillName.Hiding]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Provocation{ get{ return this[SkillName.Provocation]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Inscribe{ get{ return this[SkillName.Inscribe]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Lockpicking{ get{ return this[SkillName.Lockpicking]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Magery{ get{ return this[SkillName.Magery]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill MagicResist{ get{ return this[SkillName.MagicResist]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Tactics{ get{ return this[SkillName.Tactics]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Snooping{ get{ return this[SkillName.Snooping]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Musicianship{ get{ return this[SkillName.Musicianship]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Poisoning{ get{ return this[SkillName.Poisoning]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Archery{ get{ return this[SkillName.Archery]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill SpiritSpeak{ get{ return this[SkillName.SpiritSpeak]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Stealing{ get{ return this[SkillName.Stealing]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Tailoring{ get{ return this[SkillName.Tailoring]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill AnimalTaming{ get{ return this[SkillName.AnimalTaming]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill TasteID{ get{ return this[SkillName.TasteID]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Tinkering{ get{ return this[SkillName.Tinkering]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Tracking{ get{ return this[SkillName.Tracking]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Veterinary{ get{ return this[SkillName.Veterinary]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Swords{ get{ return this[SkillName.Swords]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Macing{ get{ return this[SkillName.Macing]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Fencing{ get{ return this[SkillName.Fencing]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Wrestling{ get{ return this[SkillName.Wrestling]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Lumberjacking{ get{ return this[SkillName.Lumberjacking]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Mining{ get{ return this[SkillName.Mining]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Meditation{ get{ return this[SkillName.Meditation]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Stealth{ get{ return this[SkillName.Stealth]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill RemoveTrap{ get{ return this[SkillName.RemoveTrap]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Necromancy{ get{ return this[SkillName.Necromancy]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Focus{ get{ return this[SkillName.Focus]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Chivalry{ get{ return this[SkillName.Chivalry]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Bushido{ get{ return this[SkillName.Bushido]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Ninjitsu{ get{ return this[SkillName.Ninjitsu]; } set{} } + + [CommandProperty( AccessLevel.Counselor )] + public Skill Spellweaving { get { return this[SkillName.Spellweaving]; } set { } } + + #endregion + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Cap + { + get{ return m_Cap; } + set{ m_Cap = value; } + } + + public int Total + { + get{ return m_Total; } + set{ m_Total = value; } + } + + public Mobile Owner + { + get{ return m_Owner; } + } + + public int Length + { + get{ return m_Skills.Length; } + } + + public Skill this[SkillName name] + { + get{ return this[(int)name]; } + } + + public Skill this[int skillID] + { + get + { + if ( skillID < 0 || skillID >= m_Skills.Length ) + return null; + + Skill sk = m_Skills[skillID]; + + if ( sk == null ) + m_Skills[skillID] = sk = new Skill( this, SkillInfo.Table[skillID], 0, 1000, SkillLock.Up ); + + return sk; + } + } + + public override string ToString() + { + return "..."; + } + + public static bool UseSkill( Mobile from, SkillName name ) + { + return UseSkill( from, (int)name ); + } + + public static bool UseSkill( Mobile from, int skillID ) + { + if ( !from.CheckAlive() ) + return false; + else if ( !from.Region.OnSkillUse( from, skillID ) ) + return false; + else if ( !from.AllowSkillUse( (SkillName)skillID ) ) + return false; + + if ( skillID >= 0 && skillID < SkillInfo.Table.Length ) + { + SkillInfo info = SkillInfo.Table[skillID]; + + if ( info.Callback != null ) + { + if ( from.NextSkillTime <= DateTime.Now && from.Spell == null ) + { + from.DisruptiveAction(); + + from.NextSkillTime = DateTime.Now + info.Callback( from ); + + return true; + } + else + { + from.SendSkillMessage(); + } + } + else + { + from.SendLocalizedMessage( 500014 ); // That skill cannot be used directly. + } + } + + return false; + } + + public Skill Highest + { + get + { + if ( m_Highest == null ) + { + Skill highest = null; + int value = int.MinValue; + + for ( int i = 0; i < m_Skills.Length; ++i ) + { + Skill sk = m_Skills[i]; + + if ( sk != null && sk.BaseFixedPoint > value ) + { + value = sk.BaseFixedPoint; + highest = sk; + } + } + + if ( highest == null && m_Skills.Length > 0 ) + highest = this[0]; + + m_Highest = highest; + } + + return m_Highest; + } + } + + public void Serialize( GenericWriter writer ) + { + m_Total = 0; + + writer.Write( (int) 3 ); // version + + writer.Write( (int) m_Cap ); + writer.Write( (int) m_Skills.Length ); + + for ( int i = 0; i < m_Skills.Length; ++i ) + { + Skill sk = m_Skills[i]; + + if ( sk == null ) + { + writer.Write( (byte) 0xFF ); + } + else + { + sk.Serialize( writer ); + m_Total += sk.BaseFixedPoint; + } + } + } + + public Skills( Mobile owner ) + { + m_Owner = owner; + m_Cap = 7000; + + SkillInfo[] info = SkillInfo.Table; + + m_Skills = new Skill[info.Length]; + + //for ( int i = 0; i < info.Length; ++i ) + // m_Skills[i] = new Skill( this, info[i], 0, 1000, SkillLock.Up ); + } + + public Skills( Mobile owner, GenericReader reader ) + { + m_Owner = owner; + + int version = reader.ReadInt(); + + switch ( version ) + { + case 3: + case 2: + { + m_Cap = reader.ReadInt(); + + goto case 1; + } + case 1: + { + if ( version < 2 ) + m_Cap = 7000; + + if ( version < 3 ) + /*m_Total =*/ reader.ReadInt(); + + SkillInfo[] info = SkillInfo.Table; + + m_Skills = new Skill[info.Length]; + + int count = reader.ReadInt(); + + for ( int i = 0; i < count; ++i ) + { + if ( i < info.Length ) + { + Skill sk = new Skill( this, info[i], reader ); + + if ( sk.BaseFixedPoint != 0 || sk.CapFixedPoint != 1000 || sk.Lock != SkillLock.Up ) + { + m_Skills[i] = sk; + m_Total += sk.BaseFixedPoint; + } + } + else + { + new Skill( this, null, reader ); + } + } + + //for ( int i = count; i < info.Length; ++i ) + // m_Skills[i] = new Skill( this, info[i], 0, 1000, SkillLock.Up ); + + break; + } + case 0: + { + reader.ReadInt(); + + goto case 1; + } + } + } + + public void OnSkillChange( Skill skill ) + { + if ( skill == m_Highest ) // could be downgrading the skill, force a recalc + m_Highest = null; + else if ( m_Highest != null && skill.BaseFixedPoint > m_Highest.BaseFixedPoint ) + m_Highest = skill; + + m_Owner.OnSkillInvalidated( skill ); + + NetState ns = m_Owner.NetState; + + if ( ns != null ) + ns.Send( new SkillChange( skill ) ); + } + } +} \ No newline at end of file diff --git a/Server/Targeting/LandTarget.cs b/Server/Targeting/LandTarget.cs new file mode 100644 index 000000000..be7288419 --- /dev/null +++ b/Server/Targeting/LandTarget.cs @@ -0,0 +1,104 @@ +/*************************************************************************** + * LandTarget.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: LandTarget.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Targeting +{ + public class LandTarget : IPoint3D + { + private Point3D m_Location; + private int m_TileID; + + public LandTarget( Point3D location, Map map ) + { + m_Location = location; + + if ( map != null ) + { + m_Location.Z = map.GetAverageZ( m_Location.X, m_Location.Y ); + m_TileID = map.Tiles.GetLandTile( m_Location.X, m_Location.Y ).ID & 0x3FFF; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public string Name + { + get + { + return TileData.LandTable[m_TileID].Name; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public TileFlag Flags + { + get + { + return TileData.LandTable[m_TileID].Flags; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int TileID + { + get + { + return m_TileID; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public Point3D Location + { + get + { + return m_Location; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int X + { + get + { + return m_Location.X; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Y + { + get + { + return m_Location.Y; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Z + { + get + { + return m_Location.Z; + } + } + } +} \ No newline at end of file diff --git a/Server/Targeting/MultiTarget.cs b/Server/Targeting/MultiTarget.cs new file mode 100644 index 000000000..7e77878bd --- /dev/null +++ b/Server/Targeting/MultiTarget.cs @@ -0,0 +1,71 @@ +/*************************************************************************** + * MultiTarget.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: MultiTarget.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server; +using Server.Network; + +namespace Server.Targeting +{ + public abstract class MultiTarget : Target + { + private int m_MultiID; + private Point3D m_Offset; + + public int MultiID + { + get + { + return m_MultiID; + } + set + { + m_MultiID = value; + } + } + + public Point3D Offset + { + get + { + return m_Offset; + } + set + { + m_Offset = value; + } + } + + public MultiTarget( int multiID, Point3D offset ) : this( multiID, offset, 10, true, TargetFlags.None ) + { + } + + public MultiTarget( int multiID, Point3D offset, int range, bool allowGround, TargetFlags flags ) : base( range, allowGround, flags ) + { + m_MultiID = multiID; + m_Offset = offset; + } + + public override Packet GetPacket() + { + return new MultiTargetReq( this ); + } + } +} \ No newline at end of file diff --git a/Server/Targeting/StaticTarget.cs b/Server/Targeting/StaticTarget.cs new file mode 100644 index 000000000..8e04dd214 --- /dev/null +++ b/Server/Targeting/StaticTarget.cs @@ -0,0 +1,100 @@ +/*************************************************************************** + * StaticTarget.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: StaticTarget.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Targeting +{ + public class StaticTarget : IPoint3D + { + private Point3D m_Location; + private int m_ItemID; + + public StaticTarget( Point3D location, int itemID ) + { + m_Location = location; + m_ItemID = itemID & 0x3FFF; + m_Location.Z += TileData.ItemTable[m_ItemID].CalcHeight; + } + + [CommandProperty( AccessLevel.Counselor )] + public Point3D Location + { + get + { + return m_Location; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public string Name + { + get + { + return TileData.ItemTable[m_ItemID].Name; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public TileFlag Flags + { + get + { + return TileData.ItemTable[m_ItemID].Flags; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int X + { + get + { + return m_Location.X; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Y + { + get + { + return m_Location.Y; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int Z + { + get + { + return m_Location.Z; + } + } + + [CommandProperty( AccessLevel.Counselor )] + public int ItemID + { + get + { + return m_ItemID; + } + } + } +} \ No newline at end of file diff --git a/Server/Targeting/Target.cs b/Server/Targeting/Target.cs new file mode 100644 index 000000000..1f59fca9c --- /dev/null +++ b/Server/Targeting/Target.cs @@ -0,0 +1,376 @@ +/*************************************************************************** + * Target.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Target.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using Server.Network; + +namespace Server.Targeting +{ + public abstract class Target + { + private static int m_NextTargetID; + + private int m_TargetID; + private int m_Range; + private bool m_AllowGround; + private bool m_CheckLOS; + private bool m_AllowNonlocal; + private bool m_DisallowMultis; + private TargetFlags m_Flags; + private DateTime m_TimeoutTime; + + public DateTime TimeoutTime{ get{ return m_TimeoutTime; } } + + public Target( int range, bool allowGround, TargetFlags flags ) + { + m_TargetID = ++m_NextTargetID; + m_Range = range; + m_AllowGround = allowGround; + m_Flags = flags; + + m_CheckLOS = true; + } + + public static void Cancel( Mobile m ) + { + NetState ns = m.NetState; + + if ( ns != null ) + ns.Send( CancelTarget.Instance ); + + Target targ = m.Target; + + if ( targ != null ) + targ.OnTargetCancel( m, TargetCancelType.Canceled ); + } + + private Timer m_TimeoutTimer; + + public void BeginTimeout( Mobile from, TimeSpan delay ) + { + m_TimeoutTime = DateTime.Now + delay; + + if ( m_TimeoutTimer != null ) + m_TimeoutTimer.Stop(); + + m_TimeoutTimer = new TimeoutTimer( this, from, delay ); + m_TimeoutTimer.Start(); + } + + public void CancelTimeout() + { + if ( m_TimeoutTimer != null ) + m_TimeoutTimer.Stop(); + + m_TimeoutTimer = null; + } + + public void Timeout( Mobile from ) + { + CancelTimeout(); + from.ClearTarget(); + + Cancel( from ); + + OnTargetCancel( from, TargetCancelType.Timeout ); + OnTargetFinish( from ); + } + + private class TimeoutTimer : Timer + { + private Target m_Target; + private Mobile m_Mobile; + + private static TimeSpan ThirtySeconds = TimeSpan.FromSeconds( 30.0 ); + private static TimeSpan TenSeconds = TimeSpan.FromSeconds( 10.0 ); + private static TimeSpan OneSecond = TimeSpan.FromSeconds( 1.0 ); + + public TimeoutTimer( Target target, Mobile m, TimeSpan delay ) : base( delay ) + { + m_Target = target; + m_Mobile = m; + + if ( delay >= ThirtySeconds ) + Priority = TimerPriority.FiveSeconds; + else if ( delay >= TenSeconds ) + Priority = TimerPriority.OneSecond; + else if ( delay >= OneSecond ) + Priority = TimerPriority.TwoFiftyMS; + else + Priority = TimerPriority.TwentyFiveMS; + } + + protected override void OnTick() + { + if ( m_Mobile.Target == m_Target ) + m_Target.Timeout( m_Mobile ); + } + } + + public bool CheckLOS + { + get + { + return m_CheckLOS; + } + set + { + m_CheckLOS = value; + } + } + + public bool DisallowMultis + { + get + { + return m_DisallowMultis; + } + set + { + m_DisallowMultis = value; + } + } + + public bool AllowNonlocal + { + get + { + return m_AllowNonlocal; + } + set + { + m_AllowNonlocal = value; + } + } + + public int TargetID + { + get + { + return m_TargetID; + } + } + + public virtual Packet GetPacket() + { + return new TargetReq( this ); + } + + public void Cancel( Mobile from, TargetCancelType type ) + { + CancelTimeout(); + from.ClearTarget(); + + OnTargetCancel( from, type ); + OnTargetFinish( from ); + } + + public void Invoke( Mobile from, object targeted ) + { + CancelTimeout(); + from.ClearTarget(); + + if ( from.Deleted ) + { + OnTargetCancel( from, TargetCancelType.Canceled ); + OnTargetFinish( from ); + return; + } + + Point3D loc; + Map map; + + if ( targeted is LandTarget ) + { + loc = ((LandTarget)targeted).Location; + map = from.Map; + } + else if ( targeted is StaticTarget ) + { + loc = ((StaticTarget)targeted).Location; + map = from.Map; + } + else if ( targeted is Mobile ) + { + if ( ((Mobile)targeted).Deleted ) + { + OnTargetDeleted( from, targeted ); + OnTargetFinish( from ); + return; + } + else if ( !((Mobile)targeted).CanTarget ) + { + OnTargetUntargetable( from, targeted ); + OnTargetFinish( from ); + return; + } + + loc = ((Mobile)targeted).Location; + map = ((Mobile)targeted).Map; + } + else if ( targeted is Item ) + { + Item item = (Item)targeted; + + if ( item.Deleted ) + { + OnTargetDeleted( from, targeted ); + OnTargetFinish( from ); + return; + } + else if ( !item.CanTarget ) + { + OnTargetUntargetable( from, targeted ); + OnTargetFinish( from ); + return; + } + + object root = item.RootParent; + + if ( !m_AllowNonlocal && root is Mobile && root != from && from.AccessLevel == AccessLevel.Player ) + { + OnNonlocalTarget( from, targeted ); + OnTargetFinish( from ); + return; + } + + loc = item.GetWorldLocation(); + map = item.Map; + } + else + { + OnTargetCancel( from, TargetCancelType.Canceled ); + OnTargetFinish( from ); + return; + } + + if ( map == null || map != from.Map || ( m_Range != -1 && !from.InRange( loc, m_Range ) ) ) + { + OnTargetOutOfRange( from, targeted ); + } + else + { + if ( !from.CanSee( targeted ) ) + OnCantSeeTarget( from, targeted ); + else if ( m_CheckLOS && !from.InLOS( targeted ) ) + OnTargetOutOfLOS( from, targeted ); + else if ( targeted is Item && ((Item)targeted).InSecureTrade ) + OnTargetInSecureTrade( from, targeted ); + else if ( targeted is Item && !((Item)targeted).IsAccessibleTo( from ) ) + OnTargetNotAccessible( from, targeted ); + else if ( targeted is Item && !((Item)targeted).CheckTarget( from, this, targeted ) ) + OnTargetUntargetable( from, targeted ); + else if ( targeted is Mobile && !((Mobile)targeted).CheckTarget( from, this, targeted ) ) + OnTargetUntargetable( from, targeted ); + else if ( from.Region.OnTarget( from, this, targeted ) ) + OnTarget( from, targeted ); + } + + OnTargetFinish( from ); + } + + protected virtual void OnTarget( Mobile from, object targeted ) + { + } + + protected virtual void OnTargetNotAccessible( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500447 ); // That is not accessible. + } + + protected virtual void OnTargetInSecureTrade( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500447 ); // That is not accessible. + } + + protected virtual void OnNonlocalTarget( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500447 ); // That is not accessible. + } + + protected virtual void OnCantSeeTarget( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + + protected virtual void OnTargetOutOfLOS( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500237 ); // Target can not be seen. + } + + protected virtual void OnTargetOutOfRange( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500446 ); // That is too far away. + } + + protected virtual void OnTargetDeleted( Mobile from, object targeted ) + { + } + + protected virtual void OnTargetUntargetable( Mobile from, object targeted ) + { + from.SendLocalizedMessage( 500447 ); // That is not accessible. + } + + protected virtual void OnTargetCancel( Mobile from, TargetCancelType cancelType ) + { + } + + protected virtual void OnTargetFinish( Mobile from ) + { + } + + public int Range + { + get + { + return m_Range; + } + set + { + m_Range = value; + } + } + + public bool AllowGround + { + get + { + return m_AllowGround; + } + set + { + m_AllowGround = value; + } + } + + public TargetFlags Flags + { + get + { + return m_Flags; + } + set + { + m_Flags = value; + } + } + } +} \ No newline at end of file diff --git a/Server/Targeting/TargetCancelType.cs b/Server/Targeting/TargetCancelType.cs new file mode 100644 index 000000000..5e88c9505 --- /dev/null +++ b/Server/Targeting/TargetCancelType.cs @@ -0,0 +1,32 @@ +/*************************************************************************** + * TargetCancelType.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: TargetCancelType.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Targeting +{ + public enum TargetCancelType + { + Overriden, + Canceled, + Disconnected, + Timeout + } +} \ No newline at end of file diff --git a/Server/Targeting/TargetFlags.cs b/Server/Targeting/TargetFlags.cs new file mode 100644 index 000000000..b1d9a5024 --- /dev/null +++ b/Server/Targeting/TargetFlags.cs @@ -0,0 +1,31 @@ +/*************************************************************************** + * TargetFlags.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: TargetFlags.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server.Targeting +{ + public enum TargetFlags : byte + { + None = 0x00, + Harmful = 0x01, + Beneficial = 0x02, + } +} \ No newline at end of file diff --git a/Server/TileData.cs b/Server/TileData.cs new file mode 100644 index 000000000..a2b213032 --- /dev/null +++ b/Server/TileData.cs @@ -0,0 +1,293 @@ +/*************************************************************************** + * TileData.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: TileData.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Text; + +namespace Server +{ + public struct LandData + { + private string m_Name; + private TileFlag m_Flags; + + public LandData( string name, TileFlag flags ) + { + m_Name = name; + m_Flags = flags; + } + + public string Name + { + get{ return m_Name; } + set{ m_Name = value; } + } + + public TileFlag Flags + { + get{ return m_Flags; } + set{ m_Flags = value; } + } + } + + public struct ItemData + { + private string m_Name; + private TileFlag m_Flags; + private byte m_Weight; + private byte m_Quality; + private byte m_Quantity; + private byte m_Value; + private byte m_Height; + + public ItemData( string name, TileFlag flags, int weight, int quality, int quantity, int value, int height ) + { + m_Name = name; + m_Flags = flags; + m_Weight = (byte)weight; + m_Quality = (byte)quality; + m_Quantity = (byte)quantity; + m_Value = (byte)value; + m_Height = (byte)height; + } + + public string Name + { + get{ return m_Name; } + set{ m_Name = value; } + } + + public TileFlag Flags + { + get{ return m_Flags; } + set{ m_Flags = value; } + } + + public bool Bridge + { + get{ return (m_Flags & TileFlag.Bridge) != 0; } + set + { + if ( value ) + m_Flags |= TileFlag.Bridge; + else + m_Flags &= ~TileFlag.Bridge; + } + } + + public bool Impassable + { + get{ return (m_Flags & TileFlag.Impassable) != 0; } + set + { + if ( value ) + m_Flags |= TileFlag.Impassable; + else + m_Flags &= ~TileFlag.Impassable; + } + } + + public bool Surface + { + get{ return (m_Flags & TileFlag.Surface) != 0; } + set + { + if ( value ) + m_Flags |= TileFlag.Surface; + else + m_Flags &= ~TileFlag.Surface; + } + } + + public int Weight + { + get{ return m_Weight; } + set{ m_Weight = (byte)value; } + } + + public int Quality + { + get{ return m_Quality; } + set{ m_Quality = (byte)value; } + } + + public int Quantity + { + get{ return m_Quantity; } + set{ m_Quantity = (byte)value; } + } + + public int Value + { + get{ return m_Value; } + set{ m_Value = (byte)value; } + } + + public int Height + { + get{ return m_Height; } + set{ m_Height = (byte)value; } + } + + public int CalcHeight + { + get + { + if ( (m_Flags & TileFlag.Bridge) != 0 ) + return m_Height / 2; + else + return m_Height; + } + } + } + + [Flags] + public enum TileFlag + { + None = 0x00000000, + Background = 0x00000001, + Weapon = 0x00000002, + Transparent = 0x00000004, + Translucent = 0x00000008, + Wall = 0x00000010, + Damaging = 0x00000020, + Impassable = 0x00000040, + Wet = 0x00000080, + Unknown1 = 0x00000100, + Surface = 0x00000200, + Bridge = 0x00000400, + Generic = 0x00000800, + Window = 0x00001000, + NoShoot = 0x00002000, + ArticleA = 0x00004000, + ArticleAn = 0x00008000, + Internal = 0x00010000, + Foliage = 0x00020000, + PartialHue = 0x00040000, + Unknown2 = 0x00080000, + Map = 0x00100000, + Container = 0x00200000, + Wearable = 0x00400000, + LightSource = 0x00800000, + Animation = 0x01000000, + NoDiagonal = 0x02000000, + Unknown3 = 0x04000000, + Armor = 0x08000000, + Roof = 0x10000000, + Door = 0x20000000, + StairBack = 0x40000000, + StairRight = unchecked( (int)0x80000000 ) + } + + public class TileData + { + private static LandData[] m_LandData; + private static ItemData[] m_ItemData; + + public static LandData[] LandTable + { + get + { + return m_LandData; + } + } + + public static ItemData[] ItemTable + { + get + { + return m_ItemData; + } + } + + private static byte[] m_StringBuffer = new byte[20]; + + private static string ReadNameString( BinaryReader bin ) + { + bin.Read( m_StringBuffer, 0, 20 ); + + int count; + + for ( count = 0; count < 20 && m_StringBuffer[count] != 0; ++count ); + + return Encoding.ASCII.GetString( m_StringBuffer, 0, count ); + } + + static TileData() + { + string filePath = Core.FindDataFile( "tiledata.mul" ); + + if ( File.Exists( filePath ) ) + { + using ( FileStream fs = new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + BinaryReader bin = new BinaryReader( fs ); + + m_LandData = new LandData[0x4000]; + + for ( int i = 0; i < 0x4000; ++i ) + { + if ( (i & 0x1F) == 0 ) + { + bin.ReadInt32(); // header + } + + TileFlag flags = (TileFlag)bin.ReadInt32(); + bin.ReadInt16(); // skip 2 bytes -- textureID + + m_LandData[i] = new LandData( ReadNameString( bin ), flags ); + } + + m_ItemData = new ItemData[0x4000]; + + for ( int i = 0; i < 0x4000; ++i ) + { + if ( (i & 0x1F) == 0 ) + { + bin.ReadInt32(); // header + } + + TileFlag flags = (TileFlag)bin.ReadInt32(); + int weight = bin.ReadByte(); + int quality = bin.ReadByte(); + bin.ReadInt16(); + bin.ReadByte(); + int quantity = bin.ReadByte(); + bin.ReadInt32(); + bin.ReadByte(); + int value = bin.ReadByte(); + int height = bin.ReadByte(); + + m_ItemData[i] = new ItemData( ReadNameString( bin ), flags, weight, quality, quantity, value, height ); + } + } + } + else + { + Console.WriteLine( "tiledata.mul was not found" ); + Console.WriteLine( "Make sure your Scripts/Misc/DataPath.cs is properly configured" ); + Console.WriteLine( "After pressing return an exception will be thrown and the server will terminate" ); + + throw new Exception( String.Format( "TileData: {0} not found", filePath ) ); + } + } + } +} \ No newline at end of file diff --git a/Server/TileList.cs b/Server/TileList.cs new file mode 100644 index 000000000..dc37ec37d --- /dev/null +++ b/Server/TileList.cs @@ -0,0 +1,92 @@ +/*************************************************************************** + * TileList.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: TileList.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + public class TileList + { + private Tile[] m_Tiles; + private int m_Count; + + public TileList() + { + m_Tiles = new Tile[8]; + m_Count = 0; + } + + public int Count + { + get + { + return m_Count; + } + } + + public void AddRange( Tile[] tiles ) + { + if ( (m_Count + tiles.Length) > m_Tiles.Length ) + { + Tile[] old = m_Tiles; + m_Tiles = new Tile[(m_Count + tiles.Length) * 2]; + + for ( int i = 0; i < old.Length; ++i ) + m_Tiles[i] = old[i]; + } + + for ( int i = 0; i < tiles.Length; ++i ) + m_Tiles[m_Count++] = tiles[i]; + } + + public void Add( short id, sbyte z ) + { + if ( (m_Count + 1) > m_Tiles.Length ) + { + Tile[] old = m_Tiles; + m_Tiles = new Tile[old.Length * 2]; + + for ( int i = 0; i < old.Length; ++i ) + m_Tiles[i] = old[i]; + } + + m_Tiles[m_Count].m_ID = id; + m_Tiles[m_Count].m_Z = z; + ++m_Count; + } + + private static Tile[] m_EmptyTiles = new Tile[0]; + + public Tile[] ToArray() + { + if ( m_Count == 0 ) + return m_EmptyTiles; + + Tile[] tiles = new Tile[m_Count]; + + for ( int i = 0; i < m_Count; ++i ) + tiles[i] = m_Tiles[i]; + + m_Count = 0; + + return tiles; + } + } +} \ No newline at end of file diff --git a/Server/TileMatrix.cs b/Server/TileMatrix.cs new file mode 100644 index 000000000..460d7849c --- /dev/null +++ b/Server/TileMatrix.cs @@ -0,0 +1,616 @@ +/*************************************************************************** + * TileMatrix.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: TileMatrix.cs 104 2006-02-04 07:04:40Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; + +namespace Server +{ + public class TileMatrix + { + private Tile[][][][][] m_StaticTiles; + private Tile[][][] m_LandTiles; + + private Tile[] m_InvalidLandBlock; + private Tile[][][] m_EmptyStaticBlock; + + private FileStream m_Map; + + private FileStream m_Index; + private BinaryReader m_IndexReader; + + private FileStream m_Statics; + + private int m_FileIndex; + private int m_BlockWidth, m_BlockHeight; + private int m_Width, m_Height; + + private Map m_Owner; + + private TileMatrixPatch m_Patch; + private int[][] m_StaticPatches; + private int[][] m_LandPatches; + +#if !MONO + [System.Runtime.InteropServices.DllImport( "Kernel32" )] + private unsafe static extern int _lread( IntPtr hFile, void *lpBuffer, int wBytes ); +#endif + + public Map Owner + { + get + { + return m_Owner; + } + } + + public TileMatrixPatch Patch + { + get + { + return m_Patch; + } + } + + public int BlockWidth + { + get + { + return m_BlockWidth; + } + } + + public int BlockHeight + { + get + { + return m_BlockHeight; + } + } + + public int Width + { + get + { + return m_Width; + } + } + + public int Height + { + get + { + return m_Height; + } + } + + public FileStream MapStream + { + get{ return m_Map; } + set{ m_Map = value; } + } + + public FileStream IndexStream + { + get{ return m_Index; } + set{ m_Index = value; } + } + + public FileStream DataStream + { + get{ return m_Statics; } + set{ m_Statics = value; } + } + + public BinaryReader IndexReader + { + get{ return m_IndexReader; } + set{ m_IndexReader = value; } + } + + public bool Exists + { + get{ return ( m_Map != null && m_Index != null && m_Statics != null ); } + } + + private static List m_Instances = new List(); + private List m_FileShare = new List(); + + public TileMatrix( Map owner, int fileIndex, int mapID, int width, int height ) + { + for ( int i = 0; i < m_Instances.Count; ++i ) + { + TileMatrix tm = m_Instances[i]; + + if ( tm.m_FileIndex == fileIndex ) + { + tm.m_FileShare.Add( this ); + m_FileShare.Add( tm ); + } + } + + m_Instances.Add( this ); + m_FileIndex = fileIndex; + m_Width = width; + m_Height = height; + m_BlockWidth = width >> 3; + m_BlockHeight = height >> 3; + + m_Owner = owner; + + if ( fileIndex != 0x7F ) + { + string mapPath = Core.FindDataFile( "map{0}.mul", fileIndex ); + + if ( File.Exists( mapPath ) ) + m_Map = new FileStream( mapPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ); + + string indexPath = Core.FindDataFile( "staidx{0}.mul", fileIndex ); + + if ( File.Exists( indexPath ) ) + { + m_Index = new FileStream( indexPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ); + m_IndexReader = new BinaryReader( m_Index ); + } + + string staticsPath = Core.FindDataFile( "statics{0}.mul", fileIndex ); + + if ( File.Exists( staticsPath ) ) + m_Statics = new FileStream( staticsPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ); + } + + m_EmptyStaticBlock = new Tile[8][][]; + + for ( int i = 0; i < 8; ++i ) + { + m_EmptyStaticBlock[i] = new Tile[8][]; + + for ( int j = 0; j < 8; ++j ) + m_EmptyStaticBlock[i][j] = new Tile[0]; + } + + m_InvalidLandBlock = new Tile[196]; + + m_LandTiles = new Tile[m_BlockWidth][][]; + m_StaticTiles = new Tile[m_BlockWidth][][][][]; + m_StaticPatches = new int[m_BlockWidth][]; + m_LandPatches = new int[m_BlockWidth][]; + + m_Patch = new TileMatrixPatch( this, mapID ); + } + + public Tile[][][] EmptyStaticBlock + { + get + { + return m_EmptyStaticBlock; + } + } + + public void SetStaticBlock( int x, int y, Tile[][][] value ) + { + if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight ) + return; + + if ( m_StaticTiles[x] == null ) + m_StaticTiles[x] = new Tile[m_BlockHeight][][][]; + + m_StaticTiles[x][y] = value; + + if ( m_StaticPatches[x] == null ) + m_StaticPatches[x] = new int[(m_BlockHeight + 31) >> 5]; + + m_StaticPatches[x][y >> 5] |= 1 << (y & 0x1F); + } + + public Tile[][][] GetStaticBlock( int x, int y ) + { + if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight || m_Statics == null || m_Index == null ) + return m_EmptyStaticBlock; + + if ( m_StaticTiles[x] == null ) + m_StaticTiles[x] = new Tile[m_BlockHeight][][][]; + + Tile[][][] tiles = m_StaticTiles[x][y]; + + if ( tiles == null ) + { + for ( int i = 0; tiles == null && i < m_FileShare.Count; ++i ) + { + TileMatrix shared = m_FileShare[i]; + + if ( x >= 0 && x < shared.m_BlockWidth && y >= 0 && y < shared.m_BlockHeight ) + { + Tile[][][][] theirTiles = shared.m_StaticTiles[x]; + + if ( theirTiles != null ) + tiles = theirTiles[y]; + + if ( tiles != null ) + { + int[] theirBits = shared.m_StaticPatches[x]; + + if ( theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0 ) + tiles = null; + } + } + } + + if ( tiles == null ) + tiles = ReadStaticBlock( x, y ); + + m_StaticTiles[x][y] = tiles; + } + + return tiles; + } + + public Tile[] GetStaticTiles( int x, int y ) + { + Tile[][][] tiles = GetStaticBlock( x >> 3, y >> 3 ); + + return tiles[x & 0x7][y & 0x7]; + } + + private static TileList m_TilesList = new TileList(); + + public Tile[] GetStaticTiles( int x, int y, bool multis ) + { + Tile[][][] tiles = GetStaticBlock( x >> 3, y >> 3 ); + + if ( multis ) + { + IPooledEnumerable eable = m_Owner.GetMultiTilesAt( x, y ); + + if ( eable == Map.NullEnumerable.Instance ) + return tiles[x & 0x7][y & 0x7]; + + bool any = false; + + foreach ( Tile[] multiTiles in eable ) + { + if ( !any ) + any = true; + + m_TilesList.AddRange( multiTiles ); + } + + eable.Free(); + + if ( !any ) + return tiles[x & 0x7][y & 0x7]; + + m_TilesList.AddRange( tiles[x & 0x7][y & 0x7] ); + + return m_TilesList.ToArray(); + } + else + { + return tiles[x & 0x7][y & 0x7]; + } + } + + public void SetLandBlock( int x, int y, Tile[] value ) + { + if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight ) + return; + + if ( m_LandTiles[x] == null ) + m_LandTiles[x] = new Tile[m_BlockHeight][]; + + m_LandTiles[x][y] = value; + + if ( m_LandPatches[x] == null ) + m_LandPatches[x] = new int[(m_BlockHeight + 31) >> 5]; + + m_LandPatches[x][y >> 5] |= 1 << (y & 0x1F); + } + + public Tile[] GetLandBlock( int x, int y ) + { + if ( x < 0 || y < 0 || x >= m_BlockWidth || y >= m_BlockHeight || m_Map == null ) + return m_InvalidLandBlock; + + if ( m_LandTiles[x] == null ) + m_LandTiles[x] = new Tile[m_BlockHeight][]; + + Tile[] tiles = m_LandTiles[x][y]; + + if ( tiles == null ) + { + for ( int i = 0; tiles == null && i < m_FileShare.Count; ++i ) + { + TileMatrix shared = m_FileShare[i]; + + if ( x >= 0 && x < shared.m_BlockWidth && y >= 0 && y < shared.m_BlockHeight ) + { + Tile[][] theirTiles = shared.m_LandTiles[x]; + + if ( theirTiles != null ) + tiles = theirTiles[y]; + + if ( tiles != null ) + { + int[] theirBits = shared.m_LandPatches[x]; + + if ( theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0 ) + tiles = null; + } + } + } + + if ( tiles == null ) + tiles = ReadLandBlock( x, y ); + + m_LandTiles[x][y] = tiles; + } + + return tiles; + } + + public Tile GetLandTile( int x, int y ) + { + Tile[] tiles = GetLandBlock( x >> 3, y >> 3 ); + + return tiles[((y & 0x7) << 3) + (x & 0x7)]; + } + + private static TileList[][] m_Lists; + +#if MONO + private static byte[] m_Buffer; +#endif + + private static StaticTile[] m_TileBuffer = new StaticTile[128]; + + private unsafe Tile[][][] ReadStaticBlock( int x, int y ) + { + try + { + m_IndexReader.BaseStream.Seek( ((x * m_BlockHeight) + y) * 12, SeekOrigin.Begin ); + + int lookup = m_IndexReader.ReadInt32(); + int length = m_IndexReader.ReadInt32(); + + if ( lookup < 0 || length <= 0 ) + { + return m_EmptyStaticBlock; + } + else + { + int count = length / 7; + + m_Statics.Seek( lookup, SeekOrigin.Begin ); + + if ( m_TileBuffer.Length < count ) + m_TileBuffer = new StaticTile[count]; + + StaticTile[] staTiles = m_TileBuffer;//new StaticTile[tileCount]; + + fixed ( StaticTile *pTiles = staTiles ) + { +#if !MONO + _lread( m_Statics.SafeFileHandle.DangerousGetHandle(), pTiles, length ); +#else + if ( m_Buffer == null || length > m_Buffer.Length ) + m_Buffer = new byte[length]; + + m_Statics.Read( m_Buffer, 0, length ); + + fixed ( byte *pbBuffer = m_Buffer ) + { + StaticTile *pCopyBuffer = (StaticTile *)pbBuffer; + StaticTile *pCopyEnd = pCopyBuffer + count; + StaticTile *pCopyCur = pTiles; + + while ( pCopyBuffer < pCopyEnd ) + *pCopyCur++ = *pCopyBuffer++; + } +#endif + + if ( m_Lists == null ) + { + m_Lists = new TileList[8][]; + + for ( int i = 0; i < 8; ++i ) + { + m_Lists[i] = new TileList[8]; + + for ( int j = 0; j < 8; ++j ) + m_Lists[i][j] = new TileList(); + } + } + + TileList[][] lists = m_Lists; + + StaticTile *pCur = pTiles, pEnd = pTiles + count; + + while ( pCur < pEnd ) + { + lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add( (short)((pCur->m_ID & 0x3FFF) + 0x4000), pCur->m_Z ); + ++pCur; + } + + Tile[][][] tiles = new Tile[8][][]; + + for ( int i = 0; i < 8; ++i ) + { + tiles[i] = new Tile[8][]; + + for ( int j = 0; j < 8; ++j ) + tiles[i][j] = lists[i][j].ToArray(); + } + + return tiles; + } + } + } + catch ( EndOfStreamException ) + { + if ( DateTime.Now >= m_NextStaticWarning ) + { + Console.WriteLine( "Warning: Static EOS for {0} ({1}, {2})", m_Owner, x, y ); + m_NextStaticWarning = DateTime.Now + TimeSpan.FromMinutes( 1.0 ); + } + + return m_EmptyStaticBlock; + } + } + + private DateTime m_NextStaticWarning; + private DateTime m_NextLandWarning; + + public void Force() + { + if ( ScriptCompiler.Assemblies == null || ScriptCompiler.Assemblies.Length == 0 ) + throw new Exception(); + } + + private unsafe Tile[] ReadLandBlock( int x, int y ) + { + try + { + m_Map.Seek( ((x * m_BlockHeight) + y) * 196 + 4, SeekOrigin.Begin ); + + Tile[] tiles = new Tile[64]; + + fixed ( Tile *pTiles = tiles ) + { +#if !MONO + _lread( m_Map.SafeFileHandle.DangerousGetHandle(), pTiles, 192 ); +#else + if ( m_Buffer == null || 192 > m_Buffer.Length ) + m_Buffer = new byte[192]; + + m_Map.Read( m_Buffer, 0, 192 ); + + fixed ( byte *pbBuffer = m_Buffer ) + { + Tile *pBuffer = (Tile *)pbBuffer; + Tile *pEnd = pBuffer + 64; + Tile *pCur = pTiles; + + while ( pBuffer < pEnd ) + *pCur++ = *pBuffer++; + } +#endif + } + + return tiles; + } + catch + { + if ( DateTime.Now >= m_NextLandWarning ) + { + Console.WriteLine( "Warning: Land EOS for {0} ({1}, {2})", m_Owner, x, y ); + m_NextLandWarning = DateTime.Now + TimeSpan.FromMinutes( 1.0 ); + } + + return m_InvalidLandBlock; + } + } + + public void Dispose() + { + if ( m_Map != null ) + m_Map.Close(); + + if ( m_Statics != null ) + m_Statics.Close(); + + if ( m_IndexReader != null ) + m_IndexReader.Close(); + } + } + + [System.Runtime.InteropServices.StructLayout( System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1 )] + public struct StaticTile + { + public short m_ID; + public byte m_X; + public byte m_Y; + public sbyte m_Z; + public short m_Hue; + } + + [System.Runtime.InteropServices.StructLayout( System.Runtime.InteropServices.LayoutKind.Sequential, Pack=1 )] + public struct Tile + { + internal short m_ID; + internal sbyte m_Z; + + public int ID + { + get + { + return m_ID; + } + } + + public int Z + { + get + { + return m_Z; + } + set + { + m_Z = (sbyte)value; + } + } + + public int Height + { + get + { + if ( m_ID < 0x4000 ) + { + return 0; + } + else + { + return TileData.ItemTable[m_ID & 0x3FFF].Height; + } + } + } + + public bool Ignored + { + get + { + return ( m_ID == 2 || m_ID == 0x1DB || ( m_ID >= 0x1AE && m_ID <= 0x1B5 ) ); + } + } + + public Tile( short id, sbyte z ) + { + m_ID = id; + m_Z = z; + } + + public void Set( short id, sbyte z ) + { + m_ID = id; + m_Z = z; + } + } +} \ No newline at end of file diff --git a/Server/TileMatrixPatch.cs b/Server/TileMatrixPatch.cs new file mode 100644 index 000000000..f3dff95ef --- /dev/null +++ b/Server/TileMatrixPatch.cs @@ -0,0 +1,243 @@ +/*************************************************************************** + * TileMatrixPatch.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: TileMatrixPatch.cs 104 2006-02-04 07:04:40Z mark $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.IO; + +namespace Server +{ + public class TileMatrixPatch + { + private int m_LandBlocks, m_StaticBlocks; + + private static bool m_Enabled = true; + + public static bool Enabled + { + get + { + return m_Enabled; + } + set + { + m_Enabled = value; + } + } + +#if !MONO + [System.Runtime.InteropServices.DllImport( "Kernel32" )] + private unsafe static extern int _lread( IntPtr hFile, void *lpBuffer, int wBytes ); +#endif + + public int LandBlocks + { + get + { + return m_LandBlocks; + } + } + + public int StaticBlocks + { + get + { + return m_StaticBlocks; + } + } + + public TileMatrixPatch( TileMatrix matrix, int index ) + { + if ( !m_Enabled ) + return; + + string mapDataPath = Core.FindDataFile( "mapdif{0}.mul", index ); + string mapIndexPath = Core.FindDataFile( "mapdifl{0}.mul", index ); + + if ( File.Exists( mapDataPath ) && File.Exists( mapIndexPath ) ) + m_LandBlocks = PatchLand( matrix, mapDataPath, mapIndexPath ); + + string staDataPath = Core.FindDataFile( "stadif{0}.mul", index ); + string staIndexPath = Core.FindDataFile( "stadifl{0}.mul", index ); + string staLookupPath = Core.FindDataFile( "stadifi{0}.mul", index ); + + if ( File.Exists( staDataPath ) && File.Exists( staIndexPath ) && File.Exists( staLookupPath ) ) + m_StaticBlocks = PatchStatics( matrix, staDataPath, staIndexPath, staLookupPath ); + } + + private unsafe int PatchLand( TileMatrix matrix, string dataPath, string indexPath ) + { + using ( FileStream fsData = new FileStream( dataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + using ( FileStream fsIndex = new FileStream( indexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + BinaryReader indexReader = new BinaryReader( fsIndex ); + + int count = (int)(indexReader.BaseStream.Length / 4); + + for ( int i = 0; i < count; ++i ) + { + int blockID = indexReader.ReadInt32(); + int x = blockID / matrix.BlockHeight; + int y = blockID % matrix.BlockHeight; + + fsData.Seek( 4, SeekOrigin.Current ); + + Tile[] tiles = new Tile[64]; + + fixed ( Tile *pTiles = tiles ) + { +#if !MONO + _lread( fsData.SafeFileHandle.DangerousGetHandle(), pTiles, 192 ); +#else + if ( m_Buffer == null || 192 > m_Buffer.Length ) + m_Buffer = new byte[192]; + + fsData.Read( m_Buffer, 0, 192 ); + + fixed ( byte *pbBuffer = m_Buffer ) + { + Tile *pBuffer = (Tile *)pbBuffer; + Tile *pEnd = pBuffer + 64; + Tile *pCur = pTiles; + + while ( pBuffer < pEnd ) + *pCur++ = *pBuffer++; + } +#endif + } + + matrix.SetLandBlock( x, y, tiles ); + } + + indexReader.Close(); + + return count; + } + } + } + +#if MONO + private static byte[] m_Buffer; +#endif + + private static StaticTile[] m_TileBuffer = new StaticTile[128]; + + private unsafe int PatchStatics( TileMatrix matrix, string dataPath, string indexPath, string lookupPath ) + { + using ( FileStream fsData = new FileStream( dataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + using ( FileStream fsIndex = new FileStream( indexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + using ( FileStream fsLookup = new FileStream( lookupPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) + { + BinaryReader indexReader = new BinaryReader( fsIndex ); + BinaryReader lookupReader = new BinaryReader( fsLookup ); + + int count = (int)(indexReader.BaseStream.Length / 4); + + TileList[][] lists = new TileList[8][]; + + for ( int x = 0; x < 8; ++x ) + { + lists[x] = new TileList[8]; + + for ( int y = 0; y < 8; ++y ) + lists[x][y] = new TileList(); + } + + for ( int i = 0; i < count; ++i ) + { + int blockID = indexReader.ReadInt32(); + int blockX = blockID / matrix.BlockHeight; + int blockY = blockID % matrix.BlockHeight; + + int offset = lookupReader.ReadInt32(); + int length = lookupReader.ReadInt32(); + lookupReader.ReadInt32(); // Extra + + if ( offset < 0 || length <= 0 ) + { + matrix.SetStaticBlock( blockX, blockY, matrix.EmptyStaticBlock ); + continue; + } + + fsData.Seek( offset, SeekOrigin.Begin ); + + int tileCount = length / 7; + + if ( m_TileBuffer.Length < tileCount ) + m_TileBuffer = new StaticTile[tileCount]; + + StaticTile[] staTiles = m_TileBuffer;//new StaticTile[tileCount]; + + fixed ( StaticTile *pTiles = staTiles ) + { +#if !MONO + _lread( fsData.SafeFileHandle.DangerousGetHandle(), pTiles, length ); +#else + if ( m_Buffer == null || length > m_Buffer.Length ) + m_Buffer = new byte[length]; + + fsData.Read( m_Buffer, 0, length ); + + fixed ( byte *pbBuffer = m_Buffer ) + { + StaticTile *pCopyBuffer = (StaticTile *)pbBuffer; + StaticTile *pCopyEnd = pCopyBuffer + tileCount; + StaticTile *pCopyCur = pTiles; + + while ( pCopyBuffer < pCopyEnd ) + *pCopyCur++ = *pCopyBuffer++; + } +#endif + + StaticTile *pCur = pTiles, pEnd = pTiles + tileCount; + + while ( pCur < pEnd ) + { + lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add( (short)((pCur->m_ID & 0x3FFF) + 0x4000), pCur->m_Z ); + ++pCur; + } + + Tile[][][] tiles = new Tile[8][][]; + + for ( int x = 0; x < 8; ++x ) + { + tiles[x] = new Tile[8][]; + + for ( int y = 0; y < 8; ++y ) + tiles[x][y] = lists[x][y].ToArray(); + } + + matrix.SetStaticBlock( blockX, blockY, tiles ); + } + } + + indexReader.Close(); + lookupReader.Close(); + + return count; + } + } + } + } + } +} \ No newline at end of file diff --git a/Server/Timer.cs b/Server/Timer.cs new file mode 100644 index 000000000..ea4639c13 --- /dev/null +++ b/Server/Timer.cs @@ -0,0 +1,693 @@ +/*************************************************************************** + * Timer.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Timer.cs 113 2006-02-12 23:07:32Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Diagnostics; + +namespace Server +{ + public enum TimerPriority + { + EveryTick, + TenMS, + TwentyFiveMS, + FiftyMS, + TwoFiftyMS, + OneSecond, + FiveSeconds, + OneMinute + } + + public delegate void TimerCallback(); + public delegate void TimerStateCallback( object state ); + + public class TimerProfile + { + private int m_Created; + private int m_Started; + private int m_Stopped; + private int m_Ticked; + private TimeSpan m_TotalProcTime; + private TimeSpan m_PeakProcTime; + + [CommandProperty( AccessLevel.Administrator )] + public int Created + { + get{ return m_Created; } + } + + [CommandProperty( AccessLevel.Administrator )] + public int Started + { + get{ return m_Started; } + } + + [CommandProperty( AccessLevel.Administrator )] + public int Stopped + { + get{ return m_Stopped; } + } + + [CommandProperty( AccessLevel.Administrator )] + public int Ticked + { + get{ return m_Ticked; } + } + + [CommandProperty( AccessLevel.Administrator )] + public TimeSpan TotalProcTime + { + get{ return m_TotalProcTime; } + } + + [CommandProperty( AccessLevel.Administrator )] + public TimeSpan PeakProcTime + { + get{ return m_PeakProcTime; } + } + + [CommandProperty( AccessLevel.Administrator )] + public TimeSpan AverageProcTime + { + get + { + if ( m_Ticked == 0 ) + return TimeSpan.Zero; + + return TimeSpan.FromTicks( m_TotalProcTime.Ticks / m_Ticked ); + } + } + + public void RegCreation() + { + ++m_Created; + } + + public void RegStart() + { + ++m_Started; + } + + public void RegStopped() + { + ++m_Stopped; + } + + public void RegTicked( TimeSpan procTime ) + { + ++m_Ticked; + m_TotalProcTime += procTime; + + if ( procTime > m_PeakProcTime ) + m_PeakProcTime = procTime; + } + + public TimerProfile() + { + } + } + + public class Timer + { + private DateTime m_Next; + private TimeSpan m_Delay; + private TimeSpan m_Interval; + private bool m_Running; + private int m_Index, m_Count; + private TimerPriority m_Priority; + private List m_List; + + private static string FormatDelegate( Delegate callback ) + { + if ( callback == null ) + return "null"; + + return String.Format( "{0}.{1}", callback.Method.DeclaringType.FullName, callback.Method.Name ); + } + + public static void DumpInfo( TextWriter tw ) + { + TimerThread.DumpInfo( tw ); + } + + public TimerPriority Priority + { + get + { + return m_Priority; + } + set + { + if ( m_Priority != value ) + { + m_Priority = value; + + if ( m_Running ) + TimerThread.PriorityChange( this, (int)m_Priority ); + } + } + } + + public DateTime Next + { + get { return m_Next; } + } + + public TimeSpan Delay + { + get + { + return m_Delay; + } + set + { + m_Delay = value; + } + } + + public TimeSpan Interval + { + get + { + return m_Interval; + } + set + { + m_Interval = value; + } + } + + public bool Running + { + get + { + return m_Running; + } + set + { + if ( value ) + { + Start(); + } + else + { + Stop(); + } + } + } + + private static Dictionary m_Profiles = new Dictionary(); + + public static Dictionary Profiles{ get{ return m_Profiles; } } + + public TimerProfile GetProfile() + { + if ( !Core.Profiling ) + return null; + + string name = ToString(); + + if ( name == null ) + name = "null"; + + TimerProfile prof = null; + m_Profiles.TryGetValue( name, out prof ); + + if ( prof == null ) + m_Profiles[name] = prof = new TimerProfile(); + + return prof; + } + + public class TimerThread + { + private static Queue m_ChangeQueue = Queue.Synchronized( new Queue() ); + + private static DateTime[] m_NextPriorities = new DateTime[8]; + private static TimeSpan[] m_PriorityDelays = new TimeSpan[8] + { + TimeSpan.Zero, + TimeSpan.FromMilliseconds( 10.0 ), + TimeSpan.FromMilliseconds( 25.0 ), + TimeSpan.FromMilliseconds( 50.0 ), + TimeSpan.FromMilliseconds( 250.0 ), + TimeSpan.FromSeconds( 1.0 ), + TimeSpan.FromSeconds( 5.0 ), + TimeSpan.FromMinutes( 1.0 ) + }; + + private static List[] m_Timers = new List[8] + { + new List(), + new List(), + new List(), + new List(), + new List(), + new List(), + new List(), + new List(), + }; + + public static void DumpInfo( TextWriter tw ) + { + for ( int i = 0; i < 8; ++i ) + { + tw.WriteLine( "Priority: {0}", (TimerPriority)i ); + tw.WriteLine(); + + Dictionary> hash = new Dictionary>(); + + for ( int j = 0; j < m_Timers[i].Count; ++j ) + { + Timer t = m_Timers[i][j]; + + string key = t.ToString(); + + List list; + hash.TryGetValue( key, out list ); + + if ( list == null ) + hash[key] = list = new List(); + + list.Add( t ); + } + + foreach ( KeyValuePair> kv in hash ) + { + string key = kv.Key; + List list = kv.Value; + + tw.WriteLine( "Type: {0}; Count: {1}; Percent: {2}%", key, list.Count, (int)(100 * (list.Count / (double)m_Timers[i].Count)) ); + } + + tw.WriteLine(); + tw.WriteLine(); + } + } + + private class TimerChangeEntry + { + public Timer m_Timer; + public int m_NewIndex; + public bool m_IsAdd; + + private TimerChangeEntry( Timer t, int newIndex, bool isAdd ) + { + m_Timer = t; + m_NewIndex = newIndex; + m_IsAdd = isAdd; + } + + public void Free() + { + //m_InstancePool.Enqueue( this ); + } + + private static Queue m_InstancePool = new Queue(); + + public static TimerChangeEntry GetInstance( Timer t, int newIndex, bool isAdd ) + { + TimerChangeEntry e; + + if ( m_InstancePool.Count > 0 ) + { + e = m_InstancePool.Dequeue(); + + if ( e == null ) + e = new TimerChangeEntry( t, newIndex, isAdd ); + else + { + e.m_Timer = t; + e.m_NewIndex = newIndex; + e.m_IsAdd = isAdd; + } + } + else + { + e = new TimerChangeEntry( t, newIndex, isAdd ); + } + + return e; + } + } + + public TimerThread() + { + } + + public static void Change( Timer t, int newIndex, bool isAdd ) + { + m_ChangeQueue.Enqueue( TimerChangeEntry.GetInstance( t, newIndex, isAdd ) ); + } + + public static void AddTimer( Timer t ) + { + Change( t, (int)t.Priority, true ); + } + + public static void PriorityChange( Timer t, int newPrio ) + { + Change( t, newPrio, false ); + } + + public static void RemoveTimer( Timer t ) + { + Change( t, -1, false ); + } + + private static void ProcessChangeQueue() + { + while ( m_ChangeQueue.Count > 0 ) + { + TimerChangeEntry tce = (TimerChangeEntry)m_ChangeQueue.Dequeue(); + Timer timer = tce.m_Timer; + int newIndex = tce.m_NewIndex; + + if ( timer.m_List != null ) + timer.m_List.Remove( timer ); + + if ( tce.m_IsAdd ) + { + timer.m_Next = DateTime.Now + timer.m_Delay; + timer.m_Index = 0; + } + + if ( newIndex >= 0 ) + { + timer.m_List = m_Timers[newIndex]; + timer.m_List.Add( timer ); + } + else + { + timer.m_List = null; + } + + tce.Free(); + } + } + + public void TimerMain() + { + DateTime now; + int i, j; + + while ( !Core.Closing ) + { + Thread.Sleep( 10 ); + + ProcessChangeQueue(); + + for (i=0;i t.m_Next ) + { + t.m_Queued = true; + + lock ( m_Queue ) + m_Queue.Enqueue( t ); + + if ( t.m_Count != 0 && (++t.m_Index >= t.m_Count) ) + { + t.Stop(); + } + else + { + t.m_Next = now + t.m_Interval; + } + } + } + } + } + } + } + + private static Queue m_Queue = new Queue(); + private static int m_BreakCount = 20000; + + public static int BreakCount{ get{ return m_BreakCount; } set{ m_BreakCount = value; } } + + private static int m_QueueCountAtSlice; + + private bool m_Queued; + + public static void Slice() + { + lock ( m_Queue ) + { + m_QueueCountAtSlice = m_Queue.Count; + + int index = 0; + + Stopwatch watch = null; + + while ( index < m_BreakCount && m_Queue.Count != 0 ) + { + Timer t = m_Queue.Dequeue(); + TimerProfile prof = t.GetProfile(); + + if ( prof != null ) { + if ( watch == null ) { + watch = Stopwatch.StartNew(); + } else { + watch.Start(); + } + } + + t.OnTick(); + t.m_Queued = false; + ++index; + + if ( prof != null ) { + prof.RegTicked( watch.Elapsed ); + watch.Reset(); + } + } + } + } + + public Timer( TimeSpan delay ) : this( delay, TimeSpan.Zero, 1 ) + { + } + + public Timer( TimeSpan delay, TimeSpan interval ) : this( delay, interval, 0 ) + { + } + + public virtual bool DefRegCreation + { + get{ return true; } + } + + public virtual void RegCreation() + { + TimerProfile prof = GetProfile(); + + if ( prof != null ) + prof.RegCreation(); + } + + public Timer( TimeSpan delay, TimeSpan interval, int count ) + { + m_Delay = delay; + m_Interval = interval; + m_Count = count; + + if ( DefRegCreation ) + RegCreation(); + } + + public override string ToString() + { + return GetType().FullName; + } + + public static TimerPriority ComputePriority( TimeSpan ts ) + { + if ( ts >= TimeSpan.FromMinutes( 1.0 ) ) + return TimerPriority.FiveSeconds; + + if ( ts >= TimeSpan.FromSeconds( 10.0 ) ) + return TimerPriority.OneSecond; + + if ( ts >= TimeSpan.FromSeconds( 5.0 ) ) + return TimerPriority.TwoFiftyMS; + + if ( ts >= TimeSpan.FromSeconds( 2.5 ) ) + return TimerPriority.FiftyMS; + + if ( ts >= TimeSpan.FromSeconds( 1.0 ) ) + return TimerPriority.TwentyFiveMS; + + if ( ts >= TimeSpan.FromSeconds( 0.5 ) ) + return TimerPriority.TenMS; + + return TimerPriority.EveryTick; + } + + public static Timer DelayCall( TimeSpan delay, TimerCallback callback ) + { + return DelayCall( delay, TimeSpan.Zero, 1, callback ); + } + + public static Timer DelayCall( TimeSpan delay, TimeSpan interval, TimerCallback callback ) + { + return DelayCall( delay, interval, 0, callback ); + } + + public static Timer DelayCall( TimeSpan delay, TimeSpan interval, int count, TimerCallback callback ) + { + Timer t = new DelayCallTimer( delay, interval, count, callback ); + + if ( count == 1 ) + t.Priority = ComputePriority( delay ); + else + t.Priority = ComputePriority( interval ); + + t.Start(); + + return t; + } + + public static Timer DelayCall( TimeSpan delay, TimerStateCallback callback, object state ) + { + return DelayCall( delay, TimeSpan.Zero, 1, callback, state ); + } + + public static Timer DelayCall( TimeSpan delay, TimeSpan interval, TimerStateCallback callback, object state ) + { + return DelayCall( delay, interval, 0, callback, state ); + } + + public static Timer DelayCall( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, object state ) + { + Timer t = new DelayStateCallTimer( delay, interval, count, callback, state ); + + if ( count == 1 ) + t.Priority = ComputePriority( delay ); + else + t.Priority = ComputePriority( interval ); + + t.Start(); + + return t; + } + + private class DelayCallTimer : Timer + { + private TimerCallback m_Callback; + + public TimerCallback Callback{ get{ return m_Callback; } } + + public override bool DefRegCreation{ get{ return false; } } + + public DelayCallTimer( TimeSpan delay, TimeSpan interval, int count, TimerCallback callback ) : base( delay, interval, count ) + { + m_Callback = callback; + RegCreation(); + } + + protected override void OnTick() + { + if ( m_Callback != null ) + m_Callback(); + } + + public override string ToString() + { + return String.Format( "DelayCallTimer[{0}]", FormatDelegate( m_Callback ) ); + } + } + + private class DelayStateCallTimer : Timer + { + private TimerStateCallback m_Callback; + private object m_State; + + public TimerStateCallback Callback{ get{ return m_Callback; } } + + public override bool DefRegCreation{ get{ return false; } } + + public DelayStateCallTimer( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, object state ) : base( delay, interval, count ) + { + m_Callback = callback; + m_State = state; + + RegCreation(); + } + + protected override void OnTick() + { + if ( m_Callback != null ) + m_Callback( m_State ); + } + + public override string ToString() + { + return String.Format( "DelayStateCall[{0}]", FormatDelegate( m_Callback ) ); + } + } + + public void Start() + { + if ( !m_Running ) + { + m_Running = true; + TimerThread.AddTimer( this ); + + TimerProfile prof = GetProfile(); + + if ( prof != null ) + prof.RegStart(); + } + } + + public void Stop() + { + if ( m_Running ) + { + m_Running = false; + TimerThread.RemoveTimer( this ); + + TimerProfile prof = GetProfile(); + + if ( prof != null ) + prof.RegStopped(); + } + } + + protected virtual void OnTick() + { + } + } +} \ No newline at end of file diff --git a/Server/Utility.cs b/Server/Utility.cs new file mode 100644 index 000000000..ffa977f47 --- /dev/null +++ b/Server/Utility.cs @@ -0,0 +1,1092 @@ +/*************************************************************************** + * Utility.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: Utility.cs 156 2006-06-11 09:36:25Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text; +using System.Xml; +using Microsoft.Win32; +using Server.Network; + +namespace Server +{ + public static class Utility + { + private static Random m_Random = new Random(); + private static Encoding m_UTF8, m_UTF8WithEncoding; + + public static Encoding UTF8 + { + get + { + if ( m_UTF8 == null ) + m_UTF8 = new UTF8Encoding( false, false ); + + return m_UTF8; + } + } + + public static Encoding UTF8WithEncoding + { + get + { + if ( m_UTF8WithEncoding == null ) + m_UTF8WithEncoding = new UTF8Encoding( true, false ); + + return m_UTF8WithEncoding; + } + } + + public static void Seperate( StringBuilder sb, string value, string seperator ) + { + if ( sb.Length > 0 ) + sb.Append( seperator ); + + sb.Append( value ); + } + + public static string Intern( string str ) + { + if ( str == null ) + return null; + else if ( str.Length == 0 ) + return String.Empty; + + return String.Intern( str ); + } + + public static void Intern( ref string str ) + { + str = Intern( str ); + } + + public static bool IsValidIP( string text ) + { + bool valid = true; + + IPMatch( text, IPAddress.None, ref valid ); + + return valid; + } + + public static bool IPMatch( string val, IPAddress ip ) + { + bool valid = true; + + return IPMatch( val, ip, ref valid ); + } + + public static string FixHtml( string str ) + { + if( str == null ) + return ""; + + bool hasOpen = ( str.IndexOf( '<' ) >= 0 ); + bool hasClose = ( str.IndexOf( '>' ) >= 0 ); + bool hasPound = ( str.IndexOf( '#' ) >= 0 ); + + if ( !hasOpen && !hasClose && !hasPound ) + return str; + + StringBuilder sb = new StringBuilder( str ); + + if ( hasOpen ) + sb.Replace( '<', '(' ); + + if ( hasClose ) + sb.Replace( '>', ')' ); + + if ( hasPound ) + sb.Replace( '#', '-' ); + + return sb.ToString(); + } + + public static bool IPMatchCIDR( string cidr, IPAddress ip ) + { + string[] str = cidr.Split( '/' ); + + if ( str.Length < 2 ) + return false; + + IPAddress cidrPrefix; + + if ( !IPAddress.TryParse( str[0], out cidrPrefix ) ) + return false; + + int cidrLength = Utility.ToInt32( str[1] ); + + return IPMatchCIDR( cidrPrefix, ip, cidrLength ); + } + + public static bool IPMatchCIDR( IPAddress cidrPrefix, IPAddress ip, int cidrLength ) + { + if ( cidrPrefix == null || ip == null ) + return false; + + if ( cidrLength <= 0 || cidrLength >= 32 ) //if invalid cidr Length, just compare IPs + return ip.Equals( cidrPrefix ); + + uint mask = uint.MaxValue << cidrLength; + + long cidrValue = Utility.GetLongAddressValue( cidrPrefix ); + long ipValue = Utility.GetLongAddressValue( ip ); + + return ( ( cidrValue & mask ) == ( ipValue & mask ) ); + } + + public static bool IPMatch( string val, IPAddress ip, ref bool valid ) + { + valid = true; + + string[] split = val.Split( '.' ); + + for ( int i = 0; i < 4; ++i ) + { + int lowPart, highPart; + + if ( i >= split.Length ) + { + lowPart = 0; + highPart = 255; + } + else + { + string pattern = split[i]; + + if ( pattern == "*" ) + { + lowPart = 0; + highPart = 255; + } + else + { + lowPart = 0; + highPart = 0; + + bool highOnly = false; + int lowBase = 10; + int highBase = 10; + + for ( int j = 0; j < pattern.Length; ++j ) + { + char c = (char)pattern[j]; + + if ( c == '?' ) + { + if ( !highOnly ) + { + lowPart *= lowBase; + lowPart += 0; + } + + highPart *= highBase; + highPart += highBase - 1; + } + else if ( c == '-' ) + { + highOnly = true; + highPart = 0; + } + else if ( c == 'x' || c == 'X' ) + { + lowBase = 16; + highBase = 16; + } + else if ( c >= '0' && c <= '9' ) + { + int offset = c - '0'; + + if ( !highOnly ) + { + lowPart *= lowBase; + lowPart += offset; + } + + highPart *= highBase; + highPart += offset; + } + else if ( c >= 'a' && c <= 'f' ) + { + int offset = 10 + (c - 'a'); + + if ( !highOnly ) + { + lowPart *= lowBase; + lowPart += offset; + } + + highPart *= highBase; + highPart += offset; + } + else if ( c >= 'A' && c <= 'F' ) + { + int offset = 10 + (c - 'A'); + + if ( !highOnly ) + { + lowPart *= lowBase; + lowPart += offset; + } + + highPart *= highBase; + highPart += offset; + } + else + { + valid = false; + } + } + } + } + + int b = (byte)(Utility.GetAddressValue( ip ) >> (i * 8)); + + if ( b < lowPart || b > highPart ) + return false; + } + + return true; + } + + public static bool IPMatchClassC( IPAddress ip1, IPAddress ip2 ) + { + return ( (Utility.GetAddressValue( ip1 ) & 0xFFFFFF) == (Utility.GetAddressValue( ip2 ) & 0xFFFFFF) ); + } + + public static int InsensitiveCompare( string first, string second ) + { + return Insensitive.Compare( first, second ); + } + + public static bool InsensitiveStartsWith( string first, string second ) + { + return Insensitive.StartsWith( first, second ); + } + + #region To[Something] + public static bool ToBoolean( string value ) + { + bool b; + bool.TryParse( value, out b ); + + return b; + } + + public static double ToDouble( string value ) + { + double d; + double.TryParse( value, out d ); + + return d; + } + + public static TimeSpan ToTimeSpan( string value ) + { + TimeSpan t; + TimeSpan.TryParse( value, out t ); + + return t; + } + + public static int ToInt32( string value ) + { + int i; + + if( value.StartsWith( "0x" ) ) + int.TryParse( value.Substring( 2 ), NumberStyles.HexNumber, null, out i ); + else + int.TryParse( value, out i ); + + return i; + + /* + try + { + if ( value.StartsWith( "0x" ) ) + { + return Convert.ToInt32( value.Substring( 2 ), 16 ); + } + else + { + return Convert.ToInt32( value ); + } + } + catch + { + return 0; + } + * */ + } + #endregion + + #region Get[Something] + public static int GetInt32( string intString, int defaultValue ) + { + try + { + return XmlConvert.ToInt32( intString ); + } + catch + { + try + { + return Convert.ToInt32( intString ); + } + catch + { + return defaultValue; + } + } + } + + public static DateTime GetDateTime( string dateTimeString, DateTime defaultValue ) + { + try + { + return XmlConvert.ToDateTime( dateTimeString, XmlDateTimeSerializationMode.Local ); + } + catch + { + DateTime d; + + if( DateTime.TryParse( dateTimeString, out d ) ) + return d; + + return defaultValue; + } + } + + public static TimeSpan GetTimeSpan( string timeSpanString, TimeSpan defaultValue ) + { + try + { + return XmlConvert.ToTimeSpan( timeSpanString ); + } + catch + { + return defaultValue; + } + } + + public static string GetAttribute( XmlElement node, string attributeName ) + { + return GetAttribute( node, attributeName, null ); + } + + public static string GetAttribute( XmlElement node, string attributeName, string defaultValue ) + { + if ( node == null ) + return defaultValue; + + XmlAttribute attr = node.Attributes[attributeName]; + + if ( attr == null ) + return defaultValue; + + return attr.Value; + } + + public static string GetText( XmlElement node, string defaultValue ) + { + if ( node == null ) + return defaultValue; + + return node.InnerText; + } + + public static int GetAddressValue( IPAddress address ) + { +#pragma warning disable 618 + return (int)address.Address; +#pragma warning restore 618 + } + + public static long GetLongAddressValue( IPAddress address ) + { +#pragma warning disable 618 + return address.Address; +#pragma warning restore 618 + } + + #endregion + + public static double RandomDouble() + { + return m_Random.NextDouble(); + } + #region In[...]Range + public static bool InRange( Point3D p1, Point3D p2, int range ) + { + return ( p1.m_X >= (p2.m_X - range) ) + && ( p1.m_X <= (p2.m_X + range) ) + && ( p1.m_Y >= (p2.m_Y - range) ) + && ( p1.m_Y <= (p2.m_Y + range) ); + } + + public static bool InUpdateRange( Point3D p1, Point3D p2 ) + { + return ( p1.m_X >= (p2.m_X - 18) ) + && ( p1.m_X <= (p2.m_X + 18) ) + && ( p1.m_Y >= (p2.m_Y - 18) ) + && ( p1.m_Y <= (p2.m_Y + 18) ); + } + + public static bool InUpdateRange( Point2D p1, Point2D p2 ) + { + return ( p1.m_X >= (p2.m_X - 18) ) + && ( p1.m_X <= (p2.m_X + 18) ) + && ( p1.m_Y >= (p2.m_Y - 18) ) + && ( p1.m_Y <= (p2.m_Y + 18) ); + } + + public static bool InUpdateRange( IPoint2D p1, IPoint2D p2 ) + { + return ( p1.X >= (p2.X - 18) ) + && ( p1.X <= (p2.X + 18) ) + && ( p1.Y >= (p2.Y - 18) ) + && ( p1.Y <= (p2.Y + 18) ); + } + + #endregion + public static Direction GetDirection( IPoint2D from, IPoint2D to ) + { + int dx = to.X - from.X; + int dy = to.Y - from.Y; + + int adx = Math.Abs( dx ); + int ady = Math.Abs( dy ); + + if ( adx >= ady * 3 ) + { + if ( dx > 0 ) + return Direction.East; + else + return Direction.West; + } + else if ( ady >= adx * 3 ) + { + if ( dy > 0 ) + return Direction.South; + else + return Direction.North; + } + else if ( dx > 0 ) + { + if ( dy > 0 ) + return Direction.Down; + else + return Direction.Right; + } + else + { + if ( dy > 0 ) + return Direction.Left; + else + return Direction.Up; + } + } + + public static bool CanMobileFit( int z, Tile[] tiles ) + { + int checkHeight = 15; + int checkZ = z; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + if ( ((checkZ + checkHeight) > tile.Z && checkZ < (tile.Z + tile.Height))/* || (tile.Z < (checkZ + checkHeight) && (tile.Z + tile.Height) > checkZ)*/ ) + { + return false; + } + else if ( checkHeight == 0 && tile.Height == 0 && checkZ == tile.Z ) + { + return false; + } + } + + return true; + } + + public static bool IsInContact( Tile check, Tile[] tiles ) + { + int checkHeight = check.Height; + int checkZ = check.Z; + + for ( int i = 0; i < tiles.Length; ++i ) + { + Tile tile = tiles[i]; + + if ( ((checkZ + checkHeight) > tile.Z && checkZ < (tile.Z + tile.Height))/* || (tile.Z < (checkZ + checkHeight) && (tile.Z + tile.Height) > checkZ)*/ ) + { + return true; + } + else if ( checkHeight == 0 && tile.Height == 0 && checkZ == tile.Z ) + { + return true; + } + } + + return false; + } + + public static object GetArrayCap( Array array, int index ) + { + return GetArrayCap( array, index, null ); + } + + public static object GetArrayCap( Array array, int index, object emptyValue ) + { + if ( array.Length > 0 ) + { + if ( index < 0 ) + { + index = 0; + } + else if ( index >= array.Length ) + { + index = array.Length - 1; + } + + return array.GetValue( index ); + } + else + { + return emptyValue; + } + } + + //4d6+8 would be: Utility.Dice( 4, 6, 8 ) + public static int Dice( int numDice, int numSides, int bonus ) + { + int total = 0; + for (int i=0;i max ) + { + int copy = min; + min = max; + max = copy; + } + else if ( min == max ) + { + return min; + } + + return min + m_Random.Next( (max - min) + 1 ); + } + + public static int Random( int from, int count ) + { + if ( count == 0 ) + { + return from; + } + else if ( count > 0 ) + { + return from + m_Random.Next( count ); + } + else + { + return from - m_Random.Next( -count ); + } + } + + public static int Random( int count ) + { + return m_Random.Next( count ); + } + + #region Random Hues + + public static int RandomNondyedHue() + { + switch ( Random( 6 ) ) + { + case 0: return RandomPinkHue(); + case 1: return RandomBlueHue(); + case 2: return RandomGreenHue(); + case 3: return RandomOrangeHue(); + case 4: return RandomRedHue(); + case 5: return RandomYellowHue(); + } + + return 0; + } + + public static int RandomPinkHue() + { + return Random( 1201, 54 ); + } + + public static int RandomBlueHue() + { + return Random( 1301, 54 ); + } + + public static int RandomGreenHue() + { + return Random( 1401, 54 ); + } + + public static int RandomOrangeHue() + { + return Random( 1501, 54 ); + } + + public static int RandomRedHue() + { + return Random( 1601, 54 ); + } + + public static int RandomYellowHue() + { + return Random( 1701, 54 ); + } + + public static int RandomNeutralHue() + { + return Random( 1801, 108 ); + } + + public static int RandomSnakeHue() + { + return Random( 2001, 18 ); + } + + public static int RandomBirdHue() + { + return Random( 2101, 30 ); + } + + public static int RandomSlimeHue() + { + return Random( 2201, 24 ); + } + + public static int RandomAnimalHue() + { + return Random( 2301, 18 ); + } + + public static int RandomMetalHue() + { + return Random( 2401, 30 ); + } + + public static int ClipDyedHue( int hue ) + { + if ( hue < 2 ) + return 2; + else if ( hue > 1001 ) + return 1001; + else + return hue; + } + + public static int RandomDyedHue() + { + return Random( 2, 1000 ); + } + + //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] + public static int ClipSkinHue( int hue ) + { + if ( hue < 1002 ) + return 1002; + else if ( hue > 1058 ) + return 1058; + else + return hue; + } + + //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] + public static int RandomSkinHue() + { + return Random( 1002, 57 ) | 0x8000; + } + + //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] + public static int ClipHairHue( int hue ) + { + if ( hue < 1102 ) + return 1102; + else if ( hue > 1149 ) + return 1149; + else + return hue; + } + + //[Obsolete( "Depreciated, use the methods for the Mobile's race", false )] + public static int RandomHairHue() + { + return Random( 1102, 48 ); + } + + #endregion + + private static SkillName[] m_AllSkills = new SkillName[] + { + SkillName.Alchemy, + SkillName.Anatomy, + SkillName.AnimalLore, + SkillName.ItemID, + SkillName.ArmsLore, + SkillName.Parry, + SkillName.Begging, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Peacemaking, + SkillName.Camping, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.DetectHidden, + SkillName.Discordance, + SkillName.EvalInt, + SkillName.Healing, + SkillName.Fishing, + SkillName.Forensics, + SkillName.Herding, + SkillName.Hiding, + SkillName.Provocation, + SkillName.Inscribe, + SkillName.Lockpicking, + SkillName.Magery, + SkillName.MagicResist, + SkillName.Tactics, + SkillName.Snooping, + SkillName.Musicianship, + SkillName.Poisoning, + SkillName.Archery, + SkillName.SpiritSpeak, + SkillName.Stealing, + SkillName.Tailoring, + SkillName.AnimalTaming, + SkillName.TasteID, + SkillName.Tinkering, + SkillName.Tracking, + SkillName.Veterinary, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling, + SkillName.Lumberjacking, + SkillName.Mining, + SkillName.Meditation, + SkillName.Stealth, + SkillName.RemoveTrap, + SkillName.Necromancy, + SkillName.Focus, + SkillName.Chivalry, + SkillName.Bushido, + SkillName.Ninjitsu, + SkillName.Spellweaving + }; + + private static SkillName[] m_CombatSkills = new SkillName[] + { + SkillName.Archery, + SkillName.Swords, + SkillName.Macing, + SkillName.Fencing, + SkillName.Wrestling + }; + + private static SkillName[] m_CraftSkills = new SkillName[] + { + SkillName.Alchemy, + SkillName.Blacksmith, + SkillName.Fletching, + SkillName.Carpentry, + SkillName.Cartography, + SkillName.Cooking, + SkillName.Inscribe, + SkillName.Tailoring, + SkillName.Tinkering + }; + + public static SkillName RandomSkill() + { + return m_AllSkills[Utility.Random(m_AllSkills.Length - ( Core.ML ? 0 : Core.SE ? 1 : Core.AOS ? 3 : 6 ) )]; + } + + public static SkillName RandomCombatSkill() + { + return m_CombatSkills[Utility.Random(m_CombatSkills.Length)]; + } + + public static SkillName RandomCraftSkill() + { + return m_CraftSkills[Utility.Random(m_CraftSkills.Length)]; + } + + public static void FixPoints( ref Point3D top, ref Point3D bottom ) + { + if ( bottom.m_X < top.m_X ) + { + int swap = top.m_X; + top.m_X = bottom.m_X; + bottom.m_X = swap; + } + + if ( bottom.m_Y < top.m_Y ) + { + int swap = top.m_Y; + top.m_Y = bottom.m_Y; + bottom.m_Y = swap; + } + + if ( bottom.m_Z < top.m_Z ) + { + int swap = top.m_Z; + top.m_Z = bottom.m_Z; + bottom.m_Z = swap; + } + } + + public static ArrayList BuildArrayList( IEnumerable enumerable ) + { + IEnumerator e = enumerable.GetEnumerator(); + + ArrayList list = new ArrayList(); + + while ( e.MoveNext() ) + { + list.Add( e.Current ); + } + + return list; + } + + public static bool RangeCheck( IPoint2D p1, IPoint2D p2, int range ) + { + return ( p1.X >= (p2.X - range) ) + && ( p1.X <= (p2.X + range) ) + && ( p1.Y >= (p2.Y - range) ) + && ( p2.Y <= (p2.Y + range) ); + } + + public static void FormatBuffer( TextWriter output, Stream input, int length ) + { + output.WriteLine( " 0 1 2 3 4 5 6 7 8 9 A B C D E F" ); + output.WriteLine( " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --" ); + + int byteIndex = 0; + + int whole = length >> 4; + int rem = length & 0xF; + + for ( int i = 0; i < whole; ++i, byteIndex += 16 ) + { + StringBuilder bytes = new StringBuilder( 49 ); + StringBuilder chars = new StringBuilder( 16 ); + + for ( int j = 0; j < 16; ++j ) + { + int c = input.ReadByte(); + + bytes.Append( c.ToString( "X2" ) ); + + if ( j != 7 ) + { + bytes.Append( ' ' ); + } + else + { + bytes.Append( " " ); + } + + if ( c >= 0x20 && c < 0x80 ) + { + chars.Append( (char)c ); + } + else + { + chars.Append( '.' ); + } + } + + output.Write( byteIndex.ToString( "X4" ) ); + output.Write( " " ); + output.Write( bytes.ToString() ); + output.Write( " " ); + output.WriteLine( chars.ToString() ); + } + + if ( rem != 0 ) + { + StringBuilder bytes = new StringBuilder( 49 ); + StringBuilder chars = new StringBuilder( rem ); + + for ( int j = 0; j < 16; ++j ) + { + if ( j < rem ) + { + int c = input.ReadByte(); + + bytes.Append( c.ToString( "X2" ) ); + + if ( j != 7 ) + { + bytes.Append( ' ' ); + } + else + { + bytes.Append( " " ); + } + + if ( c >= 0x20 && c < 0x80 ) + { + chars.Append( (char)c ); + } + else + { + chars.Append( '.' ); + } + } + else + { + bytes.Append( " " ); + } + } + + output.Write( byteIndex.ToString( "X4" ) ); + output.Write( " " ); + output.Write( bytes.ToString() ); + output.Write( " " ); + output.WriteLine( chars.ToString() ); + } + } + + private static Stack m_ConsoleColors = new Stack(); + + public static void PushColor( ConsoleColor color ) + { + try + { + m_ConsoleColors.Push( Console.ForegroundColor ); + Console.ForegroundColor = color; + } + catch + { + } + } + + public static void PopColor() + { + try + { + Console.ForegroundColor = m_ConsoleColors.Pop(); + } + catch + { + } + } + + public static bool NumberBetween( double num, int bound1, int bound2, double allowance ) + { + if ( bound1 > bound2 ) + { + int i = bound1; + bound1 = bound2; + bound2 = i; + } + + return ( numbound1-allowance ); + } + + public static void AssignRandomHair( Mobile m ) + { + AssignRandomHair( m, true ); + } + public static void AssignRandomHair( Mobile m, int hue ) + { + m.HairItemID = m.Race.RandomHair( m ); + m.HairHue = hue; + } + public static void AssignRandomHair( Mobile m, bool randomHue ) + { + m.HairItemID = m.Race.RandomHair( m ); + + if( randomHue ) + m.HairHue = m.Race.RandomHairHue(); + } + + public static void AssignRandomFacialHair( Mobile m ) + { + AssignRandomFacialHair( m, true ); + } + public static void AssignRandomFacialHair( Mobile m, int hue ) + { + m.FacialHairHue = m.Race.RandomFacialHair( m ); + m.FacialHairHue = hue; + } + public static void AssignRandomFacialHair( Mobile m, bool randomHue ) + { + m.FacialHairItemID = m.Race.RandomFacialHair( m ); + + if( randomHue ) + m.FacialHairHue = m.Race.RandomHairHue(); + } + + public static List CastConvertList( List list ) where TOutput : TInput + { + return list.ConvertAll( new Converter( delegate( TInput value ) { return (TOutput)value; } ) ); + } + + public static List SafeConvertList( List list ) where TOutput : class + { + List output = new List( list.Capacity ); + + for( int i = 0; i < list.Count; i++ ) + { + TOutput t = list[i] as TOutput; + + if( t != null ) + output.Add( t ); + } + + return output; + } + } +} \ No newline at end of file diff --git a/Server/VirtueInfo.cs b/Server/VirtueInfo.cs new file mode 100644 index 000000000..944f59869 --- /dev/null +++ b/Server/VirtueInfo.cs @@ -0,0 +1,147 @@ +/*************************************************************************** + * VirtueInfo.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: VirtueInfo.cs 20 2006-01-15 23:50:35Z asayre $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; + +namespace Server +{ + [PropertyObject] + public class VirtueInfo + { + private int[] m_Values; + + public int[] Values + { + get + { + return m_Values; + } + } + + public int GetValue( int index ) + { + if ( m_Values == null ) + return 0; + else + return m_Values[index]; + } + + public void SetValue( int index, int value ) + { + if ( m_Values == null ) + m_Values = new int[8]; + + m_Values[index] = value; + } + + public override string ToString() + { + return "..."; + } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Humility{ get{ return GetValue( 0 ); } set{ SetValue( 0, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Sacrifice{ get{ return GetValue( 1 ); } set{ SetValue( 1, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Compassion{ get{ return GetValue( 2 ); } set{ SetValue( 2, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Spirituality{ get{ return GetValue( 3 ); } set{ SetValue( 3, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Valor{ get{ return GetValue( 4 ); } set{ SetValue( 4, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Honor{ get{ return GetValue( 5 ); } set{ SetValue( 5, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Justice{ get{ return GetValue( 6 ); } set{ SetValue( 6, value ); } } + + [CommandProperty( AccessLevel.Counselor, AccessLevel.GameMaster )] + public int Honesty{ get{ return GetValue( 7 ); } set{ SetValue( 7, value ); } } + + public VirtueInfo() + { + } + + public VirtueInfo( GenericReader reader ) + { + int version = reader.ReadByte(); + + switch ( version ) + { + case 1: //Changed the values throughout the virtue system + case 0: + { + int mask = reader.ReadByte(); + + if ( mask != 0 ) + { + m_Values = new int[8]; + + for ( int i = 0; i < 8; ++i ) + if ( (mask & (1 << i)) != 0 ) + m_Values[i] = reader.ReadInt(); + } + + break; + } + } + + if( version == 0 ) + { + Compassion *= 200; + Sacrifice *= 250; //Even though 40 (the max) only gives 10k, It's because it was formerly too easy + + //No direct conversion factor for Justice, this is just an approximation + Justice *= 500; + + //All the other virtues haven't been defined at 'version 0' point in time in the scripts. + } + } + + public static void Serialize( GenericWriter writer, VirtueInfo info ) + { + writer.Write( (byte) 1 ); // version + + if ( info.m_Values == null ) + { + writer.Write( (byte) 0 ); + } + else + { + int mask = 0; + + for ( int i = 0; i < 8; ++i ) + if ( info.m_Values[i] != 0 ) + mask |= 1 << i; + + writer.Write( (byte) mask ); + + for ( int i = 0; i < 8; ++i ) + if ( info.m_Values[i] != 0 ) + writer.Write( (int) info.m_Values[i] ); + } + } + } +} \ No newline at end of file diff --git a/Server/World.cs b/Server/World.cs new file mode 100644 index 000000000..eb549b607 --- /dev/null +++ b/Server/World.cs @@ -0,0 +1,789 @@ +/*************************************************************************** + * World.cs + * ------------------- + * begin : May 1, 2002 + * copyright : (C) The RunUO Software Team + * email : info@runuo.com + * + * $Id: World.cs 163 2006-06-15 00:59:08Z krrios $ + * + ***************************************************************************/ + +/*************************************************************************** + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + ***************************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using System.Diagnostics; +using Server; +using Server.Mobiles; +using Server.Accounting; +using Server.Network; +using Server.Guilds; + +namespace Server { + public static class World { + public enum SaveOption { + Normal, + Threaded + } + + public static SaveOption SaveType = SaveOption.Normal; + + private static Dictionary m_Mobiles; + private static Dictionary m_Items; + + private static bool m_Loading; + private static bool m_Loaded; + private static bool m_Saving; + private static ArrayList m_DeleteList; + + public static bool Saving { get { return m_Saving; } } + public static bool Loaded { get { return m_Loaded; } } + public static bool Loading { get { return m_Loading; } } + + public readonly static string MobileIndexPath = Path.Combine( "Saves/Mobiles/", "Mobiles.idx" ); + public readonly static string MobileTypesPath = Path.Combine( "Saves/Mobiles/", "Mobiles.tdb" ); + public readonly static string MobileDataPath = Path.Combine( "Saves/Mobiles/", "Mobiles.bin" ); + + public readonly static string ItemIndexPath = Path.Combine( "Saves/Items/", "Items.idx" ); + public readonly static string ItemTypesPath = Path.Combine( "Saves/Items/", "Items.tdb" ); + public readonly static string ItemDataPath = Path.Combine( "Saves/Items/", "Items.bin" ); + + public readonly static string GuildIndexPath = Path.Combine( "Saves/Guilds/", "Guilds.idx" ); + public readonly static string GuildDataPath = Path.Combine( "Saves/Guilds/", "Guilds.bin" ); + + public static Dictionary Mobiles { + get { return m_Mobiles; } + } + + public static Dictionary Items { + get { return m_Items; } + } + + public static bool OnDelete( object o ) { + if ( !m_Loading ) + return true; + + m_DeleteList.Add( o ); + + return false; + } + + public static void Broadcast( int hue, bool ascii, string text ) { + Packet p; + + if ( ascii ) + p = new AsciiMessage( Serial.MinusOne, -1, MessageType.Regular, hue, 3, "System", text ); + else + p = new UnicodeMessage( Serial.MinusOne, -1, MessageType.Regular, hue, 3, "ENU", "System", text ); + + List list = NetState.Instances; + + p.Acquire(); + + for ( int i = 0; i < list.Count; ++i ) { + if ( list[i].Mobile != null ) + list[i].Send( p ); + } + + p.Release(); + + NetState.FlushAll(); + } + + public static void Broadcast( int hue, bool ascii, string format, params object[] args ) { + Broadcast( hue, ascii, String.Format( format, args ) ); + } + + private interface IEntityEntry { + Serial Serial { get; } + int TypeID { get; } + long Position { get; } + int Length { get; } + } + + private sealed class GuildEntry : IEntityEntry { + private BaseGuild m_Guild; + private long m_Position; + private int m_Length; + + public BaseGuild Guild { + get { + return m_Guild; + } + } + + public Serial Serial { + get { + return m_Guild == null ? 0 : m_Guild.Id; + } + } + + public int TypeID { + get { + return 0; + } + } + + public long Position { + get { + return m_Position; + } + } + + public int Length { + get { + return m_Length; + } + } + + public GuildEntry( BaseGuild g, long pos, int length ) { + m_Guild = g; + m_Position = pos; + m_Length = length; + } + } + + private sealed class ItemEntry : IEntityEntry { + private Item m_Item; + private int m_TypeID; + private string m_TypeName; + private long m_Position; + private int m_Length; + + public Item Item { + get { + return m_Item; + } + } + + public Serial Serial { + get { + return m_Item == null ? Serial.MinusOne : m_Item.Serial; + } + } + + public int TypeID { + get { + return m_TypeID; + } + } + + public string TypeName { + get { + return m_TypeName; + } + } + + public long Position { + get { + return m_Position; + } + } + + public int Length { + get { + return m_Length; + } + } + + public ItemEntry( Item item, int typeID, string typeName, long pos, int length ) { + m_Item = item; + m_TypeID = typeID; + m_TypeName = typeName; + m_Position = pos; + m_Length = length; + } + } + + private sealed class MobileEntry : IEntityEntry { + private Mobile m_Mobile; + private int m_TypeID; + private string m_TypeName; + private long m_Position; + private int m_Length; + + public Mobile Mobile { + get { + return m_Mobile; + } + } + + public Serial Serial { + get { + return m_Mobile == null ? Serial.MinusOne : m_Mobile.Serial; + } + } + + public int TypeID { + get { + return m_TypeID; + } + } + + public string TypeName { + get { + return m_TypeName; + } + } + + public long Position { + get { + return m_Position; + } + } + + public int Length { + get { + return m_Length; + } + } + + public MobileEntry( Mobile mobile, int typeID, string typeName, long pos, int length ) { + m_Mobile = mobile; + m_TypeID = typeID; + m_TypeName = typeName; + m_Position = pos; + m_Length = length; + } + } + + private static string m_LoadingType; + + public static string LoadingType { + get { return m_LoadingType; } + } + + public static void Load() { + if ( m_Loaded ) + return; + + m_Loaded = true; + m_LoadingType = null; + + Console.Write( "World: Loading..." ); + + Stopwatch watch = Stopwatch.StartNew(); + + m_Loading = true; + m_DeleteList = new ArrayList(); + + int mobileCount = 0, itemCount = 0, guildCount = 0; + + object[] ctorArgs = new object[1]; + Type[] ctorTypes = new Type[1] { typeof( Serial ) }; + + List items = new List(); + List mobiles = new List(); + List guilds = new List(); + + if ( File.Exists( MobileIndexPath ) && File.Exists( MobileTypesPath ) ) { + using ( FileStream idx = new FileStream( MobileIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryReader idxReader = new BinaryReader( idx ); + + using ( FileStream tdb = new FileStream( MobileTypesPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryReader tdbReader = new BinaryReader( tdb ); + + int count = tdbReader.ReadInt32(); + + ArrayList types = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) { + string typeName = tdbReader.ReadString(); + + Type t = ScriptCompiler.FindTypeByFullName( typeName ); + + if ( t == null ) { + Console.WriteLine( "failed" ); + Console.WriteLine( "Error: Type '{0}' was not found. Delete all of those types? (y/n)", typeName ); + + if ( Console.ReadKey( true ).Key == ConsoleKey.Y ) { + types.Add( null ); + Console.Write( "World: Loading..." ); + continue; + } + + Console.WriteLine( "Types will not be deleted. An exception will be thrown when you press return" ); + + throw new Exception( String.Format( "Bad type '{0}'", typeName ) ); + } + + ConstructorInfo ctor = t.GetConstructor( ctorTypes ); + + if ( ctor != null ) { + types.Add( new object[] { ctor, null } ); + } else { + throw new Exception( String.Format( "Type '{0}' does not have a serialization constructor", t ) ); + } + } + + mobileCount = idxReader.ReadInt32(); + + m_Mobiles = new Dictionary( mobileCount ); + + for ( int i = 0; i < mobileCount; ++i ) { + int typeID = idxReader.ReadInt32(); + int serial = idxReader.ReadInt32(); + long pos = idxReader.ReadInt64(); + int length = idxReader.ReadInt32(); + + object[] objs = ( object[] ) types[typeID]; + + if ( objs == null ) + continue; + + Mobile m = null; + ConstructorInfo ctor = ( ConstructorInfo ) objs[0]; + string typeName = ( string ) objs[1]; + + try { + ctorArgs[0] = ( Serial ) serial; + m = ( Mobile ) ( ctor.Invoke( ctorArgs ) ); + } catch { + } + + if ( m != null ) { + mobiles.Add( new MobileEntry( m, typeID, typeName, pos, length ) ); + AddMobile( m ); + } + } + + tdbReader.Close(); + } + + idxReader.Close(); + } + } else { + m_Mobiles = new Dictionary(); + } + + if ( File.Exists( ItemIndexPath ) && File.Exists( ItemTypesPath ) ) { + using ( FileStream idx = new FileStream( ItemIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryReader idxReader = new BinaryReader( idx ); + + using ( FileStream tdb = new FileStream( ItemTypesPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryReader tdbReader = new BinaryReader( tdb ); + + int count = tdbReader.ReadInt32(); + + ArrayList types = new ArrayList( count ); + + for ( int i = 0; i < count; ++i ) { + string typeName = tdbReader.ReadString(); + + Type t = ScriptCompiler.FindTypeByFullName( typeName ); + + if ( t == null ) { + Console.WriteLine( "failed" ); + Console.WriteLine( "Error: Type '{0}' was not found. Delete all of those types? (y/n)", typeName ); + + if ( Console.ReadKey( true ).Key == ConsoleKey.Y ) { + types.Add( null ); + Console.Write( "World: Loading..." ); + continue; + } + + Console.WriteLine( "Types will not be deleted. An exception will be thrown when you press return" ); + + throw new Exception( String.Format( "Bad type '{0}'", typeName ) ); + } + + ConstructorInfo ctor = t.GetConstructor( ctorTypes ); + + if ( ctor != null ) { + types.Add( new object[] { ctor, typeName } ); + } else { + throw new Exception( String.Format( "Type '{0}' does not have a serialization constructor", t ) ); + } + } + + itemCount = idxReader.ReadInt32(); + + m_Items = new Dictionary( itemCount ); + + for ( int i = 0; i < itemCount; ++i ) { + int typeID = idxReader.ReadInt32(); + int serial = idxReader.ReadInt32(); + long pos = idxReader.ReadInt64(); + int length = idxReader.ReadInt32(); + + object[] objs = ( object[] ) types[typeID]; + + if ( objs == null ) + continue; + + Item item = null; + ConstructorInfo ctor = ( ConstructorInfo ) objs[0]; + string typeName = ( string ) objs[1]; + + try { + ctorArgs[0] = ( Serial ) serial; + item = ( Item ) ( ctor.Invoke( ctorArgs ) ); + } catch { + } + + if ( item != null ) { + items.Add( new ItemEntry( item, typeID, typeName, pos, length ) ); + AddItem( item ); + } + } + + tdbReader.Close(); + } + + idxReader.Close(); + } + } else { + m_Items = new Dictionary(); + } + + if ( File.Exists( GuildIndexPath ) ) { + using ( FileStream idx = new FileStream( GuildIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryReader idxReader = new BinaryReader( idx ); + + guildCount = idxReader.ReadInt32(); + + CreateGuildEventArgs createEventArgs = new CreateGuildEventArgs( -1 ); + for ( int i = 0; i < guildCount; ++i ) { + idxReader.ReadInt32();//no typeid for guilds + int id = idxReader.ReadInt32(); + long pos = idxReader.ReadInt64(); + int length = idxReader.ReadInt32(); + + createEventArgs.Id = id; + BaseGuild guild = EventSink.InvokeCreateGuild( createEventArgs ); + if ( guild != null ) + guilds.Add( new GuildEntry( guild, pos, length ) ); + } + + idxReader.Close(); + } + } + + bool failedMobiles = false, failedItems = false, failedGuilds = false; + Type failedType = null; + Serial failedSerial = Serial.Zero; + Exception failed = null; + int failedTypeID = 0; + + if ( File.Exists( MobileDataPath ) ) { + using ( FileStream bin = new FileStream( MobileDataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryFileReader reader = new BinaryFileReader( new BinaryReader( bin ) ); + + for ( int i = 0; i < mobiles.Count; ++i ) { + MobileEntry entry = mobiles[i]; + Mobile m = entry.Mobile; + + if ( m != null ) { + reader.Seek( entry.Position, SeekOrigin.Begin ); + + try { + m_LoadingType = entry.TypeName; + m.Deserialize( reader ); + + if ( reader.Position != ( entry.Position + entry.Length ) ) + throw new Exception( String.Format( "***** Bad serialize on {0} *****", m.GetType() ) ); + } catch ( Exception e ) { + mobiles.RemoveAt( i ); + + failed = e; + failedMobiles = true; + failedType = m.GetType(); + failedTypeID = entry.TypeID; + failedSerial = m.Serial; + + break; + } + } + } + + reader.Close(); + } + } + + if ( !failedMobiles && File.Exists( ItemDataPath ) ) { + using ( FileStream bin = new FileStream( ItemDataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryFileReader reader = new BinaryFileReader( new BinaryReader( bin ) ); + + for ( int i = 0; i < items.Count; ++i ) { + ItemEntry entry = items[i]; + Item item = entry.Item; + + if ( item != null ) { + reader.Seek( entry.Position, SeekOrigin.Begin ); + + try { + m_LoadingType = entry.TypeName; + item.Deserialize( reader ); + + if ( reader.Position != ( entry.Position + entry.Length ) ) + throw new Exception( String.Format( "***** Bad serialize on {0} *****", item.GetType() ) ); + } catch ( Exception e ) { + items.RemoveAt( i ); + + failed = e; + failedItems = true; + failedType = item.GetType(); + failedTypeID = entry.TypeID; + failedSerial = item.Serial; + + break; + } + } + } + + reader.Close(); + } + } + + m_LoadingType = null; + + if ( !failedMobiles && !failedItems && File.Exists( GuildDataPath ) ) { + using ( FileStream bin = new FileStream( GuildDataPath, FileMode.Open, FileAccess.Read, FileShare.Read ) ) { + BinaryFileReader reader = new BinaryFileReader( new BinaryReader( bin ) ); + + for ( int i = 0; i < guilds.Count; ++i ) { + GuildEntry entry = guilds[i]; + BaseGuild g = entry.Guild; + + if ( g != null ) { + reader.Seek( entry.Position, SeekOrigin.Begin ); + + try { + g.Deserialize( reader ); + + if ( reader.Position != ( entry.Position + entry.Length ) ) + throw new Exception( String.Format( "***** Bad serialize on Guild {0} *****", g.Id ) ); + } catch ( Exception e ) { + guilds.RemoveAt( i ); + + failed = e; + failedGuilds = true; + failedType = typeof( BaseGuild ); + failedTypeID = g.Id; + failedSerial = g.Id; + + break; + } + } + } + + reader.Close(); + } + } + + if ( failedItems || failedMobiles || failedGuilds ) { + Console.WriteLine( "An error was encountered while loading a saved object" ); + + Console.WriteLine( " - Type: {0}", failedType ); + Console.WriteLine( " - Serial: {0}", failedSerial ); + + Console.WriteLine( "Delete the object? (y/n)" ); + + if ( Console.ReadKey( true ).Key == ConsoleKey.Y ) { + if ( failedType != typeof( BaseGuild ) ) { + Console.WriteLine( "Delete all objects of that type? (y/n)" ); + + if ( Console.ReadKey( true ).Key == ConsoleKey.Y ) { + if ( failedMobiles ) { + for ( int i = 0; i < mobiles.Count; ) { + if ( mobiles[i].TypeID == failedTypeID ) + mobiles.RemoveAt( i ); + else + ++i; + } + } else if ( failedItems ) { + for ( int i = 0; i < items.Count; ) { + if ( items[i].TypeID == failedTypeID ) + items.RemoveAt( i ); + else + ++i; + } + } + } + } + + SaveIndex( mobiles, MobileIndexPath ); + SaveIndex( items, ItemIndexPath ); + SaveIndex( guilds, GuildIndexPath ); + } + + Console.WriteLine( "After pressing return an exception will be thrown and the server will terminate" ); + Console.ReadLine(); + + throw new Exception( String.Format( "Load failed (items={0}, mobiles={1}, guilds={2}, type={3}, serial={4})", failedItems, failedMobiles, failedGuilds, failedType, failedSerial ), failed ); + } + + EventSink.InvokeWorldLoad(); + + m_Loading = false; + + for ( int i = 0; i < m_DeleteList.Count; ++i ) { + object o = m_DeleteList[i]; + + if ( o is Item ) + ( ( Item ) o ).Delete(); + else if ( o is Mobile ) + ( ( Mobile ) o ).Delete(); + } + + m_DeleteList.Clear(); + + foreach ( Item item in m_Items.Values ) { + if ( item.Parent == null ) + item.UpdateTotals(); + + item.ClearProperties(); + } + + foreach ( Mobile m in m_Mobiles.Values ) { + m.UpdateRegion(); // Is this really needed? + m.UpdateTotals(); + + m.ClearProperties(); + } + + watch.Stop(); + + Console.WriteLine( "done ({1} items, {2} mobiles) ({0:F2} seconds)", watch.Elapsed.TotalSeconds, m_Items.Count, m_Mobiles.Count ); + } + + private static void SaveIndex( List list, string path ) where T : IEntityEntry { + if ( !Directory.Exists( "Saves/Mobiles/" ) ) + Directory.CreateDirectory( "Saves/Mobiles/" ); + + if ( !Directory.Exists( "Saves/Items/" ) ) + Directory.CreateDirectory( "Saves/Items/" ); + + if ( !Directory.Exists( "Saves/Guilds/" ) ) + Directory.CreateDirectory( "Saves/Guilds/" ); + + using ( FileStream idx = new FileStream( path, FileMode.Create, FileAccess.Write, FileShare.None ) ) { + BinaryWriter idxWriter = new BinaryWriter( idx ); + + idxWriter.Write( list.Count ); + + for ( int i = 0; i < list.Count; ++i ) { + T e = list[i]; + + idxWriter.Write( e.TypeID ); + idxWriter.Write( e.Serial ); + idxWriter.Write( e.Position ); + idxWriter.Write( e.Length ); + } + + idxWriter.Close(); + } + } + + internal static int m_Saves; + + public static void Save() { + ++m_Saves; + Save( true ); + } + + public static void Save( bool message ) { + if ( m_Saving || AsyncWriter.ThreadCount > 0 ) + return; + + NetState.FlushAll(); + NetState.Pause(); + + m_Saving = true; + + if ( message ) + Broadcast( 0x35, true, "The world is saving, please wait." ); + + SaveStrategy strategy = SaveStrategy.Acquire(); + Console.WriteLine( "Core: Using {0} save strategy", strategy.Name.ToLowerInvariant() ); + + Console.Write( "World: Saving..." ); + + Stopwatch watch = Stopwatch.StartNew(); + + if ( !Directory.Exists( "Saves/Mobiles/" ) ) + Directory.CreateDirectory( "Saves/Mobiles/" ); + if ( !Directory.Exists( "Saves/Items/" ) ) + Directory.CreateDirectory( "Saves/Items/" ); + if ( !Directory.Exists( "Saves/Guilds/" ) ) + Directory.CreateDirectory( "Saves/Guilds/" ); + + + /*using ( SaveMetrics metrics = new SaveMetrics() ) {*/ + strategy.Save( null ); + /*}*/ + + try { + EventSink.InvokeWorldSave( new WorldSaveEventArgs( message ) ); + } catch ( Exception e ) { + throw new Exception( "World Save event threw an exception. Save failed!", e ); + } + + watch.Stop(); + + Console.WriteLine( "done in {0:F2} seconds.", watch.Elapsed.TotalSeconds ); + + if ( message ) + Broadcast( 0x35, true, "World save complete. The entire process took {0:F1} seconds.", watch.Elapsed.TotalSeconds ); + + m_Saving = false; + NetState.Resume(); + } + + internal static List m_ItemTypes = new List(); + internal static List m_MobileTypes = new List(); + + public static IEntity FindEntity( Serial serial ) { + if ( serial.IsItem ) + return FindItem( serial ); + else if ( serial.IsMobile ) + return FindMobile( serial ); + + return null; + } + + public static Mobile FindMobile( Serial serial ) { + Mobile mob; + + m_Mobiles.TryGetValue( serial, out mob ); + + return mob; + } + + public static void AddMobile( Mobile m ) { + m_Mobiles[m.Serial] = m; + } + + public static Item FindItem( Serial serial ) { + Item item; + + m_Items.TryGetValue( serial, out item ); + + return item; + } + + public static void AddItem( Item item ) { + m_Items[item.Serial] = item; + } + + public static void RemoveMobile( Mobile m ) { + m_Mobiles.Remove( m.Serial ); + } + + public static void RemoveItem( Item item ) { + m_Items.Remove( item.Serial ); + } + } +} \ No newline at end of file diff --git a/Server/runuo.ico b/Server/runuo.ico new file mode 100644 index 0000000000000000000000000000000000000000..d77808b2010f30087f0018e6430454bb03149674 GIT binary patch literal 13094 zcmeHMdu&uy8vjf%qlqDCqPv^$2VkO->?Lf5EKm*G*d5JU>jTA6M3F*oZ`6)F%D{?f z6-8iZfw?So!1~}JBJ0S4MUdg4@=zIrS{{nFtF*jin9k!4MM3WVzH{%)+|H|IP1a59 zH$C^B^PTVUJKs6?yh?Nl^`gOpNzj|UiEcrzUq7>a8BrL#K7EX`aw*Z0-w_oT8|53W zA-d3)=mr$o#3M+T5R{lMH(ug-37(ulx~|iO3m53;pMR$F=YFPWdxB1#jM0gs5jt@! zLPw8;=~!!+;;{~D{`v$RK6so?orux?&s%9{;49j)p@}xU^%-qg^BKLoXgkt2+Pv;_ zYQX!wH#gJbIh$$0vyDg_X;!tLY9{}arYozF-k=F1eYCVLK*8NDuzixo-oAt;m#-kt z(;H~j(w%hQ%?oMW>b*3zVinz6ynr6~>wLQX&-2JxvWV^+K9AmB`3cRho=;=`_7A%I z#(7jV=2aRsVm6Jt^Cdcy_>saPjY=V32miCDuLJgD^u*ZLp*x%IE_$Bsxp5Y4TK5HP z&Vi4WbkASv=*P3?sAA+&YW;VJc5eNO?iltWbl;)7Z>ptxZ+aHK7Sn>~Hqz?)b#(jC zdC;9pOR(V?cJ^y=ad=t=IPe+X9+9V?AwrYZpx) zzl07zZugGEbojtAn(^2gDo2|G=!+x3eg6D8(z#F1pGTjbqw_!gL_eN6LubzDE}6VlGR?0TLyM|w$TR*yYH9kMR96LgCX6P(cRq!V zwZX^tBX`4)Bar_(kJh2paXk$Qmy>>Zd%0Hl3mQ6CQ+*qcm&6RA8D&`*(jx)9$UH zgCFk#u7_yqNQI`}qtN$<4$#N2yJFT;^#1Y&_$#Ms)cK}8L9fho)2lVpY4Jpbmd~0& zD_?k;<~-u0ZL62lo(=EN+;R6)0C9-7hiS$0GbtK6MID+(t7bm~-Kq5CedB5Qb2I3> z15Gp+danR?UFE~HZ+igmv51FEA2)a9Rd1k@5khwG?5md?Q_RD zsrB&JGzY$x0#jo{J@QV(aSXk`qJj3jyAJLDh?YG$iH?4I7=G@jTKHJ}L?z<+0Bw4G z8NKk(Xo{T<(c6nWz<)n_⋙DCRz!+N1MN)x4j;6cR|++n%ISuQ9X31 z+bszya=V9hsWP;rWT;yz>D{}xROGHG9oU7!&=N@+GDMmTG0EAtRFVoSK>YFBq|%}R zkgqP0oP)~>DzLcQJvfQFVxs%jf(lnjgBmgbS4+;`87xb&zf{7dYp+wL47r|F$CWz! zSn4yg!<-6O1D(>8l0j10B)5B#XnLlYS-!exvQ+JM_mQSlfHT-xcB$LlS1Kw3FDFfr zBqw@HDZN}&UnO0QD52}naXD#9NpFN^lJp0&wv55`9+x4%N%tK090%1FKZLjEPmD>> zWQkwg(H5H1mZIWOoPAHa-mD%{%9#v#PfEP4xOn8qwzh&rMi?y|CEo%zm?cP3g| zj$G%2)j@+HQ7sKYgso(iuPAli33nd%us+p1L_xPn_#Uiem7nOm;rGDfvDEjM6!fx% z>RT-M`#4LRnnv7rtEeAfCQqKs-SlV%7`V-p=lB2#sMD}9(Pzt}`PTTP5QkPrRBAh`~s9(T2~j6M_f|!3xJ}neiPL5+c)K#Vfa)F z_cX2OIu4@^92^qPigsc48~*Wvz$sP`4?Gy+svFo7G_d@EsN`! z)1XDr%(A>^+qP{i-o}|vXJiXmIFN@1KazC9Qk$I>@<~zgZZ6mc#-j(5#IRZlMwI16 z4++<+no+FDMv>;QJdX#1W^nOWmtT9vq?tS>y267|R^M805O+j|mBp6MT6JhTr|->dquS=45z(;oCR?^*n#A2=FYUmOg(O+dsYEs6k7{Z- zhlC$8J9g}#9pCPt$fs{muU@_Avdb=`E3UYLuDtR}y5^c|sBhoCG+@908aQwujT$wI z%F4><=9_P(;lqbh%~Lwvb(c;m9V9&zq}>PG>EQ9>v~bPa@L)%hk$gx&qi6@u4xSx6 zJM(lyLTk6a+sAeh~a17(wuLly#K#pzv(R(+>Z3 z7_vjjPC5zqc-Vo#4h(i+umgjD1Ppdyumgh~80^3xU;=|380^4c2L?MZ2$WH^`MNQO}vSVS05DI+5TiwrCx zEWjZHhYTDtaLB+RLIoT$Fv!3l0|SRn1`Zh*WMBxwmyV|hA^aFWltp+sIzR>AK1hjB z!?zEZd~oW610OK?;M<2vAB_0m+XvsGJA`lW;ok@UKKSP@^1;6kvGD%y2VBDqYs*+{D-36N}>NJ;2*BlV;OsdNUHv88g$gMCb>h&{KR{8xQ z!>lW;>$X(As6~BcrQh%1c4g?JJ`Czmm5{&E6*v8dQvPAq@Z^F4mqT%6;7``8N{8Pc z)glJOl)9d*k1H;JC6HytFIlh06&TNsf2v+@uk^cID)*nYdo1;Szt`ITiD*>Qg?|l$ zE3UT#pBXgqiu(QSqFxIj2+{oYT#aVs*4Gp0fDjyIkzrp)wKrF_j{3$ZcV8$POJr&5 zT%V>K7CO~)WHX5UYl3W|r5V+1AMIy~q;f{tcy^=S%R?s|MjLEa&6Iu`f244|2_#!6 zF9*}XuS&d6;VbukNU42_d4CR;ahIMd*YM^93HuX%@ei4veM-+hrDvb=8`-DC0;(EM z&+{m0HdReA)MyM_m(c?6bRRf}o*a2qkc2boDS6bJJKs$TJe+L^Fk8ieWY7ZK01#Yi6S@X|cMybeyv*HFZbD|LvdhAcxmw4taNCkN zY-T*ORWdkezc?jDxf?iiVmXdprK9K{xw?%AKV3n{EEVvkhfM~Hoeq2>O>AP%+l8y4OQb= zr6{;y0aZFIcQ=Lfaa&-alzx>FMFT(~{3-0hN|oXeH#oS%Nkc6dKg0QHlR~1Cn1=?K zXZ1+I{uBD<{eh;%1K}{l!2}Y;CVB$nhr2I~a*nyw&^H4uUKlwXLm1!*ke{rcZ~SKK zW8C8k3VTyy=IP@5$<&Y6B65O>i?lGl-{kC{=ljjnhnRwU5>Vz<2XJML9==xsS*|t2 z>8~Tm8q|@flBVaS`&HU`7=vj7!bq2fonegkeL{K`h&7`SGK+|fMJ+k zKUB7nXZ^^bug6Yt58@Iu5Q`b#teSWI%B3GSt~o91rzVD?1$HW%Tc5ch(QrI+^jbIar@-q9cOB?z0{bH-srMjM1Uku-0pnp2&9GPX%@E?dRt#9{p89d%qPS~;-d6B54iseeg6MRH^^vz4*VCT&!?jlMU3y@V*H=(NE5E!m`-1e szM<0T`Af%NfjQ3_)_miiC;#qHZv9Z+MYcOO|3@<7F0kkEn~lJK06&l&WdHyG literal 0 HcmV?d00001